#!/usr/bin/env python3 """CC0 regression fixture: a skinned glTF body with an external .bin, three external PNG textures and three materials (factor only, texture, texture x factor), one primitive per material. Three quads side by side facing +Z. KHR_materials_specular: Green has specularFactor 0.5; YellowTimesFactor has specularFactor 1 with an RGBA specular texture whose alpha is 102 (0.4). Writes tests/fixtures/bodies/multi-material/{body.gltf,body.bin,textures/*.png}. """ import json, os, struct, zlib OUT = os.path.join(os.path.dirname(__file__), "..", "tests", "fixtures", "bodies", "multi-material") def png(path, w, h, pixel, color_type=2): raw = b"".join(b"\0" + b"".join(bytes(pixel(x, y)) for x in range(w)) for y in range(h)) def chunk(t, d): return struct.pack(">I", len(d)) + t + d + struct.pack(">I", zlib.crc32(t + d) & 0xFFFFFFFF) data = b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, color_type, 0, 0, 0)) data += chunk(b"IDAT", zlib.compress(raw)) + chunk(b"IEND", b"") with open(path, "wb") as f: f.write(data) def main(): os.makedirs(os.path.join(OUT, "textures"), exist_ok=True) # Texture A: solid green. Texture B: solid yellow (multiplied by factor 0.5,1,1). png(os.path.join(OUT, "textures", "green_baseColor.png"), 4, 4, lambda x, y: (0, 200, 0)) png(os.path.join(OUT, "textures", "yellow_baseColor.png"), 4, 4, lambda x, y: (200, 200, 0)) png(os.path.join(OUT, "textures", "specular.png"), 4, 4, lambda x, y: (0, 0, 0, 102), color_type=6) blob = b"" views, accessors, prims = [], [], [] def add(data, target=None): nonlocal blob while len(blob) % 4: blob += b"\0" view = {"buffer": 0, "byteOffset": len(blob), "byteLength": len(data)} if target: view["target"] = target views.append(view) blob += data return len(views) - 1 def accessor(view, ctype, count, typ, **kw): accessors.append({"bufferView": view, "componentType": ctype, "count": count, "type": typ, **kw}) return len(accessors) - 1 for i, x0 in enumerate([-0.75, -0.25, 0.25]): x1 = x0 + 0.5 pos = [(x0, 0.0, 0.0), (x1, 0.0, 0.0), (x1, 1.6, 0.0), (x0, 1.6, 0.0)] p = accessor(add(b"".join(struct.pack("<3f", *v) for v in pos), 34962), 5126, 4, "VEC3", min=[x0, 0.0, 0.0], max=[x1, 1.6, 0.0]) n = accessor(add(struct.pack("<3f", 0, 0, 1) * 4, 34962), 5126, 4, "VEC3") uv = accessor(add(b"".join(struct.pack("<2f", *t) for t in [(0, 1), (1, 1), (1, 0), (0, 0)]), 34962), 5126, 4, "VEC2") j = accessor(add(struct.pack("<4H", 0, 0, 0, 0) * 4, 34962), 5123, 4, "VEC4") w = accessor(add(struct.pack("<4f", 1, 0, 0, 0) * 4, 34962), 5126, 4, "VEC4") idx = accessor(add(struct.pack("<6H", 0, 1, 2, 0, 2, 3), 34963), 5123, 6, "SCALAR") prims.append({"attributes": {"POSITION": p, "NORMAL": n, "TEXCOORD_0": uv, "JOINTS_0": j, "WEIGHTS_0": w}, "indices": idx, "material": i}) 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") times = accessor(add(struct.pack("<2f", 0.0, 1.0)), 5126, 2, "SCALAR", min=[0.0], max=[1.0]) rots = accessor(add(struct.pack("<8f", 0, 0, 0, 1, 0, 0, 0, 1)), 5126, 2, "VEC4") gltf = { "extensionsUsed": ["KHR_materials_specular"], "asset": {"version": "2.0", "generator": "paperdoll generate_multi_material_body.py (CC0)"}, "extras": {"forward": "+Z"}, "scene": 0, "scenes": [{"nodes": [0, 1]}], "nodes": [{"name": "Root"}, {"name": "Body", "mesh": 0, "skin": 0}], "skins": [{"joints": [0], "inverseBindMatrices": ibm}], "meshes": [{"name": "Body", "primitives": prims}], "materials": [ {"name": "FactorOnly", "pbrMetallicRoughness": {"baseColorFactor": [0.2, 0.4, 0.9, 1.0]}}, {"name": "Green", "pbrMetallicRoughness": {"baseColorTexture": {"index": 0}}, "extensions": {"KHR_materials_specular": {"specularFactor": 0.5}}}, {"name": "YellowTimesFactor", "pbrMetallicRoughness": {"baseColorTexture": {"index": 1}, "baseColorFactor": [0.5, 1.0, 1.0, 1.0]}, "extensions": {"KHR_materials_specular": {"specularTexture": {"index": 2}}}}, ], "textures": [{"sampler": 0, "source": 0}, {"sampler": 0, "source": 1}, {"sampler": 0, "source": 2}], "samplers": [{"magFilter": 9728, "minFilter": 9728}], "images": [{"uri": "textures/green_baseColor.png"}, {"uri": "textures/yellow_baseColor.png"}, {"uri": "textures/specular.png"}], "animations": [{"name": "idle", "samplers": [{"input": times, "output": rots}], "channels": [{"sampler": 0, "target": {"node": 0, "path": "rotation"}}]}], "buffers": [{"uri": "body.bin", "byteLength": len(blob)}], "bufferViews": views, "accessors": accessors, } with open(os.path.join(OUT, "body.bin"), "wb") as f: f.write(blob) with open(os.path.join(OUT, "body.gltf"), "w") as f: json.dump(gltf, f, indent=1) if __name__ == "__main__": main()