@forgeax/engine-gltf 0.1.29 → 0.1.31

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/dist/index.mjs CHANGED
@@ -5,6 +5,7 @@ import { box3, mat4, vec3, quat } from '@forgeax/engine-math';
5
5
  import { AssetGuid } from '@forgeax/engine-pack/guid';
6
6
  import { deriveDefaultLodScreenCoverages, reconcileMeshLodMeta } from '@forgeax/engine-import';
7
7
  import { packMeshBinV4 } from '@forgeax/engine-import/mesh-bin';
8
+ import { deriveConservativeAnimatedBounds } from '@forgeax/engine-animation/animated-bounds';
8
9
  import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
9
10
 
10
11
  // src/errors.ts
@@ -1169,6 +1170,118 @@ function checkExtensions(json) {
1169
1170
  return ok({ unsupportedUsed });
1170
1171
  }
1171
1172
 
1173
+ // src/node-path.ts
1174
+ function buildNodeParentMap(nodes) {
1175
+ const parents = /* @__PURE__ */ new Map();
1176
+ for (let index = 0; index < nodes.length; index++) {
1177
+ for (const child of nodes[index]?.children ?? []) parents.set(child, index);
1178
+ }
1179
+ return parents;
1180
+ }
1181
+ function resolveNamedNodePath(nodes, parents, nodeIndex) {
1182
+ const reversed = [];
1183
+ const visited = /* @__PURE__ */ new Set();
1184
+ let current = nodeIndex;
1185
+ while (current !== void 0) {
1186
+ if (visited.has(current)) {
1187
+ return { ok: false, reason: "hierarchy-cycle", nodeIndex: current };
1188
+ }
1189
+ visited.add(current);
1190
+ const name = nodes[current]?.name;
1191
+ if (name === void 0 || name.length === 0) {
1192
+ return { ok: false, reason: "name-missing", nodeIndex: current };
1193
+ }
1194
+ reversed.push(name);
1195
+ current = parents.get(current);
1196
+ }
1197
+ return { ok: true, value: reversed.reverse() };
1198
+ }
1199
+
1200
+ // src/animated-bounds.ts
1201
+ function deriveGltfAnimatedBounds(doc) {
1202
+ const parents = buildNodeParentMap(doc.nodes);
1203
+ const paths = doc.nodes.map((_, index) => {
1204
+ const path = resolveNamedNodePath(doc.nodes, parents, index);
1205
+ return path.ok ? path.value.join("/") : void 0;
1206
+ });
1207
+ const nodes = doc.nodes.map((node, index) => ({
1208
+ ...node.transform,
1209
+ parent: parents.get(index) ?? null
1210
+ }));
1211
+ const channels = doc.animationClips.flatMap(
1212
+ (clip) => clip.channels.flatMap(
1213
+ (channel) => channel.property === "weights" ? [] : [
1214
+ {
1215
+ node: channel.targetNodeIndex,
1216
+ property: channel.property,
1217
+ values: channel.sampler.output,
1218
+ interpolation: channel.sampler.interpolation
1219
+ }
1220
+ ]
1221
+ )
1222
+ );
1223
+ const primitiveStarts = [];
1224
+ let offset = 0;
1225
+ for (const count of doc.meshPrimitiveCount?.values() ?? doc.meshes.map(() => 1)) {
1226
+ primitiveStarts.push(offset);
1227
+ offset += count;
1228
+ }
1229
+ const skeletons = doc.skeletons.map((skeleton, skinIndex) => {
1230
+ if (skeleton.bounds !== void 0) return skeleton;
1231
+ const meshes = [];
1232
+ for (let index = 0; index < doc.nodes.length; index++) {
1233
+ const node = doc.nodes[index];
1234
+ if (node === void 0) return skeleton;
1235
+ if (node.skinIndex !== skinIndex || node.meshIndex === null) continue;
1236
+ const start = primitiveStarts[node.meshIndex];
1237
+ if (start === void 0) return skeleton;
1238
+ for (const mesh of doc.meshes.slice(
1239
+ start,
1240
+ start + (doc.meshPrimitiveCount?.get(node.meshIndex) ?? 1)
1241
+ )) {
1242
+ if (mesh.joints0 === void 0 || mesh.weights0 === void 0) return skeleton;
1243
+ let maxMorphWeight = Math.max(
1244
+ 0,
1245
+ ...Array.from(node.morphWeights ?? mesh.morphWeights ?? [], Math.abs)
1246
+ );
1247
+ for (const clip of doc.animationClips)
1248
+ for (const channel of clip.channels)
1249
+ if (channel.targetNodeIndex === index && channel.property === "weights")
1250
+ for (const weight of channel.sampler.output)
1251
+ maxMorphWeight = Math.max(maxMorphWeight, Math.abs(weight));
1252
+ const morphExtent = Float32Array.from(
1253
+ mesh.positions,
1254
+ (_, component) => (mesh.morphTargets ?? []).reduce(
1255
+ (sum, target) => sum + Math.abs(target.position?.[component] ?? 0) * maxMorphWeight,
1256
+ 0
1257
+ )
1258
+ );
1259
+ meshes.push({
1260
+ node: index,
1261
+ positions: mesh.positions,
1262
+ joints: mesh.joints0,
1263
+ weights: mesh.weights0,
1264
+ morphExtent
1265
+ });
1266
+ }
1267
+ }
1268
+ const jointNodes = skeleton.jointPaths.map((path) => paths.indexOf(path));
1269
+ if (jointNodes.some(
1270
+ (node, index) => node < 0 || paths.lastIndexOf(skeleton.jointPaths[index]) !== node
1271
+ ))
1272
+ return skeleton;
1273
+ const bounds = deriveConservativeAnimatedBounds({
1274
+ nodes,
1275
+ channels,
1276
+ jointNodes,
1277
+ inverseBindMatrices: skeleton.inverseBindMatrices,
1278
+ meshes
1279
+ });
1280
+ return bounds === void 0 ? skeleton : { ...skeleton, bounds };
1281
+ });
1282
+ return { ...doc, skeletons };
1283
+ }
1284
+
1172
1285
  // src/data-uri.ts
