SOURCE / PINNED RELEASE
Made of little things.
Supersonic RC Revive
- Release
- 1ba42f1ca1d6…
- Author-recorded commit
- baecad10b1cd…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-143146b0d6f
Archive hash verified: 90d22b206672eba4…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Level derivation from Blender-authored GLBs (src/game/level/editorLevel.ts),
// on synthetic files shaped like Blender's glTF exporter output: Y-up metres,
// custom properties as extras, ".001" names, empties, hidden collision objects.
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { after, before, test } from 'node:test';
import { fileURLToPath } from 'node:url';
import { createServer } from 'vite';
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
let server;
let lvl;
let glbModule;
before(async () => {
server = await createServer({ root, logLevel: 'error', server: { middlewareMode: true }, appType: 'custom', optimizeDeps: { noDiscovery: true, entries: [] } });
lvl = await server.ssrLoadModule('/src/game/level/editorLevel.ts');
glbModule = await server.ssrLoadModule('/src/game/render/glb.ts');
});
after(() => server?.close());
/** A GLB from a glTF document whose accessors are given as typed arrays (`data`). */
function glb(doc, arrays = []) {
const chunks = [];
let offset = 0;
doc.bufferViews = [];
doc.accessors = arrays.map(({ data, type, normalized }, i) => {
const bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
chunks.push(bytes, new Uint8Array((4 - (bytes.length % 4)) % 4));
doc.bufferViews.push({ buffer: 0, byteOffset: offset, byteLength: bytes.length });
offset += bytes.length + ((4 - (bytes.length % 4)) % 4);
const componentType = data instanceof Float32Array ? 5126 : data instanceof Uint32Array ? 5125 : data instanceof Uint16Array ? 5123 : 5121;
const width = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4 }[type];
return { bufferView: i, componentType, count: data.length / width, type, ...(normalized ? { normalized: true } : {}) };
});
doc.asset = { version: '2.0', generator: 'test' };
doc.buffers = [{ byteLength: offset }];
const json = new TextEncoder().encode(JSON.stringify(doc));
const jsonPad = (4 - (json.length % 4)) % 4;
const total = 12 + 8 + json.length + jsonPad + 8 + offset;
const out = new Uint8Array(total);
const view = new DataView(out.buffer);
view.setUint32(0, 0x46546c67, true);
view.setUint32(4, 2, true);
view.setUint32(8, total, true);
view.setUint32(12, json.length + jsonPad, true);
view.setUint32(16, 0x4e4f534a, true);
out.set(json, 20);
out.fill(0x20, 20 + json.length, 20 + json.length + jsonPad);
let at = 20 + json.length + jsonPad;
view.setUint32(at, offset, true);
view.setUint32(at + 4, 0x004e4942, true);
at += 8;
for (const c of chunks) {
out.set(c, at);
at += c.length;
}
return out.buffer;
}
const INCH = 1 / 0.0254;
const close = (a, b, eps = 1e-6) => assert.ok(Math.abs(a - b) < eps, `${a} ≈ ${b}`);
// A 1 m quad in Blender's XY plane (glTF XZ), split per face as Blender does
// for flat shading: two triangles, six corners, four distinct positions.
const quadPositions = new Float32Array([0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0, -1]);
const quadIndices = new Uint16Array([0, 1, 2, 3, 4, 5]);
function blenderLevel() {
return glb(
{
scene: 0,
scenes: [{ nodes: [0, 1, 2, 3, 4, 5, 6, 7], extras: { ssrc_level: 1, ssrc_title: 'Test' } }],
meshes: [
{ name: 'Quad', primitives: [{ attributes: { POSITION: 0 }, indices: 1 }] },
{ name: 'Baked', primitives: [{ attributes: { POSITION: 0, COLOR_0: 2 }, indices: 1 }] },
],
nodes: [
{ name: 'Floor', mesh: 0, translation: [10, 0, 0] },
{ name: 'COL_Ramp.001', mesh: 0, extras: { ssrc_collide: 'convex', ssrc_friction: 0.7 } },
{ name: 'checkpoint_1.001', translation: [1, 3, -2] },
{ name: 'Deco', mesh: 0, extras: { ssrc_collide: 'none' } },
{ name: 'Mirror', mesh: 0, scale: [-1, 1, 1] },
{ name: 'Hull', mesh: 0, extras: { ssrc_role: 'car_hull' } },
{ name: 'Group', translation: [0, 0, -5], children: [8] },
{ name: 'Glow', mesh: 1, extras: { ssrc_order: -1 } },
{ name: 'prizes_2', translation: [0, 1, 0] },
],
},
[
{ data: quadPositions, type: 'VEC3' },
{ data: quadIndices, type: 'SCALAR' },
{ data: new Uint16Array(6 * 4).fill(32768), type: 'VEC4', normalized: true },
],
);
}
test('maps glTF Y-up metres to game Z-up inches', () => {
const m = lvl.GLTF_TO_GAME.elements;
const p = { x: 1, y: 3, z: -2 };
// (x, y, z) glTF → (x, −z, y) / 0.0254 game.
close(m[0] * p.x + m[4] * p.y + m[8] * p.z, 1 * INCH);
close(m[1] * p.x + m[5] * p.y + m[9] * p.z, 2 * INCH);
close(m[2] * p.x + m[6] * p.y + m[10] * p.z, 3 * INCH);
});
test('derives bodies, ray mesh, markers and hull from a Blender level', () => {
const { level, settings, warnings } = lvl.deriveLevel(blenderLevel());
assert.equal(settings.version, 1);
assert.equal(settings.title, 'Test');
assert.deepEqual(warnings, []);
const { bodies } = level.collision;
// Glow sorts first (ssrc_order -1); Deco does not collide; Hull is the car.
assert.deepEqual(bodies.map((b) => b.name), ['Glow', 'Floor', 'Ramp', 'Mirror']);
const floor = bodies[1];
assert.equal(floor.verts.length, 4 * 3, 'flat-shading splits are welded');
assert.equal(floor.faces.length, 6);
assert.equal(floor.convex, false);
assert.equal(floor.friction, 0.3);
// Floor at glTF x = 10 m: game x from 10 m to 11 m, rounded to 1e-3 in.
assert.deepEqual(floor.verts.slice(0, 3), [393.701, 0, 0]);
const ramp = bodies[2];
assert.equal(ramp.convex, true);
assert.equal(ramp.friction, 0.7);
assert.deepEqual(level.collision.gravity, lvl.DEFAULT_GRAVITY);
assert.equal(level.collision.scale, lvl.DEFAULT_HAVOK_SCALE);
// Ray models: rendered objects (and Deco, rendered but not solid); not the hidden collision twin or the hull.
assert.deepEqual(level.markers.raymesh_models, ['Glow', 'Floor', 'Deco', 'Mirror']);
assert.equal(level.rayMesh.owner.length, 8);
assert.equal(level.rayMesh.positions.length, 8 * 9);
const groups = Object.fromEntries(level.markers.level.map((g) => [g.name, g]));
assert.deepEqual(Object.keys(groups).sort(), ['Group', 'checkpoint_1', 'prizes_2']);
// Marker positions are rounded to 1e-3 in.
const cp = groups.checkpoint_1.matrix;
assert.deepEqual([cp[12], cp[13], cp[14]], [39.37, 78.74, 118.11]);
assert.deepEqual([cp[0], cp[5], cp[10]].map((v) => Math.round(v * 1e9) / 1e9), [1, 1, 1], 'markers keep Blender orientation');
// Child of Group (glTF z −5 → game y +5 m): glTF (0, 1, −5) → game (0, 5, 1) m.
const prize = groups.prizes_2.matrix;
close(prize[13], 5 * INCH, 1e-3);
close(prize[14], 1 * INCH, 1e-3);
assert.equal(level.markers.car_hull.name, 'Hull');
assert.equal(level.markers.car_hull.positions.length, 4);
});
test('keeps triangle facing under a mirroring transform', () => {
const { level } = lvl.deriveLevel(blenderLevel());
const normalZ = (body) => {
const [a, b, c] = body.faces.slice(0, 3).map((i) => body.verts.slice(i * 3, i * 3 + 3));
const u = b.map((v, k) => v - a[k]);
const w = c.map((v, k) => v - a[k]);
return u[0] * w[1] - u[1] * w[0];
};
const [, floor, , mirror] = level.collision.bodies;
// The quad faces Blender +Z (game +Z) and still does when mirrored in x.
assert.ok(normalZ(floor) > 0);
assert.ok(normalZ(mirror) > 0);
});
test('reads object flags and their defaults', () => {
const visual = lvl.objectFlags('Box.002', {});
assert.deepEqual([visual.name, visual.role, visual.render, visual.collide, visual.ray], ['Box', 'visual', true, 'mesh', true]);
const col = lvl.objectFlags('COL_Wall', {});
assert.deepEqual([col.name, col.render, col.collide, col.ray], ['Wall', false, 'mesh', false]);
const named = lvl.objectFlags('COL_Wall.003', { ssrc_name: 'Mddle Ramp', ssrc_collide: 'convex' });
assert.deepEqual([named.name, named.collide], ['Mddle Ramp', 'convex']);
const hidden = lvl.objectFlags('Lamp', { ssrc_render: false });
assert.equal(hidden.ray, false, 'unrendered visuals stay out of the ray mesh');
const ignored = lvl.objectFlags('Note', { ssrc_role: 'ignore', ssrc_render: true });
assert.deepEqual([ignored.render, ignored.collide, ignored.ray], [false, 'none', false]);
assert.equal(lvl.objectFlags('Rug', {}).baked, false, 'a colour attribute is not baked light unless marked');
assert.equal(lvl.objectFlags('Rug', { ssrc_baked: true }).baked, true);
assert.equal(lvl.objectFlags('Rug', {}, 'vertex').baked, true);
});
test('derives a plain GLB with a warning', () => {
const plain = glb(
{ scene: 0, scenes: [{ nodes: [0, 1] }], meshes: [{ primitives: [{ attributes: { POSITION: 0 }, indices: 1 }] }], nodes: [{ name: 'Ground', mesh: 0 }, { name: 'ssrc_start' }] },
[{ data: quadPositions, type: 'VEC3' }, { data: quadIndices, type: 'SCALAR' }],
);
const { level, settings, warnings } = lvl.deriveLevel(plain, { carHull: { name: 'pack', node_matrix: [], positions: [[0, 0, 0]] } });
assert.equal(settings, null);
assert.equal(warnings.length, 1);
assert.equal(level.collision.bodies.length, 1);
assert.equal(level.markers.level[0].name, 'ssrc_start');
assert.equal(level.markers.car_hull.name, 'pack', 'the pack hull is the fallback');
assert.equal(lvl.isEditorLevel(plain), false);
assert.equal(lvl.isEditorLevel(blenderLevel()), true);
});
test('falls back to the built-in car hull', () => {
const { level } = lvl.deriveLevel(
glb({ scene: 0, scenes: [{ nodes: [0], extras: { ssrc_level: 1 } }], meshes: [{ primitives: [{ attributes: { POSITION: 0 }, indices: 1 }] }], nodes: [{ name: 'Ground', mesh: 0 }] }, [
{ data: quadPositions, type: 'VEC3' },
{ data: quadIndices, type: 'SCALAR' },
]),
);
assert.deepEqual(level.markers.car_hull, lvl.DEFAULT_CAR_HULL);
});
test('reads level settings from a node when the scene has none', () => {
const doc = { scenes: [{ nodes: [0] }], nodes: [{ name: 'Settings', extras: { ssrc_level: 1, ssrc_gravity: [0, 0, -100], ssrc_lighting: 'vertex' } }] };
const settings = lvl.levelSettings(doc);
assert.deepEqual(settings.gravity, [0, 0, -100]);
assert.equal(settings.lighting, 'vertex');
});
test('refuses compressed GLBs it cannot read', () => {
const draco = glb({ extensionsRequired: ['KHR_draco_mesh_compression'], scenes: [{ nodes: [] }], nodes: [] });
assert.throws(() => glbModule.parseGlb(draco), /KHR_draco_mesh_compression/);
});
test('reads interleaved (strided) accessors', () => {
const buffer = glb({ scenes: [{ nodes: [] }] }, [{ data: new Float32Array([1, 2, 3, 9, 4, 5, 6, 9]), type: 'VEC4' }]);
const parsed = glbModule.parseGlb(buffer);
parsed.doc.bufferViews[0].byteStride = 16;
parsed.doc.accessors[0] = { bufferView: 0, componentType: 5126, count: 2, type: 'VEC3' };
assert.deepEqual([...parsed.accessor(0).array], [1, 2, 3, 4, 5, 6]);
});
// The original level converted to Blender and exported (pnpm level:original), against the
// extracted legacy files. Local only: skipped without ref-out/.
const originalGlb = resolve(root, 'ref-out/editor/level.glb');
const originalDir = resolve(root, 'ref-out/game');
test('the converted original level matches its legacy files', { skip: !existsSync(originalGlb) && 'no ref-out/editor/level.glb (pnpm level:original)' }, async () => {
const { parseRayMesh } = await server.ssrLoadModule('/src/game/level/data.ts');
const { level } = lvl.deriveLevel(new Uint8Array(readFileSync(originalGlb)).buffer);
const collision = JSON.parse(readFileSync(resolve(originalDir, 'collision.json'), 'utf8'));
const markers = JSON.parse(readFileSync(resolve(originalDir, 'markers.json'), 'utf8'));
const ray = parseRayMesh(new Uint8Array(readFileSync(resolve(originalDir, 'raymesh.bin'))).buffer);
// Havok bodies: bit-identical, in order (the handling depends on them).
assert.deepEqual(level.collision.bodies, collision.bodies);
assert.deepEqual(level.collision.gravity, collision.gravity);
assert.equal(level.collision.scale, collision.scale);
assert.deepEqual(level.markers.car_hull, markers.car_hull);
// Ray mesh: every legacy triangle has a derived one with the same winding, within 1e-2 in.
const byModel = (r, names) => {
const m = new Map();
for (let t = 0; t < r.owner.length; t++) {
const tri = [0, 1, 2].map((k) => [...r.positions.subarray(r.indices[t * 3 + k] * 3, r.indices[t * 3 + k] * 3 + 3)]);
const name = names[r.owner[t]];
m.set(name, [...(m.get(name) ?? []), tri]);
}
return m;
};
const derived = byModel(level.rayMesh, level.markers.raymesh_models);
const legacy = byModel(ray, markers.raymesh_models);
assert.deepEqual([...derived.keys()].sort(), [...legacy.keys()].sort());
const near = (a, b) => a.every((p, k) => p.every((x, c) => Math.abs(x - b[k][c]) < 1e-2));
for (const [name, tris] of legacy) {
const mine = derived.get(name);
assert.equal(mine.length, tris.length, name);
for (const t of tris) {
const turns = [t, [t[1], t[2], t[0]], [t[2], t[0], t[1]]];
assert.ok(mine.some((m) => turns.some((r) => near(r, m))), `${name}: a ray triangle has no match`);
}
}
// Marker groups within 1e-3 in.
const groups = new Map(level.markers.level.map((g) => [g.name.toLowerCase(), g.matrix]));
for (const g of markers.level.filter((n) => n.kind === 'group')) {
const m = groups.get(g.name.toLowerCase());
assert.ok(m, `group ${g.name}`);
g.matrix.forEach((v, k) => assert.ok(Math.abs(v - m[k]) < 1e-3, `${g.name}[${k}]`));
}
});
