6761 lines
943 KiB
JavaScript
6761 lines
943 KiB
JavaScript
|
|
/*!
|
||
|
|
* @pixiv/three-vrm v3.4.1
|
||
|
|
* 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 {@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 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 = [];
|
||
|
|
const nodePrimitivesMap = yield gltfExtractPrimitivesFromNodes(gltf);
|
||
|
|
Array.from(nodePrimitivesMap.entries()).forEach(([nodeIndex, primitives]) => {
|
||
|
|
var _a2, _b2;
|
||
|
|
const annotation = (_a2 = schemaFirstPerson == null ? void 0 : schemaFirstPerson.meshAnnotations) == null ? void 0 : _a2.find((a) => a.node === nodeIndex);
|
||
|
|
meshAnnotations.push({
|
||
|
|
meshes: primitives,
|
||
|
|
type: (_b2 = annotation == null ? void 0 : annotation.type) != null ? _b2 : "auto"
|
||
|
|
});
|
||
|
|
});
|
||
|
|
return new VRMFirstPerson(humanoid, meshAnnotations);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_v0Import(gltf, humanoid) {
|
||
|
|
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 schemaFirstPerson = vrmExt.firstPerson;
|
||
|
|
if (!schemaFirstPerson) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const meshAnnotations = [];
|
||
|
|
const nodePrimitivesMap = yield gltfExtractPrimitivesFromNodes(gltf);
|
||
|
|
Array.from(nodePrimitivesMap.entries()).forEach(([nodeIndex, primitives]) => {
|
||
|
|
const schemaNode = json.nodes[nodeIndex];
|
||
|
|
const flag = schemaFirstPerson.meshAnnotations ? schemaFirstPerson.meshAnnotations.find((a) => a.mesh === schemaNode.mesh) : void 0;
|
||
|
|
meshAnnotations.push({
|
||
|
|
meshes: primitives,
|
||
|
|
type: this._convertV0FlagToV1Type(flag == null ? void 0 : flag.firstPersonFlag)
|
||
|
|
});
|
||
|
|
});
|
||
|
|
return new VRMFirstPerson(humanoid, meshAnnotations);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_convertV0FlagToV1Type(flag) {
|
||
|
|
if (flag === "FirstPersonOnly") {
|
||
|
|
return "firstPersonOnly";
|
||
|
|
} else if (flag === "ThirdPersonOnly") {
|
||
|
|
return "thirdPersonOnly";
|
||
|
|
} else if (flag === "Both") {
|
||
|
|
return "both";
|
||
|
|
} else {
|
||
|
|
return "auto";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var VRMFirstPersonMeshAnnotationType = {
|
||
|
|
Auto: "auto",
|
||
|
|
Both: "both",
|
||
|
|
ThirdPersonOnly: "thirdPersonOnly",
|
||
|
|
FirstPersonOnly: "firstPersonOnly"
|
||
|
|
};
|
||
|
|
var _v3A = new THREE6.Vector3();
|
||
|
|
var _v3B = new THREE6.Vector3();
|
||
|
|
var _quatA = new THREE6.Quaternion();
|
||
|
|
var VRMHumanoidHelper = class extends THREE6.Group {
|
||
|
|
constructor(humanoid) {
|
||
|
|
super();
|
||
|
|
this.vrmHumanoid = humanoid;
|
||
|
|
this._boneAxesMap = /* @__PURE__ */ new Map();
|
||
|
|
Object.values(humanoid.humanBones).forEach((bone) => {
|
||
|
|
const helper = new THREE6.AxesHelper(1);
|
||
|
|
helper.matrixAutoUpdate = false;
|
||
|
|
helper.material.depthTest = false;
|
||
|
|
helper.material.depthWrite = false;
|
||
|
|
this.add(helper);
|
||
|
|
this._boneAxesMap.set(bone, helper);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
dispose() {
|
||
|
|
Array.from(this._boneAxesMap.values()).forEach((axes) => {
|
||
|
|
axes.geometry.dispose();
|
||
|
|
axes.material.dispose();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
updateMatrixWorld(force) {
|
||
|
|
Array.from(this._boneAxesMap.entries()).forEach(([bone, axes]) => {
|
||
|
|
bone.node.updateWorldMatrix(true, false);
|
||
|
|
bone.node.matrixWorld.decompose(_v3A, _quatA, _v3B);
|
||
|
|
const scale = _v3A.set(0.1, 0.1, 0.1).divide(_v3B);
|
||
|
|
axes.matrix.copy(bone.node.matrixWorld).scale(scale);
|
||
|
|
});
|
||
|
|
super.updateMatrixWorld(force);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var VRMHumanBoneList = [
|
||
|
|
"hips",
|
||
|
|
"spine",
|
||
|
|
"chest",
|
||
|
|
"upperChest",
|
||
|
|
"neck",
|
||
|
|
"head",
|
||
|
|
"leftEye",
|
||
|
|
"rightEye",
|
||
|
|
"jaw",
|
||
|
|
"leftUpperLeg",
|
||
|
|
"leftLowerLeg",
|
||
|
|
"leftFoot",
|
||
|
|
"leftToes",
|
||
|
|
"rightUpperLeg",
|
||
|
|
"rightLowerLeg",
|
||
|
|
"rightFoot",
|
||
|
|
"rightToes",
|
||
|
|
"leftShoulder",
|
||
|
|
"leftUpperArm",
|
||
|
|
"leftLowerArm",
|
||
|
|
"leftHand",
|
||
|
|
"rightShoulder",
|
||
|
|
"rightUpperArm",
|
||
|
|
"rightLowerArm",
|
||
|
|
"rightHand",
|
||
|
|
"leftThumbMetacarpal",
|
||
|
|
"leftThumbProximal",
|
||
|
|
"leftThumbDistal",
|
||
|
|
"leftIndexProximal",
|
||
|
|
"leftIndexIntermediate",
|
||
|
|
"leftIndexDistal",
|
||
|
|
"leftMiddleProximal",
|
||
|
|
"leftMiddleIntermediate",
|
||
|
|
"leftMiddleDistal",
|
||
|
|
"leftRingProximal",
|
||
|
|
"leftRingIntermediate",
|
||
|
|
"leftRingDistal",
|
||
|
|
"leftLittleProximal",
|
||
|
|
"leftLittleIntermediate",
|
||
|
|
"leftLittleDistal",
|
||
|
|
"rightThumbMetacarpal",
|
||
|
|
"rightThumbProximal",
|
||
|
|
"rightThumbDistal",
|
||
|
|
"rightIndexProximal",
|
||
|
|
"rightIndexIntermediate",
|
||
|
|
"rightIndexDistal",
|
||
|
|
"rightMiddleProximal",
|
||
|
|
"rightMiddleIntermediate",
|
||
|
|
"rightMiddleDistal",
|
||
|
|
"rightRingProximal",
|
||
|
|
"rightRingIntermediate",
|
||
|
|
"rightRingDistal",
|
||
|
|
"rightLittleProximal",
|
||
|
|
"rightLittleIntermediate",
|
||
|
|
"rightLittleDistal"
|
||
|
|
];
|
||
|
|
var VRMHumanBoneName = {
|
||
|
|
Hips: "hips",
|
||
|
|
Spine: "spine",
|
||
|
|
Chest: "chest",
|
||
|
|
UpperChest: "upperChest",
|
||
|
|
Neck: "neck",
|
||
|
|
Head: "head",
|
||
|
|
LeftEye: "leftEye",
|
||
|
|
RightEye: "rightEye",
|
||
|
|
Jaw: "jaw",
|
||
|
|
LeftUpperLeg: "leftUpperLeg",
|
||
|
|
LeftLowerLeg: "leftLowerLeg",
|
||
|
|
LeftFoot: "leftFoot",
|
||
|
|
LeftToes: "leftToes",
|
||
|
|
RightUpperLeg: "rightUpperLeg",
|
||
|
|
RightLowerLeg: "rightLowerLeg",
|
||
|
|
RightFoot: "rightFoot",
|
||
|
|
RightToes: "rightToes",
|
||
|
|
LeftShoulder: "leftShoulder",
|
||
|
|
LeftUpperArm: "leftUpperArm",
|
||
|
|
LeftLowerArm: "leftLowerArm",
|
||
|
|
LeftHand: "leftHand",
|
||
|
|
RightShoulder: "rightShoulder",
|
||
|
|
RightUpperArm: "rightUpperArm",
|
||
|
|
RightLowerArm: "rightLowerArm",
|
||
|
|
RightHand: "rightHand",
|
||
|
|
LeftThumbMetacarpal: "leftThumbMetacarpal",
|
||
|
|
LeftThumbProximal: "leftThumbProximal",
|
||
|
|
LeftThumbDistal: "leftThumbDistal",
|
||
|
|
LeftIndexProximal: "leftIndexProximal",
|
||
|
|
LeftIndexIntermediate: "leftIndexIntermediate",
|
||
|
|
LeftIndexDistal: "leftIndexDistal",
|
||
|
|
LeftMiddleProximal: "leftMiddleProximal",
|
||
|
|
LeftMiddleIntermediate: "leftMiddleIntermediate",
|
||
|
|
LeftMiddleDistal: "leftMiddleDistal",
|
||
|
|
LeftRingProximal: "leftRingProximal",
|
||
|
|
LeftRingIntermediate: "leftRingIntermediate",
|
||
|
|
LeftRingDistal: "leftRingDistal",
|
||
|
|
LeftLittleProximal: "leftLittleProximal",
|
||
|
|
LeftLittleIntermediate: "leftLittleIntermediate",
|
||
|
|
LeftLittleDistal: "leftLittleDistal",
|
||
|
|
RightThumbMetacarpal: "rightThumbMetacarpal",
|
||
|
|
RightThumbProximal: "rightThumbProximal",
|
||
|
|
RightThumbDistal: "rightThumbDistal",
|
||
|
|
RightIndexProximal: "rightIndexProximal",
|
||
|
|
RightIndexIntermediate: "rightIndexIntermediate",
|
||
|
|
RightIndexDistal: "rightIndexDistal",
|
||
|
|
RightMiddleProximal: "rightMiddleProximal",
|
||
|
|
RightMiddleIntermediate: "rightMiddleIntermediate",
|
||
|
|
RightMiddleDistal: "rightMiddleDistal",
|
||
|
|
RightRingProximal: "rightRingProximal",
|
||
|
|
RightRingIntermediate: "rightRingIntermediate",
|
||
|
|
RightRingDistal: "rightRingDistal",
|
||
|
|
RightLittleProximal: "rightLittleProximal",
|
||
|
|
RightLittleIntermediate: "rightLittleIntermediate",
|
||
|
|
RightLittleDistal: "rightLittleDistal"
|
||
|
|
};
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
var _v3A2 = new THREE7.Vector3();
|
||
|
|
var _quatA2 = new THREE7.Quaternion();
|
||
|
|
var VRMRig = class {
|
||
|
|
/**
|
||
|
|
* Create a new {@link VRMHumanoid}.
|
||
|
|
* @param humanBones A {@link VRMHumanBones} contains all the bones of the new humanoid
|
||
|
|
*/
|
||
|
|
constructor(humanBones) {
|
||
|
|
this.humanBones = humanBones;
|
||
|
|
this.restPose = this.getAbsolutePose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return the current absolute pose of this humanoid as a {@link VRMPose}.
|
||
|
|
* Note that the output result will contain initial state of the VRM and not compatible between different models.
|
||
|
|
* You might want to use {@link getPose} instead.
|
||
|
|
*/
|
||
|
|
getAbsolutePose() {
|
||
|
|
const pose = {};
|
||
|
|
Object.keys(this.humanBones).forEach((vrmBoneNameString) => {
|
||
|
|
const vrmBoneName = vrmBoneNameString;
|
||
|
|
const node = this.getBoneNode(vrmBoneName);
|
||
|
|
if (!node) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
_v3A2.copy(node.position);
|
||
|
|
_quatA2.copy(node.quaternion);
|
||
|
|
pose[vrmBoneName] = {
|
||
|
|
position: _v3A2.toArray(),
|
||
|
|
rotation: _quatA2.toArray()
|
||
|
|
};
|
||
|
|
});
|
||
|
|
return pose;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return the current pose of this humanoid as a {@link VRMPose}.
|
||
|
|
*
|
||
|
|
* Each transform is a local transform relative from rest pose (T-pose).
|
||
|
|
*/
|
||
|
|
getPose() {
|
||
|
|
const pose = {};
|
||
|
|
Object.keys(this.humanBones).forEach((boneNameString) => {
|
||
|
|
const boneName = boneNameString;
|
||
|
|
const node = this.getBoneNode(boneName);
|
||
|
|
if (!node) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
_v3A2.set(0, 0, 0);
|
||
|
|
_quatA2.identity();
|
||
|
|
const restState = this.restPose[boneName];
|
||
|
|
if (restState == null ? void 0 : restState.position) {
|
||
|
|
_v3A2.fromArray(restState.position).negate();
|
||
|
|
}
|
||
|
|
if (restState == null ? void 0 : restState.rotation) {
|
||
|
|
quatInvertCompat(_quatA2.fromArray(restState.rotation));
|
||
|
|
}
|
||
|
|
_v3A2.add(node.position);
|
||
|
|
_quatA2.premultiply(node.quaternion);
|
||
|
|
pose[boneName] = {
|
||
|
|
position: _v3A2.toArray(),
|
||
|
|
rotation: _quatA2.toArray()
|
||
|
|
};
|
||
|
|
});
|
||
|
|
return pose;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Let the humanoid do a specified pose.
|
||
|
|
*
|
||
|
|
* Each transform have to be a local transform relative from rest pose (T-pose).
|
||
|
|
* You can pass what you got from {@link getPose}.
|
||
|
|
*
|
||
|
|
* @param poseObject A {@link VRMPose} that represents a single pose
|
||
|
|
*/
|
||
|
|
setPose(poseObject) {
|
||
|
|
Object.entries(poseObject).forEach(([boneNameString, state]) => {
|
||
|
|
const boneName = boneNameString;
|
||
|
|
const node = this.getBoneNode(boneName);
|
||
|
|
if (!node) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const restState = this.restPose[boneName];
|
||
|
|
if (!restState) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (state == null ? void 0 : state.position) {
|
||
|
|
node.position.fromArray(state.position);
|
||
|
|
if (restState.position) {
|
||
|
|
node.position.add(_v3A2.fromArray(restState.position));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (state == null ? void 0 : state.rotation) {
|
||
|
|
node.quaternion.fromArray(state.rotation);
|
||
|
|
if (restState.rotation) {
|
||
|
|
node.quaternion.multiply(_quatA2.fromArray(restState.rotation));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Reset the humanoid to its rest pose.
|
||
|
|
*/
|
||
|
|
resetPose() {
|
||
|
|
Object.entries(this.restPose).forEach(([boneName, rest]) => {
|
||
|
|
const node = this.getBoneNode(boneName);
|
||
|
|
if (!node) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (rest == null ? void 0 : rest.position) {
|
||
|
|
node.position.fromArray(rest.position);
|
||
|
|
}
|
||
|
|
if (rest == null ? void 0 : rest.rotation) {
|
||
|
|
node.quaternion.fromArray(rest.rotation);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return a bone bound to a specified {@link VRMHumanBoneName}, as a {@link VRMHumanBone}.
|
||
|
|
*
|
||
|
|
* @param name Name of the bone you want
|
||
|
|
*/
|
||
|
|
getBone(name) {
|
||
|
|
var _a;
|
||
|
|
return (_a = this.humanBones[name]) != null ? _a : void 0;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return a bone bound to a specified {@link VRMHumanBoneName}, as a `THREE.Object3D`.
|
||
|
|
*
|
||
|
|
* @param name Name of the bone you want
|
||
|
|
*/
|
||
|
|
getBoneNode(name) {
|
||
|
|
var _a, _b;
|
||
|
|
return (_b = (_a = this.humanBones[name]) == null ? void 0 : _a.node) != null ? _b : null;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _v3A3 = new THREE8.Vector3();
|
||
|
|
var _quatA3 = new THREE8.Quaternion();
|
||
|
|
var _boneWorldPos = new THREE8.Vector3();
|
||
|
|
var VRMHumanoidRig = class _VRMHumanoidRig extends VRMRig {
|
||
|
|
static _setupTransforms(modelRig) {
|
||
|
|
const root = new THREE8.Object3D();
|
||
|
|
root.name = "VRMHumanoidRig";
|
||
|
|
const boneWorldPositions = {};
|
||
|
|
const boneWorldRotations = {};
|
||
|
|
const boneRotations = {};
|
||
|
|
const parentWorldRotations = {};
|
||
|
|
VRMHumanBoneList.forEach((boneName) => {
|
||
|
|
var _a;
|
||
|
|
const boneNode = modelRig.getBoneNode(boneName);
|
||
|
|
if (boneNode) {
|
||
|
|
const boneWorldPosition = new THREE8.Vector3();
|
||
|
|
const boneWorldRotation = new THREE8.Quaternion();
|
||
|
|
boneNode.updateWorldMatrix(true, false);
|
||
|
|
boneNode.matrixWorld.decompose(boneWorldPosition, boneWorldRotation, _v3A3);
|
||
|
|
boneWorldPositions[boneName] = boneWorldPosition;
|
||
|
|
boneWorldRotations[boneName] = boneWorldRotation;
|
||
|
|
boneRotations[boneName] = boneNode.quaternion.clone();
|
||
|
|
const parentWorldRotation = new THREE8.Quaternion();
|
||
|
|
(_a = boneNode.parent) == null ? void 0 : _a.matrixWorld.decompose(_v3A3, parentWorldRotation, _v3A3);
|
||
|
|
parentWorldRotations[boneName] = parentWorldRotation;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
const rigBones = {};
|
||
|
|
VRMHumanBoneList.forEach((boneName) => {
|
||
|
|
var _a;
|
||
|
|
const boneNode = modelRig.getBoneNode(boneName);
|
||
|
|
if (boneNode) {
|
||
|
|
const boneWorldPosition = boneWorldPositions[boneName];
|
||
|
|
let currentBoneName = boneName;
|
||
|
|
let parentBoneWorldPosition;
|
||
|
|
while (parentBoneWorldPosition == null) {
|
||
|
|
currentBoneName = VRMHumanBoneParentMap[currentBoneName];
|
||
|
|
if (currentBoneName == null) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
parentBoneWorldPosition = boneWorldPositions[currentBoneName];
|
||
|
|
}
|
||
|
|
const rigBoneNode = new THREE8.Object3D();
|
||
|
|
rigBoneNode.name = "Normalized_" + boneNode.name;
|
||
|
|
const parentRigBoneNode = currentBoneName ? (_a = rigBones[currentBoneName]) == null ? void 0 : _a.node : root;
|
||
|
|
parentRigBoneNode.add(rigBoneNode);
|
||
|
|
rigBoneNode.position.copy(boneWorldPosition);
|
||
|
|
if (parentBoneWorldPosition) {
|
||
|
|
rigBoneNode.position.sub(parentBoneWorldPosition);
|
||
|
|
}
|
||
|
|
rigBones[boneName] = { node: rigBoneNode };
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
rigBones,
|
||
|
|
root,
|
||
|
|
parentWorldRotations,
|
||
|
|
boneRotations
|
||
|
|
};
|
||
|
|
}
|
||
|
|
constructor(humanoid) {
|
||
|
|
const { rigBones, root, parentWorldRotations, boneRotations } = _VRMHumanoidRig._setupTransforms(humanoid);
|
||
|
|
super(rigBones);
|
||
|
|
this.original = humanoid;
|
||
|
|
this.root = root;
|
||
|
|
this._parentWorldRotations = parentWorldRotations;
|
||
|
|
this._boneRotations = boneRotations;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Update this humanoid rig.
|
||
|
|
*/
|
||
|
|
update() {
|
||
|
|
VRMHumanBoneList.forEach((boneName) => {
|
||
|
|
const boneNode = this.original.getBoneNode(boneName);
|
||
|
|
if (boneNode != null) {
|
||
|
|
const rigBoneNode = this.getBoneNode(boneName);
|
||
|
|
const parentWorldRotation = this._parentWorldRotations[boneName];
|
||
|
|
const invParentWorldRotation = _quatA3.copy(parentWorldRotation).invert();
|
||
|
|
const boneRotation = this._boneRotations[boneName];
|
||
|
|
boneNode.quaternion.copy(rigBoneNode.quaternion).multiply(parentWorldRotation).premultiply(invParentWorldRotation).multiply(boneRotation);
|
||
|
|
if (boneName === "hips") {
|
||
|
|
const boneWorldPosition = rigBoneNode.getWorldPosition(_boneWorldPos);
|
||
|
|
boneNode.parent.updateWorldMatrix(true, false);
|
||
|
|
const parentWorldMatrix = boneNode.parent.matrixWorld;
|
||
|
|
const localPosition = boneWorldPosition.applyMatrix4(parentWorldMatrix.invert());
|
||
|
|
boneNode.position.copy(localPosition);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var VRMHumanoid = class _VRMHumanoid {
|
||
|
|
// TODO: Rename
|
||
|
|
/**
|
||
|
|
* @deprecated Deprecated. Use either {@link rawRestPose} or {@link normalizedRestPose} instead.
|
||
|
|
*/
|
||
|
|
get restPose() {
|
||
|
|
console.warn("VRMHumanoid: restPose is deprecated. Use either rawRestPose or normalizedRestPose instead.");
|
||
|
|
return this.rawRestPose;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* A {@link VRMPose} of its raw human bones that is its default state.
|
||
|
|
* Note that it's not compatible with {@link setRawPose} and {@link getRawPose}, since it contains non-relative values of each local transforms.
|
||
|
|
*/
|
||
|
|
get rawRestPose() {
|
||
|
|
return this._rawHumanBones.restPose;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* A {@link VRMPose} of its normalized human bones that is its default state.
|
||
|
|
* Note that it's not compatible with {@link setNormalizedPose} and {@link getNormalizedPose}, since it contains non-relative values of each local transforms.
|
||
|
|
*/
|
||
|
|
get normalizedRestPose() {
|
||
|
|
return this._normalizedHumanBones.restPose;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* A map from {@link VRMHumanBoneName} to raw {@link VRMHumanBone}s.
|
||
|
|
*/
|
||
|
|
get humanBones() {
|
||
|
|
return this._rawHumanBones.humanBones;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* A map from {@link VRMHumanBoneName} to raw {@link VRMHumanBone}s.
|
||
|
|
*/
|
||
|
|
get rawHumanBones() {
|
||
|
|
return this._rawHumanBones.humanBones;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* A map from {@link VRMHumanBoneName} to normalized {@link VRMHumanBone}s.
|
||
|
|
*/
|
||
|
|
get normalizedHumanBones() {
|
||
|
|
return this._normalizedHumanBones.humanBones;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* The root of normalized {@link VRMHumanBone}s.
|
||
|
|
*/
|
||
|
|
get normalizedHumanBonesRoot() {
|
||
|
|
return this._normalizedHumanBones.root;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Create a new {@link VRMHumanoid}.
|
||
|
|
* @param humanBones A {@link VRMHumanBones} contains all the bones of the new humanoid
|
||
|
|
* @param autoUpdateHumanBones Whether it copies pose from normalizedHumanBones to rawHumanBones on {@link update}. `true` by default.
|
||
|
|
*/
|
||
|
|
constructor(humanBones, options) {
|
||
|
|
var _a;
|
||
|
|
this.autoUpdateHumanBones = (_a = options == null ? void 0 : options.autoUpdateHumanBones) != null ? _a : true;
|
||
|
|
this._rawHumanBones = new VRMRig(humanBones);
|
||
|
|
this._normalizedHumanBones = new VRMHumanoidRig(this._rawHumanBones);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Copy the given {@link VRMHumanoid} into this one.
|
||
|
|
* @param source The {@link VRMHumanoid} you want to copy
|
||
|
|
* @returns this
|
||
|
|
*/
|
||
|
|
copy(source) {
|
||
|
|
this.autoUpdateHumanBones = source.autoUpdateHumanBones;
|
||
|
|
this._rawHumanBones = new VRMRig(source.humanBones);
|
||
|
|
this._normalizedHumanBones = new VRMHumanoidRig(this._rawHumanBones);
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Returns a clone of this {@link VRMHumanoid}.
|
||
|
|
* @returns Copied {@link VRMHumanoid}
|
||
|
|
*/
|
||
|
|
clone() {
|
||
|
|
return new _VRMHumanoid(this.humanBones, { autoUpdateHumanBones: this.autoUpdateHumanBones }).copy(this);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Deprecated. Use either {@link getRawAbsolutePose} or {@link getNormalizedAbsolutePose} instead.
|
||
|
|
*/
|
||
|
|
getAbsolutePose() {
|
||
|
|
console.warn(
|
||
|
|
"VRMHumanoid: getAbsolutePose() is deprecated. Use either getRawAbsolutePose() or getNormalizedAbsolutePose() instead."
|
||
|
|
);
|
||
|
|
return this.getRawAbsolutePose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return the current absolute pose of this raw human bones as a {@link VRMPose}.
|
||
|
|
* Note that the output result will contain initial state of the VRM and not compatible between different models.
|
||
|
|
* You might want to use {@link getRawPose} instead.
|
||
|
|
*/
|
||
|
|
getRawAbsolutePose() {
|
||
|
|
return this._rawHumanBones.getAbsolutePose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return the current absolute pose of this normalized human bones as a {@link VRMPose}.
|
||
|
|
* Note that the output result will contain initial state of the VRM and not compatible between different models.
|
||
|
|
* You might want to use {@link getNormalizedPose} instead.
|
||
|
|
*/
|
||
|
|
getNormalizedAbsolutePose() {
|
||
|
|
return this._normalizedHumanBones.getAbsolutePose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Deprecated. Use either {@link getRawPose} or {@link getNormalizedPose} instead.
|
||
|
|
*/
|
||
|
|
getPose() {
|
||
|
|
console.warn("VRMHumanoid: getPose() is deprecated. Use either getRawPose() or getNormalizedPose() instead.");
|
||
|
|
return this.getRawPose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return the current pose of raw human bones as a {@link VRMPose}.
|
||
|
|
*
|
||
|
|
* Each transform is a local transform relative from rest pose (T-pose).
|
||
|
|
*/
|
||
|
|
getRawPose() {
|
||
|
|
return this._rawHumanBones.getPose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return the current pose of normalized human bones as a {@link VRMPose}.
|
||
|
|
*
|
||
|
|
* Each transform is a local transform relative from rest pose (T-pose).
|
||
|
|
*/
|
||
|
|
getNormalizedPose() {
|
||
|
|
return this._normalizedHumanBones.getPose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Deprecated. Use either {@link setRawPose} or {@link setNormalizedPose} instead.
|
||
|
|
*/
|
||
|
|
setPose(poseObject) {
|
||
|
|
console.warn("VRMHumanoid: setPose() is deprecated. Use either setRawPose() or setNormalizedPose() instead.");
|
||
|
|
return this.setRawPose(poseObject);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Let the raw human bones do a specified pose.
|
||
|
|
*
|
||
|
|
* Each transform have to be a local transform relative from rest pose (T-pose).
|
||
|
|
* You can pass what you got from {@link getRawPose}.
|
||
|
|
*
|
||
|
|
* If you are using {@link autoUpdateHumanBones}, you might want to use {@link setNormalizedPose} instead.
|
||
|
|
*
|
||
|
|
* @param poseObject A {@link VRMPose} that represents a single pose
|
||
|
|
*/
|
||
|
|
setRawPose(poseObject) {
|
||
|
|
return this._rawHumanBones.setPose(poseObject);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Let the normalized human bones do a specified pose.
|
||
|
|
*
|
||
|
|
* Each transform have to be a local transform relative from rest pose (T-pose).
|
||
|
|
* You can pass what you got from {@link getNormalizedPose}.
|
||
|
|
*
|
||
|
|
* @param poseObject A {@link VRMPose} that represents a single pose
|
||
|
|
*/
|
||
|
|
setNormalizedPose(poseObject) {
|
||
|
|
return this._normalizedHumanBones.setPose(poseObject);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Deprecated. Use either {@link resetRawPose} or {@link resetNormalizedPose} instead.
|
||
|
|
*/
|
||
|
|
resetPose() {
|
||
|
|
console.warn("VRMHumanoid: resetPose() is deprecated. Use either resetRawPose() or resetNormalizedPose() instead.");
|
||
|
|
return this.resetRawPose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Reset the raw humanoid to its rest pose.
|
||
|
|
*
|
||
|
|
* If you are using {@link autoUpdateHumanBones}, you might want to use {@link resetNormalizedPose} instead.
|
||
|
|
*/
|
||
|
|
resetRawPose() {
|
||
|
|
return this._rawHumanBones.resetPose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Reset the normalized humanoid to its rest pose.
|
||
|
|
*/
|
||
|
|
resetNormalizedPose() {
|
||
|
|
return this._normalizedHumanBones.resetPose();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Deprecated. Use either {@link getRawBone} or {@link getNormalizedBone} instead.
|
||
|
|
*/
|
||
|
|
getBone(name) {
|
||
|
|
console.warn("VRMHumanoid: getBone() is deprecated. Use either getRawBone() or getNormalizedBone() instead.");
|
||
|
|
return this.getRawBone(name);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return a raw {@link VRMHumanBone} bound to a specified {@link VRMHumanBoneName}.
|
||
|
|
*
|
||
|
|
* @param name Name of the bone you want
|
||
|
|
*/
|
||
|
|
getRawBone(name) {
|
||
|
|
return this._rawHumanBones.getBone(name);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return a normalized {@link VRMHumanBone} bound to a specified {@link VRMHumanBoneName}.
|
||
|
|
*
|
||
|
|
* @param name Name of the bone you want
|
||
|
|
*/
|
||
|
|
getNormalizedBone(name) {
|
||
|
|
return this._normalizedHumanBones.getBone(name);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Deprecated. Use either {@link getRawBoneNode} or {@link getNormalizedBoneNode} instead.
|
||
|
|
*/
|
||
|
|
getBoneNode(name) {
|
||
|
|
console.warn(
|
||
|
|
"VRMHumanoid: getBoneNode() is deprecated. Use either getRawBoneNode() or getNormalizedBoneNode() instead."
|
||
|
|
);
|
||
|
|
return this.getRawBoneNode(name);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return a raw bone as a `THREE.Object3D` bound to a specified {@link VRMHumanBoneName}.
|
||
|
|
*
|
||
|
|
* @param name Name of the bone you want
|
||
|
|
*/
|
||
|
|
getRawBoneNode(name) {
|
||
|
|
return this._rawHumanBones.getBoneNode(name);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Return a normalized bone as a `THREE.Object3D` bound to a specified {@link VRMHumanBoneName}.
|
||
|
|
*
|
||
|
|
* @param name Name of the bone you want
|
||
|
|
*/
|
||
|
|
getNormalizedBoneNode(name) {
|
||
|
|
return this._normalizedHumanBones.getBoneNode(name);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Update the humanoid component.
|
||
|
|
*
|
||
|
|
* If {@link autoUpdateHumanBones} is `true`, it transfers the pose of normalized human bones to raw human bones.
|
||
|
|
*/
|
||
|
|
update() {
|
||
|
|
if (this.autoUpdateHumanBones) {
|
||
|
|
this._normalizedHumanBones.update();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var VRMRequiredHumanBoneName = {
|
||
|
|
Hips: "hips",
|
||
|
|
Spine: "spine",
|
||
|
|
Head: "head",
|
||
|
|
LeftUpperLeg: "leftUpperLeg",
|
||
|
|
LeftLowerLeg: "leftLowerLeg",
|
||
|
|
LeftFoot: "leftFoot",
|
||
|
|
RightUpperLeg: "rightUpperLeg",
|
||
|
|
RightLowerLeg: "rightLowerLeg",
|
||
|
|
RightFoot: "rightFoot",
|
||
|
|
LeftUpperArm: "leftUpperArm",
|
||
|
|
LeftLowerArm: "leftLowerArm",
|
||
|
|
LeftHand: "leftHand",
|
||
|
|
RightUpperArm: "rightUpperArm",
|
||
|
|
RightLowerArm: "rightLowerArm",
|
||
|
|
RightHand: "rightHand"
|
||
|
|
};
|
||
|
|
var POSSIBLE_SPEC_VERSIONS3 = /* @__PURE__ */ new Set(["1.0", "1.0-beta"]);
|
||
|
|
var thumbBoneNameMap = {
|
||
|
|
leftThumbProximal: "leftThumbMetacarpal",
|
||
|
|
leftThumbIntermediate: "leftThumbProximal",
|
||
|
|
rightThumbProximal: "rightThumbMetacarpal",
|
||
|
|
rightThumbIntermediate: "rightThumbProximal"
|
||
|
|
};
|
||
|
|
var VRMHumanoidLoaderPlugin = class {
|
||
|
|
get name() {
|
||
|
|
return "VRMHumanoidLoaderPlugin";
|
||
|
|
}
|
||
|
|
constructor(parser, options) {
|
||
|
|
this.parser = parser;
|
||
|
|
this.helperRoot = options == null ? void 0 : options.helperRoot;
|
||
|
|
this.autoUpdateHumanBones = options == null ? void 0 : options.autoUpdateHumanBones;
|
||
|
|
}
|
||
|
|
afterRoot(gltf) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
gltf.userData.vrmHumanoid = yield this._import(gltf);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Import a {@link VRMHumanoid} 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_VERSIONS3.has(specVersion)) {
|
||
|
|
console.warn(`VRMHumanoidLoaderPlugin: Unknown VRMC_vrm specVersion "${specVersion}"`);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const schemaHumanoid = extension.humanoid;
|
||
|
|
if (!schemaHumanoid) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const existsPreviousThumbName = schemaHumanoid.humanBones.leftThumbIntermediate != null || schemaHumanoid.humanBones.rightThumbIntermediate != null;
|
||
|
|
const humanBones = {};
|
||
|
|
if (schemaHumanoid.humanBones != null) {
|
||
|
|
yield Promise.all(
|
||
|
|
Object.entries(schemaHumanoid.humanBones).map((_0) => __async2(this, [_0], function* ([boneNameString, schemaHumanBone]) {
|
||
|
|
let boneName = boneNameString;
|
||
|
|
const index = schemaHumanBone.node;
|
||
|
|
if (existsPreviousThumbName) {
|
||
|
|
const thumbBoneName = thumbBoneNameMap[boneName];
|
||
|
|
if (thumbBoneName != null) {
|
||
|
|
boneName = thumbBoneName;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const node = yield this.parser.getDependency("node", index);
|
||
|
|
if (node == null) {
|
||
|
|
console.warn(`A glTF node bound to the humanoid bone ${boneName} (index = ${index}) does not exist`);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
humanBones[boneName] = { node };
|
||
|
|
}))
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const humanoid = new VRMHumanoid(this._ensureRequiredBonesExist(humanBones), {
|
||
|
|
autoUpdateHumanBones: this.autoUpdateHumanBones
|
||
|
|
});
|
||
|
|
gltf.scene.add(humanoid.normalizedHumanBonesRoot);
|
||
|
|
if (this.helperRoot) {
|
||
|
|
const helper = new VRMHumanoidHelper(humanoid);
|
||
|
|
this.helperRoot.add(helper);
|
||
|
|
helper.renderOrder = this.helperRoot.renderOrder;
|
||
|
|
}
|
||
|
|
return humanoid;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_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 schemaHumanoid = vrmExt.humanoid;
|
||
|
|
if (!schemaHumanoid) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const humanBones = {};
|
||
|
|
if (schemaHumanoid.humanBones != null) {
|
||
|
|
yield Promise.all(
|
||
|
|
schemaHumanoid.humanBones.map((bone) => __async2(this, null, function* () {
|
||
|
|
const boneName = bone.bone;
|
||
|
|
const index = bone.node;
|
||
|
|
if (boneName == null || index == null) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const node = yield this.parser.getDependency("node", index);
|
||
|
|
if (node == null) {
|
||
|
|
console.warn(`A glTF node bound to the humanoid bone ${boneName} (index = ${index}) does not exist`);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const thumbBoneName = thumbBoneNameMap[boneName];
|
||
|
|
const newBoneName = thumbBoneName != null ? thumbBoneName : boneName;
|
||
|
|
if (humanBones[newBoneName] != null) {
|
||
|
|
console.warn(
|
||
|
|
`Multiple bone entries for ${newBoneName} detected (index = ${index}), ignoring duplicated entries.`
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
humanBones[newBoneName] = { node };
|
||
|
|
}))
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const humanoid = new VRMHumanoid(this._ensureRequiredBonesExist(humanBones), {
|
||
|
|
autoUpdateHumanBones: this.autoUpdateHumanBones
|
||
|
|
});
|
||
|
|
gltf.scene.add(humanoid.normalizedHumanBonesRoot);
|
||
|
|
if (this.helperRoot) {
|
||
|
|
const helper = new VRMHumanoidHelper(humanoid);
|
||
|
|
this.helperRoot.add(helper);
|
||
|
|
helper.renderOrder = this.helperRoot.renderOrder;
|
||
|
|
}
|
||
|
|
return humanoid;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Ensure required bones exist in given human bones.
|
||
|
|
* @param humanBones Human bones
|
||
|
|
* @returns Human bones, no longer partial!
|
||
|
|
*/
|
||
|
|
_ensureRequiredBonesExist(humanBones) {
|
||
|
|
const missingRequiredBones = Object.values(VRMRequiredHumanBoneName).filter(
|
||
|
|
(requiredBoneName) => humanBones[requiredBoneName] == null
|
||
|
|
);
|
||
|
|
if (missingRequiredBones.length > 0) {
|
||
|
|
throw new Error(
|
||
|
|
`VRMHumanoidLoaderPlugin: These humanoid bones are required but not exist: ${missingRequiredBones.join(", ")}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return humanBones;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var FanBufferGeometry = class extends THREE9.BufferGeometry {
|
||
|
|
constructor() {
|
||
|
|
super();
|
||
|
|
this._currentTheta = 0;
|
||
|
|
this._currentRadius = 0;
|
||
|
|
this.theta = 0;
|
||
|
|
this.radius = 0;
|
||
|
|
this._currentTheta = 0;
|
||
|
|
this._currentRadius = 0;
|
||
|
|
this._attrPos = new THREE9.BufferAttribute(new Float32Array(65 * 3), 3);
|
||
|
|
this.setAttribute("position", this._attrPos);
|
||
|
|
this._attrIndex = new THREE9.BufferAttribute(new Uint16Array(3 * 63), 1);
|
||
|
|
this.setIndex(this._attrIndex);
|
||
|
|
this._buildIndex();
|
||
|
|
this.update();
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
let shouldUpdateGeometry = false;
|
||
|
|
if (this._currentTheta !== this.theta) {
|
||
|
|
this._currentTheta = this.theta;
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (this._currentRadius !== this.radius) {
|
||
|
|
this._currentRadius = this.radius;
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (shouldUpdateGeometry) {
|
||
|
|
this._buildPosition();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_buildPosition() {
|
||
|
|
this._attrPos.setXYZ(0, 0, 0, 0);
|
||
|
|
for (let i = 0; i < 64; i++) {
|
||
|
|
const t = i / 63 * this._currentTheta;
|
||
|
|
this._attrPos.setXYZ(i + 1, this._currentRadius * Math.sin(t), 0, this._currentRadius * Math.cos(t));
|
||
|
|
}
|
||
|
|
this._attrPos.needsUpdate = true;
|
||
|
|
}
|
||
|
|
_buildIndex() {
|
||
|
|
for (let i = 0; i < 63; i++) {
|
||
|
|
this._attrIndex.setXYZ(i * 3, 0, i + 1, i + 2);
|
||
|
|
}
|
||
|
|
this._attrIndex.needsUpdate = true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var LineAndSphereBufferGeometry = class extends THREE10.BufferGeometry {
|
||
|
|
constructor() {
|
||
|
|
super();
|
||
|
|
this.radius = 0;
|
||
|
|
this._currentRadius = 0;
|
||
|
|
this.tail = new THREE10.Vector3();
|
||
|
|
this._currentTail = new THREE10.Vector3();
|
||
|
|
this._attrPos = new THREE10.BufferAttribute(new Float32Array(294), 3);
|
||
|
|
this.setAttribute("position", this._attrPos);
|
||
|
|
this._attrIndex = new THREE10.BufferAttribute(new Uint16Array(194), 1);
|
||
|
|
this.setIndex(this._attrIndex);
|
||
|
|
this._buildIndex();
|
||
|
|
this.update();
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
let shouldUpdateGeometry = false;
|
||
|
|
if (this._currentRadius !== this.radius) {
|
||
|
|
this._currentRadius = this.radius;
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (!this._currentTail.equals(this.tail)) {
|
||
|
|
this._currentTail.copy(this.tail);
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (shouldUpdateGeometry) {
|
||
|
|
this._buildPosition();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_buildPosition() {
|
||
|
|
for (let i = 0; i < 32; i++) {
|
||
|
|
const t = i / 16 * Math.PI;
|
||
|
|
this._attrPos.setXYZ(i, Math.cos(t), Math.sin(t), 0);
|
||
|
|
this._attrPos.setXYZ(32 + i, 0, Math.cos(t), Math.sin(t));
|
||
|
|
this._attrPos.setXYZ(64 + i, Math.sin(t), 0, Math.cos(t));
|
||
|
|
}
|
||
|
|
this.scale(this._currentRadius, this._currentRadius, this._currentRadius);
|
||
|
|
this.translate(this._currentTail.x, this._currentTail.y, this._currentTail.z);
|
||
|
|
this._attrPos.setXYZ(96, 0, 0, 0);
|
||
|
|
this._attrPos.setXYZ(97, this._currentTail.x, this._currentTail.y, this._currentTail.z);
|
||
|
|
this._attrPos.needsUpdate = true;
|
||
|
|
}
|
||
|
|
_buildIndex() {
|
||
|
|
for (let i = 0; i < 32; i++) {
|
||
|
|
const i1 = (i + 1) % 32;
|
||
|
|
this._attrIndex.setXY(i * 2, i, i1);
|
||
|
|
this._attrIndex.setXY(64 + i * 2, 32 + i, 32 + i1);
|
||
|
|
this._attrIndex.setXY(128 + i * 2, 64 + i, 64 + i1);
|
||
|
|
}
|
||
|
|
this._attrIndex.setXY(192, 96, 97);
|
||
|
|
this._attrIndex.needsUpdate = true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _quatA4 = new THREE11.Quaternion();
|
||
|
|
var _quatB = new THREE11.Quaternion();
|
||
|
|
var _v3A4 = new THREE11.Vector3();
|
||
|
|
var _v3B2 = new THREE11.Vector3();
|
||
|
|
var SQRT_2_OVER_2 = Math.sqrt(2) / 2;
|
||
|
|
var QUAT_XY_CW90 = new THREE11.Quaternion(0, 0, -SQRT_2_OVER_2, SQRT_2_OVER_2);
|
||
|
|
var VEC3_POSITIVE_Y = new THREE11.Vector3(0, 1, 0);
|
||
|
|
var VRMLookAtHelper = class extends THREE11.Group {
|
||
|
|
constructor(lookAt) {
|
||
|
|
super();
|
||
|
|
this.matrixAutoUpdate = false;
|
||
|
|
this.vrmLookAt = lookAt;
|
||
|
|
{
|
||
|
|
const geometry = new FanBufferGeometry();
|
||
|
|
geometry.radius = 0.5;
|
||
|
|
const material = new THREE11.MeshBasicMaterial({
|
||
|
|
color: 65280,
|
||
|
|
transparent: true,
|
||
|
|
opacity: 0.5,
|
||
|
|
side: THREE11.DoubleSide,
|
||
|
|
depthTest: false,
|
||
|
|
depthWrite: false
|
||
|
|
});
|
||
|
|
this._meshPitch = new THREE11.Mesh(geometry, material);
|
||
|
|
this.add(this._meshPitch);
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const geometry = new FanBufferGeometry();
|
||
|
|
geometry.radius = 0.5;
|
||
|
|
const material = new THREE11.MeshBasicMaterial({
|
||
|
|
color: 16711680,
|
||
|
|
transparent: true,
|
||
|
|
opacity: 0.5,
|
||
|
|
side: THREE11.DoubleSide,
|
||
|
|
depthTest: false,
|
||
|
|
depthWrite: false
|
||
|
|
});
|
||
|
|
this._meshYaw = new THREE11.Mesh(geometry, material);
|
||
|
|
this.add(this._meshYaw);
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const geometry = new LineAndSphereBufferGeometry();
|
||
|
|
geometry.radius = 0.1;
|
||
|
|
const material = new THREE11.LineBasicMaterial({
|
||
|
|
color: 16777215,
|
||
|
|
depthTest: false,
|
||
|
|
depthWrite: false
|
||
|
|
});
|
||
|
|
this._lineTarget = new THREE11.LineSegments(geometry, material);
|
||
|
|
this._lineTarget.frustumCulled = false;
|
||
|
|
this.add(this._lineTarget);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
dispose() {
|
||
|
|
this._meshYaw.geometry.dispose();
|
||
|
|
this._meshYaw.material.dispose();
|
||
|
|
this._meshPitch.geometry.dispose();
|
||
|
|
this._meshPitch.material.dispose();
|
||
|
|
this._lineTarget.geometry.dispose();
|
||
|
|
this._lineTarget.material.dispose();
|
||
|
|
}
|
||
|
|
updateMatrixWorld(force) {
|
||
|
|
const yaw = THREE11.MathUtils.DEG2RAD * this.vrmLookAt.yaw;
|
||
|
|
this._meshYaw.geometry.theta = yaw;
|
||
|
|
this._meshYaw.geometry.update();
|
||
|
|
const pitch = THREE11.MathUtils.DEG2RAD * this.vrmLookAt.pitch;
|
||
|
|
this._meshPitch.geometry.theta = pitch;
|
||
|
|
this._meshPitch.geometry.update();
|
||
|
|
this.vrmLookAt.getLookAtWorldPosition(_v3A4);
|
||
|
|
this.vrmLookAt.getLookAtWorldQuaternion(_quatA4);
|
||
|
|
_quatA4.multiply(this.vrmLookAt.getFaceFrontQuaternion(_quatB));
|
||
|
|
this._meshYaw.position.copy(_v3A4);
|
||
|
|
this._meshYaw.quaternion.copy(_quatA4);
|
||
|
|
this._meshPitch.position.copy(_v3A4);
|
||
|
|
this._meshPitch.quaternion.copy(_quatA4);
|
||
|
|
this._meshPitch.quaternion.multiply(_quatB.setFromAxisAngle(VEC3_POSITIVE_Y, yaw));
|
||
|
|
this._meshPitch.quaternion.multiply(QUAT_XY_CW90);
|
||
|
|
const { target, autoUpdate } = this.vrmLookAt;
|
||
|
|
if (target != null && autoUpdate) {
|
||
|
|
target.getWorldPosition(_v3B2).sub(_v3A4);
|
||
|
|
this._lineTarget.geometry.tail.copy(_v3B2);
|
||
|
|
this._lineTarget.geometry.update();
|
||
|
|
this._lineTarget.position.copy(_v3A4);
|
||
|
|
}
|
||
|
|
super.updateMatrixWorld(force);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _position = new THREE12.Vector3();
|
||
|
|
var _scale = new THREE12.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 THREE13.Vector3(0, 0, 1);
|
||
|
|
var _v3A5 = new THREE13.Vector3();
|
||
|
|
var _v3B3 = new THREE13.Vector3();
|
||
|
|
var _v3C = new THREE13.Vector3();
|
||
|
|
var _quatA5 = new THREE13.Quaternion();
|
||
|
|
var _quatB2 = new THREE13.Quaternion();
|
||
|
|
var _quatC = new THREE13.Quaternion();
|
||
|
|
var _quatD = new THREE13.Quaternion();
|
||
|
|
var _eulerA = new THREE13.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 THREE13.Vector3();
|
||
|
|
this.autoUpdate = true;
|
||
|
|
this.faceFront = new THREE13.Vector3(0, 0, 1);
|
||
|
|
this.humanoid = humanoid;
|
||
|
|
this.applier = applier;
|
||
|
|
this._yaw = 0;
|
||
|
|
this._pitch = 0;
|
||
|
|
this._needsUpdate = true;
|
||
|
|
this._restHeadWorldQuaternion = this.getLookAtWorldQuaternion(new THREE13.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 THREE13.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(THREE13.MathUtils.DEG2RAD * this._pitch, THREE13.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) {
|
||
|
|
const head = this.humanoid.getRawBoneNode("head");
|
||
|
|
return getWorldQuaternionLite(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) < 0.01) {
|
||
|
|
return target.copy(this._restHeadWorldQuaternion).invert();
|
||
|
|
}
|
||
|
|
const [faceFrontAzimuth, faceFrontAltitude] = calcAzimuthAltitude(this.faceFront);
|
||
|
|
_eulerA.set(0, 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 = THREE13.MathUtils.RAD2DEG * yaw;
|
||
|
|
this._pitch = THREE13.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 THREE14.Vector3(0, 0, 1);
|
||
|
|
var _quatA6 = new THREE14.Quaternion();
|
||
|
|
var _quatB3 = new THREE14.Quaternion();
|
||
|
|
var _eulerA2 = new THREE14.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 THREE14.Vector3(0, 0, 1);
|
||
|
|
this._restQuatLeftEye = new THREE14.Quaternion();
|
||
|
|
this._restQuatRightEye = new THREE14.Quaternion();
|
||
|
|
this._restLeftEyeParentWorldQuat = new THREE14.Quaternion();
|
||
|
|
this._restRightEyeParentWorldQuat = new THREE14.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 = -THREE14.MathUtils.DEG2RAD * this.rangeMapVerticalDown.map(-pitch);
|
||
|
|
} else {
|
||
|
|
_eulerA2.x = THREE14.MathUtils.DEG2RAD * this.rangeMapVerticalUp.map(pitch);
|
||
|
|
}
|
||
|
|
if (yaw < 0) {
|
||
|
|
_eulerA2.y = -THREE14.MathUtils.DEG2RAD * this.rangeMapHorizontalInner.map(-yaw);
|
||
|
|
} else {
|
||
|
|
_eulerA2.y = THREE14.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 = -THREE14.MathUtils.DEG2RAD * this.rangeMapVerticalDown.map(-pitch);
|
||
|
|
} else {
|
||
|
|
_eulerA2.x = THREE14.MathUtils.DEG2RAD * this.rangeMapVerticalUp.map(pitch);
|
||
|
|
}
|
||
|
|
if (yaw < 0) {
|
||
|
|
_eulerA2.y = -THREE14.MathUtils.DEG2RAD * this.rangeMapHorizontalOuter.map(-yaw);
|
||
|
|
} else {
|
||
|
|
_eulerA2.y = THREE14.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 = THREE14.MathUtils.RAD2DEG * euler.y;
|
||
|
|
const pitch = THREE14.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) < 0.01) {
|
||
|
|
return target.identity();
|
||
|
|
}
|
||
|
|
const [faceFrontAzimuth, faceFrontAltitude] = calcAzimuthAltitude(this.faceFront);
|
||
|
|
_eulerA2.set(0, 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 = THREE15.MathUtils.RAD2DEG * euler.y;
|
||
|
|
const pitch = THREE15.MathUtils.RAD2DEG * euler.x;
|
||
|
|
this.applyYawPitch(yaw, pitch);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
VRMLookAtExpressionApplier.type = "expression";
|
||
|
|
var VRMLookAtRangeMap = class {
|
||
|
|
/**
|
||
|
|
* Create a new {@link VRMLookAtRangeMap}.
|
||
|
|
*
|
||
|
|
* @param inputMaxValue The {@link inputMaxValue} of the map
|
||
|
|
* @param outputScale The {@link outputScale} of the map
|
||
|
|
*/
|
||
|
|
constructor(inputMaxValue, outputScale) {
|
||
|
|
this.inputMaxValue = inputMaxValue;
|
||
|
|
this.outputScale = outputScale;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Evaluate an input value and output a mapped value.
|
||
|
|
* @param src The input value
|
||
|
|
*/
|
||
|
|
map(src) {
|
||
|
|
return this.outputScale * saturate(src / this.inputMaxValue);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var POSSIBLE_SPEC_VERSIONS4 = /* @__PURE__ */ new Set(["1.0", "1.0-beta"]);
|
||
|
|
var INPUT_MAX_VALUE_MINIMUM = 0.01;
|
||
|
|
var VRMLookAtLoaderPlugin = class {
|
||
|
|
get name() {
|
||
|
|
return "VRMLookAtLoaderPlugin";
|
||
|
|
}
|
||
|
|
constructor(parser, options) {
|
||
|
|
this.parser = parser;
|
||
|
|
this.helperRoot = options == null ? void 0 : options.helperRoot;
|
||
|
|
}
|
||
|
|
afterRoot(gltf) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
const vrmHumanoid = gltf.userData.vrmHumanoid;
|
||
|
|
if (vrmHumanoid === null) {
|
||
|
|
return;
|
||
|
|
} else if (vrmHumanoid === void 0) {
|
||
|
|
throw new Error("VRMLookAtLoaderPlugin: vrmHumanoid is undefined. VRMHumanoidLoaderPlugin have to be used first");
|
||
|
|
}
|
||
|
|
const vrmExpressionManager = gltf.userData.vrmExpressionManager;
|
||
|
|
if (vrmExpressionManager === null) {
|
||
|
|
return;
|
||
|
|
} else if (vrmExpressionManager === void 0) {
|
||
|
|
throw new Error(
|
||
|
|
"VRMLookAtLoaderPlugin: vrmExpressionManager is undefined. VRMExpressionLoaderPlugin have to be used first"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
gltf.userData.vrmLookAt = yield this._import(gltf, vrmHumanoid, vrmExpressionManager);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Import a {@link VRMLookAt} from a VRM.
|
||
|
|
*
|
||
|
|
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
||
|
|
* @param humanoid A {@link VRMHumanoid} instance that represents the VRM
|
||
|
|
* @param expressions A {@link VRMExpressionManager} instance that represents the VRM
|
||
|
|
*/
|
||
|
|
_import(gltf, humanoid, expressions) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
if (humanoid == null || expressions == null) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const v1Result = yield this._v1Import(gltf, humanoid, expressions);
|
||
|
|
if (v1Result) {
|
||
|
|
return v1Result;
|
||
|
|
}
|
||
|
|
const v0Result = yield this._v0Import(gltf, humanoid, expressions);
|
||
|
|
if (v0Result) {
|
||
|
|
return v0Result;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_v1Import(gltf, humanoid, expressions) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
var _a, _b, _c;
|
||
|
|
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_VERSIONS4.has(specVersion)) {
|
||
|
|
console.warn(`VRMLookAtLoaderPlugin: Unknown VRMC_vrm specVersion "${specVersion}"`);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const schemaLookAt = extension.lookAt;
|
||
|
|
if (!schemaLookAt) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const defaultOutputScale = schemaLookAt.type === "expression" ? 1 : 10;
|
||
|
|
const mapHI = this._v1ImportRangeMap(schemaLookAt.rangeMapHorizontalInner, defaultOutputScale);
|
||
|
|
const mapHO = this._v1ImportRangeMap(schemaLookAt.rangeMapHorizontalOuter, defaultOutputScale);
|
||
|
|
const mapVD = this._v1ImportRangeMap(schemaLookAt.rangeMapVerticalDown, defaultOutputScale);
|
||
|
|
const mapVU = this._v1ImportRangeMap(schemaLookAt.rangeMapVerticalUp, defaultOutputScale);
|
||
|
|
let applier;
|
||
|
|
if (schemaLookAt.type === "expression") {
|
||
|
|
applier = new VRMLookAtExpressionApplier(expressions, mapHI, mapHO, mapVD, mapVU);
|
||
|
|
} else {
|
||
|
|
applier = new VRMLookAtBoneApplier(humanoid, mapHI, mapHO, mapVD, mapVU);
|
||
|
|
}
|
||
|
|
const lookAt = this._importLookAt(humanoid, applier);
|
||
|
|
lookAt.offsetFromHeadBone.fromArray((_c = schemaLookAt.offsetFromHeadBone) != null ? _c : [0, 0.06, 0]);
|
||
|
|
return lookAt;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_v1ImportRangeMap(schemaRangeMap, defaultOutputScale) {
|
||
|
|
var _a, _b;
|
||
|
|
let inputMaxValue = (_a = schemaRangeMap == null ? void 0 : schemaRangeMap.inputMaxValue) != null ? _a : 90;
|
||
|
|
const outputScale = (_b = schemaRangeMap == null ? void 0 : schemaRangeMap.outputScale) != null ? _b : defaultOutputScale;
|
||
|
|
if (inputMaxValue < INPUT_MAX_VALUE_MINIMUM) {
|
||
|
|
console.warn(
|
||
|
|
"VRMLookAtLoaderPlugin: inputMaxValue of a range map is too small. Consider reviewing the range map!"
|
||
|
|
);
|
||
|
|
inputMaxValue = INPUT_MAX_VALUE_MINIMUM;
|
||
|
|
}
|
||
|
|
return new VRMLookAtRangeMap(inputMaxValue, outputScale);
|
||
|
|
}
|
||
|
|
_v0Import(gltf, humanoid, expressions) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
var _a, _b, _c, _d;
|
||
|
|
const json = this.parser.json;
|
||
|
|
const vrmExt = (_a = json.extensions) == null ? void 0 : _a.VRM;
|
||
|
|
if (!vrmExt) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const schemaFirstPerson = vrmExt.firstPerson;
|
||
|
|
if (!schemaFirstPerson) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const defaultOutputScale = schemaFirstPerson.lookAtTypeName === "BlendShape" ? 1 : 10;
|
||
|
|
const mapHI = this._v0ImportDegreeMap(schemaFirstPerson.lookAtHorizontalInner, defaultOutputScale);
|
||
|
|
const mapHO = this._v0ImportDegreeMap(schemaFirstPerson.lookAtHorizontalOuter, defaultOutputScale);
|
||
|
|
const mapVD = this._v0ImportDegreeMap(schemaFirstPerson.lookAtVerticalDown, defaultOutputScale);
|
||
|
|
const mapVU = this._v0ImportDegreeMap(schemaFirstPerson.lookAtVerticalUp, defaultOutputScale);
|
||
|
|
let applier;
|
||
|
|
if (schemaFirstPerson.lookAtTypeName === "BlendShape") {
|
||
|
|
applier = new VRMLookAtExpressionApplier(expressions, mapHI, mapHO, mapVD, mapVU);
|
||
|
|
} else {
|
||
|
|
applier = new VRMLookAtBoneApplier(humanoid, mapHI, mapHO, mapVD, mapVU);
|
||
|
|
}
|
||
|
|
const lookAt = this._importLookAt(humanoid, applier);
|
||
|
|
if (schemaFirstPerson.firstPersonBoneOffset) {
|
||
|
|
lookAt.offsetFromHeadBone.set(
|
||
|
|
(_b = schemaFirstPerson.firstPersonBoneOffset.x) != null ? _b : 0,
|
||
|
|
(_c = schemaFirstPerson.firstPersonBoneOffset.y) != null ? _c : 0.06,
|
||
|
|
-((_d = schemaFirstPerson.firstPersonBoneOffset.z) != null ? _d : 0)
|
||
|
|
);
|
||
|
|
} else {
|
||
|
|
lookAt.offsetFromHeadBone.set(0, 0.06, 0);
|
||
|
|
}
|
||
|
|
lookAt.faceFront.set(0, 0, -1);
|
||
|
|
if (applier instanceof VRMLookAtBoneApplier) {
|
||
|
|
applier.faceFront.set(0, 0, -1);
|
||
|
|
}
|
||
|
|
return lookAt;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_v0ImportDegreeMap(schemaDegreeMap, defaultOutputScale) {
|
||
|
|
var _a, _b;
|
||
|
|
const curve = schemaDegreeMap == null ? void 0 : schemaDegreeMap.curve;
|
||
|
|
if (JSON.stringify(curve) !== "[0,0,0,1,1,1,1,0]") {
|
||
|
|
console.warn("Curves of LookAtDegreeMap defined in VRM 0.0 are not supported");
|
||
|
|
}
|
||
|
|
let xRange = (_a = schemaDegreeMap == null ? void 0 : schemaDegreeMap.xRange) != null ? _a : 90;
|
||
|
|
const yRange = (_b = schemaDegreeMap == null ? void 0 : schemaDegreeMap.yRange) != null ? _b : defaultOutputScale;
|
||
|
|
if (xRange < INPUT_MAX_VALUE_MINIMUM) {
|
||
|
|
console.warn("VRMLookAtLoaderPlugin: xRange of a degree map is too small. Consider reviewing the degree map!");
|
||
|
|
xRange = INPUT_MAX_VALUE_MINIMUM;
|
||
|
|
}
|
||
|
|
return new VRMLookAtRangeMap(xRange, yRange);
|
||
|
|
}
|
||
|
|
_importLookAt(humanoid, applier) {
|
||
|
|
const lookAt = new VRMLookAt(humanoid, applier);
|
||
|
|
if (this.helperRoot) {
|
||
|
|
const helper = new VRMLookAtHelper(lookAt);
|
||
|
|
this.helperRoot.add(helper);
|
||
|
|
helper.renderOrder = this.helperRoot.renderOrder;
|
||
|
|
}
|
||
|
|
return lookAt;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var VRMLookAtTypeName = {
|
||
|
|
Bone: "bone",
|
||
|
|
Expression: "expression"
|
||
|
|
};
|
||
|
|
function resolveURL(url, path) {
|
||
|
|
if (typeof url !== "string" || url === "") return "";
|
||
|
|
if (/^https?:\/\//i.test(path) && /^\//.test(url)) {
|
||
|
|
path = path.replace(/(^https?:\/\/[^/]+).*/i, "$1");
|
||
|
|
}
|
||
|
|
if (/^(https?:)?\/\//i.test(url)) return url;
|
||
|
|
if (/^data:.*,.*$/i.test(url)) return url;
|
||
|
|
if (/^blob:.*$/i.test(url)) return url;
|
||
|
|
return path + url;
|
||
|
|
}
|
||
|
|
var POSSIBLE_SPEC_VERSIONS5 = /* @__PURE__ */ new Set(["1.0", "1.0-beta"]);
|
||
|
|
var VRMMetaLoaderPlugin = class {
|
||
|
|
get name() {
|
||
|
|
return "VRMMetaLoaderPlugin";
|
||
|
|
}
|
||
|
|
constructor(parser, options) {
|
||
|
|
var _a, _b, _c;
|
||
|
|
this.parser = parser;
|
||
|
|
this.needThumbnailImage = (_a = options == null ? void 0 : options.needThumbnailImage) != null ? _a : false;
|
||
|
|
this.acceptLicenseUrls = (_b = options == null ? void 0 : options.acceptLicenseUrls) != null ? _b : ["https://vrm.dev/licenses/1.0/"];
|
||
|
|
this.acceptV0Meta = (_c = options == null ? void 0 : options.acceptV0Meta) != null ? _c : true;
|
||
|
|
}
|
||
|
|
afterRoot(gltf) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
gltf.userData.vrmMeta = yield this._import(gltf);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_import(gltf) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
const v1Result = yield this._v1Import(gltf);
|
||
|
|
if (v1Result != null) {
|
||
|
|
return v1Result;
|
||
|
|
}
|
||
|
|
const v0Result = yield this._v0Import(gltf);
|
||
|
|
if (v0Result != null) {
|
||
|
|
return v0Result;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_v1Import(gltf) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
var _a, _b, _c;
|
||
|
|
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 == null) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const specVersion = extension.specVersion;
|
||
|
|
if (!POSSIBLE_SPEC_VERSIONS5.has(specVersion)) {
|
||
|
|
console.warn(`VRMMetaLoaderPlugin: Unknown VRMC_vrm specVersion "${specVersion}"`);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const schemaMeta = extension.meta;
|
||
|
|
if (!schemaMeta) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const licenseUrl = schemaMeta.licenseUrl;
|
||
|
|
const acceptLicenseUrlsSet = new Set(this.acceptLicenseUrls);
|
||
|
|
if (!acceptLicenseUrlsSet.has(licenseUrl)) {
|
||
|
|
throw new Error(`VRMMetaLoaderPlugin: The license url "${licenseUrl}" is not accepted`);
|
||
|
|
}
|
||
|
|
let thumbnailImage = void 0;
|
||
|
|
if (this.needThumbnailImage && schemaMeta.thumbnailImage != null) {
|
||
|
|
thumbnailImage = (_c = yield this._extractGLTFImage(schemaMeta.thumbnailImage)) != null ? _c : void 0;
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
metaVersion: "1",
|
||
|
|
name: schemaMeta.name,
|
||
|
|
version: schemaMeta.version,
|
||
|
|
authors: schemaMeta.authors,
|
||
|
|
copyrightInformation: schemaMeta.copyrightInformation,
|
||
|
|
contactInformation: schemaMeta.contactInformation,
|
||
|
|
references: schemaMeta.references,
|
||
|
|
thirdPartyLicenses: schemaMeta.thirdPartyLicenses,
|
||
|
|
thumbnailImage,
|
||
|
|
licenseUrl: schemaMeta.licenseUrl,
|
||
|
|
avatarPermission: schemaMeta.avatarPermission,
|
||
|
|
allowExcessivelyViolentUsage: schemaMeta.allowExcessivelyViolentUsage,
|
||
|
|
allowExcessivelySexualUsage: schemaMeta.allowExcessivelySexualUsage,
|
||
|
|
commercialUsage: schemaMeta.commercialUsage,
|
||
|
|
allowPoliticalOrReligiousUsage: schemaMeta.allowPoliticalOrReligiousUsage,
|
||
|
|
allowAntisocialOrHateUsage: schemaMeta.allowAntisocialOrHateUsage,
|
||
|
|
creditNotation: schemaMeta.creditNotation,
|
||
|
|
allowRedistribution: schemaMeta.allowRedistribution,
|
||
|
|
modification: schemaMeta.modification,
|
||
|
|
otherLicenseUrl: schemaMeta.otherLicenseUrl
|
||
|
|
};
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_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 schemaMeta = vrmExt.meta;
|
||
|
|
if (!schemaMeta) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
if (!this.acceptV0Meta) {
|
||
|
|
throw new Error("VRMMetaLoaderPlugin: Attempted to load VRM0.0 meta but acceptV0Meta is false");
|
||
|
|
}
|
||
|
|
let texture;
|
||
|
|
if (this.needThumbnailImage && schemaMeta.texture != null && schemaMeta.texture !== -1) {
|
||
|
|
texture = yield this.parser.getDependency("texture", schemaMeta.texture);
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
metaVersion: "0",
|
||
|
|
allowedUserName: schemaMeta.allowedUserName,
|
||
|
|
author: schemaMeta.author,
|
||
|
|
commercialUssageName: schemaMeta.commercialUssageName,
|
||
|
|
contactInformation: schemaMeta.contactInformation,
|
||
|
|
licenseName: schemaMeta.licenseName,
|
||
|
|
otherLicenseUrl: schemaMeta.otherLicenseUrl,
|
||
|
|
otherPermissionUrl: schemaMeta.otherPermissionUrl,
|
||
|
|
reference: schemaMeta.reference,
|
||
|
|
sexualUssageName: schemaMeta.sexualUssageName,
|
||
|
|
texture: texture != null ? texture : void 0,
|
||
|
|
title: schemaMeta.title,
|
||
|
|
version: schemaMeta.version,
|
||
|
|
violentUssageName: schemaMeta.violentUssageName
|
||
|
|
};
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_extractGLTFImage(index) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
var _a;
|
||
|
|
const json = this.parser.json;
|
||
|
|
const source = (_a = json.images) == null ? void 0 : _a[index];
|
||
|
|
if (source == null) {
|
||
|
|
console.warn(
|
||
|
|
`VRMMetaLoaderPlugin: Attempt to use images[${index}] of glTF as a thumbnail but the image doesn't exist`
|
||
|
|
);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
let sourceURI = source.uri;
|
||
|
|
if (source.bufferView != null) {
|
||
|
|
const bufferView = yield this.parser.getDependency("bufferView", source.bufferView);
|
||
|
|
const blob = new Blob([bufferView], { type: source.mimeType });
|
||
|
|
sourceURI = URL.createObjectURL(blob);
|
||
|
|
}
|
||
|
|
if (sourceURI == null) {
|
||
|
|
console.warn(
|
||
|
|
`VRMMetaLoaderPlugin: Attempt to use images[${index}] of glTF as a thumbnail but the image couldn't load properly`
|
||
|
|
);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const loader = new THREE16.ImageLoader();
|
||
|
|
return yield loader.loadAsync(resolveURL(sourceURI, this.parser.options.path)).catch((error) => {
|
||
|
|
console.error(error);
|
||
|
|
console.warn("VRMMetaLoaderPlugin: Failed to load a thumbnail image");
|
||
|
|
return null;
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var VRMCore = class {
|
||
|
|
/**
|
||
|
|
* Create a new VRM instance.
|
||
|
|
*
|
||
|
|
* @param params {@link VRMParameters} that represents components of the VRM
|
||
|
|
*/
|
||
|
|
constructor(params) {
|
||
|
|
this.scene = params.scene;
|
||
|
|
this.meta = params.meta;
|
||
|
|
this.humanoid = params.humanoid;
|
||
|
|
this.expressionManager = params.expressionManager;
|
||
|
|
this.firstPerson = params.firstPerson;
|
||
|
|
this.lookAt = params.lookAt;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* **You need to call this on your update loop.**
|
||
|
|
*
|
||
|
|
* This function updates every VRM components.
|
||
|
|
*
|
||
|
|
* @param delta deltaTime
|
||
|
|
*/
|
||
|
|
update(delta) {
|
||
|
|
this.humanoid.update();
|
||
|
|
if (this.lookAt) {
|
||
|
|
this.lookAt.update(delta);
|
||
|
|
}
|
||
|
|
if (this.expressionManager) {
|
||
|
|
this.expressionManager.update();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var VRMCoreLoaderPlugin = class {
|
||
|
|
get name() {
|
||
|
|
return "VRMC_vrm";
|
||
|
|
}
|
||
|
|
constructor(parser, options) {
|
||
|
|
var _a, _b, _c, _d, _e;
|
||
|
|
this.parser = parser;
|
||
|
|
const helperRoot = options == null ? void 0 : options.helperRoot;
|
||
|
|
const autoUpdateHumanBones = options == null ? void 0 : options.autoUpdateHumanBones;
|
||
|
|
this.expressionPlugin = (_a = options == null ? void 0 : options.expressionPlugin) != null ? _a : new VRMExpressionLoaderPlugin(parser);
|
||
|
|
this.firstPersonPlugin = (_b = options == null ? void 0 : options.firstPersonPlugin) != null ? _b : new VRMFirstPersonLoaderPlugin(parser);
|
||
|
|
this.humanoidPlugin = (_c = options == null ? void 0 : options.humanoidPlugin) != null ? _c : new VRMHumanoidLoaderPlugin(parser, { helperRoot, autoUpdateHumanBones });
|
||
|
|
this.lookAtPlugin = (_d = options == null ? void 0 : options.lookAtPlugin) != null ? _d : new VRMLookAtLoaderPlugin(parser, { helperRoot });
|
||
|
|
this.metaPlugin = (_e = options == null ? void 0 : options.metaPlugin) != null ? _e : new VRMMetaLoaderPlugin(parser);
|
||
|
|
}
|
||
|
|
afterRoot(gltf) {
|
||
|
|
return __async2(this, null, function* () {
|
||
|
|
yield this.metaPlugin.afterRoot(gltf);
|
||
|
|
yield this.humanoidPlugin.afterRoot(gltf);
|
||
|
|
yield this.expressionPlugin.afterRoot(gltf);
|
||
|
|
yield this.lookAtPlugin.afterRoot(gltf);
|
||
|
|
yield this.firstPersonPlugin.afterRoot(gltf);
|
||
|
|
const meta = gltf.userData.vrmMeta;
|
||
|
|
const humanoid = gltf.userData.vrmHumanoid;
|
||
|
|
if (meta && humanoid) {
|
||
|
|
const vrmCore = new VRMCore({
|
||
|
|
scene: gltf.scene,
|
||
|
|
expressionManager: gltf.userData.vrmExpressionManager,
|
||
|
|
firstPerson: gltf.userData.vrmFirstPerson,
|
||
|
|
humanoid,
|
||
|
|
lookAt: gltf.userData.vrmLookAt,
|
||
|
|
meta
|
||
|
|
});
|
||
|
|
gltf.userData.vrmCore = vrmCore;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// src/VRM.ts
|
||
|
|
var VRM = class extends VRMCore {
|
||
|
|
/**
|
||
|
|
* Create a new VRM instance.
|
||
|
|
*
|
||
|
|
* @param params {@link VRMParameters} that represents components of the VRM
|
||
|
|
*/
|
||
|
|
constructor(params) {
|
||
|
|
super(params);
|
||
|
|
this.materials = params.materials;
|
||
|
|
this.springBoneManager = params.springBoneManager;
|
||
|
|
this.nodeConstraintManager = params.nodeConstraintManager;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* **You need to call this on your update loop.**
|
||
|
|
*
|
||
|
|
* This function updates every VRM components.
|
||
|
|
*
|
||
|
|
* @param delta deltaTime
|
||
|
|
*/
|
||
|
|
update(delta) {
|
||
|
|
super.update(delta);
|
||
|
|
if (this.nodeConstraintManager) {
|
||
|
|
this.nodeConstraintManager.update();
|
||
|
|
}
|
||
|
|
if (this.springBoneManager) {
|
||
|
|
this.springBoneManager.update(delta);
|
||
|
|
}
|
||
|
|
if (this.materials) {
|
||
|
|
this.materials.forEach((material) => {
|
||
|
|
if (material.update) {
|
||
|
|
material.update(delta);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// ../three-vrm-materials-mtoon/lib/three-vrm-materials-mtoon.module.js
|
||
|
|
var THREE52 = __toESM(require("three"), 1);
|
||
|
|
var THREE22 = __toESM(require("three"), 1);
|
||
|
|
var THREE17 = __toESM(require("three"), 1);
|
||
|
|
var THREE42 = __toESM(require("three"), 1);
|
||
|
|
var THREE32 = __toESM(require("three"), 1);
|
||
|
|
var __defProp2 = Object.defineProperty;
|
||
|
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
||
|
|
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
||
|
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
||
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||
|
|
var __spreadValues = (a, b) => {
|
||
|
|
for (var prop in b || (b = {}))
|
||
|
|
if (__hasOwnProp2.call(b, prop))
|
||
|
|
__defNormalProp(a, prop, b[prop]);
|
||
|
|
if (__getOwnPropSymbols)
|
||
|
|
for (var prop of __getOwnPropSymbols(b)) {
|
||
|
|
if (__propIsEnum.call(b, prop))
|
||
|
|
__defNormalProp(a, prop, b[prop]);
|
||
|
|
}
|
||
|
|
return a;
|
||
|
|
};
|
||
|
|
var __async3 = (__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 colorSpaceEncodingMap = {
|
||
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||
|
|
"": 3e3,
|
||
|
|
srgb: 3001
|
||
|
|
};
|
||
|
|
function setTextureColorSpace(texture, colorSpace) {
|
||
|
|
if (parseInt(THREE17.REVISION, 10) >= 152) {
|
||
|
|
texture.colorSpace = colorSpace;
|
||
|
|
} else {
|
||
|
|
texture.encoding = colorSpaceEncodingMap[colorSpace];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
var GLTFMToonMaterialParamsAssignHelper = class {
|
||
|
|
get pending() {
|
||
|
|
return Promise.all(this._pendings);
|
||
|
|
}
|
||
|
|
constructor(parser, materialParams) {
|
||
|
|
this._parser = parser;
|
||
|
|
this._materialParams = materialParams;
|
||
|
|
this._pendings = [];
|
||
|
|
}
|
||
|
|
assignPrimitive(key, value) {
|
||
|
|
if (value != null) {
|
||
|
|
this._materialParams[key] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
assignColor(key, value, convertSRGBToLinear) {
|
||
|
|
if (value != null) {
|
||
|
|
this._materialParams[key] = new THREE22.Color().fromArray(value);
|
||
|
|
if (convertSRGBToLinear) {
|
||
|
|
this._materialParams[key].convertSRGBToLinear();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
assignTexture(key, texture, isColorTexture) {
|
||
|
|
return __async3(this, null, function* () {
|
||
|
|
const promise = (() => __async3(this, null, function* () {
|
||
|
|
if (texture != null) {
|
||
|
|
yield this._parser.assignTexture(this._materialParams, key, texture);
|
||
|
|
if (isColorTexture) {
|
||
|
|
setTextureColorSpace(this._materialParams[key], "srgb");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}))();
|
||
|
|
this._pendings.push(promise);
|
||
|
|
return promise;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
assignTextureByIndex(key, textureIndex, isColorTexture) {
|
||
|
|
return __async3(this, null, function* () {
|
||
|
|
return this.assignTexture(key, textureIndex != null ? { index: textureIndex } : void 0, isColorTexture);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var mtoon_default = "// #define PHONG\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\n\n#include <common>\n\n// #include <uv_pars_vertex>\n#ifdef MTOON_USE_UV\n varying vec2 vUv;\n\n // COMPAT: pre-r151 uses a common uvTransform\n #if THREE_VRM_THREE_REVISION < 151\n uniform mat3 uvTransform;\n #endif\n#endif\n\n// #include <uv2_pars_vertex>\n// COMAPT: pre-r151 uses uv2 for lightMap and aoMap\n#if THREE_VRM_THREE_REVISION < 151\n #if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n attribute vec2 uv2;\n varying vec2 vUv2;\n uniform mat3 uv2Transform;\n #endif\n#endif\n\n// #include <displacementmap_pars_vertex>\n// #include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\n\n#ifdef USE_OUTLINEWIDTHMULTIPLYTEXTURE\n uniform sampler2D outlineWidthMultiplyTexture;\n uniform mat3 outlineWidthMultiplyTextureUvTransform;\n#endif\n\nuniform float outlineWidthFactor;\n\nvoid main() {\n\n // #include <uv_vertex>\n #ifdef MTOON_USE_UV\n // COMPAT: pre-r151 uses a common uvTransform\n #if THREE_VRM_THREE_REVISION >= 151\n vUv = uv;\n #else\n vUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n #endif\n #endif\n\n // #include <uv2_vertex>\n // COMAPT: pre-r151 uses uv2 for lightMap and aoMap\n #if THREE_VRM_THREE_REVISION < 151\n #if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n vUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n #endif\n #endif\n\n #include <color_vertex>\n\n #include <beginnormal_vertex>\n #include <morphnormal_vertex>\n #include <skinbase_vertex>\n #include <skinnormal_vertex>\n\n // we need this to compute the outline properly\n objectNormal = normalize( objectNormal );\n\n #include <defaultnormal_vertex>\n\n #ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED\n vNormal = normalize( transformedNormal );\n #endif\n\n #include <begin_vertex>\n\n #include <morphtarget_vertex>\n #include <skinning_vertex>\n // #include <displacementmap_vertex>\n #include <project_vertex>\n #include <logdepthbuf_vertex>\n #include <clipping_planes_vertex>\n\n vViewPosition = - mvPosition.xyz;\n\n #ifdef OUTLINE\n float worldNormalLength = length( transformedNormal );\n vec3 outlineOffset = outlineWidthFactor * worldNormalLength * objectNormal;\n\n #ifdef USE_OUTLINEWIDTHMULTIPLYTEXTURE\n vec2 outlineWidthMultiplyTextureUv = ( outlineWidthMultiplyTextureUvTransform * vec3( vUv, 1 ) ).xy;\n float outlineTex = texture2D( outlineWidthMultiplyTexture, outlineWidthMultiplyTextureUv ).g;\n outlineOffset *= outlineTex;\n #endif\n\n #ifdef OUTLINE_WIDTH_SCREEN\n outlineOffset *= vViewPosition.z / projectionMatrix[ 1 ].y;\n #endif\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4( outlineOffset + transformed, 1.0 );\n\n gl_Position.z += 1E-6 * gl_Position.w; // anti-artifact magic\n #endif\n\n #include <worldpos_vertex>\n // #include <envmap_vertex>\n #include <shadowmap_vertex>\n #include <fog_vertex>\n\n}";
|
||
|
|
var mtoon_default2 = "// #define PHONG\n\nuniform vec3 litFactor;\n\nuniform float opacity;\n\nuniform vec3 shadeColorFactor;\n#ifdef USE_SHADEMULTIPLYTEXTURE\n uniform sampler2D shadeMultiplyTexture;\n uniform mat3 shadeMultiplyTextureUvTransform;\n#endif\n\nuniform float shadingShiftFactor;\nuniform float shadingToonyFactor;\n\n#ifdef USE_SHADINGSHIFTTEXTURE\n uniform sampler2D shadingShiftTexture;\n uniform mat3 shadingShiftTextureUvTransform;\n uniform float shadingShiftTextureScale;\n#endif\n\nuniform float giEqualizationFactor;\n\nuniform vec3 parametricRimColorFactor;\n#ifdef USE_RIMMULTIPLYTEXTURE\n uniform sampler2D rimMultiplyTexture;\n uniform mat3 rimMultiplyTextureUvTransform;\n#endif\nuniform float rimLightingMixFactor;\nuniform float parametricRimFresnelPowerFactor;\nuniform float parametricRimLiftFactor;\n\n#ifdef USE_MATCAPTEXTURE\n uniform vec3 matcapFactor;\n uniform sampler2D matcapTexture;\n uniform mat3 matcapTextureUvTransform;\n#endif\n\nuniform vec3 emissive;\nuniform float emissiveIntensity;\n\nuniform vec3 outlineColorFactor;\nuniform float outlineLightingMixFactor;\n\n#ifdef USE_UVANIMATIONMASKTEXTURE\n uniform sampler2D uvAnimationMaskTexture;\n uniform mat3 uvAnimationMaskTextureUvTransform;\n#endif\n\nuniform float uvAnimationScrollXOffset;\nuniform float uvAnimationScrollYOffset;\nuniform float uvAnimationRotationPhase;\n\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n\n// #include <uv_pars_fragment>\n#if ( defined( MTOON_USE_UV ) && !defined( MTOON_UVS_VERTEX_ONLY ) )\n varying vec2 vUv;\n#endif\n\n// #include <uv2_pars_fragment>\n// COMAPT: pre-r151 uses uv2 for lightMap and aoMap\n#if THREE_VRM_THREE_REVISION < 151\n #if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n varying vec2 vUv2;\n #endif\n#endif\n\n#include <map_pars_fragment>\n\n#ifdef USE_MAP\n uniform mat3 mapUvTransform;\n#endif\n\n// #include <alphamap_pars_fragment>\n\n#include <alphatest_pars_fragment>\n\n#include <aomap_pars_fragment>\n// #include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n\n#ifdef USE_EMISSIVEMAP\n uniform mat3 emissiveMapUvTransform;\n#endif\n\n// #include <envmap_common_pars_fragment>\n// #include <envmap_pars_fragment>\n// #include <cube_uv_reflection_fragment>\n#include <fog_pars_fragment>\n\n// #include <bsdfs>\n// COMPAT: pre-r151 doesn't have BRDF_Lambert in <common>\n#if THREE_VRM_THREE_REVISION < 151\n vec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n return RECIPROCAL_PI * diffuseColor;\n }\n#endif\n\n#include <lights_pars_begin>\n\n#include <normal_pars_fragment>\n\n// #include <lights_phong_pars_fragment>\nvarying vec3 vViewPosition;\n\nstruct MToonMaterial {\n vec3 diffuseColor;\n vec3 shadeColor;\n float shadingShift;\n};\n\nfloat linearstep( float a, float b, float t ) {\n return clamp( ( t - a ) / ( b - a ), 0.0, 1.0 );\n}\n\n/**\n * Convert NdotL into toon shading factor using shadingShift and shadingToony\n */\nfloat getShading(\n const in float dotNL,\n const in float shadow,\n const in float shadingShift\n) {\n float shading = dotNL;\n shading = shading + shadingShift;\n shading = linearstep( -1.0 + shadingToonyFactor, 1.0 - shadingToonyFactor, shading );\n shading *= shadow;\n return shading;\n}\n\n/**\n * Mix diffuseColor and shadeColor using shading factor and light color\n */\nvec3 getDiffuse(\n const in MToonMaterial material,\n const in float shading,\n in vec3 lightColor\n) {\n #ifdef DEBUG_LITSHADERATE\n return vec3( BRDF_Lambert( shading * lightColor ) );\n #endif\n\n vec3 col = lightColor * BRDF_Lambert( mix( material.shadeColor, material.diffuseColor, shading ) );\n\n // The \"comment out if you want to PBR absolutely\" line\n #ifdef V0_COMPAT_SHADE\n col = min( col, material.diffuseColor );\n #endif\n\n return col;\n}\n\n// COMPAT: pre-r156 uses a struct GeometricContext\n#if THREE_VRM_THREE_REVISION >= 157\n void RE_Direct_MToon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const
|
||
|
|
var MToonMaterialDebugMode = {
|
||
|
|
/**
|
||
|
|
* Render normally.
|
||
|
|
*/
|
||
|
|
None: "none",
|
||
|
|
/**
|
||
|
|
* Visualize normals of the surface.
|
||
|
|
*/
|
||
|
|
Normal: "normal",
|
||
|
|
/**
|
||
|
|
* Visualize lit/shade of the surface.
|
||
|
|
*/
|
||
|
|
LitShadeRate: "litShadeRate",
|
||
|
|
/**
|
||
|
|
* Visualize UV of the surface.
|
||
|
|
*/
|
||
|
|
UV: "uv"
|
||
|
|
};
|
||
|
|
var MToonMaterialOutlineWidthMode = {
|
||
|
|
None: "none",
|
||
|
|
WorldCoordinates: "worldCoordinates",
|
||
|
|
ScreenCoordinates: "screenCoordinates"
|
||
|
|
};
|
||
|
|
var encodingColorSpaceMap = {
|
||
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||
|
|
3e3: "",
|
||
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||
|
|
3001: "srgb"
|
||
|
|
};
|
||
|
|
function getTextureColorSpace(texture) {
|
||
|
|
if (parseInt(THREE32.REVISION, 10) >= 152) {
|
||
|
|
return texture.colorSpace;
|
||
|
|
} else {
|
||
|
|
return encodingColorSpaceMap[texture.encoding];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
var MToonMaterial = class extends THREE42.ShaderMaterial {
|
||
|
|
constructor(parameters = {}) {
|
||
|
|
var _a;
|
||
|
|
super({ vertexShader: mtoon_default, fragmentShader: mtoon_default2 });
|
||
|
|
this.uvAnimationScrollXSpeedFactor = 0;
|
||
|
|
this.uvAnimationScrollYSpeedFactor = 0;
|
||
|
|
this.uvAnimationRotationSpeedFactor = 0;
|
||
|
|
this.fog = true;
|
||
|
|
this.normalMapType = THREE42.TangentSpaceNormalMap;
|
||
|
|
this._ignoreVertexColor = true;
|
||
|
|
this._v0CompatShade = false;
|
||
|
|
this._debugMode = MToonMaterialDebugMode.None;
|
||
|
|
this._outlineWidthMode = MToonMaterialOutlineWidthMode.None;
|
||
|
|
this._isOutline = false;
|
||
|
|
if (parameters.transparentWithZWrite) {
|
||
|
|
parameters.depthWrite = true;
|
||
|
|
}
|
||
|
|
delete parameters.transparentWithZWrite;
|
||
|
|
parameters.fog = true;
|
||
|
|
parameters.lights = true;
|
||
|
|
parameters.clipping = true;
|
||
|
|
this.uniforms = THREE42.UniformsUtils.merge([
|
||
|
|
THREE42.UniformsLib.common,
|
||
|
|
// map
|
||
|
|
THREE42.UniformsLib.normalmap,
|
||
|
|
// normalMap
|
||
|
|
THREE42.UniformsLib.emissivemap,
|
||
|
|
// emissiveMap
|
||
|
|
THREE42.UniformsLib.fog,
|
||
|
|
THREE42.UniformsLib.lights,
|
||
|
|
{
|
||
|
|
litFactor: { value: new THREE42.Color(1, 1, 1) },
|
||
|
|
mapUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
colorAlpha: { value: 1 },
|
||
|
|
normalMapUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
shadeColorFactor: { value: new THREE42.Color(0, 0, 0) },
|
||
|
|
shadeMultiplyTexture: { value: null },
|
||
|
|
shadeMultiplyTextureUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
shadingShiftFactor: { value: 0 },
|
||
|
|
shadingShiftTexture: { value: null },
|
||
|
|
shadingShiftTextureUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
shadingShiftTextureScale: { value: 1 },
|
||
|
|
shadingToonyFactor: { value: 0.9 },
|
||
|
|
giEqualizationFactor: { value: 0.9 },
|
||
|
|
matcapFactor: { value: new THREE42.Color(1, 1, 1) },
|
||
|
|
matcapTexture: { value: null },
|
||
|
|
matcapTextureUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
parametricRimColorFactor: { value: new THREE42.Color(0, 0, 0) },
|
||
|
|
rimMultiplyTexture: { value: null },
|
||
|
|
rimMultiplyTextureUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
rimLightingMixFactor: { value: 1 },
|
||
|
|
parametricRimFresnelPowerFactor: { value: 5 },
|
||
|
|
parametricRimLiftFactor: { value: 0 },
|
||
|
|
emissive: { value: new THREE42.Color(0, 0, 0) },
|
||
|
|
emissiveIntensity: { value: 1 },
|
||
|
|
emissiveMapUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
outlineWidthMultiplyTexture: { value: null },
|
||
|
|
outlineWidthMultiplyTextureUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
outlineWidthFactor: { value: 0 },
|
||
|
|
outlineColorFactor: { value: new THREE42.Color(0, 0, 0) },
|
||
|
|
outlineLightingMixFactor: { value: 1 },
|
||
|
|
uvAnimationMaskTexture: { value: null },
|
||
|
|
uvAnimationMaskTextureUvTransform: { value: new THREE42.Matrix3() },
|
||
|
|
uvAnimationScrollXOffset: { value: 0 },
|
||
|
|
uvAnimationScrollYOffset: { value: 0 },
|
||
|
|
uvAnimationRotationPhase: { value: 0 }
|
||
|
|
},
|
||
|
|
(_a = parameters.uniforms) != null ? _a : {}
|
||
|
|
]);
|
||
|
|
this.setValues(parameters);
|
||
|
|
this._uploadUniformsWorkaround();
|
||
|
|
this.customProgramCacheKey = () => [
|
||
|
|
...Object.entries(this._generateDefines()).map(([token, macro]) => `${token}:${macro}`),
|
||
|
|
this.matcapTexture ? `matcapTextureColorSpace:${getTextureColorSpace(this.matcapTexture)}` : "",
|
||
|
|
this.shadeMultiplyTexture ? `shadeMultiplyTextureColorSpace:${getTextureColorSpace(this.shadeMultiplyTexture)}` : "",
|
||
|
|
this.rimMultiplyTexture ? `rimMultiplyTextureColorSpace:${getTextureColorSpace(this.rimMultiplyTexture)}` : ""
|
||
|
|
].join(",");
|
||
|
|
this.onBeforeCompile = (shader) => {
|
||
|
|
const threeRevision = parseInt(THREE42.REVISION, 10);
|
||
|
|
const defines = Object.entries(__spreadValues(__spreadValues({}, this._generateDefines()), this.defines)).filter(([token, macro]) => !!macro).map(([token, macro]) => `#define ${token} ${macro}`).join("\n") + "\n";
|
||
|
|
shader.vertexShader = defines + shader.vertexShader;
|
||
|
|
shader.fragmentShader = defines + shader.fragmentShader;
|
||
|
|
if (threeRevision < 154) {
|
||
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
||
|
|
"#include <colorspace_fragment>",
|
||
|
|
"#include <encodings_fragment>"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
get color() {
|
||
|
|
return this.uniforms.litFactor.value;
|
||
|
|
}
|
||
|
|
set color(value) {
|
||
|
|
this.uniforms.litFactor.value = value;
|
||
|
|
}
|
||
|
|
get map() {
|
||
|
|
return this.uniforms.map.value;
|
||
|
|
}
|
||
|
|
set map(value) {
|
||
|
|
this.uniforms.map.value = value;
|
||
|
|
}
|
||
|
|
get normalMap() {
|
||
|
|
return this.uniforms.normalMap.value;
|
||
|
|
}
|
||
|
|
set normalMap(value) {
|
||
|
|
this.uniforms.normalMap.value = value;
|
||
|
|
}
|
||
|
|
get normalScale() {
|
||
|
|
return this.uniforms.normalScale.value;
|
||
|
|
}
|
||
|
|
set normalScale(value) {
|
||
|
|
this.uniforms.normalScale.value = value;
|
||
|
|
}
|
||
|
|
get emissive() {
|
||
|
|
return this.uniforms.emissive.value;
|
||
|
|
}
|
||
|
|
set emissive(value) {
|
||
|
|
this.uniforms.emissive.value = value;
|
||
|
|
}
|
||
|
|
get emissiveIntensity() {
|
||
|
|
return this.uniforms.emissiveIntensity.value;
|
||
|
|
}
|
||
|
|
set emissiveIntensity(value) {
|
||
|
|
this.uniforms.emissiveIntensity.value = value;
|
||
|
|
}
|
||
|
|
get emissiveMap() {
|
||
|
|
return this.uniforms.emissiveMap.value;
|
||
|
|
}
|
||
|
|
set emissiveMap(value) {
|
||
|
|
this.uniforms.emissiveMap.value = value;
|
||
|
|
}
|
||
|
|
get shadeColorFactor() {
|
||
|
|
return this.uniforms.shadeColorFactor.value;
|
||
|
|
}
|
||
|
|
set shadeColorFactor(value) {
|
||
|
|
this.uniforms.shadeColorFactor.value = value;
|
||
|
|
}
|
||
|
|
get shadeMultiplyTexture() {
|
||
|
|
return this.uniforms.shadeMultiplyTexture.value;
|
||
|
|
}
|
||
|
|
set shadeMultiplyTexture(value) {
|
||
|
|
this.uniforms.shadeMultiplyTexture.value = value;
|
||
|
|
}
|
||
|
|
get shadingShiftFactor() {
|
||
|
|
return this.uniforms.shadingShiftFactor.value;
|
||
|
|
}
|
||
|
|
set shadingShiftFactor(value) {
|
||
|
|
this.uniforms.shadingShiftFactor.value = value;
|
||
|
|
}
|
||
|
|
get shadingShiftTexture() {
|
||
|
|
return this.uniforms.shadingShiftTexture.value;
|
||
|
|
}
|
||
|
|
set shadingShiftTexture(value) {
|
||
|
|
this.uniforms.shadingShiftTexture.value = value;
|
||
|
|
}
|
||
|
|
get shadingShiftTextureScale() {
|
||
|
|
return this.uniforms.shadingShiftTextureScale.value;
|
||
|
|
}
|
||
|
|
set shadingShiftTextureScale(value) {
|
||
|
|
this.uniforms.shadingShiftTextureScale.value = value;
|
||
|
|
}
|
||
|
|
get shadingToonyFactor() {
|
||
|
|
return this.uniforms.shadingToonyFactor.value;
|
||
|
|
}
|
||
|
|
set shadingToonyFactor(value) {
|
||
|
|
this.uniforms.shadingToonyFactor.value = value;
|
||
|
|
}
|
||
|
|
get giEqualizationFactor() {
|
||
|
|
return this.uniforms.giEqualizationFactor.value;
|
||
|
|
}
|
||
|
|
set giEqualizationFactor(value) {
|
||
|
|
this.uniforms.giEqualizationFactor.value = value;
|
||
|
|
}
|
||
|
|
get matcapFactor() {
|
||
|
|
return this.uniforms.matcapFactor.value;
|
||
|
|
}
|
||
|
|
set matcapFactor(value) {
|
||
|
|
this.uniforms.matcapFactor.value = value;
|
||
|
|
}
|
||
|
|
get matcapTexture() {
|
||
|
|
return this.uniforms.matcapTexture.value;
|
||
|
|
}
|
||
|
|
set matcapTexture(value) {
|
||
|
|
this.uniforms.matcapTexture.value = value;
|
||
|
|
}
|
||
|
|
get parametricRimColorFactor() {
|
||
|
|
return this.uniforms.parametricRimColorFactor.value;
|
||
|
|
}
|
||
|
|
set parametricRimColorFactor(value) {
|
||
|
|
this.uniforms.parametricRimColorFactor.value = value;
|
||
|
|
}
|
||
|
|
get rimMultiplyTexture() {
|
||
|
|
return this.uniforms.rimMultiplyTexture.value;
|
||
|
|
}
|
||
|
|
set rimMultiplyTexture(value) {
|
||
|
|
this.uniforms.rimMultiplyTexture.value = value;
|
||
|
|
}
|
||
|
|
get rimLightingMixFactor() {
|
||
|
|
return this.uniforms.rimLightingMixFactor.value;
|
||
|
|
}
|
||
|
|
set rimLightingMixFactor(value) {
|
||
|
|
this.uniforms.rimLightingMixFactor.value = value;
|
||
|
|
}
|
||
|
|
get parametricRimFresnelPowerFactor() {
|
||
|
|
return this.uniforms.parametricRimFresnelPowerFactor.value;
|
||
|
|
}
|
||
|
|
set parametricRimFresnelPowerFactor(value) {
|
||
|
|
this.uniforms.parametricRimFresnelPowerFactor.value = value;
|
||
|
|
}
|
||
|
|
get parametricRimLiftFactor() {
|
||
|
|
return this.uniforms.parametricRimLiftFactor.value;
|
||
|
|
}
|
||
|
|
set parametricRimLiftFactor(value) {
|
||
|
|
this.uniforms.parametricRimLiftFactor.value = value;
|
||
|
|
}
|
||
|
|
get outlineWidthMultiplyTexture() {
|
||
|
|
return this.uniforms.outlineWidthMultiplyTexture.value;
|
||
|
|
}
|
||
|
|
set outlineWidthMultiplyTexture(value) {
|
||
|
|
this.uniforms.outlineWidthMultiplyTexture.value = value;
|
||
|
|
}
|
||
|
|
get outlineWidthFactor() {
|
||
|
|
return this.uniforms.outlineWidthFactor.value;
|
||
|
|
}
|
||
|
|
set outlineWidthFactor(value) {
|
||
|
|
this.uniforms.outlineWidthFactor.value = value;
|
||
|
|
}
|
||
|
|
get outlineColorFactor() {
|
||
|
|
return this.uniforms.outlineColorFactor.value;
|
||
|
|
}
|
||
|
|
set outlineColorFactor(value) {
|
||
|
|
this.uniforms.outlineColorFactor.value = value;
|
||
|
|
}
|
||
|
|
get outlineLightingMixFactor() {
|
||
|
|
return this.uniforms.outlineLightingMixFactor.value;
|
||
|
|
}
|
||
|
|
set outlineLightingMixFactor(value) {
|
||
|
|
this.uniforms.outlineLightingMixFactor.value = value;
|
||
|
|
}
|
||
|
|
get uvAnimationMaskTexture() {
|
||
|
|
return this.uniforms.uvAnimationMaskTexture.value;
|
||
|
|
}
|
||
|
|
set uvAnimationMaskTexture(value) {
|
||
|
|
this.uniforms.uvAnimationMaskTexture.value = value;
|
||
|
|
}
|
||
|
|
get uvAnimationScrollXOffset() {
|
||
|
|
return this.uniforms.uvAnimationScrollXOffset.value;
|
||
|
|
}
|
||
|
|
set uvAnimationScrollXOffset(value) {
|
||
|
|
this.uniforms.uvAnimationScrollXOffset.value = value;
|
||
|
|
}
|
||
|
|
get uvAnimationScrollYOffset() {
|
||
|
|
return this.uniforms.uvAnimationScrollYOffset.value;
|
||
|
|
}
|
||
|
|
set uvAnimationScrollYOffset(value) {
|
||
|
|
this.uniforms.uvAnimationScrollYOffset.value = value;
|
||
|
|
}
|
||
|
|
get uvAnimationRotationPhase() {
|
||
|
|
return this.uniforms.uvAnimationRotationPhase.value;
|
||
|
|
}
|
||
|
|
set uvAnimationRotationPhase(value) {
|
||
|
|
this.uniforms.uvAnimationRotationPhase.value = value;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* When this is `true`, vertex colors will be ignored.
|
||
|
|
* `true` by default.
|
||
|
|
*/
|
||
|
|
get ignoreVertexColor() {
|
||
|
|
return this._ignoreVertexColor;
|
||
|
|
}
|
||
|
|
set ignoreVertexColor(value) {
|
||
|
|
this._ignoreVertexColor = value;
|
||
|
|
this.needsUpdate = true;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* There is a line of the shader called "comment out if you want to PBR absolutely" in VRM0.0 MToon.
|
||
|
|
* When this is true, the material enables the line to make it compatible with the legacy rendering of VRM.
|
||
|
|
* Usually not recommended to turn this on.
|
||
|
|
* `false` by default.
|
||
|
|
*/
|
||
|
|
get v0CompatShade() {
|
||
|
|
return this._v0CompatShade;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* There is a line of the shader called "comment out if you want to PBR absolutely" in VRM0.0 MToon.
|
||
|
|
* When this is true, the material enables the line to make it compatible with the legacy rendering of VRM.
|
||
|
|
* Usually not recommended to turn this on.
|
||
|
|
* `false` by default.
|
||
|
|
*/
|
||
|
|
set v0CompatShade(v) {
|
||
|
|
this._v0CompatShade = v;
|
||
|
|
this.needsUpdate = true;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Debug mode for the material.
|
||
|
|
* You can visualize several components for diagnosis using debug mode.
|
||
|
|
*
|
||
|
|
* See: {@link MToonMaterialDebugMode}
|
||
|
|
*/
|
||
|
|
get debugMode() {
|
||
|
|
return this._debugMode;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Debug mode for the material.
|
||
|
|
* You can visualize several components for diagnosis using debug mode.
|
||
|
|
*
|
||
|
|
* See: {@link MToonMaterialDebugMode}
|
||
|
|
*/
|
||
|
|
set debugMode(m) {
|
||
|
|
this._debugMode = m;
|
||
|
|
this.needsUpdate = true;
|
||
|
|
}
|
||
|
|
get outlineWidthMode() {
|
||
|
|
return this._outlineWidthMode;
|
||
|
|
}
|
||
|
|
set outlineWidthMode(m) {
|
||
|
|
this._outlineWidthMode = m;
|
||
|
|
this.needsUpdate = true;
|
||
|
|
}
|
||
|
|
get isOutline() {
|
||
|
|
return this._isOutline;
|
||
|
|
}
|
||
|
|
set isOutline(b) {
|
||
|
|
this._isOutline = b;
|
||
|
|
this.needsUpdate = true;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Readonly boolean that indicates this is a {@link MToonMaterial}.
|
||
|
|
*/
|
||
|
|
get isMToonMaterial() {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Update this material.
|
||
|
|
*
|
||
|
|
* @param delta deltaTime since last update
|
||
|
|
*/
|
||
|
|
update(delta) {
|
||
|
|
this._uploadUniformsWorkaround();
|
||
|
|
this._updateUVAnimation(delta);
|
||
|
|
}
|
||
|
|
copy(source) {
|
||
|
|
super.copy(source);
|
||
|
|
this.map = source.map;
|
||
|
|
this.normalMap = source.normalMap;
|
||
|
|
this.emissiveMap = source.emissiveMap;
|
||
|
|
this.shadeMultiplyTexture = source.shadeMultiplyTexture;
|
||
|
|
this.shadingShiftTexture = source.shadingShiftTexture;
|
||
|
|
this.matcapTexture = source.matcapTexture;
|
||
|
|
this.rimMultiplyTexture = source.rimMultiplyTexture;
|
||
|
|
this.outlineWidthMultiplyTexture = source.outlineWidthMultiplyTexture;
|
||
|
|
this.uvAnimationMaskTexture = source.uvAnimationMaskTexture;
|
||
|
|
this.normalMapType = source.normalMapType;
|
||
|
|
this.uvAnimationScrollXSpeedFactor = source.uvAnimationScrollXSpeedFactor;
|
||
|
|
this.uvAnimationScrollYSpeedFactor = source.uvAnimationScrollYSpeedFactor;
|
||
|
|
this.uvAnimationRotationSpeedFactor = source.uvAnimationRotationSpeedFactor;
|
||
|
|
this.ignoreVertexColor = source.ignoreVertexColor;
|
||
|
|
this.v0CompatShade = source.v0CompatShade;
|
||
|
|
this.debugMode = source.debugMode;
|
||
|
|
this.outlineWidthMode = source.outlineWidthMode;
|
||
|
|
this.isOutline = source.isOutline;
|
||
|
|
this.needsUpdate = true;
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Update UV animation state.
|
||
|
|
* Intended to be called via {@link update}.
|
||
|
|
* @param delta deltaTime
|
||
|
|
*/
|
||
|
|
_updateUVAnimation(delta) {
|
||
|
|
this.uniforms.uvAnimationScrollXOffset.value += delta * this.uvAnimationScrollXSpeedFactor;
|
||
|
|
this.uniforms.uvAnimationScrollYOffset.value += delta * this.uvAnimationScrollYSpeedFactor;
|
||
|
|
this.uniforms.uvAnimationRotationPhase.value += delta * this.uvAnimationRotationSpeedFactor;
|
||
|
|
this.uniforms.alphaTest.value = this.alphaTest;
|
||
|
|
this.uniformsNeedUpdate = true;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Upload uniforms that need to upload but doesn't automatically because of reasons.
|
||
|
|
* Intended to be called via {@link constructor} and {@link update}.
|
||
|
|
*/
|
||
|
|
_uploadUniformsWorkaround() {
|
||
|
|
this.uniforms.opacity.value = this.opacity;
|
||
|
|
this._updateTextureMatrix(this.uniforms.map, this.uniforms.mapUvTransform);
|
||
|
|
this._updateTextureMatrix(this.uniforms.normalMap, this.uniforms.normalMapUvTransform);
|
||
|
|
this._updateTextureMatrix(this.uniforms.emissiveMap, this.uniforms.emissiveMapUvTransform);
|
||
|
|
this._updateTextureMatrix(this.uniforms.shadeMultiplyTexture, this.uniforms.shadeMultiplyTextureUvTransform);
|
||
|
|
this._updateTextureMatrix(this.uniforms.shadingShiftTexture, this.uniforms.shadingShiftTextureUvTransform);
|
||
|
|
this._updateTextureMatrix(this.uniforms.matcapTexture, this.uniforms.matcapTextureUvTransform);
|
||
|
|
this._updateTextureMatrix(this.uniforms.rimMultiplyTexture, this.uniforms.rimMultiplyTextureUvTransform);
|
||
|
|
this._updateTextureMatrix(
|
||
|
|
this.uniforms.outlineWidthMultiplyTexture,
|
||
|
|
this.uniforms.outlineWidthMultiplyTextureUvTransform
|
||
|
|
);
|
||
|
|
this._updateTextureMatrix(this.uniforms.uvAnimationMaskTexture, this.uniforms.uvAnimationMaskTextureUvTransform);
|
||
|
|
this.uniformsNeedUpdate = true;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Returns a map object of preprocessor token and macro of the shader program.
|
||
|
|
*/
|
||
|
|
_generateDefines() {
|
||
|
|
const threeRevision = parseInt(THREE42.REVISION, 10);
|
||
|
|
const useUvInVert = this.outlineWidthMultiplyTexture !== null;
|
||
|
|
const useUvInFrag = this.map !== null || this.normalMap !== null || this.emissiveMap !== null || this.shadeMultiplyTexture !== null || this.shadingShiftTexture !== null || this.rimMultiplyTexture !== null || this.uvAnimationMaskTexture !== null;
|
||
|
|
return {
|
||
|
|
// Temporary compat against shader change @ Three.js r126
|
||
|
|
// See: #21205, #21307, #21299
|
||
|
|
THREE_VRM_THREE_REVISION: threeRevision,
|
||
|
|
OUTLINE: this._isOutline,
|
||
|
|
MTOON_USE_UV: useUvInVert || useUvInFrag,
|
||
|
|
// we can't use `USE_UV` , it will be redefined in WebGLProgram.js
|
||
|
|
MTOON_UVS_VERTEX_ONLY: useUvInVert && !useUvInFrag,
|
||
|
|
V0_COMPAT_SHADE: this._v0CompatShade,
|
||
|
|
USE_SHADEMULTIPLYTEXTURE: this.shadeMultiplyTexture !== null,
|
||
|
|
USE_SHADINGSHIFTTEXTURE: this.shadingShiftTexture !== null,
|
||
|
|
USE_MATCAPTEXTURE: this.matcapTexture !== null,
|
||
|
|
USE_RIMMULTIPLYTEXTURE: this.rimMultiplyTexture !== null,
|
||
|
|
USE_OUTLINEWIDTHMULTIPLYTEXTURE: this._isOutline && this.outlineWidthMultiplyTexture !== null,
|
||
|
|
USE_UVANIMATIONMASKTEXTURE: this.uvAnimationMaskTexture !== null,
|
||
|
|
IGNORE_VERTEX_COLOR: this._ignoreVertexColor === true,
|
||
|
|
DEBUG_NORMAL: this._debugMode === "normal",
|
||
|
|
DEBUG_LITSHADERATE: this._debugMode === "litShadeRate",
|
||
|
|
DEBUG_UV: this._debugMode === "uv",
|
||
|
|
OUTLINE_WIDTH_SCREEN: this._isOutline && this._outlineWidthMode === MToonMaterialOutlineWidthMode.ScreenCoordinates
|
||
|
|
};
|
||
|
|
}
|
||
|
|
_updateTextureMatrix(src, dst) {
|
||
|
|
if (src.value) {
|
||
|
|
if (src.value.matrixAutoUpdate) {
|
||
|
|
src.value.updateMatrix();
|
||
|
|
}
|
||
|
|
dst.value.copy(src.value.matrix);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var POSSIBLE_SPEC_VERSIONS6 = /* @__PURE__ */ new Set(["1.0", "1.0-beta"]);
|
||
|
|
var _MToonMaterialLoaderPlugin = class _MToonMaterialLoaderPlugin2 {
|
||
|
|
get name() {
|
||
|
|
return _MToonMaterialLoaderPlugin2.EXTENSION_NAME;
|
||
|
|
}
|
||
|
|
constructor(parser, options = {}) {
|
||
|
|
var _a, _b, _c, _d;
|
||
|
|
this.parser = parser;
|
||
|
|
this.materialType = (_a = options.materialType) != null ? _a : MToonMaterial;
|
||
|
|
this.renderOrderOffset = (_b = options.renderOrderOffset) != null ? _b : 0;
|
||
|
|
this.v0CompatShade = (_c = options.v0CompatShade) != null ? _c : false;
|
||
|
|
this.debugMode = (_d = options.debugMode) != null ? _d : "none";
|
||
|
|
this._mToonMaterialSet = /* @__PURE__ */ new Set();
|
||
|
|
}
|
||
|
|
beforeRoot() {
|
||
|
|
return __async3(this, null, function* () {
|
||
|
|
this._removeUnlitExtensionIfMToonExists();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
afterRoot(gltf) {
|
||
|
|
return __async3(this, null, function* () {
|
||
|
|
gltf.userData.vrmMToonMaterials = Array.from(this._mToonMaterialSet);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
getMaterialType(materialIndex) {
|
||
|
|
const v1Extension = this._getMToonExtension(materialIndex);
|
||
|
|
if (v1Extension) {
|
||
|
|
return this.materialType;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
extendMaterialParams(materialIndex, materialParams) {
|
||
|
|
const extension = this._getMToonExtension(materialIndex);
|
||
|
|
if (extension) {
|
||
|
|
return this._extendMaterialParams(extension, materialParams);
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
loadMesh(meshIndex) {
|
||
|
|
return __async3(this, null, function* () {
|
||
|
|
var _a;
|
||
|
|
const parser = this.parser;
|
||
|
|
const json = parser.json;
|
||
|
|
const meshDef = (_a = json.meshes) == null ? void 0 : _a[meshIndex];
|
||
|
|
if (meshDef == null) {
|
||
|
|
throw new Error(
|
||
|
|
`MToonMaterialLoaderPlugin: Attempt to use meshes[${meshIndex}] of glTF but the mesh doesn't exist`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
const primitivesDef = meshDef.primitives;
|
||
|
|
const meshOrGroup = yield parser.loadMesh(meshIndex);
|
||
|
|
if (primitivesDef.length === 1) {
|
||
|
|
const mesh = meshOrGroup;
|
||
|
|
const materialIndex = primitivesDef[0].material;
|
||
|
|
if (materialIndex != null) {
|
||
|
|
this._setupPrimitive(mesh, materialIndex);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
const group = meshOrGroup;
|
||
|
|
for (let i = 0; i < primitivesDef.length; i++) {
|
||
|
|
const mesh = group.children[i];
|
||
|
|
const materialIndex = primitivesDef[i].material;
|
||
|
|
if (materialIndex != null) {
|
||
|
|
this._setupPrimitive(mesh, materialIndex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return meshOrGroup;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Delete use of `KHR_materials_unlit` from its `materials` if the material is using MToon.
|
||
|
|
*
|
||
|
|
* Since GLTFLoader have so many hardcoded procedure related to `KHR_materials_unlit`
|
||
|
|
* we have to delete the extension before we start to parse the glTF.
|
||
|
|
*/
|
||
|
|
_removeUnlitExtensionIfMToonExists() {
|
||
|
|
const parser = this.parser;
|
||
|
|
const json = parser.json;
|
||
|
|
const materialDefs = json.materials;
|
||
|
|
materialDefs == null ? void 0 : materialDefs.map((materialDef, iMaterial) => {
|
||
|
|
var _a;
|
||
|
|
const extension = this._getMToonExtension(iMaterial);
|
||
|
|
if (extension && ((_a = materialDef.extensions) == null ? void 0 : _a["KHR_materials_unlit"])) {
|
||
|
|
delete materialDef.extensions["KHR_materials_unlit"];
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_getMToonExtension(materialIndex) {
|
||
|
|
var _a, _b;
|
||
|
|
const parser = this.parser;
|
||
|
|
const json = parser.json;
|
||
|
|
const materialDef = (_a = json.materials) == null ? void 0 : _a[materialIndex];
|
||
|
|
if (materialDef == null) {
|
||
|
|
console.warn(
|
||
|
|
`MToonMaterialLoaderPlugin: Attempt to use materials[${materialIndex}] of glTF but the material doesn't exist`
|
||
|
|
);
|
||
|
|
return void 0;
|
||
|
|
}
|
||
|
|
const extension = (_b = materialDef.extensions) == null ? void 0 : _b[_MToonMaterialLoaderPlugin2.EXTENSION_NAME];
|
||
|
|
if (extension == null) {
|
||
|
|
return void 0;
|
||
|
|
}
|
||
|
|
const specVersion = extension.specVersion;
|
||
|
|
if (!POSSIBLE_SPEC_VERSIONS6.has(specVersion)) {
|
||
|
|
console.warn(
|
||
|
|
`MToonMaterialLoaderPlugin: Unknown ${_MToonMaterialLoaderPlugin2.EXTENSION_NAME} specVersion "${specVersion}"`
|
||
|
|
);
|
||
|
|
return void 0;
|
||
|
|
}
|
||
|
|
return extension;
|
||
|
|
}
|
||
|
|
_extendMaterialParams(extension, materialParams) {
|
||
|
|
return __async3(this, null, function* () {
|
||
|
|
var _a;
|
||
|
|
delete materialParams.metalness;
|
||
|
|
delete materialParams.roughness;
|
||
|
|
const assignHelper = new GLTFMToonMaterialParamsAssignHelper(this.parser, materialParams);
|
||
|
|
assignHelper.assignPrimitive("transparentWithZWrite", extension.transparentWithZWrite);
|
||
|
|
assignHelper.assignColor("shadeColorFactor", extension.shadeColorFactor);
|
||
|
|
assignHelper.assignTexture("shadeMultiplyTexture", extension.shadeMultiplyTexture, true);
|
||
|
|
assignHelper.assignPrimitive("shadingShiftFactor", extension.shadingShiftFactor);
|
||
|
|
assignHelper.assignTexture("shadingShiftTexture", extension.shadingShiftTexture, true);
|
||
|
|
assignHelper.assignPrimitive("shadingShiftTextureScale", (_a = extension.shadingShiftTexture) == null ? void 0 : _a.scale);
|
||
|
|
assignHelper.assignPrimitive("shadingToonyFactor", extension.shadingToonyFactor);
|
||
|
|
assignHelper.assignPrimitive("giEqualizationFactor", extension.giEqualizationFactor);
|
||
|
|
assignHelper.assignColor("matcapFactor", extension.matcapFactor);
|
||
|
|
assignHelper.assignTexture("matcapTexture", extension.matcapTexture, true);
|
||
|
|
assignHelper.assignColor("parametricRimColorFactor", extension.parametricRimColorFactor);
|
||
|
|
assignHelper.assignTexture("rimMultiplyTexture", extension.rimMultiplyTexture, true);
|
||
|
|
assignHelper.assignPrimitive("rimLightingMixFactor", extension.rimLightingMixFactor);
|
||
|
|
assignHelper.assignPrimitive("parametricRimFresnelPowerFactor", extension.parametricRimFresnelPowerFactor);
|
||
|
|
assignHelper.assignPrimitive("parametricRimLiftFactor", extension.parametricRimLiftFactor);
|
||
|
|
assignHelper.assignPrimitive("outlineWidthMode", extension.outlineWidthMode);
|
||
|
|
assignHelper.assignPrimitive("outlineWidthFactor", extension.outlineWidthFactor);
|
||
|
|
assignHelper.assignTexture("outlineWidthMultiplyTexture", extension.outlineWidthMultiplyTexture, false);
|
||
|
|
assignHelper.assignColor("outlineColorFactor", extension.outlineColorFactor);
|
||
|
|
assignHelper.assignPrimitive("outlineLightingMixFactor", extension.outlineLightingMixFactor);
|
||
|
|
assignHelper.assignTexture("uvAnimationMaskTexture", extension.uvAnimationMaskTexture, false);
|
||
|
|
assignHelper.assignPrimitive("uvAnimationScrollXSpeedFactor", extension.uvAnimationScrollXSpeedFactor);
|
||
|
|
assignHelper.assignPrimitive("uvAnimationScrollYSpeedFactor", extension.uvAnimationScrollYSpeedFactor);
|
||
|
|
assignHelper.assignPrimitive("uvAnimationRotationSpeedFactor", extension.uvAnimationRotationSpeedFactor);
|
||
|
|
assignHelper.assignPrimitive("v0CompatShade", this.v0CompatShade);
|
||
|
|
assignHelper.assignPrimitive("debugMode", this.debugMode);
|
||
|
|
yield assignHelper.pending;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* This will do two processes that is required to render MToon properly.
|
||
|
|
*
|
||
|
|
* - Set render order
|
||
|
|
* - Generate outline
|
||
|
|
*
|
||
|
|
* @param mesh A target GLTF primitive
|
||
|
|
* @param materialIndex The material index of the primitive
|
||
|
|
*/
|
||
|
|
_setupPrimitive(mesh, materialIndex) {
|
||
|
|
const extension = this._getMToonExtension(materialIndex);
|
||
|
|
if (extension) {
|
||
|
|
const renderOrder = this._parseRenderOrder(extension);
|
||
|
|
mesh.renderOrder = renderOrder + this.renderOrderOffset;
|
||
|
|
this._generateOutline(mesh);
|
||
|
|
this._addToMaterialSet(mesh);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Check whether the material should generate outline or not.
|
||
|
|
* @param surfaceMaterial The material to check
|
||
|
|
* @returns True if the material should generate outline
|
||
|
|
*/
|
||
|
|
_shouldGenerateOutline(surfaceMaterial) {
|
||
|
|
return typeof surfaceMaterial.outlineWidthMode === "string" && surfaceMaterial.outlineWidthMode !== "none" && typeof surfaceMaterial.outlineWidthFactor === "number" && surfaceMaterial.outlineWidthFactor > 0;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Generate outline for the given mesh, if it needs.
|
||
|
|
*
|
||
|
|
* @param mesh The target mesh
|
||
|
|
*/
|
||
|
|
_generateOutline(mesh) {
|
||
|
|
const surfaceMaterial = mesh.material;
|
||
|
|
if (!(surfaceMaterial instanceof THREE52.Material)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!this._shouldGenerateOutline(surfaceMaterial)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
mesh.material = [surfaceMaterial];
|
||
|
|
const outlineMaterial = surfaceMaterial.clone();
|
||
|
|
outlineMaterial.name += " (Outline)";
|
||
|
|
outlineMaterial.isOutline = true;
|
||
|
|
outlineMaterial.side = THREE52.BackSide;
|
||
|
|
mesh.material.push(outlineMaterial);
|
||
|
|
const geometry = mesh.geometry;
|
||
|
|
const primitiveVertices = geometry.index ? geometry.index.count : geometry.attributes.position.count / 3;
|
||
|
|
geometry.addGroup(0, primitiveVertices, 0);
|
||
|
|
geometry.addGroup(0, primitiveVertices, 1);
|
||
|
|
}
|
||
|
|
_addToMaterialSet(mesh) {
|
||
|
|
const materialOrMaterials = mesh.material;
|
||
|
|
const materialSet = /* @__PURE__ */ new Set();
|
||
|
|
if (Array.isArray(materialOrMaterials)) {
|
||
|
|
materialOrMaterials.forEach((material) => materialSet.add(material));
|
||
|
|
} else {
|
||
|
|
materialSet.add(materialOrMaterials);
|
||
|
|
}
|
||
|
|
for (const material of materialSet) {
|
||
|
|
this._mToonMaterialSet.add(material);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_parseRenderOrder(extension) {
|
||
|
|
var _a;
|
||
|
|
const enabledZWrite = extension.transparentWithZWrite;
|
||
|
|
return (enabledZWrite ? 0 : 19) + ((_a = extension.renderQueueOffsetNumber) != null ? _a : 0);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
_MToonMaterialLoaderPlugin.EXTENSION_NAME = "VRMC_materials_mtoon";
|
||
|
|
var MToonMaterialLoaderPlugin = _MToonMaterialLoaderPlugin;
|
||
|
|
|
||
|
|
// ../three-vrm-materials-hdr-emissive-multiplier/lib/three-vrm-materials-hdr-emissive-multiplier.module.js
|
||
|
|
var __async4 = (__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 _VRMMaterialsHDREmissiveMultiplierLoaderPlugin = class _VRMMaterialsHDREmissiveMultiplierLoaderPlugin2 {
|
||
|
|
get name() {
|
||
|
|
return _VRMMaterialsHDREmissiveMultiplierLoaderPlugin2.EXTENSION_NAME;
|
||
|
|
}
|
||
|
|
constructor(parser) {
|
||
|
|
this.parser = parser;
|
||
|
|
}
|
||
|
|
extendMaterialParams(materialIndex, materialParams) {
|
||
|
|
return __async4(this, null, function* () {
|
||
|
|
const extension = this._getHDREmissiveMultiplierExtension(materialIndex);
|
||
|
|
if (extension == null) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
console.warn(
|
||
|
|
"VRMMaterialsHDREmissiveMultiplierLoaderPlugin: `VRMC_materials_hdr_emissiveMultiplier` is archived. Use `KHR_materials_emissive_strength` instead."
|
||
|
|
);
|
||
|
|
const emissiveMultiplier = extension.emissiveMultiplier;
|
||
|
|
materialParams.emissiveIntensity = emissiveMultiplier;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_getHDREmissiveMultiplierExtension(materialIndex) {
|
||
|
|
var _a, _b;
|
||
|
|
const parser = this.parser;
|
||
|
|
const json = parser.json;
|
||
|
|
const materialDef = (_a = json.materials) == null ? void 0 : _a[materialIndex];
|
||
|
|
if (materialDef == null) {
|
||
|
|
console.warn(
|
||
|
|
`VRMMaterialsHDREmissiveMultiplierLoaderPlugin: Attempt to use materials[${materialIndex}] of glTF but the material doesn't exist`
|
||
|
|
);
|
||
|
|
return void 0;
|
||
|
|
}
|
||
|
|
const extension = (_b = materialDef.extensions) == null ? void 0 : _b[_VRMMaterialsHDREmissiveMultiplierLoaderPlugin2.EXTENSION_NAME];
|
||
|
|
if (extension == null) {
|
||
|
|
return void 0;
|
||
|
|
}
|
||
|
|
return extension;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
_VRMMaterialsHDREmissiveMultiplierLoaderPlugin.EXTENSION_NAME = "VRMC_materials_hdr_emissiveMultiplier";
|
||
|
|
var VRMMaterialsHDREmissiveMultiplierLoaderPlugin = _VRMMaterialsHDREmissiveMultiplierLoaderPlugin;
|
||
|
|
|
||
|
|
// ../three-vrm-materials-v0compat/lib/three-vrm-materials-v0compat.module.js
|
||
|
|
var THREE18 = __toESM(require("three"), 1);
|
||
|
|
var __defProp3 = Object.defineProperty;
|
||
|
|
var __defProps = Object.defineProperties;
|
||
|
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
||
|
|
var __getOwnPropSymbols2 = Object.getOwnPropertySymbols;
|
||
|
|
var __hasOwnProp3 = Object.prototype.hasOwnProperty;
|
||
|
|
var __propIsEnum2 = Object.prototype.propertyIsEnumerable;
|
||
|
|
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||
|
|
var __spreadValues2 = (a, b) => {
|
||
|
|
for (var prop in b || (b = {}))
|
||
|
|
if (__hasOwnProp3.call(b, prop))
|
||
|
|
__defNormalProp2(a, prop, b[prop]);
|
||
|
|
if (__getOwnPropSymbols2)
|
||
|
|
for (var prop of __getOwnPropSymbols2(b)) {
|
||
|
|
if (__propIsEnum2.call(b, prop))
|
||
|
|
__defNormalProp2(a, prop, b[prop]);
|
||
|
|
}
|
||
|
|
return a;
|
||
|
|
};
|
||
|
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
||
|
|
var __async5 = (__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());
|
||
|
|
});
|
||
|
|
};
|
||
|
|
function gammaEOTF(e) {
|
||
|
|
return Math.pow(e, 2.2);
|
||
|
|
}
|
||
|
|
var VRMMaterialsV0CompatPlugin = class {
|
||
|
|
get name() {
|
||
|
|
return "VRMMaterialsV0CompatPlugin";
|
||
|
|
}
|
||
|
|
constructor(parser) {
|
||
|
|
var _a;
|
||
|
|
this.parser = parser;
|
||
|
|
this._renderQueueMapTransparent = /* @__PURE__ */ new Map();
|
||
|
|
this._renderQueueMapTransparentZWrite = /* @__PURE__ */ new Map();
|
||
|
|
const json = this.parser.json;
|
||
|
|
json.extensionsUsed = (_a = json.extensionsUsed) != null ? _a : [];
|
||
|
|
if (json.extensionsUsed.indexOf("KHR_texture_transform") === -1) {
|
||
|
|
json.extensionsUsed.push("KHR_texture_transform");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
beforeRoot() {
|
||
|
|
return __async5(this, null, function* () {
|
||
|
|
var _a;
|
||
|
|
const json = this.parser.json;
|
||
|
|
const v0VRMExtension = (_a = json.extensions) == null ? void 0 : _a["VRM"];
|
||
|
|
const v0MaterialProperties = v0VRMExtension == null ? void 0 : v0VRMExtension.materialProperties;
|
||
|
|
if (!v0MaterialProperties) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
this._populateRenderQueueMap(v0MaterialProperties);
|
||
|
|
v0MaterialProperties.forEach((materialProperties, materialIndex) => {
|
||
|
|
var _a2, _b;
|
||
|
|
const materialDef = (_a2 = json.materials) == null ? void 0 : _a2[materialIndex];
|
||
|
|
if (materialDef == null) {
|
||
|
|
console.warn(
|
||
|
|
`VRMMaterialsV0CompatPlugin: Attempt to use materials[${materialIndex}] of glTF but the material doesn't exist`
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (materialProperties.shader === "VRM/MToon") {
|
||
|
|
const material = this._parseV0MToonProperties(materialProperties, materialDef);
|
||
|
|
json.materials[materialIndex] = material;
|
||
|
|
} else if ((_b = materialProperties.shader) == null ? void 0 : _b.startsWith("VRM/Unlit")) {
|
||
|
|
const material = this._parseV0UnlitProperties(materialProperties, materialDef);
|
||
|
|
json.materials[materialIndex] = material;
|
||
|
|
} else if (materialProperties.shader === "VRM_USE_GLTFSHADER") {
|
||
|
|
} else {
|
||
|
|
console.warn(`VRMMaterialsV0CompatPlugin: Unknown shader: ${materialProperties.shader}`);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_parseV0MToonProperties(materialProperties, schemaMaterial) {
|
||
|
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa;
|
||
|
|
const isTransparent = (_b = (_a = materialProperties.keywordMap) == null ? void 0 : _a["_ALPHABLEND_ON"]) != null ? _b : false;
|
||
|
|
const enabledZWrite = ((_c = materialProperties.floatProperties) == null ? void 0 : _c["_ZWrite"]) === 1;
|
||
|
|
const transparentWithZWrite = enabledZWrite && isTransparent;
|
||
|
|
const renderQueueOffsetNumber = this._v0ParseRenderQueue(materialProperties);
|
||
|
|
const isCutoff = (_e = (_d = materialProperties.keywordMap) == null ? void 0 : _d["_ALPHATEST_ON"]) != null ? _e : false;
|
||
|
|
const alphaMode = isTransparent ? "BLEND" : isCutoff ? "MASK" : "OPAQUE";
|
||
|
|
const alphaCutoff = isCutoff ? (_g = (_f = materialProperties.floatProperties) == null ? void 0 : _f["_Cutoff"]) != null ? _g : 0.5 : void 0;
|
||
|
|
const cullMode = (_i = (_h = materialProperties.floatProperties) == null ? void 0 : _h["_CullMode"]) != null ? _i : 2;
|
||
|
|
const doubleSided = cullMode === 0;
|
||
|
|
const textureTransformExt = this._portTextureTransform(materialProperties);
|
||
|
|
const baseColorFactor = ((_k = (_j = materialProperties.vectorProperties) == null ? void 0 : _j["_Color"]) != null ? _k : [1, 1, 1, 1]).map(
|
||
|
|
(v, i) => i === 3 ? v : gammaEOTF(v)
|
||
|
|
// alpha channel is stored in linear
|
||
|
|
);
|
||
|
|
const baseColorTextureIndex = (_l = materialProperties.textureProperties) == null ? void 0 : _l["_MainTex"];
|
||
|
|
const baseColorTexture = baseColorTextureIndex != null ? {
|
||
|
|
index: baseColorTextureIndex,
|
||
|
|
extensions: __spreadValues2({}, textureTransformExt)
|
||
|
|
} : void 0;
|
||
|
|
const normalTextureScale = (_n = (_m = materialProperties.floatProperties) == null ? void 0 : _m["_BumpScale"]) != null ? _n : 1;
|
||
|
|
const normalTextureIndex = (_o = materialProperties.textureProperties) == null ? void 0 : _o["_BumpMap"];
|
||
|
|
const normalTexture = normalTextureIndex != null ? {
|
||
|
|
index: normalTextureIndex,
|
||
|
|
scale: normalTextureScale,
|
||
|
|
extensions: __spreadValues2({}, textureTransformExt)
|
||
|
|
} : void 0;
|
||
|
|
const emissiveFactor = ((_q = (_p = materialProperties.vectorProperties) == null ? void 0 : _p["_EmissionColor"]) != null ? _q : [0, 0, 0, 1]).map(
|
||
|
|
gammaEOTF
|
||
|
|
);
|
||
|
|
const emissiveTextureIndex = (_r = materialProperties.textureProperties) == null ? void 0 : _r["_EmissionMap"];
|
||
|
|
const emissiveTexture = emissiveTextureIndex != null ? {
|
||
|
|
index: emissiveTextureIndex,
|
||
|
|
extensions: __spreadValues2({}, textureTransformExt)
|
||
|
|
} : void 0;
|
||
|
|
const shadeColorFactor = ((_t = (_s = materialProperties.vectorProperties) == null ? void 0 : _s["_ShadeColor"]) != null ? _t : [0.97, 0.81, 0.86, 1]).map(
|
||
|
|
gammaEOTF
|
||
|
|
);
|
||
|
|
const shadeMultiplyTextureIndex = (_u = materialProperties.textureProperties) == null ? void 0 : _u["_ShadeTexture"];
|
||
|
|
const shadeMultiplyTexture = shadeMultiplyTextureIndex != null ? {
|
||
|
|
index: shadeMultiplyTextureIndex,
|
||
|
|
extensions: __spreadValues2({}, textureTransformExt)
|
||
|
|
} : void 0;
|
||
|
|
let shadingShiftFactor = (_w = (_v = materialProperties.floatProperties) == null ? void 0 : _v["_ShadeShift"]) != null ? _w : 0;
|
||
|
|
let shadingToonyFactor = (_y = (_x = materialProperties.floatProperties) == null ? void 0 : _x["_ShadeToony"]) != null ? _y : 0.9;
|
||
|
|
shadingToonyFactor = THREE18.MathUtils.lerp(shadingToonyFactor, 1, 0.5 + 0.5 * shadingShiftFactor);
|
||
|
|
shadingShiftFactor = -shadingShiftFactor - (1 - shadingToonyFactor);
|
||
|
|
const giIntensityFactor = (_A = (_z = materialProperties.floatProperties) == null ? void 0 : _z["_IndirectLightIntensity"]) != null ? _A : 0.1;
|
||
|
|
const giEqualizationFactor = giIntensityFactor ? 1 - giIntensityFactor : void 0;
|
||
|
|
const matcapTextureIndex = (_B = materialProperties.textureProperties) == null ? void 0 : _B["_SphereAdd"];
|
||
|
|
const matcapFactor = matcapTextureIndex != null ? [1, 1, 1] : void 0;
|
||
|
|
const matcapTexture = matcapTextureIndex != null ? {
|
||
|
|
index: matcapTextureIndex
|
||
|
|
} : void 0;
|
||
|
|
const rimLightingMixFactor = (_D = (_C = materialProperties.floatProperties) == null ? void 0 : _C["_RimLightingMix"]) != null ? _D : 0;
|
||
|
|
const rimMultiplyTextureIndex = (_E = materialProperties.textureProperties) == null ? void 0 : _E["_RimTexture"];
|
||
|
|
const rimMultiplyTexture = rimMultiplyTextureIndex != null ? {
|
||
|
|
index: rimMultiplyTextureIndex,
|
||
|
|
extensions: __spreadValues2({}, textureTransformExt)
|
||
|
|
} : void 0;
|
||
|
|
const parametricRimColorFactor = ((_G = (_F = materialProperties.vectorProperties) == null ? void 0 : _F["_RimColor"]) != null ? _G : [0, 0, 0, 1]).map(
|
||
|
|
gammaEOTF
|
||
|
|
);
|
||
|
|
const parametricRimFresnelPowerFactor = (_I = (_H = materialProperties.floatProperties) == null ? void 0 : _H["_RimFresnelPower"]) != null ? _I : 1;
|
||
|
|
const parametricRimLiftFactor = (_K = (_J = materialProperties.floatProperties) == null ? void 0 : _J["_RimLift"]) != null ? _K : 0;
|
||
|
|
const outlineWidthMode = ["none", "worldCoordinates", "screenCoordinates"][(_M = (_L = materialProperties.floatProperties) == null ? void 0 : _L["_OutlineWidthMode"]) != null ? _M : 0];
|
||
|
|
let outlineWidthFactor = (_O = (_N = materialProperties.floatProperties) == null ? void 0 : _N["_OutlineWidth"]) != null ? _O : 0;
|
||
|
|
outlineWidthFactor = 0.01 * outlineWidthFactor;
|
||
|
|
const outlineWidthMultiplyTextureIndex = (_P = materialProperties.textureProperties) == null ? void 0 : _P["_OutlineWidthTexture"];
|
||
|
|
const outlineWidthMultiplyTexture = outlineWidthMultiplyTextureIndex != null ? {
|
||
|
|
index: outlineWidthMultiplyTextureIndex,
|
||
|
|
extensions: __spreadValues2({}, textureTransformExt)
|
||
|
|
} : void 0;
|
||
|
|
const outlineColorFactor = ((_R = (_Q = materialProperties.vectorProperties) == null ? void 0 : _Q["_OutlineColor"]) != null ? _R : [0, 0, 0]).map(
|
||
|
|
gammaEOTF
|
||
|
|
);
|
||
|
|
const outlineColorMode = (_T = (_S = materialProperties.floatProperties) == null ? void 0 : _S["_OutlineColorMode"]) != null ? _T : 0;
|
||
|
|
const outlineLightingMixFactor = outlineColorMode === 1 ? (_V = (_U = materialProperties.floatProperties) == null ? void 0 : _U["_OutlineLightingMix"]) != null ? _V : 1 : 0;
|
||
|
|
const uvAnimationMaskTextureIndex = (_W = materialProperties.textureProperties) == null ? void 0 : _W["_UvAnimMaskTexture"];
|
||
|
|
const uvAnimationMaskTexture = uvAnimationMaskTextureIndex != null ? {
|
||
|
|
index: uvAnimationMaskTextureIndex,
|
||
|
|
extensions: __spreadValues2({}, textureTransformExt)
|
||
|
|
} : void 0;
|
||
|
|
const uvAnimationScrollXSpeedFactor = (_Y = (_X = materialProperties.floatProperties) == null ? void 0 : _X["_UvAnimScrollX"]) != null ? _Y : 0;
|
||
|
|
let uvAnimationScrollYSpeedFactor = (__ = (_Z = materialProperties.floatProperties) == null ? void 0 : _Z["_UvAnimScrollY"]) != null ? __ : 0;
|
||
|
|
if (uvAnimationScrollYSpeedFactor != null) {
|
||
|
|
uvAnimationScrollYSpeedFactor = -uvAnimationScrollYSpeedFactor;
|
||
|
|
}
|
||
|
|
const uvAnimationRotationSpeedFactor = (_aa = (_$ = materialProperties.floatProperties) == null ? void 0 : _$["_UvAnimRotation"]) != null ? _aa : 0;
|
||
|
|
const mtoonExtension = {
|
||
|
|
specVersion: "1.0",
|
||
|
|
transparentWithZWrite,
|
||
|
|
renderQueueOffsetNumber,
|
||
|
|
shadeColorFactor,
|
||
|
|
shadeMultiplyTexture,
|
||
|
|
shadingShiftFactor,
|
||
|
|
shadingToonyFactor,
|
||
|
|
giEqualizationFactor,
|
||
|
|
matcapFactor,
|
||
|
|
matcapTexture,
|
||
|
|
rimLightingMixFactor,
|
||
|
|
rimMultiplyTexture,
|
||
|
|
parametricRimColorFactor,
|
||
|
|
parametricRimFresnelPowerFactor,
|
||
|
|
parametricRimLiftFactor,
|
||
|
|
outlineWidthMode,
|
||
|
|
outlineWidthFactor,
|
||
|
|
outlineWidthMultiplyTexture,
|
||
|
|
outlineColorFactor,
|
||
|
|
outlineLightingMixFactor,
|
||
|
|
uvAnimationMaskTexture,
|
||
|
|
uvAnimationScrollXSpeedFactor,
|
||
|
|
uvAnimationScrollYSpeedFactor,
|
||
|
|
uvAnimationRotationSpeedFactor
|
||
|
|
};
|
||
|
|
return __spreadProps(__spreadValues2({}, schemaMaterial), {
|
||
|
|
pbrMetallicRoughness: {
|
||
|
|
baseColorFactor,
|
||
|
|
baseColorTexture
|
||
|
|
},
|
||
|
|
normalTexture,
|
||
|
|
emissiveTexture,
|
||
|
|
emissiveFactor,
|
||
|
|
alphaMode,
|
||
|
|
alphaCutoff,
|
||
|
|
doubleSided,
|
||
|
|
extensions: {
|
||
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||
|
|
VRMC_materials_mtoon: mtoonExtension
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_parseV0UnlitProperties(materialProperties, schemaMaterial) {
|
||
|
|
var _a, _b, _c, _d, _e;
|
||
|
|
const isTransparentZWrite = materialProperties.shader === "VRM/UnlitTransparentZWrite";
|
||
|
|
const isTransparent = materialProperties.shader === "VRM/UnlitTransparent" || isTransparentZWrite;
|
||
|
|
const renderQueueOffsetNumber = this._v0ParseRenderQueue(materialProperties);
|
||
|
|
const isCutoff = materialProperties.shader === "VRM/UnlitCutout";
|
||
|
|
const alphaMode = isTransparent ? "BLEND" : isCutoff ? "MASK" : "OPAQUE";
|
||
|
|
const alphaCutoff = isCutoff ? (_b = (_a = materialProperties.floatProperties) == null ? void 0 : _a["_Cutoff"]) != null ? _b : 0.5 : void 0;
|
||
|
|
const textureTransformExt = this._portTextureTransform(materialProperties);
|
||
|
|
const baseColorFactor = ((_d = (_c = materialProperties.vectorProperties) == null ? void 0 : _c["_Color"]) != null ? _d : [1, 1, 1, 1]).map(gammaEOTF);
|
||
|
|
const baseColorTextureIndex = (_e = materialProperties.textureProperties) == null ? void 0 : _e["_MainTex"];
|
||
|
|
const baseColorTexture = baseColorTextureIndex != null ? {
|
||
|
|
index: baseColorTextureIndex,
|
||
|
|
extensions: __spreadValues2({}, textureTransformExt)
|
||
|
|
} : void 0;
|
||
|
|
const mtoonExtension = {
|
||
|
|
specVersion: "1.0",
|
||
|
|
transparentWithZWrite: isTransparentZWrite,
|
||
|
|
renderQueueOffsetNumber,
|
||
|
|
shadeColorFactor: baseColorFactor,
|
||
|
|
shadeMultiplyTexture: baseColorTexture
|
||
|
|
};
|
||
|
|
return __spreadProps(__spreadValues2({}, schemaMaterial), {
|
||
|
|
pbrMetallicRoughness: {
|
||
|
|
baseColorFactor,
|
||
|
|
baseColorTexture
|
||
|
|
},
|
||
|
|
alphaMode,
|
||
|
|
alphaCutoff,
|
||
|
|
extensions: {
|
||
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||
|
|
VRMC_materials_mtoon: mtoonExtension
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Create a glTF `KHR_texture_transform` extension from v0 texture transform info.
|
||
|
|
*/
|
||
|
|
_portTextureTransform(materialProperties) {
|
||
|
|
var _a, _b, _c, _d, _e;
|
||
|
|
const textureTransform = (_a = materialProperties.vectorProperties) == null ? void 0 : _a["_MainTex"];
|
||
|
|
if (textureTransform == null) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
const offset = [(_b = textureTransform == null ? void 0 : textureTransform[0]) != null ? _b : 0, (_c = textureTransform == null ? void 0 : textureTransform[1]) != null ? _c : 0];
|
||
|
|
const scale = [(_d = textureTransform == null ? void 0 : textureTransform[2]) != null ? _d : 1, (_e = textureTransform == null ? void 0 : textureTransform[3]) != null ? _e : 1];
|
||
|
|
offset[1] = 1 - scale[1] - offset[1];
|
||
|
|
return {
|
||
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||
|
|
KHR_texture_transform: { offset, scale }
|
||
|
|
};
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Convert v0 render order into v1 render order.
|
||
|
|
* This uses a map from v0 render queue to v1 compliant render queue offset which is generated in {@link _populateRenderQueueMap}.
|
||
|
|
*/
|
||
|
|
_v0ParseRenderQueue(materialProperties) {
|
||
|
|
var _a, _b;
|
||
|
|
const isTransparentZWrite = materialProperties.shader === "VRM/UnlitTransparentZWrite";
|
||
|
|
const isTransparent = ((_a = materialProperties.keywordMap) == null ? void 0 : _a["_ALPHABLEND_ON"]) != void 0 || materialProperties.shader === "VRM/UnlitTransparent" || isTransparentZWrite;
|
||
|
|
const enabledZWrite = ((_b = materialProperties.floatProperties) == null ? void 0 : _b["_ZWrite"]) === 1 || isTransparentZWrite;
|
||
|
|
let offset = 0;
|
||
|
|
if (isTransparent) {
|
||
|
|
const v0Queue = materialProperties.renderQueue;
|
||
|
|
if (v0Queue != null) {
|
||
|
|
if (enabledZWrite) {
|
||
|
|
offset = this._renderQueueMapTransparentZWrite.get(v0Queue);
|
||
|
|
} else {
|
||
|
|
offset = this._renderQueueMapTransparent.get(v0Queue);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return offset;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Create a map which maps v0 render queue to v1 compliant render queue offset.
|
||
|
|
* This lists up all render queues the model use and creates a map to new render queue offsets in the same order.
|
||
|
|
*/
|
||
|
|
_populateRenderQueueMap(materialPropertiesList) {
|
||
|
|
const renderQueuesTransparent = /* @__PURE__ */ new Set();
|
||
|
|
const renderQueuesTransparentZWrite = /* @__PURE__ */ new Set();
|
||
|
|
materialPropertiesList.forEach((materialProperties) => {
|
||
|
|
var _a, _b;
|
||
|
|
const isTransparentZWrite = materialProperties.shader === "VRM/UnlitTransparentZWrite";
|
||
|
|
const isTransparent = ((_a = materialProperties.keywordMap) == null ? void 0 : _a["_ALPHABLEND_ON"]) != void 0 || materialProperties.shader === "VRM/UnlitTransparent" || isTransparentZWrite;
|
||
|
|
const enabledZWrite = ((_b = materialProperties.floatProperties) == null ? void 0 : _b["_ZWrite"]) === 1 || isTransparentZWrite;
|
||
|
|
if (isTransparent) {
|
||
|
|
const v0Queue = materialProperties.renderQueue;
|
||
|
|
if (v0Queue != null) {
|
||
|
|
if (enabledZWrite) {
|
||
|
|
renderQueuesTransparentZWrite.add(v0Queue);
|
||
|
|
} else {
|
||
|
|
renderQueuesTransparent.add(v0Queue);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
if (renderQueuesTransparent.size > 10) {
|
||
|
|
console.warn(
|
||
|
|
`VRMMaterialsV0CompatPlugin: This VRM uses ${renderQueuesTransparent.size} render queues for Transparent materials while VRM 1.0 only supports up to 10 render queues. The model might not be rendered correctly.`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (renderQueuesTransparentZWrite.size > 10) {
|
||
|
|
console.warn(
|
||
|
|
`VRMMaterialsV0CompatPlugin: This VRM uses ${renderQueuesTransparentZWrite.size} render queues for TransparentZWrite materials while VRM 1.0 only supports up to 10 render queues. The model might not be rendered correctly.`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
Array.from(renderQueuesTransparent).sort().forEach((queue, i) => {
|
||
|
|
const newQueueOffset = Math.min(Math.max(i - renderQueuesTransparent.size + 1, -9), 0);
|
||
|
|
this._renderQueueMapTransparent.set(queue, newQueueOffset);
|
||
|
|
});
|
||
|
|
Array.from(renderQueuesTransparentZWrite).sort().forEach((queue, i) => {
|
||
|
|
const newQueueOffset = Math.min(Math.max(i, 0), 9);
|
||
|
|
this._renderQueueMapTransparentZWrite.set(queue, newQueueOffset);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// ../three-vrm-node-constraint/lib/three-vrm-node-constraint.module.js
|
||
|
|
var THREE19 = __toESM(require("three"), 1);
|
||
|
|
var THREE33 = __toESM(require("three"), 1);
|
||
|
|
var THREE23 = __toESM(require("three"), 1);
|
||
|
|
var THREE43 = __toESM(require("three"), 1);
|
||
|
|
var THREE53 = __toESM(require("three"), 1);
|
||
|
|
var __async6 = (__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 _v3A6 = new THREE19.Vector3();
|
||
|
|
var VRMNodeConstraintHelper = class extends THREE19.Group {
|
||
|
|
constructor(constraint) {
|
||
|
|
super();
|
||
|
|
this._attrPosition = new THREE19.BufferAttribute(new Float32Array([0, 0, 0, 0, 0, 0]), 3);
|
||
|
|
this._attrPosition.setUsage(THREE19.DynamicDrawUsage);
|
||
|
|
const geometry = new THREE19.BufferGeometry();
|
||
|
|
geometry.setAttribute("position", this._attrPosition);
|
||
|
|
const material = new THREE19.LineBasicMaterial({
|
||
|
|
color: 16711935,
|
||
|
|
depthTest: false,
|
||
|
|
depthWrite: false
|
||
|
|
});
|
||
|
|
this._line = new THREE19.Line(geometry, material);
|
||
|
|
this.add(this._line);
|
||
|
|
this.constraint = constraint;
|
||
|
|
}
|
||
|
|
updateMatrixWorld(force) {
|
||
|
|
_v3A6.setFromMatrixPosition(this.constraint.destination.matrixWorld);
|
||
|
|
this._attrPosition.setXYZ(0, _v3A6.x, _v3A6.y, _v3A6.z);
|
||
|
|
if (this.constraint.source) {
|
||
|
|
_v3A6.setFromMatrixPosition(this.constraint.source.matrixWorld);
|
||
|
|
}
|
||
|
|
this._attrPosition.setXYZ(1, _v3A6.x, _v3A6.y, _v3A6.z);
|
||
|
|
this._attrPosition.needsUpdate = true;
|
||
|
|
super.updateMatrixWorld(force);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
function decomposePosition(matrix, target) {
|
||
|
|
return target.set(matrix.elements[12], matrix.elements[13], matrix.elements[14]);
|
||
|
|
}
|
||
|
|
var _v3A22 = new THREE23.Vector3();
|
||
|
|
var _v3B4 = new THREE23.Vector3();
|
||
|
|
function decomposeRotation(matrix, target) {
|
||
|
|
matrix.decompose(_v3A22, target, _v3B4);
|
||
|
|
return target;
|
||
|
|
}
|
||
|
|
function quatInvertCompat2(target) {
|
||
|
|
if (target.invert) {
|
||
|
|
target.invert();
|
||
|
|
} else {
|
||
|
|
target.inverse();
|
||
|
|
}
|
||
|
|
return target;
|
||
|
|
}
|
||
|
|
var VRMNodeConstraint = class {
|
||
|
|
/**
|
||
|
|
* @param destination The destination object
|
||
|
|
* @param source The source object
|
||
|
|
*/
|
||
|
|
constructor(destination, source) {
|
||
|
|
this.destination = destination;
|
||
|
|
this.source = source;
|
||
|
|
this.weight = 1;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _v3A32 = new THREE33.Vector3();
|
||
|
|
var _v3B22 = new THREE33.Vector3();
|
||
|
|
var _v3C2 = new THREE33.Vector3();
|
||
|
|
var _quatA7 = new THREE33.Quaternion();
|
||
|
|
var _quatB4 = new THREE33.Quaternion();
|
||
|
|
var _quatC2 = new THREE33.Quaternion();
|
||
|
|
var VRMAimConstraint = class extends VRMNodeConstraint {
|
||
|
|
/**
|
||
|
|
* The aim axis of the constraint.
|
||
|
|
*/
|
||
|
|
get aimAxis() {
|
||
|
|
return this._aimAxis;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* The aim axis of the constraint.
|
||
|
|
*/
|
||
|
|
set aimAxis(aimAxis) {
|
||
|
|
this._aimAxis = aimAxis;
|
||
|
|
this._v3AimAxis.set(
|
||
|
|
aimAxis === "PositiveX" ? 1 : aimAxis === "NegativeX" ? -1 : 0,
|
||
|
|
aimAxis === "PositiveY" ? 1 : aimAxis === "NegativeY" ? -1 : 0,
|
||
|
|
aimAxis === "PositiveZ" ? 1 : aimAxis === "NegativeZ" ? -1 : 0
|
||
|
|
);
|
||
|
|
}
|
||
|
|
get dependencies() {
|
||
|
|
const set = /* @__PURE__ */ new Set([this.source]);
|
||
|
|
if (this.destination.parent) {
|
||
|
|
set.add(this.destination.parent);
|
||
|
|
}
|
||
|
|
return set;
|
||
|
|
}
|
||
|
|
constructor(destination, source) {
|
||
|
|
super(destination, source);
|
||
|
|
this._aimAxis = "PositiveX";
|
||
|
|
this._v3AimAxis = new THREE33.Vector3(1, 0, 0);
|
||
|
|
this._dstRestQuat = new THREE33.Quaternion();
|
||
|
|
}
|
||
|
|
setInitState() {
|
||
|
|
this._dstRestQuat.copy(this.destination.quaternion);
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
this.destination.updateWorldMatrix(true, false);
|
||
|
|
this.source.updateWorldMatrix(true, false);
|
||
|
|
const dstParentWorldQuat = _quatA7.identity();
|
||
|
|
const invDstParentWorldQuat = _quatB4.identity();
|
||
|
|
if (this.destination.parent) {
|
||
|
|
decomposeRotation(this.destination.parent.matrixWorld, dstParentWorldQuat);
|
||
|
|
quatInvertCompat2(invDstParentWorldQuat.copy(dstParentWorldQuat));
|
||
|
|
}
|
||
|
|
const a0 = _v3A32.copy(this._v3AimAxis).applyQuaternion(this._dstRestQuat).applyQuaternion(dstParentWorldQuat);
|
||
|
|
const a1 = decomposePosition(this.source.matrixWorld, _v3B22).sub(decomposePosition(this.destination.matrixWorld, _v3C2)).normalize();
|
||
|
|
const targetQuat = _quatC2.setFromUnitVectors(a0, a1).premultiply(invDstParentWorldQuat).multiply(dstParentWorldQuat).multiply(this._dstRestQuat);
|
||
|
|
this.destination.quaternion.copy(this._dstRestQuat).slerp(targetQuat, this.weight);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
function traverseAncestorsFromRoot(object, callback) {
|
||
|
|
const ancestors = [object];
|
||
|
|
let head = object.parent;
|
||
|
|
while (head !== null) {
|
||
|
|
ancestors.unshift(head);
|
||
|
|
head = head.parent;
|
||
|
|
}
|
||
|
|
ancestors.forEach((ancestor) => {
|
||
|
|
callback(ancestor);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
var VRMNodeConstraintManager = class {
|
||
|
|
constructor() {
|
||
|
|
this._constraints = /* @__PURE__ */ new Set();
|
||
|
|
this._objectConstraintsMap = /* @__PURE__ */ new Map();
|
||
|
|
}
|
||
|
|
get constraints() {
|
||
|
|
return this._constraints;
|
||
|
|
}
|
||
|
|
addConstraint(constraint) {
|
||
|
|
this._constraints.add(constraint);
|
||
|
|
let objectSet = this._objectConstraintsMap.get(constraint.destination);
|
||
|
|
if (objectSet == null) {
|
||
|
|
objectSet = /* @__PURE__ */ new Set();
|
||
|
|
this._objectConstraintsMap.set(constraint.destination, objectSet);
|
||
|
|
}
|
||
|
|
objectSet.add(constraint);
|
||
|
|
}
|
||
|
|
deleteConstraint(constraint) {
|
||
|
|
this._constraints.delete(constraint);
|
||
|
|
const objectSet = this._objectConstraintsMap.get(constraint.destination);
|
||
|
|
objectSet.delete(constraint);
|
||
|
|
}
|
||
|
|
setInitState() {
|
||
|
|
const constraintsTried = /* @__PURE__ */ new Set();
|
||
|
|
const constraintsDone = /* @__PURE__ */ new Set();
|
||
|
|
for (const constraint of this._constraints) {
|
||
|
|
this._processConstraint(constraint, constraintsTried, constraintsDone, (constraint2) => constraint2.setInitState());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
const constraintsTried = /* @__PURE__ */ new Set();
|
||
|
|
const constraintsDone = /* @__PURE__ */ new Set();
|
||
|
|
for (const constraint of this._constraints) {
|
||
|
|
this._processConstraint(constraint, constraintsTried, constraintsDone, (constraint2) => constraint2.update());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Update a constraint.
|
||
|
|
* If there are other constraints that are dependant, it will try to update them recursively.
|
||
|
|
* It might throw an error if there are circular dependencies.
|
||
|
|
*
|
||
|
|
* Intended to be used in {@link update} and {@link _processConstraint} itself recursively.
|
||
|
|
*
|
||
|
|
* @param constraint A constraint you want to update
|
||
|
|
* @param constraintsTried Set of constraints that are already tried to be updated
|
||
|
|
* @param constraintsDone Set of constraints that are already up to date
|
||
|
|
*/
|
||
|
|
_processConstraint(constraint, constraintsTried, constraintsDone, callback) {
|
||
|
|
if (constraintsDone.has(constraint)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (constraintsTried.has(constraint)) {
|
||
|
|
throw new Error("VRMNodeConstraintManager: Circular dependency detected while updating constraints");
|
||
|
|
}
|
||
|
|
constraintsTried.add(constraint);
|
||
|
|
const depObjects = constraint.dependencies;
|
||
|
|
for (const depObject of depObjects) {
|
||
|
|
traverseAncestorsFromRoot(depObject, (depObjectAncestor) => {
|
||
|
|
const objectSet = this._objectConstraintsMap.get(depObjectAncestor);
|
||
|
|
if (objectSet) {
|
||
|
|
for (const depConstraint of objectSet) {
|
||
|
|
this._processConstraint(depConstraint, constraintsTried, constraintsDone, callback);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
callback(constraint);
|
||
|
|
constraintsDone.add(constraint);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _quatA22 = new THREE43.Quaternion();
|
||
|
|
var _quatB22 = new THREE43.Quaternion();
|
||
|
|
var VRMRotationConstraint = class extends VRMNodeConstraint {
|
||
|
|
get dependencies() {
|
||
|
|
return /* @__PURE__ */ new Set([this.source]);
|
||
|
|
}
|
||
|
|
constructor(destination, source) {
|
||
|
|
super(destination, source);
|
||
|
|
this._dstRestQuat = new THREE43.Quaternion();
|
||
|
|
this._invSrcRestQuat = new THREE43.Quaternion();
|
||
|
|
}
|
||
|
|
setInitState() {
|
||
|
|
this._dstRestQuat.copy(this.destination.quaternion);
|
||
|
|
quatInvertCompat2(this._invSrcRestQuat.copy(this.source.quaternion));
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
const srcDeltaQuat = _quatA22.copy(this._invSrcRestQuat).multiply(this.source.quaternion);
|
||
|
|
const targetQuat = _quatB22.copy(this._dstRestQuat).multiply(srcDeltaQuat);
|
||
|
|
this.destination.quaternion.copy(this._dstRestQuat).slerp(targetQuat, this.weight);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _v3A42 = new THREE53.Vector3();
|
||
|
|
var _quatA32 = new THREE53.Quaternion();
|
||
|
|
var _quatB32 = new THREE53.Quaternion();
|
||
|
|
var VRMRollConstraint = class extends VRMNodeConstraint {
|
||
|
|
/**
|
||
|
|
* The roll axis of the constraint.
|
||
|
|
*/
|
||
|
|
get rollAxis() {
|
||
|
|
return this._rollAxis;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* The roll axis of the constraint.
|
||
|
|
*/
|
||
|
|
set rollAxis(rollAxis) {
|
||
|
|
this._rollAxis = rollAxis;
|
||
|
|
this._v3RollAxis.set(rollAxis === "X" ? 1 : 0, rollAxis === "Y" ? 1 : 0, rollAxis === "Z" ? 1 : 0);
|
||
|
|
}
|
||
|
|
get dependencies() {
|
||
|
|
return /* @__PURE__ */ new Set([this.source]);
|
||
|
|
}
|
||
|
|
constructor(destination, source) {
|
||
|
|
super(destination, source);
|
||
|
|
this._rollAxis = "X";
|
||
|
|
this._v3RollAxis = new THREE53.Vector3(1, 0, 0);
|
||
|
|
this._dstRestQuat = new THREE53.Quaternion();
|
||
|
|
this._invDstRestQuat = new THREE53.Quaternion();
|
||
|
|
this._invSrcRestQuatMulDstRestQuat = new THREE53.Quaternion();
|
||
|
|
}
|
||
|
|
setInitState() {
|
||
|
|
this._dstRestQuat.copy(this.destination.quaternion);
|
||
|
|
quatInvertCompat2(this._invDstRestQuat.copy(this._dstRestQuat));
|
||
|
|
quatInvertCompat2(this._invSrcRestQuatMulDstRestQuat.copy(this.source.quaternion)).multiply(this._dstRestQuat);
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
const quatDelta = _quatA32.copy(this._invDstRestQuat).multiply(this.source.quaternion).multiply(this._invSrcRestQuatMulDstRestQuat);
|
||
|
|
const n1 = _v3A42.copy(this._v3RollAxis).applyQuaternion(quatDelta);
|
||
|
|
const quatFromTo = _quatB32.setFromUnitVectors(n1, this._v3RollAxis);
|
||
|
|
const targetQuat = quatFromTo.premultiply(this._dstRestQuat).multiply(quatDelta);
|
||
|
|
this.destination.quaternion.copy(this._dstRestQuat).slerp(targetQuat, this.weight);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var POSSIBLE_SPEC_VERSIONS7 = /* @__PURE__ */ new Set(["1.0", "1.0-beta"]);
|
||
|
|
var _VRMNodeConstraintLoaderPlugin = class _VRMNodeConstraintLoaderPlugin2 {
|
||
|
|
get name() {
|
||
|
|
return _VRMNodeConstraintLoaderPlugin2.EXTENSION_NAME;
|
||
|
|
}
|
||
|
|
constructor(parser, options) {
|
||
|
|
this.parser = parser;
|
||
|
|
this.helperRoot = options == null ? void 0 : options.helperRoot;
|
||
|
|
}
|
||
|
|
afterRoot(gltf) {
|
||
|
|
return __async6(this, null, function* () {
|
||
|
|
gltf.userData.vrmNodeConstraintManager = yield this._import(gltf);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Import constraints from a GLTF and returns a {@link VRMNodeConstraintManager}.
|
||
|
|
* It might return `null` instead when it does not need to be created or something go wrong.
|
||
|
|
*
|
||
|
|
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
||
|
|
*/
|
||
|
|
_import(gltf) {
|
||
|
|
return __async6(this, null, function* () {
|
||
|
|
var _a;
|
||
|
|
const json = this.parser.json;
|
||
|
|
const isConstraintsUsed = ((_a = json.extensionsUsed) == null ? void 0 : _a.indexOf(_VRMNodeConstraintLoaderPlugin2.EXTENSION_NAME)) !== -1;
|
||
|
|
if (!isConstraintsUsed) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const manager = new VRMNodeConstraintManager();
|
||
|
|
const threeNodes = yield this.parser.getDependencies("node");
|
||
|
|
threeNodes.forEach((node, nodeIndex) => {
|
||
|
|
var _a2;
|
||
|
|
const schemaNode = json.nodes[nodeIndex];
|
||
|
|
const extension = (_a2 = schemaNode == null ? void 0 : schemaNode.extensions) == null ? void 0 : _a2[_VRMNodeConstraintLoaderPlugin2.EXTENSION_NAME];
|
||
|
|
if (extension == null) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const specVersion = extension.specVersion;
|
||
|
|
if (!POSSIBLE_SPEC_VERSIONS7.has(specVersion)) {
|
||
|
|
console.warn(
|
||
|
|
`VRMNodeConstraintLoaderPlugin: Unknown ${_VRMNodeConstraintLoaderPlugin2.EXTENSION_NAME} specVersion "${specVersion}"`
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const constraintDef = extension.constraint;
|
||
|
|
if (constraintDef.roll != null) {
|
||
|
|
const constraint = this._importRollConstraint(node, threeNodes, constraintDef.roll);
|
||
|
|
manager.addConstraint(constraint);
|
||
|
|
} else if (constraintDef.aim != null) {
|
||
|
|
const constraint = this._importAimConstraint(node, threeNodes, constraintDef.aim);
|
||
|
|
manager.addConstraint(constraint);
|
||
|
|
} else if (constraintDef.rotation != null) {
|
||
|
|
const constraint = this._importRotationConstraint(node, threeNodes, constraintDef.rotation);
|
||
|
|
manager.addConstraint(constraint);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
gltf.scene.updateMatrixWorld();
|
||
|
|
manager.setInitState();
|
||
|
|
return manager;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_importRollConstraint(destination, nodes, rollConstraintDef) {
|
||
|
|
const { source: sourceIndex, rollAxis, weight } = rollConstraintDef;
|
||
|
|
const source = nodes[sourceIndex];
|
||
|
|
const constraint = new VRMRollConstraint(destination, source);
|
||
|
|
if (rollAxis != null) {
|
||
|
|
constraint.rollAxis = rollAxis;
|
||
|
|
}
|
||
|
|
if (weight != null) {
|
||
|
|
constraint.weight = weight;
|
||
|
|
}
|
||
|
|
if (this.helperRoot) {
|
||
|
|
const helper = new VRMNodeConstraintHelper(constraint);
|
||
|
|
this.helperRoot.add(helper);
|
||
|
|
}
|
||
|
|
return constraint;
|
||
|
|
}
|
||
|
|
_importAimConstraint(destination, nodes, aimConstraintDef) {
|
||
|
|
const { source: sourceIndex, aimAxis, weight } = aimConstraintDef;
|
||
|
|
const source = nodes[sourceIndex];
|
||
|
|
const constraint = new VRMAimConstraint(destination, source);
|
||
|
|
if (aimAxis != null) {
|
||
|
|
constraint.aimAxis = aimAxis;
|
||
|
|
}
|
||
|
|
if (weight != null) {
|
||
|
|
constraint.weight = weight;
|
||
|
|
}
|
||
|
|
if (this.helperRoot) {
|
||
|
|
const helper = new VRMNodeConstraintHelper(constraint);
|
||
|
|
this.helperRoot.add(helper);
|
||
|
|
}
|
||
|
|
return constraint;
|
||
|
|
}
|
||
|
|
_importRotationConstraint(destination, nodes, rotationConstraintDef) {
|
||
|
|
const { source: sourceIndex, weight } = rotationConstraintDef;
|
||
|
|
const source = nodes[sourceIndex];
|
||
|
|
const constraint = new VRMRotationConstraint(destination, source);
|
||
|
|
if (weight != null) {
|
||
|
|
constraint.weight = weight;
|
||
|
|
}
|
||
|
|
if (this.helperRoot) {
|
||
|
|
const helper = new VRMNodeConstraintHelper(constraint);
|
||
|
|
this.helperRoot.add(helper);
|
||
|
|
}
|
||
|
|
return constraint;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
_VRMNodeConstraintLoaderPlugin.EXTENSION_NAME = "VRMC_node_constraint";
|
||
|
|
var VRMNodeConstraintLoaderPlugin = _VRMNodeConstraintLoaderPlugin;
|
||
|
|
|
||
|
|
// ../three-vrm-springbone/lib/three-vrm-springbone.module.js
|
||
|
|
var THREE72 = __toESM(require("three"), 1);
|
||
|
|
var THREE20 = __toESM(require("three"), 1);
|
||
|
|
var THREE24 = __toESM(require("three"), 1);
|
||
|
|
var THREE34 = __toESM(require("three"), 1);
|
||
|
|
var THREE44 = __toESM(require("three"), 1);
|
||
|
|
var THREE54 = __toESM(require("three"), 1);
|
||
|
|
var THREE62 = __toESM(require("three"), 1);
|
||
|
|
var THREE92 = __toESM(require("three"), 1);
|
||
|
|
var THREE82 = __toESM(require("three"), 1);
|
||
|
|
var THREE102 = __toESM(require("three"), 1);
|
||
|
|
var THREE132 = __toESM(require("three"), 1);
|
||
|
|
var THREE122 = __toESM(require("three"), 1);
|
||
|
|
var THREE112 = __toESM(require("three"), 1);
|
||
|
|
var THREE142 = __toESM(require("three"), 1);
|
||
|
|
var __async7 = (__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 VRMSpringBoneColliderShape = class {
|
||
|
|
};
|
||
|
|
var _v3A7 = new THREE20.Vector3();
|
||
|
|
var _v3B5 = new THREE20.Vector3();
|
||
|
|
var VRMSpringBoneColliderShapeCapsule = class extends VRMSpringBoneColliderShape {
|
||
|
|
get type() {
|
||
|
|
return "capsule";
|
||
|
|
}
|
||
|
|
constructor(params) {
|
||
|
|
var _a, _b, _c, _d;
|
||
|
|
super();
|
||
|
|
this.offset = (_a = params == null ? void 0 : params.offset) != null ? _a : new THREE20.Vector3(0, 0, 0);
|
||
|
|
this.tail = (_b = params == null ? void 0 : params.tail) != null ? _b : new THREE20.Vector3(0, 0, 0);
|
||
|
|
this.radius = (_c = params == null ? void 0 : params.radius) != null ? _c : 0;
|
||
|
|
this.inside = (_d = params == null ? void 0 : params.inside) != null ? _d : false;
|
||
|
|
}
|
||
|
|
calculateCollision(colliderMatrix, objectPosition, objectRadius, target) {
|
||
|
|
_v3A7.setFromMatrixPosition(colliderMatrix);
|
||
|
|
_v3B5.subVectors(this.tail, this.offset).applyMatrix4(colliderMatrix);
|
||
|
|
_v3B5.sub(_v3A7);
|
||
|
|
const lengthSqCapsule = _v3B5.lengthSq();
|
||
|
|
target.copy(objectPosition).sub(_v3A7);
|
||
|
|
const dot = _v3B5.dot(target);
|
||
|
|
if (dot <= 0) {
|
||
|
|
} else if (lengthSqCapsule <= dot) {
|
||
|
|
target.sub(_v3B5);
|
||
|
|
} else {
|
||
|
|
_v3B5.multiplyScalar(dot / lengthSqCapsule);
|
||
|
|
target.sub(_v3B5);
|
||
|
|
}
|
||
|
|
const length = target.length();
|
||
|
|
const distance = this.inside ? this.radius - objectRadius - length : length - objectRadius - this.radius;
|
||
|
|
if (distance < 0) {
|
||
|
|
target.multiplyScalar(1 / length);
|
||
|
|
if (this.inside) {
|
||
|
|
target.negate();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return distance;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _v3A23 = new THREE24.Vector3();
|
||
|
|
var _mat3A = new THREE24.Matrix3();
|
||
|
|
var VRMSpringBoneColliderShapePlane = class extends VRMSpringBoneColliderShape {
|
||
|
|
get type() {
|
||
|
|
return "plane";
|
||
|
|
}
|
||
|
|
constructor(params) {
|
||
|
|
var _a, _b;
|
||
|
|
super();
|
||
|
|
this.offset = (_a = params == null ? void 0 : params.offset) != null ? _a : new THREE24.Vector3(0, 0, 0);
|
||
|
|
this.normal = (_b = params == null ? void 0 : params.normal) != null ? _b : new THREE24.Vector3(0, 0, 1);
|
||
|
|
}
|
||
|
|
calculateCollision(colliderMatrix, objectPosition, objectRadius, target) {
|
||
|
|
target.setFromMatrixPosition(colliderMatrix);
|
||
|
|
target.negate().add(objectPosition);
|
||
|
|
_mat3A.getNormalMatrix(colliderMatrix);
|
||
|
|
_v3A23.copy(this.normal).applyNormalMatrix(_mat3A).normalize();
|
||
|
|
const distance = target.dot(_v3A23) - objectRadius;
|
||
|
|
target.copy(_v3A23);
|
||
|
|
return distance;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _v3A33 = new THREE34.Vector3();
|
||
|
|
var VRMSpringBoneColliderShapeSphere = class extends VRMSpringBoneColliderShape {
|
||
|
|
get type() {
|
||
|
|
return "sphere";
|
||
|
|
}
|
||
|
|
constructor(params) {
|
||
|
|
var _a, _b, _c;
|
||
|
|
super();
|
||
|
|
this.offset = (_a = params == null ? void 0 : params.offset) != null ? _a : new THREE34.Vector3(0, 0, 0);
|
||
|
|
this.radius = (_b = params == null ? void 0 : params.radius) != null ? _b : 0;
|
||
|
|
this.inside = (_c = params == null ? void 0 : params.inside) != null ? _c : false;
|
||
|
|
}
|
||
|
|
calculateCollision(colliderMatrix, objectPosition, objectRadius, target) {
|
||
|
|
target.subVectors(objectPosition, _v3A33.setFromMatrixPosition(colliderMatrix));
|
||
|
|
const length = target.length();
|
||
|
|
const distance = this.inside ? this.radius - objectRadius - length : length - objectRadius - this.radius;
|
||
|
|
if (distance < 0) {
|
||
|
|
target.multiplyScalar(1 / length);
|
||
|
|
if (this.inside) {
|
||
|
|
target.negate();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return distance;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _v3A43 = new THREE44.Vector3();
|
||
|
|
var ColliderShapeCapsuleBufferGeometry = class extends THREE44.BufferGeometry {
|
||
|
|
constructor(shape) {
|
||
|
|
super();
|
||
|
|
this.worldScale = 1;
|
||
|
|
this._currentRadius = 0;
|
||
|
|
this._currentOffset = new THREE44.Vector3();
|
||
|
|
this._currentTail = new THREE44.Vector3();
|
||
|
|
this._shape = shape;
|
||
|
|
this._attrPos = new THREE44.BufferAttribute(new Float32Array(396), 3);
|
||
|
|
this.setAttribute("position", this._attrPos);
|
||
|
|
this._attrIndex = new THREE44.BufferAttribute(new Uint16Array(264), 1);
|
||
|
|
this.setIndex(this._attrIndex);
|
||
|
|
this._buildIndex();
|
||
|
|
this.update();
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
let shouldUpdateGeometry = false;
|
||
|
|
const radius = this._shape.radius / this.worldScale;
|
||
|
|
if (this._currentRadius !== radius) {
|
||
|
|
this._currentRadius = radius;
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (!this._currentOffset.equals(this._shape.offset)) {
|
||
|
|
this._currentOffset.copy(this._shape.offset);
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
const tail = _v3A43.copy(this._shape.tail).divideScalar(this.worldScale);
|
||
|
|
if (this._currentTail.distanceToSquared(tail) > 1e-10) {
|
||
|
|
this._currentTail.copy(tail);
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (shouldUpdateGeometry) {
|
||
|
|
this._buildPosition();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_buildPosition() {
|
||
|
|
_v3A43.copy(this._currentTail).sub(this._currentOffset);
|
||
|
|
const l = _v3A43.length() / this._currentRadius;
|
||
|
|
for (let i = 0; i <= 16; i++) {
|
||
|
|
const t = i / 16 * Math.PI;
|
||
|
|
this._attrPos.setXYZ(i, -Math.sin(t), -Math.cos(t), 0);
|
||
|
|
this._attrPos.setXYZ(17 + i, l + Math.sin(t), Math.cos(t), 0);
|
||
|
|
this._attrPos.setXYZ(34 + i, -Math.sin(t), 0, -Math.cos(t));
|
||
|
|
this._attrPos.setXYZ(51 + i, l + Math.sin(t), 0, Math.cos(t));
|
||
|
|
}
|
||
|
|
for (let i = 0; i < 32; i++) {
|
||
|
|
const t = i / 16 * Math.PI;
|
||
|
|
this._attrPos.setXYZ(68 + i, 0, Math.sin(t), Math.cos(t));
|
||
|
|
this._attrPos.setXYZ(100 + i, l, Math.sin(t), Math.cos(t));
|
||
|
|
}
|
||
|
|
const theta = Math.atan2(_v3A43.y, Math.sqrt(_v3A43.x * _v3A43.x + _v3A43.z * _v3A43.z));
|
||
|
|
const phi = -Math.atan2(_v3A43.z, _v3A43.x);
|
||
|
|
this.rotateZ(theta);
|
||
|
|
this.rotateY(phi);
|
||
|
|
this.scale(this._currentRadius, this._currentRadius, this._currentRadius);
|
||
|
|
this.translate(this._currentOffset.x, this._currentOffset.y, this._currentOffset.z);
|
||
|
|
this._attrPos.needsUpdate = true;
|
||
|
|
}
|
||
|
|
_buildIndex() {
|
||
|
|
for (let i = 0; i < 34; i++) {
|
||
|
|
const i1 = (i + 1) % 34;
|
||
|
|
this._attrIndex.setXY(i * 2, i, i1);
|
||
|
|
this._attrIndex.setXY(68 + i * 2, 34 + i, 34 + i1);
|
||
|
|
}
|
||
|
|
for (let i = 0; i < 32; i++) {
|
||
|
|
const i1 = (i + 1) % 32;
|
||
|
|
this._attrIndex.setXY(136 + i * 2, 68 + i, 68 + i1);
|
||
|
|
this._attrIndex.setXY(200 + i * 2, 100 + i, 100 + i1);
|
||
|
|
}
|
||
|
|
this._attrIndex.needsUpdate = true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var ColliderShapePlaneBufferGeometry = class extends THREE54.BufferGeometry {
|
||
|
|
constructor(shape) {
|
||
|
|
super();
|
||
|
|
this.worldScale = 1;
|
||
|
|
this._currentOffset = new THREE54.Vector3();
|
||
|
|
this._currentNormal = new THREE54.Vector3();
|
||
|
|
this._shape = shape;
|
||
|
|
this._attrPos = new THREE54.BufferAttribute(new Float32Array(6 * 3), 3);
|
||
|
|
this.setAttribute("position", this._attrPos);
|
||
|
|
this._attrIndex = new THREE54.BufferAttribute(new Uint16Array(10), 1);
|
||
|
|
this.setIndex(this._attrIndex);
|
||
|
|
this._buildIndex();
|
||
|
|
this.update();
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
let shouldUpdateGeometry = false;
|
||
|
|
if (!this._currentOffset.equals(this._shape.offset)) {
|
||
|
|
this._currentOffset.copy(this._shape.offset);
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (!this._currentNormal.equals(this._shape.normal)) {
|
||
|
|
this._currentNormal.copy(this._shape.normal);
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (shouldUpdateGeometry) {
|
||
|
|
this._buildPosition();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_buildPosition() {
|
||
|
|
this._attrPos.setXYZ(0, -0.5, -0.5, 0);
|
||
|
|
this._attrPos.setXYZ(1, 0.5, -0.5, 0);
|
||
|
|
this._attrPos.setXYZ(2, 0.5, 0.5, 0);
|
||
|
|
this._attrPos.setXYZ(3, -0.5, 0.5, 0);
|
||
|
|
this._attrPos.setXYZ(4, 0, 0, 0);
|
||
|
|
this._attrPos.setXYZ(5, 0, 0, 0.25);
|
||
|
|
this.translate(this._currentOffset.x, this._currentOffset.y, this._currentOffset.z);
|
||
|
|
this.lookAt(this._currentNormal);
|
||
|
|
this._attrPos.needsUpdate = true;
|
||
|
|
}
|
||
|
|
_buildIndex() {
|
||
|
|
this._attrIndex.setXY(0, 0, 1);
|
||
|
|
this._attrIndex.setXY(2, 1, 2);
|
||
|
|
this._attrIndex.setXY(4, 2, 3);
|
||
|
|
this._attrIndex.setXY(6, 3, 0);
|
||
|
|
this._attrIndex.setXY(8, 4, 5);
|
||
|
|
this._attrIndex.needsUpdate = true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var ColliderShapeSphereBufferGeometry = class extends THREE62.BufferGeometry {
|
||
|
|
constructor(shape) {
|
||
|
|
super();
|
||
|
|
this.worldScale = 1;
|
||
|
|
this._currentRadius = 0;
|
||
|
|
this._currentOffset = new THREE62.Vector3();
|
||
|
|
this._shape = shape;
|
||
|
|
this._attrPos = new THREE62.BufferAttribute(new Float32Array(32 * 3 * 3), 3);
|
||
|
|
this.setAttribute("position", this._attrPos);
|
||
|
|
this._attrIndex = new THREE62.BufferAttribute(new Uint16Array(64 * 3), 1);
|
||
|
|
this.setIndex(this._attrIndex);
|
||
|
|
this._buildIndex();
|
||
|
|
this.update();
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
let shouldUpdateGeometry = false;
|
||
|
|
const radius = this._shape.radius / this.worldScale;
|
||
|
|
if (this._currentRadius !== radius) {
|
||
|
|
this._currentRadius = radius;
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (!this._currentOffset.equals(this._shape.offset)) {
|
||
|
|
this._currentOffset.copy(this._shape.offset);
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (shouldUpdateGeometry) {
|
||
|
|
this._buildPosition();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_buildPosition() {
|
||
|
|
for (let i = 0; i < 32; i++) {
|
||
|
|
const t = i / 16 * Math.PI;
|
||
|
|
this._attrPos.setXYZ(i, Math.cos(t), Math.sin(t), 0);
|
||
|
|
this._attrPos.setXYZ(32 + i, 0, Math.cos(t), Math.sin(t));
|
||
|
|
this._attrPos.setXYZ(64 + i, Math.sin(t), 0, Math.cos(t));
|
||
|
|
}
|
||
|
|
this.scale(this._currentRadius, this._currentRadius, this._currentRadius);
|
||
|
|
this.translate(this._currentOffset.x, this._currentOffset.y, this._currentOffset.z);
|
||
|
|
this._attrPos.needsUpdate = true;
|
||
|
|
}
|
||
|
|
_buildIndex() {
|
||
|
|
for (let i = 0; i < 32; i++) {
|
||
|
|
const i1 = (i + 1) % 32;
|
||
|
|
this._attrIndex.setXY(i * 2, i, i1);
|
||
|
|
this._attrIndex.setXY(64 + i * 2, 32 + i, 32 + i1);
|
||
|
|
this._attrIndex.setXY(128 + i * 2, 64 + i, 64 + i1);
|
||
|
|
}
|
||
|
|
this._attrIndex.needsUpdate = true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _v3A52 = new THREE72.Vector3();
|
||
|
|
var VRMSpringBoneColliderHelper = class extends THREE72.Group {
|
||
|
|
constructor(collider) {
|
||
|
|
super();
|
||
|
|
this.matrixAutoUpdate = false;
|
||
|
|
this.collider = collider;
|
||
|
|
if (this.collider.shape instanceof VRMSpringBoneColliderShapeSphere) {
|
||
|
|
this._geometry = new ColliderShapeSphereBufferGeometry(this.collider.shape);
|
||
|
|
} else if (this.collider.shape instanceof VRMSpringBoneColliderShapeCapsule) {
|
||
|
|
this._geometry = new ColliderShapeCapsuleBufferGeometry(this.collider.shape);
|
||
|
|
} else if (this.collider.shape instanceof VRMSpringBoneColliderShapePlane) {
|
||
|
|
this._geometry = new ColliderShapePlaneBufferGeometry(this.collider.shape);
|
||
|
|
} else {
|
||
|
|
throw new Error("VRMSpringBoneColliderHelper: Unknown collider shape type detected");
|
||
|
|
}
|
||
|
|
const material = new THREE72.LineBasicMaterial({
|
||
|
|
color: 16711935,
|
||
|
|
depthTest: false,
|
||
|
|
depthWrite: false
|
||
|
|
});
|
||
|
|
this._line = new THREE72.LineSegments(this._geometry, material);
|
||
|
|
this.add(this._line);
|
||
|
|
}
|
||
|
|
dispose() {
|
||
|
|
this._geometry.dispose();
|
||
|
|
}
|
||
|
|
updateMatrixWorld(force) {
|
||
|
|
this.collider.updateWorldMatrix(true, false);
|
||
|
|
this.matrix.copy(this.collider.matrixWorld);
|
||
|
|
const matrixWorldElements = this.matrix.elements;
|
||
|
|
this._geometry.worldScale = _v3A52.set(matrixWorldElements[0], matrixWorldElements[1], matrixWorldElements[2]).length();
|
||
|
|
this._geometry.update();
|
||
|
|
super.updateMatrixWorld(force);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var SpringBoneBufferGeometry = class extends THREE82.BufferGeometry {
|
||
|
|
constructor(springBone) {
|
||
|
|
super();
|
||
|
|
this.worldScale = 1;
|
||
|
|
this._currentRadius = 0;
|
||
|
|
this._currentTail = new THREE82.Vector3();
|
||
|
|
this._springBone = springBone;
|
||
|
|
this._attrPos = new THREE82.BufferAttribute(new Float32Array(294), 3);
|
||
|
|
this.setAttribute("position", this._attrPos);
|
||
|
|
this._attrIndex = new THREE82.BufferAttribute(new Uint16Array(194), 1);
|
||
|
|
this.setIndex(this._attrIndex);
|
||
|
|
this._buildIndex();
|
||
|
|
this.update();
|
||
|
|
}
|
||
|
|
update() {
|
||
|
|
let shouldUpdateGeometry = false;
|
||
|
|
const radius = this._springBone.settings.hitRadius / this.worldScale;
|
||
|
|
if (this._currentRadius !== radius) {
|
||
|
|
this._currentRadius = radius;
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (!this._currentTail.equals(this._springBone.initialLocalChildPosition)) {
|
||
|
|
this._currentTail.copy(this._springBone.initialLocalChildPosition);
|
||
|
|
shouldUpdateGeometry = true;
|
||
|
|
}
|
||
|
|
if (shouldUpdateGeometry) {
|
||
|
|
this._buildPosition();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_buildPosition() {
|
||
|
|
for (let i = 0; i < 32; i++) {
|
||
|
|
const t = i / 16 * Math.PI;
|
||
|
|
this._attrPos.setXYZ(i, Math.cos(t), Math.sin(t), 0);
|
||
|
|
this._attrPos.setXYZ(32 + i, 0, Math.cos(t), Math.sin(t));
|
||
|
|
this._attrPos.setXYZ(64 + i, Math.sin(t), 0, Math.cos(t));
|
||
|
|
}
|
||
|
|
this.scale(this._currentRadius, this._currentRadius, this._currentRadius);
|
||
|
|
this.translate(this._currentTail.x, this._currentTail.y, this._currentTail.z);
|
||
|
|
this._attrPos.setXYZ(96, 0, 0, 0);
|
||
|
|
this._attrPos.setXYZ(97, this._currentTail.x, this._currentTail.y, this._currentTail.z);
|
||
|
|
this._attrPos.needsUpdate = true;
|
||
|
|
}
|
||
|
|
_buildIndex() {
|
||
|
|
for (let i = 0; i < 32; i++) {
|
||
|
|
const i1 = (i + 1) % 32;
|
||
|
|
this._attrIndex.setXY(i * 2, i, i1);
|
||
|
|
this._attrIndex.setXY(64 + i * 2, 32 + i, 32 + i1);
|
||
|
|
this._attrIndex.setXY(128 + i * 2, 64 + i, 64 + i1);
|
||
|
|
}
|
||
|
|
this._attrIndex.setXY(192, 96, 97);
|
||
|
|
this._attrIndex.needsUpdate = true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var _v3A62 = new THREE92.Vector3();
|
||
|
|
var VRMSpringBoneJointHelper = class extends THREE92.Group {
|
||
|
|
constructor(springBone) {
|
||
|
|
super();
|
||
|
|
this.matrixAutoUpdate = false;
|
||
|
|
this.springBone = springBone;
|
||
|
|
this._geometry = new SpringBoneBufferGeometry(this.springBone);
|
||
|
|
const material = new THREE92.LineBasicMaterial({
|
||
|
|
color: 16776960,
|
||
|
|
depthTest: false,
|
||
|
|
depthWrite: false
|
||
|
|
});
|
||
|
|
this._line = new THREE92.LineSegments(this._geometry, material);
|
||
|
|
this.add(this._line);
|
||
|
|
}
|
||
|
|
dispose() {
|
||
|
|
this._geometry.dispose();
|
||
|
|
}
|
||
|
|
updateMatrixWorld(force) {
|
||
|
|
this.springBone.bone.updateWorldMatrix(true, false);
|
||
|
|
this.matrix.copy(this.springBone.bone.matrixWorld);
|
||
|
|
const matrixWorldElements = this.matrix.elements;
|
||
|
|
this._geometry.worldScale = _v3A62.set(matrixWorldElements[0], matrixWorldElements[1], matrixWorldElements[2]).length();
|
||
|
|
this._geometry.update();
|
||
|
|
super.updateMatrixWorld(force);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var VRMSpringBoneCollider = class extends THREE102.Object3D {
|
||
|
|
constructor(shape) {
|
||
|
|
super();
|
||
|
|
this.colliderMatrix = new THREE102.Matrix4();
|
||
|
|
this.shape = shape;
|
||
|
|
}
|
||
|
|
updateWorldMatrix(updateParents, updateChildren) {
|
||
|
|
super.updateWorldMatrix(updateParents, updateChildren);
|
||
|
|
updateColliderMatrix(this.colliderMatrix, this.matrixWorld, this.shape.offset);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
function updateColliderMatrix(colliderMatrix, matrixWorld, offset) {
|
||
|
|
const me = matrixWorld.elements;
|
||
|
|
colliderMatrix.copy(matrixWorld);
|
||
|
|
if (offset) {
|
||
|
|
colliderMatrix.elements[12] = me[0] * offset.x + me[4] * offset.y + me[8] * offset.z + me[12];
|
||
|
|
colliderMatrix.elements[13] = me[1] * offset.x + me[5] * offset.y + me[9] * offset.z + me[13];
|
||
|
|
colliderMatrix.elements[14] = me[2] * offset.x + me[6] * offset.y + me[10] * offset.z + me[14];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
var _matA = new THREE112.Matrix4();
|
||
|
|
function mat4InvertCompat(target) {
|
||
|
|
if (target.invert) {
|
||
|
|
target.invert();
|
||
|
|
} else {
|
||
|
|
target.getInverse(_matA.copy(target));
|
||
|
|
}
|
||
|
|
return target;
|
||
|
|
}
|
||
|
|
var Matrix4InverseCache = class {
|
||
|
|
constructor(matrix) {
|
||
|
|
this._inverseCache = new THREE122.Matrix4();
|
||
|
|
this._shouldUpdateInverse = true;
|
||
|
|
this.matrix = matrix;
|
||
|
|
const handler = {
|
||
|
|
set: (obj, prop, newVal) => {
|
||
|
|
this._shouldUpdateInverse = true;
|
||
|
|
obj[prop] = newVal;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
this._originalElements = matrix.elements;
|
||
|
|
matrix.elements = new Proxy(matrix.elements, handler);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Inverse of given matrix.
|
||
|
|
* Note that it will return its internal private instance.
|
||
|
|
* Make sure copying this before mutate this.
|
||
|
|
*/
|
||
|
|
get inverse() {
|
||
|
|
if (this._shouldUpdateInverse) {
|
||
|
|
mat4InvertCompat(this._inverseCache.copy(this.matrix));
|
||
|
|
this._shouldUpdateInverse = false;
|
||
|
|
}
|
||
|
|
return this._inverseCache;
|
||
|
|
}
|
||
|
|
revert() {
|
||
|
|
this.matrix.elements = this._originalElements;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var IDENTITY_MATRIX4 = new THREE132.Matrix4();
|
||
|
|
var _v3A72 = new THREE132.Vector3();
|
||
|
|
var _v3B23 = new THREE132.Vector3();
|
||
|
|
var _worldSpacePosition = new THREE132.Vector3();
|
||
|
|
var _nextTail = new THREE132.Vector3();
|
||
|
|
var _matA2 = new THREE132.Matrix4();
|
||
|
|
var VRMSpringBoneJoint = class {
|
||
|
|
/**
|
||
|
|
* Create a new VRMSpringBone.
|
||
|
|
*
|
||
|
|
* @param bone An Object3D that will be attached to this bone
|
||
|
|
* @param child An Object3D that will be used as a tail of this spring bone. It can be null when the spring bone is imported from VRM 0.0
|
||
|
|
* @param settings Several parameters related to behavior of the spring bone
|
||
|
|
* @param colliderGroups Collider groups that will be collided with this spring bone
|
||
|
|
*/
|
||
|
|
constructor(bone, child, settings = {}, colliderGroups = []) {
|
||
|
|
this._currentTail = new THREE132.Vector3();
|
||
|
|
this._prevTail = new THREE132.Vector3();
|
||
|
|
this._boneAxis = new THREE132.Vector3();
|
||
|
|
this._worldSpaceBoneLength = 0;
|
||
|
|
this._center = null;
|
||
|
|
this._initialLocalMatrix = new THREE132.Matrix4();
|
||
|
|
this._initialLocalRotation = new THREE132.Quaternion();
|
||
|
|
this._initialLocalChildPosition = new THREE132.Vector3();
|
||
|
|
var _a, _b, _c, _d, _e, _f;
|
||
|
|
this.bone = bone;
|
||
|
|
this.bone.matrixAutoUpdate = false;
|
||
|
|
this.child = child;
|
||
|
|
this.settings = {
|
||
|
|
hitRadius: (_a = settings.hitRadius) != null ? _a : 0,
|
||
|
|
stiffness: (_b = settings.stiffness) != null ? _b : 1,
|
||
|
|
gravityPower: (_c = settings.gravityPower) != null ? _c : 0,
|
||
|
|
gravityDir: (_e = (_d = settings.gravityDir) == null ? void 0 : _d.clone()) != null ? _e : new THREE132.Vector3(0, -1, 0),
|
||
|
|
dragForce: (_f = settings.dragForce) != null ? _f : 0.4
|
||
|
|
};
|
||
|
|
this.colliderGroups = colliderGroups;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Set of dependencies that need to be updated before this joint.
|
||
|
|
*/
|
||
|
|
get dependencies() {
|
||
|
|
const set = /* @__PURE__ */ new Set();
|
||
|
|
const parent = this.bone.parent;
|
||
|
|
if (parent) {
|
||
|
|
set.add(parent);
|
||
|
|
}
|
||
|
|
for (let cg = 0; cg < this.colliderGroups.length; cg++) {
|
||
|
|
for (let c = 0; c < this.colliderGroups[cg].colliders.length; c++) {
|
||
|
|
set.add(this.colliderGroups[cg].colliders[c]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return set;
|
||
|
|
}
|
||
|
|
get center() {
|
||
|
|
return this._center;
|
||
|
|
}
|
||
|
|
set center(center) {
|
||
|
|
var _a;
|
||
|
|
if ((_a = this._center) == null ? void 0 : _a.userData.inverseCacheProxy) {
|
||
|
|
this._center.userData.inverseCacheProxy.revert();
|
||
|
|
delete this._center.userData.inverseCacheProxy;
|
||
|
|
}
|
||
|
|
this._center = center;
|
||
|
|
if (this._center) {
|
||
|
|
if (!this._center.userData.inverseCacheProxy) {
|
||
|
|
this._center.userData.inverseCacheProxy = new Matrix4InverseCache(this._center.matrixWorld);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
get initialLocalChildPosition() {
|
||
|
|
return this._initialLocalChildPosition;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Returns the world matrix of its parent object.
|
||
|
|
* Note that it returns a reference to the matrix. Don't mutate this directly!
|
||
|
|
*/
|
||
|
|
get _parentMatrixWorld() {
|
||
|
|
return this.bone.parent ? this.bone.parent.matrixWorld : IDENTITY_MATRIX4;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Set the initial state of this spring bone.
|
||
|
|
* You might want to call {@link VRMSpringBoneManager.setInitState} instead.
|
||
|
|
*/
|
||
|
|
setInitState() {
|
||
|
|
this._initialLocalMatrix.copy(this.bone.matrix);
|
||
|
|
this._initialLocalRotation.copy(this.bone.quaternion);
|
||
|
|
if (this.child) {
|
||
|
|
this._initialLocalChildPosition.copy(this.child.position);
|
||
|
|
} else {
|
||
|
|
this._initialLocalChildPosition.copy(this.bone.position).normalize().multiplyScalar(0.07);
|
||
|
|
}
|
||
|
|
const matrixWorldToCenter = this._getMatrixWorldToCenter();
|
||
|
|
this.bone.localToWorld(this._currentTail.copy(this._initialLocalChildPosition)).applyMatrix4(matrixWorldToCenter);
|
||
|
|
this._prevTail.copy(this._currentTail);
|
||
|
|
this._boneAxis.copy(this._initialLocalChildPosition).normalize();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Reset the state of this bone.
|
||
|
|
* You might want to call {@link VRMSpringBoneManager.reset} instead.
|
||
|
|
*/
|
||
|
|
reset() {
|
||
|
|
this.bone.quaternion.copy(this._initialLocalRotation);
|
||
|
|
this.bone.updateMatrix();
|
||
|
|
this.bone.matrixWorld.multiplyMatrices(this._parentMatrixWorld, this.bone.matrix);
|
||
|
|
const matrixWorldToCenter = this._getMatrixWorldToCenter();
|
||
|
|
this.bone.localToWorld(this._currentTail.copy(this._initialLocalChildPosition)).applyMatrix4(matrixWorldToCenter);
|
||
|
|
this._prevTail.copy(this._currentTail);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Update the state of this bone.
|
||
|
|
* You might want to call {@link VRMSpringBoneManager.update} instead.
|
||
|
|
*
|
||
|
|
* @param delta deltaTime
|
||
|
|
*/
|
||
|
|
update(delta) {
|
||
|
|
if (delta <= 0) return;
|
||
|
|
this._calcWorldSpaceBoneLength();
|
||
|
|
const worldSpaceBoneAxis = _v3B23.copy(this._boneAxis).transformDirection(this._initialLocalMatrix).transformDirection(this._parentMatrixWorld);
|
||
|
|
_nextTail.copy(this._currentTail).add(_v3A72.subVectors(this._currentTail, this._prevTail).multiplyScalar(1 - this.settings.dragForce)).applyMatrix4(this._getMatrixCenterToWorld()).addScaledVector(worldSpaceBoneAxis, this.settings.stiffness * delta).addScaledVector(this.settings.gravityDir, this.settings.gravityPower * delta);
|
||
|
|
_worldSpacePosition.setFromMatrixPosition(this.bone.matrixWorld);
|
||
|
|
_nextTail.sub(_worldSpacePosition).normalize().multiplyScalar(this._worldSpaceBoneLength).add(_worldSpacePosition);
|
||
|
|
this._collision(_nextTail);
|
||
|
|
this._prevTail.copy(this._currentTail);
|
||
|
|
this._currentTail.copy(_nextTail).applyMatrix4(this._getMatrixWorldToCenter());
|
||
|
|
const worldSpaceInitialMatrixInv = _matA2.multiplyMatrices(this._parentMatrixWorld, this._initialLocalMatrix).invert();
|
||
|
|
this.bone.quaternion.setFromUnitVectors(this._boneAxis, _v3A72.copy(_nextTail).applyMatrix4(worldSpaceInitialMatrixInv).normalize()).premultiply(this._initialLocalRotation);
|
||
|
|
this.bone.updateMatrix();
|
||
|
|
this.bone.matrixWorld.multiplyMatrices(this._parentMatrixWorld, this.bone.matrix);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Do collision math against every colliders attached to this bone.
|
||
|
|
*
|
||
|
|
* @param tail The tail you want to process
|
||
|
|
*/
|
||
|
|
_collision(tail) {
|
||
|
|
for (let cg = 0; cg < this.colliderGroups.length; cg++) {
|
||
|
|
for (let c = 0; c < this.colliderGroups[cg].colliders.length; c++) {
|
||
|
|
const collider = this.colliderGroups[cg].colliders[c];
|
||
|
|
const dist = collider.shape.calculateCollision(collider.colliderMatrix, tail, this.settings.hitRadius, _v3A72);
|
||
|
|
if (dist < 0) {
|
||
|
|
tail.addScaledVector(_v3A72, -dist);
|
||
|
|
tail.sub(_worldSpacePosition);
|
||
|
|
const length = tail.length();
|
||
|
|
tail.multiplyScalar(this._worldSpaceBoneLength / length).add(_worldSpacePosition);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Calculate the {@link _worldSpaceBoneLength}.
|
||
|
|
* Intended to be used in {@link update}.
|
||
|
|
*/
|
||
|
|
_calcWorldSpaceBoneLength() {
|
||
|
|
_v3A72.setFromMatrixPosition(this.bone.matrixWorld);
|
||
|
|
if (this.child) {
|
||
|
|
_v3B23.setFromMatrixPosition(this.child.matrixWorld);
|
||
|
|
} else {
|
||
|
|
_v3B23.copy(this._initialLocalChildPosition);
|
||
|
|
_v3B23.applyMatrix4(this.bone.matrixWorld);
|
||
|
|
}
|
||
|
|
this._worldSpaceBoneLength = _v3A72.sub(_v3B23).length();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Create a matrix that converts center space into world space.
|
||
|
|
*/
|
||
|
|
_getMatrixCenterToWorld() {
|
||
|
|
return this._center ? this._center.matrixWorld : IDENTITY_MATRIX4;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Create a matrix that converts world space into center space.
|
||
|
|
*/
|
||
|
|
_getMatrixWorldToCenter() {
|
||
|
|
return this._center ? this._center.userData.inverseCacheProxy.inverse : IDENTITY_MATRIX4;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
function traverseAncestorsFromRoot2(object, callback) {
|
||
|
|
const ancestors = [];
|
||
|
|
let head = object;
|
||
|
|
while (head !== null) {
|
||
|
|
ancestors.unshift(head);
|
||
|
|
head = head.parent;
|
||
|
|
}
|
||
|
|
ancestors.forEach((ancestor) => {
|
||
|
|
callback(ancestor);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
function traverseChildrenUntilConditionMet(object, callback) {
|
||
|
|
object.children.forEach((child) => {
|
||
|
|
const result = callback(child);
|
||
|
|
if (!result) {
|
||
|
|
traverseChildrenUntilConditionMet(child, callback);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
function lowestCommonAncestor(objects) {
|
||
|
|
var _a;
|
||
|
|
const sharedAncestors = /* @__PURE__ */ new Map();
|
||
|
|
for (const object of objects) {
|
||
|
|
let current = object;
|
||
|
|
do {
|
||
|
|
const newValue = ((_a = sharedAncestors.get(current)) != null ? _a : 0) + 1;
|
||
|
|
if (newValue === objects.size) {
|
||
|
|
return current;
|
||
|
|
}
|
||
|
|
sharedAncestors.set(current, newValue);
|
||
|
|
current = current.parent;
|
||
|
|
} while (current !== null);
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
var VRMSpringBoneManager = class {
|
||
|
|
constructor() {
|
||
|
|
this._joints = /* @__PURE__ */ new Set();
|
||
|
|
this._sortedJoints = [];
|
||
|
|
this._hasWarnedCircularDependency = false;
|
||
|
|
this._ancestors = [];
|
||
|
|
this._objectSpringBonesMap = /* @__PURE__ */ new Map();
|
||
|
|
this._isSortedJointsDirty = false;
|
||
|
|
this._relevantChildrenUpdated = this._relevantChildrenUpdated.bind(this);
|
||
|
|
}
|
||
|
|
get joints() {
|
||
|
|
return this._joints;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Use {@link joints} instead.
|
||
|
|
*/
|
||
|
|
get springBones() {
|
||
|
|
console.warn("VRMSpringBoneManager: springBones is deprecated. use joints instead.");
|
||
|
|
return this._joints;
|
||
|
|
}
|
||
|
|
get colliderGroups() {
|
||
|
|
const set = /* @__PURE__ */ new Set();
|
||
|
|
this._joints.forEach((springBone) => {
|
||
|
|
springBone.colliderGroups.forEach((colliderGroup) => {
|
||
|
|
set.add(colliderGroup);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
return Array.from(set);
|
||
|
|
}
|
||
|
|
get colliders() {
|
||
|
|
const set = /* @__PURE__ */ new Set();
|
||
|
|
this.colliderGroups.forEach((colliderGroup) => {
|
||
|
|
colliderGroup.colliders.forEach((collider) => {
|
||
|
|
set.add(collider);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
return Array.from(set);
|
||
|
|
}
|
||
|
|
addJoint(joint) {
|
||
|
|
this._joints.add(joint);
|
||
|
|
let objectSet = this._objectSpringBonesMap.get(joint.bone);
|
||
|
|
if (objectSet == null) {
|
||
|
|
objectSet = /* @__PURE__ */ new Set();
|
||
|
|
this._objectSpringBonesMap.set(joint.bone, objectSet);
|
||
|
|
}
|
||
|
|
objectSet.add(joint);
|
||
|
|
this._isSortedJointsDirty = true;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Use {@link addJoint} instead.
|
||
|
|
*/
|
||
|
|
addSpringBone(joint) {
|
||
|
|
console.warn("VRMSpringBoneManager: addSpringBone() is deprecated. use addJoint() instead.");
|
||
|
|
this.addJoint(joint);
|
||
|
|
}
|
||
|
|
deleteJoint(joint) {
|
||
|
|
this._joints.delete(joint);
|
||
|
|
const objectSet = this._objectSpringBonesMap.get(joint.bone);
|
||
|
|
objectSet.delete(joint);
|
||
|
|
this._isSortedJointsDirty = true;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* @deprecated Use {@link deleteJoint} instead.
|
||
|
|
*/
|
||
|
|
deleteSpringBone(joint) {
|
||
|
|
console.warn("VRMSpringBoneManager: deleteSpringBone() is deprecated. use deleteJoint() instead.");
|
||
|
|
this.deleteJoint(joint);
|
||
|
|
}
|
||
|
|
setInitState() {
|
||
|
|
this._sortJoints();
|
||
|
|
for (let i = 0; i < this._sortedJoints.length; i++) {
|
||
|
|
const springBone = this._sortedJoints[i];
|
||
|
|
springBone.bone.updateMatrix();
|
||
|
|
springBone.bone.updateWorldMatrix(false, false);
|
||
|
|
springBone.setInitState();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
reset() {
|
||
|
|
this._sortJoints();
|
||
|
|
for (let i = 0; i < this._sortedJoints.length; i++) {
|
||
|
|
const springBone = this._sortedJoints[i];
|
||
|
|
springBone.bone.updateMatrix();
|
||
|
|
springBone.bone.updateWorldMatrix(false, false);
|
||
|
|
springBone.reset();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
update(delta) {
|
||
|
|
this._sortJoints();
|
||
|
|
for (let i = 0; i < this._ancestors.length; i++) {
|
||
|
|
this._ancestors[i].updateWorldMatrix(i === 0, false);
|
||
|
|
}
|
||
|
|
for (let i = 0; i < this._sortedJoints.length; i++) {
|
||
|
|
const springBone = this._sortedJoints[i];
|
||
|
|
springBone.bone.updateMatrix();
|
||
|
|
springBone.bone.updateWorldMatrix(false, false);
|
||
|
|
springBone.update(delta);
|
||
|
|
traverseChildrenUntilConditionMet(springBone.bone, this._relevantChildrenUpdated);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Sorts the joints ensuring they are updated in the correct order taking dependencies into account.
|
||
|
|
*
|
||
|
|
* This method updates {@link _sortedJoints} and {@link _ancestors}.
|
||
|
|
* Make sure to call this before using them.
|
||
|
|
*/
|
||
|
|
_sortJoints() {
|
||
|
|
if (!this._isSortedJointsDirty) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const springBoneOrder = [];
|
||
|
|
const springBonesTried = /* @__PURE__ */ new Set();
|
||
|
|
const springBonesDone = /* @__PURE__ */ new Set();
|
||
|
|
const ancestors = /* @__PURE__ */ new Set();
|
||
|
|
for (const springBone of this._joints) {
|
||
|
|
this._insertJointSort(springBone, springBonesTried, springBonesDone, springBoneOrder, ancestors);
|
||
|
|
}
|
||
|
|
this._sortedJoints = springBoneOrder;
|
||
|
|
const lca = lowestCommonAncestor(ancestors);
|
||
|
|
this._ancestors = [];
|
||
|
|
if (lca) {
|
||
|
|
this._ancestors.push(lca);
|
||
|
|
traverseChildrenUntilConditionMet(lca, (object) => {
|
||
|
|
var _a, _b;
|
||
|
|
if (((_b = (_a = this._objectSpringBonesMap.get(object)) == null ? void 0 : _a.size) != null ? _b : 0) > 0) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
this._ancestors.push(object);
|
||
|
|
return false;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
this._isSortedJointsDirty = false;
|
||
|
|
}
|
||
|
|
_insertJointSort(springBone, springBonesTried, springBonesDone, springBoneOrder, ancestors) {
|
||
|
|
if (springBonesDone.has(springBone)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (springBonesTried.has(springBone)) {
|
||
|
|
if (!this._hasWarnedCircularDependency) {
|
||
|
|
console.warn("VRMSpringBoneManager: Circular dependency detected");
|
||
|
|
this._hasWarnedCircularDependency = true;
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
springBonesTried.add(springBone);
|
||
|
|
const depObjects = springBone.dependencies;
|
||
|
|
for (const depObject of depObjects) {
|
||
|
|
let encounteredSpringBone = false;
|
||
|
|
let ancestor = null;
|
||
|
|
traverseAncestorsFromRoot2(depObject, (depObjectAncestor) => {
|
||
|
|
const objectSet = this._objectSpringBonesMap.get(depObjectAncestor);
|
||
|
|
if (objectSet) {
|
||
|
|
for (const depSpringBone of objectSet) {
|
||
|
|
encounteredSpringBone = true;
|
||
|
|
this._insertJointSort(depSpringBone, springBonesTried, springBonesDone, springBoneOrder, ancestors);
|
||
|
|
}
|
||
|
|
} else if (!encounteredSpringBone) {
|
||
|
|
ancestor = depObjectAncestor;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
if (ancestor) {
|
||
|
|
ancestors.add(ancestor);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
springBoneOrder.push(springBone);
|
||
|
|
springBonesDone.add(springBone);
|
||
|
|
}
|
||
|
|
_relevantChildrenUpdated(object) {
|
||
|
|
var _a, _b;
|
||
|
|
if (((_b = (_a = this._objectSpringBonesMap.get(object)) == null ? void 0 : _a.size) != null ? _b : 0) > 0) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
object.updateWorldMatrix(false, false);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var EXTENSION_NAME_EXTENDED_COLLIDER = "VRMC_springBone_extended_collider";
|
||
|
|
var POSSIBLE_SPEC_VERSIONS8 = /* @__PURE__ */ new Set(["1.0", "1.0-beta"]);
|
||
|
|
var POSSIBLE_SPEC_VERSIONS_EXTENDED_COLLIDERS = /* @__PURE__ */ new Set(["1.0"]);
|
||
|
|
var _VRMSpringBoneLoaderPlugin = class _VRMSpringBoneLoaderPlugin2 {
|
||
|
|
get name() {
|
||
|
|
return _VRMSpringBoneLoaderPlugin2.EXTENSION_NAME;
|
||
|
|
}
|
||
|
|
constructor(parser, options) {
|
||
|
|
var _a;
|
||
|
|
this.parser = parser;
|
||
|
|
this.jointHelperRoot = options == null ? void 0 : options.jointHelperRoot;
|
||
|
|
this.colliderHelperRoot = options == null ? void 0 : options.colliderHelperRoot;
|
||
|
|
this.useExtendedColliders = (_a = options == null ? void 0 : options.useExtendedColliders) != null ? _a : true;
|
||
|
|
}
|
||
|
|
afterRoot(gltf) {
|
||
|
|
return __async7(this, null, function* () {
|
||
|
|
gltf.userData.vrmSpringBoneManager = yield this._import(gltf);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Import spring bones from a GLTF and return a {@link VRMSpringBoneManager}.
|
||
|
|
* It might return `null` instead when it does not need to be created or something go wrong.
|
||
|
|
*
|
||
|
|
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
||
|
|
*/
|
||
|
|
_import(gltf) {
|
||
|
|
return __async7(this, null, function* () {
|
||
|
|
const v1Result = yield this._v1Import(gltf);
|
||
|
|
if (v1Result != null) {
|
||
|
|
return v1Result;
|
||
|
|
}
|
||
|
|
const v0Result = yield this._v0Import(gltf);
|
||
|
|
if (v0Result != null) {
|
||
|
|
return v0Result;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_v1Import(gltf) {
|
||
|
|
return __async7(this, null, function* () {
|
||
|
|
var _a, _b, _c, _d, _e;
|
||
|
|
const json = gltf.parser.json;
|
||
|
|
const isSpringBoneUsed = ((_a = json.extensionsUsed) == null ? void 0 : _a.indexOf(_VRMSpringBoneLoaderPlugin2.EXTENSION_NAME)) !== -1;
|
||
|
|
if (!isSpringBoneUsed) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const manager = new VRMSpringBoneManager();
|
||
|
|
const threeNodes = yield gltf.parser.getDependencies("node");
|
||
|
|
const extension = (_b = json.extensions) == null ? void 0 : _b[_VRMSpringBoneLoaderPlugin2.EXTENSION_NAME];
|
||
|
|
if (!extension) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const specVersion = extension.specVersion;
|
||
|
|
if (!POSSIBLE_SPEC_VERSIONS8.has(specVersion)) {
|
||
|
|
console.warn(
|
||
|
|
`VRMSpringBoneLoaderPlugin: Unknown ${_VRMSpringBoneLoaderPlugin2.EXTENSION_NAME} specVersion "${specVersion}"`
|
||
|
|
);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const colliders = (_c = extension.colliders) == null ? void 0 : _c.map((schemaCollider, iCollider) => {
|
||
|
|
var _a2, _b2, _c2, _d2, _e2, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
||
|
|
const node = threeNodes[schemaCollider.node];
|
||
|
|
if (node == null) {
|
||
|
|
console.warn(
|
||
|
|
`VRMSpringBoneLoaderPlugin: The collider #${iCollider} attempted to use the node #${schemaCollider.node} but not found`
|
||
|
|
);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const schemaShape = schemaCollider.shape;
|
||
|
|
const schemaExCollider = (_a2 = schemaCollider.extensions) == null ? void 0 : _a2[EXTENSION_NAME_EXTENDED_COLLIDER];
|
||
|
|
if (this.useExtendedColliders && schemaExCollider != null) {
|
||
|
|
const specVersionExCollider = schemaExCollider.specVersion;
|
||
|
|
if (!POSSIBLE_SPEC_VERSIONS_EXTENDED_COLLIDERS.has(specVersionExCollider)) {
|
||
|
|
console.warn(
|
||
|
|
`VRMSpringBoneLoaderPlugin: Unknown ${EXTENSION_NAME_EXTENDED_COLLIDER} specVersion "${specVersionExCollider}". Fallbacking to the ${_VRMSpringBoneLoaderPlugin2.EXTENSION_NAME} definition`
|
||
|
|
);
|
||
|
|
} else {
|
||
|
|
const schemaExShape = schemaExCollider.shape;
|
||
|
|
if (schemaExShape.sphere) {
|
||
|
|
return this._importSphereCollider(node, {
|
||
|
|
offset: new THREE142.Vector3().fromArray((_b2 = schemaExShape.sphere.offset) != null ? _b2 : [0, 0, 0]),
|
||
|
|
radius: (_c2 = schemaExShape.sphere.radius) != null ? _c2 : 0,
|
||
|
|
inside: (_d2 = schemaExShape.sphere.inside) != null ? _d2 : false
|
||
|
|
});
|
||
|
|
} else if (schemaExShape.capsule) {
|
||
|
|
return this._importCapsuleCollider(node, {
|
||
|
|
offset: new THREE142.Vector3().fromArray((_e2 = schemaExShape.capsule.offset) != null ? _e2 : [0, 0, 0]),
|
||
|
|
radius: (_f = schemaExShape.capsule.radius) != null ? _f : 0,
|
||
|
|
tail: new THREE142.Vector3().fromArray((_g = schemaExShape.capsule.tail) != null ? _g : [0, 0, 0]),
|
||
|
|
inside: (_h = schemaExShape.capsule.inside) != null ? _h : false
|
||
|
|
});
|
||
|
|
} else if (schemaExShape.plane) {
|
||
|
|
return this._importPlaneCollider(node, {
|
||
|
|
offset: new THREE142.Vector3().fromArray((_i = schemaExShape.plane.offset) != null ? _i : [0, 0, 0]),
|
||
|
|
normal: new THREE142.Vector3().fromArray((_j = schemaExShape.plane.normal) != null ? _j : [0, 0, 1])
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (schemaShape.sphere) {
|
||
|
|
return this._importSphereCollider(node, {
|
||
|
|
offset: new THREE142.Vector3().fromArray((_k = schemaShape.sphere.offset) != null ? _k : [0, 0, 0]),
|
||
|
|
radius: (_l = schemaShape.sphere.radius) != null ? _l : 0,
|
||
|
|
inside: false
|
||
|
|
});
|
||
|
|
} else if (schemaShape.capsule) {
|
||
|
|
return this._importCapsuleCollider(node, {
|
||
|
|
offset: new THREE142.Vector3().fromArray((_m = schemaShape.capsule.offset) != null ? _m : [0, 0, 0]),
|
||
|
|
radius: (_n = schemaShape.capsule.radius) != null ? _n : 0,
|
||
|
|
tail: new THREE142.Vector3().fromArray((_o = schemaShape.capsule.tail) != null ? _o : [0, 0, 0]),
|
||
|
|
inside: false
|
||
|
|
});
|
||
|
|
}
|
||
|
|
throw new Error(`VRMSpringBoneLoaderPlugin: The collider #${iCollider} has no valid shape`);
|
||
|
|
});
|
||
|
|
const colliderGroups = (_d = extension.colliderGroups) == null ? void 0 : _d.map(
|
||
|
|
(schemaColliderGroup, iColliderGroup) => {
|
||
|
|
var _a2;
|
||
|
|
const cols = ((_a2 = schemaColliderGroup.colliders) != null ? _a2 : []).flatMap((iCollider) => {
|
||
|
|
const col = colliders == null ? void 0 : colliders[iCollider];
|
||
|
|
if (col == null) {
|
||
|
|
console.warn(
|
||
|
|
`VRMSpringBoneLoaderPlugin: The colliderGroup #${iColliderGroup} attempted to use a collider #${iCollider} but not found`
|
||
|
|
);
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
return col;
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
colliders: cols,
|
||
|
|
name: schemaColliderGroup.name
|
||
|
|
};
|
||
|
|
}
|
||
|
|
);
|
||
|
|
(_e = extension.springs) == null ? void 0 : _e.forEach((schemaSpring, iSpring) => {
|
||
|
|
var _a2;
|
||
|
|
const schemaJoints = schemaSpring.joints;
|
||
|
|
const colliderGroupsForSpring = (_a2 = schemaSpring.colliderGroups) == null ? void 0 : _a2.map((iColliderGroup) => {
|
||
|
|
const group = colliderGroups == null ? void 0 : colliderGroups[iColliderGroup];
|
||
|
|
if (group == null) {
|
||
|
|
throw new Error(
|
||
|
|
`VRMSpringBoneLoaderPlugin: The spring #${iSpring} attempted to use a colliderGroup ${iColliderGroup} but not found`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return group;
|
||
|
|
});
|
||
|
|
const center = schemaSpring.center != null ? threeNodes[schemaSpring.center] : void 0;
|
||
|
|
let prevSchemaJoint;
|
||
|
|
schemaJoints.forEach((schemaJoint) => {
|
||
|
|
if (prevSchemaJoint) {
|
||
|
|
const nodeIndex = prevSchemaJoint.node;
|
||
|
|
const node = threeNodes[nodeIndex];
|
||
|
|
const childIndex = schemaJoint.node;
|
||
|
|
const child = threeNodes[childIndex];
|
||
|
|
const setting = {
|
||
|
|
hitRadius: prevSchemaJoint.hitRadius,
|
||
|
|
dragForce: prevSchemaJoint.dragForce,
|
||
|
|
gravityPower: prevSchemaJoint.gravityPower,
|
||
|
|
stiffness: prevSchemaJoint.stiffness,
|
||
|
|
gravityDir: prevSchemaJoint.gravityDir != null ? new THREE142.Vector3().fromArray(prevSchemaJoint.gravityDir) : void 0
|
||
|
|
};
|
||
|
|
const joint = this._importJoint(node, child, setting, colliderGroupsForSpring);
|
||
|
|
if (center) {
|
||
|
|
joint.center = center;
|
||
|
|
}
|
||
|
|
manager.addJoint(joint);
|
||
|
|
}
|
||
|
|
prevSchemaJoint = schemaJoint;
|
||
|
|
});
|
||
|
|
});
|
||
|
|
manager.setInitState();
|
||
|
|
return manager;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_v0Import(gltf) {
|
||
|
|
return __async7(this, null, function* () {
|
||
|
|
var _a, _b, _c;
|
||
|
|
const json = gltf.parser.json;
|
||
|
|
const isVRMUsed = ((_a = json.extensionsUsed) == null ? void 0 : _a.indexOf("VRM")) !== -1;
|
||
|
|
if (!isVRMUsed) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const extension = (_b = json.extensions) == null ? void 0 : _b["VRM"];
|
||
|
|
const schemaSecondaryAnimation = extension == null ? void 0 : extension.secondaryAnimation;
|
||
|
|
if (!schemaSecondaryAnimation) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const schemaBoneGroups = schemaSecondaryAnimation == null ? void 0 : schemaSecondaryAnimation.boneGroups;
|
||
|
|
if (!schemaBoneGroups) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const manager = new VRMSpringBoneManager();
|
||
|
|
const threeNodes = yield gltf.parser.getDependencies("node");
|
||
|
|
const colliderGroups = (_c = schemaSecondaryAnimation.colliderGroups) == null ? void 0 : _c.map(
|
||
|
|
(schemaColliderGroup) => {
|
||
|
|
var _a2;
|
||
|
|
const node = threeNodes[schemaColliderGroup.node];
|
||
|
|
const colliders = ((_a2 = schemaColliderGroup.colliders) != null ? _a2 : []).map((schemaCollider, iCollider) => {
|
||
|
|
var _a3, _b2, _c2;
|
||
|
|
const offset = new THREE142.Vector3(0, 0, 0);
|
||
|
|
if (schemaCollider.offset) {
|
||
|
|
offset.set(
|
||
|
|
(_a3 = schemaCollider.offset.x) != null ? _a3 : 0,
|
||
|
|
(_b2 = schemaCollider.offset.y) != null ? _b2 : 0,
|
||
|
|
schemaCollider.offset.z ? -schemaCollider.offset.z : 0
|
||
|
|
// z is opposite in VRM0.0
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return this._importSphereCollider(node, {
|
||
|
|
offset,
|
||
|
|
radius: (_c2 = schemaCollider.radius) != null ? _c2 : 0,
|
||
|
|
inside: false
|
||
|
|
});
|
||
|
|
});
|
||
|
|
return { colliders };
|
||
|
|
}
|
||
|
|
);
|
||
|
|
schemaBoneGroups == null ? void 0 : schemaBoneGroups.forEach((schemaBoneGroup, iBoneGroup) => {
|
||
|
|
const rootIndices = schemaBoneGroup.bones;
|
||
|
|
if (!rootIndices) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
rootIndices.forEach((rootIndex) => {
|
||
|
|
var _a2, _b2, _c2, _d;
|
||
|
|
const root = threeNodes[rootIndex];
|
||
|
|
const gravityDir = new THREE142.Vector3();
|
||
|
|
if (schemaBoneGroup.gravityDir) {
|
||
|
|
gravityDir.set(
|
||
|
|
(_a2 = schemaBoneGroup.gravityDir.x) != null ? _a2 : 0,
|
||
|
|
(_b2 = schemaBoneGroup.gravityDir.y) != null ? _b2 : 0,
|
||
|
|
(_c2 = schemaBoneGroup.gravityDir.z) != null ? _c2 : 0
|
||
|
|
);
|
||
|
|
} else {
|
||
|
|
gravityDir.set(0, -1, 0);
|
||
|
|
}
|
||
|
|
const center = schemaBoneGroup.center != null ? threeNodes[schemaBoneGroup.center] : void 0;
|
||
|
|
const setting = {
|
||
|
|
hitRadius: schemaBoneGroup.hitRadius,
|
||
|
|
dragForce: schemaBoneGroup.dragForce,
|
||
|
|
gravityPower: schemaBoneGroup.gravityPower,
|
||
|
|
stiffness: schemaBoneGroup.stiffiness,
|
||
|
|
gravityDir
|
||
|
|
};
|
||
|
|
const colliderGroupsForSpring = (_d = schemaBoneGroup.colliderGroups) == null ? void 0 : _d.map((iColliderGroup) => {
|
||
|
|
const group = colliderGroups == null ? void 0 : colliderGroups[iColliderGroup];
|
||
|
|
if (group == null) {
|
||
|
|
throw new Error(
|
||
|
|
`VRMSpringBoneLoaderPlugin: The spring #${iBoneGroup} attempted to use a colliderGroup ${iColliderGroup} but not found`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return group;
|
||
|
|
});
|
||
|
|
root.traverse((node) => {
|
||
|
|
var _a3;
|
||
|
|
const child = (_a3 = node.children[0]) != null ? _a3 : null;
|
||
|
|
const joint = this._importJoint(node, child, setting, colliderGroupsForSpring);
|
||
|
|
if (center) {
|
||
|
|
joint.center = center;
|
||
|
|
}
|
||
|
|
manager.addJoint(joint);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
gltf.scene.updateMatrixWorld();
|
||
|
|
manager.setInitState();
|
||
|
|
return manager;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
_importJoint(node, child, setting, colliderGroupsForSpring) {
|
||
|
|
const springBone = new VRMSpringBoneJoint(node, child, setting, colliderGroupsForSpring);
|
||
|
|
if (this.jointHelperRoot) {
|
||
|
|
const helper = new VRMSpringBoneJointHelper(springBone);
|
||
|
|
this.jointHelperRoot.add(helper);
|
||
|
|
helper.renderOrder = this.jointHelperRoot.renderOrder;
|
||
|
|
}
|
||
|
|
return springBone;
|
||
|
|
}
|
||
|
|
_importSphereCollider(destination, params) {
|
||
|
|
const shape = new VRMSpringBoneColliderShapeSphere(params);
|
||
|
|
const collider = new VRMSpringBoneCollider(shape);
|
||
|
|
destination.add(collider);
|
||
|
|
if (this.colliderHelperRoot) {
|
||
|
|
const helper = new VRMSpringBoneColliderHelper(collider);
|
||
|
|
this.colliderHelperRoot.add(helper);
|
||
|
|
helper.renderOrder = this.colliderHelperRoot.renderOrder;
|
||
|
|
}
|
||
|
|
return collider;
|
||
|
|
}
|
||
|
|
_importCapsuleCollider(destination, params) {
|
||
|
|
const shape = new VRMSpringBoneColliderShapeCapsule(params);
|
||
|
|
const collider = new VRMSpringBoneCollider(shape);
|
||
|
|
destination.add(collider);
|
||
|
|
if (this.colliderHelperRoot) {
|
||
|
|
const helper = new VRMSpringBoneColliderHelper(collider);
|
||
|
|
this.colliderHelperRoot.add(helper);
|
||
|
|
helper.renderOrder = this.colliderHelperRoot.renderOrder;
|
||
|
|
}
|
||
|
|
return collider;
|
||
|
|
}
|
||
|
|
_importPlaneCollider(destination, params) {
|
||
|
|
const shape = new VRMSpringBoneColliderShapePlane(params);
|
||
|
|
const collider = new VRMSpringBoneCollider(shape);
|
||
|
|
destination.add(collider);
|
||
|
|
if (this.colliderHelperRoot) {
|
||
|
|
const helper = new VRMSpringBoneColliderHelper(collider);
|
||
|
|
this.colliderHelperRoot.add(helper);
|
||
|
|
helper.renderOrder = this.colliderHelperRoot.renderOrder;
|
||
|
|
}
|
||
|
|
return collider;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
_VRMSpringBoneLoaderPlugin.EXTENSION_NAME = "VRMC_springBone";
|
||
|
|
var VRMSpringBoneLoaderPlugin = _VRMSpringBoneLoaderPlugin;
|
||
|
|
|
||
|
|
// src/VRMLoaderPlugin.ts
|
||
|
|
var VRMLoaderPlugin = class {
|
||
|
|
get name() {
|
||
|
|
return "VRMLoaderPlugin";
|
||
|
|
}
|
||
|
|
constructor(parser, options) {
|
||
|
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
||
|
|
this.parser = parser;
|
||
|
|
const helperRoot = options == null ? void 0 : options.helperRoot;
|
||
|
|
const autoUpdateHumanBones = options == null ? void 0 : options.autoUpdateHumanBones;
|
||
|
|
this.expressionPlugin = (_a = options == null ? void 0 : options.expressionPlugin) != null ? _a : new VRMExpressionLoaderPlugin(parser);
|
||
|
|
this.firstPersonPlugin = (_b = options == null ? void 0 : options.firstPersonPlugin) != null ? _b : new VRMFirstPersonLoaderPlugin(parser);
|
||
|
|
this.humanoidPlugin = (_c = options == null ? void 0 : options.humanoidPlugin) != null ? _c : new VRMHumanoidLoaderPlugin(parser, {
|
||
|
|
helperRoot,
|
||
|
|
autoUpdateHumanBones
|
||
|
|
});
|
||
|
|
this.lookAtPlugin = (_d = options == null ? void 0 : options.lookAtPlugin) != null ? _d : new VRMLookAtLoaderPlugin(parser, { helperRoot });
|
||
|
|
this.metaPlugin = (_e = options == null ? void 0 : options.metaPlugin) != null ? _e : new VRMMetaLoaderPlugin(parser);
|
||
|
|
this.mtoonMaterialPlugin = (_f = options == null ? void 0 : options.mtoonMaterialPlugin) != null ? _f : new MToonMaterialLoaderPlugin(parser);
|
||
|
|
this.materialsHDREmissiveMultiplierPlugin = (_g = options == null ? void 0 : options.materialsHDREmissiveMultiplierPlugin) != null ? _g : new VRMMaterialsHDREmissiveMultiplierLoaderPlugin(parser);
|
||
|
|
this.materialsV0CompatPlugin = (_h = options == null ? void 0 : options.materialsV0CompatPlugin) != null ? _h : new VRMMaterialsV0CompatPlugin(parser);
|
||
|
|
this.springBonePlugin = (_i = options == null ? void 0 : options.springBonePlugin) != null ? _i : new VRMSpringBoneLoaderPlugin(parser, {
|
||
|
|
colliderHelperRoot: helperRoot,
|
||
|
|
jointHelperRoot: helperRoot
|
||
|
|
});
|
||
|
|
this.nodeConstraintPlugin = (_j = options == null ? void 0 : options.nodeConstraintPlugin) != null ? _j : new VRMNodeConstraintLoaderPlugin(parser, { helperRoot });
|
||
|
|
}
|
||
|
|
beforeRoot() {
|
||
|
|
return __async(this, null, function* () {
|
||
|
|
yield this.materialsV0CompatPlugin.beforeRoot();
|
||
|
|
yield this.mtoonMaterialPlugin.beforeRoot();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
loadMesh(meshIndex) {
|
||
|
|
return __async(this, null, function* () {
|
||
|
|
return yield this.mtoonMaterialPlugin.loadMesh(meshIndex);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
getMaterialType(materialIndex) {
|
||
|
|
const mtoonType = this.mtoonMaterialPlugin.getMaterialType(materialIndex);
|
||
|
|
if (mtoonType != null) {
|
||
|
|
return mtoonType;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
extendMaterialParams(materialIndex, materialParams) {
|
||
|
|
return __async(this, null, function* () {
|
||
|
|
yield this.materialsHDREmissiveMultiplierPlugin.extendMaterialParams(materialIndex, materialParams);
|
||
|
|
yield this.mtoonMaterialPlugin.extendMaterialParams(materialIndex, materialParams);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
afterRoot(gltf) {
|
||
|
|
return __async(this, null, function* () {
|
||
|
|
yield this.metaPlugin.afterRoot(gltf);
|
||
|
|
yield this.humanoidPlugin.afterRoot(gltf);
|
||
|
|
yield this.expressionPlugin.afterRoot(gltf);
|
||
|
|
yield this.lookAtPlugin.afterRoot(gltf);
|
||
|
|
yield this.firstPersonPlugin.afterRoot(gltf);
|
||
|
|
yield this.springBonePlugin.afterRoot(gltf);
|
||
|
|
yield this.nodeConstraintPlugin.afterRoot(gltf);
|
||
|
|
yield this.mtoonMaterialPlugin.afterRoot(gltf);
|
||
|
|
const meta = gltf.userData.vrmMeta;
|
||
|
|
const humanoid = gltf.userData.vrmHumanoid;
|
||
|
|
if (meta && humanoid) {
|
||
|
|
const vrm = new VRM({
|
||
|
|
scene: gltf.scene,
|
||
|
|
expressionManager: gltf.userData.vrmExpressionManager,
|
||
|
|
firstPerson: gltf.userData.vrmFirstPerson,
|
||
|
|
humanoid,
|
||
|
|
lookAt: gltf.userData.vrmLookAt,
|
||
|
|
meta,
|
||
|
|
materials: gltf.userData.vrmMToonMaterials,
|
||
|
|
springBoneManager: gltf.userData.vrmSpringBoneManager,
|
||
|
|
nodeConstraintManager: gltf.userData.vrmNodeConstraintManager
|
||
|
|
});
|
||
|
|
gltf.userData.vrm = vrm;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// src/VRMUtils/combineMorphs.ts
|
||
|
|
var THREE21 = __toESM(require("three"), 1);
|
||
|
|
function collectMeshes(scene) {
|
||
|
|
const meshes = /* @__PURE__ */ new Set();
|
||
|
|
scene.traverse((obj) => {
|
||
|
|
if (!obj.isMesh) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const mesh = obj;
|
||
|
|
meshes.add(mesh);
|
||
|
|
});
|
||
|
|
return meshes;
|
||
|
|
}
|
||
|
|
function combineMorph(positionAttributes, binds, morphTargetsRelative) {
|
||
|
|
if (binds.size === 1) {
|
||
|
|
const bind = binds.values().next().value;
|
||
|
|
if (bind.weight === 1) {
|
||
|
|
return positionAttributes[bind.index];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const newArray = new Float32Array(positionAttributes[0].count * 3);
|
||
|
|
let weightSum = 0;
|
||
|
|
if (morphTargetsRelative) {
|
||
|
|
weightSum = 1;
|
||
|
|
} else {
|
||
|
|
for (const bind of binds) {
|
||
|
|
weightSum += bind.weight;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (const bind of binds) {
|
||
|
|
const src = positionAttributes[bind.index];
|
||
|
|
const weight = bind.weight / weightSum;
|
||
|
|
for (let i = 0; i < src.count; i++) {
|
||
|
|
newArray[i * 3 + 0] += src.getX(i) * weight;
|
||
|
|
newArray[i * 3 + 1] += src.getY(i) * weight;
|
||
|
|
newArray[i * 3 + 2] += src.getZ(i) * weight;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const newAttribute = new THREE21.BufferAttribute(newArray, 3);
|
||
|
|
return newAttribute;
|
||
|
|
}
|
||
|
|
function combineMorphs(vrm) {
|
||
|
|
var _a;
|
||
|
|
const meshes = collectMeshes(vrm.scene);
|
||
|
|
const meshNameBindSetMapMap = /* @__PURE__ */ new Map();
|
||
|
|
const expressionMap = (_a = vrm.expressionManager) == null ? void 0 : _a.expressionMap;
|
||
|
|
if (expressionMap != null) {
|
||
|
|
for (const [expressionName, expression] of Object.entries(expressionMap)) {
|
||
|
|
const bindsToDeleteSet = /* @__PURE__ */ new Set();
|
||
|
|
for (const bind of expression.binds) {
|
||
|
|
if (bind instanceof VRMExpressionMorphTargetBind) {
|
||
|
|
if (bind.weight !== 0) {
|
||
|
|
for (const mesh of bind.primitives) {
|
||
|
|
let nameBindSetMap = meshNameBindSetMapMap.get(mesh);
|
||
|
|
if (nameBindSetMap == null) {
|
||
|
|
nameBindSetMap = /* @__PURE__ */ new Map();
|
||
|
|
meshNameBindSetMapMap.set(mesh, nameBindSetMap);
|
||
|
|
}
|
||
|
|
let bindSet = nameBindSetMap.get(expressionName);
|
||
|
|
if (bindSet == null) {
|
||
|
|
bindSet = /* @__PURE__ */ new Set();
|
||
|
|
nameBindSetMap.set(expressionName, bindSet);
|
||
|
|
}
|
||
|
|
bindSet.add(bind);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
bindsToDeleteSet.add(bind);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (const bind of bindsToDeleteSet) {
|
||
|
|
expression.deleteBind(bind);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (const mesh of meshes) {
|
||
|
|
const nameBindSetMap = meshNameBindSetMapMap.get(mesh);
|
||
|
|
if (nameBindSetMap == null) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
const originalMorphAttributes = mesh.geometry.morphAttributes;
|
||
|
|
mesh.geometry.morphAttributes = {};
|
||
|
|
const geometry = mesh.geometry.clone();
|
||
|
|
mesh.geometry = geometry;
|
||
|
|
const morphTargetsRelative = geometry.morphTargetsRelative;
|
||
|
|
const hasPMorph = originalMorphAttributes.position != null;
|
||
|
|
const hasNMorph = originalMorphAttributes.normal != null;
|
||
|
|
const morphAttributes = {};
|
||
|
|
const morphTargetDictionary = {};
|
||
|
|
const morphTargetInfluences = [];
|
||
|
|
if (hasPMorph || hasNMorph) {
|
||
|
|
if (hasPMorph) {
|
||
|
|
morphAttributes.position = [];
|
||
|
|
}
|
||
|
|
if (hasNMorph) {
|
||
|
|
morphAttributes.normal = [];
|
||
|
|
}
|
||
|
|
let i = 0;
|
||
|
|
for (const [name, bindSet] of nameBindSetMap) {
|
||
|
|
if (hasPMorph) {
|
||
|
|
morphAttributes.position[i] = combineMorph(originalMorphAttributes.position, bindSet, morphTargetsRelative);
|
||
|
|
}
|
||
|
|
if (hasNMorph) {
|
||
|
|
morphAttributes.normal[i] = combineMorph(originalMorphAttributes.normal, bindSet, morphTargetsRelative);
|
||
|
|
}
|
||
|
|
expressionMap == null ? void 0 : expressionMap[name].addBind(
|
||
|
|
new VRMExpressionMorphTargetBind({
|
||
|
|
index: i,
|
||
|
|
weight: 1,
|
||
|
|
primitives: [mesh]
|
||
|
|
})
|
||
|
|
);
|
||
|
|
morphTargetDictionary[name] = i;
|
||
|
|
morphTargetInfluences.push(0);
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
geometry.morphAttributes = morphAttributes;
|
||
|
|
mesh.morphTargetDictionary = morphTargetDictionary;
|
||
|
|
mesh.morphTargetInfluences = morphTargetInfluences;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/VRMUtils/combineSkeletons.ts
|
||
|
|
var THREE27 = __toESM(require("three"), 1);
|
||
|
|
|
||
|
|
// src/utils/attributeGetComponentCompat.ts
|
||
|
|
var THREE25 = __toESM(require("three"), 1);
|
||
|
|
function attributeGetComponentCompat(attribute, index, component) {
|
||
|
|
if (attribute.getComponent) {
|
||
|
|
return attribute.getComponent(index, component);
|
||
|
|
} else {
|
||
|
|
let value = attribute.array[index * attribute.itemSize + component];
|
||
|
|
if (attribute.normalized) {
|
||
|
|
value = THREE25.MathUtils.denormalize(value, attribute.array);
|
||
|
|
}
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/utils/attributeSetComponentCompat.ts
|
||
|
|
var THREE26 = __toESM(require("three"), 1);
|
||
|
|
function attributeSetComponentCompat(attribute, index, component, value) {
|
||
|
|
if (attribute.setComponent) {
|
||
|
|
attribute.setComponent(index, component, value);
|
||
|
|
} else {
|
||
|
|
if (attribute.normalized) {
|
||
|
|
value = THREE26.MathUtils.normalize(value, attribute.array);
|
||
|
|
}
|
||
|
|
attribute.array[index * attribute.itemSize + component] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/VRMUtils/combineSkeletons.ts
|
||
|
|
function combineSkeletons(root) {
|
||
|
|
var _a;
|
||
|
|
const skinnedMeshes = collectSkinnedMeshes(root);
|
||
|
|
const geometries = /* @__PURE__ */ new Set();
|
||
|
|
for (const mesh of skinnedMeshes) {
|
||
|
|
if (geometries.has(mesh.geometry)) {
|
||
|
|
mesh.geometry = shallowCloneBufferGeometry(mesh.geometry);
|
||
|
|
}
|
||
|
|
geometries.add(mesh.geometry);
|
||
|
|
}
|
||
|
|
const attributeUsedIndexSetMap = /* @__PURE__ */ new Map();
|
||
|
|
for (const geometry of geometries) {
|
||
|
|
const skinIndexAttr = geometry.getAttribute("skinIndex");
|
||
|
|
const skinIndexMap = (_a = attributeUsedIndexSetMap.get(skinIndexAttr)) != null ? _a : /* @__PURE__ */ new Map();
|
||
|
|
attributeUsedIndexSetMap.set(skinIndexAttr, skinIndexMap);
|
||
|
|
const skinWeightAttr = geometry.getAttribute("skinWeight");
|
||
|
|
const usedIndicesSet = listUsedIndices(skinIndexAttr, skinWeightAttr);
|
||
|
|
skinIndexMap.set(skinWeightAttr, usedIndicesSet);
|
||
|
|
}
|
||
|
|
const meshBoneInverseMapMap = /* @__PURE__ */ new Map();
|
||
|
|
for (const mesh of skinnedMeshes) {
|
||
|
|
const boneInverseMap = listUsedBones(mesh, attributeUsedIndexSetMap);
|
||
|
|
meshBoneInverseMapMap.set(mesh, boneInverseMap);
|
||
|
|
}
|
||
|
|
const groups = [];
|
||
|
|
for (const [mesh, boneInverseMap] of meshBoneInverseMapMap) {
|
||
|
|
let foundMergeableGroup = false;
|
||
|
|
for (const candidate of groups) {
|
||
|
|
const isMergeable = boneInverseMapIsMergeable(boneInverseMap, candidate.boneInverseMap);
|
||
|
|
if (isMergeable) {
|
||
|
|
foundMergeableGroup = true;
|
||
|
|
candidate.meshes.add(mesh);
|
||
|
|
for (const [bone, boneInverse] of boneInverseMap) {
|
||
|
|
candidate.boneInverseMap.set(bone, boneInverse);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!foundMergeableGroup) {
|
||
|
|
groups.push({ boneInverseMap, meshes: /* @__PURE__ */ new Set([mesh]) });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const cache = /* @__PURE__ */ new Map();
|
||
|
|
const skinIndexDispatcher = new ObjectIndexDispatcher();
|
||
|
|
const skeletonDispatcher = new ObjectIndexDispatcher();
|
||
|
|
const boneDispatcher = new ObjectIndexDispatcher();
|
||
|
|
for (const group of groups) {
|
||
|
|
const { boneInverseMap, meshes } = group;
|
||
|
|
const newBones = Array.from(boneInverseMap.keys());
|
||
|
|
const newBoneInverses = Array.from(boneInverseMap.values());
|
||
|
|
const newSkeleton = new THREE27.Skeleton(newBones, newBoneInverses);
|
||
|
|
const skeletonKey = skeletonDispatcher.getOrCreate(newSkeleton);
|
||
|
|
for (const mesh of meshes) {
|
||
|
|
const skinIndexAttr = mesh.geometry.getAttribute("skinIndex");
|
||
|
|
const skinIndexKey = skinIndexDispatcher.getOrCreate(skinIndexAttr);
|
||
|
|
const bones = mesh.skeleton.bones;
|
||
|
|
const bonesKey = bones.map((bone) => boneDispatcher.getOrCreate(bone)).join(",");
|
||
|
|
const key = `${skinIndexKey};${skeletonKey};${bonesKey}`;
|
||
|
|
let newSkinIndexAttr = cache.get(key);
|
||
|
|
if (newSkinIndexAttr == null) {
|
||
|
|
newSkinIndexAttr = skinIndexAttr.clone();
|
||
|
|
remapSkinIndexAttribute(newSkinIndexAttr, bones, newBones);
|
||
|
|
cache.set(key, newSkinIndexAttr);
|
||
|
|
}
|
||
|
|
mesh.geometry.setAttribute("skinIndex", newSkinIndexAttr);
|
||
|
|
}
|
||
|
|
for (const mesh of meshes) {
|
||
|
|
mesh.bind(newSkeleton, new THREE27.Matrix4());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function collectSkinnedMeshes(scene) {
|
||
|
|
const skinnedMeshes = /* @__PURE__ */ new Set();
|
||
|
|
scene.traverse((obj) => {
|
||
|
|
if (!obj.isSkinnedMesh) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const skinnedMesh = obj;
|
||
|
|
skinnedMeshes.add(skinnedMesh);
|
||
|
|
});
|
||
|
|
return skinnedMeshes;
|
||
|
|
}
|
||
|
|
function listUsedIndices(skinIndexAttr, skinWeightAttr) {
|
||
|
|
const usedIndices = /* @__PURE__ */ new Set();
|
||
|
|
for (let i = 0; i < skinIndexAttr.count; i++) {
|
||
|
|
for (let j = 0; j < skinIndexAttr.itemSize; j++) {
|
||
|
|
const index = attributeGetComponentCompat(skinIndexAttr, i, j);
|
||
|
|
const weight = attributeGetComponentCompat(skinWeightAttr, i, j);
|
||
|
|
if (weight !== 0) {
|
||
|
|
usedIndices.add(index);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return usedIndices;
|
||
|
|
}
|
||
|
|
function listUsedBones(mesh, attributeUsedIndexSetMap) {
|
||
|
|
const boneInverseMap = /* @__PURE__ */ new Map();
|
||
|
|
const skeleton = mesh.skeleton;
|
||
|
|
const geometry = mesh.geometry;
|
||
|
|
const skinIndexAttr = geometry.getAttribute("skinIndex");
|
||
|
|
const skinWeightAttr = geometry.getAttribute("skinWeight");
|
||
|
|
const skinIndexMap = attributeUsedIndexSetMap.get(skinIndexAttr);
|
||
|
|
const usedIndicesSet = skinIndexMap == null ? void 0 : skinIndexMap.get(skinWeightAttr);
|
||
|
|
if (!usedIndicesSet) {
|
||
|
|
throw new Error(
|
||
|
|
"Unreachable. attributeUsedIndexSetMap does not know the skin index attribute or the skin weight attribute."
|
||
|
|
);
|
||
|
|
}
|
||
|
|
for (const index of usedIndicesSet) {
|
||
|
|
boneInverseMap.set(skeleton.bones[index], skeleton.boneInverses[index]);
|
||
|
|
}
|
||
|
|
return boneInverseMap;
|
||
|
|
}
|
||
|
|
function boneInverseMapIsMergeable(toCheck, candidate) {
|
||
|
|
for (const [bone, boneInverse] of toCheck.entries()) {
|
||
|
|
const candidateBoneInverse = candidate.get(bone);
|
||
|
|
if (candidateBoneInverse != null) {
|
||
|
|
if (!matrixEquals(boneInverse, candidateBoneInverse)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
function remapSkinIndexAttribute(attribute, oldBones, newBones) {
|
||
|
|
const boneOldIndexMap = /* @__PURE__ */ new Map();
|
||
|
|
for (const bone of oldBones) {
|
||
|
|
boneOldIndexMap.set(bone, boneOldIndexMap.size);
|
||
|
|
}
|
||
|
|
const oldToNew = /* @__PURE__ */ new Map();
|
||
|
|
for (const [i, bone] of newBones.entries()) {
|
||
|
|
const oldIndex = boneOldIndexMap.get(bone);
|
||
|
|
oldToNew.set(oldIndex, i);
|
||
|
|
}
|
||
|
|
for (let i = 0; i < attribute.count; i++) {
|
||
|
|
for (let j = 0; j < attribute.itemSize; j++) {
|
||
|
|
const oldIndex = attributeGetComponentCompat(attribute, i, j);
|
||
|
|
const newIndex = oldToNew.get(oldIndex);
|
||
|
|
attributeSetComponentCompat(attribute, i, j, newIndex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
attribute.needsUpdate = true;
|
||
|
|
}
|
||
|
|
function matrixEquals(a, b, tolerance) {
|
||
|
|
tolerance = tolerance || 1e-4;
|
||
|
|
if (a.elements.length != b.elements.length) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
for (let i = 0, il = a.elements.length; i < il; i++) {
|
||
|
|
const delta = Math.abs(a.elements[i] - b.elements[i]);
|
||
|
|
if (delta > tolerance) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
var ObjectIndexDispatcher = class {
|
||
|
|
constructor() {
|
||
|
|
this._objectIndexMap = /* @__PURE__ */ new Map();
|
||
|
|
this._index = 0;
|
||
|
|
}
|
||
|
|
get(obj) {
|
||
|
|
return this._objectIndexMap.get(obj);
|
||
|
|
}
|
||
|
|
getOrCreate(obj) {
|
||
|
|
let index = this._objectIndexMap.get(obj);
|
||
|
|
if (index == null) {
|
||
|
|
index = this._index;
|
||
|
|
this._objectIndexMap.set(obj, index);
|
||
|
|
this._index++;
|
||
|
|
}
|
||
|
|
return index;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
function shallowCloneBufferGeometry(geometry) {
|
||
|
|
var _a, _b, _c, _d;
|
||
|
|
const clone = new THREE27.BufferGeometry();
|
||
|
|
clone.name = geometry.name;
|
||
|
|
clone.setIndex(geometry.index);
|
||
|
|
for (const [name, attribute] of Object.entries(geometry.attributes)) {
|
||
|
|
clone.setAttribute(name, attribute);
|
||
|
|
}
|
||
|
|
for (const [key, morphAttributes] of Object.entries(geometry.morphAttributes)) {
|
||
|
|
const attributeName = key;
|
||
|
|
clone.morphAttributes[attributeName] = morphAttributes.concat();
|
||
|
|
}
|
||
|
|
clone.morphTargetsRelative = geometry.morphTargetsRelative;
|
||
|
|
clone.groups = [];
|
||
|
|
for (const group of geometry.groups) {
|
||
|
|
clone.addGroup(group.start, group.count, group.materialIndex);
|
||
|
|
}
|
||
|
|
clone.boundingSphere = (_b = (_a = geometry.boundingSphere) == null ? void 0 : _a.clone()) != null ? _b : null;
|
||
|
|
clone.boundingBox = (_d = (_c = geometry.boundingBox) == null ? void 0 : _c.clone()) != null ? _d : null;
|
||
|
|
clone.drawRange.start = geometry.drawRange.start;
|
||
|
|
clone.drawRange.count = geometry.drawRange.count;
|
||
|
|
clone.userData = geometry.userData;
|
||
|
|
return clone;
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/VRMUtils/deepDispose.ts
|
||
|
|
function disposeMaterial(material) {
|
||
|
|
Object.values(material).forEach((value) => {
|
||
|
|
if (value == null ? void 0 : value.isTexture) {
|
||
|
|
const texture = value;
|
||
|
|
texture.dispose();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
if (material.isShaderMaterial) {
|
||
|
|
const uniforms = material.uniforms;
|
||
|
|
if (uniforms) {
|
||
|
|
Object.values(uniforms).forEach((uniform) => {
|
||
|
|
const value = uniform.value;
|
||
|
|
if (value == null ? void 0 : value.isTexture) {
|
||
|
|
const texture = value;
|
||
|
|
texture.dispose();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
material.dispose();
|
||
|
|
}
|
||
|
|
function dispose(object3D) {
|
||
|
|
const geometry = object3D.geometry;
|
||
|
|
if (geometry) {
|
||
|
|
geometry.dispose();
|
||
|
|
}
|
||
|
|
const skeleton = object3D.skeleton;
|
||
|
|
if (skeleton) {
|
||
|
|
skeleton.dispose();
|
||
|
|
}
|
||
|
|
const material = object3D.material;
|
||
|
|
if (material) {
|
||
|
|
if (Array.isArray(material)) {
|
||
|
|
material.forEach((material2) => disposeMaterial(material2));
|
||
|
|
} else if (material) {
|
||
|
|
disposeMaterial(material);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function deepDispose(object3D) {
|
||
|
|
object3D.traverse(dispose);
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/VRMUtils/removeUnnecessaryJoints.ts
|
||
|
|
var THREE28 = __toESM(require("three"), 1);
|
||
|
|
function removeUnnecessaryJoints(root, options) {
|
||
|
|
var _a, _b;
|
||
|
|
console.warn(
|
||
|
|
"VRMUtils.removeUnnecessaryJoints: removeUnnecessaryJoints is deprecated. Use combineSkeletons instead. combineSkeletons contributes more to the performance improvement. This function will be removed in the next major version."
|
||
|
|
);
|
||
|
|
const experimentalSameBoneCounts = (_a = options == null ? void 0 : options.experimentalSameBoneCounts) != null ? _a : false;
|
||
|
|
const skinnedMeshes = [];
|
||
|
|
root.traverse((obj) => {
|
||
|
|
if (obj.type !== "SkinnedMesh") {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
skinnedMeshes.push(obj);
|
||
|
|
});
|
||
|
|
const attributeToBoneIndexMapMap = /* @__PURE__ */ new Map();
|
||
|
|
let maxBones = 0;
|
||
|
|
for (const mesh of skinnedMeshes) {
|
||
|
|
const geometry = mesh.geometry;
|
||
|
|
const attribute = geometry.getAttribute("skinIndex");
|
||
|
|
if (attributeToBoneIndexMapMap.has(attribute)) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
const oldToNew = /* @__PURE__ */ new Map();
|
||
|
|
const newToOld = /* @__PURE__ */ new Map();
|
||
|
|
for (let i = 0; i < attribute.count; i++) {
|
||
|
|
for (let j = 0; j < attribute.itemSize; j++) {
|
||
|
|
const oldIndex = attributeGetComponentCompat(attribute, i, j);
|
||
|
|
let newIndex = oldToNew.get(oldIndex);
|
||
|
|
if (newIndex == null) {
|
||
|
|
newIndex = oldToNew.size;
|
||
|
|
oldToNew.set(oldIndex, newIndex);
|
||
|
|
newToOld.set(newIndex, oldIndex);
|
||
|
|
}
|
||
|
|
attributeSetComponentCompat(attribute, i, j, newIndex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
attribute.needsUpdate = true;
|
||
|
|
attributeToBoneIndexMapMap.set(attribute, newToOld);
|
||
|
|
maxBones = Math.max(maxBones, oldToNew.size);
|
||
|
|
}
|
||
|
|
for (const mesh of skinnedMeshes) {
|
||
|
|
const geometry = mesh.geometry;
|
||
|
|
const attribute = geometry.getAttribute("skinIndex");
|
||
|
|
const newToOld = attributeToBoneIndexMapMap.get(attribute);
|
||
|
|
const bones = [];
|
||
|
|
const boneInverses = [];
|
||
|
|
const nBones = experimentalSameBoneCounts ? maxBones : newToOld.size;
|
||
|
|
for (let newIndex = 0; newIndex < nBones; newIndex++) {
|
||
|
|
const oldIndex = (_b = newToOld.get(newIndex)) != null ? _b : 0;
|
||
|
|
bones.push(mesh.skeleton.bones[oldIndex]);
|
||
|
|
boneInverses.push(mesh.skeleton.boneInverses[oldIndex]);
|
||
|
|
}
|
||
|
|
const skeleton = new THREE28.Skeleton(bones, boneInverses);
|
||
|
|
mesh.bind(skeleton, new THREE28.Matrix4());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/VRMUtils/removeUnnecessaryVertices.ts
|
||
|
|
var THREE29 = __toESM(require("three"), 1);
|
||
|
|
var import_three = require("three");
|
||
|
|
function removeUnnecessaryVertices(root) {
|
||
|
|
const geometryMap = /* @__PURE__ */ new Map();
|
||
|
|
root.traverse((obj) => {
|
||
|
|
var _a, _b, _c, _d;
|
||
|
|
if (!obj.isMesh) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const mesh = obj;
|
||
|
|
const geometry = mesh.geometry;
|
||
|
|
const originalIndex = geometry.index;
|
||
|
|
if (originalIndex == null) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const newGeometryAlreadyExisted = geometryMap.get(geometry);
|
||
|
|
if (newGeometryAlreadyExisted != null) {
|
||
|
|
mesh.geometry = newGeometryAlreadyExisted;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const vertexCount = Object.values(geometry.attributes)[0].count;
|
||
|
|
const vertexInUse = new Array(vertexCount);
|
||
|
|
let verticesUsed = 0;
|
||
|
|
const originalIndexArray = originalIndex.array;
|
||
|
|
for (let i = 0; i < originalIndexArray.length; i++) {
|
||
|
|
const index = originalIndexArray[i];
|
||
|
|
if (!vertexInUse[index]) {
|
||
|
|
vertexInUse[index] = true;
|
||
|
|
verticesUsed++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (verticesUsed === vertexCount) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const originalIndexNewIndexMap = [];
|
||
|
|
const newIndexOriginalIndexMap = [];
|
||
|
|
let indexHead = 0;
|
||
|
|
for (let i = 0; i < vertexInUse.length; i++) {
|
||
|
|
if (vertexInUse[i]) {
|
||
|
|
const newIndex = indexHead++;
|
||
|
|
originalIndexNewIndexMap[i] = newIndex;
|
||
|
|
newIndexOriginalIndexMap[newIndex] = i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const newGeometry = new THREE29.BufferGeometry();
|
||
|
|
newGeometry.name = geometry.name;
|
||
|
|
newGeometry.morphTargetsRelative = geometry.morphTargetsRelative;
|
||
|
|
geometry.groups.forEach((group) => {
|
||
|
|
newGeometry.addGroup(group.start, group.count, group.materialIndex);
|
||
|
|
});
|
||
|
|
newGeometry.boundingBox = (_b = (_a = geometry.boundingBox) == null ? void 0 : _a.clone()) != null ? _b : null;
|
||
|
|
newGeometry.boundingSphere = (_d = (_c = geometry.boundingSphere) == null ? void 0 : _c.clone()) != null ? _d : null;
|
||
|
|
newGeometry.setDrawRange(geometry.drawRange.start, geometry.drawRange.count);
|
||
|
|
newGeometry.userData = geometry.userData;
|
||
|
|
geometryMap.set(geometry, newGeometry);
|
||
|
|
{
|
||
|
|
const originalIndexArray2 = originalIndex.array;
|
||
|
|
const newIndexArray = new originalIndexArray2.constructor(originalIndexArray2.length);
|
||
|
|
for (let i = 0; i < originalIndexArray2.length; i++) {
|
||
|
|
const originalIndex2 = originalIndexArray2[i];
|
||
|
|
const newIndex = originalIndexNewIndexMap[originalIndex2];
|
||
|
|
newIndexArray[i] = newIndex;
|
||
|
|
}
|
||
|
|
newGeometry.setIndex(new import_three.BufferAttribute(newIndexArray, 1, false));
|
||
|
|
}
|
||
|
|
Object.keys(geometry.attributes).forEach((attributeName) => {
|
||
|
|
const originalAttribute = geometry.attributes[attributeName];
|
||
|
|
if (originalAttribute.isInterleavedBufferAttribute) {
|
||
|
|
throw new Error("removeUnnecessaryVertices: InterleavedBufferAttribute is not supported");
|
||
|
|
}
|
||
|
|
const originalAttributeArray = originalAttribute.array;
|
||
|
|
const { itemSize, normalized } = originalAttribute;
|
||
|
|
const newAttributeArray = new originalAttributeArray.constructor(
|
||
|
|
newIndexOriginalIndexMap.length * itemSize
|
||
|
|
);
|
||
|
|
newIndexOriginalIndexMap.forEach((originalIndex2, i) => {
|
||
|
|
for (let j = 0; j < itemSize; j++) {
|
||
|
|
newAttributeArray[i * itemSize + j] = originalAttributeArray[originalIndex2 * itemSize + j];
|
||
|
|
}
|
||
|
|
});
|
||
|
|
newGeometry.setAttribute(attributeName, new import_three.BufferAttribute(newAttributeArray, itemSize, normalized));
|
||
|
|
});
|
||
|
|
let isNullMorph = true;
|
||
|
|
for (const [key, morphAttributes] of Object.entries(geometry.morphAttributes)) {
|
||
|
|
const attributeName = key;
|
||
|
|
newGeometry.morphAttributes[attributeName] = [];
|
||
|
|
for (let iMorph = 0; iMorph < morphAttributes.length; iMorph++) {
|
||
|
|
const originalAttribute = morphAttributes[iMorph];
|
||
|
|
if (originalAttribute.isInterleavedBufferAttribute) {
|
||
|
|
throw new Error("removeUnnecessaryVertices: InterleavedBufferAttribute is not supported");
|
||
|
|
}
|
||
|
|
const originalAttributeArray = originalAttribute.array;
|
||
|
|
const { itemSize, normalized } = originalAttribute;
|
||
|
|
const newAttributeArray = new originalAttributeArray.constructor(
|
||
|
|
newIndexOriginalIndexMap.length * itemSize
|
||
|
|
);
|
||
|
|
newIndexOriginalIndexMap.forEach((originalIndex2, i) => {
|
||
|
|
for (let j = 0; j < itemSize; j++) {
|
||
|
|
newAttributeArray[i * itemSize + j] = originalAttributeArray[originalIndex2 * itemSize + j];
|
||
|
|
}
|
||
|
|
});
|
||
|
|
isNullMorph = isNullMorph && newAttributeArray.every((v) => v === 0);
|
||
|
|
newGeometry.morphAttributes[attributeName][iMorph] = new import_three.BufferAttribute(
|
||
|
|
newAttributeArray,
|
||
|
|
itemSize,
|
||
|
|
normalized
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (isNullMorph) {
|
||
|
|
newGeometry.morphAttributes = {};
|
||
|
|
}
|
||
|
|
mesh.geometry = newGeometry;
|
||
|
|
});
|
||
|
|
Array.from(geometryMap.keys()).forEach((originalGeometry) => {
|
||
|
|
originalGeometry.dispose();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/VRMUtils/rotateVRM0.ts
|
||
|
|
function rotateVRM0(vrm) {
|
||
|
|
var _a;
|
||
|
|
if (((_a = vrm.meta) == null ? void 0 : _a.metaVersion) === "0") {
|
||
|
|
vrm.scene.rotation.y = Math.PI;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/VRMUtils/index.ts
|
||
|
|
var VRMUtils = class {
|
||
|
|
constructor() {
|
||
|
|
}
|
||
|
|
};
|
||
|
|
VRMUtils.combineMorphs = combineMorphs;
|
||
|
|
VRMUtils.combineSkeletons = combineSkeletons;
|
||
|
|
VRMUtils.deepDispose = deepDispose;
|
||
|
|
VRMUtils.removeUnnecessaryJoints = removeUnnecessaryJoints;
|
||
|
|
VRMUtils.removeUnnecessaryVertices = removeUnnecessaryVertices;
|
||
|
|
VRMUtils.rotateVRM0 = rotateVRM0;
|
||
|
|
/*!
|
||
|
|
* @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
|
||
|
|
*/
|
||
|
|
/*!
|
||
|
|
* @pixiv/three-vrm-materials-mtoon v3.4.1
|
||
|
|
* MToon (toon material) module for @pixiv/three-vrm
|
||
|
|
*
|
||
|
|
* Copyright (c) 2019-2025 pixiv Inc.
|
||
|
|
* @pixiv/three-vrm-materials-mtoon is distributed under MIT License
|
||
|
|
* https://github.com/pixiv/three-vrm/blob/release/LICENSE
|
||
|
|
*/
|
||
|
|
/*!
|
||
|
|
* @pixiv/three-vrm-materials-hdr-emissive-multiplier v3.4.1
|
||
|
|
* Support VRMC_hdr_emissiveMultiplier for @pixiv/three-vrm
|
||
|
|
*
|
||
|
|
* Copyright (c) 2019-2025 pixiv Inc.
|
||
|
|
* @pixiv/three-vrm-materials-hdr-emissive-multiplier is distributed under MIT License
|
||
|
|
* https://github.com/pixiv/three-vrm/blob/release/LICENSE
|
||
|
|
*/
|
||
|
|
/*!
|
||
|
|
* @pixiv/three-vrm-materials-v0compat v3.4.1
|
||
|
|
* VRM0.0 materials compatibility layer plugin for @pixiv/three-vrm
|
||
|
|
*
|
||
|
|
* Copyright (c) 2019-2025 pixiv Inc.
|
||
|
|
* @pixiv/three-vrm-materials-v0compat is distributed under MIT License
|
||
|
|
* https://github.com/pixiv/three-vrm/blob/release/LICENSE
|
||
|
|
*/
|
||
|
|
/*!
|
||
|
|
* @pixiv/three-vrm-node-constraint v3.4.1
|
||
|
|
* Node constraint module for @pixiv/three-vrm
|
||
|
|
*
|
||
|
|
* Copyright (c) 2019-2025 pixiv Inc.
|
||
|
|
* @pixiv/three-vrm-node-constraint is distributed under MIT License
|
||
|
|
* https://github.com/pixiv/three-vrm/blob/release/LICENSE
|
||
|
|
*/
|
||
|
|
/*!
|
||
|
|
* @pixiv/three-vrm-springbone v3.4.1
|
||
|
|
* Spring bone module for @pixiv/three-vrm
|
||
|
|
*
|
||
|
|
* Copyright (c) 2019-2025 pixiv Inc.
|
||
|
|
* @pixiv/three-vrm-springbone is distributed under MIT License
|
||
|
|
* https://github.com/pixiv/three-vrm/blob/release/LICENSE
|
||
|
|
*/
|
||
|
|
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2luZGV4LnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9leHByZXNzaW9ucy9WUk1FeHByZXNzaW9uLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9leHByZXNzaW9ucy9WUk1FeHByZXNzaW9uTG9hZGVyUGx1Z2luLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy91dGlscy9nbHRmRXh0cmFjdFByaW1pdGl2ZXNGcm9tTm9kZS50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvZXhwcmVzc2lvbnMvVlJNRXhwcmVzc2lvblByZXNldE5hbWUudHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL3V0aWxzL3NhdHVyYXRlLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9leHByZXNzaW9ucy9WUk1FeHByZXNzaW9uTWFuYWdlci50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvZXhwcmVzc2lvbnMvVlJNRXhwcmVzc2lvbk1hdGVyaWFsQ29sb3JUeXBlLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9leHByZXNzaW9ucy9WUk1FeHByZXNzaW9uTWF0ZXJpYWxDb2xvckJpbmQudHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL2V4cHJlc3Npb25zL1ZSTUV4cHJlc3Npb25Nb3JwaFRhcmdldEJpbmQudHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL2V4cHJlc3Npb25zL1ZSTUV4cHJlc3Npb25UZXh0dXJlVHJhbnNmb3JtQmluZC50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvZXhwcmVzc2lvbnMvVlJNRXhwcmVzc2lvbk92ZXJyaWRlVHlwZS50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvZmlyc3RQZXJzb24vVlJNRmlyc3RQZXJzb24udHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL2ZpcnN0UGVyc29uL1ZSTUZpcnN0UGVyc29uTG9hZGVyUGx1Z2luLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9maXJzdFBlcnNvbi9WUk1GaXJzdFBlcnNvbk1lc2hBbm5vdGF0aW9uVHlwZS50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvaHVtYW5vaWQvaGVscGVycy9WUk1IdW1hbm9pZEhlbHBlci50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvaHVtYW5vaWQvVlJNSHVtYW5Cb25lTGlzdC50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvaHVtYW5vaWQvVlJNSHVtYW5Cb25lTmFtZS50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvaHVtYW5vaWQvVlJNSHVtYW5Cb25lUGFyZW50TWFwLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9odW1hbm9pZC9WUk1SaWcudHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL3V0aWxzL3F1YXRJbnZlcnRDb21wYXQudHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL2h1bWFub2lkL1ZSTUh1bWFub2lkUmlnLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9odW1hbm9pZC9WUk1IdW1hbm9pZC50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvaHVtYW5vaWQvVlJNUmVxdWlyZWRIdW1hbkJvbmVOYW1lLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9odW1hbm9pZC9WUk1IdW1hbm9pZExvYWRlclBsdWdpbi50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvbG9va0F0L2hlbHBlcnMvVlJNTG9va0F0SGVscGVyLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9sb29rQXQvaGVscGVycy91dGlscy9GYW5CdWZmZXJHZW9tZXRyeS50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvbG9va0F0L2hlbHBlcnMvdXRpbHMvTGluZUFuZFNwaGVyZUJ1ZmZlckdlb21ldHJ5LnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9sb29rQXQvVlJNTG9va0F0LnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy91dGlscy9nZXRXb3JsZFF1YXRlcm5pb25MaXRlLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9sb29rQXQvdXRpbHMvY2FsY0F6aW11dGhBbHRpdHVkZS50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvbG9va0F0L3V0aWxzL3Nhbml0aXplQW5nbGUudHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL2xvb2tBdC9WUk1Mb29rQXRCb25lQXBwbGllci50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvbG9va0F0L1ZSTUxvb2tBdEV4cHJlc3Npb25BcHBsaWVyLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9sb29rQXQvVlJNTG9va0F0UmFuZ2VNYXAudHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL2xvb2tBdC9WUk1Mb29rQXRMb2FkZXJQbHVnaW4udHMiLCAiLi4vLi4vdGhyZWUtdnJtLWNvcmUvc3JjL2xvb2tBdC9WUk1Mb29rQXRUeXBlTmFtZS50cyIsICIuLi8uLi90aHJlZS12cm0tY29yZS9zcmMvbWV0YS9WUk1NZXRhTG9hZGVyUGx1Z2luLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy91dGlscy9yZXNvbHZlVVJMLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9WUk1Db3JlLnRzIiwgIi4uLy4uL3RocmVlLXZybS1jb3JlL3NyYy9WUk1Db3JlTG9hZGVyUGx1Z2luLnRzIiwgIi4uL3NyYy9WUk0udHMiLCAiLi4vLi4vdGhyZWUtdnJtLW1hdGVyaWFscy1tdG9vbi9zcmMvTVRvb25NYXRlcmlhbExvYWRlclBsdWdpbi50cyIsICIuLi8uLi90aHJlZS12cm0tbWF0ZXJpYWxzLW10b29uL3NyYy9HTFRGTVRvb25NYXRlcmlhbFBhcmFtc0Fzc2lnbkhlbHBlci50cyIsICIuLi8uLi90aHJlZS12cm0tbWF0ZXJpYWxzLW10b29uL3NyYy91dGlscy9zZXRUZXh0dXJlQ29sb3JTcGFjZS50cyIsICIuLi8uLi90aHJlZS12cm0tbWF0ZXJpYWxzLW10b29uL3NyYy9NVG9vbk1hdGVyaWFsLnRzIiwgIi4uLy4uL3RocmVlLXZybS1tYXRlcmlhbHMtbXRvb24vc3JjL3NoYWRlcnMvbXRvb24udmVydCIsICIuLi8uLi90aHJlZS12cm0tbWF0ZXJpYWxzLW10b29uL3NyYy9zaGFkZXJzL210b29uLmZyYWciLCAiLi4vLi4vdGhyZWUtdnJtLW1hdGVyaWFscy1tdG9vbi9zcmMvTVRvb25NYXRlcmlhbERlYnVnTW9kZS50cyIsICIuLi8uLi90aHJlZS12cm0tbWF0ZXJpYWxzLW10b29uL3NyYy9NVG9vbk1hdGVyaWFsT3V0bGluZVdpZHRoT
|