xrfragment/src/3rd/js/three/xrf/t.js

151 lines
4.4 KiB
JavaScript

// this ns the global #t mediafragment handler (which affects the 3D animation)
xrf.frag.t = function(v, opts){
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
// handle object media players
if( mesh && mesh.media ){
for( let i in mesh.media ) mesh.media[i].set("t",v)
return
}
// otherwise handle global 3D animations
if( !model.mixer ) return
if( !model.animations || model.animations[0] == undefined ){
console.warn('no animations found in model')
return xrf.emit( v.x == 0 ? 'stop' : 'play',{isPlaying: v.x != 0 })
}
xrf.mixers.map ( (mixer) => {
mixer.t = v
// update speed
mixer.timeScale = mixer.loop.speed
mixer.loop.speedAbs = Math.abs( mixer.timeScale )
mixer.updateLoop( v )
// play animations
mixer.play( v )
})
}
xrf.frag.t.default = {
x:0, // (play from) offset (in seconds)
y:0 // optional: (stop at) offset (in seconds)
}
// setup animation mixer for global scene & src scenes
xrf.addEventListener('parseModel', (opts) => {
let {model} = opts
let mixer = model.mixer = new xrf.THREE.AnimationMixer(model.scene)
mixer.model = model
mixer.loop = {timeStart:0,timeStop:0,speed:1.0}
mixer.i = xrf.mixers.length
mixer.actions = []
model.animations.map( (anim) => {
anim.optimize()
if( xrf.debug ) console.log("action: "+anim.name)
mixer.actions.push( mixer.clipAction( anim, model.scene ) )
})
mixer.play = (t) => {
mixer.isPlaying = t.x !== undefined && t.x != t.y
mixer.updateLoop(t)
xrf.emit( mixer.isPlaying === false ? 'stop' : 'play',{isPlaying: mixer.isPlaying})
}
mixer.stop = () => {
mixer.play(false)
}
mixer.updateLoop = (t) => {
if( t ){
mixer.loop.timeStart = t.x != undefined ? t.x : mixer.loop.timeStart
mixer.loop.timeStop = t.y != undefined ? t.y : mixer.duration
}
mixer.actions.map( (action) => {
if( mixer.loop.timeStart != undefined ){
action.time = mixer.loop.timeStart
action.setLoop( xrf.THREE.LoopOnce, )
action.timeScale = mixer.timeScale
action.enabled = true
if( t && t.x != undefined ) action.play()
}
})
mixer.setTime(mixer.loop.timeStart)
mixer.time = Math.abs( mixer.loop.timeStart )
mixer.update(0)
}
// monkeypatch: update loop when needed
if( !mixer.update.patched ){
let update = mixer.update
mixer.update = function(time){
mixer.time = Math.abs(mixer.time)
if( time == 0 ) return update.call(this,time)
// loop jump
if( mixer.loop.timeStop > 0 && mixer.time > mixer.loop.timeStop ){
if( mixer.loop.enabled ){
setTimeout( () => mixer.updateLoop(), 0 ) // prevent recursion
}else mixer.stop()
}
return update.call( this, time )
}
mixer.update.patched = true
}
// calculate total duration/frame based on longest animation
mixer.duration = 0
if( model.animations.length ){
model.animations.map( (a) => mixer.duration = ( a.duration > mixer.duration ) ? a.duration : mixer.duration )
}
xrf.mixers.push(mixer)
})
if( document.location.hash.match(/t=/) ){
let url = document.location.href
let playAfterUserGesture = () => {
xrf.hashbus.pub(url) // re-post t fragment on the hashbus again
window.removeEventListener('click',playAfterUserGesture)
window.removeEventListener('touchstart',playAfterUserGesture)
}
window.addEventListener('click', playAfterUserGesture )
window.addEventListener('touchstart', playAfterUserGesture )
}
xrf.addEventListener('render', (opts) => {
let model = xrf.model
let {time} = opts
if( !model ) return
if( xrf.mixers.length ){
xrf.mixers.map( (m) => m.isPlaying && (m.update( time )) )
// update active camera in case selected by dynamicKey in URI
if( xrf.model.camera && xrf.model.camera.length && model.mixer.isPlaying ){
let cam = xrf.camera.getCam()
// cam.fov = model.cameras[0].fov (why is blender not exporting radians?)
cam.far = model.cameras[0].far
cam.near = model.cameras[0].near
let rig = xrf.camera
rig.position.copy( model.cameras[0].position )
rig.position.y -= rig.offsetY // VR/AR compensate camera rig
//rig.rotation.copy( model.cameras[0].rotation )
rig.updateProjectionMatrix()
}
}
})
// remove mixers and stop mixers when loading another scene
xrf.addEventListener('reset', (opts) => {
xrf.mixers.map( (m) => m.stop())
xrf.mixers = []
})