2023-10-11 13:46:38 +02:00
|
|
|
xrf.frag.t = function(v, opts){
|
|
|
|
|
let { frag, mesh, model, camera, scene, renderer, THREE} = opts
|
2023-10-30 16:15:08 +01:00
|
|
|
if( !model.mixer ) return
|
2023-12-08 13:35:19 +01:00
|
|
|
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 })
|
|
|
|
|
}
|
2023-10-11 13:46:38 +02:00
|
|
|
|
2023-11-02 16:49:18 +01:00
|
|
|
xrf.mixers.map ( (mixer) => {
|
2023-11-17 16:34:11 +01:00
|
|
|
|
2023-11-02 16:49:18 +01:00
|
|
|
mixer.t = v
|
|
|
|
|
|
|
|
|
|
// update speed
|
|
|
|
|
mixer.timeScale = mixer.loop.speed = v.x
|
|
|
|
|
mixer.loop.speedAbs = Math.abs(v.x)
|
|
|
|
|
|
|
|
|
|
if( v.y != undefined || v.z != undefined ) mixer.updateLoop( v )
|
|
|
|
|
|
|
|
|
|
// play animations
|
2023-11-02 18:15:05 +01:00
|
|
|
mixer.play( v )
|
2023-11-02 16:49:18 +01:00
|
|
|
})
|
2023-10-30 16:15:08 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-02 16:49:18 +01:00
|
|
|
xrf.frag.t.default = {
|
|
|
|
|
x:0, // (play from) offset (in seconds)
|
|
|
|
|
y:0 // optional: (stop at) offset (in seconds)
|
2023-10-30 16:15:08 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-02 16:49:18 +01:00
|
|
|
// 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
|
2023-11-17 16:34:11 +01:00
|
|
|
mixer.loop = {timeStart:0,timeStop:0}
|
2023-11-02 16:49:18 +01:00
|
|
|
mixer.i = xrf.mixers.length
|
2023-11-17 16:34:11 +01:00
|
|
|
mixer.actions = []
|
2023-11-02 16:49:18 +01:00
|
|
|
|
|
|
|
|
model.animations.map( (anim) => {
|
2023-11-17 16:34:11 +01:00
|
|
|
anim.optimize()
|
2024-01-30 09:58:00 +00:00
|
|
|
if( xrf.debug ) console.log("action: "+anim.name)
|
2023-11-17 16:34:11 +01:00
|
|
|
mixer.actions.push( mixer.clipAction( anim, model.scene ) )
|
2023-11-02 16:49:18 +01:00
|
|
|
})
|
|
|
|
|
|
2023-11-17 16:34:11 +01:00
|
|
|
|
2023-11-02 16:49:18 +01:00
|
|
|
mixer.checkZombies = (animations) => {
|
|
|
|
|
if( mixer.zombieCheck ) return // fire only once
|
|
|
|
|
animations.map( (anim) => {
|
|
|
|
|
// collect zombie animations and warn user
|
2023-11-01 19:15:12 +01:00
|
|
|
let zombies = anim.tracks.map( (t) => {
|
|
|
|
|
let name = t.name.replace(/\..*/,'')
|
2023-11-17 16:34:11 +01:00
|
|
|
let obj = model.scene.getObjectByName(name)
|
|
|
|
|
return !model.scene.getObjectByName(name) ? {anim:anim.name,obj:name} : undefined
|
2023-11-01 19:15:12 +01:00
|
|
|
})
|
2023-11-17 16:34:11 +01:00
|
|
|
if( zombies.length > 0 && mixer.i == 0 ){ // only warn for zombies in main scene (because src-scenes might be filtered anyways)
|
2023-11-01 19:15:12 +01:00
|
|
|
zombies
|
|
|
|
|
.filter( (z) => z ) // filter out undefined
|
|
|
|
|
.map( (z) => console.warn(`gltf: object '${z.obj}' not found (anim: '${z.anim}'`) )
|
2023-10-31 13:00:29 +01:00
|
|
|
console.warn(`TIP: remove dots in objectnames in blender (which adds dots when duplicating)`)
|
|
|
|
|
}
|
|
|
|
|
})
|
2023-11-02 16:49:18 +01:00
|
|
|
mixer.zombieCheck = true
|
2023-10-31 13:00:29 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-02 16:49:18 +01:00
|
|
|
mixer.play = (t) => {
|
|
|
|
|
mixer.isPlaying = t.x != 0
|
|
|
|
|
mixer.updateLoop(t)
|
|
|
|
|
xrf.emit( mixer.isPlaying === false ? 'stop' : 'play',{isPlaying: mixer.isPlaying})
|
2023-10-30 16:15:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mixer.stop = () => {
|
|
|
|
|
mixer.play(false)
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-02 16:49:18 +01:00
|
|
|
mixer.updateLoop = (t) => {
|
|
|
|
|
mixer.loop.timeStart = t.y != undefined ? t.y : mixer.loop.timeStart
|
|
|
|
|
mixer.loop.timeStop = t.z != undefined ? t.z : mixer.loop.timeStop
|
2023-11-17 16:34:11 +01:00
|
|
|
mixer.actions.map( (action) => {
|
2023-11-02 16:49:18 +01:00
|
|
|
if( mixer.loop.timeStart != undefined ){
|
2023-11-17 16:34:11 +01:00
|
|
|
action.time = mixer.loop.timeStart
|
2023-12-06 15:20:58 +01:00
|
|
|
action.setLoop( xrf.THREE.LoopOnce, )
|
2023-11-17 16:34:11 +01:00
|
|
|
action.timeScale = mixer.timeScale
|
|
|
|
|
action.enabled = true
|
|
|
|
|
if( t.x != 0 ){
|
|
|
|
|
action.play()
|
|
|
|
|
}
|
2023-11-02 16:49:18 +01:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
mixer.setTime(mixer.loop.timeStart)
|
|
|
|
|
mixer.time = Math.abs( mixer.loop.timeStart )
|
|
|
|
|
mixer.update(0)
|
|
|
|
|
mixer.checkZombies( model.animations)
|
2023-10-26 18:45:13 +02:00
|
|
|
}
|
2023-10-11 13:46:38 +02:00
|
|
|
|
2023-10-11 20:12:48 +02:00
|
|
|
// update loop when needed
|
2023-10-11 13:46:38 +02:00
|
|
|
if( !mixer.update.patched ){
|
|
|
|
|
let update = mixer.update
|
|
|
|
|
mixer.update = function(time){
|
|
|
|
|
mixer.time = Math.abs(mixer.time)
|
2023-10-26 18:45:13 +02:00
|
|
|
if( time == 0 ) return update.call(this,time)
|
2023-10-11 13:46:38 +02:00
|
|
|
|
2023-10-30 16:15:08 +01:00
|
|
|
// loop jump
|
2023-11-17 16:34:11 +01:00
|
|
|
if( mixer.loop.speed > 0.0 && (mixer.loop.timeStop > 0 && mixer.time > mixer.loop.timeStop) ){
|
2023-11-02 16:49:18 +01:00
|
|
|
setTimeout( (time,anims) => mixer.updateLoop(time), 0, mixer.loop.timeStart ) // prevent recursion
|
|
|
|
|
}
|
2023-10-30 16:15:08 +01:00
|
|
|
return update.call( this, time )
|
2023-10-11 13:46:38 +02:00
|
|
|
}
|
|
|
|
|
mixer.update.patched = true
|
|
|
|
|
}
|
2023-10-18 22:07:20 +02:00
|
|
|
|
2023-10-30 16:15:08 +01:00
|
|
|
// calculate total duration/frame based on longest animation
|
|
|
|
|
mixer.duration = 0
|
|
|
|
|
if( model.animations.length ){
|
2023-11-02 16:49:18 +01:00
|
|
|
model.animations.map( (a) => mixer.duration = ( a.duration > mixer.duration ) ? a.duration : mixer.duration )
|
2023-10-30 16:15:08 +01:00
|
|
|
}
|
2023-10-18 22:07:20 +02:00
|
|
|
|
2023-10-30 16:15:08 +01:00
|
|
|
xrf.mixers.push(mixer)
|
2023-11-02 16:49:18 +01:00
|
|
|
})
|
2023-10-24 18:18:29 +02:00
|
|
|
|
|
|
|
|
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
|
2023-10-30 16:15:08 +01:00
|
|
|
if( !model ) return
|
2023-11-02 18:15:05 +01:00
|
|
|
if( xrf.mixers.length ){
|
2023-11-09 18:20:47 +01:00
|
|
|
xrf.mixers.map( (m) => m.isPlaying && (m.update( time )) )
|
2023-10-24 18:18:29 +02:00
|
|
|
|
2023-11-02 18:15:05 +01:00
|
|
|
// update active camera in case selected by dynamicKey in URI
|
|
|
|
|
if( xrf.model.camera && model.mixer.isPlaying ){
|
2023-10-24 18:18:29 +02:00
|
|
|
|
2023-11-02 18:15:05 +01:00
|
|
|
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
|
2023-10-24 18:18:29 +02:00
|
|
|
|
2023-11-02 18:15:05 +01:00
|
|
|
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 )
|
2023-10-24 18:18:29 +02:00
|
|
|
|
2023-11-02 18:15:05 +01:00
|
|
|
rig.updateProjectionMatrix()
|
|
|
|
|
}
|
2023-10-24 18:18:29 +02:00
|
|
|
}
|
|
|
|
|
})
|
2023-11-02 18:15:05 +01:00
|
|
|
|
|
|
|
|
xrf.addEventListener('dynamicKey', (opts) => {
|
|
|
|
|
// select active camera if any
|
|
|
|
|
let {id,match,v} = opts
|
|
|
|
|
match.map( (w) => {
|
|
|
|
|
w.nodes.map( (node) => {
|
|
|
|
|
if( node.isCamera ){
|
2024-02-01 09:04:01 +00:00
|
|
|
console.log("switching camera to cam: "+node.name)
|
2023-11-02 18:15:05 +01:00
|
|
|
xrf.model.camera = node
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
2024-02-01 09:04:01 +00:00
|
|
|
|
|
|
|
|
// remove mixers and stop mixers when loading another scene
|
|
|
|
|
xrf.addEventListener('reset', (opts) => {
|
|
|
|
|
xrf.mixers.map( (m) => m.stop())
|
|
|
|
|
xrf.mixers = []
|
|
|
|
|
})
|