diff --git a/example/assets/index.glb b/example/assets/index.glb index a1b58b6..eab4a05 100644 Binary files a/example/assets/index.glb and b/example/assets/index.glb differ diff --git a/src/3rd/js/three/util/optimize.js b/src/3rd/js/three/util/optimize.js new file mode 100644 index 0000000..18a4764 --- /dev/null +++ b/src/3rd/js/three/util/optimize.js @@ -0,0 +1,85 @@ +xrf.optimize = (opts) => { + opts.animatedObjects = [] + + xrf.optimize + .checkAnimations(opts) + .freezeUnAnimatedObjects(opts) + .disableShadows(opts) + .disableEmbeddedLights(opts) + .removeDuplicateLights() +} + + // check unused animations +xrf.optimize.checkAnimations = (opts) => { + console.log("TODO: fix freezeUnAnimatedObjects for SRC's") + return xrf.optimize + + let {model} = opts + model.animations.map( (anim) => { + // collect zombie animations and warn user + let zombies = anim.tracks.map( (t) => { + let name = t.name.replace(/\..*/,'') + let obj = model.scene.getObjectByName(name) + if( !model.scene.getObjectByName(name) ) return {anim:anim.name,obj:name} + else opts.animatedObjects.push(name) + return undefined + }) + if( zombies.length > 0 ){ // only warn for zombies in main scene (because src-scenes might be filtered anyways) + zombies + .filter( (z) => z ) // filter out undefined + .map( (z) => console.warn(`gltf: object '${z.obj}' not found (anim: '${z.anim}'`) ) + console.warn(`TIP: remove dots in objectnames in blender (which adds dots when duplicating)`) + } + }) + return xrf.optimize +} + +xrf.optimize.freezeUnAnimatedObjects = (opts) => { + console.log("TODO: fix freezeUnAnimatedObjects for SRC's") + return xrf.optimize + + let {model} = opts + let scene = model.scene + // increase performance by freezing all objects + scene.traverse( (n) => n.matrixAutoUpdate = false ) + // except animated objects and children + scene.traverse( (n) => { + if( ~opts.animatedObjects.indexOf(n.name) ){ + n.matrixAutoUpdate = true + n.traverse( (m) => m.matrixAutoUpdate = true ) + } + }) + return xrf.optimize +} + +xrf.optimize.disableShadows = (opts) => { + opts.model.scene.traverse( (n) => { + if( n.castShadow !== undefined ) n.castShadow = false + }) + return xrf.optimize +} + +xrf.optimize.disableEmbeddedLights = (opts) => { + if( !opts.isSRC ) return xrf.optimize + // remove lights from SRC's + opts.model.scene.traverse( (n) => { + if( n.type.match(/light/i) ) n.remove() + }) + return xrf.optimize +} + +xrf.optimize.removeDuplicateLights = () => { + // local/extern src's can cause duplicate lights which tax performance + let lights = {} + xrf.scene.traverse( (n) => { + if( n.type.match(/light/i) ){ + if( !lights[n.name] ) lights[n.name] = true + else n.remove() + } + }) + return xrf.optimize +} + +xrf.addEventListener('parseModel', (opts) => { + xrf.optimize(opts) +}) diff --git a/src/3rd/js/three/xrf/src.js b/src/3rd/js/three/xrf/src.js index 8c5dbdf..87066d5 100644 --- a/src/3rd/js/three/xrf/src.js +++ b/src/3rd/js/three/xrf/src.js @@ -40,10 +40,11 @@ xrf.frag.src.addModel = (model,url,frag,opts) => { }else{ xrf.frag.src.scale( scene, opts, url ) // scale scene mesh.add(scene) - xrf.emit('parseModel', {...opts, isSRC:true, scene, model}) } // flag everything isSRC & isXRF mesh.traverse( (n) => { n.isSRC = n.isXRF = n[ opts.isLocal ? 'isSRCLocal' : 'isSRCExternal' ] = true }) + + xrf.emit('parseModel', {...opts, isSRC:true, scene, model}) } xrf.frag.src.renderAsPortal = (mesh) => { diff --git a/src/3rd/js/three/xrf/t.js b/src/3rd/js/three/xrf/t.js index d1b0e9d..792cc4e 100644 --- a/src/3rd/js/three/xrf/t.js +++ b/src/3rd/js/three/xrf/t.js @@ -51,25 +51,6 @@ xrf.addEventListener('parseModel', (opts) => { mixer.actions.push( mixer.clipAction( anim, model.scene ) ) }) - mixer.checkZombies = (animations) => { - if( mixer.zombieCheck ) return // fire only once - animations.map( (anim) => { - // collect zombie animations and warn user - let zombies = anim.tracks.map( (t) => { - let name = t.name.replace(/\..*/,'') - let obj = model.scene.getObjectByName(name) - return !model.scene.getObjectByName(name) ? {anim:anim.name,obj:name} : undefined - }) - if( zombies.length > 0 && mixer.i == 0 ){ // only warn for zombies in main scene (because src-scenes might be filtered anyways) - zombies - .filter( (z) => z ) // filter out undefined - .map( (z) => console.warn(`gltf: object '${z.obj}' not found (anim: '${z.anim}'`) ) - console.warn(`TIP: remove dots in objectnames in blender (which adds dots when duplicating)`) - } - }) - mixer.zombieCheck = true - } - mixer.play = (t) => { mixer.isPlaying = t.x !== undefined && t.x != t.y mixer.updateLoop(t) @@ -118,8 +99,6 @@ xrf.addEventListener('parseModel', (opts) => { mixer.update.patched = true } - mixer.checkZombies( model.animations) - // calculate total duration/frame based on longest animation mixer.duration = 0 if( model.animations.length ){ diff --git a/src/3rd/js/three/xrf/uv.js b/src/3rd/js/three/xrf/uv.js index 25eab18..d4a55c1 100644 --- a/src/3rd/js/three/xrf/uv.js +++ b/src/3rd/js/three/xrf/uv.js @@ -2,24 +2,23 @@ xrf.frag.uv = function(v, opts){ let { frag, mesh, model, camera, scene, renderer, THREE} = opts if( !mesh.geometry ) return // nothing to do here - if( v.floats.length < 2 ) return console.warn('xrfragment.js: got less than 4 uv values ') + if( v.floats.length != 4 ) return console.warn('xrfragment.js: got less than 4 uv values ') xrf.frag.uv.init(mesh) mesh.uv.u = v.floats[0] mesh.uv.v = v.floats[1] - mesh.uv.uspeed = v.floats[2] || 1.0 + mesh.uv.uspeed = v.floats[2] || 1.0 mesh.uv.vspeed = v.floats[3] || 1.0 - mesh.uv.ushift = v.shift[0] - mesh.uv.vshift = v.shift[1] - mesh.uv.uloop = v.shift[2] || false - mesh.uv.vloop = v.shift[3] || false - debugger + mesh.uv.ushift = v.shift[0] || v.floats[0] < 0 // negative u is always relative + mesh.uv.vshift = v.shift[1] || v.floats[1] < 0 // negative v is always relative + mesh.uv.uloop = v.shift[2] || false + mesh.uv.vloop = v.shift[3] || false mesh.onBeforeRender = xrf.frag.uv.scroll } xrf.frag.uv.init = function(mesh){ - if( !mesh.uv ) mesh.uv = {u:0, v:0, uspeed:1, vspeed:1, uloop:false, vloop:false, uv:false, ushift:false,vshift:false} + if( !mesh.uv ) mesh.uv = {u:0, v:0, uspeed:1, vspeed:1, uloop:false, vloop:false, uv:false} let uv = mesh.geometry.getAttribute("uv") if( !uv.old ) uv.old = mesh.geometry.getAttribute("uv").clone() @@ -33,34 +32,43 @@ xrf.frag.uv.scroll = function(){ // translate! for( let i = 0; i < uv.count; i++ ){ - let u = uv.getX(i) - let v = uv.getY(i) - let uTarget = (this.uv.ushift ? u : uv.old.getX(i) ) + this.uv.u - let vTarget = (this.uv.vshift ? v : uv.old.getY(i) ) + this.uv.v - // scroll U - if( this.uv.uloop ){ - u += this.uv.uspeed * xrf.clock.delta - }else{ - // recover from super-high uv-values due to looped scrolling - if( Math.abs(u-uTarget) > 1.0 ) u = uv.old.getX(i) - u = u > uTarget ? u + (this.uv.uspeed * -uTarget ) // -xrf.clock.delta) - : u + (this.uv.uspeed * uTarget ) // xrf.clock.delta) - diffU += Math.abs( u - uTarget ) // are we done yet? (non-looping mode) - } + if( this.uv.uspeed == 1.0 ) uv.setX(i, this.uv.ushift ? uv.getX(i) + this.uv.u : uv.old.getX(i) + this.uv.u ) + if( this.uv.vspeed == 1.0 ) uv.setY(i, this.uv.vshift ? uv.getY(i) + this.uv.v : uv.old.getY(i) + this.uv.v ) - // scroll V - if( this.uv.vloop ){ - v += this.uv.vspeed * xrf.clock.delta - }else{ - // recover from super-high uv-values due to looped scrolling - if( Math.abs(v-vTarget) > 1.0 ) v = uv.old.getY(i) - v = v > vTarget ? v + (this.uv.vspeed * -vTarget ) // -xrf.clock.delta) - : v + (this.uv.vspeed * vTarget ) // xrf.clock.delta) - diffV += Math.abs( v - vTarget ) + if( this.uv.uloop || this.uv.vloop ){ + let u = uv.getX(i) + let v = uv.getY(i) + let uTarget = this.uv.ushift ? uv.getX(i) + this.uv.u : uv.old.getX(i) + this.uv.u + let vTarget = this.uv.vshift ? uv.getY(i) + this.uv.v : uv.old.getY(i) + this.uv.v + // scroll U + if( this.uv.uloop ){ + u += this.uv.uspeed * xrf.clock.delta + }else{ + // *TODO* tween to offset + //// recover from super-high uv-values due to looped scrolling + //if( Math.abs(u-uTarget) > 10.0 ) u = uv.old.getX(i) + //u = u > uTarget ? u + (this.uv.uspeed * -xrf.clock.delta) + // : u + (this.uv.uspeed * xrf.clock.delta) + //diffU += Math.abs( u - uTarget ) // are we done yet? (non-looping mode) + } + + // scroll V + if( this.uv.vloop ){ + v += this.uv.vspeed * xrf.clock.delta + }else{ + // *TODO* tween to offset + //// recover from super-high uv-values due to looped scrolling + //// recover from super-high uv-values due to looped scrolling + //if( Math.abs(v-vTarget) > 10.0 ) v = uv.old.getY(i) + //v = v > vTarget ? v + (this.uv.vspeed * -xrf.clock.delta) + // : v + (this.uv.vspeed * xrf.clock.delta) + //diffV += Math.abs( v - vTarget ) + } + uv.setXY(i,u,v) } - uv.setXY(i,u,v) + } uv.needsUpdate = true