@zephyr3d/scene 0.9.3 → 0.9.5

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.
Files changed (188) hide show
  1. package/dist/animation/animationmask.js +224 -0
  2. package/dist/animation/animationmask.js.map +1 -0
  3. package/dist/animation/animationset.js +268 -67
  4. package/dist/animation/animationset.js.map +1 -1
  5. package/dist/animation/animationtrack.js.map +1 -1
  6. package/dist/animation/eulerrotationtrack.js +3 -0
  7. package/dist/animation/eulerrotationtrack.js.map +1 -1
  8. package/dist/animation/fixed_geometry_cache_track.js +7 -0
  9. package/dist/animation/fixed_geometry_cache_track.js.map +1 -1
  10. package/dist/animation/ik_modifier.js.map +1 -1
  11. package/dist/animation/joint_dynamics/collision.js +6 -4
  12. package/dist/animation/joint_dynamics/collision.js.map +1 -1
  13. package/dist/animation/joint_dynamics/controller.js +390 -43
  14. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  15. package/dist/animation/joint_dynamics/joint_dynamics_system.js +62 -3
  16. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  17. package/dist/animation/joint_dynamics/solver.js +4 -3
  18. package/dist/animation/joint_dynamics/solver.js.map +1 -1
  19. package/dist/animation/joint_dynamics/types.js.map +1 -1
  20. package/dist/animation/joint_dynamics_modifier.js +2 -0
  21. package/dist/animation/joint_dynamics_modifier.js.map +1 -1
  22. package/dist/animation/morphtarget.js +3 -72
  23. package/dist/animation/morphtarget.js.map +1 -1
  24. package/dist/animation/morphtrack.js +3 -0
  25. package/dist/animation/morphtrack.js.map +1 -1
  26. package/dist/animation/pca_geometry_cache_track.js +3 -0
  27. package/dist/animation/pca_geometry_cache_track.js.map +1 -1
  28. package/dist/animation/proptrack.js +3 -0
  29. package/dist/animation/proptrack.js.map +1 -1
  30. package/dist/animation/rotationtrack.js +3 -0
  31. package/dist/animation/rotationtrack.js.map +1 -1
  32. package/dist/animation/scaletrack.js +3 -0
  33. package/dist/animation/scaletrack.js.map +1 -1
  34. package/dist/animation/skeleton.js +254 -79
  35. package/dist/animation/skeleton.js.map +1 -1
  36. package/dist/animation/skeleton_modifier.js.map +1 -1
  37. package/dist/animation/spring/spring_particle.js +4 -4
  38. package/dist/animation/spring/spring_particle.js.map +1 -1
  39. package/dist/animation/spring_modifier.js.map +1 -1
  40. package/dist/animation/translationtrack.js +3 -0
  41. package/dist/animation/translationtrack.js.map +1 -1
  42. package/dist/app/engine.js +129 -17
  43. package/dist/app/engine.js.map +1 -1
  44. package/dist/app/scriptregistry.js +251 -80
  45. package/dist/app/scriptregistry.js.map +1 -1
  46. package/dist/asset/assetmanager.js +133 -102
  47. package/dist/asset/assetmanager.js.map +1 -1
  48. package/dist/asset/loaders/gltf/gltf_loader.js +18 -14
  49. package/dist/asset/loaders/gltf/gltf_loader.js.map +1 -1
  50. package/dist/asset/loaders/loader.js +1 -6
  51. package/dist/asset/loaders/loader.js.map +1 -1
  52. package/dist/asset/loaders/zabc/zabc_loader.js.map +1 -1
  53. package/dist/asset/model.js +1052 -183
  54. package/dist/asset/model.js.map +1 -1
  55. package/dist/avatar/wardrobe.js +474 -0
  56. package/dist/avatar/wardrobe.js.map +1 -0
  57. package/dist/index.d.ts +1810 -233
  58. package/dist/index.js +13 -3
  59. package/dist/index.js.map +1 -1
  60. package/dist/material/material.js +1 -1
  61. package/dist/material/material.js.map +1 -1
  62. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +6 -3
  63. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  64. package/dist/material/mixins/pbr/common.js +72 -15
  65. package/dist/material/mixins/pbr/common.js.map +1 -1
  66. package/dist/material/msdf_text.js +180 -0
  67. package/dist/material/msdf_text.js.map +1 -0
  68. package/dist/material/msdf_text_sprite.js +64 -0
  69. package/dist/material/msdf_text_sprite.js.map +1 -0
  70. package/dist/material/pbrblueprint.js +21 -6
  71. package/dist/material/pbrblueprint.js.map +1 -1
  72. package/dist/material/shader/helper.js +277 -277
  73. package/dist/material/shader/helper.js.map +1 -1
  74. package/dist/material/sprite_sdf.js +106 -0
  75. package/dist/material/sprite_sdf.js.map +1 -0
  76. package/dist/posteffect/ssr.js +1 -0
  77. package/dist/posteffect/ssr.js.map +1 -1
  78. package/dist/render/abuffer_oit.js +1 -0
  79. package/dist/render/abuffer_oit.js.map +1 -1
  80. package/dist/render/cluster_light.js.map +1 -1
  81. package/dist/render/cull_visitor.js +28 -0
  82. package/dist/render/cull_visitor.js.map +1 -1
  83. package/dist/render/drawable.js.map +1 -1
  84. package/dist/render/drawable_mixin.js +1 -0
  85. package/dist/render/drawable_mixin.js.map +1 -1
  86. package/dist/render/envlight.js +54 -1
  87. package/dist/render/envlight.js.map +1 -1
  88. package/dist/render/fbm_wavegenerator.js +1 -0
  89. package/dist/render/fbm_wavegenerator.js.map +1 -1
  90. package/dist/render/gerstner_wavegenerator.js +1 -0
  91. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  92. package/dist/render/globalbindgroup_allocator.js +1 -0
  93. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  94. package/dist/render/render_queue.js +1 -0
  95. package/dist/render/render_queue.js.map +1 -1
  96. package/dist/render/sky.js +9 -0
  97. package/dist/render/sky.js.map +1 -1
  98. package/dist/scene/basesprite.js +1 -5
  99. package/dist/scene/basesprite.js.map +1 -1
  100. package/dist/scene/batchgroup.js +18 -8
  101. package/dist/scene/batchgroup.js.map +1 -1
  102. package/dist/scene/environment.js +16 -2
  103. package/dist/scene/environment.js.map +1 -1
  104. package/dist/scene/mesh.js +164 -102
  105. package/dist/scene/mesh.js.map +1 -1
  106. package/dist/scene/meshdrawable.js +96 -0
  107. package/dist/scene/meshdrawable.js.map +1 -0
  108. package/dist/scene/msdftext.js +341 -0
  109. package/dist/scene/msdftext.js.map +1 -0
  110. package/dist/scene/msdftextsprite.js +349 -0
  111. package/dist/scene/msdftextsprite.js.map +1 -0
  112. package/dist/scene/particlesys.js +1 -0
  113. package/dist/scene/particlesys.js.map +1 -1
  114. package/dist/scene/scene.js +2 -0
  115. package/dist/scene/scene.js.map +1 -1
  116. package/dist/scene/scene_node.js +437 -230
  117. package/dist/scene/scene_node.js.map +1 -1
  118. package/dist/scene/sdftextsprite.js +322 -0
  119. package/dist/scene/sdftextsprite.js.map +1 -0
  120. package/dist/scene/terrain-cm/grass.js +1 -0
  121. package/dist/scene/terrain-cm/grass.js.map +1 -1
  122. package/dist/scene/terrain-cm/grassmaterial.js +1 -0
  123. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  124. package/dist/scene/terrain-cm/terrain-cm.js +1 -0
  125. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  126. package/dist/scene/textsprite.js +120 -0
  127. package/dist/scene/textsprite.js.map +1 -0
  128. package/dist/scene/water.js +1 -0
  129. package/dist/scene/water.js.map +1 -1
  130. package/dist/shadow/shader.js +1 -0
  131. package/dist/shadow/shader.js.map +1 -1
  132. package/dist/text/font/font_asset.js +884 -0
  133. package/dist/text/font/font_asset.js.map +1 -0
  134. package/dist/text/font/sfnt_reader.js +43 -0
  135. package/dist/text/font/sfnt_reader.js.map +1 -0
  136. package/dist/text/msdf/generator.js +417 -0
  137. package/dist/text/msdf/generator.js.map +1 -0
  138. package/dist/text/msdf/shape.js +146 -0
  139. package/dist/text/msdf/shape.js.map +1 -0
  140. package/dist/text/runtime/msdf_glyph_atlas.js +146 -0
  141. package/dist/text/runtime/msdf_glyph_atlas.js.map +1 -0
  142. package/dist/text/runtime/msdf_text_atlas_manager.js +50 -0
  143. package/dist/text/runtime/msdf_text_atlas_manager.js.map +1 -0
  144. package/dist/text/runtime/text_layout.js +96 -0
  145. package/dist/text/runtime/text_layout.js.map +1 -0
  146. package/dist/utility/blueprint/material/inputs.js +188 -1
  147. package/dist/utility/blueprint/material/inputs.js.map +1 -1
  148. package/dist/utility/blueprint/material/ir.js +16 -1
  149. package/dist/utility/blueprint/material/ir.js.map +1 -1
  150. package/dist/utility/blueprint/material/pbr.js +32 -0
  151. package/dist/utility/blueprint/material/pbr.js.map +1 -1
  152. package/dist/utility/bounding_volume.js.map +1 -1
  153. package/dist/utility/pmrem.js +34 -8
  154. package/dist/utility/pmrem.js.map +1 -1
  155. package/dist/utility/serialization/manager.js +340 -283
  156. package/dist/utility/serialization/manager.js.map +1 -1
  157. package/dist/utility/serialization/scene/animation.js +702 -7
  158. package/dist/utility/serialization/scene/animation.js.map +1 -1
  159. package/dist/utility/serialization/scene/batch.js +2 -0
  160. package/dist/utility/serialization/scene/batch.js.map +1 -1
  161. package/dist/utility/serialization/scene/camera.js +2 -0
  162. package/dist/utility/serialization/scene/camera.js.map +1 -1
  163. package/dist/utility/serialization/scene/light.js +2 -0
  164. package/dist/utility/serialization/scene/light.js.map +1 -1
  165. package/dist/utility/serialization/scene/material.js +1 -0
  166. package/dist/utility/serialization/scene/material.js.map +1 -1
  167. package/dist/utility/serialization/scene/mesh.js +66 -4
  168. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  169. package/dist/utility/serialization/scene/node.js +189 -8
  170. package/dist/utility/serialization/scene/node.js.map +1 -1
  171. package/dist/utility/serialization/scene/particle.js +2 -0
  172. package/dist/utility/serialization/scene/particle.js.map +1 -1
  173. package/dist/utility/serialization/scene/primitive.js +1 -0
  174. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  175. package/dist/utility/serialization/scene/sprite.js +2 -0
  176. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  177. package/dist/utility/serialization/scene/terrain.js +2 -0
  178. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  179. package/dist/utility/serialization/scene/text.js +516 -0
  180. package/dist/utility/serialization/scene/text.js.map +1 -0
  181. package/dist/utility/serialization/scene/water.js +2 -0
  182. package/dist/utility/serialization/scene/water.js.map +1 -1
  183. package/dist/utility/serialization/types.js.map +1 -1
  184. package/dist/utility/textures/sheenlut.js +138 -0
  185. package/dist/utility/textures/sheenlut.js.map +1 -0
  186. package/dist/values.js +48 -48
  187. package/dist/values.js.map +1 -1
  188. package/package.json +3 -3
