Back to Supersonic RC Revive
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.

tools/assets/files.ts
// Minimal writers for the custom bundle: GLB (the subset src/game/render/glb.ts
// reads) and PNG.
import { deflateSync } from 'node:zlib';

export interface GlbPrimitiveIn {
  material: number;
  positions: Float32Array;
  normals: Float32Array;
  uvs?: Float32Array;
  /** RGBA bytes: baked light (rgb). */
  colors?: Uint8Array;
  indices: Uint32Array;
}

export interface GlbMaterialIn {
  name: string;
  color: [number, number, number, number];
  texture?: number;
}

export interface GlbNodeIn {
  name: string;
  translation?: [number, number, number];
  mesh?: { name: string; primitives: GlbPrimitiveIn[]; extras?: Record<string, unknown> };
  children?: GlbNodeIn[];
}

export function writeGlb(nodes: GlbNodeIn[], materials: GlbMaterialIn[], images: Uint8Array[] = []): Buffer {
  const chunks: Buffer[] = [];
  let offset = 0;
  const views: object[] = [];
  const accessors: object[] = [];
  const pushView = (bytes: Uint8Array, target?: number): number => {
    const pad = (4 - (offset % 4)) % 4;
    if (pad) {
      chunks.push(Buffer.alloc(pad));
      offset += pad;
    }
    chunks.push(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength));
    views.push({ buffer: 0, byteOffset: offset, byteLength: bytes.byteLength, ...(target ? { target } : {}) });
    offset += bytes.byteLength;
    return views.length - 1;
  };
  const pushAccessor = (data: Float32Array | Uint32Array | Uint8Array, type: string, width: number, target: number, normalized = false): number => {
    const view = pushView(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), target);
    const componentType = data instanceof Float32Array ? 5126 : data instanceof Uint32Array ? 5125 : 5121;
    const acc: Record<string, unknown> = { bufferView: view, componentType, count: data.length / width, type };
    if (normalized) acc.normalized = true;
    if (type === 'VEC3' && data instanceof Float32Array) {
      const min = [Infinity, Infinity, Infinity];
      const max = [-Infinity, -Infinity, -Infinity];
      for (let i = 0; i < data.length; i += 3) {
        for (let k = 0; k < 3; k++) {
          min[k] = Math.min(min[k], data[i + k]);
          max[k] = Math.max(max[k], data[i + k]);
        }
      }
      acc.min = min;
      acc.max = max;
    }
    accessors.push(acc);
    return accessors.length - 1;
  };

  const imageViews = images.map((png) => pushView(png));
  const meshes: object[] = [];
  const gltfNodes: object[] = [];
  const addNode = (n: GlbNodeIn): number => {
    const index = gltfNodes.length;
    const out: Record<string, unknown> = { name: n.name };
    gltfNodes.push(out);
    if (n.translation) out.translation = n.translation;
    if (n.mesh) {
      const primitives = n.mesh.primitives.map((p) => {
        const attributes: Record<string, number> = {
          POSITION: pushAccessor(p.positions, 'VEC3', 3, 34962),
          NORMAL: pushAccessor(p.normals, 'VEC3', 3, 34962),
        };
        if (p.uvs) attributes.TEXCOORD_0 = pushAccessor(p.uvs, 'VEC2', 2, 34962);
        if (p.colors) attributes.COLOR_0 = pushAccessor(p.colors, 'VEC4', 4, 34962, true);
        return { attributes, indices: pushAccessor(p.indices, 'SCALAR', 1, 34963), material: p.material };
      });
      meshes.push({ name: n.mesh.name, primitives, ...(n.mesh.extras ? { extras: n.mesh.extras } : {}) });
      out.mesh = meshes.length - 1;
    }
    if (n.children?.length) out.children = n.children.map(addNode);
    return index;
  };
  const roots = nodes.map(addNode);

  const doc: Record<string, unknown> = {
    asset: { version: '2.0', generator: 'ssrc tools/assets' },
    scene: 0,
    scenes: [{ nodes: roots }],
    nodes: gltfNodes,
    meshes,
    materials: materials.map((m) => ({
      name: m.name,
      pbrMetallicRoughness: {
        baseColorFactor: m.color,
        metallicFactor: 0,
        ...(m.texture !== undefined ? { baseColorTexture: { index: m.texture } } : {}),
      },
    })),
    accessors,
    bufferViews: views,
    buffers: [{ byteLength: 0 }],
  };
  if (images.length) {
    doc.images = imageViews.map((bufferView) => ({ bufferView, mimeType: 'image/png' }));
    doc.samplers = [{ wrapS: 10497, wrapT: 10497 }];
    doc.textures = images.map((_, source) => ({ source, sampler: 0 }));
  }
  const pad = (4 - (offset % 4)) % 4;
  if (pad) chunks.push(Buffer.alloc(pad));
  const bin = Buffer.concat(chunks);
  (doc.buffers as Array<{ byteLength: number }>)[0].byteLength = bin.length;
  let json = Buffer.from(JSON.stringify(doc));
  json = Buffer.concat([json, Buffer.alloc((4 - (json.length % 4)) % 4, 0x20)]);
  const header = Buffer.alloc(12);
  header.writeUInt32LE(0x46546c67, 0);
  header.writeUInt32LE(2, 4);
  header.writeUInt32LE(12 + 8 + json.length + 8 + bin.length, 8);
  const chunkHeader = (length: number, type: number) => {
    const b = Buffer.alloc(8);
    b.writeUInt32LE(length, 0);
    b.writeUInt32LE(type, 4);
    return b;
  };
  return Buffer.concat([header, chunkHeader(json.length, 0x4e4f534a), json, chunkHeader(bin.length, 0x004e4942), bin]);
}

const CRC_TABLE = Array.from({ length: 256 }, (_, n) => {
  let c = n;
  for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
  return c >>> 0;
});

function crc32(bytes: Uint8Array): number {
  let c = 0xffffffff;
  for (const b of bytes) c = CRC_TABLE[(c ^ b) & 0xff] ^ (c >>> 8);
  return (c ^ 0xffffffff) >>> 0;
}

/** RGBA8 pixels → PNG. */
export function writePng(width: number, height: number, rgba: Uint8Array): Buffer {
  const raw = Buffer.alloc((width * 4 + 1) * height);
  for (let y = 0; y < height; y++) {
    raw[y * (width * 4 + 1)] = 0;
    Buffer.from(rgba.buffer, rgba.byteOffset + y * width * 4, width * 4).copy(raw, y * (width * 4 + 1) + 1);
  }
  const chunk = (type: string, data: Buffer) => {
    const head = Buffer.alloc(8);
    head.writeUInt32BE(data.length, 0);
    head.write(type, 4, 'ascii');
    const crc = Buffer.alloc(4);
    crc.writeUInt32BE(crc32(Buffer.concat([head.subarray(4), data])), 0);
    return Buffer.concat([head, data, crc]);
  };
  const ihdr = Buffer.alloc(13);
  ihdr.writeUInt32BE(width, 0);
  ihdr.writeUInt32BE(height, 4);
  ihdr[8] = 8;
  ihdr[9] = 6;
  return Buffer.concat([
    Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
    chunk('IHDR', ihdr),
    chunk('IDAT', deflateSync(raw, { level: 9 })),
    chunk('IEND', Buffer.alloc(0)),
  ]);
}