create-playcanvas 0.5.1 → 0.6.1
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 +20 -27
- package/dist/index.mjs +44 -42
- package/package.json +1 -1
- package/skills/calibrate-model/references/direct-engine.md +7 -4
- package/skills/calibrate-model/references/react.md +8 -3
- package/skills/calibrate-model/references/web-components.md +7 -4
- package/skills/configure-animation/references/web-components.md +18 -9
- package/skills/inspect-glb/SKILL.md +27 -18
- package/skills/inspect-glb/scripts/inspect.mjs +179 -50
- package/templates/_features/physics/assets/public/ammo/LICENSE +21 -0
- package/templates/_features/physics/assets/public/ammo/SOURCE.md +16 -0
- package/templates/_features/physics/assets/public/ammo/ammo.js +973 -0
- package/templates/_features/physics/assets/public/ammo/ammo.wasm.js +937 -0
- package/templates/_features/physics/assets/public/ammo/ammo.wasm.wasm +0 -0
- package/templates/_features/physics/react/package.json +5 -0
- package/templates/_features/physics/react/src/sync-ammo.d.ts +4 -0
- package/templates/_shared/ar-placement/README.md +7 -0
- package/templates/_shared/ar-placement/src/ar-placement.ts +123 -0
- package/templates/_shared/first-person-controller/README.md +7 -0
- package/templates/_shared/first-person-controller/src/controller.ts +18 -0
- package/templates/_shared/first-person-controller/src/scripts.d.ts +5 -0
- package/templates/_shared/interactive-sphere/README.md +7 -0
- package/templates/_shared/model-viewer/README.md +7 -0
- package/templates/_shared/physics-playground/README.md +7 -0
- package/templates/_shared/product-configurator/ASSET_LICENSES.md +3 -0
- package/templates/_shared/product-configurator/README.md +7 -0
- package/templates/_shared/product-configurator/src/product.css +14 -0
- package/templates/_shared/scene-editor/README.md +7 -0
- package/templates/_shared/scene-editor/src/scene-editor.ts +57 -0
- package/templates/_shared/spinning-cube/README.md +7 -0
- package/templates/_shared/splat-viewer/README.md +7 -0
- package/templates/_shared/sprite-game/README.md +7 -0
- package/templates/_shared/sprite-game/src/sprite-game.ts +197 -0
- package/templates/_shared/third-person-controller/README.md +7 -0
- package/templates/_shared/third-person-controller/src/controller.ts +53 -0
- package/templates/_shared/third-person-controller/src/scripts.d.ts +5 -0
- package/templates/_shared/vr-starter/README.md +7 -0
- package/templates/_shared/vr-starter/src/vr.ts +40 -0
- package/templates/_shared/vr-starter/src/xr.d.ts +11 -0
- package/templates/engine/ar-placement/src/main.ts +77 -0
- package/templates/engine/base/.prettierignore +1 -0
- package/templates/engine/base/README.md +1 -1
- package/templates/engine/base/_npmrc +1 -0
- package/templates/engine/base/eslint.config.mjs +1 -1
- package/templates/engine/base/index.html +13 -0
- package/templates/engine/base/src/starter.css +104 -0
- package/templates/engine/first-person-controller/src/main.ts +139 -0
- package/templates/engine/interactive-sphere/src/App.ts +9 -7
- package/templates/engine/interactive-sphere/src/main.ts +2 -29
- package/templates/engine/interactive-sphere/src/style.css +18 -97
- package/templates/engine/model-viewer/src/main.ts +3 -2
- package/templates/engine/physics-playground/src/main.ts +132 -0
- package/templates/engine/product-configurator/src/main.ts +156 -0
- package/templates/engine/scene-editor/src/main.ts +90 -0
- package/templates/engine/splat-viewer/src/main.ts +4 -3
- package/templates/engine/sprite-game/src/main.ts +72 -0
- package/templates/engine/third-person-controller/src/main.ts +216 -0
- package/templates/engine/vr-starter/src/main.ts +101 -0
- package/templates/index.ts +66 -5
- package/templates/react/ar-placement/src/App.tsx +58 -0
- package/templates/react/base/.prettierignore +1 -0
- package/templates/react/base/README.md +1 -1
- package/templates/react/base/_npmrc +1 -0
- package/templates/react/base/eslint.config.mjs +1 -1
- package/templates/react/base/index.html +13 -0
- package/templates/react/base/src/main.tsx +10 -0
- package/templates/react/base/src/starter.css +104 -0
- package/templates/react/first-person-controller/src/App.tsx +115 -0
- package/templates/react/interactive-sphere/src/App.css +18 -68
- package/templates/react/interactive-sphere/src/App.tsx +1 -22
- package/templates/react/interactive-sphere/src/Scene.tsx +7 -6
- package/templates/react/interactive-sphere/src/index.css +1 -48
- package/templates/react/model-viewer/src/App.tsx +2 -2
- package/templates/react/physics-playground/src/App.tsx +85 -0
- package/templates/react/product-configurator/src/App.tsx +157 -0
- package/templates/react/scene-editor/src/App.tsx +84 -0
- package/templates/react/splat-viewer/src/App.tsx +3 -3
- package/templates/react/sprite-game/src/App.tsx +38 -0
- package/templates/react/third-person-controller/src/App.tsx +193 -0
- package/templates/react/vr-starter/src/App.tsx +99 -0
- package/templates/web-components/ar-placement/index.html +44 -0
- package/templates/web-components/ar-placement/src/main.ts +13 -0
- package/templates/web-components/base/.prettierignore +1 -0
- package/templates/web-components/base/README.md +1 -1
- package/templates/web-components/base/_npmrc +1 -0
- package/templates/web-components/base/eslint.config.mjs +1 -1
- package/templates/web-components/base/package.json +1 -1
- package/templates/web-components/base/src/starter.css +104 -0
- package/templates/web-components/first-person-controller/index.html +168 -0
- package/templates/web-components/first-person-controller/src/main.ts +15 -0
- package/templates/web-components/interactive-sphere/index.html +5 -22
- package/templates/web-components/interactive-sphere/src/main.ts +5 -4
- package/templates/web-components/interactive-sphere/src/style.css +18 -76
- package/templates/web-components/model-viewer/index.html +1 -1
- package/templates/web-components/model-viewer/src/main.ts +2 -1
- package/templates/web-components/physics-playground/index.html +51 -0
- package/templates/web-components/physics-playground/src/main.ts +32 -0
- package/templates/web-components/product-configurator/index.html +51 -0
- package/templates/web-components/product-configurator/src/main.ts +93 -0
- package/templates/web-components/scene-editor/index.html +51 -0
- package/templates/web-components/scene-editor/src/main.ts +26 -0
- package/templates/web-components/spinning-cube/src/main.ts +1 -0
- package/templates/web-components/splat-viewer/index.html +2 -2
- package/templates/web-components/splat-viewer/src/main.ts +2 -1
- package/templates/web-components/sprite-game/index.html +34 -0
- package/templates/web-components/sprite-game/src/main.ts +9 -0
- package/templates/web-components/third-person-controller/index.html +183 -0
- package/templates/web-components/third-person-controller/src/main.ts +16 -0
- package/templates/web-components/vr-starter/index.html +80 -0
- package/templates/web-components/vr-starter/src/main.ts +12 -0
- package/templates/engine/interactive-sphere/src/assets/typescript.svg +0 -1
- package/templates/react/interactive-sphere/src/assets/react.svg +0 -1
- package/templates/web-components/interactive-sphere/src/assets/typescript.svg +0 -1
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// zero-dep glb metadata inspector. bounds come from decoded vertex positions, not accessor min/max,
|
|
3
3
|
// so a rotated node reports its true extent and models can be placed touching.
|
|
4
4
|
import fs from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
5
6
|
|
|
6
7
|
const GLB_MAGIC = 0x46546c67;
|
|
7
8
|
const JSON_CHUNK = 0x4e4f534a;
|
|
8
9
|
const BIN_CHUNK = 0x004e4942;
|
|
9
10
|
const IDENTITY = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
11
|
+
const MAX_ACCESSOR_VALUES = 30_000_000;
|
|
12
|
+
const TYPES = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 };
|
|
10
13
|
|
|
11
14
|
// componentType -> byte size, signedness and normalized divisor (glTF 2.0 table 3.1 / 5.1)
|
|
12
15
|
const COMPONENTS = {
|
|
@@ -18,6 +21,14 @@ const COMPONENTS = {
|
|
|
18
21
|
5126: { size: 4, signed: true, norm: 1, read: (b, o) => b.readFloatLE(o) }
|
|
19
22
|
};
|
|
20
23
|
|
|
24
|
+
const tryCatch = (fn) => {
|
|
25
|
+
try {
|
|
26
|
+
return [null, fn()];
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return [error];
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
21
32
|
const mul = (a, b) => {
|
|
22
33
|
const o = new Array(16);
|
|
23
34
|
for (let c = 0; c < 4; c++) {
|
|
@@ -73,41 +84,99 @@ const round = (v) => Math.round(v * 1e4) / 1e4 + 0;
|
|
|
73
84
|
|
|
74
85
|
// buffer 0 of a GLB is the BIN chunk; other buffers are external files or data URIs
|
|
75
86
|
const bufferBytes = (g, bin, index) => {
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
87
|
+
const buffer = g.buffers?.[index];
|
|
88
|
+
if (!buffer) return null;
|
|
89
|
+
const uri = buffer.uri;
|
|
90
|
+
if (uri === undefined) return index === 0 ? bin : null;
|
|
78
91
|
const base64 = /^data:[^,]*;base64,(.*)$/s.exec(uri);
|
|
79
92
|
return base64 ? Buffer.from(base64[1], 'base64') : null;
|
|
80
93
|
};
|
|
81
94
|
|
|
82
|
-
|
|
83
|
-
// otherwise a reason the vertices could not be read so the caller can fall back and say so.
|
|
84
|
-
const accumulate = (g, bin, accessor, matrix, min, max) => {
|
|
95
|
+
const readAccessor = (g, bin, accessor, type = 'VEC3') => {
|
|
85
96
|
const acc = g.accessors?.[accessor];
|
|
86
|
-
if (!acc) return 'no-position-accessor';
|
|
87
|
-
if (acc.sparse) return '
|
|
88
|
-
if (acc.bufferView === undefined) return 'no-vertex-data';
|
|
97
|
+
if (!acc) return ['no-position-accessor'];
|
|
98
|
+
if (acc.bufferView === undefined && !acc.sparse) return ['no-vertex-data'];
|
|
89
99
|
const comp = COMPONENTS[acc.componentType];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const stride = view.byteStride ?? comp.size * 3;
|
|
97
|
-
const base = (view.byteOffset ?? 0) + (acc.byteOffset ?? 0);
|
|
98
|
-
if (!acc.count || base + (acc.count - 1) * stride + comp.size * 3 > bytes.length) return 'truncated-buffer';
|
|
100
|
+
const size = TYPES[acc.type];
|
|
101
|
+
if (!comp || !size || acc.type !== type || !Number.isInteger(acc.count) || acc.count < 1) {
|
|
102
|
+
return ['unsupported-accessor'];
|
|
103
|
+
}
|
|
104
|
+
if (acc.count * size > MAX_ACCESSOR_VALUES) return ['accessor-too-large'];
|
|
105
|
+
const data = new Float32Array(acc.count * size);
|
|
99
106
|
const scale = acc.normalized ? comp.norm : 1;
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
const read = (bytes, offset) => {
|
|
108
|
+
const value = comp.read(bytes, offset) / scale;
|
|
109
|
+
return acc.normalized && comp.signed && value < -1 ? -1 : value;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (acc.bufferView !== undefined) {
|
|
113
|
+
const view = g.bufferViews?.[acc.bufferView];
|
|
114
|
+
if (!view || !Number.isInteger(view.byteLength)) return ['unsupported-accessor'];
|
|
115
|
+
if (view.extensions?.EXT_meshopt_compression) return ['meshopt-compressed'];
|
|
116
|
+
const bytes = bufferBytes(g, bin, view.buffer ?? 0);
|
|
117
|
+
if (!bytes) return ['external-buffer'];
|
|
118
|
+
const width = comp.size * size;
|
|
119
|
+
const stride = view.byteStride ?? width;
|
|
120
|
+
const base = (view.byteOffset ?? 0) + (acc.byteOffset ?? 0);
|
|
121
|
+
const used = (acc.byteOffset ?? 0) + (acc.count - 1) * stride + width;
|
|
122
|
+
if (stride < width || base < 0 || used > view.byteLength || base + (acc.count - 1) * stride + width > bytes.length) {
|
|
123
|
+
return ['truncated-buffer'];
|
|
124
|
+
}
|
|
125
|
+
for (let i = 0; i < acc.count; i++) {
|
|
126
|
+
for (let k = 0; k < size; k++) data[i * size + k] = read(bytes, base + i * stride + k * comp.size);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (acc.sparse) {
|
|
131
|
+
const sparse = acc.sparse;
|
|
132
|
+
const idx = sparse.indices;
|
|
133
|
+
const values = sparse.values;
|
|
134
|
+
const idxComp = idx && [5121, 5123, 5125].includes(idx.componentType) ? COMPONENTS[idx.componentType] : null;
|
|
135
|
+
const idxView = g.bufferViews?.[idx?.bufferView];
|
|
136
|
+
const valueView = g.bufferViews?.[values?.bufferView];
|
|
137
|
+
if (!Number.isInteger(sparse.count) || sparse.count < 1 || sparse.count > acc.count || !idxComp ||
|
|
138
|
+
!Number.isInteger(idxView?.byteLength) || !Number.isInteger(valueView?.byteLength)) {
|
|
139
|
+
return ['invalid-sparse-accessor'];
|
|
140
|
+
}
|
|
141
|
+
if (idxView.extensions?.EXT_meshopt_compression || valueView.extensions?.EXT_meshopt_compression) {
|
|
142
|
+
return ['meshopt-compressed'];
|
|
143
|
+
}
|
|
144
|
+
const idxBytes = bufferBytes(g, bin, idxView.buffer ?? 0);
|
|
145
|
+
const valueBytes = bufferBytes(g, bin, valueView.buffer ?? 0);
|
|
146
|
+
if (!idxBytes || !valueBytes) return ['external-buffer'];
|
|
147
|
+
const idxBase = (idxView.byteOffset ?? 0) + (idx.byteOffset ?? 0);
|
|
148
|
+
const valueBase = (valueView.byteOffset ?? 0) + (values.byteOffset ?? 0);
|
|
149
|
+
const valueWidth = comp.size * size;
|
|
150
|
+
const idxUsed = (idx.byteOffset ?? 0) + sparse.count * idxComp.size;
|
|
151
|
+
const valueUsed = (values.byteOffset ?? 0) + sparse.count * valueWidth;
|
|
152
|
+
if (idxBase < 0 || valueBase < 0 || idxUsed > idxView.byteLength || valueUsed > valueView.byteLength ||
|
|
153
|
+
idxBase + sparse.count * idxComp.size > idxBytes.length || valueBase + sparse.count * valueWidth > valueBytes.length) {
|
|
154
|
+
return ['truncated-buffer'];
|
|
155
|
+
}
|
|
156
|
+
let prev = -1;
|
|
157
|
+
for (let i = 0; i < sparse.count; i++) {
|
|
158
|
+
const index = idxComp.read(idxBytes, idxBase + i * idxComp.size);
|
|
159
|
+
if (index <= prev || index >= acc.count) return ['invalid-sparse-accessor'];
|
|
160
|
+
prev = index;
|
|
161
|
+
for (let k = 0; k < size; k++) {
|
|
162
|
+
data[index * size + k] = read(valueBytes, valueBase + i * valueWidth + k * comp.size);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return [null, data];
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const accumulate = (data, matrix, min, max, morphs = []) => {
|
|
171
|
+
for (let i = 0; i < data.length; i += 3) {
|
|
172
|
+
let x = data[i];
|
|
173
|
+
let y = data[i + 1];
|
|
174
|
+
let z = data[i + 2];
|
|
175
|
+
for (const [weight, delta] of morphs) {
|
|
176
|
+
x += delta[i] * weight;
|
|
177
|
+
y += delta[i + 1] * weight;
|
|
178
|
+
z += delta[i + 2] * weight;
|
|
109
179
|
}
|
|
110
|
-
// inlined to avoid allocating a vector per vertex on million-vertex meshes
|
|
111
180
|
const px = matrix[0] * x + matrix[4] * y + matrix[8] * z + matrix[12];
|
|
112
181
|
const py = matrix[1] * x + matrix[5] * y + matrix[9] * z + matrix[13];
|
|
113
182
|
const pz = matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14];
|
|
@@ -118,13 +187,12 @@ const accumulate = (g, bin, accessor, matrix, min, max) => {
|
|
|
118
187
|
if (py > max[1]) max[1] = py;
|
|
119
188
|
if (pz > max[2]) max[2] = pz;
|
|
120
189
|
}
|
|
121
|
-
return null;
|
|
122
190
|
};
|
|
123
191
|
|
|
124
192
|
// loose fallback: bound the 8 corners of the accessor's local box. over-estimates a mesh that does
|
|
125
193
|
// not fill its box when the node rotation is not a multiple of 90 degrees.
|
|
126
194
|
const accumulateCorners = (acc, matrix, min, max) => {
|
|
127
|
-
if (!acc?.min || !acc?.max) return;
|
|
195
|
+
if (!acc?.min || !acc?.max) return false;
|
|
128
196
|
for (let c = 0; c < 8; c++) {
|
|
129
197
|
const point = xform(matrix, [
|
|
130
198
|
c & 1 ? acc.max[0] : acc.min[0],
|
|
@@ -136,35 +204,55 @@ const accumulateCorners = (acc, matrix, min, max) => {
|
|
|
136
204
|
if (point[k] > max[k]) max[k] = point[k];
|
|
137
205
|
}
|
|
138
206
|
}
|
|
207
|
+
return true;
|
|
139
208
|
};
|
|
140
209
|
|
|
141
210
|
export const inspectGlb = (buf) => {
|
|
142
211
|
if (buf.length < 20 || buf.readUInt32LE(0) !== GLB_MAGIC || buf.readUInt32LE(16) !== JSON_CHUNK) {
|
|
143
212
|
throw new Error('not a GLB (or JSON chunk missing)');
|
|
144
213
|
}
|
|
214
|
+
if (buf.readUInt32LE(4) !== 2) throw new Error('unsupported GLB version');
|
|
215
|
+
if (buf.readUInt32LE(8) !== buf.length) throw new Error('invalid GLB length');
|
|
145
216
|
const jsonLength = buf.readUInt32LE(12);
|
|
217
|
+
if (20 + jsonLength > buf.length) throw new Error('invalid JSON chunk length');
|
|
146
218
|
const g = JSON.parse(buf.toString('utf8', 20, 20 + jsonLength));
|
|
147
219
|
const binAt = 20 + jsonLength;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
220
|
+
let bin = null;
|
|
221
|
+
if (binAt < buf.length) {
|
|
222
|
+
if (binAt + 8 > buf.length || buf.readUInt32LE(binAt + 4) !== BIN_CHUNK) {
|
|
223
|
+
throw new Error('invalid BIN chunk');
|
|
224
|
+
}
|
|
225
|
+
const length = buf.readUInt32LE(binAt);
|
|
226
|
+
if (binAt + 8 + length !== buf.length) throw new Error('invalid BIN chunk length');
|
|
227
|
+
bin = buf.subarray(binAt + 8);
|
|
228
|
+
}
|
|
152
229
|
const nodes = g.nodes ?? [];
|
|
153
230
|
const nodeName = (i) => nodes[i]?.name ?? `node_${i}`;
|
|
154
231
|
const parents = new Array(nodes.length).fill(-1);
|
|
155
232
|
for (let i = 0; i < nodes.length; i++) {
|
|
156
|
-
for (const child of nodes[i].children ?? [])
|
|
233
|
+
for (const child of nodes[i].children ?? []) {
|
|
234
|
+
if (!Number.isInteger(child) || !nodes[child] || parents[child] !== -1) throw new Error('invalid node graph');
|
|
235
|
+
parents[child] = i;
|
|
236
|
+
}
|
|
157
237
|
}
|
|
158
238
|
const nodePath = (i) => {
|
|
159
239
|
const names = [];
|
|
160
|
-
|
|
240
|
+
const seen = new Set();
|
|
241
|
+
for (; i >= 0; i = parents[i]) {
|
|
242
|
+
if (seen.has(i)) throw new Error('invalid node graph');
|
|
243
|
+
seen.add(i);
|
|
244
|
+
names.unshift(nodeName(i));
|
|
245
|
+
}
|
|
161
246
|
return names.join('/');
|
|
162
247
|
};
|
|
163
248
|
const children = new Set(nodes.flatMap((n) => n.children ?? []));
|
|
164
249
|
const roots = g.scenes?.[g.scene ?? 0]?.nodes ?? nodes.map((_, i) => i).filter((i) => !children.has(i));
|
|
165
250
|
|
|
166
251
|
const world = new Array(nodes.length);
|
|
252
|
+
const seen = new Set();
|
|
167
253
|
const walk = (i, parent) => {
|
|
254
|
+
if (!Number.isInteger(i) || !nodes[i] || seen.has(i)) throw new Error('invalid node graph');
|
|
255
|
+
seen.add(i);
|
|
168
256
|
const n = nodes[i];
|
|
169
257
|
const local = n.matrix ?? compose(n.translation ?? [0, 0, 0], n.rotation ?? [0, 0, 0, 1], n.scale ?? [1, 1, 1]);
|
|
170
258
|
world[i] = mul(parent, local);
|
|
@@ -176,28 +264,67 @@ export const inspectGlb = (buf) => {
|
|
|
176
264
|
const max = [-Infinity, -Infinity, -Infinity];
|
|
177
265
|
const notes = new Set();
|
|
178
266
|
let meshNodes = 0;
|
|
267
|
+
let morphed = false;
|
|
268
|
+
let fallback = false;
|
|
269
|
+
let incomplete = false;
|
|
179
270
|
for (let i = 0; i < nodes.length; i++) {
|
|
180
271
|
if (nodes[i].mesh === undefined || !world[i]) continue;
|
|
181
272
|
meshNodes++;
|
|
182
273
|
// glTF 2.0: "the transform of the skinned mesh node MUST be ignored" - joints drive the pose
|
|
183
274
|
const matrix = nodes[i].skin === undefined ? world[i] : IDENTITY;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
275
|
+
const mesh = g.meshes?.[nodes[i].mesh];
|
|
276
|
+
for (const prim of mesh?.primitives ?? []) {
|
|
277
|
+
const [error, data] = prim.extensions?.KHR_draco_mesh_compression
|
|
278
|
+
? ['draco-compressed']
|
|
279
|
+
: readAccessor(g, bin, prim.attributes?.POSITION);
|
|
280
|
+
if (error) {
|
|
281
|
+
notes.add(error);
|
|
282
|
+
if (error === 'meshopt-compressed' || !accumulateCorners(g.accessors?.[prim.attributes?.POSITION], matrix, min, max)) {
|
|
283
|
+
incomplete = true;
|
|
284
|
+
} else {
|
|
285
|
+
fallback = true;
|
|
286
|
+
}
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const morphs = [];
|
|
290
|
+
if (prim.targets?.length) {
|
|
291
|
+
morphed = true;
|
|
292
|
+
const weights = nodes[i].weights ?? mesh.weights ?? [];
|
|
293
|
+
for (let k = 0; k < prim.targets.length; k++) {
|
|
294
|
+
const weight = weights[k] ?? 0;
|
|
295
|
+
if (!Number.isFinite(weight)) {
|
|
296
|
+
notes.add('invalid-morph-weight');
|
|
297
|
+
incomplete = true;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const accessor = prim.targets[k].POSITION;
|
|
301
|
+
if (!weight || accessor === undefined) continue;
|
|
302
|
+
const [morphError, delta] = readAccessor(g, bin, accessor);
|
|
303
|
+
if (morphError || delta.length !== data.length) {
|
|
304
|
+
notes.add(`morph-${morphError ?? 'accessor-size'}`);
|
|
305
|
+
incomplete = true;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
morphs.push([weight, delta]);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
accumulate(data, matrix, min, max, morphs);
|
|
191
312
|
}
|
|
192
313
|
}
|
|
193
314
|
|
|
194
315
|
const ok = min[0] !== Infinity;
|
|
316
|
+
const morphAnimated = (g.animations ?? []).some((a) =>
|
|
317
|
+
(a.channels ?? []).some((c) => c.target?.path === 'weights')
|
|
318
|
+
);
|
|
319
|
+
const skinned = !!g.skins?.length;
|
|
195
320
|
return {
|
|
196
321
|
aabb: ok ? { min: min.map(round), max: max.map(round) } : null,
|
|
197
322
|
dims: ok ? max.map((v, i) => round(v - min[i])) : null,
|
|
198
323
|
center: ok ? max.map((v, i) => round((v + min[i]) / 2)) : null,
|
|
199
324
|
groundOffset: ok ? round(-min[1]) : null,
|
|
200
|
-
boundsSource: ok ? (
|
|
325
|
+
boundsSource: ok ? (incomplete ? 'incomplete' : fallback ? 'accessor-minmax' : 'vertices') : null,
|
|
326
|
+
boundsPose: skinned ? 'bind' : morphed ? 'default-morph' : 'static',
|
|
327
|
+
requiresRuntimeCheck: skinned || morphAnimated || notes.size > 0,
|
|
201
328
|
...(notes.size ? { boundsNotes: [...notes].sort() } : {}),
|
|
202
329
|
nodes: nodes.length,
|
|
203
330
|
nodePaths: nodes.map((_, i) => nodePath(i)).sort(),
|
|
@@ -217,11 +344,15 @@ export const inspectGlb = (buf) => {
|
|
|
217
344
|
)
|
|
218
345
|
)
|
|
219
346
|
].sort(),
|
|
220
|
-
|
|
347
|
+
morphed,
|
|
348
|
+
morphAnimated,
|
|
349
|
+
skinned
|
|
221
350
|
};
|
|
222
351
|
};
|
|
223
352
|
|
|
224
|
-
|
|
353
|
+
const main = import.meta.main ?? (process.argv[1] &&
|
|
354
|
+
fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(process.argv[1]));
|
|
355
|
+
if (main) {
|
|
225
356
|
const files = process.argv.slice(2);
|
|
226
357
|
if (!files.length) {
|
|
227
358
|
console.error('usage: node inspect.mjs <file.glb> [more.glb ...]');
|
|
@@ -229,11 +360,9 @@ if (import.meta.main ?? import.meta.filename === process.argv[1]) {
|
|
|
229
360
|
}
|
|
230
361
|
// report per-file failures so one unreadable file cannot discard a whole glob
|
|
231
362
|
const out = files.map((file) => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
} catch (e) {
|
|
235
|
-
return { file, error: e.message };
|
|
236
|
-
}
|
|
363
|
+
const [error, result] = tryCatch(() => inspectGlb(fs.readFileSync(file)));
|
|
364
|
+
return error ? { file, error: error instanceof Error ? error.message : String(error) } : { file, ...result };
|
|
237
365
|
});
|
|
366
|
+
if (out.some((result) => result.error)) process.exitCode = 1;
|
|
238
367
|
console.log(JSON.stringify(out.length === 1 ? out[0] : out, null, 2));
|
|
239
368
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Copyright (c) 2011 ammo.js contributors
|
|
2
|
+
|
|
3
|
+
This software is provided 'as-is', without any express or implied
|
|
4
|
+
warranty. In no event will the authors be held liable for any damages
|
|
5
|
+
arising from the use of this software.
|
|
6
|
+
|
|
7
|
+
Permission is granted to anyone to use this software for any purpose,
|
|
8
|
+
including commercial applications, and to alter it and redistribute it
|
|
9
|
+
freely, subject to the following restrictions:
|
|
10
|
+
|
|
11
|
+
1. The origin of this software must not be misrepresented; you must not
|
|
12
|
+
claim that you wrote the original software. If you use this software
|
|
13
|
+
in a product, an acknowledgment in the product documentation would be
|
|
14
|
+
appreciated but is not required.
|
|
15
|
+
2. Altered source versions must be plainly marked as such, and must not be
|
|
16
|
+
misrepresented as being the original software.
|
|
17
|
+
3. This notice may not be removed or altered from any source distribution.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
See AUTHORS for list of contributors.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Ammo.js
|
|
2
|
+
|
|
3
|
+
These files are copied from `playcanvas/engine` tag `v2.21.4` (commit
|
|
4
|
+
`e287e0c67f3c20c689a52b7c53d2b7fedbe887da`) at
|
|
5
|
+
`examples/assets/wasm/ammo/`.
|
|
6
|
+
|
|
7
|
+
They are a WebAssembly build of [ammo.js](https://github.com/kripken/ammo.js),
|
|
8
|
+
distributed under the zlib license in `LICENSE`.
|
|
9
|
+
|
|
10
|
+
## SHA-256
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
ef166d1315bc4a6441a8de341ecdf6ac4e7d69055caec65c523ed1a4e8e19b15 ammo.js
|
|
14
|
+
5645b5a0c4f03be9d9d1ae604ffacd5e5e525310cfd1d0ed27474cdd1f34aab0 ammo.wasm.js
|
|
15
|
+
a61b504d4a6ce6bb93bd843e0f61edb8115e7317f1b3462247031a83ddb25d09 ammo.wasm.wasm
|
|
16
|
+
```
|