@@ -1,36 +1,42 @@
1
- import { Vector3, Quaternion, Matrix4x4, Disposable, DRef } from '@zephyr3d/base';
1
+ import { Vector3, Quaternion, Matrix4x4, Disposable, uint8ArrayToBase64, ASSERT, DRef, Vector4 } from '@zephyr3d/base';
2
+ import { getVertexFormatComponentCount } from '@zephyr3d/device';
2
3
  import { Mesh } from '../scene/mesh.js';
3
- import { SceneNode } from '../scene/scene_node.js';
4
+ import { BoundingBox } from '../utility/bounding_volume.js';
5
+ import { SceneNode, setSceneMeshAssetBinding } from '../scene/scene_node.js';
6
+ import { SkeletonRig, SkinBinding } from '../animation/skeleton.js';
4
7
  import { NodeRotationTrack } from '../animation/rotationtrack.js';
5
8
  import '../animation/eulerrotationtrack.js';
6
9
  import { NodeTranslationTrack } from '../animation/translationtrack.js';
7
10
  import { NodeScaleTrack } from '../animation/scaletrack.js';
8
- import { Skeleton } from '../animation/skeleton.js';
9
11
  import { MorphTargetTrack } from '../animation/morphtrack.js';
10
12
  import { FixedGeometryCacheTrack } from '../animation/fixed_geometry_cache_track.js';
13
+ import { getDevice } from '../app/api.js';
11
14
  import { PCAGeometryCacheTrack } from '../animation/pca_geometry_cache_track.js';
12
- import '@zephyr3d/device';
13
15
  import '../animation/joint_dynamics/collision.js';
14
16
  import '../animation/joint_dynamics/solver.js';
15
- import '../animation/joint_dynamics/joint_dynamics_system.js';
16
- import { processMorphData } from '../animation/morphtarget.js';
17
- import { MAX_MORPH_TARGETS } from '../values.js';
17
+ import { createTransformAccess, JointDynamicsSystem } from '../animation/joint_dynamics/joint_dynamics_system.js';
18
+ import { JointDynamicsModifier } from '../animation/joint_dynamics_modifier.js';
19
+ import { MAX_MORPH_TARGETS, MAX_MORPH_ATTRIBUTES } from '../values.js';
20
+ import { Primitive } from '../render/primitive.js';
21
+ import { UnlitMaterial } from '../material/unlit.js';
22
+ import { PBRSpecularGlossinessMaterial } from '../material/pbrsg.js';
23
+ import { PBRMetallicRoughnessMaterial } from '../material/pbrmr.js';
18
24
 
19
- /**
20
- * Named object interface for model loading
21
- * @public
25
+ /**
26
+ * Named object interface for model loading
27
+ * @public
22
28
  */ class NamedObject {
23
29
  name;
24
- /**
25
- * Creates an instance of NamedObject
26
- * @param name - Name of the object
30
+ /**
31
+ * Creates an instance of NamedObject
32
+ * @param name - Name of the object
27
33
  */ constructor(name){
28
34
  this.name = name;
29
35
  }
30
36
  }