1173
1286
  var DATA_URI_BASE64_RE = /^data:[^;,]*(?:;[^,;]+)*;base64,(.*)$/;
1174
1287
  var Base64DecodeError = class extends Error {
@@ -2122,35 +2235,6 @@ async function projectMeshoptBufferViews(inputViews, inputBuffers, extensionsReq
2122
2235
  }
2123
2236
  return ok({ bufferViews, buffers, decodedCount });
2124
2237
  }
2125
-
2126
- // src/node-path.ts
2127
- function buildNodeParentMap(nodes) {
2128
- const parents = /* @__PURE__ */ new Map();
2129
- for (let index = 0; index < nodes.length; index++) {
2130
- for (const child of nodes[index]?.children ?? []) parents.set(child, index);
2131
- }
2132
- return parents;
2133
- }
2134
- function resolveNamedNodePath(nodes, parents, nodeIndex) {
2135
- const reversed = [];
2136
- const visited = /* @__PURE__ */ new Set();
2137
- let current = nodeIndex;
2138
- while (current !== void 0) {
2139
- if (visited.has(current)) {
2140
- return { ok: false, reason: "hierarchy-cycle", nodeIndex: current };
2141
- }
2142
- visited.add(current);
2143
- const name = nodes[current]?.name;
2144
- if (name === void 0 || name.length === 0) {
2145
- return { ok: false, reason: "name-missing", nodeIndex: current };
2146
- }
2147
- reversed.push(name);
2148
- current = parents.get(current);
2149
- }
2150
- return { ok: true, value: reversed.reverse() };
2151
- }
2152
-
2153
- // src/parse-animation.ts
2154
2238
  var ANIMATION_ACCESSOR_TYPES = ["SCALAR", "VEC2", "VEC3", "VEC4"];
2155
2239
  function parseAnimation(animationsJson, nodesJson, accessors, bufferViews, buffers) {
2156
2240
  if (animationsJson === void 0 || animationsJson.length === 0) {
@@ -3943,7 +4027,7 @@ async function importGltf(ctx, meshopt) {
3943
4027
  }
3944
4028
  const parsed = await parseDoc(ctx.source, read.value, ctx);
3945
4029
  if (!parsed.ok) return parsed;
3946
- const doc = applyImportSettingsBounds(parsed.value, ctx.importSettings);
4030
+ const doc = deriveGltfAnimatedBounds(applyImportSettingsBounds(parsed.value, ctx.importSettings));
3947
4031
  const maps = buildHandleMaps(ctx.subAssets, doc);
3948
4032
  const imageColorSpaces = deriveTextureColorSpace({
3949
4033
  imageCount: (doc.images ?? []).length,