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/extract/hke.py
#!/usr/bin/env python3
"""Parse the Havok scene (.hke, "MAX_exported_world") from the original game.

Dev-only tooling. Writes ref-out/assets/havok.json with every static rigid
body: its collision mesh, placement and material values, all converted to
Director/W3D world units (the file stores metres; scale 0.0254 = inches).

    python3 tools/extract/hke.py [in.hke] [out.json]

Format notes (reverse-engineered, little-endian):
- Header: u32 magic 0x0edee442, u8 4, "MAX_exported_world\\0", then tagged
  fields; tag 0x0a1b3305 is the world scale (0.0254).
- Geometry records: [0x12abcdef 0x0ea30ac9] 0x019feea9 0x05053045
  name\\0 u32 nverts, nverts * f32[3], u32 nfaces, nfaces * u32[3].
- Subspace "default_subspace": tag 0x0c66aed9 gravity f32[3] (m/s^2).
- Rigid bodies: tag 0x03e37799 name\\0 (usually after 0x12abcdef 0x0834cb85 0x0f471a89),
  then tagged fields (u32 tag + value). Known tags are in BODY_TAGS.
"""
import json
import math
import os
import struct
import sys

ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
SRC = os.path.join(ROOT, "ref-out", "assets", "3d", "havok.hke")
DST = os.path.join(ROOT, "ref-out", "assets", "havok.json")

GEOM_TAG = bytes.fromhex("a9ee9f0145300505")
BODY_MARK = bytes.fromhex("9977e303")  # rigid-body name tag


def f32(b, i, n=1):
    v = struct.unpack_from(f"<{n}f", b, i)
    return v if n > 1 else v[0]


def cstr(b, i):
    e = b.index(b"\0", i)
    return b[i:e].decode("latin1"), e + 1


def parse_geometry(b):
    geoms = {}
    i = 0
    while True:
        j = b.find(GEOM_TAG, i)
        if j < 0:
            return geoms
        name, k = cstr(b, j + 8)
        nv = struct.unpack_from("<I", b, k)[0]
        verts = [list(f32(b, k + 4 + 12 * v, 3)) for v in range(nv)]
        k += 4 + 12 * nv
        nf = struct.unpack_from("<I", b, k)[0]
        faces = [list(struct.unpack_from("<3I", b, k + 4 + 12 * f)) for f in range(nf)]
        geoms[name] = dict(verts=verts, faces=faces)
        i = k + 4 + 12 * nf


def parse_bodies(b):
    bodies = []
    i = 0
    while True:
        j = b.find(BODY_MARK, i)
        if j < 0:
            return bodies
        name, k = cstr(b, j + 4)
        tag = lambda t: struct.unpack_from("<I", b, k + t)[0]
        if tag(0) != 0x08C76EF9:  # not a rigid body record
            i = j + 4
            continue
        # Fixed layout observed for every body in this file.
        body = dict(
            name=name,
            friction=f32(b, k + 4),
            restitution=f32(b, k + 12),
            third=f32(b, k + 20),
            rotation_angle=f32(b, k + 28),
            rotation_axis=list(f32(b, k + 32, 3)),
            position=list(f32(b, k + 48, 3)),
            linear_velocity=list(f32(b, k + 64, 3)),
            angular_velocity=list(f32(b, k + 80, 3)),
        )
        assert tag(0) == 0x08C76EF9 and tag(24) == 0x0486894E and tag(44) == 0x085FEC0E, name
        # Shape section: geometry name, local shape transform, convex flag.
        s = b.index(b"\x55\x8d\xfa\x07\x85\x54\x73\x01", k) + 8
        shape, s = cstr(b, s)
        rot = s + 8  # skip tag 0x00051683 + u32
        assert struct.unpack_from("<I", b, rot)[0] == 0x0486894E
        body["shape"] = shape
        body["shape_rotation_angle"] = f32(b, rot + 4)
        body["shape_rotation_axis"] = list(f32(b, rot + 8, 3))
        assert struct.unpack_from("<I", b, rot + 20)[0] == 0x085FEC0E
        body["shape_position"] = list(f32(b, rot + 24, 3))
        n = b.index(b"\xc9\xad\x41\x0a", rot) + 4
        _, n = cstr(b, n)
        assert struct.unpack_from("<I", b, n)[0] == 0x04843AA8
        body["convex"] = bool(b[n + 4])
        bodies.append(body)
        i = n


def axis_angle_matrix(angle, axis):
    x, y, z = axis
    ln = math.sqrt(x * x + y * y + z * z) or 1.0
    x, y, z = x / ln, y / ln, z / ln
    c, s, t = math.cos(angle), math.sin(angle), 1 - math.cos(angle)
    return [
        [t * x * x + c, t * x * y - s * z, t * x * z + s * y],
        [t * x * y + s * z, t * y * y + c, t * y * z - s * x],
        [t * x * z - s * y, t * y * z + s * x, t * z * z + c],
    ]


def apply(m, v):
    return [sum(m[r][c] * v[c] for c in range(3)) for r in range(3)]


def main():
    src = sys.argv[1] if len(sys.argv) > 1 else SRC
    dst = sys.argv[2] if len(sys.argv) > 2 else DST
    b = open(src, "rb").read()
    scale = f32(b, b.find(bytes.fromhex("05331b0a")) + 4)
    sub = b.find(b"default_subspace\0") + len("default_subspace\0")
    assert struct.unpack_from("<I", b, sub)[0] == 0x0C66AED9
    gravity = list(f32(b, sub + 4, 3))
    geoms = parse_geometry(b)
    out = dict(scale=scale, gravity_mps2=gravity, gravity_world=[g / scale for g in gravity], bodies=[])
    for body in parse_bodies(b):
        g = geoms[body["shape"]]
        rb = axis_angle_matrix(body["rotation_angle"], body["rotation_axis"])
        rs = axis_angle_matrix(body["shape_rotation_angle"], body["shape_rotation_axis"])
        world = []
        for v in g["verts"]:
            p = apply(rs, v)
            p = [p[a] + body["shape_position"][a] for a in range(3)]
            p = apply(rb, p)
            world.append([(p[a] + body["position"][a]) / scale for a in range(3)])
        body["world_verts"] = world
        body["faces"] = g["faces"]
        out["bodies"].append(body)
    json.dump(out, open(dst, "w"))
    print(f"scale {scale}, gravity {gravity}, {len(out['bodies'])} bodies -> {dst}")
    for body in out["bodies"][:6]:
        ws = body["world_verts"]
        lo = [round(min(v[a] for v in ws)) for a in range(3)]
        hi = [round(max(v[a] for v in ws)) for a in range(3)]
        print(f"  {body['name']:12} convex={body['convex']!s:5} f={body['friction']:.2f} r={body['restitution']:.2f} {lo}..{hi}")


if __name__ == "__main__":
    main()