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.
#!/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()
