Buckets:
ktongue/docker_container / simsite /frontend /node_modules /three-stdlib /animation /MMDAnimationHelper.cjs
| ; | |
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | |
| const THREE = require("three"); | |
| const CCDIKSolver = require("./CCDIKSolver.cjs"); | |
| const MMDPhysics = require("./MMDPhysics.cjs"); | |
| class MMDAnimationHelper { | |
| /** | |
| * @param {Object} params - (optional) | |
| * @param {boolean} params.sync - Whether animation durations of added objects are synched. Default is true. | |
| * @param {Number} params.afterglow - Default is 0.0. | |
| * @param {boolean} params.resetPhysicsOnLoop - Default is true. | |
| */ | |
| constructor(params = {}) { | |
| this.meshes = []; | |
| this.camera = null; | |
| this.cameraTarget = new THREE.Object3D(); | |
| this.cameraTarget.name = "target"; | |
| this.audio = null; | |
| this.audioManager = null; | |
| this.objects = /* @__PURE__ */ new WeakMap(); | |
| this.configuration = { | |
| sync: params.sync !== void 0 ? params.sync : true, | |
| afterglow: params.afterglow !== void 0 ? params.afterglow : 0, | |
| resetPhysicsOnLoop: params.resetPhysicsOnLoop !== void 0 ? params.resetPhysicsOnLoop : true, | |
| pmxAnimation: params.pmxAnimation !== void 0 ? params.pmxAnimation : false | |
| }; | |
| this.enabled = { | |
| animation: true, | |
| ik: true, | |
| grant: true, | |
| physics: true, | |
| cameraAnimation: true | |
| }; | |
| this.onBeforePhysics = function() { | |
| }; | |
| this.sharedPhysics = false; | |
| this.masterPhysics = null; | |
| } | |
| /** | |
| * Adds an Three.js Object to helper and setups animation. | |
| * The anmation durations of added objects are synched | |
| * if this.configuration.sync is true. | |
| * | |
| * @param {THREE.SkinnedMesh|THREE.Camera|THREE.Audio} object | |
| * @param {Object} params - (optional) | |
| * @param {THREE.AnimationClip|Array<THREE.AnimationClip>} params.animation - Only for THREE.SkinnedMesh and THREE.Camera. Default is undefined. | |
| * @param {boolean} params.physics - Only for THREE.SkinnedMesh. Default is true. | |
| * @param {Integer} params.warmup - Only for THREE.SkinnedMesh and physics is true. Default is 60. | |
| * @param {Number} params.unitStep - Only for THREE.SkinnedMesh and physics is true. Default is 1 / 65. | |
| * @param {Integer} params.maxStepNum - Only for THREE.SkinnedMesh and physics is true. Default is 3. | |
| * @param {Vector3} params.gravity - Only for THREE.SkinnedMesh and physics is true. Default ( 0, - 9.8 * 10, 0 ). | |
| * @param {Number} params.delayTime - Only for THREE.Audio. Default is 0.0. | |
| * @return {MMDAnimationHelper} | |
| */ | |
| add(object, params = {}) { | |
| if (object.isSkinnedMesh) { | |
| this._addMesh(object, params); | |
| } else if (object.isCamera) { | |
| this._setupCamera(object, params); | |
| } else if (object.type === "Audio") { | |
| this._setupAudio(object, params); | |
| } else { | |
| throw new Error( | |
| "THREE.MMDAnimationHelper.add: accepts only THREE.SkinnedMesh or THREE.Camera or THREE.Audio instance." | |
| ); | |
| } | |
| if (this.configuration.sync) | |
| this._syncDuration(); | |
| return this; | |
| } | |
| /** | |
| * Removes an Three.js Object from helper. | |
| * | |
| * @param {THREE.SkinnedMesh|THREE.Camera|THREE.Audio} object | |
| * @return {MMDAnimationHelper} | |
| */ | |
| remove(object) { | |
| if (object.isSkinnedMesh) { | |
| this._removeMesh(object); | |
| } else if (object.isCamera) { | |
| this._clearCamera(object); | |
| } else if (object.type === "Audio") { | |
| this._clearAudio(object); | |
| } else { | |
| throw new Error( | |
| "THREE.MMDAnimationHelper.remove: accepts only THREE.SkinnedMesh or THREE.Camera or THREE.Audio instance." | |
| ); | |
| } | |
| if (this.configuration.sync) | |
| this._syncDuration(); | |
| return this; | |
| } | |
| /** | |
| * Updates the animation. | |
| * | |
| * @param {Number} delta | |
| * @return {MMDAnimationHelper} | |
| */ | |
| update(delta) { | |
| if (this.audioManager !== null) | |
| this.audioManager.control(delta); | |
| for (let i = 0; i < this.meshes.length; i++) { | |
| this._animateMesh(this.meshes[i], delta); | |
| } | |
| if (this.sharedPhysics) | |
| this._updateSharedPhysics(delta); | |
| if (this.camera !== null) | |
| this._animateCamera(this.camera, delta); | |
| return this; | |
| } | |
| /** | |
| * Changes the pose of SkinnedMesh as VPD specifies. | |
| * | |
| * @param {THREE.SkinnedMesh} mesh | |
| * @param {Object} vpd - VPD content parsed MMDParser | |
| * @param {Object} params - (optional) | |
| * @param {boolean} params.resetPose - Default is true. | |
| * @param {boolean} params.ik - Default is true. | |
| * @param {boolean} params.grant - Default is true. | |
| * @return {MMDAnimationHelper} | |
| */ | |
| pose(mesh, vpd, params = {}) { | |
| if (params.resetPose !== false) | |
| mesh.pose(); | |
| const bones = mesh.skeleton.bones; | |
| const boneParams = vpd.bones; | |
| const boneNameDictionary = {}; | |
| for (let i = 0, il = bones.length; i < il; i++) { | |
| boneNameDictionary[bones[i].name] = i; | |
| } | |
| const vector = new THREE.Vector3(); | |
| const quaternion = new THREE.Quaternion(); | |
| for (let i = 0, il = boneParams.length; i < il; i++) { | |
| const boneParam = boneParams[i]; | |
| const boneIndex = boneNameDictionary[boneParam.name]; | |
| if (boneIndex === void 0) | |
| continue; | |
| const bone = bones[boneIndex]; | |
| bone.position.add(vector.fromArray(boneParam.translation)); | |
| bone.quaternion.multiply(quaternion.fromArray(boneParam.quaternion)); | |
| } | |
| mesh.updateMatrixWorld(true); | |
| if (this.configuration.pmxAnimation && mesh.geometry.userData.MMD && mesh.geometry.userData.MMD.format === "pmx") { | |
| const sortedBonesData = this._sortBoneDataArray(mesh.geometry.userData.MMD.bones.slice()); | |
| const ikSolver = params.ik !== false ? this._createCCDIKSolver(mesh) : null; | |
| const grantSolver = params.grant !== false ? this.createGrantSolver(mesh) : null; | |
| this._animatePMXMesh(mesh, sortedBonesData, ikSolver, grantSolver); | |
| } else { | |
| if (params.ik !== false) { | |
| this._createCCDIKSolver(mesh).update(); | |
| } | |
| if (params.grant !== false) { | |
| this.createGrantSolver(mesh).update(); | |
| } | |
| } | |
| return this; | |
| } | |
| /** | |
| * Enabes/Disables an animation feature. | |
| * | |
| * @param {string} key | |
| * @param {boolean} enabled | |
| * @return {MMDAnimationHelper} | |
| */ | |
| enable(key, enabled) { | |
| if (this.enabled[key] === void 0) { | |
| throw new Error("THREE.MMDAnimationHelper.enable: unknown key " + key); | |
| } | |
| this.enabled[key] = enabled; | |
| if (key === "physics") { | |
| for (let i = 0, il = this.meshes.length; i < il; i++) { | |
| this._optimizeIK(this.meshes[i], enabled); | |
| } | |
| } | |
| return this; | |
| } | |
| /** | |
| * Creates an GrantSolver instance. | |
| * | |
| * @param {THREE.SkinnedMesh} mesh | |
| * @return {GrantSolver} | |
| */ | |
| createGrantSolver(mesh) { | |
| return new GrantSolver(mesh, mesh.geometry.userData.MMD.grants); | |
| } | |
| // private methods | |
| _addMesh(mesh, params) { | |
| if (this.meshes.indexOf(mesh) >= 0) { | |
| throw new Error("THREE.MMDAnimationHelper._addMesh: SkinnedMesh '" + mesh.name + "' has already been added."); | |
| } | |
| this.meshes.push(mesh); | |
| this.objects.set(mesh, { looped: false }); | |
| this._setupMeshAnimation(mesh, params.animation); | |
| if (params.physics !== false) { | |
| this._setupMeshPhysics(mesh, params); | |
| } | |
| return this; | |
| } | |
| _setupCamera(camera, params) { | |
| if (this.camera === camera) { | |
| throw new Error("THREE.MMDAnimationHelper._setupCamera: Camera '" + camera.name + "' has already been set."); | |
| } | |
| if (this.camera) | |
| this.clearCamera(this.camera); | |
| this.camera = camera; | |
| camera.add(this.cameraTarget); | |
| this.objects.set(camera, {}); | |
| if (params.animation !== void 0) { | |
| this._setupCameraAnimation(camera, params.animation); | |
| } | |
| return this; | |
| } | |
| _setupAudio(audio, params) { | |
| if (this.audio === audio) { | |
| throw new Error("THREE.MMDAnimationHelper._setupAudio: Audio '" + audio.name + "' has already been set."); | |
| } | |
| if (this.audio) | |
| this.clearAudio(this.audio); | |
| this.audio = audio; | |
| this.audioManager = new AudioManager(audio, params); | |
| this.objects.set(this.audioManager, { | |
| duration: this.audioManager.duration | |
| }); | |
| return this; | |
| } | |
| _removeMesh(mesh) { | |
| let found = false; | |
| let writeIndex = 0; | |
| for (let i = 0, il = this.meshes.length; i < il; i++) { | |
| if (this.meshes[i] === mesh) { | |
| this.objects.delete(mesh); | |
| found = true; | |
| continue; | |
| } | |
| this.meshes[writeIndex++] = this.meshes[i]; | |
| } | |
| if (!found) { | |
| throw new Error( | |
| "THREE.MMDAnimationHelper._removeMesh: SkinnedMesh '" + mesh.name + "' has not been added yet." | |
| ); | |
| } | |
| this.meshes.length = writeIndex; | |
| return this; | |
| } | |
| _clearCamera(camera) { | |
| if (camera !== this.camera) { | |
| throw new Error("THREE.MMDAnimationHelper._clearCamera: Camera '" + camera.name + "' has not been set yet."); | |
| } | |
| this.camera.remove(this.cameraTarget); | |
| this.objects.delete(this.camera); | |
| this.camera = null; | |
| return this; | |
| } | |
| _clearAudio(audio) { | |
| if (audio !== this.audio) { | |
| throw new Error("THREE.MMDAnimationHelper._clearAudio: Audio '" + audio.name + "' has not been set yet."); | |
| } | |
| this.objects.delete(this.audioManager); | |
| this.audio = null; | |
| this.audioManager = null; | |
| return this; | |
| } | |
| _setupMeshAnimation(mesh, animation) { | |
| const objects = this.objects.get(mesh); | |
| if (animation !== void 0) { | |
| const animations = Array.isArray(animation) ? animation : [animation]; | |
| objects.mixer = new THREE.AnimationMixer(mesh); | |
| for (let i = 0, il = animations.length; i < il; i++) { | |
| objects.mixer.clipAction(animations[i]).play(); | |
| } | |
| objects.mixer.addEventListener("loop", function(event) { | |
| const tracks = event.action._clip.tracks; | |
| if (tracks.length > 0 && tracks[0].name.slice(0, 6) !== ".bones") | |
| return; | |
| objects.looped = true; | |
| }); | |
| } | |
| objects.ikSolver = this._createCCDIKSolver(mesh); | |
| objects.grantSolver = this.createGrantSolver(mesh); | |
| return this; | |
| } | |
| _setupCameraAnimation(camera, animation) { | |
| const animations = Array.isArray(animation) ? animation : [animation]; | |
| const objects = this.objects.get(camera); | |
| objects.mixer = new THREE.AnimationMixer(camera); | |
| for (let i = 0, il = animations.length; i < il; i++) { | |
| objects.mixer.clipAction(animations[i]).play(); | |
| } | |
| } | |
| _setupMeshPhysics(mesh, params) { | |
| const objects = this.objects.get(mesh); | |
| if (params.world === void 0 && this.sharedPhysics) { | |
| const masterPhysics = this._getMasterPhysics(); | |
| if (masterPhysics !== null) | |
| world = masterPhysics.world; | |
| } | |
| objects.physics = this._createMMDPhysics(mesh, params); | |
| if (objects.mixer && params.animationWarmup !== false) { | |
| this._animateMesh(mesh, 0); | |
| objects.physics.reset(); | |
| } | |
| objects.physics.warmup(params.warmup !== void 0 ? params.warmup : 60); | |
| this._optimizeIK(mesh, true); | |
| } | |
| _animateMesh(mesh, delta) { | |
| const objects = this.objects.get(mesh); | |
| const mixer = objects.mixer; | |
| const ikSolver = objects.ikSolver; | |
| const grantSolver = objects.grantSolver; | |
| const physics = objects.physics; | |
| const looped = objects.looped; | |
| if (mixer && this.enabled.animation) { | |
| this._restoreBones(mesh); | |
| mixer.update(delta); | |
| this._saveBones(mesh); | |
| if (this.configuration.pmxAnimation && mesh.geometry.userData.MMD && mesh.geometry.userData.MMD.format === "pmx") { | |
| if (!objects.sortedBonesData) | |
| objects.sortedBonesData = this._sortBoneDataArray(mesh.geometry.userData.MMD.bones.slice()); | |
| this._animatePMXMesh( | |
| mesh, | |
| objects.sortedBonesData, | |
| ikSolver && this.enabled.ik ? ikSolver : null, | |
| grantSolver && this.enabled.grant ? grantSolver : null | |
| ); | |
| } else { | |
| if (ikSolver && this.enabled.ik) { | |
| mesh.updateMatrixWorld(true); | |
| ikSolver.update(); | |
| } | |
| if (grantSolver && this.enabled.grant) { | |
| grantSolver.update(); | |
| } | |
| } | |
| } | |
| if (looped === true && this.enabled.physics) { | |
| if (physics && this.configuration.resetPhysicsOnLoop) | |
| physics.reset(); | |
| objects.looped = false; | |
| } | |
| if (physics && this.enabled.physics && !this.sharedPhysics) { | |
| this.onBeforePhysics(mesh); | |
| physics.update(delta); | |
| } | |
| } | |
| // Sort bones in order by 1. transformationClass and 2. bone index. | |
| // In PMX animation system, bone transformations should be processed | |
| // in this order. | |
| _sortBoneDataArray(boneDataArray) { | |
| return boneDataArray.sort(function(a, b) { | |
| if (a.transformationClass !== b.transformationClass) { | |
| return a.transformationClass - b.transformationClass; | |
| } else { | |
| return a.index - b.index; | |
| } | |
| }); | |
| } | |
| // PMX Animation system is a bit too complex and doesn't great match to | |
| // Three.js Animation system. This method attempts to simulate it as much as | |
| // possible but doesn't perfectly simulate. | |
| // This method is more costly than the regular one so | |
| // you are recommended to set constructor parameter "pmxAnimation: true" | |
| // only if your PMX model animation doesn't work well. | |
| // If you need better method you would be required to write your own. | |
| _animatePMXMesh(mesh, sortedBonesData, ikSolver, grantSolver) { | |
| _quaternionIndex = 0; | |
| _grantResultMap.clear(); | |
| for (let i = 0, il = sortedBonesData.length; i < il; i++) { | |
| updateOne(mesh, sortedBonesData[i].index, ikSolver, grantSolver); | |
| } | |
| mesh.updateMatrixWorld(true); | |
| return this; | |
| } | |
| _animateCamera(camera, delta) { | |
| const mixer = this.objects.get(camera).mixer; | |
| if (mixer && this.enabled.cameraAnimation) { | |
| mixer.update(delta); | |
| camera.updateProjectionMatrix(); | |
| camera.up.set(0, 1, 0); | |
| camera.up.applyQuaternion(camera.quaternion); | |
| camera.lookAt(this.cameraTarget.position); | |
| } | |
| } | |
| _optimizeIK(mesh, physicsEnabled) { | |
| const iks = mesh.geometry.userData.MMD.iks; | |
| const bones = mesh.geometry.userData.MMD.bones; | |
| for (let i = 0, il = iks.length; i < il; i++) { | |
| const ik = iks[i]; | |
| const links = ik.links; | |
| for (let j = 0, jl = links.length; j < jl; j++) { | |
| const link = links[j]; | |
| if (physicsEnabled === true) { | |
| link.enabled = bones[link.index].rigidBodyType > 0 ? false : true; | |
| } else { | |
| link.enabled = true; | |
| } | |
| } | |
| } | |
| } | |
| _createCCDIKSolver(mesh) { | |
| if (CCDIKSolver.CCDIKSolver === void 0) { | |
| throw new Error("THREE.MMDAnimationHelper: Import CCDIKSolver."); | |
| } | |
| return new CCDIKSolver.CCDIKSolver(mesh, mesh.geometry.userData.MMD.iks); | |
| } | |
| _createMMDPhysics(mesh, params) { | |
| if (MMDPhysics.MMDPhysics === void 0) { | |
| throw new Error("THREE.MMDPhysics: Import MMDPhysics."); | |
| } | |
| return new MMDPhysics.MMDPhysics(mesh, mesh.geometry.userData.MMD.rigidBodies, mesh.geometry.userData.MMD.constraints, params); | |
| } | |
| /* | |
| * Detects the longest duration and then sets it to them to sync. | |
| * TODO: Not to access private properties ( ._actions and ._clip ) | |
| */ | |
| _syncDuration() { | |
| let max = 0; | |
| const objects = this.objects; | |
| const meshes = this.meshes; | |
| const camera = this.camera; | |
| const audioManager = this.audioManager; | |
| for (let i = 0, il = meshes.length; i < il; i++) { | |
| const mixer = this.objects.get(meshes[i]).mixer; | |
| if (mixer === void 0) | |
| continue; | |
| for (let j = 0; j < mixer._actions.length; j++) { | |
| const clip = mixer._actions[j]._clip; | |
| if (!objects.has(clip)) { | |
| objects.set(clip, { | |
| duration: clip.duration | |
| }); | |
| } | |
| max = Math.max(max, objects.get(clip).duration); | |
| } | |
| } | |
| if (camera !== null) { | |
| const mixer = this.objects.get(camera).mixer; | |
| if (mixer !== void 0) { | |
| for (let i = 0, il = mixer._actions.length; i < il; i++) { | |
| const clip = mixer._actions[i]._clip; | |
| if (!objects.has(clip)) { | |
| objects.set(clip, { | |
| duration: clip.duration | |
| }); | |
| } | |
| max = Math.max(max, objects.get(clip).duration); | |
| } | |
| } | |
| } | |
| if (audioManager !== null) { | |
| max = Math.max(max, objects.get(audioManager).duration); | |
| } | |
| max += this.configuration.afterglow; | |
| for (let i = 0, il = this.meshes.length; i < il; i++) { | |
| const mixer = this.objects.get(this.meshes[i]).mixer; | |
| if (mixer === void 0) | |
| continue; | |
| for (let j = 0, jl = mixer._actions.length; j < jl; j++) { | |
| mixer._actions[j]._clip.duration = max; | |
| } | |
| } | |
| if (camera !== null) { | |
| const mixer = this.objects.get(camera).mixer; | |
| if (mixer !== void 0) { | |
| for (let i = 0, il = mixer._actions.length; i < il; i++) { | |
| mixer._actions[i]._clip.duration = max; | |
| } | |
| } | |
| } | |
| if (audioManager !== null) { | |
| audioManager.duration = max; | |
| } | |
| } | |
| // workaround | |
| _updatePropertyMixersBuffer(mesh) { | |
| const mixer = this.objects.get(mesh).mixer; | |
| const propertyMixers = mixer._bindings; | |
| const accuIndex = mixer._accuIndex; | |
| for (let i = 0, il = propertyMixers.length; i < il; i++) { | |
| const propertyMixer = propertyMixers[i]; | |
| const buffer = propertyMixer.buffer; | |
| const stride = propertyMixer.valueSize; | |
| const offset = (accuIndex + 1) * stride; | |
| propertyMixer.binding.getValue(buffer, offset); | |
| } | |
| } | |
| /* | |
| * Avoiding these two issues by restore/save bones before/after mixer animation. | |
| * | |
| * 1. PropertyMixer used by AnimationMixer holds cache value in .buffer. | |
| * Calculating IK, Grant, and Physics after mixer animation can break | |
| * the cache coherency. | |
| * | |
| * 2. Applying Grant two or more times without reset the posing breaks model. | |
| */ | |
| _saveBones(mesh) { | |
| const objects = this.objects.get(mesh); | |
| const bones = mesh.skeleton.bones; | |
| let backupBones = objects.backupBones; | |
| if (backupBones === void 0) { | |
| backupBones = new Float32Array(bones.length * 7); | |
| objects.backupBones = backupBones; | |
| } | |
| for (let i = 0, il = bones.length; i < il; i++) { | |
| const bone = bones[i]; | |
| bone.position.toArray(backupBones, i * 7); | |
| bone.quaternion.toArray(backupBones, i * 7 + 3); | |
| } | |
| } | |
| _restoreBones(mesh) { | |
| const objects = this.objects.get(mesh); | |
| const backupBones = objects.backupBones; | |
| if (backupBones === void 0) | |
| return; | |
| const bones = mesh.skeleton.bones; | |
| for (let i = 0, il = bones.length; i < il; i++) { | |
| const bone = bones[i]; | |
| bone.position.fromArray(backupBones, i * 7); | |
| bone.quaternion.fromArray(backupBones, i * 7 + 3); | |
| } | |
| } | |
| // experimental | |
| _getMasterPhysics() { | |
| if (this.masterPhysics !== null) | |
| return this.masterPhysics; | |
| for (let i = 0, il = this.meshes.length; i < il; i++) { | |
| const physics = this.meshes[i].physics; | |
| if (physics !== void 0 && physics !== null) { | |
| this.masterPhysics = physics; | |
| return this.masterPhysics; | |
| } | |
| } | |
| return null; | |
| } | |
| _updateSharedPhysics(delta) { | |
| if (this.meshes.length === 0 || !this.enabled.physics || !this.sharedPhysics) | |
| return; | |
| const physics = this._getMasterPhysics(); | |
| if (physics === null) | |
| return; | |
| for (let i = 0, il = this.meshes.length; i < il; i++) { | |
| const p = this.meshes[i].physics; | |
| if (p !== null && p !== void 0) { | |
| p.updateRigidBodies(); | |
| } | |
| } | |
| physics.stepSimulation(delta); | |
| for (let i = 0, il = this.meshes.length; i < il; i++) { | |
| const p = this.meshes[i].physics; | |
| if (p !== null && p !== void 0) { | |
| p.updateBones(); | |
| } | |
| } | |
| } | |
| } | |
| const _quaternions = []; | |
| let _quaternionIndex = 0; | |
| function getQuaternion() { | |
| if (_quaternionIndex >= _quaternions.length) { | |
| _quaternions.push(new THREE.Quaternion()); | |
| } | |
| return _quaternions[_quaternionIndex++]; | |
| } | |
| const _grantResultMap = /* @__PURE__ */ new Map(); | |
| function updateOne(mesh, boneIndex, ikSolver, grantSolver) { | |
| const bones = mesh.skeleton.bones; | |
| const bonesData = mesh.geometry.userData.MMD.bones; | |
| const boneData = bonesData[boneIndex]; | |
| const bone = bones[boneIndex]; | |
| if (_grantResultMap.has(boneIndex)) | |
| return; | |
| const quaternion = getQuaternion(); | |
| _grantResultMap.set(boneIndex, quaternion.copy(bone.quaternion)); | |
| if (grantSolver && boneData.grant && !boneData.grant.isLocal && boneData.grant.affectRotation) { | |
| const parentIndex = boneData.grant.parentIndex; | |
| const ratio = boneData.grant.ratio; | |
| if (!_grantResultMap.has(parentIndex)) { | |
| updateOne(mesh, parentIndex, ikSolver, grantSolver); | |
| } | |
| grantSolver.addGrantRotation(bone, _grantResultMap.get(parentIndex), ratio); | |
| } | |
| if (ikSolver && boneData.ik) { | |
| mesh.updateMatrixWorld(true); | |
| ikSolver.updateOne(boneData.ik); | |
| const links = boneData.ik.links; | |
| for (let i = 0, il = links.length; i < il; i++) { | |
| const link = links[i]; | |
| if (link.enabled === false) | |
| continue; | |
| const linkIndex = link.index; | |
| if (_grantResultMap.has(linkIndex)) { | |
| _grantResultMap.set(linkIndex, _grantResultMap.get(linkIndex).copy(bones[linkIndex].quaternion)); | |
| } | |
| } | |
| } | |
| quaternion.copy(bone.quaternion); | |
| } | |
| class AudioManager { | |
| /** | |
| * @param {THREE.Audio} audio | |
| * @param {Object} params - (optional) | |
| * @param {Nuumber} params.delayTime | |
| */ | |
| constructor(audio, params = {}) { | |
| this.audio = audio; | |
| this.elapsedTime = 0; | |
| this.currentTime = 0; | |
| this.delayTime = params.delayTime !== void 0 ? params.delayTime : 0; | |
| this.audioDuration = this.audio.buffer.duration; | |
| this.duration = this.audioDuration + this.delayTime; | |
| } | |
| /** | |
| * @param {Number} delta | |
| * @return {AudioManager} | |
| */ | |
| control(delta) { | |
| this.elapsed += delta; | |
| this.currentTime += delta; | |
| if (this._shouldStopAudio()) | |
| this.audio.stop(); | |
| if (this._shouldStartAudio()) | |
| this.audio.play(); | |
| return this; | |
| } | |
| // private methods | |
| _shouldStartAudio() { | |
| if (this.audio.isPlaying) | |
| return false; | |
| while (this.currentTime >= this.duration) { | |
| this.currentTime -= this.duration; | |
| } | |
| if (this.currentTime < this.delayTime) | |
| return false; | |
| if (this.currentTime - this.delayTime > this.audioDuration) | |
| return false; | |
| return true; | |
| } | |
| _shouldStopAudio() { | |
| return this.audio.isPlaying && this.currentTime >= this.duration; | |
| } | |
| } | |
| const _q = /* @__PURE__ */ new THREE.Quaternion(); | |
| class GrantSolver { | |
| constructor(mesh, grants = []) { | |
| this.mesh = mesh; | |
| this.grants = grants; | |
| } | |
| /** | |
| * Solve all the grant bones | |
| * @return {GrantSolver} | |
| */ | |
| update() { | |
| const grants = this.grants; | |
| for (let i = 0, il = grants.length; i < il; i++) { | |
| this.updateOne(grants[i]); | |
| } | |
| return this; | |
| } | |
| /** | |
| * Solve a grant bone | |
| * @param {Object} grant - grant parameter | |
| * @return {GrantSolver} | |
| */ | |
| updateOne(grant) { | |
| const bones = this.mesh.skeleton.bones; | |
| const bone = bones[grant.index]; | |
| const parentBone = bones[grant.parentIndex]; | |
| if (grant.isLocal) { | |
| if (grant.affectPosition) | |
| ; | |
| if (grant.affectRotation) | |
| ; | |
| } else { | |
| if (grant.affectPosition) | |
| ; | |
| if (grant.affectRotation) { | |
| this.addGrantRotation(bone, parentBone.quaternion, grant.ratio); | |
| } | |
| } | |
| return this; | |
| } | |
| addGrantRotation(bone, q, ratio) { | |
| _q.set(0, 0, 0, 1); | |
| _q.slerp(q, ratio); | |
| bone.quaternion.multiply(_q); | |
| return this; | |
| } | |
| } | |
| exports.MMDAnimationHelper = MMDAnimationHelper; | |
| //# sourceMappingURL=MMDAnimationHelper.cjs.map | |
Xet Storage Details
- Size:
- 23.5 kB
- Xet hash:
- cf844e02447e78c74be7c945262137ad97b83293890459b556246a289da7a03b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.