@pixiv/three-vrm-core 1.0.0-beta.10 → 1.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/three-vrm-core.js +2549 -2549
- package/lib/three-vrm-core.module.js +2549 -2549
- package/package.json +4 -4
- package/types/VRMCore.d.ts +58 -58
- package/types/VRMCoreLoaderPlugin.d.ts +18 -18
- package/types/VRMCoreLoaderPluginOptions.d.ts +18 -18
- package/types/VRMCoreParameters.d.ts +16 -16
- package/types/expressions/VRMExpression.d.ts +64 -64
- package/types/expressions/VRMExpressionBind.d.ts +11 -11
- package/types/expressions/VRMExpressionLoaderPlugin.d.ts +23 -23
- package/types/expressions/VRMExpressionManager.d.ts +124 -124
- package/types/expressions/VRMExpressionMaterialColorBind.d.ts +42 -42
- package/types/expressions/VRMExpressionMaterialColorType.d.ts +8 -8
- package/types/expressions/VRMExpressionMorphTargetBind.d.ts +35 -35
- package/types/expressions/VRMExpressionOverrideType.d.ts +6 -6
- package/types/expressions/VRMExpressionPresetName.d.ts +21 -21
- package/types/expressions/VRMExpressionTextureTransformBind.d.ts +40 -40
- package/types/expressions/index.d.ts +10 -10
- package/types/firstPerson/VRMFirstPerson.d.ts +84 -84
- package/types/firstPerson/VRMFirstPersonLoaderPlugin.d.ts +20 -20
- package/types/firstPerson/VRMFirstPersonMeshAnnotation.d.ts +5 -5
- package/types/firstPerson/VRMFirstPersonMeshAnnotationType.d.ts +7 -7
- package/types/firstPerson/index.d.ts +4 -4
- package/types/humanoid/VRMHumanBone.d.ts +10 -10
- package/types/humanoid/VRMHumanBoneName.d.ts +58 -58
- package/types/humanoid/VRMHumanBones.d.ts +11 -11
- package/types/humanoid/VRMHumanoid.d.ts +73 -73
- package/types/humanoid/VRMHumanoidLoaderPlugin.d.ts +24 -24
- package/types/humanoid/VRMPose.d.ts +24 -24
- package/types/humanoid/VRMPoseTransform.d.ts +15 -15
- package/types/humanoid/VRMRequiredHumanBoneName.d.ts +18 -18
- package/types/humanoid/index.d.ts +8 -8
- package/types/index.d.ts +9 -9
- package/types/lookAt/VRMLookAt.d.ts +104 -104
- package/types/lookAt/VRMLookAtApplier.d.ts +12 -12
- package/types/lookAt/VRMLookAtBoneApplier.d.ts +50 -50
- package/types/lookAt/VRMLookAtExpressionApplier.d.ts +51 -51
- package/types/lookAt/VRMLookAtLoaderPlugin.d.ts +30 -30
- package/types/lookAt/VRMLookAtLoaderPluginOptions.d.ts +8 -8
- package/types/lookAt/VRMLookAtRangeMap.d.ts +23 -23
- package/types/lookAt/VRMLookAtTypeName.d.ts +8 -8
- package/types/lookAt/helpers/VRMLookAtHelper.d.ts +11 -11
- package/types/lookAt/helpers/index.d.ts +1 -1
- package/types/lookAt/helpers/utils/FanBufferGeometry.d.ts +13 -13
- package/types/lookAt/helpers/utils/LineAndSphereBufferGeometry.d.ts +13 -13
- package/types/lookAt/index.d.ts +8 -8
- package/types/meta/VRM0Meta.d.ts +62 -62
- package/types/meta/VRM1Meta.d.ts +86 -86
- package/types/meta/VRMMeta.d.ts +6 -6
- package/types/meta/VRMMetaLoaderPlugin.d.ts +32 -32
- package/types/meta/VRMMetaLoaderPluginOptions.d.ts +22 -22
- package/types/meta/index.d.ts +5 -5
- package/types/utils/getWorldQuaternionLite.d.ts +8 -8
- package/types/utils/gltfExtractPrimitivesFromNode.d.ts +21 -21
- package/types/utils/gltfGetAssociatedMaterialIndex.d.ts +10 -10
- package/types/utils/quatInvertCompat.d.ts +8 -8
- package/types/utils/resolveURL.d.ts +4 -4
- package/types/utils/saturate.d.ts +6 -6
package/lib/three-vrm-core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @pixiv/three-vrm-core v1.0.0-beta.
|
|
2
|
+
* @pixiv/three-vrm-core v1.0.0-beta.11
|
|
3
3
|
* The implementation of core features of VRM, for @pixiv/three-vrm
|
|
4
4
|
*
|
|
5
5
|
* Copyright (c) 2020-2021 pixiv Inc.
|
|
@@ -34,104 +34,104 @@
|
|
|
34
34
|
|
|
35
35
|
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
36
36
|
|
|
37
|
-
// animationMixer の監視対象は、Scene の中に入っている必要がある。
|
|
38
|
-
// そのため、表示オブジェクトではないけれど、Object3D を継承して Scene に投入できるようにする。
|
|
39
|
-
class VRMExpression extends THREE__namespace.Object3D {
|
|
40
|
-
constructor(expressionName) {
|
|
41
|
-
super();
|
|
42
|
-
/**
|
|
43
|
-
* The current weight of the expression.
|
|
44
|
-
*/
|
|
45
|
-
this.weight = 0.0;
|
|
46
|
-
/**
|
|
47
|
-
* Interpret non-zero values as 1.
|
|
48
|
-
*/
|
|
49
|
-
this.isBinary = false;
|
|
50
|
-
/**
|
|
51
|
-
* Specify how the expression overrides blink expressions.
|
|
52
|
-
*/
|
|
53
|
-
this.overrideBlink = 'none';
|
|
54
|
-
/**
|
|
55
|
-
* Specify how the expression overrides lookAt expressions.
|
|
56
|
-
*/
|
|
57
|
-
this.overrideLookAt = 'none';
|
|
58
|
-
/**
|
|
59
|
-
* Specify how the expression overrides mouth expressions.
|
|
60
|
-
*/
|
|
61
|
-
this.overrideMouth = 'none';
|
|
62
|
-
this._binds = [];
|
|
63
|
-
this.name = `VRMExpression_${expressionName}`;
|
|
64
|
-
this.expressionName = expressionName;
|
|
65
|
-
// traverse 時の救済手段として Object3D ではないことを明示しておく
|
|
66
|
-
this.type = 'VRMExpression';
|
|
67
|
-
// 表示目的のオブジェクトではないので、負荷軽減のために visible を false にしておく。
|
|
68
|
-
// これにより、このインスタンスに対する毎フレームの matrix 自動計算を省略できる。
|
|
69
|
-
this.visible = false;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* A value represents how much it should override blink expressions.
|
|
73
|
-
* `0.0` == no override at all, `1.0` == completely block the expressions.
|
|
74
|
-
*/
|
|
75
|
-
get overrideBlinkAmount() {
|
|
76
|
-
if (this.overrideBlink === 'block') {
|
|
77
|
-
return 0.0 < this.weight ? 1.0 : 0.0;
|
|
78
|
-
}
|
|
79
|
-
else if (this.overrideBlink === 'blend') {
|
|
80
|
-
return this.weight;
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
return 0.0;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* A value represents how much it should override lookAt expressions.
|
|
88
|
-
* `0.0` == no override at all, `1.0` == completely block the expressions.
|
|
89
|
-
*/
|
|
90
|
-
get overrideLookAtAmount() {
|
|
91
|
-
if (this.overrideLookAt === 'block') {
|
|
92
|
-
return 0.0 < this.weight ? 1.0 : 0.0;
|
|
93
|
-
}
|
|
94
|
-
else if (this.overrideLookAt === 'blend') {
|
|
95
|
-
return this.weight;
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
return 0.0;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* A value represents how much it should override mouth expressions.
|
|
103
|
-
* `0.0` == no override at all, `1.0` == completely block the expressions.
|
|
104
|
-
*/
|
|
105
|
-
get overrideMouthAmount() {
|
|
106
|
-
if (this.overrideMouth === 'block') {
|
|
107
|
-
return 0.0 < this.weight ? 1.0 : 0.0;
|
|
108
|
-
}
|
|
109
|
-
else if (this.overrideMouth === 'blend') {
|
|
110
|
-
return this.weight;
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
return 0.0;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
addBind(bind) {
|
|
117
|
-
this._binds.push(bind);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Apply weight to every assigned blend shapes.
|
|
121
|
-
* Should be called every frame.
|
|
122
|
-
*/
|
|
123
|
-
applyWeight(options) {
|
|
124
|
-
var _a;
|
|
125
|
-
let actualWeight = this.isBinary ? (this.weight === 0.0 ? 0.0 : 1.0) : this.weight;
|
|
126
|
-
actualWeight *= (_a = options === null || options === void 0 ? void 0 : options.multiplier) !== null && _a !== void 0 ? _a : 1.0;
|
|
127
|
-
this._binds.forEach((bind) => bind.applyWeight(actualWeight));
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Clear previously assigned blend shapes.
|
|
131
|
-
*/
|
|
132
|
-
clearAppliedWeight() {
|
|
133
|
-
this._binds.forEach((bind) => bind.clearAppliedWeight());
|
|
134
|
-
}
|
|
37
|
+
// animationMixer の監視対象は、Scene の中に入っている必要がある。
|
|
38
|
+
// そのため、表示オブジェクトではないけれど、Object3D を継承して Scene に投入できるようにする。
|
|
39
|
+
class VRMExpression extends THREE__namespace.Object3D {
|
|
40
|
+
constructor(expressionName) {
|
|
41
|
+
super();
|
|
42
|
+
/**
|
|
43
|
+
* The current weight of the expression.
|
|
44
|
+
*/
|
|
45
|
+
this.weight = 0.0;
|
|
46
|
+
/**
|
|
47
|
+
* Interpret non-zero values as 1.
|
|
48
|
+
*/
|
|
49
|
+
this.isBinary = false;
|
|
50
|
+
/**
|
|
51
|
+
* Specify how the expression overrides blink expressions.
|
|
52
|
+
*/
|
|
53
|
+
this.overrideBlink = 'none';
|
|
54
|
+
/**
|
|
55
|
+
* Specify how the expression overrides lookAt expressions.
|
|
56
|
+
*/
|
|
57
|
+
this.overrideLookAt = 'none';
|
|
58
|
+
/**
|
|
59
|
+
* Specify how the expression overrides mouth expressions.
|
|
60
|
+
*/
|
|
61
|
+
this.overrideMouth = 'none';
|
|
62
|
+
this._binds = [];
|
|
63
|
+
this.name = `VRMExpression_${expressionName}`;
|
|
64
|
+
this.expressionName = expressionName;
|
|
65
|
+
// traverse 時の救済手段として Object3D ではないことを明示しておく
|
|
66
|
+
this.type = 'VRMExpression';
|
|
67
|
+
// 表示目的のオブジェクトではないので、負荷軽減のために visible を false にしておく。
|
|
68
|
+
// これにより、このインスタンスに対する毎フレームの matrix 自動計算を省略できる。
|
|
69
|
+
this.visible = false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A value represents how much it should override blink expressions.
|
|
73
|
+
* `0.0` == no override at all, `1.0` == completely block the expressions.
|
|
74
|
+
*/
|
|
75
|
+
get overrideBlinkAmount() {
|
|
76
|
+
if (this.overrideBlink === 'block') {
|
|
77
|
+
return 0.0 < this.weight ? 1.0 : 0.0;
|
|
78
|
+
}
|
|
79
|
+
else if (this.overrideBlink === 'blend') {
|
|
80
|
+
return this.weight;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
return 0.0;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A value represents how much it should override lookAt expressions.
|
|
88
|
+
* `0.0` == no override at all, `1.0` == completely block the expressions.
|
|
89
|
+
*/
|
|
90
|
+
get overrideLookAtAmount() {
|
|
91
|
+
if (this.overrideLookAt === 'block') {
|
|
92
|
+
return 0.0 < this.weight ? 1.0 : 0.0;
|
|
93
|
+
}
|
|
94
|
+
else if (this.overrideLookAt === 'blend') {
|
|
95
|
+
return this.weight;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return 0.0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A value represents how much it should override mouth expressions.
|
|
103
|
+
* `0.0` == no override at all, `1.0` == completely block the expressions.
|
|
104
|
+
*/
|
|
105
|
+
get overrideMouthAmount() {
|
|
106
|
+
if (this.overrideMouth === 'block') {
|
|
107
|
+
return 0.0 < this.weight ? 1.0 : 0.0;
|
|
108
|
+
}
|
|
109
|
+
else if (this.overrideMouth === 'blend') {
|
|
110
|
+
return this.weight;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
return 0.0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
addBind(bind) {
|
|
117
|
+
this._binds.push(bind);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Apply weight to every assigned blend shapes.
|
|
121
|
+
* Should be called every frame.
|
|
122
|
+
*/
|
|
123
|
+
applyWeight(options) {
|
|
124
|
+
var _a;
|
|
125
|
+
let actualWeight = this.isBinary ? (this.weight === 0.0 ? 0.0 : 1.0) : this.weight;
|
|
126
|
+
actualWeight *= (_a = options === null || options === void 0 ? void 0 : options.multiplier) !== null && _a !== void 0 ? _a : 1.0;
|
|
127
|
+
this._binds.forEach((bind) => bind.applyWeight(actualWeight));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clear previously assigned blend shapes.
|
|
131
|
+
*/
|
|
132
|
+
clearAppliedWeight() {
|
|
133
|
+
this._binds.forEach((bind) => bind.clearAppliedWeight());
|
|
134
|
+
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
/*! *****************************************************************************
|
|
@@ -159,2520 +159,2520 @@
|
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
function extractPrimitivesInternal(gltf, nodeIndex, node) {
|
|
163
|
-
var _a, _b;
|
|
164
|
-
const json = gltf.parser.json;
|
|
165
|
-
/**
|
|
166
|
-
* Let's list up every possible patterns that parsed gltf nodes with a mesh can have,,,
|
|
167
|
-
*
|
|
168
|
-
* "*" indicates that those meshes should be listed up using this function
|
|
169
|
-
*
|
|
170
|
-
* ### A node with a (mesh, a signle primitive)
|
|
171
|
-
*
|
|
172
|
-
* - `THREE.Mesh`: The only primitive of the mesh *
|
|
173
|
-
*
|
|
174
|
-
* ### A node with a (mesh, multiple primitives)
|
|
175
|
-
*
|
|
176
|
-
* - `THREE.Group`: The root of the mesh
|
|
177
|
-
* - `THREE.Mesh`: A primitive of the mesh *
|
|
178
|
-
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
179
|
-
*
|
|
180
|
-
* ### A node with a (mesh, multiple primitives) AND (a child with a mesh, a single primitive)
|
|
181
|
-
*
|
|
182
|
-
* - `THREE.Group`: The root of the mesh
|
|
183
|
-
* - `THREE.Mesh`: A primitive of the mesh *
|
|
184
|
-
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
185
|
-
* - `THREE.Mesh`: A primitive of a MESH OF THE CHILD
|
|
186
|
-
*
|
|
187
|
-
* ### A node with a (mesh, multiple primitives) AND (a child with a mesh, multiple primitives)
|
|
188
|
-
*
|
|
189
|
-
* - `THREE.Group`: The root of the mesh
|
|
190
|
-
* - `THREE.Mesh`: A primitive of the mesh *
|
|
191
|
-
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
192
|
-
* - `THREE.Group`: The root of a MESH OF THE CHILD
|
|
193
|
-
* - `THREE.Mesh`: A primitive of the mesh of the child
|
|
194
|
-
* - `THREE.Mesh`: A primitive of the mesh of the child (2)
|
|
195
|
-
*
|
|
196
|
-
* ### A node with a (mesh, multiple primitives) BUT the node is a bone
|
|
197
|
-
*
|
|
198
|
-
* - `THREE.Bone`: The root of the node, as a bone
|
|
199
|
-
* - `THREE.Group`: The root of the mesh
|
|
200
|
-
* - `THREE.Mesh`: A primitive of the mesh *
|
|
201
|
-
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
202
|
-
*
|
|
203
|
-
* ### A node with a (mesh, multiple primitives) AND (a child with a mesh, multiple primitives) BUT the node is a bone
|
|
204
|
-
*
|
|
205
|
-
* - `THREE.Bone`: The root of the node, as a bone
|
|
206
|
-
* - `THREE.Group`: The root of the mesh
|
|
207
|
-
* - `THREE.Mesh`: A primitive of the mesh *
|
|
208
|
-
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
209
|
-
* - `THREE.Group`: The root of a MESH OF THE CHILD
|
|
210
|
-
* - `THREE.Mesh`: A primitive of the mesh of the child
|
|
211
|
-
* - `THREE.Mesh`: A primitive of the mesh of the child (2)
|
|
212
|
-
*
|
|
213
|
-
* ...I will take a strategy that traverses the root of the node and take first (primitiveCount) meshes.
|
|
214
|
-
*/
|
|
215
|
-
// Make sure that the node has a mesh
|
|
216
|
-
const schemaNode = (_a = json.nodes) === null || _a === void 0 ? void 0 : _a[nodeIndex];
|
|
217
|
-
if (schemaNode == null) {
|
|
218
|
-
console.warn(`extractPrimitivesInternal: Attempt to use nodes[${nodeIndex}] of glTF but the node doesn't exist`);
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
const meshIndex = schemaNode.mesh;
|
|
222
|
-
if (meshIndex == null) {
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
225
|
-
// How many primitives the mesh has?
|
|
226
|
-
const schemaMesh = (_b = json.meshes) === null || _b === void 0 ? void 0 : _b[meshIndex];
|
|
227
|
-
if (schemaMesh == null) {
|
|
228
|
-
console.warn(`extractPrimitivesInternal: Attempt to use meshes[${meshIndex}] of glTF but the mesh doesn't exist`);
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
const primitiveCount = schemaMesh.primitives.length;
|
|
232
|
-
// Traverse the node and take first (primitiveCount) meshes
|
|
233
|
-
const primitives = [];
|
|
234
|
-
node.traverse((object) => {
|
|
235
|
-
if (primitives.length < primitiveCount) {
|
|
236
|
-
if (object.isMesh) {
|
|
237
|
-
primitives.push(object);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
return primitives;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Extract primitives ( `THREE.Mesh[]` ) of a node from a loaded GLTF.
|
|
245
|
-
* The main purpose of this function is to distinguish primitives and children from a node that has both meshes and children.
|
|
246
|
-
*
|
|
247
|
-
* It utilizes the behavior that GLTFLoader adds mesh primitives to the node object ( `THREE.Group` ) first then adds its children.
|
|
248
|
-
*
|
|
249
|
-
* @param gltf A GLTF object taken from GLTFLoader
|
|
250
|
-
* @param nodeIndex The index of the node
|
|
251
|
-
*/
|
|
252
|
-
function gltfExtractPrimitivesFromNode(gltf, nodeIndex) {
|
|
253
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
254
|
-
const node = yield gltf.parser.getDependency('node', nodeIndex);
|
|
255
|
-
return extractPrimitivesInternal(gltf, nodeIndex, node);
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Extract primitives ( `THREE.Mesh[]` ) of nodes from a loaded GLTF.
|
|
260
|
-
* See {@link gltfExtractPrimitivesFromNode} for more details.
|
|
261
|
-
*
|
|
262
|
-
* It returns a map from node index to extraction result.
|
|
263
|
-
* If a node does not have a mesh, the entry for the node will not be put in the returning map.
|
|
264
|
-
*
|
|
265
|
-
* @param gltf A GLTF object taken from GLTFLoader
|
|
266
|
-
*/
|
|
267
|
-
function gltfExtractPrimitivesFromNodes(gltf) {
|
|
268
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
269
|
-
const nodes = yield gltf.parser.getDependencies('node');
|
|
270
|
-
const map = new Map();
|
|
271
|
-
nodes.forEach((node, index) => {
|
|
272
|
-
const result = extractPrimitivesInternal(gltf, index, node);
|
|
273
|
-
if (result != null) {
|
|
274
|
-
map.set(index, result);
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
return map;
|
|
278
|
-
});
|
|
162
|
+
function extractPrimitivesInternal(gltf, nodeIndex, node) {
|
|
163
|
+
var _a, _b;
|
|
164
|
+
const json = gltf.parser.json;
|
|
165
|
+
/**
|
|
166
|
+
* Let's list up every possible patterns that parsed gltf nodes with a mesh can have,,,
|
|
167
|
+
*
|
|
168
|
+
* "*" indicates that those meshes should be listed up using this function
|
|
169
|
+
*
|
|
170
|
+
* ### A node with a (mesh, a signle primitive)
|
|
171
|
+
*
|
|
172
|
+
* - `THREE.Mesh`: The only primitive of the mesh *
|
|
173
|
+
*
|
|
174
|
+
* ### A node with a (mesh, multiple primitives)
|
|
175
|
+
*
|
|
176
|
+
* - `THREE.Group`: The root of the mesh
|
|
177
|
+
* - `THREE.Mesh`: A primitive of the mesh *
|
|
178
|
+
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
179
|
+
*
|
|
180
|
+
* ### A node with a (mesh, multiple primitives) AND (a child with a mesh, a single primitive)
|
|
181
|
+
*
|
|
182
|
+
* - `THREE.Group`: The root of the mesh
|
|
183
|
+
* - `THREE.Mesh`: A primitive of the mesh *
|
|
184
|
+
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
185
|
+
* - `THREE.Mesh`: A primitive of a MESH OF THE CHILD
|
|
186
|
+
*
|
|
187
|
+
* ### A node with a (mesh, multiple primitives) AND (a child with a mesh, multiple primitives)
|
|
188
|
+
*
|
|
189
|
+
* - `THREE.Group`: The root of the mesh
|
|
190
|
+
* - `THREE.Mesh`: A primitive of the mesh *
|
|
191
|
+
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
192
|
+
* - `THREE.Group`: The root of a MESH OF THE CHILD
|
|
193
|
+
* - `THREE.Mesh`: A primitive of the mesh of the child
|
|
194
|
+
* - `THREE.Mesh`: A primitive of the mesh of the child (2)
|
|
195
|
+
*
|
|
196
|
+
* ### A node with a (mesh, multiple primitives) BUT the node is a bone
|
|
197
|
+
*
|
|
198
|
+
* - `THREE.Bone`: The root of the node, as a bone
|
|
199
|
+
* - `THREE.Group`: The root of the mesh
|
|
200
|
+
* - `THREE.Mesh`: A primitive of the mesh *
|
|
201
|
+
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
202
|
+
*
|
|
203
|
+
* ### A node with a (mesh, multiple primitives) AND (a child with a mesh, multiple primitives) BUT the node is a bone
|
|
204
|
+
*
|
|
205
|
+
* - `THREE.Bone`: The root of the node, as a bone
|
|
206
|
+
* - `THREE.Group`: The root of the mesh
|
|
207
|
+
* - `THREE.Mesh`: A primitive of the mesh *
|
|
208
|
+
* - `THREE.Mesh`: A primitive of the mesh (2) *
|
|
209
|
+
* - `THREE.Group`: The root of a MESH OF THE CHILD
|
|
210
|
+
* - `THREE.Mesh`: A primitive of the mesh of the child
|
|
211
|
+
* - `THREE.Mesh`: A primitive of the mesh of the child (2)
|
|
212
|
+
*
|
|
213
|
+
* ...I will take a strategy that traverses the root of the node and take first (primitiveCount) meshes.
|
|
214
|
+
*/
|
|
215
|
+
// Make sure that the node has a mesh
|
|
216
|
+
const schemaNode = (_a = json.nodes) === null || _a === void 0 ? void 0 : _a[nodeIndex];
|
|
217
|
+
if (schemaNode == null) {
|
|
218
|
+
console.warn(`extractPrimitivesInternal: Attempt to use nodes[${nodeIndex}] of glTF but the node doesn't exist`);
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
const meshIndex = schemaNode.mesh;
|
|
222
|
+
if (meshIndex == null) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
// How many primitives the mesh has?
|
|
226
|
+
const schemaMesh = (_b = json.meshes) === null || _b === void 0 ? void 0 : _b[meshIndex];
|
|
227
|
+
if (schemaMesh == null) {
|
|
228
|
+
console.warn(`extractPrimitivesInternal: Attempt to use meshes[${meshIndex}] of glTF but the mesh doesn't exist`);
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const primitiveCount = schemaMesh.primitives.length;
|
|
232
|
+
// Traverse the node and take first (primitiveCount) meshes
|
|
233
|
+
const primitives = [];
|
|
234
|
+
node.traverse((object) => {
|
|
235
|
+
if (primitives.length < primitiveCount) {
|
|
236
|
+
if (object.isMesh) {
|
|
237
|
+
primitives.push(object);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
return primitives;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Extract primitives ( `THREE.Mesh[]` ) of a node from a loaded GLTF.
|
|
245
|
+
* The main purpose of this function is to distinguish primitives and children from a node that has both meshes and children.
|
|
246
|
+
*
|
|
247
|
+
* It utilizes the behavior that GLTFLoader adds mesh primitives to the node object ( `THREE.Group` ) first then adds its children.
|
|
248
|
+
*
|
|
249
|
+
* @param gltf A GLTF object taken from GLTFLoader
|
|
250
|
+
* @param nodeIndex The index of the node
|
|
251
|
+
*/
|
|
252
|
+
function gltfExtractPrimitivesFromNode(gltf, nodeIndex) {
|
|
253
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
254
|
+
const node = yield gltf.parser.getDependency('node', nodeIndex);
|
|
255
|
+
return extractPrimitivesInternal(gltf, nodeIndex, node);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Extract primitives ( `THREE.Mesh[]` ) of nodes from a loaded GLTF.
|
|
260
|
+
* See {@link gltfExtractPrimitivesFromNode} for more details.
|
|
261
|
+
*
|
|
262
|
+
* It returns a map from node index to extraction result.
|
|
263
|
+
* If a node does not have a mesh, the entry for the node will not be put in the returning map.
|
|
264
|
+
*
|
|
265
|
+
* @param gltf A GLTF object taken from GLTFLoader
|
|
266
|
+
*/
|
|
267
|
+
function gltfExtractPrimitivesFromNodes(gltf) {
|
|
268
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
269
|
+
const nodes = yield gltf.parser.getDependencies('node');
|
|
270
|
+
const map = new Map();
|
|
271
|
+
nodes.forEach((node, index) => {
|
|
272
|
+
const result = extractPrimitivesInternal(gltf, index, node);
|
|
273
|
+
if (result != null) {
|
|
274
|
+
map.set(index, result);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return map;
|
|
278
|
+
});
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
/**
|
|
282
|
-
* Get a material definition index of glTF from associated material.
|
|
283
|
-
* It's basically a comat code between Three.js r133 or above and previous versions.
|
|
284
|
-
* @param parser GLTFParser
|
|
285
|
-
* @param material A material of gltf
|
|
286
|
-
* @returns Material definition index of glTF
|
|
287
|
-
*/
|
|
288
|
-
function gltfGetAssociatedMaterialIndex(parser, material) {
|
|
289
|
-
var _a, _b;
|
|
290
|
-
const threeRevision = parseInt(THREE__namespace.REVISION, 10);
|
|
291
|
-
let index = null;
|
|
292
|
-
if (threeRevision >= 133) {
|
|
293
|
-
index = (_b = (_a = parser.associations.get(material)) === null || _a === void 0 ? void 0 : _a.materials) !== null && _b !== void 0 ? _b : null;
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
const associations = parser.associations;
|
|
297
|
-
const reference = associations.get(material);
|
|
298
|
-
if ((reference === null || reference === void 0 ? void 0 : reference.type) === 'materials') {
|
|
299
|
-
index = reference.index;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
return index;
|
|
281
|
+
/**
|
|
282
|
+
* Get a material definition index of glTF from associated material.
|
|
283
|
+
* It's basically a comat code between Three.js r133 or above and previous versions.
|
|
284
|
+
* @param parser GLTFParser
|
|
285
|
+
* @param material A material of gltf
|
|
286
|
+
* @returns Material definition index of glTF
|
|
287
|
+
*/
|
|
288
|
+
function gltfGetAssociatedMaterialIndex(parser, material) {
|
|
289
|
+
var _a, _b;
|
|
290
|
+
const threeRevision = parseInt(THREE__namespace.REVISION, 10);
|
|
291
|
+
let index = null;
|
|
292
|
+
if (threeRevision >= 133) {
|
|
293
|
+
index = (_b = (_a = parser.associations.get(material)) === null || _a === void 0 ? void 0 : _a.materials) !== null && _b !== void 0 ? _b : null;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const associations = parser.associations;
|
|
297
|
+
const reference = associations.get(material);
|
|
298
|
+
if ((reference === null || reference === void 0 ? void 0 : reference.type) === 'materials') {
|
|
299
|
+
index = reference.index;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return index;
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
306
|
-
const VRMExpressionPresetName = {
|
|
307
|
-
Aa: 'aa',
|
|
308
|
-
Ih: 'ih',
|
|
309
|
-
Ou: 'ou',
|
|
310
|
-
Ee: 'ee',
|
|
311
|
-
Oh: 'oh',
|
|
312
|
-
Blink: 'blink',
|
|
313
|
-
Happy: 'happy',
|
|
314
|
-
Angry: 'angry',
|
|
315
|
-
Sad: 'sad',
|
|
316
|
-
Relaxed: 'relaxed',
|
|
317
|
-
LookUp: 'lookUp',
|
|
318
|
-
Surprised: 'surprised',
|
|
319
|
-
LookDown: 'lookDown',
|
|
320
|
-
LookLeft: 'lookLeft',
|
|
321
|
-
LookRight: 'lookRight',
|
|
322
|
-
BlinkLeft: 'blinkLeft',
|
|
323
|
-
BlinkRight: 'blinkRight',
|
|
324
|
-
Neutral: 'neutral',
|
|
305
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
306
|
+
const VRMExpressionPresetName = {
|
|
307
|
+
Aa: 'aa',
|
|
308
|
+
Ih: 'ih',
|
|
309
|
+
Ou: 'ou',
|
|
310
|
+
Ee: 'ee',
|
|
311
|
+
Oh: 'oh',
|
|
312
|
+
Blink: 'blink',
|
|
313
|
+
Happy: 'happy',
|
|
314
|
+
Angry: 'angry',
|
|
315
|
+
Sad: 'sad',
|
|
316
|
+
Relaxed: 'relaxed',
|
|
317
|
+
LookUp: 'lookUp',
|
|
318
|
+
Surprised: 'surprised',
|
|
319
|
+
LookDown: 'lookDown',
|
|
320
|
+
LookLeft: 'lookLeft',
|
|
321
|
+
LookRight: 'lookRight',
|
|
322
|
+
BlinkLeft: 'blinkLeft',
|
|
323
|
+
BlinkRight: 'blinkRight',
|
|
324
|
+
Neutral: 'neutral',
|
|
325
325
|
};
|
|
326
326
|
|
|
327
|
-
/**
|
|
328
|
-
* Clamp the input value within [0.0 - 1.0].
|
|
329
|
-
*
|
|
330
|
-
* @param value The input value
|
|
331
|
-
*/
|
|
332
|
-
function saturate(value) {
|
|
333
|
-
return Math.max(Math.min(value, 1.0), 0.0);
|
|
327
|
+
/**
|
|
328
|
+
* Clamp the input value within [0.0 - 1.0].
|
|
329
|
+
*
|
|
330
|
+
* @param value The input value
|
|
331
|
+
*/
|
|
332
|
+
function saturate(value) {
|
|
333
|
+
return Math.max(Math.min(value, 1.0), 0.0);
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
-
class VRMExpressionManager {
|
|
337
|
-
/**
|
|
338
|
-
* Create a new {@link VRMExpressionManager}.
|
|
339
|
-
*/
|
|
340
|
-
constructor() {
|
|
341
|
-
/**
|
|
342
|
-
* A set of name or preset name of expressions that will be overridden by {@link VRMExpression.overrideBlink}.
|
|
343
|
-
*/
|
|
344
|
-
this.blinkExpressionNames = ['blink', 'blinkLeft', 'blinkRight'];
|
|
345
|
-
/**
|
|
346
|
-
* A set of name or preset name of expressions that will be overridden by {@link VRMExpression.overrideLookAt}.
|
|
347
|
-
*/
|
|
348
|
-
this.lookAtExpressionNames = ['lookLeft', 'lookRight', 'lookUp', 'lookDown'];
|
|
349
|
-
/**
|
|
350
|
-
* A set of name or preset name of expressions that will be overridden by {@link VRMExpression.overrideMouth}.
|
|
351
|
-
*/
|
|
352
|
-
this.mouthExpressionNames = ['aa', 'ee', 'ih', 'oh', 'ou'];
|
|
353
|
-
/**
|
|
354
|
-
* A set of {@link VRMExpression}.
|
|
355
|
-
* When you want to register expressions, use {@link registerExpression}
|
|
356
|
-
*/
|
|
357
|
-
this._expressions = [];
|
|
358
|
-
/**
|
|
359
|
-
* A map from name to expression.
|
|
360
|
-
*/
|
|
361
|
-
this._expressionMap = {};
|
|
362
|
-
// do nothing
|
|
363
|
-
}
|
|
364
|
-
get expressions() {
|
|
365
|
-
return this._expressions.concat();
|
|
366
|
-
}
|
|
367
|
-
get expressionMap() {
|
|
368
|
-
return Object.assign({}, this._expressionMap);
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* A map from name to expression, but excluding custom expressions.
|
|
372
|
-
*/
|
|
373
|
-
get presetExpressionMap() {
|
|
374
|
-
const result = {};
|
|
375
|
-
const presetNameSet = new Set(Object.values(VRMExpressionPresetName));
|
|
376
|
-
Object.entries(this._expressionMap).forEach(([name, expression]) => {
|
|
377
|
-
if (presetNameSet.has(name)) {
|
|
378
|
-
result[name] = expression;
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
return result;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* A map from name to expression, but excluding preset expressions.
|
|
385
|
-
*/
|
|
386
|
-
get customExpressionMap() {
|
|
387
|
-
const result = {};
|
|
388
|
-
const presetNameSet = new Set(Object.values(VRMExpressionPresetName));
|
|
389
|
-
Object.entries(this._expressionMap).forEach(([name, expression]) => {
|
|
390
|
-
if (!presetNameSet.has(name)) {
|
|
391
|
-
result[name] = expression;
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
return result;
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Copy the given {@link VRMExpressionManager} into this one.
|
|
398
|
-
* @param source The {@link VRMExpressionManager} you want to copy
|
|
399
|
-
* @returns this
|
|
400
|
-
*/
|
|
401
|
-
copy(source) {
|
|
402
|
-
// first unregister all the expression it has
|
|
403
|
-
const expressions = this._expressions.concat();
|
|
404
|
-
expressions.forEach((expression) => {
|
|
405
|
-
this.unregisterExpression(expression);
|
|
406
|
-
});
|
|
407
|
-
// then register all the expression of the source
|
|
408
|
-
source._expressions.forEach((expression) => {
|
|
409
|
-
this.registerExpression(expression);
|
|
410
|
-
});
|
|
411
|
-
// copy remaining members
|
|
412
|
-
this.blinkExpressionNames = source.blinkExpressionNames.concat();
|
|
413
|
-
this.lookAtExpressionNames = source.lookAtExpressionNames.concat();
|
|
414
|
-
this.mouthExpressionNames = source.mouthExpressionNames.concat();
|
|
415
|
-
return this;
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Returns a clone of this {@link VRMExpressionManager}.
|
|
419
|
-
* @returns Copied {@link VRMExpressionManager}
|
|
420
|
-
*/
|
|
421
|
-
clone() {
|
|
422
|
-
return new VRMExpressionManager().copy(this);
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Return a registered expression.
|
|
426
|
-
* If it cannot find an expression, it will return `null` instead.
|
|
427
|
-
*
|
|
428
|
-
* @param name Name or preset name of the expression
|
|
429
|
-
*/
|
|
430
|
-
getExpression(name) {
|
|
431
|
-
var _a;
|
|
432
|
-
return (_a = this._expressionMap[name]) !== null && _a !== void 0 ? _a : null;
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Register an expression.
|
|
436
|
-
*
|
|
437
|
-
* @param expression {@link VRMExpression} that describes the expression
|
|
438
|
-
*/
|
|
439
|
-
registerExpression(expression) {
|
|
440
|
-
this._expressions.push(expression);
|
|
441
|
-
this._expressionMap[expression.expressionName] = expression;
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Unregister an expression.
|
|
445
|
-
*
|
|
446
|
-
* @param expression The expression you want to unregister
|
|
447
|
-
*/
|
|
448
|
-
unregisterExpression(expression) {
|
|
449
|
-
const index = this._expressions.indexOf(expression);
|
|
450
|
-
if (index === -1) {
|
|
451
|
-
console.warn('VRMExpressionManager: The specified expressions is not registered');
|
|
452
|
-
}
|
|
453
|
-
this._expressions.splice(index, 1);
|
|
454
|
-
delete this._expressionMap[expression.expressionName];
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Get the current weight of the specified expression.
|
|
458
|
-
* If it doesn't have an expression of given name, it will return `null` instead.
|
|
459
|
-
*
|
|
460
|
-
* @param name Name of the expression
|
|
461
|
-
*/
|
|
462
|
-
getValue(name) {
|
|
463
|
-
var _a;
|
|
464
|
-
const expression = this.getExpression(name);
|
|
465
|
-
return (_a = expression === null || expression === void 0 ? void 0 : expression.weight) !== null && _a !== void 0 ? _a : null;
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* Set a weight to the specified expression.
|
|
469
|
-
*
|
|
470
|
-
* @param name Name of the expression
|
|
471
|
-
* @param weight Weight
|
|
472
|
-
*/
|
|
473
|
-
setValue(name, weight) {
|
|
474
|
-
const expression = this.getExpression(name);
|
|
475
|
-
if (expression) {
|
|
476
|
-
expression.weight = saturate(weight);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
/**
|
|
480
|
-
* Get a track name of specified expression.
|
|
481
|
-
* This track name is needed to manipulate its expression via keyframe animations.
|
|
482
|
-
*
|
|
483
|
-
* @example Manipulate an expression using keyframe animation
|
|
484
|
-
* ```js
|
|
485
|
-
* const trackName = vrm.expressionManager.getExpressionTrackName( 'blink' );
|
|
486
|
-
* const track = new THREE.NumberKeyframeTrack(
|
|
487
|
-
* name,
|
|
488
|
-
* [ 0.0, 0.5, 1.0 ], // times
|
|
489
|
-
* [ 0.0, 1.0, 0.0 ] // values
|
|
490
|
-
* );
|
|
491
|
-
*
|
|
492
|
-
* const clip = new THREE.AnimationClip(
|
|
493
|
-
* 'blink', // name
|
|
494
|
-
* 1.0, // duration
|
|
495
|
-
* [ track ] // tracks
|
|
496
|
-
* );
|
|
497
|
-
*
|
|
498
|
-
* const mixer = new THREE.AnimationMixer( vrm.scene );
|
|
499
|
-
* const action = mixer.clipAction( clip );
|
|
500
|
-
* action.play();
|
|
501
|
-
* ```
|
|
502
|
-
*
|
|
503
|
-
* @param name Name of the expression
|
|
504
|
-
*/
|
|
505
|
-
getExpressionTrackName(name) {
|
|
506
|
-
const expression = this.getExpression(name);
|
|
507
|
-
return expression ? `${expression.name}.weight` : null;
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* Update every expressions.
|
|
511
|
-
*/
|
|
512
|
-
update() {
|
|
513
|
-
// see how much we should override certain expressions
|
|
514
|
-
const weightMultipliers = this._calculateWeightMultipliers();
|
|
515
|
-
// reset expression binds first
|
|
516
|
-
this._expressions.forEach((expression) => {
|
|
517
|
-
expression.clearAppliedWeight();
|
|
518
|
-
});
|
|
519
|
-
// then apply binds
|
|
520
|
-
this._expressions.forEach((expression) => {
|
|
521
|
-
let multiplier = 1.0;
|
|
522
|
-
const name = expression.expressionName;
|
|
523
|
-
if (this.blinkExpressionNames.indexOf(name) !== -1) {
|
|
524
|
-
multiplier *= weightMultipliers.blink;
|
|
525
|
-
}
|
|
526
|
-
if (this.lookAtExpressionNames.indexOf(name) !== -1) {
|
|
527
|
-
multiplier *= weightMultipliers.lookAt;
|
|
528
|
-
}
|
|
529
|
-
if (this.mouthExpressionNames.indexOf(name) !== -1) {
|
|
530
|
-
multiplier *= weightMultipliers.mouth;
|
|
531
|
-
}
|
|
532
|
-
expression.applyWeight({ multiplier });
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Calculate sum of override amounts to see how much we should multiply weights of certain expressions.
|
|
537
|
-
*/
|
|
538
|
-
_calculateWeightMultipliers() {
|
|
539
|
-
let blink = 1.0;
|
|
540
|
-
let lookAt = 1.0;
|
|
541
|
-
let mouth = 1.0;
|
|
542
|
-
this._expressions.forEach((expression) => {
|
|
543
|
-
blink -= expression.overrideBlinkAmount;
|
|
544
|
-
lookAt -= expression.overrideLookAtAmount;
|
|
545
|
-
mouth -= expression.overrideMouthAmount;
|
|
546
|
-
});
|
|
547
|
-
blink = Math.max(0.0, blink);
|
|
548
|
-
lookAt = Math.max(0.0, lookAt);
|
|
549
|
-
mouth = Math.max(0.0, mouth);
|
|
550
|
-
return { blink, lookAt, mouth };
|
|
551
|
-
}
|
|
336
|
+
class VRMExpressionManager {
|
|
337
|
+
/**
|
|
338
|
+
* Create a new {@link VRMExpressionManager}.
|
|
339
|
+
*/
|
|
340
|
+
constructor() {
|
|
341
|
+
/**
|
|
342
|
+
* A set of name or preset name of expressions that will be overridden by {@link VRMExpression.overrideBlink}.
|
|
343
|
+
*/
|
|
344
|
+
this.blinkExpressionNames = ['blink', 'blinkLeft', 'blinkRight'];
|
|
345
|
+
/**
|
|
346
|
+
* A set of name or preset name of expressions that will be overridden by {@link VRMExpression.overrideLookAt}.
|
|
347
|
+
*/
|
|
348
|
+
this.lookAtExpressionNames = ['lookLeft', 'lookRight', 'lookUp', 'lookDown'];
|
|
349
|
+
/**
|
|
350
|
+
* A set of name or preset name of expressions that will be overridden by {@link VRMExpression.overrideMouth}.
|
|
351
|
+
*/
|
|
352
|
+
this.mouthExpressionNames = ['aa', 'ee', 'ih', 'oh', 'ou'];
|
|
353
|
+
/**
|
|
354
|
+
* A set of {@link VRMExpression}.
|
|
355
|
+
* When you want to register expressions, use {@link registerExpression}
|
|
356
|
+
*/
|
|
357
|
+
this._expressions = [];
|
|
358
|
+
/**
|
|
359
|
+
* A map from name to expression.
|
|
360
|
+
*/
|
|
361
|
+
this._expressionMap = {};
|
|
362
|
+
// do nothing
|
|
363
|
+
}
|
|
364
|
+
get expressions() {
|
|
365
|
+
return this._expressions.concat();
|
|
366
|
+
}
|
|
367
|
+
get expressionMap() {
|
|
368
|
+
return Object.assign({}, this._expressionMap);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* A map from name to expression, but excluding custom expressions.
|
|
372
|
+
*/
|
|
373
|
+
get presetExpressionMap() {
|
|
374
|
+
const result = {};
|
|
375
|
+
const presetNameSet = new Set(Object.values(VRMExpressionPresetName));
|
|
376
|
+
Object.entries(this._expressionMap).forEach(([name, expression]) => {
|
|
377
|
+
if (presetNameSet.has(name)) {
|
|
378
|
+
result[name] = expression;
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* A map from name to expression, but excluding preset expressions.
|
|
385
|
+
*/
|
|
386
|
+
get customExpressionMap() {
|
|
387
|
+
const result = {};
|
|
388
|
+
const presetNameSet = new Set(Object.values(VRMExpressionPresetName));
|
|
389
|
+
Object.entries(this._expressionMap).forEach(([name, expression]) => {
|
|
390
|
+
if (!presetNameSet.has(name)) {
|
|
391
|
+
result[name] = expression;
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Copy the given {@link VRMExpressionManager} into this one.
|
|
398
|
+
* @param source The {@link VRMExpressionManager} you want to copy
|
|
399
|
+
* @returns this
|
|
400
|
+
*/
|
|
401
|
+
copy(source) {
|
|
402
|
+
// first unregister all the expression it has
|
|
403
|
+
const expressions = this._expressions.concat();
|
|
404
|
+
expressions.forEach((expression) => {
|
|
405
|
+
this.unregisterExpression(expression);
|
|
406
|
+
});
|
|
407
|
+
// then register all the expression of the source
|
|
408
|
+
source._expressions.forEach((expression) => {
|
|
409
|
+
this.registerExpression(expression);
|
|
410
|
+
});
|
|
411
|
+
// copy remaining members
|
|
412
|
+
this.blinkExpressionNames = source.blinkExpressionNames.concat();
|
|
413
|
+
this.lookAtExpressionNames = source.lookAtExpressionNames.concat();
|
|
414
|
+
this.mouthExpressionNames = source.mouthExpressionNames.concat();
|
|
415
|
+
return this;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Returns a clone of this {@link VRMExpressionManager}.
|
|
419
|
+
* @returns Copied {@link VRMExpressionManager}
|
|
420
|
+
*/
|
|
421
|
+
clone() {
|
|
422
|
+
return new VRMExpressionManager().copy(this);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Return a registered expression.
|
|
426
|
+
* If it cannot find an expression, it will return `null` instead.
|
|
427
|
+
*
|
|
428
|
+
* @param name Name or preset name of the expression
|
|
429
|
+
*/
|
|
430
|
+
getExpression(name) {
|
|
431
|
+
var _a;
|
|
432
|
+
return (_a = this._expressionMap[name]) !== null && _a !== void 0 ? _a : null;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Register an expression.
|
|
436
|
+
*
|
|
437
|
+
* @param expression {@link VRMExpression} that describes the expression
|
|
438
|
+
*/
|
|
439
|
+
registerExpression(expression) {
|
|
440
|
+
this._expressions.push(expression);
|
|
441
|
+
this._expressionMap[expression.expressionName] = expression;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Unregister an expression.
|
|
445
|
+
*
|
|
446
|
+
* @param expression The expression you want to unregister
|
|
447
|
+
*/
|
|
448
|
+
unregisterExpression(expression) {
|
|
449
|
+
const index = this._expressions.indexOf(expression);
|
|
450
|
+
if (index === -1) {
|
|
451
|
+
console.warn('VRMExpressionManager: The specified expressions is not registered');
|
|
452
|
+
}
|
|
453
|
+
this._expressions.splice(index, 1);
|
|
454
|
+
delete this._expressionMap[expression.expressionName];
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Get the current weight of the specified expression.
|
|
458
|
+
* If it doesn't have an expression of given name, it will return `null` instead.
|
|
459
|
+
*
|
|
460
|
+
* @param name Name of the expression
|
|
461
|
+
*/
|
|
462
|
+
getValue(name) {
|
|
463
|
+
var _a;
|
|
464
|
+
const expression = this.getExpression(name);
|
|
465
|
+
return (_a = expression === null || expression === void 0 ? void 0 : expression.weight) !== null && _a !== void 0 ? _a : null;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Set a weight to the specified expression.
|
|
469
|
+
*
|
|
470
|
+
* @param name Name of the expression
|
|
471
|
+
* @param weight Weight
|
|
472
|
+
*/
|
|
473
|
+
setValue(name, weight) {
|
|
474
|
+
const expression = this.getExpression(name);
|
|
475
|
+
if (expression) {
|
|
476
|
+
expression.weight = saturate(weight);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Get a track name of specified expression.
|
|
481
|
+
* This track name is needed to manipulate its expression via keyframe animations.
|
|
482
|
+
*
|
|
483
|
+
* @example Manipulate an expression using keyframe animation
|
|
484
|
+
* ```js
|
|
485
|
+
* const trackName = vrm.expressionManager.getExpressionTrackName( 'blink' );
|
|
486
|
+
* const track = new THREE.NumberKeyframeTrack(
|
|
487
|
+
* name,
|
|
488
|
+
* [ 0.0, 0.5, 1.0 ], // times
|
|
489
|
+
* [ 0.0, 1.0, 0.0 ] // values
|
|
490
|
+
* );
|
|
491
|
+
*
|
|
492
|
+
* const clip = new THREE.AnimationClip(
|
|
493
|
+
* 'blink', // name
|
|
494
|
+
* 1.0, // duration
|
|
495
|
+
* [ track ] // tracks
|
|
496
|
+
* );
|
|
497
|
+
*
|
|
498
|
+
* const mixer = new THREE.AnimationMixer( vrm.scene );
|
|
499
|
+
* const action = mixer.clipAction( clip );
|
|
500
|
+
* action.play();
|
|
501
|
+
* ```
|
|
502
|
+
*
|
|
503
|
+
* @param name Name of the expression
|
|
504
|
+
*/
|
|
505
|
+
getExpressionTrackName(name) {
|
|
506
|
+
const expression = this.getExpression(name);
|
|
507
|
+
return expression ? `${expression.name}.weight` : null;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Update every expressions.
|
|
511
|
+
*/
|
|
512
|
+
update() {
|
|
513
|
+
// see how much we should override certain expressions
|
|
514
|
+
const weightMultipliers = this._calculateWeightMultipliers();
|
|
515
|
+
// reset expression binds first
|
|
516
|
+
this._expressions.forEach((expression) => {
|
|
517
|
+
expression.clearAppliedWeight();
|
|
518
|
+
});
|
|
519
|
+
// then apply binds
|
|
520
|
+
this._expressions.forEach((expression) => {
|
|
521
|
+
let multiplier = 1.0;
|
|
522
|
+
const name = expression.expressionName;
|
|
523
|
+
if (this.blinkExpressionNames.indexOf(name) !== -1) {
|
|
524
|
+
multiplier *= weightMultipliers.blink;
|
|
525
|
+
}
|
|
526
|
+
if (this.lookAtExpressionNames.indexOf(name) !== -1) {
|
|
527
|
+
multiplier *= weightMultipliers.lookAt;
|
|
528
|
+
}
|
|
529
|
+
if (this.mouthExpressionNames.indexOf(name) !== -1) {
|
|
530
|
+
multiplier *= weightMultipliers.mouth;
|
|
531
|
+
}
|
|
532
|
+
expression.applyWeight({ multiplier });
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Calculate sum of override amounts to see how much we should multiply weights of certain expressions.
|
|
537
|
+
*/
|
|
538
|
+
_calculateWeightMultipliers() {
|
|
539
|
+
let blink = 1.0;
|
|
540
|
+
let lookAt = 1.0;
|
|
541
|
+
let mouth = 1.0;
|
|
542
|
+
this._expressions.forEach((expression) => {
|
|
543
|
+
blink -= expression.overrideBlinkAmount;
|
|
544
|
+
lookAt -= expression.overrideLookAtAmount;
|
|
545
|
+
mouth -= expression.overrideMouthAmount;
|
|
546
|
+
});
|
|
547
|
+
blink = Math.max(0.0, blink);
|
|
548
|
+
lookAt = Math.max(0.0, lookAt);
|
|
549
|
+
mouth = Math.max(0.0, mouth);
|
|
550
|
+
return { blink, lookAt, mouth };
|
|
551
|
+
}
|
|
552
552
|
}
|
|
553
553
|
|
|
554
|
-
const _color = new THREE__namespace.Color();
|
|
555
|
-
/**
|
|
556
|
-
* A bind of expression influences to a material color.
|
|
557
|
-
*/
|
|
558
|
-
class VRMExpressionMaterialColorBind {
|
|
559
|
-
constructor({ material, type, targetValue, }) {
|
|
560
|
-
var _a, _b, _c;
|
|
561
|
-
this.material = material;
|
|
562
|
-
this.type = type;
|
|
563
|
-
this.targetValue = targetValue;
|
|
564
|
-
// init property name
|
|
565
|
-
const propertyNameMap = (_a = Object.entries(VRMExpressionMaterialColorBind._propertyNameMapMap).find(([distinguisher]) => {
|
|
566
|
-
return material[distinguisher] === true;
|
|
567
|
-
})) === null || _a === void 0 ? void 0 : _a[1];
|
|
568
|
-
const propertyName = (_b = propertyNameMap === null || propertyNameMap === void 0 ? void 0 : propertyNameMap[type]) !== null && _b !== void 0 ? _b : null;
|
|
569
|
-
if (propertyName == null) {
|
|
570
|
-
console.warn(`Tried to add a material color bind to the material ${(_c = material.name) !== null && _c !== void 0 ? _c : '(no name)'}, the type ${type} but the material or the type is not supported.`);
|
|
571
|
-
this._state = null;
|
|
572
|
-
}
|
|
573
|
-
else {
|
|
574
|
-
const target = material[propertyName];
|
|
575
|
-
const initialValue = target.clone();
|
|
576
|
-
const deltaValue = this.targetValue.clone().sub(initialValue);
|
|
577
|
-
this._state = {
|
|
578
|
-
propertyName,
|
|
579
|
-
initialValue,
|
|
580
|
-
deltaValue,
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
applyWeight(weight) {
|
|
585
|
-
if (this._state == null) {
|
|
586
|
-
// warning is already emitted in constructor
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
const { propertyName, deltaValue } = this._state;
|
|
590
|
-
const target = this.material[propertyName];
|
|
591
|
-
if (target === undefined) {
|
|
592
|
-
return;
|
|
593
|
-
} // TODO: we should kick this at `addMaterialValue`
|
|
594
|
-
target.add(_color.copy(deltaValue).multiplyScalar(weight));
|
|
595
|
-
if (typeof this.material.shouldApplyUniforms === 'boolean') {
|
|
596
|
-
this.material.shouldApplyUniforms = true;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
clearAppliedWeight() {
|
|
600
|
-
if (this._state == null) {
|
|
601
|
-
// warning is already emitted in constructor
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
const { propertyName, initialValue } = this._state;
|
|
605
|
-
const target = this.material[propertyName];
|
|
606
|
-
if (target === undefined) {
|
|
607
|
-
return;
|
|
608
|
-
} // TODO: we should kick this at `addMaterialValue`
|
|
609
|
-
target.copy(initialValue);
|
|
610
|
-
if (typeof this.material.shouldApplyUniforms === 'boolean') {
|
|
611
|
-
this.material.shouldApplyUniforms = true;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
VRMExpressionMaterialColorBind._propertyNameMapMap = {
|
|
616
|
-
isMeshStandardMaterial: {
|
|
617
|
-
color: 'color',
|
|
618
|
-
emissionColor: 'emissive',
|
|
619
|
-
},
|
|
620
|
-
isMeshBasicMaterial: {
|
|
621
|
-
color: 'color',
|
|
622
|
-
},
|
|
623
|
-
isMToonMaterial: {
|
|
624
|
-
color: 'color',
|
|
625
|
-
emissionColor: 'emissive',
|
|
626
|
-
outlineColor: 'outlineFactor',
|
|
627
|
-
rimColor: 'rimFactor',
|
|
628
|
-
shadeColor: 'shadeFactor',
|
|
629
|
-
},
|
|
554
|
+
const _color = new THREE__namespace.Color();
|
|
555
|
+
/**
|
|
556
|
+
* A bind of expression influences to a material color.
|
|
557
|
+
*/
|
|
558
|
+
class VRMExpressionMaterialColorBind {
|
|
559
|
+
constructor({ material, type, targetValue, }) {
|
|
560
|
+
var _a, _b, _c;
|
|
561
|
+
this.material = material;
|
|
562
|
+
this.type = type;
|
|
563
|
+
this.targetValue = targetValue;
|
|
564
|
+
// init property name
|
|
565
|
+
const propertyNameMap = (_a = Object.entries(VRMExpressionMaterialColorBind._propertyNameMapMap).find(([distinguisher]) => {
|
|
566
|
+
return material[distinguisher] === true;
|
|
567
|
+
})) === null || _a === void 0 ? void 0 : _a[1];
|
|
568
|
+
const propertyName = (_b = propertyNameMap === null || propertyNameMap === void 0 ? void 0 : propertyNameMap[type]) !== null && _b !== void 0 ? _b : null;
|
|
569
|
+
if (propertyName == null) {
|
|
570
|
+
console.warn(`Tried to add a material color bind to the material ${(_c = material.name) !== null && _c !== void 0 ? _c : '(no name)'}, the type ${type} but the material or the type is not supported.`);
|
|
571
|
+
this._state = null;
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
const target = material[propertyName];
|
|
575
|
+
const initialValue = target.clone();
|
|
576
|
+
const deltaValue = this.targetValue.clone().sub(initialValue);
|
|
577
|
+
this._state = {
|
|
578
|
+
propertyName,
|
|
579
|
+
initialValue,
|
|
580
|
+
deltaValue,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
applyWeight(weight) {
|
|
585
|
+
if (this._state == null) {
|
|
586
|
+
// warning is already emitted in constructor
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
const { propertyName, deltaValue } = this._state;
|
|
590
|
+
const target = this.material[propertyName];
|
|
591
|
+
if (target === undefined) {
|
|
592
|
+
return;
|
|
593
|
+
} // TODO: we should kick this at `addMaterialValue`
|
|
594
|
+
target.add(_color.copy(deltaValue).multiplyScalar(weight));
|
|
595
|
+
if (typeof this.material.shouldApplyUniforms === 'boolean') {
|
|
596
|
+
this.material.shouldApplyUniforms = true;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
clearAppliedWeight() {
|
|
600
|
+
if (this._state == null) {
|
|
601
|
+
// warning is already emitted in constructor
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
const { propertyName, initialValue } = this._state;
|
|
605
|
+
const target = this.material[propertyName];
|
|
606
|
+
if (target === undefined) {
|
|
607
|
+
return;
|
|
608
|
+
} // TODO: we should kick this at `addMaterialValue`
|
|
609
|
+
target.copy(initialValue);
|
|
610
|
+
if (typeof this.material.shouldApplyUniforms === 'boolean') {
|
|
611
|
+
this.material.shouldApplyUniforms = true;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
VRMExpressionMaterialColorBind._propertyNameMapMap = {
|
|
616
|
+
isMeshStandardMaterial: {
|
|
617
|
+
color: 'color',
|
|
618
|
+
emissionColor: 'emissive',
|
|
619
|
+
},
|
|
620
|
+
isMeshBasicMaterial: {
|
|
621
|
+
color: 'color',
|
|
622
|
+
},
|
|
623
|
+
isMToonMaterial: {
|
|
624
|
+
color: 'color',
|
|
625
|
+
emissionColor: 'emissive',
|
|
626
|
+
outlineColor: 'outlineFactor',
|
|
627
|
+
rimColor: 'rimFactor',
|
|
628
|
+
shadeColor: 'shadeFactor',
|
|
629
|
+
},
|
|
630
630
|
};
|
|
631
631
|
|
|
632
|
-
/**
|
|
633
|
-
* A bind of {@link VRMExpression} influences to morph targets.
|
|
634
|
-
*/
|
|
635
|
-
class VRMExpressionMorphTargetBind {
|
|
636
|
-
constructor({ primitives, index, weight, }) {
|
|
637
|
-
this.primitives = primitives;
|
|
638
|
-
this.index = index;
|
|
639
|
-
this.weight = weight;
|
|
640
|
-
}
|
|
641
|
-
applyWeight(weight) {
|
|
642
|
-
this.primitives.forEach((mesh) => {
|
|
643
|
-
var _a;
|
|
644
|
-
if (((_a = mesh.morphTargetInfluences) === null || _a === void 0 ? void 0 : _a[this.index]) != null) {
|
|
645
|
-
mesh.morphTargetInfluences[this.index] += this.weight * weight;
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
clearAppliedWeight() {
|
|
650
|
-
this.primitives.forEach((mesh) => {
|
|
651
|
-
var _a;
|
|
652
|
-
if (((_a = mesh.morphTargetInfluences) === null || _a === void 0 ? void 0 : _a[this.index]) != null) {
|
|
653
|
-
mesh.morphTargetInfluences[this.index] = 0.0;
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
}
|
|
632
|
+
/**
|
|
633
|
+
* A bind of {@link VRMExpression} influences to morph targets.
|
|
634
|
+
*/
|
|
635
|
+
class VRMExpressionMorphTargetBind {
|
|
636
|
+
constructor({ primitives, index, weight, }) {
|
|
637
|
+
this.primitives = primitives;
|
|
638
|
+
this.index = index;
|
|
639
|
+
this.weight = weight;
|
|
640
|
+
}
|
|
641
|
+
applyWeight(weight) {
|
|
642
|
+
this.primitives.forEach((mesh) => {
|
|
643
|
+
var _a;
|
|
644
|
+
if (((_a = mesh.morphTargetInfluences) === null || _a === void 0 ? void 0 : _a[this.index]) != null) {
|
|
645
|
+
mesh.morphTargetInfluences[this.index] += this.weight * weight;
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
clearAppliedWeight() {
|
|
650
|
+
this.primitives.forEach((mesh) => {
|
|
651
|
+
var _a;
|
|
652
|
+
if (((_a = mesh.morphTargetInfluences) === null || _a === void 0 ? void 0 : _a[this.index]) != null) {
|
|
653
|
+
mesh.morphTargetInfluences[this.index] = 0.0;
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
-
const _v2 = new THREE__namespace.Vector2();
|
|
660
|
-
/**
|
|
661
|
-
* A bind of expression influences to texture transforms.
|
|
662
|
-
*/
|
|
663
|
-
class VRMExpressionTextureTransformBind {
|
|
664
|
-
constructor({ material, scale, offset, }) {
|
|
665
|
-
var _a, _b;
|
|
666
|
-
this.material = material;
|
|
667
|
-
this.scale = scale;
|
|
668
|
-
this.offset = offset;
|
|
669
|
-
const propertyNames = (_a = Object.entries(VRMExpressionTextureTransformBind._propertyNamesMap).find(([distinguisher]) => {
|
|
670
|
-
return material[distinguisher] === true;
|
|
671
|
-
})) === null || _a === void 0 ? void 0 : _a[1];
|
|
672
|
-
if (propertyNames == null) {
|
|
673
|
-
console.warn(`Tried to add a texture transform bind to the material ${(_b = material.name) !== null && _b !== void 0 ? _b : '(no name)'} but the material is not supported.`);
|
|
674
|
-
this._properties = [];
|
|
675
|
-
}
|
|
676
|
-
else {
|
|
677
|
-
this._properties = [];
|
|
678
|
-
propertyNames.forEach((propertyName) => {
|
|
679
|
-
var _a;
|
|
680
|
-
const texture = (_a = material[propertyName]) === null || _a === void 0 ? void 0 : _a.clone();
|
|
681
|
-
if (!texture) {
|
|
682
|
-
return null;
|
|
683
|
-
}
|
|
684
|
-
material[propertyName] = texture; // because the texture is cloned
|
|
685
|
-
const initialOffset = texture.offset.clone();
|
|
686
|
-
const initialScale = texture.repeat.clone();
|
|
687
|
-
const deltaOffset = offset.clone().sub(initialOffset);
|
|
688
|
-
const deltaScale = scale.clone().sub(initialScale);
|
|
689
|
-
this._properties.push({
|
|
690
|
-
name: propertyName,
|
|
691
|
-
initialOffset,
|
|
692
|
-
deltaOffset,
|
|
693
|
-
initialScale,
|
|
694
|
-
deltaScale,
|
|
695
|
-
});
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
applyWeight(weight) {
|
|
700
|
-
this._properties.forEach((property) => {
|
|
701
|
-
const target = this.material[property.name];
|
|
702
|
-
if (target === undefined) {
|
|
703
|
-
return;
|
|
704
|
-
} // TODO: we should kick this at `addMaterialValue`
|
|
705
|
-
target.offset.add(_v2.copy(property.deltaOffset).multiplyScalar(weight));
|
|
706
|
-
target.repeat.add(_v2.copy(property.deltaScale).multiplyScalar(weight));
|
|
707
|
-
target.needsUpdate = true;
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
clearAppliedWeight() {
|
|
711
|
-
this._properties.forEach((property) => {
|
|
712
|
-
const target = this.material[property.name];
|
|
713
|
-
if (target === undefined) {
|
|
714
|
-
return;
|
|
715
|
-
} // TODO: we should kick this at `addMaterialValue`
|
|
716
|
-
target.offset.copy(property.initialOffset);
|
|
717
|
-
target.repeat.copy(property.initialScale);
|
|
718
|
-
target.needsUpdate = true;
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
VRMExpressionTextureTransformBind._propertyNamesMap = {
|
|
723
|
-
isMeshStandardMaterial: [
|
|
724
|
-
'map',
|
|
725
|
-
'emissiveMap',
|
|
726
|
-
'bumpMap',
|
|
727
|
-
'normalMap',
|
|
728
|
-
'displacementMap',
|
|
729
|
-
'roughnessMap',
|
|
730
|
-
'metalnessMap',
|
|
731
|
-
'alphaMap',
|
|
732
|
-
],
|
|
733
|
-
isMeshBasicMaterial: ['map', 'specularMap', 'alphaMap'],
|
|
734
|
-
isMToonMaterial: [
|
|
735
|
-
'map',
|
|
736
|
-
'normalMap',
|
|
737
|
-
'emissiveMap',
|
|
738
|
-
'shadeMultiplyTexture',
|
|
739
|
-
'rimMultiplyTexture',
|
|
740
|
-
'outlineWidthMultiplyTexture',
|
|
741
|
-
'uvAnimationMaskTexture',
|
|
742
|
-
],
|
|
659
|
+
const _v2 = new THREE__namespace.Vector2();
|
|
660
|
+
/**
|
|
661
|
+
* A bind of expression influences to texture transforms.
|
|
662
|
+
*/
|
|
663
|
+
class VRMExpressionTextureTransformBind {
|
|
664
|
+
constructor({ material, scale, offset, }) {
|
|
665
|
+
var _a, _b;
|
|
666
|
+
this.material = material;
|
|
667
|
+
this.scale = scale;
|
|
668
|
+
this.offset = offset;
|
|
669
|
+
const propertyNames = (_a = Object.entries(VRMExpressionTextureTransformBind._propertyNamesMap).find(([distinguisher]) => {
|
|
670
|
+
return material[distinguisher] === true;
|
|
671
|
+
})) === null || _a === void 0 ? void 0 : _a[1];
|
|
672
|
+
if (propertyNames == null) {
|
|
673
|
+
console.warn(`Tried to add a texture transform bind to the material ${(_b = material.name) !== null && _b !== void 0 ? _b : '(no name)'} but the material is not supported.`);
|
|
674
|
+
this._properties = [];
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
this._properties = [];
|
|
678
|
+
propertyNames.forEach((propertyName) => {
|
|
679
|
+
var _a;
|
|
680
|
+
const texture = (_a = material[propertyName]) === null || _a === void 0 ? void 0 : _a.clone();
|
|
681
|
+
if (!texture) {
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
material[propertyName] = texture; // because the texture is cloned
|
|
685
|
+
const initialOffset = texture.offset.clone();
|
|
686
|
+
const initialScale = texture.repeat.clone();
|
|
687
|
+
const deltaOffset = offset.clone().sub(initialOffset);
|
|
688
|
+
const deltaScale = scale.clone().sub(initialScale);
|
|
689
|
+
this._properties.push({
|
|
690
|
+
name: propertyName,
|
|
691
|
+
initialOffset,
|
|
692
|
+
deltaOffset,
|
|
693
|
+
initialScale,
|
|
694
|
+
deltaScale,
|
|
695
|
+
});
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
applyWeight(weight) {
|
|
700
|
+
this._properties.forEach((property) => {
|
|
701
|
+
const target = this.material[property.name];
|
|
702
|
+
if (target === undefined) {
|
|
703
|
+
return;
|
|
704
|
+
} // TODO: we should kick this at `addMaterialValue`
|
|
705
|
+
target.offset.add(_v2.copy(property.deltaOffset).multiplyScalar(weight));
|
|
706
|
+
target.repeat.add(_v2.copy(property.deltaScale).multiplyScalar(weight));
|
|
707
|
+
target.needsUpdate = true;
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
clearAppliedWeight() {
|
|
711
|
+
this._properties.forEach((property) => {
|
|
712
|
+
const target = this.material[property.name];
|
|
713
|
+
if (target === undefined) {
|
|
714
|
+
return;
|
|
715
|
+
} // TODO: we should kick this at `addMaterialValue`
|
|
716
|
+
target.offset.copy(property.initialOffset);
|
|
717
|
+
target.repeat.copy(property.initialScale);
|
|
718
|
+
target.needsUpdate = true;
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
VRMExpressionTextureTransformBind._propertyNamesMap = {
|
|
723
|
+
isMeshStandardMaterial: [
|
|
724
|
+
'map',
|
|
725
|
+
'emissiveMap',
|
|
726
|
+
'bumpMap',
|
|
727
|
+
'normalMap',
|
|
728
|
+
'displacementMap',
|
|
729
|
+
'roughnessMap',
|
|
730
|
+
'metalnessMap',
|
|
731
|
+
'alphaMap',
|
|
732
|
+
],
|
|
733
|
+
isMeshBasicMaterial: ['map', 'specularMap', 'alphaMap'],
|
|
734
|
+
isMToonMaterial: [
|
|
735
|
+
'map',
|
|
736
|
+
'normalMap',
|
|
737
|
+
'emissiveMap',
|
|
738
|
+
'shadeMultiplyTexture',
|
|
739
|
+
'rimMultiplyTexture',
|
|
740
|
+
'outlineWidthMultiplyTexture',
|
|
741
|
+
'uvAnimationMaskTexture',
|
|
742
|
+
],
|
|
743
743
|
};
|
|
744
744
|
|
|
745
|
-
/**
|
|
746
|
-
* A plugin of GLTFLoader that imports a {@link VRMExpressionManager} from a VRM extension of a GLTF.
|
|
747
|
-
*/
|
|
748
|
-
class VRMExpressionLoaderPlugin {
|
|
749
|
-
constructor(parser) {
|
|
750
|
-
this.parser = parser;
|
|
751
|
-
}
|
|
752
|
-
get name() {
|
|
753
|
-
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
754
|
-
return 'VRMExpressionLoaderPlugin';
|
|
755
|
-
}
|
|
756
|
-
afterRoot(gltf) {
|
|
757
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
758
|
-
gltf.userData.vrmExpressionManager = yield this._import(gltf);
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* Import a {@link VRMExpressionManager} from a VRM.
|
|
763
|
-
*
|
|
764
|
-
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
|
765
|
-
*/
|
|
766
|
-
_import(gltf) {
|
|
767
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
768
|
-
const v1Result = yield this._v1Import(gltf);
|
|
769
|
-
if (v1Result) {
|
|
770
|
-
return v1Result;
|
|
771
|
-
}
|
|
772
|
-
const v0Result = yield this._v0Import(gltf);
|
|
773
|
-
if (v0Result) {
|
|
774
|
-
return v0Result;
|
|
775
|
-
}
|
|
776
|
-
return null;
|
|
777
|
-
});
|
|
778
|
-
}
|
|
779
|
-
_v1Import(gltf) {
|
|
780
|
-
var _a, _b;
|
|
781
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
782
|
-
const json = this.parser.json;
|
|
783
|
-
// early abort if it doesn't use vrm
|
|
784
|
-
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
785
|
-
if (!isVRMUsed) {
|
|
786
|
-
return null;
|
|
787
|
-
}
|
|
788
|
-
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
789
|
-
if (!extension) {
|
|
790
|
-
return null;
|
|
791
|
-
}
|
|
792
|
-
const specVersion = extension.specVersion;
|
|
793
|
-
if (specVersion !== '1.0-beta') {
|
|
794
|
-
return null;
|
|
795
|
-
}
|
|
796
|
-
const schemaExpressions = extension.expressions;
|
|
797
|
-
if (!schemaExpressions) {
|
|
798
|
-
return null;
|
|
799
|
-
}
|
|
800
|
-
// list expressions
|
|
801
|
-
const presetNameSet = new Set(Object.values(VRMExpressionPresetName));
|
|
802
|
-
const nameSchemaExpressionMap = new Map();
|
|
803
|
-
if (schemaExpressions.preset != null) {
|
|
804
|
-
Object.entries(schemaExpressions.preset).forEach(([name, schemaExpression]) => {
|
|
805
|
-
if (schemaExpression == null) {
|
|
806
|
-
return;
|
|
807
|
-
} // typescript
|
|
808
|
-
if (!presetNameSet.has(name)) {
|
|
809
|
-
console.warn(`VRMExpressionLoaderPlugin: Unknown preset name "${name}" detected. Ignoring the expression`);
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
nameSchemaExpressionMap.set(name, schemaExpression);
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
if (schemaExpressions.custom != null) {
|
|
816
|
-
Object.entries(schemaExpressions.custom).forEach(([name, schemaExpression]) => {
|
|
817
|
-
if (presetNameSet.has(name)) {
|
|
818
|
-
console.warn(`VRMExpressionLoaderPlugin: Custom expression cannot have preset name "${name}". Ignoring the expression`);
|
|
819
|
-
return;
|
|
820
|
-
}
|
|
821
|
-
nameSchemaExpressionMap.set(name, schemaExpression);
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
// prepare manager
|
|
825
|
-
const manager = new VRMExpressionManager();
|
|
826
|
-
// load expressions
|
|
827
|
-
yield Promise.all(Array.from(nameSchemaExpressionMap.entries()).map(([name, schemaExpression]) => __awaiter(this, void 0, void 0, function* () {
|
|
828
|
-
var _c, _d, _e, _f, _g, _h, _j;
|
|
829
|
-
const expression = new VRMExpression(name);
|
|
830
|
-
gltf.scene.add(expression);
|
|
831
|
-
expression.isBinary = (_c = schemaExpression.isBinary) !== null && _c !== void 0 ? _c : false;
|
|
832
|
-
expression.overrideBlink = (_d = schemaExpression.overrideBlink) !== null && _d !== void 0 ? _d : 'none';
|
|
833
|
-
expression.overrideLookAt = (_e = schemaExpression.overrideLookAt) !== null && _e !== void 0 ? _e : 'none';
|
|
834
|
-
expression.overrideMouth = (_f = schemaExpression.overrideMouth) !== null && _f !== void 0 ? _f : 'none';
|
|
835
|
-
(_g = schemaExpression.morphTargetBinds) === null || _g === void 0 ? void 0 : _g.forEach((bind) => __awaiter(this, void 0, void 0, function* () {
|
|
836
|
-
var _k;
|
|
837
|
-
if (bind.node === undefined || bind.index === undefined) {
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
const primitives = (yield gltfExtractPrimitivesFromNode(gltf, bind.node));
|
|
841
|
-
const morphTargetIndex = bind.index;
|
|
842
|
-
// check if the mesh has the target morph target
|
|
843
|
-
if (!primitives.every((primitive) => Array.isArray(primitive.morphTargetInfluences) &&
|
|
844
|
-
morphTargetIndex < primitive.morphTargetInfluences.length)) {
|
|
845
|
-
console.warn(`VRMExpressionLoaderPlugin: ${schemaExpression.name} attempts to index morph #${morphTargetIndex} but not found.`);
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
expression.addBind(new VRMExpressionMorphTargetBind({
|
|
849
|
-
primitives,
|
|
850
|
-
index: morphTargetIndex,
|
|
851
|
-
weight: (_k = bind.weight) !== null && _k !== void 0 ? _k : 1.0,
|
|
852
|
-
}));
|
|
853
|
-
}));
|
|
854
|
-
if (schemaExpression.materialColorBinds || schemaExpression.textureTransformBinds) {
|
|
855
|
-
// list up every material in `gltf.scene`
|
|
856
|
-
const gltfMaterials = [];
|
|
857
|
-
gltf.scene.traverse((object) => {
|
|
858
|
-
const material = object.material;
|
|
859
|
-
if (material) {
|
|
860
|
-
gltfMaterials.push(material);
|
|
861
|
-
}
|
|
862
|
-
});
|
|
863
|
-
(_h = schemaExpression.materialColorBinds) === null || _h === void 0 ? void 0 : _h.forEach((bind) => __awaiter(this, void 0, void 0, function* () {
|
|
864
|
-
const materials = gltfMaterials.filter((material) => {
|
|
865
|
-
const materialIndex = gltfGetAssociatedMaterialIndex(this.parser, material);
|
|
866
|
-
return bind.material === materialIndex;
|
|
867
|
-
});
|
|
868
|
-
materials.forEach((material) => {
|
|
869
|
-
expression.addBind(new VRMExpressionMaterialColorBind({
|
|
870
|
-
material,
|
|
871
|
-
type: bind.type,
|
|
872
|
-
targetValue: new THREE__namespace.Color().fromArray(bind.targetValue),
|
|
873
|
-
}));
|
|
874
|
-
});
|
|
875
|
-
}));
|
|
876
|
-
(_j = schemaExpression.textureTransformBinds) === null || _j === void 0 ? void 0 : _j.forEach((bind) => __awaiter(this, void 0, void 0, function* () {
|
|
877
|
-
const materials = gltfMaterials.filter((material) => {
|
|
878
|
-
const materialIndex = gltfGetAssociatedMaterialIndex(this.parser, material);
|
|
879
|
-
return bind.material === materialIndex;
|
|
880
|
-
});
|
|
881
|
-
materials.forEach((material) => {
|
|
882
|
-
var _a, _b;
|
|
883
|
-
expression.addBind(new VRMExpressionTextureTransformBind({
|
|
884
|
-
material,
|
|
885
|
-
offset: new THREE__namespace.Vector2().fromArray((_a = bind.offset) !== null && _a !== void 0 ? _a : [0.0, 0.0]),
|
|
886
|
-
scale: new THREE__namespace.Vector2().fromArray((_b = bind.scale) !== null && _b !== void 0 ? _b : [1.0, 1.0]),
|
|
887
|
-
}));
|
|
888
|
-
});
|
|
889
|
-
}));
|
|
890
|
-
}
|
|
891
|
-
manager.registerExpression(expression);
|
|
892
|
-
})));
|
|
893
|
-
return manager;
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
_v0Import(gltf) {
|
|
897
|
-
var _a;
|
|
898
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
899
|
-
const json = this.parser.json;
|
|
900
|
-
// early abort if it doesn't use vrm
|
|
901
|
-
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
902
|
-
if (!vrmExt) {
|
|
903
|
-
return null;
|
|
904
|
-
}
|
|
905
|
-
const schemaBlendShape = vrmExt.blendShapeMaster;
|
|
906
|
-
if (!schemaBlendShape) {
|
|
907
|
-
return null;
|
|
908
|
-
}
|
|
909
|
-
const manager = new VRMExpressionManager();
|
|
910
|
-
const schemaBlendShapeGroups = schemaBlendShape.blendShapeGroups;
|
|
911
|
-
if (!schemaBlendShapeGroups) {
|
|
912
|
-
return manager;
|
|
913
|
-
}
|
|
914
|
-
const blendShapeNameSet = new Set();
|
|
915
|
-
yield Promise.all(schemaBlendShapeGroups.map((schemaGroup) => __awaiter(this, void 0, void 0, function* () {
|
|
916
|
-
var _b;
|
|
917
|
-
const v0PresetName = schemaGroup.presetName;
|
|
918
|
-
const v1PresetName = (v0PresetName != null && VRMExpressionLoaderPlugin.v0v1PresetNameMap[v0PresetName]) || null;
|
|
919
|
-
const name = v1PresetName !== null && v1PresetName !== void 0 ? v1PresetName : schemaGroup.name;
|
|
920
|
-
if (name == null) {
|
|
921
|
-
console.warn('VRMExpressionLoaderPlugin: One of custom expressions has no name. Ignoring the expression');
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
|
-
// duplication check
|
|
925
|
-
if (blendShapeNameSet.has(name)) {
|
|
926
|
-
console.warn(`VRMExpressionLoaderPlugin: An expression preset ${v0PresetName} has duplicated entries. Ignoring the expression`);
|
|
927
|
-
return;
|
|
928
|
-
}
|
|
929
|
-
blendShapeNameSet.add(name);
|
|
930
|
-
const expression = new VRMExpression(name);
|
|
931
|
-
gltf.scene.add(expression);
|
|
932
|
-
expression.isBinary = (_b = schemaGroup.isBinary) !== null && _b !== void 0 ? _b : false;
|
|
933
|
-
// v0 doesn't have ignore properties
|
|
934
|
-
if (schemaGroup.binds) {
|
|
935
|
-
schemaGroup.binds.forEach((bind) => __awaiter(this, void 0, void 0, function* () {
|
|
936
|
-
var _c;
|
|
937
|
-
if (bind.mesh === undefined || bind.index === undefined) {
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
const nodesUsingMesh = [];
|
|
941
|
-
(_c = json.nodes) === null || _c === void 0 ? void 0 : _c.forEach((node, i) => {
|
|
942
|
-
if (node.mesh === bind.mesh) {
|
|
943
|
-
nodesUsingMesh.push(i);
|
|
944
|
-
}
|
|
945
|
-
});
|
|
946
|
-
const morphTargetIndex = bind.index;
|
|
947
|
-
yield Promise.all(nodesUsingMesh.map((nodeIndex) => __awaiter(this, void 0, void 0, function* () {
|
|
948
|
-
var _d;
|
|
949
|
-
const primitives = (yield gltfExtractPrimitivesFromNode(gltf, nodeIndex));
|
|
950
|
-
// check if the mesh has the target morph target
|
|
951
|
-
if (!primitives.every((primitive) => Array.isArray(primitive.morphTargetInfluences) &&
|
|
952
|
-
morphTargetIndex < primitive.morphTargetInfluences.length)) {
|
|
953
|
-
console.warn(`VRMExpressionLoaderPlugin: ${schemaGroup.name} attempts to index ${morphTargetIndex}th morph but not found.`);
|
|
954
|
-
return;
|
|
955
|
-
}
|
|
956
|
-
expression.addBind(new VRMExpressionMorphTargetBind({
|
|
957
|
-
primitives,
|
|
958
|
-
index: morphTargetIndex,
|
|
959
|
-
weight: 0.01 * ((_d = bind.weight) !== null && _d !== void 0 ? _d : 100),
|
|
960
|
-
}));
|
|
961
|
-
})));
|
|
962
|
-
}));
|
|
963
|
-
}
|
|
964
|
-
const materialValues = schemaGroup.materialValues;
|
|
965
|
-
if (materialValues && materialValues.length !== 0) {
|
|
966
|
-
console.warn('Material binds of VRM 0.0 are not supported. Setup the model in VRM 1.0 and try again');
|
|
967
|
-
}
|
|
968
|
-
manager.registerExpression(expression);
|
|
969
|
-
})));
|
|
970
|
-
return manager;
|
|
971
|
-
});
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
VRMExpressionLoaderPlugin.v0v1PresetNameMap = {
|
|
975
|
-
a: 'aa',
|
|
976
|
-
e: 'ee',
|
|
977
|
-
i: 'ih',
|
|
978
|
-
o: 'oh',
|
|
979
|
-
u: 'ou',
|
|
980
|
-
blink: 'blink',
|
|
981
|
-
joy: 'happy',
|
|
982
|
-
angry: 'angry',
|
|
983
|
-
sorrow: 'sad',
|
|
984
|
-
fun: 'relaxed',
|
|
985
|
-
lookup: 'lookUp',
|
|
986
|
-
lookdown: 'lookDown',
|
|
987
|
-
lookleft: 'lookLeft',
|
|
988
|
-
lookright: 'lookRight',
|
|
989
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
990
|
-
blink_l: 'blinkLeft',
|
|
991
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
992
|
-
blink_r: 'blinkRight',
|
|
993
|
-
neutral: 'neutral',
|
|
745
|
+
/**
|
|
746
|
+
* A plugin of GLTFLoader that imports a {@link VRMExpressionManager} from a VRM extension of a GLTF.
|
|
747
|
+
*/
|
|
748
|
+
class VRMExpressionLoaderPlugin {
|
|
749
|
+
constructor(parser) {
|
|
750
|
+
this.parser = parser;
|
|
751
|
+
}
|
|
752
|
+
get name() {
|
|
753
|
+
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
754
|
+
return 'VRMExpressionLoaderPlugin';
|
|
755
|
+
}
|
|
756
|
+
afterRoot(gltf) {
|
|
757
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
758
|
+
gltf.userData.vrmExpressionManager = yield this._import(gltf);
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Import a {@link VRMExpressionManager} from a VRM.
|
|
763
|
+
*
|
|
764
|
+
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
|
765
|
+
*/
|
|
766
|
+
_import(gltf) {
|
|
767
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
768
|
+
const v1Result = yield this._v1Import(gltf);
|
|
769
|
+
if (v1Result) {
|
|
770
|
+
return v1Result;
|
|
771
|
+
}
|
|
772
|
+
const v0Result = yield this._v0Import(gltf);
|
|
773
|
+
if (v0Result) {
|
|
774
|
+
return v0Result;
|
|
775
|
+
}
|
|
776
|
+
return null;
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
_v1Import(gltf) {
|
|
780
|
+
var _a, _b;
|
|
781
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
782
|
+
const json = this.parser.json;
|
|
783
|
+
// early abort if it doesn't use vrm
|
|
784
|
+
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
785
|
+
if (!isVRMUsed) {
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
789
|
+
if (!extension) {
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
const specVersion = extension.specVersion;
|
|
793
|
+
if (specVersion !== '1.0-beta') {
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
const schemaExpressions = extension.expressions;
|
|
797
|
+
if (!schemaExpressions) {
|
|
798
|
+
return null;
|
|
799
|
+
}
|
|
800
|
+
// list expressions
|
|
801
|
+
const presetNameSet = new Set(Object.values(VRMExpressionPresetName));
|
|
802
|
+
const nameSchemaExpressionMap = new Map();
|
|
803
|
+
if (schemaExpressions.preset != null) {
|
|
804
|
+
Object.entries(schemaExpressions.preset).forEach(([name, schemaExpression]) => {
|
|
805
|
+
if (schemaExpression == null) {
|
|
806
|
+
return;
|
|
807
|
+
} // typescript
|
|
808
|
+
if (!presetNameSet.has(name)) {
|
|
809
|
+
console.warn(`VRMExpressionLoaderPlugin: Unknown preset name "${name}" detected. Ignoring the expression`);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
nameSchemaExpressionMap.set(name, schemaExpression);
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
if (schemaExpressions.custom != null) {
|
|
816
|
+
Object.entries(schemaExpressions.custom).forEach(([name, schemaExpression]) => {
|
|
817
|
+
if (presetNameSet.has(name)) {
|
|
818
|
+
console.warn(`VRMExpressionLoaderPlugin: Custom expression cannot have preset name "${name}". Ignoring the expression`);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
nameSchemaExpressionMap.set(name, schemaExpression);
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
// prepare manager
|
|
825
|
+
const manager = new VRMExpressionManager();
|
|
826
|
+
// load expressions
|
|
827
|
+
yield Promise.all(Array.from(nameSchemaExpressionMap.entries()).map(([name, schemaExpression]) => __awaiter(this, void 0, void 0, function* () {
|
|
828
|
+
var _c, _d, _e, _f, _g, _h, _j;
|
|
829
|
+
const expression = new VRMExpression(name);
|
|
830
|
+
gltf.scene.add(expression);
|
|
831
|
+
expression.isBinary = (_c = schemaExpression.isBinary) !== null && _c !== void 0 ? _c : false;
|
|
832
|
+
expression.overrideBlink = (_d = schemaExpression.overrideBlink) !== null && _d !== void 0 ? _d : 'none';
|
|
833
|
+
expression.overrideLookAt = (_e = schemaExpression.overrideLookAt) !== null && _e !== void 0 ? _e : 'none';
|
|
834
|
+
expression.overrideMouth = (_f = schemaExpression.overrideMouth) !== null && _f !== void 0 ? _f : 'none';
|
|
835
|
+
(_g = schemaExpression.morphTargetBinds) === null || _g === void 0 ? void 0 : _g.forEach((bind) => __awaiter(this, void 0, void 0, function* () {
|
|
836
|
+
var _k;
|
|
837
|
+
if (bind.node === undefined || bind.index === undefined) {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
const primitives = (yield gltfExtractPrimitivesFromNode(gltf, bind.node));
|
|
841
|
+
const morphTargetIndex = bind.index;
|
|
842
|
+
// check if the mesh has the target morph target
|
|
843
|
+
if (!primitives.every((primitive) => Array.isArray(primitive.morphTargetInfluences) &&
|
|
844
|
+
morphTargetIndex < primitive.morphTargetInfluences.length)) {
|
|
845
|
+
console.warn(`VRMExpressionLoaderPlugin: ${schemaExpression.name} attempts to index morph #${morphTargetIndex} but not found.`);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
expression.addBind(new VRMExpressionMorphTargetBind({
|
|
849
|
+
primitives,
|
|
850
|
+
index: morphTargetIndex,
|
|
851
|
+
weight: (_k = bind.weight) !== null && _k !== void 0 ? _k : 1.0,
|
|
852
|
+
}));
|
|
853
|
+
}));
|
|
854
|
+
if (schemaExpression.materialColorBinds || schemaExpression.textureTransformBinds) {
|
|
855
|
+
// list up every material in `gltf.scene`
|
|
856
|
+
const gltfMaterials = [];
|
|
857
|
+
gltf.scene.traverse((object) => {
|
|
858
|
+
const material = object.material;
|
|
859
|
+
if (material) {
|
|
860
|
+
gltfMaterials.push(material);
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
(_h = schemaExpression.materialColorBinds) === null || _h === void 0 ? void 0 : _h.forEach((bind) => __awaiter(this, void 0, void 0, function* () {
|
|
864
|
+
const materials = gltfMaterials.filter((material) => {
|
|
865
|
+
const materialIndex = gltfGetAssociatedMaterialIndex(this.parser, material);
|
|
866
|
+
return bind.material === materialIndex;
|
|
867
|
+
});
|
|
868
|
+
materials.forEach((material) => {
|
|
869
|
+
expression.addBind(new VRMExpressionMaterialColorBind({
|
|
870
|
+
material,
|
|
871
|
+
type: bind.type,
|
|
872
|
+
targetValue: new THREE__namespace.Color().fromArray(bind.targetValue),
|
|
873
|
+
}));
|
|
874
|
+
});
|
|
875
|
+
}));
|
|
876
|
+
(_j = schemaExpression.textureTransformBinds) === null || _j === void 0 ? void 0 : _j.forEach((bind) => __awaiter(this, void 0, void 0, function* () {
|
|
877
|
+
const materials = gltfMaterials.filter((material) => {
|
|
878
|
+
const materialIndex = gltfGetAssociatedMaterialIndex(this.parser, material);
|
|
879
|
+
return bind.material === materialIndex;
|
|
880
|
+
});
|
|
881
|
+
materials.forEach((material) => {
|
|
882
|
+
var _a, _b;
|
|
883
|
+
expression.addBind(new VRMExpressionTextureTransformBind({
|
|
884
|
+
material,
|
|
885
|
+
offset: new THREE__namespace.Vector2().fromArray((_a = bind.offset) !== null && _a !== void 0 ? _a : [0.0, 0.0]),
|
|
886
|
+
scale: new THREE__namespace.Vector2().fromArray((_b = bind.scale) !== null && _b !== void 0 ? _b : [1.0, 1.0]),
|
|
887
|
+
}));
|
|
888
|
+
});
|
|
889
|
+
}));
|
|
890
|
+
}
|
|
891
|
+
manager.registerExpression(expression);
|
|
892
|
+
})));
|
|
893
|
+
return manager;
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
_v0Import(gltf) {
|
|
897
|
+
var _a;
|
|
898
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
899
|
+
const json = this.parser.json;
|
|
900
|
+
// early abort if it doesn't use vrm
|
|
901
|
+
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
902
|
+
if (!vrmExt) {
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
const schemaBlendShape = vrmExt.blendShapeMaster;
|
|
906
|
+
if (!schemaBlendShape) {
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
909
|
+
const manager = new VRMExpressionManager();
|
|
910
|
+
const schemaBlendShapeGroups = schemaBlendShape.blendShapeGroups;
|
|
911
|
+
if (!schemaBlendShapeGroups) {
|
|
912
|
+
return manager;
|
|
913
|
+
}
|
|
914
|
+
const blendShapeNameSet = new Set();
|
|
915
|
+
yield Promise.all(schemaBlendShapeGroups.map((schemaGroup) => __awaiter(this, void 0, void 0, function* () {
|
|
916
|
+
var _b;
|
|
917
|
+
const v0PresetName = schemaGroup.presetName;
|
|
918
|
+
const v1PresetName = (v0PresetName != null && VRMExpressionLoaderPlugin.v0v1PresetNameMap[v0PresetName]) || null;
|
|
919
|
+
const name = v1PresetName !== null && v1PresetName !== void 0 ? v1PresetName : schemaGroup.name;
|
|
920
|
+
if (name == null) {
|
|
921
|
+
console.warn('VRMExpressionLoaderPlugin: One of custom expressions has no name. Ignoring the expression');
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
// duplication check
|
|
925
|
+
if (blendShapeNameSet.has(name)) {
|
|
926
|
+
console.warn(`VRMExpressionLoaderPlugin: An expression preset ${v0PresetName} has duplicated entries. Ignoring the expression`);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
blendShapeNameSet.add(name);
|
|
930
|
+
const expression = new VRMExpression(name);
|
|
931
|
+
gltf.scene.add(expression);
|
|
932
|
+
expression.isBinary = (_b = schemaGroup.isBinary) !== null && _b !== void 0 ? _b : false;
|
|
933
|
+
// v0 doesn't have ignore properties
|
|
934
|
+
if (schemaGroup.binds) {
|
|
935
|
+
schemaGroup.binds.forEach((bind) => __awaiter(this, void 0, void 0, function* () {
|
|
936
|
+
var _c;
|
|
937
|
+
if (bind.mesh === undefined || bind.index === undefined) {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
const nodesUsingMesh = [];
|
|
941
|
+
(_c = json.nodes) === null || _c === void 0 ? void 0 : _c.forEach((node, i) => {
|
|
942
|
+
if (node.mesh === bind.mesh) {
|
|
943
|
+
nodesUsingMesh.push(i);
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
const morphTargetIndex = bind.index;
|
|
947
|
+
yield Promise.all(nodesUsingMesh.map((nodeIndex) => __awaiter(this, void 0, void 0, function* () {
|
|
948
|
+
var _d;
|
|
949
|
+
const primitives = (yield gltfExtractPrimitivesFromNode(gltf, nodeIndex));
|
|
950
|
+
// check if the mesh has the target morph target
|
|
951
|
+
if (!primitives.every((primitive) => Array.isArray(primitive.morphTargetInfluences) &&
|
|
952
|
+
morphTargetIndex < primitive.morphTargetInfluences.length)) {
|
|
953
|
+
console.warn(`VRMExpressionLoaderPlugin: ${schemaGroup.name} attempts to index ${morphTargetIndex}th morph but not found.`);
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
expression.addBind(new VRMExpressionMorphTargetBind({
|
|
957
|
+
primitives,
|
|
958
|
+
index: morphTargetIndex,
|
|
959
|
+
weight: 0.01 * ((_d = bind.weight) !== null && _d !== void 0 ? _d : 100),
|
|
960
|
+
}));
|
|
961
|
+
})));
|
|
962
|
+
}));
|
|
963
|
+
}
|
|
964
|
+
const materialValues = schemaGroup.materialValues;
|
|
965
|
+
if (materialValues && materialValues.length !== 0) {
|
|
966
|
+
console.warn('Material binds of VRM 0.0 are not supported. Setup the model in VRM 1.0 and try again');
|
|
967
|
+
}
|
|
968
|
+
manager.registerExpression(expression);
|
|
969
|
+
})));
|
|
970
|
+
return manager;
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
VRMExpressionLoaderPlugin.v0v1PresetNameMap = {
|
|
975
|
+
a: 'aa',
|
|
976
|
+
e: 'ee',
|
|
977
|
+
i: 'ih',
|
|
978
|
+
o: 'oh',
|
|
979
|
+
u: 'ou',
|
|
980
|
+
blink: 'blink',
|
|
981
|
+
joy: 'happy',
|
|
982
|
+
angry: 'angry',
|
|
983
|
+
sorrow: 'sad',
|
|
984
|
+
fun: 'relaxed',
|
|
985
|
+
lookup: 'lookUp',
|
|
986
|
+
lookdown: 'lookDown',
|
|
987
|
+
lookleft: 'lookLeft',
|
|
988
|
+
lookright: 'lookRight',
|
|
989
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
990
|
+
blink_l: 'blinkLeft',
|
|
991
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
992
|
+
blink_r: 'blinkRight',
|
|
993
|
+
neutral: 'neutral',
|
|
994
994
|
};
|
|
995
995
|
|
|
996
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
997
|
-
const VRMExpressionMaterialColorType = {
|
|
998
|
-
Color: 'color',
|
|
999
|
-
EmissionColor: 'emissionColor',
|
|
1000
|
-
ShadeColor: 'shadeColor',
|
|
1001
|
-
RimColor: 'rimColor',
|
|
1002
|
-
OutlineColor: 'outlineColor',
|
|
996
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
997
|
+
const VRMExpressionMaterialColorType = {
|
|
998
|
+
Color: 'color',
|
|
999
|
+
EmissionColor: 'emissionColor',
|
|
1000
|
+
ShadeColor: 'shadeColor',
|
|
1001
|
+
RimColor: 'rimColor',
|
|
1002
|
+
OutlineColor: 'outlineColor',
|
|
1003
1003
|
};
|
|
1004
1004
|
|
|
1005
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1006
|
-
const VRMExpressionOverrideType = {
|
|
1007
|
-
None: 'none',
|
|
1008
|
-
Block: 'block',
|
|
1009
|
-
Blend: 'blend',
|
|
1005
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1006
|
+
const VRMExpressionOverrideType = {
|
|
1007
|
+
None: 'none',
|
|
1008
|
+
Block: 'block',
|
|
1009
|
+
Blend: 'blend',
|
|
1010
1010
|
};
|
|
1011
1011
|
|
|
1012
|
-
class VRMFirstPerson {
|
|
1013
|
-
/**
|
|
1014
|
-
* Create a new VRMFirstPerson object.
|
|
1015
|
-
*
|
|
1016
|
-
* @param humanoid A {@link VRMHumanoid}
|
|
1017
|
-
* @param meshAnnotations A renderer settings. See the description of [[RendererFirstPersonFlags]] for more info
|
|
1018
|
-
*/
|
|
1019
|
-
constructor(humanoid, meshAnnotations) {
|
|
1020
|
-
this._firstPersonOnlyLayer = VRMFirstPerson.DEFAULT_FIRSTPERSON_ONLY_LAYER;
|
|
1021
|
-
this._thirdPersonOnlyLayer = VRMFirstPerson.DEFAULT_THIRDPERSON_ONLY_LAYER;
|
|
1022
|
-
this._initializedLayers = false;
|
|
1023
|
-
this.humanoid = humanoid;
|
|
1024
|
-
this.meshAnnotations = meshAnnotations;
|
|
1025
|
-
}
|
|
1026
|
-
/**
|
|
1027
|
-
* Copy the given {@link VRMFirstPerson} into this one.
|
|
1028
|
-
* {@link humanoid} must be same as the source one.
|
|
1029
|
-
* @param source The {@link VRMFirstPerson} you want to copy
|
|
1030
|
-
* @returns this
|
|
1031
|
-
*/
|
|
1032
|
-
copy(source) {
|
|
1033
|
-
if (this.humanoid !== source.humanoid) {
|
|
1034
|
-
throw new Error('VRMFirstPerson: humanoid must be same in order to copy');
|
|
1035
|
-
}
|
|
1036
|
-
this.meshAnnotations = source.meshAnnotations.map((annotation) => ({
|
|
1037
|
-
meshes: annotation.meshes.concat(),
|
|
1038
|
-
type: annotation.type,
|
|
1039
|
-
}));
|
|
1040
|
-
return this;
|
|
1041
|
-
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Returns a clone of this {@link VRMFirstPerson}.
|
|
1044
|
-
* @returns Copied {@link VRMFirstPerson}
|
|
1045
|
-
*/
|
|
1046
|
-
clone() {
|
|
1047
|
-
return new VRMFirstPerson(this.humanoid, this.meshAnnotations).copy(this);
|
|
1048
|
-
}
|
|
1049
|
-
/**
|
|
1050
|
-
* A camera layer represents `FirstPersonOnly` layer.
|
|
1051
|
-
* Note that **you must call {@link setup} first before you use the layer feature** or it does not work properly.
|
|
1052
|
-
*
|
|
1053
|
-
* The value is {@link DEFAULT_FIRSTPERSON_ONLY_LAYER} by default but you can change the layer by specifying via {@link setup} if you prefer.
|
|
1054
|
-
*
|
|
1055
|
-
* @see https://vrm.dev/en/univrm/api/univrm_use_firstperson/
|
|
1056
|
-
* @see https://threejs.org/docs/#api/en/core/Layers
|
|
1057
|
-
*/
|
|
1058
|
-
get firstPersonOnlyLayer() {
|
|
1059
|
-
return this._firstPersonOnlyLayer;
|
|
1060
|
-
}
|
|
1061
|
-
/**
|
|
1062
|
-
* A camera layer represents `ThirdPersonOnly` layer.
|
|
1063
|
-
* Note that **you must call {@link setup} first before you use the layer feature** or it does not work properly.
|
|
1064
|
-
*
|
|
1065
|
-
* The value is {@link DEFAULT_THIRDPERSON_ONLY_LAYER} by default but you can change the layer by specifying via {@link setup} if you prefer.
|
|
1066
|
-
*
|
|
1067
|
-
* @see https://vrm.dev/en/univrm/api/univrm_use_firstperson/
|
|
1068
|
-
* @see https://threejs.org/docs/#api/en/core/Layers
|
|
1069
|
-
*/
|
|
1070
|
-
get thirdPersonOnlyLayer() {
|
|
1071
|
-
return this._thirdPersonOnlyLayer;
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* In this method, it assigns layers for every meshes based on mesh annotations.
|
|
1075
|
-
* You must call this method first before you use the layer feature.
|
|
1076
|
-
*
|
|
1077
|
-
* 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.
|
|
1078
|
-
*
|
|
1079
|
-
* The `cameraLayer` parameter specifies which layer will be assigned for `FirstPersonOnly` / `ThirdPersonOnly`.
|
|
1080
|
-
* In UniVRM, we specified those by naming each desired layer as `FIRSTPERSON_ONLY_LAYER` / `THIRDPERSON_ONLY_LAYER`
|
|
1081
|
-
* but we are going to specify these layers at here since we are unable to name layers in Three.js.
|
|
1082
|
-
*
|
|
1083
|
-
* @param cameraLayer Specify which layer will be for `FirstPersonOnly` / `ThirdPersonOnly`.
|
|
1084
|
-
*/
|
|
1085
|
-
setup({ firstPersonOnlyLayer = VRMFirstPerson.DEFAULT_FIRSTPERSON_ONLY_LAYER, thirdPersonOnlyLayer = VRMFirstPerson.DEFAULT_THIRDPERSON_ONLY_LAYER, } = {}) {
|
|
1086
|
-
if (this._initializedLayers) {
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
this._firstPersonOnlyLayer = firstPersonOnlyLayer;
|
|
1090
|
-
this._thirdPersonOnlyLayer = thirdPersonOnlyLayer;
|
|
1091
|
-
this.meshAnnotations.forEach((item) => {
|
|
1092
|
-
item.meshes.forEach((mesh) => {
|
|
1093
|
-
if (item.type === 'firstPersonOnly') {
|
|
1094
|
-
mesh.layers.set(this._firstPersonOnlyLayer);
|
|
1095
|
-
mesh.traverse((child) => child.layers.set(this._firstPersonOnlyLayer));
|
|
1096
|
-
}
|
|
1097
|
-
else if (item.type === 'thirdPersonOnly') {
|
|
1098
|
-
mesh.layers.set(this._thirdPersonOnlyLayer);
|
|
1099
|
-
mesh.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer));
|
|
1100
|
-
}
|
|
1101
|
-
else if (item.type === 'auto') {
|
|
1102
|
-
this._createHeadlessModel(mesh);
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
});
|
|
1106
|
-
this._initializedLayers = true;
|
|
1107
|
-
}
|
|
1108
|
-
_excludeTriangles(triangles, bws, skinIndex, exclude) {
|
|
1109
|
-
let count = 0;
|
|
1110
|
-
if (bws != null && bws.length > 0) {
|
|
1111
|
-
for (let i = 0; i < triangles.length; i += 3) {
|
|
1112
|
-
const a = triangles[i];
|
|
1113
|
-
const b = triangles[i + 1];
|
|
1114
|
-
const c = triangles[i + 2];
|
|
1115
|
-
const bw0 = bws[a];
|
|
1116
|
-
const skin0 = skinIndex[a];
|
|
1117
|
-
if (bw0[0] > 0 && exclude.includes(skin0[0]))
|
|
1118
|
-
continue;
|
|
1119
|
-
if (bw0[1] > 0 && exclude.includes(skin0[1]))
|
|
1120
|
-
continue;
|
|
1121
|
-
if (bw0[2] > 0 && exclude.includes(skin0[2]))
|
|
1122
|
-
continue;
|
|
1123
|
-
if (bw0[3] > 0 && exclude.includes(skin0[3]))
|
|
1124
|
-
continue;
|
|
1125
|
-
const bw1 = bws[b];
|
|
1126
|
-
const skin1 = skinIndex[b];
|
|
1127
|
-
if (bw1[0] > 0 && exclude.includes(skin1[0]))
|
|
1128
|
-
continue;
|
|
1129
|
-
if (bw1[1] > 0 && exclude.includes(skin1[1]))
|
|
1130
|
-
continue;
|
|
1131
|
-
if (bw1[2] > 0 && exclude.includes(skin1[2]))
|
|
1132
|
-
continue;
|
|
1133
|
-
if (bw1[3] > 0 && exclude.includes(skin1[3]))
|
|
1134
|
-
continue;
|
|
1135
|
-
const bw2 = bws[c];
|
|
1136
|
-
const skin2 = skinIndex[c];
|
|
1137
|
-
if (bw2[0] > 0 && exclude.includes(skin2[0]))
|
|
1138
|
-
continue;
|
|
1139
|
-
if (bw2[1] > 0 && exclude.includes(skin2[1]))
|
|
1140
|
-
continue;
|
|
1141
|
-
if (bw2[2] > 0 && exclude.includes(skin2[2]))
|
|
1142
|
-
continue;
|
|
1143
|
-
if (bw2[3] > 0 && exclude.includes(skin2[3]))
|
|
1144
|
-
continue;
|
|
1145
|
-
triangles[count++] = a;
|
|
1146
|
-
triangles[count++] = b;
|
|
1147
|
-
triangles[count++] = c;
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
return count;
|
|
1151
|
-
}
|
|
1152
|
-
_createErasedMesh(src, erasingBonesIndex) {
|
|
1153
|
-
const dst = new THREE__namespace.SkinnedMesh(src.geometry.clone(), src.material);
|
|
1154
|
-
dst.name = `${src.name}(erase)`;
|
|
1155
|
-
dst.frustumCulled = src.frustumCulled;
|
|
1156
|
-
dst.layers.set(this._firstPersonOnlyLayer);
|
|
1157
|
-
const geometry = dst.geometry;
|
|
1158
|
-
const skinIndexAttr = geometry.getAttribute('skinIndex').array;
|
|
1159
|
-
const skinIndex = [];
|
|
1160
|
-
for (let i = 0; i < skinIndexAttr.length; i += 4) {
|
|
1161
|
-
skinIndex.push([skinIndexAttr[i], skinIndexAttr[i + 1], skinIndexAttr[i + 2], skinIndexAttr[i + 3]]);
|
|
1162
|
-
}
|
|
1163
|
-
const skinWeightAttr = geometry.getAttribute('skinWeight').array;
|
|
1164
|
-
const skinWeight = [];
|
|
1165
|
-
for (let i = 0; i < skinWeightAttr.length; i += 4) {
|
|
1166
|
-
skinWeight.push([skinWeightAttr[i], skinWeightAttr[i + 1], skinWeightAttr[i + 2], skinWeightAttr[i + 3]]);
|
|
1167
|
-
}
|
|
1168
|
-
const index = geometry.getIndex();
|
|
1169
|
-
if (!index) {
|
|
1170
|
-
throw new Error("The geometry doesn't have an index buffer");
|
|
1171
|
-
}
|
|
1172
|
-
const oldTriangles = Array.from(index.array);
|
|
1173
|
-
const count = this._excludeTriangles(oldTriangles, skinWeight, skinIndex, erasingBonesIndex);
|
|
1174
|
-
const newTriangle = [];
|
|
1175
|
-
for (let i = 0; i < count; i++) {
|
|
1176
|
-
newTriangle[i] = oldTriangles[i];
|
|
1177
|
-
}
|
|
1178
|
-
geometry.setIndex(newTriangle);
|
|
1179
|
-
// mtoon material includes onBeforeRender. this is unsupported at SkinnedMesh#clone
|
|
1180
|
-
if (src.onBeforeRender) {
|
|
1181
|
-
dst.onBeforeRender = src.onBeforeRender;
|
|
1182
|
-
}
|
|
1183
|
-
dst.bind(new THREE__namespace.Skeleton(src.skeleton.bones, src.skeleton.boneInverses), new THREE__namespace.Matrix4());
|
|
1184
|
-
return dst;
|
|
1185
|
-
}
|
|
1186
|
-
_createHeadlessModelForSkinnedMesh(parent, mesh) {
|
|
1187
|
-
const eraseBoneIndexes = [];
|
|
1188
|
-
mesh.skeleton.bones.forEach((bone, index) => {
|
|
1189
|
-
if (this._isEraseTarget(bone))
|
|
1190
|
-
eraseBoneIndexes.push(index);
|
|
1191
|
-
});
|
|
1192
|
-
// Unlike UniVRM we don't copy mesh if no invisible bone was found
|
|
1193
|
-
if (!eraseBoneIndexes.length) {
|
|
1194
|
-
mesh.layers.enable(this._thirdPersonOnlyLayer);
|
|
1195
|
-
mesh.layers.enable(this._firstPersonOnlyLayer);
|
|
1196
|
-
return;
|
|
1197
|
-
}
|
|
1198
|
-
mesh.layers.set(this._thirdPersonOnlyLayer);
|
|
1199
|
-
const newMesh = this._createErasedMesh(mesh, eraseBoneIndexes);
|
|
1200
|
-
parent.add(newMesh);
|
|
1201
|
-
}
|
|
1202
|
-
_createHeadlessModel(node) {
|
|
1203
|
-
if (node.type === 'Group') {
|
|
1204
|
-
node.layers.set(this._thirdPersonOnlyLayer);
|
|
1205
|
-
if (this._isEraseTarget(node)) {
|
|
1206
|
-
node.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer));
|
|
1207
|
-
}
|
|
1208
|
-
else {
|
|
1209
|
-
const parent = new THREE__namespace.Group();
|
|
1210
|
-
parent.name = `_headless_${node.name}`;
|
|
1211
|
-
parent.layers.set(this._firstPersonOnlyLayer);
|
|
1212
|
-
node.parent.add(parent);
|
|
1213
|
-
node.children
|
|
1214
|
-
.filter((child) => child.type === 'SkinnedMesh')
|
|
1215
|
-
.forEach((child) => {
|
|
1216
|
-
const skinnedMesh = child;
|
|
1217
|
-
this._createHeadlessModelForSkinnedMesh(parent, skinnedMesh);
|
|
1218
|
-
});
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
else if (node.type === 'SkinnedMesh') {
|
|
1222
|
-
const skinnedMesh = node;
|
|
1223
|
-
this._createHeadlessModelForSkinnedMesh(node.parent, skinnedMesh);
|
|
1224
|
-
}
|
|
1225
|
-
else {
|
|
1226
|
-
if (this._isEraseTarget(node)) {
|
|
1227
|
-
node.layers.set(this._thirdPersonOnlyLayer);
|
|
1228
|
-
node.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer));
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
_isEraseTarget(bone) {
|
|
1233
|
-
if (bone === this.humanoid.getBoneNode('head')) {
|
|
1234
|
-
return true;
|
|
1235
|
-
}
|
|
1236
|
-
else if (!bone.parent) {
|
|
1237
|
-
return false;
|
|
1238
|
-
}
|
|
1239
|
-
else {
|
|
1240
|
-
return this._isEraseTarget(bone.parent);
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
/**
|
|
1245
|
-
* A default camera layer for `FirstPersonOnly` layer.
|
|
1246
|
-
*
|
|
1247
|
-
* @see [[getFirstPersonOnlyLayer]]
|
|
1248
|
-
*/
|
|
1249
|
-
VRMFirstPerson.DEFAULT_FIRSTPERSON_ONLY_LAYER = 9;
|
|
1250
|
-
/**
|
|
1251
|
-
* A default camera layer for `ThirdPersonOnly` layer.
|
|
1252
|
-
*
|
|
1253
|
-
* @see [[getThirdPersonOnlyLayer]]
|
|
1254
|
-
*/
|
|
1012
|
+
class VRMFirstPerson {
|
|
1013
|
+
/**
|
|
1014
|
+
* Create a new VRMFirstPerson object.
|
|
1015
|
+
*
|
|
1016
|
+
* @param humanoid A {@link VRMHumanoid}
|
|
1017
|
+
* @param meshAnnotations A renderer settings. See the description of [[RendererFirstPersonFlags]] for more info
|
|
1018
|
+
*/
|
|
1019
|
+
constructor(humanoid, meshAnnotations) {
|
|
1020
|
+
this._firstPersonOnlyLayer = VRMFirstPerson.DEFAULT_FIRSTPERSON_ONLY_LAYER;
|
|
1021
|
+
this._thirdPersonOnlyLayer = VRMFirstPerson.DEFAULT_THIRDPERSON_ONLY_LAYER;
|
|
1022
|
+
this._initializedLayers = false;
|
|
1023
|
+
this.humanoid = humanoid;
|
|
1024
|
+
this.meshAnnotations = meshAnnotations;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Copy the given {@link VRMFirstPerson} into this one.
|
|
1028
|
+
* {@link humanoid} must be same as the source one.
|
|
1029
|
+
* @param source The {@link VRMFirstPerson} you want to copy
|
|
1030
|
+
* @returns this
|
|
1031
|
+
*/
|
|
1032
|
+
copy(source) {
|
|
1033
|
+
if (this.humanoid !== source.humanoid) {
|
|
1034
|
+
throw new Error('VRMFirstPerson: humanoid must be same in order to copy');
|
|
1035
|
+
}
|
|
1036
|
+
this.meshAnnotations = source.meshAnnotations.map((annotation) => ({
|
|
1037
|
+
meshes: annotation.meshes.concat(),
|
|
1038
|
+
type: annotation.type,
|
|
1039
|
+
}));
|
|
1040
|
+
return this;
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Returns a clone of this {@link VRMFirstPerson}.
|
|
1044
|
+
* @returns Copied {@link VRMFirstPerson}
|
|
1045
|
+
*/
|
|
1046
|
+
clone() {
|
|
1047
|
+
return new VRMFirstPerson(this.humanoid, this.meshAnnotations).copy(this);
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* A camera layer represents `FirstPersonOnly` layer.
|
|
1051
|
+
* Note that **you must call {@link setup} first before you use the layer feature** or it does not work properly.
|
|
1052
|
+
*
|
|
1053
|
+
* The value is {@link DEFAULT_FIRSTPERSON_ONLY_LAYER} by default but you can change the layer by specifying via {@link setup} if you prefer.
|
|
1054
|
+
*
|
|
1055
|
+
* @see https://vrm.dev/en/univrm/api/univrm_use_firstperson/
|
|
1056
|
+
* @see https://threejs.org/docs/#api/en/core/Layers
|
|
1057
|
+
*/
|
|
1058
|
+
get firstPersonOnlyLayer() {
|
|
1059
|
+
return this._firstPersonOnlyLayer;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* A camera layer represents `ThirdPersonOnly` layer.
|
|
1063
|
+
* Note that **you must call {@link setup} first before you use the layer feature** or it does not work properly.
|
|
1064
|
+
*
|
|
1065
|
+
* The value is {@link DEFAULT_THIRDPERSON_ONLY_LAYER} by default but you can change the layer by specifying via {@link setup} if you prefer.
|
|
1066
|
+
*
|
|
1067
|
+
* @see https://vrm.dev/en/univrm/api/univrm_use_firstperson/
|
|
1068
|
+
* @see https://threejs.org/docs/#api/en/core/Layers
|
|
1069
|
+
*/
|
|
1070
|
+
get thirdPersonOnlyLayer() {
|
|
1071
|
+
return this._thirdPersonOnlyLayer;
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* In this method, it assigns layers for every meshes based on mesh annotations.
|
|
1075
|
+
* You must call this method first before you use the layer feature.
|
|
1076
|
+
*
|
|
1077
|
+
* 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.
|
|
1078
|
+
*
|
|
1079
|
+
* The `cameraLayer` parameter specifies which layer will be assigned for `FirstPersonOnly` / `ThirdPersonOnly`.
|
|
1080
|
+
* In UniVRM, we specified those by naming each desired layer as `FIRSTPERSON_ONLY_LAYER` / `THIRDPERSON_ONLY_LAYER`
|
|
1081
|
+
* but we are going to specify these layers at here since we are unable to name layers in Three.js.
|
|
1082
|
+
*
|
|
1083
|
+
* @param cameraLayer Specify which layer will be for `FirstPersonOnly` / `ThirdPersonOnly`.
|
|
1084
|
+
*/
|
|
1085
|
+
setup({ firstPersonOnlyLayer = VRMFirstPerson.DEFAULT_FIRSTPERSON_ONLY_LAYER, thirdPersonOnlyLayer = VRMFirstPerson.DEFAULT_THIRDPERSON_ONLY_LAYER, } = {}) {
|
|
1086
|
+
if (this._initializedLayers) {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
this._firstPersonOnlyLayer = firstPersonOnlyLayer;
|
|
1090
|
+
this._thirdPersonOnlyLayer = thirdPersonOnlyLayer;
|
|
1091
|
+
this.meshAnnotations.forEach((item) => {
|
|
1092
|
+
item.meshes.forEach((mesh) => {
|
|
1093
|
+
if (item.type === 'firstPersonOnly') {
|
|
1094
|
+
mesh.layers.set(this._firstPersonOnlyLayer);
|
|
1095
|
+
mesh.traverse((child) => child.layers.set(this._firstPersonOnlyLayer));
|
|
1096
|
+
}
|
|
1097
|
+
else if (item.type === 'thirdPersonOnly') {
|
|
1098
|
+
mesh.layers.set(this._thirdPersonOnlyLayer);
|
|
1099
|
+
mesh.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer));
|
|
1100
|
+
}
|
|
1101
|
+
else if (item.type === 'auto') {
|
|
1102
|
+
this._createHeadlessModel(mesh);
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
});
|
|
1106
|
+
this._initializedLayers = true;
|
|
1107
|
+
}
|
|
1108
|
+
_excludeTriangles(triangles, bws, skinIndex, exclude) {
|
|
1109
|
+
let count = 0;
|
|
1110
|
+
if (bws != null && bws.length > 0) {
|
|
1111
|
+
for (let i = 0; i < triangles.length; i += 3) {
|
|
1112
|
+
const a = triangles[i];
|
|
1113
|
+
const b = triangles[i + 1];
|
|
1114
|
+
const c = triangles[i + 2];
|
|
1115
|
+
const bw0 = bws[a];
|
|
1116
|
+
const skin0 = skinIndex[a];
|
|
1117
|
+
if (bw0[0] > 0 && exclude.includes(skin0[0]))
|
|
1118
|
+
continue;
|
|
1119
|
+
if (bw0[1] > 0 && exclude.includes(skin0[1]))
|
|
1120
|
+
continue;
|
|
1121
|
+
if (bw0[2] > 0 && exclude.includes(skin0[2]))
|
|
1122
|
+
continue;
|
|
1123
|
+
if (bw0[3] > 0 && exclude.includes(skin0[3]))
|
|
1124
|
+
continue;
|
|
1125
|
+
const bw1 = bws[b];
|
|
1126
|
+
const skin1 = skinIndex[b];
|
|
1127
|
+
if (bw1[0] > 0 && exclude.includes(skin1[0]))
|
|
1128
|
+
continue;
|
|
1129
|
+
if (bw1[1] > 0 && exclude.includes(skin1[1]))
|
|
1130
|
+
continue;
|
|
1131
|
+
if (bw1[2] > 0 && exclude.includes(skin1[2]))
|
|
1132
|
+
continue;
|
|
1133
|
+
if (bw1[3] > 0 && exclude.includes(skin1[3]))
|
|
1134
|
+
continue;
|
|
1135
|
+
const bw2 = bws[c];
|
|
1136
|
+
const skin2 = skinIndex[c];
|
|
1137
|
+
if (bw2[0] > 0 && exclude.includes(skin2[0]))
|
|
1138
|
+
continue;
|
|
1139
|
+
if (bw2[1] > 0 && exclude.includes(skin2[1]))
|
|
1140
|
+
continue;
|
|
1141
|
+
if (bw2[2] > 0 && exclude.includes(skin2[2]))
|
|
1142
|
+
continue;
|
|
1143
|
+
if (bw2[3] > 0 && exclude.includes(skin2[3]))
|
|
1144
|
+
continue;
|
|
1145
|
+
triangles[count++] = a;
|
|
1146
|
+
triangles[count++] = b;
|
|
1147
|
+
triangles[count++] = c;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
return count;
|
|
1151
|
+
}
|
|
1152
|
+
_createErasedMesh(src, erasingBonesIndex) {
|
|
1153
|
+
const dst = new THREE__namespace.SkinnedMesh(src.geometry.clone(), src.material);
|
|
1154
|
+
dst.name = `${src.name}(erase)`;
|
|
1155
|
+
dst.frustumCulled = src.frustumCulled;
|
|
1156
|
+
dst.layers.set(this._firstPersonOnlyLayer);
|
|
1157
|
+
const geometry = dst.geometry;
|
|
1158
|
+
const skinIndexAttr = geometry.getAttribute('skinIndex').array;
|
|
1159
|
+
const skinIndex = [];
|
|
1160
|
+
for (let i = 0; i < skinIndexAttr.length; i += 4) {
|
|
1161
|
+
skinIndex.push([skinIndexAttr[i], skinIndexAttr[i + 1], skinIndexAttr[i + 2], skinIndexAttr[i + 3]]);
|
|
1162
|
+
}
|
|
1163
|
+
const skinWeightAttr = geometry.getAttribute('skinWeight').array;
|
|
1164
|
+
const skinWeight = [];
|
|
1165
|
+
for (let i = 0; i < skinWeightAttr.length; i += 4) {
|
|
1166
|
+
skinWeight.push([skinWeightAttr[i], skinWeightAttr[i + 1], skinWeightAttr[i + 2], skinWeightAttr[i + 3]]);
|
|
1167
|
+
}
|
|
1168
|
+
const index = geometry.getIndex();
|
|
1169
|
+
if (!index) {
|
|
1170
|
+
throw new Error("The geometry doesn't have an index buffer");
|
|
1171
|
+
}
|
|
1172
|
+
const oldTriangles = Array.from(index.array);
|
|
1173
|
+
const count = this._excludeTriangles(oldTriangles, skinWeight, skinIndex, erasingBonesIndex);
|
|
1174
|
+
const newTriangle = [];
|
|
1175
|
+
for (let i = 0; i < count; i++) {
|
|
1176
|
+
newTriangle[i] = oldTriangles[i];
|
|
1177
|
+
}
|
|
1178
|
+
geometry.setIndex(newTriangle);
|
|
1179
|
+
// mtoon material includes onBeforeRender. this is unsupported at SkinnedMesh#clone
|
|
1180
|
+
if (src.onBeforeRender) {
|
|
1181
|
+
dst.onBeforeRender = src.onBeforeRender;
|
|
1182
|
+
}
|
|
1183
|
+
dst.bind(new THREE__namespace.Skeleton(src.skeleton.bones, src.skeleton.boneInverses), new THREE__namespace.Matrix4());
|
|
1184
|
+
return dst;
|
|
1185
|
+
}
|
|
1186
|
+
_createHeadlessModelForSkinnedMesh(parent, mesh) {
|
|
1187
|
+
const eraseBoneIndexes = [];
|
|
1188
|
+
mesh.skeleton.bones.forEach((bone, index) => {
|
|
1189
|
+
if (this._isEraseTarget(bone))
|
|
1190
|
+
eraseBoneIndexes.push(index);
|
|
1191
|
+
});
|
|
1192
|
+
// Unlike UniVRM we don't copy mesh if no invisible bone was found
|
|
1193
|
+
if (!eraseBoneIndexes.length) {
|
|
1194
|
+
mesh.layers.enable(this._thirdPersonOnlyLayer);
|
|
1195
|
+
mesh.layers.enable(this._firstPersonOnlyLayer);
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
mesh.layers.set(this._thirdPersonOnlyLayer);
|
|
1199
|
+
const newMesh = this._createErasedMesh(mesh, eraseBoneIndexes);
|
|
1200
|
+
parent.add(newMesh);
|
|
1201
|
+
}
|
|
1202
|
+
_createHeadlessModel(node) {
|
|
1203
|
+
if (node.type === 'Group') {
|
|
1204
|
+
node.layers.set(this._thirdPersonOnlyLayer);
|
|
1205
|
+
if (this._isEraseTarget(node)) {
|
|
1206
|
+
node.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer));
|
|
1207
|
+
}
|
|
1208
|
+
else {
|
|
1209
|
+
const parent = new THREE__namespace.Group();
|
|
1210
|
+
parent.name = `_headless_${node.name}`;
|
|
1211
|
+
parent.layers.set(this._firstPersonOnlyLayer);
|
|
1212
|
+
node.parent.add(parent);
|
|
1213
|
+
node.children
|
|
1214
|
+
.filter((child) => child.type === 'SkinnedMesh')
|
|
1215
|
+
.forEach((child) => {
|
|
1216
|
+
const skinnedMesh = child;
|
|
1217
|
+
this._createHeadlessModelForSkinnedMesh(parent, skinnedMesh);
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
else if (node.type === 'SkinnedMesh') {
|
|
1222
|
+
const skinnedMesh = node;
|
|
1223
|
+
this._createHeadlessModelForSkinnedMesh(node.parent, skinnedMesh);
|
|
1224
|
+
}
|
|
1225
|
+
else {
|
|
1226
|
+
if (this._isEraseTarget(node)) {
|
|
1227
|
+
node.layers.set(this._thirdPersonOnlyLayer);
|
|
1228
|
+
node.traverse((child) => child.layers.set(this._thirdPersonOnlyLayer));
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
_isEraseTarget(bone) {
|
|
1233
|
+
if (bone === this.humanoid.getBoneNode('head')) {
|
|
1234
|
+
return true;
|
|
1235
|
+
}
|
|
1236
|
+
else if (!bone.parent) {
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
else {
|
|
1240
|
+
return this._isEraseTarget(bone.parent);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* A default camera layer for `FirstPersonOnly` layer.
|
|
1246
|
+
*
|
|
1247
|
+
* @see [[getFirstPersonOnlyLayer]]
|
|
1248
|
+
*/
|
|
1249
|
+
VRMFirstPerson.DEFAULT_FIRSTPERSON_ONLY_LAYER = 9;
|
|
1250
|
+
/**
|
|
1251
|
+
* A default camera layer for `ThirdPersonOnly` layer.
|
|
1252
|
+
*
|
|
1253
|
+
* @see [[getThirdPersonOnlyLayer]]
|
|
1254
|
+
*/
|
|
1255
1255
|
VRMFirstPerson.DEFAULT_THIRDPERSON_ONLY_LAYER = 10;
|
|
1256
1256
|
|
|
1257
|
-
/**
|
|
1258
|
-
* A plugin of GLTFLoader that imports a {@link VRMFirstPerson} from a VRM extension of a GLTF.
|
|
1259
|
-
*/
|
|
1260
|
-
class VRMFirstPersonLoaderPlugin {
|
|
1261
|
-
constructor(parser) {
|
|
1262
|
-
this.parser = parser;
|
|
1263
|
-
}
|
|
1264
|
-
get name() {
|
|
1265
|
-
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
1266
|
-
return 'VRMFirstPersonLoaderPlugin';
|
|
1267
|
-
}
|
|
1268
|
-
afterRoot(gltf) {
|
|
1269
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1270
|
-
const vrmHumanoid = gltf.userData.vrmHumanoid;
|
|
1271
|
-
// explicitly distinguish null and undefined
|
|
1272
|
-
// since vrmHumanoid might be null as a result
|
|
1273
|
-
if (vrmHumanoid === null) {
|
|
1274
|
-
return;
|
|
1275
|
-
}
|
|
1276
|
-
else if (vrmHumanoid === undefined) {
|
|
1277
|
-
throw new Error('VRMFirstPersonLoaderPlugin: vrmHumanoid is undefined. VRMHumanoidLoaderPlugin have to be used first');
|
|
1278
|
-
}
|
|
1279
|
-
gltf.userData.vrmFirstPerson = yield this._import(gltf, vrmHumanoid);
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Import a {@link VRMFirstPerson} from a VRM.
|
|
1284
|
-
*
|
|
1285
|
-
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
|
1286
|
-
* @param humanoid A {@link VRMHumanoid} instance that represents the VRM
|
|
1287
|
-
*/
|
|
1288
|
-
_import(gltf, humanoid) {
|
|
1289
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1290
|
-
if (humanoid == null) {
|
|
1291
|
-
return null;
|
|
1292
|
-
}
|
|
1293
|
-
const v1Result = yield this._v1Import(gltf, humanoid);
|
|
1294
|
-
if (v1Result) {
|
|
1295
|
-
return v1Result;
|
|
1296
|
-
}
|
|
1297
|
-
const v0Result = yield this._v0Import(gltf, humanoid);
|
|
1298
|
-
if (v0Result) {
|
|
1299
|
-
return v0Result;
|
|
1300
|
-
}
|
|
1301
|
-
return null;
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
|
-
_v1Import(gltf, humanoid) {
|
|
1305
|
-
var _a, _b;
|
|
1306
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1307
|
-
const json = this.parser.json;
|
|
1308
|
-
// early abort if it doesn't use vrm
|
|
1309
|
-
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
1310
|
-
if (!isVRMUsed) {
|
|
1311
|
-
return null;
|
|
1312
|
-
}
|
|
1313
|
-
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
1314
|
-
if (!extension) {
|
|
1315
|
-
return null;
|
|
1316
|
-
}
|
|
1317
|
-
const specVersion = extension.specVersion;
|
|
1318
|
-
if (specVersion !== '1.0-beta') {
|
|
1319
|
-
return null;
|
|
1320
|
-
}
|
|
1321
|
-
const schemaFirstPerson = extension.firstPerson;
|
|
1322
|
-
if (!schemaFirstPerson) {
|
|
1323
|
-
return null;
|
|
1324
|
-
}
|
|
1325
|
-
const meshAnnotations = [];
|
|
1326
|
-
const nodePrimitivesMap = yield gltfExtractPrimitivesFromNodes(gltf);
|
|
1327
|
-
Array.from(nodePrimitivesMap.entries()).forEach(([nodeIndex, primitives]) => {
|
|
1328
|
-
var _a;
|
|
1329
|
-
const annotation = schemaFirstPerson.meshAnnotations
|
|
1330
|
-
? schemaFirstPerson.meshAnnotations.find((a) => a.node === nodeIndex)
|
|
1331
|
-
: undefined;
|
|
1332
|
-
meshAnnotations.push({
|
|
1333
|
-
meshes: primitives,
|
|
1334
|
-
type: (_a = annotation === null || annotation === void 0 ? void 0 : annotation.type) !== null && _a !== void 0 ? _a : 'both',
|
|
1335
|
-
});
|
|
1336
|
-
});
|
|
1337
|
-
return new VRMFirstPerson(humanoid, meshAnnotations);
|
|
1338
|
-
});
|
|
1339
|
-
}
|
|
1340
|
-
_v0Import(gltf, humanoid) {
|
|
1341
|
-
var _a;
|
|
1342
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1343
|
-
const json = this.parser.json;
|
|
1344
|
-
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
1345
|
-
if (!vrmExt) {
|
|
1346
|
-
return null;
|
|
1347
|
-
}
|
|
1348
|
-
const schemaFirstPerson = vrmExt.firstPerson;
|
|
1349
|
-
if (!schemaFirstPerson) {
|
|
1350
|
-
return null;
|
|
1351
|
-
}
|
|
1352
|
-
const meshAnnotations = [];
|
|
1353
|
-
const nodePrimitivesMap = yield gltfExtractPrimitivesFromNodes(gltf);
|
|
1354
|
-
Array.from(nodePrimitivesMap.entries()).forEach(([nodeIndex, primitives]) => {
|
|
1355
|
-
const schemaNode = json.nodes[nodeIndex];
|
|
1356
|
-
const flag = schemaFirstPerson.meshAnnotations
|
|
1357
|
-
? schemaFirstPerson.meshAnnotations.find((a) => a.mesh === schemaNode.mesh)
|
|
1358
|
-
: undefined;
|
|
1359
|
-
meshAnnotations.push({
|
|
1360
|
-
meshes: primitives,
|
|
1361
|
-
type: this._convertV0FlagToV1Type(flag === null || flag === void 0 ? void 0 : flag.firstPersonFlag),
|
|
1362
|
-
});
|
|
1363
|
-
});
|
|
1364
|
-
return new VRMFirstPerson(humanoid, meshAnnotations);
|
|
1365
|
-
});
|
|
1366
|
-
}
|
|
1367
|
-
_convertV0FlagToV1Type(flag) {
|
|
1368
|
-
if (flag === 'FirstPersonOnly') {
|
|
1369
|
-
return 'firstPersonOnly';
|
|
1370
|
-
}
|
|
1371
|
-
else if (flag === 'ThirdPersonOnly') {
|
|
1372
|
-
return 'thirdPersonOnly';
|
|
1373
|
-
}
|
|
1374
|
-
else if (flag === 'Auto') {
|
|
1375
|
-
return 'auto';
|
|
1376
|
-
}
|
|
1377
|
-
else {
|
|
1378
|
-
return 'both';
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1257
|
+
/**
|
|
1258
|
+
* A plugin of GLTFLoader that imports a {@link VRMFirstPerson} from a VRM extension of a GLTF.
|
|
1259
|
+
*/
|
|
1260
|
+
class VRMFirstPersonLoaderPlugin {
|
|
1261
|
+
constructor(parser) {
|
|
1262
|
+
this.parser = parser;
|
|
1263
|
+
}
|
|
1264
|
+
get name() {
|
|
1265
|
+
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
1266
|
+
return 'VRMFirstPersonLoaderPlugin';
|
|
1267
|
+
}
|
|
1268
|
+
afterRoot(gltf) {
|
|
1269
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1270
|
+
const vrmHumanoid = gltf.userData.vrmHumanoid;
|
|
1271
|
+
// explicitly distinguish null and undefined
|
|
1272
|
+
// since vrmHumanoid might be null as a result
|
|
1273
|
+
if (vrmHumanoid === null) {
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
else if (vrmHumanoid === undefined) {
|
|
1277
|
+
throw new Error('VRMFirstPersonLoaderPlugin: vrmHumanoid is undefined. VRMHumanoidLoaderPlugin have to be used first');
|
|
1278
|
+
}
|
|
1279
|
+
gltf.userData.vrmFirstPerson = yield this._import(gltf, vrmHumanoid);
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Import a {@link VRMFirstPerson} from a VRM.
|
|
1284
|
+
*
|
|
1285
|
+
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
|
1286
|
+
* @param humanoid A {@link VRMHumanoid} instance that represents the VRM
|
|
1287
|
+
*/
|
|
1288
|
+
_import(gltf, humanoid) {
|
|
1289
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1290
|
+
if (humanoid == null) {
|
|
1291
|
+
return null;
|
|
1292
|
+
}
|
|
1293
|
+
const v1Result = yield this._v1Import(gltf, humanoid);
|
|
1294
|
+
if (v1Result) {
|
|
1295
|
+
return v1Result;
|
|
1296
|
+
}
|
|
1297
|
+
const v0Result = yield this._v0Import(gltf, humanoid);
|
|
1298
|
+
if (v0Result) {
|
|
1299
|
+
return v0Result;
|
|
1300
|
+
}
|
|
1301
|
+
return null;
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
_v1Import(gltf, humanoid) {
|
|
1305
|
+
var _a, _b;
|
|
1306
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1307
|
+
const json = this.parser.json;
|
|
1308
|
+
// early abort if it doesn't use vrm
|
|
1309
|
+
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
1310
|
+
if (!isVRMUsed) {
|
|
1311
|
+
return null;
|
|
1312
|
+
}
|
|
1313
|
+
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
1314
|
+
if (!extension) {
|
|
1315
|
+
return null;
|
|
1316
|
+
}
|
|
1317
|
+
const specVersion = extension.specVersion;
|
|
1318
|
+
if (specVersion !== '1.0-beta') {
|
|
1319
|
+
return null;
|
|
1320
|
+
}
|
|
1321
|
+
const schemaFirstPerson = extension.firstPerson;
|
|
1322
|
+
if (!schemaFirstPerson) {
|
|
1323
|
+
return null;
|
|
1324
|
+
}
|
|
1325
|
+
const meshAnnotations = [];
|
|
1326
|
+
const nodePrimitivesMap = yield gltfExtractPrimitivesFromNodes(gltf);
|
|
1327
|
+
Array.from(nodePrimitivesMap.entries()).forEach(([nodeIndex, primitives]) => {
|
|
1328
|
+
var _a;
|
|
1329
|
+
const annotation = schemaFirstPerson.meshAnnotations
|
|
1330
|
+
? schemaFirstPerson.meshAnnotations.find((a) => a.node === nodeIndex)
|
|
1331
|
+
: undefined;
|
|
1332
|
+
meshAnnotations.push({
|
|
1333
|
+
meshes: primitives,
|
|
1334
|
+
type: (_a = annotation === null || annotation === void 0 ? void 0 : annotation.type) !== null && _a !== void 0 ? _a : 'both',
|
|
1335
|
+
});
|
|
1336
|
+
});
|
|
1337
|
+
return new VRMFirstPerson(humanoid, meshAnnotations);
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
_v0Import(gltf, humanoid) {
|
|
1341
|
+
var _a;
|
|
1342
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1343
|
+
const json = this.parser.json;
|
|
1344
|
+
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
1345
|
+
if (!vrmExt) {
|
|
1346
|
+
return null;
|
|
1347
|
+
}
|
|
1348
|
+
const schemaFirstPerson = vrmExt.firstPerson;
|
|
1349
|
+
if (!schemaFirstPerson) {
|
|
1350
|
+
return null;
|
|
1351
|
+
}
|
|
1352
|
+
const meshAnnotations = [];
|
|
1353
|
+
const nodePrimitivesMap = yield gltfExtractPrimitivesFromNodes(gltf);
|
|
1354
|
+
Array.from(nodePrimitivesMap.entries()).forEach(([nodeIndex, primitives]) => {
|
|
1355
|
+
const schemaNode = json.nodes[nodeIndex];
|
|
1356
|
+
const flag = schemaFirstPerson.meshAnnotations
|
|
1357
|
+
? schemaFirstPerson.meshAnnotations.find((a) => a.mesh === schemaNode.mesh)
|
|
1358
|
+
: undefined;
|
|
1359
|
+
meshAnnotations.push({
|
|
1360
|
+
meshes: primitives,
|
|
1361
|
+
type: this._convertV0FlagToV1Type(flag === null || flag === void 0 ? void 0 : flag.firstPersonFlag),
|
|
1362
|
+
});
|
|
1363
|
+
});
|
|
1364
|
+
return new VRMFirstPerson(humanoid, meshAnnotations);
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
_convertV0FlagToV1Type(flag) {
|
|
1368
|
+
if (flag === 'FirstPersonOnly') {
|
|
1369
|
+
return 'firstPersonOnly';
|
|
1370
|
+
}
|
|
1371
|
+
else if (flag === 'ThirdPersonOnly') {
|
|
1372
|
+
return 'thirdPersonOnly';
|
|
1373
|
+
}
|
|
1374
|
+
else if (flag === 'Auto') {
|
|
1375
|
+
return 'auto';
|
|
1376
|
+
}
|
|
1377
|
+
else {
|
|
1378
|
+
return 'both';
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
1381
|
}
|
|
1382
1382
|
|
|
1383
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1384
|
-
const VRMFirstPersonMeshAnnotationType = {
|
|
1385
|
-
Auto: 'auto',
|
|
1386
|
-
Both: 'both',
|
|
1387
|
-
ThirdPersonOnly: 'thirdPersonOnly',
|
|
1388
|
-
FirstPersonOnly: 'firstPersonOnly',
|
|
1383
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1384
|
+
const VRMFirstPersonMeshAnnotationType = {
|
|
1385
|
+
Auto: 'auto',
|
|
1386
|
+
Both: 'both',
|
|
1387
|
+
ThirdPersonOnly: 'thirdPersonOnly',
|
|
1388
|
+
FirstPersonOnly: 'firstPersonOnly',
|
|
1389
1389
|
};
|
|
1390
1390
|
|
|
1391
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1392
|
-
const VRMHumanBoneName = {
|
|
1393
|
-
Hips: 'hips',
|
|
1394
|
-
Spine: 'spine',
|
|
1395
|
-
Chest: 'chest',
|
|
1396
|
-
UpperChest: 'upperChest',
|
|
1397
|
-
Neck: 'neck',
|
|
1398
|
-
Head: 'head',
|
|
1399
|
-
LeftEye: 'leftEye',
|
|
1400
|
-
RightEye: 'rightEye',
|
|
1401
|
-
Jaw: 'jaw',
|
|
1402
|
-
LeftUpperLeg: 'leftUpperLeg',
|
|
1403
|
-
LeftLowerLeg: 'leftLowerLeg',
|
|
1404
|
-
LeftFoot: 'leftFoot',
|
|
1405
|
-
LeftToes: 'leftToes',
|
|
1406
|
-
RightUpperLeg: 'rightUpperLeg',
|
|
1407
|
-
RightLowerLeg: 'rightLowerLeg',
|
|
1408
|
-
RightFoot: 'rightFoot',
|
|
1409
|
-
RightToes: 'rightToes',
|
|
1410
|
-
LeftShoulder: 'leftShoulder',
|
|
1411
|
-
LeftUpperArm: 'leftUpperArm',
|
|
1412
|
-
LeftLowerArm: 'leftLowerArm',
|
|
1413
|
-
LeftHand: 'leftHand',
|
|
1414
|
-
RightShoulder: 'rightShoulder',
|
|
1415
|
-
RightUpperArm: 'rightUpperArm',
|
|
1416
|
-
RightLowerArm: 'rightLowerArm',
|
|
1417
|
-
RightHand: 'rightHand',
|
|
1418
|
-
LeftThumbProximal: 'leftThumbProximal',
|
|
1419
|
-
LeftThumbIntermediate: 'leftThumbIntermediate',
|
|
1420
|
-
LeftThumbDistal: 'leftThumbDistal',
|
|
1421
|
-
LeftIndexProximal: 'leftIndexProximal',
|
|
1422
|
-
LeftIndexIntermediate: 'leftIndexIntermediate',
|
|
1423
|
-
LeftIndexDistal: 'leftIndexDistal',
|
|
1424
|
-
LeftMiddleProximal: 'leftMiddleProximal',
|
|
1425
|
-
LeftMiddleIntermediate: 'leftMiddleIntermediate',
|
|
1426
|
-
LeftMiddleDistal: 'leftMiddleDistal',
|
|
1427
|
-
LeftRingProximal: 'leftRingProximal',
|
|
1428
|
-
LeftRingIntermediate: 'leftRingIntermediate',
|
|
1429
|
-
LeftRingDistal: 'leftRingDistal',
|
|
1430
|
-
LeftLittleProximal: 'leftLittleProximal',
|
|
1431
|
-
LeftLittleIntermediate: 'leftLittleIntermediate',
|
|
1432
|
-
LeftLittleDistal: 'leftLittleDistal',
|
|
1433
|
-
RightThumbProximal: 'rightThumbProximal',
|
|
1434
|
-
RightThumbIntermediate: 'rightThumbIntermediate',
|
|
1435
|
-
RightThumbDistal: 'rightThumbDistal',
|
|
1436
|
-
RightIndexProximal: 'rightIndexProximal',
|
|
1437
|
-
RightIndexIntermediate: 'rightIndexIntermediate',
|
|
1438
|
-
RightIndexDistal: 'rightIndexDistal',
|
|
1439
|
-
RightMiddleProximal: 'rightMiddleProximal',
|
|
1440
|
-
RightMiddleIntermediate: 'rightMiddleIntermediate',
|
|
1441
|
-
RightMiddleDistal: 'rightMiddleDistal',
|
|
1442
|
-
RightRingProximal: 'rightRingProximal',
|
|
1443
|
-
RightRingIntermediate: 'rightRingIntermediate',
|
|
1444
|
-
RightRingDistal: 'rightRingDistal',
|
|
1445
|
-
RightLittleProximal: 'rightLittleProximal',
|
|
1446
|
-
RightLittleIntermediate: 'rightLittleIntermediate',
|
|
1447
|
-
RightLittleDistal: 'rightLittleDistal',
|
|
1391
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1392
|
+
const VRMHumanBoneName = {
|
|
1393
|
+
Hips: 'hips',
|
|
1394
|
+
Spine: 'spine',
|
|
1395
|
+
Chest: 'chest',
|
|
1396
|
+
UpperChest: 'upperChest',
|
|
1397
|
+
Neck: 'neck',
|
|
1398
|
+
Head: 'head',
|
|
1399
|
+
LeftEye: 'leftEye',
|
|
1400
|
+
RightEye: 'rightEye',
|
|
1401
|
+
Jaw: 'jaw',
|
|
1402
|
+
LeftUpperLeg: 'leftUpperLeg',
|
|
1403
|
+
LeftLowerLeg: 'leftLowerLeg',
|
|
1404
|
+
LeftFoot: 'leftFoot',
|
|
1405
|
+
LeftToes: 'leftToes',
|
|
1406
|
+
RightUpperLeg: 'rightUpperLeg',
|
|
1407
|
+
RightLowerLeg: 'rightLowerLeg',
|
|
1408
|
+
RightFoot: 'rightFoot',
|
|
1409
|
+
RightToes: 'rightToes',
|
|
1410
|
+
LeftShoulder: 'leftShoulder',
|
|
1411
|
+
LeftUpperArm: 'leftUpperArm',
|
|
1412
|
+
LeftLowerArm: 'leftLowerArm',
|
|
1413
|
+
LeftHand: 'leftHand',
|
|
1414
|
+
RightShoulder: 'rightShoulder',
|
|
1415
|
+
RightUpperArm: 'rightUpperArm',
|
|
1416
|
+
RightLowerArm: 'rightLowerArm',
|
|
1417
|
+
RightHand: 'rightHand',
|
|
1418
|
+
LeftThumbProximal: 'leftThumbProximal',
|
|
1419
|
+
LeftThumbIntermediate: 'leftThumbIntermediate',
|
|
1420
|
+
LeftThumbDistal: 'leftThumbDistal',
|
|
1421
|
+
LeftIndexProximal: 'leftIndexProximal',
|
|
1422
|
+
LeftIndexIntermediate: 'leftIndexIntermediate',
|
|
1423
|
+
LeftIndexDistal: 'leftIndexDistal',
|
|
1424
|
+
LeftMiddleProximal: 'leftMiddleProximal',
|
|
1425
|
+
LeftMiddleIntermediate: 'leftMiddleIntermediate',
|
|
1426
|
+
LeftMiddleDistal: 'leftMiddleDistal',
|
|
1427
|
+
LeftRingProximal: 'leftRingProximal',
|
|
1428
|
+
LeftRingIntermediate: 'leftRingIntermediate',
|
|
1429
|
+
LeftRingDistal: 'leftRingDistal',
|
|
1430
|
+
LeftLittleProximal: 'leftLittleProximal',
|
|
1431
|
+
LeftLittleIntermediate: 'leftLittleIntermediate',
|
|
1432
|
+
LeftLittleDistal: 'leftLittleDistal',
|
|
1433
|
+
RightThumbProximal: 'rightThumbProximal',
|
|
1434
|
+
RightThumbIntermediate: 'rightThumbIntermediate',
|
|
1435
|
+
RightThumbDistal: 'rightThumbDistal',
|
|
1436
|
+
RightIndexProximal: 'rightIndexProximal',
|
|
1437
|
+
RightIndexIntermediate: 'rightIndexIntermediate',
|
|
1438
|
+
RightIndexDistal: 'rightIndexDistal',
|
|
1439
|
+
RightMiddleProximal: 'rightMiddleProximal',
|
|
1440
|
+
RightMiddleIntermediate: 'rightMiddleIntermediate',
|
|
1441
|
+
RightMiddleDistal: 'rightMiddleDistal',
|
|
1442
|
+
RightRingProximal: 'rightRingProximal',
|
|
1443
|
+
RightRingIntermediate: 'rightRingIntermediate',
|
|
1444
|
+
RightRingDistal: 'rightRingDistal',
|
|
1445
|
+
RightLittleProximal: 'rightLittleProximal',
|
|
1446
|
+
RightLittleIntermediate: 'rightLittleIntermediate',
|
|
1447
|
+
RightLittleDistal: 'rightLittleDistal',
|
|
1448
1448
|
};
|
|
1449
1449
|
|
|
1450
|
-
/**
|
|
1451
|
-
* A compat function for `Quaternion.invert()` / `Quaternion.inverse()`.
|
|
1452
|
-
* `Quaternion.invert()` is introduced in r123 and `Quaternion.inverse()` emits a warning.
|
|
1453
|
-
* We are going to use this compat for a while.
|
|
1454
|
-
* @param target A target quaternion
|
|
1455
|
-
*/
|
|
1456
|
-
function quatInvertCompat(target) {
|
|
1457
|
-
if (target.invert) {
|
|
1458
|
-
target.invert();
|
|
1459
|
-
}
|
|
1460
|
-
else {
|
|
1461
|
-
target.inverse();
|
|
1462
|
-
}
|
|
1463
|
-
return target;
|
|
1450
|
+
/**
|
|
1451
|
+
* A compat function for `Quaternion.invert()` / `Quaternion.inverse()`.
|
|
1452
|
+
* `Quaternion.invert()` is introduced in r123 and `Quaternion.inverse()` emits a warning.
|
|
1453
|
+
* We are going to use this compat for a while.
|
|
1454
|
+
* @param target A target quaternion
|
|
1455
|
+
*/
|
|
1456
|
+
function quatInvertCompat(target) {
|
|
1457
|
+
if (target.invert) {
|
|
1458
|
+
target.invert();
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
target.inverse();
|
|
1462
|
+
}
|
|
1463
|
+
return target;
|
|
1464
1464
|
}
|
|
1465
1465
|
|
|
1466
|
-
const _v3A$2 = new THREE__namespace.Vector3();
|
|
1467
|
-
const _quatA$2 = new THREE__namespace.Quaternion();
|
|
1468
|
-
/**
|
|
1469
|
-
* A class represents a humanoid of a VRM.
|
|
1470
|
-
*/
|
|
1471
|
-
class VRMHumanoid {
|
|
1472
|
-
/**
|
|
1473
|
-
* Create a new {@link VRMHumanoid}.
|
|
1474
|
-
* @param boneArray A {@link VRMHumanBones} contains all the bones of the new humanoid
|
|
1475
|
-
*/
|
|
1476
|
-
constructor(humanBones) {
|
|
1477
|
-
this.humanBones = humanBones;
|
|
1478
|
-
this.restPose = this.getAbsolutePose();
|
|
1479
|
-
}
|
|
1480
|
-
/**
|
|
1481
|
-
* Copy the given {@link VRMHumanoid} into this one.
|
|
1482
|
-
* @param source The {@link VRMHumanoid} you want to copy
|
|
1483
|
-
* @returns this
|
|
1484
|
-
*/
|
|
1485
|
-
copy(source) {
|
|
1486
|
-
this.humanBones = source.humanBones;
|
|
1487
|
-
this.restPose = source.restPose;
|
|
1488
|
-
return this;
|
|
1489
|
-
}
|
|
1490
|
-
/**
|
|
1491
|
-
* Returns a clone of this {@link VRMHumanoid}.
|
|
1492
|
-
* @returns Copied {@link VRMHumanoid}
|
|
1493
|
-
*/
|
|
1494
|
-
clone() {
|
|
1495
|
-
return new VRMHumanoid(this.humanBones).copy(this);
|
|
1496
|
-
}
|
|
1497
|
-
/**
|
|
1498
|
-
* Return the current absolute pose of this humanoid as a {@link VRMPose}.
|
|
1499
|
-
* Note that the output result will contain initial state of the VRM and not compatible between different models.
|
|
1500
|
-
* You might want to use {@link getPose} instead.
|
|
1501
|
-
*/
|
|
1502
|
-
getAbsolutePose() {
|
|
1503
|
-
const pose = {};
|
|
1504
|
-
Object.keys(this.humanBones).forEach((vrmBoneNameString) => {
|
|
1505
|
-
const vrmBoneName = vrmBoneNameString;
|
|
1506
|
-
const node = this.getBoneNode(vrmBoneName);
|
|
1507
|
-
// Ignore when there are no bone on the VRMHumanoid
|
|
1508
|
-
if (!node) {
|
|
1509
|
-
return;
|
|
1510
|
-
}
|
|
1511
|
-
// Get the position / rotation from the node
|
|
1512
|
-
_v3A$2.copy(node.position);
|
|
1513
|
-
_quatA$2.copy(node.quaternion);
|
|
1514
|
-
// Convert to raw arrays
|
|
1515
|
-
pose[vrmBoneName] = {
|
|
1516
|
-
position: _v3A$2.toArray(),
|
|
1517
|
-
rotation: _quatA$2.toArray(),
|
|
1518
|
-
};
|
|
1519
|
-
});
|
|
1520
|
-
return pose;
|
|
1521
|
-
}
|
|
1522
|
-
/**
|
|
1523
|
-
* Return the current pose of this humanoid as a {@link VRMPose}.
|
|
1524
|
-
*
|
|
1525
|
-
* Each transform is a local transform relative from rest pose (T-pose).
|
|
1526
|
-
*/
|
|
1527
|
-
getPose() {
|
|
1528
|
-
const pose = {};
|
|
1529
|
-
Object.keys(this.humanBones).forEach((boneNameString) => {
|
|
1530
|
-
const boneName = boneNameString;
|
|
1531
|
-
const node = this.getBoneNode(boneName);
|
|
1532
|
-
// Ignore when there are no bone on the VRMHumanoid
|
|
1533
|
-
if (!node) {
|
|
1534
|
-
return;
|
|
1535
|
-
}
|
|
1536
|
-
// Take a diff from restPose
|
|
1537
|
-
_v3A$2.set(0, 0, 0);
|
|
1538
|
-
_quatA$2.identity();
|
|
1539
|
-
const restState = this.restPose[boneName];
|
|
1540
|
-
if (restState === null || restState === void 0 ? void 0 : restState.position) {
|
|
1541
|
-
_v3A$2.fromArray(restState.position).negate();
|
|
1542
|
-
}
|
|
1543
|
-
if (restState === null || restState === void 0 ? void 0 : restState.rotation) {
|
|
1544
|
-
quatInvertCompat(_quatA$2.fromArray(restState.rotation));
|
|
1545
|
-
}
|
|
1546
|
-
// Get the position / rotation from the node
|
|
1547
|
-
_v3A$2.add(node.position);
|
|
1548
|
-
_quatA$2.premultiply(node.quaternion);
|
|
1549
|
-
// Convert to raw arrays
|
|
1550
|
-
pose[boneName] = {
|
|
1551
|
-
position: _v3A$2.toArray(),
|
|
1552
|
-
rotation: _quatA$2.toArray(),
|
|
1553
|
-
};
|
|
1554
|
-
});
|
|
1555
|
-
return pose;
|
|
1556
|
-
}
|
|
1557
|
-
/**
|
|
1558
|
-
* Let the humanoid do a specified pose.
|
|
1559
|
-
*
|
|
1560
|
-
* Each transform have to be a local transform relative from rest pose (T-pose).
|
|
1561
|
-
* You can pass what you got from {@link getPose}.
|
|
1562
|
-
*
|
|
1563
|
-
* @param poseObject A [[VRMPose]] that represents a single pose
|
|
1564
|
-
*/
|
|
1565
|
-
setPose(poseObject) {
|
|
1566
|
-
Object.entries(poseObject).forEach(([boneNameString, state]) => {
|
|
1567
|
-
const boneName = boneNameString;
|
|
1568
|
-
const node = this.getBoneNode(boneName);
|
|
1569
|
-
// Ignore when there are no bone that is defined in the pose on the VRMHumanoid
|
|
1570
|
-
if (!node) {
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
const restState = this.restPose[boneName];
|
|
1574
|
-
if (!restState) {
|
|
1575
|
-
// It's very unlikely. Possibly a bug
|
|
1576
|
-
return;
|
|
1577
|
-
}
|
|
1578
|
-
// Apply the state to the actual bone
|
|
1579
|
-
if (state === null || state === void 0 ? void 0 : state.position) {
|
|
1580
|
-
node.position.fromArray(state.position);
|
|
1581
|
-
if (restState.position) {
|
|
1582
|
-
node.position.add(_v3A$2.fromArray(restState.position));
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
if (state === null || state === void 0 ? void 0 : state.rotation) {
|
|
1586
|
-
node.quaternion.fromArray(state.rotation);
|
|
1587
|
-
if (restState.rotation) {
|
|
1588
|
-
node.quaternion.multiply(_quatA$2.fromArray(restState.rotation));
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
});
|
|
1592
|
-
}
|
|
1593
|
-
/**
|
|
1594
|
-
* Reset the humanoid to its rest pose.
|
|
1595
|
-
*/
|
|
1596
|
-
resetPose() {
|
|
1597
|
-
Object.entries(this.restPose).forEach(([boneName, rest]) => {
|
|
1598
|
-
const node = this.getBoneNode(boneName);
|
|
1599
|
-
if (!node) {
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1602
|
-
if (rest === null || rest === void 0 ? void 0 : rest.position) {
|
|
1603
|
-
node.position.fromArray(rest.position);
|
|
1604
|
-
}
|
|
1605
|
-
if (rest === null || rest === void 0 ? void 0 : rest.rotation) {
|
|
1606
|
-
node.quaternion.fromArray(rest.rotation);
|
|
1607
|
-
}
|
|
1608
|
-
});
|
|
1609
|
-
}
|
|
1610
|
-
/**
|
|
1611
|
-
* Return a bone bound to a specified {@link VRMHumanBoneName}, as a {@link VRMHumanBone}.
|
|
1612
|
-
*
|
|
1613
|
-
* @param name Name of the bone you want
|
|
1614
|
-
*/
|
|
1615
|
-
getBone(name) {
|
|
1616
|
-
var _a;
|
|
1617
|
-
return (_a = this.humanBones[name]) !== null && _a !== void 0 ? _a : undefined;
|
|
1618
|
-
}
|
|
1619
|
-
/**
|
|
1620
|
-
* Return a bone bound to a specified {@link VRMHumanBoneName}, as a `THREE.Object3D`.
|
|
1621
|
-
*
|
|
1622
|
-
* @param name Name of the bone you want
|
|
1623
|
-
*/
|
|
1624
|
-
getBoneNode(name) {
|
|
1625
|
-
var _a, _b;
|
|
1626
|
-
return (_b = (_a = this.humanBones[name]) === null || _a === void 0 ? void 0 : _a.node) !== null && _b !== void 0 ? _b : null;
|
|
1627
|
-
}
|
|
1466
|
+
const _v3A$2 = new THREE__namespace.Vector3();
|
|
1467
|
+
const _quatA$2 = new THREE__namespace.Quaternion();
|
|
1468
|
+
/**
|
|
1469
|
+
* A class represents a humanoid of a VRM.
|
|
1470
|
+
*/
|
|
1471
|
+
class VRMHumanoid {
|
|
1472
|
+
/**
|
|
1473
|
+
* Create a new {@link VRMHumanoid}.
|
|
1474
|
+
* @param boneArray A {@link VRMHumanBones} contains all the bones of the new humanoid
|
|
1475
|
+
*/
|
|
1476
|
+
constructor(humanBones) {
|
|
1477
|
+
this.humanBones = humanBones;
|
|
1478
|
+
this.restPose = this.getAbsolutePose();
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Copy the given {@link VRMHumanoid} into this one.
|
|
1482
|
+
* @param source The {@link VRMHumanoid} you want to copy
|
|
1483
|
+
* @returns this
|
|
1484
|
+
*/
|
|
1485
|
+
copy(source) {
|
|
1486
|
+
this.humanBones = source.humanBones;
|
|
1487
|
+
this.restPose = source.restPose;
|
|
1488
|
+
return this;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Returns a clone of this {@link VRMHumanoid}.
|
|
1492
|
+
* @returns Copied {@link VRMHumanoid}
|
|
1493
|
+
*/
|
|
1494
|
+
clone() {
|
|
1495
|
+
return new VRMHumanoid(this.humanBones).copy(this);
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Return the current absolute pose of this humanoid as a {@link VRMPose}.
|
|
1499
|
+
* Note that the output result will contain initial state of the VRM and not compatible between different models.
|
|
1500
|
+
* You might want to use {@link getPose} instead.
|
|
1501
|
+
*/
|
|
1502
|
+
getAbsolutePose() {
|
|
1503
|
+
const pose = {};
|
|
1504
|
+
Object.keys(this.humanBones).forEach((vrmBoneNameString) => {
|
|
1505
|
+
const vrmBoneName = vrmBoneNameString;
|
|
1506
|
+
const node = this.getBoneNode(vrmBoneName);
|
|
1507
|
+
// Ignore when there are no bone on the VRMHumanoid
|
|
1508
|
+
if (!node) {
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
// Get the position / rotation from the node
|
|
1512
|
+
_v3A$2.copy(node.position);
|
|
1513
|
+
_quatA$2.copy(node.quaternion);
|
|
1514
|
+
// Convert to raw arrays
|
|
1515
|
+
pose[vrmBoneName] = {
|
|
1516
|
+
position: _v3A$2.toArray(),
|
|
1517
|
+
rotation: _quatA$2.toArray(),
|
|
1518
|
+
};
|
|
1519
|
+
});
|
|
1520
|
+
return pose;
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Return the current pose of this humanoid as a {@link VRMPose}.
|
|
1524
|
+
*
|
|
1525
|
+
* Each transform is a local transform relative from rest pose (T-pose).
|
|
1526
|
+
*/
|
|
1527
|
+
getPose() {
|
|
1528
|
+
const pose = {};
|
|
1529
|
+
Object.keys(this.humanBones).forEach((boneNameString) => {
|
|
1530
|
+
const boneName = boneNameString;
|
|
1531
|
+
const node = this.getBoneNode(boneName);
|
|
1532
|
+
// Ignore when there are no bone on the VRMHumanoid
|
|
1533
|
+
if (!node) {
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
// Take a diff from restPose
|
|
1537
|
+
_v3A$2.set(0, 0, 0);
|
|
1538
|
+
_quatA$2.identity();
|
|
1539
|
+
const restState = this.restPose[boneName];
|
|
1540
|
+
if (restState === null || restState === void 0 ? void 0 : restState.position) {
|
|
1541
|
+
_v3A$2.fromArray(restState.position).negate();
|
|
1542
|
+
}
|
|
1543
|
+
if (restState === null || restState === void 0 ? void 0 : restState.rotation) {
|
|
1544
|
+
quatInvertCompat(_quatA$2.fromArray(restState.rotation));
|
|
1545
|
+
}
|
|
1546
|
+
// Get the position / rotation from the node
|
|
1547
|
+
_v3A$2.add(node.position);
|
|
1548
|
+
_quatA$2.premultiply(node.quaternion);
|
|
1549
|
+
// Convert to raw arrays
|
|
1550
|
+
pose[boneName] = {
|
|
1551
|
+
position: _v3A$2.toArray(),
|
|
1552
|
+
rotation: _quatA$2.toArray(),
|
|
1553
|
+
};
|
|
1554
|
+
});
|
|
1555
|
+
return pose;
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Let the humanoid do a specified pose.
|
|
1559
|
+
*
|
|
1560
|
+
* Each transform have to be a local transform relative from rest pose (T-pose).
|
|
1561
|
+
* You can pass what you got from {@link getPose}.
|
|
1562
|
+
*
|
|
1563
|
+
* @param poseObject A [[VRMPose]] that represents a single pose
|
|
1564
|
+
*/
|
|
1565
|
+
setPose(poseObject) {
|
|
1566
|
+
Object.entries(poseObject).forEach(([boneNameString, state]) => {
|
|
1567
|
+
const boneName = boneNameString;
|
|
1568
|
+
const node = this.getBoneNode(boneName);
|
|
1569
|
+
// Ignore when there are no bone that is defined in the pose on the VRMHumanoid
|
|
1570
|
+
if (!node) {
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
const restState = this.restPose[boneName];
|
|
1574
|
+
if (!restState) {
|
|
1575
|
+
// It's very unlikely. Possibly a bug
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
// Apply the state to the actual bone
|
|
1579
|
+
if (state === null || state === void 0 ? void 0 : state.position) {
|
|
1580
|
+
node.position.fromArray(state.position);
|
|
1581
|
+
if (restState.position) {
|
|
1582
|
+
node.position.add(_v3A$2.fromArray(restState.position));
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
if (state === null || state === void 0 ? void 0 : state.rotation) {
|
|
1586
|
+
node.quaternion.fromArray(state.rotation);
|
|
1587
|
+
if (restState.rotation) {
|
|
1588
|
+
node.quaternion.multiply(_quatA$2.fromArray(restState.rotation));
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
/**
|
|
1594
|
+
* Reset the humanoid to its rest pose.
|
|
1595
|
+
*/
|
|
1596
|
+
resetPose() {
|
|
1597
|
+
Object.entries(this.restPose).forEach(([boneName, rest]) => {
|
|
1598
|
+
const node = this.getBoneNode(boneName);
|
|
1599
|
+
if (!node) {
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
if (rest === null || rest === void 0 ? void 0 : rest.position) {
|
|
1603
|
+
node.position.fromArray(rest.position);
|
|
1604
|
+
}
|
|
1605
|
+
if (rest === null || rest === void 0 ? void 0 : rest.rotation) {
|
|
1606
|
+
node.quaternion.fromArray(rest.rotation);
|
|
1607
|
+
}
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Return a bone bound to a specified {@link VRMHumanBoneName}, as a {@link VRMHumanBone}.
|
|
1612
|
+
*
|
|
1613
|
+
* @param name Name of the bone you want
|
|
1614
|
+
*/
|
|
1615
|
+
getBone(name) {
|
|
1616
|
+
var _a;
|
|
1617
|
+
return (_a = this.humanBones[name]) !== null && _a !== void 0 ? _a : undefined;
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Return a bone bound to a specified {@link VRMHumanBoneName}, as a `THREE.Object3D`.
|
|
1621
|
+
*
|
|
1622
|
+
* @param name Name of the bone you want
|
|
1623
|
+
*/
|
|
1624
|
+
getBoneNode(name) {
|
|
1625
|
+
var _a, _b;
|
|
1626
|
+
return (_b = (_a = this.humanBones[name]) === null || _a === void 0 ? void 0 : _a.node) !== null && _b !== void 0 ? _b : null;
|
|
1627
|
+
}
|
|
1628
1628
|
}
|
|
1629
1629
|
|
|
1630
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1631
|
-
const VRMRequiredHumanBoneName = {
|
|
1632
|
-
Hips: 'hips',
|
|
1633
|
-
Spine: 'spine',
|
|
1634
|
-
Head: 'head',
|
|
1635
|
-
LeftUpperLeg: 'leftUpperLeg',
|
|
1636
|
-
LeftLowerLeg: 'leftLowerLeg',
|
|
1637
|
-
LeftFoot: 'leftFoot',
|
|
1638
|
-
RightUpperLeg: 'rightUpperLeg',
|
|
1639
|
-
RightLowerLeg: 'rightLowerLeg',
|
|
1640
|
-
RightFoot: 'rightFoot',
|
|
1641
|
-
LeftUpperArm: 'leftUpperArm',
|
|
1642
|
-
LeftLowerArm: 'leftLowerArm',
|
|
1643
|
-
LeftHand: 'leftHand',
|
|
1644
|
-
RightUpperArm: 'rightUpperArm',
|
|
1645
|
-
RightLowerArm: 'rightLowerArm',
|
|
1646
|
-
RightHand: 'rightHand',
|
|
1630
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1631
|
+
const VRMRequiredHumanBoneName = {
|
|
1632
|
+
Hips: 'hips',
|
|
1633
|
+
Spine: 'spine',
|
|
1634
|
+
Head: 'head',
|
|
1635
|
+
LeftUpperLeg: 'leftUpperLeg',
|
|
1636
|
+
LeftLowerLeg: 'leftLowerLeg',
|
|
1637
|
+
LeftFoot: 'leftFoot',
|
|
1638
|
+
RightUpperLeg: 'rightUpperLeg',
|
|
1639
|
+
RightLowerLeg: 'rightLowerLeg',
|
|
1640
|
+
RightFoot: 'rightFoot',
|
|
1641
|
+
LeftUpperArm: 'leftUpperArm',
|
|
1642
|
+
LeftLowerArm: 'leftLowerArm',
|
|
1643
|
+
LeftHand: 'leftHand',
|
|
1644
|
+
RightUpperArm: 'rightUpperArm',
|
|
1645
|
+
RightLowerArm: 'rightLowerArm',
|
|
1646
|
+
RightHand: 'rightHand',
|
|
1647
1647
|
};
|
|
1648
1648
|
|
|
1649
|
-
/**
|
|
1650
|
-
* A plugin of GLTFLoader that imports a {@link VRMHumanoid} from a VRM extension of a GLTF.
|
|
1651
|
-
*/
|
|
1652
|
-
class VRMHumanoidLoaderPlugin {
|
|
1653
|
-
constructor(parser) {
|
|
1654
|
-
this.parser = parser;
|
|
1655
|
-
}
|
|
1656
|
-
get name() {
|
|
1657
|
-
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
1658
|
-
return 'VRMHumanoidLoaderPlugin';
|
|
1659
|
-
}
|
|
1660
|
-
afterRoot(gltf) {
|
|
1661
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1662
|
-
gltf.userData.vrmHumanoid = yield this._import(gltf);
|
|
1663
|
-
});
|
|
1664
|
-
}
|
|
1665
|
-
/**
|
|
1666
|
-
* Import a {@link VRMHumanoid} from a VRM.
|
|
1667
|
-
*
|
|
1668
|
-
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
|
1669
|
-
*/
|
|
1670
|
-
_import(gltf) {
|
|
1671
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1672
|
-
const v1Result = yield this._v1Import(gltf);
|
|
1673
|
-
if (v1Result) {
|
|
1674
|
-
return v1Result;
|
|
1675
|
-
}
|
|
1676
|
-
const v0Result = yield this._v0Import(gltf);
|
|
1677
|
-
if (v0Result) {
|
|
1678
|
-
return v0Result;
|
|
1679
|
-
}
|
|
1680
|
-
return null;
|
|
1681
|
-
});
|
|
1682
|
-
}
|
|
1683
|
-
_v1Import(gltf) {
|
|
1684
|
-
var _a, _b;
|
|
1685
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1686
|
-
const json = this.parser.json;
|
|
1687
|
-
// early abort if it doesn't use vrm
|
|
1688
|
-
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
1689
|
-
if (!isVRMUsed) {
|
|
1690
|
-
return null;
|
|
1691
|
-
}
|
|
1692
|
-
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
1693
|
-
if (!extension) {
|
|
1694
|
-
return null;
|
|
1695
|
-
}
|
|
1696
|
-
const specVersion = extension.specVersion;
|
|
1697
|
-
if (specVersion !== '1.0-beta') {
|
|
1698
|
-
return null;
|
|
1699
|
-
}
|
|
1700
|
-
const schemaHumanoid = extension.humanoid;
|
|
1701
|
-
if (!schemaHumanoid) {
|
|
1702
|
-
return null;
|
|
1703
|
-
}
|
|
1704
|
-
const humanBones = {};
|
|
1705
|
-
if (schemaHumanoid.humanBones != null) {
|
|
1706
|
-
yield Promise.all(Object.entries(schemaHumanoid.humanBones).map(([boneNameString, schemaHumanBone]) => __awaiter(this, void 0, void 0, function* () {
|
|
1707
|
-
const boneName = boneNameString;
|
|
1708
|
-
const index = schemaHumanBone.node;
|
|
1709
|
-
const node = yield this.parser.getDependency('node', index);
|
|
1710
|
-
// if the specified node does not exist, emit a warning
|
|
1711
|
-
if (node == null) {
|
|
1712
|
-
console.warn(`A glTF node bound to the humanoid bone ${boneName} (index = ${index}) does not exist`);
|
|
1713
|
-
return;
|
|
1714
|
-
}
|
|
1715
|
-
// set to the `humanBones`
|
|
1716
|
-
humanBones[boneName] = { node };
|
|
1717
|
-
})));
|
|
1718
|
-
}
|
|
1719
|
-
return new VRMHumanoid(this._ensureRequiredBonesExist(humanBones));
|
|
1720
|
-
});
|
|
1721
|
-
}
|
|
1722
|
-
_v0Import(gltf) {
|
|
1723
|
-
var _a;
|
|
1724
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1725
|
-
const json = this.parser.json;
|
|
1726
|
-
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
1727
|
-
if (!vrmExt) {
|
|
1728
|
-
return null;
|
|
1729
|
-
}
|
|
1730
|
-
const schemaHumanoid = vrmExt.humanoid;
|
|
1731
|
-
if (!schemaHumanoid) {
|
|
1732
|
-
return null;
|
|
1733
|
-
}
|
|
1734
|
-
const humanBones = {};
|
|
1735
|
-
if (schemaHumanoid.humanBones != null) {
|
|
1736
|
-
yield Promise.all(schemaHumanoid.humanBones.map((bone) => __awaiter(this, void 0, void 0, function* () {
|
|
1737
|
-
const boneName = bone.bone;
|
|
1738
|
-
const index = bone.node;
|
|
1739
|
-
if (boneName == null || index == null) {
|
|
1740
|
-
return;
|
|
1741
|
-
}
|
|
1742
|
-
const node = yield this.parser.getDependency('node', index);
|
|
1743
|
-
// if the specified node does not exist, emit a warning
|
|
1744
|
-
if (node == null) {
|
|
1745
|
-
console.warn(`A glTF node bound to the humanoid bone ${boneName} (index = ${index}) does not exist`);
|
|
1746
|
-
return;
|
|
1747
|
-
}
|
|
1748
|
-
// v0 VRMs might have a multiple nodes attached to a single bone...
|
|
1749
|
-
// so if there already is an entry in the `humanBones`, show a warning and ignore it
|
|
1750
|
-
if (humanBones[boneName] != null) {
|
|
1751
|
-
console.warn(`Multiple bone entries for ${boneName} detected (index = ${index}), ignoring duplicated entries.`);
|
|
1752
|
-
return;
|
|
1753
|
-
}
|
|
1754
|
-
// set to the `humanBones`
|
|
1755
|
-
humanBones[boneName] = { node };
|
|
1756
|
-
})));
|
|
1757
|
-
}
|
|
1758
|
-
return new VRMHumanoid(this._ensureRequiredBonesExist(humanBones));
|
|
1759
|
-
});
|
|
1760
|
-
}
|
|
1761
|
-
/**
|
|
1762
|
-
* Ensure required bones exist in given human bones.
|
|
1763
|
-
* @param humanBones Human bones
|
|
1764
|
-
* @returns Human bones, no longer partial!
|
|
1765
|
-
*/
|
|
1766
|
-
_ensureRequiredBonesExist(humanBones) {
|
|
1767
|
-
// ensure required bones exist
|
|
1768
|
-
const missingRequiredBones = Object.values(VRMRequiredHumanBoneName).filter((requiredBoneName) => humanBones[requiredBoneName] == null);
|
|
1769
|
-
// throw an error if there are missing bones
|
|
1770
|
-
if (missingRequiredBones.length > 0) {
|
|
1771
|
-
throw new Error(`VRMHumanoidLoaderPlugin: These humanoid bones are required but not exist: ${missingRequiredBones.join(', ')}`);
|
|
1772
|
-
}
|
|
1773
|
-
return humanBones;
|
|
1774
|
-
}
|
|
1649
|
+
/**
|
|
1650
|
+
* A plugin of GLTFLoader that imports a {@link VRMHumanoid} from a VRM extension of a GLTF.
|
|
1651
|
+
*/
|
|
1652
|
+
class VRMHumanoidLoaderPlugin {
|
|
1653
|
+
constructor(parser) {
|
|
1654
|
+
this.parser = parser;
|
|
1655
|
+
}
|
|
1656
|
+
get name() {
|
|
1657
|
+
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
1658
|
+
return 'VRMHumanoidLoaderPlugin';
|
|
1659
|
+
}
|
|
1660
|
+
afterRoot(gltf) {
|
|
1661
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1662
|
+
gltf.userData.vrmHumanoid = yield this._import(gltf);
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Import a {@link VRMHumanoid} from a VRM.
|
|
1667
|
+
*
|
|
1668
|
+
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
|
1669
|
+
*/
|
|
1670
|
+
_import(gltf) {
|
|
1671
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1672
|
+
const v1Result = yield this._v1Import(gltf);
|
|
1673
|
+
if (v1Result) {
|
|
1674
|
+
return v1Result;
|
|
1675
|
+
}
|
|
1676
|
+
const v0Result = yield this._v0Import(gltf);
|
|
1677
|
+
if (v0Result) {
|
|
1678
|
+
return v0Result;
|
|
1679
|
+
}
|
|
1680
|
+
return null;
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
_v1Import(gltf) {
|
|
1684
|
+
var _a, _b;
|
|
1685
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1686
|
+
const json = this.parser.json;
|
|
1687
|
+
// early abort if it doesn't use vrm
|
|
1688
|
+
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
1689
|
+
if (!isVRMUsed) {
|
|
1690
|
+
return null;
|
|
1691
|
+
}
|
|
1692
|
+
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
1693
|
+
if (!extension) {
|
|
1694
|
+
return null;
|
|
1695
|
+
}
|
|
1696
|
+
const specVersion = extension.specVersion;
|
|
1697
|
+
if (specVersion !== '1.0-beta') {
|
|
1698
|
+
return null;
|
|
1699
|
+
}
|
|
1700
|
+
const schemaHumanoid = extension.humanoid;
|
|
1701
|
+
if (!schemaHumanoid) {
|
|
1702
|
+
return null;
|
|
1703
|
+
}
|
|
1704
|
+
const humanBones = {};
|
|
1705
|
+
if (schemaHumanoid.humanBones != null) {
|
|
1706
|
+
yield Promise.all(Object.entries(schemaHumanoid.humanBones).map(([boneNameString, schemaHumanBone]) => __awaiter(this, void 0, void 0, function* () {
|
|
1707
|
+
const boneName = boneNameString;
|
|
1708
|
+
const index = schemaHumanBone.node;
|
|
1709
|
+
const node = yield this.parser.getDependency('node', index);
|
|
1710
|
+
// if the specified node does not exist, emit a warning
|
|
1711
|
+
if (node == null) {
|
|
1712
|
+
console.warn(`A glTF node bound to the humanoid bone ${boneName} (index = ${index}) does not exist`);
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
// set to the `humanBones`
|
|
1716
|
+
humanBones[boneName] = { node };
|
|
1717
|
+
})));
|
|
1718
|
+
}
|
|
1719
|
+
return new VRMHumanoid(this._ensureRequiredBonesExist(humanBones));
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
_v0Import(gltf) {
|
|
1723
|
+
var _a;
|
|
1724
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1725
|
+
const json = this.parser.json;
|
|
1726
|
+
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
1727
|
+
if (!vrmExt) {
|
|
1728
|
+
return null;
|
|
1729
|
+
}
|
|
1730
|
+
const schemaHumanoid = vrmExt.humanoid;
|
|
1731
|
+
if (!schemaHumanoid) {
|
|
1732
|
+
return null;
|
|
1733
|
+
}
|
|
1734
|
+
const humanBones = {};
|
|
1735
|
+
if (schemaHumanoid.humanBones != null) {
|
|
1736
|
+
yield Promise.all(schemaHumanoid.humanBones.map((bone) => __awaiter(this, void 0, void 0, function* () {
|
|
1737
|
+
const boneName = bone.bone;
|
|
1738
|
+
const index = bone.node;
|
|
1739
|
+
if (boneName == null || index == null) {
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
const node = yield this.parser.getDependency('node', index);
|
|
1743
|
+
// if the specified node does not exist, emit a warning
|
|
1744
|
+
if (node == null) {
|
|
1745
|
+
console.warn(`A glTF node bound to the humanoid bone ${boneName} (index = ${index}) does not exist`);
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
// v0 VRMs might have a multiple nodes attached to a single bone...
|
|
1749
|
+
// so if there already is an entry in the `humanBones`, show a warning and ignore it
|
|
1750
|
+
if (humanBones[boneName] != null) {
|
|
1751
|
+
console.warn(`Multiple bone entries for ${boneName} detected (index = ${index}), ignoring duplicated entries.`);
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
// set to the `humanBones`
|
|
1755
|
+
humanBones[boneName] = { node };
|
|
1756
|
+
})));
|
|
1757
|
+
}
|
|
1758
|
+
return new VRMHumanoid(this._ensureRequiredBonesExist(humanBones));
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Ensure required bones exist in given human bones.
|
|
1763
|
+
* @param humanBones Human bones
|
|
1764
|
+
* @returns Human bones, no longer partial!
|
|
1765
|
+
*/
|
|
1766
|
+
_ensureRequiredBonesExist(humanBones) {
|
|
1767
|
+
// ensure required bones exist
|
|
1768
|
+
const missingRequiredBones = Object.values(VRMRequiredHumanBoneName).filter((requiredBoneName) => humanBones[requiredBoneName] == null);
|
|
1769
|
+
// throw an error if there are missing bones
|
|
1770
|
+
if (missingRequiredBones.length > 0) {
|
|
1771
|
+
throw new Error(`VRMHumanoidLoaderPlugin: These humanoid bones are required but not exist: ${missingRequiredBones.join(', ')}`);
|
|
1772
|
+
}
|
|
1773
|
+
return humanBones;
|
|
1774
|
+
}
|
|
1775
1775
|
}
|
|
1776
1776
|
|
|
1777
|
-
class FanBufferGeometry extends THREE__namespace.BufferGeometry {
|
|
1778
|
-
constructor() {
|
|
1779
|
-
super();
|
|
1780
|
-
this._currentTheta = 0;
|
|
1781
|
-
this._currentRadius = 0;
|
|
1782
|
-
this.theta = 0.0;
|
|
1783
|
-
this.radius = 0.0;
|
|
1784
|
-
this._currentTheta = 0.0;
|
|
1785
|
-
this._currentRadius = 0.0;
|
|
1786
|
-
this._attrPos = new THREE__namespace.BufferAttribute(new Float32Array(65 * 3), 3);
|
|
1787
|
-
this.setAttribute('position', this._attrPos);
|
|
1788
|
-
this._attrIndex = new THREE__namespace.BufferAttribute(new Uint16Array(3 * 63), 1);
|
|
1789
|
-
this.setIndex(this._attrIndex);
|
|
1790
|
-
this._buildIndex();
|
|
1791
|
-
this.update();
|
|
1792
|
-
}
|
|
1793
|
-
update() {
|
|
1794
|
-
let shouldUpdateGeometry = false;
|
|
1795
|
-
if (this._currentTheta !== this.theta) {
|
|
1796
|
-
this._currentTheta = this.theta;
|
|
1797
|
-
shouldUpdateGeometry = true;
|
|
1798
|
-
}
|
|
1799
|
-
if (this._currentRadius !== this.radius) {
|
|
1800
|
-
this._currentRadius = this.radius;
|
|
1801
|
-
shouldUpdateGeometry = true;
|
|
1802
|
-
}
|
|
1803
|
-
if (shouldUpdateGeometry) {
|
|
1804
|
-
this._buildPosition();
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
_buildPosition() {
|
|
1808
|
-
this._attrPos.setXYZ(0, 0.0, 0.0, 0.0);
|
|
1809
|
-
for (let i = 0; i < 64; i++) {
|
|
1810
|
-
const t = (i / 63.0) * this._currentTheta;
|
|
1811
|
-
this._attrPos.setXYZ(i + 1, this._currentRadius * Math.sin(t), 0.0, this._currentRadius * Math.cos(t));
|
|
1812
|
-
}
|
|
1813
|
-
this._attrPos.needsUpdate = true;
|
|
1814
|
-
}
|
|
1815
|
-
_buildIndex() {
|
|
1816
|
-
for (let i = 0; i < 63; i++) {
|
|
1817
|
-
this._attrIndex.setXYZ(i * 3, 0, i + 1, i + 2);
|
|
1818
|
-
}
|
|
1819
|
-
this._attrIndex.needsUpdate = true;
|
|
1820
|
-
}
|
|
1777
|
+
class FanBufferGeometry extends THREE__namespace.BufferGeometry {
|
|
1778
|
+
constructor() {
|
|
1779
|
+
super();
|
|
1780
|
+
this._currentTheta = 0;
|
|
1781
|
+
this._currentRadius = 0;
|
|
1782
|
+
this.theta = 0.0;
|
|
1783
|
+
this.radius = 0.0;
|
|
1784
|
+
this._currentTheta = 0.0;
|
|
1785
|
+
this._currentRadius = 0.0;
|
|
1786
|
+
this._attrPos = new THREE__namespace.BufferAttribute(new Float32Array(65 * 3), 3);
|
|
1787
|
+
this.setAttribute('position', this._attrPos);
|
|
1788
|
+
this._attrIndex = new THREE__namespace.BufferAttribute(new Uint16Array(3 * 63), 1);
|
|
1789
|
+
this.setIndex(this._attrIndex);
|
|
1790
|
+
this._buildIndex();
|
|
1791
|
+
this.update();
|
|
1792
|
+
}
|
|
1793
|
+
update() {
|
|
1794
|
+
let shouldUpdateGeometry = false;
|
|
1795
|
+
if (this._currentTheta !== this.theta) {
|
|
1796
|
+
this._currentTheta = this.theta;
|
|
1797
|
+
shouldUpdateGeometry = true;
|
|
1798
|
+
}
|
|
1799
|
+
if (this._currentRadius !== this.radius) {
|
|
1800
|
+
this._currentRadius = this.radius;
|
|
1801
|
+
shouldUpdateGeometry = true;
|
|
1802
|
+
}
|
|
1803
|
+
if (shouldUpdateGeometry) {
|
|
1804
|
+
this._buildPosition();
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
_buildPosition() {
|
|
1808
|
+
this._attrPos.setXYZ(0, 0.0, 0.0, 0.0);
|
|
1809
|
+
for (let i = 0; i < 64; i++) {
|
|
1810
|
+
const t = (i / 63.0) * this._currentTheta;
|
|
1811
|
+
this._attrPos.setXYZ(i + 1, this._currentRadius * Math.sin(t), 0.0, this._currentRadius * Math.cos(t));
|
|
1812
|
+
}
|
|
1813
|
+
this._attrPos.needsUpdate = true;
|
|
1814
|
+
}
|
|
1815
|
+
_buildIndex() {
|
|
1816
|
+
for (let i = 0; i < 63; i++) {
|
|
1817
|
+
this._attrIndex.setXYZ(i * 3, 0, i + 1, i + 2);
|
|
1818
|
+
}
|
|
1819
|
+
this._attrIndex.needsUpdate = true;
|
|
1820
|
+
}
|
|
1821
1821
|
}
|
|
1822
1822
|
|
|
1823
|
-
class LineAndSphereBufferGeometry extends THREE__namespace.BufferGeometry {
|
|
1824
|
-
constructor() {
|
|
1825
|
-
super();
|
|
1826
|
-
this.radius = 0.0;
|
|
1827
|
-
this._currentRadius = 0.0;
|
|
1828
|
-
this.tail = new THREE__namespace.Vector3();
|
|
1829
|
-
this._currentTail = new THREE__namespace.Vector3();
|
|
1830
|
-
this._attrPos = new THREE__namespace.BufferAttribute(new Float32Array(294), 3);
|
|
1831
|
-
this.setAttribute('position', this._attrPos);
|
|
1832
|
-
this._attrIndex = new THREE__namespace.BufferAttribute(new Uint16Array(194), 1);
|
|
1833
|
-
this.setIndex(this._attrIndex);
|
|
1834
|
-
this._buildIndex();
|
|
1835
|
-
this.update();
|
|
1836
|
-
}
|
|
1837
|
-
update() {
|
|
1838
|
-
let shouldUpdateGeometry = false;
|
|
1839
|
-
if (this._currentRadius !== this.radius) {
|
|
1840
|
-
this._currentRadius = this.radius;
|
|
1841
|
-
shouldUpdateGeometry = true;
|
|
1842
|
-
}
|
|
1843
|
-
if (!this._currentTail.equals(this.tail)) {
|
|
1844
|
-
this._currentTail.copy(this.tail);
|
|
1845
|
-
shouldUpdateGeometry = true;
|
|
1846
|
-
}
|
|
1847
|
-
if (shouldUpdateGeometry) {
|
|
1848
|
-
this._buildPosition();
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
_buildPosition() {
|
|
1852
|
-
for (let i = 0; i < 32; i++) {
|
|
1853
|
-
const t = (i / 16.0) * Math.PI;
|
|
1854
|
-
this._attrPos.setXYZ(i, Math.cos(t), Math.sin(t), 0.0);
|
|
1855
|
-
this._attrPos.setXYZ(32 + i, 0.0, Math.cos(t), Math.sin(t));
|
|
1856
|
-
this._attrPos.setXYZ(64 + i, Math.sin(t), 0.0, Math.cos(t));
|
|
1857
|
-
}
|
|
1858
|
-
this.scale(this._currentRadius, this._currentRadius, this._currentRadius);
|
|
1859
|
-
this.translate(this._currentTail.x, this._currentTail.y, this._currentTail.z);
|
|
1860
|
-
this._attrPos.setXYZ(96, 0, 0, 0);
|
|
1861
|
-
this._attrPos.setXYZ(97, this._currentTail.x, this._currentTail.y, this._currentTail.z);
|
|
1862
|
-
this._attrPos.needsUpdate = true;
|
|
1863
|
-
}
|
|
1864
|
-
_buildIndex() {
|
|
1865
|
-
for (let i = 0; i < 32; i++) {
|
|
1866
|
-
const i1 = (i + 1) % 32;
|
|
1867
|
-
this._attrIndex.setXY(i * 2, i, i1);
|
|
1868
|
-
this._attrIndex.setXY(64 + i * 2, 32 + i, 32 + i1);
|
|
1869
|
-
this._attrIndex.setXY(128 + i * 2, 64 + i, 64 + i1);
|
|
1870
|
-
}
|
|
1871
|
-
this._attrIndex.setXY(192, 96, 97);
|
|
1872
|
-
this._attrIndex.needsUpdate = true;
|
|
1873
|
-
}
|
|
1823
|
+
class LineAndSphereBufferGeometry extends THREE__namespace.BufferGeometry {
|
|
1824
|
+
constructor() {
|
|
1825
|
+
super();
|
|
1826
|
+
this.radius = 0.0;
|
|
1827
|
+
this._currentRadius = 0.0;
|
|
1828
|
+
this.tail = new THREE__namespace.Vector3();
|
|
1829
|
+
this._currentTail = new THREE__namespace.Vector3();
|
|
1830
|
+
this._attrPos = new THREE__namespace.BufferAttribute(new Float32Array(294), 3);
|
|
1831
|
+
this.setAttribute('position', this._attrPos);
|
|
1832
|
+
this._attrIndex = new THREE__namespace.BufferAttribute(new Uint16Array(194), 1);
|
|
1833
|
+
this.setIndex(this._attrIndex);
|
|
1834
|
+
this._buildIndex();
|
|
1835
|
+
this.update();
|
|
1836
|
+
}
|
|
1837
|
+
update() {
|
|
1838
|
+
let shouldUpdateGeometry = false;
|
|
1839
|
+
if (this._currentRadius !== this.radius) {
|
|
1840
|
+
this._currentRadius = this.radius;
|
|
1841
|
+
shouldUpdateGeometry = true;
|
|
1842
|
+
}
|
|
1843
|
+
if (!this._currentTail.equals(this.tail)) {
|
|
1844
|
+
this._currentTail.copy(this.tail);
|
|
1845
|
+
shouldUpdateGeometry = true;
|
|
1846
|
+
}
|
|
1847
|
+
if (shouldUpdateGeometry) {
|
|
1848
|
+
this._buildPosition();
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
_buildPosition() {
|
|
1852
|
+
for (let i = 0; i < 32; i++) {
|
|
1853
|
+
const t = (i / 16.0) * Math.PI;
|
|
1854
|
+
this._attrPos.setXYZ(i, Math.cos(t), Math.sin(t), 0.0);
|
|
1855
|
+
this._attrPos.setXYZ(32 + i, 0.0, Math.cos(t), Math.sin(t));
|
|
1856
|
+
this._attrPos.setXYZ(64 + i, Math.sin(t), 0.0, Math.cos(t));
|
|
1857
|
+
}
|
|
1858
|
+
this.scale(this._currentRadius, this._currentRadius, this._currentRadius);
|
|
1859
|
+
this.translate(this._currentTail.x, this._currentTail.y, this._currentTail.z);
|
|
1860
|
+
this._attrPos.setXYZ(96, 0, 0, 0);
|
|
1861
|
+
this._attrPos.setXYZ(97, this._currentTail.x, this._currentTail.y, this._currentTail.z);
|
|
1862
|
+
this._attrPos.needsUpdate = true;
|
|
1863
|
+
}
|
|
1864
|
+
_buildIndex() {
|
|
1865
|
+
for (let i = 0; i < 32; i++) {
|
|
1866
|
+
const i1 = (i + 1) % 32;
|
|
1867
|
+
this._attrIndex.setXY(i * 2, i, i1);
|
|
1868
|
+
this._attrIndex.setXY(64 + i * 2, 32 + i, 32 + i1);
|
|
1869
|
+
this._attrIndex.setXY(128 + i * 2, 64 + i, 64 + i1);
|
|
1870
|
+
}
|
|
1871
|
+
this._attrIndex.setXY(192, 96, 97);
|
|
1872
|
+
this._attrIndex.needsUpdate = true;
|
|
1873
|
+
}
|
|
1874
1874
|
}
|
|
1875
1875
|
|
|
1876
|
-
const _quatA$1 = new THREE__namespace.Quaternion();
|
|
1877
|
-
const _quatB$1 = new THREE__namespace.Quaternion();
|
|
1878
|
-
const _v3A$1 = new THREE__namespace.Vector3();
|
|
1879
|
-
const _v3B$1 = new THREE__namespace.Vector3();
|
|
1880
|
-
const SQRT_2_OVER_2 = Math.sqrt(2.0) / 2.0;
|
|
1881
|
-
const QUAT_XY_CW90 = new THREE__namespace.Quaternion(0, 0, -SQRT_2_OVER_2, SQRT_2_OVER_2);
|
|
1882
|
-
const VEC3_POSITIVE_Y = new THREE__namespace.Vector3(0.0, 1.0, 0.0);
|
|
1883
|
-
class VRMLookAtHelper extends THREE__namespace.Group {
|
|
1884
|
-
constructor(lookAt) {
|
|
1885
|
-
super();
|
|
1886
|
-
this.matrixAutoUpdate = false;
|
|
1887
|
-
this.vrmLookAt = lookAt;
|
|
1888
|
-
{
|
|
1889
|
-
const geometry = new FanBufferGeometry();
|
|
1890
|
-
geometry.radius = 0.5;
|
|
1891
|
-
const material = new THREE__namespace.MeshBasicMaterial({
|
|
1892
|
-
color: 0x00ff00,
|
|
1893
|
-
transparent: true,
|
|
1894
|
-
opacity: 0.5,
|
|
1895
|
-
side: THREE__namespace.DoubleSide,
|
|
1896
|
-
depthTest: false,
|
|
1897
|
-
depthWrite: false,
|
|
1898
|
-
});
|
|
1899
|
-
this._meshPitch = new THREE__namespace.Mesh(geometry, material);
|
|
1900
|
-
this.add(this._meshPitch);
|
|
1901
|
-
}
|
|
1902
|
-
{
|
|
1903
|
-
const geometry = new FanBufferGeometry();
|
|
1904
|
-
geometry.radius = 0.5;
|
|
1905
|
-
const material = new THREE__namespace.MeshBasicMaterial({
|
|
1906
|
-
color: 0xff0000,
|
|
1907
|
-
transparent: true,
|
|
1908
|
-
opacity: 0.5,
|
|
1909
|
-
side: THREE__namespace.DoubleSide,
|
|
1910
|
-
depthTest: false,
|
|
1911
|
-
depthWrite: false,
|
|
1912
|
-
});
|
|
1913
|
-
this._meshYaw = new THREE__namespace.Mesh(geometry, material);
|
|
1914
|
-
this.add(this._meshYaw);
|
|
1915
|
-
}
|
|
1916
|
-
{
|
|
1917
|
-
const geometry = new LineAndSphereBufferGeometry();
|
|
1918
|
-
geometry.radius = 0.1;
|
|
1919
|
-
const material = new THREE__namespace.LineBasicMaterial({
|
|
1920
|
-
color: 0xffffff,
|
|
1921
|
-
depthTest: false,
|
|
1922
|
-
depthWrite: false,
|
|
1923
|
-
});
|
|
1924
|
-
this._lineTarget = new THREE__namespace.LineSegments(geometry, material);
|
|
1925
|
-
this._lineTarget.frustumCulled = false;
|
|
1926
|
-
this.add(this._lineTarget);
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
dispose() {
|
|
1930
|
-
this._meshYaw.geometry.dispose();
|
|
1931
|
-
this._meshYaw.material.dispose();
|
|
1932
|
-
this._meshPitch.geometry.dispose();
|
|
1933
|
-
this._meshPitch.material.dispose();
|
|
1934
|
-
this._lineTarget.geometry.dispose();
|
|
1935
|
-
this._lineTarget.material.dispose();
|
|
1936
|
-
}
|
|
1937
|
-
updateMatrixWorld(force) {
|
|
1938
|
-
this.vrmLookAt.getLookAtWorldPosition(_v3A$1);
|
|
1939
|
-
this.vrmLookAt.getLookAtWorldQuaternion(_quatA$1);
|
|
1940
|
-
const yaw = this.vrmLookAt.euler.y;
|
|
1941
|
-
const pitch = this.vrmLookAt.euler.x;
|
|
1942
|
-
this._meshYaw.geometry.theta = yaw;
|
|
1943
|
-
this._meshYaw.geometry.update();
|
|
1944
|
-
this._meshYaw.position.copy(_v3A$1);
|
|
1945
|
-
this._meshYaw.quaternion.copy(_quatA$1);
|
|
1946
|
-
this._meshPitch.geometry.theta = pitch;
|
|
1947
|
-
this._meshPitch.geometry.update();
|
|
1948
|
-
this._meshPitch.position.copy(_v3A$1);
|
|
1949
|
-
this._meshPitch.quaternion.copy(_quatA$1);
|
|
1950
|
-
this._meshPitch.quaternion.multiply(_quatB$1.setFromAxisAngle(VEC3_POSITIVE_Y, yaw));
|
|
1951
|
-
this._meshPitch.quaternion.multiply(QUAT_XY_CW90);
|
|
1952
|
-
const target = this.vrmLookAt.target;
|
|
1953
|
-
if (target != null) {
|
|
1954
|
-
target.getWorldPosition(_v3B$1).sub(_v3A$1);
|
|
1955
|
-
this._lineTarget.geometry.tail.copy(_v3B$1);
|
|
1956
|
-
this._lineTarget.geometry.update();
|
|
1957
|
-
this._lineTarget.position.copy(_v3A$1);
|
|
1958
|
-
}
|
|
1959
|
-
super.updateMatrixWorld(force);
|
|
1960
|
-
}
|
|
1876
|
+
const _quatA$1 = new THREE__namespace.Quaternion();
|
|
1877
|
+
const _quatB$1 = new THREE__namespace.Quaternion();
|
|
1878
|
+
const _v3A$1 = new THREE__namespace.Vector3();
|
|
1879
|
+
const _v3B$1 = new THREE__namespace.Vector3();
|
|
1880
|
+
const SQRT_2_OVER_2 = Math.sqrt(2.0) / 2.0;
|
|
1881
|
+
const QUAT_XY_CW90 = new THREE__namespace.Quaternion(0, 0, -SQRT_2_OVER_2, SQRT_2_OVER_2);
|
|
1882
|
+
const VEC3_POSITIVE_Y = new THREE__namespace.Vector3(0.0, 1.0, 0.0);
|
|
1883
|
+
class VRMLookAtHelper extends THREE__namespace.Group {
|
|
1884
|
+
constructor(lookAt) {
|
|
1885
|
+
super();
|
|
1886
|
+
this.matrixAutoUpdate = false;
|
|
1887
|
+
this.vrmLookAt = lookAt;
|
|
1888
|
+
{
|
|
1889
|
+
const geometry = new FanBufferGeometry();
|
|
1890
|
+
geometry.radius = 0.5;
|
|
1891
|
+
const material = new THREE__namespace.MeshBasicMaterial({
|
|
1892
|
+
color: 0x00ff00,
|
|
1893
|
+
transparent: true,
|
|
1894
|
+
opacity: 0.5,
|
|
1895
|
+
side: THREE__namespace.DoubleSide,
|
|
1896
|
+
depthTest: false,
|
|
1897
|
+
depthWrite: false,
|
|
1898
|
+
});
|
|
1899
|
+
this._meshPitch = new THREE__namespace.Mesh(geometry, material);
|
|
1900
|
+
this.add(this._meshPitch);
|
|
1901
|
+
}
|
|
1902
|
+
{
|
|
1903
|
+
const geometry = new FanBufferGeometry();
|
|
1904
|
+
geometry.radius = 0.5;
|
|
1905
|
+
const material = new THREE__namespace.MeshBasicMaterial({
|
|
1906
|
+
color: 0xff0000,
|
|
1907
|
+
transparent: true,
|
|
1908
|
+
opacity: 0.5,
|
|
1909
|
+
side: THREE__namespace.DoubleSide,
|
|
1910
|
+
depthTest: false,
|
|
1911
|
+
depthWrite: false,
|
|
1912
|
+
});
|
|
1913
|
+
this._meshYaw = new THREE__namespace.Mesh(geometry, material);
|
|
1914
|
+
this.add(this._meshYaw);
|
|
1915
|
+
}
|
|
1916
|
+
{
|
|
1917
|
+
const geometry = new LineAndSphereBufferGeometry();
|
|
1918
|
+
geometry.radius = 0.1;
|
|
1919
|
+
const material = new THREE__namespace.LineBasicMaterial({
|
|
1920
|
+
color: 0xffffff,
|
|
1921
|
+
depthTest: false,
|
|
1922
|
+
depthWrite: false,
|
|
1923
|
+
});
|
|
1924
|
+
this._lineTarget = new THREE__namespace.LineSegments(geometry, material);
|
|
1925
|
+
this._lineTarget.frustumCulled = false;
|
|
1926
|
+
this.add(this._lineTarget);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
dispose() {
|
|
1930
|
+
this._meshYaw.geometry.dispose();
|
|
1931
|
+
this._meshYaw.material.dispose();
|
|
1932
|
+
this._meshPitch.geometry.dispose();
|
|
1933
|
+
this._meshPitch.material.dispose();
|
|
1934
|
+
this._lineTarget.geometry.dispose();
|
|
1935
|
+
this._lineTarget.material.dispose();
|
|
1936
|
+
}
|
|
1937
|
+
updateMatrixWorld(force) {
|
|
1938
|
+
this.vrmLookAt.getLookAtWorldPosition(_v3A$1);
|
|
1939
|
+
this.vrmLookAt.getLookAtWorldQuaternion(_quatA$1);
|
|
1940
|
+
const yaw = this.vrmLookAt.euler.y;
|
|
1941
|
+
const pitch = this.vrmLookAt.euler.x;
|
|
1942
|
+
this._meshYaw.geometry.theta = yaw;
|
|
1943
|
+
this._meshYaw.geometry.update();
|
|
1944
|
+
this._meshYaw.position.copy(_v3A$1);
|
|
1945
|
+
this._meshYaw.quaternion.copy(_quatA$1);
|
|
1946
|
+
this._meshPitch.geometry.theta = pitch;
|
|
1947
|
+
this._meshPitch.geometry.update();
|
|
1948
|
+
this._meshPitch.position.copy(_v3A$1);
|
|
1949
|
+
this._meshPitch.quaternion.copy(_quatA$1);
|
|
1950
|
+
this._meshPitch.quaternion.multiply(_quatB$1.setFromAxisAngle(VEC3_POSITIVE_Y, yaw));
|
|
1951
|
+
this._meshPitch.quaternion.multiply(QUAT_XY_CW90);
|
|
1952
|
+
const target = this.vrmLookAt.target;
|
|
1953
|
+
if (target != null) {
|
|
1954
|
+
target.getWorldPosition(_v3B$1).sub(_v3A$1);
|
|
1955
|
+
this._lineTarget.geometry.tail.copy(_v3B$1);
|
|
1956
|
+
this._lineTarget.geometry.update();
|
|
1957
|
+
this._lineTarget.position.copy(_v3A$1);
|
|
1958
|
+
}
|
|
1959
|
+
super.updateMatrixWorld(force);
|
|
1960
|
+
}
|
|
1961
1961
|
}
|
|
1962
1962
|
|
|
1963
|
-
const _position = new THREE__namespace.Vector3();
|
|
1964
|
-
const _scale = new THREE__namespace.Vector3();
|
|
1965
|
-
/**
|
|
1966
|
-
* Extract world rotation of an object from its world space matrix, in cheaper way.
|
|
1967
|
-
*
|
|
1968
|
-
* @param object The object
|
|
1969
|
-
* @param out Target vector
|
|
1970
|
-
*/
|
|
1971
|
-
function getWorldQuaternionLite(object, out) {
|
|
1972
|
-
object.matrixWorld.decompose(_position, out, _scale);
|
|
1973
|
-
return out;
|
|
1963
|
+
const _position = new THREE__namespace.Vector3();
|
|
1964
|
+
const _scale = new THREE__namespace.Vector3();
|
|
1965
|
+
/**
|
|
1966
|
+
* Extract world rotation of an object from its world space matrix, in cheaper way.
|
|
1967
|
+
*
|
|
1968
|
+
* @param object The object
|
|
1969
|
+
* @param out Target vector
|
|
1970
|
+
*/
|
|
1971
|
+
function getWorldQuaternionLite(object, out) {
|
|
1972
|
+
object.matrixWorld.decompose(_position, out, _scale);
|
|
1973
|
+
return out;
|
|
1974
1974
|
}
|
|
1975
1975
|
|
|
1976
|
-
const _v3A = new THREE__namespace.Vector3();
|
|
1977
|
-
const _v3B = new THREE__namespace.Vector3();
|
|
1978
|
-
const _v3C = new THREE__namespace.Vector3();
|
|
1979
|
-
const _quatA = new THREE__namespace.Quaternion();
|
|
1980
|
-
const _quatB = new THREE__namespace.Quaternion();
|
|
1981
|
-
/**
|
|
1982
|
-
* A class controls eye gaze movements of a VRM.
|
|
1983
|
-
*/
|
|
1984
|
-
class VRMLookAt {
|
|
1985
|
-
/**
|
|
1986
|
-
* Create a new {@link VRMLookAt}.
|
|
1987
|
-
*
|
|
1988
|
-
* @param humanoid A {@link VRMHumanoid}
|
|
1989
|
-
* @param applier A {@link VRMLookAtApplier}
|
|
1990
|
-
*/
|
|
1991
|
-
constructor(humanoid, applier) {
|
|
1992
|
-
/**
|
|
1993
|
-
* The origin of LookAt. Position offset from the head bone.
|
|
1994
|
-
*/
|
|
1995
|
-
this.offsetFromHeadBone = new THREE__namespace.Vector3();
|
|
1996
|
-
/**
|
|
1997
|
-
* If this is true, the LookAt will be updated automatically by calling {@link update}, towarding the direction to the {@link target}.
|
|
1998
|
-
* `true` by default.
|
|
1999
|
-
*
|
|
2000
|
-
* See also: {@link target}
|
|
2001
|
-
*/
|
|
2002
|
-
this.autoUpdate = true;
|
|
2003
|
-
/**
|
|
2004
|
-
* The front direction of the face.
|
|
2005
|
-
* Intended to be used for VRM 0.0 compat (VRM 0.0 models are facing Z- instead of Z+).
|
|
2006
|
-
* You usually don't want to touch this.
|
|
2007
|
-
*/
|
|
2008
|
-
this.faceFront = new THREE__namespace.Vector3(0.0, 0.0, 1.0);
|
|
2009
|
-
this._euler = new THREE__namespace.Euler(0.0, 0.0, 0.0, VRMLookAt.EULER_ORDER);
|
|
2010
|
-
this.humanoid = humanoid;
|
|
2011
|
-
this.applier = applier;
|
|
2012
|
-
}
|
|
2013
|
-
/**
|
|
2014
|
-
* Its current euler direction.
|
|
2015
|
-
*/
|
|
2016
|
-
get euler() {
|
|
2017
|
-
return this._euler.clone();
|
|
2018
|
-
}
|
|
2019
|
-
/**
|
|
2020
|
-
* Copy the given {@link VRMLookAt} into this one.
|
|
2021
|
-
* {@link humanoid} must be same as the source one.
|
|
2022
|
-
* {@link applier} will reference the same instance as the source one.
|
|
2023
|
-
* @param source The {@link VRMLookAt} you want to copy
|
|
2024
|
-
* @returns this
|
|
2025
|
-
*/
|
|
2026
|
-
copy(source) {
|
|
2027
|
-
if (this.humanoid !== source.humanoid) {
|
|
2028
|
-
throw new Error('VRMLookAt: humanoid must be same in order to copy');
|
|
2029
|
-
}
|
|
2030
|
-
this.offsetFromHeadBone.copy(source.offsetFromHeadBone);
|
|
2031
|
-
this.applier = source.applier;
|
|
2032
|
-
this.autoUpdate = source.autoUpdate;
|
|
2033
|
-
this.target = source.target;
|
|
2034
|
-
this.faceFront.copy(source.faceFront);
|
|
2035
|
-
return this;
|
|
2036
|
-
}
|
|
2037
|
-
/**
|
|
2038
|
-
* Returns a clone of this {@link VRMLookAt}.
|
|
2039
|
-
* Note that {@link humanoid} and {@link applier} will reference the same instance as this one.
|
|
2040
|
-
* @returns Copied {@link VRMLookAt}
|
|
2041
|
-
*/
|
|
2042
|
-
clone() {
|
|
2043
|
-
return new VRMLookAt(this.humanoid, this.applier).copy(this);
|
|
2044
|
-
}
|
|
2045
|
-
/**
|
|
2046
|
-
* Reset the lookAt direction to initial direction.
|
|
2047
|
-
*/
|
|
2048
|
-
reset() {
|
|
2049
|
-
this._euler.set(0.0, 0.0, 0.0);
|
|
2050
|
-
this.applier.lookAt(this._euler);
|
|
2051
|
-
}
|
|
2052
|
-
/**
|
|
2053
|
-
* Get its head position in world coordinate.
|
|
2054
|
-
*
|
|
2055
|
-
* @param target A target `THREE.Vector3`
|
|
2056
|
-
*/
|
|
2057
|
-
getLookAtWorldPosition(target) {
|
|
2058
|
-
const head = this.humanoid.getBoneNode('head');
|
|
2059
|
-
return target.copy(this.offsetFromHeadBone).applyMatrix4(head.matrixWorld);
|
|
2060
|
-
}
|
|
2061
|
-
/**
|
|
2062
|
-
* Get its LookAt orientation in world coordinate.
|
|
2063
|
-
*
|
|
2064
|
-
* @param target A target `THREE.Vector3`
|
|
2065
|
-
*/
|
|
2066
|
-
getLookAtWorldQuaternion(target) {
|
|
2067
|
-
const head = this.humanoid.getBoneNode('head');
|
|
2068
|
-
return getWorldQuaternionLite(head, target);
|
|
2069
|
-
}
|
|
2070
|
-
/**
|
|
2071
|
-
* Get its LookAt direction in world coordinate.
|
|
2072
|
-
*
|
|
2073
|
-
* @param target A target `THREE.Vector3`
|
|
2074
|
-
*/
|
|
2075
|
-
getLookAtWorldDirection(target) {
|
|
2076
|
-
this.getLookAtWorldQuaternion(_quatA);
|
|
2077
|
-
return target.copy(this.faceFront).applyEuler(this._euler).applyQuaternion(_quatA);
|
|
2078
|
-
}
|
|
2079
|
-
/**
|
|
2080
|
-
* Set its LookAt position.
|
|
2081
|
-
* Note that its result will be instantly overwritten if {@link VRMLookAtHead.autoUpdate} is enabled.
|
|
2082
|
-
*
|
|
2083
|
-
* @param position A target position
|
|
2084
|
-
*/
|
|
2085
|
-
lookAt(position) {
|
|
2086
|
-
this._calcEuler(this._euler, position);
|
|
2087
|
-
this.applier.lookAt(this._euler);
|
|
2088
|
-
}
|
|
2089
|
-
/**
|
|
2090
|
-
* Update the VRMLookAtHead.
|
|
2091
|
-
* If {@link VRMLookAtHead.autoUpdate} is disabled, it will do nothing.
|
|
2092
|
-
*
|
|
2093
|
-
* @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}.
|
|
2094
|
-
*/
|
|
2095
|
-
update(delta) {
|
|
2096
|
-
if (this.target && this.autoUpdate) {
|
|
2097
|
-
this.lookAt(this.target.getWorldPosition(_v3A));
|
|
2098
|
-
this.applier.lookAt(this._euler);
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
_calcEuler(target, position) {
|
|
2102
|
-
// Look at direction in local coordinate
|
|
2103
|
-
const headRotInv = quatInvertCompat(this.getLookAtWorldQuaternion(_quatA));
|
|
2104
|
-
const headPos = this.getLookAtWorldPosition(_v3B);
|
|
2105
|
-
const lookAtDir = _v3C.copy(position).sub(headPos).applyQuaternion(headRotInv).normalize();
|
|
2106
|
-
// calculate the rotation
|
|
2107
|
-
const rotLocal = _quatB.setFromUnitVectors(this.faceFront, lookAtDir);
|
|
2108
|
-
// Transform the direction into local coordinate from the first person bone
|
|
2109
|
-
_v3C.set(0.0, 0.0, 1.0).applyQuaternion(rotLocal);
|
|
2110
|
-
// convert the direction into euler
|
|
2111
|
-
target.x = Math.atan2(-_v3C.y, Math.sqrt(_v3C.x * _v3C.x + _v3C.z * _v3C.z));
|
|
2112
|
-
target.y = Math.atan2(_v3C.x, _v3C.z);
|
|
2113
|
-
return target;
|
|
2114
|
-
}
|
|
2115
|
-
}
|
|
1976
|
+
const _v3A = new THREE__namespace.Vector3();
|
|
1977
|
+
const _v3B = new THREE__namespace.Vector3();
|
|
1978
|
+
const _v3C = new THREE__namespace.Vector3();
|
|
1979
|
+
const _quatA = new THREE__namespace.Quaternion();
|
|
1980
|
+
const _quatB = new THREE__namespace.Quaternion();
|
|
1981
|
+
/**
|
|
1982
|
+
* A class controls eye gaze movements of a VRM.
|
|
1983
|
+
*/
|
|
1984
|
+
class VRMLookAt {
|
|
1985
|
+
/**
|
|
1986
|
+
* Create a new {@link VRMLookAt}.
|
|
1987
|
+
*
|
|
1988
|
+
* @param humanoid A {@link VRMHumanoid}
|
|
1989
|
+
* @param applier A {@link VRMLookAtApplier}
|
|
1990
|
+
*/
|
|
1991
|
+
constructor(humanoid, applier) {
|
|
1992
|
+
/**
|
|
1993
|
+
* The origin of LookAt. Position offset from the head bone.
|
|
1994
|
+
*/
|
|
1995
|
+
this.offsetFromHeadBone = new THREE__namespace.Vector3();
|
|
1996
|
+
/**
|
|
1997
|
+
* If this is true, the LookAt will be updated automatically by calling {@link update}, towarding the direction to the {@link target}.
|
|
1998
|
+
* `true` by default.
|
|
1999
|
+
*
|
|
2000
|
+
* See also: {@link target}
|
|
2001
|
+
*/
|
|
2002
|
+
this.autoUpdate = true;
|
|
2003
|
+
/**
|
|
2004
|
+
* The front direction of the face.
|
|
2005
|
+
* Intended to be used for VRM 0.0 compat (VRM 0.0 models are facing Z- instead of Z+).
|
|
2006
|
+
* You usually don't want to touch this.
|
|
2007
|
+
*/
|
|
2008
|
+
this.faceFront = new THREE__namespace.Vector3(0.0, 0.0, 1.0);
|
|
2009
|
+
this._euler = new THREE__namespace.Euler(0.0, 0.0, 0.0, VRMLookAt.EULER_ORDER);
|
|
2010
|
+
this.humanoid = humanoid;
|
|
2011
|
+
this.applier = applier;
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Its current euler direction.
|
|
2015
|
+
*/
|
|
2016
|
+
get euler() {
|
|
2017
|
+
return this._euler.clone();
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Copy the given {@link VRMLookAt} into this one.
|
|
2021
|
+
* {@link humanoid} must be same as the source one.
|
|
2022
|
+
* {@link applier} will reference the same instance as the source one.
|
|
2023
|
+
* @param source The {@link VRMLookAt} you want to copy
|
|
2024
|
+
* @returns this
|
|
2025
|
+
*/
|
|
2026
|
+
copy(source) {
|
|
2027
|
+
if (this.humanoid !== source.humanoid) {
|
|
2028
|
+
throw new Error('VRMLookAt: humanoid must be same in order to copy');
|
|
2029
|
+
}
|
|
2030
|
+
this.offsetFromHeadBone.copy(source.offsetFromHeadBone);
|
|
2031
|
+
this.applier = source.applier;
|
|
2032
|
+
this.autoUpdate = source.autoUpdate;
|
|
2033
|
+
this.target = source.target;
|
|
2034
|
+
this.faceFront.copy(source.faceFront);
|
|
2035
|
+
return this;
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Returns a clone of this {@link VRMLookAt}.
|
|
2039
|
+
* Note that {@link humanoid} and {@link applier} will reference the same instance as this one.
|
|
2040
|
+
* @returns Copied {@link VRMLookAt}
|
|
2041
|
+
*/
|
|
2042
|
+
clone() {
|
|
2043
|
+
return new VRMLookAt(this.humanoid, this.applier).copy(this);
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Reset the lookAt direction to initial direction.
|
|
2047
|
+
*/
|
|
2048
|
+
reset() {
|
|
2049
|
+
this._euler.set(0.0, 0.0, 0.0);
|
|
2050
|
+
this.applier.lookAt(this._euler);
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Get its head position in world coordinate.
|
|
2054
|
+
*
|
|
2055
|
+
* @param target A target `THREE.Vector3`
|
|
2056
|
+
*/
|
|
2057
|
+
getLookAtWorldPosition(target) {
|
|
2058
|
+
const head = this.humanoid.getBoneNode('head');
|
|
2059
|
+
return target.copy(this.offsetFromHeadBone).applyMatrix4(head.matrixWorld);
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
2062
|
+
* Get its LookAt orientation in world coordinate.
|
|
2063
|
+
*
|
|
2064
|
+
* @param target A target `THREE.Vector3`
|
|
2065
|
+
*/
|
|
2066
|
+
getLookAtWorldQuaternion(target) {
|
|
2067
|
+
const head = this.humanoid.getBoneNode('head');
|
|
2068
|
+
return getWorldQuaternionLite(head, target);
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
2071
|
+
* Get its LookAt direction in world coordinate.
|
|
2072
|
+
*
|
|
2073
|
+
* @param target A target `THREE.Vector3`
|
|
2074
|
+
*/
|
|
2075
|
+
getLookAtWorldDirection(target) {
|
|
2076
|
+
this.getLookAtWorldQuaternion(_quatA);
|
|
2077
|
+
return target.copy(this.faceFront).applyEuler(this._euler).applyQuaternion(_quatA);
|
|
2078
|
+
}
|
|
2079
|
+
/**
|
|
2080
|
+
* Set its LookAt position.
|
|
2081
|
+
* Note that its result will be instantly overwritten if {@link VRMLookAtHead.autoUpdate} is enabled.
|
|
2082
|
+
*
|
|
2083
|
+
* @param position A target position
|
|
2084
|
+
*/
|
|
2085
|
+
lookAt(position) {
|
|
2086
|
+
this._calcEuler(this._euler, position);
|
|
2087
|
+
this.applier.lookAt(this._euler);
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* Update the VRMLookAtHead.
|
|
2091
|
+
* If {@link VRMLookAtHead.autoUpdate} is disabled, it will do nothing.
|
|
2092
|
+
*
|
|
2093
|
+
* @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}.
|
|
2094
|
+
*/
|
|
2095
|
+
update(delta) {
|
|
2096
|
+
if (this.target && this.autoUpdate) {
|
|
2097
|
+
this.lookAt(this.target.getWorldPosition(_v3A));
|
|
2098
|
+
this.applier.lookAt(this._euler);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
_calcEuler(target, position) {
|
|
2102
|
+
// Look at direction in local coordinate
|
|
2103
|
+
const headRotInv = quatInvertCompat(this.getLookAtWorldQuaternion(_quatA));
|
|
2104
|
+
const headPos = this.getLookAtWorldPosition(_v3B);
|
|
2105
|
+
const lookAtDir = _v3C.copy(position).sub(headPos).applyQuaternion(headRotInv).normalize();
|
|
2106
|
+
// calculate the rotation
|
|
2107
|
+
const rotLocal = _quatB.setFromUnitVectors(this.faceFront, lookAtDir);
|
|
2108
|
+
// Transform the direction into local coordinate from the first person bone
|
|
2109
|
+
_v3C.set(0.0, 0.0, 1.0).applyQuaternion(rotLocal);
|
|
2110
|
+
// convert the direction into euler
|
|
2111
|
+
target.x = Math.atan2(-_v3C.y, Math.sqrt(_v3C.x * _v3C.x + _v3C.z * _v3C.z));
|
|
2112
|
+
target.y = Math.atan2(_v3C.x, _v3C.z);
|
|
2113
|
+
return target;
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
2116
|
VRMLookAt.EULER_ORDER = 'YXZ'; // yaw-pitch-roll
|
|
2117
2117
|
|
|
2118
|
-
const _eulerA = new THREE__namespace.Euler(0.0, 0.0, 0.0, 'YXZ');
|
|
2119
|
-
/**
|
|
2120
|
-
* A class that applies eye gaze directions to a VRM.
|
|
2121
|
-
* It will be used by {@link VRMLookAt}.
|
|
2122
|
-
*/
|
|
2123
|
-
class VRMLookAtBoneApplier {
|
|
2124
|
-
/**
|
|
2125
|
-
* Create a new {@link VRMLookAtBoneApplier}.
|
|
2126
|
-
*
|
|
2127
|
-
* @param humanoid A {@link VRMHumanoid}
|
|
2128
|
-
* @param rangeMapHorizontalInner A {@link VRMLookAtRangeMap} used for inner transverse direction
|
|
2129
|
-
* @param rangeMapHorizontalOuter A {@link VRMLookAtRangeMap} used for outer transverse direction
|
|
2130
|
-
* @param rangeMapVerticalDown A {@link VRMLookAtRangeMap} used for down direction
|
|
2131
|
-
* @param rangeMapVerticalUp A {@link VRMLookAtRangeMap} used for up direction
|
|
2132
|
-
*/
|
|
2133
|
-
constructor(humanoid, rangeMapHorizontalInner, rangeMapHorizontalOuter, rangeMapVerticalDown, rangeMapVerticalUp) {
|
|
2134
|
-
this.humanoid = humanoid;
|
|
2135
|
-
this.rangeMapHorizontalInner = rangeMapHorizontalInner;
|
|
2136
|
-
this.rangeMapHorizontalOuter = rangeMapHorizontalOuter;
|
|
2137
|
-
this.rangeMapVerticalDown = rangeMapVerticalDown;
|
|
2138
|
-
this.rangeMapVerticalUp = rangeMapVerticalUp;
|
|
2139
|
-
}
|
|
2140
|
-
/**
|
|
2141
|
-
* Apply the input angle to its associated VRM model.
|
|
2142
|
-
*
|
|
2143
|
-
* @param angle An input angle
|
|
2144
|
-
*/
|
|
2145
|
-
lookAt(angle) {
|
|
2146
|
-
const srcX = (angle.x * 180.0) / Math.PI;
|
|
2147
|
-
const srcY = (angle.y * 180.0) / Math.PI;
|
|
2148
|
-
const leftEye = this.humanoid.getBoneNode('leftEye');
|
|
2149
|
-
const rightEye = this.humanoid.getBoneNode('rightEye');
|
|
2150
|
-
// left
|
|
2151
|
-
if (leftEye) {
|
|
2152
|
-
if (srcX < 0.0) {
|
|
2153
|
-
_eulerA.x = (-this.rangeMapVerticalDown.map(-srcX) / 180.0) * Math.PI;
|
|
2154
|
-
}
|
|
2155
|
-
else {
|
|
2156
|
-
_eulerA.x = (this.rangeMapVerticalUp.map(srcX) / 180.0) * Math.PI;
|
|
2157
|
-
}
|
|
2158
|
-
if (srcY < 0.0) {
|
|
2159
|
-
_eulerA.y = (-this.rangeMapHorizontalInner.map(-srcY) / 180.0) * Math.PI;
|
|
2160
|
-
}
|
|
2161
|
-
else {
|
|
2162
|
-
_eulerA.y = (this.rangeMapHorizontalOuter.map(srcY) / 180.0) * Math.PI;
|
|
2163
|
-
}
|
|
2164
|
-
leftEye.quaternion.setFromEuler(_eulerA);
|
|
2165
|
-
}
|
|
2166
|
-
// right
|
|
2167
|
-
if (rightEye) {
|
|
2168
|
-
if (srcX < 0.0) {
|
|
2169
|
-
_eulerA.x = (-this.rangeMapVerticalDown.map(-srcX) / 180.0) * Math.PI;
|
|
2170
|
-
}
|
|
2171
|
-
else {
|
|
2172
|
-
_eulerA.x = (this.rangeMapVerticalUp.map(srcX) / 180.0) * Math.PI;
|
|
2173
|
-
}
|
|
2174
|
-
if (srcY < 0.0) {
|
|
2175
|
-
_eulerA.y = (-this.rangeMapHorizontalOuter.map(-srcY) / 180.0) * Math.PI;
|
|
2176
|
-
}
|
|
2177
|
-
else {
|
|
2178
|
-
_eulerA.y = (this.rangeMapHorizontalInner.map(srcY) / 180.0) * Math.PI;
|
|
2179
|
-
}
|
|
2180
|
-
rightEye.quaternion.setFromEuler(_eulerA);
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
|
-
/**
|
|
2185
|
-
* Represent its type of applier.
|
|
2186
|
-
*/
|
|
2118
|
+
const _eulerA = new THREE__namespace.Euler(0.0, 0.0, 0.0, 'YXZ');
|
|
2119
|
+
/**
|
|
2120
|
+
* A class that applies eye gaze directions to a VRM.
|
|
2121
|
+
* It will be used by {@link VRMLookAt}.
|
|
2122
|
+
*/
|
|
2123
|
+
class VRMLookAtBoneApplier {
|
|
2124
|
+
/**
|
|
2125
|
+
* Create a new {@link VRMLookAtBoneApplier}.
|
|
2126
|
+
*
|
|
2127
|
+
* @param humanoid A {@link VRMHumanoid}
|
|
2128
|
+
* @param rangeMapHorizontalInner A {@link VRMLookAtRangeMap} used for inner transverse direction
|
|
2129
|
+
* @param rangeMapHorizontalOuter A {@link VRMLookAtRangeMap} used for outer transverse direction
|
|
2130
|
+
* @param rangeMapVerticalDown A {@link VRMLookAtRangeMap} used for down direction
|
|
2131
|
+
* @param rangeMapVerticalUp A {@link VRMLookAtRangeMap} used for up direction
|
|
2132
|
+
*/
|
|
2133
|
+
constructor(humanoid, rangeMapHorizontalInner, rangeMapHorizontalOuter, rangeMapVerticalDown, rangeMapVerticalUp) {
|
|
2134
|
+
this.humanoid = humanoid;
|
|
2135
|
+
this.rangeMapHorizontalInner = rangeMapHorizontalInner;
|
|
2136
|
+
this.rangeMapHorizontalOuter = rangeMapHorizontalOuter;
|
|
2137
|
+
this.rangeMapVerticalDown = rangeMapVerticalDown;
|
|
2138
|
+
this.rangeMapVerticalUp = rangeMapVerticalUp;
|
|
2139
|
+
}
|
|
2140
|
+
/**
|
|
2141
|
+
* Apply the input angle to its associated VRM model.
|
|
2142
|
+
*
|
|
2143
|
+
* @param angle An input angle
|
|
2144
|
+
*/
|
|
2145
|
+
lookAt(angle) {
|
|
2146
|
+
const srcX = (angle.x * 180.0) / Math.PI;
|
|
2147
|
+
const srcY = (angle.y * 180.0) / Math.PI;
|
|
2148
|
+
const leftEye = this.humanoid.getBoneNode('leftEye');
|
|
2149
|
+
const rightEye = this.humanoid.getBoneNode('rightEye');
|
|
2150
|
+
// left
|
|
2151
|
+
if (leftEye) {
|
|
2152
|
+
if (srcX < 0.0) {
|
|
2153
|
+
_eulerA.x = (-this.rangeMapVerticalDown.map(-srcX) / 180.0) * Math.PI;
|
|
2154
|
+
}
|
|
2155
|
+
else {
|
|
2156
|
+
_eulerA.x = (this.rangeMapVerticalUp.map(srcX) / 180.0) * Math.PI;
|
|
2157
|
+
}
|
|
2158
|
+
if (srcY < 0.0) {
|
|
2159
|
+
_eulerA.y = (-this.rangeMapHorizontalInner.map(-srcY) / 180.0) * Math.PI;
|
|
2160
|
+
}
|
|
2161
|
+
else {
|
|
2162
|
+
_eulerA.y = (this.rangeMapHorizontalOuter.map(srcY) / 180.0) * Math.PI;
|
|
2163
|
+
}
|
|
2164
|
+
leftEye.quaternion.setFromEuler(_eulerA);
|
|
2165
|
+
}
|
|
2166
|
+
// right
|
|
2167
|
+
if (rightEye) {
|
|
2168
|
+
if (srcX < 0.0) {
|
|
2169
|
+
_eulerA.x = (-this.rangeMapVerticalDown.map(-srcX) / 180.0) * Math.PI;
|
|
2170
|
+
}
|
|
2171
|
+
else {
|
|
2172
|
+
_eulerA.x = (this.rangeMapVerticalUp.map(srcX) / 180.0) * Math.PI;
|
|
2173
|
+
}
|
|
2174
|
+
if (srcY < 0.0) {
|
|
2175
|
+
_eulerA.y = (-this.rangeMapHorizontalOuter.map(-srcY) / 180.0) * Math.PI;
|
|
2176
|
+
}
|
|
2177
|
+
else {
|
|
2178
|
+
_eulerA.y = (this.rangeMapHorizontalInner.map(srcY) / 180.0) * Math.PI;
|
|
2179
|
+
}
|
|
2180
|
+
rightEye.quaternion.setFromEuler(_eulerA);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
/**
|
|
2185
|
+
* Represent its type of applier.
|
|
2186
|
+
*/
|
|
2187
2187
|
VRMLookAtBoneApplier.type = 'bone';
|
|
2188
2188
|
|
|
2189
|
-
/**
|
|
2190
|
-
* A class that applies eye gaze directions to a VRM.
|
|
2191
|
-
* It will be used by {@link VRMLookAt}.
|
|
2192
|
-
*/
|
|
2193
|
-
class VRMLookAtExpressionApplier {
|
|
2194
|
-
/**
|
|
2195
|
-
* Create a new {@link VRMLookAtExpressionApplier}.
|
|
2196
|
-
*
|
|
2197
|
-
* @param expressions A {@link VRMExpressionManager}
|
|
2198
|
-
* @param rangeMapHorizontalInner A {@link VRMLookAtRangeMap} used for inner transverse direction
|
|
2199
|
-
* @param rangeMapHorizontalOuter A {@link VRMLookAtRangeMap} used for outer transverse direction
|
|
2200
|
-
* @param rangeMapVerticalDown A {@link VRMLookAtRangeMap} used for down direction
|
|
2201
|
-
* @param rangeMapVerticalUp A {@link VRMLookAtRangeMap} used for up direction
|
|
2202
|
-
*/
|
|
2203
|
-
constructor(expressions, rangeMapHorizontalInner, rangeMapHorizontalOuter, rangeMapVerticalDown, rangeMapVerticalUp) {
|
|
2204
|
-
this.expressions = expressions;
|
|
2205
|
-
this.rangeMapHorizontalInner = rangeMapHorizontalInner;
|
|
2206
|
-
this.rangeMapHorizontalOuter = rangeMapHorizontalOuter;
|
|
2207
|
-
this.rangeMapVerticalDown = rangeMapVerticalDown;
|
|
2208
|
-
this.rangeMapVerticalUp = rangeMapVerticalUp;
|
|
2209
|
-
}
|
|
2210
|
-
/**
|
|
2211
|
-
* Apply the input angle to its associated VRM model.
|
|
2212
|
-
*
|
|
2213
|
-
* @param angle An input angle
|
|
2214
|
-
*/
|
|
2215
|
-
lookAt(angle) {
|
|
2216
|
-
const srcX = (angle.x * 180.0) / Math.PI;
|
|
2217
|
-
const srcY = (angle.y * 180.0) / Math.PI;
|
|
2218
|
-
if (srcX < 0.0) {
|
|
2219
|
-
this.expressions.setValue('lookDown', 0.0);
|
|
2220
|
-
this.expressions.setValue('lookUp', this.rangeMapVerticalUp.map(-srcX));
|
|
2221
|
-
}
|
|
2222
|
-
else {
|
|
2223
|
-
this.expressions.setValue('lookUp', 0.0);
|
|
2224
|
-
this.expressions.setValue('lookDown', this.rangeMapVerticalDown.map(srcX));
|
|
2225
|
-
}
|
|
2226
|
-
if (srcY < 0.0) {
|
|
2227
|
-
this.expressions.setValue('lookLeft', 0.0);
|
|
2228
|
-
this.expressions.setValue('lookRight', this.rangeMapHorizontalOuter.map(-srcY));
|
|
2229
|
-
}
|
|
2230
|
-
else {
|
|
2231
|
-
this.expressions.setValue('lookRight', 0.0);
|
|
2232
|
-
this.expressions.setValue('lookLeft', this.rangeMapHorizontalOuter.map(srcY));
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
/**
|
|
2237
|
-
* Represent its type of applier.
|
|
2238
|
-
*/
|
|
2189
|
+
/**
|
|
2190
|
+
* A class that applies eye gaze directions to a VRM.
|
|
2191
|
+
* It will be used by {@link VRMLookAt}.
|
|
2192
|
+
*/
|
|
2193
|
+
class VRMLookAtExpressionApplier {
|
|
2194
|
+
/**
|
|
2195
|
+
* Create a new {@link VRMLookAtExpressionApplier}.
|
|
2196
|
+
*
|
|
2197
|
+
* @param expressions A {@link VRMExpressionManager}
|
|
2198
|
+
* @param rangeMapHorizontalInner A {@link VRMLookAtRangeMap} used for inner transverse direction
|
|
2199
|
+
* @param rangeMapHorizontalOuter A {@link VRMLookAtRangeMap} used for outer transverse direction
|
|
2200
|
+
* @param rangeMapVerticalDown A {@link VRMLookAtRangeMap} used for down direction
|
|
2201
|
+
* @param rangeMapVerticalUp A {@link VRMLookAtRangeMap} used for up direction
|
|
2202
|
+
*/
|
|
2203
|
+
constructor(expressions, rangeMapHorizontalInner, rangeMapHorizontalOuter, rangeMapVerticalDown, rangeMapVerticalUp) {
|
|
2204
|
+
this.expressions = expressions;
|
|
2205
|
+
this.rangeMapHorizontalInner = rangeMapHorizontalInner;
|
|
2206
|
+
this.rangeMapHorizontalOuter = rangeMapHorizontalOuter;
|
|
2207
|
+
this.rangeMapVerticalDown = rangeMapVerticalDown;
|
|
2208
|
+
this.rangeMapVerticalUp = rangeMapVerticalUp;
|
|
2209
|
+
}
|
|
2210
|
+
/**
|
|
2211
|
+
* Apply the input angle to its associated VRM model.
|
|
2212
|
+
*
|
|
2213
|
+
* @param angle An input angle
|
|
2214
|
+
*/
|
|
2215
|
+
lookAt(angle) {
|
|
2216
|
+
const srcX = (angle.x * 180.0) / Math.PI;
|
|
2217
|
+
const srcY = (angle.y * 180.0) / Math.PI;
|
|
2218
|
+
if (srcX < 0.0) {
|
|
2219
|
+
this.expressions.setValue('lookDown', 0.0);
|
|
2220
|
+
this.expressions.setValue('lookUp', this.rangeMapVerticalUp.map(-srcX));
|
|
2221
|
+
}
|
|
2222
|
+
else {
|
|
2223
|
+
this.expressions.setValue('lookUp', 0.0);
|
|
2224
|
+
this.expressions.setValue('lookDown', this.rangeMapVerticalDown.map(srcX));
|
|
2225
|
+
}
|
|
2226
|
+
if (srcY < 0.0) {
|
|
2227
|
+
this.expressions.setValue('lookLeft', 0.0);
|
|
2228
|
+
this.expressions.setValue('lookRight', this.rangeMapHorizontalOuter.map(-srcY));
|
|
2229
|
+
}
|
|
2230
|
+
else {
|
|
2231
|
+
this.expressions.setValue('lookRight', 0.0);
|
|
2232
|
+
this.expressions.setValue('lookLeft', this.rangeMapHorizontalOuter.map(srcY));
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
/**
|
|
2237
|
+
* Represent its type of applier.
|
|
2238
|
+
*/
|
|
2239
2239
|
VRMLookAtExpressionApplier.type = 'expression';
|
|
2240
2240
|
|
|
2241
|
-
class VRMLookAtRangeMap {
|
|
2242
|
-
/**
|
|
2243
|
-
* Create a new {@link VRMLookAtRangeMap}.
|
|
2244
|
-
*
|
|
2245
|
-
* @param inputMaxValue The {@link inputMaxValue} of the map
|
|
2246
|
-
* @param outputScale The {@link outputScale} of the map
|
|
2247
|
-
*/
|
|
2248
|
-
constructor(inputMaxValue, outputScale) {
|
|
2249
|
-
this.inputMaxValue = inputMaxValue;
|
|
2250
|
-
this.outputScale = outputScale;
|
|
2251
|
-
}
|
|
2252
|
-
/**
|
|
2253
|
-
* Evaluate an input value and output a mapped value.
|
|
2254
|
-
* @param src The input value
|
|
2255
|
-
*/
|
|
2256
|
-
map(src) {
|
|
2257
|
-
return this.outputScale * saturate(src / this.inputMaxValue);
|
|
2258
|
-
}
|
|
2241
|
+
class VRMLookAtRangeMap {
|
|
2242
|
+
/**
|
|
2243
|
+
* Create a new {@link VRMLookAtRangeMap}.
|
|
2244
|
+
*
|
|
2245
|
+
* @param inputMaxValue The {@link inputMaxValue} of the map
|
|
2246
|
+
* @param outputScale The {@link outputScale} of the map
|
|
2247
|
+
*/
|
|
2248
|
+
constructor(inputMaxValue, outputScale) {
|
|
2249
|
+
this.inputMaxValue = inputMaxValue;
|
|
2250
|
+
this.outputScale = outputScale;
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* Evaluate an input value and output a mapped value.
|
|
2254
|
+
* @param src The input value
|
|
2255
|
+
*/
|
|
2256
|
+
map(src) {
|
|
2257
|
+
return this.outputScale * saturate(src / this.inputMaxValue);
|
|
2258
|
+
}
|
|
2259
2259
|
}
|
|
2260
2260
|
|
|
2261
|
-
/**
|
|
2262
|
-
* A plugin of GLTFLoader that imports a {@link VRMLookAt} from a VRM extension of a GLTF.
|
|
2263
|
-
*/
|
|
2264
|
-
class VRMLookAtLoaderPlugin {
|
|
2265
|
-
constructor(parser, options) {
|
|
2266
|
-
this.parser = parser;
|
|
2267
|
-
this.helperRoot = options === null || options === void 0 ? void 0 : options.helperRoot;
|
|
2268
|
-
}
|
|
2269
|
-
get name() {
|
|
2270
|
-
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
2271
|
-
return 'VRMLookAtLoaderPlugin';
|
|
2272
|
-
}
|
|
2273
|
-
afterRoot(gltf) {
|
|
2274
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2275
|
-
const vrmHumanoid = gltf.userData.vrmHumanoid;
|
|
2276
|
-
// explicitly distinguish null and undefined
|
|
2277
|
-
// since vrmHumanoid might be null as a result
|
|
2278
|
-
if (vrmHumanoid === null) {
|
|
2279
|
-
return;
|
|
2280
|
-
}
|
|
2281
|
-
else if (vrmHumanoid === undefined) {
|
|
2282
|
-
throw new Error('VRMFirstPersonLoaderPlugin: vrmHumanoid is undefined. VRMHumanoidLoaderPlugin have to be used first');
|
|
2283
|
-
}
|
|
2284
|
-
const vrmExpressionManager = gltf.userData.vrmExpressionManager;
|
|
2285
|
-
if (vrmExpressionManager === null) {
|
|
2286
|
-
return;
|
|
2287
|
-
}
|
|
2288
|
-
else if (vrmExpressionManager === undefined) {
|
|
2289
|
-
throw new Error('VRMFirstPersonLoaderPlugin: vrmExpressionManager is undefined. VRMExpressionLoaderPlugin have to be used first');
|
|
2290
|
-
}
|
|
2291
|
-
gltf.userData.vrmLookAt = yield this._import(gltf, vrmHumanoid, vrmExpressionManager);
|
|
2292
|
-
});
|
|
2293
|
-
}
|
|
2294
|
-
/**
|
|
2295
|
-
* Import a {@link VRMLookAt} from a VRM.
|
|
2296
|
-
*
|
|
2297
|
-
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
|
2298
|
-
* @param humanoid A {@link VRMHumanoid} instance that represents the VRM
|
|
2299
|
-
* @param expressions A {@link VRMExpressionManager} instance that represents the VRM
|
|
2300
|
-
*/
|
|
2301
|
-
_import(gltf, humanoid, expressions) {
|
|
2302
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2303
|
-
if (humanoid == null || expressions == null) {
|
|
2304
|
-
return null;
|
|
2305
|
-
}
|
|
2306
|
-
const v1Result = yield this._v1Import(gltf, humanoid, expressions);
|
|
2307
|
-
if (v1Result) {
|
|
2308
|
-
return v1Result;
|
|
2309
|
-
}
|
|
2310
|
-
const v0Result = yield this._v0Import(gltf, humanoid, expressions);
|
|
2311
|
-
if (v0Result) {
|
|
2312
|
-
return v0Result;
|
|
2313
|
-
}
|
|
2314
|
-
return null;
|
|
2315
|
-
});
|
|
2316
|
-
}
|
|
2317
|
-
_v1Import(gltf, humanoid, expressions) {
|
|
2318
|
-
var _a, _b, _c;
|
|
2319
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2320
|
-
const json = this.parser.json;
|
|
2321
|
-
// early abort if it doesn't use vrm
|
|
2322
|
-
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
2323
|
-
if (!isVRMUsed) {
|
|
2324
|
-
return null;
|
|
2325
|
-
}
|
|
2326
|
-
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
2327
|
-
if (!extension) {
|
|
2328
|
-
return null;
|
|
2329
|
-
}
|
|
2330
|
-
const specVersion = extension.specVersion;
|
|
2331
|
-
if (specVersion !== '1.0-beta') {
|
|
2332
|
-
return null;
|
|
2333
|
-
}
|
|
2334
|
-
const schemaLookAt = extension.lookAt;
|
|
2335
|
-
if (!schemaLookAt) {
|
|
2336
|
-
return null;
|
|
2337
|
-
}
|
|
2338
|
-
const defaultOutputScale = schemaLookAt.type === 'expression' ? 1.0 : 10.0;
|
|
2339
|
-
const mapHI = this._v1ImportRangeMap(schemaLookAt.rangeMapHorizontalInner, defaultOutputScale);
|
|
2340
|
-
const mapHO = this._v1ImportRangeMap(schemaLookAt.rangeMapHorizontalOuter, defaultOutputScale);
|
|
2341
|
-
const mapVD = this._v1ImportRangeMap(schemaLookAt.rangeMapVerticalDown, defaultOutputScale);
|
|
2342
|
-
const mapVU = this._v1ImportRangeMap(schemaLookAt.rangeMapVerticalUp, defaultOutputScale);
|
|
2343
|
-
let applier;
|
|
2344
|
-
if (schemaLookAt.type === 'expression') {
|
|
2345
|
-
applier = new VRMLookAtExpressionApplier(expressions, mapHI, mapHO, mapVD, mapVU);
|
|
2346
|
-
}
|
|
2347
|
-
else {
|
|
2348
|
-
applier = new VRMLookAtBoneApplier(humanoid, mapHI, mapHO, mapVD, mapVU);
|
|
2349
|
-
}
|
|
2350
|
-
const lookAt = this._importLookAt(humanoid, applier);
|
|
2351
|
-
lookAt.offsetFromHeadBone.fromArray((_c = schemaLookAt.offsetFromHeadBone) !== null && _c !== void 0 ? _c : [0.0, 0.06, 0.0]);
|
|
2352
|
-
return lookAt;
|
|
2353
|
-
});
|
|
2354
|
-
}
|
|
2355
|
-
_v1ImportRangeMap(schemaRangeMap, defaultOutputScale) {
|
|
2356
|
-
var _a, _b;
|
|
2357
|
-
return new VRMLookAtRangeMap((_a = schemaRangeMap === null || schemaRangeMap === void 0 ? void 0 : schemaRangeMap.inputMaxValue) !== null && _a !== void 0 ? _a : 90.0, (_b = schemaRangeMap === null || schemaRangeMap === void 0 ? void 0 : schemaRangeMap.outputScale) !== null && _b !== void 0 ? _b : defaultOutputScale);
|
|
2358
|
-
}
|
|
2359
|
-
_v0Import(gltf, humanoid, expressions) {
|
|
2360
|
-
var _a, _b, _c, _d;
|
|
2361
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2362
|
-
const json = this.parser.json;
|
|
2363
|
-
// early abort if it doesn't use vrm
|
|
2364
|
-
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
2365
|
-
if (!vrmExt) {
|
|
2366
|
-
return null;
|
|
2367
|
-
}
|
|
2368
|
-
const schemaFirstPerson = vrmExt.firstPerson;
|
|
2369
|
-
if (!schemaFirstPerson) {
|
|
2370
|
-
return null;
|
|
2371
|
-
}
|
|
2372
|
-
const defaultOutputScale = schemaFirstPerson.lookAtTypeName === 'BlendShape' ? 1.0 : 10.0;
|
|
2373
|
-
const mapHI = this._v0ImportDegreeMap(schemaFirstPerson.lookAtHorizontalInner, defaultOutputScale);
|
|
2374
|
-
const mapHO = this._v0ImportDegreeMap(schemaFirstPerson.lookAtHorizontalOuter, defaultOutputScale);
|
|
2375
|
-
const mapVD = this._v0ImportDegreeMap(schemaFirstPerson.lookAtVerticalDown, defaultOutputScale);
|
|
2376
|
-
const mapVU = this._v0ImportDegreeMap(schemaFirstPerson.lookAtVerticalUp, defaultOutputScale);
|
|
2377
|
-
let applier;
|
|
2378
|
-
if (schemaFirstPerson.lookAtTypeName === 'BlendShape') {
|
|
2379
|
-
applier = new VRMLookAtExpressionApplier(expressions, mapHI, mapHO, mapVD, mapVU);
|
|
2380
|
-
}
|
|
2381
|
-
else {
|
|
2382
|
-
applier = new VRMLookAtBoneApplier(humanoid, mapHI, mapHO, mapVD, mapVU);
|
|
2383
|
-
}
|
|
2384
|
-
const lookAt = this._importLookAt(humanoid, applier);
|
|
2385
|
-
if (schemaFirstPerson.firstPersonBoneOffset) {
|
|
2386
|
-
lookAt.offsetFromHeadBone.set((_b = schemaFirstPerson.firstPersonBoneOffset.x) !== null && _b !== void 0 ? _b : 0.0, (_c = schemaFirstPerson.firstPersonBoneOffset.y) !== null && _c !== void 0 ? _c : 0.06, -((_d = schemaFirstPerson.firstPersonBoneOffset.z) !== null && _d !== void 0 ? _d : 0.0));
|
|
2387
|
-
}
|
|
2388
|
-
else {
|
|
2389
|
-
lookAt.offsetFromHeadBone.set(0.0, 0.06, 0.0);
|
|
2390
|
-
}
|
|
2391
|
-
// VRM 0.0 are facing Z- instead of Z+
|
|
2392
|
-
lookAt.faceFront.set(0.0, 0.0, -1.0);
|
|
2393
|
-
return lookAt;
|
|
2394
|
-
});
|
|
2395
|
-
}
|
|
2396
|
-
_v0ImportDegreeMap(schemaDegreeMap, defaultOutputScale) {
|
|
2397
|
-
var _a, _b;
|
|
2398
|
-
const curve = schemaDegreeMap === null || schemaDegreeMap === void 0 ? void 0 : schemaDegreeMap.curve;
|
|
2399
|
-
if (JSON.stringify(curve) !== '[0,0,0,1,1,1,1,0]') {
|
|
2400
|
-
console.warn('Curves of LookAtDegreeMap defined in VRM 0.0 are not supported');
|
|
2401
|
-
}
|
|
2402
|
-
return new VRMLookAtRangeMap((_a = schemaDegreeMap === null || schemaDegreeMap === void 0 ? void 0 : schemaDegreeMap.xRange) !== null && _a !== void 0 ? _a : 90.0, (_b = schemaDegreeMap === null || schemaDegreeMap === void 0 ? void 0 : schemaDegreeMap.yRange) !== null && _b !== void 0 ? _b : defaultOutputScale);
|
|
2403
|
-
}
|
|
2404
|
-
_importLookAt(humanoid, applier) {
|
|
2405
|
-
const lookAt = new VRMLookAt(humanoid, applier);
|
|
2406
|
-
if (this.helperRoot) {
|
|
2407
|
-
const helper = new VRMLookAtHelper(lookAt);
|
|
2408
|
-
this.helperRoot.add(helper);
|
|
2409
|
-
helper.renderOrder = this.helperRoot.renderOrder;
|
|
2410
|
-
}
|
|
2411
|
-
return lookAt;
|
|
2412
|
-
}
|
|
2261
|
+
/**
|
|
2262
|
+
* A plugin of GLTFLoader that imports a {@link VRMLookAt} from a VRM extension of a GLTF.
|
|
2263
|
+
*/
|
|
2264
|
+
class VRMLookAtLoaderPlugin {
|
|
2265
|
+
constructor(parser, options) {
|
|
2266
|
+
this.parser = parser;
|
|
2267
|
+
this.helperRoot = options === null || options === void 0 ? void 0 : options.helperRoot;
|
|
2268
|
+
}
|
|
2269
|
+
get name() {
|
|
2270
|
+
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
2271
|
+
return 'VRMLookAtLoaderPlugin';
|
|
2272
|
+
}
|
|
2273
|
+
afterRoot(gltf) {
|
|
2274
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2275
|
+
const vrmHumanoid = gltf.userData.vrmHumanoid;
|
|
2276
|
+
// explicitly distinguish null and undefined
|
|
2277
|
+
// since vrmHumanoid might be null as a result
|
|
2278
|
+
if (vrmHumanoid === null) {
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
else if (vrmHumanoid === undefined) {
|
|
2282
|
+
throw new Error('VRMFirstPersonLoaderPlugin: vrmHumanoid is undefined. VRMHumanoidLoaderPlugin have to be used first');
|
|
2283
|
+
}
|
|
2284
|
+
const vrmExpressionManager = gltf.userData.vrmExpressionManager;
|
|
2285
|
+
if (vrmExpressionManager === null) {
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
else if (vrmExpressionManager === undefined) {
|
|
2289
|
+
throw new Error('VRMFirstPersonLoaderPlugin: vrmExpressionManager is undefined. VRMExpressionLoaderPlugin have to be used first');
|
|
2290
|
+
}
|
|
2291
|
+
gltf.userData.vrmLookAt = yield this._import(gltf, vrmHumanoid, vrmExpressionManager);
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Import a {@link VRMLookAt} from a VRM.
|
|
2296
|
+
*
|
|
2297
|
+
* @param gltf A parsed result of GLTF taken from GLTFLoader
|
|
2298
|
+
* @param humanoid A {@link VRMHumanoid} instance that represents the VRM
|
|
2299
|
+
* @param expressions A {@link VRMExpressionManager} instance that represents the VRM
|
|
2300
|
+
*/
|
|
2301
|
+
_import(gltf, humanoid, expressions) {
|
|
2302
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2303
|
+
if (humanoid == null || expressions == null) {
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2306
|
+
const v1Result = yield this._v1Import(gltf, humanoid, expressions);
|
|
2307
|
+
if (v1Result) {
|
|
2308
|
+
return v1Result;
|
|
2309
|
+
}
|
|
2310
|
+
const v0Result = yield this._v0Import(gltf, humanoid, expressions);
|
|
2311
|
+
if (v0Result) {
|
|
2312
|
+
return v0Result;
|
|
2313
|
+
}
|
|
2314
|
+
return null;
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
_v1Import(gltf, humanoid, expressions) {
|
|
2318
|
+
var _a, _b, _c;
|
|
2319
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2320
|
+
const json = this.parser.json;
|
|
2321
|
+
// early abort if it doesn't use vrm
|
|
2322
|
+
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
2323
|
+
if (!isVRMUsed) {
|
|
2324
|
+
return null;
|
|
2325
|
+
}
|
|
2326
|
+
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
2327
|
+
if (!extension) {
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
const specVersion = extension.specVersion;
|
|
2331
|
+
if (specVersion !== '1.0-beta') {
|
|
2332
|
+
return null;
|
|
2333
|
+
}
|
|
2334
|
+
const schemaLookAt = extension.lookAt;
|
|
2335
|
+
if (!schemaLookAt) {
|
|
2336
|
+
return null;
|
|
2337
|
+
}
|
|
2338
|
+
const defaultOutputScale = schemaLookAt.type === 'expression' ? 1.0 : 10.0;
|
|
2339
|
+
const mapHI = this._v1ImportRangeMap(schemaLookAt.rangeMapHorizontalInner, defaultOutputScale);
|
|
2340
|
+
const mapHO = this._v1ImportRangeMap(schemaLookAt.rangeMapHorizontalOuter, defaultOutputScale);
|
|
2341
|
+
const mapVD = this._v1ImportRangeMap(schemaLookAt.rangeMapVerticalDown, defaultOutputScale);
|
|
2342
|
+
const mapVU = this._v1ImportRangeMap(schemaLookAt.rangeMapVerticalUp, defaultOutputScale);
|
|
2343
|
+
let applier;
|
|
2344
|
+
if (schemaLookAt.type === 'expression') {
|
|
2345
|
+
applier = new VRMLookAtExpressionApplier(expressions, mapHI, mapHO, mapVD, mapVU);
|
|
2346
|
+
}
|
|
2347
|
+
else {
|
|
2348
|
+
applier = new VRMLookAtBoneApplier(humanoid, mapHI, mapHO, mapVD, mapVU);
|
|
2349
|
+
}
|
|
2350
|
+
const lookAt = this._importLookAt(humanoid, applier);
|
|
2351
|
+
lookAt.offsetFromHeadBone.fromArray((_c = schemaLookAt.offsetFromHeadBone) !== null && _c !== void 0 ? _c : [0.0, 0.06, 0.0]);
|
|
2352
|
+
return lookAt;
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
_v1ImportRangeMap(schemaRangeMap, defaultOutputScale) {
|
|
2356
|
+
var _a, _b;
|
|
2357
|
+
return new VRMLookAtRangeMap((_a = schemaRangeMap === null || schemaRangeMap === void 0 ? void 0 : schemaRangeMap.inputMaxValue) !== null && _a !== void 0 ? _a : 90.0, (_b = schemaRangeMap === null || schemaRangeMap === void 0 ? void 0 : schemaRangeMap.outputScale) !== null && _b !== void 0 ? _b : defaultOutputScale);
|
|
2358
|
+
}
|
|
2359
|
+
_v0Import(gltf, humanoid, expressions) {
|
|
2360
|
+
var _a, _b, _c, _d;
|
|
2361
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2362
|
+
const json = this.parser.json;
|
|
2363
|
+
// early abort if it doesn't use vrm
|
|
2364
|
+
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
2365
|
+
if (!vrmExt) {
|
|
2366
|
+
return null;
|
|
2367
|
+
}
|
|
2368
|
+
const schemaFirstPerson = vrmExt.firstPerson;
|
|
2369
|
+
if (!schemaFirstPerson) {
|
|
2370
|
+
return null;
|
|
2371
|
+
}
|
|
2372
|
+
const defaultOutputScale = schemaFirstPerson.lookAtTypeName === 'BlendShape' ? 1.0 : 10.0;
|
|
2373
|
+
const mapHI = this._v0ImportDegreeMap(schemaFirstPerson.lookAtHorizontalInner, defaultOutputScale);
|
|
2374
|
+
const mapHO = this._v0ImportDegreeMap(schemaFirstPerson.lookAtHorizontalOuter, defaultOutputScale);
|
|
2375
|
+
const mapVD = this._v0ImportDegreeMap(schemaFirstPerson.lookAtVerticalDown, defaultOutputScale);
|
|
2376
|
+
const mapVU = this._v0ImportDegreeMap(schemaFirstPerson.lookAtVerticalUp, defaultOutputScale);
|
|
2377
|
+
let applier;
|
|
2378
|
+
if (schemaFirstPerson.lookAtTypeName === 'BlendShape') {
|
|
2379
|
+
applier = new VRMLookAtExpressionApplier(expressions, mapHI, mapHO, mapVD, mapVU);
|
|
2380
|
+
}
|
|
2381
|
+
else {
|
|
2382
|
+
applier = new VRMLookAtBoneApplier(humanoid, mapHI, mapHO, mapVD, mapVU);
|
|
2383
|
+
}
|
|
2384
|
+
const lookAt = this._importLookAt(humanoid, applier);
|
|
2385
|
+
if (schemaFirstPerson.firstPersonBoneOffset) {
|
|
2386
|
+
lookAt.offsetFromHeadBone.set((_b = schemaFirstPerson.firstPersonBoneOffset.x) !== null && _b !== void 0 ? _b : 0.0, (_c = schemaFirstPerson.firstPersonBoneOffset.y) !== null && _c !== void 0 ? _c : 0.06, -((_d = schemaFirstPerson.firstPersonBoneOffset.z) !== null && _d !== void 0 ? _d : 0.0));
|
|
2387
|
+
}
|
|
2388
|
+
else {
|
|
2389
|
+
lookAt.offsetFromHeadBone.set(0.0, 0.06, 0.0);
|
|
2390
|
+
}
|
|
2391
|
+
// VRM 0.0 are facing Z- instead of Z+
|
|
2392
|
+
lookAt.faceFront.set(0.0, 0.0, -1.0);
|
|
2393
|
+
return lookAt;
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
_v0ImportDegreeMap(schemaDegreeMap, defaultOutputScale) {
|
|
2397
|
+
var _a, _b;
|
|
2398
|
+
const curve = schemaDegreeMap === null || schemaDegreeMap === void 0 ? void 0 : schemaDegreeMap.curve;
|
|
2399
|
+
if (JSON.stringify(curve) !== '[0,0,0,1,1,1,1,0]') {
|
|
2400
|
+
console.warn('Curves of LookAtDegreeMap defined in VRM 0.0 are not supported');
|
|
2401
|
+
}
|
|
2402
|
+
return new VRMLookAtRangeMap((_a = schemaDegreeMap === null || schemaDegreeMap === void 0 ? void 0 : schemaDegreeMap.xRange) !== null && _a !== void 0 ? _a : 90.0, (_b = schemaDegreeMap === null || schemaDegreeMap === void 0 ? void 0 : schemaDegreeMap.yRange) !== null && _b !== void 0 ? _b : defaultOutputScale);
|
|
2403
|
+
}
|
|
2404
|
+
_importLookAt(humanoid, applier) {
|
|
2405
|
+
const lookAt = new VRMLookAt(humanoid, applier);
|
|
2406
|
+
if (this.helperRoot) {
|
|
2407
|
+
const helper = new VRMLookAtHelper(lookAt);
|
|
2408
|
+
this.helperRoot.add(helper);
|
|
2409
|
+
helper.renderOrder = this.helperRoot.renderOrder;
|
|
2410
|
+
}
|
|
2411
|
+
return lookAt;
|
|
2412
|
+
}
|
|
2413
2413
|
}
|
|
2414
2414
|
|
|
2415
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2416
|
-
/**
|
|
2417
|
-
* Represents a type of applier.
|
|
2418
|
-
*/
|
|
2419
|
-
const VRMLookAtTypeName = {
|
|
2420
|
-
Bone: 'bone',
|
|
2421
|
-
Expression: 'expression',
|
|
2415
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2416
|
+
/**
|
|
2417
|
+
* Represents a type of applier.
|
|
2418
|
+
*/
|
|
2419
|
+
const VRMLookAtTypeName = {
|
|
2420
|
+
Bone: 'bone',
|
|
2421
|
+
Expression: 'expression',
|
|
2422
2422
|
};
|
|
2423
2423
|
|
|
2424
|
-
/**
|
|
2425
|
-
* Yoinked from https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/GLTFLoader.js
|
|
2426
|
-
*/
|
|
2427
|
-
function resolveURL(url, path) {
|
|
2428
|
-
// Invalid URL
|
|
2429
|
-
if (typeof url !== 'string' || url === '')
|
|
2430
|
-
return '';
|
|
2431
|
-
// Host Relative URL
|
|
2432
|
-
if (/^https?:\/\//i.test(path) && /^\//.test(url)) {
|
|
2433
|
-
path = path.replace(/(^https?:\/\/[^/]+).*/i, '$1');
|
|
2434
|
-
}
|
|
2435
|
-
// Absolute URL http://,https://,//
|
|
2436
|
-
if (/^(https?:)?\/\//i.test(url))
|
|
2437
|
-
return url;
|
|
2438
|
-
// Data URI
|
|
2439
|
-
if (/^data:.*,.*$/i.test(url))
|
|
2440
|
-
return url;
|
|
2441
|
-
// Blob URL
|
|
2442
|
-
if (/^blob:.*$/i.test(url))
|
|
2443
|
-
return url;
|
|
2444
|
-
// Relative URL
|
|
2445
|
-
return path + url;
|
|
2424
|
+
/**
|
|
2425
|
+
* Yoinked from https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/GLTFLoader.js
|
|
2426
|
+
*/
|
|
2427
|
+
function resolveURL(url, path) {
|
|
2428
|
+
// Invalid URL
|
|
2429
|
+
if (typeof url !== 'string' || url === '')
|
|
2430
|
+
return '';
|
|
2431
|
+
// Host Relative URL
|
|
2432
|
+
if (/^https?:\/\//i.test(path) && /^\//.test(url)) {
|
|
2433
|
+
path = path.replace(/(^https?:\/\/[^/]+).*/i, '$1');
|
|
2434
|
+
}
|
|
2435
|
+
// Absolute URL http://,https://,//
|
|
2436
|
+
if (/^(https?:)?\/\//i.test(url))
|
|
2437
|
+
return url;
|
|
2438
|
+
// Data URI
|
|
2439
|
+
if (/^data:.*,.*$/i.test(url))
|
|
2440
|
+
return url;
|
|
2441
|
+
// Blob URL
|
|
2442
|
+
if (/^blob:.*$/i.test(url))
|
|
2443
|
+
return url;
|
|
2444
|
+
// Relative URL
|
|
2445
|
+
return path + url;
|
|
2446
2446
|
}
|
|
2447
2447
|
|
|
2448
|
-
/**
|
|
2449
|
-
* A plugin of GLTFLoader that imports a {@link VRM1Meta} from a VRM extension of a GLTF.
|
|
2450
|
-
*/
|
|
2451
|
-
class VRMMetaLoaderPlugin {
|
|
2452
|
-
constructor(parser, options) {
|
|
2453
|
-
var _a, _b, _c;
|
|
2454
|
-
this.parser = parser;
|
|
2455
|
-
this.needThumbnailImage = (_a = options === null || options === void 0 ? void 0 : options.needThumbnailImage) !== null && _a !== void 0 ? _a : true;
|
|
2456
|
-
this.acceptLicenseUrls = (_b = options === null || options === void 0 ? void 0 : options.acceptLicenseUrls) !== null && _b !== void 0 ? _b : ['https://vrm.dev/licenses/1.0/'];
|
|
2457
|
-
this.acceptV0Meta = (_c = options === null || options === void 0 ? void 0 : options.acceptV0Meta) !== null && _c !== void 0 ? _c : true;
|
|
2458
|
-
}
|
|
2459
|
-
get name() {
|
|
2460
|
-
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
2461
|
-
return 'VRMMetaLoaderPlugin';
|
|
2462
|
-
}
|
|
2463
|
-
afterRoot(gltf) {
|
|
2464
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2465
|
-
gltf.userData.vrmMeta = yield this._import(gltf);
|
|
2466
|
-
});
|
|
2467
|
-
}
|
|
2468
|
-
_import(gltf) {
|
|
2469
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2470
|
-
const v1Result = yield this._v1Import(gltf);
|
|
2471
|
-
if (v1Result != null) {
|
|
2472
|
-
return v1Result;
|
|
2473
|
-
}
|
|
2474
|
-
const v0Result = yield this._v0Import(gltf);
|
|
2475
|
-
if (v0Result != null) {
|
|
2476
|
-
return v0Result;
|
|
2477
|
-
}
|
|
2478
|
-
return null;
|
|
2479
|
-
});
|
|
2480
|
-
}
|
|
2481
|
-
_v1Import(gltf) {
|
|
2482
|
-
var _a, _b, _c;
|
|
2483
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2484
|
-
const json = this.parser.json;
|
|
2485
|
-
// early abort if it doesn't use vrm
|
|
2486
|
-
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
2487
|
-
if (!isVRMUsed) {
|
|
2488
|
-
return null;
|
|
2489
|
-
}
|
|
2490
|
-
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
2491
|
-
if (extension == null) {
|
|
2492
|
-
return null;
|
|
2493
|
-
}
|
|
2494
|
-
const specVersion = extension.specVersion;
|
|
2495
|
-
if (specVersion !== '1.0-beta') {
|
|
2496
|
-
return null;
|
|
2497
|
-
}
|
|
2498
|
-
const schemaMeta = extension.meta;
|
|
2499
|
-
if (!schemaMeta) {
|
|
2500
|
-
return null;
|
|
2501
|
-
}
|
|
2502
|
-
// throw an error if acceptV0Meta is false
|
|
2503
|
-
const licenseUrl = schemaMeta.licenseUrl;
|
|
2504
|
-
const acceptLicenseUrlsSet = new Set(this.acceptLicenseUrls);
|
|
2505
|
-
if (!acceptLicenseUrlsSet.has(licenseUrl)) {
|
|
2506
|
-
throw new Error(`VRMMetaLoaderPlugin: The license url "${licenseUrl}" is not accepted`);
|
|
2507
|
-
}
|
|
2508
|
-
let thumbnailImage = undefined;
|
|
2509
|
-
if (this.needThumbnailImage && schemaMeta.thumbnailImage != null) {
|
|
2510
|
-
thumbnailImage = (_c = (yield this._extractGLTFImage(schemaMeta.thumbnailImage))) !== null && _c !== void 0 ? _c : undefined;
|
|
2511
|
-
}
|
|
2512
|
-
return {
|
|
2513
|
-
metaVersion: '1',
|
|
2514
|
-
name: schemaMeta.name,
|
|
2515
|
-
version: schemaMeta.version,
|
|
2516
|
-
authors: schemaMeta.authors,
|
|
2517
|
-
copyrightInformation: schemaMeta.copyrightInformation,
|
|
2518
|
-
contactInformation: schemaMeta.contactInformation,
|
|
2519
|
-
references: schemaMeta.references,
|
|
2520
|
-
thirdPartyLicenses: schemaMeta.thirdPartyLicenses,
|
|
2521
|
-
thumbnailImage,
|
|
2522
|
-
licenseUrl: schemaMeta.licenseUrl,
|
|
2523
|
-
avatarPermission: schemaMeta.avatarPermission,
|
|
2524
|
-
allowExcessivelyViolentUsage: schemaMeta.allowExcessivelyViolentUsage,
|
|
2525
|
-
allowExcessivelySexualUsage: schemaMeta.allowExcessivelySexualUsage,
|
|
2526
|
-
commercialUsage: schemaMeta.commercialUsage,
|
|
2527
|
-
allowPoliticalOrReligiousUsage: schemaMeta.allowPoliticalOrReligiousUsage,
|
|
2528
|
-
allowAntisocialOrHateUsage: schemaMeta.allowAntisocialOrHateUsage,
|
|
2529
|
-
creditNotation: schemaMeta.creditNotation,
|
|
2530
|
-
allowRedistribution: schemaMeta.allowRedistribution,
|
|
2531
|
-
modification: schemaMeta.modification,
|
|
2532
|
-
otherLicenseUrl: schemaMeta.otherLicenseUrl,
|
|
2533
|
-
};
|
|
2534
|
-
});
|
|
2535
|
-
}
|
|
2536
|
-
_v0Import(gltf) {
|
|
2537
|
-
var _a;
|
|
2538
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2539
|
-
const json = this.parser.json;
|
|
2540
|
-
// early abort if it doesn't use vrm
|
|
2541
|
-
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
2542
|
-
if (!vrmExt) {
|
|
2543
|
-
return null;
|
|
2544
|
-
}
|
|
2545
|
-
const schemaMeta = vrmExt.meta;
|
|
2546
|
-
if (!schemaMeta) {
|
|
2547
|
-
return null;
|
|
2548
|
-
}
|
|
2549
|
-
// throw an error if acceptV0Meta is false
|
|
2550
|
-
if (!this.acceptV0Meta) {
|
|
2551
|
-
throw new Error('VRMMetaLoaderPlugin: Attempted to load VRM0.X meta but acceptV0Meta is false');
|
|
2552
|
-
}
|
|
2553
|
-
// load thumbnail texture
|
|
2554
|
-
let texture;
|
|
2555
|
-
if (this.needThumbnailImage && schemaMeta.texture != null && schemaMeta.texture !== -1) {
|
|
2556
|
-
texture = yield this.parser.getDependency('texture', schemaMeta.texture);
|
|
2557
|
-
}
|
|
2558
|
-
return {
|
|
2559
|
-
metaVersion: '0',
|
|
2560
|
-
allowedUserName: schemaMeta.allowedUserName,
|
|
2561
|
-
author: schemaMeta.author,
|
|
2562
|
-
commercialUssageName: schemaMeta.commercialUssageName,
|
|
2563
|
-
contactInformation: schemaMeta.contactInformation,
|
|
2564
|
-
licenseName: schemaMeta.licenseName,
|
|
2565
|
-
otherLicenseUrl: schemaMeta.otherLicenseUrl,
|
|
2566
|
-
otherPermissionUrl: schemaMeta.otherPermissionUrl,
|
|
2567
|
-
reference: schemaMeta.reference,
|
|
2568
|
-
sexualUssageName: schemaMeta.sexualUssageName,
|
|
2569
|
-
texture: texture !== null && texture !== void 0 ? texture : undefined,
|
|
2570
|
-
title: schemaMeta.title,
|
|
2571
|
-
version: schemaMeta.version,
|
|
2572
|
-
violentUssageName: schemaMeta.violentUssageName,
|
|
2573
|
-
};
|
|
2574
|
-
});
|
|
2575
|
-
}
|
|
2576
|
-
_extractGLTFImage(index) {
|
|
2577
|
-
var _a;
|
|
2578
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2579
|
-
const json = this.parser.json;
|
|
2580
|
-
const source = (_a = json.images) === null || _a === void 0 ? void 0 : _a[index];
|
|
2581
|
-
if (source == null) {
|
|
2582
|
-
console.warn(`VRMMetaLoaderPlugin: Attempt to use images[${index}] of glTF as a thumbnail but the image doesn't exist`);
|
|
2583
|
-
return null;
|
|
2584
|
-
}
|
|
2585
|
-
// Ref: https://github.com/mrdoob/three.js/blob/r124/examples/jsm/loaders/GLTFLoader.js#L2467
|
|
2586
|
-
// `source.uri` might be a reference to a file
|
|
2587
|
-
let sourceURI = source.uri;
|
|
2588
|
-
// Load the binary as a blob
|
|
2589
|
-
if (source.bufferView != null) {
|
|
2590
|
-
const bufferView = yield this.parser.getDependency('bufferView', source.bufferView);
|
|
2591
|
-
const blob = new Blob([bufferView], { type: source.mimeType });
|
|
2592
|
-
sourceURI = URL.createObjectURL(blob);
|
|
2593
|
-
}
|
|
2594
|
-
if (sourceURI == null) {
|
|
2595
|
-
console.warn(`VRMMetaLoaderPlugin: Attempt to use images[${index}] of glTF as a thumbnail but the image couldn't load properly`);
|
|
2596
|
-
return null;
|
|
2597
|
-
}
|
|
2598
|
-
const loader = new THREE__namespace.ImageLoader();
|
|
2599
|
-
return yield loader.loadAsync(resolveURL(sourceURI, this.parser.options.path)).catch((error) => {
|
|
2600
|
-
console.error(error);
|
|
2601
|
-
console.warn('VRMMetaLoaderPlugin: Failed to load a thumbnail image');
|
|
2602
|
-
return null;
|
|
2603
|
-
});
|
|
2604
|
-
});
|
|
2605
|
-
}
|
|
2448
|
+
/**
|
|
2449
|
+
* A plugin of GLTFLoader that imports a {@link VRM1Meta} from a VRM extension of a GLTF.
|
|
2450
|
+
*/
|
|
2451
|
+
class VRMMetaLoaderPlugin {
|
|
2452
|
+
constructor(parser, options) {
|
|
2453
|
+
var _a, _b, _c;
|
|
2454
|
+
this.parser = parser;
|
|
2455
|
+
this.needThumbnailImage = (_a = options === null || options === void 0 ? void 0 : options.needThumbnailImage) !== null && _a !== void 0 ? _a : true;
|
|
2456
|
+
this.acceptLicenseUrls = (_b = options === null || options === void 0 ? void 0 : options.acceptLicenseUrls) !== null && _b !== void 0 ? _b : ['https://vrm.dev/licenses/1.0/'];
|
|
2457
|
+
this.acceptV0Meta = (_c = options === null || options === void 0 ? void 0 : options.acceptV0Meta) !== null && _c !== void 0 ? _c : true;
|
|
2458
|
+
}
|
|
2459
|
+
get name() {
|
|
2460
|
+
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
2461
|
+
return 'VRMMetaLoaderPlugin';
|
|
2462
|
+
}
|
|
2463
|
+
afterRoot(gltf) {
|
|
2464
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2465
|
+
gltf.userData.vrmMeta = yield this._import(gltf);
|
|
2466
|
+
});
|
|
2467
|
+
}
|
|
2468
|
+
_import(gltf) {
|
|
2469
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2470
|
+
const v1Result = yield this._v1Import(gltf);
|
|
2471
|
+
if (v1Result != null) {
|
|
2472
|
+
return v1Result;
|
|
2473
|
+
}
|
|
2474
|
+
const v0Result = yield this._v0Import(gltf);
|
|
2475
|
+
if (v0Result != null) {
|
|
2476
|
+
return v0Result;
|
|
2477
|
+
}
|
|
2478
|
+
return null;
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
_v1Import(gltf) {
|
|
2482
|
+
var _a, _b, _c;
|
|
2483
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2484
|
+
const json = this.parser.json;
|
|
2485
|
+
// early abort if it doesn't use vrm
|
|
2486
|
+
const isVRMUsed = ((_a = json.extensionsUsed) === null || _a === void 0 ? void 0 : _a.indexOf('VRMC_vrm')) !== -1;
|
|
2487
|
+
if (!isVRMUsed) {
|
|
2488
|
+
return null;
|
|
2489
|
+
}
|
|
2490
|
+
const extension = (_b = json.extensions) === null || _b === void 0 ? void 0 : _b['VRMC_vrm'];
|
|
2491
|
+
if (extension == null) {
|
|
2492
|
+
return null;
|
|
2493
|
+
}
|
|
2494
|
+
const specVersion = extension.specVersion;
|
|
2495
|
+
if (specVersion !== '1.0-beta') {
|
|
2496
|
+
return null;
|
|
2497
|
+
}
|
|
2498
|
+
const schemaMeta = extension.meta;
|
|
2499
|
+
if (!schemaMeta) {
|
|
2500
|
+
return null;
|
|
2501
|
+
}
|
|
2502
|
+
// throw an error if acceptV0Meta is false
|
|
2503
|
+
const licenseUrl = schemaMeta.licenseUrl;
|
|
2504
|
+
const acceptLicenseUrlsSet = new Set(this.acceptLicenseUrls);
|
|
2505
|
+
if (!acceptLicenseUrlsSet.has(licenseUrl)) {
|
|
2506
|
+
throw new Error(`VRMMetaLoaderPlugin: The license url "${licenseUrl}" is not accepted`);
|
|
2507
|
+
}
|
|
2508
|
+
let thumbnailImage = undefined;
|
|
2509
|
+
if (this.needThumbnailImage && schemaMeta.thumbnailImage != null) {
|
|
2510
|
+
thumbnailImage = (_c = (yield this._extractGLTFImage(schemaMeta.thumbnailImage))) !== null && _c !== void 0 ? _c : undefined;
|
|
2511
|
+
}
|
|
2512
|
+
return {
|
|
2513
|
+
metaVersion: '1',
|
|
2514
|
+
name: schemaMeta.name,
|
|
2515
|
+
version: schemaMeta.version,
|
|
2516
|
+
authors: schemaMeta.authors,
|
|
2517
|
+
copyrightInformation: schemaMeta.copyrightInformation,
|
|
2518
|
+
contactInformation: schemaMeta.contactInformation,
|
|
2519
|
+
references: schemaMeta.references,
|
|
2520
|
+
thirdPartyLicenses: schemaMeta.thirdPartyLicenses,
|
|
2521
|
+
thumbnailImage,
|
|
2522
|
+
licenseUrl: schemaMeta.licenseUrl,
|
|
2523
|
+
avatarPermission: schemaMeta.avatarPermission,
|
|
2524
|
+
allowExcessivelyViolentUsage: schemaMeta.allowExcessivelyViolentUsage,
|
|
2525
|
+
allowExcessivelySexualUsage: schemaMeta.allowExcessivelySexualUsage,
|
|
2526
|
+
commercialUsage: schemaMeta.commercialUsage,
|
|
2527
|
+
allowPoliticalOrReligiousUsage: schemaMeta.allowPoliticalOrReligiousUsage,
|
|
2528
|
+
allowAntisocialOrHateUsage: schemaMeta.allowAntisocialOrHateUsage,
|
|
2529
|
+
creditNotation: schemaMeta.creditNotation,
|
|
2530
|
+
allowRedistribution: schemaMeta.allowRedistribution,
|
|
2531
|
+
modification: schemaMeta.modification,
|
|
2532
|
+
otherLicenseUrl: schemaMeta.otherLicenseUrl,
|
|
2533
|
+
};
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
_v0Import(gltf) {
|
|
2537
|
+
var _a;
|
|
2538
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2539
|
+
const json = this.parser.json;
|
|
2540
|
+
// early abort if it doesn't use vrm
|
|
2541
|
+
const vrmExt = (_a = json.extensions) === null || _a === void 0 ? void 0 : _a.VRM;
|
|
2542
|
+
if (!vrmExt) {
|
|
2543
|
+
return null;
|
|
2544
|
+
}
|
|
2545
|
+
const schemaMeta = vrmExt.meta;
|
|
2546
|
+
if (!schemaMeta) {
|
|
2547
|
+
return null;
|
|
2548
|
+
}
|
|
2549
|
+
// throw an error if acceptV0Meta is false
|
|
2550
|
+
if (!this.acceptV0Meta) {
|
|
2551
|
+
throw new Error('VRMMetaLoaderPlugin: Attempted to load VRM0.X meta but acceptV0Meta is false');
|
|
2552
|
+
}
|
|
2553
|
+
// load thumbnail texture
|
|
2554
|
+
let texture;
|
|
2555
|
+
if (this.needThumbnailImage && schemaMeta.texture != null && schemaMeta.texture !== -1) {
|
|
2556
|
+
texture = yield this.parser.getDependency('texture', schemaMeta.texture);
|
|
2557
|
+
}
|
|
2558
|
+
return {
|
|
2559
|
+
metaVersion: '0',
|
|
2560
|
+
allowedUserName: schemaMeta.allowedUserName,
|
|
2561
|
+
author: schemaMeta.author,
|
|
2562
|
+
commercialUssageName: schemaMeta.commercialUssageName,
|
|
2563
|
+
contactInformation: schemaMeta.contactInformation,
|
|
2564
|
+
licenseName: schemaMeta.licenseName,
|
|
2565
|
+
otherLicenseUrl: schemaMeta.otherLicenseUrl,
|
|
2566
|
+
otherPermissionUrl: schemaMeta.otherPermissionUrl,
|
|
2567
|
+
reference: schemaMeta.reference,
|
|
2568
|
+
sexualUssageName: schemaMeta.sexualUssageName,
|
|
2569
|
+
texture: texture !== null && texture !== void 0 ? texture : undefined,
|
|
2570
|
+
title: schemaMeta.title,
|
|
2571
|
+
version: schemaMeta.version,
|
|
2572
|
+
violentUssageName: schemaMeta.violentUssageName,
|
|
2573
|
+
};
|
|
2574
|
+
});
|
|
2575
|
+
}
|
|
2576
|
+
_extractGLTFImage(index) {
|
|
2577
|
+
var _a;
|
|
2578
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2579
|
+
const json = this.parser.json;
|
|
2580
|
+
const source = (_a = json.images) === null || _a === void 0 ? void 0 : _a[index];
|
|
2581
|
+
if (source == null) {
|
|
2582
|
+
console.warn(`VRMMetaLoaderPlugin: Attempt to use images[${index}] of glTF as a thumbnail but the image doesn't exist`);
|
|
2583
|
+
return null;
|
|
2584
|
+
}
|
|
2585
|
+
// Ref: https://github.com/mrdoob/three.js/blob/r124/examples/jsm/loaders/GLTFLoader.js#L2467
|
|
2586
|
+
// `source.uri` might be a reference to a file
|
|
2587
|
+
let sourceURI = source.uri;
|
|
2588
|
+
// Load the binary as a blob
|
|
2589
|
+
if (source.bufferView != null) {
|
|
2590
|
+
const bufferView = yield this.parser.getDependency('bufferView', source.bufferView);
|
|
2591
|
+
const blob = new Blob([bufferView], { type: source.mimeType });
|
|
2592
|
+
sourceURI = URL.createObjectURL(blob);
|
|
2593
|
+
}
|
|
2594
|
+
if (sourceURI == null) {
|
|
2595
|
+
console.warn(`VRMMetaLoaderPlugin: Attempt to use images[${index}] of glTF as a thumbnail but the image couldn't load properly`);
|
|
2596
|
+
return null;
|
|
2597
|
+
}
|
|
2598
|
+
const loader = new THREE__namespace.ImageLoader();
|
|
2599
|
+
return yield loader.loadAsync(resolveURL(sourceURI, this.parser.options.path)).catch((error) => {
|
|
2600
|
+
console.error(error);
|
|
2601
|
+
console.warn('VRMMetaLoaderPlugin: Failed to load a thumbnail image');
|
|
2602
|
+
return null;
|
|
2603
|
+
});
|
|
2604
|
+
});
|
|
2605
|
+
}
|
|
2606
2606
|
}
|
|
2607
2607
|
|
|
2608
|
-
/**
|
|
2609
|
-
* A class that represents a single VRM model.
|
|
2610
|
-
* This class only includes core spec of the VRM (`VRMC_vrm`).
|
|
2611
|
-
*/
|
|
2612
|
-
class VRMCore {
|
|
2613
|
-
/**
|
|
2614
|
-
* Create a new VRM instance.
|
|
2615
|
-
*
|
|
2616
|
-
* @param params [[VRMParameters]] that represents components of the VRM
|
|
2617
|
-
*/
|
|
2618
|
-
constructor(params) {
|
|
2619
|
-
this.scene = params.scene;
|
|
2620
|
-
this.humanoid = params.humanoid;
|
|
2621
|
-
this.expressionManager = params.expressionManager;
|
|
2622
|
-
this.firstPerson = params.firstPerson;
|
|
2623
|
-
this.lookAt = params.lookAt;
|
|
2624
|
-
this.meta = params.meta;
|
|
2625
|
-
}
|
|
2626
|
-
/**
|
|
2627
|
-
* **You need to call this on your update loop.**
|
|
2628
|
-
*
|
|
2629
|
-
* This function updates every VRM components.
|
|
2630
|
-
*
|
|
2631
|
-
* @param delta deltaTime
|
|
2632
|
-
*/
|
|
2633
|
-
update(delta) {
|
|
2634
|
-
if (this.lookAt) {
|
|
2635
|
-
this.lookAt.update(delta);
|
|
2636
|
-
}
|
|
2637
|
-
if (this.expressionManager) {
|
|
2638
|
-
this.expressionManager.update();
|
|
2639
|
-
}
|
|
2640
|
-
}
|
|
2608
|
+
/**
|
|
2609
|
+
* A class that represents a single VRM model.
|
|
2610
|
+
* This class only includes core spec of the VRM (`VRMC_vrm`).
|
|
2611
|
+
*/
|
|
2612
|
+
class VRMCore {
|
|
2613
|
+
/**
|
|
2614
|
+
* Create a new VRM instance.
|
|
2615
|
+
*
|
|
2616
|
+
* @param params [[VRMParameters]] that represents components of the VRM
|
|
2617
|
+
*/
|
|
2618
|
+
constructor(params) {
|
|
2619
|
+
this.scene = params.scene;
|
|
2620
|
+
this.humanoid = params.humanoid;
|
|
2621
|
+
this.expressionManager = params.expressionManager;
|
|
2622
|
+
this.firstPerson = params.firstPerson;
|
|
2623
|
+
this.lookAt = params.lookAt;
|
|
2624
|
+
this.meta = params.meta;
|
|
2625
|
+
}
|
|
2626
|
+
/**
|
|
2627
|
+
* **You need to call this on your update loop.**
|
|
2628
|
+
*
|
|
2629
|
+
* This function updates every VRM components.
|
|
2630
|
+
*
|
|
2631
|
+
* @param delta deltaTime
|
|
2632
|
+
*/
|
|
2633
|
+
update(delta) {
|
|
2634
|
+
if (this.lookAt) {
|
|
2635
|
+
this.lookAt.update(delta);
|
|
2636
|
+
}
|
|
2637
|
+
if (this.expressionManager) {
|
|
2638
|
+
this.expressionManager.update();
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
2641
|
}
|
|
2642
2642
|
|
|
2643
|
-
class VRMCoreLoaderPlugin {
|
|
2644
|
-
constructor(parser, options) {
|
|
2645
|
-
var _a, _b, _c, _d, _e;
|
|
2646
|
-
this.parser = parser;
|
|
2647
|
-
const helperRoot = options === null || options === void 0 ? void 0 : options.helperRoot;
|
|
2648
|
-
this.expressionPlugin = (_a = options === null || options === void 0 ? void 0 : options.expressionPlugin) !== null && _a !== void 0 ? _a : new VRMExpressionLoaderPlugin(parser);
|
|
2649
|
-
this.firstPersonPlugin = (_b = options === null || options === void 0 ? void 0 : options.firstPersonPlugin) !== null && _b !== void 0 ? _b : new VRMFirstPersonLoaderPlugin(parser);
|
|
2650
|
-
this.humanoidPlugin = (_c = options === null || options === void 0 ? void 0 : options.humanoidPlugin) !== null && _c !== void 0 ? _c : new VRMHumanoidLoaderPlugin(parser);
|
|
2651
|
-
this.lookAtPlugin = (_d = options === null || options === void 0 ? void 0 : options.lookAtPlugin) !== null && _d !== void 0 ? _d : new VRMLookAtLoaderPlugin(parser, { helperRoot });
|
|
2652
|
-
this.metaPlugin = (_e = options === null || options === void 0 ? void 0 : options.metaPlugin) !== null && _e !== void 0 ? _e : new VRMMetaLoaderPlugin(parser);
|
|
2653
|
-
}
|
|
2654
|
-
get name() {
|
|
2655
|
-
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
2656
|
-
return 'VRMC_vrm';
|
|
2657
|
-
}
|
|
2658
|
-
afterRoot(gltf) {
|
|
2659
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2660
|
-
yield this.metaPlugin.afterRoot(gltf);
|
|
2661
|
-
yield this.humanoidPlugin.afterRoot(gltf);
|
|
2662
|
-
yield this.expressionPlugin.afterRoot(gltf);
|
|
2663
|
-
yield this.lookAtPlugin.afterRoot(gltf);
|
|
2664
|
-
yield this.firstPersonPlugin.afterRoot(gltf);
|
|
2665
|
-
const vrmCore = new VRMCore({
|
|
2666
|
-
scene: gltf.scene,
|
|
2667
|
-
expressionManager: gltf.userData.vrmExpressionManager,
|
|
2668
|
-
firstPerson: gltf.userData.vrmFirstPerson,
|
|
2669
|
-
humanoid: gltf.userData.vrmHumanoid,
|
|
2670
|
-
lookAt: gltf.userData.vrmLookAt,
|
|
2671
|
-
meta: gltf.userData.vrmMeta,
|
|
2672
|
-
});
|
|
2673
|
-
gltf.userData.vrmCore = vrmCore;
|
|
2674
|
-
});
|
|
2675
|
-
}
|
|
2643
|
+
class VRMCoreLoaderPlugin {
|
|
2644
|
+
constructor(parser, options) {
|
|
2645
|
+
var _a, _b, _c, _d, _e;
|
|
2646
|
+
this.parser = parser;
|
|
2647
|
+
const helperRoot = options === null || options === void 0 ? void 0 : options.helperRoot;
|
|
2648
|
+
this.expressionPlugin = (_a = options === null || options === void 0 ? void 0 : options.expressionPlugin) !== null && _a !== void 0 ? _a : new VRMExpressionLoaderPlugin(parser);
|
|
2649
|
+
this.firstPersonPlugin = (_b = options === null || options === void 0 ? void 0 : options.firstPersonPlugin) !== null && _b !== void 0 ? _b : new VRMFirstPersonLoaderPlugin(parser);
|
|
2650
|
+
this.humanoidPlugin = (_c = options === null || options === void 0 ? void 0 : options.humanoidPlugin) !== null && _c !== void 0 ? _c : new VRMHumanoidLoaderPlugin(parser);
|
|
2651
|
+
this.lookAtPlugin = (_d = options === null || options === void 0 ? void 0 : options.lookAtPlugin) !== null && _d !== void 0 ? _d : new VRMLookAtLoaderPlugin(parser, { helperRoot });
|
|
2652
|
+
this.metaPlugin = (_e = options === null || options === void 0 ? void 0 : options.metaPlugin) !== null && _e !== void 0 ? _e : new VRMMetaLoaderPlugin(parser);
|
|
2653
|
+
}
|
|
2654
|
+
get name() {
|
|
2655
|
+
// We should use the extension name instead but we have multiple plugins for an extension...
|
|
2656
|
+
return 'VRMC_vrm';
|
|
2657
|
+
}
|
|
2658
|
+
afterRoot(gltf) {
|
|
2659
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2660
|
+
yield this.metaPlugin.afterRoot(gltf);
|
|
2661
|
+
yield this.humanoidPlugin.afterRoot(gltf);
|
|
2662
|
+
yield this.expressionPlugin.afterRoot(gltf);
|
|
2663
|
+
yield this.lookAtPlugin.afterRoot(gltf);
|
|
2664
|
+
yield this.firstPersonPlugin.afterRoot(gltf);
|
|
2665
|
+
const vrmCore = new VRMCore({
|
|
2666
|
+
scene: gltf.scene,
|
|
2667
|
+
expressionManager: gltf.userData.vrmExpressionManager,
|
|
2668
|
+
firstPerson: gltf.userData.vrmFirstPerson,
|
|
2669
|
+
humanoid: gltf.userData.vrmHumanoid,
|
|
2670
|
+
lookAt: gltf.userData.vrmLookAt,
|
|
2671
|
+
meta: gltf.userData.vrmMeta,
|
|
2672
|
+
});
|
|
2673
|
+
gltf.userData.vrmCore = vrmCore;
|
|
2674
|
+
});
|
|
2675
|
+
}
|
|
2676
2676
|
}
|
|
2677
2677
|
|
|
2678
2678
|
exports.VRMCore = VRMCore;
|