handles: one interleaved vertices/indices arena

Geometry becomes a span in a single 80-byte interleaved `vertices` array
plus a span in a single `indices` array, allocated through the core's
span allocator instead of one arena array per mesh attribute. Draws are
always indexed with baseVertex; face-material overrides emit one draw per
run of same-material faces rather than one per face. Vertex data is
validated as a whole before any span is touched, same-geometry mutations
are serialised, and render signal 4 is held across a span change until
the graph reflecting it is published. Sibling arrays grow to the primary
array's capacity in one batch; reserve() takes vertex/index totals.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0b2dd-db32-712d-99ce-12ce4a86d979
Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
Amp
2026-09-18 07:00:36 +00:00
co-authored by heaust
parent 92638e6c9f
commit f324ff6bb2
5 changed files with 699 additions and 591 deletions
+14
View File
@@ -45,6 +45,20 @@ export async function importGltf(scene: Scene, url: string | URL) {
await scene.reserve({
nodes: result.primitives.length,
materials: result.materials.length,
vertices: result.primitives.reduce(
(total: number, primitive: any) => total + primitive.positions.length / 3,
0,
),
// Each geometry's index span is padded to whole rows of four.
indices: result.primitives.reduce(
(total: number, primitive: any) =>
total +
Math.ceil(
(primitive.indices?.length ?? primitive.positions.length / 3) / 4,
) *
4,
0,
),
});
let textures: Texture[] = [];
const imported = await scene.batchGraphUpdates(async () => {