diff --git a/make b/make
index 8d991dd..2a9d3bc 100755
--- a/make
+++ b/make
@@ -116,7 +116,7 @@ build(){
for file in dist/xrfragment.{aframe,module,three,three.module,aframe.all}.js; do
awk 'BEGIN{
print "/*"
- print " * '"$(git tag)"' generated at '"$(date)"'"
+ print " * '"$(git tag | head -n1)"' generated at '"$(date)"'"
print " * https://xrfragment.org"
print " * SPDX-License-Identifier: MPL-2.0"
print " */"
diff --git a/src/3rd/js/plugin/frontend/$chat.js b/src/3rd/js/plugin/frontend/$chat.js
index 6abd17c..ba3ff79 100644
--- a/src/3rd/js/plugin/frontend/$chat.js
+++ b/src/3rd/js/plugin/frontend/$chat.js
@@ -409,9 +409,13 @@ chatComponent.css = `
margin:0;
}
+ .envelope{
+ margin-right:15px;
+ max-width:80%;
+ }
+
.envelope,
.envelope * {
- overflow:hidden;
transition:1s;
pointer-events:none;
}
diff --git a/src/3rd/js/plugin/frontend/accessibility.js b/src/3rd/js/plugin/frontend/accessibility.js
index 3d9f014..e93de4a 100644
--- a/src/3rd/js/plugin/frontend/accessibility.js
+++ b/src/3rd/js/plugin/frontend/accessibility.js
@@ -101,14 +101,50 @@ window.accessibility = (opts) => new Proxy({
setTimeout( () => this.initCommands(), 200 )
// auto-enable if previously enabled
if( window.localStorage.getItem("accessibility") ){
- setTimeout( () => this.enabled = true, 100 )
+ setTimeout( () => {
+ this.enabled = true
+ this.setFontSize()
+ }, 100 )
}
},
initCommands(){
+
document.addEventListener('chat.command.help', (e) => {
e.detail.message += `
/fontsize set fontsize (default=14) `
})
+
+ document.addEventListener('chat.command', (e) => {
+ if( e.detail.message.match(/^fontsize/) ){
+ try{
+ let fontsize = parseInt( e.detail.message.replace(/^fontsize /,'').trim() )
+ if( fontsize == NaN ) throw 'not a number'
+ this.setFontSize(fontsize)
+ $chat.send({message:'fontsize set to '+fontsize})
+ }catch(e){
+ console.error(e)
+ $chat.send({message:'example usage: /fontsize 20'})
+ }
+ }
+ })
+
+ },
+
+ setFontSize(size){
+ if( size ){
+ window.localStorage.setItem("fontsize",size)
+ }else size = window.localStorage.getItem("fontsize")
+ if( !size ) return
+ document.head.innerHTML += `
+
+ `
+ $messages = document.querySelector('#messages')
+ setTimeout( () => $messages.scrollTop = $messages.scrollHeight, 1000 )
},
posToMessage(opts){
@@ -182,9 +218,6 @@ document.querySelector('head').innerHTML += `
padding-top:15px;
padding-bottom:15px;
}
- .accessibility .transcript {
- max-height:unset;
- }
.accessibility #chatbar{
display: block !important;
}
diff --git a/src/3rd/js/plugin/frontend/css.js b/src/3rd/js/plugin/frontend/css.js
index 644649f..ebdbe19 100644
--- a/src/3rd/js/plugin/frontend/css.js
+++ b/src/3rd/js/plugin/frontend/css.js
@@ -370,7 +370,7 @@ document.head.innerHTML += `
.transcript{
max-height:105px;
- max-width:405px;
+ width:100%;
overflow-y:auto;
border: 1px solid var(--xrf-gray);
border-radius: 5px;
diff --git a/src/3rd/js/plugin/vendors/README.md b/src/3rd/js/plugin/vendors/README.md
new file mode 100644
index 0000000..291fbed
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/README.md
@@ -0,0 +1,4 @@
+vendor specific macros are meta-dat mappings which translate to vendor specific features:
+
+'-three-fog' : '10,100' ==> three/fog.js ==> scene.fog = new THREE.Fog( scene.background, v.x, v.y );
+
diff --git a/src/3rd/js/plugin/vendors/spec.json b/src/3rd/js/plugin/vendors/spec.json
new file mode 100644
index 0000000..e9a6a9f
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/spec.json
@@ -0,0 +1,6 @@
+{
+ "-{vendor}-fog": {
+ "near": "x",
+ "far": "y"
+ }
+}
diff --git a/src/3rd/js/plugin/vendors/three/README.md b/src/3rd/js/plugin/vendors/three/README.md
new file mode 100644
index 0000000..8370beb
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/README.md
@@ -0,0 +1 @@
+currently broken
diff --git a/src/3rd/js/plugin/vendors/three/fragments.js b/src/3rd/js/plugin/vendors/three/fragments.js
new file mode 100644
index 0000000..16280c9
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/fragments.js
@@ -0,0 +1,10 @@
+xrf.frag.clip = function(v, opts){
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+
+ if( v.x == 0 ) v.x = 1; // THREE.js .near restriction
+ console.log("└ clip "+v.x+","+v.y);
+
+ camera.near = v.x
+ camera.far = v.y
+ camera.updateProjectionMatrix();
+}
diff --git a/src/3rd/js/plugin/vendors/three/metadata.js b/src/3rd/js/plugin/vendors/three/metadata.js
new file mode 100644
index 0000000..a68f4de
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/metadata.js
@@ -0,0 +1,16 @@
+xrf.addEventListener('-three-fog', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ let v = frag.fog
+ console.log("└ threejs fog "+v.x+","+v.y);
+ if( v.x == 0 && v.y == 0 ){
+ if( scene.fog ) delete scene.fog
+ scene.fog = null;
+ }else scene.fog = new THREE.Fog( scene.background, v.x, v.y );
+})
+
+xrf.addEventListener('bg', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ console.log("└ bg "+v.x+","+v.y+","+v.z);
+ if( scene.background ) delete scene.background
+ scene.background = new THREE.Color( v.x, v.y, v.z )
+})
diff --git a/src/3rd/js/plugin/vendors/three/refactor/env.js b/src/3rd/js/plugin/vendors/three/refactor/env.js
new file mode 100644
index 0000000..04885f1
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/refactor/env.js
@@ -0,0 +1,15 @@
+xrf.addEventListener('env', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ if( frag.env && !scene.environment ){
+ //let env = scene.getObjectByName(frag.env.string)
+ //if( !env ) env = xrf.scene.getObjectByName(frag.env.string) // repurpose from parent scene
+ //if( !env ) return console.warn("xrf.env "+frag.env.string+" not found")
+ //env.material.map.mapping = THREE.EquirectangularReflectionMapping;
+ //scene.environment = env.material.map
+ //scene.texture = env.material.map
+ // renderer.toneMapping = THREE.ACESFilmicToneMapping;
+ // renderer.toneMappingExposure = 2;
+ // console.log(` └ applied image '${frag.env.string}' as environment map`)
+ }
+
+})
diff --git a/src/3rd/js/plugin/vendors/three/refactor/fov.js b/src/3rd/js/plugin/vendors/three/refactor/fov.js
new file mode 100644
index 0000000..047cd7f
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/refactor/fov.js
@@ -0,0 +1,7 @@
+xrf.frag.fov = function(v, opts){
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+
+ console.log("└ fov "+v.int);
+ camera.fov = v.int;
+ camera.updateProjectionMatrix();
+}
diff --git a/src/3rd/js/plugin/vendors/three/refactor/macro.js b/src/3rd/js/plugin/vendors/three/refactor/macro.js
new file mode 100644
index 0000000..1cfb366
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/refactor/macro.js
@@ -0,0 +1,71 @@
+xrf.macros = {}
+
+xrf.addEventListener('frag2mesh', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE, hashbus} = opts
+
+ for( let k in frag ){
+ let id = mesh.name+"_"+k
+ let fragment = frag[k]
+
+ if( k.match(/^!/) ){
+ if( mesh.material) mesh.material = mesh.material.clone()
+ if( mesh.isSRC || scene.isSRC ) return; // dont allow recursion for now
+
+ if( xrf.macros[k] ) return // already initialized
+
+ console.log("└ initing xrmacro: "+k)
+ xrf.macros[k] = fragment
+ fragment.args = fragment.string.split("|")
+
+ fragment.trigger = (e) => {
+ xrf
+ .emit('macro',{click:true,mesh,xrf:frag}) // let all listeners agree
+ .then( () => {
+ rrFrag = fragment.args[ xrf.roundrobin( fragment,model) ]
+ console.log("└ xrmacro: "+rrFrag)
+ if( xrf.macros[ rrFrag ] ){
+ xrf.macros[ rrFrag ].trigger()
+ } else {
+ xrf.navigator.to( rrFrag,null,0)
+ }
+ })
+ }
+
+ let selected = (state) => () => {
+ if( mesh.selected == state ) return // nothing changed
+ if( mesh.material ){
+ if( mesh.material.uniforms ) mesh.material.uniforms.selected.value = state
+ else mesh.material.color.r = mesh.material.color.g = mesh.material.color.b = state ? 2.0 : 1.0
+ }
+ // update mouse cursor
+ if( !renderer.domElement.lastCursor )
+ renderer.domElement.lastCursor = renderer.domElement.style.cursor
+ renderer.domElement.style.cursor = state ? 'pointer' : renderer.domElement.lastCursor
+ xrf
+ .emit('macro',{selected:state,mesh,xrf:frag}) // let all listeners agree
+ .then( () => mesh.selected = state )
+ }
+
+ mesh.addEventListener('click', fragment.trigger )
+ mesh.addEventListener('mousemove', selected(true) )
+ mesh.addEventListener('nocollide', selected(false) )
+
+ // lazy add mesh to interactive group (because we're inside a recursive traverse)
+ setTimeout( (mesh) => {
+ const world = {
+ pos: new THREE.Vector3(),
+ scale: new THREE.Vector3(),
+ quat: new THREE.Quaternion()
+ }
+ mesh.getWorldPosition(world.pos)
+ mesh.getWorldScale(world.scale)
+ mesh.getWorldQuaternion(world.quat);
+ mesh.position.copy(world.pos)
+ mesh.scale.copy(world.scale)
+ mesh.setRotationFromQuaternion(world.quat);
+ xrf.interactive.add(mesh)
+ xrf.emit('interactionReady', {mesh,xrf:fragment, clickHandler: fragment.trigger})
+ }, 10, mesh )
+ }
+ }
+})
diff --git a/src/3rd/js/plugin/vendors/three/refactor/mov.js b/src/3rd/js/plugin/vendors/three/refactor/mov.js
new file mode 100644
index 0000000..90431e4
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/refactor/mov.js
@@ -0,0 +1,12 @@
+xrf.addEventListener('mov', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ if( frag.mov && frag.q ){
+
+ // let wait for the queried objects (as we're inside promise which traverses the graph)
+ setTimeout( (v) => {
+ frag.q.getObjects().map( (o) => {
+ o.position.add( new THREE.Vector3( v.x, v.y, v.z ) )
+ })
+ },10, frag.mov )
+ }
+})
diff --git a/src/3rd/js/plugin/vendors/three/refactor/pos.js b/src/3rd/js/plugin/vendors/three/refactor/pos.js
new file mode 100644
index 0000000..493643a
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/refactor/pos.js
@@ -0,0 +1,14 @@
+xrf.addEventListener('pos', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ //if( frag.pos && frag.q ){
+ // // apply roundrobin (if any)
+ // if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
+
+ // frag.q.getObjects().map( (o) => {
+ // // if object has no parent (name == 'Scene') use absolute positioning, otherwise relative to parent
+ // o.position.x = o.parent.name == 'Scene' ? v.x : o.positionOriginal.x + v.x
+ // o.position.y = o.parent.name == 'Scene' ? v.z : o.positionOriginal.y + v.z
+ // o.position.z = o.parent.name == 'Scene' ? v.y : o.positionOriginal.z + v.y
+ // })
+ //}
+})
diff --git a/src/3rd/js/plugin/vendors/three/refactor/rot.js b/src/3rd/js/plugin/vendors/three/refactor/rot.js
new file mode 100644
index 0000000..efb3e96
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/refactor/rot.js
@@ -0,0 +1,17 @@
+xrf.addEventListener('rot', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ if( frag.rot && frag.q ){
+ // apply roundrobin (if any)
+ if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
+
+ if( frag.q ){ // only operate on queried object(s)
+ frag.q.getObjects().map( (o) => {
+ o.rotation.set(
+ v.x * Math.PI / 180,
+ v.y * Math.PI / 180,
+ v.z * Math.PI / 180
+ )
+ })
+ }
+ }
+})
diff --git a/src/3rd/js/plugin/vendors/three/refactor/scale.js b/src/3rd/js/plugin/vendors/three/refactor/scale.js
new file mode 100644
index 0000000..35fb241
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/refactor/scale.js
@@ -0,0 +1,13 @@
+xrf.addEventListener('scale', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ if( frag.scale && frag.q ){
+ // apply roundrobin (if any)
+ if( v.args ) v = v.args[ xrf.roundrobin(v,model) ]
+
+ frag.q.getObjects().map( (o) => {
+ o.scale.x = v.x
+ o.scale.y = v.y
+ o.scale.z = v.z
+ })
+ }
+})
diff --git a/src/3rd/js/plugin/vendors/three/refactor/show.js b/src/3rd/js/plugin/vendors/three/refactor/show.js
new file mode 100644
index 0000000..8aa23f3
--- /dev/null
+++ b/src/3rd/js/plugin/vendors/three/refactor/show.js
@@ -0,0 +1,17 @@
+xrf.addEventListener('show', (opts) => {
+ let { frag, mesh, model, camera, scene, renderer, THREE} = opts
+ if( frag.show && frag.q ){
+ let show = frag.show
+
+ // apply roundrobin (if any)
+ if( show.args ) v = show.args[ xrf.roundrobin(show,model) ]
+ else v = show.int
+
+ // let wait for the queried objects (as we're inside promise which traverses the graph)
+ setTimeout( (v) => {
+ frag.q.getObjects().map( (o) => {
+ o.visible = v.int == 1;
+ })
+ }, 20, v)
+ }
+})