@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/README.md +1 -1
- package/dist/animated-bounds.d.ts +4 -0
- package/dist/animated-bounds.d.ts.map +1 -0
- package/dist/gltf-importer.d.ts.map +1 -1
- package/dist/importer-entry.mjs +114 -30
- package/dist/importer-entry.mjs.map +1 -1
- package/dist/index.mjs +114 -30
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -12
- package/src/__tests__/gltf.unit.test.ts +67 -0
- package/src/animated-bounds.ts +94 -0
- package/src/gltf-importer.ts +2 -1
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,
|