BlueGit

golden-age paperdoll-renderer

Files

paperdoll-renderer / scripts / generate_multi_material_body.py

generate_multi_material_body.py

generate_multi_material_body.py 4.6 KiB Download Raw
1#!/usr/bin/env python3
2"""CC0 regression fixture: a skinned glTF body with an external .bin, two
3external PNG textures and three materials (factor only, texture, texture x
4factor), one primitive per material. Three quads side by side facing +Z.
5
6Writes tests/fixtures/bodies/multi-material/{body.gltf,body.bin,textures/*.png}.
7"""
8import json, os, struct, zlib
9
10OUT = os.path.join(os.path.dirname(__file__), "..", "tests", "fixtures", "bodies", "multi-material")
11
12
13def png(path, w, h, pixel):
14 raw = b"".join(b"\0" + b"".join(bytes(pixel(x, y)) for x in range(w)) for y in range(h))
15 def chunk(t, d):
16 return struct.pack(">I", len(d)) + t + d + struct.pack(">I", zlib.crc32(t + d) & 0xFFFFFFFF)
17 data = b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0))
18 data += chunk(b"IDAT", zlib.compress(raw)) + chunk(b"IEND", b"")
19 with open(path, "wb") as f:
20 f.write(data)
21
22
23def main():
24 os.makedirs(os.path.join(OUT, "textures"), exist_ok=True)
25 # Texture A: solid green. Texture B: solid yellow (multiplied by factor 0.5,1,1).
26 png(os.path.join(OUT, "textures", "green_baseColor.png"), 4, 4, lambda x, y: (0, 200, 0))
27 png(os.path.join(OUT, "textures", "yellow_baseColor.png"), 4, 4, lambda x, y: (200, 200, 0))
28 blob = b""
29 views, accessors, prims = [], [], []
30
31 def add(data, target=None):
32 nonlocal blob
33 while len(blob) % 4:
34 blob += b"\0"
35 view = {"buffer": 0, "byteOffset": len(blob), "byteLength": len(data)}
36 if target:
37 view["target"] = target
38 views.append(view)
39 blob += data
40 return len(views) - 1
41
42 def accessor(view, ctype, count, typ, **kw):
43 accessors.append({"bufferView": view, "componentType": ctype, "count": count, "type": typ, **kw})
44 return len(accessors) - 1
45
46 for i, x0 in enumerate([-0.75, -0.25, 0.25]):
47 x1 = x0 + 0.5
48 pos = [(x0, 0.0, 0.0), (x1, 0.0, 0.0), (x1, 1.6, 0.0), (x0, 1.6, 0.0)]
49 p = accessor(add(b"".join(struct.pack("<3f", *v) for v in pos), 34962), 5126, 4, "VEC3",
50 min=[x0, 0.0, 0.0], max=[x1, 1.6, 0.0])
51 n = accessor(add(struct.pack("<3f", 0, 0, 1) * 4, 34962), 5126, 4, "VEC3")
52 uv = accessor(add(b"".join(struct.pack("<2f", *t) for t in [(0, 1), (1, 1), (1, 0), (0, 0)]), 34962), 5126, 4, "VEC2")
53 j = accessor(add(struct.pack("<4H", 0, 0, 0, 0) * 4, 34962), 5123, 4, "VEC4")
54 w = accessor(add(struct.pack("<4f", 1, 0, 0, 0) * 4, 34962), 5126, 4, "VEC4")
55 idx = accessor(add(struct.pack("<6H", 0, 1, 2, 0, 2, 3), 34963), 5123, 6, "SCALAR")
56 prims.append({"attributes": {"POSITION": p, "NORMAL": n, "TEXCOORD_0": uv, "JOINTS_0": j, "WEIGHTS_0": w},
57 "indices": idx, "material": i})
58 ibm = accessor(add(struct.pack("<16f", 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)), 5126, 1, "MAT4")
59 times = accessor(add(struct.pack("<2f", 0.0, 1.0)), 5126, 2, "SCALAR", min=[0.0], max=[1.0])
60 rots = accessor(add(struct.pack("<8f", 0, 0, 0, 1, 0, 0, 0, 1)), 5126, 2, "VEC4")
61 gltf = {
62 "asset": {"version": "2.0", "generator": "paperdoll generate_multi_material_body.py (CC0)"},
63 "extras": {"forward": "+Z"},
64 "scene": 0,
65 "scenes": [{"nodes": [0, 1]}],
66 "nodes": [{"name": "Root"}, {"name": "Body", "mesh": 0, "skin": 0}],
67 "skins": [{"joints": [0], "inverseBindMatrices": ibm}],
68 "meshes": [{"name": "Body", "primitives": prims}],
69 "materials": [
70 {"name": "FactorOnly", "pbrMetallicRoughness": {"baseColorFactor": [0.2, 0.4, 0.9, 1.0]}},
71 {"name": "Green", "pbrMetallicRoughness": {"baseColorTexture": {"index": 0}}},
72 {"name": "YellowTimesFactor", "pbrMetallicRoughness": {"baseColorTexture": {"index": 1},
73 "baseColorFactor": [0.5, 1.0, 1.0, 1.0]}},
74 ],
75 "textures": [{"sampler": 0, "source": 0}, {"sampler": 0, "source": 1}],
76 "samplers": [{"magFilter": 9728, "minFilter": 9728}],
77 "images": [{"uri": "textures/green_baseColor.png"}, {"uri": "textures/yellow_baseColor.png"}],
78 "animations": [{"name": "idle", "samplers": [{"input": times, "output": rots}],
79 "channels": [{"sampler": 0, "target": {"node": 0, "path": "rotation"}}]}],
80 "buffers": [{"uri": "body.bin", "byteLength": len(blob)}],
81 "bufferViews": views,
82 "accessors": accessors,
83 }
84 with open(os.path.join(OUT, "body.bin"), "wb") as f:
85 f.write(blob)
86 with open(os.path.join(OUT, "body.gltf"), "w") as f:
87 json.dump(gltf, f, indent=1)
88
89
90if __name__ == "__main__":
91 main()