31
- /**
32
- * Heirarchical node interface for model loading
33
- * @public
37
+ /**
38
+ * Heirarchical node interface for model loading
39
+ * @public
34
40
  */ class AssetHierarchyNode extends NamedObject {
35
41
  _parent;
36
42
  _position;
@@ -39,18 +45,18 @@ import { MAX_MORPH_TARGETS } from '../values.js';
39
45
  _mesh;
40
46
  _skeleton;
41
47
  _attachToSkeleton;
42
- _meshAttached;
43
48
  _matrix;
44
49
  _worldMatrix;
45
50
  _weights;
46
51
  _children;
47
52
  _instances;
48
- /**
49
- * Creates an instance of AssetHierarchyNode
50
- * @param name - Name of the node
51
- * @param parent - Parent of the node
52
- */ constructor(name, parent){
53
+ /**
54
+ * Creates an instance of AssetHierarchyNode
55
+ * @param name - Name of the node
56
+ * @param parent - Parent of the node
57
+ */ constructor(name, model, parent){
53
58
  super(name);
59
+ model.nodes.push(this);
54
60
  this._parent = null;
55
61
  this._position = Vector3.zero();
56
62
  this._rotation = Quaternion.identity();
@@ -59,7 +65,6 @@ import { MAX_MORPH_TARGETS } from '../values.js';
59
65
  this._mesh = null;
60
66
  this._skeleton = null;
61
67
  this._attachToSkeleton = null;
62
- this._meshAttached = false;
63
68
  this._matrix = null;
64
69
  this._weights = null;
65
70
  this._worldMatrix = null;
@@ -80,10 +85,9 @@ import { MAX_MORPH_TARGETS } from '../values.js';
80
85
  }
81
86
  set mesh(data) {
82
87
  this._mesh = data;
83
- this.setMeshAttached();
84
88
  }
85
89
  /** instances */ get instances() {
86
- return this._instances ?? null;
90
+ return this._instances;
87
91
  }
88
92
  /** Default morph target weights */ get weights() {
89
93
  return this._weights;
@@ -115,38 +119,41 @@ import { MAX_MORPH_TARGETS } from '../values.js';
115
119
  set scaling(val) {
116
120
  this._scaling = val;
117
121
  }
118
- /** true if the node is parent of a mesh node */ get meshAttached() {
119
- return this._meshAttached;
120
- }
121
122
  /** Children of the node */ get children() {
122
123
  return this._children;
123
124
  }
124
125
  /** The skeleton to which the node belongs if this is a joint node */ get skeletonAttached() {
125
126
  return this._attachToSkeleton;
126
127
  }
127
- /** @internal */ computeTransforms(parentTransform) {
128
+ isParentOf(child) {
129
+ while(child && child !== this){
130
+ child = child.parent;
131
+ }
132
+ return child === this;
133
+ }
134
+ getWorldPosition() {
135
+ return new Vector3(this.worldMatrix.m03, this.worldMatrix.m13, this.worldMatrix.m23);
136
+ }
137
+ computeTransforms(parentTransform) {
128
138
  this._matrix = Matrix4x4.scaling(this._scaling).rotateLeft(this._rotation).translateLeft(this._position);
129
139
  this._worldMatrix = parentTransform ? Matrix4x4.multiply(parentTransform, this._matrix) : new Matrix4x4(this._matrix);
130
140
  for (const child of this._children){
131
141
  child.computeTransforms(this._worldMatrix);
132
142
  }
133
143
  }
134
- /**
135
- * Adds a child to this node
136
- * @param child - The child node to be added
144
+ /**
145
+ * Adds a child to this node
146
+ * @param child - The child node to be added
137
147
  */ addChild(child) {
138
148
  if (!child || child.parent) {
139
149
  throw new Error('AssetHierarchyNode.addChild(): invalid child node');
140
150
  }
141
151
  this._children.push(child);
142
152
  child._parent = this;
143
- if (child.meshAttached) {
144
- this.setMeshAttached();
145
- }
146
153
  }
147
- /**
148
- * Removes a child of this node
149
- * @param child - The child node to be removed
154
+ /**
155
+ * Removes a child of this node
156
+ * @param child - The child node to be removed
150
157
  */ removeChild(child) {
151
158
  const index = this._children.indexOf(child);
152
159
  if (index < 0) {
@@ -155,32 +162,28 @@ import { MAX_MORPH_TARGETS } from '../values.js';
155
162
  this._children[index]._parent = null;
156
163
  this._children.splice(index, 1);
157
164
  }
158
- /**
159
- * Attach this node to a skeleton
160
- * @param skeleton - The skeleton to which to node will attach
161
- * @param index - The joint index
165
+ /**
166
+ * Attach this node to a skeleton
167
+ * @param skeleton - The skeleton to which to node will attach
168
+ * @param index - The joint index
162
169
  */ attachToSkeleton(skeleton) {
163
170
  if (!this._attachToSkeleton) {
164
171
  this._attachToSkeleton = new Set();
165
172
  }
166
173
  this._attachToSkeleton.add(skeleton);
167
174
  }
168
- /** @internal */ setMeshAttached() {
169
- this._meshAttached = true;
170
- this._parent?.setMeshAttached();
171
- }
172
175
  }
173
- /**
174
- * Skeleton information for model loading
175
- * @public
176
+ /**
177
+ * Skeleton information for model loading
178
+ * @public
176
179
  */ class AssetSkeleton extends NamedObject {
177
180
  /** The pivot node */ pivot;
178
181
  /** Joints of the skeleton */ joints;
179
182
  /** Inverse of the binding matrices of the joints */ inverseBindMatrices;
180
183
  /** Binding pose matrices of the joints */ bindPose;
181
- /**
182
- * Creates an instance of AssetSkeleton
183
- * @param name - Name of the skeleton
184
+ /**
185
+ * Creates an instance of AssetSkeleton
186
+ * @param name - Name of the skeleton
184
187
  */ constructor(name){
185
188
  super(name);
186
189
  this.name = name;
@@ -189,10 +192,10 @@ import { MAX_MORPH_TARGETS } from '../values.js';
189
192
  this.inverseBindMatrices = [];
190
193
  this.bindPose = [];
191
194
  }
192
- /**
193
- * Adds a joint to the skeleton
194
- * @param joint - The joint node
195
- * @param inverseBindMatrix - Inverse binding matrix of the joint
195
+ /**
196
+ * Adds a joint to the skeleton
197
+ * @param joint - The joint node
198
+ * @param inverseBindMatrix - Inverse binding matrix of the joint
196
199
  */ addJoint(joint, inverseBindMatrix) {
197
200
  joint.attachToSkeleton(this);
198
201
  this.joints.push(joint);
@@ -203,59 +206,105 @@ import { MAX_MORPH_TARGETS } from '../values.js';
203
206
  scale: joint.scaling.clone()
204
207
  });
205
208
  }
209
+ get root() {
210
+ if (this.pivot) {
211
+ return this.pivot;
212
+ }
213
+ let root = this.joints[0];
214
+ for(let i = 1; i < this.joints.length; i++){
215
+ while(root && !root.isParentOf(this.joints[i])){
216
+ root = root.parent;
217
+ }
218
+ }
219
+ return root;
220
+ }
206
221
  }
207
- /**
208
- * Scene for model loading
209
- * @public
222
+ /**
223
+ * Scene for model loading
224
+ * @public
210
225
  */ class AssetScene extends NamedObject {
211
226
  /** Root nodes of the scene */ rootNodes;
212
- /**
213
- * Creates an instance of AssetScene
214
- * @param name - Name of the scene
227
+ /**
228
+ * Creates an instance of AssetScene
229
+ * @param name - Name of the scene
215
230
  */ constructor(name){
216
231
  super(name);
217
232
  this.rootNodes = [];
218
233
  }
219
234
  }
220
- /**
221
- * Model information that can be shared by multiple model nodes
222
- * @public
235
+ /**
236
+ * Model information that can be shared by multiple model nodes
237
+ * @public
223
238
  */ class SharedModel extends Disposable {
224
- /** @internal */ _name;
225
- /** @internal */ _skeletons;
226
239
  /** @internal */ _nodes;
240
+ /** @internal */ _skeletons;
227
241
  /** @internal */ _animations;
228
242
  /** @internal */ _scenes;
229
243
  /** @internal */ _activeScene;
230
- /**
231
- * Creates an instance of SharedModel
232
- * @param name - Name of the model
233
- */ constructor(name){
244
+ /** @internal */ _imageList;
245
+ /** @internal */ _primitiveList;
246
+ /** @internal */ _materialList;
247
+ /** @internal */ _springBoneColliders;
248
+ /** @internal */ _springBoneColliderGroups;
249
+ /** @internal */ _springBones;
250
+ /** @internal */ _jointDynamicsColliders;
251
+ /** @internal */ _jointDynamicsSpringBones;
252
+ /** @internal */ _morphTargetGroups;
253
+ /** @internal */ _textureMap;
254
+ /** @internal */ _primitiveMap;
255
+ /**
256
+ * Creates an instance of SharedModel
257
+ */ constructor(){
234
258
  super();
235
- this._name = name || '';
236
- this._skeletons = [];
237
259
  this._nodes = [];
260
+ this._skeletons = [];
238
261
  this._scenes = [];
239
262
  this._animations = [];
263
+ this._imageList = [];
264
+ this._primitiveList = [];
265
+ this._materialList = {};
266
+ this._springBoneColliders = [];
267
+ this._springBoneColliderGroups = [];
268
+ this._springBones = [];
269
+ this._jointDynamicsColliders = [];
270
+ this._jointDynamicsSpringBones = [];
271
+ this._morphTargetGroups = [];
240
272
  this._activeScene = -1;
241
- }
242
- /** Name of the model */ get name() {
243
- return this._name;
244
- }
245
- set name(val) {
246
- this._name = val;
273
+ this._textureMap = new Map();
274
+ this._primitiveMap = new Map();
247
275
  }
248
276
  /** All scenes that the model contains */ get scenes() {
249
277
  return this._scenes;
250
278
  }
279
+ /** All Primitives that the model contains */ get primitives() {
280
+ return this._primitiveList;
281
+ }
282
+ /** All nodes that the model contains */ get nodes() {
283
+ return this._nodes;
284
+ }
251
285
  /** All animations that the model contains */ get animations() {
252
286
  return this._animations;
253
287
  }
254
288
  /** All skeletons that the model contains */ get skeletons() {
255
289
  return this._skeletons;
256
290
  }
257
- /** All nodes that the model contains */ get nodes() {
258
- return this._nodes;
291
+ /** All SpringBone colliders that the model contains */ get springBoneColliders() {
292
+ return this._springBoneColliders;
293
+ }
294
+ /** All SpringBone collider groups that the model contains */ get springBoneColliderGroups() {
295
+ return this._springBoneColliderGroups;
296
+ }
297
+ /** All SpringBone springs that the model contains */ get springBones() {
298
+ return this._springBones;
299
+ }
300
+ /** SpringBone colliders converted to Zephyr3D JointDynamics collider format */ get jointDynamicsColliders() {
301
+ return this._jointDynamicsColliders;
302
+ }
303
+ /** SpringBone springs converted to Zephyr3D JointDynamics system format */ get jointDynamicsSpringBones() {
304
+ return this._jointDynamicsSpringBones;
305
+ }
306
+ /** Model-level morph target groups, commonly facial expressions. */ get morphTargetGroups() {
307
+ return this._morphTargetGroups;
259
308
  }
260
309
  /** The active scene of the model */ get activeScene() {
261
310
  return this._activeScene;
@@ -263,90 +312,379 @@ import { MAX_MORPH_TARGETS } from '../values.js';
263
312
  set activeScene(val) {
264
313
  this._activeScene = val;
265
314
  }
266
- /**
267
- * Adds a node to the scene
268
- * @param parent - Under which node the node should be added
269
- * @param index - Index of the node
270
- * @param name - Name of the node
271
- * @returns The added node
272
- */ addNode(parent, index, name) {
273
- const childNode = new AssetHierarchyNode(name, parent);
274
- this._nodes[index] = childNode;
275
- return childNode;
276
- }
277
- /**
278
- * Adds a skeleton to the scene
279
- * @param skeleton - The skeleton to be added
315
+ getImage(index) {
316
+ return this._imageList[index];
317
+ }
318
+ setImage(index, img) {
319
+ this._imageList[index] = img;
320
+ }
321
+ getMaterial(hash) {
322
+ return this._materialList[hash];
323
+ }
324
+ setMaterial(hash, material) {
325
+ this._materialList[hash] = material;
326
+ }
327
+ addPrimitive(prim) {
328
+ this._primitiveList.push(prim);
329
+ }
330
+ /**
331
+ * Adds a model-level morph target group.
332
+ * @param group - Morph target group to add
333
+ */ addMorphTargetGroup(group) {
334
+ if (group?.name && group.bindings?.length > 0) {
335
+ this._morphTargetGroups.push(group);
336
+ }
337
+ }
338
+ /**
339
+ * Removes all model-level morph target groups.
340
+ */ clearMorphTargetGroups() {
341
+ this._morphTargetGroups = [];
342
+ }
343
+ /**
344
+ * Finds a model-level morph target group by name.
345
+ * @param name - Group name
346
+ * @returns The matching group, or null if not found
347
+ */ getMorphTargetGroup(name) {
348
+ return this._morphTargetGroups.find((group)=>group.name === name) ?? null;
349
+ }
350
+ /**
351
+ * Builds morph target groups by collecting morph target names from every mesh.
352
+ * @returns Generated morph target groups
353
+ */ buildMorphTargetGroupsByName() {
354
+ this.clearMorphTargetGroups();
355
+ const groups = new Map();
356
+ const meshes = new Set();
357
+ for (const node of this._nodes){
358
+ if (node.mesh) {
359
+ meshes.add(node.mesh);
360
+ }
361
+ }
362
+ for (const mesh of meshes){
363
+ const count = getAssetMeshMorphTargetCount(mesh);
364
+ for(let i = 0; i < count; i++){
365
+ const targetName = getAssetMeshMorphTargetName(mesh, i);
366
+ let group = groups.get(targetName);
367
+ if (!group) {
368
+ group = {
369
+ name: targetName,
370
+ bindings: []
371
+ };
372
+ groups.set(targetName, group);
373
+ }
374
+ group.bindings.push({
375
+ mesh,
376
+ targetIndex: i,
377
+ targetName,
378
+ weight: 1
379
+ });
380
+ }
381
+ }
382
+ this._morphTargetGroups = Array.from(groups.values());
383
+ return this._morphTargetGroups;
384
+ }
385
+ /**
386
+ * Adds a skeleton to the scene
387
+ * @param skeleton - The skeleton to be added
280
388
  */ addSkeleton(skeleton) {
281
389
  this._skeletons.push(skeleton);
282
390
  }
283
- /**
284
- * Adds an animation to the scene
285
- * @param animation - The animation to be added
391
+ /**
392
+ * Adds an animation to the scene
393
+ * @param animation - The animation to be added
286
394
  */ addAnimation(animation) {
287
395
  this._animations.push(animation);
288
396
  }
289
- createSceneNode(scene, instancing) {
397
+ static async writePrimitive(vfs, primitive, path) {
398
+ const data = {
399
+ vertices: {},
400
+ indices: primitive.indices ? uint8ArrayToBase64(new Uint8Array(primitive.indices.buffer, primitive.indices.byteOffset, primitive.indices.byteLength)) : null,
401
+ indexType: primitive.indices ? primitive.indices instanceof Uint16Array ? 'u16' : 'u32' : '',
402
+ indexCount: primitive.indexCount,
403
+ type: primitive.type,
404
+ boxMin: [
405
+ primitive.boxMin.x,
406
+ primitive.boxMin.y,
407
+ primitive.boxMin.z
408
+ ],
409
+ boxMax: [
410
+ primitive.boxMax.x,
411
+ primitive.boxMax.y,
412
+ primitive.boxMax.z
413
+ ]
414
+ };
415
+ for(const k in primitive.vertices){
416
+ const v = primitive.vertices[k];
417
+ data.vertices[k] = {
418
+ format: v.format,
419
+ data: uint8ArrayToBase64(new Uint8Array(v.data.buffer, v.data.byteOffset, v.data.byteLength))
420
+ };
421
+ }
422
+ const content = JSON.stringify({
423
+ type: 'Primitive',
424
+ data
425
+ }, null, 2);
426
+ await vfs.writeFile(path, content, {
427
+ encoding: 'utf8',
428
+ create: true
429
+ });
430
+ }
431
+ /** preprocess */ async preprocess(manager, name, destPath, srcVFS, dstVFS) {
432
+ const destName = name;
433
+ if (this._imageList.length > 0) {
434
+ console.info(`Importing ${this._imageList.length} textures`);
435
+ for(let i = 0; i < this._imageList.length; i++){
436
+ const img = this._imageList[i];
437
+ if (!img) {
438
+ continue;
439
+ }
440
+ let ext = '';
441
+ const mimeType = img.uri ? srcVFS.guessMIMEType(img.uri) : img.data ? img.mimeType : '';
442
+ if (mimeType === 'image/jpeg') {
443
+ ext = '.jpg';
444
+ } else if (mimeType === 'image/png') {
445
+ ext = '.png';
446
+ } else if (mimeType === 'image/webp') {
447
+ ext = '.webp';
448
+ } else if (mimeType === 'image/tga') {
449
+ ext = '.tga';
450
+ } else if (mimeType === 'image/vnd.radiance') {
451
+ ext = '.hdr';
452
+ } else if (mimeType === 'image/ktx') {
453
+ ext = '.ktx';
454
+ } else if (mimeType === 'image/ktx2') {
455
+ ext = '.ktx2';
456
+ } else {
457
+ continue;
458
+ }
459
+ ASSERT(!!ext, `Unknown image mime type: ${mimeType}`);
460
+ const path = dstVFS.join(destPath, `${destName}_texture_${i}${ext}`);
461
+ if (img.uri) {
462
+ img.data = new Uint8Array(await srcVFS.readFile(img.uri, {
463
+ encoding: 'binary'
464
+ }));
465
+ }
466
+ await dstVFS.writeFile(path, img.data.buffer.slice(img.data.byteOffset, img.data.byteOffset + img.data.byteLength), {
467
+ encoding: 'binary',
468
+ create: true
469
+ });
470
+ img.uri = path;
471
+ img.data = undefined;
472
+ img.mimeType = '';
473
+ }
474
+ }
475
+ const materialKeys = Object.keys(this._materialList);
476
+ if (materialKeys.length > 0) {
477
+ console.info(`Importing ${materialKeys.length} materials`);
478
+ for (const k of materialKeys){
479
+ const path = dstVFS.join(destPath, `${destName}_material_${k}.zmtl`);
480
+ const m = await this.createMaterial(manager, this._materialList[k], dstVFS);
481
+ const data = await manager.serializeObject(m);
482
+ const content = JSON.stringify({
483
+ type: 'Default',
484
+ data
485
+ }, null, 2);
486
+ await dstVFS.writeFile(path, content, {
487
+ encoding: 'utf8',
488
+ create: true
489
+ });
490
+ this._materialList[k].path = path;
491
+ m.dispose();
492
+ }
493
+ }
494
+ if (this._primitiveList.length > 0) {
495
+ console.info(`Importing ${this._primitiveList.length} meshes`);
496
+ for(let i = 0; i < this._primitiveList.length; i++){
497
+ const info = this._primitiveList[i];
498
+ if (!info) {
499
+ continue;
500
+ }
501
+ const path = dstVFS.join(destPath, `${destName}_mesh_${i}.zmsh`);
502
+ await SharedModel.writePrimitive(dstVFS, info, path);
503
+ info.path = path;
504
+ }
505
+ }
506
+ }
507
+ createJointDynamics(rootNode, nodeMap) {
508
+ if (rootNode.animationSet.rigs.length === 0) {
509
+ return;
510
+ }
511
+ for (const jd of this._jointDynamicsSpringBones){
512
+ const chains = jd.chains.map((chain)=>{
513
+ const start = nodeMap.get(chain.start);
514
+ const end = nodeMap.get(chain.end);
515
+ return {
516
+ start,
517
+ end
518
+ };
519
+ }).filter((chain)=>{
520
+ if (!chain.start || !chain.end) {
521
+ return false;
522
+ }
523
+ return true;
524
+ });
525
+ const rigs = rootNode.animationSet.rigs.filter((ref)=>{
526
+ const joints = ref.get().joints;
527
+ return chains.every((chain)=>joints.includes(chain.start));
528
+ });
529
+ if (rigs.length === 0) {
530
+ continue;
531
+ }
532
+ const colliders = jd.colliders.map((collider)=>{
533
+ const parent = nodeMap.get(collider.node);
534
+ if (!parent) {
535
+ return null;
536
+ }
537
+ const node = new SceneNode(parent.scene);
538
+ node.name = collider.name ? `${collider.name}_JointDynamicsCollider` : 'JointDynamicsCollider';
539
+ node.parent = parent;
540
+ node.position.set(collider.localPosition);
541
+ node.rotation.set(collider.localRotation);
542
+ return {
543
+ r: {
544
+ ...collider.collider
545
+ },
546
+ transform: createTransformAccess(node)
547
+ };
548
+ }).filter((collider)=>{
549
+ return !!collider;
550
+ });
551
+ const flatPlanes = jd.flatPlanes.map((flatPlane)=>{
552
+ const node = nodeMap.get(flatPlane.node);
553
+ if (!node) {
554
+ return null;
555
+ }
556
+ const up = node.worldMatrix.transformVectorAffine(flatPlane.up, new Vector3());
557
+ if (up.magnitudeSq === 0) {
558
+ return null;
559
+ }
560
+ return {
561
+ up: up.inplaceNormalize(),
562
+ position: node.worldMatrix.transformPointAffine(flatPlane.position, new Vector3())
563
+ };
564
+ }).filter((flatPlane)=>{
565
+ return !!flatPlane;
566
+ });
567
+ for (const rig of rigs){
568
+ const system = new JointDynamicsSystem({
569
+ chainConfig: {
570
+ chains,
571
+ systemRoot: rootNode
572
+ },
573
+ controllerConfig: jd.controllerConfig
574
+ }, colliders, [], flatPlanes);
575
+ const modifier = new JointDynamicsModifier(system);
576
+ rig.get().modifiers.push(modifier);
577
+ }
578
+ }
579
+ }
580
+ createMorphTargetGroups(rootNode, meshMap) {
581
+ const runtimeGroups = [];
582
+ for (const group of this._morphTargetGroups){
583
+ const bindings = [];
584
+ for (const binding of group.bindings){
585
+ const assetMesh = binding.node?.mesh ?? binding.mesh ?? null;
586
+ if (!assetMesh) {
587
+ continue;
588
+ }
589
+ for (const subMesh of assetMesh.subMeshes){
590
+ const mesh = meshMap.get(subMesh);
591
+ if (mesh) {
592
+ bindings.push({
593
+ mesh,
594
+ targetIndex: binding.targetIndex,
595
+ targetName: binding.targetName,
596
+ weight: binding.weight
597
+ });
598
+ }
599
+ }
600
+ }
601
+ if (bindings.length > 0) {
602
+ runtimeGroups.push({
603
+ name: group.name,
604
+ bindings,
605
+ isBinary: group.isBinary,
606
+ weight: this.getInitialMorphTargetGroupWeight(bindings, group.isBinary)
607
+ });
608
+ }
609
+ }
610
+ rootNode.morphTargetGroups = runtimeGroups;
611
+ }
612
+ getInitialMorphTargetGroupWeight(bindings, isBinary) {
613
+ let result = 0;
614
+ let found = false;
615
+ for (const binding of bindings){
616
+ if (!binding.weight) {
617
+ continue;
618
+ }
619
+ const targetWeight = this.getMorphTargetBindingWeight(binding);
620
+ if (targetWeight === null) {
621
+ continue;
622
+ }
623
+ const groupWeight = targetWeight / binding.weight;
624
+ if (!found || Math.abs(groupWeight) > Math.abs(result)) {
625
+ result = groupWeight;
626
+ found = true;
627
+ }
628
+ }
629
+ return isBinary ? result > 0.5 ? 1 : 0 : result;
630
+ }
631
+ getMorphTargetBindingWeight(binding) {
632
+ const targetIndex = binding.targetIndex;
633
+ if (typeof targetIndex === 'number' && targetIndex >= 0 && targetIndex < binding.mesh.getNumMorphTargets()) {
634
+ const targetName = binding.mesh.getMorphTargetName(targetIndex);
635
+ if (targetName) {
636
+ return binding.mesh.getMorphWeight(targetName);
637
+ }
638
+ }
639
+ if (binding.targetName) {
640
+ return binding.mesh.getMorphWeight(binding.targetName);
641
+ }
642
+ return null;
643
+ }
644
+ async createSceneNode(manager, scene, instancing, saveMeshes, saveSkeletons, saveAnimations, saveJointDynamics, srcVFS) {
290
645
  const group = new SceneNode(scene);
291
- group.name = this.name;
292
- const animationSet = group.animationSet;
646
+ const nodeMap = new Map();
647
+ const meshMap = new Map();
293
648
  for(let i = 0; i < this.scenes.length; i++){
294
649
  const assetScene = this.scenes[i];
295
650
  const skeletonMeshMap = new Map();
296
- const nodeMap = new Map();
297
651
  for(let k = 0; k < assetScene.rootNodes.length; k++){
298
- this.setAssetNodeToSceneNode(scene, group, assetScene.rootNodes[k], skeletonMeshMap, nodeMap, instancing);
299
- }
300
- for (const animationData of this.animations){
301
- let name = animationData.name ?? `_embbeded_animation`;
302
- if (animationSet.getAnimationClip(name)) {
303
- const baseName = name;
304
- for(let t = 1;; t++){
305
- name = `${baseName}_${t}`;
306
- if (!animationSet.getAnimationClip(name)) {
307
- break;
308
- }
652
+ await this.setAssetNodeToSceneNode(manager, scene, group, assetScene.rootNodes[k], skeletonMeshMap, nodeMap, meshMap, instancing, saveMeshes, saveSkeletons, saveAnimations, srcVFS);
653
+ }
654
+ if (saveSkeletons) {
655
+ for (const sk of this.skeletons){
656
+ if (!skeletonMeshMap.has(sk)) {
657
+ skeletonMeshMap.set(sk, {
658
+ mesh: [],
659
+ bounding: []
660
+ });
309
661
  }
310
662
  }
311
- const animation = animationSet.createAnimation(name, true);
312
- for (const track of animationData.tracks){
313
- if (track.type === 'translation') {
314
- animation.addTrack(nodeMap.get(track.node), new NodeTranslationTrack(track.interpolator, true));
315
- } else if (track.type === 'scale') {
316
- animation.addTrack(nodeMap.get(track.node), new NodeScaleTrack(track.interpolator, true));
317
- } else if (track.type === 'rotation') {
318
- animation.addTrack(nodeMap.get(track.node), new NodeRotationTrack(track.interpolator, true));
319
- } else if (track.type === 'weights') {
320
- for (const m of track.node.mesh.subMeshes){
321
- if (track.interpolator.stride > MAX_MORPH_TARGETS) {
322
- console.error(`Morph target too large: ${track.interpolator.stride}, the maximum is ${MAX_MORPH_TARGETS}`);
323
- } else {
324
- const morphTrack = new MorphTargetTrack(track.interpolator, track.defaultMorphWeights, m.targetBox, m.mesh.getBoundingVolume().toAABB(), true);
325
- animation.addTrack(m.mesh, morphTrack);
326
- }
327
- }
328
- } else if (track.type === 'geometry-cache') {
329
- const subMesh = track.node.mesh?.subMeshes[track.subMeshIndex];
330
- if (!subMesh?.mesh) {
331
- console.error(`Invalid geometry cache sub mesh: ${track.subMeshIndex}`);
332
- } else {
333
- if (track.codec === 'pca') {
334
- const pcaData = this.remapPCAGeometryCacheData(subMesh, track);
335
- animation.addTrack(subMesh.mesh, new PCAGeometryCacheTrack(pcaData, true));
336
- } else {
337
- const frames = this.remapGeometryCacheFrames(subMesh, track.frames);
338
- animation.addTrack(subMesh.mesh, new FixedGeometryCacheTrack(track.times, frames, true));
339
- }
340
- }
341
- } else {
342
- console.error(`Invalid animation track type: ${track.type}`);
663
+ const rigMap = new Map();
664
+ for (const v of skeletonMeshMap){
665
+ const sk = v[0];
666
+ const joints = sk.joints.map((val)=>{
667
+ const node = nodeMap.get(val);
668
+ node.jointTypeT = 'static';
669
+ node.jointTypeS = 'static';
670
+ node.jointTypeR = 'static';
671
+ return node;
672
+ });
673
+ const rootJoint = sk.root ? nodeMap.get(sk.root) ?? null : null;
674
+ const rigKey = SkeletonRig.getRigKey(joints, rootJoint);
675
+ let rig = rigMap.get(rigKey);
676
+ if (!rig) {
677
+ rig = new SkeletonRig(joints, sk.bindPose, {
678
+ rootJoint
679
+ });
680
+ rigMap.set(rigKey, rig);
681
+ group.animationSet.rigs.push(new DRef(rig));
343
682
  }
344
- }
345
- for (const sk of animationData.skeletons){
683
+ const binding = new SkinBinding(rig, sk.inverseBindMatrices, joints);
346
684
  const nodes = skeletonMeshMap.get(sk);
347
685
  if (nodes) {
348
- if (!nodes.skeleton) {
349
- nodes.skeleton = new Skeleton(sk.joints.map((val)=>nodeMap.get(val)), sk.inverseBindMatrices, sk.bindPose);
686
+ if (!nodes.binding) {
687
+ nodes.binding = binding;
350
688
  for(let i = 0; i < nodes.mesh.length; i++){
351
689
  const mesh = nodes.mesh[i];
352
690
  const v = {
@@ -354,22 +692,82 @@ import { MAX_MORPH_TARGETS } from '../values.js';
354
692
  blendIndices: nodes.bounding[i].rawBlendIndices,
355
693
  weights: nodes.bounding[i].rawJointWeights
356
694
  };
357
- mesh.setSkinnedBoundingInfo(nodes.skeleton.getBoundingInfo(v));
358
- mesh.skeletonName = nodes.skeleton.persistentId;
695
+ mesh.setSkinnedBoundingInfo(nodes.binding.getBoundingInfo(v));
696
+ mesh.skeletonName = nodes.binding.persistentId;
359
697
  }
360
- animationSet.skeletons.push(new DRef(nodes.skeleton));
361
698
  }
362
- animation.addSkeleton(nodes.skeleton.persistentId);
363
699
  }
700
+ group.animationSet.skeletons.push(new DRef(nodes.binding));
364
701
  }
365
702
  }
366
- }
367
- group.iterate((child)=>{
368
- if (child !== group) {
369
- child.sealed = true;
703
+ if (saveAnimations) {
704
+ for (const animationData of this.animations){
705
+ if (animationData.skeletons.length > 0 && !saveSkeletons) {
706
+ continue;
707
+ }
708
+ let name = animationData.name ?? `_embbeded_animation`;
709
+ if (group.animationSet.getAnimationClip(name)) {
710
+ const baseName = name;
711
+ for(let t = 1;; t++){
712
+ name = `${baseName}_${t}`;
713
+ if (!group.animationSet.getAnimationClip(name)) {
714
+ break;
715
+ }
716
+ }
717
+ }
718
+ const animation = group.animationSet.createAnimation(name, true);
719
+ for (const sk of animationData.skeletons){
720
+ const nodes = skeletonMeshMap.get(sk);
721
+ if (nodes) {
722
+ animation.addSkeleton(nodes.binding.rig.persistentId);
723
+ }
724
+ }
725
+ for (const track of animationData.tracks){
726
+ const target = nodeMap.get(track.node);
727
+ if (track.type === 'translation') {
728
+ animation.addTrack(target, new NodeTranslationTrack(track.interpolator, true));
729
+ target.jointTypeT = 'animated';
730
+ } else if (track.type === 'scale') {
731
+ animation.addTrack(target, new NodeScaleTrack(track.interpolator, true));
732
+ target.jointTypeS = 'animated';
733
+ } else if (track.type === 'rotation') {
734
+ animation.addTrack(target, new NodeRotationTrack(track.interpolator, true));
735
+ target.jointTypeR = 'animated';
736
+ } else if (track.type === 'weights') {
737
+ for (const m of track.node.mesh.subMeshes){
738
+ const mesh = meshMap.get(m);
739
+ if (track.interpolator.stride > MAX_MORPH_TARGETS) {
740
+ console.error(`Morph target too large: ${track.interpolator.stride}, the maximum is ${MAX_MORPH_TARGETS}`);
741
+ } else {
742
+ const morphTrack = new MorphTargetTrack(track.interpolator, track.defaultMorphWeights, m.targetBox, mesh.getBoundingVolume().toAABB(), true);
743
+ animation.addTrack(mesh, morphTrack);
744
+ }
745
+ }
746
+ } else if (track.type === 'geometry-cache') {
747
+ const subMesh = track.node.mesh?.subMeshes[track.subMeshIndex];
748
+ const mesh = subMesh ? meshMap.get(subMesh) : null;
749
+ if (!subMesh || !mesh) {
750
+ console.error(`Invalid geometry cache sub mesh: ${track.subMeshIndex}`);
751
+ } else {
752
+ if (track.codec === 'pca') {
753
+ const pcaData = this.remapPCAGeometryCacheData(subMesh, track);
754
+ animation.addTrack(mesh, new PCAGeometryCacheTrack(pcaData, true));
755
+ } else {
756
+ const frames = this.remapGeometryCacheFrames(subMesh, track.frames);
757
+ animation.addTrack(mesh, new FixedGeometryCacheTrack(track.times, frames, true));
758
+ }
759
+ }
760
+ } else {
761
+ console.error(`Invalid animation track type: ${track.type}`);
762
+ }
763
+ }
764
+ }
370
765
  }
371
- });
372
- group.sharedModel = this;
766
+ }
767
+ if (saveJointDynamics && saveSkeletons) {
768
+ this.createJointDynamics(group, nodeMap);
769
+ }
770
+ this.createMorphTargetGroups(group, meshMap);
373
771
  return group;
374
772
  }
375
773
  remapGeometryCacheFrames(subMesh, frames) {
@@ -547,51 +945,61 @@ import { MAX_MORPH_TARGETS } from '../values.js';
547
945
  }
548
946
  onDispose() {
549
947
  super.onDispose();
550
- const nodes = [
551
- ...this._nodes
552
- ];
553
- while(nodes.length > 0){
554
- const node = nodes.shift();
555
- nodes.push(...node.children);
556
- const mesh = node.mesh;
557
- if (mesh) {
558
- for (const subMesh of mesh.subMeshes){
559
- subMesh.primitive?.dispose();
560
- subMesh.material?.dispose();
561
- }
562
- }
948
+ for (const prim of this._primitiveMap.values()){
949
+ prim.dispose();
950
+ }
951
+ for (const tex of this._textureMap.values()){
952
+ tex.dispose();
563
953
  }
564
- this._nodes = [];
565
954
  this._skeletons = [];
566
955
  this._scenes = [];
567
956
  this._animations = [];
568
957
  }
569
- setAssetNodeToSceneNode(scene, parent, assetNode, skeletonMeshMap, nodeMap, instancing) {
958
+ async setAssetNodeToSceneNode(manager, scene, parent, assetNode, skeletonMeshMap, nodeMap, meshMap, instancing, saveMeshes, saveSkeletons, saveAnimations, srcVFS) {
570
959
  const node = new SceneNode(scene);
571
960
  nodeMap.set(assetNode, node);
572
961
  node.name = assetNode.name ?? '';
573
962
  node.position.set(assetNode.position);
574
963
  node.rotation.set(assetNode.rotation);
575
964
  node.scale.set(assetNode.scaling);
576
- if (assetNode.mesh) {
965
+ if (saveMeshes && assetNode.mesh) {
577
966
  const meshData = assetNode.mesh;
578
- const skeleton = assetNode.skeleton;
967
+ const skeleton = saveSkeletons ? assetNode.skeleton : null;
579
968
  for (const subMesh of meshData.subMeshes){
969
+ if (assetNode.instances.length === 0) {
970
+ assetNode.instances.push({
971
+ t: Vector3.zero(),
972
+ s: Vector3.one(),
973
+ r: Quaternion.identity()
974
+ });
975
+ }
580
976
  for (const instance of assetNode.instances){
581
977
  const meshNode = new Mesh(scene);
582
- const skinAnimation = !!skeleton;
583
- const morphAnimation = subMesh.numTargets > 0;
584
978
  meshNode.position = instance.t;
585
979
  meshNode.scale = instance.s;
586
980
  meshNode.rotation = instance.r;
587
981
  meshNode.name = subMesh.name;
588
982
  meshNode.clipTestEnabled = true;
589
983
  meshNode.showState = 'inherit';
590
- meshNode.primitive = subMesh.primitive.get();
591
- meshNode.material = instancing && !skinAnimation && !morphAnimation ? subMesh.material.get().createInstance() : subMesh.material.get().clone();
984
+ if (!this._primitiveMap.get(subMesh.primitive)) {
985
+ this._primitiveMap.set(subMesh.primitive, new DRef(await this.createPrimitive(manager, subMesh.primitive, srcVFS) ?? undefined));
986
+ }
987
+ meshNode.primitive = this._primitiveMap.get(subMesh.primitive).get();
988
+ if (!subMesh.material.material) {
989
+ subMesh.material.material = new DRef(await this.createMaterial(manager, subMesh.material, srcVFS) ?? undefined);
990
+ }
991
+ meshNode.material = subMesh.material.material?.get() ?? null;
992
+ if (instancing) {
993
+ meshNode.material = meshNode.material.createInstance();
994
+ }
592
995
  meshNode.parent = node;
593
- subMesh.mesh = meshNode;
594
- processMorphData(subMesh, meshData.morphWeights);
996
+ meshMap.set(subMesh, meshNode);
997
+ setSceneMeshAssetBinding(meshNode, {
998
+ node: assetNode,
999
+ mesh: meshData,
1000
+ subMesh
1001
+ });
1002
+ processMorphData(subMesh, meshNode, assetNode.weights ?? meshData.morphWeights, meshData.morphNames);
595
1003
  if (skeleton) {
596
1004
  if (!skeletonMeshMap.has(skeleton)) {
597
1005
  skeletonMeshMap.set(skeleton, {
@@ -612,8 +1020,469 @@ import { MAX_MORPH_TARGETS } from '../values.js';
612
1020
  }
613
1021
  node.parent = parent;
614
1022
  for (const child of assetNode.children){
615
- this.setAssetNodeToSceneNode(scene, node, child, skeletonMeshMap, nodeMap, instancing);
1023
+ await this.setAssetNodeToSceneNode(manager, scene, node, child, skeletonMeshMap, nodeMap, meshMap, instancing, saveMeshes, saveSkeletons, saveAnimations, srcVFS);
1024
+ }
1025
+ return node;
1026
+ }
1027
+ async image2Texture(manager, info, srcVFS) {
1028
+ if (info.image.uri) {
1029
+ const texture = await manager.fetchTexture(info.image.uri, {
1030
+ linearColorSpace: !info.sRGB,
1031
+ overrideVFS: srcVFS
1032
+ });
1033
+ texture.name = info.image.uri;
1034
+ return texture;
1035
+ } else if (info.image.data && info.image.mimeType) {
1036
+ const texture = await manager.loadTextureFromBuffer(info.image.data, info.image.mimeType, !!info.sRGB);
1037
+ return texture;
1038
+ }
1039
+ return null;
1040
+ }
1041
+ async createTexture(manager, info, srcVFS) {
1042
+ try {
1043
+ let textureRef = this._textureMap.get(info);
1044
+ if (!textureRef) {
1045
+ textureRef = new DRef(await this.image2Texture(manager, info, srcVFS));
1046
+ this._textureMap.set(info, textureRef);
1047
+ }
1048
+ const sampler = getDevice().createSampler({
1049
+ addressU: info.sampler.wrapS,
1050
+ addressV: info.sampler.wrapT,
1051
+ magFilter: info.sampler.magFilter,
1052
+ minFilter: info.sampler.minFilter,
1053
+ mipFilter: info.sampler.mipFilter
1054
+ });
1055
+ const transform = info.transform;
1056
+ const texCoord = info.texCoord;
1057
+ return {
1058
+ texture: textureRef.get(),
1059
+ sampler,
1060
+ texCoord,
1061
+ transform
1062
+ };
1063
+ } catch (err) {
1064
+ console.error(`Load asset texture failed: ${err}`);
1065
+ return null;
1066
+ }
1067
+ }
1068
+ async createPrimitive(manager, info, srcVFS) {
1069
+ if (info.path) {
1070
+ return manager.fetchPrimitive(info.path, {
1071
+ overrideVFS: srcVFS
1072
+ });
616
1073
  }
1074
+ const primitive = new Primitive();
1075
+ for(const k in info.vertices){
1076
+ const v = info.vertices[k];
1077
+ primitive.createAndSetVertexBuffer(v.format, v.data);
1078
+ }
1079
+ if (info.indices) {
1080
+ primitive.createAndSetIndexBuffer(info.indices);
1081
+ }
1082
+ primitive.primitiveType = info.type;
1083
+ primitive.indexCount = info.indexCount;
1084
+ primitive.setBoundingVolume(new BoundingBox(info.boxMin, info.boxMax));
1085
+ return primitive;
1086
+ }
1087
+ async createMaterial(manager, assetMaterial, srcVFS) {
1088
+ if (assetMaterial.path) {
1089
+ return manager.fetchMaterial(assetMaterial.path, {
1090
+ overrideVFS: srcVFS
1091
+ });
1092
+ }
1093
+ const infoMap = new Map();
1094
+ const that = this;
1095
+ async function getTextureInfo(info) {
1096
+ let t = infoMap.get(info);
1097
+ if (t === undefined) {
1098
+ t = await that.createTexture(manager, info, srcVFS);
1099
+ infoMap.set(info, t);
1100
+ }
1101
+ return t;
1102
+ }
1103
+ if (assetMaterial.type === 'unlit') {
1104
+ const unlitAssetMaterial = assetMaterial;
1105
+ const unlitMaterial = new UnlitMaterial();
1106
+ unlitMaterial.albedoColor = unlitAssetMaterial.diffuse ?? Vector4.one();
1107
+ if (unlitAssetMaterial.diffuseMap) {
1108
+ const info = await getTextureInfo(unlitAssetMaterial.diffuseMap);
1109
+ if (info) {
1110
+ unlitMaterial.albedoTexture = info.texture;
1111
+ unlitMaterial.albedoTextureSampler = info.sampler;
1112
+ unlitMaterial.albedoTexCoordIndex = info.texCoord;
1113
+ unlitMaterial.albedoTexCoordMatrix = info.transform;
1114
+ }
1115
+ }
1116
+ unlitMaterial.vertexColor = unlitAssetMaterial.common.vertexColor;
1117
+ if (assetMaterial.common.alphaMode === 'blend') {
1118
+ unlitMaterial.blendMode = 'blend';
1119
+ } else if (assetMaterial.common.alphaMode === 'mask') {
1120
+ unlitMaterial.alphaCutoff = assetMaterial.common.alphaCutoff;
1121
+ }
1122
+ if (assetMaterial.common.doubleSided) {
1123
+ unlitMaterial.cullMode = 'none';
1124
+ }
1125
+ return unlitMaterial;
1126
+ } else if (assetMaterial.type === 'pbrSpecularGlossiness') {
1127
+ const assetPBRMaterial = assetMaterial;
1128
+ const pbrMaterial = new PBRSpecularGlossinessMaterial();
1129
+ pbrMaterial.ior = assetPBRMaterial.ior;
1130
+ pbrMaterial.albedoColor = assetPBRMaterial.diffuse;
1131
+ pbrMaterial.specularFactor = new Vector3(assetPBRMaterial.specular.x, assetPBRMaterial.specular.y, assetPBRMaterial.specular.z);
1132
+ pbrMaterial.glossinessFactor = assetPBRMaterial.glossness;
1133
+ if (assetPBRMaterial.diffuseMap) {
1134
+ const info = await getTextureInfo(assetPBRMaterial.diffuseMap);
1135
+ if (info) {
1136
+ pbrMaterial.albedoTexture = info.texture;
1137
+ pbrMaterial.albedoTextureSampler = info.sampler;
1138
+ pbrMaterial.albedoTexCoordIndex = info.texCoord;
1139
+ pbrMaterial.albedoTexCoordMatrix = info.transform;
1140
+ }
1141
+ }
1142
+ if (assetPBRMaterial.common.normalMap) {
1143
+ const info = await getTextureInfo(assetPBRMaterial.common.normalMap);
1144
+ if (info) {
1145
+ pbrMaterial.normalTexture = info.texture;
1146
+ pbrMaterial.normalTextureSampler = info.sampler;
1147
+ pbrMaterial.normalTexCoordIndex = info.texCoord;
1148
+ pbrMaterial.normalTexCoordMatrix = info.transform;
1149
+ }
1150
+ }
1151
+ pbrMaterial.normalScale = assetPBRMaterial.common.bumpScale;
1152
+ if (assetPBRMaterial.common.emissiveMap) {
1153
+ const info = await getTextureInfo(assetPBRMaterial.common.emissiveMap);
1154
+ if (info) {
1155
+ pbrMaterial.emissiveTexture = info.texture;
1156
+ pbrMaterial.emissiveTextureSampler = info.sampler;
1157
+ pbrMaterial.emissiveTexCoordIndex = info.texCoord;
1158
+ pbrMaterial.emissiveTexCoordMatrix = info.transform;
1159
+ }
1160
+ }
1161
+ pbrMaterial.emissiveColor = assetPBRMaterial.common.emissiveColor;
1162
+ pbrMaterial.emissiveStrength = assetPBRMaterial.common.emissiveStrength;
1163
+ if (assetPBRMaterial.common.occlusionMap) {
1164
+ const info = await getTextureInfo(assetPBRMaterial.common.occlusionMap);
1165
+ if (info) {
1166
+ pbrMaterial.occlusionTexture = info.texture;
1167
+ pbrMaterial.occlusionTextureSampler = info.sampler;
1168
+ pbrMaterial.occlusionTexCoordIndex = info.texCoord;
1169
+ pbrMaterial.occlusionTexCoordMatrix = info.transform;
1170
+ }
1171
+ }
1172
+ pbrMaterial.occlusionStrength = assetPBRMaterial.common.occlusionStrength;
1173
+ if (assetPBRMaterial.specularGlossnessMap) {
1174
+ const info = await getTextureInfo(assetPBRMaterial.specularGlossnessMap);
1175
+ if (info) {
1176
+ pbrMaterial.specularTexture = info.texture;
1177
+ pbrMaterial.specularTextureSampler = info.sampler;
1178
+ pbrMaterial.specularTexCoordIndex = info.texCoord;
1179
+ pbrMaterial.specularTexCoordMatrix = info.transform;
1180
+ }
1181
+ }
1182
+ pbrMaterial.vertexTangent = assetPBRMaterial.common.useTangent;
1183
+ pbrMaterial.vertexColor = assetPBRMaterial.common.vertexColor;
1184
+ if (assetPBRMaterial.common.alphaMode === 'blend') {
1185
+ pbrMaterial.blendMode = 'blend';
1186
+ } else if (assetPBRMaterial.common.alphaMode === 'mask') {
1187
+ pbrMaterial.alphaCutoff = assetPBRMaterial.common.alphaCutoff;
1188
+ }
1189
+ if (assetPBRMaterial.common.doubleSided) {
1190
+ pbrMaterial.cullMode = 'none';
1191
+ }
1192
+ pbrMaterial.vertexNormal = !!assetMaterial.common.vertexNormal;
1193
+ return pbrMaterial;
1194
+ } else if (assetMaterial.type === 'pbrMetallicRoughness') {
1195
+ const assetPBRMaterial = assetMaterial;
1196
+ const pbrMaterial = new PBRMetallicRoughnessMaterial();
1197
+ pbrMaterial.ior = assetPBRMaterial.ior;
1198
+ pbrMaterial.albedoColor = assetPBRMaterial.diffuse;
1199
+ pbrMaterial.metallic = assetPBRMaterial.metallic;
1200
+ pbrMaterial.roughness = assetPBRMaterial.roughness;
1201
+ if (assetPBRMaterial.diffuseMap) {
1202
+ const info = await getTextureInfo(assetPBRMaterial.diffuseMap);
1203
+ if (info) {
1204
+ pbrMaterial.albedoTexture = info.texture;
1205
+ pbrMaterial.albedoTextureSampler = info.sampler;
1206
+ pbrMaterial.albedoTexCoordIndex = info.texCoord;
1207
+ pbrMaterial.albedoTexCoordMatrix = info.transform;
1208
+ }
1209
+ }
1210
+ if (assetPBRMaterial.common.normalMap) {
1211
+ const info = await getTextureInfo(assetPBRMaterial.common.normalMap);
1212
+ if (info) {
1213
+ pbrMaterial.normalTexture = info.texture;
1214
+ pbrMaterial.normalTextureSampler = info.sampler;
1215
+ pbrMaterial.normalTexCoordIndex = info.texCoord;
1216
+ pbrMaterial.normalTexCoordMatrix = info.transform;
1217
+ }
1218
+ }
1219
+ pbrMaterial.normalScale = assetPBRMaterial.common.bumpScale;
1220
+ if (assetPBRMaterial.common.emissiveMap) {
1221
+ const info = await getTextureInfo(assetPBRMaterial.common.emissiveMap);
1222
+ if (info) {
1223
+ pbrMaterial.emissiveTexture = info.texture;
1224
+ pbrMaterial.emissiveTextureSampler = info.sampler;
1225
+ pbrMaterial.emissiveTexCoordIndex = info.texCoord;
1226
+ pbrMaterial.emissiveTexCoordMatrix = info.transform;
1227
+ }
1228
+ }
1229
+ pbrMaterial.emissiveColor = assetPBRMaterial.common.emissiveColor;
1230
+ pbrMaterial.emissiveStrength = assetPBRMaterial.common.emissiveStrength;
1231
+ if (assetPBRMaterial.common.occlusionMap) {
1232
+ const info = await getTextureInfo(assetPBRMaterial.common.occlusionMap);
1233
+ if (info) {
1234
+ pbrMaterial.occlusionTexture = info.texture;
1235
+ pbrMaterial.occlusionTextureSampler = info.sampler;
1236
+ pbrMaterial.occlusionTexCoordIndex = info.texCoord;
1237
+ pbrMaterial.occlusionTexCoordMatrix = info.transform;
1238
+ }
1239
+ pbrMaterial.occlusionStrength = assetPBRMaterial.common.occlusionStrength;
1240
+ }
1241
+ if (assetPBRMaterial.metallicMap) {
1242
+ const info = await getTextureInfo(assetPBRMaterial.metallicMap);
1243
+ if (info) {
1244
+ pbrMaterial.metallicRoughnessTexture = info.texture;
1245
+ pbrMaterial.metallicRoughnessTextureSampler = info.sampler;
1246
+ pbrMaterial.metallicRoughnessTexCoordIndex = info.texCoord;
1247
+ pbrMaterial.metallicRoughnessTexCoordMatrix = info.transform;
1248
+ }
1249
+ }
1250
+ pbrMaterial.specularFactor = assetPBRMaterial.specularFactor;
1251
+ if (assetPBRMaterial.specularMap) {
1252
+ const info = await getTextureInfo(assetPBRMaterial.specularMap);
1253
+ if (info) {
1254
+ pbrMaterial.specularTexture = info.texture;
1255
+ pbrMaterial.specularTextureSampler = info.sampler;
1256
+ pbrMaterial.specularTexCoordIndex = info.texCoord;
1257
+ pbrMaterial.specularTexCoordMatrix = info.transform;
1258
+ }
1259
+ }
1260
+ if (assetPBRMaterial.specularColorMap) {
1261
+ const info = await getTextureInfo(assetPBRMaterial.specularColorMap);
1262
+ if (info) {
1263
+ pbrMaterial.specularColorTexture = info.texture;
1264
+ pbrMaterial.specularColorTextureSampler = info.sampler;
1265
+ pbrMaterial.specularColorTexCoordIndex = info.texCoord;
1266
+ pbrMaterial.specularColorTexCoordMatrix = info.transform;
1267
+ }
1268
+ }
1269
+ if (assetPBRMaterial.sheen) {
1270
+ const sheen = assetPBRMaterial.sheen;
1271
+ pbrMaterial.sheen = true;
1272
+ pbrMaterial.sheenColorFactor = sheen.sheenColorFactor;
1273
+ pbrMaterial.sheenRoughnessFactor = sheen.sheenRoughnessFactor;
1274
+ if (sheen.sheenColorMap) {
1275
+ const info = await getTextureInfo(sheen.sheenColorMap);
1276
+ if (info) {
1277
+ pbrMaterial.sheenColorTexture = info.texture;
1278
+ pbrMaterial.sheenColorTextureSampler = info.sampler;
1279
+ pbrMaterial.sheenColorTexCoordIndex = info.texCoord;
1280
+ pbrMaterial.sheenColorTexCoordMatrix = info.transform;
1281
+ }
1282
+ }
1283
+ if (sheen.sheenRoughnessMap) {
1284
+ const info = await getTextureInfo(sheen.sheenRoughnessMap);
1285
+ if (info) {
1286
+ pbrMaterial.sheenRoughnessTexture = info.texture;
1287
+ pbrMaterial.sheenRoughnessTextureSampler = info.sampler;
1288
+ pbrMaterial.sheenRoughnessTexCoordIndex = info.texCoord;
1289
+ pbrMaterial.sheenRoughnessTexCoordMatrix = info.transform;
1290
+ }
1291
+ }
1292
+ }
1293
+ if (assetPBRMaterial.iridescence) {
1294
+ const iridescence = assetPBRMaterial.iridescence;
1295
+ pbrMaterial.iridescence = true;
1296
+ pbrMaterial.iridescenceFactor = iridescence.iridescenceFactor;
1297
+ pbrMaterial.iridescenceIor = iridescence.iridescenceIor;
1298
+ if (iridescence.iridescenceMap) {
1299
+ const info = await getTextureInfo(iridescence.iridescenceMap);
1300
+ if (info) {
1301
+ pbrMaterial.iridescenceTexture = info.texture;
1302
+ pbrMaterial.iridescenceTextureSampler = info.sampler;
1303
+ pbrMaterial.iridescenceTexCoordIndex = info.texCoord;
1304
+ pbrMaterial.iridescenceTexCoordMatrix = info.transform;
1305
+ }
1306
+ }
1307
+ pbrMaterial.iridescenceThicknessMin = iridescence.iridescenceThicknessMinimum;
1308
+ pbrMaterial.iridescenceThicknessMax = iridescence.iridescenceThicknessMaximum;
1309
+ if (iridescence.iridescenceThicknessMap) {
1310
+ const info = await getTextureInfo(iridescence.iridescenceThicknessMap);
1311
+ if (info) {
1312
+ pbrMaterial.iridescenceThicknessTexture = info.texture;
1313
+ pbrMaterial.iridescenceThicknessTextureSampler = info.sampler;
1314
+ pbrMaterial.iridescenceThicknessTexCoordIndex = info.texCoord;
1315
+ pbrMaterial.iridescenceThicknessTexCoordMatrix = info.transform;
1316
+ }
1317
+ }
1318
+ }
1319
+ if (assetPBRMaterial.transmission) {
1320
+ const transmission = assetPBRMaterial.transmission;
1321
+ pbrMaterial.transmission = true;
1322
+ pbrMaterial.transmissionFactor = transmission.transmissionFactor;
1323
+ if (transmission.transmissionMap) {
1324
+ const info = await getTextureInfo(transmission.transmissionMap);
1325
+ if (info) {
1326
+ pbrMaterial.transmissionTexture = info.texture;
1327
+ pbrMaterial.transmissionTextureSampler = info.sampler;
1328
+ pbrMaterial.transmissionTexCoordIndex = info.texCoord;
1329
+ pbrMaterial.transmissionTexCoordMatrix = info.transform;
1330
+ }
1331
+ }
1332
+ pbrMaterial.thicknessFactor = transmission.thicknessFactor;
1333
+ if (transmission.thicknessMap) {
1334
+ const info = await getTextureInfo(transmission.thicknessMap);
1335
+ if (info) {
1336
+ pbrMaterial.thicknessTexture = info.texture;
1337
+ pbrMaterial.thicknessTextureSampler = info.sampler;
1338
+ pbrMaterial.thicknessTexCoordIndex = info.texCoord;
1339
+ pbrMaterial.thicknessTexCoordMatrix = info.transform;
1340
+ }
1341
+ }
1342
+ pbrMaterial.attenuationDistance = transmission.attenuationDistance;
1343
+ pbrMaterial.attenuationColor = transmission.attenuationColor;
1344
+ }
1345
+ if (assetPBRMaterial.clearcoat) {
1346
+ const cc = assetPBRMaterial.clearcoat;
1347
+ pbrMaterial.clearcoat = true;
1348
+ pbrMaterial.clearcoatIntensity = cc.clearCoatFactor;
1349
+ pbrMaterial.clearcoatRoughnessFactor = cc.clearCoatRoughnessFactor;
1350
+ if (cc.clearCoatIntensityMap) {
1351
+ const info = await getTextureInfo(cc.clearCoatIntensityMap);
1352
+ if (info) {
1353
+ pbrMaterial.clearcoatIntensityTexture = info.texture;
1354
+ pbrMaterial.clearcoatIntensityTextureSampler = info.sampler;
1355
+ pbrMaterial.clearcoatIntensityTexCoordIndex = info.texCoord;
1356
+ pbrMaterial.clearcoatIntensityTexCoordMatrix = info.transform;
1357
+ }
1358
+ }
1359
+ if (cc.clearCoatRoughnessMap) {
1360
+ const info = await getTextureInfo(cc.clearCoatRoughnessMap);
1361
+ if (info) {
1362
+ pbrMaterial.clearcoatRoughnessTexture = info.texture;
1363
+ pbrMaterial.clearcoatRoughnessTextureSampler = info.sampler;
1364
+ pbrMaterial.clearcoatRoughnessTexCoordIndex = info.texCoord;
1365
+ pbrMaterial.clearcoatRoughnessTexCoordMatrix = info.transform;
1366
+ }
1367
+ }
1368
+ if (cc.clearCoatNormalMap) {
1369
+ const info = await getTextureInfo(cc.clearCoatNormalMap);
1370
+ if (info) {
1371
+ pbrMaterial.clearcoatNormalTexture = info.texture;
1372
+ pbrMaterial.clearcoatNormalTextureSampler = info.sampler;
1373
+ pbrMaterial.clearcoatNormalTexCoordIndex = info.texCoord;
1374
+ pbrMaterial.clearcoatNormalTexCoordMatrix = info.transform;
1375
+ }
1376
+ }
1377
+ }
1378
+ pbrMaterial.vertexTangent = assetPBRMaterial.common.useTangent;
1379
+ pbrMaterial.vertexColor = assetPBRMaterial.common.vertexColor;
1380
+ if (assetPBRMaterial.common.alphaMode === 'blend') {
1381
+ pbrMaterial.blendMode = 'blend';
1382
+ } else if (assetPBRMaterial.common.alphaMode === 'mask') {
1383
+ pbrMaterial.alphaCutoff = assetPBRMaterial.common.alphaCutoff;
1384
+ }
1385
+ if (assetPBRMaterial.common.doubleSided) {
1386
+ pbrMaterial.cullMode = 'none';
1387
+ }
1388
+ pbrMaterial.vertexNormal = !!assetMaterial.common.vertexNormal;
1389
+ return pbrMaterial;
1390
+ }
1391
+ return null;
1392
+ }
1393
+ }
1394
+ /** @internal */ function processMorphData(subMesh, mesh, morphWeights, morphNames) {
1395
+ const device = getDevice();
1396
+ const numTargets = subMesh.numTargets;
1397
+ if (numTargets === 0) {
1398
+ return;
1399
+ }
1400
+ const attributes = Object.getOwnPropertyNames(subMesh.targets);
1401
+ const positionInfo = subMesh.primitive.vertices['position'];
1402
+ const numVertices = positionInfo ? positionInfo.data.length / getVertexFormatComponentCount(positionInfo.format) >> 0 : 0;
1403
+ const weightsAndOffsets = new Float32Array(4 + MAX_MORPH_TARGETS + MAX_MORPH_ATTRIBUTES);
1404
+ for(let i = 0; i < numTargets; i++){
1405
+ weightsAndOffsets[4 + i] = morphWeights?.[i] ?? 0;
1406
+ }
1407
+ const textureSize = Math.ceil(Math.sqrt(numVertices * attributes.length * numTargets));
1408
+ if (textureSize > device.getDeviceCaps().textureCaps.maxTextureSize) {
1409
+ // TODO: reduce morph attributes
1410
+ throw new Error(`Morph target data too large`);
1411
+ }
1412
+ weightsAndOffsets[0] = textureSize;
1413
+ weightsAndOffsets[1] = textureSize;
1414
+ weightsAndOffsets[2] = numVertices;
1415
+ weightsAndOffsets[3] = numTargets;
1416
+ let offset = 0;
1417
+ const textureData = new Float32Array(textureSize * textureSize * 4);
1418
+ for(let attrib = 0; attrib < MAX_MORPH_ATTRIBUTES; attrib++){
1419
+ const index = attributes.indexOf(String(attrib));
1420
+ if (index < 0) {
1421
+ weightsAndOffsets[4 + MAX_MORPH_TARGETS + attrib] = -1;
1422
+ continue;
1423
+ }
1424
+ weightsAndOffsets[4 + MAX_MORPH_TARGETS + attrib] = offset >> 2;
1425
+ const info = subMesh.targets[attrib];
1426
+ if (info.data.length !== numTargets) {
1427
+ console.error(`Invalid morph target data`);
1428
+ return;
1429
+ }
1430
+ for(let t = 0; t < numTargets; t++){
1431
+ const data = info.data[t];
1432
+ for(let i = 0; i < numVertices; i++){
1433
+ for(let j = 0; j < 4; j++){
1434
+ textureData[offset++] = j < info.numComponents ? data[i * info.numComponents + j] : 1;
1435
+ }
1436
+ }
1437
+ }
1438
+ }
1439
+ const morphBoundingBox = new BoundingBox();
1440
+ calculateMorphBoundingBox(morphBoundingBox, subMesh.targetBox, weightsAndOffsets.subarray(4, 4 + MAX_MORPH_TARGETS), numTargets);
1441
+ const meshAABB = mesh.getBoundingVolume().toAABB();
1442
+ morphBoundingBox.minPoint.addBy(meshAABB.minPoint);
1443
+ morphBoundingBox.maxPoint.addBy(meshAABB.maxPoint);
1444
+ const names = {};
1445
+ for(let i = 0; i < numTargets; i++){
1446
+ const name = morphNames?.[i] ?? `Target${i}`;
1447
+ names[name] = i;
1448
+ }
1449
+ mesh.setMorphData({
1450
+ width: textureSize,
1451
+ height: textureSize,
1452
+ data: textureData
1453
+ });
1454
+ mesh.setMorphInfo({
1455
+ data: weightsAndOffsets,
1456
+ names
1457
+ });
1458
+ mesh.setMorphBoundingInfo({
1459
+ targetBoxes: subMesh.targetBox,
1460
+ originBox: new BoundingBox(meshAABB)
1461
+ });
1462
+ mesh.setAnimatedBoundingBox(morphBoundingBox);
1463
+ }
1464
+ /** @internal */ function getAssetMeshMorphTargetCount(mesh) {
1465
+ let count = 0;
1466
+ for (const subMesh of mesh.subMeshes){
1467
+ count = Math.max(count, subMesh.numTargets);
1468
+ }
1469
+ return count;
1470
+ }
1471
+ /** @internal */ function getAssetMeshMorphTargetName(mesh, index) {
1472
+ return mesh.morphNames?.[index] ?? `Target${index}`;
1473
+ }
1474
+ /** @internal */ function calculateMorphBoundingBox(morphBoundingBox, keyframeBoundingBox, weights, numTargets) {
1475
+ morphBoundingBox.minPoint.setXYZ(0, 0, 0);
1476
+ morphBoundingBox.maxPoint.setXYZ(0, 0, 0);
1477
+ for(let i = 0; i < numTargets; i++){
1478
+ const weight = weights[i];
1479
+ const keyframeBox = keyframeBoundingBox[i];
1480
+ morphBoundingBox.minPoint.x += keyframeBox.minPoint.x * weight;
1481
+ morphBoundingBox.minPoint.y += keyframeBox.minPoint.y * weight;
1482
+ morphBoundingBox.minPoint.z += keyframeBox.minPoint.z * weight;
1483
+ morphBoundingBox.maxPoint.x += keyframeBox.maxPoint.x * weight;
1484
+ morphBoundingBox.maxPoint.y += keyframeBox.maxPoint.y * weight;
1485
+ morphBoundingBox.maxPoint.z += keyframeBox.maxPoint.z * weight;
617
1486
  }
618
1487
  }
619
1488