@forgeax/engine-geometry 0.1.28 → 0.1.29
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 +8 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +33 -37
- package/dist/index.mjs.map +1 -1
- package/dist/vertex-attribute-layout.d.ts +14 -0
- package/dist/vertex-attribute-layout.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/vertex-attribute-layout-owner.unit.test.ts +51 -0
- package/src/index.ts +3 -0
- package/src/vertex-attribute-layout.ts +56 -55
package/README.md
CHANGED
|
@@ -204,6 +204,8 @@ produce the normal vec4 or mesh result.
|
|
|
204
204
|
| `deriveVertexBufferLayout(map, opts?)` | fn | Derives a fixed-order `GpuVertexBufferLayoutEntry[]` from a `VertexAttributeMap`. The canonical 14-key order (position / normal / uv / tangent / skinIndex / skinWeight / uv1..uv7 / color) assigns `@location(N)` per key. Absent keys reserve no space; `opts.shaderUvSetCount` enables clamp-to-last alias for UV sets. |
|
|
205
205
|
| `deriveVertexLayoutProjection(map)` | fn | Creates the immutable canonical projection (`schemaVersion`, attributes, mask, stride, digest). `color` is fixed at host location 13 and `float32x4`. |
|
|
206
206
|
| `packInterleavedVertexAttributes(map, vertexCount)` | fn | Packs attribute arrays using that projection and returns `Result<{ projection, vertices }, VertexAttributePackError>`; closed detail reasons identify storage, cardinality, vertex-count, or non-finite failures. |
|
|
207
|
+
| `DEFAULT_VERTEX_ATTRIBUTE_MAP` / `SKIN_VERTEX_ATTRIBUTE_MAP` | const | Canonical presence maps for ordinary and skinned interleaved meshes. Render, recovery, and feature assembly use these maps instead of spelling a fallback layout locally. |
|
|
208
|
+
| `deriveVertexCount(vertices, projection)` | fn | Validates `vertices.byteLength % projection.arrayStride === 0` and derives the integer vertex count shared by upload, LOD, update, morph, and draw-range consumers. |
|
|
207
209
|
| `deriveVertexLayoutProjectionFromMask(mask)` | fn | Reconstructs a wire projection through the same canonical key owner; returns a typed error for empty or unknown mask bits. |
|
|
208
210
|
| `buildMeshAttributeMapForUvSets(uvSetCount)` | fn | Synthesise a `VertexAttributeMap` with empty `Float32Array(0)` placeholders for `uv` + `uv1..uvN-1`. Used by the forward record stage to pre-declare UV-set slots before interleaving. |
|
|
209
211
|
| `GpuVertexBufferLayoutEntry` | type | `{ shaderLocation: number; offset: number; format: GPUVertexFormat }` -- one GPU vertex buffer layout entry. |
|
|
@@ -223,6 +225,12 @@ The canonical key order and per-key `GPUVertexFormat` mapping is the SSOT in
|
|
|
223
225
|
Shader `@location(N)` declarations and naga reflection deep-equal tests keep
|
|
224
226
|
them in sync.
|
|
225
227
|
|
|
228
|
+
> [!IMPORTANT]
|
|
229
|
+
> Keep vertex bytes and `VertexLayoutProjection` together across every GPU
|
|
230
|
+
> owner. Derive the count with `deriveVertexCount`; a non-divisible payload is
|
|
231
|
+
> rejected before GPU resource construction instead of being truncated into a
|
|
232
|
+
> fractional draw range.
|
|
233
|
+
|
|
226
234
|
## Three.js r184 mental alignment
|
|
227
235
|
|
|
228
236
|
The 6 factory signatures mirror Three.js `BufferGeometry` constructors
|
package/dist/index.d.ts
CHANGED
|
@@ -14,5 +14,5 @@ export { createSphereGeometry } from './sphere';
|
|
|
14
14
|
export { computeTangentVec4 } from './tangent';
|
|
15
15
|
export { createTeapotGeometry, type TeapotMeshAsset, type TeapotProvenance, } from './teapot';
|
|
16
16
|
export { createTorusGeometry } from './torus';
|
|
17
|
-
export { buildMeshAttributeMapForUvSets, deriveVertexBufferLayout, deriveVertexBufferLayoutFromProjection, deriveVertexLayoutProjection, deriveVertexLayoutProjectionFromMask, type GpuVertexBufferLayoutEntry, type PackedVertexAttributes, packInterleavedVertexAttributes, VertexAttributePackError, type VertexLayoutProjection, type VertexLayoutProjectionAttribute, type VertexLayoutProjectionMaskError, } from './vertex-attribute-layout';
|
|
17
|
+
export { buildMeshAttributeMapForUvSets, DEFAULT_VERTEX_ATTRIBUTE_MAP, deriveVertexBufferLayout, deriveVertexBufferLayoutFromProjection, deriveVertexCount, deriveVertexLayoutProjection, deriveVertexLayoutProjectionFromMask, type GpuVertexBufferLayoutEntry, type PackedVertexAttributes, packInterleavedVertexAttributes, SKIN_VERTEX_ATTRIBUTE_MAP, VertexAttributePackError, type VertexLayoutProjection, type VertexLayoutProjectionAttribute, type VertexLayoutProjectionMaskError, } from './vertex-attribute-layout';
|
|
18
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,YAAY,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,iBAAiB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,OAAO,EACZ,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,IAAI,GACV,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EACL,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EACL,8BAA8B,EAC9B,wBAAwB,EACxB,sCAAsC,EACtC,4BAA4B,EAC5B,oCAAoC,EACpC,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,+BAA+B,EAC/B,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,+BAA+B,EACpC,KAAK,+BAA+B,GACrC,MAAM,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,YAAY,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,iBAAiB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,OAAO,EACZ,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,IAAI,GACV,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EACL,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EACL,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,EACxB,sCAAsC,EACtC,iBAAiB,EACjB,4BAA4B,EAC5B,oCAAoC,EACpC,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,+BAA+B,EAC/B,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,+BAA+B,EACpC,KAAK,+BAA+B,GACrC,MAAM,2BAA2B,CAAC"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { decodeMeshBinHeader, MESH_BIN_HEADER_V4_BYTES, MESH_BIN_VERSION } from '@forgeax/engine-pack';
|
|
2
2
|
import { box3, circle2, box2 } from '@forgeax/engine-math';
|
|
3
|
-
import { err, ok,
|
|
3
|
+
import { err, ok, AssetError, ASSET_ERROR_HINTS } from '@forgeax/engine-types';
|
|
4
4
|
|
|
5
5
|
// src/assets/mesh-binary.ts
|
|
6
6
|
var EPSILON = 1e-8;
|
|
@@ -275,9 +275,25 @@ var CANONICAL_KEYS = Object.keys(ATTRIBUTE_FORMAT_MAP).filter(isAttributeKey);
|
|
|
275
275
|
var UV_KEYS = CANONICAL_KEYS.filter(
|
|
276
276
|
(key) => key === "uv" || key.startsWith("uv")
|
|
277
277
|
);
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
var EMPTY_FLOAT32 = new Float32Array(0);
|
|
279
|
+
var EMPTY_UINT16 = new Uint16Array(0);
|
|
280
|
+
var DEFAULT_VERTEX_ATTRIBUTE_MAP = Object.freeze({
|
|
281
|
+
position: EMPTY_FLOAT32,
|
|
282
|
+
normal: EMPTY_FLOAT32,
|
|
283
|
+
uv: EMPTY_FLOAT32,
|
|
284
|
+
tangent: EMPTY_FLOAT32
|
|
285
|
+
});
|
|
286
|
+
var SKIN_VERTEX_ATTRIBUTE_MAP = Object.freeze({
|
|
287
|
+
...DEFAULT_VERTEX_ATTRIBUTE_MAP,
|
|
288
|
+
skinIndex: EMPTY_UINT16,
|
|
289
|
+
skinWeight: EMPTY_FLOAT32
|
|
290
|
+
});
|
|
291
|
+
function emitAliasEntries(entries, toIndex, currentStride) {
|
|
292
|
+
const lastUv = entries.filter((entry) => UV_KEYS.includes(entry.key)).at(-1);
|
|
293
|
+
const aliasOffset = lastUv?.offset ?? currentStride;
|
|
294
|
+
for (let k = 0; k < toIndex && k < UV_KEYS.length; k++) {
|
|
280
295
|
const uvKey = UV_KEYS[k];
|
|
296
|
+
if (entries.some((entry) => entry.key === uvKey)) continue;
|
|
281
297
|
entries.push({
|
|
282
298
|
key: uvKey,
|
|
283
299
|
shaderLocation: CANONICAL_KEYS.indexOf(uvKey),
|
|
@@ -285,15 +301,10 @@ function emitAliasEntries(entries, fromIndex, toIndex, aliasOffset, currentStrid
|
|
|
285
301
|
format: ATTRIBUTE_FORMAT_MAP[uvKey]
|
|
286
302
|
});
|
|
287
303
|
}
|
|
288
|
-
return
|
|
304
|
+
return lastUv === void 0 ? currentStride + ATTRIBUTE_BYTE_STRIDE.uv : currentStride;
|
|
289
305
|
}
|
|
290
306
|
function buildMeshAttributeMapForUvSets(uvSetCount) {
|
|
291
|
-
const map = {
|
|
292
|
-
position: new Float32Array(0),
|
|
293
|
-
normal: new Float32Array(0),
|
|
294
|
-
uv: new Float32Array(0),
|
|
295
|
-
tangent: new Float32Array(0)
|
|
296
|
-
};
|
|
307
|
+
const map = { ...DEFAULT_VERTEX_ATTRIBUTE_MAP };
|
|
297
308
|
for (let set = 1; set < uvSetCount && set < UV_KEYS.length; set++) {
|
|
298
309
|
map[UV_KEYS[set]] = new Float32Array(0);
|
|
299
310
|
}
|
|
@@ -315,14 +326,7 @@ function deriveVertexBufferLayout(map, opts) {
|
|
|
315
326
|
}
|
|
316
327
|
const present = entries.length;
|
|
317
328
|
if (shaderUvSetCount > 0) {
|
|
318
|
-
|
|
319
|
-
if (shaderUvSetCount > meshUvSetCount) {
|
|
320
|
-
const lastUvIndex = meshUvSetCount - 1;
|
|
321
|
-
const lastUvKey = lastUvIndex >= 0 && lastUvIndex < UV_KEYS.length ? UV_KEYS[lastUvIndex] : void 0;
|
|
322
|
-
const aliasOffset = lastUvKey !== void 0 ? CANONICAL_KEYS.indexOf(lastUvKey) : -1;
|
|
323
|
-
const aliasByteOffset = aliasOffset >= 0 ? entries.find((e) => e.shaderLocation === aliasOffset)?.offset ?? 0 : offset;
|
|
324
|
-
offset = emitAliasEntries(entries, meshUvSetCount, shaderUvSetCount, aliasByteOffset, offset);
|
|
325
|
-
}
|
|
329
|
+
offset = emitAliasEntries(entries, shaderUvSetCount, offset);
|
|
326
330
|
}
|
|
327
331
|
if (present === 0 && shaderUvSetCount === 0) return [];
|
|
328
332
|
entries.sort((a, b) => a.shaderLocation - b.shaderLocation);
|
|
@@ -347,24 +351,7 @@ function deriveVertexBufferLayoutFromProjection(projection, opts) {
|
|
|
347
351
|
let arrayStride = projection.arrayStride;
|
|
348
352
|
const shaderUvSetCount = opts?.shaderUvSetCount ?? 0;
|
|
349
353
|
if (shaderUvSetCount > 0) {
|
|
350
|
-
|
|
351
|
-
(entry) => entry.key === "uv" || entry.key.startsWith("uv")
|
|
352
|
-
).length;
|
|
353
|
-
if (shaderUvSetCount > meshUvSetCount) {
|
|
354
|
-
const lastUv = entries.filter((entry) => entry.key === "uv" || entry.key.startsWith("uv")).at(-1);
|
|
355
|
-
const aliasOffset = lastUv?.offset ?? arrayStride;
|
|
356
|
-
for (let index = meshUvSetCount; index < shaderUvSetCount && index < UV_KEYS.length; index += 1) {
|
|
357
|
-
const key = UV_KEYS[index];
|
|
358
|
-
if (key === void 0) continue;
|
|
359
|
-
entries.push({
|
|
360
|
-
key,
|
|
361
|
-
shaderLocation: CANONICAL_KEYS.indexOf(key),
|
|
362
|
-
offset: aliasOffset,
|
|
363
|
-
format: ATTRIBUTE_FORMAT_MAP[key]
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
if (meshUvSetCount === 0) arrayStride += ATTRIBUTE_BYTE_STRIDE.uv;
|
|
367
|
-
}
|
|
354
|
+
arrayStride = emitAliasEntries(entries, shaderUvSetCount, arrayStride);
|
|
368
355
|
}
|
|
369
356
|
entries.sort((a, b) => a.shaderLocation - b.shaderLocation);
|
|
370
357
|
return projection.attributes.length === 0 ? [] : [
|
|
@@ -378,6 +365,15 @@ function deriveVertexBufferLayoutFromProjection(projection, opts) {
|
|
|
378
365
|
}
|
|
379
366
|
];
|
|
380
367
|
}
|
|
368
|
+
function deriveVertexCount(vertices, projection) {
|
|
369
|
+
const stride = projection.arrayStride;
|
|
370
|
+
const byteLength = vertices.byteLength;
|
|
371
|
+
if (!Number.isSafeInteger(stride) || stride <= 0 || !Number.isSafeInteger(byteLength) || byteLength < 0 || byteLength % stride !== 0) {
|
|
372
|
+
return void 0;
|
|
373
|
+
}
|
|
374
|
+
const count = byteLength / stride;
|
|
375
|
+
return Number.isSafeInteger(count) ? count : void 0;
|
|
376
|
+
}
|
|
381
377
|
function bytesForFormat(format) {
|
|
382
378
|
if (format === "uint16x4" || format === "float32x2") return 8;
|
|
383
379
|
if (format === "float32x3") return 12;
|
|
@@ -4286,6 +4282,6 @@ function createTorusGeometry(radius, tube, radialSegments = 8, tubularSegments =
|
|
|
4286
4282
|
return meshFromInterleaved(vertices, indices);
|
|
4287
4283
|
}
|
|
4288
4284
|
|
|
4289
|
-
export { PROCEDURAL_FLOATS_PER_VERTEX, VertexAttributePackError, buildMeshAttributeMapForUvSets, compute2dBounds, computeTangentVec4, create2dGeometry, create2dRingGeometry, createBoxGeometry, createCapsuleGeometry, createConeGeometry, createCylinderGeometry, createEdgesGeometry, createMeshBuilder, createPlaneGeometry, createPrimitiveMesh, createProceduralMesh, createSphereGeometry, createTeapotGeometry, createTorusGeometry, createWireframeGeometry, decodeMeshBinary, deriveVertexBufferLayout, deriveVertexBufferLayoutFromProjection, deriveVertexLayoutProjection, deriveVertexLayoutProjectionFromMask, meshAssetContribution, meshAssetDecoder, meshAssetKind, meshFromInterleaved, normalizeMeshPayload, packInterleavedVertexAttributes };
|
|
4285
|
+
export { DEFAULT_VERTEX_ATTRIBUTE_MAP, PROCEDURAL_FLOATS_PER_VERTEX, SKIN_VERTEX_ATTRIBUTE_MAP, VertexAttributePackError, buildMeshAttributeMapForUvSets, compute2dBounds, computeTangentVec4, create2dGeometry, create2dRingGeometry, createBoxGeometry, createCapsuleGeometry, createConeGeometry, createCylinderGeometry, createEdgesGeometry, createMeshBuilder, createPlaneGeometry, createPrimitiveMesh, createProceduralMesh, createSphereGeometry, createTeapotGeometry, createTorusGeometry, createWireframeGeometry, decodeMeshBinary, deriveVertexBufferLayout, deriveVertexBufferLayoutFromProjection, deriveVertexCount, deriveVertexLayoutProjection, deriveVertexLayoutProjectionFromMask, meshAssetContribution, meshAssetDecoder, meshAssetKind, meshFromInterleaved, normalizeMeshPayload, packInterleavedVertexAttributes };
|
|
4290
4286
|
//# sourceMappingURL=index.mjs.map
|
|
4291
4287
|
//# sourceMappingURL=index.mjs.map
|