Files
resolved/scripts/make-example-model.py
T

68 lines
3.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""Writes examples/models/boxwing.gltf: a simple box-wing satellite.
A 1 x 1 x 1.5 m bus wrapped in gold-colored insulation, a white radiator panel on
its -Z face, and two 3 x 1 m dark blue solar arrays on short booms along +Y and -Y.
The arrays and radiator are zero-thickness quads, which the renderer shades from
whichever side faces the camera. Body frame: +X forward, +Z up through the bus.
No normals are stored, so the renderer gives every triangle a flat normal from its
winding; faces are wound counterclockwise seen from outside.
"""
import base64
import json
import pathlib
import struct
def box(cx, cy, cz, sx, sy, sz):
"""Triangles of an axis-aligned box centered at (cx, cy, cz) with sides s."""
hx, hy, hz = sx / 2, sy / 2, sz / 2
corner = lambda i: (cx + (hx if i & 1 else -hx), cy + (hy if i & 2 else -hy), cz + (hz if i & 4 else -hz))
faces = [[0, 2, 3, 1], [4, 5, 7, 6], [0, 1, 5, 4], [2, 6, 7, 3], [0, 4, 6, 2], [1, 3, 7, 5]]
tris = []
for f in faces:
tris += [(corner(f[0]), corner(f[1]), corner(f[2])), (corner(f[0]), corner(f[2]), corner(f[3]))]
return tris
def quad(a, b, c, d):
return [(a, b, c), (a, c, d)]
parts = {
"bus": ((0.80, 0.60, 0.20), box(0, 0, 0, 1.0, 1.0, 1.5)),
"radiator": ((0.85, 0.85, 0.85), quad((-0.45, -0.45, -0.76), (-0.45, 0.45, -0.76), (0.45, 0.45, -0.76), (0.45, -0.45, -0.76))),
"boom +y": ((0.30, 0.30, 0.30), box(0, 0.75, 0, 0.05, 0.5, 0.05)),
"boom -y": ((0.30, 0.30, 0.30), box(0, -0.75, 0, 0.05, 0.5, 0.05)),
# Arrays face +X.
"array +y": ((0.05, 0.08, 0.20), quad((0, 1.0, -0.5), (0, 4.0, -0.5), (0, 4.0, 0.5), (0, 1.0, 0.5))),
"array -y": ((0.05, 0.08, 0.20), quad((0, -4.0, -0.5), (0, -1.0, -0.5), (0, -1.0, 0.5), (0, -4.0, 0.5))),
}
buffer = bytearray()
gltf = {"asset": {"version": "2.0", "generator": "resolved scripts/make-example-model.py"},
"scene": 0, "scenes": [{"nodes": []}], "nodes": [], "meshes": [], "materials": [],
"accessors": [], "bufferViews": [], "buffers": []}
for i, (name, (color, tris)) in enumerate(parts.items()):
points = [p for t in tris for p in t]
offset = len(buffer)
for p in points:
buffer += struct.pack("<3f", *p)
gltf["bufferViews"].append({"buffer": 0, "byteOffset": offset, "byteLength": 12 * len(points)})
gltf["accessors"].append({"bufferView": i, "componentType": 5126, "count": len(points), "type": "VEC3",
"min": [min(p[k] for p in points) for k in range(3)],
"max": [max(p[k] for p in points) for k in range(3)]})
gltf["materials"].append({"name": name, "pbrMetallicRoughness": {"baseColorFactor": [*color, 1.0]}})
gltf["meshes"].append({"name": name, "primitives": [{"attributes": {"POSITION": i}, "material": i}]})
gltf["nodes"].append({"name": name, "mesh": i})
gltf["scenes"][0]["nodes"].append(i)
gltf["buffers"].append({"byteLength": len(buffer),
"uri": "data:application/octet-stream;base64," + base64.b64encode(buffer).decode()})
out = pathlib.Path(__file__).resolve().parent.parent / "examples" / "models" / "boxwing.gltf"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(gltf, indent=1) + "\n")
print(f"wrote {out}")