#!/usr/bin/env python3 """Generates src/clay_spirv.h: the built-in shaders Frame Home / Frame Studio draw Clay (.opa) homes with. Clay homes (the pre-HSL Quest environments, e.g. cascadia.apk) carry no shader code: libshell builds its Clay programs natively. Like Quest Home Editor's V79 path, they are drawn with a small unlit shader that outputs the base texture's colour AND alpha (Clay textures carry their lighting baked in), times the vertex colour and the per-draw tint, plus a cut-out variant for AlphaTest materials. The interface uses the names Meta's own shaders use, so the renderer binds it with no special cases: vertex in loc0 vec3 position, loc1 vec2 uv, loc2 vec4 color, loc3 vec3 normal, loc4 vec2 lightmapUv set0 b0 uniform GlobalUniforms { mat4 clipFromWorld[2]; } (indexed by gl_ViewIndex: stereo-ready) push PushConstants { mat4 worldFromModel; vec4 color; vec4 uvTransformU; vec4 uvTransformV; } (vUV = (dot(uvTransformU.xyz, (uv, 1)), dot(uvTransformV.xyz, (uv, 1))): a Clay UV scroll / flipbook frame drawn without rewriting the vertices; the renderer pushes identity rows when a mesh has none) out loc0 vec2 vUV, loc1 vec4 vColor, loc2 vec3 vNormal, loc3 vec2 vUV1 fragment set2 b0 sampler linearWrapSampler, set2 b1 texture2D baseColorTex, set2 b2 texture2D lightmapTex out = texture(baseColorTex, vUV) * vColor * vec4(texture(lightmapTex, vUV1).rgb, 1) vColor = the per-draw tint (BaseColorFactor x lightmappower): libshell's model shader (libshell.so @0x1e7550, the MeshShellEnv path) applies no vertex colour. (libshell's ShellEnv: diffuse x lightmap x lightmappower, the power riding in the tint; a mesh with no lightmap gets white. Cut-out: discard when alpha < 0.5.) SPIR-V is hand-assembled (no shader compiler on this machine), every id and word count under our control. Usage: python3 gen_clay_shader.py """ import struct import sys OpExtension = 10; OpCapability = 17; OpMemoryModel = 14; OpEntryPoint = 15; OpExecutionMode = 16 OpName = 5; OpMemberName = 6; OpDecorate = 71; OpMemberDecorate = 72 OpTypeVoid = 19; OpTypeBool = 20; OpTypeInt = 21; OpTypeFloat = 22; OpTypeVector = 23; OpTypeMatrix = 24 OpTypeImage = 25; OpTypeSampler = 26; OpTypeSampledImage = 27; OpTypeArray = 28; OpTypeStruct = 30 OpTypePointer = 32; OpTypeFunction = 33; OpConstant = 43; OpFunction = 54; OpFunctionEnd = 56; OpVariable = 59 OpLoad = 61; OpStore = 62; OpAccessChain = 65; OpVectorShuffle = 79; OpCompositeConstruct = 80; OpCompositeExtract = 81 OpSampledImage = 86; OpImageSampleImplicitLod = 87; OpFMul = 133; OpMatrixTimesVector = 145; OpDot = 148 OpFAdd = 129; OpFSub = 131; OpFDiv = 136; OpVectorTimesScalar = 142; OpSelect = 169 OpFOrdLessThan = 184; OpSelectionMerge = 247; OpLabel = 248; OpBranch = 249; OpBranchConditional = 250 OpKill = 252; OpReturn = 253 DEC_BLOCK = 2; DEC_COLMAJOR = 5; DEC_ARRAYSTRIDE = 6; DEC_MATSTRIDE = 7; DEC_BUILTIN = 11; DEC_LOCATION = 30 DEC_BINDING = 33; DEC_DESCSET = 34; DEC_OFFSET = 35 BUILTIN_POSITION = 0; BUILTIN_VIEWINDEX = 4440 SC_UNIFORMCONSTANT = 0; SC_INPUT = 1; SC_UNIFORM = 2; SC_OUTPUT = 3; SC_FUNCTION = 7; SC_PUSHCONSTANT = 9 CAP_SHADER = 1; CAP_MULTIVIEW = 4439 def strw(s): b = s.encode() + b"\0" while len(b) % 4: b += b"\0" return list(struct.unpack("<%dI" % (len(b) // 4), b)) def f32(x): return struct.unpack("> 16 if wc == 0: raise SystemExit("zero word count at %d" % i) i += wc if i != len(words): raise SystemExit("instruction stream overruns the module") return words if __name__ == "__main__" and len(sys.argv) > 2 and sys.argv[1] == "--selection": out = sys.argv[2] body = ["#pragma once", "// GENERATED by tools/gen_clay_shader.py --selection: the selection-highlight overlay shaders. Do not edit by hand.", "// vert: in vec3 position @0, vec2 uv @1; set0 b0 uniform { mat4 mvp } frag: set0 b1 sampler2D tex -> rgb, a=1", "#include ", "#include ", "", emit("kSelectionVertSpirv", check(build_sel_vert())), "static const size_t kSelectionVertSize = sizeof(kSelectionVertSpirv);", "", emit("kSelectionFragSpirv", check(build_sel_frag())), "static const size_t kSelectionFragSize = sizeof(kSelectionFragSpirv);", ""] open(out, "w", newline="\n").write("\n".join(body)) print("wrote", out) elif __name__ == "__main__": out = sys.argv[1] body = ["#pragma once", "// GENERATED by tools/gen_clay_shader.py: the built-in Clay (.opa home) shaders. Do not edit by hand.", "#include ", "", emit("kClayVertSpirv", build_vert()), "", emit("kClayFragSpirv", build_frag(False)), "", emit("kClayFragCutoutSpirv", build_frag(True)), "", emit("kClaySkinnedVertSpirv", check(build_vert(True))), ""] open(out, "w", newline="\n").write("\n".join(body)) print("wrote", out)