import { Ai as Object3D, Cn as GLBufferAttribute, Cs as Vector2, Gr as MathUtils, Jr as Matrix4, Nn as Group, So as SkinnedMesh, at as Color, bo as Skeleton, ki as NumberKeyframeTrack, oa as Quaternion, sa as QuaternionKeyframeTrack, un as Euler, ws as Vector3, y as AnimationClip } from "./three.module-C9LqGydR.js"; //#region node_modules/@pixiv/three-vrm-animation/lib/three-vrm-animation.module.js /*! * @pixiv/three-vrm-animation v3.4.1 * The implementation of VRM Animation * * Copyright (c) 2019-2025 pixiv Inc. * @pixiv/three-vrm-animation is distributed under MIT License * https://github.com/pixiv/three-vrm/blob/release/LICENSE */ var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; var __async2 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; var VRMExpression = class extends Object3D { constructor(expressionName) { super(); this.weight = 0; this.isBinary = false; this.overrideBlink = "none"; this.overrideLookAt = "none"; this.overrideMouth = "none"; this._binds = []; this.name = `VRMExpression_${expressionName}`; this.expressionName = expressionName; this.type = "VRMExpression"; this.visible = false; } /** * Binds that this expression influences. */ get binds() { return this._binds; } /** * A value represents how much it should override blink expressions. * `0.0` == no override at all, `1.0` == completely block the expressions. */ get overrideBlinkAmount() { if (this.overrideBlink === "block") return 0 < this.outputWeight ? 1 : 0; else if (this.overrideBlink === "blend") return this.outputWeight; else return 0; } /** * A value represents how much it should override lookAt expressions. * `0.0` == no override at all, `1.0` == completely block the expressions. */ get overrideLookAtAmount() { if (this.overrideLookAt === "block") return 0 < this.outputWeight ? 1 : 0; else if (this.overrideLookAt === "blend") return this.outputWeight; else return 0; } /** * A value represents how much it should override mouth expressions. * `0.0` == no override at all, `1.0` == completely block the expressions. */ get overrideMouthAmount() { if (this.overrideMouth === "block") return 0 < this.outputWeight ? 1 : 0; else if (this.overrideMouth === "blend") return this.outputWeight; else return 0; } /** * An output weight of this expression, considering the {@link isBinary}. */ get outputWeight() { if (this.isBinary) return this.weight > .5 ? 1 : 0; return this.weight; } /** * Add an expression bind to the expression. * * @param bind A bind to add */ addBind(bind) { this._binds.push(bind); } /** * Delete an expression bind from the expression. * * @param bind A bind to delete */ deleteBind(bind) { const index = this._binds.indexOf(bind); if (index >= 0) this._binds.splice(index, 1); } /** * Apply weight to every assigned blend shapes. * Should be called every frame. */ applyWeight(options) { var _a; let actualWeight = this.outputWeight; actualWeight *= (_a = options == null ? void 0 : options.multiplier) != null ? _a : 1; if (this.isBinary && actualWeight < 1) actualWeight = 0; this._binds.forEach((bind) => bind.applyWeight(actualWeight)); } /** * Clear previously assigned blend shapes. */ clearAppliedWeight() { this._binds.forEach((bind) => bind.clearAppliedWeight()); } }; function extractPrimitivesInternal(gltf, nodeIndex, node) { var _a, _b; const json = gltf.parser.json; const schemaNode = (_a = json.nodes) == null ? void 0 : _a[nodeIndex]; if (schemaNode == null) { console.warn(`extractPrimitivesInternal: Attempt to use nodes[${nodeIndex}] of glTF but the node doesn't exist`); return null; } const meshIndex = schemaNode.mesh; if (meshIndex == null) return null; const schemaMesh = (_b = json.meshes) == null ? void 0 : _b[meshIndex]; if (schemaMesh == null) { console.warn(`extractPrimitivesInternal: Attempt to use meshes[${meshIndex}] of glTF but the mesh doesn't exist`); return null; } const primitiveCount = schemaMesh.primitives.length; const primitives = []; node.traverse((object) => { if (primitives.length < primitiveCount) { if (object.isMesh) primitives.push(object); } }); return primitives; } function gltfExtractPrimitivesFromNode(gltf, nodeIndex) { return __async2(this, null, function* () { return extractPrimitivesInternal(gltf, nodeIndex, yield gltf.parser.getDependency("node", nodeIndex)); }); } var VRMExpressionPresetName = { Aa: "aa", Ih: "ih", Ou: "ou", Ee: "ee", Oh: "oh", Blink: "blink", Happy: "happy", Angry: "angry", Sad: "sad", Relaxed: "relaxed", LookUp: "lookUp", Surprised: "surprised", LookDown: "lookDown", LookLeft: "lookLeft", LookRight: "lookRight", BlinkLeft: "blinkLeft", BlinkRight: "blinkRight", Neutral: "neutral" }; function saturate(value) { return Math.max(Math.min(value, 1), 0); } var VRMExpressionManager = class _VRMExpressionManager { /** * Create a new {@link VRMExpressionManager}. */ constructor() { this.blinkExpressionNames = [ "blink", "blinkLeft", "blinkRight" ]; this.lookAtExpressionNames = [ "lookLeft", "lookRight", "lookUp", "lookDown" ]; this.mouthExpressionNames = [ "aa", "ee", "ih", "oh", "ou" ]; this._expressions = []; this._expressionMap = {}; } get expressions() { return this._expressions.concat(); } get expressionMap() { return Object.assign({}, this._expressionMap); } /** * A map from name to expression, but excluding custom expressions. */ get presetExpressionMap() { const result = {}; const presetNameSet = new Set(Object.values(VRMExpressionPresetName)); Object.entries(this._expressionMap).forEach(([name, expression]) => { if (presetNameSet.has(name)) result[name] = expression; }); return result; } /** * A map from name to expression, but excluding preset expressions. */ get customExpressionMap() { const result = {}; const presetNameSet = new Set(Object.values(VRMExpressionPresetName)); Object.entries(this._expressionMap).forEach(([name, expression]) => { if (!presetNameSet.has(name)) result[name] = expression; }); return result; } /** * Copy the given {@link VRMExpressionManager} into this one. * @param source The {@link VRMExpressionManager} you want to copy * @returns this */ copy(source) { this._expressions.concat().forEach((expression) => { this.unregisterExpression(expression); }); source._expressions.forEach((expression) => { this.registerExpression(expression); }); this.blinkExpressionNames = source.blinkExpressionNames.concat(); this.lookAtExpressionNames = source.lookAtExpressionNames.concat(); this.mouthExpressionNames = source.mouthExpressionNames.concat(); return this; } /** * Returns a clone of this {@link VRMExpressionManager}. * @returns Copied {@link VRMExpressionManager} */ clone() { return new _VRMExpressionManager().copy(this); } /** * Return a registered expression. * If it cannot find an expression, it will return `null` instead. * * @param name Name or preset name of the expression */ getExpression(name) { var _a; return (_a = this._expressionMap[name]) != null ? _a : null; } /** * Register an expression. * * @param expression {@link VRMExpression} that describes the expression */ registerExpression(expression) { this._expressions.push(expression); this._expressionMap[expression.expressionName] = expression; } /** * Unregister an expression. * * @param expression The expression you want to unregister */ unregisterExpression(expression) { const index = this._expressions.indexOf(expression); if (index === -1) console.warn("VRMExpressionManager: The specified expressions is not registered"); this._expressions.splice(index, 1); delete this._expressionMap[expression.expressionName]; } /** * Get the current weight of the specified expression. * If it doesn't have an expression of given name, it will return `null` instead. * * @param name Name of the expression */ getValue(name) { var _a; const expression = this.getExpression(name); return (_a = expression == null ? void 0 : expression.weight) != null ? _a : null; } /** * Set a weight to the specified expression. * * @param name Name of the expression * @param weight Weight */ setValue(name, weight) { const expression = this.getExpression(name); if (expression) expression.weight = saturate(weight); } /** * Reset weights of all expressions to `0.0`. */ resetValues() { this._expressions.forEach((expression) => { expression.weight = 0; }); } /** * Get a track name of specified expression. * This track name is needed to manipulate its expression via keyframe animations. * * @example Manipulate an expression using keyframe animation * ```js * const trackName = vrm.expressionManager.getExpressionTrackName( 'blink' ); * const track = new THREE.NumberKeyframeTrack( * name, * [ 0.0, 0.5, 1.0 ], // times * [ 0.0, 1.0, 0.0 ] // values * ); * * const clip = new THREE.AnimationClip( * 'blink', // name * 1.0, // duration * [ track ] // tracks * ); * * const mixer = new THREE.AnimationMixer( vrm.scene ); * const action = mixer.clipAction( clip ); * action.play(); * ``` * * @param name Name of the expression */ getExpressionTrackName(name) { const expression = this.getExpression(name); return expression ? `${expression.name}.weight` : null; } /** * Update every expressions. */ update() { const weightMultipliers = this._calculateWeightMultipliers(); this._expressions.forEach((expression) => { expression.clearAppliedWeight(); }); this._expressions.forEach((expression) => { let multiplier = 1; const name = expression.expressionName; if (this.blinkExpressionNames.indexOf(name) !== -1) multiplier *= weightMultipliers.blink; if (this.lookAtExpressionNames.indexOf(name) !== -1) multiplier *= weightMultipliers.lookAt; if (this.mouthExpressionNames.indexOf(name) !== -1) multiplier *= weightMultipliers.mouth; expression.applyWeight({ multiplier }); }); } /** * Calculate sum of override amounts to see how much we should multiply weights of certain expressions. */ _calculateWeightMultipliers() { let blink = 1; let lookAt = 1; let mouth = 1; this._expressions.forEach((expression) => { blink -= expression.overrideBlinkAmount; lookAt -= expression.overrideLookAtAmount; mouth -= expression.overrideMouthAmount; }); blink = Math.max(0, blink); lookAt = Math.max(0, lookAt); mouth = Math.max(0, mouth); return { blink, lookAt, mouth }; } }; var VRMExpressionMaterialColorType = { Color: "color", EmissionColor: "emissionColor", ShadeColor: "shadeColor", MatcapColor: "matcapColor", RimColor: "rimColor", OutlineColor: "outlineColor" }; var v0ExpressionMaterialColorMap = { _Color: VRMExpressionMaterialColorType.Color, _EmissionColor: VRMExpressionMaterialColorType.EmissionColor, _ShadeColor: VRMExpressionMaterialColorType.ShadeColor, _RimColor: VRMExpressionMaterialColorType.RimColor, _OutlineColor: VRMExpressionMaterialColorType.OutlineColor }; var _color = new Color(); var _VRMExpressionMaterialColorBind = class _VRMExpressionMaterialColorBind2 { constructor({ material, type, targetValue, targetAlpha }) { this.material = material; this.type = type; this.targetValue = targetValue; this.targetAlpha = targetAlpha != null ? targetAlpha : 1; const color = this._initColorBindState(); const alpha = this._initAlphaBindState(); this._state = { color, alpha }; } applyWeight(weight) { const { color, alpha } = this._state; if (color != null) { const { propertyName, deltaValue } = color; const target = this.material[propertyName]; if (target != void 0) target.add(_color.copy(deltaValue).multiplyScalar(weight)); } if (alpha != null) { const { propertyName, deltaValue } = alpha; if (this.material[propertyName] != void 0) this.material[propertyName] += deltaValue * weight; } } clearAppliedWeight() { const { color, alpha } = this._state; if (color != null) { const { propertyName, initialValue } = color; const target = this.material[propertyName]; if (target != void 0) target.copy(initialValue); } if (alpha != null) { const { propertyName, initialValue } = alpha; if (this.material[propertyName] != void 0) this.material[propertyName] = initialValue; } } _initColorBindState() { var _a, _b, _c; const { material, type, targetValue } = this; const propertyNameMap = this._getPropertyNameMap(); const propertyName = (_b = (_a = propertyNameMap == null ? void 0 : propertyNameMap[type]) == null ? void 0 : _a[0]) != null ? _b : null; if (propertyName == null) { console.warn(`Tried to add a material color bind to the material ${(_c = material.name) != null ? _c : "(no name)"}, the type ${type} but the material or the type is not supported.`); return null; } const initialValue = material[propertyName].clone(); return { propertyName, initialValue, deltaValue: new Color(targetValue.r - initialValue.r, targetValue.g - initialValue.g, targetValue.b - initialValue.b) }; } _initAlphaBindState() { var _a, _b, _c; const { material, type, targetAlpha } = this; const propertyNameMap = this._getPropertyNameMap(); const propertyName = (_b = (_a = propertyNameMap == null ? void 0 : propertyNameMap[type]) == null ? void 0 : _a[1]) != null ? _b : null; if (propertyName == null && targetAlpha !== 1) { console.warn(`Tried to add a material alpha bind to the material ${(_c = material.name) != null ? _c : "(no name)"}, the type ${type} but the material or the type does not support alpha.`); return null; } if (propertyName == null) return null; const initialValue = material[propertyName]; return { propertyName, initialValue, deltaValue: targetAlpha - initialValue }; } _getPropertyNameMap() { var _a, _b; return (_b = (_a = Object.entries(_VRMExpressionMaterialColorBind2._propertyNameMapMap).find(([distinguisher]) => { return this.material[distinguisher] === true; })) == null ? void 0 : _a[1]) != null ? _b : null; } }; _VRMExpressionMaterialColorBind._propertyNameMapMap = { isMeshStandardMaterial: { color: ["color", "opacity"], emissionColor: ["emissive", null] }, isMeshBasicMaterial: { color: ["color", "opacity"] }, isMToonMaterial: { color: ["color", "opacity"], emissionColor: ["emissive", null], outlineColor: ["outlineColorFactor", null], matcapColor: ["matcapFactor", null], rimColor: ["parametricRimColorFactor", null], shadeColor: ["shadeColorFactor", null] } }; var VRMExpressionMaterialColorBind = _VRMExpressionMaterialColorBind; var VRMExpressionMorphTargetBind = class { constructor({ primitives, index, weight }) { this.primitives = primitives; this.index = index; this.weight = weight; } applyWeight(weight) { this.primitives.forEach((mesh) => { var _a; if (((_a = mesh.morphTargetInfluences) == null ? void 0 : _a[this.index]) != null) mesh.morphTargetInfluences[this.index] += this.weight * weight; }); } clearAppliedWeight() { this.primitives.forEach((mesh) => { var _a; if (((_a = mesh.morphTargetInfluences) == null ? void 0 : _a[this.index]) != null) mesh.morphTargetInfluences[this.index] = 0; }); } }; var _v2 = new Vector2(); var _VRMExpressionTextureTransformBind = class _VRMExpressionTextureTransformBind2 { constructor({ material, scale, offset }) { var _a, _b; this.material = material; this.scale = scale; this.offset = offset; const propertyNames = (_a = Object.entries(_VRMExpressionTextureTransformBind2._propertyNamesMap).find(([distinguisher]) => { return material[distinguisher] === true; })) == null ? void 0 : _a[1]; if (propertyNames == null) { console.warn(`Tried to add a texture transform bind to the material ${(_b = material.name) != null ? _b : "(no name)"} but the material is not supported.`); this._properties = []; } else { this._properties = []; propertyNames.forEach((propertyName) => { var _a2; const texture = (_a2 = material[propertyName]) == null ? void 0 : _a2.clone(); if (!texture) return null; material[propertyName] = texture; const initialOffset = texture.offset.clone(); const initialScale = texture.repeat.clone(); const deltaOffset = offset.clone().sub(initialOffset); const deltaScale = scale.clone().sub(initialScale); this._properties.push({ name: propertyName, initialOffset, deltaOffset, initialScale, deltaScale }); }); } } applyWeight(weight) { this._properties.forEach((property) => { const target = this.material[property.name]; if (target === void 0) return; target.offset.add(_v2.copy(property.deltaOffset).multiplyScalar(weight)); target.repeat.add(_v2.copy(property.deltaScale).multiplyScalar(weight)); }); } clearAppliedWeight() { this._properties.forEach((property) => { const target = this.material[property.name]; if (target === void 0) return; target.offset.copy(property.initialOffset); target.repeat.copy(property.initialScale); }); } }; _VRMExpressionTextureTransformBind._propertyNamesMap = { isMeshStandardMaterial: [ "map", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "roughnessMap", "metalnessMap", "alphaMap" ], isMeshBasicMaterial: [ "map", "specularMap", "alphaMap" ], isMToonMaterial: [ "map", "normalMap", "emissiveMap", "shadeMultiplyTexture", "rimMultiplyTexture", "outlineWidthMultiplyTexture", "uvAnimationMaskTexture" ] }; var VRMExpressionTextureTransformBind = _VRMExpressionTextureTransformBind; var POSSIBLE_SPEC_VERSIONS = /* @__PURE__ */ new Set(["1.0", "1.0-beta"]); var _VRMExpressionLoaderPlugin = class _VRMExpressionLoaderPlugin2 { get name() { return "VRMExpressionLoaderPlugin"; } constructor(parser) { this.parser = parser; } afterRoot(gltf) { return __async2(this, null, function* () { gltf.userData.vrmExpressionManager = yield this._import(gltf); }); } /** * Import a {@link VRMExpressionManager} from a VRM. * * @param gltf A parsed result of GLTF taken from GLTFLoader */ _import(gltf) { return __async2(this, null, function* () { const v1Result = yield this._v1Import(gltf); if (v1Result) return v1Result; const v0Result = yield this._v0Import(gltf); if (v0Result) return v0Result; return null; }); } _v1Import(gltf) { return __async2(this, null, function* () { var _a, _b; const json = this.parser.json; if (!(((_a = json.extensionsUsed) == null ? void 0 : _a.indexOf("VRMC_vrm")) !== -1)) return null; const extension = (_b = json.extensions) == null ? void 0 : _b["VRMC_vrm"]; if (!extension) return null; const specVersion = extension.specVersion; if (!POSSIBLE_SPEC_VERSIONS.has(specVersion)) { console.warn(`VRMExpressionLoaderPlugin: Unknown VRMC_vrm specVersion "${specVersion}"`); return null; } const schemaExpressions = extension.expressions; if (!schemaExpressions) return null; const presetNameSet = new Set(Object.values(VRMExpressionPresetName)); const nameSchemaExpressionMap = /* @__PURE__ */ new Map(); if (schemaExpressions.preset != null) Object.entries(schemaExpressions.preset).forEach(([name, schemaExpression]) => { if (schemaExpression == null) return; if (!presetNameSet.has(name)) { console.warn(`VRMExpressionLoaderPlugin: Unknown preset name "${name}" detected. Ignoring the expression`); return; } nameSchemaExpressionMap.set(name, schemaExpression); }); if (schemaExpressions.custom != null) Object.entries(schemaExpressions.custom).forEach(([name, schemaExpression]) => { if (presetNameSet.has(name)) { console.warn(`VRMExpressionLoaderPlugin: Custom expression cannot have preset name "${name}". Ignoring the expression`); return; } nameSchemaExpressionMap.set(name, schemaExpression); }); const manager = new VRMExpressionManager(); yield Promise.all(Array.from(nameSchemaExpressionMap.entries()).map((_0) => __async2(this, [_0], function* ([name, schemaExpression]) { var _a2, _b2, _c, _d, _e, _f, _g; const expression = new VRMExpression(name); gltf.scene.add(expression); expression.isBinary = (_a2 = schemaExpression.isBinary) != null ? _a2 : false; expression.overrideBlink = (_b2 = schemaExpression.overrideBlink) != null ? _b2 : "none"; expression.overrideLookAt = (_c = schemaExpression.overrideLookAt) != null ? _c : "none"; expression.overrideMouth = (_d = schemaExpression.overrideMouth) != null ? _d : "none"; (_e = schemaExpression.morphTargetBinds) == null || _e.forEach((bind) => __async2(this, null, function* () { var _a3; if (bind.node === void 0 || bind.index === void 0) return; const primitives = yield gltfExtractPrimitivesFromNode(gltf, bind.node); const morphTargetIndex = bind.index; if (!primitives.every((primitive) => Array.isArray(primitive.morphTargetInfluences) && morphTargetIndex < primitive.morphTargetInfluences.length)) { console.warn(`VRMExpressionLoaderPlugin: ${schemaExpression.name} attempts to index morph #${morphTargetIndex} but not found.`); return; } expression.addBind(new VRMExpressionMorphTargetBind({ primitives, index: morphTargetIndex, weight: (_a3 = bind.weight) != null ? _a3 : 1 })); })); if (schemaExpression.materialColorBinds || schemaExpression.textureTransformBinds) { const gltfMaterials = []; gltf.scene.traverse((object) => { const material = object.material; if (material) if (Array.isArray(material)) gltfMaterials.push(...material); else gltfMaterials.push(material); }); (_f = schemaExpression.materialColorBinds) == null || _f.forEach((bind) => __async2(this, null, function* () { gltfMaterials.filter((material) => { var _a3; const materialIndex = (_a3 = this.parser.associations.get(material)) == null ? void 0 : _a3.materials; return bind.material === materialIndex; }).forEach((material) => { expression.addBind(new VRMExpressionMaterialColorBind({ material, type: bind.type, targetValue: new Color().fromArray(bind.targetValue), targetAlpha: bind.targetValue[3] })); }); })); (_g = schemaExpression.textureTransformBinds) == null || _g.forEach((bind) => __async2(this, null, function* () { gltfMaterials.filter((material) => { var _a3; const materialIndex = (_a3 = this.parser.associations.get(material)) == null ? void 0 : _a3.materials; return bind.material === materialIndex; }).forEach((material) => { var _a3, _b3; expression.addBind(new VRMExpressionTextureTransformBind({ material, offset: new Vector2().fromArray((_a3 = bind.offset) != null ? _a3 : [0, 0]), scale: new Vector2().fromArray((_b3 = bind.scale) != null ? _b3 : [1, 1]) })); }); })); } manager.registerExpression(expression); }))); return manager; }); } _v0Import(gltf) { return __async2(this, null, function* () { var _a; const json = this.parser.json; const vrmExt = (_a = json.extensions) == null ? void 0 : _a.VRM; if (!vrmExt) return null; const schemaBlendShape = vrmExt.blendShapeMaster; if (!schemaBlendShape) return null; const manager = new VRMExpressionManager(); const schemaBlendShapeGroups = schemaBlendShape.blendShapeGroups; if (!schemaBlendShapeGroups) return manager; const blendShapeNameSet = /* @__PURE__ */ new Set(); yield Promise.all(schemaBlendShapeGroups.map((schemaGroup) => __async2(this, null, function* () { var _a2; const v0PresetName = schemaGroup.presetName; const v1PresetName = v0PresetName != null && _VRMExpressionLoaderPlugin2.v0v1PresetNameMap[v0PresetName] || null; const name = v1PresetName != null ? v1PresetName : schemaGroup.name; if (name == null) { console.warn("VRMExpressionLoaderPlugin: One of custom expressions has no name. Ignoring the expression"); return; } if (blendShapeNameSet.has(name)) { console.warn(`VRMExpressionLoaderPlugin: An expression preset ${v0PresetName} has duplicated entries. Ignoring the expression`); return; } blendShapeNameSet.add(name); const expression = new VRMExpression(name); gltf.scene.add(expression); expression.isBinary = (_a2 = schemaGroup.isBinary) != null ? _a2 : false; if (schemaGroup.binds) schemaGroup.binds.forEach((bind) => __async2(this, null, function* () { var _a3; if (bind.mesh === void 0 || bind.index === void 0) return; const nodesUsingMesh = []; (_a3 = json.nodes) == null || _a3.forEach((node, i) => { if (node.mesh === bind.mesh) nodesUsingMesh.push(i); }); const morphTargetIndex = bind.index; yield Promise.all(nodesUsingMesh.map((nodeIndex) => __async2(this, null, function* () { var _a4; const primitives = yield gltfExtractPrimitivesFromNode(gltf, nodeIndex); if (!primitives.every((primitive) => Array.isArray(primitive.morphTargetInfluences) && morphTargetIndex < primitive.morphTargetInfluences.length)) { console.warn(`VRMExpressionLoaderPlugin: ${schemaGroup.name} attempts to index ${morphTargetIndex}th morph but not found.`); return; } expression.addBind(new VRMExpressionMorphTargetBind({ primitives, index: morphTargetIndex, weight: .01 * ((_a4 = bind.weight) != null ? _a4 : 100) })); }))); })); const materialValues = schemaGroup.materialValues; if (materialValues && materialValues.length !== 0) materialValues.forEach((materialValue) => { if (materialValue.materialName === void 0 || materialValue.propertyName === void 0 || materialValue.targetValue === void 0) return; const materials = []; gltf.scene.traverse((object) => { if (object.material) { const material = object.material; if (Array.isArray(material)) materials.push(...material.filter((mtl) => (mtl.name === materialValue.materialName || mtl.name === materialValue.materialName + " (Outline)") && materials.indexOf(mtl) === -1)); else if (material.name === materialValue.materialName && materials.indexOf(material) === -1) materials.push(material); } }); const materialPropertyName = materialValue.propertyName; materials.forEach((material) => { if (materialPropertyName === "_MainTex_ST") { const scale = new Vector2(materialValue.targetValue[0], materialValue.targetValue[1]); const offset = new Vector2(materialValue.targetValue[2], materialValue.targetValue[3]); offset.y = 1 - offset.y - scale.y; expression.addBind(new VRMExpressionTextureTransformBind({ material, scale, offset })); return; } const materialColorType = v0ExpressionMaterialColorMap[materialPropertyName]; if (materialColorType) { expression.addBind(new VRMExpressionMaterialColorBind({ material, type: materialColorType, targetValue: new Color().fromArray(materialValue.targetValue), targetAlpha: materialValue.targetValue[3] })); return; } console.warn(materialPropertyName + " is not supported"); }); }); manager.registerExpression(expression); }))); return manager; }); } }; _VRMExpressionLoaderPlugin.v0v1PresetNameMap = { a: "aa", e: "ee", i: "ih", o: "oh", u: "ou", blink: "blink", joy: "happy", angry: "angry", sorrow: "sad", fun: "relaxed", lookup: "lookUp", lookdown: "lookDown", lookleft: "lookLeft", lookright: "lookRight", blink_l: "blinkLeft", blink_r: "blinkRight", neutral: "neutral" }; var _VRMFirstPerson = class _VRMFirstPerson2 { /** * Create a new VRMFirstPerson object. * * @param humanoid A {@link VRMHumanoid} * @param meshAnnotations A {@link VRMFirstPersonMeshAnnotation} */ constructor(humanoid, meshAnnotations) { this._firstPersonOnlyLayer = _VRMFirstPerson2.DEFAULT_FIRSTPERSON_ONLY_LAYER; this._thirdPersonOnlyLayer = _VRMFirstPerson2.DEFAULT_THIRDPERSON_ONLY_LAYER; this._initializedLayers = false; this.humanoid = humanoid; this.meshAnnotations = meshAnnotations; } /** * Copy the given {@link VRMFirstPerson} into this one. * {@link humanoid} must be same as the source one. * @param source The {@link VRMFirstPerson} you want to copy * @returns this */ copy(source) { if (this.humanoid !== source.humanoid) throw new Error("VRMFirstPerson: humanoid must be same in order to copy"); this.meshAnnotations = source.meshAnnotations.map((annotation) => ({ meshes: annotation.meshes.concat(), type: annotation.type })); return this; } /** * Returns a clone of this {@link VRMFirstPerson}. * @returns Copied {@link VRMFirstPerson} */ clone() { return new _VRMFirstPerson2(this.humanoid, this.meshAnnotations).copy(this); } /** * A camera layer represents `FirstPersonOnly` layer. * Note that **you must call {@link setup} first before you use the layer feature** or it does not work properly. * * The value is {@link DEFAULT_FIRSTPERSON_ONLY_LAYER} by default but you can change the layer by specifying via {@link setup} if you prefer. * * @see https://vrm.dev/en/univrm/api/univrm_use_firstperson/ * @see https://threejs.org/docs/#api/en/core/Layers */ get firstPersonOnlyLayer() { return this._firstPersonOnlyLayer; } /** * A camera layer represents `ThirdPersonOnly` layer. * Note that **you must call {@link setup} first before you use the layer feature** or it does not work properly. * * The value is {@link DEFAULT_THIRDPERSON_ONLY_LAYER} by default but you can change the layer by specifying via {@link setup} if you prefer. * * @see https://vrm.dev/en/univrm/api/univrm_use_firstperson/ * @see https://threejs.org/docs/#api/en/core/Layers */ get thirdPersonOnlyLayer() { return this._thirdPersonOnlyLayer; } /** * In this method, it assigns layers for every meshes based on mesh annotations. * You must call this method first before you use the layer feature. * * This is an equivalent of [VRMFirstPerson.Setup](https://github.com/vrm-c/UniVRM/blob/73a5bd8fcddaa2a7a8735099a97e63c9db3e5ea0/Assets/VRM/Runtime/FirstPerson/VRMFirstPerson.cs#L295-L299) of the UniVRM. * * The `cameraLayer` parameter specifies which layer will be assigned for `FirstPersonOnly` / `ThirdPersonOnly`. * In UniVRM, we specified those by naming each desired layer as `FIRSTPERSON_ONLY_LAYER` / `THIRDPERSON_ONLY_LAYER` * but we are going to specify these layers at here since we are unable to name layers in Three.js. * * @param cameraLayer Specify which layer will be for `FirstPersonOnly` / `ThirdPersonOnly`. */ setup({ firstPersonOnlyLayer = _VRMFirstPerson2.DEFAULT_FIRSTPERSON_ONLY_LAYER, thirdPersonOnlyLayer = _VRMFirstPerson2.DEFAULT_THIRDPERSON_ONLY_LAYER } = {}) { if (this._initializedLayers) return; this._firstPersonOnlyLayer = firstPersonOnlyLayer; this._thirdPersonOnlyLayer = thirdPersonOnlyLayer; this.meshAnnotations.forEach((item) => { item.meshes.forEach((mesh) => { if (item.type === "firstPersonOnly") { mesh.layers.set(this._firstPersonOnlyLayer); mesh.traverse((child) => child.layers.set(this._firstPersonOnlyLayer)); } else if (item.type === "thirdPersonOnly") { mesh.layers.set(this._thirdPersonOnlyLayer); mesh.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer)); } else if (item.type === "auto") this._createHeadlessModel(mesh); }); }); this._initializedLayers = true; } _excludeTriangles(triangles, bws, skinIndex, exclude) { let count = 0; if (bws != null && bws.length > 0) for (let i = 0; i < triangles.length; i += 3) { const a = triangles[i]; const b = triangles[i + 1]; const c = triangles[i + 2]; const bw0 = bws[a]; const skin0 = skinIndex[a]; if (bw0[0] > 0 && exclude.includes(skin0[0])) continue; if (bw0[1] > 0 && exclude.includes(skin0[1])) continue; if (bw0[2] > 0 && exclude.includes(skin0[2])) continue; if (bw0[3] > 0 && exclude.includes(skin0[3])) continue; const bw1 = bws[b]; const skin1 = skinIndex[b]; if (bw1[0] > 0 && exclude.includes(skin1[0])) continue; if (bw1[1] > 0 && exclude.includes(skin1[1])) continue; if (bw1[2] > 0 && exclude.includes(skin1[2])) continue; if (bw1[3] > 0 && exclude.includes(skin1[3])) continue; const bw2 = bws[c]; const skin2 = skinIndex[c]; if (bw2[0] > 0 && exclude.includes(skin2[0])) continue; if (bw2[1] > 0 && exclude.includes(skin2[1])) continue; if (bw2[2] > 0 && exclude.includes(skin2[2])) continue; if (bw2[3] > 0 && exclude.includes(skin2[3])) continue; triangles[count++] = a; triangles[count++] = b; triangles[count++] = c; } return count; } _createErasedMesh(src, erasingBonesIndex) { const dst = new SkinnedMesh(src.geometry.clone(), src.material); dst.name = `${src.name}(erase)`; dst.frustumCulled = src.frustumCulled; dst.layers.set(this._firstPersonOnlyLayer); const geometry = dst.geometry; const skinIndexAttr = geometry.getAttribute("skinIndex"); const skinIndexAttrArray = skinIndexAttr instanceof GLBufferAttribute ? [] : skinIndexAttr.array; const skinIndex = []; for (let i = 0; i < skinIndexAttrArray.length; i += 4) skinIndex.push([ skinIndexAttrArray[i], skinIndexAttrArray[i + 1], skinIndexAttrArray[i + 2], skinIndexAttrArray[i + 3] ]); const skinWeightAttr = geometry.getAttribute("skinWeight"); const skinWeightAttrArray = skinWeightAttr instanceof GLBufferAttribute ? [] : skinWeightAttr.array; const skinWeight = []; for (let i = 0; i < skinWeightAttrArray.length; i += 4) skinWeight.push([ skinWeightAttrArray[i], skinWeightAttrArray[i + 1], skinWeightAttrArray[i + 2], skinWeightAttrArray[i + 3] ]); const index = geometry.getIndex(); if (!index) throw new Error("The geometry doesn't have an index buffer"); const oldTriangles = Array.from(index.array); const count = this._excludeTriangles(oldTriangles, skinWeight, skinIndex, erasingBonesIndex); const newTriangle = []; for (let i = 0; i < count; i++) newTriangle[i] = oldTriangles[i]; geometry.setIndex(newTriangle); if (src.onBeforeRender) dst.onBeforeRender = src.onBeforeRender; dst.bind(new Skeleton(src.skeleton.bones, src.skeleton.boneInverses), new Matrix4()); return dst; } _createHeadlessModelForSkinnedMesh(parent, mesh) { const eraseBoneIndexes = []; mesh.skeleton.bones.forEach((bone, index) => { if (this._isEraseTarget(bone)) eraseBoneIndexes.push(index); }); if (!eraseBoneIndexes.length) { mesh.layers.enable(this._thirdPersonOnlyLayer); mesh.layers.enable(this._firstPersonOnlyLayer); return; } mesh.layers.set(this._thirdPersonOnlyLayer); const newMesh = this._createErasedMesh(mesh, eraseBoneIndexes); parent.add(newMesh); } _createHeadlessModel(node) { if (node.type === "Group") { node.layers.set(this._thirdPersonOnlyLayer); if (this._isEraseTarget(node)) node.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer)); else { const parent = new Group(); parent.name = `_headless_${node.name}`; parent.layers.set(this._firstPersonOnlyLayer); node.parent.add(parent); node.children.filter((child) => child.type === "SkinnedMesh").forEach((child) => { const skinnedMesh = child; this._createHeadlessModelForSkinnedMesh(parent, skinnedMesh); }); } } else if (node.type === "SkinnedMesh") { const skinnedMesh = node; this._createHeadlessModelForSkinnedMesh(node.parent, skinnedMesh); } else if (this._isEraseTarget(node)) { node.layers.set(this._thirdPersonOnlyLayer); node.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer)); } } _isEraseTarget(bone) { if (bone === this.humanoid.getRawBoneNode("head")) return true; else if (!bone.parent) return false; else return this._isEraseTarget(bone.parent); } }; _VRMFirstPerson.DEFAULT_FIRSTPERSON_ONLY_LAYER = 9; _VRMFirstPerson.DEFAULT_THIRDPERSON_ONLY_LAYER = 10; new Vector3(); new Vector3(); new Quaternion(); var VRMHumanBoneParentMap = { hips: null, spine: "hips", chest: "spine", upperChest: "chest", neck: "upperChest", head: "neck", leftEye: "head", rightEye: "head", jaw: "head", leftUpperLeg: "hips", leftLowerLeg: "leftUpperLeg", leftFoot: "leftLowerLeg", leftToes: "leftFoot", rightUpperLeg: "hips", rightLowerLeg: "rightUpperLeg", rightFoot: "rightLowerLeg", rightToes: "rightFoot", leftShoulder: "upperChest", leftUpperArm: "leftShoulder", leftLowerArm: "leftUpperArm", leftHand: "leftLowerArm", rightShoulder: "upperChest", rightUpperArm: "rightShoulder", rightLowerArm: "rightUpperArm", rightHand: "rightLowerArm", leftThumbMetacarpal: "leftHand", leftThumbProximal: "leftThumbMetacarpal", leftThumbDistal: "leftThumbProximal", leftIndexProximal: "leftHand", leftIndexIntermediate: "leftIndexProximal", leftIndexDistal: "leftIndexIntermediate", leftMiddleProximal: "leftHand", leftMiddleIntermediate: "leftMiddleProximal", leftMiddleDistal: "leftMiddleIntermediate", leftRingProximal: "leftHand", leftRingIntermediate: "leftRingProximal", leftRingDistal: "leftRingIntermediate", leftLittleProximal: "leftHand", leftLittleIntermediate: "leftLittleProximal", leftLittleDistal: "leftLittleIntermediate", rightThumbMetacarpal: "rightHand", rightThumbProximal: "rightThumbMetacarpal", rightThumbDistal: "rightThumbProximal", rightIndexProximal: "rightHand", rightIndexIntermediate: "rightIndexProximal", rightIndexDistal: "rightIndexIntermediate", rightMiddleProximal: "rightHand", rightMiddleIntermediate: "rightMiddleProximal", rightMiddleDistal: "rightMiddleIntermediate", rightRingProximal: "rightHand", rightRingIntermediate: "rightRingProximal", rightRingDistal: "rightRingIntermediate", rightLittleProximal: "rightHand", rightLittleIntermediate: "rightLittleProximal", rightLittleDistal: "rightLittleIntermediate" }; function quatInvertCompat(target) { if (target.invert) target.invert(); else target.inverse(); return target; } new Vector3(); new Quaternion(); new Vector3(); new Quaternion(); new Vector3(); new Quaternion(); new Quaternion(); new Vector3(); new Vector3(); var SQRT_2_OVER_2 = Math.sqrt(2) / 2; new Quaternion(0, 0, -SQRT_2_OVER_2, SQRT_2_OVER_2); new Vector3(0, 1, 0); var _position = new Vector3(); var _scale = new Vector3(); function getWorldQuaternionLite(object, out) { object.matrixWorld.decompose(_position, out, _scale); return out; } function calcAzimuthAltitude(vector) { return [Math.atan2(-vector.z, vector.x), Math.atan2(vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z))]; } function sanitizeAngle(angle) { const roundTurn = Math.round(angle / 2 / Math.PI); return angle - 2 * Math.PI * roundTurn; } var VEC3_POSITIVE_Z = new Vector3(0, 0, 1); var _v3A5 = new Vector3(); var _v3B3 = new Vector3(); var _v3C = new Vector3(); var _quatA5 = new Quaternion(); var _quatB2 = new Quaternion(); var _quatC = new Quaternion(); var _quatD = new Quaternion(); var _eulerA = new Euler(); var _VRMLookAt = class _VRMLookAt2 { /** * Create a new {@link VRMLookAt}. * * @param humanoid A {@link VRMHumanoid} * @param applier A {@link VRMLookAtApplier} */ constructor(humanoid, applier) { this.offsetFromHeadBone = new Vector3(); this.autoUpdate = true; this.faceFront = new Vector3(0, 0, 1); this.humanoid = humanoid; this.applier = applier; this._yaw = 0; this._pitch = 0; this._needsUpdate = true; this._restHeadWorldQuaternion = this.getLookAtWorldQuaternion(new Quaternion()); } /** * Its current angle around Y axis, in degree. */ get yaw() { return this._yaw; } /** * Its current angle around Y axis, in degree. */ set yaw(value) { this._yaw = value; this._needsUpdate = true; } /** * Its current angle around X axis, in degree. */ get pitch() { return this._pitch; } /** * Its current angle around X axis, in degree. */ set pitch(value) { this._pitch = value; this._needsUpdate = true; } /** * @deprecated Use {@link getEuler} instead. */ get euler() { console.warn("VRMLookAt: euler is deprecated. use getEuler() instead."); return this.getEuler(new Euler()); } /** * Get its yaw-pitch angles as an `Euler`. * Does NOT consider {@link faceFront}; it returns `Euler(0, 0, 0; "YXZ")` by default regardless of the faceFront value. * * @param target The target euler */ getEuler(target) { return target.set(MathUtils.DEG2RAD * this._pitch, MathUtils.DEG2RAD * this._yaw, 0, "YXZ"); } /** * Copy the given {@link VRMLookAt} into this one. * {@link humanoid} must be same as the source one. * {@link applier} will reference the same instance as the source one. * @param source The {@link VRMLookAt} you want to copy * @returns this */ copy(source) { if (this.humanoid !== source.humanoid) throw new Error("VRMLookAt: humanoid must be same in order to copy"); this.offsetFromHeadBone.copy(source.offsetFromHeadBone); this.applier = source.applier; this.autoUpdate = source.autoUpdate; this.target = source.target; this.faceFront.copy(source.faceFront); return this; } /** * Returns a clone of this {@link VRMLookAt}. * Note that {@link humanoid} and {@link applier} will reference the same instance as this one. * @returns Copied {@link VRMLookAt} */ clone() { return new _VRMLookAt2(this.humanoid, this.applier).copy(this); } /** * Reset the lookAt direction (yaw and pitch) to the initial direction. */ reset() { this._yaw = 0; this._pitch = 0; this._needsUpdate = true; } /** * Get its lookAt position in world coordinate. * * @param target A target `THREE.Vector3` */ getLookAtWorldPosition(target) { const head = this.humanoid.getRawBoneNode("head"); return target.copy(this.offsetFromHeadBone).applyMatrix4(head.matrixWorld); } /** * Get its lookAt rotation in world coordinate. * Does NOT consider {@link faceFront}. * * @param target A target `THREE.Quaternion` */ getLookAtWorldQuaternion(target) { return getWorldQuaternionLite(this.humanoid.getRawBoneNode("head"), target); } /** * Get a quaternion that rotates the +Z unit vector of the humanoid Head to the {@link faceFront} direction. * * @param target A target `THREE.Quaternion` */ getFaceFrontQuaternion(target) { if (this.faceFront.distanceToSquared(VEC3_POSITIVE_Z) < .01) return target.copy(this._restHeadWorldQuaternion).invert(); const [faceFrontAzimuth, faceFrontAltitude] = calcAzimuthAltitude(this.faceFront); _eulerA.set(0, .5 * Math.PI + faceFrontAzimuth, faceFrontAltitude, "YZX"); return target.setFromEuler(_eulerA).premultiply(_quatD.copy(this._restHeadWorldQuaternion).invert()); } /** * Get its LookAt direction in world coordinate. * * @param target A target `THREE.Vector3` */ getLookAtWorldDirection(target) { this.getLookAtWorldQuaternion(_quatB2); this.getFaceFrontQuaternion(_quatC); return target.copy(VEC3_POSITIVE_Z).applyQuaternion(_quatB2).applyQuaternion(_quatC).applyEuler(this.getEuler(_eulerA)); } /** * Set its lookAt target position. * * Note that its result will be instantly overwritten if {@link VRMLookAtHead.autoUpdate} is enabled. * * If you want to track an object continuously, you might want to use {@link target} instead. * * @param position A target position, in world space */ lookAt(position) { const headRotDiffInv = _quatA5.copy(this._restHeadWorldQuaternion).multiply(quatInvertCompat(this.getLookAtWorldQuaternion(_quatB2))); const headPos = this.getLookAtWorldPosition(_v3B3); const lookAtDir = _v3C.copy(position).sub(headPos).applyQuaternion(headRotDiffInv).normalize(); const [azimuthFrom, altitudeFrom] = calcAzimuthAltitude(this.faceFront); const [azimuthTo, altitudeTo] = calcAzimuthAltitude(lookAtDir); const yaw = sanitizeAngle(azimuthTo - azimuthFrom); const pitch = sanitizeAngle(altitudeFrom - altitudeTo); this._yaw = MathUtils.RAD2DEG * yaw; this._pitch = MathUtils.RAD2DEG * pitch; this._needsUpdate = true; } /** * Update the VRMLookAtHead. * If {@link autoUpdate} is enabled, this will make it look at the {@link target}. * * @param delta deltaTime, it isn't used though. You can use the parameter if you want to use this in your own extended {@link VRMLookAt}. */ update(delta) { if (this.target != null && this.autoUpdate) this.lookAt(this.target.getWorldPosition(_v3A5)); if (this._needsUpdate) { this._needsUpdate = false; this.applier.applyYawPitch(this._yaw, this._pitch); } } }; _VRMLookAt.EULER_ORDER = "YXZ"; var VRMLookAt = _VRMLookAt; var VEC3_POSITIVE_Z2 = new Vector3(0, 0, 1); var _quatA6 = new Quaternion(); var _quatB3 = new Quaternion(); var _eulerA2 = new Euler(0, 0, 0, "YXZ"); var VRMLookAtBoneApplier = class { /** * Create a new {@link VRMLookAtBoneApplier}. * * @param humanoid A {@link VRMHumanoid} * @param rangeMapHorizontalInner A {@link VRMLookAtRangeMap} used for inner transverse direction * @param rangeMapHorizontalOuter A {@link VRMLookAtRangeMap} used for outer transverse direction * @param rangeMapVerticalDown A {@link VRMLookAtRangeMap} used for down direction * @param rangeMapVerticalUp A {@link VRMLookAtRangeMap} used for up direction */ constructor(humanoid, rangeMapHorizontalInner, rangeMapHorizontalOuter, rangeMapVerticalDown, rangeMapVerticalUp) { this.humanoid = humanoid; this.rangeMapHorizontalInner = rangeMapHorizontalInner; this.rangeMapHorizontalOuter = rangeMapHorizontalOuter; this.rangeMapVerticalDown = rangeMapVerticalDown; this.rangeMapVerticalUp = rangeMapVerticalUp; this.faceFront = new Vector3(0, 0, 1); this._restQuatLeftEye = new Quaternion(); this._restQuatRightEye = new Quaternion(); this._restLeftEyeParentWorldQuat = new Quaternion(); this._restRightEyeParentWorldQuat = new Quaternion(); const leftEye = this.humanoid.getRawBoneNode("leftEye"); const rightEye = this.humanoid.getRawBoneNode("rightEye"); if (leftEye) { this._restQuatLeftEye.copy(leftEye.quaternion); getWorldQuaternionLite(leftEye.parent, this._restLeftEyeParentWorldQuat); } if (rightEye) { this._restQuatRightEye.copy(rightEye.quaternion); getWorldQuaternionLite(rightEye.parent, this._restRightEyeParentWorldQuat); } } /** * Apply the input angle to its associated VRM model. * * @param yaw Rotation around Y axis, in degree * @param pitch Rotation around X axis, in degree */ applyYawPitch(yaw, pitch) { const leftEye = this.humanoid.getRawBoneNode("leftEye"); const rightEye = this.humanoid.getRawBoneNode("rightEye"); const leftEyeNormalized = this.humanoid.getNormalizedBoneNode("leftEye"); const rightEyeNormalized = this.humanoid.getNormalizedBoneNode("rightEye"); if (leftEye) { if (pitch < 0) _eulerA2.x = -MathUtils.DEG2RAD * this.rangeMapVerticalDown.map(-pitch); else _eulerA2.x = MathUtils.DEG2RAD * this.rangeMapVerticalUp.map(pitch); if (yaw < 0) _eulerA2.y = -MathUtils.DEG2RAD * this.rangeMapHorizontalInner.map(-yaw); else _eulerA2.y = MathUtils.DEG2RAD * this.rangeMapHorizontalOuter.map(yaw); _quatA6.setFromEuler(_eulerA2); this._getWorldFaceFrontQuat(_quatB3); leftEyeNormalized.quaternion.copy(_quatB3).multiply(_quatA6).multiply(_quatB3.invert()); _quatA6.copy(this._restLeftEyeParentWorldQuat); leftEye.quaternion.copy(leftEyeNormalized.quaternion).multiply(_quatA6).premultiply(_quatA6.invert()).multiply(this._restQuatLeftEye); } if (rightEye) { if (pitch < 0) _eulerA2.x = -MathUtils.DEG2RAD * this.rangeMapVerticalDown.map(-pitch); else _eulerA2.x = MathUtils.DEG2RAD * this.rangeMapVerticalUp.map(pitch); if (yaw < 0) _eulerA2.y = -MathUtils.DEG2RAD * this.rangeMapHorizontalOuter.map(-yaw); else _eulerA2.y = MathUtils.DEG2RAD * this.rangeMapHorizontalInner.map(yaw); _quatA6.setFromEuler(_eulerA2); this._getWorldFaceFrontQuat(_quatB3); rightEyeNormalized.quaternion.copy(_quatB3).multiply(_quatA6).multiply(_quatB3.invert()); _quatA6.copy(this._restRightEyeParentWorldQuat); rightEye.quaternion.copy(rightEyeNormalized.quaternion).multiply(_quatA6).premultiply(_quatA6.invert()).multiply(this._restQuatRightEye); } } /** * @deprecated Use {@link applyYawPitch} instead. */ lookAt(euler) { console.warn("VRMLookAtBoneApplier: lookAt() is deprecated. use apply() instead."); const yaw = MathUtils.RAD2DEG * euler.y; const pitch = MathUtils.RAD2DEG * euler.x; this.applyYawPitch(yaw, pitch); } /** * Get a quaternion that rotates the world-space +Z unit vector to the {@link faceFront} direction. * * @param target A target `THREE.Quaternion` */ _getWorldFaceFrontQuat(target) { if (this.faceFront.distanceToSquared(VEC3_POSITIVE_Z2) < .01) return target.identity(); const [faceFrontAzimuth, faceFrontAltitude] = calcAzimuthAltitude(this.faceFront); _eulerA2.set(0, .5 * Math.PI + faceFrontAzimuth, faceFrontAltitude, "YZX"); return target.setFromEuler(_eulerA2); } }; VRMLookAtBoneApplier.type = "bone"; var VRMLookAtExpressionApplier = class { /** * Create a new {@link VRMLookAtExpressionApplier}. * * @param expressions A {@link VRMExpressionManager} * @param rangeMapHorizontalInner A {@link VRMLookAtRangeMap} used for inner transverse direction * @param rangeMapHorizontalOuter A {@link VRMLookAtRangeMap} used for outer transverse direction * @param rangeMapVerticalDown A {@link VRMLookAtRangeMap} used for down direction * @param rangeMapVerticalUp A {@link VRMLookAtRangeMap} used for up direction */ constructor(expressions, rangeMapHorizontalInner, rangeMapHorizontalOuter, rangeMapVerticalDown, rangeMapVerticalUp) { this.expressions = expressions; this.rangeMapHorizontalInner = rangeMapHorizontalInner; this.rangeMapHorizontalOuter = rangeMapHorizontalOuter; this.rangeMapVerticalDown = rangeMapVerticalDown; this.rangeMapVerticalUp = rangeMapVerticalUp; } /** * Apply the input angle to its associated VRM model. * * @param yaw Rotation around Y axis, in degree * @param pitch Rotation around X axis, in degree */ applyYawPitch(yaw, pitch) { if (pitch < 0) { this.expressions.setValue("lookDown", 0); this.expressions.setValue("lookUp", this.rangeMapVerticalUp.map(-pitch)); } else { this.expressions.setValue("lookUp", 0); this.expressions.setValue("lookDown", this.rangeMapVerticalDown.map(pitch)); } if (yaw < 0) { this.expressions.setValue("lookLeft", 0); this.expressions.setValue("lookRight", this.rangeMapHorizontalOuter.map(-yaw)); } else { this.expressions.setValue("lookRight", 0); this.expressions.setValue("lookLeft", this.rangeMapHorizontalOuter.map(yaw)); } } /** * @deprecated Use {@link applyYawPitch} instead. */ lookAt(euler) { console.warn("VRMLookAtBoneApplier: lookAt() is deprecated. use apply() instead."); const yaw = MathUtils.RAD2DEG * euler.y; const pitch = MathUtils.RAD2DEG * euler.x; this.applyYawPitch(yaw, pitch); } }; VRMLookAtExpressionApplier.type = "expression"; var RAD2DEG = 180 / Math.PI; var _eulerA3 = /* @__PURE__ */ new Euler(); var VRMLookAtQuaternionProxy = class extends Object3D { constructor(lookAt) { super(); this.vrmLookAt = lookAt; this.type = "VRMLookAtQuaternionProxy"; const prevRotationOnChangeCallback = this.rotation._onChangeCallback; this.rotation._onChange(() => { prevRotationOnChangeCallback(); this._applyToLookAt(); }); const prevQuaternionOnChangeCallback = this.quaternion._onChangeCallback; this.quaternion._onChange(() => { prevQuaternionOnChangeCallback(); this._applyToLookAt(); }); } _applyToLookAt() { _eulerA3.setFromQuaternion(this.quaternion, VRMLookAt.EULER_ORDER); this.vrmLookAt.yaw = RAD2DEG * _eulerA3.y; this.vrmLookAt.pitch = RAD2DEG * _eulerA3.x; } }; function createVRMAnimationHumanoidTracks(vrmAnimation, humanoid, metaVersion) { var _a, _b; const translation = /* @__PURE__ */ new Map(); const rotation = /* @__PURE__ */ new Map(); for (const [name, origTrack] of vrmAnimation.humanoidTracks.rotation.entries()) { const nodeName = (_a = humanoid.getNormalizedBoneNode(name)) == null ? void 0 : _a.name; if (nodeName != null) { const track = new QuaternionKeyframeTrack(`${nodeName}.quaternion`, origTrack.times, origTrack.values.map((v, i) => metaVersion === "0" && i % 2 === 0 ? -v : v)); rotation.set(name, track); } } for (const [name, origTrack] of vrmAnimation.humanoidTracks.translation.entries()) { const nodeName = (_b = humanoid.getNormalizedBoneNode(name)) == null ? void 0 : _b.name; if (nodeName != null) { const animationY = vrmAnimation.restHipsPosition.y; const scale = humanoid.normalizedRestPose.hips.position[1] / animationY; const track = origTrack.clone(); track.values = track.values.map((v, i) => (metaVersion === "0" && i % 3 !== 1 ? -v : v) * scale); track.name = `${nodeName}.position`; translation.set(name, track); } } return { translation, rotation }; } function createVRMAnimationExpressionTracks(vrmAnimation, expressionManager) { const preset = /* @__PURE__ */ new Map(); const custom = /* @__PURE__ */ new Map(); for (const [name, origTrack] of vrmAnimation.expressionTracks.preset.entries()) { const trackName = expressionManager.getExpressionTrackName(name); if (trackName != null) { const track = origTrack.clone(); track.name = trackName; preset.set(name, track); } } for (const [name, origTrack] of vrmAnimation.expressionTracks.custom.entries()) { const trackName = expressionManager.getExpressionTrackName(name); if (trackName != null) { const track = origTrack.clone(); track.name = trackName; custom.set(name, track); } } return { preset, custom }; } function createVRMAnimationLookAtTrack(vrmAnimation, trackName) { if (vrmAnimation.lookAtTrack == null) return null; const track = vrmAnimation.lookAtTrack.clone(); track.name = trackName; return track; } function createVRMAnimationClip(vrmAnimation, vrm) { const tracks = []; const humanoidTracks = createVRMAnimationHumanoidTracks(vrmAnimation, vrm.humanoid, vrm.meta.metaVersion); tracks.push(...humanoidTracks.translation.values()); tracks.push(...humanoidTracks.rotation.values()); if (vrm.expressionManager != null) { const expressionTracks = createVRMAnimationExpressionTracks(vrmAnimation, vrm.expressionManager); tracks.push(...expressionTracks.preset.values()); tracks.push(...expressionTracks.custom.values()); } if (vrm.lookAt != null) { let proxy = vrm.scene.children.find((obj) => obj instanceof VRMLookAtQuaternionProxy); if (proxy == null) { console.warn("createVRMAnimationClip: VRMLookAtQuaternionProxy is not found. Creating a new one automatically. To suppress this warning, create a VRMLookAtQuaternionProxy manually"); proxy = new VRMLookAtQuaternionProxy(vrm.lookAt); proxy.name = "VRMLookAtQuaternionProxy"; vrm.scene.add(proxy); } else if (proxy.name === "") { console.warn("createVRMAnimationClip: VRMLookAtQuaternionProxy is found but its name is not set. Setting the name automatically. To suppress this warning, set the name manually"); proxy.name = "VRMLookAtQuaternionProxy"; } const track = createVRMAnimationLookAtTrack(vrmAnimation, `${proxy.name}.quaternion`); if (track != null) tracks.push(track); } return new AnimationClip("Clip", vrmAnimation.duration, tracks); } var VRMAnimation = class { constructor() { this.duration = 0; this.restHipsPosition = new Vector3(); this.humanoidTracks = { translation: /* @__PURE__ */ new Map(), rotation: /* @__PURE__ */ new Map() }; this.expressionTracks = { preset: /* @__PURE__ */ new Map(), custom: /* @__PURE__ */ new Map() }; this.lookAtTrack = null; } }; function arrayChunk(array, every) { const N = array.length; const ret = []; let current = []; let remaining = 0; for (let i = 0; i < N; i++) { const el = array[i]; if (remaining <= 0) { remaining = every; current = []; ret.push(current); } current.push(el); remaining--; } return ret; } var MAT4_IDENTITY = /* @__PURE__ */ new Matrix4(); var _v3A6 = /* @__PURE__ */ new Vector3(); var _quatA7 = /* @__PURE__ */ new Quaternion(); var _quatB4 = /* @__PURE__ */ new Quaternion(); var _quatC2 = /* @__PURE__ */ new Quaternion(); var POSSIBLE_SPEC_VERSIONS2 = /* @__PURE__ */ new Set(["1.0", "1.0-draft"]); var vrmExpressionPresetNameSet = /* @__PURE__ */ new Set(Object.values(VRMExpressionPresetName)); var VRMAnimationLoaderPlugin = class { constructor(parser) { this.parser = parser; } get name() { return "VRMC_vrm_animation"; } afterRoot(gltf) { return __async(this, null, function* () { var _a, _b, _c; const defGltf = gltf.parser.json; const defExtensionsUsed = defGltf.extensionsUsed; if (defExtensionsUsed == null || defExtensionsUsed.indexOf(this.name) == -1) return; const defExtension = (_a = defGltf.extensions) == null ? void 0 : _a[this.name]; if (defExtension == null) return; const specVersion = defExtension.specVersion; if (specVersion == null) console.warn("VRMAnimationLoaderPlugin: specVersion of the VRMA is not defined. Consider updating the animation file. Assuming the spec version is 1.0."); else { if (!POSSIBLE_SPEC_VERSIONS2.has(specVersion)) { console.warn(`VRMAnimationLoaderPlugin: Unknown VRMC_vrm_animation spec version: ${specVersion}`); return; } if (specVersion === "1.0-draft") console.warn("VRMAnimationLoaderPlugin: Using a draft spec version: 1.0-draft. Some behaviors may be different. Consider updating the animation file."); } const nodeMap = this._createNodeMap(defExtension); const worldMatrixMap = yield this._createBoneWorldMatrixMap(gltf, defExtension); const hipsNode = (_c = (_b = defExtension.humanoid) == null ? void 0 : _b.humanBones["hips"]) == null ? void 0 : _c.node; const hips = hipsNode != null ? yield gltf.parser.getDependency("node", hipsNode) : null; const restHipsPosition = new Vector3(); hips?.getWorldPosition(restHipsPosition); if (restHipsPosition.y < .001) console.warn("VRMAnimationLoaderPlugin: The loaded VRM Animation might violate the VRM T-pose (The y component of the rest hips position is approximately zero or below.)"); const animations = gltf.animations.map((clip, iAnimation) => { const defAnimation = defGltf.animations[iAnimation]; const animation = this._parseAnimation(clip, defAnimation, nodeMap, worldMatrixMap); animation.restHipsPosition = restHipsPosition; return animation; }); gltf.userData.vrmAnimations = animations; }); } _createNodeMap(defExtension) { var _a, _b, _c, _d, _e; const humanoidIndexToName = /* @__PURE__ */ new Map(); const expressionsIndexToName = /* @__PURE__ */ new Map(); const humanBones = (_a = defExtension.humanoid) == null ? void 0 : _a.humanBones; if (humanBones) Object.entries(humanBones).forEach(([name, bone]) => { const node = bone == null ? void 0 : bone.node; if (node != null) humanoidIndexToName.set(node, name); }); const preset = (_b = defExtension.expressions) == null ? void 0 : _b.preset; if (preset) Object.entries(preset).forEach(([name, expression]) => { const node = expression == null ? void 0 : expression.node; if (node != null) expressionsIndexToName.set(node, name); }); const custom = (_c = defExtension.expressions) == null ? void 0 : _c.custom; if (custom) Object.entries(custom).forEach(([name, expression]) => { const { node } = expression; expressionsIndexToName.set(node, name); }); return { humanoidIndexToName, expressionsIndexToName, lookAtIndex: (_e = (_d = defExtension.lookAt) == null ? void 0 : _d.node) != null ? _e : null }; } _createBoneWorldMatrixMap(gltf, defExtension) { return __async(this, null, function* () { var _a, _b; gltf.scene.updateWorldMatrix(false, true); const threeNodes = yield gltf.parser.getDependencies("node"); const worldMatrixMap = /* @__PURE__ */ new Map(); if (defExtension.humanoid == null) return worldMatrixMap; for (const [boneName, humanBone] of Object.entries(defExtension.humanoid.humanBones)) { const node = humanBone == null ? void 0 : humanBone.node; if (node != null) { const threeNode = threeNodes[node]; worldMatrixMap.set(boneName, threeNode.matrixWorld); if (boneName === "hips") worldMatrixMap.set("hipsParent", (_b = (_a = threeNode.parent) == null ? void 0 : _a.matrixWorld) != null ? _b : MAT4_IDENTITY); } } return worldMatrixMap; }); } _parseAnimation(animationClip, defAnimation, nodeMap, worldMatrixMap) { const tracks = animationClip.tracks; const defChannels = defAnimation.channels; const result = new VRMAnimation(); result.duration = animationClip.duration; defChannels.forEach((channel, iChannel) => { const { node, path } = channel.target; const origTrack = tracks[iChannel]; if (node == null) return; const boneName = nodeMap.humanoidIndexToName.get(node); if (boneName != null) { let parentBoneName = VRMHumanBoneParentMap[boneName]; while (parentBoneName != null && worldMatrixMap.get(parentBoneName) == null) parentBoneName = VRMHumanBoneParentMap[parentBoneName]; if (parentBoneName == null) parentBoneName = "hipsParent"; if (path === "translation") if (boneName !== "hips") console.warn(`The loading animation contains a translation track for ${boneName}, which is not permitted in the VRMC_vrm_animation spec. ignoring the track`); else { const hipsParentWorldMatrix = worldMatrixMap.get("hipsParent"); const trackValues = arrayChunk(origTrack.values, 3).flatMap((v) => _v3A6.fromArray(v).applyMatrix4(hipsParentWorldMatrix).toArray()); const track = origTrack.clone(); track.values = new Float32Array(trackValues); result.humanoidTracks.translation.set(boneName, track); } else if (path === "rotation") { const worldMatrix = worldMatrixMap.get(boneName); const parentWorldMatrix = worldMatrixMap.get(parentBoneName); worldMatrix.decompose(_v3A6, _quatA7, _v3A6); _quatA7.invert(); parentWorldMatrix.decompose(_v3A6, _quatB4, _v3A6); const trackValues = arrayChunk(origTrack.values, 4).flatMap((q) => _quatC2.fromArray(q).premultiply(_quatB4).multiply(_quatA7).toArray()); const track = origTrack.clone(); track.values = new Float32Array(trackValues); result.humanoidTracks.rotation.set(boneName, track); } else throw new Error(`Invalid path "${path}"`); return; } const expressionName = nodeMap.expressionsIndexToName.get(node); if (expressionName != null) { if (path === "translation") { const times = origTrack.times; const values = new Float32Array(origTrack.values.length / 3); for (let i = 0; i < values.length; i++) values[i] = origTrack.values[3 * i]; const newTrack = new NumberKeyframeTrack(`${expressionName}.weight`, times, values); if (vrmExpressionPresetNameSet.has(expressionName)) result.expressionTracks.preset.set(expressionName, newTrack); else result.expressionTracks.custom.set(expressionName, newTrack); } else throw new Error(`Invalid path "${path}"`); return; } if (node === nodeMap.lookAtIndex) if (path === "rotation") result.lookAtTrack = origTrack; else throw new Error(`Invalid path "${path}"`); }); return result; } }; /*! * @pixiv/three-vrm-core v3.4.1 * The implementation of core features of VRM, for @pixiv/three-vrm * * Copyright (c) 2019-2025 pixiv Inc. * @pixiv/three-vrm-core is distributed under MIT License * https://github.com/pixiv/three-vrm/blob/release/LICENSE */ //#endregion export { VRMAnimation, VRMAnimationLoaderPlugin, VRMLookAtQuaternionProxy, createVRMAnimationClip, createVRMAnimationExpressionTracks, createVRMAnimationHumanoidTracks, createVRMAnimationLookAtTrack }; //# sourceMappingURL=@pixiv_three-vrm-animation.js.map