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

140 lines
4.5 KiB
JavaScript

xrf.frag.t = function(v, opts){
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
if( !model.mixer ) return
if( !model.animations || model.animations[0] == undefined ) return console.warn('no animation in scene')
model.mixer.t = v
let mixer = model.mixer
xrf.frag.t.calculateLoop( v, mixer.loop, mixer.loop.fps )
// update speed
mixer.timeScale = mixer.loop.speed
if( v.y != undefined || v.z != undefined ) xrf.mixers[0].updateLoop( mixer.loop.timeStart )
if( v.y > 0 && v.z > 0 ){
xrf.model.animations.map( (anim) => {
if( !anim.action ) return
anim.action.setLoop( v.z == 0 ? THREE.LoopOnce : THREE.LoopRepeat, v.z == 0 ? 0 : 99999999)
})
}
// play animations
mixer.play( v.x != 0 )
}
xrf.frag.t.default = {x:1, y:0, z:0}
xrf.frag.t.calculateLoop = (t,obj,fps) => {
obj.speed = t ? t.x : 0
obj.speedAbs = Math.abs(obj.speed)
obj.frameStart = t ? t.y || obj.frameStart : 1
obj.frameStop = t ? t.z || obj.frameStop : 0
// always recalculate time using frameStart/Stop
obj.timeStart = obj.frameStart == 0 ? obj.mixerStart || 0 : obj.frameStart-1 / (fps * obj.speedAbs)
obj.timeStop = obj.frameStop / (fps * obj.speedAbs)
}
xrf.frag.t.setupMixer = function(opts){
let {model,file,url} = opts
let mixer = model.mixer = new xrf.THREE.AnimationMixer(model.scene)
mixer.play = (play) => {
mixer.isPlaying = play
model.animations.map( (anim) => {
if( !anim.action ){
anim.action = mixer.clipAction( anim )
anim.action.enabled = true
anim.action.setLoop(THREE.LoopOnce)
}
if( mixer.isPlaying === false) anim.action.stop()
else{
anim.action.play()
}
mixer.update(0)
})
xrf.emit( play === false ? 'stop' : 'play',{play})
}
mixer.stop = () => {
mixer.play(false)
}
mixer.updateLoop = (time) => {
console.log("updateLoop "+time)
mixer.setTime(time)
mixer.time = Math.abs(time)
mixer.update(0) // (forgetting) this little buddy costed me lots of time :]
}
// 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.speed > 0.0 && mixer.time > mixer.loop.timeStop * mixer.loop.speedAbs ){
// setTimeout( (time) => mixer.updateLoop(time),0,mixer.loop.timeStart) // prevent recursion
//}
return update.call( this, time )
}
mixer.update.patched = true
}
// calculate total duration/frame based on longest animation
mixer.duration = 0
mixer.frames = 0
if( model.animations.length ){
model.animations.map( (a) => {
mixer.duration = a.duration > mixer.duration ? a.duration : mixer.duration
mixer.frames = a.tracks[0].times.length > mixer.frames ? a.tracks[0].times.length : mixer.frames
})
}
mixer.loop = {fps: mixer.frames / mixer.duration}
xrf.frag.t.calculateLoop( null, mixer.loop, mixer.loop.fps ) // gltf uses looppoints in seconds (not frames)
xrf.mixers.push(mixer)
console.log("mixer fps="+mixer.loop.fps+" frames:"+mixer.frames+" duration:"+mixer.duration)
}
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('parseModel', (opts) => {
let {model,file,url} = opts
// add animations
xrf.frag.t.setupMixer(opts)
})
xrf.addEventListener('render', (opts) => {
let model = xrf.model
let {time} = opts
if( !model ) return
if( xrf.mixers.length ) xrf.mixers.map( (m) => m.update( time ) )
// update camera if possible
if( model.cameras && model.cameras.length && xrf.mixers.length ){
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()
}
})