diff --git a/metaverse_modules/spawner/index.js b/metaverse_modules/spawner/index.js index 9fa3111819..85483ab2b2 100644 --- a/metaverse_modules/spawner/index.js +++ b/metaverse_modules/spawner/index.js @@ -29,7 +29,7 @@ export default e => { live = false; }); - const mobData = await mobManager.loadData(appUrls); + const mobData = await mobManager.loadData(app, appUrls); if (!live) return; const procGenInstance = procGenManager.getInstance(seed, range); @@ -59,5 +59,7 @@ export default e => { } }); + app.getPhysicsObjects = () => mobManager.getPhysicsObjects(); + return app; }; \ No newline at end of file diff --git a/metaversefile-api.js b/metaversefile-api.js index af4074be76..3f7268f7da 100644 --- a/metaversefile-api.js +++ b/metaversefile-api.js @@ -1038,7 +1038,7 @@ export default () => { } // mob - for (const mob of mobManager.mobs) { + for (const mob of mobManager.getMobs()) { const mobPhysicsObjects = mob.getPhysicsObjects(); for (const mobPhysicsObject of mobPhysicsObjects) { if (mobPhysicsObject.physicsId === physicsId) { @@ -1074,7 +1074,7 @@ export default () => { } // mob - for (const mob of mobManager.mobs) { + for (const mob of mobManager.getMobs()) { const mobPhysicsObjects = mob.getPhysicsObjects(); for (const mobPhysicsObject of mobPhysicsObjects) { if (mobPhysicsObject.physicsId === physicsId) { @@ -1113,7 +1113,7 @@ export default () => { } // mob - for (const mob of mobManager.mobs) { + for (const mob of mobManager.getMobs()) { const mobPhysicsObjects = mob.getPhysicsObjects(); for (const mobPhysicsObject of mobPhysicsObjects) { if (mobPhysicsObject.physicsId === physicsId) { diff --git a/mob-manager.js b/mob-manager.js index ddc282c77c..6ad9737d02 100644 --- a/mob-manager.js +++ b/mob-manager.js @@ -41,6 +41,8 @@ const maxAnimationFrameLength = 512; let unifiedBoneTextureSize = 1024; +const physicsScene = physicsManager.getScene(); + // window.THREE = THREE; const _zeroY = v => { @@ -97,7 +99,7 @@ function makeCharacterController(app, { .applyQuaternion(localQuaternion) ); - const characterController = physicsManager.createCharacterController( + const characterController = physicsScene.createCharacterController( radius - contactOffset, innerHeight, contactOffset, @@ -108,18 +110,30 @@ function makeCharacterController(app, { } class Mob { - constructor(app = null, srcUrl = '') { + constructor(app = null, srcUrl = '', spec = {}) { this.app = app; + const { + position, + quaternion, + timeOffset, + updateMobGeometry + } = spec; + this.position = position; + this.quaternion = quaternion; + this.timeOffset = timeOffset; + this.updateMobGeometry = updateMobGeometry; this.subApp = null; - this.name = this.app.name + '@' + this.app.position.toArray().join(','); + this.name = this.app.name + '@' + this.position.toArray().join(','); this.updateFns = []; this.cleanupFns = []; if (srcUrl) { (async () => { - await this.loadApp(srcUrl); + const m = await metaversefile.import(srcUrl); + const mobJsonUrl = m.srcUrl; + await this.loadApp(mobJsonUrl); })(); } } @@ -156,11 +170,12 @@ class Mob { const modelPrerotation = new THREE.Quaternion().fromArray(modelQuaternion); const subApp = await metaversefile.createAppAsync({ - start_url: mobUrl, - position: this.app.position, - quaternion: this.app.quaternion, + start_url: '', + position: this.app.position.clone().add(this.position), + quaternion: this.app.quaternion.clone().multiply(this.quaternion), scale: this.app.scale, }); + subApp.visible = false; if (!live) return; const rng = this.#getRng(); @@ -171,13 +186,13 @@ class Mob { this.app.add(subApp); this.subApp = subApp; - this.app.position.set(0, 0, 0); - this.app.quaternion.identity(); - this.app.scale.set(1, 1, 1); - this.app.updateMatrixWorld(); + //this.app.position.set(0, 0, 0); + //this.app.quaternion.identity(); + //this.app.scale.set(1, 1, 1); + //this.app.updateMatrixWorld(); this.cleanupFns.push(() => { - this.app.clear(); + // this.app.clear(); }); }; _attachToApp(); @@ -200,7 +215,7 @@ class Mob { hitTracker.bind(subApp); subApp.dispatchEvent({type: 'hittrackeradded'}); const die = () => { - this.app.destroy(); + this.subApp.destroy(); _drop(); }; subApp.addEventListener('die', die, {once: true}); @@ -208,7 +223,7 @@ class Mob { _bindHitTracker(); const mesh = subApp; - const animations = subApp.glb.animations; + //const animations = subApp.glb.animations; let {idleAnimation = ['idle'], aggroDistance, walkSpeed = 1} = mobComponent; if (idleAnimation) { if (!Array.isArray(idleAnimation)) { @@ -248,7 +263,7 @@ class Mob { halfHeight, } = spec; _getPhysicsExtraPositionQuaternion(spec, localVector, localQuaternion, localVector2, localMatrix); - const physicsObject = physicsManager.addCapsuleGeometry(localVector, localQuaternion, radius, halfHeight); + const physicsObject = physicsScene.addCapsuleGeometry(localVector, localQuaternion, radius, halfHeight); physicsObject.spec = spec; return physicsObject; }); @@ -256,20 +271,20 @@ class Mob { subApp.getPhysicsObjects = () => physicsObjects; this.cleanupFns.push(() => { - physicsManager.destroyCharacterController(characterController); + physicsScene.destroyCharacterController(characterController); for (const extraPhysicsObject of extraPhysicsObjects) { - physicsManager.removeGeometry(extraPhysicsObject); + physicsScene.removeGeometry(extraPhysicsObject); } }); // rotation hacks - { + /* { mesh.position.y = 0; localEuler.setFromQuaternion(mesh.quaternion, 'YXZ'); localEuler.x = 0; localEuler.z = 0; mesh.quaternion.setFromEuler(localEuler); - } + } // initialize animation const idleAnimationClips = idleAnimation.map(name => animations.find(a => a.name === name)).filter(a => !!a); @@ -280,11 +295,12 @@ class Mob { idleAction.play(); } - this.updateFns.push((timestamp, timeDiff) => { - const deltaSeconds = timeDiff / 1000; - mixer.update(deltaSeconds); - }); - } + if (idleActions.length != 0) { + this.updateFns.push((timestamp, timeDiff) => { + mixer.setTime(timestamp / 1000 + this.timeOffset * idleActions[0].duration); + }); + } + } */ // set up frame loop let animation = null; @@ -295,12 +311,12 @@ class Mob { if (animation) { mesh.position.add(localVector.copy(animation.velocity).multiplyScalar(timeDiff/1000)); - animation.velocity.add(localVector.copy(physicsManager.getGravity()).multiplyScalar(timeDiff/1000)); + animation.velocity.add(localVector.copy(physicsScene.getGravity()).multiplyScalar(timeDiff/1000)); if (mesh.position.y < 0) { animation = null; } - physicsManager.setCharacterControllerPosition(characterController, mesh.position); + physicsScene.setCharacterControllerPosition(characterController, mesh.position); mesh.updateMatrixWorld(); @@ -311,7 +327,7 @@ class Mob { const meshPosition = localVector2; const meshQuaternion = localQuaternion; const meshScale = localVector3; - + const meshPositionY0 = localVector4.copy(meshPosition); const characterPositionY0 = localVector5.copy(localPlayer.position) .add(localVector6.set(0, localPlayer.avatar ? -localPlayer.avatar.height : 0, 0)); @@ -334,16 +350,16 @@ class Mob { const popExtraGeometry = (() => { for (const extraPhysicsObject of extraPhysicsObjects) { - physicsManager.disableActor(extraPhysicsObject); + physicsScene.disableActor(extraPhysicsObject); } return () => { for (const extraPhysicsObject of extraPhysicsObjects) { - physicsManager.enableActor(extraPhysicsObject); + physicsScene.enableActor(extraPhysicsObject); } }; })(); - const flags = physicsManager.moveCharacterController( + const flags = physicsScene.moveCharacterController( characterController, moveDelta, minDist, @@ -356,7 +372,7 @@ class Mob { let grounded = !!(flags & 0x1); if (!grounded) { velocity.add( - localVector.copy(physicsManager.getGravity()) + localVector.copy(physicsScene.getGravity()) .multiplyScalar(timeDiffS) ); } else { @@ -384,10 +400,12 @@ class Mob { mesh.matrixWorld.compose(meshPosition, meshQuaternion, meshScale); mesh.matrix.copy(mesh.matrixWorld); - if (app.parent) { - mesh.matrix.premultiply(localMatrix.copy(app.parent.matrixWorld).invert()); + if (this.app.parent) { + mesh.matrix.premultiply(localMatrix.copy(this.app.parent.matrixWorld).invert()); } mesh.matrix.decompose(mesh.position, mesh.quaternion, mesh.scale); + + this.updateMobGeometry(mesh.position, mesh.quaternion); } } }; @@ -413,7 +431,7 @@ class Mob { extraPhysicsObject.position.copy(localVector); extraPhysicsObject.quaternion.copy(localQuaternion); extraPhysicsObject.updateMatrixWorld(); - physicsManager.setTransform(extraPhysicsObject); + physicsScene.setTransform(extraPhysicsObject); } }; _updateExtraPhysics(); @@ -457,6 +475,7 @@ class Mob { class MobInstance { constructor() { + this.mob = null; } } @@ -473,7 +492,7 @@ class InstancedSkeleton extends THREE.Skeleton { unifiedBoneTextureSize = boneTexture.image.width; // console.log('boneTextureSize', unifiedBoneTextureSize); } - bakeFrame(skeleton, drawCallIndex, frameIndex) { + bakeFrame(glb, skeleton, drawCallIndex, frameIndex) { const boneMatrices = this.unifiedBoneMatrices; const drawCall = this.parent.drawCalls[drawCallIndex]; @@ -502,6 +521,11 @@ class InstancedSkeleton extends THREE.Skeleton { const scale = new THREE.Vector3(); localMatrix.decompose(translation, rotation, scale); rotation.invert(); + // check if scale is uniform + var tolerance = 1.192092896e-07 * 1000; + if ( !(Math.abs(scale.x - scale.y) < tolerance && Math.abs(scale.x - scale.z) < tolerance) ) { + console.log('Non-uniform scale bone matrix', glb, scale); + } const pos = new THREE.Vector4( localMatrix.elements[12], localMatrix.elements[13], @@ -522,6 +546,8 @@ class MobBatchedMesh extends InstancedBatchedMesh { const { glbs, skinnedMeshes: meshes, + app, + appUrls, } = mobData; const { // atlas, @@ -870,6 +896,8 @@ gl_Position = projectionMatrix * mvPosition; super(geometry, material, allocator); this.frustumCulled = false; + this.app = app; + this.appUrls = appUrls; this.procGenInstance = procGenInstance; { @@ -906,6 +934,7 @@ gl_Position = projectionMatrix * mvPosition; this.glbs = glbs; this.meshes = meshes; + this.mobs = []; // this.rootBones = rootBones; this.drawCalls = Array(meshes.length).fill(null); @@ -930,6 +959,7 @@ gl_Position = projectionMatrix * mvPosition; // animations let mixer = null; + // const idleAnimation = animations.find(a => a.name === 'idle'); const clip = animations[0]; //{ mixer = new THREE.AnimationMixer(rootBone2); @@ -950,7 +980,7 @@ gl_Position = projectionMatrix * mvPosition; mixer.update(1. / bakeFps); mixer.updateMatrixWorld(); - this.skeleton.bakeFrame(skeleton2, i, t); + this.skeleton.bakeFrame(glb, skeleton2, i, t); } } this.skeleton.unifiedBoneTexture.needsUpdate = true; @@ -972,7 +1002,7 @@ gl_Position = projectionMatrix * mvPosition; // mob geometry { - const _renderMobGeometry = (drawCall, ps, qs, index) => { + const _renderMobGeometry = (drawCall, ps, qs, to, index) => { // locals const instanceIndex = drawCall.getInstanceCount(); @@ -1008,8 +1038,7 @@ gl_Position = projectionMatrix * mvPosition; drawCall.updateTexture('q', qOffset + instanceIndex * 4, 4); // time offset - - timeOffsetTexture.image.data[timeOffsetOffset + instanceIndex] = Math.random(); + timeOffsetTexture.image.data[timeOffsetOffset + instanceIndex] = to; drawCall.updateTexture('timeOffset', timeOffsetOffset + instanceIndex, 1); @@ -1075,13 +1104,61 @@ gl_Position = projectionMatrix * mvPosition; } let mobInstances = []; + mobData.timeOffsets = []; for (let i = 0; i < mobData.instances.length; i++) { const geometryNoise = mobData.instances[i]; // console.log('got noise', geometryNoise); const geometryIndex = Math.floor(geometryNoise * this.meshes.length); - + + const px = mobData.ps[i * 3]; + const py = mobData.ps[i * 3 + 1]; + const pz = mobData.ps[i * 3 + 2]; + const position = new THREE.Vector3(px, py, pz); + + const qx = mobData.qs[i * 4]; + const qy = mobData.qs[i * 4 + 1]; + const qz = mobData.qs[i * 4 + 2]; + const qw = mobData.qs[i * 4 + 3]; + const quaternion = new THREE.Quaternion(qx, qy, qz, qw); + + const timeOffset = Math.random(); + mobData.timeOffsets[i] = timeOffset; // this can be generated when ps, qs are filled + const drawCall = this.getDrawCall(geometryIndex); - const mobInstance = _renderMobGeometry(drawCall, mobData.ps, mobData.qs, i); + const mobInstance = _renderMobGeometry(drawCall, mobData.ps, mobData.qs, timeOffset, i); + + const _updateMobGeometry = (position, quaternion) => { + const instanceIndex = drawCall.instances.indexOf(mobInstance); + if (instanceIndex >= 0) { + // locals + + const pTexture = drawCall.getTexture('p'); + const pOffset = drawCall.getTextureOffset('p'); + const qTexture = drawCall.getTexture('q'); + const qOffset = drawCall.getTextureOffset('q'); + + // update the texture + + pTexture.image.data[pOffset + instanceIndex * 3] = position.x; + pTexture.image.data[pOffset + instanceIndex * 3 + 1] = position.y; + pTexture.image.data[pOffset + instanceIndex * 3 + 2] = position.z; + + qTexture.image.data[qOffset + instanceIndex * 4] = quaternion.x; + qTexture.image.data[qOffset + instanceIndex * 4 + 1] = quaternion.y; + qTexture.image.data[qOffset + instanceIndex * 4 + 2] = quaternion.z; + qTexture.image.data[qOffset + instanceIndex * 4 + 3] = quaternion.w; + + + drawCall.updateTexture('p', pOffset + instanceIndex * 3, 3); + drawCall.updateTexture('q', qOffset + instanceIndex * 4, 4); + } + } + + const mob = this.addMobApp(this.app, this.appUrls[geometryIndex], + {position, quaternion, timeOffset, updateMobGeometry: _updateMobGeometry} + ); + mobInstance.mob = mob; + mobInstances.push(mobInstance); } @@ -1096,6 +1173,7 @@ gl_Position = projectionMatrix * mvPosition; const mobInstance = mobInstances[i]; + this.removeMobApp(mobInstance.mob.subApp); _unrenderMobGeometry(drawCall, mobInstance); } @@ -1111,13 +1189,51 @@ gl_Position = projectionMatrix * mvPosition; const frameIndex = timestamp * bakeFps / 1000; shader.uniforms.uTime.value = frameIndex; } + + for (const mob of this.mobs) { + mob.update(timestamp, timeDiff); + } + } + getPhysicsObjects() { + let results = []; + for (const mob of this.mobs) { + const physicsObjects = mob.getPhysicsObjects(); + results.push(physicsObjects); + } + results = results.flat(); + return results; + } + addMobApp(app, srcUrl, spec) { + /*if (app.appType !== 'mob') { + console.warn('not a mob app', app); + throw new Error('only mob apps can be mobs'); + }*/ + if (!srcUrl) { + console.warn('no srcUrl', app); + throw new Error('srcUrl is required'); + } + + const mob = new Mob(app, srcUrl, spec); + this.mobs.push(mob); + return mob; + } + removeMobApp(app) { + const index = this.mobs.findIndex(mob => mob.subApp === app); + if (index !== -1) { + const mob = this.mobs[index]; + mob.destroy(); + this.mobs.splice(index, 1); + } } } class MobsCompiledData { constructor({ + app = null, appUrls = [], } = {}) { + this.app = app; + this.appUrls = appUrls; this.glbs = null; this.skinnedMeshes = null; @@ -1206,9 +1322,6 @@ class Mobber { mobData, }); this.generator = generator; - /* this.tracker = new LodChunkTracker(this.generator, { - chunkWorldSize, - }); */ const numLods = 1; const tracker = procGenInstance.getChunkTracker({ numLods, @@ -1238,8 +1351,6 @@ class Mobber { const {renderData, chunk} = e; generator.mobBatchedMesh.drawChunk(chunk, renderData, tracker); }; - // tracker.addEventListener('chunkdatarequest', chunkdatarequest); - // tracker.addEventListener('chunkadd', chunkadd); tracker.onChunkDataRequest(chunkdatarequest); tracker.onChunkAdd(chunkadd); @@ -1262,6 +1373,12 @@ class Mobber { getChunks() { return this.generator.object; } + getPhysicsObjects() { + return this.generator.mobBatchedMesh.getPhysicsObjects(); + } + getMobs() { + return this.generator.mobBatchedMesh.mobs; + } update(timestamp, timeDiff) { const localPlayer = getLocalPlayer(); !this.procGenInstance.range && this.tracker.update(localPlayer.position); @@ -1276,7 +1393,6 @@ class Mobber { class MobManager { constructor() { this.mobbers = []; - this.mobs = []; } createMobber({ procGenInstance, @@ -1293,52 +1409,37 @@ class MobManager { mobber.destroy(); this.mobbers.splice(this.mobbers.indexOf(mobber), 1); } - async loadData(appUrls) { + async loadData(app, appUrls) { const mobData = new MobsCompiledData({ + app, appUrls, }); await mobData.waitForLoad(); return mobData; } - addMobApp(app, srcUrl) { - if (app.appType !== 'mob') { - console.warn('not a mob app', app); - throw new Error('only mob apps can be mobs'); - } - if (!srcUrl) { - console.warn('no srcUrl', app); - throw new Error('srcUrl is required'); - } - - const mob = new Mob(app, srcUrl); - this.mobs.push(mob); - } - removeMobApp(app) { - const index = this.mobs.findIndex(mob => mob.app === app); - if (index !== -1) { - const mob = this.mobs[index]; - mob.destroy(); - this.mobs.splice(index, 1); - } - } getPhysicsObjects() { let results = []; - for (const mob of this.mobs) { - const physicsObjects = mob.getPhysicsObjects(); + for (const mobber of this.mobbers) { + const physicsObjects = mobber.getPhysicsObjects(); results.push(physicsObjects); } results = results.flat(); return results; } + getMobs() { + let results = []; + for (const mobber of this.mobbers) { + const mobs = mobber.getMobs(); + results.push(mobs); + } + results = results.flat(); + return results; + } update(timestamp, timeDiff) { // mobber is updated by the app that created it /* for (const mobber of this.mobbers) { mobber.update(timestamp, timeDiff); } */ - - for (const mob of this.mobs) { - mob.update(timestamp, timeDiff); - } } } const mobManager = new MobManager();