@pixiv/three-vrm
Version:
VRM file loader for three.js.
1,388 lines (1,385 loc) • 958 kB
JavaScript
/*!
* @pixiv/three-vrm v3.4.0
* VRM file loader for three.js.
*
* Copyright (c) 2019-2025 pixiv Inc.
* @pixiv/three-vrm is distributed under MIT License
* https://github.com/pixiv/three-vrm/blob/release/LICENSE
*/
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
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());
});
};
// src/index.ts
var src_exports = {};
__export(src_exports, {
MToonMaterial: () => MToonMaterial,
MToonMaterialDebugMode: () => MToonMaterialDebugMode,
MToonMaterialLoaderPlugin: () => MToonMaterialLoaderPlugin,
MToonMaterialOutlineWidthMode: () => MToonMaterialOutlineWidthMode,
VRM: () => VRM,
VRMAimConstraint: () => VRMAimConstraint,
VRMCore: () => VRMCore,
VRMCoreLoaderPlugin: () => VRMCoreLoaderPlugin,
VRMExpression: () => VRMExpression,
VRMExpressionLoaderPlugin: () => VRMExpressionLoaderPlugin,
VRMExpressionManager: () => VRMExpressionManager,
VRMExpressionMaterialColorBind: () => VRMExpressionMaterialColorBind,
VRMExpressionMaterialColorType: () => VRMExpressionMaterialColorType,
VRMExpressionMorphTargetBind: () => VRMExpressionMorphTargetBind,
VRMExpressionOverrideType: () => VRMExpressionOverrideType,
VRMExpressionPresetName: () => VRMExpressionPresetName,
VRMExpressionTextureTransformBind: () => VRMExpressionTextureTransformBind,
VRMFirstPerson: () => VRMFirstPerson,
VRMFirstPersonLoaderPlugin: () => VRMFirstPersonLoaderPlugin,
VRMFirstPersonMeshAnnotationType: () => VRMFirstPersonMeshAnnotationType,
VRMHumanBoneList: () => VRMHumanBoneList,
VRMHumanBoneName: () => VRMHumanBoneName,
VRMHumanBoneParentMap: () => VRMHumanBoneParentMap,
VRMHumanoid: () => VRMHumanoid,
VRMHumanoidHelper: () => VRMHumanoidHelper,
VRMHumanoidLoaderPlugin: () => VRMHumanoidLoaderPlugin,
VRMLoaderPlugin: () => VRMLoaderPlugin,
VRMLookAt: () => VRMLookAt,
VRMLookAtBoneApplier: () => VRMLookAtBoneApplier,
VRMLookAtExpressionApplier: () => VRMLookAtExpressionApplier,
VRMLookAtHelper: () => VRMLookAtHelper,
VRMLookAtLoaderPlugin: () => VRMLookAtLoaderPlugin,
VRMLookAtRangeMap: () => VRMLookAtRangeMap,
VRMLookAtTypeName: () => VRMLookAtTypeName,
VRMMetaLoaderPlugin: () => VRMMetaLoaderPlugin,
VRMNodeConstraint: () => VRMNodeConstraint,
VRMNodeConstraintHelper: () => VRMNodeConstraintHelper,
VRMNodeConstraintLoaderPlugin: () => VRMNodeConstraintLoaderPlugin,
VRMNodeConstraintManager: () => VRMNodeConstraintManager,
VRMRequiredHumanBoneName: () => VRMRequiredHumanBoneName,
VRMRollConstraint: () => VRMRollConstraint,
VRMRotationConstraint: () => VRMRotationConstraint,
VRMSpringBoneCollider: () => VRMSpringBoneCollider,
VRMSpringBoneColliderHelper: () => VRMSpringBoneColliderHelper,
VRMSpringBoneColliderShape: () => VRMSpringBoneColliderShape,
VRMSpringBoneColliderShapeCapsule: () => VRMSpringBoneColliderShapeCapsule,
VRMSpringBoneColliderShapePlane: () => VRMSpringBoneColliderShapePlane,
VRMSpringBoneColliderShapeSphere: () => VRMSpringBoneColliderShapeSphere,
VRMSpringBoneJoint: () => VRMSpringBoneJoint,
VRMSpringBoneJointHelper: () => VRMSpringBoneJointHelper,
VRMSpringBoneLoaderPlugin: () => VRMSpringBoneLoaderPlugin,
VRMSpringBoneManager: () => VRMSpringBoneManager,
VRMUtils: () => VRMUtils
});
module.exports = __toCommonJS(src_exports);
// ../three-vrm-core/lib/three-vrm-core.module.js
var THREE = __toESM(require("three"), 1);
var THREE4 = __toESM(require("three"), 1);
var THREE2 = __toESM(require("three"), 1);
var THREE3 = __toESM(require("three"), 1);
var THREE5 = __toESM(require("three"), 1);
var THREE6 = __toESM(require("three"), 1);
var THREE7 = __toESM(require("three"), 1);
var THREE8 = __toESM(require("three"), 1);
var THREE11 = __toESM(require("three"), 1);
var THREE9 = __toESM(require("three"), 1);
var THREE10 = __toESM(require("three"), 1);
var THREE13 = __toESM(require("three"), 1);
var THREE12 = __toESM(require("three"), 1);
var THREE14 = __toESM(require("three"), 1);
var THREE15 = __toESM(require("three"), 1);
var THREE16 = __toESM(require("three"), 1);
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 THREE.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 > 0.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* () {
const node = yield gltf.parser.getDependency("node", nodeIndex);
return extractPrimitivesInternal(gltf, nodeIndex, node);
});
}
function gltfExtractPrimitivesFromNodes(gltf) {
return __async2(this, null, function* () {
const nodes = yield gltf.parser.getDependencies("node");
const map = /* @__PURE__ */ new Map();
nodes.forEach((node, index) => {
const result = extractPrimitivesInternal(gltf, index, node);
if (result != null) {
map.set(index, result);
}
});
return map;
});
}
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) {
const expressions = this._expressions.concat();
expressions.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 THREE2.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;
const target = this.material[propertyName];
if (target != 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;
const target = this.material[propertyName];
if (target != 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 target = material[propertyName];
const initialValue = target.clone();
const deltaValue = new THREE2.Color(
targetValue.r - initialValue.r,
targetValue.g - initialValue.g,
targetValue.b - initialValue.b
);
return { propertyName, initialValue, deltaValue };
}
_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];
const deltaValue = targetAlpha - initialValue;
return { propertyName, initialValue, deltaValue };
}
_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 THREE3.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;
const isVRMUsed = ((_a = json.extensionsUsed) == null ? void 0 : _a.indexOf("VRMC_vrm")) !== -1;
if (!isVRMUsed) {
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 ? void 0 : _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 ? void 0 : _f.forEach((bind) => __async2(this, null, function* () {
const materials = gltfMaterials.filter((material) => {
var _a3;
const materialIndex = (_a3 = this.parser.associations.get(material)) == null ? void 0 : _a3.materials;
return bind.material === materialIndex;
});
materials.forEach((material) => {
expression.addBind(
new VRMExpressionMaterialColorBind({
material,
type: bind.type,
targetValue: new THREE4.Color().fromArray(bind.targetValue),
targetAlpha: bind.targetValue[3]
})
);
});
}));
(_g = schemaExpression.textureTransformBinds) == null ? void 0 : _g.forEach((bind) => __async2(this, null, function* () {
const materials = gltfMaterials.filter((material) => {
var _a3;
const materialIndex = (_a3 = this.parser.associations.get(material)) == null ? void 0 : _a3.materials;
return bind.material === materialIndex;
});
materials.forEach((material) => {
var _a3, _b3;
expression.addBind(
new VRMExpressionTextureTransformBind({
material,
offset: new THREE4.Vector2().fromArray((_a3 = bind.offset) != null ? _a3 : [0, 0]),
scale: new THREE4.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 ? void 0 : _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: 0.01 * ((_a4 = bind.weight) != null ? _a4 : 100)
// narrowing the range from [ 0.0 - 100.0 ] to [ 0.0 - 1.0 ]
})
);
}))
);
}));
}
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 THREE4.Vector2(materialValue.targetValue[0], materialValue.targetValue[1]);
const offset = new THREE4.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 THREE4.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",
// eslint-disable-next-line @typescript-eslint/naming-convention
blink_l: "blinkLeft",
// eslint-disable-next-line @typescript-eslint/naming-convention
blink_r: "blinkRight",
neutral: "neutral"
};
var VRMExpressionLoaderPlugin = _VRMExpressionLoaderPlugin;
var VRMExpressionOverrideType = {
None: "none",
Block: "block",
Blend: "blend"
};
var _VRMFirstPerson = class _VRMFirstPerson2 {
/**
* Create a new VRMFirstPerson object.
*
* @param humanoid A {@link VRMHumanoid}
* @param meshAnnotations A renderer settings. See the description of [[RendererFirstPersonFlags]] for more info
*/
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 THREE5.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 THREE5.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 THREE5.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 THREE5.Skeleton(src.skeleton.bones, src.skeleton.boneInverses), new THREE5.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 THREE5.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;
var VRMFirstPerson = _VRMFirstPerson;
var POSSIBLE_SPEC_VERSIONS2 = /* @__PURE__ */ new Set(["1.0", "1.0-beta"]);
var VRMFirstPersonLoaderPlugin = class {
get name() {
return "VRMFirstPersonLoaderPlugin";
}
constructor(parser) {
this.parser = parser;
}
afterRoot(gltf) {
return __async2(this, null, function* () {
const vrmHumanoid = gltf.userData.vrmHumanoid;
if (vrmHumanoid === null) {
return;
} else if (vrmHumanoid === void 0) {
throw new Error(
"VRMFirstPersonLoaderPlugin: vrmHumanoid is undefined. VRMHumanoidLoaderPlugin have to be used first"
);
}
gltf.userData.vrmFirstPerson = yield this._import(gltf, vrmHumanoid);
});
}
/**
* Import a {@link VRMFirstPerson} from a VRM.
*
* @param gltf A parsed result of GLTF taken from GLTFLoader
* @param humanoid A {@link VRMHumanoid} instance that represents the VRM
*/
_import(gltf, humanoid) {
return __async2(this, null, function* () {
if (humanoid == null) {
return null;
}
const v1Result = yield this._v1Import(gltf, humanoid);
if (v1Result) {
return v1Result;
}
const v0Result = yield this._v0Import(gltf, humanoid);
if (v0Result) {
return v0Result;
}
return null;
});
}
_v1Import(gltf, humanoid) {
return __async2(this, null, function* () {
var _a, _b;
const json = this.parser.json;
const isVRMUsed = ((_a = json.extensionsUsed) == null ? void 0 : _a.indexOf("VRMC_vrm")) !== -1;
if (!isVRMUsed) {
return null;
}
const extension = (_b = json.extensions) == null ? void 0 : _b["VRMC_vrm"];
if (!extension) {
return null;
}
const specVersion = extension.specVersion;
if (!POSSIBLE_SPEC_VERSIONS2.has(specVersion)) {
console.warn(`VRMFirstPersonLoaderPlugin: Unknown VRMC_vrm specVersion "${specVersion}"`);
return null;
}
const schemaFirstPerson = extension.firstPerson;
const meshAnnotations = [];