From 04a9ec831d183a56748900d2083bfd37ede49964 Mon Sep 17 00:00:00 2001 From: EtienneLescot Date: Wed, 30 Sep 2026 07:51:57 +0200 Subject: [PATCH 1/5] feat(cursor): model Prism Glow as a mesh traced on its 2D art The arrow and the hand are traced by hand on design/cursors/prism-glow/source.png alone: its facets, a height designed for each, the art's colours kept per facet. prism_geo.py writes prism.json; build_blend.py builds the Blender scene (prism-glow.blend) and the glTF export (prism-glow.glb) from it. --- .../cursors/prism-glow/model/build_blend.py | 265 +++++ .../cursors/prism-glow/model/prism-glow.blend | Bin 0 -> 206195 bytes .../cursors/prism-glow/model/prism-glow.glb | Bin 0 -> 76452 bytes design/cursors/prism-glow/model/prism.json | 985 ++++++++++++++++++ design/cursors/prism-glow/model/prism_geo.py | 157 +++ 5 files changed, 1407 insertions(+) create mode 100644 design/cursors/prism-glow/model/build_blend.py create mode 100644 design/cursors/prism-glow/model/prism-glow.blend create mode 100644 design/cursors/prism-glow/model/prism-glow.glb create mode 100644 design/cursors/prism-glow/model/prism.json create mode 100644 design/cursors/prism-glow/model/prism_geo.py diff --git a/design/cursors/prism-glow/model/build_blend.py b/design/cursors/prism-glow/model/build_blend.py new file mode 100644 index 000000000..4a941e04c --- /dev/null +++ b/design/cursors/prism-glow/model/build_blend.py @@ -0,0 +1,265 @@ +"""Build the Prism Glow cursor in Blender from prism.json, then save prism-glow.blend and export +prism-glow.glb next to this script. + + blender --background --python build_blend.py + +Per cursor, two objects: the faceted crystal (a closed mesh, one flat face per facet of the 2D art, +with its colour in the face attribute `concept`) and the navy rim (a filled 2D curve with a hole, +extruded and rounded). Units: 1 = 1000 px of source.png, origin on the hotspot, y up, z toward the +viewer, rim top at z = 0, everything's underside at z = -0.04. +""" + +import json +import math +import os + +import bmesh +import bpy +from mathutils import Matrix, Vector + +HERE = globals().get("HERE") or os.path.dirname(os.path.abspath(__file__)) +S = 0.001 +T_RIM = 40.0 # rim and crystal-body thickness, px +BEVEL = 5.0 # rim edge rounding, px +HAND_AT = ((1220 - 208) * S, -(50 - 42) * S) # the hand stands where it is in source.png + +D = json.load(open(os.path.join(HERE, "prism.json"))) + + +def srgb_to_linear(c): + return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4 + + +def hex_rgba(h): + return tuple(srgb_to_linear(int(h[i : i + 2], 16) / 255) for i in (1, 3, 5)) + (1.0,) + + +def signed_area(pts): + return 0.5 * sum(a[0] * b[1] - b[0] * a[1] for a, b in zip(pts, pts[1:] + pts[:1])) + + +def model_xy(sh, x, y): + hx, hy = sh["hotspot"] + return ((x - hx) * S, -(y - hy) * S) + + +def fresh_collection(name): + col = bpy.data.collections.get(name) or bpy.data.collections.new(name) + if col.name not in bpy.context.scene.collection.children: + bpy.context.scene.collection.children.link(col) + for ob in list(col.objects): + bpy.data.objects.remove(ob) + return col + + +def build_crystal(name, sh): + V = sh["verts"] + bm = bmesh.new() + concept = bm.faces.layers.float_color.new("concept") + top = {k: bm.verts.new((*model_xy(sh, x, y), z * S)) for k, (x, y, z) in V.items()} + outline = list(sh["outline"]) + if signed_area([model_xy(sh, *V[k][:2]) for k in outline]) < 0: + outline.reverse() + bot = {k: bm.verts.new((*model_xy(sh, *V[k][:2]), -T_RIM * S)) for k in outline} + for f, hexcol in zip(sh["facets"], sh["colors"]): + vs = [top[k] for k in f] + if signed_area([v.co.xy[:] for v in vs]) < 0: + vs.reverse() + bm.faces.new(vs)[concept] = hex_rgba(hexcol) + side = hex_rgba("#1a3cc8") + for a, b in zip(outline, outline[1:] + outline[:1]): + bm.faces.new((top[a], bot[a], bot[b], top[b]))[concept] = side + bm.faces.new([bot[k] for k in reversed(outline)])[concept] = side + bmesh.ops.triangulate(bm, faces=[f for f in bm.faces if len(f.verts) > 3], + quad_method="BEAUTY", ngon_method="BEAUTY") + bmesh.ops.recalc_face_normals(bm, faces=bm.faces[:]) + me = bpy.data.meshes.get(name) or bpy.data.meshes.new(name) + bm.to_mesh(me) + bm.free() + # glTF carries colours per corner: mirror the face colour there + corner = me.attributes.get("Col") or me.attributes.new("Col", "FLOAT_COLOR", "CORNER") + for p in me.polygons: + p.use_smooth = False + for li in p.loop_indices: + corner.data[li].color = me.attributes["concept"].data[p.index].color + me.color_attributes.active_color = corner + return bpy.data.objects.new(name, me) + + +def inset(loop, d): + """Offset a closed counter-clockwise loop inward by d (mitred).""" + out = [] + for i in range(len(loop)): + p0, p1, p2 = Vector(loop[i - 1]), Vector(loop[i]), Vector(loop[(i + 1) % len(loop)]) + e0, e1 = (p1 - p0).normalized(), (p2 - p1).normalized() + n0, n1 = Vector((-e0.y, e0.x)), Vector((-e1.y, e1.x)) + m = (n0 + n1).normalized() if (n0 + n1).length > 1e-6 else n0 + out.append(tuple(p1 + m * (d / max(m.dot(n0), 0.25)))) + return out + + +def build_rim(name, sh): + outer = [model_xy(sh, x, y) for x, y in sh["silhouette"]] + if signed_area(outer) < 0: + outer.reverse() + inner = [model_xy(sh, *sh["verts"][k][:2]) for k in sh["outline"]] + # the bevel rounds outward from the spline: pull the outer loop in so the silhouette stays exact + outer = inset(outer, BEVEL * S) + cu = bpy.data.curves.get(name) or bpy.data.curves.new(name, "CURVE") + cu.splines.clear() + cu.dimensions = "2D" + cu.fill_mode = "BOTH" + cu.extrude = (T_RIM / 2 - BEVEL) * S + cu.bevel_depth = BEVEL * S + cu.bevel_resolution = 3 + for loop in (outer, inner): + sp = cu.splines.new("POLY") + sp.points.add(len(loop) - 1) + for p, (x, y) in zip(sp.points, loop): + p.co = (x, y, 0.0, 1.0) + sp.use_cyclic_u = True + ob = bpy.data.objects.new(name, cu) + ob.location.z = -T_RIM / 2 * S + return ob + + +def nodes_of(name): + m = bpy.data.materials.get(name) or bpy.data.materials.new(name) + m.use_nodes = True + m.node_tree.nodes.clear() + return m, m.node_tree, m.node_tree.nodes.new("ShaderNodeOutputMaterial") + + +def material_crystal(): + """Clear glass whose facets glow with their colour in the art (the look chosen on 30/09/2026).""" + m, nt, out = nodes_of("Prism Crystal") + bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled") + attr = nt.nodes.new("ShaderNodeAttribute") + attr.attribute_name = "concept" + nt.links.new(attr.outputs["Color"], bsdf.inputs["Emission Color"]) + bsdf.inputs["Base Color"].default_value = (1, 1, 1, 1) + bsdf.inputs["Emission Strength"].default_value = 0.45 + bsdf.inputs["Transmission Weight"].default_value = 1.0 + bsdf.inputs["IOR"].default_value = 1.61 + bsdf.inputs["Roughness"].default_value = 0.0 + nt.links.new(bsdf.outputs["BSDF"], out.inputs["Surface"]) + return m + + +def material_rim(): + m, nt, out = nodes_of("Prism Rim") + bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled") + bsdf.inputs["Base Color"].default_value = hex_rgba("#040c5a") + bsdf.inputs["Roughness"].default_value = 0.22 + bsdf.inputs["Coat Weight"].default_value = 1.0 + bsdf.inputs["Coat Roughness"].default_value = 0.05 + nt.links.new(bsdf.outputs["BSDF"], out.inputs["Surface"]) + return m + + +def build(): + col = fresh_collection("Prism Glow") + crystal, rim = material_crystal(), material_rim() + obs = [] + for key, at in (("arrow", (0.0, 0.0)), ("hand", HAND_AT)): + c = build_crystal(f"prism_{key}_crystal", D[key]) + r = build_rim(f"prism_{key}_rim", D[key]) + c.data.materials.clear() + c.data.materials.append(crystal) + r.data.materials.clear() + r.data.materials.append(rim) + for ob in (c, r): + ob.location.x, ob.location.y = at + col.objects.link(ob) + obs += [c, r] + return obs + + +def studio(): + """Studio HDRI for light and reflections, the editor screenshot as the screen, a 3/4 camera.""" + sc = bpy.context.scene + w = sc.world or bpy.data.worlds.new("World") + sc.world = w + w.use_nodes = True + nt = w.node_tree + nt.nodes.clear() + env = nt.nodes.new("ShaderNodeTexEnvironment") + env.image = bpy.data.images.load(os.path.join(bpy.utils.system_resource("DATAFILES"), "studiolights", "world", "studio.exr"), check_existing=True) + env.image.pack() # the install path differs per machine; 100 KB inside the .blend does not + bg, flat = nt.nodes.new("ShaderNodeBackground"), nt.nodes.new("ShaderNodeBackground") + flat.inputs["Color"].default_value = (0.06, 0.06, 0.07, 1) + lp, mix, out = nt.nodes.new("ShaderNodeLightPath"), nt.nodes.new("ShaderNodeMixShader"), nt.nodes.new("ShaderNodeOutputWorld") + nt.links.new(env.outputs["Color"], bg.inputs["Color"]) + nt.links.new(lp.outputs["Is Camera Ray"], mix.inputs["Fac"]) + nt.links.new(bg.outputs["Background"], mix.inputs[1]) + nt.links.new(flat.outputs["Background"], mix.inputs[2]) + nt.links.new(mix.outputs["Shader"], out.inputs["Surface"]) + + shot = os.path.normpath(os.path.join(HERE, "..", "..", "..", "..", "public", "preview4.png")) + screen = bpy.data.objects.get("screen") + if screen is None and os.path.exists(shot): + me = bpy.data.meshes.new("screen") + me.from_pydata([(-3, -1.66, 0), (3, -1.66, 0), (3, 1.66, 0), (-3, 1.66, 0)], [], [(0, 1, 2, 3)]) + uv = me.uv_layers.new() + for li, st in enumerate(((0, 0), (1, 0), (1, 1), (0, 1))): + uv.data[li].uv = st + m, mnt, mout = nodes_of("screen") + em, tex = mnt.nodes.new("ShaderNodeEmission"), mnt.nodes.new("ShaderNodeTexImage") + tex.image = bpy.data.images.load(shot, check_existing=True) + mnt.links.new(tex.outputs["Color"], em.inputs["Color"]) + mnt.links.new(em.outputs["Emission"], mout.inputs["Surface"]) + me.materials.append(m) + screen = bpy.data.objects.new("screen", me) + screen.location = (1.9, -1.0, -T_RIM * S - 0.0005) + sc.collection.objects.link(screen) + + target = Vector((0.679, -0.43, 0.0)) + cam = bpy.data.objects.get("cam_34") or bpy.data.objects.new("cam_34", bpy.data.cameras.new("cam_34")) + if cam.name not in sc.collection.objects: + sc.collection.objects.link(cam) + eye = target + Matrix.Rotation(math.radians(-28), 3, "Y") @ Matrix.Rotation(math.radians(-38), 3, "X") @ Vector((0, 0, 4.4)) + f = (target - eye).normalized() + r = f.cross(Vector((0, 1, 0))).normalized() + cam.location = eye + cam.rotation_euler = Matrix((r, r.cross(f), -f)).transposed().to_euler() + cam.data.lens = 85 + sc.camera = cam + sc.render.engine = "CYCLES" + sc.cycles.device = "GPU" + sc.cycles.samples = 128 + sc.cycles.transmission_bounces = sc.cycles.max_bounces = 24 + sc.render.resolution_x, sc.render.resolution_y = 1600, 1000 + sc.view_settings.view_transform = "AgX" + sc.view_settings.look = "AgX - Medium High Contrast" + + +def export_glb(obs, path): + """The rims are curves; glTF wants meshes: bake them into temporary twins for the export.""" + dg = bpy.context.evaluated_depsgraph_get() + twins = [] + for ob in obs: + if ob.type == "CURVE": + name = ob.name + me = bpy.data.meshes.new_from_object(ob.evaluated_get(dg)) + ob.name = name + "_curve" # the twin takes the name the glTF node should carry + tw = bpy.data.objects.new(name, me) + tw.matrix_world = ob.matrix_world + me.materials.append(ob.data.materials[0]) + bpy.context.scene.collection.objects.link(tw) + twins.append((tw, ob, name)) + bpy.ops.object.select_all(action="DESELECT") + for ob in [o for o in obs if o.type == "MESH"] + [tw for tw, _, _ in twins]: + ob.select_set(True) + bpy.ops.export_scene.gltf(filepath=path, export_format="GLB", use_selection=True, + export_vertex_color="ACTIVE", export_apply=True) + for tw, ob, name in twins: + me = tw.data + bpy.data.objects.remove(tw) + bpy.data.meshes.remove(me) + ob.name = name + + +obs = build() +studio() +export_glb(obs, os.path.join(HERE, "prism-glow.glb")) +bpy.ops.wm.save_as_mainfile(filepath=os.path.join(HERE, "prism-glow.blend"), relative_remap=True) diff --git a/design/cursors/prism-glow/model/prism-glow.blend b/design/cursors/prism-glow/model/prism-glow.blend new file mode 100644 index 0000000000000000000000000000000000000000..8edc4553b1854f19b6d0869128ba1af6f7e0ad5d GIT binary patch literal 206195 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

_(f$y~qXri)Q+9xbZn; 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 literal 0 HcmV?d00001 diff --git a/design/cursors/prism-glow/model/prism.json b/design/cursors/prism-glow/model/prism.json new file mode 100644 index 000000000..5aae7a713 --- /dev/null +++ b/design/cursors/prism-glow/model/prism.json @@ -0,0 +1,985 @@ +{ + "arrow": { + "hotspot": [ + 208, + 42 + ], + "verts": { + "A": [ + 237, + 100, + 0.0 + ], + "B": [ + 443, + 290, + 0.0 + ], + "F": [ + 712, + 549, + 0.0 + ], + "K": [ + 543, + 563, + 0.0 + ], + "J": [ + 612, + 744, + 0.0 + ], + "I": [ + 492, + 807, + 0.0 + ], + "H": [ + 394, + 597, + 0.0 + ], + "G": [ + 241, + 732, + 0.0 + ], + "D": [ + 241, + 491, + 0.0 + ], + "C": [ + 366, + 374, + 108.0 + ], + "E": [ + 458, + 539, + 76.0 + ], + "X": [ + 515, + 449, + 66.0 + ] + }, + "outline": [ + "A", + "B", + "F", + "K", + "J", + "I", + "H", + "G", + "D" + ], + "facets": [ + [ + "A", + "C", + "D" + ], + [ + "A", + "B", + "C" + ], + [ + "B", + "X", + "C" + ], + [ + "B", + "F", + "X" + ], + [ + "C", + "X", + "E" + ], + [ + "X", + "F", + "E" + ], + [ + "E", + "F", + "K" + ], + [ + "C", + "E", + "H" + ], + [ + "C", + "H", + "G" + ], + [ + "C", + "G", + "D" + ], + [ + "E", + "I", + "H" + ], + [ + "E", + "J", + "I" + ], + [ + "E", + "K", + "J" + ] + ], + "colors": [ + "#01f8ee", + "#cadffc", + "#a643fc", + "#c348fc", + "#033df4", + "#002cc0", + "#124efb", + "#00208a", + "#001b80", + "#00b7fb", + "#0022b7", + "#008afc", + "#00fbfb" + ], + "silhouette": [ + [ + 214, + 40 + ], + [ + 207, + 43 + ], + [ + 204, + 49 + ], + [ + 204, + 793 + ], + [ + 207, + 796 + ], + [ + 212, + 796 + ], + [ + 229, + 784 + ], + [ + 381, + 653 + ], + [ + 470, + 846 + ], + [ + 475, + 853 + ], + [ + 481, + 854 + ], + [ + 642, + 770 + ], + [ + 654, + 762 + ], + [ + 654, + 753 + ], + [ + 588, + 594 + ], + [ + 628, + 588 + ], + [ + 768, + 576 + ], + [ + 773, + 572 + ], + [ + 773, + 568 + ], + [ + 768, + 560 + ], + [ + 675, + 464 + ], + [ + 604, + 393 + ], + [ + 478, + 272 + ], + [ + 284, + 96 + ], + [ + 221, + 42 + ] + ] + }, + "hand": { + "hotspot": [ + 1220, + 50 + ], + "verts": { + "I1": [ + 1180, + 122, + 0.0 + ], + "I2": [ + 1221, + 85, + 0.0 + ], + "I3": [ + 1261, + 90, + 0.0 + ], + "I4": [ + 1290, + 132, + 0.0 + ], + "IT1": [ + 1224, + 118, + 46.0 + ], + "IT2": [ + 1274, + 135, + 35.0 + ], + "IT3": [ + 1191, + 262, + 54.0 + ], + "IL": [ + 1179, + 265, + 0.0 + ], + "P1": [ + 1292, + 427, + 0.0 + ], + "W1": [ + 1321, + 456, + 0.0 + ], + "M1": [ + 1323, + 334, + 0.0 + ], + "M2": [ + 1356, + 302, + 0.0 + ], + "M3": [ + 1395, + 309, + 0.0 + ], + "M4": [ + 1415, + 340, + 0.0 + ], + "MT1": [ + 1354, + 334, + 46.0 + ], + "MT2": [ + 1390, + 344, + 35.0 + ], + "P2": [ + 1339, + 479, + 59.0 + ], + "MB1": [ + 1386, + 481, + 54.0 + ], + "MB2": [ + 1415, + 462, + 0.0 + ], + "RL": [ + 1441, + 467, + 0.0 + ], + "R1": [ + 1443, + 368, + 0.0 + ], + "R2": [ + 1476, + 338, + 0.0 + ], + "R3": [ + 1515, + 345, + 0.0 + ], + "R4": [ + 1536, + 377, + 0.0 + ], + "RT1": [ + 1480, + 367, + 43.0 + ], + "RT2": [ + 1513, + 381, + 34.0 + ], + "RB": [ + 1448, + 475, + 57.0 + ], + "RB1": [ + 1476, + 498, + 54.0 + ], + "RB2": [ + 1535, + 489, + 0.0 + ], + "PK": [ + 1567, + 502, + 0.0 + ], + "K1": [ + 1568, + 403, + 0.0 + ], + "K2": [ + 1600, + 375, + 0.0 + ], + "K3": [ + 1637, + 382, + 0.0 + ], + "K4": [ + 1658, + 412, + 0.0 + ], + "KT1": [ + 1601, + 404, + 40.0 + ], + "KT2": [ + 1640, + 420, + 30.0 + ], + "K5": [ + 1658, + 608, + 0.0 + ], + "PR": [ + 1651, + 622, + 0.0 + ], + "BR": [ + 1522, + 818, + 0.0 + ], + "BL": [ + 1208, + 820, + 0.0 + ], + "HC": [ + 1406, + 640, + 76.0 + ], + "Q": [ + 1178, + 572, + 49.0 + ], + "TB": [ + 1128, + 682, + 0.0 + ], + "TT2": [ + 1015, + 490, + 0.0 + ], + "TL": [ + 1013, + 427, + 0.0 + ], + "TT": [ + 1069, + 402, + 0.0 + ], + "TR": [ + 1133, + 449, + 0.0 + ], + "TT1": [ + 1065, + 435, + 43.0 + ], + "TH": [ + 1144, + 541, + 40.0 + ] + }, + "outline": [ + "I1", + "I2", + "I3", + "I4", + "P1", + "W1", + "M1", + "M2", + "M3", + "M4", + "MB2", + "RL", + "R1", + "R2", + "R3", + "R4", + "RB2", + "PK", + "K1", + "K2", + "K3", + "K4", + "K5", + "PR", + "BR", + "BL", + "TB", + "TT2", + "TL", + "TT", + "TR", + "TH", + "Q", + "IL" + ], + "facets": [ + [ + "IT1", + "IT2", + "IT3" + ], + [ + "IT1", + "I2", + "I3", + "IT2" + ], + [ + "I1", + "I2", + "IT1" + ], + [ + "I1", + "IT1", + "IT3", + "IL" + ], + [ + "I3", + "I4", + "IT2" + ], + [ + "IT2", + "I4", + "P1", + "IT3" + ], + [ + "IL", + "IT3", + "P1", + "Q" + ], + [ + "Q", + "P1", + "W1" + ], + [ + "Q", + "W1", + "P2" + ], + [ + "Q", + "P2", + "HC" + ], + [ + "MT1", + "MT2", + "P2" + ], + [ + "MT1", + "M2", + "M3", + "MT2" + ], + [ + "M1", + "M2", + "MT1" + ], + [ + "M1", + "MT1", + "P2", + "W1" + ], + [ + "M3", + "M4", + "MT2" + ], + [ + "MT2", + "M4", + "MB2", + "MB1", + "P2" + ], + [ + "RT1", + "RT2", + "RB" + ], + [ + "RT1", + "R2", + "R3", + "RT2" + ], + [ + "R1", + "R2", + "RT1" + ], + [ + "R1", + "RT1", + "RB", + "RL" + ], + [ + "R3", + "R4", + "RT2" + ], + [ + "RT2", + "R4", + "RB2", + "RB1", + "RB" + ], + [ + "KT1", + "KT2", + "PK" + ], + [ + "KT1", + "K2", + "K3", + "KT2" + ], + [ + "K1", + "K2", + "KT1" + ], + [ + "K1", + "KT1", + "PK" + ], + [ + "K3", + "K4", + "KT2" + ], + [ + "KT2", + "K4", + "K5", + "PR", + "PK" + ], + [ + "P2", + "MB1", + "MB2", + "RL", + "RB", + "RB1", + "RB2", + "PK", + "HC" + ], + [ + "HC", + "PK", + "BR" + ], + [ + "PK", + "PR", + "BR" + ], + [ + "HC", + "BR", + "BL" + ], + [ + "Q", + "HC", + "BL" + ], + [ + "TT1", + "TH", + "TT2" + ], + [ + "TL", + "TT", + "TT1", + "TT2" + ], + [ + "TT", + "TR", + "TH", + "TT1" + ], + [ + "TT2", + "TH", + "TB" + ], + [ + "TH", + "Q", + "TB" + ], + [ + "Q", + "BL", + "TB" + ] + ], + "colors": [ + "#b2fcfc", + "#01fbfc", + "#00faf3", + "#01faf4", + "#00b7f4", + "#00a5eb", + "#bf54fc", + "#662ffa", + "#7237fa", + "#7651fc", + "#88fbfc", + "#00e8ee", + "#00f5f4", + "#76fafb", + "#00beed", + "#00b1f0", + "#8cfcfc", + "#00f3f3", + "#00e1ed", + "#01edf3", + "#00cef6", + "#00b7f7", + "#82fbfc", + "#01f1f5", + "#00e0ee", + "#01f1f3", + "#00c0f2", + "#6f31fb", + "#0129a6", + "#1542fb", + "#0021af", + "#002eba", + "#01f7fc", + "#bffcfc", + "#01fafc", + "#00e3f0", + "#00ddee", + "#00beef", + "#008cf5" + ], + "silhouette": [ + [ + 1209, + 50 + ], + [ + 1169, + 85 + ], + [ + 1145, + 109 + ], + [ + 1143, + 113 + ], + [ + 1142, + 404 + ], + [ + 1137, + 404 + ], + [ + 1073, + 360 + ], + [ + 1066, + 361 + ], + [ + 988, + 401 + ], + [ + 978, + 409 + ], + [ + 976, + 477 + ], + [ + 977, + 500 + ], + [ + 1060, + 640 + ], + [ + 1179, + 846 + ], + [ + 1186, + 852 + ], + [ + 1197, + 856 + ], + [ + 1530, + 856 + ], + [ + 1543, + 852 + ], + [ + 1556, + 839 + ], + [ + 1695, + 623 + ], + [ + 1695, + 401 + ], + [ + 1651, + 348 + ], + [ + 1647, + 346 + ], + [ + 1589, + 338 + ], + [ + 1584, + 340 + ], + [ + 1566, + 357 + ], + [ + 1538, + 317 + ], + [ + 1531, + 311 + ], + [ + 1469, + 301 + ], + [ + 1464, + 303 + ], + [ + 1445, + 320 + ], + [ + 1416, + 276 + ], + [ + 1412, + 273 + ], + [ + 1358, + 264 + ], + [ + 1348, + 264 + ], + [ + 1330, + 280 + ], + [ + 1328, + 256 + ], + [ + 1329, + 200 + ], + [ + 1327, + 121 + ], + [ + 1283, + 61 + ], + [ + 1278, + 57 + ], + [ + 1225, + 50 + ] + ] + } +} diff --git a/design/cursors/prism-glow/model/prism_geo.py b/design/cursors/prism-glow/model/prism_geo.py new file mode 100644 index 000000000..28a115496 --- /dev/null +++ b/design/cursors/prism-glow/model/prism_geo.py @@ -0,0 +1,157 @@ +"""Prism Glow cursor as a faceted mesh: the facets traced from ../source.png, and their heights. + +Coordinates are source-PNG pixels (x right, y down); `z` is the height above the navy rim's top in +the same pixels (unlisted vertices sit on the rim). `outline` is the crystal's edge against the rim, +`facets` are the flat faces of the 2D art, each an ordered polygon. + + python prism_geo.py # writes prism.json: vertices, facets, their colours in the art, silhouettes + +Then build the Blender scene and the glTF from it with build_blend.py. +""" + +import json +from pathlib import Path + +import cv2 +import numpy as np + +HERE = Path(__file__).resolve().parent + +ARROW = { + "hotspot": (208, 42), + "verts": { + "A": (237, 100), "B": (443, 290), "F": (712, 549), "K": (543, 563), "J": (612, 744), + "I": (492, 807), "H": (394, 597), "G": (241, 732), "D": (241, 491), + "C": (366, 374), "E": (458, 539), "X": (515, 449), + }, + "outline": ["A", "B", "F", "K", "J", "I", "H", "G", "D"], + "z": {"C": 108, "E": 76, "X": 66}, + "facets": [ + ["A", "C", "D"], ["A", "B", "C"], ["B", "X", "C"], ["B", "F", "X"], + ["C", "X", "E"], ["X", "F", "E"], ["E", "F", "K"], + # the notch H sits on the E-G line: one concept facet, folded along C-H + ["C", "E", "H"], ["C", "H", "G"], ["C", "G", "D"], + ["E", "I", "H"], ["E", "J", "I"], ["E", "K", "J"], + ], +} + +HAND = { + "hotspot": (1220, 50), + "verts": { + # index finger + "I1": (1180, 122), "I2": (1221, 85), "I3": (1261, 90), "I4": (1290, 132), + "IT1": (1224, 118), "IT2": (1274, 135), "IT3": (1191, 262), "IL": (1179, 265), + "P1": (1292, 427), + # middle finger + "W1": (1321, 456), "M1": (1323, 334), "M2": (1356, 302), "M3": (1395, 309), + "M4": (1415, 340), "MT1": (1354, 334), "MT2": (1390, 344), "P2": (1339, 479), + "MB1": (1386, 481), "MB2": (1415, 462), + # ring finger + "RL": (1441, 467), "R1": (1443, 368), "R2": (1476, 338), "R3": (1515, 345), + "R4": (1536, 377), "RT1": (1480, 367), "RT2": (1513, 381), "RB": (1448, 475), + "RB1": (1476, 498), "RB2": (1535, 489), + # pinky + "PK": (1567, 502), "K1": (1568, 403), "K2": (1600, 375), "K3": (1637, 382), + "K4": (1658, 412), "KT1": (1601, 404), "KT2": (1640, 420), "K5": (1658, 608), + "PR": (1651, 622), + # palm + "BR": (1522, 818), "BL": (1208, 820), "HC": (1406, 640), "Q": (1178, 572), + # thumb + "TB": (1128, 682), "TT2": (1015, 490), "TL": (1013, 427), "TT": (1069, 402), + "TR": (1133, 449), "TT1": (1065, 435), "TH": (1144, 541), + }, + # Each fingertip is cut like a gem: a table (the white facet), crown facets down to the rim. + # The thumb web (TH, Q) rises above the rim so the thumb's lower facets are not flat. + "z": { + "IT1": 46, "IT2": 35, "IT3": 54, + "MT1": 46, "MT2": 35, "P2": 59, "MB1": 54, + "RT1": 43, "RT2": 34, "RB": 57, "RB1": 54, + "KT1": 40, "KT2": 30, + "TT1": 43, "TH": 40, "Q": 49, + "HC": 76, + }, + "outline": [ + "I1", "I2", "I3", "I4", "P1", "W1", "M1", "M2", "M3", "M4", "MB2", "RL", "R1", "R2", + "R3", "R4", "RB2", "PK", "K1", "K2", "K3", "K4", "K5", "PR", "BR", "BL", "TB", "TT2", + "TL", "TT", "TR", "TH", "Q", "IL", + ], + "facets": [ + # index: table, crown, sides + ["IT1", "IT2", "IT3"], ["IT1", "I2", "I3", "IT2"], ["I1", "I2", "IT1"], + ["I1", "IT1", "IT3", "IL"], ["I3", "I4", "IT2"], ["IT2", "I4", "P1", "IT3"], + ["IL", "IT3", "P1", "Q"], + # palm under the index (W1 sits on the P1-P2 line) + ["Q", "P1", "W1"], ["Q", "W1", "P2"], ["Q", "P2", "HC"], + # middle finger + ["MT1", "MT2", "P2"], ["MT1", "M2", "M3", "MT2"], ["M1", "M2", "MT1"], + ["M1", "MT1", "P2", "W1"], ["M3", "M4", "MT2"], ["MT2", "M4", "MB2", "MB1", "P2"], + # ring finger + ["RT1", "RT2", "RB"], ["RT1", "R2", "R3", "RT2"], ["R1", "R2", "RT1"], + ["R1", "RT1", "RB", "RL"], ["R3", "R4", "RT2"], ["RT2", "R4", "RB2", "RB1", "RB"], + # pinky + ["KT1", "KT2", "PK"], ["KT1", "K2", "K3", "KT2"], ["K1", "K2", "KT1"], + ["K1", "KT1", "PK"], ["K3", "K4", "KT2"], ["KT2", "K4", "K5", "PR", "PK"], + # palm + ["P2", "MB1", "MB2", "RL", "RB", "RB1", "RB2", "PK", "HC"], + ["HC", "PK", "BR"], ["PK", "PR", "BR"], ["HC", "BR", "BL"], ["Q", "HC", "BL"], + # thumb + ["TT1", "TH", "TT2"], ["TL", "TT", "TT1", "TT2"], ["TT", "TR", "TH", "TT1"], + ["TT2", "TH", "TB"], ["TH", "Q", "TB"], ["Q", "BL", "TB"], + ], +} + + +def area(poly): + p = np.asarray(poly, float) + return 0.5 * np.sum(p[:, 0] * np.roll(p[:, 1], -1) - np.roll(p[:, 0], -1) * p[:, 1]) + + +def check(shape, name): + """The facets tile the outline exactly: no gap, no overlap, no stray vertex.""" + v = shape["verts"] + outline = abs(area([v[k] for k in shape["outline"]])) + facets = sum(abs(area([v[k] for k in f])) for f in shape["facets"]) + unused = set(v) - {k for f in shape["facets"] for k in f} + assert abs(facets / outline - 1) < 1e-9 and not unused, (name, facets / outline, unused) + + +def facet_colors(shape, img): + """Median colour of each facet in the art, away from its edges.""" + v = shape["verts"] + out = [] + for f in shape["facets"]: + mask = np.zeros(img.shape[:2], np.uint8) + cv2.fillPoly(mask, [np.array([v[k] for k in f], np.int32)], 255) + inner = cv2.erode(mask, np.ones((7, 7), np.uint8)) + px = img[(inner if inner.any() else mask) > 0][:, :3] + b, g, r = np.median(px, axis=0) + out.append("#%02x%02x%02x" % (int(r), int(g), int(b))) + return out + + +def silhouette(img, x0, x1): + """Outer edge of the navy rim, from the art's alpha.""" + a = (img[:, :, 3] > 127).astype(np.uint8) + a[:, :x0] = 0 + a[:, x1:] = 0 + contours, _ = cv2.findContours(a, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) + c = max(contours, key=cv2.contourArea) + return cv2.approxPolyDP(c, 1.2, True)[:, 0, :].tolist() + + +if __name__ == "__main__": + img = cv2.imread(str(HERE.parent / "source.png"), cv2.IMREAD_UNCHANGED) + out = {} + for shape, name, (x0, x1) in ((ARROW, "arrow", (0, 887)), (HAND, "hand", (887, 1774))): + check(shape, name) + z = shape["z"] + out[name] = { + "hotspot": shape["hotspot"], + "verts": {k: [*p, float(z.get(k, 0))] for k, p in shape["verts"].items()}, + "outline": shape["outline"], + "facets": shape["facets"], + "colors": facet_colors(shape, img), + "silhouette": silhouette(img, x0, x1), + } + (HERE / "prism.json").write_text(json.dumps(out, indent=1) + "\n", newline="\n") + print({k: f"{len(v['facets'])} facets" for k, v in out.items()}) From cf93ee5c14d1cf55a23ee9a50c43caebb498252d Mon Sep 17 00:00:00 2001 From: EtienneLescot Date: Wed, 30 Sep 2026 07:53:03 +0200 Subject: [PATCH 2/5] feat(compositor): ray-trace Prism Glow as a glass crystal in 3D With the 3D cursor on, mode 15 draws Prism Glow's arrow and hand from the mesh instead of extruding their PNG. The navy rim is the silhouette extruded, a distance field like the other sculpted models: it gives the coverage and the shadows. The crystal is ray-traced triangle by triangle: refraction per colour channel with a slight dispersion, total internal reflection, and an exit through its flat base onto the recording under the cursor, read through the plane's UV cut (trail_b). Each facet glows faintly in its drawn colour; a light line marks the folds. export_compositor.py writes the mesh into prism_mesh.rs and the three shaders; a test checks they agree. On Linux the mesh sits in a uniform buffer: as a const or private table in the WGSL, lavapipe copied it into every invocation of every layer, and a frame without any cursor rendered 3.4 times slower. --- crates/compositor/src/compositor_linux.rs | 79 +- crates/compositor/src/compositor_macos.rs | 8 +- crates/compositor/src/compositor_windows.rs | 4 + crates/compositor/src/frame_geometry.rs | 27 +- crates/compositor/src/lib.rs | 1 + crates/compositor/src/prism_mesh.rs | 822 ++++++++++++ crates/compositor/src/sculpt.rs | 112 +- crates/compositor/src/shaders.hlsl | 1124 ++++++++++++++++- crates/compositor/src/shaders.metal | 1082 +++++++++++++++- crates/compositor/src/vk_shaders/layer.wgsl | 327 ++++- .../compositor/tests/cursor_model_render.rs | 40 +- design/cursors/3d-direction.md | 23 +- design/cursors/README.md | 7 +- .../prism-glow/model/export_compositor.py | 306 +++++ design/cursors/requirements.md | 19 +- docs/3d-effects-v2.md | 21 +- src/lib/cursor/cursorThemes.test.ts | 11 +- src/lib/cursor/cursorThemes.ts | 8 +- 18 files changed, 3917 insertions(+), 104 deletions(-) create mode 100644 crates/compositor/src/prism_mesh.rs create mode 100644 design/cursors/prism-glow/model/export_compositor.py diff --git a/crates/compositor/src/compositor_linux.rs b/crates/compositor/src/compositor_linux.rs index d6f14670f..808f93f9f 100644 --- a/crates/compositor/src/compositor_linux.rs +++ b/crates/compositor/src/compositor_linux.rs @@ -71,6 +71,13 @@ fn layer_bytes(cb: &LayerCB) -> &[u8] { unsafe { std::slice::from_raw_parts(cb as *const LayerCB as *const u8, LAYER_BYTES as usize) } } +/// Le maillage de Prism Glow tel que le lit le WGSL (`PrismMesh`) : les triangles, puis les +/// polygones, un point par vec4 (le pas d'un tableau uniform). +fn prism_mesh_bytes() -> Vec { + let points = crate::prism_mesh::POLYS.iter().map(|&[x, y]| [x, y, 0.0, 0.0]); + crate::prism_mesh::TRIS.iter().copied().chain(points).flatten().flat_map(f32::to_ne_bytes).collect() +} + /// Un calque de fond deja lie, en attente de son `draw`. `_buf`/`_tex`/`_view` /// ne sont jamais relus : ils gardent en vie ce que le bind group reference /// jusqu'au submit. Ce backend encode toute la frame avant de la soumettre, la @@ -272,6 +279,8 @@ pub struct Compositor { pipeline_copy: wgpu::RenderPipeline, bind_group_layout: wgpu::BindGroupLayout, sampler: wgpu::Sampler, + /// Le maillage de Prism Glow (`prism_mesh.rs`) tel que le lit le WGSL (`PrismMesh`, binding 7). + prism_mesh: wgpu::Buffer, // Chaine de blur Kawase du fond (`blur.wgsl`) : layout dedie (uniform + 1 // tex + sampler), 2 pipelines (down/up), 3 textures de pyramide (1/2, 1/4, @@ -474,8 +483,26 @@ impl Compositor { // Pyramide de profondeur de champ (modes 8 et 18), `dummy` ailleurs : le // mode 18 lit a la fois le metrage, son rendu isole (binding 4) et elle. tex_entry(6), + // Le maillage de Prism Glow (mode 15). Un uniform et non des tables dans le + // shader : lavapipe les recopiait a chaque pixel de chaque calque, et une + // frame sans curseur se rendait 3,4 fois plus lentement. + wgpu::BindGroupLayoutEntry { + binding: 7, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, ], }); + let prism_mesh = gpu.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("prism-mesh"), + contents: &prism_mesh_bytes(), + usage: wgpu::BufferUsages::UNIFORM, + }); let pipeline_layout = gpu.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("layer"), bind_group_layouts: &[&bind_group_layout], @@ -663,6 +690,7 @@ impl Compositor { pipeline_copy, bind_group_layout, sampler, + prism_mesh, blur_bgl, blur_down, blur_up, @@ -1283,6 +1311,7 @@ impl Compositor { /// `make_bind`, binding 4 impose (le rendu isole du mode 18, a la place du masque webcam /// qu'il ne lit pas), et la pyramide de profondeur de champ en binding 6 (modes 8 et 18). + /// Le mode 15 y met le champ de son sprite et le sprite. fn make_bind_b4( &self, cb: &LayerCB, @@ -1338,6 +1367,7 @@ impl Compositor { binding: 6, resource: wgpu::BindingResource::TextureView(dof.unwrap_or(dummy)), }, + wgpu::BindGroupEntry { binding: 7, resource: self.prism_mesh.as_entire_binding() }, ], }); (uniform, bind) @@ -2906,7 +2936,9 @@ impl Compositor { let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); // Curseur modelise (mode 15) : ce meme sprite extrude, `plan_cursor` en a tire la - // pose. Sprite au binding 1 (texY), champ au binding 2 (texU). Parite Windows/macOS. + // pose. L'enregistrement reste aux bindings 1, 2 et 5 (le cristal de Prism Glow le + // refracte), le champ au binding 4 (texMask), le sprite au binding 6 (texDof). + // Parite Windows/macOS. if let Some(pose) = plan.model { match self.cursor_sdf(&sprite.path) { Ok((sdf, shape)) => { @@ -2923,8 +2955,13 @@ impl Compositor { ) else { continue; }; - let (buf, bind) = - self.make_bind(&cb, Some((&view, &sdf_view, &view)), &dummy); + let (buf, bind) = self.make_bind_b4( + &cb, + Some((&sy, &su, &sv)), + &dummy, + Some(&sdf_view), + Some(&view), + ); bufs.push(buf); binds.push(bind); } @@ -5523,6 +5560,22 @@ mod tests { .collect() } + /// Les pixels que couvre un curseur de VERRE (le cristal de Prism Glow, qui laisse voir le + /// contenu et le réfracte) : opaques, ou changés sur les deux teintes sans y être plus sombres + /// (une ombre ne fait qu'assombrir ; le verre réfracte, reflète et luit). + fn model_covered(on_blue: &[u8], on_orange: &[u8], bare: &[u8], bare_orange: &[u8]) -> Vec { + let opaque = model_opaque(on_blue, on_orange, bare); + (0..1280 * 720) + .map(|i| { + let lit = |on: &[u8], under: &[u8]| { + let (a, b) = (&on[i * 4..i * 4 + 3], &under[i * 4..i * 4 + 3]); + a != b && model_luma(a) >= model_luma(b) - 2.0 + }; + opaque[i] || (lit(on_blue, bare) && lit(on_orange, bare_orange)) + }) + .collect() + } + fn model_iou(a: &[bool], b: &[bool]) -> f32 { let inter = a.iter().zip(b).filter(|(x, y)| **x && **y).count(); let union = a.iter().zip(b).filter(|(x, y)| **x || **y).count(); @@ -5681,7 +5734,8 @@ mod tests { /// Pendant de `the_sculpted_cursors_stand_at_the_hotspot` (Windows) : la flèche et la main /// sculptées des thèmes d'origine (`sculpt.rs`) passent par le WGSL, tiennent au hotspot, en - /// bas à droite de lui, et portent leur ombre en l'air. + /// bas à droite de lui, et portent leur ombre en l'air. Le cristal de Prism Glow est de verre : + /// son corps compte ce qu'il couvre, et une bonne part de lui change avec la teinte de l'écran. #[test] fn the_sculpted_cursors_stand_at_the_hotspot() { let Some(gpu) = gpu() else { return }; @@ -5693,11 +5747,12 @@ mod tests { let extruded = model_scene_json("null", Some(true), "default", true, 5.0); let hidden = model_scene_json("null", Some(true), "default", false, 5.0); let bare = compose_model(&comp, &blue, &hidden, &model_track("arrow", false, 0.5)); + let bare_orange = compose_model(&comp, &orange, &hidden, &model_track("arrow", false, 0.5)); let mut failures = Vec::new(); for state in ["arrow", "pointer"] { let still = model_track(state, false, 0.5); let sprite = compose_model(&comp, &blue, &extruded, &still); - for theme in ["studio-ink", "pop-coral", "pixel-candy", "star-sprout"] { + for theme in ["studio-ink", "prism-glow", "pop-coral", "pixel-candy", "star-sprout"] { // Le sprite reste celui du theme par defaut : seul le nom pose le modele. let json = extruded .replace(&format!(r#"/{state}.png","#), &format!(r#"/{state}.png","sculpt":"{theme}/{state}","#)); @@ -5707,7 +5762,12 @@ mod tests { failures.push(format!("{theme}/{state}: le sprite extrude au lieu du modele")); } let (tip, u) = model_tip(&json, &still); - let mask = model_opaque(&hover, &hover_b, &bare); + let glass = theme == "prism-glow"; + let mask = if glass { + model_covered(&hover, &hover_b, &bare, &bare_orange) + } else { + model_opaque(&hover, &hover_b, &bare) + }; let (mut body, mut c, mut near) = (0usize, [0.0f32; 2], f32::MAX); for (i, _) in mask.iter().enumerate().filter(|(_, m)| **m) { let (x, y) = ((i % 1280) as f32, (i / 1280) as f32); @@ -5735,6 +5795,13 @@ mod tests { if shadow * 10 < body * 3 { failures.push(format!("{theme}/{state}: pas d'ombre en l'air ({shadow} px)")); } + if glass { + let through = (0..1280 * 720).filter(|&i| mask[i] && hover[i * 4..i * 4 + 3] != hover_b[i * 4..i * 4 + 3]).count(); + println!("{theme}/{state} : {through} px du corps laissent voir l'ecran"); + if through * 10 < body * 3 { + failures.push(format!("{theme}/{state}: le cristal ne laisse voir l'ecran que sur {through} px")); + } + } } } assert!(failures.is_empty(), "{failures:#?}"); diff --git a/crates/compositor/src/compositor_macos.rs b/crates/compositor/src/compositor_macos.rs index b00a288ee..1e59f2de1 100644 --- a/crates/compositor/src/compositor_macos.rs +++ b/crates/compositor/src/compositor_macos.rs @@ -2066,7 +2066,8 @@ impl Compositor { /// `frame_geometry::cursor_sprite_cb`, partagée avec Windows et Linux. /// /// Avec `model`, le même sprite extrudé (mode 15, `cursor_model_cb`) : le sprite en - /// texture(2), son champ de distance en texture(4). + /// texture(2), son champ de distance en texture(4), l'enregistrement que l'appelant a lié en + /// texture(0) et (1). #[allow(clippy::too_many_arguments)] unsafe fn draw_cursor_sprite( &self, @@ -2432,6 +2433,9 @@ impl Compositor { } if plan.taps <= 1 { let e = self.begin_pass(cmd_buf, &self.rt, None, &self.pipeline_main)?; + // Le cristal de Prism Glow (mode 15) réfracte l'enregistrement : texture(0) et (1). + e.set_fragment_texture(0, Some(&sy)); + e.set_fragment_texture(1, Some(&suv)); self.draw_cur_themed( e, &sprites, @@ -2454,6 +2458,8 @@ impl Compositor { Some(metal::MTLClearColor::new(0.0, 0.0, 0.0, 0.0)), &self.pipeline_add, )?; + e.set_fragment_texture(0, Some(&sy)); + e.set_fragment_texture(1, Some(&suv)); for k in 0..plan.taps { let f = k as f32 / (plan.taps - 1) as f32; let w = crate::frame_geometry::cursor_tap_weight(k, plan.taps); diff --git a/crates/compositor/src/compositor_windows.rs b/crates/compositor/src/compositor_windows.rs index 59431e383..360f307ba 100644 --- a/crates/compositor/src/compositor_windows.rs +++ b/crates/compositor/src/compositor_windows.rs @@ -2161,6 +2161,10 @@ impl Compositor { .map(|s| s.cursor.cursor_sprites.clone()) .unwrap_or_default(); let cursor_type = plan.cursor_type.as_deref(); + // L'enregistrement en t0/t1 : le cristal de Prism Glow (mode 15) le lit sous le + // curseur pour le réfracter. Les plans y sont déjà depuis le dessin de l'écran ; on + // ne compte pas là-dessus. + self.ctx.PSSetShaderResources(0, Some(&[Some(sy.clone()), Some(suv.clone())])); // L'impact des clics (mode 16, sans texture), posé sur l'écran SOUS le curseur. for cb in &plan.impacts { self.draw_solid(cb); diff --git a/crates/compositor/src/frame_geometry.rs b/crates/compositor/src/frame_geometry.rs index 7c367a9a1..4b872ce12 100644 --- a/crates/compositor/src/frame_geometry.rs +++ b/crates/compositor/src/frame_geometry.rs @@ -1066,6 +1066,9 @@ pub enum CursorPlacement { screen_px: [f32; 2], /// Taille de la cible de rendu en px, pour repasser des px aux 0..1 de la sortie. render_px: [f32; 2], + /// La coupe que montre le plan, en uv de la TEXTURE écran (`FrameGeometry::cut`) : de + /// quoi lire l'enregistrement sous le curseur (le cristal de Prism Glow le réfracte). + screen_uv: [f32; 4], }, } impl CursorPlacement { @@ -1080,12 +1083,13 @@ impl CursorPlacement { pub(crate) fn lerp(self, other: CursorPlacement, f: f32) -> CursorPlacement { match (self, other) { ( - CursorPlacement::Tilted { plane_pt: a, quad, center_px, screen_px, render_px }, + CursorPlacement::Tilted { plane_pt: a, quad, center_px, screen_px, render_px, screen_uv }, CursorPlacement::Tilted { plane_pt: b, quad: quad_b, center_px: center_b, screen_px: screen_b, + screen_uv: uv_b, .. }, ) => CursorPlacement::Tilted { @@ -1111,6 +1115,7 @@ impl CursorPlacement { center_px: [lerp(center_px[0], center_b[0], f), lerp(center_px[1], center_b[1], f)], screen_px: [lerp(screen_px[0], screen_b[0], f), lerp(screen_px[1], screen_b[1], f)], render_px, + screen_uv: lerp4(screen_uv, uv_b, f), }, (a, b) => { let (p, q) = (a.upright_center(), b.upright_center()); @@ -3570,6 +3575,7 @@ pub fn plan_cursor(g: &FrameGeometry, input: &CursorPlanInput) -> Option Option CursorPlacement::Upright { @@ -3763,9 +3770,14 @@ pub fn plan_cursor(g: &FrameGeometry, input: &CursorPlanInput) -> Option Option { let view = ModelView::new(placement, size_px, pose, shape)?; - let CursorPlacement::Tilted { render_px: [rw, rh], .. } = placement else { return None }; + let CursorPlacement::Tilted { render_px: [rw, rh], screen_uv, .. } = placement else { return None }; let [x0, y0, x1, y1] = view.footprint()?; // Bords entiers : `local` vaut alors k + 0,5 au centre des pixels, comme le rastériseur. let (x0, y0, x1, y1) = (x0.floor(), y0.floor(), x1.ceil(), y1.ceil()); @@ -4190,6 +4202,7 @@ pub fn cursor_model_cb( mb: [view.half[0], view.half[1], view.offset[0], view.offset[1]], radius_px: shape.size[0] / shape.size[1], trail_a: [shape.sculpt as f32, shape.thick, shape.max_height, 0.0], + trail_b: screen_uv, ..Default::default() }) } @@ -4265,7 +4278,7 @@ fn cursor_impact_cb( impact: Impact, alpha: f32, ) -> Option { - let CursorPlacement::Tilted { plane_pt, quad, center_px, screen_px, render_px: [rw, rh] } = + let CursorPlacement::Tilted { plane_pt, quad, center_px, screen_px, render_px: [rw, rh], .. } = placement else { return None; @@ -7638,7 +7651,7 @@ mod tests { a: f32, clip: [f32; 4], ) -> LayerCB { - let CursorPlacement::Tilted { plane_pt, quad, center_px, screen_px, render_px } = p + let CursorPlacement::Tilted { plane_pt, quad, center_px, screen_px, render_px, .. } = p else { unreachable!() }; diff --git a/crates/compositor/src/lib.rs b/crates/compositor/src/lib.rs index e26480cc6..8e5f63e17 100644 --- a/crates/compositor/src/lib.rs +++ b/crates/compositor/src/lib.rs @@ -43,6 +43,7 @@ pub mod regions; // cibles) et le shim C. Seul Linux l'appelle aujourd'hui, parce que c'est la // seule plateforme dont la capture passe par `MediaRecorder`, mais rien dedans // n'est spécifique à Linux. +pub mod prism_mesh; pub mod remux; pub mod scene; pub mod sculpt; diff --git a/crates/compositor/src/prism_mesh.rs b/crates/compositor/src/prism_mesh.rs new file mode 100644 index 000000000..2be2df9a7 --- /dev/null +++ b/crates/compositor/src/prism_mesh.rs @@ -0,0 +1,822 @@ +//! Les deux curseurs de Prism Glow en MAILLAGE : les facettes tracées sur l'art 2D +//! (`design/cursors/prism-glow/model`), taillées en cristal. Fichier GÉNÉRÉ par +//! `design/cursors/prism-glow/model/export_compositor.py` : ne pas l'éditer à la main. +//! +//! Repère du modèle du mode 15 : unité = côté du sprite 128 px du thème, origine au hotspot, +//! x à droite, y vers le bas, z vers la caméra, dessus du serti en z = 0. Chaque triangle du +//! cristal tient en quatre float4 : (v0, drapeaux), (e1, r), (e2, g), (normale, b) ; drapeaux = +//! paroi (1) + 2 × arêtes réelles (bit k : l'arête opposée au sommet k). Le fond plat, en +//! z = -épaisseur, est laissé au shader. `POLYS` tient les silhouettes (bord extérieur du +//! serti) puis les contours du cristal (son bord intérieur). + +/// Un des deux modèles : ses triangles et sa silhouette dans les tables, sa boîte de dessin. +pub struct PrismModel { + pub tri_start: usize, + pub tri_count: usize, + pub sil_start: usize, + pub sil_count: usize, + pub out_start: usize, + pub out_count: usize, + /// Largeur de la boîte, hauteur 1 (le sprite), en unités du modèle. + pub size: [f32; 2], + /// Hotspot, fraction de la boîte. + pub hotspot: [f32; 2], + /// Haut de la silhouette, fraction de la hauteur. + pub top: f32, + /// Sommet du cristal au-dessus de z = 0, et épaisseur sous z = 0. + pub height: f32, + pub thick: f32, +} + +/// Arrondi des arêtes du serti, en unités du modèle (`PRISM_BEVEL` des shaders). +pub const BEVEL: f32 = 0.012883; + +/// Flèche puis main, dans l'ordre des identifiants du shader. +pub const MODELS: [PrismModel; 2] = [ + PrismModel { + tri_start: 0, + tri_count: 31, + sil_start: 0, + sil_count: 25, + out_start: 67, + out_count: 9, + size: [0.63089, 1.0], + hotspot: [0.022658, 0.064647], + top: 0.0625, + height: 0.115951, + thick: 0.042945, + }, + PrismModel { + tri_start: 31, + tri_count: 130, + sil_start: 25, + sil_count: 42, + out_start: 76, + out_count: 34, + size: [0.797658, 1.0], + hotspot: [0.343388, 0.063582], + top: 0.0625, + height: 0.0822, + thick: 0.043263, + }, +]; + +pub const TRIS: [[f32; 4]; 644] = [ + [0.035429, 0.482055, 0.0, 14.0], + [0.134202, -0.125614, 0.115951, 0.000305], + [-0.004294, -0.419785, 0.0, 0.938689], + [-0.650191, 0.006652, 0.759742, 0.854994], + [0.169632, 0.356442, 0.115951, 14.0], + [0.082669, -0.090184, -0.115951, 0.590616], + [-0.138497, -0.294172, -0.115951, 0.737911], + [0.466385, -0.505659, 0.725806, 0.973449], + [0.169632, 0.356442, 0.115951, 14.0], + [0.159969, 0.080521, -0.045092, 0.381323], + [0.082669, -0.090184, -0.115951, 0.056123], + [0.461407, -0.510214, 0.725799, 0.973449], + [0.329601, 0.436963, 0.070859, 14.0], + [0.211503, 0.107362, -0.070859, 0.545724], + [-0.077301, -0.170706, -0.070859, 0.064805], + [0.49567, -0.514808, 0.699488, 0.973449], + [0.268405, 0.533589, 0.081595, 14.0], + [0.061196, -0.096626, -0.010736, 0.000916], + [-0.098773, -0.177147, 0.034356, 0.046662], + [0.247834, 0.049458, 0.967539, 0.904662], + [0.268405, 0.533589, 0.081595, 14.0], + [0.272699, 0.010736, -0.081595, 0.0], + [0.061196, -0.096626, -0.010736, 0.025193], + [0.283244, 0.073137, 0.956255, 0.527108], + [0.359663, 0.559356, 0.0, 14.0], + [0.181442, -0.015031, 0.0, 0.006043], + [-0.091258, -0.025767, 0.081595, 0.076188], + [0.076465, 0.923048, 0.37701, 0.964691], + [0.199693, 0.595859, 0.0, 14.0], + [0.068712, -0.06227, 0.081595, 0.0], + [-0.030061, -0.239417, 0.115951, 0.01445], + [-0.504461, 0.426837, 0.750553, 0.254154], + [0.035429, 0.740798, 0.0, 14.0], + [0.164264, -0.144939, 0.0, 0.0], + [0.134202, -0.384356, 0.115951, 0.010956], + [0.332572, 0.376915, 0.864483, 0.215854], + [0.035429, 0.482055, 0.0, 14.0], + [0.0, 0.258742, 0.0, 0.0], + [0.134202, -0.125614, 0.115951, 0.473533], + [-0.653777, 0.0, 0.756687, 0.964691], + [0.199693, 0.595859, 0.0, 14.0], + [0.105215, 0.22546, 0.0, 0.0], + [0.068712, -0.06227, 0.081595, 0.015991], + [-0.613882, 0.286478, 0.735581, 0.473533], + [0.304908, 0.821319, 0.0, 14.0], + [0.128834, -0.067638, 0.0, 0.0], + [-0.036503, -0.28773, 0.081595, 0.254154], + [0.133686, 0.254641, 0.957751, 0.973449], + [0.433742, 0.753681, 0.0, 14.0], + [-0.07408, -0.194325, 0.0, 0.0], + [-0.165337, -0.220092, 0.081595, 0.964691], + [0.683362, -0.260508, 0.682021, 0.964691], + [0.035429, 0.482055, -0.042945, 3.0], + [0.0, 0.258742, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [-1.0, 0.0, 0.0, 0.577584], + [0.035429, 0.740798, -0.042945, 7.0], + [0.164264, -0.144939, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.661622, 0.749838, 0.0, 0.577584], + [0.199693, 0.595859, -0.042945, 7.0], + [0.105215, 0.22546, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [-0.906183, 0.422886, 0.0, 0.577584], + [0.304908, 0.821319, -0.042945, 7.0], + [0.128834, -0.067638, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.464834, 0.885398, 0.0, 0.577584], + [0.433742, 0.753681, -0.042945, 7.0], + [-0.07408, -0.194325, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.934406, -0.35621, 0.0, 0.577584], + [0.359663, 0.559356, -0.042945, 7.0], + [0.181442, -0.015031, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.082557, 0.996586, 0.0, 0.577584], + [0.541104, 0.544325, -0.042945, 7.0], + [-0.288804, -0.278068, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.69359, -0.72037, 0.0, 0.577584], + [0.252301, 0.266258, -0.042945, 3.0], + [-0.221166, -0.203988, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.677984, -0.735077, 0.0, 0.577584], + [0.031135, 0.06227, -0.042945, 7.0], + [0.004294, 0.419785, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [-0.999948, 0.01023, 0.0, 0.577584], + [0.035429, 0.482055, -0.042945, 11.0], + [0.0, 0.258742, 0.0, 0.01033], + [0.0, 0.258742, 0.042945, 0.045182], + [-1.0, 0.0, 0.0, 0.577584], + [0.035429, 0.740798, -0.042945, 11.0], + [0.164264, -0.144939, 0.0, 0.01033], + [0.164264, -0.144939, 0.042945, 0.045182], + [0.661622, 0.749838, 0.0, 0.577584], + [0.199693, 0.595859, -0.042945, 11.0], + [0.105215, 0.22546, 0.0, 0.01033], + [0.105215, 0.22546, 0.042945, 0.045182], + [-0.906183, 0.422886, 0.0, 0.577584], + [0.304908, 0.821319, -0.042945, 11.0], + [0.128834, -0.067638, 0.0, 0.01033], + [0.128834, -0.067638, 0.042945, 0.045182], + [0.464834, 0.885398, 0.0, 0.577584], + [0.433742, 0.753681, -0.042945, 11.0], + [-0.07408, -0.194325, 0.0, 0.01033], + [-0.07408, -0.194325, 0.042945, 0.045182], + [0.934406, -0.35621, 0.0, 0.577584], + [0.359663, 0.559356, -0.042945, 11.0], + [0.181442, -0.015031, 0.0, 0.01033], + [0.181442, -0.015031, 0.042945, 0.045182], + [0.082557, 0.996586, 0.0, 0.577584], + [0.541104, 0.544325, -0.042945, 9.0], + [-0.288804, -0.278068, 0.0, 0.01033], + [-0.288804, -0.278068, 0.042945, 0.045182], + [0.69359, -0.72037, 0.0, 0.577584], + [0.252301, 0.266258, -0.042945, 11.0], + [-0.221166, -0.203988, 0.0, 0.01033], + [-0.221166, -0.203988, 0.042945, 0.045182], + [0.677984, -0.735077, 0.0, 0.577584], + [0.031135, 0.06227, -0.042945, 9.0], + [0.004294, 0.419785, 0.0, 0.01033], + [0.004294, 0.419785, 0.042945, 0.045182], + [-0.999948, 0.01023, 0.0, 0.577584], + [-0.031366, 0.229295, 0.058405, 14.0], + [0.089771, -0.137361, -0.02055, 0.445197], + [0.035692, -0.155748, -0.008653, 0.973449], + [0.216369, -0.004654, 0.976301, 0.973449], + [0.044345, 0.043263, 0.0, 6.0], + [-0.040019, 0.030284, 0.049753, 0.000305], + [0.014061, 0.048671, 0.037855, 0.964691], + [0.365607, -0.634949, 0.680566, 0.973449], + [0.004326, 0.073548, 0.049753, 14.0], + [-0.003245, -0.035692, -0.049753, 0.0], + [-0.04759, 0.004326, -0.049753, 0.955978], + [-0.580475, -0.643229, 0.499304, 0.896269], + [-0.031366, 0.229295, 0.058405, 14.0], + [-0.011897, -0.151422, -0.058405, 0.000305], + [-0.012979, 0.003245, -0.058405, 0.955978], + [-0.976245, -0.006827, 0.216564, 0.904662], + [0.058405, 0.091934, 0.037855, 14.0], + [0.017305, -0.003245, -0.037855, 0.0], + [-0.014061, -0.048671, -0.037855, 0.473533], + [0.757369, -0.522945, 0.39105, 0.904662], + [-0.031366, 0.229295, 0.058405, 14.0], + [0.107077, -0.140606, -0.058405, 0.0], + [0.089771, -0.137361, -0.02055, 0.376257], + [0.812052, 0.481346, 0.329966, 0.830777], + [-0.045426, 0.564586, 0.052998, 14.0], + [0.014061, -0.335291, 0.005408, 0.520989], + [0.001082, -0.332046, -0.052998, 0.088655], + [-0.975941, -0.037462, 0.214794, 0.973449], + [0.10924, 0.439122, 0.0, 14.0], + [-0.031366, -0.031366, 0.0, 0.132876], + [-0.154666, 0.125464, 0.052998, 0.028428], + [0.182761, -0.182761, 0.966021, 0.955978], + [0.128708, 0.463999, 0.063813, 14.0], + [-0.019468, -0.024876, -0.063813, 0.168276], + [-0.174135, 0.100587, -0.010816, 0.038209], + [-0.469238, -0.764881, 0.441331, 0.955978], + [0.201174, 0.638133, 0.0822, 14.0], + [-0.072466, -0.174135, -0.018387, 0.18117], + [-0.246601, -0.073548, -0.029203, 0.082277], + [-0.098561, -0.063844, 0.993081, 0.973449], + [0.128708, 0.463999, 0.063813, 14.0], + [0.055161, -0.146014, -0.025958, 0.246204], + [0.016224, -0.156829, -0.014061, 0.964691], + [0.305397, -0.053644, 0.950713, 0.973449], + [0.183869, 0.317985, 0.037855, 6.0], + [-0.036774, -0.045426, -0.037855, 0.0], + [-0.038937, -0.010816, 0.011897, 0.806958], + [0.374433, -0.753477, 0.540438, 0.854994], + [0.144932, 0.307169, 0.049753, 14.0], + [0.002163, -0.034611, -0.049753, 0.0], + [-0.033529, 0.0, -0.049753, 0.9131], + [-0.630237, -0.649931, 0.424725, 0.904662], + [0.10924, 0.439122, 0.0, 6.0], + [0.035692, -0.131953, 0.049753, 0.18117], + [0.002163, -0.131953, 0.0, 0.955978], + [-0.82919, -0.013593, 0.558802, 0.964691], + [0.183869, 0.317985, 0.037855, 14.0], + [0.02704, -0.004326, -0.037855, 0.0], + [0.005408, -0.037855, -0.037855, 0.514916], + [0.70062, -0.452013, 0.552101, 0.846876], + [0.210908, 0.445612, 0.0, 10.0], + [0.0, -0.131953, 0.0, 0.0], + [-0.02704, -0.127627, 0.037855, 0.439658], + [0.813734, 0.0, 0.581238, 0.871366], + [0.246601, 0.459672, 0.06165, 14.0], + [0.070303, -0.101669, -0.024876, 0.262257], + [0.034611, -0.116811, -0.015142, 0.973449], + [0.27928, -0.041605, 0.959308, 0.973449], + [0.319067, 0.319067, 0.0, 6.0], + [-0.037855, 0.023795, 0.046508, 0.0], + [-0.002163, 0.038937, 0.036774, 0.896269], + [0.437906, -0.604311, 0.665618, 0.896269], + [0.281211, 0.342862, 0.046508, 14.0], + [-0.004326, -0.031366, -0.046508, 0.0], + [-0.040019, 0.001082, -0.046508, 0.752941], + [-0.58651, -0.645161, 0.489667, 0.846876], + [0.23903, 0.45102, 0.0, 14.0], + [0.042182, -0.108158, 0.046508, 0.000305], + [0.002163, -0.107077, 0.0, 0.846876], + [-0.758098, -0.015315, 0.651961, 0.896269], + [0.316904, 0.358004, 0.036774, 14.0], + [0.024876, -0.004326, -0.036774, 0.0], + [0.002163, -0.038937, -0.036774, 0.617212], + [0.707338, -0.464191, 0.533104, 0.921584], + [0.340698, 0.474815, 0.0, 14.0], + [0.001082, -0.121137, 0.0, 0.0], + [-0.023795, -0.116811, 0.036774, 0.473533], + [0.828664, 0.007399, 0.559697, 0.930114], + [0.375309, 0.488875, 0.0, 14.0], + [0.078955, -0.08869, 0.032447, 0.223224], + [0.036774, -0.105995, 0.043263, 0.964691], + [0.071225, 0.39802, 0.914608, 0.973449], + [0.45102, 0.359085, 0.0, 6.0], + [-0.038937, 0.023795, 0.043263, 0.000305], + [0.003245, 0.0411, 0.032447, 0.879622], + [0.417852, -0.583049, 0.696744, 0.9131], + [0.412083, 0.38288, 0.043263, 14.0], + [-0.001082, -0.031366, -0.043263, 0.0], + [-0.035692, -0.001082, -0.043263, 0.745403], + [-0.574039, -0.656045, 0.489984, 0.854994], + [0.375309, 0.488875, 0.0, 14.0], + [0.036774, -0.105995, 0.043263, 0.000305], + [0.001082, -0.107077, 0.0, 0.879622], + [-0.771254, -0.00779, 0.63648, 0.896269], + [0.454264, 0.400185, 0.032447, 14.0], + [0.019468, -0.008653, -0.032447, 0.0], + [-0.003245, -0.0411, -0.032447, 0.527108], + [0.688617, -0.482032, 0.541712, 0.887922], + [0.473733, 0.603523, 0.0, 14.0], + [0.0, -0.21199, 0.0, 0.158953], + [-0.019468, -0.203337, 0.032447, 0.030716], + [0.857493, 0.0, 0.514496, 0.964691], + [0.179543, 0.466162, 0.058405, 10.0], + [-0.050834, -0.002163, 0.005408, 0.000305], + [0.021632, 0.171972, 0.023795, 0.022171], + [0.110896, -0.149888, 0.982464, 0.381323], + [0.326638, 0.830655, 0.0, 14.0], + [0.048671, -0.34178, 0.0, 0.007492], + [-0.125464, -0.192522, 0.0822, 0.054475], + [0.472493, 0.067285, 0.878762, 0.964691], + [0.326638, 0.830655, 0.0, 14.0], + [0.139524, -0.21199, 0.0, 0.0], + [0.048671, -0.34178, 0.0, 0.015213], + [0.0, 0.0, 1.0, 0.428687], + [-0.012979, 0.832818, 0.0, 14.0], + [0.339617, -0.002163, 0.0, 0.0], + [0.214153, -0.194685, 0.0822, 0.027314], + [0.002492, 0.391296, 0.920261, 0.49102], + [-0.012979, 0.832818, 0.0, 14.0], + [0.214153, -0.194685, 0.0822, 0.000305], + [-0.032447, -0.268232, 0.052998, 0.930114], + [-0.176863, 0.211325, 0.961281, 0.973449], + [-0.221724, 0.475896, 0.0, 14.0], + [0.139524, 0.055161, 0.043263, 0.520989], + [0.054079, -0.059487, 0.046508, 0.973449], + [-0.39307, 0.317373, 0.863001, 0.973449], + [-0.167645, 0.416409, 0.046508, 14.0], + [-0.056242, -0.008653, -0.046508, 0.000305], + [-0.054079, 0.059487, -0.046508, 0.955978], + [-0.638985, 0.020285, 0.768952, 0.973449], + [-0.167645, 0.416409, 0.046508, 6.0], + [0.073548, 0.015142, -0.046508, 0.0], + [0.004326, -0.035692, -0.046508, 0.768154], + [0.490917, -0.668482, 0.558688, 0.871366], + [-0.099506, 0.68356, 0.0, 14.0], + [0.017305, -0.152503, 0.043263, 0.0], + [-0.122219, -0.207664, 0.0, 0.723049], + [-0.365879, 0.215335, 0.905408, 0.854994], + [-0.099506, 0.68356, 0.0, 14.0], + [0.054079, -0.118974, 0.052998, 0.0], + [0.017305, -0.152503, 0.043263, 0.514916], + [-0.41958, 0.203352, 0.884647, 0.863157], + [-0.099506, 0.68356, 0.0, 14.0], + [0.086527, 0.149258, 0.0, 0.0], + [0.054079, -0.118974, 0.052998, 0.262257], + [-0.385563, 0.223515, 0.8952, 0.9131], + [-0.044345, 0.23254, 0.0, 7.0], + [-0.001082, 0.332046, -0.043263, 0.01033], + [-0.001082, 0.332046, 0.052998, 0.045182], + [-0.999995, -0.003257, 0.0, 0.577584], + [-0.045426, 0.564586, -0.043263, 7.0], + [-0.036774, -0.033529, 0.086527, 0.01033], + [0.0, 0.0, 0.096261, 0.045182], + [0.673754, -0.738956, 0.0, 0.577584], + [-0.0822, 0.531057, -0.043263, 7.0], + [-0.011897, -0.099506, 0.043263, 0.01033], + [0.0, 0.0, 0.086527, 0.045182], + [0.992928, -0.11872, 0.0, 0.577584], + [-0.094098, 0.431551, -0.043263, 7.0], + [-0.069221, -0.050834, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.591909, -0.806004, 0.0, 0.577584], + [-0.163319, 0.380717, -0.043263, 7.0], + [-0.060569, 0.02704, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.407651, -0.913138, 0.0, 0.577584], + [-0.223888, 0.407756, -0.043263, 7.0], + [0.002163, 0.06814, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999496, 0.03173, 0.0, 0.577584], + [-0.221724, 0.475896, -0.043263, 7.0], + [0.122219, 0.207664, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.861819, 0.507216, 0.0, 0.577584], + [-0.099506, 0.68356, -0.043263, 3.0], + [0.086527, 0.149258, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.86514, 0.501531, 0.0, 0.577584], + [-0.012979, 0.832818, -0.043263, 7.0], + [0.339617, -0.002163, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.006369, 0.99998, 0.0, 0.577584], + [0.326638, 0.830655, -0.043263, 7.0], + [0.139524, -0.21199, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.835314, 0.549773, 0.0, 0.577584], + [0.466162, 0.618665, -0.043263, 3.0], + [0.007571, -0.015142, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.894427, 0.447214, 0.0, 0.577584], + [0.473733, 0.603523, -0.043263, 7.0], + [0.0, -0.21199, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [1.0, 0.0, 0.0, 0.577584], + [0.473733, 0.391533, -0.043263, 7.0], + [-0.022713, -0.032447, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.819232, -0.573462, 0.0, 0.577584], + [0.45102, 0.359085, -0.043263, 7.0], + [-0.040019, -0.007571, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.185892, -0.98257, 0.0, 0.577584], + [0.411001, 0.351514, -0.043263, 7.0], + [-0.034611, 0.030284, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.658505, -0.752577, 0.0, 0.577584], + [0.376391, 0.381799, -0.043263, 7.0], + [-0.001082, 0.107077, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999949, -0.0101, 0.0, 0.577584], + [0.375309, 0.488875, -0.043263, 7.0], + [-0.034611, -0.014061, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.376377, -0.926467, 0.0, 0.577584], + [0.340698, 0.474815, -0.043263, 7.0], + [0.001082, -0.121137, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.99996, 0.008928, 0.0, 0.577584], + [0.34178, 0.353677, -0.043263, 7.0], + [-0.022713, -0.034611, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.836048, -0.548657, 0.0, 0.577584], + [0.319067, 0.319067, -0.043263, 7.0], + [-0.042182, -0.007571, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.176664, -0.984271, 0.0, 0.577584], + [0.276885, 0.311496, -0.043263, 7.0], + [-0.035692, 0.032447, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.672673, -0.73994, 0.0, 0.577584], + [0.241193, 0.343943, -0.043263, 7.0], + [-0.002163, 0.107077, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999796, -0.020198, 0.0, 0.577584], + [0.23903, 0.45102, -0.043263, 7.0], + [-0.028121, -0.005408, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.188847, -0.982006, 0.0, 0.577584], + [0.210908, 0.445612, -0.043263, 7.0], + [0.0, -0.131953, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [1.0, 0.0, 0.0, 0.577584], + [0.210908, 0.313659, -0.043263, 7.0], + [-0.021632, -0.033529, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.840297, -0.542127, 0.0, 0.577584], + [0.189277, 0.28013, -0.043263, 7.0], + [-0.042182, -0.007571, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.176664, -0.984271, 0.0, 0.577584], + [0.147095, 0.272559, -0.043263, 7.0], + [-0.035692, 0.034611, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.696146, -0.7179, 0.0, 0.577584], + [0.111403, 0.307169, -0.043263, 7.0], + [-0.002163, 0.131953, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999866, -0.016391, 0.0, 0.577584], + [0.10924, 0.439122, -0.043263, 7.0], + [-0.031366, -0.031366, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.707107, -0.707107, 0.0, 0.577584], + [0.077874, 0.407756, -0.043263, 7.0], + [-0.002163, -0.319067, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.999977, -0.006779, 0.0, 0.577584], + [0.075711, 0.08869, -0.043263, 7.0], + [-0.031366, -0.045426, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.822897, -0.568191, 0.0, 0.577584], + [0.044345, 0.043263, -0.043263, 7.0], + [-0.043263, -0.005408, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.124035, -0.992278, 0.0, 0.577584], + [0.001082, 0.037855, -0.043263, 7.0], + [-0.044345, 0.040019, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.669964, -0.742393, 0.0, 0.577584], + [-0.043263, 0.077874, -0.043263, 7.0], + [-0.001082, 0.154666, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999976, -0.006993, 0.0, 0.577584], + [0.044345, 0.043263, 0.0, 10.0], + [-0.043263, -0.005408, 0.0, 0.000305], + [-0.040019, 0.030284, 0.049753, 0.964691], + [0.101434, -0.811469, 0.575525, 0.973449], + [-0.031366, 0.229295, 0.058405, 14.0], + [0.035692, -0.155748, -0.008653, 0.000305], + [-0.011897, -0.151422, -0.058405, 0.955978], + [-0.716533, -0.20131, 0.667874, 0.904662], + [-0.031366, 0.229295, 0.058405, 14.0], + [0.10924, 0.178461, -0.058405, 0.0], + [0.107077, -0.140606, -0.058405, 0.376257], + [0.475583, -0.003224, 0.879665, 0.830777], + [-0.045426, 0.564586, 0.052998, 14.0], + [0.1233, -0.156829, -0.052998, 0.520989], + [0.014061, -0.335291, 0.005408, 0.088655], + [0.429355, 0.032562, 0.902549, 0.973449], + [0.183869, 0.317985, 0.037855, 10.0], + [0.005408, -0.037855, -0.037855, 0.0], + [-0.036774, -0.045426, -0.037855, 0.806958], + [0.124328, -0.692684, 0.710445, 0.854994], + [0.10924, 0.439122, 0.0, 10.0], + [0.019468, 0.024876, 0.063813, 0.18117], + [0.035692, -0.131953, 0.049753, 0.955978], + [-0.933936, -0.126584, 0.334276, 0.964691], + [0.183869, 0.317985, 0.037855, 10.0], + [-0.055161, 0.146014, 0.025958, 0.0], + [0.02704, 0.127627, -0.037855, 0.439658], + [0.623841, 0.097823, 0.775405, 0.871366], + [0.128708, 0.463999, 0.063813, 14.0], + [0.050834, 0.002163, -0.005408, 0.0], + [0.0822, -0.018387, -0.063813, 0.439658], + [0.07859, -0.92643, 0.368172, 0.871366], + [0.319067, 0.319067, 0.0, 10.0], + [-0.042182, -0.007571, 0.0, 0.0], + [-0.037855, 0.023795, 0.046508, 0.896269], + [0.1483, -0.826245, 0.54344, 0.896269], + [0.23903, 0.45102, 0.0, 15.0], + [0.007571, 0.008653, 0.06165, 0.000305], + [0.042182, -0.108158, 0.046508, 0.846876], + [-0.94107, -0.299262, 0.157572, 0.896269], + [0.316904, 0.358004, 0.036774, 14.0], + [-0.070303, 0.101669, 0.024876, 0.0], + [0.023795, 0.116811, -0.036774, 0.473533], + [0.523424, 0.157027, 0.837478, 0.930114], + [0.246601, 0.459672, 0.06165, 14.0], + [0.030284, 0.024876, -0.003245, 0.0], + [0.094098, 0.015142, -0.06165, 0.473533], + [0.518869, -0.545855, 0.657888, 0.930114], + [0.45102, 0.359085, 0.0, 10.0], + [-0.040019, -0.007571, 0.0, 0.000305], + [-0.038937, 0.023795, 0.043263, 0.879622], + [0.151736, -0.802035, 0.577682, 0.9131], + [0.454264, 0.400185, 0.032447, 12.0], + [-0.078955, 0.08869, -0.032447, 0.158953], + [0.019468, 0.203337, -0.032447, 0.030716], + [-0.201684, 0.173144, 0.964025, 0.964691], + [0.375309, 0.488875, 0.0, 10.0], + [0.090853, 0.12979, 0.0, 0.158953], + [0.098424, 0.114648, 0.0, 0.030716], + [0.0, 0.0, 1.0, 0.964691], + [0.340698, 0.474815, 0.0, 14.0], + [-0.063813, 0.009734, 0.058405, 0.000305], + [0.034611, 0.014061, 0.0, 0.022171], + [0.3276, -0.806402, 0.492334, 0.381323], + [0.276885, 0.484549, 0.058405, 10.0], + [-0.030284, -0.024876, 0.003245, 0.000305], + [-0.097342, -0.018387, 0.0, 0.022171], + [-0.03153, 0.166923, 0.985466, 0.381323], + [0.201174, 0.638133, 0.0822, 8.0], + [0.075711, -0.153585, -0.023795, 0.000305], + [-0.021632, -0.171972, -0.023795, 0.022171], + [0.026497, -0.140281, 0.989757, 0.381323], + [0.276885, 0.484549, 0.058405, 14.0], + [-0.075711, 0.153585, 0.023795, 0.000305], + [0.098424, 0.004326, -0.058405, 0.022171], + [0.50316, 0.115345, 0.856461, 0.381323], + [0.246601, 0.459672, 0.06165, 11.0], + [-0.007571, -0.008653, -0.06165, 0.000305], + [-0.035692, -0.014061, -0.06165, 0.022171], + [0.187618, -0.975617, 0.113888, 0.381323], + [0.210908, 0.445612, 0.0, 10.0], + [-0.031366, 0.02055, 0.058405, 0.000305], + [0.035692, 0.014061, 0.06165, 0.022171], + [-0.105864, -0.954447, 0.278971, 0.381323], + [-0.167645, 0.416409, 0.046508, 14.0], + [0.004326, -0.035692, -0.046508, 0.000305], + [-0.056242, -0.008653, -0.046508, 0.955978], + [-0.339782, -0.761112, 0.552501, 0.973449], + [-0.167645, 0.416409, 0.046508, 10.0], + [0.085445, 0.114648, -0.003245, 0.0], + [0.073548, 0.015142, -0.046508, 0.768154], + [0.548356, -0.38771, 0.740936, 0.871366], + [-0.044345, 0.23254, 0.0, 3.0], + [0.0, 0.0, -0.043263, 0.01033], + [-0.001082, 0.332046, -0.043263, 0.045182], + [-0.999995, -0.003257, 0.0, 0.577584], + [-0.045426, 0.564586, -0.043263, 11.0], + [-0.036774, -0.033529, 0.0, 0.01033], + [-0.036774, -0.033529, 0.086527, 0.045182], + [0.673754, -0.738956, 0.0, 0.577584], + [-0.0822, 0.531057, -0.043263, 11.0], + [-0.011897, -0.099506, 0.0, 0.01033], + [-0.011897, -0.099506, 0.043263, 0.045182], + [0.992928, -0.11872, 0.0, 0.577584], + [-0.094098, 0.431551, -0.043263, 11.0], + [-0.069221, -0.050834, 0.0, 0.01033], + [-0.069221, -0.050834, 0.043263, 0.045182], + [0.591909, -0.806004, 0.0, 0.577584], + [-0.163319, 0.380717, -0.043263, 11.0], + [-0.060569, 0.02704, 0.0, 0.01033], + [-0.060569, 0.02704, 0.043263, 0.045182], + [-0.407651, -0.913138, 0.0, 0.577584], + [-0.223888, 0.407756, -0.043263, 11.0], + [0.002163, 0.06814, 0.0, 0.01033], + [0.002163, 0.06814, 0.043263, 0.045182], + [-0.999496, 0.03173, 0.0, 0.577584], + [-0.221724, 0.475896, -0.043263, 9.0], + [0.122219, 0.207664, 0.0, 0.01033], + [0.122219, 0.207664, 0.043263, 0.045182], + [-0.861819, 0.507216, 0.0, 0.577584], + [-0.099506, 0.68356, -0.043263, 11.0], + [0.086527, 0.149258, 0.0, 0.01033], + [0.086527, 0.149258, 0.043263, 0.045182], + [-0.86514, 0.501531, 0.0, 0.577584], + [-0.012979, 0.832818, -0.043263, 11.0], + [0.339617, -0.002163, 0.0, 0.01033], + [0.339617, -0.002163, 0.043263, 0.045182], + [0.006369, 0.99998, 0.0, 0.577584], + [0.326638, 0.830655, -0.043263, 9.0], + [0.139524, -0.21199, 0.0, 0.01033], + [0.139524, -0.21199, 0.043263, 0.045182], + [0.835314, 0.549773, 0.0, 0.577584], + [0.466162, 0.618665, -0.043263, 11.0], + [0.007571, -0.015142, 0.0, 0.01033], + [0.007571, -0.015142, 0.043263, 0.045182], + [0.894427, 0.447214, 0.0, 0.577584], + [0.473733, 0.603523, -0.043263, 11.0], + [0.0, -0.21199, 0.0, 0.01033], + [0.0, -0.21199, 0.043263, 0.045182], + [1.0, 0.0, 0.0, 0.577584], + [0.473733, 0.391533, -0.043263, 11.0], + [-0.022713, -0.032447, 0.0, 0.01033], + [-0.022713, -0.032447, 0.043263, 0.045182], + [0.819232, -0.573462, 0.0, 0.577584], + [0.45102, 0.359085, -0.043263, 11.0], + [-0.040019, -0.007571, 0.0, 0.01033], + [-0.040019, -0.007571, 0.043263, 0.045182], + [0.185892, -0.98257, 0.0, 0.577584], + [0.411001, 0.351514, -0.043263, 11.0], + [-0.034611, 0.030284, 0.0, 0.01033], + [-0.034611, 0.030284, 0.043263, 0.045182], + [-0.658505, -0.752577, 0.0, 0.577584], + [0.376391, 0.381799, -0.043263, 11.0], + [-0.001082, 0.107077, 0.0, 0.01033], + [-0.001082, 0.107077, 0.043263, 0.045182], + [-0.999949, -0.0101, 0.0, 0.577584], + [0.375309, 0.488875, -0.043263, 11.0], + [-0.034611, -0.014061, 0.0, 0.01033], + [-0.034611, -0.014061, 0.043263, 0.045182], + [0.376377, -0.926467, 0.0, 0.577584], + [0.340698, 0.474815, -0.043263, 11.0], + [0.001082, -0.121137, 0.0, 0.01033], + [0.001082, -0.121137, 0.043263, 0.045182], + [0.99996, 0.008928, 0.0, 0.577584], + [0.34178, 0.353677, -0.043263, 11.0], + [-0.022713, -0.034611, 0.0, 0.01033], + [-0.022713, -0.034611, 0.043263, 0.045182], + [0.836048, -0.548657, 0.0, 0.577584], + [0.319067, 0.319067, -0.043263, 11.0], + [-0.042182, -0.007571, 0.0, 0.01033], + [-0.042182, -0.007571, 0.043263, 0.045182], + [0.176664, -0.984271, 0.0, 0.577584], + [0.276885, 0.311496, -0.043263, 11.0], + [-0.035692, 0.032447, 0.0, 0.01033], + [-0.035692, 0.032447, 0.043263, 0.045182], + [-0.672673, -0.73994, 0.0, 0.577584], + [0.241193, 0.343943, -0.043263, 11.0], + [-0.002163, 0.107077, 0.0, 0.01033], + [-0.002163, 0.107077, 0.043263, 0.045182], + [-0.999796, -0.020198, 0.0, 0.577584], + [0.23903, 0.45102, -0.043263, 11.0], + [-0.028121, -0.005408, 0.0, 0.01033], + [-0.028121, -0.005408, 0.043263, 0.045182], + [0.188847, -0.982006, 0.0, 0.577584], + [0.210908, 0.445612, -0.043263, 11.0], + [0.0, -0.131953, 0.0, 0.01033], + [0.0, -0.131953, 0.043263, 0.045182], + [1.0, 0.0, 0.0, 0.577584], + [0.210908, 0.313659, -0.043263, 11.0], + [-0.021632, -0.033529, 0.0, 0.01033], + [-0.021632, -0.033529, 0.043263, 0.045182], + [0.840297, -0.542127, 0.0, 0.577584], + [0.189277, 0.28013, -0.043263, 11.0], + [-0.042182, -0.007571, 0.0, 0.01033], + [-0.042182, -0.007571, 0.043263, 0.045182], + [0.176664, -0.984271, 0.0, 0.577584], + [0.147095, 0.272559, -0.043263, 11.0], + [-0.035692, 0.034611, 0.0, 0.01033], + [-0.035692, 0.034611, 0.043263, 0.045182], + [-0.696146, -0.7179, 0.0, 0.577584], + [0.111403, 0.307169, -0.043263, 11.0], + [-0.002163, 0.131953, 0.0, 0.01033], + [-0.002163, 0.131953, 0.043263, 0.045182], + [-0.999866, -0.016391, 0.0, 0.577584], + [0.10924, 0.439122, -0.043263, 11.0], + [-0.031366, -0.031366, 0.0, 0.01033], + [-0.031366, -0.031366, 0.043263, 0.045182], + [0.707107, -0.707107, 0.0, 0.577584], + [0.077874, 0.407756, -0.043263, 11.0], + [-0.002163, -0.319067, 0.0, 0.01033], + [-0.002163, -0.319067, 0.043263, 0.045182], + [0.999977, -0.006779, 0.0, 0.577584], + [0.075711, 0.08869, -0.043263, 11.0], + [-0.031366, -0.045426, 0.0, 0.01033], + [-0.031366, -0.045426, 0.043263, 0.045182], + [0.822897, -0.568191, 0.0, 0.577584], + [0.044345, 0.043263, -0.043263, 11.0], + [-0.043263, -0.005408, 0.0, 0.01033], + [-0.043263, -0.005408, 0.043263, 0.045182], + [0.124035, -0.992278, 0.0, 0.577584], + [0.001082, 0.037855, -0.043263, 11.0], + [-0.044345, 0.040019, 0.0, 0.01033], + [-0.044345, 0.040019, 0.043263, 0.045182], + [-0.669964, -0.742393, 0.0, 0.577584], + [-0.043263, 0.077874, -0.043263, 9.0], + [-0.001082, 0.154666, 0.0, 0.01033], + [-0.001082, 0.154666, 0.043263, 0.045182], + [-0.999976, -0.006993, 0.0, 0.577584], +]; + +pub const POLYS: [[f32; 2]; 110] = [ + [0.006442, -0.002147], + [-0.001074, 0.001074], + [-0.004294, 0.007515], + [-0.004294, 0.806288], + [-0.001074, 0.809509], + [0.004294, 0.809509], + [0.022546, 0.796626], + [0.185736, 0.655982], + [0.281288, 0.86319], + [0.286656, 0.870706], + [0.293098, 0.871779], + [0.465951, 0.781595], + [0.478834, 0.773006], + [0.478834, 0.763344], + [0.407975, 0.592638], + [0.45092, 0.586196], + [0.601227, 0.573313], + [0.606595, 0.569018], + [0.606595, 0.564724], + [0.601227, 0.556135], + [0.50138, 0.453067], + [0.425153, 0.37684], + [0.289877, 0.246933], + [0.081595, 0.057975], + [0.013957, 0.0], + [-0.011897, 0.0], + [-0.055161, 0.037855], + [-0.081119, 0.063813], + [-0.083282, 0.06814], + [-0.084363, 0.38288], + [-0.089771, 0.38288], + [-0.158993, 0.33529], + [-0.166564, 0.336372], + [-0.250927, 0.379635], + [-0.261743, 0.388288], + [-0.263906, 0.461836], + [-0.262824, 0.486712], + [-0.173053, 0.638133], + [-0.044345, 0.860939], + [-0.036774, 0.867429], + [-0.024876, 0.871755], + [0.33529, 0.871755], + [0.349351, 0.867429], + [0.363412, 0.853368], + [0.513752, 0.619747], + [0.513752, 0.379635], + [0.466162, 0.322311], + [0.461836, 0.320148], + [0.399104, 0.311496], + [0.393696, 0.313659], + [0.374227, 0.332046], + [0.343943, 0.288782], + [0.336372, 0.282293], + [0.269314, 0.271477], + [0.263906, 0.27364], + [0.243356, 0.292027], + [0.21199, 0.244438], + [0.207664, 0.241193], + [0.149258, 0.231459], + [0.138443, 0.231459], + [0.118974, 0.248764], + [0.116811, 0.222806], + [0.117892, 0.162237], + [0.115729, 0.076792], + [0.06814, 0.011897], + [0.062732, 0.007571], + [0.005408, 0.0], + [0.031135, 0.06227], + [0.252301, 0.266258], + [0.541104, 0.544325], + [0.359663, 0.559356], + [0.433742, 0.753681], + [0.304908, 0.821319], + [0.199693, 0.595859], + [0.035429, 0.740798], + [0.035429, 0.482055], + [-0.043263, 0.077874], + [0.001082, 0.037855], + [0.044345, 0.043263], + [0.075711, 0.08869], + [0.077874, 0.407756], + [0.10924, 0.439122], + [0.111403, 0.307169], + [0.147095, 0.272559], + [0.189277, 0.28013], + [0.210909, 0.313659], + [0.210909, 0.445612], + [0.23903, 0.45102], + [0.241193, 0.343943], + [0.276885, 0.311496], + [0.319067, 0.319067], + [0.34178, 0.353677], + [0.340698, 0.474815], + [0.375309, 0.488875], + [0.376391, 0.381799], + [0.411001, 0.351514], + [0.45102, 0.359085], + [0.473733, 0.391533], + [0.473733, 0.603523], + [0.466162, 0.618665], + [0.326638, 0.830655], + [-0.012979, 0.832818], + [-0.099506, 0.68356], + [-0.221724, 0.475896], + [-0.223888, 0.407756], + [-0.163319, 0.380717], + [-0.094098, 0.431551], + [-0.0822, 0.531057], + [-0.045426, 0.564586], + [-0.044345, 0.23254], +]; diff --git a/crates/compositor/src/sculpt.rs b/crates/compositor/src/sculpt.rs index 612c2c65f..551f43776 100644 --- a/crates/compositor/src/sculpt.rs +++ b/crates/compositor/src/sculpt.rs @@ -1,9 +1,10 @@ //! Curseurs sculptés : la flèche et la main des thèmes d'origine, modelées en volumes dans les //! shaders (mode 15, `sculpt_proto`) au lieu d'extruder leur PNG — voxels (Pixel Candy), pièces -//! cerclées du trait de leur dessin (Studio Ink, Pop Coral, Star Sprout). Prism Glow n'en a pas : -//! son dessin à facettes est extrudé comme tout sprite. Ce module en tient ce que la géométrie -//! doit savoir côté CPU : l'identifiant que lit le shader, et la boîte du modèle (`SpriteShape`) -//! qui pose le hotspot, règle la garde au sol et borne la boîte de dessin. +//! cerclées du trait de leur dessin (Studio Ink, Pop Coral, Star Sprout). Prism Glow est à part : +//! un MAILLAGE tracé sur son dessin à facettes, un cristal lancé de rayons triangle par triangle +//! dans son serti marine (`prism_mesh`). Ce module en tient ce que la géométrie doit savoir côté +//! CPU : l'identifiant que lit le shader, et la boîte du modèle (`SpriteShape`) qui pose le +//! hotspot, règle la garde au sol et borne la boîte de dessin. //! //! Les formes sont écrites dans le repère du PROTOTYPE où elles ont été dessinées : hauteur du //! curseur 1, x à droite, y VERS LE HAUT, z vers la caméra, l'écran en z = 0. Les constantes @@ -43,9 +44,8 @@ const BOX_CORAL_HAND: [f32; 3] = [-0.38, 0.62, 0.04]; const BOX_SPROUT_ARROW: [f32; 3] = [-0.08, 0.87, 0.03]; const BOX_SPROUT_HAND: [f32; 3] = [-0.33, 0.56, 0.03]; -/// Dans l'ordre des identifiants du shader. Prism Glow y garde sa place, sans modèle. +/// Dans l'ordre des identifiants du shader. const THEMES: [&str; 5] = ["studio-ink", "prism-glow", "pop-coral", "pixel-candy", "star-sprout"]; -const EXTRUDED: &str = "prism-glow"; /// La boîte du modèle (x0, x1, haut) dans le prototype. fn model_box(theme: usize, arrow: bool) -> [f32; 3] { @@ -63,16 +63,28 @@ fn model_box(theme: usize, arrow: bool) -> [f32; 3] { } /// Le curseur sculpté que nomme la scène (`"/<état>"`, cf. `resolveCursorSprites`), sous -/// la forme que le mode 15 attend. `None` pour un nom inconnu ou un thème sans modèle (Prism Glow, -/// qu'un ancien projet peut encore nommer) : l'appelant extrude le sprite. +/// la forme que le mode 15 attend. `None` pour un nom inconnu : l'appelant extrude le sprite. pub fn sculpted_shape(name: &str) -> Option { let (theme, state) = name.split_once('/')?; - let theme = THEMES.iter().position(|t| *t == theme && *t != EXTRUDED)?; + let theme = THEMES.iter().position(|t| *t == theme)?; let arrow = match state { "arrow" => true, "pointer" => false, _ => return None, }; + let sculpt = 1 + 2 * theme as u32 + u32::from(!arrow); + if THEMES[theme] == "prism-glow" { + // Le maillage est déjà dans le repère du modèle : sa boîte, son dessus et son dessous. + let m = &crate::prism_mesh::MODELS[usize::from(!arrow)]; + return Some(SpriteShape { + size: m.size, + hotspot: m.hotspot, + top: m.top, + max_height: m.height, + thick: m.thick, + sculpt, + }); + } let [x0, x1, y1] = model_box(theme, arrow); let (zref, z_high) = if arrow { (ZREF_ARROW, Z_HIGH_ARROW) } else { (ZREF_HAND, Z_HIGH_HAND) }; let s = SCULPT_SCALE; @@ -84,7 +96,7 @@ pub fn sculpted_shape(name: &str) -> Option { top: y1 * s, max_height: (z_high - zref) * s, thick: (zref - Z_LOW) * s, - sculpt: 1 + 2 * theme as u32 + u32::from(!arrow), + sculpt, }) } @@ -108,29 +120,28 @@ mod tests { #[test] fn each_theme_arrow_and_hand_has_its_own_id_in_shader_order() { for (k, name) in NAMES.iter().enumerate() { - match sculpted_shape(name) { - Some(shape) => assert_eq!(shape.sculpt, k as u32 + 1, "{name}"), - None => assert!(name.starts_with(EXTRUDED), "{name} : pas de modèle"), - } + let shape = sculpted_shape(name).expect(name); + assert_eq!(shape.sculpt, k as u32 + 1, "{name}"); } } #[test] fn other_states_and_themes_keep_the_extruded_sprite() { - for name in ["default/arrow", "pop-coral/text", "pop-coral", "", "pop-coral/arrow/x", "prism-glow/arrow"] { + for name in ["default/arrow", "pop-coral/text", "pop-coral", "", "pop-coral/arrow/x", "prism-glow/text"] { assert!(sculpted_shape(name).is_none(), "{name}"); } } /// Le hotspot est dans la boîte, sous le haut de la silhouette ; le modèle a une épaisseur et /// une hauteur ; la boîte a la hauteur du sprite, son plus grand côté, comme le veut le mode 15. + /// Le serti de Prism Glow est le plus mince (0,043) : c'est son épaisseur dans le dessin. #[test] fn the_box_holds_the_hotspot_and_the_silhouette_top() { - for name in NAMES.into_iter().filter(|n| !n.starts_with(EXTRUDED)) { + for name in NAMES { let s = sculpted_shape(name).unwrap(); assert!((0.0..1.0).contains(&s.hotspot[0]) && (0.0..1.0).contains(&s.hotspot[1]), "{name}"); assert!(s.top <= s.hotspot[1] + 1e-6, "{name} : haut de silhouette sous le hotspot"); - assert!(s.thick > 0.05 && s.max_height > 0.0, "{name}"); + assert!(s.thick > 0.04 && s.max_height > 0.0, "{name}"); assert!(s.size[0] < 1.0 && s.size[1] == 1.0, "{name} : le plus grand côté n'est pas 1"); } } @@ -150,6 +161,7 @@ mod tests { ("SCULPT_ZREF_HAND", ZREF_HAND), ("SCULPT_HOVER", Z_LOW), ("SCULPT_VOX", VOX), + ("PRISM_BEVEL", crate::prism_mesh::BEVEL), ] { let line = src .lines() @@ -212,4 +224,70 @@ mod tests { } } } + + /// Le maillage de Prism Glow tient dans sa boîte (le shader n'y lance de rayons que là), son + /// dessous est à l'épaisseur annoncée (la garde au sol en dépend) et son sommet à la hauteur. + #[test] + fn the_prism_boxes_hold_their_mesh() { + use crate::prism_mesh::{MODELS, POLYS, TRIS}; + for (k, name) in ["prism-glow/arrow", "prism-glow/pointer"].iter().enumerate() { + let s = sculpted_shape(name).unwrap(); + let m = &MODELS[k]; + let [x0, y0] = [-s.hotspot[0] * s.size[0], -s.hotspot[1] * s.size[1]]; + let inside = |x: f32, y: f32| x >= x0 && x <= x0 + s.size[0] && y >= y0 && y <= y0 + s.size[1]; + let (mut zlo, mut zhi) = (f32::MAX, f32::MIN); + for t in TRIS[4 * m.tri_start..4 * (m.tri_start + m.tri_count)].chunks(4) { + for e in [[0.0; 3], [t[1][0], t[1][1], t[1][2]], [t[2][0], t[2][1], t[2][2]]] { + let [x, y, z] = [t[0][0] + e[0], t[0][1] + e[1], t[0][2] + e[2]]; + assert!(inside(x, y), "{name} : sommet ({x}, {y}) hors de la boîte"); + zlo = zlo.min(z); + zhi = zhi.max(z); + } + } + let sil = &POLYS[m.sil_start..m.sil_start + m.sil_count]; + let out = &POLYS[m.out_start..m.out_start + m.out_count]; + for p in sil.iter().chain(out) { + assert!(inside(p[0], p[1]), "{name} : contour ({}, {}) hors de la boîte", p[0], p[1]); + } + assert!((zlo + s.thick).abs() < 1e-5, "{name} : dessous {zlo}, épaisseur {}", s.thick); + assert!((zhi - s.max_height).abs() < 1e-5, "{name} : sommet {zhi}, hauteur {}", s.max_height); + } + } + + /// Les trois shaders portent le maillage de `prism_mesh.rs` tel quel : triangles, polygones, + /// et les débuts et longueurs de chaque modèle dans ces tables. Le WGSL lit triangles et + /// polygones dans un uniform rempli de `prism_mesh.rs` (`compositor_linux.rs`) : il n'en + /// déclare que la taille. + #[test] + fn the_shaders_carry_the_prism_mesh() { + use crate::prism_mesh::{PrismModel, MODELS, POLYS, TRIS}; + let per_model = |f: fn(&PrismModel) -> usize| MODELS.iter().map(|m| f(m) as f32).collect::>(); + let want: [(&str, Vec); 8] = [ + ("PRISM_TRIS", TRIS.iter().flatten().copied().collect()), + ("PRISM_POLY", POLYS.iter().flatten().copied().collect()), + ("PRISM_TRI_START", per_model(|m| m.tri_start)), + ("PRISM_TRI_COUNT", per_model(|m| m.tri_count)), + ("PRISM_SIL_START", per_model(|m| m.sil_start)), + ("PRISM_SIL_COUNT", per_model(|m| m.sil_count)), + ("PRISM_OUT_START", per_model(|m| m.out_start)), + ("PRISM_OUT_COUNT", per_model(|m| m.out_count)), + ]; + let wgsl = include_str!("vk_shaders/layer.wgsl"); + for field in [format!("tris: array, {}>", TRIS.len()), format!("poly: array, {}>", POLYS.len())] { + assert!(wgsl.contains(&field), "vk_shaders/layer.wgsl : `{field}` attendu dans PrismMesh"); + } + for (file, src, tables) in [ + ("shaders.hlsl", include_str!("shaders.hlsl"), &want[..]), + ("shaders.metal", include_str!("shaders.metal"), &want[..]), + ("vk_shaders/layer.wgsl", wgsl, &want[2..]), + ] { + for (name, values) in tables { + let got = table(src, name); + assert_eq!(got.len(), values.len(), "{file} : {name}, nombre de valeurs"); + for (i, (g, w)) in got.iter().zip(values).enumerate() { + assert!((g - w).abs() <= 1e-6 + 1e-6 * w.abs(), "{file} : {name}[{i}] = {g} au lieu de {w}"); + } + } + } + } } diff --git a/crates/compositor/src/shaders.hlsl b/crates/compositor/src/shaders.hlsl index 16c62192d..3d149ab64 100644 --- a/crates/compositor/src/shaders.hlsl +++ b/crates/compositor/src/shaders.hlsl @@ -14,7 +14,7 @@ cbuffer Layer : register(b0) float4 dst_prev; // dst à la frame précédente ; modes 13 et 15 : rect de clip ; mode 17 : angle du socle, rayon et recouvrement de l'ouverture, pénombre de l'ombre float4 mb; // mb.x = nombre de taps de motion blur (1 = désactivé) ; modes 15 et 17 : demi-taille du plan, translation ; mode 18 : .z = 1 si le plan est incliné, .w = 1 si son warp est projectif float4 trail_a; // mode 8 : coins TL, TR du plan à la frame précédente (px locaux, comme fx) ; mode 18 incliné : en fractions de sortie - float4 trail_b; // mode 8 : coins BR, BL du plan à la frame précédente (comme src_prev) ; mode 18 incliné : en fractions de sortie + float4 trail_b; // mode 8 : coins BR, BL du plan à la frame précédente (comme src_prev) ; mode 18 incliné : en fractions de sortie ; mode 15 : la coupe du plan en uv de l'écran (u0, v0, u1, v1), que réfracte le cristal de Prism Glow float4 trail_mb; // mode 8 : x = taps, y = force du flou de mouvement (ceux du mode 0) ; mode 18 incliné : le `mb` du mode 8 (profondeur de champ), et `color.xy` sa lampe ; 0 ailleurs }; // Mode 15 (curseur modélisé) : le détail des emplacements est dans `frame_geometry.rs`, en tête @@ -81,6 +81,12 @@ float3 sample_yuv(float2 uv) return yuv709_limited(y, cbcr); } +// La même, au niveau 0 : lisible dans une boucle ou une branche (le cristal du mode 15). +float3 sample_yuv_level(float2 uv) +{ + return yuv709_limited(texY.SampleLevel(samp, uv, 0.0), texUV.SampleLevel(samp, uv, 0.0)); +} + // SDF segment à bouts ronds — la primitive des flèches d'annotation, dont les tracés SVG sont // trois segments `stroke-linecap="round"` (cf. ArrowSvgs.tsx). float sd_segment(float2 p, float2 a, float2 b) @@ -589,8 +595,7 @@ static const float MODEL_CONTACT_ALPHA = 0.5; // 1 + 2 × thème + forme ; thèmes 0 Studio Ink, 1 Prism Glow, 2 Pop Coral, 3 Pixel Candy, // 4 Star Sprout ; formes 0 flèche, 1 main. Id des matières : 1 corps, 2 bande, calque ou // manchette, 3 tirets, calque corail ou étoile, 4 feuilles, 5 trait des thèmes cerclés (et -// yeux), 6 cubes de Pixel Candy. Prism Glow n'a pas de modèle : son dessin est extrudé, comme un -// sprite (id 0). +// yeux), 6 cubes de Pixel Candy, 8 serti de Prism Glow, 9 son cristal (plus bas, `prism_*`). static const float SCULPT_SCALE = 0.85; static const float SCULPT_HOVER = 0.05; static const float SCULPT_VOX = 0.0625; @@ -1043,6 +1048,880 @@ float s_pixel_outline(float2 p, int shape) return d; } +// ---- Prism Glow : un cristal en MAILLAGE ---- +// Les facettes tracées sur l'art 2D du thème (`design/cursors/prism-glow/model`), en triangles dans +// le repère du MODÈLE lui-même (unité = côté du sprite 128 px, origine au hotspot, y vers le bas, +// dessus du serti en z = 0). Le serti marine est la silhouette de l'art extrudée sous z = 0, un +// champ de distance comme les autres sculptés : il donne la couverture, l'ombre portée et l'ombre +// de contact. Le cristal est lancé de rayons triangle par triangle : réfraction par canal +// (dispersion), réflexions totales internes, sortie par son fond plat vers l'enregistrement, lu +// sous le curseur (t0/t1, `trail_b`). Tables : miroir de `prism_mesh.rs` ; un triangle = quatre +// float4 (v0, drapeaux), (e1, r), (e2, g), (normale sortante, b) ; drapeaux = paroi (1) + 2 × +// arêtes réelles (bit k : l'arête opposée au sommet k). +// prism mesh: generated by design/cursors/prism-glow/model/export_compositor.py +static const int PRISM_TRI_START[2] = { 0, 31 }; +static const int PRISM_TRI_COUNT[2] = { 31, 130 }; +static const int PRISM_SIL_START[2] = { 0, 25 }; +static const int PRISM_SIL_COUNT[2] = { 25, 42 }; +static const int PRISM_OUT_START[2] = { 67, 76 }; +static const int PRISM_OUT_COUNT[2] = { 9, 34 }; +static const float4 PRISM_TRIS[644] = { + float4(0.035429, 0.482055, 0.0, 14.0), + float4(0.134202, -0.125614, 0.115951, 0.000305), + float4(-0.004294, -0.419785, 0.0, 0.938689), + float4(-0.650191, 0.006652, 0.759742, 0.854994), + float4(0.169632, 0.356442, 0.115951, 14.0), + float4(0.082669, -0.090184, -0.115951, 0.590616), + float4(-0.138497, -0.294172, -0.115951, 0.737911), + float4(0.466385, -0.505659, 0.725806, 0.973449), + float4(0.169632, 0.356442, 0.115951, 14.0), + float4(0.159969, 0.080521, -0.045092, 0.381323), + float4(0.082669, -0.090184, -0.115951, 0.056123), + float4(0.461407, -0.510214, 0.725799, 0.973449), + float4(0.329601, 0.436963, 0.070859, 14.0), + float4(0.211503, 0.107362, -0.070859, 0.545724), + float4(-0.077301, -0.170706, -0.070859, 0.064805), + float4(0.49567, -0.514808, 0.699488, 0.973449), + float4(0.268405, 0.533589, 0.081595, 14.0), + float4(0.061196, -0.096626, -0.010736, 0.000916), + float4(-0.098773, -0.177147, 0.034356, 0.046662), + float4(0.247834, 0.049458, 0.967539, 0.904662), + float4(0.268405, 0.533589, 0.081595, 14.0), + float4(0.272699, 0.010736, -0.081595, 0.0), + float4(0.061196, -0.096626, -0.010736, 0.025193), + float4(0.283244, 0.073137, 0.956255, 0.527108), + float4(0.359663, 0.559356, 0.0, 14.0), + float4(0.181442, -0.015031, 0.0, 0.006043), + float4(-0.091258, -0.025767, 0.081595, 0.076188), + float4(0.076465, 0.923048, 0.37701, 0.964691), + float4(0.199693, 0.595859, 0.0, 14.0), + float4(0.068712, -0.06227, 0.081595, 0.0), + float4(-0.030061, -0.239417, 0.115951, 0.01445), + float4(-0.504461, 0.426837, 0.750553, 0.254154), + float4(0.035429, 0.740798, 0.0, 14.0), + float4(0.164264, -0.144939, 0.0, 0.0), + float4(0.134202, -0.384356, 0.115951, 0.010956), + float4(0.332572, 0.376915, 0.864483, 0.215854), + float4(0.035429, 0.482055, 0.0, 14.0), + float4(0.0, 0.258742, 0.0, 0.0), + float4(0.134202, -0.125614, 0.115951, 0.473533), + float4(-0.653777, 0.0, 0.756687, 0.964691), + float4(0.199693, 0.595859, 0.0, 14.0), + float4(0.105215, 0.22546, 0.0, 0.0), + float4(0.068712, -0.06227, 0.081595, 0.015991), + float4(-0.613882, 0.286478, 0.735581, 0.473533), + float4(0.304908, 0.821319, 0.0, 14.0), + float4(0.128834, -0.067638, 0.0, 0.0), + float4(-0.036503, -0.28773, 0.081595, 0.254154), + float4(0.133686, 0.254641, 0.957751, 0.973449), + float4(0.433742, 0.753681, 0.0, 14.0), + float4(-0.07408, -0.194325, 0.0, 0.0), + float4(-0.165337, -0.220092, 0.081595, 0.964691), + float4(0.683362, -0.260508, 0.682021, 0.964691), + float4(0.035429, 0.482055, -0.042945, 3.0), + float4(0.0, 0.258742, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-1.0, 0.0, 0.0, 0.577584), + float4(0.035429, 0.740798, -0.042945, 7.0), + float4(0.164264, -0.144939, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.661622, 0.749838, 0.0, 0.577584), + float4(0.199693, 0.595859, -0.042945, 7.0), + float4(0.105215, 0.22546, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-0.906183, 0.422886, 0.0, 0.577584), + float4(0.304908, 0.821319, -0.042945, 7.0), + float4(0.128834, -0.067638, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.464834, 0.885398, 0.0, 0.577584), + float4(0.433742, 0.753681, -0.042945, 7.0), + float4(-0.07408, -0.194325, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.934406, -0.35621, 0.0, 0.577584), + float4(0.359663, 0.559356, -0.042945, 7.0), + float4(0.181442, -0.015031, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.082557, 0.996586, 0.0, 0.577584), + float4(0.541104, 0.544325, -0.042945, 7.0), + float4(-0.288804, -0.278068, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.69359, -0.72037, 0.0, 0.577584), + float4(0.252301, 0.266258, -0.042945, 3.0), + float4(-0.221166, -0.203988, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.677984, -0.735077, 0.0, 0.577584), + float4(0.031135, 0.06227, -0.042945, 7.0), + float4(0.004294, 0.419785, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-0.999948, 0.01023, 0.0, 0.577584), + float4(0.035429, 0.482055, -0.042945, 11.0), + float4(0.0, 0.258742, 0.0, 0.01033), + float4(0.0, 0.258742, 0.042945, 0.045182), + float4(-1.0, 0.0, 0.0, 0.577584), + float4(0.035429, 0.740798, -0.042945, 11.0), + float4(0.164264, -0.144939, 0.0, 0.01033), + float4(0.164264, -0.144939, 0.042945, 0.045182), + float4(0.661622, 0.749838, 0.0, 0.577584), + float4(0.199693, 0.595859, -0.042945, 11.0), + float4(0.105215, 0.22546, 0.0, 0.01033), + float4(0.105215, 0.22546, 0.042945, 0.045182), + float4(-0.906183, 0.422886, 0.0, 0.577584), + float4(0.304908, 0.821319, -0.042945, 11.0), + float4(0.128834, -0.067638, 0.0, 0.01033), + float4(0.128834, -0.067638, 0.042945, 0.045182), + float4(0.464834, 0.885398, 0.0, 0.577584), + float4(0.433742, 0.753681, -0.042945, 11.0), + float4(-0.07408, -0.194325, 0.0, 0.01033), + float4(-0.07408, -0.194325, 0.042945, 0.045182), + float4(0.934406, -0.35621, 0.0, 0.577584), + float4(0.359663, 0.559356, -0.042945, 11.0), + float4(0.181442, -0.015031, 0.0, 0.01033), + float4(0.181442, -0.015031, 0.042945, 0.045182), + float4(0.082557, 0.996586, 0.0, 0.577584), + float4(0.541104, 0.544325, -0.042945, 9.0), + float4(-0.288804, -0.278068, 0.0, 0.01033), + float4(-0.288804, -0.278068, 0.042945, 0.045182), + float4(0.69359, -0.72037, 0.0, 0.577584), + float4(0.252301, 0.266258, -0.042945, 11.0), + float4(-0.221166, -0.203988, 0.0, 0.01033), + float4(-0.221166, -0.203988, 0.042945, 0.045182), + float4(0.677984, -0.735077, 0.0, 0.577584), + float4(0.031135, 0.06227, -0.042945, 9.0), + float4(0.004294, 0.419785, 0.0, 0.01033), + float4(0.004294, 0.419785, 0.042945, 0.045182), + float4(-0.999948, 0.01023, 0.0, 0.577584), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.089771, -0.137361, -0.02055, 0.445197), + float4(0.035692, -0.155748, -0.008653, 0.973449), + float4(0.216369, -0.004654, 0.976301, 0.973449), + float4(0.044345, 0.043263, 0.0, 6.0), + float4(-0.040019, 0.030284, 0.049753, 0.000305), + float4(0.014061, 0.048671, 0.037855, 0.964691), + float4(0.365607, -0.634949, 0.680566, 0.973449), + float4(0.004326, 0.073548, 0.049753, 14.0), + float4(-0.003245, -0.035692, -0.049753, 0.0), + float4(-0.04759, 0.004326, -0.049753, 0.955978), + float4(-0.580475, -0.643229, 0.499304, 0.896269), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(-0.011897, -0.151422, -0.058405, 0.000305), + float4(-0.012979, 0.003245, -0.058405, 0.955978), + float4(-0.976245, -0.006827, 0.216564, 0.904662), + float4(0.058405, 0.091934, 0.037855, 14.0), + float4(0.017305, -0.003245, -0.037855, 0.0), + float4(-0.014061, -0.048671, -0.037855, 0.473533), + float4(0.757369, -0.522945, 0.39105, 0.904662), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.107077, -0.140606, -0.058405, 0.0), + float4(0.089771, -0.137361, -0.02055, 0.376257), + float4(0.812052, 0.481346, 0.329966, 0.830777), + float4(-0.045426, 0.564586, 0.052998, 14.0), + float4(0.014061, -0.335291, 0.005408, 0.520989), + float4(0.001082, -0.332046, -0.052998, 0.088655), + float4(-0.975941, -0.037462, 0.214794, 0.973449), + float4(0.10924, 0.439122, 0.0, 14.0), + float4(-0.031366, -0.031366, 0.0, 0.132876), + float4(-0.154666, 0.125464, 0.052998, 0.028428), + float4(0.182761, -0.182761, 0.966021, 0.955978), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(-0.019468, -0.024876, -0.063813, 0.168276), + float4(-0.174135, 0.100587, -0.010816, 0.038209), + float4(-0.469238, -0.764881, 0.441331, 0.955978), + float4(0.201174, 0.638133, 0.0822, 14.0), + float4(-0.072466, -0.174135, -0.018387, 0.18117), + float4(-0.246601, -0.073548, -0.029203, 0.082277), + float4(-0.098561, -0.063844, 0.993081, 0.973449), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(0.055161, -0.146014, -0.025958, 0.246204), + float4(0.016224, -0.156829, -0.014061, 0.964691), + float4(0.305397, -0.053644, 0.950713, 0.973449), + float4(0.183869, 0.317985, 0.037855, 6.0), + float4(-0.036774, -0.045426, -0.037855, 0.0), + float4(-0.038937, -0.010816, 0.011897, 0.806958), + float4(0.374433, -0.753477, 0.540438, 0.854994), + float4(0.144932, 0.307169, 0.049753, 14.0), + float4(0.002163, -0.034611, -0.049753, 0.0), + float4(-0.033529, 0.0, -0.049753, 0.9131), + float4(-0.630237, -0.649931, 0.424725, 0.904662), + float4(0.10924, 0.439122, 0.0, 6.0), + float4(0.035692, -0.131953, 0.049753, 0.18117), + float4(0.002163, -0.131953, 0.0, 0.955978), + float4(-0.82919, -0.013593, 0.558802, 0.964691), + float4(0.183869, 0.317985, 0.037855, 14.0), + float4(0.02704, -0.004326, -0.037855, 0.0), + float4(0.005408, -0.037855, -0.037855, 0.514916), + float4(0.70062, -0.452013, 0.552101, 0.846876), + float4(0.210908, 0.445612, 0.0, 10.0), + float4(0.0, -0.131953, 0.0, 0.0), + float4(-0.02704, -0.127627, 0.037855, 0.439658), + float4(0.813734, 0.0, 0.581238, 0.871366), + float4(0.246601, 0.459672, 0.06165, 14.0), + float4(0.070303, -0.101669, -0.024876, 0.262257), + float4(0.034611, -0.116811, -0.015142, 0.973449), + float4(0.27928, -0.041605, 0.959308, 0.973449), + float4(0.319067, 0.319067, 0.0, 6.0), + float4(-0.037855, 0.023795, 0.046508, 0.0), + float4(-0.002163, 0.038937, 0.036774, 0.896269), + float4(0.437906, -0.604311, 0.665618, 0.896269), + float4(0.281211, 0.342862, 0.046508, 14.0), + float4(-0.004326, -0.031366, -0.046508, 0.0), + float4(-0.040019, 0.001082, -0.046508, 0.752941), + float4(-0.58651, -0.645161, 0.489667, 0.846876), + float4(0.23903, 0.45102, 0.0, 14.0), + float4(0.042182, -0.108158, 0.046508, 0.000305), + float4(0.002163, -0.107077, 0.0, 0.846876), + float4(-0.758098, -0.015315, 0.651961, 0.896269), + float4(0.316904, 0.358004, 0.036774, 14.0), + float4(0.024876, -0.004326, -0.036774, 0.0), + float4(0.002163, -0.038937, -0.036774, 0.617212), + float4(0.707338, -0.464191, 0.533104, 0.921584), + float4(0.340698, 0.474815, 0.0, 14.0), + float4(0.001082, -0.121137, 0.0, 0.0), + float4(-0.023795, -0.116811, 0.036774, 0.473533), + float4(0.828664, 0.007399, 0.559697, 0.930114), + float4(0.375309, 0.488875, 0.0, 14.0), + float4(0.078955, -0.08869, 0.032447, 0.223224), + float4(0.036774, -0.105995, 0.043263, 0.964691), + float4(0.071225, 0.39802, 0.914608, 0.973449), + float4(0.45102, 0.359085, 0.0, 6.0), + float4(-0.038937, 0.023795, 0.043263, 0.000305), + float4(0.003245, 0.0411, 0.032447, 0.879622), + float4(0.417852, -0.583049, 0.696744, 0.9131), + float4(0.412083, 0.38288, 0.043263, 14.0), + float4(-0.001082, -0.031366, -0.043263, 0.0), + float4(-0.035692, -0.001082, -0.043263, 0.745403), + float4(-0.574039, -0.656045, 0.489984, 0.854994), + float4(0.375309, 0.488875, 0.0, 14.0), + float4(0.036774, -0.105995, 0.043263, 0.000305), + float4(0.001082, -0.107077, 0.0, 0.879622), + float4(-0.771254, -0.00779, 0.63648, 0.896269), + float4(0.454264, 0.400185, 0.032447, 14.0), + float4(0.019468, -0.008653, -0.032447, 0.0), + float4(-0.003245, -0.0411, -0.032447, 0.527108), + float4(0.688617, -0.482032, 0.541712, 0.887922), + float4(0.473733, 0.603523, 0.0, 14.0), + float4(0.0, -0.21199, 0.0, 0.158953), + float4(-0.019468, -0.203337, 0.032447, 0.030716), + float4(0.857493, 0.0, 0.514496, 0.964691), + float4(0.179543, 0.466162, 0.058405, 10.0), + float4(-0.050834, -0.002163, 0.005408, 0.000305), + float4(0.021632, 0.171972, 0.023795, 0.022171), + float4(0.110896, -0.149888, 0.982464, 0.381323), + float4(0.326638, 0.830655, 0.0, 14.0), + float4(0.048671, -0.34178, 0.0, 0.007492), + float4(-0.125464, -0.192522, 0.0822, 0.054475), + float4(0.472493, 0.067285, 0.878762, 0.964691), + float4(0.326638, 0.830655, 0.0, 14.0), + float4(0.139524, -0.21199, 0.0, 0.0), + float4(0.048671, -0.34178, 0.0, 0.015213), + float4(0.0, 0.0, 1.0, 0.428687), + float4(-0.012979, 0.832818, 0.0, 14.0), + float4(0.339617, -0.002163, 0.0, 0.0), + float4(0.214153, -0.194685, 0.0822, 0.027314), + float4(0.002492, 0.391296, 0.920261, 0.49102), + float4(-0.012979, 0.832818, 0.0, 14.0), + float4(0.214153, -0.194685, 0.0822, 0.000305), + float4(-0.032447, -0.268232, 0.052998, 0.930114), + float4(-0.176863, 0.211325, 0.961281, 0.973449), + float4(-0.221724, 0.475896, 0.0, 14.0), + float4(0.139524, 0.055161, 0.043263, 0.520989), + float4(0.054079, -0.059487, 0.046508, 0.973449), + float4(-0.39307, 0.317373, 0.863001, 0.973449), + float4(-0.167645, 0.416409, 0.046508, 14.0), + float4(-0.056242, -0.008653, -0.046508, 0.000305), + float4(-0.054079, 0.059487, -0.046508, 0.955978), + float4(-0.638985, 0.020285, 0.768952, 0.973449), + float4(-0.167645, 0.416409, 0.046508, 6.0), + float4(0.073548, 0.015142, -0.046508, 0.0), + float4(0.004326, -0.035692, -0.046508, 0.768154), + float4(0.490917, -0.668482, 0.558688, 0.871366), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.017305, -0.152503, 0.043263, 0.0), + float4(-0.122219, -0.207664, 0.0, 0.723049), + float4(-0.365879, 0.215335, 0.905408, 0.854994), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.054079, -0.118974, 0.052998, 0.0), + float4(0.017305, -0.152503, 0.043263, 0.514916), + float4(-0.41958, 0.203352, 0.884647, 0.863157), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.086527, 0.149258, 0.0, 0.0), + float4(0.054079, -0.118974, 0.052998, 0.262257), + float4(-0.385563, 0.223515, 0.8952, 0.9131), + float4(-0.044345, 0.23254, 0.0, 7.0), + float4(-0.001082, 0.332046, -0.043263, 0.01033), + float4(-0.001082, 0.332046, 0.052998, 0.045182), + float4(-0.999995, -0.003257, 0.0, 0.577584), + float4(-0.045426, 0.564586, -0.043263, 7.0), + float4(-0.036774, -0.033529, 0.086527, 0.01033), + float4(0.0, 0.0, 0.096261, 0.045182), + float4(0.673754, -0.738956, 0.0, 0.577584), + float4(-0.0822, 0.531057, -0.043263, 7.0), + float4(-0.011897, -0.099506, 0.043263, 0.01033), + float4(0.0, 0.0, 0.086527, 0.045182), + float4(0.992928, -0.11872, 0.0, 0.577584), + float4(-0.094098, 0.431551, -0.043263, 7.0), + float4(-0.069221, -0.050834, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.591909, -0.806004, 0.0, 0.577584), + float4(-0.163319, 0.380717, -0.043263, 7.0), + float4(-0.060569, 0.02704, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.407651, -0.913138, 0.0, 0.577584), + float4(-0.223888, 0.407756, -0.043263, 7.0), + float4(0.002163, 0.06814, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999496, 0.03173, 0.0, 0.577584), + float4(-0.221724, 0.475896, -0.043263, 7.0), + float4(0.122219, 0.207664, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.861819, 0.507216, 0.0, 0.577584), + float4(-0.099506, 0.68356, -0.043263, 3.0), + float4(0.086527, 0.149258, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.86514, 0.501531, 0.0, 0.577584), + float4(-0.012979, 0.832818, -0.043263, 7.0), + float4(0.339617, -0.002163, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.006369, 0.99998, 0.0, 0.577584), + float4(0.326638, 0.830655, -0.043263, 7.0), + float4(0.139524, -0.21199, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.835314, 0.549773, 0.0, 0.577584), + float4(0.466162, 0.618665, -0.043263, 3.0), + float4(0.007571, -0.015142, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.894427, 0.447214, 0.0, 0.577584), + float4(0.473733, 0.603523, -0.043263, 7.0), + float4(0.0, -0.21199, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.473733, 0.391533, -0.043263, 7.0), + float4(-0.022713, -0.032447, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.819232, -0.573462, 0.0, 0.577584), + float4(0.45102, 0.359085, -0.043263, 7.0), + float4(-0.040019, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.185892, -0.98257, 0.0, 0.577584), + float4(0.411001, 0.351514, -0.043263, 7.0), + float4(-0.034611, 0.030284, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.658505, -0.752577, 0.0, 0.577584), + float4(0.376391, 0.381799, -0.043263, 7.0), + float4(-0.001082, 0.107077, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999949, -0.0101, 0.0, 0.577584), + float4(0.375309, 0.488875, -0.043263, 7.0), + float4(-0.034611, -0.014061, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.376377, -0.926467, 0.0, 0.577584), + float4(0.340698, 0.474815, -0.043263, 7.0), + float4(0.001082, -0.121137, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.99996, 0.008928, 0.0, 0.577584), + float4(0.34178, 0.353677, -0.043263, 7.0), + float4(-0.022713, -0.034611, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.836048, -0.548657, 0.0, 0.577584), + float4(0.319067, 0.319067, -0.043263, 7.0), + float4(-0.042182, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.276885, 0.311496, -0.043263, 7.0), + float4(-0.035692, 0.032447, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.672673, -0.73994, 0.0, 0.577584), + float4(0.241193, 0.343943, -0.043263, 7.0), + float4(-0.002163, 0.107077, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999796, -0.020198, 0.0, 0.577584), + float4(0.23903, 0.45102, -0.043263, 7.0), + float4(-0.028121, -0.005408, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.188847, -0.982006, 0.0, 0.577584), + float4(0.210908, 0.445612, -0.043263, 7.0), + float4(0.0, -0.131953, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.210908, 0.313659, -0.043263, 7.0), + float4(-0.021632, -0.033529, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.840297, -0.542127, 0.0, 0.577584), + float4(0.189277, 0.28013, -0.043263, 7.0), + float4(-0.042182, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.147095, 0.272559, -0.043263, 7.0), + float4(-0.035692, 0.034611, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.696146, -0.7179, 0.0, 0.577584), + float4(0.111403, 0.307169, -0.043263, 7.0), + float4(-0.002163, 0.131953, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999866, -0.016391, 0.0, 0.577584), + float4(0.10924, 0.439122, -0.043263, 7.0), + float4(-0.031366, -0.031366, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.707107, -0.707107, 0.0, 0.577584), + float4(0.077874, 0.407756, -0.043263, 7.0), + float4(-0.002163, -0.319067, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.999977, -0.006779, 0.0, 0.577584), + float4(0.075711, 0.08869, -0.043263, 7.0), + float4(-0.031366, -0.045426, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.822897, -0.568191, 0.0, 0.577584), + float4(0.044345, 0.043263, -0.043263, 7.0), + float4(-0.043263, -0.005408, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.124035, -0.992278, 0.0, 0.577584), + float4(0.001082, 0.037855, -0.043263, 7.0), + float4(-0.044345, 0.040019, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.669964, -0.742393, 0.0, 0.577584), + float4(-0.043263, 0.077874, -0.043263, 7.0), + float4(-0.001082, 0.154666, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999976, -0.006993, 0.0, 0.577584), + float4(0.044345, 0.043263, 0.0, 10.0), + float4(-0.043263, -0.005408, 0.0, 0.000305), + float4(-0.040019, 0.030284, 0.049753, 0.964691), + float4(0.101434, -0.811469, 0.575525, 0.973449), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.035692, -0.155748, -0.008653, 0.000305), + float4(-0.011897, -0.151422, -0.058405, 0.955978), + float4(-0.716533, -0.20131, 0.667874, 0.904662), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.10924, 0.178461, -0.058405, 0.0), + float4(0.107077, -0.140606, -0.058405, 0.376257), + float4(0.475583, -0.003224, 0.879665, 0.830777), + float4(-0.045426, 0.564586, 0.052998, 14.0), + float4(0.1233, -0.156829, -0.052998, 0.520989), + float4(0.014061, -0.335291, 0.005408, 0.088655), + float4(0.429355, 0.032562, 0.902549, 0.973449), + float4(0.183869, 0.317985, 0.037855, 10.0), + float4(0.005408, -0.037855, -0.037855, 0.0), + float4(-0.036774, -0.045426, -0.037855, 0.806958), + float4(0.124328, -0.692684, 0.710445, 0.854994), + float4(0.10924, 0.439122, 0.0, 10.0), + float4(0.019468, 0.024876, 0.063813, 0.18117), + float4(0.035692, -0.131953, 0.049753, 0.955978), + float4(-0.933936, -0.126584, 0.334276, 0.964691), + float4(0.183869, 0.317985, 0.037855, 10.0), + float4(-0.055161, 0.146014, 0.025958, 0.0), + float4(0.02704, 0.127627, -0.037855, 0.439658), + float4(0.623841, 0.097823, 0.775405, 0.871366), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(0.050834, 0.002163, -0.005408, 0.0), + float4(0.0822, -0.018387, -0.063813, 0.439658), + float4(0.07859, -0.92643, 0.368172, 0.871366), + float4(0.319067, 0.319067, 0.0, 10.0), + float4(-0.042182, -0.007571, 0.0, 0.0), + float4(-0.037855, 0.023795, 0.046508, 0.896269), + float4(0.1483, -0.826245, 0.54344, 0.896269), + float4(0.23903, 0.45102, 0.0, 15.0), + float4(0.007571, 0.008653, 0.06165, 0.000305), + float4(0.042182, -0.108158, 0.046508, 0.846876), + float4(-0.94107, -0.299262, 0.157572, 0.896269), + float4(0.316904, 0.358004, 0.036774, 14.0), + float4(-0.070303, 0.101669, 0.024876, 0.0), + float4(0.023795, 0.116811, -0.036774, 0.473533), + float4(0.523424, 0.157027, 0.837478, 0.930114), + float4(0.246601, 0.459672, 0.06165, 14.0), + float4(0.030284, 0.024876, -0.003245, 0.0), + float4(0.094098, 0.015142, -0.06165, 0.473533), + float4(0.518869, -0.545855, 0.657888, 0.930114), + float4(0.45102, 0.359085, 0.0, 10.0), + float4(-0.040019, -0.007571, 0.0, 0.000305), + float4(-0.038937, 0.023795, 0.043263, 0.879622), + float4(0.151736, -0.802035, 0.577682, 0.9131), + float4(0.454264, 0.400185, 0.032447, 12.0), + float4(-0.078955, 0.08869, -0.032447, 0.158953), + float4(0.019468, 0.203337, -0.032447, 0.030716), + float4(-0.201684, 0.173144, 0.964025, 0.964691), + float4(0.375309, 0.488875, 0.0, 10.0), + float4(0.090853, 0.12979, 0.0, 0.158953), + float4(0.098424, 0.114648, 0.0, 0.030716), + float4(0.0, 0.0, 1.0, 0.964691), + float4(0.340698, 0.474815, 0.0, 14.0), + float4(-0.063813, 0.009734, 0.058405, 0.000305), + float4(0.034611, 0.014061, 0.0, 0.022171), + float4(0.3276, -0.806402, 0.492334, 0.381323), + float4(0.276885, 0.484549, 0.058405, 10.0), + float4(-0.030284, -0.024876, 0.003245, 0.000305), + float4(-0.097342, -0.018387, 0.0, 0.022171), + float4(-0.03153, 0.166923, 0.985466, 0.381323), + float4(0.201174, 0.638133, 0.0822, 8.0), + float4(0.075711, -0.153585, -0.023795, 0.000305), + float4(-0.021632, -0.171972, -0.023795, 0.022171), + float4(0.026497, -0.140281, 0.989757, 0.381323), + float4(0.276885, 0.484549, 0.058405, 14.0), + float4(-0.075711, 0.153585, 0.023795, 0.000305), + float4(0.098424, 0.004326, -0.058405, 0.022171), + float4(0.50316, 0.115345, 0.856461, 0.381323), + float4(0.246601, 0.459672, 0.06165, 11.0), + float4(-0.007571, -0.008653, -0.06165, 0.000305), + float4(-0.035692, -0.014061, -0.06165, 0.022171), + float4(0.187618, -0.975617, 0.113888, 0.381323), + float4(0.210908, 0.445612, 0.0, 10.0), + float4(-0.031366, 0.02055, 0.058405, 0.000305), + float4(0.035692, 0.014061, 0.06165, 0.022171), + float4(-0.105864, -0.954447, 0.278971, 0.381323), + float4(-0.167645, 0.416409, 0.046508, 14.0), + float4(0.004326, -0.035692, -0.046508, 0.000305), + float4(-0.056242, -0.008653, -0.046508, 0.955978), + float4(-0.339782, -0.761112, 0.552501, 0.973449), + float4(-0.167645, 0.416409, 0.046508, 10.0), + float4(0.085445, 0.114648, -0.003245, 0.0), + float4(0.073548, 0.015142, -0.046508, 0.768154), + float4(0.548356, -0.38771, 0.740936, 0.871366), + float4(-0.044345, 0.23254, 0.0, 3.0), + float4(0.0, 0.0, -0.043263, 0.01033), + float4(-0.001082, 0.332046, -0.043263, 0.045182), + float4(-0.999995, -0.003257, 0.0, 0.577584), + float4(-0.045426, 0.564586, -0.043263, 11.0), + float4(-0.036774, -0.033529, 0.0, 0.01033), + float4(-0.036774, -0.033529, 0.086527, 0.045182), + float4(0.673754, -0.738956, 0.0, 0.577584), + float4(-0.0822, 0.531057, -0.043263, 11.0), + float4(-0.011897, -0.099506, 0.0, 0.01033), + float4(-0.011897, -0.099506, 0.043263, 0.045182), + float4(0.992928, -0.11872, 0.0, 0.577584), + float4(-0.094098, 0.431551, -0.043263, 11.0), + float4(-0.069221, -0.050834, 0.0, 0.01033), + float4(-0.069221, -0.050834, 0.043263, 0.045182), + float4(0.591909, -0.806004, 0.0, 0.577584), + float4(-0.163319, 0.380717, -0.043263, 11.0), + float4(-0.060569, 0.02704, 0.0, 0.01033), + float4(-0.060569, 0.02704, 0.043263, 0.045182), + float4(-0.407651, -0.913138, 0.0, 0.577584), + float4(-0.223888, 0.407756, -0.043263, 11.0), + float4(0.002163, 0.06814, 0.0, 0.01033), + float4(0.002163, 0.06814, 0.043263, 0.045182), + float4(-0.999496, 0.03173, 0.0, 0.577584), + float4(-0.221724, 0.475896, -0.043263, 9.0), + float4(0.122219, 0.207664, 0.0, 0.01033), + float4(0.122219, 0.207664, 0.043263, 0.045182), + float4(-0.861819, 0.507216, 0.0, 0.577584), + float4(-0.099506, 0.68356, -0.043263, 11.0), + float4(0.086527, 0.149258, 0.0, 0.01033), + float4(0.086527, 0.149258, 0.043263, 0.045182), + float4(-0.86514, 0.501531, 0.0, 0.577584), + float4(-0.012979, 0.832818, -0.043263, 11.0), + float4(0.339617, -0.002163, 0.0, 0.01033), + float4(0.339617, -0.002163, 0.043263, 0.045182), + float4(0.006369, 0.99998, 0.0, 0.577584), + float4(0.326638, 0.830655, -0.043263, 9.0), + float4(0.139524, -0.21199, 0.0, 0.01033), + float4(0.139524, -0.21199, 0.043263, 0.045182), + float4(0.835314, 0.549773, 0.0, 0.577584), + float4(0.466162, 0.618665, -0.043263, 11.0), + float4(0.007571, -0.015142, 0.0, 0.01033), + float4(0.007571, -0.015142, 0.043263, 0.045182), + float4(0.894427, 0.447214, 0.0, 0.577584), + float4(0.473733, 0.603523, -0.043263, 11.0), + float4(0.0, -0.21199, 0.0, 0.01033), + float4(0.0, -0.21199, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.473733, 0.391533, -0.043263, 11.0), + float4(-0.022713, -0.032447, 0.0, 0.01033), + float4(-0.022713, -0.032447, 0.043263, 0.045182), + float4(0.819232, -0.573462, 0.0, 0.577584), + float4(0.45102, 0.359085, -0.043263, 11.0), + float4(-0.040019, -0.007571, 0.0, 0.01033), + float4(-0.040019, -0.007571, 0.043263, 0.045182), + float4(0.185892, -0.98257, 0.0, 0.577584), + float4(0.411001, 0.351514, -0.043263, 11.0), + float4(-0.034611, 0.030284, 0.0, 0.01033), + float4(-0.034611, 0.030284, 0.043263, 0.045182), + float4(-0.658505, -0.752577, 0.0, 0.577584), + float4(0.376391, 0.381799, -0.043263, 11.0), + float4(-0.001082, 0.107077, 0.0, 0.01033), + float4(-0.001082, 0.107077, 0.043263, 0.045182), + float4(-0.999949, -0.0101, 0.0, 0.577584), + float4(0.375309, 0.488875, -0.043263, 11.0), + float4(-0.034611, -0.014061, 0.0, 0.01033), + float4(-0.034611, -0.014061, 0.043263, 0.045182), + float4(0.376377, -0.926467, 0.0, 0.577584), + float4(0.340698, 0.474815, -0.043263, 11.0), + float4(0.001082, -0.121137, 0.0, 0.01033), + float4(0.001082, -0.121137, 0.043263, 0.045182), + float4(0.99996, 0.008928, 0.0, 0.577584), + float4(0.34178, 0.353677, -0.043263, 11.0), + float4(-0.022713, -0.034611, 0.0, 0.01033), + float4(-0.022713, -0.034611, 0.043263, 0.045182), + float4(0.836048, -0.548657, 0.0, 0.577584), + float4(0.319067, 0.319067, -0.043263, 11.0), + float4(-0.042182, -0.007571, 0.0, 0.01033), + float4(-0.042182, -0.007571, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.276885, 0.311496, -0.043263, 11.0), + float4(-0.035692, 0.032447, 0.0, 0.01033), + float4(-0.035692, 0.032447, 0.043263, 0.045182), + float4(-0.672673, -0.73994, 0.0, 0.577584), + float4(0.241193, 0.343943, -0.043263, 11.0), + float4(-0.002163, 0.107077, 0.0, 0.01033), + float4(-0.002163, 0.107077, 0.043263, 0.045182), + float4(-0.999796, -0.020198, 0.0, 0.577584), + float4(0.23903, 0.45102, -0.043263, 11.0), + float4(-0.028121, -0.005408, 0.0, 0.01033), + float4(-0.028121, -0.005408, 0.043263, 0.045182), + float4(0.188847, -0.982006, 0.0, 0.577584), + float4(0.210908, 0.445612, -0.043263, 11.0), + float4(0.0, -0.131953, 0.0, 0.01033), + float4(0.0, -0.131953, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.210908, 0.313659, -0.043263, 11.0), + float4(-0.021632, -0.033529, 0.0, 0.01033), + float4(-0.021632, -0.033529, 0.043263, 0.045182), + float4(0.840297, -0.542127, 0.0, 0.577584), + float4(0.189277, 0.28013, -0.043263, 11.0), + float4(-0.042182, -0.007571, 0.0, 0.01033), + float4(-0.042182, -0.007571, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.147095, 0.272559, -0.043263, 11.0), + float4(-0.035692, 0.034611, 0.0, 0.01033), + float4(-0.035692, 0.034611, 0.043263, 0.045182), + float4(-0.696146, -0.7179, 0.0, 0.577584), + float4(0.111403, 0.307169, -0.043263, 11.0), + float4(-0.002163, 0.131953, 0.0, 0.01033), + float4(-0.002163, 0.131953, 0.043263, 0.045182), + float4(-0.999866, -0.016391, 0.0, 0.577584), + float4(0.10924, 0.439122, -0.043263, 11.0), + float4(-0.031366, -0.031366, 0.0, 0.01033), + float4(-0.031366, -0.031366, 0.043263, 0.045182), + float4(0.707107, -0.707107, 0.0, 0.577584), + float4(0.077874, 0.407756, -0.043263, 11.0), + float4(-0.002163, -0.319067, 0.0, 0.01033), + float4(-0.002163, -0.319067, 0.043263, 0.045182), + float4(0.999977, -0.006779, 0.0, 0.577584), + float4(0.075711, 0.08869, -0.043263, 11.0), + float4(-0.031366, -0.045426, 0.0, 0.01033), + float4(-0.031366, -0.045426, 0.043263, 0.045182), + float4(0.822897, -0.568191, 0.0, 0.577584), + float4(0.044345, 0.043263, -0.043263, 11.0), + float4(-0.043263, -0.005408, 0.0, 0.01033), + float4(-0.043263, -0.005408, 0.043263, 0.045182), + float4(0.124035, -0.992278, 0.0, 0.577584), + float4(0.001082, 0.037855, -0.043263, 11.0), + float4(-0.044345, 0.040019, 0.0, 0.01033), + float4(-0.044345, 0.040019, 0.043263, 0.045182), + float4(-0.669964, -0.742393, 0.0, 0.577584), + float4(-0.043263, 0.077874, -0.043263, 9.0), + float4(-0.001082, 0.154666, 0.0, 0.01033), + float4(-0.001082, 0.154666, 0.043263, 0.045182), + float4(-0.999976, -0.006993, 0.0, 0.577584) +}; +static const float2 PRISM_POLY[110] = { + float2(0.006442, -0.002147), + float2(-0.001074, 0.001074), + float2(-0.004294, 0.007515), + float2(-0.004294, 0.806288), + float2(-0.001074, 0.809509), + float2(0.004294, 0.809509), + float2(0.022546, 0.796626), + float2(0.185736, 0.655982), + float2(0.281288, 0.86319), + float2(0.286656, 0.870706), + float2(0.293098, 0.871779), + float2(0.465951, 0.781595), + float2(0.478834, 0.773006), + float2(0.478834, 0.763344), + float2(0.407975, 0.592638), + float2(0.45092, 0.586196), + float2(0.601227, 0.573313), + float2(0.606595, 0.569018), + float2(0.606595, 0.564724), + float2(0.601227, 0.556135), + float2(0.50138, 0.453067), + float2(0.425153, 0.37684), + float2(0.289877, 0.246933), + float2(0.081595, 0.057975), + float2(0.013957, 0.0), + float2(-0.011897, 0.0), + float2(-0.055161, 0.037855), + float2(-0.081119, 0.063813), + float2(-0.083282, 0.06814), + float2(-0.084363, 0.38288), + float2(-0.089771, 0.38288), + float2(-0.158993, 0.33529), + float2(-0.166564, 0.336372), + float2(-0.250927, 0.379635), + float2(-0.261743, 0.388288), + float2(-0.263906, 0.461836), + float2(-0.262824, 0.486712), + float2(-0.173053, 0.638133), + float2(-0.044345, 0.860939), + float2(-0.036774, 0.867429), + float2(-0.024876, 0.871755), + float2(0.33529, 0.871755), + float2(0.349351, 0.867429), + float2(0.363412, 0.853368), + float2(0.513752, 0.619747), + float2(0.513752, 0.379635), + float2(0.466162, 0.322311), + float2(0.461836, 0.320148), + float2(0.399104, 0.311496), + float2(0.393696, 0.313659), + float2(0.374227, 0.332046), + float2(0.343943, 0.288782), + float2(0.336372, 0.282293), + float2(0.269314, 0.271477), + float2(0.263906, 0.27364), + float2(0.243356, 0.292027), + float2(0.21199, 0.244438), + float2(0.207664, 0.241193), + float2(0.149258, 0.231459), + float2(0.138443, 0.231459), + float2(0.118974, 0.248764), + float2(0.116811, 0.222806), + float2(0.117892, 0.162237), + float2(0.115729, 0.076792), + float2(0.06814, 0.011897), + float2(0.062732, 0.007571), + float2(0.005408, 0.0), + float2(0.031135, 0.06227), + float2(0.252301, 0.266258), + float2(0.541104, 0.544325), + float2(0.359663, 0.559356), + float2(0.433742, 0.753681), + float2(0.304908, 0.821319), + float2(0.199693, 0.595859), + float2(0.035429, 0.740798), + float2(0.035429, 0.482055), + float2(-0.043263, 0.077874), + float2(0.001082, 0.037855), + float2(0.044345, 0.043263), + float2(0.075711, 0.08869), + float2(0.077874, 0.407756), + float2(0.10924, 0.439122), + float2(0.111403, 0.307169), + float2(0.147095, 0.272559), + float2(0.189277, 0.28013), + float2(0.210909, 0.313659), + float2(0.210909, 0.445612), + float2(0.23903, 0.45102), + float2(0.241193, 0.343943), + float2(0.276885, 0.311496), + float2(0.319067, 0.319067), + float2(0.34178, 0.353677), + float2(0.340698, 0.474815), + float2(0.375309, 0.488875), + float2(0.376391, 0.381799), + float2(0.411001, 0.351514), + float2(0.45102, 0.359085), + float2(0.473733, 0.391533), + float2(0.473733, 0.603523), + float2(0.466162, 0.618665), + float2(0.326638, 0.830655), + float2(-0.012979, 0.832818), + float2(-0.099506, 0.68356), + float2(-0.221724, 0.475896), + float2(-0.223888, 0.407756), + float2(-0.163319, 0.380717), + float2(-0.094098, 0.431551), + float2(-0.0822, 0.531057), + float2(-0.045426, 0.564586), + float2(-0.044345, 0.23254) +}; +static const float PRISM_BEVEL = 0.012883; +// end of the prism mesh + +// Le verre validé sur le prototype (30/09/2026). +static const float PRISM_IOR = 1.61; +static const float PRISM_DISPERSION = 0.035; +static const float PRISM_GLOW = 0.45; +static const float PRISM_FOLD = 0.35; +// Rebonds au plus dans le cristal, par canal ; l'id de matière du cristal. +static const int PRISM_BOUNCES = 6; +static const float PRISM_MAT = 9.0; + +// Le modèle de Prism Glow de ce dessin : 0 flèche, 1 main, -1 un autre curseur. +int prism_shape() +{ + int id = sculpt_id(); + return id == 3 || id == 4 ? id - 3 : -1; +} + +// Distance signée au polygone `n` sommets de PRISM_POLY à partir de `i0` (plan xy du modèle), +// négative dedans : la silhouette du serti, ou le contour du cristal. +float prism_poly_dist(float2 p, int i0, int n) +{ + float2 vj = PRISM_POLY[i0 + n - 1]; + float d = 1e9; + float sgn = 1.0; + [loop] for (int i = 0; i < n; i++) + { + float2 vi = PRISM_POLY[i0 + i]; + float2 e = vj - vi; + float2 w = p - vi; + float2 b = w - e * saturate(dot(w, e) / dot(e, e)); + d = min(d, dot(b, b)); + bool c0 = p.y >= vi.y; + bool c1 = p.y < vj.y; + bool c2 = e.x * w.y > e.y * w.x; + if ((c0 && c1 && c2) || (!c0 && !c1 && !c2)) + { + sgn = -sgn; + } + vj = vi; + } + return sgn * sqrt(d); +} + +// Le serti : la silhouette extrudée de z = -épaisseur à 0, arêtes arrondies de PRISM_BEVEL. +float prism_rim(float3 p, int shape) +{ + float half_t = model_thick() * 0.5; + float sil = prism_poly_dist(p.xy, PRISM_SIL_START[shape], PRISM_SIL_COUNT[shape]); + float2 w = float2(sil + PRISM_BEVEL, abs(p.z + half_t) - (half_t - PRISM_BEVEL)); + return min(max(w.x, w.y), 0.0) + length(max(w, 0.0)) - PRISM_BEVEL; +} + +// Distance le long de (ro, rd) au triangle `k` (Möller–Trumbore), -1 s'il le rate. Un rien de jeu +// sur les bords : un rayon ne passe pas entre deux facettes voisines. +float prism_tri(float3 ro, float3 rd, int k) +{ + float3 v0 = PRISM_TRIS[4 * k].xyz; + float3 e1 = PRISM_TRIS[4 * k + 1].xyz; + float3 e2 = PRISM_TRIS[4 * k + 2].xyz; + float3 pv = cross(rd, e2); + float det = dot(e1, pv); + if (abs(det) < 1e-10) + { + return -1.0; + } + float inv = 1.0 / det; + float3 sv = ro - v0; + float u = dot(sv, pv) * inv; + float3 qv = cross(sv, e1); + float v = dot(rd, qv) * inv; + if (u < -2e-4 || v < -2e-4 || u + v > 1.0004) + { + return -1.0; + } + return dot(e2, qv) * inv; +} + +// Le triangle du cristal `shape` le plus proche devant (ro, rd) : sa distance (1e9 si aucun), son +// indice dans `hit` (-1 si aucun). +float prism_trace(float3 ro, float3 rd, int shape, out int hit) +{ + int k0 = PRISM_TRI_START[shape]; + int n = PRISM_TRI_COUNT[shape]; + float best = 1e9; + hit = -1; + [loop] for (int i = 0; i < n; i++) + { + float t = prism_tri(ro, rd, k0 + i); + if (t > 1e-5 && t < best) + { + best = t; + hit = k0 + i; + } + } + return best; +} + // Le modèle `shape` du thème `theme`, repère du prototype : (distance, id de matière). float2 sculpt_proto(float3 p, int theme, int shape) { @@ -1071,11 +1950,17 @@ float sculpt_units() // Le curseur sculpté en `q` (repère du modèle) : distance et id de matière. `occ` : ce qui porte // l'ombre sur l'écran. La borne des plans d'une gemme et le champ des voxels sont de mauvaises // distances loin de la surface : la pénombre s'y strie, ou s'arrête net au bord de la boîte des -// voxels. Pixel Candy porte donc l'ombre de sa forme, extrudée sur la hauteur des cubes. +// voxels. Pixel Candy porte donc l'ombre de sa forme, extrudée sur la hauteur des cubes. Prism +// Glow n'a ici que son serti, dans le repère du modèle : son cristal est tracé à part +// (`prism_primary`), et l'ombre est celle de toute sa silhouette. float2 sculpt_eval(float3 q, bool occ) { int id = sculpt_id() - 1; int theme = id / 2, shape = id % 2; + if (theme == 1) + { + return float2(prism_rim(q, shape), 8.0); + } float3 p = sculpt_point(q); float2 r; if (occ && theme == 3) @@ -1226,6 +2111,11 @@ SculptMat sculpt_material(float mat, float3 p, int theme, int shape) if (mat < 2.5) return s_mat(s_lin(0.95, 0.92, 0.85), 0.3, 0.6, 0.35, 0.25); return s_mat(s_lin(0.1, 0.1, 0.11), 0.55, 0.05, 0.0, 0.1); } + if (theme == 1) + { + // Le serti de Prism Glow : un émail marine, brillant sur son arrondi seulement (`model_shade`). + return s_mat(s_lin(0.02, 0.05, 0.33), 0.35, 0.5, 0.05, 0.3); + } if (theme == 2) { // Corail : la flèche, le calque et les tirets de la main ; jaune : le gant, le calque et les @@ -1300,6 +2190,12 @@ float3 model_shade(float3 q, float3 n, float3 rd, float3 L, float fall, float sh // La matière d'un voxel se lit juste sous la surface : son flanc lit sa propre cellule. float3 p = sculpt_point(q) - float3(n.x, -n.y, n.z) * 0.01; m = sculpt_material(mat, p, (id - 1) / 2, (id - 1) % 2); + if (id == 3 || id == 4) + { + // Le serti de Prism Glow, comme un sprite : son dessus plat, face à la lampe, prendrait + // tout son reflet et virerait au gris. + gloss = 1.0 - smoothstep(0.97, 0.995, abs(n.z)); + } } else { @@ -1325,6 +2221,158 @@ float3 model_shade(float3 q, float3 n, float3 rd, float3 L, float fall, float sh return model_tonemap(col); } +// ---- Le cristal de Prism Glow ---- + +float prism_fresnel(float c, float ior) +{ + float f0 = (ior - 1.0) / (ior + 1.0); + f0 *= f0; + return f0 + (1.0 - f0) * pow(1.0 - c, 5.0); +} + +// Le cristal sur le rayon (ro, rd) : distance au point touché (1e9 si raté ou hors de Prism Glow), +// facette dans `hc`. Une paroi sous le dessus du serti ne compte pas : le rayon a traversé le +// serti avant, c'est lui qu'on voit. +float prism_primary(float3 ro, float3 rd, out int hc) +{ + hc = -1; + int shape = prism_shape(); + if (shape < 0) + { + return 1e9; + } + float t = prism_trace(ro, rd, shape, hc); + if (hc >= 0 && fmod(PRISM_TRIS[4 * hc].w, 2.0) > 0.5 && ro.z + rd.z * t < 0.0) + { + hc = -1; + return 1e9; + } + return t; +} + +// La normale de la facette `h`, tournée vers le rayon qui arrive le long de `rd`. +float3 prism_normal(int h, float3 rd) +{ + float3 n = PRISM_TRIS[4 * h + 3].xyz; + return dot(n, rd) > 0.0 ? -n : n; +} + +// Distance de `p` au segment [a, b]. +float prism_seg(float3 p, float3 a, float3 b) +{ + float3 pa = p - a, ba = b - a; + return length(pa - ba * saturate(dot(pa, ba) / dot(ba, ba))); +} + +// Distance, en pixels (`px` = unités du modèle par pixel au point), de `p` aux arêtes réelles de +// la facette `h` : les plis de l'art et le bord du serti. +float prism_edge_px(float3 p, int h, float px) +{ + float4 r0 = PRISM_TRIS[4 * h]; + float3 a = r0.xyz; + float3 b = a + PRISM_TRIS[4 * h + 1].xyz; + float3 c = a + PRISM_TRIS[4 * h + 2].xyz; + int bits = (int)(r0.w * 0.5); + float d = 1e9; + if ((bits & 1) != 0) d = min(d, prism_seg(p, b, c)); + if ((bits & 2) != 0) d = min(d, prism_seg(p, c, a)); + if ((bits & 4) != 0) d = min(d, prism_seg(p, a, b)); + return d / px; +} + +// L'enregistrement là où le rayon (q, d) du modèle retombe sur le plan : la vidéo, lue par la coupe +// du plan (`trail_b`, uv de la texture), en linéaire. Hors du plan, l'écran sombre du studio. +float3 prism_screen(float3 q, float3 d, float3 nz, float hz, float3 tip, float unit, ModelFrame f) +{ + float denom = dot(d, nz); + if (denom > -1e-4) + { + return SCULPT_SCREEN * 0.2; + } + float3 g = q + d * ((hz - dot(q, nz)) / denom); + float2 fp = (tip + unit * model_to_plane(g, f)).xy / (2.0 * mb.xy) + 0.5; + if (any(fp < 0.0) || any(fp > 1.0)) + { + return SCULPT_SCREEN * 0.2; + } + return pow(sample_yuv_level(lerp(trail_b.xy, trail_b.zw, fp)), 2.2); +} + +// La couleur (sRGB, comme `model_shade`) du cristal au point `p` de la facette `h`, vu le long de +// `rd` : reflet du studio, l'enregistrement réfracté par canal (la dispersion) à travers les +// facettes et le fond plat, la lueur de la couleur de la facette dans l'art, ses plis en liseré. +float3 prism_shade(float3 p, float3 n, float3 rd, int h, float px, float3 l, float3 fill, float3 nz, + float hz, float3 tip, float unit, ModelFrame f) +{ + int shape = prism_shape(); + float4 r3 = PRISM_TRIS[4 * h + 3]; + float3 facet = float3(PRISM_TRIS[4 * h + 1].w, PRISM_TRIS[4 * h + 2].w, r3.w); + float F = prism_fresnel(saturate(-dot(rd, n)), PRISM_IOR); + float zb = -model_thick(); + float3 trans = 0.0; + [loop] for (int ch = 0; ch < 3; ch++) + { + // Le rouge plie le moins, le bleu le plus. FXC n'indexe pas un vecteur par une variable en + // écriture (il déroulerait la boucle) : le canal passe par un masque. + float3 mask = float3(ch == 0, ch == 1, ch == 2); + float ior = PRISM_IOR + PRISM_DISPERSION * (ch - 1); + float3 d = refract(rd, n, 1.0 / ior); + float3 pos = p; + float thr = 1.0; + float acc = 0.0; + [loop] for (int b = 0; b < PRISM_BOUNCES; b++) + { + int hh; + float th = prism_trace(pos, d, shape, hh); + float tb = d.z < -1e-6 ? (zb - pos.z) / d.z : 1e9; + if (tb <= th) + { + // Le fond plat : le rayon sort vers l'écran, ou s'y réfléchit tout entier. + float3 qb = pos + d * tb; + float3 d2 = refract(d, float3(0.0, 0.0, 1.0), ior); + if (dot(d2, d2) < 1e-8) + { + pos = qb; + d = reflect(d, float3(0.0, 0.0, 1.0)); + continue; + } + acc += thr * dot(prism_screen(qb, d2, nz, hz, tip, unit, f), mask); + thr = 0.0; + break; + } + if (hh < 0) + { + break; + } + pos += d * th; + float4 rn = PRISM_TRIS[4 * hh + 3]; + if (fmod(PRISM_TRIS[4 * hh].w, 2.0) > 0.5 && pos.z < 0.0) + { + // Une paroi sous le dessus du serti : le marine de son flanc intérieur. + acc += thr * dot(s_lin(0.03, 0.06, 0.37) * 0.35, mask); + thr = 0.0; + break; + } + float3 dout = refract(d, -rn.xyz, ior); + if (dot(dout, dout) < 1e-8) + { + d = reflect(d, -rn.xyz); + continue; + } + float fi = prism_fresnel(saturate(dot(dout, rn.xyz)), ior); + acc += thr * (1.0 - fi) * dot(model_env(dout, 0.05, l, fill), mask); + thr *= fi; + d = reflect(d, -rn.xyz); + } + trans += acc * mask; + } + float3 col = F * model_env(reflect(rd, n), 0.05, l, fill) + (1.0 - F) * trans + facet * PRISM_GLOW; + // Les plis de l'art : un liseré clair sur chaque arête réelle, fin d'un pixel. + float fold = 1.0 - smoothstep(0.2, 1.0, prism_edge_px(p, h, px)); + col = lerp(col, s_lin(0.96, 0.99, 1.0) * 1.3, PRISM_FOLD * fold * (0.35 + 0.65 * saturate(dot(n, l)))); + return model_tonemap(col); +} + // Les passes de la boucle unique de `cursor_model`, dans l'ordre. Chaque tour n'évalue le modèle // QU'UNE fois (`model_eval`) : FXC recopie une fonction à chaque appel, et un curseur sculpté est // assez gros pour qu'un appel par passe multiplie par neuf le temps de compilation de ps_main. @@ -1388,6 +2436,11 @@ float4 cursor_model(float2 local) float3 ray = rd; float2 tb = ray_box(ro, ray, lo - 0.02, hi + 0.02); int stage = tb.x < tb.y && tb.y > 0.0 ? STAGE_MARCH : STAGE_DONE; + // Prism Glow : le cristal est tracé triangle par triangle au départ de chaque rayon ; `tc` est + // son point touché (1e9 : raté), `hc` sa facette. La marche du serti s'arrête à lui. + bool need_tc = stage == STAGE_MARCH; + float tc = 1e9; + int hc = -1; // Le point du plan derrière le pixel est à régler avant le prochain tour. bool plane_next = stage == STAGE_DONE; int k = 0; @@ -1418,6 +2471,11 @@ float4 cursor_model(float2 local) float4 acc = 0.0; [loop] for (int it = 0; it < 500; it++) { + if (need_tc) + { + need_tc = false; + tc = prism_primary(ro, ray, hc); + } if (plane_next) { // Le plan, là où le modèle ne couvre pas tout le pixel : ombre portée et ombre de @@ -1442,9 +2500,17 @@ float4 cursor_model(float2 local) { if (cov_s > 0.0) { - float3 tl = lamp - q; - float fall = SCULPT_LAMP_DIST * SCULPT_LAMP_DIST / dot(tl, tl); - float3 c = model_shade(q, n, ray, normalize(tl), fall, sh, ao, mat, l, fill); + float3 c; + if (mat > PRISM_MAT - 0.5) + { + c = prism_shade(q, n, ray, hc, t_best / dlen, l, fill, nz, hz, tip, unit, f); + } + else + { + float3 tl = lamp - q; + float fall = SCULPT_LAMP_DIST * SCULPT_LAMP_DIST / dot(tl, tl); + c = model_shade(q, n, ray, normalize(tl), fall, sh, ao, mat, l, fill); + } acc += float4(c * cov_s, cov_s); } shaded++; @@ -1467,6 +2533,7 @@ float4 cursor_model(float2 local) t = max(tb.x, 0.0); best = 1e9; t_best = t; + need_tc = true; } } continue; @@ -1517,6 +2584,15 @@ float4 cursor_model(float2 local) } t += d * stride; k++; + if (!hit && t >= tc) + { + // Le pas dépasse le cristal : c'est lui que le rayon touche, avant le serti. + hit = true; + best = 0.0; + t_best = tc; + d_best = 0.0; + mat = PRISM_MAT; + } if (hit || t > tb.y || k == 96) { cov_s = saturate(1.0 - best); @@ -1545,6 +2621,27 @@ float4 cursor_model(float2 local) plane_next = true; } } + if (stage == STAGE_NORMAL && mat > PRISM_MAT - 0.5) + { + // Le cristal : la normale de sa facette, sans sondes ; ni occlusion ni ombre + // propre. Un bord réel à moins d'un pixel (le serti, un pli) appelle les trois + // rayons de plus, comme les bords intérieurs des autres modèles. + n = prism_normal(hc, ray); + k = 0; + if (shaded > 0) + { + stage = STAGE_SHADE; + } + else + { + if (prism_edge_px(q, hc, t_best / dlen) < 0.75) + { + sub = 3; + } + stage = STAGE_DONE; + plane_next = true; + } + } } } else if (stage == STAGE_NORMAL) @@ -1566,6 +2663,19 @@ float4 cursor_model(float2 local) { sub = 3; } + if (k == 0 && mat > 7.5) + { + // Le serti de Prism Glow au bord du cristal : son contour à moins d'un pixel. + int ps = prism_shape(); + if (ps >= 0) + { + float dout = prism_poly_dist(q.xy, PRISM_OUT_START[ps], PRISM_OUT_COUNT[ps]); + if (abs(dout) < 0.75 * t_best / dlen) + { + sub = 3; + } + } + } k++; if (k == 4) { diff --git a/crates/compositor/src/shaders.metal b/crates/compositor/src/shaders.metal index 01cce9358..1baa306cc 100644 --- a/crates/compositor/src/shaders.metal +++ b/crates/compositor/src/shaders.metal @@ -58,7 +58,7 @@ struct Layer float4 dst_prev; // dst à la frame précédente ; modes 13 et 15 : rect de clip ; mode 17 : angle du socle, rayon et recouvrement de l'ouverture, pénombre de l'ombre float4 mb; // mb.x = nombre de taps de motion blur (1 = désactivé) ; modes 15 et 17 : demi-taille du plan, translation ; mode 18 : .z = 1 si le plan est incliné, .w = 1 si son warp est projectif float4 trail_a; // mode 8 : coins TL, TR du plan à la frame précédente (px locaux, comme fx) ; mode 18 incliné : en fractions de sortie - float4 trail_b; // mode 8 : coins BR, BL du plan à la frame précédente (comme src_prev) ; mode 18 incliné : en fractions de sortie + float4 trail_b; // mode 8 : coins BR, BL du plan à la frame précédente (comme src_prev) ; mode 18 incliné : en fractions de sortie ; mode 15 : la coupe du plan en uv de l'écran (u0, v0, u1, v1), que réfracte le cristal de Prism Glow float4 trail_mb; // mode 8 : x = taps, y = force du flou de mouvement (ceux du mode 0) ; 0 ailleurs }; // Mode 15 (curseur modélisé) : le détail des emplacements est dans `frame_geometry.rs`, en tête @@ -147,6 +147,14 @@ inline float3 sample_yuv(float2 uv, return yuv709_limited(y, cbcr); } +// `sample_yuv` au niveau 0, pour les boucles à sortie anticipée (pas de dérivées). +inline float3 sample_yuv_level(float2 uv, + texture2d texY, + texture2d texUV) +{ + return yuv709_limited(texY.sample(samp, uv, level(0.0)).r, texUV.sample(samp, uv, level(0.0)).rg); +} + // SDF segment à bouts ronds — la primitive des flèches d'annotation. inline float sd_segment(float2 p, float2 a, float2 b) { @@ -545,7 +553,8 @@ inline float3 image_motion(float2 q, float time, float motion, float aspect) // ============ Curseur MODÉLISÉ (mode 15) ============ // Port ligne pour ligne de `cursor_model` (HLSL), dont les commentaires font foi ; seules // différences : `layer` et les textures arrivent en paramètres (le sprite en texture(2), son -// champ R16F en texture(4)), `lerp` s'écrit `mix`, `SampleLevel` s'écrit `sample(…, level(0.0))`, +// champ R16F en texture(4), l'enregistrement en texture(0) et (1) pour le cristal de Prism Glow), +// `lerp` s'écrit `mix`, `SampleLevel` s'écrit `sample(…, level(0.0))`, un `out` s'écrit `thread &`, // et `pow` veut un exposant du type de sa base. // Constantes : miroir exact de `frame_geometry.rs` (MODEL_*) et de `sculpt.rs` (SCULPT_*). constant float MODEL_BEVEL = 0.045; @@ -973,6 +982,861 @@ static float s_pixel_outline(float2 p, int shape) return d; } +// ---- Prism Glow : un cristal en MAILLAGE ---- (cf. HLSL) +// prism mesh: generated by design/cursors/prism-glow/model/export_compositor.py +constant int PRISM_TRI_START[2] = { 0, 31 }; +constant int PRISM_TRI_COUNT[2] = { 31, 130 }; +constant int PRISM_SIL_START[2] = { 0, 25 }; +constant int PRISM_SIL_COUNT[2] = { 25, 42 }; +constant int PRISM_OUT_START[2] = { 67, 76 }; +constant int PRISM_OUT_COUNT[2] = { 9, 34 }; +constant float4 PRISM_TRIS[644] = { + float4(0.035429, 0.482055, 0.0, 14.0), + float4(0.134202, -0.125614, 0.115951, 0.000305), + float4(-0.004294, -0.419785, 0.0, 0.938689), + float4(-0.650191, 0.006652, 0.759742, 0.854994), + float4(0.169632, 0.356442, 0.115951, 14.0), + float4(0.082669, -0.090184, -0.115951, 0.590616), + float4(-0.138497, -0.294172, -0.115951, 0.737911), + float4(0.466385, -0.505659, 0.725806, 0.973449), + float4(0.169632, 0.356442, 0.115951, 14.0), + float4(0.159969, 0.080521, -0.045092, 0.381323), + float4(0.082669, -0.090184, -0.115951, 0.056123), + float4(0.461407, -0.510214, 0.725799, 0.973449), + float4(0.329601, 0.436963, 0.070859, 14.0), + float4(0.211503, 0.107362, -0.070859, 0.545724), + float4(-0.077301, -0.170706, -0.070859, 0.064805), + float4(0.49567, -0.514808, 0.699488, 0.973449), + float4(0.268405, 0.533589, 0.081595, 14.0), + float4(0.061196, -0.096626, -0.010736, 0.000916), + float4(-0.098773, -0.177147, 0.034356, 0.046662), + float4(0.247834, 0.049458, 0.967539, 0.904662), + float4(0.268405, 0.533589, 0.081595, 14.0), + float4(0.272699, 0.010736, -0.081595, 0.0), + float4(0.061196, -0.096626, -0.010736, 0.025193), + float4(0.283244, 0.073137, 0.956255, 0.527108), + float4(0.359663, 0.559356, 0.0, 14.0), + float4(0.181442, -0.015031, 0.0, 0.006043), + float4(-0.091258, -0.025767, 0.081595, 0.076188), + float4(0.076465, 0.923048, 0.37701, 0.964691), + float4(0.199693, 0.595859, 0.0, 14.0), + float4(0.068712, -0.06227, 0.081595, 0.0), + float4(-0.030061, -0.239417, 0.115951, 0.01445), + float4(-0.504461, 0.426837, 0.750553, 0.254154), + float4(0.035429, 0.740798, 0.0, 14.0), + float4(0.164264, -0.144939, 0.0, 0.0), + float4(0.134202, -0.384356, 0.115951, 0.010956), + float4(0.332572, 0.376915, 0.864483, 0.215854), + float4(0.035429, 0.482055, 0.0, 14.0), + float4(0.0, 0.258742, 0.0, 0.0), + float4(0.134202, -0.125614, 0.115951, 0.473533), + float4(-0.653777, 0.0, 0.756687, 0.964691), + float4(0.199693, 0.595859, 0.0, 14.0), + float4(0.105215, 0.22546, 0.0, 0.0), + float4(0.068712, -0.06227, 0.081595, 0.015991), + float4(-0.613882, 0.286478, 0.735581, 0.473533), + float4(0.304908, 0.821319, 0.0, 14.0), + float4(0.128834, -0.067638, 0.0, 0.0), + float4(-0.036503, -0.28773, 0.081595, 0.254154), + float4(0.133686, 0.254641, 0.957751, 0.973449), + float4(0.433742, 0.753681, 0.0, 14.0), + float4(-0.07408, -0.194325, 0.0, 0.0), + float4(-0.165337, -0.220092, 0.081595, 0.964691), + float4(0.683362, -0.260508, 0.682021, 0.964691), + float4(0.035429, 0.482055, -0.042945, 3.0), + float4(0.0, 0.258742, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-1.0, 0.0, 0.0, 0.577584), + float4(0.035429, 0.740798, -0.042945, 7.0), + float4(0.164264, -0.144939, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.661622, 0.749838, 0.0, 0.577584), + float4(0.199693, 0.595859, -0.042945, 7.0), + float4(0.105215, 0.22546, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-0.906183, 0.422886, 0.0, 0.577584), + float4(0.304908, 0.821319, -0.042945, 7.0), + float4(0.128834, -0.067638, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.464834, 0.885398, 0.0, 0.577584), + float4(0.433742, 0.753681, -0.042945, 7.0), + float4(-0.07408, -0.194325, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.934406, -0.35621, 0.0, 0.577584), + float4(0.359663, 0.559356, -0.042945, 7.0), + float4(0.181442, -0.015031, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.082557, 0.996586, 0.0, 0.577584), + float4(0.541104, 0.544325, -0.042945, 7.0), + float4(-0.288804, -0.278068, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.69359, -0.72037, 0.0, 0.577584), + float4(0.252301, 0.266258, -0.042945, 3.0), + float4(-0.221166, -0.203988, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.677984, -0.735077, 0.0, 0.577584), + float4(0.031135, 0.06227, -0.042945, 7.0), + float4(0.004294, 0.419785, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-0.999948, 0.01023, 0.0, 0.577584), + float4(0.035429, 0.482055, -0.042945, 11.0), + float4(0.0, 0.258742, 0.0, 0.01033), + float4(0.0, 0.258742, 0.042945, 0.045182), + float4(-1.0, 0.0, 0.0, 0.577584), + float4(0.035429, 0.740798, -0.042945, 11.0), + float4(0.164264, -0.144939, 0.0, 0.01033), + float4(0.164264, -0.144939, 0.042945, 0.045182), + float4(0.661622, 0.749838, 0.0, 0.577584), + float4(0.199693, 0.595859, -0.042945, 11.0), + float4(0.105215, 0.22546, 0.0, 0.01033), + float4(0.105215, 0.22546, 0.042945, 0.045182), + float4(-0.906183, 0.422886, 0.0, 0.577584), + float4(0.304908, 0.821319, -0.042945, 11.0), + float4(0.128834, -0.067638, 0.0, 0.01033), + float4(0.128834, -0.067638, 0.042945, 0.045182), + float4(0.464834, 0.885398, 0.0, 0.577584), + float4(0.433742, 0.753681, -0.042945, 11.0), + float4(-0.07408, -0.194325, 0.0, 0.01033), + float4(-0.07408, -0.194325, 0.042945, 0.045182), + float4(0.934406, -0.35621, 0.0, 0.577584), + float4(0.359663, 0.559356, -0.042945, 11.0), + float4(0.181442, -0.015031, 0.0, 0.01033), + float4(0.181442, -0.015031, 0.042945, 0.045182), + float4(0.082557, 0.996586, 0.0, 0.577584), + float4(0.541104, 0.544325, -0.042945, 9.0), + float4(-0.288804, -0.278068, 0.0, 0.01033), + float4(-0.288804, -0.278068, 0.042945, 0.045182), + float4(0.69359, -0.72037, 0.0, 0.577584), + float4(0.252301, 0.266258, -0.042945, 11.0), + float4(-0.221166, -0.203988, 0.0, 0.01033), + float4(-0.221166, -0.203988, 0.042945, 0.045182), + float4(0.677984, -0.735077, 0.0, 0.577584), + float4(0.031135, 0.06227, -0.042945, 9.0), + float4(0.004294, 0.419785, 0.0, 0.01033), + float4(0.004294, 0.419785, 0.042945, 0.045182), + float4(-0.999948, 0.01023, 0.0, 0.577584), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.089771, -0.137361, -0.02055, 0.445197), + float4(0.035692, -0.155748, -0.008653, 0.973449), + float4(0.216369, -0.004654, 0.976301, 0.973449), + float4(0.044345, 0.043263, 0.0, 6.0), + float4(-0.040019, 0.030284, 0.049753, 0.000305), + float4(0.014061, 0.048671, 0.037855, 0.964691), + float4(0.365607, -0.634949, 0.680566, 0.973449), + float4(0.004326, 0.073548, 0.049753, 14.0), + float4(-0.003245, -0.035692, -0.049753, 0.0), + float4(-0.04759, 0.004326, -0.049753, 0.955978), + float4(-0.580475, -0.643229, 0.499304, 0.896269), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(-0.011897, -0.151422, -0.058405, 0.000305), + float4(-0.012979, 0.003245, -0.058405, 0.955978), + float4(-0.976245, -0.006827, 0.216564, 0.904662), + float4(0.058405, 0.091934, 0.037855, 14.0), + float4(0.017305, -0.003245, -0.037855, 0.0), + float4(-0.014061, -0.048671, -0.037855, 0.473533), + float4(0.757369, -0.522945, 0.39105, 0.904662), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.107077, -0.140606, -0.058405, 0.0), + float4(0.089771, -0.137361, -0.02055, 0.376257), + float4(0.812052, 0.481346, 0.329966, 0.830777), + float4(-0.045426, 0.564586, 0.052998, 14.0), + float4(0.014061, -0.335291, 0.005408, 0.520989), + float4(0.001082, -0.332046, -0.052998, 0.088655), + float4(-0.975941, -0.037462, 0.214794, 0.973449), + float4(0.10924, 0.439122, 0.0, 14.0), + float4(-0.031366, -0.031366, 0.0, 0.132876), + float4(-0.154666, 0.125464, 0.052998, 0.028428), + float4(0.182761, -0.182761, 0.966021, 0.955978), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(-0.019468, -0.024876, -0.063813, 0.168276), + float4(-0.174135, 0.100587, -0.010816, 0.038209), + float4(-0.469238, -0.764881, 0.441331, 0.955978), + float4(0.201174, 0.638133, 0.0822, 14.0), + float4(-0.072466, -0.174135, -0.018387, 0.18117), + float4(-0.246601, -0.073548, -0.029203, 0.082277), + float4(-0.098561, -0.063844, 0.993081, 0.973449), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(0.055161, -0.146014, -0.025958, 0.246204), + float4(0.016224, -0.156829, -0.014061, 0.964691), + float4(0.305397, -0.053644, 0.950713, 0.973449), + float4(0.183869, 0.317985, 0.037855, 6.0), + float4(-0.036774, -0.045426, -0.037855, 0.0), + float4(-0.038937, -0.010816, 0.011897, 0.806958), + float4(0.374433, -0.753477, 0.540438, 0.854994), + float4(0.144932, 0.307169, 0.049753, 14.0), + float4(0.002163, -0.034611, -0.049753, 0.0), + float4(-0.033529, 0.0, -0.049753, 0.9131), + float4(-0.630237, -0.649931, 0.424725, 0.904662), + float4(0.10924, 0.439122, 0.0, 6.0), + float4(0.035692, -0.131953, 0.049753, 0.18117), + float4(0.002163, -0.131953, 0.0, 0.955978), + float4(-0.82919, -0.013593, 0.558802, 0.964691), + float4(0.183869, 0.317985, 0.037855, 14.0), + float4(0.02704, -0.004326, -0.037855, 0.0), + float4(0.005408, -0.037855, -0.037855, 0.514916), + float4(0.70062, -0.452013, 0.552101, 0.846876), + float4(0.210908, 0.445612, 0.0, 10.0), + float4(0.0, -0.131953, 0.0, 0.0), + float4(-0.02704, -0.127627, 0.037855, 0.439658), + float4(0.813734, 0.0, 0.581238, 0.871366), + float4(0.246601, 0.459672, 0.06165, 14.0), + float4(0.070303, -0.101669, -0.024876, 0.262257), + float4(0.034611, -0.116811, -0.015142, 0.973449), + float4(0.27928, -0.041605, 0.959308, 0.973449), + float4(0.319067, 0.319067, 0.0, 6.0), + float4(-0.037855, 0.023795, 0.046508, 0.0), + float4(-0.002163, 0.038937, 0.036774, 0.896269), + float4(0.437906, -0.604311, 0.665618, 0.896269), + float4(0.281211, 0.342862, 0.046508, 14.0), + float4(-0.004326, -0.031366, -0.046508, 0.0), + float4(-0.040019, 0.001082, -0.046508, 0.752941), + float4(-0.58651, -0.645161, 0.489667, 0.846876), + float4(0.23903, 0.45102, 0.0, 14.0), + float4(0.042182, -0.108158, 0.046508, 0.000305), + float4(0.002163, -0.107077, 0.0, 0.846876), + float4(-0.758098, -0.015315, 0.651961, 0.896269), + float4(0.316904, 0.358004, 0.036774, 14.0), + float4(0.024876, -0.004326, -0.036774, 0.0), + float4(0.002163, -0.038937, -0.036774, 0.617212), + float4(0.707338, -0.464191, 0.533104, 0.921584), + float4(0.340698, 0.474815, 0.0, 14.0), + float4(0.001082, -0.121137, 0.0, 0.0), + float4(-0.023795, -0.116811, 0.036774, 0.473533), + float4(0.828664, 0.007399, 0.559697, 0.930114), + float4(0.375309, 0.488875, 0.0, 14.0), + float4(0.078955, -0.08869, 0.032447, 0.223224), + float4(0.036774, -0.105995, 0.043263, 0.964691), + float4(0.071225, 0.39802, 0.914608, 0.973449), + float4(0.45102, 0.359085, 0.0, 6.0), + float4(-0.038937, 0.023795, 0.043263, 0.000305), + float4(0.003245, 0.0411, 0.032447, 0.879622), + float4(0.417852, -0.583049, 0.696744, 0.9131), + float4(0.412083, 0.38288, 0.043263, 14.0), + float4(-0.001082, -0.031366, -0.043263, 0.0), + float4(-0.035692, -0.001082, -0.043263, 0.745403), + float4(-0.574039, -0.656045, 0.489984, 0.854994), + float4(0.375309, 0.488875, 0.0, 14.0), + float4(0.036774, -0.105995, 0.043263, 0.000305), + float4(0.001082, -0.107077, 0.0, 0.879622), + float4(-0.771254, -0.00779, 0.63648, 0.896269), + float4(0.454264, 0.400185, 0.032447, 14.0), + float4(0.019468, -0.008653, -0.032447, 0.0), + float4(-0.003245, -0.0411, -0.032447, 0.527108), + float4(0.688617, -0.482032, 0.541712, 0.887922), + float4(0.473733, 0.603523, 0.0, 14.0), + float4(0.0, -0.21199, 0.0, 0.158953), + float4(-0.019468, -0.203337, 0.032447, 0.030716), + float4(0.857493, 0.0, 0.514496, 0.964691), + float4(0.179543, 0.466162, 0.058405, 10.0), + float4(-0.050834, -0.002163, 0.005408, 0.000305), + float4(0.021632, 0.171972, 0.023795, 0.022171), + float4(0.110896, -0.149888, 0.982464, 0.381323), + float4(0.326638, 0.830655, 0.0, 14.0), + float4(0.048671, -0.34178, 0.0, 0.007492), + float4(-0.125464, -0.192522, 0.0822, 0.054475), + float4(0.472493, 0.067285, 0.878762, 0.964691), + float4(0.326638, 0.830655, 0.0, 14.0), + float4(0.139524, -0.21199, 0.0, 0.0), + float4(0.048671, -0.34178, 0.0, 0.015213), + float4(0.0, 0.0, 1.0, 0.428687), + float4(-0.012979, 0.832818, 0.0, 14.0), + float4(0.339617, -0.002163, 0.0, 0.0), + float4(0.214153, -0.194685, 0.0822, 0.027314), + float4(0.002492, 0.391296, 0.920261, 0.49102), + float4(-0.012979, 0.832818, 0.0, 14.0), + float4(0.214153, -0.194685, 0.0822, 0.000305), + float4(-0.032447, -0.268232, 0.052998, 0.930114), + float4(-0.176863, 0.211325, 0.961281, 0.973449), + float4(-0.221724, 0.475896, 0.0, 14.0), + float4(0.139524, 0.055161, 0.043263, 0.520989), + float4(0.054079, -0.059487, 0.046508, 0.973449), + float4(-0.39307, 0.317373, 0.863001, 0.973449), + float4(-0.167645, 0.416409, 0.046508, 14.0), + float4(-0.056242, -0.008653, -0.046508, 0.000305), + float4(-0.054079, 0.059487, -0.046508, 0.955978), + float4(-0.638985, 0.020285, 0.768952, 0.973449), + float4(-0.167645, 0.416409, 0.046508, 6.0), + float4(0.073548, 0.015142, -0.046508, 0.0), + float4(0.004326, -0.035692, -0.046508, 0.768154), + float4(0.490917, -0.668482, 0.558688, 0.871366), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.017305, -0.152503, 0.043263, 0.0), + float4(-0.122219, -0.207664, 0.0, 0.723049), + float4(-0.365879, 0.215335, 0.905408, 0.854994), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.054079, -0.118974, 0.052998, 0.0), + float4(0.017305, -0.152503, 0.043263, 0.514916), + float4(-0.41958, 0.203352, 0.884647, 0.863157), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.086527, 0.149258, 0.0, 0.0), + float4(0.054079, -0.118974, 0.052998, 0.262257), + float4(-0.385563, 0.223515, 0.8952, 0.9131), + float4(-0.044345, 0.23254, 0.0, 7.0), + float4(-0.001082, 0.332046, -0.043263, 0.01033), + float4(-0.001082, 0.332046, 0.052998, 0.045182), + float4(-0.999995, -0.003257, 0.0, 0.577584), + float4(-0.045426, 0.564586, -0.043263, 7.0), + float4(-0.036774, -0.033529, 0.086527, 0.01033), + float4(0.0, 0.0, 0.096261, 0.045182), + float4(0.673754, -0.738956, 0.0, 0.577584), + float4(-0.0822, 0.531057, -0.043263, 7.0), + float4(-0.011897, -0.099506, 0.043263, 0.01033), + float4(0.0, 0.0, 0.086527, 0.045182), + float4(0.992928, -0.11872, 0.0, 0.577584), + float4(-0.094098, 0.431551, -0.043263, 7.0), + float4(-0.069221, -0.050834, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.591909, -0.806004, 0.0, 0.577584), + float4(-0.163319, 0.380717, -0.043263, 7.0), + float4(-0.060569, 0.02704, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.407651, -0.913138, 0.0, 0.577584), + float4(-0.223888, 0.407756, -0.043263, 7.0), + float4(0.002163, 0.06814, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999496, 0.03173, 0.0, 0.577584), + float4(-0.221724, 0.475896, -0.043263, 7.0), + float4(0.122219, 0.207664, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.861819, 0.507216, 0.0, 0.577584), + float4(-0.099506, 0.68356, -0.043263, 3.0), + float4(0.086527, 0.149258, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.86514, 0.501531, 0.0, 0.577584), + float4(-0.012979, 0.832818, -0.043263, 7.0), + float4(0.339617, -0.002163, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.006369, 0.99998, 0.0, 0.577584), + float4(0.326638, 0.830655, -0.043263, 7.0), + float4(0.139524, -0.21199, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.835314, 0.549773, 0.0, 0.577584), + float4(0.466162, 0.618665, -0.043263, 3.0), + float4(0.007571, -0.015142, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.894427, 0.447214, 0.0, 0.577584), + float4(0.473733, 0.603523, -0.043263, 7.0), + float4(0.0, -0.21199, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.473733, 0.391533, -0.043263, 7.0), + float4(-0.022713, -0.032447, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.819232, -0.573462, 0.0, 0.577584), + float4(0.45102, 0.359085, -0.043263, 7.0), + float4(-0.040019, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.185892, -0.98257, 0.0, 0.577584), + float4(0.411001, 0.351514, -0.043263, 7.0), + float4(-0.034611, 0.030284, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.658505, -0.752577, 0.0, 0.577584), + float4(0.376391, 0.381799, -0.043263, 7.0), + float4(-0.001082, 0.107077, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999949, -0.0101, 0.0, 0.577584), + float4(0.375309, 0.488875, -0.043263, 7.0), + float4(-0.034611, -0.014061, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.376377, -0.926467, 0.0, 0.577584), + float4(0.340698, 0.474815, -0.043263, 7.0), + float4(0.001082, -0.121137, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.99996, 0.008928, 0.0, 0.577584), + float4(0.34178, 0.353677, -0.043263, 7.0), + float4(-0.022713, -0.034611, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.836048, -0.548657, 0.0, 0.577584), + float4(0.319067, 0.319067, -0.043263, 7.0), + float4(-0.042182, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.276885, 0.311496, -0.043263, 7.0), + float4(-0.035692, 0.032447, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.672673, -0.73994, 0.0, 0.577584), + float4(0.241193, 0.343943, -0.043263, 7.0), + float4(-0.002163, 0.107077, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999796, -0.020198, 0.0, 0.577584), + float4(0.23903, 0.45102, -0.043263, 7.0), + float4(-0.028121, -0.005408, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.188847, -0.982006, 0.0, 0.577584), + float4(0.210908, 0.445612, -0.043263, 7.0), + float4(0.0, -0.131953, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.210908, 0.313659, -0.043263, 7.0), + float4(-0.021632, -0.033529, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.840297, -0.542127, 0.0, 0.577584), + float4(0.189277, 0.28013, -0.043263, 7.0), + float4(-0.042182, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.147095, 0.272559, -0.043263, 7.0), + float4(-0.035692, 0.034611, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.696146, -0.7179, 0.0, 0.577584), + float4(0.111403, 0.307169, -0.043263, 7.0), + float4(-0.002163, 0.131953, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999866, -0.016391, 0.0, 0.577584), + float4(0.10924, 0.439122, -0.043263, 7.0), + float4(-0.031366, -0.031366, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.707107, -0.707107, 0.0, 0.577584), + float4(0.077874, 0.407756, -0.043263, 7.0), + float4(-0.002163, -0.319067, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.999977, -0.006779, 0.0, 0.577584), + float4(0.075711, 0.08869, -0.043263, 7.0), + float4(-0.031366, -0.045426, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.822897, -0.568191, 0.0, 0.577584), + float4(0.044345, 0.043263, -0.043263, 7.0), + float4(-0.043263, -0.005408, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.124035, -0.992278, 0.0, 0.577584), + float4(0.001082, 0.037855, -0.043263, 7.0), + float4(-0.044345, 0.040019, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.669964, -0.742393, 0.0, 0.577584), + float4(-0.043263, 0.077874, -0.043263, 7.0), + float4(-0.001082, 0.154666, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999976, -0.006993, 0.0, 0.577584), + float4(0.044345, 0.043263, 0.0, 10.0), + float4(-0.043263, -0.005408, 0.0, 0.000305), + float4(-0.040019, 0.030284, 0.049753, 0.964691), + float4(0.101434, -0.811469, 0.575525, 0.973449), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.035692, -0.155748, -0.008653, 0.000305), + float4(-0.011897, -0.151422, -0.058405, 0.955978), + float4(-0.716533, -0.20131, 0.667874, 0.904662), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.10924, 0.178461, -0.058405, 0.0), + float4(0.107077, -0.140606, -0.058405, 0.376257), + float4(0.475583, -0.003224, 0.879665, 0.830777), + float4(-0.045426, 0.564586, 0.052998, 14.0), + float4(0.1233, -0.156829, -0.052998, 0.520989), + float4(0.014061, -0.335291, 0.005408, 0.088655), + float4(0.429355, 0.032562, 0.902549, 0.973449), + float4(0.183869, 0.317985, 0.037855, 10.0), + float4(0.005408, -0.037855, -0.037855, 0.0), + float4(-0.036774, -0.045426, -0.037855, 0.806958), + float4(0.124328, -0.692684, 0.710445, 0.854994), + float4(0.10924, 0.439122, 0.0, 10.0), + float4(0.019468, 0.024876, 0.063813, 0.18117), + float4(0.035692, -0.131953, 0.049753, 0.955978), + float4(-0.933936, -0.126584, 0.334276, 0.964691), + float4(0.183869, 0.317985, 0.037855, 10.0), + float4(-0.055161, 0.146014, 0.025958, 0.0), + float4(0.02704, 0.127627, -0.037855, 0.439658), + float4(0.623841, 0.097823, 0.775405, 0.871366), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(0.050834, 0.002163, -0.005408, 0.0), + float4(0.0822, -0.018387, -0.063813, 0.439658), + float4(0.07859, -0.92643, 0.368172, 0.871366), + float4(0.319067, 0.319067, 0.0, 10.0), + float4(-0.042182, -0.007571, 0.0, 0.0), + float4(-0.037855, 0.023795, 0.046508, 0.896269), + float4(0.1483, -0.826245, 0.54344, 0.896269), + float4(0.23903, 0.45102, 0.0, 15.0), + float4(0.007571, 0.008653, 0.06165, 0.000305), + float4(0.042182, -0.108158, 0.046508, 0.846876), + float4(-0.94107, -0.299262, 0.157572, 0.896269), + float4(0.316904, 0.358004, 0.036774, 14.0), + float4(-0.070303, 0.101669, 0.024876, 0.0), + float4(0.023795, 0.116811, -0.036774, 0.473533), + float4(0.523424, 0.157027, 0.837478, 0.930114), + float4(0.246601, 0.459672, 0.06165, 14.0), + float4(0.030284, 0.024876, -0.003245, 0.0), + float4(0.094098, 0.015142, -0.06165, 0.473533), + float4(0.518869, -0.545855, 0.657888, 0.930114), + float4(0.45102, 0.359085, 0.0, 10.0), + float4(-0.040019, -0.007571, 0.0, 0.000305), + float4(-0.038937, 0.023795, 0.043263, 0.879622), + float4(0.151736, -0.802035, 0.577682, 0.9131), + float4(0.454264, 0.400185, 0.032447, 12.0), + float4(-0.078955, 0.08869, -0.032447, 0.158953), + float4(0.019468, 0.203337, -0.032447, 0.030716), + float4(-0.201684, 0.173144, 0.964025, 0.964691), + float4(0.375309, 0.488875, 0.0, 10.0), + float4(0.090853, 0.12979, 0.0, 0.158953), + float4(0.098424, 0.114648, 0.0, 0.030716), + float4(0.0, 0.0, 1.0, 0.964691), + float4(0.340698, 0.474815, 0.0, 14.0), + float4(-0.063813, 0.009734, 0.058405, 0.000305), + float4(0.034611, 0.014061, 0.0, 0.022171), + float4(0.3276, -0.806402, 0.492334, 0.381323), + float4(0.276885, 0.484549, 0.058405, 10.0), + float4(-0.030284, -0.024876, 0.003245, 0.000305), + float4(-0.097342, -0.018387, 0.0, 0.022171), + float4(-0.03153, 0.166923, 0.985466, 0.381323), + float4(0.201174, 0.638133, 0.0822, 8.0), + float4(0.075711, -0.153585, -0.023795, 0.000305), + float4(-0.021632, -0.171972, -0.023795, 0.022171), + float4(0.026497, -0.140281, 0.989757, 0.381323), + float4(0.276885, 0.484549, 0.058405, 14.0), + float4(-0.075711, 0.153585, 0.023795, 0.000305), + float4(0.098424, 0.004326, -0.058405, 0.022171), + float4(0.50316, 0.115345, 0.856461, 0.381323), + float4(0.246601, 0.459672, 0.06165, 11.0), + float4(-0.007571, -0.008653, -0.06165, 0.000305), + float4(-0.035692, -0.014061, -0.06165, 0.022171), + float4(0.187618, -0.975617, 0.113888, 0.381323), + float4(0.210908, 0.445612, 0.0, 10.0), + float4(-0.031366, 0.02055, 0.058405, 0.000305), + float4(0.035692, 0.014061, 0.06165, 0.022171), + float4(-0.105864, -0.954447, 0.278971, 0.381323), + float4(-0.167645, 0.416409, 0.046508, 14.0), + float4(0.004326, -0.035692, -0.046508, 0.000305), + float4(-0.056242, -0.008653, -0.046508, 0.955978), + float4(-0.339782, -0.761112, 0.552501, 0.973449), + float4(-0.167645, 0.416409, 0.046508, 10.0), + float4(0.085445, 0.114648, -0.003245, 0.0), + float4(0.073548, 0.015142, -0.046508, 0.768154), + float4(0.548356, -0.38771, 0.740936, 0.871366), + float4(-0.044345, 0.23254, 0.0, 3.0), + float4(0.0, 0.0, -0.043263, 0.01033), + float4(-0.001082, 0.332046, -0.043263, 0.045182), + float4(-0.999995, -0.003257, 0.0, 0.577584), + float4(-0.045426, 0.564586, -0.043263, 11.0), + float4(-0.036774, -0.033529, 0.0, 0.01033), + float4(-0.036774, -0.033529, 0.086527, 0.045182), + float4(0.673754, -0.738956, 0.0, 0.577584), + float4(-0.0822, 0.531057, -0.043263, 11.0), + float4(-0.011897, -0.099506, 0.0, 0.01033), + float4(-0.011897, -0.099506, 0.043263, 0.045182), + float4(0.992928, -0.11872, 0.0, 0.577584), + float4(-0.094098, 0.431551, -0.043263, 11.0), + float4(-0.069221, -0.050834, 0.0, 0.01033), + float4(-0.069221, -0.050834, 0.043263, 0.045182), + float4(0.591909, -0.806004, 0.0, 0.577584), + float4(-0.163319, 0.380717, -0.043263, 11.0), + float4(-0.060569, 0.02704, 0.0, 0.01033), + float4(-0.060569, 0.02704, 0.043263, 0.045182), + float4(-0.407651, -0.913138, 0.0, 0.577584), + float4(-0.223888, 0.407756, -0.043263, 11.0), + float4(0.002163, 0.06814, 0.0, 0.01033), + float4(0.002163, 0.06814, 0.043263, 0.045182), + float4(-0.999496, 0.03173, 0.0, 0.577584), + float4(-0.221724, 0.475896, -0.043263, 9.0), + float4(0.122219, 0.207664, 0.0, 0.01033), + float4(0.122219, 0.207664, 0.043263, 0.045182), + float4(-0.861819, 0.507216, 0.0, 0.577584), + float4(-0.099506, 0.68356, -0.043263, 11.0), + float4(0.086527, 0.149258, 0.0, 0.01033), + float4(0.086527, 0.149258, 0.043263, 0.045182), + float4(-0.86514, 0.501531, 0.0, 0.577584), + float4(-0.012979, 0.832818, -0.043263, 11.0), + float4(0.339617, -0.002163, 0.0, 0.01033), + float4(0.339617, -0.002163, 0.043263, 0.045182), + float4(0.006369, 0.99998, 0.0, 0.577584), + float4(0.326638, 0.830655, -0.043263, 9.0), + float4(0.139524, -0.21199, 0.0, 0.01033), + float4(0.139524, -0.21199, 0.043263, 0.045182), + float4(0.835314, 0.549773, 0.0, 0.577584), + float4(0.466162, 0.618665, -0.043263, 11.0), + float4(0.007571, -0.015142, 0.0, 0.01033), + float4(0.007571, -0.015142, 0.043263, 0.045182), + float4(0.894427, 0.447214, 0.0, 0.577584), + float4(0.473733, 0.603523, -0.043263, 11.0), + float4(0.0, -0.21199, 0.0, 0.01033), + float4(0.0, -0.21199, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.473733, 0.391533, -0.043263, 11.0), + float4(-0.022713, -0.032447, 0.0, 0.01033), + float4(-0.022713, -0.032447, 0.043263, 0.045182), + float4(0.819232, -0.573462, 0.0, 0.577584), + float4(0.45102, 0.359085, -0.043263, 11.0), + float4(-0.040019, -0.007571, 0.0, 0.01033), + float4(-0.040019, -0.007571, 0.043263, 0.045182), + float4(0.185892, -0.98257, 0.0, 0.577584), + float4(0.411001, 0.351514, -0.043263, 11.0), + float4(-0.034611, 0.030284, 0.0, 0.01033), + float4(-0.034611, 0.030284, 0.043263, 0.045182), + float4(-0.658505, -0.752577, 0.0, 0.577584), + float4(0.376391, 0.381799, -0.043263, 11.0), + float4(-0.001082, 0.107077, 0.0, 0.01033), + float4(-0.001082, 0.107077, 0.043263, 0.045182), + float4(-0.999949, -0.0101, 0.0, 0.577584), + float4(0.375309, 0.488875, -0.043263, 11.0), + float4(-0.034611, -0.014061, 0.0, 0.01033), + float4(-0.034611, -0.014061, 0.043263, 0.045182), + float4(0.376377, -0.926467, 0.0, 0.577584), + float4(0.340698, 0.474815, -0.043263, 11.0), + float4(0.001082, -0.121137, 0.0, 0.01033), + float4(0.001082, -0.121137, 0.043263, 0.045182), + float4(0.99996, 0.008928, 0.0, 0.577584), + float4(0.34178, 0.353677, -0.043263, 11.0), + float4(-0.022713, -0.034611, 0.0, 0.01033), + float4(-0.022713, -0.034611, 0.043263, 0.045182), + float4(0.836048, -0.548657, 0.0, 0.577584), + float4(0.319067, 0.319067, -0.043263, 11.0), + float4(-0.042182, -0.007571, 0.0, 0.01033), + float4(-0.042182, -0.007571, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.276885, 0.311496, -0.043263, 11.0), + float4(-0.035692, 0.032447, 0.0, 0.01033), + float4(-0.035692, 0.032447, 0.043263, 0.045182), + float4(-0.672673, -0.73994, 0.0, 0.577584), + float4(0.241193, 0.343943, -0.043263, 11.0), + float4(-0.002163, 0.107077, 0.0, 0.01033), + float4(-0.002163, 0.107077, 0.043263, 0.045182), + float4(-0.999796, -0.020198, 0.0, 0.577584), + float4(0.23903, 0.45102, -0.043263, 11.0), + float4(-0.028121, -0.005408, 0.0, 0.01033), + float4(-0.028121, -0.005408, 0.043263, 0.045182), + float4(0.188847, -0.982006, 0.0, 0.577584), + float4(0.210908, 0.445612, -0.043263, 11.0), + float4(0.0, -0.131953, 0.0, 0.01033), + float4(0.0, -0.131953, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.210908, 0.313659, -0.043263, 11.0), + float4(-0.021632, -0.033529, 0.0, 0.01033), + float4(-0.021632, -0.033529, 0.043263, 0.045182), + float4(0.840297, -0.542127, 0.0, 0.577584), + float4(0.189277, 0.28013, -0.043263, 11.0), + float4(-0.042182, -0.007571, 0.0, 0.01033), + float4(-0.042182, -0.007571, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.147095, 0.272559, -0.043263, 11.0), + float4(-0.035692, 0.034611, 0.0, 0.01033), + float4(-0.035692, 0.034611, 0.043263, 0.045182), + float4(-0.696146, -0.7179, 0.0, 0.577584), + float4(0.111403, 0.307169, -0.043263, 11.0), + float4(-0.002163, 0.131953, 0.0, 0.01033), + float4(-0.002163, 0.131953, 0.043263, 0.045182), + float4(-0.999866, -0.016391, 0.0, 0.577584), + float4(0.10924, 0.439122, -0.043263, 11.0), + float4(-0.031366, -0.031366, 0.0, 0.01033), + float4(-0.031366, -0.031366, 0.043263, 0.045182), + float4(0.707107, -0.707107, 0.0, 0.577584), + float4(0.077874, 0.407756, -0.043263, 11.0), + float4(-0.002163, -0.319067, 0.0, 0.01033), + float4(-0.002163, -0.319067, 0.043263, 0.045182), + float4(0.999977, -0.006779, 0.0, 0.577584), + float4(0.075711, 0.08869, -0.043263, 11.0), + float4(-0.031366, -0.045426, 0.0, 0.01033), + float4(-0.031366, -0.045426, 0.043263, 0.045182), + float4(0.822897, -0.568191, 0.0, 0.577584), + float4(0.044345, 0.043263, -0.043263, 11.0), + float4(-0.043263, -0.005408, 0.0, 0.01033), + float4(-0.043263, -0.005408, 0.043263, 0.045182), + float4(0.124035, -0.992278, 0.0, 0.577584), + float4(0.001082, 0.037855, -0.043263, 11.0), + float4(-0.044345, 0.040019, 0.0, 0.01033), + float4(-0.044345, 0.040019, 0.043263, 0.045182), + float4(-0.669964, -0.742393, 0.0, 0.577584), + float4(-0.043263, 0.077874, -0.043263, 9.0), + float4(-0.001082, 0.154666, 0.0, 0.01033), + float4(-0.001082, 0.154666, 0.043263, 0.045182), + float4(-0.999976, -0.006993, 0.0, 0.577584) +}; +constant float2 PRISM_POLY[110] = { + float2(0.006442, -0.002147), + float2(-0.001074, 0.001074), + float2(-0.004294, 0.007515), + float2(-0.004294, 0.806288), + float2(-0.001074, 0.809509), + float2(0.004294, 0.809509), + float2(0.022546, 0.796626), + float2(0.185736, 0.655982), + float2(0.281288, 0.86319), + float2(0.286656, 0.870706), + float2(0.293098, 0.871779), + float2(0.465951, 0.781595), + float2(0.478834, 0.773006), + float2(0.478834, 0.763344), + float2(0.407975, 0.592638), + float2(0.45092, 0.586196), + float2(0.601227, 0.573313), + float2(0.606595, 0.569018), + float2(0.606595, 0.564724), + float2(0.601227, 0.556135), + float2(0.50138, 0.453067), + float2(0.425153, 0.37684), + float2(0.289877, 0.246933), + float2(0.081595, 0.057975), + float2(0.013957, 0.0), + float2(-0.011897, 0.0), + float2(-0.055161, 0.037855), + float2(-0.081119, 0.063813), + float2(-0.083282, 0.06814), + float2(-0.084363, 0.38288), + float2(-0.089771, 0.38288), + float2(-0.158993, 0.33529), + float2(-0.166564, 0.336372), + float2(-0.250927, 0.379635), + float2(-0.261743, 0.388288), + float2(-0.263906, 0.461836), + float2(-0.262824, 0.486712), + float2(-0.173053, 0.638133), + float2(-0.044345, 0.860939), + float2(-0.036774, 0.867429), + float2(-0.024876, 0.871755), + float2(0.33529, 0.871755), + float2(0.349351, 0.867429), + float2(0.363412, 0.853368), + float2(0.513752, 0.619747), + float2(0.513752, 0.379635), + float2(0.466162, 0.322311), + float2(0.461836, 0.320148), + float2(0.399104, 0.311496), + float2(0.393696, 0.313659), + float2(0.374227, 0.332046), + float2(0.343943, 0.288782), + float2(0.336372, 0.282293), + float2(0.269314, 0.271477), + float2(0.263906, 0.27364), + float2(0.243356, 0.292027), + float2(0.21199, 0.244438), + float2(0.207664, 0.241193), + float2(0.149258, 0.231459), + float2(0.138443, 0.231459), + float2(0.118974, 0.248764), + float2(0.116811, 0.222806), + float2(0.117892, 0.162237), + float2(0.115729, 0.076792), + float2(0.06814, 0.011897), + float2(0.062732, 0.007571), + float2(0.005408, 0.0), + float2(0.031135, 0.06227), + float2(0.252301, 0.266258), + float2(0.541104, 0.544325), + float2(0.359663, 0.559356), + float2(0.433742, 0.753681), + float2(0.304908, 0.821319), + float2(0.199693, 0.595859), + float2(0.035429, 0.740798), + float2(0.035429, 0.482055), + float2(-0.043263, 0.077874), + float2(0.001082, 0.037855), + float2(0.044345, 0.043263), + float2(0.075711, 0.08869), + float2(0.077874, 0.407756), + float2(0.10924, 0.439122), + float2(0.111403, 0.307169), + float2(0.147095, 0.272559), + float2(0.189277, 0.28013), + float2(0.210909, 0.313659), + float2(0.210909, 0.445612), + float2(0.23903, 0.45102), + float2(0.241193, 0.343943), + float2(0.276885, 0.311496), + float2(0.319067, 0.319067), + float2(0.34178, 0.353677), + float2(0.340698, 0.474815), + float2(0.375309, 0.488875), + float2(0.376391, 0.381799), + float2(0.411001, 0.351514), + float2(0.45102, 0.359085), + float2(0.473733, 0.391533), + float2(0.473733, 0.603523), + float2(0.466162, 0.618665), + float2(0.326638, 0.830655), + float2(-0.012979, 0.832818), + float2(-0.099506, 0.68356), + float2(-0.221724, 0.475896), + float2(-0.223888, 0.407756), + float2(-0.163319, 0.380717), + float2(-0.094098, 0.431551), + float2(-0.0822, 0.531057), + float2(-0.045426, 0.564586), + float2(-0.044345, 0.23254) +}; +constant float PRISM_BEVEL = 0.012883; +// end of the prism mesh + +constant float PRISM_IOR = 1.61; +constant float PRISM_DISPERSION = 0.035; +constant float PRISM_GLOW = 0.45; +constant float PRISM_FOLD = 0.35; +constant int PRISM_BOUNCES = 6; +constant float PRISM_MAT = 9.0; + +inline int prism_shape(constant Layer &layer) +{ + int id = sculpt_id(layer); + return id == 3 || id == 4 ? id - 3 : -1; +} + +static float prism_poly_dist(float2 p, int i0, int n) +{ + float2 vj = PRISM_POLY[i0 + n - 1]; + float d = 1e9; + float sgn = 1.0; + for (int i = 0; i < n; i++) + { + float2 vi = PRISM_POLY[i0 + i]; + float2 e = vj - vi; + float2 w = p - vi; + float2 b = w - e * saturate(dot(w, e) / dot(e, e)); + d = min(d, dot(b, b)); + bool c0 = p.y >= vi.y; + bool c1 = p.y < vj.y; + bool c2 = e.x * w.y > e.y * w.x; + if ((c0 && c1 && c2) || (!c0 && !c1 && !c2)) + { + sgn = -sgn; + } + vj = vi; + } + return sgn * sqrt(d); +} + +static float prism_rim(float3 p, int shape, constant Layer &layer) +{ + float half_t = model_thick(layer) * 0.5; + float sil = prism_poly_dist(p.xy, PRISM_SIL_START[shape], PRISM_SIL_COUNT[shape]); + float2 w = float2(sil + PRISM_BEVEL, abs(p.z + half_t) - (half_t - PRISM_BEVEL)); + return min(max(w.x, w.y), 0.0) + length(max(w, 0.0)) - PRISM_BEVEL; +} + +static float prism_tri(float3 ro, float3 rd, int k) +{ + float3 v0 = PRISM_TRIS[4 * k].xyz; + float3 e1 = PRISM_TRIS[4 * k + 1].xyz; + float3 e2 = PRISM_TRIS[4 * k + 2].xyz; + float3 pv = cross(rd, e2); + float det = dot(e1, pv); + if (abs(det) < 1e-10) + { + return -1.0; + } + float inv = 1.0 / det; + float3 sv = ro - v0; + float u = dot(sv, pv) * inv; + float3 qv = cross(sv, e1); + float v = dot(rd, qv) * inv; + if (u < -2e-4 || v < -2e-4 || u + v > 1.0004) + { + return -1.0; + } + return dot(e2, qv) * inv; +} + +static float prism_trace(float3 ro, float3 rd, int shape, thread int &hit) +{ + int k0 = PRISM_TRI_START[shape]; + int n = PRISM_TRI_COUNT[shape]; + float best = 1e9; + hit = -1; + for (int i = 0; i < n; i++) + { + float t = prism_tri(ro, rd, k0 + i); + if (t > 1e-5 && t < best) + { + best = t; + hit = k0 + i; + } + } + return best; +} + static float2 sculpt_proto(float3 p, int theme, int shape) { if (theme == 3) @@ -997,6 +1861,10 @@ static float2 sculpt_eval(float3 q, bool occ, constant Layer &layer) { int id = sculpt_id(layer) - 1; int theme = id / 2, shape = id % 2; + if (theme == 1) + { + return float2(prism_rim(q, shape, layer), 8.0); + } float3 p = sculpt_point(q, layer); float2 r; if (occ && theme == 3) @@ -1129,6 +1997,7 @@ static SculptMat sculpt_material(float mat, float3 p, int theme, int shape) if (mat < 2.5) return s_mat(s_lin(0.95, 0.92, 0.85), 0.3, 0.6, 0.35, 0.25); return s_mat(s_lin(0.1, 0.1, 0.11), 0.55, 0.05, 0.0, 0.1); } + if (theme == 1) return s_mat(s_lin(0.02, 0.05, 0.33), 0.35, 0.5, 0.05, 0.3); if (theme == 2) { if ((primary && shape == 0) || (mat > 2.5 && mat < 3.5 && shape == 1)) return s_mat(s_lin(1.0, 0.40, 0.30), 0.85, 0.08, 0.15, 0.03); @@ -1186,6 +2055,10 @@ static float3 model_shade(float3 q, float3 n, float3 rd, float3 L, float fall, f { float3 p = sculpt_point(q, layer) - float3(n.x, -n.y, n.z) * 0.01; m = sculpt_material(mat, p, (id - 1) / 2, (id - 1) % 2); + if (id == 3 || id == 4) + { + gloss = 1.0 - smoothstep(0.97, 0.995, abs(n.z)); + } } else { @@ -1209,6 +2082,144 @@ static float3 model_shade(float3 q, float3 n, float3 rd, float3 L, float fall, f return model_tonemap(col); } +// ---- Le cristal de Prism Glow ---- (cf. HLSL) + +inline float prism_fresnel(float c, float ior) +{ + float f0 = (ior - 1.0) / (ior + 1.0); + f0 *= f0; + return f0 + (1.0 - f0) * pow(1.0 - c, 5.0); +} + +static float prism_primary(float3 ro, float3 rd, thread int &hc, constant Layer &layer) +{ + hc = -1; + int shape = prism_shape(layer); + if (shape < 0) + { + return 1e9; + } + float t = prism_trace(ro, rd, shape, hc); + if (hc >= 0 && fmod(PRISM_TRIS[4 * hc].w, 2.0) > 0.5 && ro.z + rd.z * t < 0.0) + { + hc = -1; + return 1e9; + } + return t; +} + +inline float3 prism_normal(int h, float3 rd) +{ + float3 n = PRISM_TRIS[4 * h + 3].xyz; + return dot(n, rd) > 0.0 ? -n : n; +} + +inline float prism_seg(float3 p, float3 a, float3 b) +{ + float3 pa = p - a, ba = b - a; + return length(pa - ba * saturate(dot(pa, ba) / dot(ba, ba))); +} + +static float prism_edge_px(float3 p, int h, float px) +{ + float4 r0 = PRISM_TRIS[4 * h]; + float3 a = r0.xyz; + float3 b = a + PRISM_TRIS[4 * h + 1].xyz; + float3 c = a + PRISM_TRIS[4 * h + 2].xyz; + int bits = int(r0.w * 0.5); + float d = 1e9; + if ((bits & 1) != 0) d = min(d, prism_seg(p, b, c)); + if ((bits & 2) != 0) d = min(d, prism_seg(p, c, a)); + if ((bits & 4) != 0) d = min(d, prism_seg(p, a, b)); + return d / px; +} + +static float3 prism_screen(float3 q, float3 d, float3 nz, float hz, float3 tip, float unit, ModelFrame f, + constant Layer &layer, texture2d texY, + texture2d texUV) +{ + float denom = dot(d, nz); + if (denom > -1e-4) + { + return SCULPT_SCREEN * 0.2; + } + float3 g = q + d * ((hz - dot(q, nz)) / denom); + float2 fp = (tip + unit * model_to_plane(g, f)).xy / (2.0 * layer.mb.xy) + 0.5; + if (any(fp < 0.0) || any(fp > 1.0)) + { + return SCULPT_SCREEN * 0.2; + } + return pow(sample_yuv_level(mix(layer.trail_b.xy, layer.trail_b.zw, fp), texY, texUV), float3(2.2)); +} + +static float3 prism_shade(float3 p, float3 n, float3 rd, int h, float px, float3 l, float3 fill, float3 nz, + float hz, float3 tip, float unit, ModelFrame f, constant Layer &layer, + texture2d texY, texture2d texUV) +{ + int shape = prism_shape(layer); + float4 r3 = PRISM_TRIS[4 * h + 3]; + float3 facet = float3(PRISM_TRIS[4 * h + 1].w, PRISM_TRIS[4 * h + 2].w, r3.w); + float F = prism_fresnel(saturate(-dot(rd, n)), PRISM_IOR); + float zb = -model_thick(layer); + float3 trans = float3(0.0); + for (int ch = 0; ch < 3; ch++) + { + float3 mask = float3(float(ch == 0), float(ch == 1), float(ch == 2)); + float ior = PRISM_IOR + PRISM_DISPERSION * float(ch - 1); + float3 d = refract(rd, n, 1.0 / ior); + float3 pos = p; + float thr = 1.0; + float acc = 0.0; + for (int b = 0; b < PRISM_BOUNCES; b++) + { + int hh; + float th = prism_trace(pos, d, shape, hh); + float tb = d.z < -1e-6 ? (zb - pos.z) / d.z : 1e9; + if (tb <= th) + { + float3 qb = pos + d * tb; + float3 d2 = refract(d, float3(0.0, 0.0, 1.0), ior); + if (dot(d2, d2) < 1e-8) + { + pos = qb; + d = reflect(d, float3(0.0, 0.0, 1.0)); + continue; + } + acc += thr * dot(prism_screen(qb, d2, nz, hz, tip, unit, f, layer, texY, texUV), mask); + thr = 0.0; + break; + } + if (hh < 0) + { + break; + } + pos += d * th; + float4 rn = PRISM_TRIS[4 * hh + 3]; + if (fmod(PRISM_TRIS[4 * hh].w, 2.0) > 0.5 && pos.z < 0.0) + { + acc += thr * dot(s_lin(0.03, 0.06, 0.37) * 0.35, mask); + thr = 0.0; + break; + } + float3 dout = refract(d, -rn.xyz, ior); + if (dot(dout, dout) < 1e-8) + { + d = reflect(d, -rn.xyz); + continue; + } + float fi = prism_fresnel(saturate(dot(dout, rn.xyz)), ior); + acc += thr * (1.0 - fi) * dot(model_env(dout, 0.05, l, fill), mask); + thr *= fi; + d = reflect(d, -rn.xyz); + } + trans += acc * mask; + } + float3 col = F * model_env(reflect(rd, n), 0.05, l, fill) + (1.0 - F) * trans + facet * PRISM_GLOW; + float fold = 1.0 - smoothstep(0.2, 1.0, prism_edge_px(p, h, px)); + col = mix(col, s_lin(0.96, 0.99, 1.0) * 1.3, PRISM_FOLD * fold * (0.35 + 0.65 * saturate(dot(n, l)))); + return model_tonemap(col); +} + // Les passes de la boucle unique de `cursor_model` (cf. HLSL : un seul appel de `model_eval`). constant int STAGE_MARCH = 0; constant int STAGE_NORMAL = 1; @@ -1228,7 +2239,9 @@ inline float3 model_tetra(int k) static float4 cursor_model(float2 local, constant Layer &layer, texture2d texSdf, - texture2d texImg) + texture2d texImg, + texture2d texY, + texture2d texUV) { ModelFrame f; f.c = cos(layer.fx.xyz); @@ -1259,6 +2272,9 @@ static float4 cursor_model(float2 local, constant Layer &layer, float2 tb = ray_box(ro, ray, lo - 0.02, hi + 0.02); int stage = tb.x < tb.y && tb.y > 0.0 ? STAGE_MARCH : STAGE_DONE; bool plane_next = stage == STAGE_DONE; + bool need_tc = stage == STAGE_MARCH; + float tc = 1e9; + int hc = -1; int k = 0; float t = max(tb.x, 0.0); float best = 1e9; @@ -1283,6 +2299,11 @@ static float4 cursor_model(float2 local, constant Layer &layer, float4 acc = float4(0.0); for (int it = 0; it < 500; it++) { + if (need_tc) + { + need_tc = false; + tc = prism_primary(ro, ray, hc, layer); + } if (plane_next) { plane_next = false; @@ -1305,9 +2326,17 @@ static float4 cursor_model(float2 local, constant Layer &layer, { if (cov_s > 0.0) { - float3 tl = lamp - q; - float fall = SCULPT_LAMP_DIST * SCULPT_LAMP_DIST / dot(tl, tl); - float3 c = model_shade(q, n, ray, normalize(tl), fall, sh, ao, mat, l, fill, layer, texSdf, texImg); + float3 c; + if (mat > PRISM_MAT - 0.5) + { + c = prism_shade(q, n, ray, hc, t_best / dlen, l, fill, nz, hz, tip, unit, f, layer, texY, texUV); + } + else + { + float3 tl = lamp - q; + float fall = SCULPT_LAMP_DIST * SCULPT_LAMP_DIST / dot(tl, tl); + c = model_shade(q, n, ray, normalize(tl), fall, sh, ao, mat, l, fill, layer, texSdf, texImg); + } acc += float4(c * cov_s, cov_s); } shaded++; @@ -1328,6 +2357,7 @@ static float4 cursor_model(float2 local, constant Layer &layer, t = max(tb.x, 0.0); best = 1e9; t_best = t; + need_tc = true; } } continue; @@ -1378,6 +2408,14 @@ static float4 cursor_model(float2 local, constant Layer &layer, } t += d * stride; k++; + if (!hit && t >= tc) + { + hit = true; + best = 0.0; + t_best = tc; + d_best = 0.0; + mat = PRISM_MAT; + } if (hit || t > tb.y || k == 96) { cov_s = saturate(1.0 - best); @@ -1406,6 +2444,24 @@ static float4 cursor_model(float2 local, constant Layer &layer, plane_next = true; } } + if (stage == STAGE_NORMAL && mat > PRISM_MAT - 0.5) + { + n = prism_normal(hc, ray); + k = 0; + if (shaded > 0) + { + stage = STAGE_SHADE; + } + else + { + if (prism_edge_px(q, hc, t_best / dlen) < 0.75) + { + sub = 3; + } + stage = STAGE_DONE; + plane_next = true; + } + } } } else if (stage == STAGE_NORMAL) @@ -1425,6 +2481,18 @@ static float4 cursor_model(float2 local, constant Layer &layer, { sub = 3; } + if (k == 0 && mat > 7.5) + { + int ps = prism_shape(layer); + if (ps >= 0) + { + float dout = prism_poly_dist(q.xy, PRISM_OUT_START[ps], PRISM_OUT_COUNT[ps]); + if (abs(dout) < 0.75 * t_best / dlen) + { + sub = 3; + } + } + } k++; if (k == 4) { @@ -2171,7 +3239,7 @@ fragment float4 ps_main(VSOut i [[stage_in]], { return float4(0.0, 0.0, 0.0, 0.0); } - return cursor_model(i.local, layer, texSdf, texImg); + return cursor_model(i.local, layer, texSdf, texImg, texY, texUV); } // mode 14 : CADRE DE FENÊTRE autour de l'écran, dessiné SOUS lui. Cf. commentaires HLSL. diff --git a/crates/compositor/src/vk_shaders/layer.wgsl b/crates/compositor/src/vk_shaders/layer.wgsl index a1a84f6ac..cb3a51083 100644 --- a/crates/compositor/src/vk_shaders/layer.wgsl +++ b/crates/compositor/src/vk_shaders/layer.wgsl @@ -29,21 +29,26 @@ struct Layer { dst_prev: vec4, // mode 8 : .xy = taille du plan en px AVANT projection (le rayon y vit), .z = 1 si coins hauts carres (sous un cadre), .w = 1 si warp projectif ; mode 14 : .xy = taille du plan du cadre, .z = hauteur de la barre, .w = epaisseur du filet (px du plan) ; modes 13 et 15 : rect de clip ; mode 9 : barbe 2 ; mode 10 incliné : coins TL,TR du masque ; mode 17 : (angle du socle depuis le plan en rad, rayon de l'ouverture et recouvrement de la lunette en unites du modele, penombre de l'ombre ou 0) mb: vec4, // mode 8 : [gx, gy, z_focus, k], profondeur du plan et flou (texels source) par px d'ecart, k = 0 coupe ; mode 0 : .x taps, .y force du flou, .w = 1 si coins hauts carres (sous un cadre) ; mode 5 : mb.x = aspect w/h de la sortie (fond anime) ; mode 12 : mb.y = spread de la pénombre en px ; mode 9 : mb.y = demi-épaisseur du trait en px ; mode 10 : mb.z = 1 si masque incliné, mb.w = 1 si son warp est projectif ; mode 13 : mb.x = 1 si warp projectif ; mode 14 : couleur du filet (alpha droit) ; modes 15 et 17 : .xy = demi-taille du plan dans son repere (px pour le 15, unites pour le 17), .zw = translation du plan (repere camera, px) ; mode 18 : .z = 1 si le plan est incline, .w = 1 si son warp est projectif trail_a: vec4, // mode 8 : coins TL, TR du plan a la frame precedente (px locaux, comme fx) ; mode 18 incline : en fractions de sortie - trail_b: vec4, // mode 8 : coins BR, BL du plan a la frame precedente (comme src_prev) ; mode 18 incline : en fractions de sortie + trail_b: vec4, // mode 8 : coins BR, BL du plan a la frame precedente (comme src_prev) ; mode 18 incline : en fractions de sortie ; mode 15 : la coupe du plan en uv de l'ecran (u0, v0, u1, v1), que refracte le cristal de Prism Glow trail_mb: vec4, // mode 8 : x = taps, y = force du flou de mouvement (ceux du mode 0) ; mode 18 incline : le `mb` du mode 8 (profondeur de champ), et `color.xy` sa lampe ; 0 ailleurs } @group(0) @binding(0) var layer: Layer; -@group(0) @binding(1) var texY: texture_2d; // R8Unorm, sample .r ; modes 7, 13 et 15 : le sprite RGBA -@group(0) @binding(2) var texU: texture_2d; // R8Unorm, sample .r ; mode 15 : le champ R16F du sprite +@group(0) @binding(1) var texY: texture_2d; // R8Unorm, sample .r ; modes 7 et 13 : le sprite RGBA +@group(0) @binding(2) var texU: texture_2d; // R8Unorm, sample .r @group(0) @binding(3) var samp: sampler; // Masque de segmentation du sujet webcam, R8. Une vue 1x1 est liee quand aucun masque // n'existe : la branche n'est de toute facon prise que si layer.fx.z > 0.5. -// Mode 18 : ce binding porte a la place le rendu isole de l'ecran cadre. +// Mode 18 : ce binding porte a la place le rendu isole de l'ecran cadre ; mode 15, le champ R16F +// du sprite. @group(0) @binding(4) var texMask: texture_2d; // Pyramide RGBA de profondeur de champ (`tilted_sample`), lue par le mode 8 et par le repli du -// mode 18, qui garde le binding 4 pour son rendu isole : d'ou un binding a elle. Vue 1x1 ailleurs. +// mode 18, qui garde le binding 4 pour son rendu isole : d'ou un binding a elle. Mode 15 : le +// sprite RGBA. Vue 1x1 ailleurs. @group(0) @binding(6) var texDof: texture_2d; +// Le maillage de Prism Glow (mode 15), rempli de `prism_mesh.rs` : un uniform et non des tables +// dans le shader, que lavapipe recopierait a chaque pixel de chaque calque (cf. `PrismMesh`). +@group(0) @binding(7) var prism: PrismMesh; // Plafond de la profondeur de champ du mode 8, en niveau de la pyramide demi-resolution. // Meme valeur que `DOF_MAX_LOD` du HLSL. @@ -568,9 +573,10 @@ fn blur_webcam_bg(uv: vec2, intensity: f32, qpx: vec2, local_px: vec2< // propres et occlusion, et qui porte une ombre douce et une ombre de contact sur le plan de // l'ecran. Constantes : miroir exact de `frame_geometry.rs` (MODEL_*) et de `sculpt.rs` // (SCULPT_*), emplacements du cbuffer : `cursor_model_cb`. -// Textures : le sprite RGBA (alpha droit) au binding 1 (`texY`, comme aux modes 7 et 13), son -// champ R16F au binding 2 (`texU`), sur le meme rect ; `color.rg` = coin du sprite dans le repere -// du modele, `sprite_size()` = sa taille (w/h dans `radius_px`), `color.b` = l'ecrasement au clic. +// Textures : l'enregistrement reste aux bindings 1, 2 et 5 (le cristal de Prism Glow le lit sous +// le curseur) ; le sprite RGBA (alpha droit) est au binding 6 (`texDof`), son champ R16F au +// binding 4 (`texMask`), sur le meme rect ; `color.rg` = coin du sprite dans le repere du modele, +// `sprite_size()` = sa taille (w/h dans `radius_px`), `color.b` = l'ecrasement au clic. const MODEL_BEVEL: f32 = 0.045; const MODEL_LIGHT = vec3(-0.4194, -0.5792, 0.6990); const MODEL_FILL = vec3(0.7557, 0.2519, 0.6046); @@ -592,7 +598,7 @@ fn sprite_size() -> vec2 { // Un texel du sprite, en unites du modele : le champ est le sprite surechantillonne x4. const CURSOR_SDF_UPSAMPLE: f32 = 4.0; fn sprite_texel() -> f32 { - let d = vec2(textureDimensions(texU)); + let d = vec2(textureDimensions(texMask)); return CURSOR_SDF_UPSAMPLE / max(d.x, d.y); } @@ -958,6 +964,108 @@ fn s_pixel_outline(p: vec2, shape: i32) -> f32 { return d; } +// ---- Prism Glow : un cristal en MAILLAGE ---- (port du HLSL, dont les commentaires font foi) +// Les facettes tracees sur son dessin, en triangles dans le repere du modele ; le serti est la +// silhouette extrudee (champ de distance), le cristal est lance de rayons triangle par triangle. +// prism mesh: generated by design/cursors/prism-glow/model/export_compositor.py +var PRISM_TRI_START = array(0, 31); +var PRISM_TRI_COUNT = array(31, 130); +var PRISM_SIL_START = array(0, 25); +var PRISM_SIL_COUNT = array(25, 42); +var PRISM_OUT_START = array(67, 76); +var PRISM_OUT_COUNT = array(9, 34); +struct PrismMesh { + tris: array, 644>, + poly: array, 110>, +} +const PRISM_BEVEL: f32 = 0.012883; +// end of the prism mesh + +const PRISM_IOR: f32 = 1.61; +const PRISM_DISPERSION: f32 = 0.035; +const PRISM_GLOW: f32 = 0.45; +const PRISM_FOLD: f32 = 0.35; +const PRISM_BOUNCES: i32 = 6; +const PRISM_MAT: f32 = 9.0; + +fn prism_shape() -> i32 { + let id = sculpt_id(); + if id == 3 || id == 4 { + return id - 3; + } + return -1; +} + +// Drapeaux d'un triangle : paroi (bit 0), aretes reelles (bits 1 a 3). +fn prism_flags(k: i32) -> i32 { + return i32(prism.tris[4 * k].w + 0.5); +} + +fn prism_poly_dist(p: vec2, i0: i32, n: i32) -> f32 { + var vj = prism.poly[i0 + n - 1].xy; + var d = 1e9; + var sgn = 1.0; + for (var i = 0; i < n; i++) { + let vi = prism.poly[i0 + i].xy; + let e = vj - vi; + let w = p - vi; + let b = w - e * saturate(dot(w, e) / dot(e, e)); + d = min(d, dot(b, b)); + let c0 = p.y >= vi.y; + let c1 = p.y < vj.y; + let c2 = e.x * w.y > e.y * w.x; + if (c0 && c1 && c2) || (!c0 && !c1 && !c2) { + sgn = -sgn; + } + vj = vi; + } + return sgn * sqrt(d); +} + +fn prism_rim(p: vec3, shape: i32) -> f32 { + let half_t = model_thick() * 0.5; + let sil = prism_poly_dist(p.xy, PRISM_SIL_START[shape], PRISM_SIL_COUNT[shape]); + let w = vec2(sil + PRISM_BEVEL, abs(p.z + half_t) - (half_t - PRISM_BEVEL)); + return min(max(w.x, w.y), 0.0) + length(max(w, vec2(0.0))) - PRISM_BEVEL; +} + +fn prism_tri(ro: vec3, rd: vec3, k: i32) -> f32 { + let v0 = prism.tris[4 * k].xyz; + let e1 = prism.tris[4 * k + 1].xyz; + let e2 = prism.tris[4 * k + 2].xyz; + let pv = cross(rd, e2); + let det = dot(e1, pv); + if abs(det) < 1e-10 { + return -1.0; + } + let inv = 1.0 / det; + let sv = ro - v0; + let u = dot(sv, pv) * inv; + let qv = cross(sv, e1); + let v = dot(rd, qv) * inv; + if u < -2e-4 || v < -2e-4 || u + v > 1.0004 { + return -1.0; + } + return dot(e2, qv) * inv; +} + +// (distance, indice) du triangle du cristal `shape` le plus proche devant (ro, rd) ; (1e9, -1) +// s'il n'y en a pas. +fn prism_trace(ro: vec3, rd: vec3, shape: i32) -> vec2 { + let k0 = PRISM_TRI_START[shape]; + let n = PRISM_TRI_COUNT[shape]; + var best = 1e9; + var hit = -1; + for (var i = 0; i < n; i++) { + let t = prism_tri(ro, rd, k0 + i); + if t > 1e-5 && t < best { + best = t; + hit = k0 + i; + } + } + return vec2(best, f32(hit)); +} + fn sculpt_proto(p: vec3, theme: i32, shape: i32) -> vec2 { if theme == 3 { return s_voxels(p, shape); @@ -978,6 +1086,10 @@ fn sculpt_eval(q: vec3, occ: bool) -> vec2 { let id = sculpt_id() - 1; let theme = id / 2; let shape = id % 2; + if theme == 1 { + // Prism Glow : son serti seul, dans le repere du modele (le cristal est trace a part). + return vec2(prism_rim(q, shape), 8.0); + } let p = sculpt_point(q); var r: vec2; if occ && theme == 3 { @@ -994,7 +1106,7 @@ fn sd_sprite2(p: vec2) -> f32 { let lo = layer.color.rg; let c = clamp(p, lo, lo + sprite_size()); // Niveau 0 explicite : la marche est une boucle a sortie anticipee (pas de derivees). - let d = textureSampleLevel(texU, samp, (c - lo) / sprite_size(), 0.0).r; + let d = textureSampleLevel(texMask, samp, (c - lo) / sprite_size(), 0.0).r; let o = p - c; let out2 = dot(o, o); let e = max(d, 0.0); @@ -1054,7 +1166,7 @@ fn model_albedo(p: vec2) -> vec3 { let g = vec2(sd_sprite2(p + vec2(e, 0.0)) - sd_sprite2(p - vec2(e, 0.0)), sd_sprite2(p + vec2(0.0, e)) - sd_sprite2(p - vec2(0.0, e))); let q = p - g / max(length(g), 1e-6) * max(sd_sprite2(p) + MODEL_RIM_INSET * texel, 0.0); - return textureSampleLevel(texY, samp, (q - layer.color.rg) / sprite_size(), 0.0).rgb; + return textureSampleLevel(texDof, samp, (q - layer.color.rg) / sprite_size(), 0.0).rgb; } struct SculptMat { @@ -1089,6 +1201,9 @@ fn sculpt_material(mat: f32, p: vec3, theme: i32, shape: i32) -> SculptMat } return SculptMat(s_lin(0.1, 0.1, 0.11), 0.55, 0.05, 0.0, 0.1); } + if theme == 1 { + return SculptMat(s_lin(0.02, 0.05, 0.33), 0.35, 0.5, 0.05, 0.3); + } if theme == 2 { if (primary && shape == 0) || (mat > 2.5 && mat < 3.5 && shape == 1) { return SculptMat(s_lin(1.0, 0.40, 0.30), 0.85, 0.08, 0.15, 0.03); @@ -1151,6 +1266,10 @@ fn model_shade(q: vec3, n: vec3, rd: vec3, L: vec3, fall: f3 if id > 0 { let p = sculpt_point(q) - vec3(n.x, -n.y, n.z) * 0.01; m = sculpt_material(mat, p, (id - 1) / 2, (id - 1) % 2); + if id == 3 || id == 4 { + // Le serti de Prism Glow, comme un sprite : brillant sur son arrondi seulement. + gloss = 1.0 - smoothstep(0.97, 0.995, abs(n.z)); + } } else { m = SculptMat(pow(model_albedo(q.xy), vec3(2.2)), 0.45, 0.35, 0.2, 0.3); gloss = 1.0 - smoothstep(0.97, 0.995, abs(n.z)); @@ -1172,6 +1291,135 @@ fn model_shade(q: vec3, n: vec3, rd: vec3, L: vec3, fall: f3 return model_tonemap(col); } +// ---- Le cristal de Prism Glow ---- + +fn prism_fresnel(c: f32, ior: f32) -> f32 { + var f0 = (ior - 1.0) / (ior + 1.0); + f0 = f0 * f0; + return f0 + (1.0 - f0) * pow(1.0 - c, 5.0); +} + +// (distance, facette) du cristal sur le rayon ; (1e9, -1) si rate, hors de Prism Glow, ou sur une +// paroi sous le dessus du serti (le rayon a traverse le serti avant). +fn prism_primary(ro: vec3, rd: vec3) -> vec2 { + let shape = prism_shape(); + if shape < 0 { + return vec2(1e9, -1.0); + } + let r = prism_trace(ro, rd, shape); + let h = i32(r.y); + if h >= 0 && (prism_flags(h) & 1) == 1 && ro.z + rd.z * r.x < 0.0 { + return vec2(1e9, -1.0); + } + return r; +} + +fn prism_normal(h: i32, rd: vec3) -> vec3 { + let n = prism.tris[4 * h + 3].xyz; + return select(n, -n, dot(n, rd) > 0.0); +} + +fn prism_seg(p: vec3, a: vec3, b: vec3) -> f32 { + let pa = p - a; + let ba = b - a; + return length(pa - ba * saturate(dot(pa, ba) / dot(ba, ba))); +} + +// Distance, en pixels, de `p` aux aretes reelles de la facette `h` (plis et bord du serti). +fn prism_edge_px(p: vec3, h: i32, px: f32) -> f32 { + let a = prism.tris[4 * h].xyz; + let b = a + prism.tris[4 * h + 1].xyz; + let c = a + prism.tris[4 * h + 2].xyz; + let bits = prism_flags(h) >> 1u; + var d = 1e9; + if (bits & 1) != 0 { + d = min(d, prism_seg(p, b, c)); + } + if (bits & 2) != 0 { + d = min(d, prism_seg(p, c, a)); + } + if (bits & 4) != 0 { + d = min(d, prism_seg(p, a, b)); + } + return d / px; +} + +fn prism_screen(q: vec3, d: vec3, nz: vec3, hz: f32, tip: vec3, unit: f32, f: ModelFrame) -> vec3 { + let denom = dot(d, nz); + if denom > -1e-4 { + return SCULPT_SCREEN * 0.2; + } + let g = q + d * ((hz - dot(q, nz)) / denom); + let fp = (tip + unit * model_to_plane(g, f)).xy / (2.0 * layer.mb.xy) + vec2(0.5); + if any(fp < vec2(0.0)) || any(fp > vec2(1.0)) { + return SCULPT_SCREEN * 0.2; + } + return pow(sample_yuv_level(mix(layer.trail_b.xy, layer.trail_b.zw, fp)), vec3(2.2)); +} + +fn prism_shade(p: vec3, n: vec3, rd: vec3, h: i32, px: f32, l: vec3, fill: vec3, + nz: vec3, hz: f32, tip: vec3, unit: f32, f: ModelFrame) -> vec3 { + let shape = prism_shape(); + let facet = vec3(prism.tris[4 * h + 1].w, prism.tris[4 * h + 2].w, prism.tris[4 * h + 3].w); + let F = prism_fresnel(saturate(-dot(rd, n)), PRISM_IOR); + let zb = -model_thick(); + var trans = vec3(0.0); + for (var ch = 0; ch < 3; ch++) { + // Le rouge plie le moins, le bleu le plus ; le canal passe par un masque (comme au HLSL). + let mask = vec3(f32(ch == 0), f32(ch == 1), f32(ch == 2)); + let ior = PRISM_IOR + PRISM_DISPERSION * f32(ch - 1); + var d = refract(rd, n, 1.0 / ior); + var pos = p; + var thr = 1.0; + var acc = 0.0; + for (var b = 0; b < PRISM_BOUNCES; b++) { + let hit = prism_trace(pos, d, shape); + let hh = i32(hit.y); + let th = hit.x; + var tb = 1e9; + if d.z < -1e-6 { + tb = (zb - pos.z) / d.z; + } + if tb <= th { + let qb = pos + d * tb; + let d2 = refract(d, vec3(0.0, 0.0, 1.0), ior); + if dot(d2, d2) < 1e-8 { + pos = qb; + d = reflect(d, vec3(0.0, 0.0, 1.0)); + continue; + } + acc = acc + thr * dot(prism_screen(qb, d2, nz, hz, tip, unit, f), mask); + thr = 0.0; + break; + } + if hh < 0 { + break; + } + pos = pos + d * th; + let rn = prism.tris[4 * hh + 3].xyz; + if (prism_flags(hh) & 1) == 1 && pos.z < 0.0 { + acc = acc + thr * dot(s_lin(0.03, 0.06, 0.37) * 0.35, mask); + thr = 0.0; + break; + } + let dout = refract(d, -rn, ior); + if dot(dout, dout) < 1e-8 { + d = reflect(d, -rn); + continue; + } + let fi = prism_fresnel(saturate(dot(dout, rn)), ior); + acc = acc + thr * (1.0 - fi) * dot(model_env(dout, 0.05, l, fill), mask); + thr = thr * fi; + d = reflect(d, -rn); + } + trans = trans + acc * mask; + } + var col = F * model_env(reflect(rd, n), 0.05, l, fill) + (1.0 - F) * trans + facet * PRISM_GLOW; + let fold = 1.0 - smoothstep(0.2, 1.0, prism_edge_px(p, h, px)); + col = mix(col, s_lin(0.96, 0.99, 1.0) * 1.3, PRISM_FOLD * fold * (0.35 + 0.65 * saturate(dot(n, l)))); + return model_tonemap(col); +} + // Les passes de la boucle unique de `cursor_model` (cf. HLSL : un seul appel de `model_eval`). const STAGE_MARCH: i32 = 0; const STAGE_NORMAL: i32 = 1; @@ -1219,6 +1467,10 @@ fn cursor_model(local: vec2) -> vec4 { var tb = ray_box(ro, ray, lo - vec3(0.02), hi + vec3(0.02)); var stage = select(STAGE_DONE, STAGE_MARCH, tb.x < tb.y && tb.y > 0.0); var plane_next = stage == STAGE_DONE; + // Prism Glow : le cristal, trace au depart de chaque rayon (`tc` son point, `hc` sa facette). + var need_tc = stage == STAGE_MARCH; + var tc = 1e9; + var hc = -1; var k = 0; var t = max(tb.x, 0.0); var best = 1e9; @@ -1242,6 +1494,12 @@ fn cursor_model(local: vec2) -> vec4 { var shaded = 0; var acc = vec4(0.0); for (var it = 0; it < 500; it++) { + if need_tc { + need_tc = false; + let pr = prism_primary(ro, ray); + tc = pr.x; + hc = i32(pr.y); + } if plane_next { plane_next = false; stage = select(STAGE_DONE, STAGE_SHADE, cov > 0.0); @@ -1259,9 +1517,14 @@ fn cursor_model(local: vec2) -> vec4 { } if stage == STAGE_SHADE { if cov_s > 0.0 { - let tl = lamp - q; - let fall = SCULPT_LAMP_DIST * SCULPT_LAMP_DIST / dot(tl, tl); - let c = model_shade(q, n, ray, normalize(tl), fall, sh, ao, mat, l, fill); + var c: vec3; + if mat > PRISM_MAT - 0.5 { + c = prism_shade(q, n, ray, hc, t_best / dlen, l, fill, nz, hz, tip, unit, f); + } else { + let tl = lamp - q; + let fall = SCULPT_LAMP_DIST * SCULPT_LAMP_DIST / dot(tl, tl); + c = model_shade(q, n, ray, normalize(tl), fall, sh, ao, mat, l, fill); + } acc = acc + vec4(c * cov_s, cov_s); } shaded++; @@ -1280,6 +1543,7 @@ fn cursor_model(local: vec2) -> vec4 { t = max(tb.x, 0.0); best = 1e9; t_best = t; + need_tc = true; } } continue; @@ -1306,7 +1570,7 @@ fn cursor_model(local: vec2) -> vec4 { let d = m.x; if stage == STAGE_MARCH { let fp = t / dlen; - let hit = d < 0.1 * fp; + var hit = d < 0.1 * fp; if hit || d / fp < best { best = select(d / fp, 0.0, hit); t_best = t; @@ -1315,6 +1579,14 @@ fn cursor_model(local: vec2) -> vec4 { } t = t + d * stride; k++; + if !hit && t >= tc { + // Le pas depasse le cristal : c'est lui que le rayon touche, avant le serti. + hit = true; + best = 0.0; + t_best = tc; + d_best = 0.0; + mat = PRISM_MAT; + } if hit || t > tb.y || k == 96 { cov_s = saturate(1.0 - best); if shaded > 0 { @@ -1335,6 +1607,21 @@ fn cursor_model(local: vec2) -> vec4 { plane_next = true; } } + if stage == STAGE_NORMAL && mat > PRISM_MAT - 0.5 { + // Le cristal : la normale de sa facette, ni occlusion ni ombre propre ; un bord + // reel a moins d'un pixel appelle les trois rayons de plus. + n = prism_normal(hc, ray); + k = 0; + if shaded > 0 { + stage = STAGE_SHADE; + } else { + if prism_edge_px(q, hc, t_best / dlen) < 0.75 { + sub = 3; + } + stage = STAGE_DONE; + plane_next = true; + } + } } } else if stage == STAGE_NORMAL { n = n + model_tetra(k) * d; @@ -1348,6 +1635,16 @@ fn cursor_model(local: vec2) -> vec4 { if m.y != mat || abs(d - d_best) > 0.02 * t_best / dlen { sub = 3; } + if k == 0 && mat > 7.5 { + // Le serti de Prism Glow au bord du cristal : son contour a moins d'un pixel. + let ps = prism_shape(); + if ps >= 0 { + let dout = prism_poly_dist(q.xy, PRISM_OUT_START[ps], PRISM_OUT_COUNT[ps]); + if abs(dout) < 0.75 * t_best / dlen { + sub = 3; + } + } + } k++; if k == 4 { stage = STAGE_AO; diff --git a/crates/compositor/tests/cursor_model_render.rs b/crates/compositor/tests/cursor_model_render.rs index a48105252..70151158f 100644 --- a/crates/compositor/tests/cursor_model_render.rs +++ b/crates/compositor/tests/cursor_model_render.rs @@ -161,11 +161,11 @@ fn cursors_dir() -> String { .replace('\\', "/") } -/// Les thèmes d'origine qui ont un modèle (Prism Glow n'en a pas : son dessin est extrudé) et les -/// hotspots de leur flèche et de leur main plates (`CURSOR_THEMES`, +/// Les thèmes d'origine et les hotspots de leur flèche et de leur main plates (`CURSOR_THEMES`, /// `src/lib/cursor/cursorThemes.ts`, sur 32). -const SCULPTED: [(&str, [f32; 2], [f32; 2]); 4] = [ +const SCULPTED: [(&str, [f32; 2], [f32; 2]); 5] = [ ("studio-ink", [6.2304, 2.0992], [12.848, 2.0704]), + ("prism-glow", [6.3456, 2.0672], [11.968, 2.0352]), ("pop-coral", [10.4768, 2.1792], [12.3456, 2.0]), ("pixel-candy", [6.5, 2.0], [10.75, 2.0]), ("star-sprout", [4.7232, 2.1152], [13.1712, 2.0384]), @@ -447,6 +447,20 @@ fn opaque_mask(on_blue: &[u8], on_orange: &[u8], bare_blue: &[u8]) -> Vec .collect() } +/// Les pixels que couvre un curseur de VERRE (le cristal de Prism Glow, qui laisse voir le contenu +/// et le réfracte) : opaques, ou changés sur les deux teintes sans y être plus sombres (une ombre +/// ne fait qu'assombrir ; le verre réfracte, reflète et luit). +fn covered_mask(on_blue: &[u8], on_orange: &[u8], bare_blue: &[u8], bare_orange: &[u8]) -> Vec { + let opaque = opaque_mask(on_blue, on_orange, bare_blue); + let px = |img: &[u8], i: usize| [img[i * 4], img[i * 4 + 1], img[i * 4 + 2], 255]; + (0..1280 * 720) + .map(|i| { + let lit = |on: &[u8], bare: &[u8]| px(on, i) != px(bare, i) && luma(px(on, i)) >= luma(px(bare, i)) - 2.0; + opaque[i] || (lit(on_blue, bare_blue) && lit(on_orange, bare_orange)) + }) + .collect() +} + fn iou(a: &[bool], b: &[bool]) -> f32 { let inter = a.iter().zip(b).filter(|(x, y)| **x && **y).count(); let union = a.iter().zip(b).filter(|(x, y)| **x || **y).count(); @@ -765,16 +779,18 @@ fn the_motion_blur_trail_draws_modelled_copies() { assert!(rgba != flat, "la traînée 3D est celle du sprite plat"); } -/// Les curseurs sculptés (flèche et main de chaque thème qui a un modèle), dessinés par le -/// shader et non extrudés d'un PNG : chacun est là, sa pointe sur le hotspot, son corps en bas à -/// droite de celle-ci (y vers le bas : un modèle retourné finirait au-dessus), et il porte son -/// ombre en l'air. +/// Les curseurs sculptés (flèche et main de chaque thème), dessinés par le shader et non extrudés +/// d'un PNG : chacun est là, sa pointe sur le hotspot, son corps en bas à droite de celle-ci (y +/// vers le bas : un modèle retourné finirait au-dessus), et il porte son ombre en l'air. Le +/// cristal de Prism Glow est de verre : son corps compte ce qu'il couvre, et il doit laisser voir +/// le contenu (une bonne part de lui change avec la teinte de l'écran). #[test] fn the_sculpted_cursors_stand_at_the_hotspot() { let Some(gpu) = gpu() else { return }; let comp = Compositor::new_sized(&gpu, 1280, 720).expect("compositor"); let (blue, orange) = (FakeFrame::new(&gpu, Tint::Blue), FakeFrame::new(&gpu, Tint::Orange)); let bare = render_any(&comp, &blue, &hidden_json("null"), &resting("sculpt-bare", false)).0; + let bare_orange = render_any(&comp, &orange, &hidden_json("null"), &resting("sculpt-bare", false)).0; let mut failures = Vec::new(); for (theme, ..) in SCULPTED { for state in ["arrow", "pointer"] { @@ -783,7 +799,8 @@ fn the_sculpted_cursors_stand_at_the_hotspot() { let (hover, p) = render(&comp, &blue, &json, &still); let hover_b = render(&comp, &orange, &json, &still).0; save(&format!("sculpt-{theme}-{state}"), &hover); - let mask = opaque_mask(&hover, &hover_b, &bare); + let glass = theme == "prism-glow"; + let mask = if glass { covered_mask(&hover, &hover_b, &bare, &bare_orange) } else { opaque_mask(&hover, &hover_b, &bare) }; let (mut body, mut c, mut near) = (0usize, [0.0f32; 2], f32::MAX); for y in 0..720 { for x in 0..1280 { @@ -819,6 +836,13 @@ fn the_sculpted_cursors_stand_at_the_hotspot() { if shadow * 10 < body * 3 { failures.push(format!("{theme}/{state} : pas d'ombre en l'air ({shadow} px)")); } + if glass { + let through = (0..1280 * 720).filter(|&i| mask[i] && hover[i * 4..i * 4 + 3] != hover_b[i * 4..i * 4 + 3]).count(); + println!("{theme}/{state} : {through} px du corps laissent voir l'écran"); + if through * 10 < body * 3 { + failures.push(format!("{theme}/{state} : le cristal ne laisse voir l'écran que sur {through} px")); + } + } } } assert!(failures.is_empty(), "{failures:#?}"); diff --git a/design/cursors/3d-direction.md b/design/cursors/3d-direction.md index 33dd399ae..e8f594bea 100644 --- a/design/cursors/3d-direction.md +++ b/design/cursors/3d-direction.md @@ -5,15 +5,16 @@ With the 3D option on, mode 15 ray-marches the arrow and hand of Studio Ink, Pop Coral, Pixel Candy and Star Sprout as sculpted models: signed distance functions written in the compositor's three shaders (HLSL, Metal, WGSL), not a PNG face or -a height map. `crates/compositor/src/sculpt.rs` names the eight models and holds -their bounding boxes and hotspots; Prism Glow has none. The scene names the -model (`"/"`); text, resize, move and the other OS states keep +a height map. Prism Glow's arrow and hand are glass crystals: a triangle mesh +traced on its drawing, ray-traced in a navy rim. `crates/compositor/src/sculpt.rs` +names the ten models and holds their bounding boxes and hotspots. The scene names +the model (`"/"`); text, resize, move and the other OS states keep their built-in art and the compositor's beveled extrusion. | Theme | Arrow model | Hand model | | --- | --- | --- | | Studio Ink | Black rim, a raised ivory band, a recessed satin black field. | Ivory glove cushion in a black rim, black ridges between the fingers. | -| Prism Glow | No sculpted model: its faceted drawing is extruded as drawn, with a beveled edge, like the default cursors. | The same. | +| Prism Glow | A glass crystal cut along the facets of its drawing, set in a thin navy rim. The recording shows through it, refracted with a slight dispersion; each facet glows faintly in its drawn colour, and a light line marks its folds. | The same crystal: a facet for each plane of the drawn hand, the fingers and thumb included. | | Pop Coral | Cut paper: flat-topped sheets, no rounded bead or gloss. A coral sheet on a navy sheet, a yellow sheet behind them offset down-left, two yellow click dashes. | A yellow sheet on a navy sheet, the navy showing between the fingers, a coral sheet behind offset down-left, two coral click dashes. | | Pixel Candy | Simplified pixel art, one cube per pixel: a plum outline, a pink body with a pale highlight down the lit edge and a darker shade along the outline. The 3D model and the 2D sprite come from the same grid. | The same, a pixel hand: the index up, three knuckles apart, the thumb. | | Star Sprout | Glossy mint cushion framed by the navy outline of its drawing, carrying a star with a face and two leaves. | Ivory glove cushion in the same navy frame, navy ridges between the fingers, a mint cuff and the same star. | @@ -29,6 +30,16 @@ sheets instead (`s_paper`). What stands in front is theme-specific: Studio Ink's ivory band, Pop Coral's yellow and coral layers and click dashes, Star Sprout's cuff, star, leaves and eyes. +Prism Glow is built from its 2D drawing alone (`prism-glow/source.png`), not from +`3d-concept.png`. `prism-glow/model/` holds the mesh: `prism_geo.py` lists its +vertices, heights and facets and writes `prism.json`; `build_blend.py` builds +`prism-glow.blend` and `prism-glow.glb` from it in Blender; `export_compositor.py` +writes the mesh into `crates/compositor/src/prism_mesh.rs` and the three shaders. +The compositor traces the crystal triangle by triangle: refraction per colour +channel, total internal reflection, and an exit through its flat base onto the +recording under the cursor. The rim is its silhouette extruded, a distance field +like the other models: it gives the coverage and the shadows. + All models share one close lamp: a gradient and a highlight even on flat faces, soft self shadows, ambient occlusion and a studio environment in the reflections, plus the compositor's cast and contact shadows on the screen. The @@ -40,4 +51,6 @@ To change a model, edit it in all three shaders (`sculpt_proto`, shapes, and a test checks that the shaders mirror its constants. Pixel Candy's voxel grids are tables written by `scripts/generate-pixel-candy-voxels.mjs`: change a grid there and run the script; it rewrites all three shaders, and a -test checks the tables agree. +test checks the tables agree. To change Prism Glow, edit `prism_geo.py`, run it, +run `build_blend.py` in Blender, then `export_compositor.py`; a test checks that +the three shaders carry the mesh of `prism_mesh.rs`. diff --git a/design/cursors/README.md b/design/cursors/README.md index 8b58dc6dd..c6cd471c4 100644 --- a/design/cursors/README.md +++ b/design/cursors/README.md @@ -21,9 +21,10 @@ fringe pixels, and resize them into transparent 128 × 128 PNGs under With the 3D option on, the compositor draws the arrow and hand of Studio Ink, Pop Coral, Pixel Candy and Star Sprout as models sculpted in its shaders (`crates/compositor/src/sculpt.rs`), not from these PNGs. Prism Glow's arrow and -hand keep their PNGs and receive the shared 3D extrusion. The theme picker and -the flat cursor keep using the PNGs. Text, resize, move and other states remain -state-accurate through the built-in art and receive the shared 3D extrusion. +hand are glass crystals meshed on its drawing (`prism-glow/model/`). The theme +picker and the flat cursor keep using the PNGs. Text, resize, move and other +states remain state-accurate through the built-in art and receive the shared 3D +extrusion. `contact-sheet.png` shows the sprites enlarged on a light background; `dark-32px.png` shows them at their 32-pixel reference size on a dark background. diff --git a/design/cursors/prism-glow/model/export_compositor.py b/design/cursors/prism-glow/model/export_compositor.py new file mode 100644 index 000000000..144961acb --- /dev/null +++ b/design/cursors/prism-glow/model/export_compositor.py @@ -0,0 +1,306 @@ +"""Write the Prism Glow meshes into the compositor: `crates/compositor/src/prism_mesh.rs` and the +matching tables in the three shaders, between their `prism mesh` markers. + + python export_compositor.py # after build_blend.py has written prism-glow.glb + +Everything is expressed in the mode 15 model frame: the unit is the side of the theme's 128 px +sprite (the flat cursor's size), origin on the hotspot, x right, y DOWN, z toward the camera, +rim top at z = 0. The source-to-sprite transform is the one scripts/generate-original-cursor-themes.mjs +applies, so the crystal lands exactly on the flat cursor. +""" + +import json +import math +import struct +from pathlib import Path + +import cv2 +import numpy as np + +HERE = Path(__file__).resolve().parent +REPO = HERE.parents[3] +SRC = REPO / "crates" / "compositor" / "src" +SPRITE, INNER, ALPHA_MIN = 128, 112, 28 # generate-original-cursor-themes.mjs +BEVEL_PX = 12.0 # rounding of the rim's edges, source px +EDGE_FOLD_DEG = 25.0 + + +def glb_meshes(path): + """{node name: (positions (n,3), triangle indices (m,3), colours (n,3))}, glTF frame (y up).""" + b = path.read_bytes() + n = struct.unpack_from("= ALPHA_MIN + ys, xs = np.nonzero(a) + w, h = xs.max() - xs.min() + 1, ys.max() - ys.min() + 1 + scale = INNER / max(w, h) + tx, ty = (SPRITE - w * scale) / 2, (SPRITE - h * scale) / 2 + hx = (tx + (hotspot[0] - x0 - xs.min()) * scale) / SPRITE + hy = (ty + (hotspot[1] - ys.min()) * scale) / SPRITE + return scale / SPRITE, (hx, hy), ty / SPRITE + + +def crystal_triangles(pos, tri, col, sheet, s): + """Triangles of the crystal (facets and walls; the flat bottom is left to the shader), each + with its outward normal, its colour in the art and which of its edges are real fold lines.""" + # glTF (x, y up, z) -> Blender (x, y, z up), hotspot at the origin, then the model frame + blender = np.c_[pos[:, 0], -pos[:, 2], pos[:, 1]] - np.array([sheet[0], sheet[1], 0.0]) + model = np.c_[blender[:, 0], -blender[:, 1], blender[:, 2]] * (1000.0 * s) + zmin = model[:, 2].min() + tris = [] + for t in tri: + a, b, c = model[t] + nb = np.cross(blender[t[1]] - blender[t[0]], blender[t[2]] - blender[t[0]]) + nb /= np.linalg.norm(nb) + n = np.array([nb[0], -nb[1], nb[2]]) # the y mirror keeps the normal outward + if n[2] < -0.9 and abs(a[2] - zmin) < 1e-6: + continue + tris.append({"v": (a, b, c), "n": n, "col": col[t[0]], "key": tuple(map(tuple, np.round(model[t], 6)))}) + # real edges: the mesh's boundary, a change of facet colour, or a sharp fold + owners = {} + for i, t in enumerate(tris): + for e in range(3): + k = tuple(sorted((t["key"][(e + 1) % 3], t["key"][(e + 2) % 3]))) + owners.setdefault(k, []).append(i) + for i, t in enumerate(tris): + bits = 0 + for e in range(3): + k = tuple(sorted((t["key"][(e + 1) % 3], t["key"][(e + 2) % 3]))) + other = [j for j in owners[k] if j != i] + real = not other or np.abs(tris[other[0]]["col"] - t["col"]).sum() > 1e-3 or ( + tris[other[0]]["n"] @ t["n"] < math.cos(math.radians(EDGE_FOLD_DEG))) + bits |= int(real) << e + t["flags"] = (1 if abs(t["n"][2]) < 0.2 else 0) + 2 * bits + return tris, float(zmin), float(model[:, 2].max()), model + + +def main(): + img = cv2.imread(str(HERE.parent / "source.png"), cv2.IMREAD_UNCHANGED) + geo = json.loads((HERE / "prism.json").read_text()) + meshes = glb_meshes(HERE / "prism-glow.glb") + shapes = [] + for kind, node, (x0, x1), sheet in ( + ("arrow", "prism_arrow_crystal", (0, 887), (0.0, 0.0)), + ("hand", "prism_hand_crystal", (887, 1774), ((1220 - 208) / 1000, -(50 - 42) / 1000)), + ): + hot = geo[kind]["hotspot"] + s, (hx, hy), top = sprite_transform(img, x0, x1, hot) + tris, zmin, zmax, model = crystal_triangles(*meshes[node], sheet, s) + sil = [((x - hot[0]) * s, (y - hot[1]) * s) for x, y in geo[kind]["silhouette"]] + out = [((geo[kind]["verts"][k][0] - hot[0]) * s, (geo[kind]["verts"][k][1] - hot[1]) * s) + for k in geo[kind]["outline"]] + sx = [p[0] for p in sil] + # the draw box: the sprite's full height (1 unit), the silhouette's width + bx0, bx1 = min(sx) - 0.01, max(sx) + 0.01 + shapes.append({ + "kind": kind, "tris": tris, "sil": sil, "out": out, "thick": -zmin, "height": zmax, + "size": (bx1 - bx0, 1.0), "hotspot": (-bx0 / (bx1 - bx0), hy), "top": top, + "per_px": s, + }) + print(f"{kind}: {len(tris)} triangles, {len(sil)} silhouette points, " + f"box {bx1 - bx0:.4f} x 1, hotspot ({-bx0 / (bx1 - bx0):.4f}, {hy:.4f}), " + f"height {zmax:.4f}, thick {-zmin:.4f}") + write_rust(shapes) + write_shaders(shapes) + + +def f(x): + return f"{x:.6f}".rstrip("0").rstrip(".") if abs(x) > 5e-7 else "0.0" + + +def fl(x): # a float literal every language reads as a float + s = f(x) + return s if "." in s else s + ".0" + + +def bevel(shapes): + return BEVEL_PX * shapes[0]["per_px"] + + +def polygons(shapes): + """Silhouettes, then outlines, in one table; their starts.""" + poly, sil_start, out_start = [], [], [] + for sh in shapes: + sil_start.append(len(poly)) + poly += sh["sil"] + for sh in shapes: + out_start.append(len(poly)) + poly += sh["out"] + return poly, sil_start, out_start + + +def records(shapes): + """Per triangle, four float4: (v0, flags), (e1, r), (e2, g), (n, b).""" + rows = [] + for sh in shapes: + for t in sh["tris"]: + a, b, c = t["v"] + e1, e2 = b - a, c - a + r, g, bl = t["col"] + rows += [(*a, t["flags"]), (*e1, r), (*e2, g), (*t["n"], bl)] + return rows + + +def write_rust(shapes): + tri_start, rows = [], records(shapes) + k = 0 + for sh in shapes: + tri_start.append(k) + k += len(sh["tris"]) + poly, sil_start, out_start = polygons(shapes) + lines = [ + "//! Les deux curseurs de Prism Glow en MAILLAGE : les facettes tracées sur l'art 2D", + "//! (`design/cursors/prism-glow/model`), taillées en cristal. Fichier GÉNÉRÉ par", + "//! `design/cursors/prism-glow/model/export_compositor.py` : ne pas l'éditer à la main.", + "//!", + "//! Repère du modèle du mode 15 : unité = côté du sprite 128 px du thème, origine au hotspot,", + "//! x à droite, y vers le bas, z vers la caméra, dessus du serti en z = 0. Chaque triangle du", + "//! cristal tient en quatre float4 : (v0, drapeaux), (e1, r), (e2, g), (normale, b) ; drapeaux =", + "//! paroi (1) + 2 × arêtes réelles (bit k : l'arête opposée au sommet k). Le fond plat, en", + "//! z = -épaisseur, est laissé au shader. `POLYS` tient les silhouettes (bord extérieur du", + "//! serti) puis les contours du cristal (son bord intérieur).", + "", + "/// Un des deux modèles : ses triangles et sa silhouette dans les tables, sa boîte de dessin.", + "pub struct PrismModel {", + " pub tri_start: usize,", + " pub tri_count: usize,", + " pub sil_start: usize,", + " pub sil_count: usize,", + " pub out_start: usize,", + " pub out_count: usize,", + " /// Largeur de la boîte, hauteur 1 (le sprite), en unités du modèle.", + " pub size: [f32; 2],", + " /// Hotspot, fraction de la boîte.", + " pub hotspot: [f32; 2],", + " /// Haut de la silhouette, fraction de la hauteur.", + " pub top: f32,", + " /// Sommet du cristal au-dessus de z = 0, et épaisseur sous z = 0.", + " pub height: f32,", + " pub thick: f32,", + "}", + "", + "/// Arrondi des arêtes du serti, en unités du modèle (`PRISM_BEVEL` des shaders).", + f"pub const BEVEL: f32 = {fl(bevel(shapes))};", + "", + "/// Flèche puis main, dans l'ordre des identifiants du shader.", + "pub const MODELS: [PrismModel; 2] = [", + ] + for i, sh in enumerate(shapes): + lines += [ + " PrismModel {", + f" tri_start: {tri_start[i]},", + f" tri_count: {len(sh['tris'])},", + f" sil_start: {sil_start[i]},", + f" sil_count: {len(sh['sil'])},", + f" out_start: {out_start[i]},", + f" out_count: {len(sh['out'])},", + f" size: [{fl(sh['size'][0])}, {fl(sh['size'][1])}],", + f" hotspot: [{fl(sh['hotspot'][0])}, {fl(sh['hotspot'][1])}],", + f" top: {fl(sh['top'])},", + f" height: {fl(sh['height'])},", + f" thick: {fl(sh['thick'])},", + " },", + ] + lines += ["];", "", f"pub const TRIS: [[f32; 4]; {len(rows)}] = ["] + lines += [f" [{', '.join(fl(v) for v in r)}]," for r in rows] + lines += ["];", "", f"pub const POLYS: [[f32; 2]; {len(poly)}] = ["] + lines += [f" [{fl(x)}, {fl(y)}]," for x, y in poly] + lines += ["];", ""] + (SRC / "prism_mesh.rs").write_text("\n".join(lines), encoding="utf-8", newline="\n") + + +def table_lines(shapes, lang): + rows = records(shapes) + poly, sil_start, out_start = polygons(shapes) + counts = [len(sh["tris"]) for sh in shapes] + scounts = [len(sh["sil"]) for sh in shapes] + ocounts = [len(sh["out"]) for sh in shapes] + if lang == "hlsl": + v4 = lambda r: f"float4({', '.join(fl(v) for v in r)})" + v2 = lambda p: f"float2({fl(p[0])}, {fl(p[1])})" + head4 = f"static const float4 PRISM_TRIS[{len(rows)}] = {{" + head2 = f"static const float2 PRISM_POLY[{len(poly)}] = {{" + tail = "};" + ints = lambda name, vals: f"static const int {name}[2] = {{ {vals[0]}, {vals[1]} }};" + scalar = f"static const float PRISM_BEVEL = {fl(bevel(shapes))};" + elif lang == "metal": + v4 = lambda r: f"float4({', '.join(fl(v) for v in r)})" + v2 = lambda p: f"float2({fl(p[0])}, {fl(p[1])})" + head4 = f"constant float4 PRISM_TRIS[{len(rows)}] = {{" + head2 = f"constant float2 PRISM_POLY[{len(poly)}] = {{" + tail = "};" + ints = lambda name, vals: f"constant int {name}[2] = {{ {vals[0]}, {vals[1]} }};" + scalar = f"constant float PRISM_BEVEL = {fl(bevel(shapes))};" + else: + ints = lambda name, vals: f"var {name} = array({vals[0]}, {vals[1]});" + scalar = f"const PRISM_BEVEL: f32 = {fl(bevel(shapes))};" + out = [ints("PRISM_TRI_START", [0, counts[0]]), ints("PRISM_TRI_COUNT", counts), + ints("PRISM_SIL_START", sil_start), ints("PRISM_SIL_COUNT", scounts), + ints("PRISM_OUT_START", out_start), ints("PRISM_OUT_COUNT", ocounts)] + if lang == "wgsl": + # The triangles and polygons themselves go in a uniform buffer that compositor_linux.rs + # fills from prism_mesh.rs (a polygon point per vec4, the stride of a uniform array). As + # a `const` or `var` table, lavapipe copies them into every invocation of every + # layer: a frame without any cursor rendered 3.4 times slower. + return out + ["struct PrismMesh {", f" tris: array, {len(rows)}>,", + f" poly: array, {len(poly)}>,", "}", scalar] + out.append(head4) + out += [f" {v4(r)}," for r in rows] + out[-1] = out[-1].rstrip(",") + out += [tail, head2] + out += [f" {v2(p)}," for p in poly] + out[-1] = out[-1].rstrip(",") + out += [tail, scalar] + return out + + +BEGIN = "prism mesh: generated by design/cursors/prism-glow/model/export_compositor.py" +END = "end of the prism mesh" + + +def write_shaders(shapes): + for name, lang in (("shaders.hlsl", "hlsl"), ("shaders.metal", "metal"), ("vk_shaders/layer.wgsl", "wgsl")): + path = SRC / name + text = path.read_text(encoding="utf-8") + a, b = text.find(BEGIN), text.find(END) + if a < 0 or b < 0: + print(f"{name}: no markers yet, tables not written") + continue + start = text.index("\n", a) + 1 + stop = text.rindex("\n", 0, b) + 1 + text = text[:start] + "\n".join(table_lines(shapes, lang)) + "\n" + text[stop:] + path.write_text(text, encoding="utf-8", newline="\n") + print(f"{name}: tables written") + + +if __name__ == "__main__": + main() diff --git a/design/cursors/requirements.md b/design/cursors/requirements.md index 5495e39c7..1d191897b 100644 --- a/design/cursors/requirements.md +++ b/design/cursors/requirements.md @@ -21,15 +21,15 @@ their 3D models. - Every replacement theme and supported cursor state must work with the existing 3D cursor option. -- Every replacement arrow and hand except Prism Glow's is a real volume, - sculpted in the compositor's shaders (`crates/compositor/src/sculpt.rs`): - lighting, normals, self shadows and contact shadows respond to cursor tilt - and rotation. A PNG face or a height map is not a model. +- Every replacement arrow and hand is a real volume, modelled in the + compositor's shaders (`crates/compositor/src/sculpt.rs`): lighting, normals, + self shadows and contact shadows respond to cursor tilt and rotation. A PNG + face or a height map is not a model. - A rounded extrusion of the PNG silhouette remains the fallback for cursor - states that do not have a dedicated theme model, and for Prism Glow, whose - faceted drawing is extruded as drawn. The four sculpted packs add + states that do not have a dedicated theme model. Each pack adds style-specific volume: gloves with separate fingers, beveled voxels, flat - cut-paper sheets, and cushions set in the rim of their drawn outline. + cut-paper sheets, cushions set in the rim of their drawn outline, and a + faceted glass crystal that refracts the recording. - Model the arrow and hand separately for every sculpted theme. Do not apply one contour, material, or roundness rule to all designs. - Preserve the cursor hotspot through hover, tilt, yaw, click, and size changes. @@ -42,7 +42,7 @@ their 3D models. | Theme | Arrow | Hand | | --- | --- | --- | | Studio Ink | Black rim, raised ivory band, recessed satin black field. | Ivory glove cushion; the black outline stays as a physical rim, with black ridges between the fingers. | -| Prism Glow | The faceted drawing extruded as drawn, beveled edge; no sculpted model. | The same. | +| Prism Glow | A glass crystal cut along the drawing's facets, in a thin navy rim; the recording shows through, refracted with a slight dispersion; the facets glow faintly in their drawn colours. Modelled from the 2D drawing alone, keeping its polygonal simplicity. | The same, a crystal hand. | | Pop Coral | Cut paper: a coral sheet on a navy sheet, flat tops, matte; the yellow offset becomes a flat sheet behind; the click dashes become flat pieces. | A yellow sheet on a navy sheet, flat tops, matte; the coral offset becomes a flat sheet behind; the coral click dashes become flat pieces. | | Pixel Candy | Simplified pixel art that reads at 20 px as at full size; the 3D model is one cube per pixel of the same grid as the sprite. | A pixel hand from the same kind of grid: index, three knuckles, thumb. | | Star Sprout | Puffy mint cushion; the navy outline stays as a physical rim (a navy tray and a rounded bead) around it, the star and its leaves in front, each in its own navy rim. | Puffy ivory glove in the same navy rim, with navy ridges between the fingers; separate mint cuff framed in navy; the star and leaves in front, as in `star-sprout/3d-reference.png`. | @@ -55,7 +55,8 @@ becomes an inlay, side material, separate piece, or disappears. - `contact-sheet.png`: 2D PNG themes. - `3d-concept.png`: the 3D reference sheet the eight sculpted models follow - (Prism Glow is extruded), not rendered by the app. + (Prism Glow's crystal follows its 2D drawing instead), not rendered by the + app. - `star-sprout/3d-reference.png`: a closer view of the Star Sprout hand; also not rendered by the app. - `3d-direction.md`: per-theme modeling notes. diff --git a/docs/3d-effects-v2.md b/docs/3d-effects-v2.md index 41528c542..f21bcdcfc 100644 --- a/docs/3d-effects-v2.md +++ b/docs/3d-effects-v2.md @@ -233,9 +233,8 @@ inconnues sont ignorées). ### B.2 Le réglage **Un seul interrupteur**, `cursor.model3d` (« 3D cursor », **éteint par défaut**) : il passe -**chaque état** du curseur en 3D. La flèche et la main de quatre des cinq thèmes d'origine -deviennent leur modèle sculpté (B.3 bis) ; celles de Prism Glow, les autres états, et tous ceux -du thème par défaut (les seize de `DEFAULT_CURSOR_SPRITES` : flèche, I, main, croix, mains +**chaque état** du curseur en 3D. La flèche et la main des cinq thèmes d'origine deviennent +leur modèle (B.3 bis) ; les autres états, et tous ceux du thème par défaut (les seize de `DEFAULT_CURSOR_SPRITES` : flèche, I, main, croix, mains ouverte et fermée, redimensionnements, déplacement, interdit, attente…), leur sprite extrudé. Curseur masqué, l'interrupteur est grisé et son info-bulle dit pourquoi. Éteint, la frame est celle d'avant **à l'octet** (vérifié à plat et incliné contre le commit de base). @@ -292,11 +291,17 @@ distance signée écrites dans les trois shaders (`sculpt_proto`, `sculpt_materi pour Pixel Candy ; pour Studio Ink et Star Sprout des pièces qui gardent le trait de leur dessin : un plateau et un jonc de la couleur du trait, un coussin de couleur dedans (`s_rimmed`) ; pour Pop Coral les mêmes formes en papier découpé, des feuilles au dessus plat -(`s_paper`). Prism Glow n'a pas de modèle : son dessin à facettes est extrudé comme tout sprite -(B.3). Le PNG du thème reste l'art en 2D. En 3D, la scène nomme le modèle ; `sculpt.rs` en tient -la boîte, qui pose le hotspot (pointe de la flèche, bout de l'index), règle la garde au sol et -borne la boîte de dessin. Emplacement du cbuffer : `trail_a` = [modèle, épaisseur sous z = 0, -hauteur au-dessus, 0] (`cursor_model_cb`). +(`s_paper`). Prism Glow est à part : un **cristal en maillage**, ses facettes tracées sur son +seul dessin 2D (`design/cursors/prism-glow/model/`), serti dans sa silhouette extrudée, marine. +Le shader le lance de rayons triangle par triangle : réfraction par canal (une légère +dispersion), réflexions totales internes, et sortie par son fond plat sur l'enregistrement sous +le curseur ; chaque facette luit un peu de sa couleur du dessin, ses plis d'un liseré clair. +`export_compositor.py` écrit le maillage dans `prism_mesh.rs` et dans les trois shaders. Le PNG +du thème reste l'art en 2D. En 3D, la scène nomme le modèle ; `sculpt.rs` en tient la boîte, qui +pose le hotspot (pointe de la flèche, bout de l'index), règle la garde au sol et borne la boîte +de dessin. Emplacements du cbuffer : `trail_a` = [modèle, épaisseur sous z = 0, hauteur +au-dessus, 0], `trail_b` = la coupe du plan en uv de l'écran, que le cristal réfracte +(`cursor_model_cb`). Pixel Candy est un pixel art dessiné une fois, en grille, dans `scripts/generate-pixel-candy-voxels.mjs` : le script en tire ses PNG 2D et les tables des trois shaders, un cube par pixel plein, tous de même hauteur, colorés comme leur pixel (contour prune, diff --git a/src/lib/cursor/cursorThemes.test.ts b/src/lib/cursor/cursorThemes.test.ts index c6dceb37a..e712e8ad4 100644 --- a/src/lib/cursor/cursorThemes.test.ts +++ b/src/lib/cursor/cursorThemes.test.ts @@ -67,9 +67,7 @@ describe("normalizeCursorThemeId", () => { // The compositor models these two states itself (`crates/compositor/src/sculpt.rs`, which // knows the same names): in 3D the scene names the model, and the sprite stays the flat art. - // Prism Glow is the exception: its drawing is extruded, like the default art. - const sculptedThemes = CURSOR_THEMES.filter((theme) => theme.id !== "prism-glow"); - it.each(sculptedThemes)("names the sculpted 3D arrow and hand of $name", (theme) => { + it.each(CURSOR_THEMES)("names the sculpted 3D arrow and hand of $name", (theme) => { const flat = resolveCursorSprites(theme.id); const sprites = resolveCursorSprites(theme.id, [], true); for (const state of ["arrow", "pointer"] as const) { @@ -80,11 +78,8 @@ describe("normalizeCursorThemeId", () => { expect(sprites.text).toBe(DEFAULT_CURSOR_SPRITES.text); }); - it.each([ - DEFAULT_CURSOR_THEME_ID, - "prism-glow", - ])("leaves %s to the extruded sprite in 3D", (id) => { - for (const sprite of Object.values(resolveCursorSprites(id, [], true))) { + it("leaves the default art to the extruded sprite in 3D", () => { + for (const sprite of Object.values(resolveCursorSprites(DEFAULT_CURSOR_THEME_ID, [], true))) { expect(sprite.sculpt).toBeUndefined(); } }); diff --git a/src/lib/cursor/cursorThemes.ts b/src/lib/cursor/cursorThemes.ts index fb7cf2e11..b3a743bdc 100644 --- a/src/lib/cursor/cursorThemes.ts +++ b/src/lib/cursor/cursorThemes.ts @@ -136,9 +136,9 @@ export function readCursorAsArrow( * * To add one: drop arrow.png/pointer.png into public/cursors// and add an entry here * with hotspots normalized to the 32-logical reference (divide a 128px-pack hotspot by 4). - * Mark an asset `sculpted` when the compositor has a 3D model for it (`sculpt.rs`); other states - * are extruded from their sprite in 3D, and so is all of Prism Glow, whose faceted art reads - * best as it is drawn. An id that leaves this list reads back as the default art + * Mark an asset `sculpted` when the compositor has a 3D model for it (`sculpt.rs`; Prism Glow's + * is a crystal mesh traced on its drawing, `prism_mesh.rs`); other states are extruded from their + * sprite in 3D. An id that leaves this list reads back as the default art * through `normalizeCursorThemeId`, so a project saved with it still opens. */ export const CURSOR_THEMES: readonly CursorTheme[] = [ @@ -174,6 +174,7 @@ export const CURSOR_THEMES: readonly CursorTheme[] = [ height: 32, hotspotX: 6.3456, hotspotY: 2.0672, + sculpted: true, }, pointer: { assetPath: "cursors/prism-glow/pointer.png", @@ -181,6 +182,7 @@ export const CURSOR_THEMES: readonly CursorTheme[] = [ height: 32, hotspotX: 11.968, hotspotY: 2.0352, + sculpted: true, }, }, }, From 66304d9601219cafe3db84cd8bd7d1cb266bf74b Mon Sep 17 00:00:00 2001 From: EtienneLescot Date: Wed, 30 Sep 2026 08:54:09 +0200 Subject: [PATCH 3/5] fix(compositor): draw the cursor under the camera and the annotations on Linux Linux drew the cursor last, over the camera and the annotations, where Windows and macOS draw it under both. With Prism Glow's crystal, which refracts the raw recording, that order let the glass show what a privacy blur hides. The cursor now sits between the screen and the camera, as on the other platforms, and a test checks that a blur leaves no sharp edge of the crystal under it. --- crates/compositor/src/compositor_linux.rs | 150 ++++++++++++++++------ 1 file changed, 109 insertions(+), 41 deletions(-) diff --git a/crates/compositor/src/compositor_linux.rs b/crates/compositor/src/compositor_linux.rs index 808f93f9f..b99db602a 100644 --- a/crates/compositor/src/compositor_linux.rs +++ b/crates/compositor/src/compositor_linux.rs @@ -3105,9 +3105,9 @@ impl Compositor { } // Blur du fond (avant l'ecran), si active par la scene/l'inspector. self.blur_bg(&mut encoder, cfg.bg_blur); - // Passe 2 : avant-plan (ecran + webcam), compose par-dessus le fond - // (eventuellement floute) avec `LoadOp::Load`. Les annotations sont dans - // une passe a part, cf. plus bas. + // Passe 2 : l'ecran, compose par-dessus le fond (eventuellement floute) avec + // `LoadOp::Load`. Puis le curseur, la camera et les annotations, dans cet ordre : + // celui de Windows et macOS. { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("fg-pass"), @@ -3140,40 +3140,12 @@ impl Compositor { &device_frame, ), } - if let Some((_buf, bind)) = &webcam_shadow { - rpass.set_bind_group(0, bind, &[]); - rpass.draw(0..4, 0..1); - } - // Fond personnalise : ENTRE l'ombre et la camera. C'est ce sandwich qui - // remplace la branche « mode 3 » du shader — la camera, decoupee, se - // fond dessus par alpha ; l'ombre reste dessous, elle appartient a la - // bulle et non a son contenu. - if let Some(bg) = &webcam_bg { - rpass.set_bind_group(0, &bg.bind, &[]); - rpass.draw(0..4, 0..1); - } - if let Some((_buf, bind)) = &webcam_draw { - rpass.set_bind_group(0, bind, &[]); - rpass.draw(0..4, 0..1); - } - } - // Fige la frame composee pour les annotations « flou ». ICI et nulle part - // ailleurs : apres l'ecran et la camera (sinon un flou masquerait du vide) - // et avant la premiere annotation (sinon deux flous qui se recouvrent - // s'echantillonnent l'un l'autre). Une passe de rendu ne peut pas lire sa - // propre cible, d'ou la copie -- et d'ou le fait que les annotations - // doivent avoir leur propre passe. - if needs_ann_copy { - self.generate_ann_mips(&mut encoder); } - // Passe 3 : annotations puis curseur net, par-dessus tout le reste. Elle - // existe meme sans flou : deux passes consecutives sur la MEME cible avec - // `LoadOp::Load` ne coutent rien de plus qu'une seule sur un GPU - // desktop, et un seul chemin de code vaut mieux qu'un branchement qui ne - // serait exerce que dans un projet sur dix. + // Le curseur net, et l'impact des clics sous lui. La trainee, elle, a besoin de sa + // propre cible : elle suit. { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("ann-pass"), + label: Some("cursor-pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &self.rt_view, resolve_target: None, @@ -3187,18 +3159,11 @@ impl Compositor { occlusion_query_set: None, }); rpass.set_pipeline(&self.pipeline); - for a in &ann_draws { - rpass.set_bind_group(0, &a.bind, &[]); - rpass.draw(0..4, 0..1); - } // L'impact des clics, sous le curseur et sa trainee (dessinee plus bas). for bind in cursor_draw.iter().flat_map(|c| &c.impacts) { rpass.set_bind_group(0, bind, &[]); rpass.draw(0..4, 0..1); } - // Curseur en dernier : au-dessus de l'ecran et des annotations. - // Une seule copie = curseur net, il tient dans cette pass. La - // trainee, elle, a besoin de sa propre cible (voir plus bas). if let Some(c) = cursor_draw.as_ref().filter(|c| c.binds.len() == 1) { rpass.set_bind_group(0, &c.binds[0], &[]); rpass.draw(0..4, 0..1); @@ -3262,6 +3227,76 @@ impl Compositor { rpass.set_bind_group(0, abind, &[]); rpass.draw(0..3, 0..1); } + // La camera, au-dessus de l'ecran et du curseur. + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("webcam-pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &self.rt_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&self.pipeline); + if let Some((_buf, bind)) = &webcam_shadow { + rpass.set_bind_group(0, bind, &[]); + rpass.draw(0..4, 0..1); + } + // Fond personnalise : ENTRE l'ombre et la camera. C'est ce sandwich qui + // remplace la branche « mode 3 » du shader — la camera, decoupee, se + // fond dessus par alpha ; l'ombre reste dessous, elle appartient a la + // bulle et non a son contenu. + if let Some(bg) = &webcam_bg { + rpass.set_bind_group(0, &bg.bind, &[]); + rpass.draw(0..4, 0..1); + } + if let Some((_buf, bind)) = &webcam_draw { + rpass.set_bind_group(0, bind, &[]); + rpass.draw(0..4, 0..1); + } + } + // Fige la frame composee pour les annotations « flou ». ICI et nulle part + // ailleurs : apres l'ecran, le curseur et la camera (sinon un flou masquerait du vide) + // et avant la premiere annotation (sinon deux flous qui se recouvrent + // s'echantillonnent l'un l'autre). Une passe de rendu ne peut pas lire sa + // propre cible, d'ou la copie -- et d'ou le fait que les annotations + // doivent avoir leur propre passe. + if needs_ann_copy { + self.generate_ann_mips(&mut encoder); + } + // Passe 3 : les annotations, par-dessus tout le reste, curseur compris : un flou de + // confidentialite doit le couvrir, sans quoi le cristal de Prism Glow, qui refracte + // l'enregistrement brut, montrerait ce que le flou cache. Elle existe meme sans flou : + // deux passes consecutives sur la MEME cible avec `LoadOp::Load` ne coutent rien de plus + // qu'une seule sur un GPU desktop, et un seul chemin de code vaut mieux qu'un + // branchement qui ne serait exerce que dans un projet sur dix. + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("ann-pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &self.rt_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&self.pipeline); + for a in &ann_draws { + rpass.set_bind_group(0, &a.bind, &[]); + rpass.draw(0..4, 0..1); + } + } self.gpu.context.submit(std::iter::once(encoder.finish())); Ok(()) } @@ -5807,6 +5842,39 @@ mod tests { assert!(failures.is_empty(), "{failures:#?}"); } + /// Un flou de confidentialité couvre le curseur, comme sous Windows et macOS. Le cristal de + /// Prism Glow réfracte l'enregistrement brut : dessiné par-dessus le flou, il montrerait ce + /// que le flou cache. Sous le flou, ce que change le curseur reste flou : aucun bord franc, là + /// où le serti du cristal, dessiné par-dessus, en ferait des centaines. + #[test] + fn a_privacy_blur_covers_the_crystal_cursor() { + let Some(gpu) = gpu() else { return }; + let comp = Compositor::new_sized(&gpu, 1280, 720).expect("Compositor::new_sized"); + let (y, uv) = model_screen_planes(false); + let blue = FakeFrame::from_planes(&gpu, 640, 360, &y, &uv); + let blur = r#""annotations":[{"id":"b","startSec":0,"endSec":10,"kind":"blur","x":0.3,"y":0.3,"w":0.6,"h":0.7,"blur":{"style":"blur","shape":"rectangle","color":"white","intensity":24,"blockSize":16}}]"#; + let scene = |show: bool| { + model_scene_json("null", Some(true), "default", show, 5.0) + .replace(r#"/arrow.png","#, r#"/arrow.png","sculpt":"prism-glow/arrow","#) + .replace(r#""annotations":[]"#, blur) + }; + let still = model_track("arrow", false, 0.5); + let with = compose_model(&comp, &blue, &scene(true), &still); + let without = compose_model(&comp, &blue, &scene(false), &still); + model_save("privacy-blur-over-crystal", &with); + // Ce que change le curseur, pixel par pixel ; un bord franc y est un saut de plus de 40 + // entre deux voisins. + let diff = |i: usize, c: usize| with[i * 4 + c] as i32 - without[i * 4 + c] as i32; + let changed = (0..1280 * 720).filter(|&i| (0..3).any(|c| diff(i, c) != 0)).count(); + let sharp = (0..1280 * 719) + .filter(|&i| i % 1280 < 1279) + .filter(|&i| (0..3).any(|c| (diff(i, c) - diff(i + 1, c)).abs() > 40 || (diff(i, c) - diff(i + 1280, c)).abs() > 40)) + .count(); + println!("sous le flou : {changed} px changés par le curseur, {sharp} bords francs"); + assert!(changed > 0, "le curseur ne change rien : il manque, ou le flou l'a effacé"); + assert_eq!(sharp, 0, "{sharp} bords francs sous le flou : le curseur est dessiné par-dessus"); + } + /// Les thèmes cerclés gardent le trait de leur dessin (`design/cursors/`) : sur la /// fleche comme sur la main, le modele est pour une bonne part de la couleur du trait (le /// plateau, le jonc, les rainures entre les doigts), autour de la couleur du corps, et montre From eeb0ae7c676e528b616acb7bd6fb96746445bc19 Mon Sep 17 00:00:00 2001 From: EtienneLescot Date: Wed, 30 Sep 2026 09:40:46 +0200 Subject: [PATCH 4/5] perf(compositor): group Prism Glow's triangles in bounding boxes Each ray tested every triangle of the crystal, 130 for the hand. The exporter now groups neighbouring triangles, eight at most, in a bounding box, and a ray skips the boxes it misses or enters past its best hit. The picture is the same (one pixel of the hand differs by 8 levels, a tie on a shared edge). The hand costs 2 to 3 times less on the GPU, and up to 3 times less on WARP. --- crates/compositor/src/compositor_linux.rs | 9 +- crates/compositor/src/prism_mesh.rs | 1040 ++++++++-------- crates/compositor/src/sculpt.rs | 49 +- crates/compositor/src/shaders.hlsl | 1075 +++++++++-------- crates/compositor/src/shaders.metal | 1066 ++++++++-------- crates/compositor/src/vk_shaders/layer.wgsl | 37 +- design/cursors/3d-direction.md | 3 +- .../prism-glow/model/export_compositor.py | 69 +- docs/3d-effects-v2.md | 3 +- 9 files changed, 1825 insertions(+), 1526 deletions(-) diff --git a/crates/compositor/src/compositor_linux.rs b/crates/compositor/src/compositor_linux.rs index b99db602a..74538623b 100644 --- a/crates/compositor/src/compositor_linux.rs +++ b/crates/compositor/src/compositor_linux.rs @@ -71,11 +71,12 @@ fn layer_bytes(cb: &LayerCB) -> &[u8] { unsafe { std::slice::from_raw_parts(cb as *const LayerCB as *const u8, LAYER_BYTES as usize) } } -/// Le maillage de Prism Glow tel que le lit le WGSL (`PrismMesh`) : les triangles, puis les -/// polygones, un point par vec4 (le pas d'un tableau uniform). +/// Le maillage de Prism Glow tel que le lit le WGSL (`PrismMesh`) : les triangles, les +/// polygones, un point par vec4 (le pas d'un tableau uniform), puis les boîtes. fn prism_mesh_bytes() -> Vec { - let points = crate::prism_mesh::POLYS.iter().map(|&[x, y]| [x, y, 0.0, 0.0]); - crate::prism_mesh::TRIS.iter().copied().chain(points).flatten().flat_map(f32::to_ne_bytes).collect() + use crate::prism_mesh::{BOXES, POLYS, TRIS}; + let points = POLYS.iter().map(|&[x, y]| [x, y, 0.0, 0.0]); + TRIS.iter().copied().chain(points).chain(BOXES).flatten().flat_map(f32::to_ne_bytes).collect() } /// Un calque de fond deja lie, en attente de son `draw`. `_buf`/`_tex`/`_view` diff --git a/crates/compositor/src/prism_mesh.rs b/crates/compositor/src/prism_mesh.rs index 2be2df9a7..c045462b0 100644 --- a/crates/compositor/src/prism_mesh.rs +++ b/crates/compositor/src/prism_mesh.rs @@ -7,7 +7,8 @@ //! cristal tient en quatre float4 : (v0, drapeaux), (e1, r), (e2, g), (normale, b) ; drapeaux = //! paroi (1) + 2 × arêtes réelles (bit k : l'arête opposée au sommet k). Le fond plat, en //! z = -épaisseur, est laissé au shader. `POLYS` tient les silhouettes (bord extérieur du -//! serti) puis les contours du cristal (son bord intérieur). +//! serti) puis les contours du cristal (son bord intérieur). `BOXES` range les triangles +//! par voisinage : deux float4 par boîte, (coin bas, premier triangle), (coin haut, nombre). /// Un des deux modèles : ses triangles et sa silhouette dans les tables, sa boîte de dessin. pub struct PrismModel { @@ -17,6 +18,8 @@ pub struct PrismModel { pub sil_count: usize, pub out_start: usize, pub out_count: usize, + pub box_start: usize, + pub box_count: usize, /// Largeur de la boîte, hauteur 1 (le sprite), en unités du modèle. pub size: [f32; 2], /// Hotspot, fraction de la boîte. @@ -40,6 +43,8 @@ pub const MODELS: [PrismModel; 2] = [ sil_count: 25, out_start: 67, out_count: 9, + box_start: 0, + box_count: 4, size: [0.63089, 1.0], hotspot: [0.022658, 0.064647], top: 0.0625, @@ -53,6 +58,8 @@ pub const MODELS: [PrismModel; 2] = [ sil_count: 42, out_start: 76, out_count: 34, + box_start: 4, + box_count: 18, size: [0.797658, 1.0], hotspot: [0.343388, 0.063582], top: 0.0625, @@ -62,18 +69,38 @@ pub const MODELS: [PrismModel; 2] = [ ]; pub const TRIS: [[f32; 4]; 644] = [ - [0.035429, 0.482055, 0.0, 14.0], - [0.134202, -0.125614, 0.115951, 0.000305], - [-0.004294, -0.419785, 0.0, 0.938689], - [-0.650191, 0.006652, 0.759742, 0.854994], + [0.252301, 0.266258, -0.042945, 11.0], + [-0.221166, -0.203988, 0.0, 0.01033], + [-0.221166, -0.203988, 0.042945, 0.045182], + [0.677984, -0.735077, 0.0, 0.577584], + [0.252301, 0.266258, -0.042945, 3.0], + [-0.221166, -0.203988, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.677984, -0.735077, 0.0, 0.577584], + [0.031135, 0.06227, -0.042945, 7.0], + [0.004294, 0.419785, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [-0.999948, 0.01023, 0.0, 0.577584], [0.169632, 0.356442, 0.115951, 14.0], [0.082669, -0.090184, -0.115951, 0.590616], [-0.138497, -0.294172, -0.115951, 0.737911], [0.466385, -0.505659, 0.725806, 0.973449], + [0.035429, 0.482055, 0.0, 14.0], + [0.134202, -0.125614, 0.115951, 0.000305], + [-0.004294, -0.419785, 0.0, 0.938689], + [-0.650191, 0.006652, 0.759742, 0.854994], + [0.031135, 0.06227, -0.042945, 9.0], + [0.004294, 0.419785, 0.0, 0.01033], + [0.004294, 0.419785, 0.042945, 0.045182], + [-0.999948, 0.01023, 0.0, 0.577584], [0.169632, 0.356442, 0.115951, 14.0], [0.159969, 0.080521, -0.045092, 0.381323], [0.082669, -0.090184, -0.115951, 0.056123], [0.461407, -0.510214, 0.725799, 0.973449], + [0.541104, 0.544325, -0.042945, 9.0], + [-0.288804, -0.278068, 0.0, 0.01033], + [-0.288804, -0.278068, 0.042945, 0.045182], + [0.69359, -0.72037, 0.0, 0.577584], [0.329601, 0.436963, 0.070859, 14.0], [0.211503, 0.107362, -0.070859, 0.545724], [-0.077301, -0.170706, -0.070859, 0.064805], @@ -82,53 +109,77 @@ pub const TRIS: [[f32; 4]; 644] = [ [0.061196, -0.096626, -0.010736, 0.000916], [-0.098773, -0.177147, 0.034356, 0.046662], [0.247834, 0.049458, 0.967539, 0.904662], + [0.541104, 0.544325, -0.042945, 7.0], + [-0.288804, -0.278068, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.69359, -0.72037, 0.0, 0.577584], + [0.199693, 0.595859, 0.0, 14.0], + [0.068712, -0.06227, 0.081595, 0.0], + [-0.030061, -0.239417, 0.115951, 0.01445], + [-0.504461, 0.426837, 0.750553, 0.254154], [0.268405, 0.533589, 0.081595, 14.0], [0.272699, 0.010736, -0.081595, 0.0], [0.061196, -0.096626, -0.010736, 0.025193], [0.283244, 0.073137, 0.956255, 0.527108], + [0.035429, 0.482055, 0.0, 14.0], + [0.0, 0.258742, 0.0, 0.0], + [0.134202, -0.125614, 0.115951, 0.473533], + [-0.653777, 0.0, 0.756687, 0.964691], [0.359663, 0.559356, 0.0, 14.0], [0.181442, -0.015031, 0.0, 0.006043], [-0.091258, -0.025767, 0.081595, 0.076188], [0.076465, 0.923048, 0.37701, 0.964691], - [0.199693, 0.595859, 0.0, 14.0], - [0.068712, -0.06227, 0.081595, 0.0], - [-0.030061, -0.239417, 0.115951, 0.01445], - [-0.504461, 0.426837, 0.750553, 0.254154], + [0.035429, 0.482055, -0.042945, 3.0], + [0.0, 0.258742, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [-1.0, 0.0, 0.0, 0.577584], + [0.035429, 0.482055, -0.042945, 11.0], + [0.0, 0.258742, 0.0, 0.01033], + [0.0, 0.258742, 0.042945, 0.045182], + [-1.0, 0.0, 0.0, 0.577584], + [0.035429, 0.740798, -0.042945, 7.0], + [0.164264, -0.144939, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.661622, 0.749838, 0.0, 0.577584], [0.035429, 0.740798, 0.0, 14.0], [0.164264, -0.144939, 0.0, 0.0], [0.134202, -0.384356, 0.115951, 0.010956], [0.332572, 0.376915, 0.864483, 0.215854], - [0.035429, 0.482055, 0.0, 14.0], - [0.0, 0.258742, 0.0, 0.0], - [0.134202, -0.125614, 0.115951, 0.473533], - [-0.653777, 0.0, 0.756687, 0.964691], + [0.035429, 0.740798, -0.042945, 11.0], + [0.164264, -0.144939, 0.0, 0.01033], + [0.164264, -0.144939, 0.042945, 0.045182], + [0.661622, 0.749838, 0.0, 0.577584], + [0.199693, 0.595859, -0.042945, 7.0], + [0.105215, 0.22546, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [-0.906183, 0.422886, 0.0, 0.577584], [0.199693, 0.595859, 0.0, 14.0], [0.105215, 0.22546, 0.0, 0.0], [0.068712, -0.06227, 0.081595, 0.015991], [-0.613882, 0.286478, 0.735581, 0.473533], + [0.199693, 0.595859, -0.042945, 11.0], + [0.105215, 0.22546, 0.0, 0.01033], + [0.105215, 0.22546, 0.042945, 0.045182], + [-0.906183, 0.422886, 0.0, 0.577584], [0.304908, 0.821319, 0.0, 14.0], [0.128834, -0.067638, 0.0, 0.0], [-0.036503, -0.28773, 0.081595, 0.254154], [0.133686, 0.254641, 0.957751, 0.973449], + [0.304908, 0.821319, -0.042945, 7.0], + [0.128834, -0.067638, 0.042945, 0.01033], + [0.0, 0.0, 0.042945, 0.045182], + [0.464834, 0.885398, 0.0, 0.577584], [0.433742, 0.753681, 0.0, 14.0], [-0.07408, -0.194325, 0.0, 0.0], [-0.165337, -0.220092, 0.081595, 0.964691], [0.683362, -0.260508, 0.682021, 0.964691], - [0.035429, 0.482055, -0.042945, 3.0], - [0.0, 0.258742, 0.042945, 0.01033], - [0.0, 0.0, 0.042945, 0.045182], - [-1.0, 0.0, 0.0, 0.577584], - [0.035429, 0.740798, -0.042945, 7.0], - [0.164264, -0.144939, 0.042945, 0.01033], - [0.0, 0.0, 0.042945, 0.045182], - [0.661622, 0.749838, 0.0, 0.577584], - [0.199693, 0.595859, -0.042945, 7.0], - [0.105215, 0.22546, 0.042945, 0.01033], - [0.0, 0.0, 0.042945, 0.045182], - [-0.906183, 0.422886, 0.0, 0.577584], - [0.304908, 0.821319, -0.042945, 7.0], - [0.128834, -0.067638, 0.042945, 0.01033], - [0.0, 0.0, 0.042945, 0.045182], + [0.433742, 0.753681, -0.042945, 11.0], + [-0.07408, -0.194325, 0.0, 0.01033], + [-0.07408, -0.194325, 0.042945, 0.045182], + [0.934406, -0.35621, 0.0, 0.577584], + [0.304908, 0.821319, -0.042945, 11.0], + [0.128834, -0.067638, 0.0, 0.01033], + [0.128834, -0.067638, 0.042945, 0.045182], [0.464834, 0.885398, 0.0, 0.577584], [0.433742, 0.753681, -0.042945, 7.0], [-0.07408, -0.194325, 0.042945, 0.01033], @@ -138,70 +189,86 @@ pub const TRIS: [[f32; 4]; 644] = [ [0.181442, -0.015031, 0.042945, 0.01033], [0.0, 0.0, 0.042945, 0.045182], [0.082557, 0.996586, 0.0, 0.577584], - [0.541104, 0.544325, -0.042945, 7.0], - [-0.288804, -0.278068, 0.042945, 0.01033], - [0.0, 0.0, 0.042945, 0.045182], - [0.69359, -0.72037, 0.0, 0.577584], - [0.252301, 0.266258, -0.042945, 3.0], - [-0.221166, -0.203988, 0.042945, 0.01033], - [0.0, 0.0, 0.042945, 0.045182], - [0.677984, -0.735077, 0.0, 0.577584], - [0.031135, 0.06227, -0.042945, 7.0], - [0.004294, 0.419785, 0.042945, 0.01033], - [0.0, 0.0, 0.042945, 0.045182], - [-0.999948, 0.01023, 0.0, 0.577584], - [0.035429, 0.482055, -0.042945, 11.0], - [0.0, 0.258742, 0.0, 0.01033], - [0.0, 0.258742, 0.042945, 0.045182], - [-1.0, 0.0, 0.0, 0.577584], - [0.035429, 0.740798, -0.042945, 11.0], - [0.164264, -0.144939, 0.0, 0.01033], - [0.164264, -0.144939, 0.042945, 0.045182], - [0.661622, 0.749838, 0.0, 0.577584], - [0.199693, 0.595859, -0.042945, 11.0], - [0.105215, 0.22546, 0.0, 0.01033], - [0.105215, 0.22546, 0.042945, 0.045182], - [-0.906183, 0.422886, 0.0, 0.577584], - [0.304908, 0.821319, -0.042945, 11.0], - [0.128834, -0.067638, 0.0, 0.01033], - [0.128834, -0.067638, 0.042945, 0.045182], - [0.464834, 0.885398, 0.0, 0.577584], - [0.433742, 0.753681, -0.042945, 11.0], - [-0.07408, -0.194325, 0.0, 0.01033], - [-0.07408, -0.194325, 0.042945, 0.045182], - [0.934406, -0.35621, 0.0, 0.577584], [0.359663, 0.559356, -0.042945, 11.0], [0.181442, -0.015031, 0.0, 0.01033], [0.181442, -0.015031, 0.042945, 0.045182], [0.082557, 0.996586, 0.0, 0.577584], - [0.541104, 0.544325, -0.042945, 9.0], - [-0.288804, -0.278068, 0.0, 0.01033], - [-0.288804, -0.278068, 0.042945, 0.045182], - [0.69359, -0.72037, 0.0, 0.577584], - [0.252301, 0.266258, -0.042945, 11.0], - [-0.221166, -0.203988, 0.0, 0.01033], - [-0.221166, -0.203988, 0.042945, 0.045182], - [0.677984, -0.735077, 0.0, 0.577584], - [0.031135, 0.06227, -0.042945, 9.0], - [0.004294, 0.419785, 0.0, 0.01033], - [0.004294, 0.419785, 0.042945, 0.045182], - [-0.999948, 0.01023, 0.0, 0.577584], - [-0.031366, 0.229295, 0.058405, 14.0], - [0.089771, -0.137361, -0.02055, 0.445197], - [0.035692, -0.155748, -0.008653, 0.973449], - [0.216369, -0.004654, 0.976301, 0.973449], - [0.044345, 0.043263, 0.0, 6.0], - [-0.040019, 0.030284, 0.049753, 0.000305], - [0.014061, 0.048671, 0.037855, 0.964691], - [0.365607, -0.634949, 0.680566, 0.973449], + [0.044345, 0.043263, -0.043263, 11.0], + [-0.043263, -0.005408, 0.0, 0.01033], + [-0.043263, -0.005408, 0.043263, 0.045182], + [0.124035, -0.992278, 0.0, 0.577584], + [0.044345, 0.043263, -0.043263, 7.0], + [-0.043263, -0.005408, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.124035, -0.992278, 0.0, 0.577584], + [0.001082, 0.037855, -0.043263, 7.0], + [-0.044345, 0.040019, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.669964, -0.742393, 0.0, 0.577584], + [0.044345, 0.043263, 0.0, 10.0], + [-0.043263, -0.005408, 0.0, 0.000305], + [-0.040019, 0.030284, 0.049753, 0.964691], + [0.101434, -0.811469, 0.575525, 0.973449], [0.004326, 0.073548, 0.049753, 14.0], [-0.003245, -0.035692, -0.049753, 0.0], [-0.04759, 0.004326, -0.049753, 0.955978], [-0.580475, -0.643229, 0.499304, 0.896269], + [0.001082, 0.037855, -0.043263, 11.0], + [-0.044345, 0.040019, 0.0, 0.01033], + [-0.044345, 0.040019, 0.043263, 0.045182], + [-0.669964, -0.742393, 0.0, 0.577584], + [-0.031366, 0.229295, 0.058405, 14.0], + [0.035692, -0.155748, -0.008653, 0.000305], + [-0.011897, -0.151422, -0.058405, 0.955978], + [-0.716533, -0.20131, 0.667874, 0.904662], + [-0.043263, 0.077874, -0.043263, 7.0], + [-0.001082, 0.154666, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999976, -0.006993, 0.0, 0.577584], + [-0.031366, 0.229295, 0.058405, 14.0], + [0.089771, -0.137361, -0.02055, 0.445197], + [0.035692, -0.155748, -0.008653, 0.973449], + [0.216369, -0.004654, 0.976301, 0.973449], [-0.031366, 0.229295, 0.058405, 14.0], [-0.011897, -0.151422, -0.058405, 0.000305], [-0.012979, 0.003245, -0.058405, 0.955978], [-0.976245, -0.006827, 0.216564, 0.904662], + [-0.043263, 0.077874, -0.043263, 9.0], + [-0.001082, 0.154666, 0.0, 0.01033], + [-0.001082, 0.154666, 0.043263, 0.045182], + [-0.999976, -0.006993, 0.0, 0.577584], + [-0.045426, 0.564586, 0.052998, 14.0], + [0.014061, -0.335291, 0.005408, 0.520989], + [0.001082, -0.332046, -0.052998, 0.088655], + [-0.975941, -0.037462, 0.214794, 0.973449], + [-0.044345, 0.23254, 0.0, 3.0], + [0.0, 0.0, -0.043263, 0.01033], + [-0.001082, 0.332046, -0.043263, 0.045182], + [-0.999995, -0.003257, 0.0, 0.577584], + [-0.163319, 0.380717, -0.043263, 7.0], + [-0.060569, 0.02704, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.407651, -0.913138, 0.0, 0.577584], + [-0.094098, 0.431551, -0.043263, 11.0], + [-0.069221, -0.050834, 0.0, 0.01033], + [-0.069221, -0.050834, 0.043263, 0.045182], + [0.591909, -0.806004, 0.0, 0.577584], + [-0.163319, 0.380717, -0.043263, 11.0], + [-0.060569, 0.02704, 0.0, 0.01033], + [-0.060569, 0.02704, 0.043263, 0.045182], + [-0.407651, -0.913138, 0.0, 0.577584], + [0.075711, 0.08869, -0.043263, 11.0], + [-0.031366, -0.045426, 0.0, 0.01033], + [-0.031366, -0.045426, 0.043263, 0.045182], + [0.822897, -0.568191, 0.0, 0.577584], + [0.044345, 0.043263, 0.0, 6.0], + [-0.040019, 0.030284, 0.049753, 0.000305], + [0.014061, 0.048671, 0.037855, 0.964691], + [0.365607, -0.634949, 0.680566, 0.973449], + [0.075711, 0.08869, -0.043263, 7.0], + [-0.031366, -0.045426, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.822897, -0.568191, 0.0, 0.577584], [0.058405, 0.091934, 0.037855, 14.0], [0.017305, -0.003245, -0.037855, 0.0], [-0.014061, -0.048671, -0.037855, 0.473533], @@ -210,310 +277,338 @@ pub const TRIS: [[f32; 4]; 644] = [ [0.107077, -0.140606, -0.058405, 0.0], [0.089771, -0.137361, -0.02055, 0.376257], [0.812052, 0.481346, 0.329966, 0.830777], - [-0.045426, 0.564586, 0.052998, 14.0], - [0.014061, -0.335291, 0.005408, 0.520989], - [0.001082, -0.332046, -0.052998, 0.088655], - [-0.975941, -0.037462, 0.214794, 0.973449], - [0.10924, 0.439122, 0.0, 14.0], - [-0.031366, -0.031366, 0.0, 0.132876], - [-0.154666, 0.125464, 0.052998, 0.028428], - [0.182761, -0.182761, 0.966021, 0.955978], - [0.128708, 0.463999, 0.063813, 14.0], - [-0.019468, -0.024876, -0.063813, 0.168276], - [-0.174135, 0.100587, -0.010816, 0.038209], - [-0.469238, -0.764881, 0.441331, 0.955978], - [0.201174, 0.638133, 0.0822, 14.0], - [-0.072466, -0.174135, -0.018387, 0.18117], - [-0.246601, -0.073548, -0.029203, 0.082277], - [-0.098561, -0.063844, 0.993081, 0.973449], - [0.128708, 0.463999, 0.063813, 14.0], - [0.055161, -0.146014, -0.025958, 0.246204], - [0.016224, -0.156829, -0.014061, 0.964691], - [0.305397, -0.053644, 0.950713, 0.973449], - [0.183869, 0.317985, 0.037855, 6.0], - [-0.036774, -0.045426, -0.037855, 0.0], - [-0.038937, -0.010816, 0.011897, 0.806958], - [0.374433, -0.753477, 0.540438, 0.854994], - [0.144932, 0.307169, 0.049753, 14.0], - [0.002163, -0.034611, -0.049753, 0.0], - [-0.033529, 0.0, -0.049753, 0.9131], - [-0.630237, -0.649931, 0.424725, 0.904662], - [0.10924, 0.439122, 0.0, 6.0], - [0.035692, -0.131953, 0.049753, 0.18117], - [0.002163, -0.131953, 0.0, 0.955978], - [-0.82919, -0.013593, 0.558802, 0.964691], - [0.183869, 0.317985, 0.037855, 14.0], - [0.02704, -0.004326, -0.037855, 0.0], - [0.005408, -0.037855, -0.037855, 0.514916], - [0.70062, -0.452013, 0.552101, 0.846876], - [0.210908, 0.445612, 0.0, 10.0], - [0.0, -0.131953, 0.0, 0.0], + [0.077874, 0.407756, -0.043263, 11.0], + [-0.002163, -0.319067, 0.0, 0.01033], + [-0.002163, -0.319067, 0.043263, 0.045182], + [0.999977, -0.006779, 0.0, 0.577584], + [-0.031366, 0.229295, 0.058405, 14.0], + [0.10924, 0.178461, -0.058405, 0.0], + [0.107077, -0.140606, -0.058405, 0.376257], + [0.475583, -0.003224, 0.879665, 0.830777], + [0.147095, 0.272559, -0.043263, 7.0], + [-0.035692, 0.034611, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.696146, -0.7179, 0.0, 0.577584], + [0.147095, 0.272559, -0.043263, 11.0], + [-0.035692, 0.034611, 0.0, 0.01033], + [-0.035692, 0.034611, 0.043263, 0.045182], + [-0.696146, -0.7179, 0.0, 0.577584], + [0.144932, 0.307169, 0.049753, 14.0], + [0.002163, -0.034611, -0.049753, 0.0], + [-0.033529, 0.0, -0.049753, 0.9131], + [-0.630237, -0.649931, 0.424725, 0.904662], + [0.183869, 0.317985, 0.037855, 6.0], + [-0.036774, -0.045426, -0.037855, 0.0], + [-0.038937, -0.010816, 0.011897, 0.806958], + [0.374433, -0.753477, 0.540438, 0.854994], + [0.077874, 0.407756, -0.043263, 7.0], + [-0.002163, -0.319067, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.999977, -0.006779, 0.0, 0.577584], + [0.111403, 0.307169, -0.043263, 7.0], + [-0.002163, 0.131953, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999866, -0.016391, 0.0, 0.577584], + [0.10924, 0.439122, 0.0, 6.0], + [0.035692, -0.131953, 0.049753, 0.18117], + [0.002163, -0.131953, 0.0, 0.955978], + [-0.82919, -0.013593, 0.558802, 0.964691], + [0.128708, 0.463999, 0.063813, 14.0], + [0.055161, -0.146014, -0.025958, 0.246204], + [0.016224, -0.156829, -0.014061, 0.964691], + [0.305397, -0.053644, 0.950713, 0.973449], + [0.111403, 0.307169, -0.043263, 11.0], + [-0.002163, 0.131953, 0.0, 0.01033], + [-0.002163, 0.131953, 0.043263, 0.045182], + [-0.999866, -0.016391, 0.0, 0.577584], + [0.189277, 0.28013, -0.043263, 11.0], + [-0.042182, -0.007571, 0.0, 0.01033], + [-0.042182, -0.007571, 0.043263, 0.045182], + [0.176664, -0.984271, 0.0, 0.577584], + [0.183869, 0.317985, 0.037855, 10.0], + [0.005408, -0.037855, -0.037855, 0.0], + [-0.036774, -0.045426, -0.037855, 0.806958], + [0.124328, -0.692684, 0.710445, 0.854994], + [0.189277, 0.28013, -0.043263, 7.0], + [-0.042182, -0.007571, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.176664, -0.984271, 0.0, 0.577584], + [0.183869, 0.317985, 0.037855, 14.0], + [0.02704, -0.004326, -0.037855, 0.0], + [0.005408, -0.037855, -0.037855, 0.514916], + [0.70062, -0.452013, 0.552101, 0.846876], + [0.210908, 0.313659, -0.043263, 11.0], + [-0.021632, -0.033529, 0.0, 0.01033], + [-0.021632, -0.033529, 0.043263, 0.045182], + [0.840297, -0.542127, 0.0, 0.577584], + [0.210908, 0.445612, 0.0, 10.0], + [0.0, -0.131953, 0.0, 0.0], [-0.02704, -0.127627, 0.037855, 0.439658], [0.813734, 0.0, 0.581238, 0.871366], + [0.210908, 0.313659, -0.043263, 7.0], + [-0.021632, -0.033529, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.840297, -0.542127, 0.0, 0.577584], + [0.210908, 0.445612, -0.043263, 11.0], + [0.0, -0.131953, 0.0, 0.01033], + [0.0, -0.131953, 0.043263, 0.045182], + [1.0, 0.0, 0.0, 0.577584], + [0.241193, 0.343943, -0.043263, 7.0], + [-0.002163, 0.107077, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999796, -0.020198, 0.0, 0.577584], + [0.276885, 0.311496, -0.043263, 11.0], + [-0.035692, 0.032447, 0.0, 0.01033], + [-0.035692, 0.032447, 0.043263, 0.045182], + [-0.672673, -0.73994, 0.0, 0.577584], + [0.23903, 0.45102, 0.0, 14.0], + [0.042182, -0.108158, 0.046508, 0.000305], + [0.002163, -0.107077, 0.0, 0.846876], + [-0.758098, -0.015315, 0.651961, 0.896269], + [0.276885, 0.311496, -0.043263, 7.0], + [-0.035692, 0.032447, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.672673, -0.73994, 0.0, 0.577584], + [0.281211, 0.342862, 0.046508, 14.0], + [-0.004326, -0.031366, -0.046508, 0.0], + [-0.040019, 0.001082, -0.046508, 0.752941], + [-0.58651, -0.645161, 0.489667, 0.846876], [0.246601, 0.459672, 0.06165, 14.0], [0.070303, -0.101669, -0.024876, 0.262257], [0.034611, -0.116811, -0.015142, 0.973449], [0.27928, -0.041605, 0.959308, 0.973449], + [0.319067, 0.319067, -0.043263, 11.0], + [-0.042182, -0.007571, 0.0, 0.01033], + [-0.042182, -0.007571, 0.043263, 0.045182], + [0.176664, -0.984271, 0.0, 0.577584], + [0.319067, 0.319067, 0.0, 10.0], + [-0.042182, -0.007571, 0.0, 0.0], + [-0.037855, 0.023795, 0.046508, 0.896269], + [0.1483, -0.826245, 0.54344, 0.896269], + [0.319067, 0.319067, -0.043263, 7.0], + [-0.042182, -0.007571, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.176664, -0.984271, 0.0, 0.577584], [0.319067, 0.319067, 0.0, 6.0], [-0.037855, 0.023795, 0.046508, 0.0], [-0.002163, 0.038937, 0.036774, 0.896269], [0.437906, -0.604311, 0.665618, 0.896269], - [0.281211, 0.342862, 0.046508, 14.0], - [-0.004326, -0.031366, -0.046508, 0.0], - [-0.040019, 0.001082, -0.046508, 0.752941], - [-0.58651, -0.645161, 0.489667, 0.846876], - [0.23903, 0.45102, 0.0, 14.0], - [0.042182, -0.108158, 0.046508, 0.000305], - [0.002163, -0.107077, 0.0, 0.846876], - [-0.758098, -0.015315, 0.651961, 0.896269], [0.316904, 0.358004, 0.036774, 14.0], [0.024876, -0.004326, -0.036774, 0.0], [0.002163, -0.038937, -0.036774, 0.617212], [0.707338, -0.464191, 0.533104, 0.921584], + [0.34178, 0.353677, -0.043263, 11.0], + [-0.022713, -0.034611, 0.0, 0.01033], + [-0.022713, -0.034611, 0.043263, 0.045182], + [0.836048, -0.548657, 0.0, 0.577584], [0.340698, 0.474815, 0.0, 14.0], [0.001082, -0.121137, 0.0, 0.0], [-0.023795, -0.116811, 0.036774, 0.473533], [0.828664, 0.007399, 0.559697, 0.930114], - [0.375309, 0.488875, 0.0, 14.0], - [0.078955, -0.08869, 0.032447, 0.223224], - [0.036774, -0.105995, 0.043263, 0.964691], - [0.071225, 0.39802, 0.914608, 0.973449], - [0.45102, 0.359085, 0.0, 6.0], - [-0.038937, 0.023795, 0.043263, 0.000305], - [0.003245, 0.0411, 0.032447, 0.879622], - [0.417852, -0.583049, 0.696744, 0.9131], + [0.34178, 0.353677, -0.043263, 7.0], + [-0.022713, -0.034611, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.836048, -0.548657, 0.0, 0.577584], + [0.340698, 0.474815, -0.043263, 11.0], + [0.001082, -0.121137, 0.0, 0.01033], + [0.001082, -0.121137, 0.043263, 0.045182], + [0.99996, 0.008928, 0.0, 0.577584], + [0.411001, 0.351514, -0.043263, 11.0], + [-0.034611, 0.030284, 0.0, 0.01033], + [-0.034611, 0.030284, 0.043263, 0.045182], + [-0.658505, -0.752577, 0.0, 0.577584], + [0.411001, 0.351514, -0.043263, 7.0], + [-0.034611, 0.030284, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.658505, -0.752577, 0.0, 0.577584], [0.412083, 0.38288, 0.043263, 14.0], [-0.001082, -0.031366, -0.043263, 0.0], [-0.035692, -0.001082, -0.043263, 0.745403], [-0.574039, -0.656045, 0.489984, 0.854994], - [0.375309, 0.488875, 0.0, 14.0], - [0.036774, -0.105995, 0.043263, 0.000305], - [0.001082, -0.107077, 0.0, 0.879622], - [-0.771254, -0.00779, 0.63648, 0.896269], + [0.45102, 0.359085, -0.043263, 11.0], + [-0.040019, -0.007571, 0.0, 0.01033], + [-0.040019, -0.007571, 0.043263, 0.045182], + [0.185892, -0.98257, 0.0, 0.577584], + [0.45102, 0.359085, 0.0, 10.0], + [-0.040019, -0.007571, 0.0, 0.000305], + [-0.038937, 0.023795, 0.043263, 0.879622], + [0.151736, -0.802035, 0.577682, 0.9131], + [0.45102, 0.359085, -0.043263, 7.0], + [-0.040019, -0.007571, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.185892, -0.98257, 0.0, 0.577584], + [0.45102, 0.359085, 0.0, 6.0], + [-0.038937, 0.023795, 0.043263, 0.000305], + [0.003245, 0.0411, 0.032447, 0.879622], + [0.417852, -0.583049, 0.696744, 0.9131], + [0.473733, 0.391533, -0.043263, 11.0], + [-0.022713, -0.032447, 0.0, 0.01033], + [-0.022713, -0.032447, 0.043263, 0.045182], + [0.819232, -0.573462, 0.0, 0.577584], [0.454264, 0.400185, 0.032447, 14.0], [0.019468, -0.008653, -0.032447, 0.0], [-0.003245, -0.0411, -0.032447, 0.527108], [0.688617, -0.482032, 0.541712, 0.887922], - [0.473733, 0.603523, 0.0, 14.0], - [0.0, -0.21199, 0.0, 0.158953], - [-0.019468, -0.203337, 0.032447, 0.030716], - [0.857493, 0.0, 0.514496, 0.964691], - [0.179543, 0.466162, 0.058405, 10.0], - [-0.050834, -0.002163, 0.005408, 0.000305], - [0.021632, 0.171972, 0.023795, 0.022171], - [0.110896, -0.149888, 0.982464, 0.381323], - [0.326638, 0.830655, 0.0, 14.0], - [0.048671, -0.34178, 0.0, 0.007492], - [-0.125464, -0.192522, 0.0822, 0.054475], - [0.472493, 0.067285, 0.878762, 0.964691], - [0.326638, 0.830655, 0.0, 14.0], - [0.139524, -0.21199, 0.0, 0.0], - [0.048671, -0.34178, 0.0, 0.015213], - [0.0, 0.0, 1.0, 0.428687], - [-0.012979, 0.832818, 0.0, 14.0], - [0.339617, -0.002163, 0.0, 0.0], - [0.214153, -0.194685, 0.0822, 0.027314], - [0.002492, 0.391296, 0.920261, 0.49102], - [-0.012979, 0.832818, 0.0, 14.0], - [0.214153, -0.194685, 0.0822, 0.000305], - [-0.032447, -0.268232, 0.052998, 0.930114], - [-0.176863, 0.211325, 0.961281, 0.973449], - [-0.221724, 0.475896, 0.0, 14.0], - [0.139524, 0.055161, 0.043263, 0.520989], - [0.054079, -0.059487, 0.046508, 0.973449], - [-0.39307, 0.317373, 0.863001, 0.973449], + [0.473733, 0.391533, -0.043263, 7.0], + [-0.022713, -0.032447, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.819232, -0.573462, 0.0, 0.577584], + [-0.223888, 0.407756, -0.043263, 7.0], + [0.002163, 0.06814, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999496, 0.03173, 0.0, 0.577584], + [-0.223888, 0.407756, -0.043263, 11.0], + [0.002163, 0.06814, 0.0, 0.01033], + [0.002163, 0.06814, 0.043263, 0.045182], + [-0.999496, 0.03173, 0.0, 0.577584], [-0.167645, 0.416409, 0.046508, 14.0], [-0.056242, -0.008653, -0.046508, 0.000305], [-0.054079, 0.059487, -0.046508, 0.955978], [-0.638985, 0.020285, 0.768952, 0.973449], + [-0.167645, 0.416409, 0.046508, 14.0], + [0.004326, -0.035692, -0.046508, 0.000305], + [-0.056242, -0.008653, -0.046508, 0.955978], + [-0.339782, -0.761112, 0.552501, 0.973449], + [-0.221724, 0.475896, 0.0, 14.0], + [0.139524, 0.055161, 0.043263, 0.520989], + [0.054079, -0.059487, 0.046508, 0.973449], + [-0.39307, 0.317373, 0.863001, 0.973449], [-0.167645, 0.416409, 0.046508, 6.0], [0.073548, 0.015142, -0.046508, 0.0], [0.004326, -0.035692, -0.046508, 0.768154], [0.490917, -0.668482, 0.558688, 0.871366], - [-0.099506, 0.68356, 0.0, 14.0], - [0.017305, -0.152503, 0.043263, 0.0], - [-0.122219, -0.207664, 0.0, 0.723049], - [-0.365879, 0.215335, 0.905408, 0.854994], - [-0.099506, 0.68356, 0.0, 14.0], - [0.054079, -0.118974, 0.052998, 0.0], - [0.017305, -0.152503, 0.043263, 0.514916], - [-0.41958, 0.203352, 0.884647, 0.863157], - [-0.099506, 0.68356, 0.0, 14.0], - [0.086527, 0.149258, 0.0, 0.0], - [0.054079, -0.118974, 0.052998, 0.262257], - [-0.385563, 0.223515, 0.8952, 0.9131], - [-0.044345, 0.23254, 0.0, 7.0], - [-0.001082, 0.332046, -0.043263, 0.01033], - [-0.001082, 0.332046, 0.052998, 0.045182], - [-0.999995, -0.003257, 0.0, 0.577584], - [-0.045426, 0.564586, -0.043263, 7.0], - [-0.036774, -0.033529, 0.086527, 0.01033], - [0.0, 0.0, 0.096261, 0.045182], - [0.673754, -0.738956, 0.0, 0.577584], - [-0.0822, 0.531057, -0.043263, 7.0], - [-0.011897, -0.099506, 0.043263, 0.01033], - [0.0, 0.0, 0.086527, 0.045182], - [0.992928, -0.11872, 0.0, 0.577584], [-0.094098, 0.431551, -0.043263, 7.0], [-0.069221, -0.050834, 0.043263, 0.01033], [0.0, 0.0, 0.043263, 0.045182], [0.591909, -0.806004, 0.0, 0.577584], - [-0.163319, 0.380717, -0.043263, 7.0], - [-0.060569, 0.02704, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.407651, -0.913138, 0.0, 0.577584], - [-0.223888, 0.407756, -0.043263, 7.0], - [0.002163, 0.06814, 0.043263, 0.01033], + [-0.167645, 0.416409, 0.046508, 10.0], + [0.085445, 0.114648, -0.003245, 0.0], + [0.073548, 0.015142, -0.046508, 0.768154], + [0.548356, -0.38771, 0.740936, 0.871366], + [-0.0822, 0.531057, -0.043263, 11.0], + [-0.011897, -0.099506, 0.0, 0.01033], + [-0.011897, -0.099506, 0.043263, 0.045182], + [0.992928, -0.11872, 0.0, 0.577584], + [-0.044345, 0.23254, 0.0, 7.0], + [-0.001082, 0.332046, -0.043263, 0.01033], + [-0.001082, 0.332046, 0.052998, 0.045182], + [-0.999995, -0.003257, 0.0, 0.577584], + [-0.045426, 0.564586, 0.052998, 14.0], + [0.1233, -0.156829, -0.052998, 0.520989], + [0.014061, -0.335291, 0.005408, 0.088655], + [0.429355, 0.032562, 0.902549, 0.973449], + [0.10924, 0.439122, 0.0, 14.0], + [-0.031366, -0.031366, 0.0, 0.132876], + [-0.154666, 0.125464, 0.052998, 0.028428], + [0.182761, -0.182761, 0.966021, 0.955978], + [0.10924, 0.439122, -0.043263, 11.0], + [-0.031366, -0.031366, 0.0, 0.01033], + [-0.031366, -0.031366, 0.043263, 0.045182], + [0.707107, -0.707107, 0.0, 0.577584], + [0.10924, 0.439122, -0.043263, 7.0], + [-0.031366, -0.031366, 0.043263, 0.01033], [0.0, 0.0, 0.043263, 0.045182], - [-0.999496, 0.03173, 0.0, 0.577584], + [0.707107, -0.707107, 0.0, 0.577584], + [0.10924, 0.439122, 0.0, 10.0], + [0.019468, 0.024876, 0.063813, 0.18117], + [0.035692, -0.131953, 0.049753, 0.955978], + [-0.933936, -0.126584, 0.334276, 0.964691], + [0.128708, 0.463999, 0.063813, 14.0], + [0.050834, 0.002163, -0.005408, 0.0], + [0.0822, -0.018387, -0.063813, 0.439658], + [0.07859, -0.92643, 0.368172, 0.871366], [-0.221724, 0.475896, -0.043263, 7.0], [0.122219, 0.207664, 0.043263, 0.01033], [0.0, 0.0, 0.043263, 0.045182], [-0.861819, 0.507216, 0.0, 0.577584], + [-0.221724, 0.475896, -0.043263, 9.0], + [0.122219, 0.207664, 0.0, 0.01033], + [0.122219, 0.207664, 0.043263, 0.045182], + [-0.861819, 0.507216, 0.0, 0.577584], + [-0.099506, 0.68356, 0.0, 14.0], + [0.017305, -0.152503, 0.043263, 0.0], + [-0.122219, -0.207664, 0.0, 0.723049], + [-0.365879, 0.215335, 0.905408, 0.854994], + [-0.0822, 0.531057, -0.043263, 7.0], + [-0.011897, -0.099506, 0.043263, 0.01033], + [0.0, 0.0, 0.086527, 0.045182], + [0.992928, -0.11872, 0.0, 0.577584], + [-0.099506, 0.68356, 0.0, 14.0], + [0.054079, -0.118974, 0.052998, 0.0], + [0.017305, -0.152503, 0.043263, 0.514916], + [-0.41958, 0.203352, 0.884647, 0.863157], [-0.099506, 0.68356, -0.043263, 3.0], [0.086527, 0.149258, 0.043263, 0.01033], [0.0, 0.0, 0.043263, 0.045182], [-0.86514, 0.501531, 0.0, 0.577584], + [-0.045426, 0.564586, -0.043263, 11.0], + [-0.036774, -0.033529, 0.0, 0.01033], + [-0.036774, -0.033529, 0.086527, 0.045182], + [0.673754, -0.738956, 0.0, 0.577584], + [-0.045426, 0.564586, -0.043263, 7.0], + [-0.036774, -0.033529, 0.086527, 0.01033], + [0.0, 0.0, 0.096261, 0.045182], + [0.673754, -0.738956, 0.0, 0.577584], + [-0.099506, 0.68356, 0.0, 14.0], + [0.086527, 0.149258, 0.0, 0.0], + [0.054079, -0.118974, 0.052998, 0.262257], + [-0.385563, 0.223515, 0.8952, 0.9131], + [-0.099506, 0.68356, -0.043263, 11.0], + [0.086527, 0.149258, 0.0, 0.01033], + [0.086527, 0.149258, 0.043263, 0.045182], + [-0.86514, 0.501531, 0.0, 0.577584], + [-0.012979, 0.832818, 0.0, 14.0], + [0.214153, -0.194685, 0.0822, 0.000305], + [-0.032447, -0.268232, 0.052998, 0.930114], + [-0.176863, 0.211325, 0.961281, 0.973449], + [0.128708, 0.463999, 0.063813, 14.0], + [-0.019468, -0.024876, -0.063813, 0.168276], + [-0.174135, 0.100587, -0.010816, 0.038209], + [-0.469238, -0.764881, 0.441331, 0.955978], + [0.201174, 0.638133, 0.0822, 14.0], + [-0.072466, -0.174135, -0.018387, 0.18117], + [-0.246601, -0.073548, -0.029203, 0.082277], + [-0.098561, -0.063844, 0.993081, 0.973449], [-0.012979, 0.832818, -0.043263, 7.0], [0.339617, -0.002163, 0.043263, 0.01033], [0.0, 0.0, 0.043263, 0.045182], [0.006369, 0.99998, 0.0, 0.577584], - [0.326638, 0.830655, -0.043263, 7.0], - [0.139524, -0.21199, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.835314, 0.549773, 0.0, 0.577584], - [0.466162, 0.618665, -0.043263, 3.0], - [0.007571, -0.015142, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.894427, 0.447214, 0.0, 0.577584], - [0.473733, 0.603523, -0.043263, 7.0], - [0.0, -0.21199, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [1.0, 0.0, 0.0, 0.577584], - [0.473733, 0.391533, -0.043263, 7.0], - [-0.022713, -0.032447, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.819232, -0.573462, 0.0, 0.577584], - [0.45102, 0.359085, -0.043263, 7.0], - [-0.040019, -0.007571, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.185892, -0.98257, 0.0, 0.577584], - [0.411001, 0.351514, -0.043263, 7.0], - [-0.034611, 0.030284, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.658505, -0.752577, 0.0, 0.577584], - [0.376391, 0.381799, -0.043263, 7.0], - [-0.001082, 0.107077, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.999949, -0.0101, 0.0, 0.577584], - [0.375309, 0.488875, -0.043263, 7.0], - [-0.034611, -0.014061, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.376377, -0.926467, 0.0, 0.577584], - [0.340698, 0.474815, -0.043263, 7.0], - [0.001082, -0.121137, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.99996, 0.008928, 0.0, 0.577584], - [0.34178, 0.353677, -0.043263, 7.0], - [-0.022713, -0.034611, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.836048, -0.548657, 0.0, 0.577584], - [0.319067, 0.319067, -0.043263, 7.0], - [-0.042182, -0.007571, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.176664, -0.984271, 0.0, 0.577584], - [0.276885, 0.311496, -0.043263, 7.0], - [-0.035692, 0.032447, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.672673, -0.73994, 0.0, 0.577584], - [0.241193, 0.343943, -0.043263, 7.0], - [-0.002163, 0.107077, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.999796, -0.020198, 0.0, 0.577584], - [0.23903, 0.45102, -0.043263, 7.0], - [-0.028121, -0.005408, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.188847, -0.982006, 0.0, 0.577584], - [0.210908, 0.445612, -0.043263, 7.0], - [0.0, -0.131953, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [1.0, 0.0, 0.0, 0.577584], - [0.210908, 0.313659, -0.043263, 7.0], - [-0.021632, -0.033529, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.840297, -0.542127, 0.0, 0.577584], - [0.189277, 0.28013, -0.043263, 7.0], - [-0.042182, -0.007571, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.176664, -0.984271, 0.0, 0.577584], - [0.147095, 0.272559, -0.043263, 7.0], - [-0.035692, 0.034611, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.696146, -0.7179, 0.0, 0.577584], - [0.111403, 0.307169, -0.043263, 7.0], - [-0.002163, 0.131953, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.999866, -0.016391, 0.0, 0.577584], - [0.10924, 0.439122, -0.043263, 7.0], - [-0.031366, -0.031366, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.707107, -0.707107, 0.0, 0.577584], - [0.077874, 0.407756, -0.043263, 7.0], - [-0.002163, -0.319067, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.999977, -0.006779, 0.0, 0.577584], - [0.075711, 0.08869, -0.043263, 7.0], - [-0.031366, -0.045426, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.822897, -0.568191, 0.0, 0.577584], - [0.044345, 0.043263, -0.043263, 7.0], - [-0.043263, -0.005408, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [0.124035, -0.992278, 0.0, 0.577584], - [0.001082, 0.037855, -0.043263, 7.0], - [-0.044345, 0.040019, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.669964, -0.742393, 0.0, 0.577584], - [-0.043263, 0.077874, -0.043263, 7.0], - [-0.001082, 0.154666, 0.043263, 0.01033], - [0.0, 0.0, 0.043263, 0.045182], - [-0.999976, -0.006993, 0.0, 0.577584], - [0.044345, 0.043263, 0.0, 10.0], - [-0.043263, -0.005408, 0.0, 0.000305], - [-0.040019, 0.030284, 0.049753, 0.964691], - [0.101434, -0.811469, 0.575525, 0.973449], - [-0.031366, 0.229295, 0.058405, 14.0], - [0.035692, -0.155748, -0.008653, 0.000305], - [-0.011897, -0.151422, -0.058405, 0.955978], - [-0.716533, -0.20131, 0.667874, 0.904662], - [-0.031366, 0.229295, 0.058405, 14.0], - [0.10924, 0.178461, -0.058405, 0.0], - [0.107077, -0.140606, -0.058405, 0.376257], - [0.475583, -0.003224, 0.879665, 0.830777], - [-0.045426, 0.564586, 0.052998, 14.0], - [0.1233, -0.156829, -0.052998, 0.520989], - [0.014061, -0.335291, 0.005408, 0.088655], - [0.429355, 0.032562, 0.902549, 0.973449], - [0.183869, 0.317985, 0.037855, 10.0], - [0.005408, -0.037855, -0.037855, 0.0], - [-0.036774, -0.045426, -0.037855, 0.806958], - [0.124328, -0.692684, 0.710445, 0.854994], - [0.10924, 0.439122, 0.0, 10.0], - [0.019468, 0.024876, 0.063813, 0.18117], - [0.035692, -0.131953, 0.049753, 0.955978], - [-0.933936, -0.126584, 0.334276, 0.964691], + [0.179543, 0.466162, 0.058405, 10.0], + [-0.050834, -0.002163, 0.005408, 0.000305], + [0.021632, 0.171972, 0.023795, 0.022171], + [0.110896, -0.149888, 0.982464, 0.381323], + [-0.012979, 0.832818, 0.0, 14.0], + [0.339617, -0.002163, 0.0, 0.0], + [0.214153, -0.194685, 0.0822, 0.027314], + [0.002492, 0.391296, 0.920261, 0.49102], [0.183869, 0.317985, 0.037855, 10.0], [-0.055161, 0.146014, 0.025958, 0.0], [0.02704, 0.127627, -0.037855, 0.439658], [0.623841, 0.097823, 0.775405, 0.871366], - [0.128708, 0.463999, 0.063813, 14.0], - [0.050834, 0.002163, -0.005408, 0.0], - [0.0822, -0.018387, -0.063813, 0.439658], - [0.07859, -0.92643, 0.368172, 0.871366], - [0.319067, 0.319067, 0.0, 10.0], - [-0.042182, -0.007571, 0.0, 0.0], - [-0.037855, 0.023795, 0.046508, 0.896269], - [0.1483, -0.826245, 0.54344, 0.896269], + [0.210908, 0.445612, -0.043263, 7.0], + [0.0, -0.131953, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [1.0, 0.0, 0.0, 0.577584], + [0.210908, 0.445612, 0.0, 10.0], + [-0.031366, 0.02055, 0.058405, 0.000305], + [0.035692, 0.014061, 0.06165, 0.022171], + [-0.105864, -0.954447, 0.278971, 0.381323], + [0.23903, 0.45102, -0.043263, 11.0], + [-0.028121, -0.005408, 0.0, 0.01033], + [-0.028121, -0.005408, 0.043263, 0.045182], + [0.188847, -0.982006, 0.0, 0.577584], + [0.23903, 0.45102, -0.043263, 7.0], + [-0.028121, -0.005408, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.188847, -0.982006, 0.0, 0.577584], + [0.246601, 0.459672, 0.06165, 11.0], + [-0.007571, -0.008653, -0.06165, 0.000305], + [-0.035692, -0.014061, -0.06165, 0.022171], + [0.187618, -0.975617, 0.113888, 0.381323], + [0.241193, 0.343943, -0.043263, 11.0], + [-0.002163, 0.107077, 0.0, 0.01033], + [-0.002163, 0.107077, 0.043263, 0.045182], + [-0.999796, -0.020198, 0.0, 0.577584], [0.23903, 0.45102, 0.0, 15.0], [0.007571, 0.008653, 0.06165, 0.000305], [0.042182, -0.108158, 0.046508, 0.846876], @@ -522,190 +617,102 @@ pub const TRIS: [[f32; 4]; 644] = [ [-0.070303, 0.101669, 0.024876, 0.0], [0.023795, 0.116811, -0.036774, 0.473533], [0.523424, 0.157027, 0.837478, 0.930114], + [0.340698, 0.474815, -0.043263, 7.0], + [0.001082, -0.121137, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.99996, 0.008928, 0.0, 0.577584], + [0.376391, 0.381799, -0.043263, 11.0], + [-0.001082, 0.107077, 0.0, 0.01033], + [-0.001082, 0.107077, 0.043263, 0.045182], + [-0.999949, -0.0101, 0.0, 0.577584], + [0.376391, 0.381799, -0.043263, 7.0], + [-0.001082, 0.107077, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [-0.999949, -0.0101, 0.0, 0.577584], + [0.375309, 0.488875, 0.0, 14.0], + [0.036774, -0.105995, 0.043263, 0.000305], + [0.001082, -0.107077, 0.0, 0.879622], + [-0.771254, -0.00779, 0.63648, 0.896269], + [0.375309, 0.488875, 0.0, 14.0], + [0.078955, -0.08869, 0.032447, 0.223224], + [0.036774, -0.105995, 0.043263, 0.964691], + [0.071225, 0.39802, 0.914608, 0.973449], + [0.473733, 0.603523, 0.0, 14.0], + [0.0, -0.21199, 0.0, 0.158953], + [-0.019468, -0.203337, 0.032447, 0.030716], + [0.857493, 0.0, 0.514496, 0.964691], + [0.473733, 0.603523, -0.043263, 11.0], + [0.0, -0.21199, 0.0, 0.01033], + [0.0, -0.21199, 0.043263, 0.045182], + [1.0, 0.0, 0.0, 0.577584], + [0.276885, 0.484549, 0.058405, 10.0], + [-0.030284, -0.024876, 0.003245, 0.000305], + [-0.097342, -0.018387, 0.0, 0.022171], + [-0.03153, 0.166923, 0.985466, 0.381323], [0.246601, 0.459672, 0.06165, 14.0], [0.030284, 0.024876, -0.003245, 0.0], [0.094098, 0.015142, -0.06165, 0.473533], [0.518869, -0.545855, 0.657888, 0.930114], - [0.45102, 0.359085, 0.0, 10.0], - [-0.040019, -0.007571, 0.0, 0.000305], - [-0.038937, 0.023795, 0.043263, 0.879622], - [0.151736, -0.802035, 0.577682, 0.9131], - [0.454264, 0.400185, 0.032447, 12.0], - [-0.078955, 0.08869, -0.032447, 0.158953], - [0.019468, 0.203337, -0.032447, 0.030716], - [-0.201684, 0.173144, 0.964025, 0.964691], - [0.375309, 0.488875, 0.0, 10.0], - [0.090853, 0.12979, 0.0, 0.158953], - [0.098424, 0.114648, 0.0, 0.030716], - [0.0, 0.0, 1.0, 0.964691], + [0.375309, 0.488875, -0.043263, 11.0], + [-0.034611, -0.014061, 0.0, 0.01033], + [-0.034611, -0.014061, 0.043263, 0.045182], + [0.376377, -0.926467, 0.0, 0.577584], [0.340698, 0.474815, 0.0, 14.0], [-0.063813, 0.009734, 0.058405, 0.000305], [0.034611, 0.014061, 0.0, 0.022171], [0.3276, -0.806402, 0.492334, 0.381323], - [0.276885, 0.484549, 0.058405, 10.0], - [-0.030284, -0.024876, 0.003245, 0.000305], - [-0.097342, -0.018387, 0.0, 0.022171], - [-0.03153, 0.166923, 0.985466, 0.381323], + [0.375309, 0.488875, -0.043263, 7.0], + [-0.034611, -0.014061, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.376377, -0.926467, 0.0, 0.577584], + [0.454264, 0.400185, 0.032447, 12.0], + [-0.078955, 0.08869, -0.032447, 0.158953], + [0.019468, 0.203337, -0.032447, 0.030716], + [-0.201684, 0.173144, 0.964025, 0.964691], [0.201174, 0.638133, 0.0822, 8.0], [0.075711, -0.153585, -0.023795, 0.000305], [-0.021632, -0.171972, -0.023795, 0.022171], [0.026497, -0.140281, 0.989757, 0.381323], + [0.473733, 0.603523, -0.043263, 7.0], + [0.0, -0.21199, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [1.0, 0.0, 0.0, 0.577584], [0.276885, 0.484549, 0.058405, 14.0], [-0.075711, 0.153585, 0.023795, 0.000305], [0.098424, 0.004326, -0.058405, 0.022171], [0.50316, 0.115345, 0.856461, 0.381323], - [0.246601, 0.459672, 0.06165, 11.0], - [-0.007571, -0.008653, -0.06165, 0.000305], - [-0.035692, -0.014061, -0.06165, 0.022171], - [0.187618, -0.975617, 0.113888, 0.381323], - [0.210908, 0.445612, 0.0, 10.0], - [-0.031366, 0.02055, 0.058405, 0.000305], - [0.035692, 0.014061, 0.06165, 0.022171], - [-0.105864, -0.954447, 0.278971, 0.381323], - [-0.167645, 0.416409, 0.046508, 14.0], - [0.004326, -0.035692, -0.046508, 0.000305], - [-0.056242, -0.008653, -0.046508, 0.955978], - [-0.339782, -0.761112, 0.552501, 0.973449], - [-0.167645, 0.416409, 0.046508, 10.0], - [0.085445, 0.114648, -0.003245, 0.0], - [0.073548, 0.015142, -0.046508, 0.768154], - [0.548356, -0.38771, 0.740936, 0.871366], - [-0.044345, 0.23254, 0.0, 3.0], - [0.0, 0.0, -0.043263, 0.01033], - [-0.001082, 0.332046, -0.043263, 0.045182], - [-0.999995, -0.003257, 0.0, 0.577584], - [-0.045426, 0.564586, -0.043263, 11.0], - [-0.036774, -0.033529, 0.0, 0.01033], - [-0.036774, -0.033529, 0.086527, 0.045182], - [0.673754, -0.738956, 0.0, 0.577584], - [-0.0822, 0.531057, -0.043263, 11.0], - [-0.011897, -0.099506, 0.0, 0.01033], - [-0.011897, -0.099506, 0.043263, 0.045182], - [0.992928, -0.11872, 0.0, 0.577584], - [-0.094098, 0.431551, -0.043263, 11.0], - [-0.069221, -0.050834, 0.0, 0.01033], - [-0.069221, -0.050834, 0.043263, 0.045182], - [0.591909, -0.806004, 0.0, 0.577584], - [-0.163319, 0.380717, -0.043263, 11.0], - [-0.060569, 0.02704, 0.0, 0.01033], - [-0.060569, 0.02704, 0.043263, 0.045182], - [-0.407651, -0.913138, 0.0, 0.577584], - [-0.223888, 0.407756, -0.043263, 11.0], - [0.002163, 0.06814, 0.0, 0.01033], - [0.002163, 0.06814, 0.043263, 0.045182], - [-0.999496, 0.03173, 0.0, 0.577584], - [-0.221724, 0.475896, -0.043263, 9.0], - [0.122219, 0.207664, 0.0, 0.01033], - [0.122219, 0.207664, 0.043263, 0.045182], - [-0.861819, 0.507216, 0.0, 0.577584], - [-0.099506, 0.68356, -0.043263, 11.0], - [0.086527, 0.149258, 0.0, 0.01033], - [0.086527, 0.149258, 0.043263, 0.045182], - [-0.86514, 0.501531, 0.0, 0.577584], - [-0.012979, 0.832818, -0.043263, 11.0], - [0.339617, -0.002163, 0.0, 0.01033], - [0.339617, -0.002163, 0.043263, 0.045182], - [0.006369, 0.99998, 0.0, 0.577584], - [0.326638, 0.830655, -0.043263, 9.0], - [0.139524, -0.21199, 0.0, 0.01033], - [0.139524, -0.21199, 0.043263, 0.045182], - [0.835314, 0.549773, 0.0, 0.577584], + [0.375309, 0.488875, 0.0, 10.0], + [0.090853, 0.12979, 0.0, 0.158953], + [0.098424, 0.114648, 0.0, 0.030716], + [0.0, 0.0, 1.0, 0.964691], [0.466162, 0.618665, -0.043263, 11.0], [0.007571, -0.015142, 0.0, 0.01033], [0.007571, -0.015142, 0.043263, 0.045182], [0.894427, 0.447214, 0.0, 0.577584], - [0.473733, 0.603523, -0.043263, 11.0], - [0.0, -0.21199, 0.0, 0.01033], - [0.0, -0.21199, 0.043263, 0.045182], - [1.0, 0.0, 0.0, 0.577584], - [0.473733, 0.391533, -0.043263, 11.0], - [-0.022713, -0.032447, 0.0, 0.01033], - [-0.022713, -0.032447, 0.043263, 0.045182], - [0.819232, -0.573462, 0.0, 0.577584], - [0.45102, 0.359085, -0.043263, 11.0], - [-0.040019, -0.007571, 0.0, 0.01033], - [-0.040019, -0.007571, 0.043263, 0.045182], - [0.185892, -0.98257, 0.0, 0.577584], - [0.411001, 0.351514, -0.043263, 11.0], - [-0.034611, 0.030284, 0.0, 0.01033], - [-0.034611, 0.030284, 0.043263, 0.045182], - [-0.658505, -0.752577, 0.0, 0.577584], - [0.376391, 0.381799, -0.043263, 11.0], - [-0.001082, 0.107077, 0.0, 0.01033], - [-0.001082, 0.107077, 0.043263, 0.045182], - [-0.999949, -0.0101, 0.0, 0.577584], - [0.375309, 0.488875, -0.043263, 11.0], - [-0.034611, -0.014061, 0.0, 0.01033], - [-0.034611, -0.014061, 0.043263, 0.045182], - [0.376377, -0.926467, 0.0, 0.577584], - [0.340698, 0.474815, -0.043263, 11.0], - [0.001082, -0.121137, 0.0, 0.01033], - [0.001082, -0.121137, 0.043263, 0.045182], - [0.99996, 0.008928, 0.0, 0.577584], - [0.34178, 0.353677, -0.043263, 11.0], - [-0.022713, -0.034611, 0.0, 0.01033], - [-0.022713, -0.034611, 0.043263, 0.045182], - [0.836048, -0.548657, 0.0, 0.577584], - [0.319067, 0.319067, -0.043263, 11.0], - [-0.042182, -0.007571, 0.0, 0.01033], - [-0.042182, -0.007571, 0.043263, 0.045182], - [0.176664, -0.984271, 0.0, 0.577584], - [0.276885, 0.311496, -0.043263, 11.0], - [-0.035692, 0.032447, 0.0, 0.01033], - [-0.035692, 0.032447, 0.043263, 0.045182], - [-0.672673, -0.73994, 0.0, 0.577584], - [0.241193, 0.343943, -0.043263, 11.0], - [-0.002163, 0.107077, 0.0, 0.01033], - [-0.002163, 0.107077, 0.043263, 0.045182], - [-0.999796, -0.020198, 0.0, 0.577584], - [0.23903, 0.45102, -0.043263, 11.0], - [-0.028121, -0.005408, 0.0, 0.01033], - [-0.028121, -0.005408, 0.043263, 0.045182], - [0.188847, -0.982006, 0.0, 0.577584], - [0.210908, 0.445612, -0.043263, 11.0], - [0.0, -0.131953, 0.0, 0.01033], - [0.0, -0.131953, 0.043263, 0.045182], - [1.0, 0.0, 0.0, 0.577584], - [0.210908, 0.313659, -0.043263, 11.0], - [-0.021632, -0.033529, 0.0, 0.01033], - [-0.021632, -0.033529, 0.043263, 0.045182], - [0.840297, -0.542127, 0.0, 0.577584], - [0.189277, 0.28013, -0.043263, 11.0], - [-0.042182, -0.007571, 0.0, 0.01033], - [-0.042182, -0.007571, 0.043263, 0.045182], - [0.176664, -0.984271, 0.0, 0.577584], - [0.147095, 0.272559, -0.043263, 11.0], - [-0.035692, 0.034611, 0.0, 0.01033], - [-0.035692, 0.034611, 0.043263, 0.045182], - [-0.696146, -0.7179, 0.0, 0.577584], - [0.111403, 0.307169, -0.043263, 11.0], - [-0.002163, 0.131953, 0.0, 0.01033], - [-0.002163, 0.131953, 0.043263, 0.045182], - [-0.999866, -0.016391, 0.0, 0.577584], - [0.10924, 0.439122, -0.043263, 11.0], - [-0.031366, -0.031366, 0.0, 0.01033], - [-0.031366, -0.031366, 0.043263, 0.045182], - [0.707107, -0.707107, 0.0, 0.577584], - [0.077874, 0.407756, -0.043263, 11.0], - [-0.002163, -0.319067, 0.0, 0.01033], - [-0.002163, -0.319067, 0.043263, 0.045182], - [0.999977, -0.006779, 0.0, 0.577584], - [0.075711, 0.08869, -0.043263, 11.0], - [-0.031366, -0.045426, 0.0, 0.01033], - [-0.031366, -0.045426, 0.043263, 0.045182], - [0.822897, -0.568191, 0.0, 0.577584], - [0.044345, 0.043263, -0.043263, 11.0], - [-0.043263, -0.005408, 0.0, 0.01033], - [-0.043263, -0.005408, 0.043263, 0.045182], - [0.124035, -0.992278, 0.0, 0.577584], - [0.001082, 0.037855, -0.043263, 11.0], - [-0.044345, 0.040019, 0.0, 0.01033], - [-0.044345, 0.040019, 0.043263, 0.045182], - [-0.669964, -0.742393, 0.0, 0.577584], - [-0.043263, 0.077874, -0.043263, 9.0], - [-0.001082, 0.154666, 0.0, 0.01033], - [-0.001082, 0.154666, 0.043263, 0.045182], - [-0.999976, -0.006993, 0.0, 0.577584], + [0.466162, 0.618665, -0.043263, 3.0], + [0.007571, -0.015142, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.894427, 0.447214, 0.0, 0.577584], + [0.326638, 0.830655, 0.0, 14.0], + [0.139524, -0.21199, 0.0, 0.0], + [0.048671, -0.34178, 0.0, 0.015213], + [0.0, 0.0, 1.0, 0.428687], + [0.326638, 0.830655, 0.0, 14.0], + [0.048671, -0.34178, 0.0, 0.007492], + [-0.125464, -0.192522, 0.0822, 0.054475], + [0.472493, 0.067285, 0.878762, 0.964691], + [0.326638, 0.830655, -0.043263, 9.0], + [0.139524, -0.21199, 0.0, 0.01033], + [0.139524, -0.21199, 0.043263, 0.045182], + [0.835314, 0.549773, 0.0, 0.577584], + [0.326638, 0.830655, -0.043263, 7.0], + [0.139524, -0.21199, 0.043263, 0.01033], + [0.0, 0.0, 0.043263, 0.045182], + [0.835314, 0.549773, 0.0, 0.577584], + [-0.012979, 0.832818, -0.043263, 11.0], + [0.339617, -0.002163, 0.0, 0.01033], + [0.339617, -0.002163, 0.043263, 0.045182], + [0.006369, 0.99998, 0.0, 0.577584], ]; pub const POLYS: [[f32; 2]; 110] = [ @@ -820,3 +827,50 @@ pub const POLYS: [[f32; 2]; 110] = [ [-0.045426, 0.564586], [-0.044345, 0.23254], ]; + +pub const BOXES: [[f32; 4]; 44] = [ + [0.030135, 0.06127, -0.043945, 0.0], + [0.330601, 0.483055, 0.116951, 7.0], + [0.034429, 0.265258, -0.043945, 7.0], + [0.542104, 0.741798, 0.116951, 8.0], + [0.034429, 0.355442, -0.043945, 15.0], + [0.305908, 0.822319, 0.116951, 8.0], + [0.267405, 0.532589, -0.043945, 23.0], + [0.542104, 0.822319, 0.082595, 8.0], + [-0.045345, 0.036855, -0.044263, 31.0], + [0.045345, 0.23354, 0.059405, 8.0], + [-0.224888, 0.072548, -0.044263, 39.0], + [0.059405, 0.565586, 0.059405, 8.0], + [-0.032366, 0.042263, -0.044263, 47.0], + [0.148095, 0.408756, 0.059405, 8.0], + [0.074711, 0.08769, -0.044263, 55.0], + [0.184869, 0.464999, 0.064813, 8.0], + [0.146095, 0.271559, -0.044263, 63.0], + [0.211908, 0.446612, 0.038855, 8.0], + [0.23803, 0.310496, -0.044263, 71.0], + [0.320067, 0.460672, 0.06265, 8.0], + [0.275885, 0.310496, -0.044263, 79.0], + [0.412001, 0.475815, 0.047508, 8.0], + [0.375391, 0.350514, -0.044263, 87.0], + [0.45202, 0.38388, 0.044263, 4.0], + [0.410001, 0.350514, -0.044263, 91.0], + [0.474733, 0.401185, 0.044263, 5.0], + [-0.224888, 0.379717, -0.044263, 96.0], + [-0.0812, 0.532057, 0.047508, 8.0], + [-0.095098, 0.228295, -0.044263, 104.0], + [0.211908, 0.565586, 0.064813, 8.0], + [-0.222724, 0.430551, -0.044263, 112.0], + [-0.011979, 0.833818, 0.053998, 8.0], + [-0.100506, 0.438122, -0.044263, 120.0], + [0.327638, 0.833818, 0.0832, 8.0], + [0.127708, 0.312659, -0.044263, 128.0], + [0.282211, 0.467162, 0.064813, 8.0], + [0.245601, 0.352677, -0.044263, 136.0], + [0.474733, 0.604523, 0.06265, 8.0], + [0.178543, 0.390533, -0.044263, 144.0], + [0.474733, 0.639133, 0.0832, 8.0], + [0.200174, 0.483549, -0.044263, 152.0], + [0.474733, 0.639133, 0.0832, 4.0], + [-0.013979, 0.487875, -0.044263, 156.0], + [0.467162, 0.833818, 0.0832, 5.0], +]; diff --git a/crates/compositor/src/sculpt.rs b/crates/compositor/src/sculpt.rs index 551f43776..5d3cdba2f 100644 --- a/crates/compositor/src/sculpt.rs +++ b/crates/compositor/src/sculpt.rs @@ -1,7 +1,7 @@ //! Curseurs sculptés : la flèche et la main des thèmes d'origine, modelées en volumes dans les //! shaders (mode 15, `sculpt_proto`) au lieu d'extruder leur PNG — voxels (Pixel Candy), pièces //! cerclées du trait de leur dessin (Studio Ink, Pop Coral, Star Sprout). Prism Glow est à part : -//! un MAILLAGE tracé sur son dessin à facettes, un cristal lancé de rayons triangle par triangle +//! un MAILLAGE tracé sur son dessin à facettes, un cristal lancé de rayons boîte par boîte //! dans son serti marine (`prism_mesh`). Ce module en tient ce que la géométrie doit savoir côté //! CPU : l'identifiant que lit le shader, et la boîte du modèle (`SpriteShape`) qui pose le //! hotspot, règle la garde au sol et borne la boîte de dessin. @@ -254,17 +254,20 @@ mod tests { } } - /// Les trois shaders portent le maillage de `prism_mesh.rs` tel quel : triangles, polygones, - /// et les débuts et longueurs de chaque modèle dans ces tables. Le WGSL lit triangles et - /// polygones dans un uniform rempli de `prism_mesh.rs` (`compositor_linux.rs`) : il n'en - /// déclare que la taille. + /// Les trois shaders portent le maillage de `prism_mesh.rs` tel quel : triangles, boîtes, + /// polygones, et les débuts et longueurs de chaque modèle dans ces tables. Le WGSL lit + /// triangles, polygones et boîtes dans un uniform rempli de `prism_mesh.rs` + /// (`compositor_linux.rs`) : il n'en déclare que la taille. #[test] fn the_shaders_carry_the_prism_mesh() { - use crate::prism_mesh::{PrismModel, MODELS, POLYS, TRIS}; + use crate::prism_mesh::{PrismModel, BOXES, MODELS, POLYS, TRIS}; let per_model = |f: fn(&PrismModel) -> usize| MODELS.iter().map(|m| f(m) as f32).collect::>(); - let want: [(&str, Vec); 8] = [ + let want: [(&str, Vec); 11] = [ ("PRISM_TRIS", TRIS.iter().flatten().copied().collect()), ("PRISM_POLY", POLYS.iter().flatten().copied().collect()), + ("PRISM_BOXES", BOXES.iter().flatten().copied().collect()), + ("PRISM_BOX_START", per_model(|m| m.box_start)), + ("PRISM_BOX_COUNT", per_model(|m| m.box_count)), ("PRISM_TRI_START", per_model(|m| m.tri_start)), ("PRISM_TRI_COUNT", per_model(|m| m.tri_count)), ("PRISM_SIL_START", per_model(|m| m.sil_start)), @@ -273,13 +276,17 @@ mod tests { ("PRISM_OUT_COUNT", per_model(|m| m.out_count)), ]; let wgsl = include_str!("vk_shaders/layer.wgsl"); - for field in [format!("tris: array, {}>", TRIS.len()), format!("poly: array, {}>", POLYS.len())] { + for field in [ + format!("tris: array, {}>", TRIS.len()), + format!("poly: array, {}>", POLYS.len()), + format!("boxes: array, {}>", BOXES.len()), + ] { assert!(wgsl.contains(&field), "vk_shaders/layer.wgsl : `{field}` attendu dans PrismMesh"); } for (file, src, tables) in [ ("shaders.hlsl", include_str!("shaders.hlsl"), &want[..]), ("shaders.metal", include_str!("shaders.metal"), &want[..]), - ("vk_shaders/layer.wgsl", wgsl, &want[2..]), + ("vk_shaders/layer.wgsl", wgsl, &want[3..]), ] { for (name, values) in tables { let got = table(src, name); @@ -290,4 +297,28 @@ mod tests { } } } + + /// Chaque triangle du cristal est dans une boîte et une seule, qui le contient : le shader ne + /// teste les triangles d'une boîte que si le rayon la touche, un triangle hors de sa boîte + /// disparaîtrait par endroits. + #[test] + fn each_crystal_triangle_sits_in_its_box() { + use crate::prism_mesh::{BOXES, MODELS, TRIS}; + for (k, m) in MODELS.iter().enumerate() { + let mut next = m.tri_start; + for b in m.box_start..m.box_start + m.box_count { + let (lo, hi) = (BOXES[2 * b], BOXES[2 * b + 1]); + let (first, count) = (lo[3] as usize, hi[3] as usize); + assert_eq!(first, next, "modèle {k}, boîte {b} : triangles non contigus"); + next += count; + for t in TRIS[4 * first..4 * (first + count)].chunks(4) { + for e in [[0.0; 3], [t[1][0], t[1][1], t[1][2]], [t[2][0], t[2][1], t[2][2]]] { + let v = [t[0][0] + e[0], t[0][1] + e[1], t[0][2] + e[2]]; + assert!((0..3).all(|a| v[a] >= lo[a] && v[a] <= hi[a]), "modèle {k}, boîte {b} : sommet {v:?} dehors"); + } + } + } + assert_eq!(next, m.tri_start + m.tri_count, "modèle {k} : triangles hors des boîtes"); + } + } } diff --git a/crates/compositor/src/shaders.hlsl b/crates/compositor/src/shaders.hlsl index 3d149ab64..96c9a4971 100644 --- a/crates/compositor/src/shaders.hlsl +++ b/crates/compositor/src/shaders.hlsl @@ -1053,11 +1053,12 @@ float s_pixel_outline(float2 p, int shape) // le repère du MODÈLE lui-même (unité = côté du sprite 128 px, origine au hotspot, y vers le bas, // dessus du serti en z = 0). Le serti marine est la silhouette de l'art extrudée sous z = 0, un // champ de distance comme les autres sculptés : il donne la couverture, l'ombre portée et l'ombre -// de contact. Le cristal est lancé de rayons triangle par triangle : réfraction par canal +// de contact. Le cristal est lancé de rayons, triangles rangés par boîtes : réfraction par canal // (dispersion), réflexions totales internes, sortie par son fond plat vers l'enregistrement, lu // sous le curseur (t0/t1, `trail_b`). Tables : miroir de `prism_mesh.rs` ; un triangle = quatre // float4 (v0, drapeaux), (e1, r), (e2, g), (normale sortante, b) ; drapeaux = paroi (1) + 2 × -// arêtes réelles (bit k : l'arête opposée au sommet k). +// arêtes réelles (bit k : l'arête opposée au sommet k) ; une boîte = deux float4 (coin bas, +// premier triangle), (coin haut, nombre de triangles). // prism mesh: generated by design/cursors/prism-glow/model/export_compositor.py static const int PRISM_TRI_START[2] = { 0, 31 }; static const int PRISM_TRI_COUNT[2] = { 31, 130 }; @@ -1065,19 +1066,41 @@ static const int PRISM_SIL_START[2] = { 0, 25 }; static const int PRISM_SIL_COUNT[2] = { 25, 42 }; static const int PRISM_OUT_START[2] = { 67, 76 }; static const int PRISM_OUT_COUNT[2] = { 9, 34 }; +static const int PRISM_BOX_START[2] = { 0, 4 }; +static const int PRISM_BOX_COUNT[2] = { 4, 18 }; static const float4 PRISM_TRIS[644] = { - float4(0.035429, 0.482055, 0.0, 14.0), - float4(0.134202, -0.125614, 0.115951, 0.000305), - float4(-0.004294, -0.419785, 0.0, 0.938689), - float4(-0.650191, 0.006652, 0.759742, 0.854994), + float4(0.252301, 0.266258, -0.042945, 11.0), + float4(-0.221166, -0.203988, 0.0, 0.01033), + float4(-0.221166, -0.203988, 0.042945, 0.045182), + float4(0.677984, -0.735077, 0.0, 0.577584), + float4(0.252301, 0.266258, -0.042945, 3.0), + float4(-0.221166, -0.203988, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.677984, -0.735077, 0.0, 0.577584), + float4(0.031135, 0.06227, -0.042945, 7.0), + float4(0.004294, 0.419785, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-0.999948, 0.01023, 0.0, 0.577584), float4(0.169632, 0.356442, 0.115951, 14.0), float4(0.082669, -0.090184, -0.115951, 0.590616), float4(-0.138497, -0.294172, -0.115951, 0.737911), float4(0.466385, -0.505659, 0.725806, 0.973449), + float4(0.035429, 0.482055, 0.0, 14.0), + float4(0.134202, -0.125614, 0.115951, 0.000305), + float4(-0.004294, -0.419785, 0.0, 0.938689), + float4(-0.650191, 0.006652, 0.759742, 0.854994), + float4(0.031135, 0.06227, -0.042945, 9.0), + float4(0.004294, 0.419785, 0.0, 0.01033), + float4(0.004294, 0.419785, 0.042945, 0.045182), + float4(-0.999948, 0.01023, 0.0, 0.577584), float4(0.169632, 0.356442, 0.115951, 14.0), float4(0.159969, 0.080521, -0.045092, 0.381323), float4(0.082669, -0.090184, -0.115951, 0.056123), float4(0.461407, -0.510214, 0.725799, 0.973449), + float4(0.541104, 0.544325, -0.042945, 9.0), + float4(-0.288804, -0.278068, 0.0, 0.01033), + float4(-0.288804, -0.278068, 0.042945, 0.045182), + float4(0.69359, -0.72037, 0.0, 0.577584), float4(0.329601, 0.436963, 0.070859, 14.0), float4(0.211503, 0.107362, -0.070859, 0.545724), float4(-0.077301, -0.170706, -0.070859, 0.064805), @@ -1086,53 +1109,77 @@ static const float4 PRISM_TRIS[644] = { float4(0.061196, -0.096626, -0.010736, 0.000916), float4(-0.098773, -0.177147, 0.034356, 0.046662), float4(0.247834, 0.049458, 0.967539, 0.904662), + float4(0.541104, 0.544325, -0.042945, 7.0), + float4(-0.288804, -0.278068, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.69359, -0.72037, 0.0, 0.577584), + float4(0.199693, 0.595859, 0.0, 14.0), + float4(0.068712, -0.06227, 0.081595, 0.0), + float4(-0.030061, -0.239417, 0.115951, 0.01445), + float4(-0.504461, 0.426837, 0.750553, 0.254154), float4(0.268405, 0.533589, 0.081595, 14.0), float4(0.272699, 0.010736, -0.081595, 0.0), float4(0.061196, -0.096626, -0.010736, 0.025193), float4(0.283244, 0.073137, 0.956255, 0.527108), + float4(0.035429, 0.482055, 0.0, 14.0), + float4(0.0, 0.258742, 0.0, 0.0), + float4(0.134202, -0.125614, 0.115951, 0.473533), + float4(-0.653777, 0.0, 0.756687, 0.964691), float4(0.359663, 0.559356, 0.0, 14.0), float4(0.181442, -0.015031, 0.0, 0.006043), float4(-0.091258, -0.025767, 0.081595, 0.076188), float4(0.076465, 0.923048, 0.37701, 0.964691), - float4(0.199693, 0.595859, 0.0, 14.0), - float4(0.068712, -0.06227, 0.081595, 0.0), - float4(-0.030061, -0.239417, 0.115951, 0.01445), - float4(-0.504461, 0.426837, 0.750553, 0.254154), + float4(0.035429, 0.482055, -0.042945, 3.0), + float4(0.0, 0.258742, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-1.0, 0.0, 0.0, 0.577584), + float4(0.035429, 0.482055, -0.042945, 11.0), + float4(0.0, 0.258742, 0.0, 0.01033), + float4(0.0, 0.258742, 0.042945, 0.045182), + float4(-1.0, 0.0, 0.0, 0.577584), + float4(0.035429, 0.740798, -0.042945, 7.0), + float4(0.164264, -0.144939, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.661622, 0.749838, 0.0, 0.577584), float4(0.035429, 0.740798, 0.0, 14.0), float4(0.164264, -0.144939, 0.0, 0.0), float4(0.134202, -0.384356, 0.115951, 0.010956), float4(0.332572, 0.376915, 0.864483, 0.215854), - float4(0.035429, 0.482055, 0.0, 14.0), - float4(0.0, 0.258742, 0.0, 0.0), - float4(0.134202, -0.125614, 0.115951, 0.473533), - float4(-0.653777, 0.0, 0.756687, 0.964691), + float4(0.035429, 0.740798, -0.042945, 11.0), + float4(0.164264, -0.144939, 0.0, 0.01033), + float4(0.164264, -0.144939, 0.042945, 0.045182), + float4(0.661622, 0.749838, 0.0, 0.577584), + float4(0.199693, 0.595859, -0.042945, 7.0), + float4(0.105215, 0.22546, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-0.906183, 0.422886, 0.0, 0.577584), float4(0.199693, 0.595859, 0.0, 14.0), float4(0.105215, 0.22546, 0.0, 0.0), float4(0.068712, -0.06227, 0.081595, 0.015991), float4(-0.613882, 0.286478, 0.735581, 0.473533), + float4(0.199693, 0.595859, -0.042945, 11.0), + float4(0.105215, 0.22546, 0.0, 0.01033), + float4(0.105215, 0.22546, 0.042945, 0.045182), + float4(-0.906183, 0.422886, 0.0, 0.577584), float4(0.304908, 0.821319, 0.0, 14.0), float4(0.128834, -0.067638, 0.0, 0.0), float4(-0.036503, -0.28773, 0.081595, 0.254154), float4(0.133686, 0.254641, 0.957751, 0.973449), + float4(0.304908, 0.821319, -0.042945, 7.0), + float4(0.128834, -0.067638, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.464834, 0.885398, 0.0, 0.577584), float4(0.433742, 0.753681, 0.0, 14.0), float4(-0.07408, -0.194325, 0.0, 0.0), float4(-0.165337, -0.220092, 0.081595, 0.964691), float4(0.683362, -0.260508, 0.682021, 0.964691), - float4(0.035429, 0.482055, -0.042945, 3.0), - float4(0.0, 0.258742, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(-1.0, 0.0, 0.0, 0.577584), - float4(0.035429, 0.740798, -0.042945, 7.0), - float4(0.164264, -0.144939, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(0.661622, 0.749838, 0.0, 0.577584), - float4(0.199693, 0.595859, -0.042945, 7.0), - float4(0.105215, 0.22546, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(-0.906183, 0.422886, 0.0, 0.577584), - float4(0.304908, 0.821319, -0.042945, 7.0), - float4(0.128834, -0.067638, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.433742, 0.753681, -0.042945, 11.0), + float4(-0.07408, -0.194325, 0.0, 0.01033), + float4(-0.07408, -0.194325, 0.042945, 0.045182), + float4(0.934406, -0.35621, 0.0, 0.577584), + float4(0.304908, 0.821319, -0.042945, 11.0), + float4(0.128834, -0.067638, 0.0, 0.01033), + float4(0.128834, -0.067638, 0.042945, 0.045182), float4(0.464834, 0.885398, 0.0, 0.577584), float4(0.433742, 0.753681, -0.042945, 7.0), float4(-0.07408, -0.194325, 0.042945, 0.01033), @@ -1142,70 +1189,86 @@ static const float4 PRISM_TRIS[644] = { float4(0.181442, -0.015031, 0.042945, 0.01033), float4(0.0, 0.0, 0.042945, 0.045182), float4(0.082557, 0.996586, 0.0, 0.577584), - float4(0.541104, 0.544325, -0.042945, 7.0), - float4(-0.288804, -0.278068, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(0.69359, -0.72037, 0.0, 0.577584), - float4(0.252301, 0.266258, -0.042945, 3.0), - float4(-0.221166, -0.203988, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(0.677984, -0.735077, 0.0, 0.577584), - float4(0.031135, 0.06227, -0.042945, 7.0), - float4(0.004294, 0.419785, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(-0.999948, 0.01023, 0.0, 0.577584), - float4(0.035429, 0.482055, -0.042945, 11.0), - float4(0.0, 0.258742, 0.0, 0.01033), - float4(0.0, 0.258742, 0.042945, 0.045182), - float4(-1.0, 0.0, 0.0, 0.577584), - float4(0.035429, 0.740798, -0.042945, 11.0), - float4(0.164264, -0.144939, 0.0, 0.01033), - float4(0.164264, -0.144939, 0.042945, 0.045182), - float4(0.661622, 0.749838, 0.0, 0.577584), - float4(0.199693, 0.595859, -0.042945, 11.0), - float4(0.105215, 0.22546, 0.0, 0.01033), - float4(0.105215, 0.22546, 0.042945, 0.045182), - float4(-0.906183, 0.422886, 0.0, 0.577584), - float4(0.304908, 0.821319, -0.042945, 11.0), - float4(0.128834, -0.067638, 0.0, 0.01033), - float4(0.128834, -0.067638, 0.042945, 0.045182), - float4(0.464834, 0.885398, 0.0, 0.577584), - float4(0.433742, 0.753681, -0.042945, 11.0), - float4(-0.07408, -0.194325, 0.0, 0.01033), - float4(-0.07408, -0.194325, 0.042945, 0.045182), - float4(0.934406, -0.35621, 0.0, 0.577584), float4(0.359663, 0.559356, -0.042945, 11.0), float4(0.181442, -0.015031, 0.0, 0.01033), float4(0.181442, -0.015031, 0.042945, 0.045182), float4(0.082557, 0.996586, 0.0, 0.577584), - float4(0.541104, 0.544325, -0.042945, 9.0), - float4(-0.288804, -0.278068, 0.0, 0.01033), - float4(-0.288804, -0.278068, 0.042945, 0.045182), - float4(0.69359, -0.72037, 0.0, 0.577584), - float4(0.252301, 0.266258, -0.042945, 11.0), - float4(-0.221166, -0.203988, 0.0, 0.01033), - float4(-0.221166, -0.203988, 0.042945, 0.045182), - float4(0.677984, -0.735077, 0.0, 0.577584), - float4(0.031135, 0.06227, -0.042945, 9.0), - float4(0.004294, 0.419785, 0.0, 0.01033), - float4(0.004294, 0.419785, 0.042945, 0.045182), - float4(-0.999948, 0.01023, 0.0, 0.577584), - float4(-0.031366, 0.229295, 0.058405, 14.0), - float4(0.089771, -0.137361, -0.02055, 0.445197), - float4(0.035692, -0.155748, -0.008653, 0.973449), - float4(0.216369, -0.004654, 0.976301, 0.973449), - float4(0.044345, 0.043263, 0.0, 6.0), - float4(-0.040019, 0.030284, 0.049753, 0.000305), - float4(0.014061, 0.048671, 0.037855, 0.964691), - float4(0.365607, -0.634949, 0.680566, 0.973449), + float4(0.044345, 0.043263, -0.043263, 11.0), + float4(-0.043263, -0.005408, 0.0, 0.01033), + float4(-0.043263, -0.005408, 0.043263, 0.045182), + float4(0.124035, -0.992278, 0.0, 0.577584), + float4(0.044345, 0.043263, -0.043263, 7.0), + float4(-0.043263, -0.005408, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.124035, -0.992278, 0.0, 0.577584), + float4(0.001082, 0.037855, -0.043263, 7.0), + float4(-0.044345, 0.040019, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.669964, -0.742393, 0.0, 0.577584), + float4(0.044345, 0.043263, 0.0, 10.0), + float4(-0.043263, -0.005408, 0.0, 0.000305), + float4(-0.040019, 0.030284, 0.049753, 0.964691), + float4(0.101434, -0.811469, 0.575525, 0.973449), float4(0.004326, 0.073548, 0.049753, 14.0), float4(-0.003245, -0.035692, -0.049753, 0.0), float4(-0.04759, 0.004326, -0.049753, 0.955978), float4(-0.580475, -0.643229, 0.499304, 0.896269), + float4(0.001082, 0.037855, -0.043263, 11.0), + float4(-0.044345, 0.040019, 0.0, 0.01033), + float4(-0.044345, 0.040019, 0.043263, 0.045182), + float4(-0.669964, -0.742393, 0.0, 0.577584), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.035692, -0.155748, -0.008653, 0.000305), + float4(-0.011897, -0.151422, -0.058405, 0.955978), + float4(-0.716533, -0.20131, 0.667874, 0.904662), + float4(-0.043263, 0.077874, -0.043263, 7.0), + float4(-0.001082, 0.154666, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999976, -0.006993, 0.0, 0.577584), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.089771, -0.137361, -0.02055, 0.445197), + float4(0.035692, -0.155748, -0.008653, 0.973449), + float4(0.216369, -0.004654, 0.976301, 0.973449), float4(-0.031366, 0.229295, 0.058405, 14.0), float4(-0.011897, -0.151422, -0.058405, 0.000305), float4(-0.012979, 0.003245, -0.058405, 0.955978), float4(-0.976245, -0.006827, 0.216564, 0.904662), + float4(-0.043263, 0.077874, -0.043263, 9.0), + float4(-0.001082, 0.154666, 0.0, 0.01033), + float4(-0.001082, 0.154666, 0.043263, 0.045182), + float4(-0.999976, -0.006993, 0.0, 0.577584), + float4(-0.045426, 0.564586, 0.052998, 14.0), + float4(0.014061, -0.335291, 0.005408, 0.520989), + float4(0.001082, -0.332046, -0.052998, 0.088655), + float4(-0.975941, -0.037462, 0.214794, 0.973449), + float4(-0.044345, 0.23254, 0.0, 3.0), + float4(0.0, 0.0, -0.043263, 0.01033), + float4(-0.001082, 0.332046, -0.043263, 0.045182), + float4(-0.999995, -0.003257, 0.0, 0.577584), + float4(-0.163319, 0.380717, -0.043263, 7.0), + float4(-0.060569, 0.02704, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.407651, -0.913138, 0.0, 0.577584), + float4(-0.094098, 0.431551, -0.043263, 11.0), + float4(-0.069221, -0.050834, 0.0, 0.01033), + float4(-0.069221, -0.050834, 0.043263, 0.045182), + float4(0.591909, -0.806004, 0.0, 0.577584), + float4(-0.163319, 0.380717, -0.043263, 11.0), + float4(-0.060569, 0.02704, 0.0, 0.01033), + float4(-0.060569, 0.02704, 0.043263, 0.045182), + float4(-0.407651, -0.913138, 0.0, 0.577584), + float4(0.075711, 0.08869, -0.043263, 11.0), + float4(-0.031366, -0.045426, 0.0, 0.01033), + float4(-0.031366, -0.045426, 0.043263, 0.045182), + float4(0.822897, -0.568191, 0.0, 0.577584), + float4(0.044345, 0.043263, 0.0, 6.0), + float4(-0.040019, 0.030284, 0.049753, 0.000305), + float4(0.014061, 0.048671, 0.037855, 0.964691), + float4(0.365607, -0.634949, 0.680566, 0.973449), + float4(0.075711, 0.08869, -0.043263, 7.0), + float4(-0.031366, -0.045426, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.822897, -0.568191, 0.0, 0.577584), float4(0.058405, 0.091934, 0.037855, 14.0), float4(0.017305, -0.003245, -0.037855, 0.0), float4(-0.014061, -0.048671, -0.037855, 0.473533), @@ -1214,310 +1277,338 @@ static const float4 PRISM_TRIS[644] = { float4(0.107077, -0.140606, -0.058405, 0.0), float4(0.089771, -0.137361, -0.02055, 0.376257), float4(0.812052, 0.481346, 0.329966, 0.830777), - float4(-0.045426, 0.564586, 0.052998, 14.0), - float4(0.014061, -0.335291, 0.005408, 0.520989), - float4(0.001082, -0.332046, -0.052998, 0.088655), - float4(-0.975941, -0.037462, 0.214794, 0.973449), - float4(0.10924, 0.439122, 0.0, 14.0), - float4(-0.031366, -0.031366, 0.0, 0.132876), - float4(-0.154666, 0.125464, 0.052998, 0.028428), - float4(0.182761, -0.182761, 0.966021, 0.955978), - float4(0.128708, 0.463999, 0.063813, 14.0), - float4(-0.019468, -0.024876, -0.063813, 0.168276), - float4(-0.174135, 0.100587, -0.010816, 0.038209), - float4(-0.469238, -0.764881, 0.441331, 0.955978), - float4(0.201174, 0.638133, 0.0822, 14.0), - float4(-0.072466, -0.174135, -0.018387, 0.18117), - float4(-0.246601, -0.073548, -0.029203, 0.082277), - float4(-0.098561, -0.063844, 0.993081, 0.973449), - float4(0.128708, 0.463999, 0.063813, 14.0), - float4(0.055161, -0.146014, -0.025958, 0.246204), - float4(0.016224, -0.156829, -0.014061, 0.964691), - float4(0.305397, -0.053644, 0.950713, 0.973449), - float4(0.183869, 0.317985, 0.037855, 6.0), - float4(-0.036774, -0.045426, -0.037855, 0.0), - float4(-0.038937, -0.010816, 0.011897, 0.806958), - float4(0.374433, -0.753477, 0.540438, 0.854994), - float4(0.144932, 0.307169, 0.049753, 14.0), - float4(0.002163, -0.034611, -0.049753, 0.0), - float4(-0.033529, 0.0, -0.049753, 0.9131), - float4(-0.630237, -0.649931, 0.424725, 0.904662), - float4(0.10924, 0.439122, 0.0, 6.0), - float4(0.035692, -0.131953, 0.049753, 0.18117), - float4(0.002163, -0.131953, 0.0, 0.955978), - float4(-0.82919, -0.013593, 0.558802, 0.964691), - float4(0.183869, 0.317985, 0.037855, 14.0), - float4(0.02704, -0.004326, -0.037855, 0.0), - float4(0.005408, -0.037855, -0.037855, 0.514916), - float4(0.70062, -0.452013, 0.552101, 0.846876), - float4(0.210908, 0.445612, 0.0, 10.0), - float4(0.0, -0.131953, 0.0, 0.0), + float4(0.077874, 0.407756, -0.043263, 11.0), + float4(-0.002163, -0.319067, 0.0, 0.01033), + float4(-0.002163, -0.319067, 0.043263, 0.045182), + float4(0.999977, -0.006779, 0.0, 0.577584), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.10924, 0.178461, -0.058405, 0.0), + float4(0.107077, -0.140606, -0.058405, 0.376257), + float4(0.475583, -0.003224, 0.879665, 0.830777), + float4(0.147095, 0.272559, -0.043263, 7.0), + float4(-0.035692, 0.034611, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.696146, -0.7179, 0.0, 0.577584), + float4(0.147095, 0.272559, -0.043263, 11.0), + float4(-0.035692, 0.034611, 0.0, 0.01033), + float4(-0.035692, 0.034611, 0.043263, 0.045182), + float4(-0.696146, -0.7179, 0.0, 0.577584), + float4(0.144932, 0.307169, 0.049753, 14.0), + float4(0.002163, -0.034611, -0.049753, 0.0), + float4(-0.033529, 0.0, -0.049753, 0.9131), + float4(-0.630237, -0.649931, 0.424725, 0.904662), + float4(0.183869, 0.317985, 0.037855, 6.0), + float4(-0.036774, -0.045426, -0.037855, 0.0), + float4(-0.038937, -0.010816, 0.011897, 0.806958), + float4(0.374433, -0.753477, 0.540438, 0.854994), + float4(0.077874, 0.407756, -0.043263, 7.0), + float4(-0.002163, -0.319067, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.999977, -0.006779, 0.0, 0.577584), + float4(0.111403, 0.307169, -0.043263, 7.0), + float4(-0.002163, 0.131953, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999866, -0.016391, 0.0, 0.577584), + float4(0.10924, 0.439122, 0.0, 6.0), + float4(0.035692, -0.131953, 0.049753, 0.18117), + float4(0.002163, -0.131953, 0.0, 0.955978), + float4(-0.82919, -0.013593, 0.558802, 0.964691), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(0.055161, -0.146014, -0.025958, 0.246204), + float4(0.016224, -0.156829, -0.014061, 0.964691), + float4(0.305397, -0.053644, 0.950713, 0.973449), + float4(0.111403, 0.307169, -0.043263, 11.0), + float4(-0.002163, 0.131953, 0.0, 0.01033), + float4(-0.002163, 0.131953, 0.043263, 0.045182), + float4(-0.999866, -0.016391, 0.0, 0.577584), + float4(0.189277, 0.28013, -0.043263, 11.0), + float4(-0.042182, -0.007571, 0.0, 0.01033), + float4(-0.042182, -0.007571, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.183869, 0.317985, 0.037855, 10.0), + float4(0.005408, -0.037855, -0.037855, 0.0), + float4(-0.036774, -0.045426, -0.037855, 0.806958), + float4(0.124328, -0.692684, 0.710445, 0.854994), + float4(0.189277, 0.28013, -0.043263, 7.0), + float4(-0.042182, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.183869, 0.317985, 0.037855, 14.0), + float4(0.02704, -0.004326, -0.037855, 0.0), + float4(0.005408, -0.037855, -0.037855, 0.514916), + float4(0.70062, -0.452013, 0.552101, 0.846876), + float4(0.210908, 0.313659, -0.043263, 11.0), + float4(-0.021632, -0.033529, 0.0, 0.01033), + float4(-0.021632, -0.033529, 0.043263, 0.045182), + float4(0.840297, -0.542127, 0.0, 0.577584), + float4(0.210908, 0.445612, 0.0, 10.0), + float4(0.0, -0.131953, 0.0, 0.0), float4(-0.02704, -0.127627, 0.037855, 0.439658), float4(0.813734, 0.0, 0.581238, 0.871366), + float4(0.210908, 0.313659, -0.043263, 7.0), + float4(-0.021632, -0.033529, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.840297, -0.542127, 0.0, 0.577584), + float4(0.210908, 0.445612, -0.043263, 11.0), + float4(0.0, -0.131953, 0.0, 0.01033), + float4(0.0, -0.131953, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.241193, 0.343943, -0.043263, 7.0), + float4(-0.002163, 0.107077, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999796, -0.020198, 0.0, 0.577584), + float4(0.276885, 0.311496, -0.043263, 11.0), + float4(-0.035692, 0.032447, 0.0, 0.01033), + float4(-0.035692, 0.032447, 0.043263, 0.045182), + float4(-0.672673, -0.73994, 0.0, 0.577584), + float4(0.23903, 0.45102, 0.0, 14.0), + float4(0.042182, -0.108158, 0.046508, 0.000305), + float4(0.002163, -0.107077, 0.0, 0.846876), + float4(-0.758098, -0.015315, 0.651961, 0.896269), + float4(0.276885, 0.311496, -0.043263, 7.0), + float4(-0.035692, 0.032447, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.672673, -0.73994, 0.0, 0.577584), + float4(0.281211, 0.342862, 0.046508, 14.0), + float4(-0.004326, -0.031366, -0.046508, 0.0), + float4(-0.040019, 0.001082, -0.046508, 0.752941), + float4(-0.58651, -0.645161, 0.489667, 0.846876), float4(0.246601, 0.459672, 0.06165, 14.0), float4(0.070303, -0.101669, -0.024876, 0.262257), float4(0.034611, -0.116811, -0.015142, 0.973449), float4(0.27928, -0.041605, 0.959308, 0.973449), + float4(0.319067, 0.319067, -0.043263, 11.0), + float4(-0.042182, -0.007571, 0.0, 0.01033), + float4(-0.042182, -0.007571, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.319067, 0.319067, 0.0, 10.0), + float4(-0.042182, -0.007571, 0.0, 0.0), + float4(-0.037855, 0.023795, 0.046508, 0.896269), + float4(0.1483, -0.826245, 0.54344, 0.896269), + float4(0.319067, 0.319067, -0.043263, 7.0), + float4(-0.042182, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), float4(0.319067, 0.319067, 0.0, 6.0), float4(-0.037855, 0.023795, 0.046508, 0.0), float4(-0.002163, 0.038937, 0.036774, 0.896269), float4(0.437906, -0.604311, 0.665618, 0.896269), - float4(0.281211, 0.342862, 0.046508, 14.0), - float4(-0.004326, -0.031366, -0.046508, 0.0), - float4(-0.040019, 0.001082, -0.046508, 0.752941), - float4(-0.58651, -0.645161, 0.489667, 0.846876), - float4(0.23903, 0.45102, 0.0, 14.0), - float4(0.042182, -0.108158, 0.046508, 0.000305), - float4(0.002163, -0.107077, 0.0, 0.846876), - float4(-0.758098, -0.015315, 0.651961, 0.896269), float4(0.316904, 0.358004, 0.036774, 14.0), float4(0.024876, -0.004326, -0.036774, 0.0), float4(0.002163, -0.038937, -0.036774, 0.617212), float4(0.707338, -0.464191, 0.533104, 0.921584), + float4(0.34178, 0.353677, -0.043263, 11.0), + float4(-0.022713, -0.034611, 0.0, 0.01033), + float4(-0.022713, -0.034611, 0.043263, 0.045182), + float4(0.836048, -0.548657, 0.0, 0.577584), float4(0.340698, 0.474815, 0.0, 14.0), float4(0.001082, -0.121137, 0.0, 0.0), float4(-0.023795, -0.116811, 0.036774, 0.473533), float4(0.828664, 0.007399, 0.559697, 0.930114), - float4(0.375309, 0.488875, 0.0, 14.0), - float4(0.078955, -0.08869, 0.032447, 0.223224), - float4(0.036774, -0.105995, 0.043263, 0.964691), - float4(0.071225, 0.39802, 0.914608, 0.973449), - float4(0.45102, 0.359085, 0.0, 6.0), - float4(-0.038937, 0.023795, 0.043263, 0.000305), - float4(0.003245, 0.0411, 0.032447, 0.879622), - float4(0.417852, -0.583049, 0.696744, 0.9131), + float4(0.34178, 0.353677, -0.043263, 7.0), + float4(-0.022713, -0.034611, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.836048, -0.548657, 0.0, 0.577584), + float4(0.340698, 0.474815, -0.043263, 11.0), + float4(0.001082, -0.121137, 0.0, 0.01033), + float4(0.001082, -0.121137, 0.043263, 0.045182), + float4(0.99996, 0.008928, 0.0, 0.577584), + float4(0.411001, 0.351514, -0.043263, 11.0), + float4(-0.034611, 0.030284, 0.0, 0.01033), + float4(-0.034611, 0.030284, 0.043263, 0.045182), + float4(-0.658505, -0.752577, 0.0, 0.577584), + float4(0.411001, 0.351514, -0.043263, 7.0), + float4(-0.034611, 0.030284, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.658505, -0.752577, 0.0, 0.577584), float4(0.412083, 0.38288, 0.043263, 14.0), float4(-0.001082, -0.031366, -0.043263, 0.0), float4(-0.035692, -0.001082, -0.043263, 0.745403), float4(-0.574039, -0.656045, 0.489984, 0.854994), - float4(0.375309, 0.488875, 0.0, 14.0), - float4(0.036774, -0.105995, 0.043263, 0.000305), - float4(0.001082, -0.107077, 0.0, 0.879622), - float4(-0.771254, -0.00779, 0.63648, 0.896269), + float4(0.45102, 0.359085, -0.043263, 11.0), + float4(-0.040019, -0.007571, 0.0, 0.01033), + float4(-0.040019, -0.007571, 0.043263, 0.045182), + float4(0.185892, -0.98257, 0.0, 0.577584), + float4(0.45102, 0.359085, 0.0, 10.0), + float4(-0.040019, -0.007571, 0.0, 0.000305), + float4(-0.038937, 0.023795, 0.043263, 0.879622), + float4(0.151736, -0.802035, 0.577682, 0.9131), + float4(0.45102, 0.359085, -0.043263, 7.0), + float4(-0.040019, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.185892, -0.98257, 0.0, 0.577584), + float4(0.45102, 0.359085, 0.0, 6.0), + float4(-0.038937, 0.023795, 0.043263, 0.000305), + float4(0.003245, 0.0411, 0.032447, 0.879622), + float4(0.417852, -0.583049, 0.696744, 0.9131), + float4(0.473733, 0.391533, -0.043263, 11.0), + float4(-0.022713, -0.032447, 0.0, 0.01033), + float4(-0.022713, -0.032447, 0.043263, 0.045182), + float4(0.819232, -0.573462, 0.0, 0.577584), float4(0.454264, 0.400185, 0.032447, 14.0), float4(0.019468, -0.008653, -0.032447, 0.0), float4(-0.003245, -0.0411, -0.032447, 0.527108), float4(0.688617, -0.482032, 0.541712, 0.887922), - float4(0.473733, 0.603523, 0.0, 14.0), - float4(0.0, -0.21199, 0.0, 0.158953), - float4(-0.019468, -0.203337, 0.032447, 0.030716), - float4(0.857493, 0.0, 0.514496, 0.964691), - float4(0.179543, 0.466162, 0.058405, 10.0), - float4(-0.050834, -0.002163, 0.005408, 0.000305), - float4(0.021632, 0.171972, 0.023795, 0.022171), - float4(0.110896, -0.149888, 0.982464, 0.381323), - float4(0.326638, 0.830655, 0.0, 14.0), - float4(0.048671, -0.34178, 0.0, 0.007492), - float4(-0.125464, -0.192522, 0.0822, 0.054475), - float4(0.472493, 0.067285, 0.878762, 0.964691), - float4(0.326638, 0.830655, 0.0, 14.0), - float4(0.139524, -0.21199, 0.0, 0.0), - float4(0.048671, -0.34178, 0.0, 0.015213), - float4(0.0, 0.0, 1.0, 0.428687), - float4(-0.012979, 0.832818, 0.0, 14.0), - float4(0.339617, -0.002163, 0.0, 0.0), - float4(0.214153, -0.194685, 0.0822, 0.027314), - float4(0.002492, 0.391296, 0.920261, 0.49102), - float4(-0.012979, 0.832818, 0.0, 14.0), - float4(0.214153, -0.194685, 0.0822, 0.000305), - float4(-0.032447, -0.268232, 0.052998, 0.930114), - float4(-0.176863, 0.211325, 0.961281, 0.973449), - float4(-0.221724, 0.475896, 0.0, 14.0), - float4(0.139524, 0.055161, 0.043263, 0.520989), - float4(0.054079, -0.059487, 0.046508, 0.973449), - float4(-0.39307, 0.317373, 0.863001, 0.973449), + float4(0.473733, 0.391533, -0.043263, 7.0), + float4(-0.022713, -0.032447, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.819232, -0.573462, 0.0, 0.577584), + float4(-0.223888, 0.407756, -0.043263, 7.0), + float4(0.002163, 0.06814, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999496, 0.03173, 0.0, 0.577584), + float4(-0.223888, 0.407756, -0.043263, 11.0), + float4(0.002163, 0.06814, 0.0, 0.01033), + float4(0.002163, 0.06814, 0.043263, 0.045182), + float4(-0.999496, 0.03173, 0.0, 0.577584), float4(-0.167645, 0.416409, 0.046508, 14.0), float4(-0.056242, -0.008653, -0.046508, 0.000305), float4(-0.054079, 0.059487, -0.046508, 0.955978), float4(-0.638985, 0.020285, 0.768952, 0.973449), + float4(-0.167645, 0.416409, 0.046508, 14.0), + float4(0.004326, -0.035692, -0.046508, 0.000305), + float4(-0.056242, -0.008653, -0.046508, 0.955978), + float4(-0.339782, -0.761112, 0.552501, 0.973449), + float4(-0.221724, 0.475896, 0.0, 14.0), + float4(0.139524, 0.055161, 0.043263, 0.520989), + float4(0.054079, -0.059487, 0.046508, 0.973449), + float4(-0.39307, 0.317373, 0.863001, 0.973449), float4(-0.167645, 0.416409, 0.046508, 6.0), float4(0.073548, 0.015142, -0.046508, 0.0), float4(0.004326, -0.035692, -0.046508, 0.768154), float4(0.490917, -0.668482, 0.558688, 0.871366), - float4(-0.099506, 0.68356, 0.0, 14.0), - float4(0.017305, -0.152503, 0.043263, 0.0), - float4(-0.122219, -0.207664, 0.0, 0.723049), - float4(-0.365879, 0.215335, 0.905408, 0.854994), - float4(-0.099506, 0.68356, 0.0, 14.0), - float4(0.054079, -0.118974, 0.052998, 0.0), - float4(0.017305, -0.152503, 0.043263, 0.514916), - float4(-0.41958, 0.203352, 0.884647, 0.863157), - float4(-0.099506, 0.68356, 0.0, 14.0), - float4(0.086527, 0.149258, 0.0, 0.0), - float4(0.054079, -0.118974, 0.052998, 0.262257), - float4(-0.385563, 0.223515, 0.8952, 0.9131), - float4(-0.044345, 0.23254, 0.0, 7.0), - float4(-0.001082, 0.332046, -0.043263, 0.01033), - float4(-0.001082, 0.332046, 0.052998, 0.045182), - float4(-0.999995, -0.003257, 0.0, 0.577584), - float4(-0.045426, 0.564586, -0.043263, 7.0), - float4(-0.036774, -0.033529, 0.086527, 0.01033), - float4(0.0, 0.0, 0.096261, 0.045182), - float4(0.673754, -0.738956, 0.0, 0.577584), - float4(-0.0822, 0.531057, -0.043263, 7.0), - float4(-0.011897, -0.099506, 0.043263, 0.01033), - float4(0.0, 0.0, 0.086527, 0.045182), - float4(0.992928, -0.11872, 0.0, 0.577584), float4(-0.094098, 0.431551, -0.043263, 7.0), float4(-0.069221, -0.050834, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), float4(0.591909, -0.806004, 0.0, 0.577584), - float4(-0.163319, 0.380717, -0.043263, 7.0), - float4(-0.060569, 0.02704, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.407651, -0.913138, 0.0, 0.577584), - float4(-0.223888, 0.407756, -0.043263, 7.0), - float4(0.002163, 0.06814, 0.043263, 0.01033), + float4(-0.167645, 0.416409, 0.046508, 10.0), + float4(0.085445, 0.114648, -0.003245, 0.0), + float4(0.073548, 0.015142, -0.046508, 0.768154), + float4(0.548356, -0.38771, 0.740936, 0.871366), + float4(-0.0822, 0.531057, -0.043263, 11.0), + float4(-0.011897, -0.099506, 0.0, 0.01033), + float4(-0.011897, -0.099506, 0.043263, 0.045182), + float4(0.992928, -0.11872, 0.0, 0.577584), + float4(-0.044345, 0.23254, 0.0, 7.0), + float4(-0.001082, 0.332046, -0.043263, 0.01033), + float4(-0.001082, 0.332046, 0.052998, 0.045182), + float4(-0.999995, -0.003257, 0.0, 0.577584), + float4(-0.045426, 0.564586, 0.052998, 14.0), + float4(0.1233, -0.156829, -0.052998, 0.520989), + float4(0.014061, -0.335291, 0.005408, 0.088655), + float4(0.429355, 0.032562, 0.902549, 0.973449), + float4(0.10924, 0.439122, 0.0, 14.0), + float4(-0.031366, -0.031366, 0.0, 0.132876), + float4(-0.154666, 0.125464, 0.052998, 0.028428), + float4(0.182761, -0.182761, 0.966021, 0.955978), + float4(0.10924, 0.439122, -0.043263, 11.0), + float4(-0.031366, -0.031366, 0.0, 0.01033), + float4(-0.031366, -0.031366, 0.043263, 0.045182), + float4(0.707107, -0.707107, 0.0, 0.577584), + float4(0.10924, 0.439122, -0.043263, 7.0), + float4(-0.031366, -0.031366, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999496, 0.03173, 0.0, 0.577584), + float4(0.707107, -0.707107, 0.0, 0.577584), + float4(0.10924, 0.439122, 0.0, 10.0), + float4(0.019468, 0.024876, 0.063813, 0.18117), + float4(0.035692, -0.131953, 0.049753, 0.955978), + float4(-0.933936, -0.126584, 0.334276, 0.964691), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(0.050834, 0.002163, -0.005408, 0.0), + float4(0.0822, -0.018387, -0.063813, 0.439658), + float4(0.07859, -0.92643, 0.368172, 0.871366), float4(-0.221724, 0.475896, -0.043263, 7.0), float4(0.122219, 0.207664, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), float4(-0.861819, 0.507216, 0.0, 0.577584), + float4(-0.221724, 0.475896, -0.043263, 9.0), + float4(0.122219, 0.207664, 0.0, 0.01033), + float4(0.122219, 0.207664, 0.043263, 0.045182), + float4(-0.861819, 0.507216, 0.0, 0.577584), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.017305, -0.152503, 0.043263, 0.0), + float4(-0.122219, -0.207664, 0.0, 0.723049), + float4(-0.365879, 0.215335, 0.905408, 0.854994), + float4(-0.0822, 0.531057, -0.043263, 7.0), + float4(-0.011897, -0.099506, 0.043263, 0.01033), + float4(0.0, 0.0, 0.086527, 0.045182), + float4(0.992928, -0.11872, 0.0, 0.577584), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.054079, -0.118974, 0.052998, 0.0), + float4(0.017305, -0.152503, 0.043263, 0.514916), + float4(-0.41958, 0.203352, 0.884647, 0.863157), float4(-0.099506, 0.68356, -0.043263, 3.0), float4(0.086527, 0.149258, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), float4(-0.86514, 0.501531, 0.0, 0.577584), + float4(-0.045426, 0.564586, -0.043263, 11.0), + float4(-0.036774, -0.033529, 0.0, 0.01033), + float4(-0.036774, -0.033529, 0.086527, 0.045182), + float4(0.673754, -0.738956, 0.0, 0.577584), + float4(-0.045426, 0.564586, -0.043263, 7.0), + float4(-0.036774, -0.033529, 0.086527, 0.01033), + float4(0.0, 0.0, 0.096261, 0.045182), + float4(0.673754, -0.738956, 0.0, 0.577584), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.086527, 0.149258, 0.0, 0.0), + float4(0.054079, -0.118974, 0.052998, 0.262257), + float4(-0.385563, 0.223515, 0.8952, 0.9131), + float4(-0.099506, 0.68356, -0.043263, 11.0), + float4(0.086527, 0.149258, 0.0, 0.01033), + float4(0.086527, 0.149258, 0.043263, 0.045182), + float4(-0.86514, 0.501531, 0.0, 0.577584), + float4(-0.012979, 0.832818, 0.0, 14.0), + float4(0.214153, -0.194685, 0.0822, 0.000305), + float4(-0.032447, -0.268232, 0.052998, 0.930114), + float4(-0.176863, 0.211325, 0.961281, 0.973449), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(-0.019468, -0.024876, -0.063813, 0.168276), + float4(-0.174135, 0.100587, -0.010816, 0.038209), + float4(-0.469238, -0.764881, 0.441331, 0.955978), + float4(0.201174, 0.638133, 0.0822, 14.0), + float4(-0.072466, -0.174135, -0.018387, 0.18117), + float4(-0.246601, -0.073548, -0.029203, 0.082277), + float4(-0.098561, -0.063844, 0.993081, 0.973449), float4(-0.012979, 0.832818, -0.043263, 7.0), float4(0.339617, -0.002163, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), float4(0.006369, 0.99998, 0.0, 0.577584), - float4(0.326638, 0.830655, -0.043263, 7.0), - float4(0.139524, -0.21199, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.835314, 0.549773, 0.0, 0.577584), - float4(0.466162, 0.618665, -0.043263, 3.0), - float4(0.007571, -0.015142, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.894427, 0.447214, 0.0, 0.577584), - float4(0.473733, 0.603523, -0.043263, 7.0), - float4(0.0, -0.21199, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(1.0, 0.0, 0.0, 0.577584), - float4(0.473733, 0.391533, -0.043263, 7.0), - float4(-0.022713, -0.032447, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.819232, -0.573462, 0.0, 0.577584), - float4(0.45102, 0.359085, -0.043263, 7.0), - float4(-0.040019, -0.007571, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.185892, -0.98257, 0.0, 0.577584), - float4(0.411001, 0.351514, -0.043263, 7.0), - float4(-0.034611, 0.030284, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.658505, -0.752577, 0.0, 0.577584), - float4(0.376391, 0.381799, -0.043263, 7.0), - float4(-0.001082, 0.107077, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999949, -0.0101, 0.0, 0.577584), - float4(0.375309, 0.488875, -0.043263, 7.0), - float4(-0.034611, -0.014061, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.376377, -0.926467, 0.0, 0.577584), - float4(0.340698, 0.474815, -0.043263, 7.0), - float4(0.001082, -0.121137, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.99996, 0.008928, 0.0, 0.577584), - float4(0.34178, 0.353677, -0.043263, 7.0), - float4(-0.022713, -0.034611, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.836048, -0.548657, 0.0, 0.577584), - float4(0.319067, 0.319067, -0.043263, 7.0), - float4(-0.042182, -0.007571, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.176664, -0.984271, 0.0, 0.577584), - float4(0.276885, 0.311496, -0.043263, 7.0), - float4(-0.035692, 0.032447, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.672673, -0.73994, 0.0, 0.577584), - float4(0.241193, 0.343943, -0.043263, 7.0), - float4(-0.002163, 0.107077, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999796, -0.020198, 0.0, 0.577584), - float4(0.23903, 0.45102, -0.043263, 7.0), - float4(-0.028121, -0.005408, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.188847, -0.982006, 0.0, 0.577584), - float4(0.210908, 0.445612, -0.043263, 7.0), - float4(0.0, -0.131953, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(1.0, 0.0, 0.0, 0.577584), - float4(0.210908, 0.313659, -0.043263, 7.0), - float4(-0.021632, -0.033529, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.840297, -0.542127, 0.0, 0.577584), - float4(0.189277, 0.28013, -0.043263, 7.0), - float4(-0.042182, -0.007571, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.176664, -0.984271, 0.0, 0.577584), - float4(0.147095, 0.272559, -0.043263, 7.0), - float4(-0.035692, 0.034611, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.696146, -0.7179, 0.0, 0.577584), - float4(0.111403, 0.307169, -0.043263, 7.0), - float4(-0.002163, 0.131953, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999866, -0.016391, 0.0, 0.577584), - float4(0.10924, 0.439122, -0.043263, 7.0), - float4(-0.031366, -0.031366, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.707107, -0.707107, 0.0, 0.577584), - float4(0.077874, 0.407756, -0.043263, 7.0), - float4(-0.002163, -0.319067, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.999977, -0.006779, 0.0, 0.577584), - float4(0.075711, 0.08869, -0.043263, 7.0), - float4(-0.031366, -0.045426, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.822897, -0.568191, 0.0, 0.577584), - float4(0.044345, 0.043263, -0.043263, 7.0), - float4(-0.043263, -0.005408, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.124035, -0.992278, 0.0, 0.577584), - float4(0.001082, 0.037855, -0.043263, 7.0), - float4(-0.044345, 0.040019, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.669964, -0.742393, 0.0, 0.577584), - float4(-0.043263, 0.077874, -0.043263, 7.0), - float4(-0.001082, 0.154666, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999976, -0.006993, 0.0, 0.577584), - float4(0.044345, 0.043263, 0.0, 10.0), - float4(-0.043263, -0.005408, 0.0, 0.000305), - float4(-0.040019, 0.030284, 0.049753, 0.964691), - float4(0.101434, -0.811469, 0.575525, 0.973449), - float4(-0.031366, 0.229295, 0.058405, 14.0), - float4(0.035692, -0.155748, -0.008653, 0.000305), - float4(-0.011897, -0.151422, -0.058405, 0.955978), - float4(-0.716533, -0.20131, 0.667874, 0.904662), - float4(-0.031366, 0.229295, 0.058405, 14.0), - float4(0.10924, 0.178461, -0.058405, 0.0), - float4(0.107077, -0.140606, -0.058405, 0.376257), - float4(0.475583, -0.003224, 0.879665, 0.830777), - float4(-0.045426, 0.564586, 0.052998, 14.0), - float4(0.1233, -0.156829, -0.052998, 0.520989), - float4(0.014061, -0.335291, 0.005408, 0.088655), - float4(0.429355, 0.032562, 0.902549, 0.973449), - float4(0.183869, 0.317985, 0.037855, 10.0), - float4(0.005408, -0.037855, -0.037855, 0.0), - float4(-0.036774, -0.045426, -0.037855, 0.806958), - float4(0.124328, -0.692684, 0.710445, 0.854994), - float4(0.10924, 0.439122, 0.0, 10.0), - float4(0.019468, 0.024876, 0.063813, 0.18117), - float4(0.035692, -0.131953, 0.049753, 0.955978), - float4(-0.933936, -0.126584, 0.334276, 0.964691), + float4(0.179543, 0.466162, 0.058405, 10.0), + float4(-0.050834, -0.002163, 0.005408, 0.000305), + float4(0.021632, 0.171972, 0.023795, 0.022171), + float4(0.110896, -0.149888, 0.982464, 0.381323), + float4(-0.012979, 0.832818, 0.0, 14.0), + float4(0.339617, -0.002163, 0.0, 0.0), + float4(0.214153, -0.194685, 0.0822, 0.027314), + float4(0.002492, 0.391296, 0.920261, 0.49102), float4(0.183869, 0.317985, 0.037855, 10.0), float4(-0.055161, 0.146014, 0.025958, 0.0), float4(0.02704, 0.127627, -0.037855, 0.439658), float4(0.623841, 0.097823, 0.775405, 0.871366), - float4(0.128708, 0.463999, 0.063813, 14.0), - float4(0.050834, 0.002163, -0.005408, 0.0), - float4(0.0822, -0.018387, -0.063813, 0.439658), - float4(0.07859, -0.92643, 0.368172, 0.871366), - float4(0.319067, 0.319067, 0.0, 10.0), - float4(-0.042182, -0.007571, 0.0, 0.0), - float4(-0.037855, 0.023795, 0.046508, 0.896269), - float4(0.1483, -0.826245, 0.54344, 0.896269), + float4(0.210908, 0.445612, -0.043263, 7.0), + float4(0.0, -0.131953, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.210908, 0.445612, 0.0, 10.0), + float4(-0.031366, 0.02055, 0.058405, 0.000305), + float4(0.035692, 0.014061, 0.06165, 0.022171), + float4(-0.105864, -0.954447, 0.278971, 0.381323), + float4(0.23903, 0.45102, -0.043263, 11.0), + float4(-0.028121, -0.005408, 0.0, 0.01033), + float4(-0.028121, -0.005408, 0.043263, 0.045182), + float4(0.188847, -0.982006, 0.0, 0.577584), + float4(0.23903, 0.45102, -0.043263, 7.0), + float4(-0.028121, -0.005408, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.188847, -0.982006, 0.0, 0.577584), + float4(0.246601, 0.459672, 0.06165, 11.0), + float4(-0.007571, -0.008653, -0.06165, 0.000305), + float4(-0.035692, -0.014061, -0.06165, 0.022171), + float4(0.187618, -0.975617, 0.113888, 0.381323), + float4(0.241193, 0.343943, -0.043263, 11.0), + float4(-0.002163, 0.107077, 0.0, 0.01033), + float4(-0.002163, 0.107077, 0.043263, 0.045182), + float4(-0.999796, -0.020198, 0.0, 0.577584), float4(0.23903, 0.45102, 0.0, 15.0), float4(0.007571, 0.008653, 0.06165, 0.000305), float4(0.042182, -0.108158, 0.046508, 0.846876), @@ -1526,190 +1617,148 @@ static const float4 PRISM_TRIS[644] = { float4(-0.070303, 0.101669, 0.024876, 0.0), float4(0.023795, 0.116811, -0.036774, 0.473533), float4(0.523424, 0.157027, 0.837478, 0.930114), + float4(0.340698, 0.474815, -0.043263, 7.0), + float4(0.001082, -0.121137, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.99996, 0.008928, 0.0, 0.577584), + float4(0.376391, 0.381799, -0.043263, 11.0), + float4(-0.001082, 0.107077, 0.0, 0.01033), + float4(-0.001082, 0.107077, 0.043263, 0.045182), + float4(-0.999949, -0.0101, 0.0, 0.577584), + float4(0.376391, 0.381799, -0.043263, 7.0), + float4(-0.001082, 0.107077, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999949, -0.0101, 0.0, 0.577584), + float4(0.375309, 0.488875, 0.0, 14.0), + float4(0.036774, -0.105995, 0.043263, 0.000305), + float4(0.001082, -0.107077, 0.0, 0.879622), + float4(-0.771254, -0.00779, 0.63648, 0.896269), + float4(0.375309, 0.488875, 0.0, 14.0), + float4(0.078955, -0.08869, 0.032447, 0.223224), + float4(0.036774, -0.105995, 0.043263, 0.964691), + float4(0.071225, 0.39802, 0.914608, 0.973449), + float4(0.473733, 0.603523, 0.0, 14.0), + float4(0.0, -0.21199, 0.0, 0.158953), + float4(-0.019468, -0.203337, 0.032447, 0.030716), + float4(0.857493, 0.0, 0.514496, 0.964691), + float4(0.473733, 0.603523, -0.043263, 11.0), + float4(0.0, -0.21199, 0.0, 0.01033), + float4(0.0, -0.21199, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.276885, 0.484549, 0.058405, 10.0), + float4(-0.030284, -0.024876, 0.003245, 0.000305), + float4(-0.097342, -0.018387, 0.0, 0.022171), + float4(-0.03153, 0.166923, 0.985466, 0.381323), float4(0.246601, 0.459672, 0.06165, 14.0), float4(0.030284, 0.024876, -0.003245, 0.0), float4(0.094098, 0.015142, -0.06165, 0.473533), float4(0.518869, -0.545855, 0.657888, 0.930114), - float4(0.45102, 0.359085, 0.0, 10.0), - float4(-0.040019, -0.007571, 0.0, 0.000305), - float4(-0.038937, 0.023795, 0.043263, 0.879622), - float4(0.151736, -0.802035, 0.577682, 0.9131), - float4(0.454264, 0.400185, 0.032447, 12.0), - float4(-0.078955, 0.08869, -0.032447, 0.158953), - float4(0.019468, 0.203337, -0.032447, 0.030716), - float4(-0.201684, 0.173144, 0.964025, 0.964691), - float4(0.375309, 0.488875, 0.0, 10.0), - float4(0.090853, 0.12979, 0.0, 0.158953), - float4(0.098424, 0.114648, 0.0, 0.030716), - float4(0.0, 0.0, 1.0, 0.964691), + float4(0.375309, 0.488875, -0.043263, 11.0), + float4(-0.034611, -0.014061, 0.0, 0.01033), + float4(-0.034611, -0.014061, 0.043263, 0.045182), + float4(0.376377, -0.926467, 0.0, 0.577584), float4(0.340698, 0.474815, 0.0, 14.0), float4(-0.063813, 0.009734, 0.058405, 0.000305), float4(0.034611, 0.014061, 0.0, 0.022171), float4(0.3276, -0.806402, 0.492334, 0.381323), - float4(0.276885, 0.484549, 0.058405, 10.0), - float4(-0.030284, -0.024876, 0.003245, 0.000305), - float4(-0.097342, -0.018387, 0.0, 0.022171), - float4(-0.03153, 0.166923, 0.985466, 0.381323), + float4(0.375309, 0.488875, -0.043263, 7.0), + float4(-0.034611, -0.014061, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.376377, -0.926467, 0.0, 0.577584), + float4(0.454264, 0.400185, 0.032447, 12.0), + float4(-0.078955, 0.08869, -0.032447, 0.158953), + float4(0.019468, 0.203337, -0.032447, 0.030716), + float4(-0.201684, 0.173144, 0.964025, 0.964691), float4(0.201174, 0.638133, 0.0822, 8.0), float4(0.075711, -0.153585, -0.023795, 0.000305), float4(-0.021632, -0.171972, -0.023795, 0.022171), float4(0.026497, -0.140281, 0.989757, 0.381323), + float4(0.473733, 0.603523, -0.043263, 7.0), + float4(0.0, -0.21199, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), float4(0.276885, 0.484549, 0.058405, 14.0), float4(-0.075711, 0.153585, 0.023795, 0.000305), float4(0.098424, 0.004326, -0.058405, 0.022171), float4(0.50316, 0.115345, 0.856461, 0.381323), - float4(0.246601, 0.459672, 0.06165, 11.0), - float4(-0.007571, -0.008653, -0.06165, 0.000305), - float4(-0.035692, -0.014061, -0.06165, 0.022171), - float4(0.187618, -0.975617, 0.113888, 0.381323), - float4(0.210908, 0.445612, 0.0, 10.0), - float4(-0.031366, 0.02055, 0.058405, 0.000305), - float4(0.035692, 0.014061, 0.06165, 0.022171), - float4(-0.105864, -0.954447, 0.278971, 0.381323), - float4(-0.167645, 0.416409, 0.046508, 14.0), - float4(0.004326, -0.035692, -0.046508, 0.000305), - float4(-0.056242, -0.008653, -0.046508, 0.955978), - float4(-0.339782, -0.761112, 0.552501, 0.973449), - float4(-0.167645, 0.416409, 0.046508, 10.0), - float4(0.085445, 0.114648, -0.003245, 0.0), - float4(0.073548, 0.015142, -0.046508, 0.768154), - float4(0.548356, -0.38771, 0.740936, 0.871366), - float4(-0.044345, 0.23254, 0.0, 3.0), - float4(0.0, 0.0, -0.043263, 0.01033), - float4(-0.001082, 0.332046, -0.043263, 0.045182), - float4(-0.999995, -0.003257, 0.0, 0.577584), - float4(-0.045426, 0.564586, -0.043263, 11.0), - float4(-0.036774, -0.033529, 0.0, 0.01033), - float4(-0.036774, -0.033529, 0.086527, 0.045182), - float4(0.673754, -0.738956, 0.0, 0.577584), - float4(-0.0822, 0.531057, -0.043263, 11.0), - float4(-0.011897, -0.099506, 0.0, 0.01033), - float4(-0.011897, -0.099506, 0.043263, 0.045182), - float4(0.992928, -0.11872, 0.0, 0.577584), - float4(-0.094098, 0.431551, -0.043263, 11.0), - float4(-0.069221, -0.050834, 0.0, 0.01033), - float4(-0.069221, -0.050834, 0.043263, 0.045182), - float4(0.591909, -0.806004, 0.0, 0.577584), - float4(-0.163319, 0.380717, -0.043263, 11.0), - float4(-0.060569, 0.02704, 0.0, 0.01033), - float4(-0.060569, 0.02704, 0.043263, 0.045182), - float4(-0.407651, -0.913138, 0.0, 0.577584), - float4(-0.223888, 0.407756, -0.043263, 11.0), - float4(0.002163, 0.06814, 0.0, 0.01033), - float4(0.002163, 0.06814, 0.043263, 0.045182), - float4(-0.999496, 0.03173, 0.0, 0.577584), - float4(-0.221724, 0.475896, -0.043263, 9.0), - float4(0.122219, 0.207664, 0.0, 0.01033), - float4(0.122219, 0.207664, 0.043263, 0.045182), - float4(-0.861819, 0.507216, 0.0, 0.577584), - float4(-0.099506, 0.68356, -0.043263, 11.0), - float4(0.086527, 0.149258, 0.0, 0.01033), - float4(0.086527, 0.149258, 0.043263, 0.045182), - float4(-0.86514, 0.501531, 0.0, 0.577584), - float4(-0.012979, 0.832818, -0.043263, 11.0), - float4(0.339617, -0.002163, 0.0, 0.01033), - float4(0.339617, -0.002163, 0.043263, 0.045182), - float4(0.006369, 0.99998, 0.0, 0.577584), - float4(0.326638, 0.830655, -0.043263, 9.0), - float4(0.139524, -0.21199, 0.0, 0.01033), - float4(0.139524, -0.21199, 0.043263, 0.045182), - float4(0.835314, 0.549773, 0.0, 0.577584), + float4(0.375309, 0.488875, 0.0, 10.0), + float4(0.090853, 0.12979, 0.0, 0.158953), + float4(0.098424, 0.114648, 0.0, 0.030716), + float4(0.0, 0.0, 1.0, 0.964691), float4(0.466162, 0.618665, -0.043263, 11.0), float4(0.007571, -0.015142, 0.0, 0.01033), float4(0.007571, -0.015142, 0.043263, 0.045182), float4(0.894427, 0.447214, 0.0, 0.577584), - float4(0.473733, 0.603523, -0.043263, 11.0), - float4(0.0, -0.21199, 0.0, 0.01033), - float4(0.0, -0.21199, 0.043263, 0.045182), - float4(1.0, 0.0, 0.0, 0.577584), - float4(0.473733, 0.391533, -0.043263, 11.0), - float4(-0.022713, -0.032447, 0.0, 0.01033), - float4(-0.022713, -0.032447, 0.043263, 0.045182), - float4(0.819232, -0.573462, 0.0, 0.577584), - float4(0.45102, 0.359085, -0.043263, 11.0), - float4(-0.040019, -0.007571, 0.0, 0.01033), - float4(-0.040019, -0.007571, 0.043263, 0.045182), - float4(0.185892, -0.98257, 0.0, 0.577584), - float4(0.411001, 0.351514, -0.043263, 11.0), - float4(-0.034611, 0.030284, 0.0, 0.01033), - float4(-0.034611, 0.030284, 0.043263, 0.045182), - float4(-0.658505, -0.752577, 0.0, 0.577584), - float4(0.376391, 0.381799, -0.043263, 11.0), - float4(-0.001082, 0.107077, 0.0, 0.01033), - float4(-0.001082, 0.107077, 0.043263, 0.045182), - float4(-0.999949, -0.0101, 0.0, 0.577584), - float4(0.375309, 0.488875, -0.043263, 11.0), - float4(-0.034611, -0.014061, 0.0, 0.01033), - float4(-0.034611, -0.014061, 0.043263, 0.045182), - float4(0.376377, -0.926467, 0.0, 0.577584), - float4(0.340698, 0.474815, -0.043263, 11.0), - float4(0.001082, -0.121137, 0.0, 0.01033), - float4(0.001082, -0.121137, 0.043263, 0.045182), - float4(0.99996, 0.008928, 0.0, 0.577584), - float4(0.34178, 0.353677, -0.043263, 11.0), - float4(-0.022713, -0.034611, 0.0, 0.01033), - float4(-0.022713, -0.034611, 0.043263, 0.045182), - float4(0.836048, -0.548657, 0.0, 0.577584), - float4(0.319067, 0.319067, -0.043263, 11.0), - float4(-0.042182, -0.007571, 0.0, 0.01033), - float4(-0.042182, -0.007571, 0.043263, 0.045182), - float4(0.176664, -0.984271, 0.0, 0.577584), - float4(0.276885, 0.311496, -0.043263, 11.0), - float4(-0.035692, 0.032447, 0.0, 0.01033), - float4(-0.035692, 0.032447, 0.043263, 0.045182), - float4(-0.672673, -0.73994, 0.0, 0.577584), - float4(0.241193, 0.343943, -0.043263, 11.0), - float4(-0.002163, 0.107077, 0.0, 0.01033), - float4(-0.002163, 0.107077, 0.043263, 0.045182), - float4(-0.999796, -0.020198, 0.0, 0.577584), - float4(0.23903, 0.45102, -0.043263, 11.0), - float4(-0.028121, -0.005408, 0.0, 0.01033), - float4(-0.028121, -0.005408, 0.043263, 0.045182), - float4(0.188847, -0.982006, 0.0, 0.577584), - float4(0.210908, 0.445612, -0.043263, 11.0), - float4(0.0, -0.131953, 0.0, 0.01033), - float4(0.0, -0.131953, 0.043263, 0.045182), - float4(1.0, 0.0, 0.0, 0.577584), - float4(0.210908, 0.313659, -0.043263, 11.0), - float4(-0.021632, -0.033529, 0.0, 0.01033), - float4(-0.021632, -0.033529, 0.043263, 0.045182), - float4(0.840297, -0.542127, 0.0, 0.577584), - float4(0.189277, 0.28013, -0.043263, 11.0), - float4(-0.042182, -0.007571, 0.0, 0.01033), - float4(-0.042182, -0.007571, 0.043263, 0.045182), - float4(0.176664, -0.984271, 0.0, 0.577584), - float4(0.147095, 0.272559, -0.043263, 11.0), - float4(-0.035692, 0.034611, 0.0, 0.01033), - float4(-0.035692, 0.034611, 0.043263, 0.045182), - float4(-0.696146, -0.7179, 0.0, 0.577584), - float4(0.111403, 0.307169, -0.043263, 11.0), - float4(-0.002163, 0.131953, 0.0, 0.01033), - float4(-0.002163, 0.131953, 0.043263, 0.045182), - float4(-0.999866, -0.016391, 0.0, 0.577584), - float4(0.10924, 0.439122, -0.043263, 11.0), - float4(-0.031366, -0.031366, 0.0, 0.01033), - float4(-0.031366, -0.031366, 0.043263, 0.045182), - float4(0.707107, -0.707107, 0.0, 0.577584), - float4(0.077874, 0.407756, -0.043263, 11.0), - float4(-0.002163, -0.319067, 0.0, 0.01033), - float4(-0.002163, -0.319067, 0.043263, 0.045182), - float4(0.999977, -0.006779, 0.0, 0.577584), - float4(0.075711, 0.08869, -0.043263, 11.0), - float4(-0.031366, -0.045426, 0.0, 0.01033), - float4(-0.031366, -0.045426, 0.043263, 0.045182), - float4(0.822897, -0.568191, 0.0, 0.577584), - float4(0.044345, 0.043263, -0.043263, 11.0), - float4(-0.043263, -0.005408, 0.0, 0.01033), - float4(-0.043263, -0.005408, 0.043263, 0.045182), - float4(0.124035, -0.992278, 0.0, 0.577584), - float4(0.001082, 0.037855, -0.043263, 11.0), - float4(-0.044345, 0.040019, 0.0, 0.01033), - float4(-0.044345, 0.040019, 0.043263, 0.045182), - float4(-0.669964, -0.742393, 0.0, 0.577584), - float4(-0.043263, 0.077874, -0.043263, 9.0), - float4(-0.001082, 0.154666, 0.0, 0.01033), - float4(-0.001082, 0.154666, 0.043263, 0.045182), - float4(-0.999976, -0.006993, 0.0, 0.577584) + float4(0.466162, 0.618665, -0.043263, 3.0), + float4(0.007571, -0.015142, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.894427, 0.447214, 0.0, 0.577584), + float4(0.326638, 0.830655, 0.0, 14.0), + float4(0.139524, -0.21199, 0.0, 0.0), + float4(0.048671, -0.34178, 0.0, 0.015213), + float4(0.0, 0.0, 1.0, 0.428687), + float4(0.326638, 0.830655, 0.0, 14.0), + float4(0.048671, -0.34178, 0.0, 0.007492), + float4(-0.125464, -0.192522, 0.0822, 0.054475), + float4(0.472493, 0.067285, 0.878762, 0.964691), + float4(0.326638, 0.830655, -0.043263, 9.0), + float4(0.139524, -0.21199, 0.0, 0.01033), + float4(0.139524, -0.21199, 0.043263, 0.045182), + float4(0.835314, 0.549773, 0.0, 0.577584), + float4(0.326638, 0.830655, -0.043263, 7.0), + float4(0.139524, -0.21199, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.835314, 0.549773, 0.0, 0.577584), + float4(-0.012979, 0.832818, -0.043263, 11.0), + float4(0.339617, -0.002163, 0.0, 0.01033), + float4(0.339617, -0.002163, 0.043263, 0.045182), + float4(0.006369, 0.99998, 0.0, 0.577584) +}; +static const float4 PRISM_BOXES[44] = { + float4(0.030135, 0.06127, -0.043945, 0.0), + float4(0.330601, 0.483055, 0.116951, 7.0), + float4(0.034429, 0.265258, -0.043945, 7.0), + float4(0.542104, 0.741798, 0.116951, 8.0), + float4(0.034429, 0.355442, -0.043945, 15.0), + float4(0.305908, 0.822319, 0.116951, 8.0), + float4(0.267405, 0.532589, -0.043945, 23.0), + float4(0.542104, 0.822319, 0.082595, 8.0), + float4(-0.045345, 0.036855, -0.044263, 31.0), + float4(0.045345, 0.23354, 0.059405, 8.0), + float4(-0.224888, 0.072548, -0.044263, 39.0), + float4(0.059405, 0.565586, 0.059405, 8.0), + float4(-0.032366, 0.042263, -0.044263, 47.0), + float4(0.148095, 0.408756, 0.059405, 8.0), + float4(0.074711, 0.08769, -0.044263, 55.0), + float4(0.184869, 0.464999, 0.064813, 8.0), + float4(0.146095, 0.271559, -0.044263, 63.0), + float4(0.211908, 0.446612, 0.038855, 8.0), + float4(0.23803, 0.310496, -0.044263, 71.0), + float4(0.320067, 0.460672, 0.06265, 8.0), + float4(0.275885, 0.310496, -0.044263, 79.0), + float4(0.412001, 0.475815, 0.047508, 8.0), + float4(0.375391, 0.350514, -0.044263, 87.0), + float4(0.45202, 0.38388, 0.044263, 4.0), + float4(0.410001, 0.350514, -0.044263, 91.0), + float4(0.474733, 0.401185, 0.044263, 5.0), + float4(-0.224888, 0.379717, -0.044263, 96.0), + float4(-0.0812, 0.532057, 0.047508, 8.0), + float4(-0.095098, 0.228295, -0.044263, 104.0), + float4(0.211908, 0.565586, 0.064813, 8.0), + float4(-0.222724, 0.430551, -0.044263, 112.0), + float4(-0.011979, 0.833818, 0.053998, 8.0), + float4(-0.100506, 0.438122, -0.044263, 120.0), + float4(0.327638, 0.833818, 0.0832, 8.0), + float4(0.127708, 0.312659, -0.044263, 128.0), + float4(0.282211, 0.467162, 0.064813, 8.0), + float4(0.245601, 0.352677, -0.044263, 136.0), + float4(0.474733, 0.604523, 0.06265, 8.0), + float4(0.178543, 0.390533, -0.044263, 144.0), + float4(0.474733, 0.639133, 0.0832, 8.0), + float4(0.200174, 0.483549, -0.044263, 152.0), + float4(0.474733, 0.639133, 0.0832, 4.0), + float4(-0.013979, 0.487875, -0.044263, 156.0), + float4(0.467162, 0.833818, 0.0832, 5.0) }; static const float2 PRISM_POLY[110] = { float2(0.006442, -0.002147), @@ -1903,20 +1952,40 @@ float prism_tri(float3 ro, float3 rd, int k) } // Le triangle du cristal `shape` le plus proche devant (ro, rd) : sa distance (1e9 si aucun), son -// indice dans `hit` (-1 si aucun). +// indice dans `hit` (-1 si aucun). Les triangles sont rangés par voisinage, une boîte englobante +// par groupe (PRISM_BOXES) : un rayon qui rate une boîte, ou n'y entre qu'au-delà du meilleur +// point, saute tous ses triangles. L'inverse de la direction sert à toutes les boîtes. float prism_trace(float3 ro, float3 rd, int shape, out int hit) { - int k0 = PRISM_TRI_START[shape]; - int n = PRISM_TRI_COUNT[shape]; + float3 inv = 1.0 / (abs(rd) > 1e-6 ? rd : 1e-6); + int b0 = PRISM_BOX_START[shape]; + int nb = PRISM_BOX_COUNT[shape]; float best = 1e9; hit = -1; - [loop] for (int i = 0; i < n; i++) + [loop] for (int j = b0; j < b0 + nb; j++) { - float t = prism_tri(ro, rd, k0 + i); - if (t > 1e-5 && t < best) + float4 lo = PRISM_BOXES[2 * j]; + float4 hi = PRISM_BOXES[2 * j + 1]; + float3 t0 = (lo.xyz - ro) * inv; + float3 t1 = (hi.xyz - ro) * inv; + float3 tn = min(t0, t1); + float3 tf = max(t0, t1); + float tmin = max(max(tn.x, tn.y), tn.z); + float tmax = min(min(tf.x, tf.y), tf.z); + if (tmax < max(tmin, 1e-5) || tmin > best) + { + continue; + } + int k0 = (int)lo.w; + int n = (int)hi.w; + [loop] for (int i = k0; i < k0 + n; i++) { - best = t; - hit = k0 + i; + float t = prism_tri(ro, rd, i); + if (t > 1e-5 && t < best) + { + best = t; + hit = i; + } } } return best; @@ -2436,7 +2505,7 @@ float4 cursor_model(float2 local) float3 ray = rd; float2 tb = ray_box(ro, ray, lo - 0.02, hi + 0.02); int stage = tb.x < tb.y && tb.y > 0.0 ? STAGE_MARCH : STAGE_DONE; - // Prism Glow : le cristal est tracé triangle par triangle au départ de chaque rayon ; `tc` est + // Prism Glow : le cristal est tracé au départ de chaque rayon ; `tc` est // son point touché (1e9 : raté), `hc` sa facette. La marche du serti s'arrête à lui. bool need_tc = stage == STAGE_MARCH; float tc = 1e9; diff --git a/crates/compositor/src/shaders.metal b/crates/compositor/src/shaders.metal index 1baa306cc..34f0be51d 100644 --- a/crates/compositor/src/shaders.metal +++ b/crates/compositor/src/shaders.metal @@ -990,19 +990,41 @@ constant int PRISM_SIL_START[2] = { 0, 25 }; constant int PRISM_SIL_COUNT[2] = { 25, 42 }; constant int PRISM_OUT_START[2] = { 67, 76 }; constant int PRISM_OUT_COUNT[2] = { 9, 34 }; +constant int PRISM_BOX_START[2] = { 0, 4 }; +constant int PRISM_BOX_COUNT[2] = { 4, 18 }; constant float4 PRISM_TRIS[644] = { - float4(0.035429, 0.482055, 0.0, 14.0), - float4(0.134202, -0.125614, 0.115951, 0.000305), - float4(-0.004294, -0.419785, 0.0, 0.938689), - float4(-0.650191, 0.006652, 0.759742, 0.854994), + float4(0.252301, 0.266258, -0.042945, 11.0), + float4(-0.221166, -0.203988, 0.0, 0.01033), + float4(-0.221166, -0.203988, 0.042945, 0.045182), + float4(0.677984, -0.735077, 0.0, 0.577584), + float4(0.252301, 0.266258, -0.042945, 3.0), + float4(-0.221166, -0.203988, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.677984, -0.735077, 0.0, 0.577584), + float4(0.031135, 0.06227, -0.042945, 7.0), + float4(0.004294, 0.419785, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-0.999948, 0.01023, 0.0, 0.577584), float4(0.169632, 0.356442, 0.115951, 14.0), float4(0.082669, -0.090184, -0.115951, 0.590616), float4(-0.138497, -0.294172, -0.115951, 0.737911), float4(0.466385, -0.505659, 0.725806, 0.973449), + float4(0.035429, 0.482055, 0.0, 14.0), + float4(0.134202, -0.125614, 0.115951, 0.000305), + float4(-0.004294, -0.419785, 0.0, 0.938689), + float4(-0.650191, 0.006652, 0.759742, 0.854994), + float4(0.031135, 0.06227, -0.042945, 9.0), + float4(0.004294, 0.419785, 0.0, 0.01033), + float4(0.004294, 0.419785, 0.042945, 0.045182), + float4(-0.999948, 0.01023, 0.0, 0.577584), float4(0.169632, 0.356442, 0.115951, 14.0), float4(0.159969, 0.080521, -0.045092, 0.381323), float4(0.082669, -0.090184, -0.115951, 0.056123), float4(0.461407, -0.510214, 0.725799, 0.973449), + float4(0.541104, 0.544325, -0.042945, 9.0), + float4(-0.288804, -0.278068, 0.0, 0.01033), + float4(-0.288804, -0.278068, 0.042945, 0.045182), + float4(0.69359, -0.72037, 0.0, 0.577584), float4(0.329601, 0.436963, 0.070859, 14.0), float4(0.211503, 0.107362, -0.070859, 0.545724), float4(-0.077301, -0.170706, -0.070859, 0.064805), @@ -1011,53 +1033,77 @@ constant float4 PRISM_TRIS[644] = { float4(0.061196, -0.096626, -0.010736, 0.000916), float4(-0.098773, -0.177147, 0.034356, 0.046662), float4(0.247834, 0.049458, 0.967539, 0.904662), + float4(0.541104, 0.544325, -0.042945, 7.0), + float4(-0.288804, -0.278068, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.69359, -0.72037, 0.0, 0.577584), + float4(0.199693, 0.595859, 0.0, 14.0), + float4(0.068712, -0.06227, 0.081595, 0.0), + float4(-0.030061, -0.239417, 0.115951, 0.01445), + float4(-0.504461, 0.426837, 0.750553, 0.254154), float4(0.268405, 0.533589, 0.081595, 14.0), float4(0.272699, 0.010736, -0.081595, 0.0), float4(0.061196, -0.096626, -0.010736, 0.025193), float4(0.283244, 0.073137, 0.956255, 0.527108), + float4(0.035429, 0.482055, 0.0, 14.0), + float4(0.0, 0.258742, 0.0, 0.0), + float4(0.134202, -0.125614, 0.115951, 0.473533), + float4(-0.653777, 0.0, 0.756687, 0.964691), float4(0.359663, 0.559356, 0.0, 14.0), float4(0.181442, -0.015031, 0.0, 0.006043), float4(-0.091258, -0.025767, 0.081595, 0.076188), float4(0.076465, 0.923048, 0.37701, 0.964691), - float4(0.199693, 0.595859, 0.0, 14.0), - float4(0.068712, -0.06227, 0.081595, 0.0), - float4(-0.030061, -0.239417, 0.115951, 0.01445), - float4(-0.504461, 0.426837, 0.750553, 0.254154), + float4(0.035429, 0.482055, -0.042945, 3.0), + float4(0.0, 0.258742, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-1.0, 0.0, 0.0, 0.577584), + float4(0.035429, 0.482055, -0.042945, 11.0), + float4(0.0, 0.258742, 0.0, 0.01033), + float4(0.0, 0.258742, 0.042945, 0.045182), + float4(-1.0, 0.0, 0.0, 0.577584), + float4(0.035429, 0.740798, -0.042945, 7.0), + float4(0.164264, -0.144939, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.661622, 0.749838, 0.0, 0.577584), float4(0.035429, 0.740798, 0.0, 14.0), float4(0.164264, -0.144939, 0.0, 0.0), float4(0.134202, -0.384356, 0.115951, 0.010956), float4(0.332572, 0.376915, 0.864483, 0.215854), - float4(0.035429, 0.482055, 0.0, 14.0), - float4(0.0, 0.258742, 0.0, 0.0), - float4(0.134202, -0.125614, 0.115951, 0.473533), - float4(-0.653777, 0.0, 0.756687, 0.964691), + float4(0.035429, 0.740798, -0.042945, 11.0), + float4(0.164264, -0.144939, 0.0, 0.01033), + float4(0.164264, -0.144939, 0.042945, 0.045182), + float4(0.661622, 0.749838, 0.0, 0.577584), + float4(0.199693, 0.595859, -0.042945, 7.0), + float4(0.105215, 0.22546, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(-0.906183, 0.422886, 0.0, 0.577584), float4(0.199693, 0.595859, 0.0, 14.0), float4(0.105215, 0.22546, 0.0, 0.0), float4(0.068712, -0.06227, 0.081595, 0.015991), float4(-0.613882, 0.286478, 0.735581, 0.473533), + float4(0.199693, 0.595859, -0.042945, 11.0), + float4(0.105215, 0.22546, 0.0, 0.01033), + float4(0.105215, 0.22546, 0.042945, 0.045182), + float4(-0.906183, 0.422886, 0.0, 0.577584), float4(0.304908, 0.821319, 0.0, 14.0), float4(0.128834, -0.067638, 0.0, 0.0), float4(-0.036503, -0.28773, 0.081595, 0.254154), float4(0.133686, 0.254641, 0.957751, 0.973449), + float4(0.304908, 0.821319, -0.042945, 7.0), + float4(0.128834, -0.067638, 0.042945, 0.01033), + float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.464834, 0.885398, 0.0, 0.577584), float4(0.433742, 0.753681, 0.0, 14.0), float4(-0.07408, -0.194325, 0.0, 0.0), float4(-0.165337, -0.220092, 0.081595, 0.964691), float4(0.683362, -0.260508, 0.682021, 0.964691), - float4(0.035429, 0.482055, -0.042945, 3.0), - float4(0.0, 0.258742, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(-1.0, 0.0, 0.0, 0.577584), - float4(0.035429, 0.740798, -0.042945, 7.0), - float4(0.164264, -0.144939, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(0.661622, 0.749838, 0.0, 0.577584), - float4(0.199693, 0.595859, -0.042945, 7.0), - float4(0.105215, 0.22546, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(-0.906183, 0.422886, 0.0, 0.577584), - float4(0.304908, 0.821319, -0.042945, 7.0), - float4(0.128834, -0.067638, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), + float4(0.433742, 0.753681, -0.042945, 11.0), + float4(-0.07408, -0.194325, 0.0, 0.01033), + float4(-0.07408, -0.194325, 0.042945, 0.045182), + float4(0.934406, -0.35621, 0.0, 0.577584), + float4(0.304908, 0.821319, -0.042945, 11.0), + float4(0.128834, -0.067638, 0.0, 0.01033), + float4(0.128834, -0.067638, 0.042945, 0.045182), float4(0.464834, 0.885398, 0.0, 0.577584), float4(0.433742, 0.753681, -0.042945, 7.0), float4(-0.07408, -0.194325, 0.042945, 0.01033), @@ -1067,70 +1113,86 @@ constant float4 PRISM_TRIS[644] = { float4(0.181442, -0.015031, 0.042945, 0.01033), float4(0.0, 0.0, 0.042945, 0.045182), float4(0.082557, 0.996586, 0.0, 0.577584), - float4(0.541104, 0.544325, -0.042945, 7.0), - float4(-0.288804, -0.278068, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(0.69359, -0.72037, 0.0, 0.577584), - float4(0.252301, 0.266258, -0.042945, 3.0), - float4(-0.221166, -0.203988, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(0.677984, -0.735077, 0.0, 0.577584), - float4(0.031135, 0.06227, -0.042945, 7.0), - float4(0.004294, 0.419785, 0.042945, 0.01033), - float4(0.0, 0.0, 0.042945, 0.045182), - float4(-0.999948, 0.01023, 0.0, 0.577584), - float4(0.035429, 0.482055, -0.042945, 11.0), - float4(0.0, 0.258742, 0.0, 0.01033), - float4(0.0, 0.258742, 0.042945, 0.045182), - float4(-1.0, 0.0, 0.0, 0.577584), - float4(0.035429, 0.740798, -0.042945, 11.0), - float4(0.164264, -0.144939, 0.0, 0.01033), - float4(0.164264, -0.144939, 0.042945, 0.045182), - float4(0.661622, 0.749838, 0.0, 0.577584), - float4(0.199693, 0.595859, -0.042945, 11.0), - float4(0.105215, 0.22546, 0.0, 0.01033), - float4(0.105215, 0.22546, 0.042945, 0.045182), - float4(-0.906183, 0.422886, 0.0, 0.577584), - float4(0.304908, 0.821319, -0.042945, 11.0), - float4(0.128834, -0.067638, 0.0, 0.01033), - float4(0.128834, -0.067638, 0.042945, 0.045182), - float4(0.464834, 0.885398, 0.0, 0.577584), - float4(0.433742, 0.753681, -0.042945, 11.0), - float4(-0.07408, -0.194325, 0.0, 0.01033), - float4(-0.07408, -0.194325, 0.042945, 0.045182), - float4(0.934406, -0.35621, 0.0, 0.577584), float4(0.359663, 0.559356, -0.042945, 11.0), float4(0.181442, -0.015031, 0.0, 0.01033), float4(0.181442, -0.015031, 0.042945, 0.045182), float4(0.082557, 0.996586, 0.0, 0.577584), - float4(0.541104, 0.544325, -0.042945, 9.0), - float4(-0.288804, -0.278068, 0.0, 0.01033), - float4(-0.288804, -0.278068, 0.042945, 0.045182), - float4(0.69359, -0.72037, 0.0, 0.577584), - float4(0.252301, 0.266258, -0.042945, 11.0), - float4(-0.221166, -0.203988, 0.0, 0.01033), - float4(-0.221166, -0.203988, 0.042945, 0.045182), - float4(0.677984, -0.735077, 0.0, 0.577584), - float4(0.031135, 0.06227, -0.042945, 9.0), - float4(0.004294, 0.419785, 0.0, 0.01033), - float4(0.004294, 0.419785, 0.042945, 0.045182), - float4(-0.999948, 0.01023, 0.0, 0.577584), - float4(-0.031366, 0.229295, 0.058405, 14.0), - float4(0.089771, -0.137361, -0.02055, 0.445197), - float4(0.035692, -0.155748, -0.008653, 0.973449), - float4(0.216369, -0.004654, 0.976301, 0.973449), - float4(0.044345, 0.043263, 0.0, 6.0), - float4(-0.040019, 0.030284, 0.049753, 0.000305), - float4(0.014061, 0.048671, 0.037855, 0.964691), - float4(0.365607, -0.634949, 0.680566, 0.973449), + float4(0.044345, 0.043263, -0.043263, 11.0), + float4(-0.043263, -0.005408, 0.0, 0.01033), + float4(-0.043263, -0.005408, 0.043263, 0.045182), + float4(0.124035, -0.992278, 0.0, 0.577584), + float4(0.044345, 0.043263, -0.043263, 7.0), + float4(-0.043263, -0.005408, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.124035, -0.992278, 0.0, 0.577584), + float4(0.001082, 0.037855, -0.043263, 7.0), + float4(-0.044345, 0.040019, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.669964, -0.742393, 0.0, 0.577584), + float4(0.044345, 0.043263, 0.0, 10.0), + float4(-0.043263, -0.005408, 0.0, 0.000305), + float4(-0.040019, 0.030284, 0.049753, 0.964691), + float4(0.101434, -0.811469, 0.575525, 0.973449), float4(0.004326, 0.073548, 0.049753, 14.0), float4(-0.003245, -0.035692, -0.049753, 0.0), float4(-0.04759, 0.004326, -0.049753, 0.955978), float4(-0.580475, -0.643229, 0.499304, 0.896269), + float4(0.001082, 0.037855, -0.043263, 11.0), + float4(-0.044345, 0.040019, 0.0, 0.01033), + float4(-0.044345, 0.040019, 0.043263, 0.045182), + float4(-0.669964, -0.742393, 0.0, 0.577584), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.035692, -0.155748, -0.008653, 0.000305), + float4(-0.011897, -0.151422, -0.058405, 0.955978), + float4(-0.716533, -0.20131, 0.667874, 0.904662), + float4(-0.043263, 0.077874, -0.043263, 7.0), + float4(-0.001082, 0.154666, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999976, -0.006993, 0.0, 0.577584), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.089771, -0.137361, -0.02055, 0.445197), + float4(0.035692, -0.155748, -0.008653, 0.973449), + float4(0.216369, -0.004654, 0.976301, 0.973449), float4(-0.031366, 0.229295, 0.058405, 14.0), float4(-0.011897, -0.151422, -0.058405, 0.000305), float4(-0.012979, 0.003245, -0.058405, 0.955978), float4(-0.976245, -0.006827, 0.216564, 0.904662), + float4(-0.043263, 0.077874, -0.043263, 9.0), + float4(-0.001082, 0.154666, 0.0, 0.01033), + float4(-0.001082, 0.154666, 0.043263, 0.045182), + float4(-0.999976, -0.006993, 0.0, 0.577584), + float4(-0.045426, 0.564586, 0.052998, 14.0), + float4(0.014061, -0.335291, 0.005408, 0.520989), + float4(0.001082, -0.332046, -0.052998, 0.088655), + float4(-0.975941, -0.037462, 0.214794, 0.973449), + float4(-0.044345, 0.23254, 0.0, 3.0), + float4(0.0, 0.0, -0.043263, 0.01033), + float4(-0.001082, 0.332046, -0.043263, 0.045182), + float4(-0.999995, -0.003257, 0.0, 0.577584), + float4(-0.163319, 0.380717, -0.043263, 7.0), + float4(-0.060569, 0.02704, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.407651, -0.913138, 0.0, 0.577584), + float4(-0.094098, 0.431551, -0.043263, 11.0), + float4(-0.069221, -0.050834, 0.0, 0.01033), + float4(-0.069221, -0.050834, 0.043263, 0.045182), + float4(0.591909, -0.806004, 0.0, 0.577584), + float4(-0.163319, 0.380717, -0.043263, 11.0), + float4(-0.060569, 0.02704, 0.0, 0.01033), + float4(-0.060569, 0.02704, 0.043263, 0.045182), + float4(-0.407651, -0.913138, 0.0, 0.577584), + float4(0.075711, 0.08869, -0.043263, 11.0), + float4(-0.031366, -0.045426, 0.0, 0.01033), + float4(-0.031366, -0.045426, 0.043263, 0.045182), + float4(0.822897, -0.568191, 0.0, 0.577584), + float4(0.044345, 0.043263, 0.0, 6.0), + float4(-0.040019, 0.030284, 0.049753, 0.000305), + float4(0.014061, 0.048671, 0.037855, 0.964691), + float4(0.365607, -0.634949, 0.680566, 0.973449), + float4(0.075711, 0.08869, -0.043263, 7.0), + float4(-0.031366, -0.045426, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.822897, -0.568191, 0.0, 0.577584), float4(0.058405, 0.091934, 0.037855, 14.0), float4(0.017305, -0.003245, -0.037855, 0.0), float4(-0.014061, -0.048671, -0.037855, 0.473533), @@ -1139,310 +1201,338 @@ constant float4 PRISM_TRIS[644] = { float4(0.107077, -0.140606, -0.058405, 0.0), float4(0.089771, -0.137361, -0.02055, 0.376257), float4(0.812052, 0.481346, 0.329966, 0.830777), - float4(-0.045426, 0.564586, 0.052998, 14.0), - float4(0.014061, -0.335291, 0.005408, 0.520989), - float4(0.001082, -0.332046, -0.052998, 0.088655), - float4(-0.975941, -0.037462, 0.214794, 0.973449), - float4(0.10924, 0.439122, 0.0, 14.0), - float4(-0.031366, -0.031366, 0.0, 0.132876), - float4(-0.154666, 0.125464, 0.052998, 0.028428), - float4(0.182761, -0.182761, 0.966021, 0.955978), - float4(0.128708, 0.463999, 0.063813, 14.0), - float4(-0.019468, -0.024876, -0.063813, 0.168276), - float4(-0.174135, 0.100587, -0.010816, 0.038209), - float4(-0.469238, -0.764881, 0.441331, 0.955978), - float4(0.201174, 0.638133, 0.0822, 14.0), - float4(-0.072466, -0.174135, -0.018387, 0.18117), - float4(-0.246601, -0.073548, -0.029203, 0.082277), - float4(-0.098561, -0.063844, 0.993081, 0.973449), - float4(0.128708, 0.463999, 0.063813, 14.0), - float4(0.055161, -0.146014, -0.025958, 0.246204), - float4(0.016224, -0.156829, -0.014061, 0.964691), - float4(0.305397, -0.053644, 0.950713, 0.973449), - float4(0.183869, 0.317985, 0.037855, 6.0), - float4(-0.036774, -0.045426, -0.037855, 0.0), - float4(-0.038937, -0.010816, 0.011897, 0.806958), - float4(0.374433, -0.753477, 0.540438, 0.854994), - float4(0.144932, 0.307169, 0.049753, 14.0), - float4(0.002163, -0.034611, -0.049753, 0.0), - float4(-0.033529, 0.0, -0.049753, 0.9131), - float4(-0.630237, -0.649931, 0.424725, 0.904662), - float4(0.10924, 0.439122, 0.0, 6.0), - float4(0.035692, -0.131953, 0.049753, 0.18117), - float4(0.002163, -0.131953, 0.0, 0.955978), - float4(-0.82919, -0.013593, 0.558802, 0.964691), - float4(0.183869, 0.317985, 0.037855, 14.0), - float4(0.02704, -0.004326, -0.037855, 0.0), - float4(0.005408, -0.037855, -0.037855, 0.514916), - float4(0.70062, -0.452013, 0.552101, 0.846876), - float4(0.210908, 0.445612, 0.0, 10.0), - float4(0.0, -0.131953, 0.0, 0.0), + float4(0.077874, 0.407756, -0.043263, 11.0), + float4(-0.002163, -0.319067, 0.0, 0.01033), + float4(-0.002163, -0.319067, 0.043263, 0.045182), + float4(0.999977, -0.006779, 0.0, 0.577584), + float4(-0.031366, 0.229295, 0.058405, 14.0), + float4(0.10924, 0.178461, -0.058405, 0.0), + float4(0.107077, -0.140606, -0.058405, 0.376257), + float4(0.475583, -0.003224, 0.879665, 0.830777), + float4(0.147095, 0.272559, -0.043263, 7.0), + float4(-0.035692, 0.034611, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.696146, -0.7179, 0.0, 0.577584), + float4(0.147095, 0.272559, -0.043263, 11.0), + float4(-0.035692, 0.034611, 0.0, 0.01033), + float4(-0.035692, 0.034611, 0.043263, 0.045182), + float4(-0.696146, -0.7179, 0.0, 0.577584), + float4(0.144932, 0.307169, 0.049753, 14.0), + float4(0.002163, -0.034611, -0.049753, 0.0), + float4(-0.033529, 0.0, -0.049753, 0.9131), + float4(-0.630237, -0.649931, 0.424725, 0.904662), + float4(0.183869, 0.317985, 0.037855, 6.0), + float4(-0.036774, -0.045426, -0.037855, 0.0), + float4(-0.038937, -0.010816, 0.011897, 0.806958), + float4(0.374433, -0.753477, 0.540438, 0.854994), + float4(0.077874, 0.407756, -0.043263, 7.0), + float4(-0.002163, -0.319067, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.999977, -0.006779, 0.0, 0.577584), + float4(0.111403, 0.307169, -0.043263, 7.0), + float4(-0.002163, 0.131953, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999866, -0.016391, 0.0, 0.577584), + float4(0.10924, 0.439122, 0.0, 6.0), + float4(0.035692, -0.131953, 0.049753, 0.18117), + float4(0.002163, -0.131953, 0.0, 0.955978), + float4(-0.82919, -0.013593, 0.558802, 0.964691), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(0.055161, -0.146014, -0.025958, 0.246204), + float4(0.016224, -0.156829, -0.014061, 0.964691), + float4(0.305397, -0.053644, 0.950713, 0.973449), + float4(0.111403, 0.307169, -0.043263, 11.0), + float4(-0.002163, 0.131953, 0.0, 0.01033), + float4(-0.002163, 0.131953, 0.043263, 0.045182), + float4(-0.999866, -0.016391, 0.0, 0.577584), + float4(0.189277, 0.28013, -0.043263, 11.0), + float4(-0.042182, -0.007571, 0.0, 0.01033), + float4(-0.042182, -0.007571, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.183869, 0.317985, 0.037855, 10.0), + float4(0.005408, -0.037855, -0.037855, 0.0), + float4(-0.036774, -0.045426, -0.037855, 0.806958), + float4(0.124328, -0.692684, 0.710445, 0.854994), + float4(0.189277, 0.28013, -0.043263, 7.0), + float4(-0.042182, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.183869, 0.317985, 0.037855, 14.0), + float4(0.02704, -0.004326, -0.037855, 0.0), + float4(0.005408, -0.037855, -0.037855, 0.514916), + float4(0.70062, -0.452013, 0.552101, 0.846876), + float4(0.210908, 0.313659, -0.043263, 11.0), + float4(-0.021632, -0.033529, 0.0, 0.01033), + float4(-0.021632, -0.033529, 0.043263, 0.045182), + float4(0.840297, -0.542127, 0.0, 0.577584), + float4(0.210908, 0.445612, 0.0, 10.0), + float4(0.0, -0.131953, 0.0, 0.0), float4(-0.02704, -0.127627, 0.037855, 0.439658), float4(0.813734, 0.0, 0.581238, 0.871366), + float4(0.210908, 0.313659, -0.043263, 7.0), + float4(-0.021632, -0.033529, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.840297, -0.542127, 0.0, 0.577584), + float4(0.210908, 0.445612, -0.043263, 11.0), + float4(0.0, -0.131953, 0.0, 0.01033), + float4(0.0, -0.131953, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.241193, 0.343943, -0.043263, 7.0), + float4(-0.002163, 0.107077, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999796, -0.020198, 0.0, 0.577584), + float4(0.276885, 0.311496, -0.043263, 11.0), + float4(-0.035692, 0.032447, 0.0, 0.01033), + float4(-0.035692, 0.032447, 0.043263, 0.045182), + float4(-0.672673, -0.73994, 0.0, 0.577584), + float4(0.23903, 0.45102, 0.0, 14.0), + float4(0.042182, -0.108158, 0.046508, 0.000305), + float4(0.002163, -0.107077, 0.0, 0.846876), + float4(-0.758098, -0.015315, 0.651961, 0.896269), + float4(0.276885, 0.311496, -0.043263, 7.0), + float4(-0.035692, 0.032447, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.672673, -0.73994, 0.0, 0.577584), + float4(0.281211, 0.342862, 0.046508, 14.0), + float4(-0.004326, -0.031366, -0.046508, 0.0), + float4(-0.040019, 0.001082, -0.046508, 0.752941), + float4(-0.58651, -0.645161, 0.489667, 0.846876), float4(0.246601, 0.459672, 0.06165, 14.0), float4(0.070303, -0.101669, -0.024876, 0.262257), float4(0.034611, -0.116811, -0.015142, 0.973449), float4(0.27928, -0.041605, 0.959308, 0.973449), + float4(0.319067, 0.319067, -0.043263, 11.0), + float4(-0.042182, -0.007571, 0.0, 0.01033), + float4(-0.042182, -0.007571, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), + float4(0.319067, 0.319067, 0.0, 10.0), + float4(-0.042182, -0.007571, 0.0, 0.0), + float4(-0.037855, 0.023795, 0.046508, 0.896269), + float4(0.1483, -0.826245, 0.54344, 0.896269), + float4(0.319067, 0.319067, -0.043263, 7.0), + float4(-0.042182, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.176664, -0.984271, 0.0, 0.577584), float4(0.319067, 0.319067, 0.0, 6.0), float4(-0.037855, 0.023795, 0.046508, 0.0), float4(-0.002163, 0.038937, 0.036774, 0.896269), float4(0.437906, -0.604311, 0.665618, 0.896269), - float4(0.281211, 0.342862, 0.046508, 14.0), - float4(-0.004326, -0.031366, -0.046508, 0.0), - float4(-0.040019, 0.001082, -0.046508, 0.752941), - float4(-0.58651, -0.645161, 0.489667, 0.846876), - float4(0.23903, 0.45102, 0.0, 14.0), - float4(0.042182, -0.108158, 0.046508, 0.000305), - float4(0.002163, -0.107077, 0.0, 0.846876), - float4(-0.758098, -0.015315, 0.651961, 0.896269), float4(0.316904, 0.358004, 0.036774, 14.0), float4(0.024876, -0.004326, -0.036774, 0.0), float4(0.002163, -0.038937, -0.036774, 0.617212), float4(0.707338, -0.464191, 0.533104, 0.921584), + float4(0.34178, 0.353677, -0.043263, 11.0), + float4(-0.022713, -0.034611, 0.0, 0.01033), + float4(-0.022713, -0.034611, 0.043263, 0.045182), + float4(0.836048, -0.548657, 0.0, 0.577584), float4(0.340698, 0.474815, 0.0, 14.0), float4(0.001082, -0.121137, 0.0, 0.0), float4(-0.023795, -0.116811, 0.036774, 0.473533), float4(0.828664, 0.007399, 0.559697, 0.930114), - float4(0.375309, 0.488875, 0.0, 14.0), - float4(0.078955, -0.08869, 0.032447, 0.223224), - float4(0.036774, -0.105995, 0.043263, 0.964691), - float4(0.071225, 0.39802, 0.914608, 0.973449), - float4(0.45102, 0.359085, 0.0, 6.0), - float4(-0.038937, 0.023795, 0.043263, 0.000305), - float4(0.003245, 0.0411, 0.032447, 0.879622), - float4(0.417852, -0.583049, 0.696744, 0.9131), + float4(0.34178, 0.353677, -0.043263, 7.0), + float4(-0.022713, -0.034611, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.836048, -0.548657, 0.0, 0.577584), + float4(0.340698, 0.474815, -0.043263, 11.0), + float4(0.001082, -0.121137, 0.0, 0.01033), + float4(0.001082, -0.121137, 0.043263, 0.045182), + float4(0.99996, 0.008928, 0.0, 0.577584), + float4(0.411001, 0.351514, -0.043263, 11.0), + float4(-0.034611, 0.030284, 0.0, 0.01033), + float4(-0.034611, 0.030284, 0.043263, 0.045182), + float4(-0.658505, -0.752577, 0.0, 0.577584), + float4(0.411001, 0.351514, -0.043263, 7.0), + float4(-0.034611, 0.030284, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.658505, -0.752577, 0.0, 0.577584), float4(0.412083, 0.38288, 0.043263, 14.0), float4(-0.001082, -0.031366, -0.043263, 0.0), float4(-0.035692, -0.001082, -0.043263, 0.745403), float4(-0.574039, -0.656045, 0.489984, 0.854994), - float4(0.375309, 0.488875, 0.0, 14.0), - float4(0.036774, -0.105995, 0.043263, 0.000305), - float4(0.001082, -0.107077, 0.0, 0.879622), - float4(-0.771254, -0.00779, 0.63648, 0.896269), + float4(0.45102, 0.359085, -0.043263, 11.0), + float4(-0.040019, -0.007571, 0.0, 0.01033), + float4(-0.040019, -0.007571, 0.043263, 0.045182), + float4(0.185892, -0.98257, 0.0, 0.577584), + float4(0.45102, 0.359085, 0.0, 10.0), + float4(-0.040019, -0.007571, 0.0, 0.000305), + float4(-0.038937, 0.023795, 0.043263, 0.879622), + float4(0.151736, -0.802035, 0.577682, 0.9131), + float4(0.45102, 0.359085, -0.043263, 7.0), + float4(-0.040019, -0.007571, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.185892, -0.98257, 0.0, 0.577584), + float4(0.45102, 0.359085, 0.0, 6.0), + float4(-0.038937, 0.023795, 0.043263, 0.000305), + float4(0.003245, 0.0411, 0.032447, 0.879622), + float4(0.417852, -0.583049, 0.696744, 0.9131), + float4(0.473733, 0.391533, -0.043263, 11.0), + float4(-0.022713, -0.032447, 0.0, 0.01033), + float4(-0.022713, -0.032447, 0.043263, 0.045182), + float4(0.819232, -0.573462, 0.0, 0.577584), float4(0.454264, 0.400185, 0.032447, 14.0), float4(0.019468, -0.008653, -0.032447, 0.0), float4(-0.003245, -0.0411, -0.032447, 0.527108), float4(0.688617, -0.482032, 0.541712, 0.887922), - float4(0.473733, 0.603523, 0.0, 14.0), - float4(0.0, -0.21199, 0.0, 0.158953), - float4(-0.019468, -0.203337, 0.032447, 0.030716), - float4(0.857493, 0.0, 0.514496, 0.964691), - float4(0.179543, 0.466162, 0.058405, 10.0), - float4(-0.050834, -0.002163, 0.005408, 0.000305), - float4(0.021632, 0.171972, 0.023795, 0.022171), - float4(0.110896, -0.149888, 0.982464, 0.381323), - float4(0.326638, 0.830655, 0.0, 14.0), - float4(0.048671, -0.34178, 0.0, 0.007492), - float4(-0.125464, -0.192522, 0.0822, 0.054475), - float4(0.472493, 0.067285, 0.878762, 0.964691), - float4(0.326638, 0.830655, 0.0, 14.0), - float4(0.139524, -0.21199, 0.0, 0.0), - float4(0.048671, -0.34178, 0.0, 0.015213), - float4(0.0, 0.0, 1.0, 0.428687), - float4(-0.012979, 0.832818, 0.0, 14.0), - float4(0.339617, -0.002163, 0.0, 0.0), - float4(0.214153, -0.194685, 0.0822, 0.027314), - float4(0.002492, 0.391296, 0.920261, 0.49102), - float4(-0.012979, 0.832818, 0.0, 14.0), - float4(0.214153, -0.194685, 0.0822, 0.000305), - float4(-0.032447, -0.268232, 0.052998, 0.930114), - float4(-0.176863, 0.211325, 0.961281, 0.973449), - float4(-0.221724, 0.475896, 0.0, 14.0), - float4(0.139524, 0.055161, 0.043263, 0.520989), - float4(0.054079, -0.059487, 0.046508, 0.973449), - float4(-0.39307, 0.317373, 0.863001, 0.973449), + float4(0.473733, 0.391533, -0.043263, 7.0), + float4(-0.022713, -0.032447, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.819232, -0.573462, 0.0, 0.577584), + float4(-0.223888, 0.407756, -0.043263, 7.0), + float4(0.002163, 0.06814, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999496, 0.03173, 0.0, 0.577584), + float4(-0.223888, 0.407756, -0.043263, 11.0), + float4(0.002163, 0.06814, 0.0, 0.01033), + float4(0.002163, 0.06814, 0.043263, 0.045182), + float4(-0.999496, 0.03173, 0.0, 0.577584), float4(-0.167645, 0.416409, 0.046508, 14.0), float4(-0.056242, -0.008653, -0.046508, 0.000305), float4(-0.054079, 0.059487, -0.046508, 0.955978), float4(-0.638985, 0.020285, 0.768952, 0.973449), + float4(-0.167645, 0.416409, 0.046508, 14.0), + float4(0.004326, -0.035692, -0.046508, 0.000305), + float4(-0.056242, -0.008653, -0.046508, 0.955978), + float4(-0.339782, -0.761112, 0.552501, 0.973449), + float4(-0.221724, 0.475896, 0.0, 14.0), + float4(0.139524, 0.055161, 0.043263, 0.520989), + float4(0.054079, -0.059487, 0.046508, 0.973449), + float4(-0.39307, 0.317373, 0.863001, 0.973449), float4(-0.167645, 0.416409, 0.046508, 6.0), float4(0.073548, 0.015142, -0.046508, 0.0), float4(0.004326, -0.035692, -0.046508, 0.768154), float4(0.490917, -0.668482, 0.558688, 0.871366), - float4(-0.099506, 0.68356, 0.0, 14.0), - float4(0.017305, -0.152503, 0.043263, 0.0), - float4(-0.122219, -0.207664, 0.0, 0.723049), - float4(-0.365879, 0.215335, 0.905408, 0.854994), - float4(-0.099506, 0.68356, 0.0, 14.0), - float4(0.054079, -0.118974, 0.052998, 0.0), - float4(0.017305, -0.152503, 0.043263, 0.514916), - float4(-0.41958, 0.203352, 0.884647, 0.863157), - float4(-0.099506, 0.68356, 0.0, 14.0), - float4(0.086527, 0.149258, 0.0, 0.0), - float4(0.054079, -0.118974, 0.052998, 0.262257), - float4(-0.385563, 0.223515, 0.8952, 0.9131), - float4(-0.044345, 0.23254, 0.0, 7.0), - float4(-0.001082, 0.332046, -0.043263, 0.01033), - float4(-0.001082, 0.332046, 0.052998, 0.045182), - float4(-0.999995, -0.003257, 0.0, 0.577584), - float4(-0.045426, 0.564586, -0.043263, 7.0), - float4(-0.036774, -0.033529, 0.086527, 0.01033), - float4(0.0, 0.0, 0.096261, 0.045182), - float4(0.673754, -0.738956, 0.0, 0.577584), - float4(-0.0822, 0.531057, -0.043263, 7.0), - float4(-0.011897, -0.099506, 0.043263, 0.01033), - float4(0.0, 0.0, 0.086527, 0.045182), - float4(0.992928, -0.11872, 0.0, 0.577584), float4(-0.094098, 0.431551, -0.043263, 7.0), float4(-0.069221, -0.050834, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), float4(0.591909, -0.806004, 0.0, 0.577584), - float4(-0.163319, 0.380717, -0.043263, 7.0), - float4(-0.060569, 0.02704, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.407651, -0.913138, 0.0, 0.577584), - float4(-0.223888, 0.407756, -0.043263, 7.0), - float4(0.002163, 0.06814, 0.043263, 0.01033), + float4(-0.167645, 0.416409, 0.046508, 10.0), + float4(0.085445, 0.114648, -0.003245, 0.0), + float4(0.073548, 0.015142, -0.046508, 0.768154), + float4(0.548356, -0.38771, 0.740936, 0.871366), + float4(-0.0822, 0.531057, -0.043263, 11.0), + float4(-0.011897, -0.099506, 0.0, 0.01033), + float4(-0.011897, -0.099506, 0.043263, 0.045182), + float4(0.992928, -0.11872, 0.0, 0.577584), + float4(-0.044345, 0.23254, 0.0, 7.0), + float4(-0.001082, 0.332046, -0.043263, 0.01033), + float4(-0.001082, 0.332046, 0.052998, 0.045182), + float4(-0.999995, -0.003257, 0.0, 0.577584), + float4(-0.045426, 0.564586, 0.052998, 14.0), + float4(0.1233, -0.156829, -0.052998, 0.520989), + float4(0.014061, -0.335291, 0.005408, 0.088655), + float4(0.429355, 0.032562, 0.902549, 0.973449), + float4(0.10924, 0.439122, 0.0, 14.0), + float4(-0.031366, -0.031366, 0.0, 0.132876), + float4(-0.154666, 0.125464, 0.052998, 0.028428), + float4(0.182761, -0.182761, 0.966021, 0.955978), + float4(0.10924, 0.439122, -0.043263, 11.0), + float4(-0.031366, -0.031366, 0.0, 0.01033), + float4(-0.031366, -0.031366, 0.043263, 0.045182), + float4(0.707107, -0.707107, 0.0, 0.577584), + float4(0.10924, 0.439122, -0.043263, 7.0), + float4(-0.031366, -0.031366, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999496, 0.03173, 0.0, 0.577584), + float4(0.707107, -0.707107, 0.0, 0.577584), + float4(0.10924, 0.439122, 0.0, 10.0), + float4(0.019468, 0.024876, 0.063813, 0.18117), + float4(0.035692, -0.131953, 0.049753, 0.955978), + float4(-0.933936, -0.126584, 0.334276, 0.964691), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(0.050834, 0.002163, -0.005408, 0.0), + float4(0.0822, -0.018387, -0.063813, 0.439658), + float4(0.07859, -0.92643, 0.368172, 0.871366), float4(-0.221724, 0.475896, -0.043263, 7.0), float4(0.122219, 0.207664, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), float4(-0.861819, 0.507216, 0.0, 0.577584), + float4(-0.221724, 0.475896, -0.043263, 9.0), + float4(0.122219, 0.207664, 0.0, 0.01033), + float4(0.122219, 0.207664, 0.043263, 0.045182), + float4(-0.861819, 0.507216, 0.0, 0.577584), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.017305, -0.152503, 0.043263, 0.0), + float4(-0.122219, -0.207664, 0.0, 0.723049), + float4(-0.365879, 0.215335, 0.905408, 0.854994), + float4(-0.0822, 0.531057, -0.043263, 7.0), + float4(-0.011897, -0.099506, 0.043263, 0.01033), + float4(0.0, 0.0, 0.086527, 0.045182), + float4(0.992928, -0.11872, 0.0, 0.577584), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.054079, -0.118974, 0.052998, 0.0), + float4(0.017305, -0.152503, 0.043263, 0.514916), + float4(-0.41958, 0.203352, 0.884647, 0.863157), float4(-0.099506, 0.68356, -0.043263, 3.0), float4(0.086527, 0.149258, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), float4(-0.86514, 0.501531, 0.0, 0.577584), + float4(-0.045426, 0.564586, -0.043263, 11.0), + float4(-0.036774, -0.033529, 0.0, 0.01033), + float4(-0.036774, -0.033529, 0.086527, 0.045182), + float4(0.673754, -0.738956, 0.0, 0.577584), + float4(-0.045426, 0.564586, -0.043263, 7.0), + float4(-0.036774, -0.033529, 0.086527, 0.01033), + float4(0.0, 0.0, 0.096261, 0.045182), + float4(0.673754, -0.738956, 0.0, 0.577584), + float4(-0.099506, 0.68356, 0.0, 14.0), + float4(0.086527, 0.149258, 0.0, 0.0), + float4(0.054079, -0.118974, 0.052998, 0.262257), + float4(-0.385563, 0.223515, 0.8952, 0.9131), + float4(-0.099506, 0.68356, -0.043263, 11.0), + float4(0.086527, 0.149258, 0.0, 0.01033), + float4(0.086527, 0.149258, 0.043263, 0.045182), + float4(-0.86514, 0.501531, 0.0, 0.577584), + float4(-0.012979, 0.832818, 0.0, 14.0), + float4(0.214153, -0.194685, 0.0822, 0.000305), + float4(-0.032447, -0.268232, 0.052998, 0.930114), + float4(-0.176863, 0.211325, 0.961281, 0.973449), + float4(0.128708, 0.463999, 0.063813, 14.0), + float4(-0.019468, -0.024876, -0.063813, 0.168276), + float4(-0.174135, 0.100587, -0.010816, 0.038209), + float4(-0.469238, -0.764881, 0.441331, 0.955978), + float4(0.201174, 0.638133, 0.0822, 14.0), + float4(-0.072466, -0.174135, -0.018387, 0.18117), + float4(-0.246601, -0.073548, -0.029203, 0.082277), + float4(-0.098561, -0.063844, 0.993081, 0.973449), float4(-0.012979, 0.832818, -0.043263, 7.0), float4(0.339617, -0.002163, 0.043263, 0.01033), float4(0.0, 0.0, 0.043263, 0.045182), float4(0.006369, 0.99998, 0.0, 0.577584), - float4(0.326638, 0.830655, -0.043263, 7.0), - float4(0.139524, -0.21199, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.835314, 0.549773, 0.0, 0.577584), - float4(0.466162, 0.618665, -0.043263, 3.0), - float4(0.007571, -0.015142, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.894427, 0.447214, 0.0, 0.577584), - float4(0.473733, 0.603523, -0.043263, 7.0), - float4(0.0, -0.21199, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(1.0, 0.0, 0.0, 0.577584), - float4(0.473733, 0.391533, -0.043263, 7.0), - float4(-0.022713, -0.032447, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.819232, -0.573462, 0.0, 0.577584), - float4(0.45102, 0.359085, -0.043263, 7.0), - float4(-0.040019, -0.007571, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.185892, -0.98257, 0.0, 0.577584), - float4(0.411001, 0.351514, -0.043263, 7.0), - float4(-0.034611, 0.030284, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.658505, -0.752577, 0.0, 0.577584), - float4(0.376391, 0.381799, -0.043263, 7.0), - float4(-0.001082, 0.107077, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999949, -0.0101, 0.0, 0.577584), - float4(0.375309, 0.488875, -0.043263, 7.0), - float4(-0.034611, -0.014061, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.376377, -0.926467, 0.0, 0.577584), - float4(0.340698, 0.474815, -0.043263, 7.0), - float4(0.001082, -0.121137, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.99996, 0.008928, 0.0, 0.577584), - float4(0.34178, 0.353677, -0.043263, 7.0), - float4(-0.022713, -0.034611, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.836048, -0.548657, 0.0, 0.577584), - float4(0.319067, 0.319067, -0.043263, 7.0), - float4(-0.042182, -0.007571, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.176664, -0.984271, 0.0, 0.577584), - float4(0.276885, 0.311496, -0.043263, 7.0), - float4(-0.035692, 0.032447, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.672673, -0.73994, 0.0, 0.577584), - float4(0.241193, 0.343943, -0.043263, 7.0), - float4(-0.002163, 0.107077, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999796, -0.020198, 0.0, 0.577584), - float4(0.23903, 0.45102, -0.043263, 7.0), - float4(-0.028121, -0.005408, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.188847, -0.982006, 0.0, 0.577584), - float4(0.210908, 0.445612, -0.043263, 7.0), - float4(0.0, -0.131953, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(1.0, 0.0, 0.0, 0.577584), - float4(0.210908, 0.313659, -0.043263, 7.0), - float4(-0.021632, -0.033529, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.840297, -0.542127, 0.0, 0.577584), - float4(0.189277, 0.28013, -0.043263, 7.0), - float4(-0.042182, -0.007571, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.176664, -0.984271, 0.0, 0.577584), - float4(0.147095, 0.272559, -0.043263, 7.0), - float4(-0.035692, 0.034611, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.696146, -0.7179, 0.0, 0.577584), - float4(0.111403, 0.307169, -0.043263, 7.0), - float4(-0.002163, 0.131953, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999866, -0.016391, 0.0, 0.577584), - float4(0.10924, 0.439122, -0.043263, 7.0), - float4(-0.031366, -0.031366, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.707107, -0.707107, 0.0, 0.577584), - float4(0.077874, 0.407756, -0.043263, 7.0), - float4(-0.002163, -0.319067, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.999977, -0.006779, 0.0, 0.577584), - float4(0.075711, 0.08869, -0.043263, 7.0), - float4(-0.031366, -0.045426, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.822897, -0.568191, 0.0, 0.577584), - float4(0.044345, 0.043263, -0.043263, 7.0), - float4(-0.043263, -0.005408, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(0.124035, -0.992278, 0.0, 0.577584), - float4(0.001082, 0.037855, -0.043263, 7.0), - float4(-0.044345, 0.040019, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.669964, -0.742393, 0.0, 0.577584), - float4(-0.043263, 0.077874, -0.043263, 7.0), - float4(-0.001082, 0.154666, 0.043263, 0.01033), - float4(0.0, 0.0, 0.043263, 0.045182), - float4(-0.999976, -0.006993, 0.0, 0.577584), - float4(0.044345, 0.043263, 0.0, 10.0), - float4(-0.043263, -0.005408, 0.0, 0.000305), - float4(-0.040019, 0.030284, 0.049753, 0.964691), - float4(0.101434, -0.811469, 0.575525, 0.973449), - float4(-0.031366, 0.229295, 0.058405, 14.0), - float4(0.035692, -0.155748, -0.008653, 0.000305), - float4(-0.011897, -0.151422, -0.058405, 0.955978), - float4(-0.716533, -0.20131, 0.667874, 0.904662), - float4(-0.031366, 0.229295, 0.058405, 14.0), - float4(0.10924, 0.178461, -0.058405, 0.0), - float4(0.107077, -0.140606, -0.058405, 0.376257), - float4(0.475583, -0.003224, 0.879665, 0.830777), - float4(-0.045426, 0.564586, 0.052998, 14.0), - float4(0.1233, -0.156829, -0.052998, 0.520989), - float4(0.014061, -0.335291, 0.005408, 0.088655), - float4(0.429355, 0.032562, 0.902549, 0.973449), - float4(0.183869, 0.317985, 0.037855, 10.0), - float4(0.005408, -0.037855, -0.037855, 0.0), - float4(-0.036774, -0.045426, -0.037855, 0.806958), - float4(0.124328, -0.692684, 0.710445, 0.854994), - float4(0.10924, 0.439122, 0.0, 10.0), - float4(0.019468, 0.024876, 0.063813, 0.18117), - float4(0.035692, -0.131953, 0.049753, 0.955978), - float4(-0.933936, -0.126584, 0.334276, 0.964691), + float4(0.179543, 0.466162, 0.058405, 10.0), + float4(-0.050834, -0.002163, 0.005408, 0.000305), + float4(0.021632, 0.171972, 0.023795, 0.022171), + float4(0.110896, -0.149888, 0.982464, 0.381323), + float4(-0.012979, 0.832818, 0.0, 14.0), + float4(0.339617, -0.002163, 0.0, 0.0), + float4(0.214153, -0.194685, 0.0822, 0.027314), + float4(0.002492, 0.391296, 0.920261, 0.49102), float4(0.183869, 0.317985, 0.037855, 10.0), float4(-0.055161, 0.146014, 0.025958, 0.0), float4(0.02704, 0.127627, -0.037855, 0.439658), float4(0.623841, 0.097823, 0.775405, 0.871366), - float4(0.128708, 0.463999, 0.063813, 14.0), - float4(0.050834, 0.002163, -0.005408, 0.0), - float4(0.0822, -0.018387, -0.063813, 0.439658), - float4(0.07859, -0.92643, 0.368172, 0.871366), - float4(0.319067, 0.319067, 0.0, 10.0), - float4(-0.042182, -0.007571, 0.0, 0.0), - float4(-0.037855, 0.023795, 0.046508, 0.896269), - float4(0.1483, -0.826245, 0.54344, 0.896269), + float4(0.210908, 0.445612, -0.043263, 7.0), + float4(0.0, -0.131953, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.210908, 0.445612, 0.0, 10.0), + float4(-0.031366, 0.02055, 0.058405, 0.000305), + float4(0.035692, 0.014061, 0.06165, 0.022171), + float4(-0.105864, -0.954447, 0.278971, 0.381323), + float4(0.23903, 0.45102, -0.043263, 11.0), + float4(-0.028121, -0.005408, 0.0, 0.01033), + float4(-0.028121, -0.005408, 0.043263, 0.045182), + float4(0.188847, -0.982006, 0.0, 0.577584), + float4(0.23903, 0.45102, -0.043263, 7.0), + float4(-0.028121, -0.005408, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.188847, -0.982006, 0.0, 0.577584), + float4(0.246601, 0.459672, 0.06165, 11.0), + float4(-0.007571, -0.008653, -0.06165, 0.000305), + float4(-0.035692, -0.014061, -0.06165, 0.022171), + float4(0.187618, -0.975617, 0.113888, 0.381323), + float4(0.241193, 0.343943, -0.043263, 11.0), + float4(-0.002163, 0.107077, 0.0, 0.01033), + float4(-0.002163, 0.107077, 0.043263, 0.045182), + float4(-0.999796, -0.020198, 0.0, 0.577584), float4(0.23903, 0.45102, 0.0, 15.0), float4(0.007571, 0.008653, 0.06165, 0.000305), float4(0.042182, -0.108158, 0.046508, 0.846876), @@ -1451,190 +1541,148 @@ constant float4 PRISM_TRIS[644] = { float4(-0.070303, 0.101669, 0.024876, 0.0), float4(0.023795, 0.116811, -0.036774, 0.473533), float4(0.523424, 0.157027, 0.837478, 0.930114), + float4(0.340698, 0.474815, -0.043263, 7.0), + float4(0.001082, -0.121137, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.99996, 0.008928, 0.0, 0.577584), + float4(0.376391, 0.381799, -0.043263, 11.0), + float4(-0.001082, 0.107077, 0.0, 0.01033), + float4(-0.001082, 0.107077, 0.043263, 0.045182), + float4(-0.999949, -0.0101, 0.0, 0.577584), + float4(0.376391, 0.381799, -0.043263, 7.0), + float4(-0.001082, 0.107077, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(-0.999949, -0.0101, 0.0, 0.577584), + float4(0.375309, 0.488875, 0.0, 14.0), + float4(0.036774, -0.105995, 0.043263, 0.000305), + float4(0.001082, -0.107077, 0.0, 0.879622), + float4(-0.771254, -0.00779, 0.63648, 0.896269), + float4(0.375309, 0.488875, 0.0, 14.0), + float4(0.078955, -0.08869, 0.032447, 0.223224), + float4(0.036774, -0.105995, 0.043263, 0.964691), + float4(0.071225, 0.39802, 0.914608, 0.973449), + float4(0.473733, 0.603523, 0.0, 14.0), + float4(0.0, -0.21199, 0.0, 0.158953), + float4(-0.019468, -0.203337, 0.032447, 0.030716), + float4(0.857493, 0.0, 0.514496, 0.964691), + float4(0.473733, 0.603523, -0.043263, 11.0), + float4(0.0, -0.21199, 0.0, 0.01033), + float4(0.0, -0.21199, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), + float4(0.276885, 0.484549, 0.058405, 10.0), + float4(-0.030284, -0.024876, 0.003245, 0.000305), + float4(-0.097342, -0.018387, 0.0, 0.022171), + float4(-0.03153, 0.166923, 0.985466, 0.381323), float4(0.246601, 0.459672, 0.06165, 14.0), float4(0.030284, 0.024876, -0.003245, 0.0), float4(0.094098, 0.015142, -0.06165, 0.473533), float4(0.518869, -0.545855, 0.657888, 0.930114), - float4(0.45102, 0.359085, 0.0, 10.0), - float4(-0.040019, -0.007571, 0.0, 0.000305), - float4(-0.038937, 0.023795, 0.043263, 0.879622), - float4(0.151736, -0.802035, 0.577682, 0.9131), - float4(0.454264, 0.400185, 0.032447, 12.0), - float4(-0.078955, 0.08869, -0.032447, 0.158953), - float4(0.019468, 0.203337, -0.032447, 0.030716), - float4(-0.201684, 0.173144, 0.964025, 0.964691), - float4(0.375309, 0.488875, 0.0, 10.0), - float4(0.090853, 0.12979, 0.0, 0.158953), - float4(0.098424, 0.114648, 0.0, 0.030716), - float4(0.0, 0.0, 1.0, 0.964691), + float4(0.375309, 0.488875, -0.043263, 11.0), + float4(-0.034611, -0.014061, 0.0, 0.01033), + float4(-0.034611, -0.014061, 0.043263, 0.045182), + float4(0.376377, -0.926467, 0.0, 0.577584), float4(0.340698, 0.474815, 0.0, 14.0), float4(-0.063813, 0.009734, 0.058405, 0.000305), float4(0.034611, 0.014061, 0.0, 0.022171), float4(0.3276, -0.806402, 0.492334, 0.381323), - float4(0.276885, 0.484549, 0.058405, 10.0), - float4(-0.030284, -0.024876, 0.003245, 0.000305), - float4(-0.097342, -0.018387, 0.0, 0.022171), - float4(-0.03153, 0.166923, 0.985466, 0.381323), + float4(0.375309, 0.488875, -0.043263, 7.0), + float4(-0.034611, -0.014061, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.376377, -0.926467, 0.0, 0.577584), + float4(0.454264, 0.400185, 0.032447, 12.0), + float4(-0.078955, 0.08869, -0.032447, 0.158953), + float4(0.019468, 0.203337, -0.032447, 0.030716), + float4(-0.201684, 0.173144, 0.964025, 0.964691), float4(0.201174, 0.638133, 0.0822, 8.0), float4(0.075711, -0.153585, -0.023795, 0.000305), float4(-0.021632, -0.171972, -0.023795, 0.022171), float4(0.026497, -0.140281, 0.989757, 0.381323), + float4(0.473733, 0.603523, -0.043263, 7.0), + float4(0.0, -0.21199, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(1.0, 0.0, 0.0, 0.577584), float4(0.276885, 0.484549, 0.058405, 14.0), float4(-0.075711, 0.153585, 0.023795, 0.000305), float4(0.098424, 0.004326, -0.058405, 0.022171), float4(0.50316, 0.115345, 0.856461, 0.381323), - float4(0.246601, 0.459672, 0.06165, 11.0), - float4(-0.007571, -0.008653, -0.06165, 0.000305), - float4(-0.035692, -0.014061, -0.06165, 0.022171), - float4(0.187618, -0.975617, 0.113888, 0.381323), - float4(0.210908, 0.445612, 0.0, 10.0), - float4(-0.031366, 0.02055, 0.058405, 0.000305), - float4(0.035692, 0.014061, 0.06165, 0.022171), - float4(-0.105864, -0.954447, 0.278971, 0.381323), - float4(-0.167645, 0.416409, 0.046508, 14.0), - float4(0.004326, -0.035692, -0.046508, 0.000305), - float4(-0.056242, -0.008653, -0.046508, 0.955978), - float4(-0.339782, -0.761112, 0.552501, 0.973449), - float4(-0.167645, 0.416409, 0.046508, 10.0), - float4(0.085445, 0.114648, -0.003245, 0.0), - float4(0.073548, 0.015142, -0.046508, 0.768154), - float4(0.548356, -0.38771, 0.740936, 0.871366), - float4(-0.044345, 0.23254, 0.0, 3.0), - float4(0.0, 0.0, -0.043263, 0.01033), - float4(-0.001082, 0.332046, -0.043263, 0.045182), - float4(-0.999995, -0.003257, 0.0, 0.577584), - float4(-0.045426, 0.564586, -0.043263, 11.0), - float4(-0.036774, -0.033529, 0.0, 0.01033), - float4(-0.036774, -0.033529, 0.086527, 0.045182), - float4(0.673754, -0.738956, 0.0, 0.577584), - float4(-0.0822, 0.531057, -0.043263, 11.0), - float4(-0.011897, -0.099506, 0.0, 0.01033), - float4(-0.011897, -0.099506, 0.043263, 0.045182), - float4(0.992928, -0.11872, 0.0, 0.577584), - float4(-0.094098, 0.431551, -0.043263, 11.0), - float4(-0.069221, -0.050834, 0.0, 0.01033), - float4(-0.069221, -0.050834, 0.043263, 0.045182), - float4(0.591909, -0.806004, 0.0, 0.577584), - float4(-0.163319, 0.380717, -0.043263, 11.0), - float4(-0.060569, 0.02704, 0.0, 0.01033), - float4(-0.060569, 0.02704, 0.043263, 0.045182), - float4(-0.407651, -0.913138, 0.0, 0.577584), - float4(-0.223888, 0.407756, -0.043263, 11.0), - float4(0.002163, 0.06814, 0.0, 0.01033), - float4(0.002163, 0.06814, 0.043263, 0.045182), - float4(-0.999496, 0.03173, 0.0, 0.577584), - float4(-0.221724, 0.475896, -0.043263, 9.0), - float4(0.122219, 0.207664, 0.0, 0.01033), - float4(0.122219, 0.207664, 0.043263, 0.045182), - float4(-0.861819, 0.507216, 0.0, 0.577584), - float4(-0.099506, 0.68356, -0.043263, 11.0), - float4(0.086527, 0.149258, 0.0, 0.01033), - float4(0.086527, 0.149258, 0.043263, 0.045182), - float4(-0.86514, 0.501531, 0.0, 0.577584), - float4(-0.012979, 0.832818, -0.043263, 11.0), - float4(0.339617, -0.002163, 0.0, 0.01033), - float4(0.339617, -0.002163, 0.043263, 0.045182), - float4(0.006369, 0.99998, 0.0, 0.577584), - float4(0.326638, 0.830655, -0.043263, 9.0), - float4(0.139524, -0.21199, 0.0, 0.01033), - float4(0.139524, -0.21199, 0.043263, 0.045182), - float4(0.835314, 0.549773, 0.0, 0.577584), + float4(0.375309, 0.488875, 0.0, 10.0), + float4(0.090853, 0.12979, 0.0, 0.158953), + float4(0.098424, 0.114648, 0.0, 0.030716), + float4(0.0, 0.0, 1.0, 0.964691), float4(0.466162, 0.618665, -0.043263, 11.0), float4(0.007571, -0.015142, 0.0, 0.01033), float4(0.007571, -0.015142, 0.043263, 0.045182), float4(0.894427, 0.447214, 0.0, 0.577584), - float4(0.473733, 0.603523, -0.043263, 11.0), - float4(0.0, -0.21199, 0.0, 0.01033), - float4(0.0, -0.21199, 0.043263, 0.045182), - float4(1.0, 0.0, 0.0, 0.577584), - float4(0.473733, 0.391533, -0.043263, 11.0), - float4(-0.022713, -0.032447, 0.0, 0.01033), - float4(-0.022713, -0.032447, 0.043263, 0.045182), - float4(0.819232, -0.573462, 0.0, 0.577584), - float4(0.45102, 0.359085, -0.043263, 11.0), - float4(-0.040019, -0.007571, 0.0, 0.01033), - float4(-0.040019, -0.007571, 0.043263, 0.045182), - float4(0.185892, -0.98257, 0.0, 0.577584), - float4(0.411001, 0.351514, -0.043263, 11.0), - float4(-0.034611, 0.030284, 0.0, 0.01033), - float4(-0.034611, 0.030284, 0.043263, 0.045182), - float4(-0.658505, -0.752577, 0.0, 0.577584), - float4(0.376391, 0.381799, -0.043263, 11.0), - float4(-0.001082, 0.107077, 0.0, 0.01033), - float4(-0.001082, 0.107077, 0.043263, 0.045182), - float4(-0.999949, -0.0101, 0.0, 0.577584), - float4(0.375309, 0.488875, -0.043263, 11.0), - float4(-0.034611, -0.014061, 0.0, 0.01033), - float4(-0.034611, -0.014061, 0.043263, 0.045182), - float4(0.376377, -0.926467, 0.0, 0.577584), - float4(0.340698, 0.474815, -0.043263, 11.0), - float4(0.001082, -0.121137, 0.0, 0.01033), - float4(0.001082, -0.121137, 0.043263, 0.045182), - float4(0.99996, 0.008928, 0.0, 0.577584), - float4(0.34178, 0.353677, -0.043263, 11.0), - float4(-0.022713, -0.034611, 0.0, 0.01033), - float4(-0.022713, -0.034611, 0.043263, 0.045182), - float4(0.836048, -0.548657, 0.0, 0.577584), - float4(0.319067, 0.319067, -0.043263, 11.0), - float4(-0.042182, -0.007571, 0.0, 0.01033), - float4(-0.042182, -0.007571, 0.043263, 0.045182), - float4(0.176664, -0.984271, 0.0, 0.577584), - float4(0.276885, 0.311496, -0.043263, 11.0), - float4(-0.035692, 0.032447, 0.0, 0.01033), - float4(-0.035692, 0.032447, 0.043263, 0.045182), - float4(-0.672673, -0.73994, 0.0, 0.577584), - float4(0.241193, 0.343943, -0.043263, 11.0), - float4(-0.002163, 0.107077, 0.0, 0.01033), - float4(-0.002163, 0.107077, 0.043263, 0.045182), - float4(-0.999796, -0.020198, 0.0, 0.577584), - float4(0.23903, 0.45102, -0.043263, 11.0), - float4(-0.028121, -0.005408, 0.0, 0.01033), - float4(-0.028121, -0.005408, 0.043263, 0.045182), - float4(0.188847, -0.982006, 0.0, 0.577584), - float4(0.210908, 0.445612, -0.043263, 11.0), - float4(0.0, -0.131953, 0.0, 0.01033), - float4(0.0, -0.131953, 0.043263, 0.045182), - float4(1.0, 0.0, 0.0, 0.577584), - float4(0.210908, 0.313659, -0.043263, 11.0), - float4(-0.021632, -0.033529, 0.0, 0.01033), - float4(-0.021632, -0.033529, 0.043263, 0.045182), - float4(0.840297, -0.542127, 0.0, 0.577584), - float4(0.189277, 0.28013, -0.043263, 11.0), - float4(-0.042182, -0.007571, 0.0, 0.01033), - float4(-0.042182, -0.007571, 0.043263, 0.045182), - float4(0.176664, -0.984271, 0.0, 0.577584), - float4(0.147095, 0.272559, -0.043263, 11.0), - float4(-0.035692, 0.034611, 0.0, 0.01033), - float4(-0.035692, 0.034611, 0.043263, 0.045182), - float4(-0.696146, -0.7179, 0.0, 0.577584), - float4(0.111403, 0.307169, -0.043263, 11.0), - float4(-0.002163, 0.131953, 0.0, 0.01033), - float4(-0.002163, 0.131953, 0.043263, 0.045182), - float4(-0.999866, -0.016391, 0.0, 0.577584), - float4(0.10924, 0.439122, -0.043263, 11.0), - float4(-0.031366, -0.031366, 0.0, 0.01033), - float4(-0.031366, -0.031366, 0.043263, 0.045182), - float4(0.707107, -0.707107, 0.0, 0.577584), - float4(0.077874, 0.407756, -0.043263, 11.0), - float4(-0.002163, -0.319067, 0.0, 0.01033), - float4(-0.002163, -0.319067, 0.043263, 0.045182), - float4(0.999977, -0.006779, 0.0, 0.577584), - float4(0.075711, 0.08869, -0.043263, 11.0), - float4(-0.031366, -0.045426, 0.0, 0.01033), - float4(-0.031366, -0.045426, 0.043263, 0.045182), - float4(0.822897, -0.568191, 0.0, 0.577584), - float4(0.044345, 0.043263, -0.043263, 11.0), - float4(-0.043263, -0.005408, 0.0, 0.01033), - float4(-0.043263, -0.005408, 0.043263, 0.045182), - float4(0.124035, -0.992278, 0.0, 0.577584), - float4(0.001082, 0.037855, -0.043263, 11.0), - float4(-0.044345, 0.040019, 0.0, 0.01033), - float4(-0.044345, 0.040019, 0.043263, 0.045182), - float4(-0.669964, -0.742393, 0.0, 0.577584), - float4(-0.043263, 0.077874, -0.043263, 9.0), - float4(-0.001082, 0.154666, 0.0, 0.01033), - float4(-0.001082, 0.154666, 0.043263, 0.045182), - float4(-0.999976, -0.006993, 0.0, 0.577584) + float4(0.466162, 0.618665, -0.043263, 3.0), + float4(0.007571, -0.015142, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.894427, 0.447214, 0.0, 0.577584), + float4(0.326638, 0.830655, 0.0, 14.0), + float4(0.139524, -0.21199, 0.0, 0.0), + float4(0.048671, -0.34178, 0.0, 0.015213), + float4(0.0, 0.0, 1.0, 0.428687), + float4(0.326638, 0.830655, 0.0, 14.0), + float4(0.048671, -0.34178, 0.0, 0.007492), + float4(-0.125464, -0.192522, 0.0822, 0.054475), + float4(0.472493, 0.067285, 0.878762, 0.964691), + float4(0.326638, 0.830655, -0.043263, 9.0), + float4(0.139524, -0.21199, 0.0, 0.01033), + float4(0.139524, -0.21199, 0.043263, 0.045182), + float4(0.835314, 0.549773, 0.0, 0.577584), + float4(0.326638, 0.830655, -0.043263, 7.0), + float4(0.139524, -0.21199, 0.043263, 0.01033), + float4(0.0, 0.0, 0.043263, 0.045182), + float4(0.835314, 0.549773, 0.0, 0.577584), + float4(-0.012979, 0.832818, -0.043263, 11.0), + float4(0.339617, -0.002163, 0.0, 0.01033), + float4(0.339617, -0.002163, 0.043263, 0.045182), + float4(0.006369, 0.99998, 0.0, 0.577584) +}; +constant float4 PRISM_BOXES[44] = { + float4(0.030135, 0.06127, -0.043945, 0.0), + float4(0.330601, 0.483055, 0.116951, 7.0), + float4(0.034429, 0.265258, -0.043945, 7.0), + float4(0.542104, 0.741798, 0.116951, 8.0), + float4(0.034429, 0.355442, -0.043945, 15.0), + float4(0.305908, 0.822319, 0.116951, 8.0), + float4(0.267405, 0.532589, -0.043945, 23.0), + float4(0.542104, 0.822319, 0.082595, 8.0), + float4(-0.045345, 0.036855, -0.044263, 31.0), + float4(0.045345, 0.23354, 0.059405, 8.0), + float4(-0.224888, 0.072548, -0.044263, 39.0), + float4(0.059405, 0.565586, 0.059405, 8.0), + float4(-0.032366, 0.042263, -0.044263, 47.0), + float4(0.148095, 0.408756, 0.059405, 8.0), + float4(0.074711, 0.08769, -0.044263, 55.0), + float4(0.184869, 0.464999, 0.064813, 8.0), + float4(0.146095, 0.271559, -0.044263, 63.0), + float4(0.211908, 0.446612, 0.038855, 8.0), + float4(0.23803, 0.310496, -0.044263, 71.0), + float4(0.320067, 0.460672, 0.06265, 8.0), + float4(0.275885, 0.310496, -0.044263, 79.0), + float4(0.412001, 0.475815, 0.047508, 8.0), + float4(0.375391, 0.350514, -0.044263, 87.0), + float4(0.45202, 0.38388, 0.044263, 4.0), + float4(0.410001, 0.350514, -0.044263, 91.0), + float4(0.474733, 0.401185, 0.044263, 5.0), + float4(-0.224888, 0.379717, -0.044263, 96.0), + float4(-0.0812, 0.532057, 0.047508, 8.0), + float4(-0.095098, 0.228295, -0.044263, 104.0), + float4(0.211908, 0.565586, 0.064813, 8.0), + float4(-0.222724, 0.430551, -0.044263, 112.0), + float4(-0.011979, 0.833818, 0.053998, 8.0), + float4(-0.100506, 0.438122, -0.044263, 120.0), + float4(0.327638, 0.833818, 0.0832, 8.0), + float4(0.127708, 0.312659, -0.044263, 128.0), + float4(0.282211, 0.467162, 0.064813, 8.0), + float4(0.245601, 0.352677, -0.044263, 136.0), + float4(0.474733, 0.604523, 0.06265, 8.0), + float4(0.178543, 0.390533, -0.044263, 144.0), + float4(0.474733, 0.639133, 0.0832, 8.0), + float4(0.200174, 0.483549, -0.044263, 152.0), + float4(0.474733, 0.639133, 0.0832, 4.0), + float4(-0.013979, 0.487875, -0.044263, 156.0), + float4(0.467162, 0.833818, 0.0832, 5.0) }; constant float2 PRISM_POLY[110] = { float2(0.006442, -0.002147), @@ -1821,17 +1869,35 @@ static float prism_tri(float3 ro, float3 rd, int k) static float prism_trace(float3 ro, float3 rd, int shape, thread int &hit) { - int k0 = PRISM_TRI_START[shape]; - int n = PRISM_TRI_COUNT[shape]; + float3 inv = 1.0 / select(float3(1e-6), rd, abs(rd) > 1e-6); + int b0 = PRISM_BOX_START[shape]; + int nb = PRISM_BOX_COUNT[shape]; float best = 1e9; hit = -1; - for (int i = 0; i < n; i++) - { - float t = prism_tri(ro, rd, k0 + i); - if (t > 1e-5 && t < best) + for (int j = b0; j < b0 + nb; j++) + { + float4 lo = PRISM_BOXES[2 * j]; + float4 hi = PRISM_BOXES[2 * j + 1]; + float3 t0 = (lo.xyz - ro) * inv; + float3 t1 = (hi.xyz - ro) * inv; + float3 tn = min(t0, t1); + float3 tf = max(t0, t1); + float tmin = max(max(tn.x, tn.y), tn.z); + float tmax = min(min(tf.x, tf.y), tf.z); + if (tmax < max(tmin, 1e-5) || tmin > best) + { + continue; + } + int k0 = int(lo.w); + int n = int(hi.w); + for (int i = k0; i < k0 + n; i++) { - best = t; - hit = k0 + i; + float t = prism_tri(ro, rd, i); + if (t > 1e-5 && t < best) + { + best = t; + hit = i; + } } } return best; diff --git a/crates/compositor/src/vk_shaders/layer.wgsl b/crates/compositor/src/vk_shaders/layer.wgsl index cb3a51083..0f4d4e452 100644 --- a/crates/compositor/src/vk_shaders/layer.wgsl +++ b/crates/compositor/src/vk_shaders/layer.wgsl @@ -966,7 +966,7 @@ fn s_pixel_outline(p: vec2, shape: i32) -> f32 { // ---- Prism Glow : un cristal en MAILLAGE ---- (port du HLSL, dont les commentaires font foi) // Les facettes tracees sur son dessin, en triangles dans le repere du modele ; le serti est la -// silhouette extrudee (champ de distance), le cristal est lance de rayons triangle par triangle. +// silhouette extrudee (champ de distance), le cristal est lance de rayons boite par boite. // prism mesh: generated by design/cursors/prism-glow/model/export_compositor.py var PRISM_TRI_START = array(0, 31); var PRISM_TRI_COUNT = array(31, 130); @@ -974,9 +974,12 @@ var PRISM_SIL_START = array(0, 25); var PRISM_SIL_COUNT = array(25, 42); var PRISM_OUT_START = array(67, 76); var PRISM_OUT_COUNT = array(9, 34); +var PRISM_BOX_START = array(0, 4); +var PRISM_BOX_COUNT = array(4, 18); struct PrismMesh { tris: array, 644>, poly: array, 110>, + boxes: array, 44>, } const PRISM_BEVEL: f32 = 0.012883; // end of the prism mesh @@ -1050,17 +1053,33 @@ fn prism_tri(ro: vec3, rd: vec3, k: i32) -> f32 { } // (distance, indice) du triangle du cristal `shape` le plus proche devant (ro, rd) ; (1e9, -1) -// s'il n'y en a pas. +// s'il n'y en a pas. Les triangles sont parcourus boîte par boîte (cf. HLSL). fn prism_trace(ro: vec3, rd: vec3, shape: i32) -> vec2 { - let k0 = PRISM_TRI_START[shape]; - let n = PRISM_TRI_COUNT[shape]; + let inv = vec3(1.0) / select(vec3(1e-6), rd, abs(rd) > vec3(1e-6)); + let b0 = PRISM_BOX_START[shape]; + let nb = PRISM_BOX_COUNT[shape]; var best = 1e9; var hit = -1; - for (var i = 0; i < n; i++) { - let t = prism_tri(ro, rd, k0 + i); - if t > 1e-5 && t < best { - best = t; - hit = k0 + i; + for (var j = b0; j < b0 + nb; j++) { + let lo = prism.boxes[2 * j]; + let hi = prism.boxes[2 * j + 1]; + let t0 = (lo.xyz - ro) * inv; + let t1 = (hi.xyz - ro) * inv; + let tn = min(t0, t1); + let tf = max(t0, t1); + let tmin = max(max(tn.x, tn.y), tn.z); + let tmax = min(min(tf.x, tf.y), tf.z); + if tmax < max(tmin, 1e-5) || tmin > best { + continue; + } + let k0 = i32(lo.w + 0.5); + let n = i32(hi.w + 0.5); + for (var i = k0; i < k0 + n; i++) { + let t = prism_tri(ro, rd, i); + if t > 1e-5 && t < best { + best = t; + hit = i; + } } } return vec2(best, f32(hit)); diff --git a/design/cursors/3d-direction.md b/design/cursors/3d-direction.md index e8f594bea..a18cb1ee6 100644 --- a/design/cursors/3d-direction.md +++ b/design/cursors/3d-direction.md @@ -35,7 +35,8 @@ Prism Glow is built from its 2D drawing alone (`prism-glow/source.png`), not fro vertices, heights and facets and writes `prism.json`; `build_blend.py` builds `prism-glow.blend` and `prism-glow.glb` from it in Blender; `export_compositor.py` writes the mesh into `crates/compositor/src/prism_mesh.rs` and the three shaders. -The compositor traces the crystal triangle by triangle: refraction per colour +The compositor ray-traces the crystal, its triangles grouped in bounding boxes that +a ray skips when it misses them: refraction per colour channel, total internal reflection, and an exit through its flat base onto the recording under the cursor. The rim is its silhouette extruded, a distance field like the other models: it gives the coverage and the shadows. diff --git a/design/cursors/prism-glow/model/export_compositor.py b/design/cursors/prism-glow/model/export_compositor.py index 144961acb..15cd261af 100644 --- a/design/cursors/prism-glow/model/export_compositor.py +++ b/design/cursors/prism-glow/model/export_compositor.py @@ -23,6 +23,8 @@ SPRITE, INNER, ALPHA_MIN = 128, 112, 28 # generate-original-cursor-themes.mjs BEVEL_PX = 12.0 # rounding of the rim's edges, source px EDGE_FOLD_DEG = 25.0 +LEAF = 8 # triangles per bounding box at most +BOX_PAD = 1e-3 # model units: past prism_tri's edge slack def glb_meshes(path): @@ -103,6 +105,31 @@ def crystal_triangles(pos, tri, col, sheet, s): return tris, float(zmin), float(model[:, 2].max()), model +def cluster(tris): + """The triangles regrouped by neighbourhood, and one bounding box per group: a ray that + misses a box skips all its triangles. Groups are halves of the centroids' longest extent, + split until they hold LEAF triangles at most; the triangles come back in group order.""" + groups = [] + + def split(idx): + if len(idx) <= LEAF: + groups.append(idx) + return + cent = np.array([np.mean(tris[i]["v"], axis=0) for i in idx]) + axis = int(np.argmax(cent.max(0) - cent.min(0))) + order = [idx[j] for j in np.argsort(cent[:, axis], kind="stable")] + split(order[: len(order) // 2]) + split(order[len(order) // 2 :]) + + split(list(range(len(tris)))) + ordered, leaves = [], [] + for g in groups: + pts = np.array([v for i in g for v in tris[i]["v"]]) + leaves.append((pts.min(0) - BOX_PAD, pts.max(0) + BOX_PAD, len(ordered), len(g))) + ordered += [tris[i] for i in g] + return ordered, leaves + + def main(): img = cv2.imread(str(HERE.parent / "source.png"), cv2.IMREAD_UNCHANGED) geo = json.loads((HERE / "prism.json").read_text()) @@ -115,6 +142,7 @@ def main(): hot = geo[kind]["hotspot"] s, (hx, hy), top = sprite_transform(img, x0, x1, hot) tris, zmin, zmax, model = crystal_triangles(*meshes[node], sheet, s) + tris, leaves = cluster(tris) sil = [((x - hot[0]) * s, (y - hot[1]) * s) for x, y in geo[kind]["silhouette"]] out = [((geo[kind]["verts"][k][0] - hot[0]) * s, (geo[kind]["verts"][k][1] - hot[1]) * s) for k in geo[kind]["outline"]] @@ -122,11 +150,12 @@ def main(): # the draw box: the sprite's full height (1 unit), the silhouette's width bx0, bx1 = min(sx) - 0.01, max(sx) + 0.01 shapes.append({ - "kind": kind, "tris": tris, "sil": sil, "out": out, "thick": -zmin, "height": zmax, + "kind": kind, "tris": tris, "leaves": leaves, "sil": sil, "out": out, "thick": -zmin, + "height": zmax, "size": (bx1 - bx0, 1.0), "hotspot": (-bx0 / (bx1 - bx0), hy), "top": top, "per_px": s, }) - print(f"{kind}: {len(tris)} triangles, {len(sil)} silhouette points, " + print(f"{kind}: {len(tris)} triangles in {len(leaves)} boxes, {len(sil)} silhouette points, " f"box {bx1 - bx0:.4f} x 1, hotspot ({-bx0 / (bx1 - bx0):.4f}, {hy:.4f}), " f"height {zmax:.4f}, thick {-zmin:.4f}") write_rust(shapes) @@ -170,6 +199,17 @@ def records(shapes): return rows +def boxes(shapes): + """Per box, two float4: (lo, first triangle), (hi, triangle count); each model's first box.""" + rows, box_start, k = [], [], 0 + for sh in shapes: + box_start.append(len(rows) // 2) + for lo, hi, first, count in sh["leaves"]: + rows += [(*lo, k + first), (*hi, count)] + k += len(sh["tris"]) + return rows, box_start + + def write_rust(shapes): tri_start, rows = [], records(shapes) k = 0 @@ -177,6 +217,7 @@ def write_rust(shapes): tri_start.append(k) k += len(sh["tris"]) poly, sil_start, out_start = polygons(shapes) + box_rows, box_start = boxes(shapes) lines = [ "//! Les deux curseurs de Prism Glow en MAILLAGE : les facettes tracées sur l'art 2D", "//! (`design/cursors/prism-glow/model`), taillées en cristal. Fichier GÉNÉRÉ par", @@ -187,7 +228,8 @@ def write_rust(shapes): "//! cristal tient en quatre float4 : (v0, drapeaux), (e1, r), (e2, g), (normale, b) ; drapeaux =", "//! paroi (1) + 2 × arêtes réelles (bit k : l'arête opposée au sommet k). Le fond plat, en", "//! z = -épaisseur, est laissé au shader. `POLYS` tient les silhouettes (bord extérieur du", - "//! serti) puis les contours du cristal (son bord intérieur).", + "//! serti) puis les contours du cristal (son bord intérieur). `BOXES` range les triangles", + "//! par voisinage : deux float4 par boîte, (coin bas, premier triangle), (coin haut, nombre).", "", "/// Un des deux modèles : ses triangles et sa silhouette dans les tables, sa boîte de dessin.", "pub struct PrismModel {", @@ -197,6 +239,8 @@ def write_rust(shapes): " pub sil_count: usize,", " pub out_start: usize,", " pub out_count: usize,", + " pub box_start: usize,", + " pub box_count: usize,", " /// Largeur de la boîte, hauteur 1 (le sprite), en unités du modèle.", " pub size: [f32; 2],", " /// Hotspot, fraction de la boîte.", @@ -223,6 +267,8 @@ def write_rust(shapes): f" sil_count: {len(sh['sil'])},", f" out_start: {out_start[i]},", f" out_count: {len(sh['out'])},", + f" box_start: {box_start[i]},", + f" box_count: {len(sh['leaves'])},", f" size: [{fl(sh['size'][0])}, {fl(sh['size'][1])}],", f" hotspot: [{fl(sh['hotspot'][0])}, {fl(sh['hotspot'][1])}],", f" top: {fl(sh['top'])},", @@ -234,6 +280,8 @@ def write_rust(shapes): lines += [f" [{', '.join(fl(v) for v in r)}]," for r in rows] lines += ["];", "", f"pub const POLYS: [[f32; 2]; {len(poly)}] = ["] lines += [f" [{fl(x)}, {fl(y)}]," for x, y in poly] + lines += ["];", "", f"pub const BOXES: [[f32; 4]; {len(box_rows)}] = ["] + lines += [f" [{', '.join(fl(v) for v in r)}]," for r in box_rows] lines += ["];", ""] (SRC / "prism_mesh.rs").write_text("\n".join(lines), encoding="utf-8", newline="\n") @@ -241,6 +289,8 @@ def write_rust(shapes): def table_lines(shapes, lang): rows = records(shapes) poly, sil_start, out_start = polygons(shapes) + box_rows, box_start = boxes(shapes) + bcounts = [len(sh["leaves"]) for sh in shapes] counts = [len(sh["tris"]) for sh in shapes] scounts = [len(sh["sil"]) for sh in shapes] ocounts = [len(sh["out"]) for sh in shapes] @@ -248,6 +298,7 @@ def table_lines(shapes, lang): v4 = lambda r: f"float4({', '.join(fl(v) for v in r)})" v2 = lambda p: f"float2({fl(p[0])}, {fl(p[1])})" head4 = f"static const float4 PRISM_TRIS[{len(rows)}] = {{" + headb = f"static const float4 PRISM_BOXES[{len(box_rows)}] = {{" head2 = f"static const float2 PRISM_POLY[{len(poly)}] = {{" tail = "};" ints = lambda name, vals: f"static const int {name}[2] = {{ {vals[0]}, {vals[1]} }};" @@ -256,6 +307,7 @@ def table_lines(shapes, lang): v4 = lambda r: f"float4({', '.join(fl(v) for v in r)})" v2 = lambda p: f"float2({fl(p[0])}, {fl(p[1])})" head4 = f"constant float4 PRISM_TRIS[{len(rows)}] = {{" + headb = f"constant float4 PRISM_BOXES[{len(box_rows)}] = {{" head2 = f"constant float2 PRISM_POLY[{len(poly)}] = {{" tail = "};" ints = lambda name, vals: f"constant int {name}[2] = {{ {vals[0]}, {vals[1]} }};" @@ -265,17 +317,22 @@ def table_lines(shapes, lang): scalar = f"const PRISM_BEVEL: f32 = {fl(bevel(shapes))};" out = [ints("PRISM_TRI_START", [0, counts[0]]), ints("PRISM_TRI_COUNT", counts), ints("PRISM_SIL_START", sil_start), ints("PRISM_SIL_COUNT", scounts), - ints("PRISM_OUT_START", out_start), ints("PRISM_OUT_COUNT", ocounts)] + ints("PRISM_OUT_START", out_start), ints("PRISM_OUT_COUNT", ocounts), + ints("PRISM_BOX_START", box_start), ints("PRISM_BOX_COUNT", bcounts)] if lang == "wgsl": - # The triangles and polygons themselves go in a uniform buffer that compositor_linux.rs + # The triangles, polygons and boxes themselves go in a uniform buffer that compositor_linux.rs # fills from prism_mesh.rs (a polygon point per vec4, the stride of a uniform array). As # a `const` or `var` table, lavapipe copies them into every invocation of every # layer: a frame without any cursor rendered 3.4 times slower. return out + ["struct PrismMesh {", f" tris: array, {len(rows)}>,", - f" poly: array, {len(poly)}>,", "}", scalar] + f" poly: array, {len(poly)}>,", + f" boxes: array, {len(box_rows)}>,", "}", scalar] out.append(head4) out += [f" {v4(r)}," for r in rows] out[-1] = out[-1].rstrip(",") + out += [tail, headb] + out += [f" {v4(r)}," for r in box_rows] + out[-1] = out[-1].rstrip(",") out += [tail, head2] out += [f" {v2(p)}," for p in poly] out[-1] = out[-1].rstrip(",") diff --git a/docs/3d-effects-v2.md b/docs/3d-effects-v2.md index f21bcdcfc..6308bed84 100644 --- a/docs/3d-effects-v2.md +++ b/docs/3d-effects-v2.md @@ -293,7 +293,8 @@ leur dessin : un plateau et un jonc de la couleur du trait, un coussin de couleu (`s_rimmed`) ; pour Pop Coral les mêmes formes en papier découpé, des feuilles au dessus plat (`s_paper`). Prism Glow est à part : un **cristal en maillage**, ses facettes tracées sur son seul dessin 2D (`design/cursors/prism-glow/model/`), serti dans sa silhouette extrudée, marine. -Le shader le lance de rayons triangle par triangle : réfraction par canal (une légère +Le shader le lance de rayons, triangles rangés par boîtes englobantes (un rayon saute les +boîtes qu'il rate) : réfraction par canal (une légère dispersion), réflexions totales internes, et sortie par son fond plat sur l'enregistrement sous le curseur ; chaque facette luit un peu de sa couleur du dessin, ses plis d'un liseré clair. `export_compositor.py` écrit le maillage dans `prism_mesh.rs` et dans les trois shaders. Le PNG From 56a0927f4abbe0d16883499433d9a29cf168a3bc Mon Sep 17 00:00:00 2001 From: EtienneLescot Date: Wed, 30 Sep 2026 10:34:55 +0200 Subject: [PATCH 5/5] fix(compositor): refract the blurred picture, the cursor sharp on top The crystal read the raw recording, so it could show what a privacy blur hides. The previous fix put the blur over the cursor, which blurred the cursor itself and still let a fringe through at the zone's edge. The order is now the recording, its privacy blurs, the cursor, sharp on top, then the camera and the other annotations, on all three backends. The crystal refracts a copy of the frame taken just before it is drawn, so only blurred pixels show through the glass. A Linux test hides fine stripes under a blur: they must not show through the crystal, and the cursor must stay sharp. --- crates/compositor/src/compositor_linux.rs | 145 +++++++++++++------- crates/compositor/src/compositor_macos.rs | 70 ++++++---- crates/compositor/src/compositor_windows.rs | 32 +++-- crates/compositor/src/frame_geometry.rs | 32 ++--- crates/compositor/src/sculpt.rs | 14 ++ crates/compositor/src/shaders.hlsl | 41 ++++-- crates/compositor/src/shaders.metal | 47 ++++--- crates/compositor/src/vk_shaders/layer.wgsl | 28 +++- design/cursors/3d-direction.md | 7 +- design/cursors/requirements.md | 4 +- docs/3d-effects-v2.md | 13 +- 11 files changed, 287 insertions(+), 146 deletions(-) diff --git a/crates/compositor/src/compositor_linux.rs b/crates/compositor/src/compositor_linux.rs index 74538623b..3b9a97a31 100644 --- a/crates/compositor/src/compositor_linux.rs +++ b/crates/compositor/src/compositor_linux.rs @@ -2533,6 +2533,8 @@ impl Compositor { .as_ref() .is_some_and(|s| s.annotations.iter().any(|a| a.kind == "blur" && visible(a))); let mut ann_draws: Vec = Vec::new(); + // Les flous de confidentialite, dessines sur le metrage avant le curseur. + let mut privacy_draws: Vec = Vec::new(); if let Some(scene) = scene_ref.as_ref() { // La liste arrive deja triee par zIndex cote app : l'ordre d'iteration // EST l'ordre de peinture. @@ -2623,7 +2625,7 @@ impl Compositor { Some((&self.ann_copy_view, &self.ann_copy_view, &self.ann_copy_view)), &dummy, ); - ann_draws.push(AnnDraw::plain(buf, bind)); + privacy_draws.push(AnnDraw::plain(buf, bind)); } "image" => { let Some(src) = a.image_path.as_ref().filter(|s| !s.is_empty()) else { @@ -2859,6 +2861,8 @@ impl Compositor { binds: Vec, /// L'impact des clics (mode 16), dessine SOUS le curseur, sur le RT. impacts: Vec, + /// Le cristal de Prism Glow : il lit la copie de l'image composee (`CursorPlan::glass`). + glass: bool, } let cursor_draw: Option = (|| { let track = cursor_ref.as_ref()?; @@ -2937,8 +2941,8 @@ impl Compositor { let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); // Curseur modelise (mode 15) : ce meme sprite extrude, `plan_cursor` en a tire la - // pose. L'enregistrement reste aux bindings 1, 2 et 5 (le cristal de Prism Glow le - // refracte), le champ au binding 4 (texMask), le sprite au binding 6 (texDof). + // pose. La copie de l'image composee au binding 1 (texY), que refracte le cristal de + // Prism Glow, le champ au binding 4 (texMask), le sprite au binding 6 (texDof). // Parite Windows/macOS. if let Some(pose) = plan.model { match self.cursor_sdf(&sprite.path) { @@ -2958,7 +2962,7 @@ impl Compositor { }; let (buf, bind) = self.make_bind_b4( &cb, - Some((&sy, &su, &sv)), + Some((&self.ann_copy_view, &dummy, &dummy)), &dummy, Some(&sdf_view), Some(&view), @@ -2971,6 +2975,7 @@ impl Compositor { _tex: vec![(tex, view), (sdf, sdf_view)], binds, impacts, + glass: plan.glass, }); } Err(e) => eprintln!("[curseur] champ de \"{}\" : {e:#}", sprite.path), @@ -2994,7 +2999,7 @@ impl Compositor { bufs.push(buf); binds.push(bind); } - Some(CursorDraw { _bufs: bufs, _tex: vec![(tex, view)], binds, impacts }) + Some(CursorDraw { _bufs: bufs, _tex: vec![(tex, view)], binds, impacts, glass: false }) })(); // Bind group de la passe de composition d'`accum` (layout du blur : // uniform + texture + sampler). Construit hors de la pass, comme les @@ -3107,8 +3112,8 @@ impl Compositor { // Blur du fond (avant l'ecran), si active par la scene/l'inspector. self.blur_bg(&mut encoder, cfg.bg_blur); // Passe 2 : l'ecran, compose par-dessus le fond (eventuellement floute) avec - // `LoadOp::Load`. Puis le curseur, la camera et les annotations, dans cet ordre : - // celui de Windows et macOS. + // `LoadOp::Load`. Puis les flous de confidentialite, le curseur, la camera et les autres + // annotations, dans cet ordre : celui de Windows et macOS. { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("fg-pass"), @@ -3142,6 +3147,49 @@ impl Compositor { ), } } + // Fige la frame composee pour les annotations « flou ». ICI et nulle part + // ailleurs : apres l'ecran (sinon un flou masquerait du vide) + // et avant le premier flou (sinon deux flous qui se recouvrent + // s'echantillonnent l'un l'autre). Une passe de rendu ne peut pas lire sa + // propre cible, d'ou la copie -- et d'ou le fait que les annotations + // doivent avoir leur propre passe. + if needs_ann_copy { + self.generate_ann_mips(&mut encoder); + } + // Passe 2 bis : les flous de confidentialite, sur le metrage, AVANT le curseur (parite + // Windows et macOS). Le curseur reste net par-dessus, et le cristal de Prism Glow, qui + // refracte l'image composee, n'y voit que des pixels deja floutes. + if !privacy_draws.is_empty() { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("privacy-pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &self.rt_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&self.pipeline); + for a in &privacy_draws { + rpass.set_bind_group(0, &a.bind, &[]); + rpass.draw(0..4, 0..1); + } + } + // Le cristal de Prism Glow refracte l'image telle qu'elle est composee a cet instant, + // flous compris : sa copie dans `ann_copy` (libre, les flous l'ont deja lue ; le mip 0 + // suffit), une fois pour toutes les copies de la trainee. + if cursor_draw.as_ref().is_some_and(|c| c.glass) { + encoder.copy_texture_to_texture( + self.rt.as_image_copy(), + self.ann_copy.as_image_copy(), + wgpu::Extent3d { width: self.render_w, height: self.render_h, depth_or_array_layers: 1 }, + ); + } // Le curseur net, et l'impact des clics sous lui. La trainee, elle, a besoin de sa // propre cible : elle suit. { @@ -3262,21 +3310,11 @@ impl Compositor { rpass.draw(0..4, 0..1); } } - // Fige la frame composee pour les annotations « flou ». ICI et nulle part - // ailleurs : apres l'ecran, le curseur et la camera (sinon un flou masquerait du vide) - // et avant la premiere annotation (sinon deux flous qui se recouvrent - // s'echantillonnent l'un l'autre). Une passe de rendu ne peut pas lire sa - // propre cible, d'ou la copie -- et d'ou le fait que les annotations - // doivent avoir leur propre passe. - if needs_ann_copy { - self.generate_ann_mips(&mut encoder); - } - // Passe 3 : les annotations, par-dessus tout le reste, curseur compris : un flou de - // confidentialite doit le couvrir, sans quoi le cristal de Prism Glow, qui refracte - // l'enregistrement brut, montrerait ce que le flou cache. Elle existe meme sans flou : - // deux passes consecutives sur la MEME cible avec `LoadOp::Load` ne coutent rien de plus - // qu'une seule sur un GPU desktop, et un seul chemin de code vaut mieux qu'un - // branchement qui ne serait exerce que dans un projet sur dix. + // Passe 3 : les autres annotations, par-dessus tout le reste (les flous sont passes avant + // le curseur). Elle existe meme sans annotation : deux passes consecutives sur la MEME + // cible avec `LoadOp::Load` ne coutent rien de plus qu'une seule sur un GPU desktop, et un + // seul chemin de code vaut mieux qu'un branchement qui ne serait exerce que dans un projet + // sur dix. { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("ann-pass"), @@ -5843,37 +5881,52 @@ mod tests { assert!(failures.is_empty(), "{failures:#?}"); } - /// Un flou de confidentialité couvre le curseur, comme sous Windows et macOS. Le cristal de - /// Prism Glow réfracte l'enregistrement brut : dessiné par-dessus le flou, il montrerait ce - /// que le flou cache. Sous le flou, ce que change le curseur reste flou : aucun bord franc, là - /// où le serti du cristal, dessiné par-dessus, en ferait des centaines. + /// Le métrage, puis ses flous de confidentialité, puis le curseur, net par-dessus (parité + /// Windows et macOS) : le cristal de Prism Glow réfracte l'image déjà floutée. Sous le flou, + /// des rayures fines décalées d'une colonne donnent la même image floutée ; si le verre lisait + /// la vidéo brute, il montrerait les rayures, et les deux rendus différeraient à travers lui. #[test] - fn a_privacy_blur_covers_the_crystal_cursor() { + fn the_crystal_refracts_the_blurred_picture() { let Some(gpu) = gpu() else { return }; let comp = Compositor::new_sized(&gpu, 1280, 720).expect("Compositor::new_sized"); - let (y, uv) = model_screen_planes(false); - let blue = FakeFrame::from_planes(&gpu, 640, 360, &y, &uv); - let blur = r#""annotations":[{"id":"b","startSec":0,"endSec":10,"kind":"blur","x":0.3,"y":0.3,"w":0.6,"h":0.7,"blur":{"style":"blur","shape":"rectangle","color":"white","intensity":24,"blockSize":16}}]"#; - let scene = |show: bool| { + // Moitié gauche grise, moitié droite (la zone floutée) rayée une colonne sur deux. + let (w, h) = (640u32, 360u32); + let stripes = |phase: u32| { + let y: Vec = (0..w * h) + .map(|i| if i % w < w / 2 { 150 } else if (i % w + phase) % 2 == 0 { 40 } else { 220 }) + .collect(); + FakeFrame::from_planes(&gpu, w, h, &y, &vec![128; (w * (h / 2)) as usize]) + }; + let (a, b) = (stripes(0), stripes(1)); + let blur = r#""annotations":[{"id":"b","startSec":0,"endSec":10,"kind":"blur","x":0.5,"y":0.0,"w":0.5,"h":1.0,"blur":{"style":"blur","shape":"rectangle","color":"white","intensity":24,"blockSize":16}}]"#; + let json = |show: bool| { model_scene_json("null", Some(true), "default", show, 5.0) .replace(r#"/arrow.png","#, r#"/arrow.png","sculpt":"prism-glow/arrow","#) .replace(r#""annotations":[]"#, blur) }; - let still = model_track("arrow", false, 0.5); - let with = compose_model(&comp, &blue, &scene(true), &still); - let without = compose_model(&comp, &blue, &scene(false), &still); - model_save("privacy-blur-over-crystal", &with); - // Ce que change le curseur, pixel par pixel ; un bord franc y est un saut de plus de 40 - // entre deux voisins. - let diff = |i: usize, c: usize| with[i * 4 + c] as i32 - without[i * 4 + c] as i32; - let changed = (0..1280 * 720).filter(|&i| (0..3).any(|c| diff(i, c) != 0)).count(); - let sharp = (0..1280 * 719) - .filter(|&i| i % 1280 < 1279) - .filter(|&i| (0..3).any(|c| (diff(i, c) - diff(i + 1, c)).abs() > 40 || (diff(i, c) - diff(i + 1280, c)).abs() > 40)) - .count(); - println!("sous le flou : {changed} px changés par le curseur, {sharp} bords francs"); - assert!(changed > 0, "le curseur ne change rien : il manque, ou le flou l'a effacé"); - assert_eq!(sharp, 0, "{sharp} bords francs sous le flou : le curseur est dessiné par-dessus"); + let mut failures = Vec::new(); + // Tout entier dans la zone, puis à cheval sur son bord. + for x in [0.7f32, 0.48] { + let track = crate::cursor::CursorTrack::new(vec![(0.0, x, 0.45), (9.0, x, 0.45)], vec![], vec![(0.0, "arrow".to_string())]); + let (ra, rb) = (compose_model(&comp, &a, &json(true), &track), compose_model(&comp, &b, &json(true), &track)); + let bare = compose_model(&comp, &a, &json(false), &track); + model_save(&format!("glass-over-blur-{x}"), &ra); + let px = |img: &[u8], i: usize, c: usize| img[i * 4 + c] as i32; + let hidden = (0..1280 * 720).filter(|&i| (0..3).any(|c| (px(&ra, i, c) - px(&rb, i, c)).abs() > 24)).count(); + // Le serti net du curseur : des sauts francs, entre voisins, de ce qu'il change. + let d = |i: usize, c: usize| px(&ra, i, c) - px(&bare, i, c); + let sharp = (0..1280 * 720 - 1) + .filter(|&i| i % 1280 < 1279 && (0..3).any(|c| (d(i, c) - d(i + 1, c)).abs() > 60)) + .count(); + println!("cristal en x = {x} : {sharp} bords francs, {hidden} px qui suivent les rayures cachées"); + if sharp < 50 { + failures.push(format!("x = {x} : {sharp} bords francs, le curseur n'est pas net par-dessus le flou")); + } + if hidden > 0 { + failures.push(format!("x = {x} : {hidden} px montrent les rayures cachées sous le flou")); + } + } + assert!(failures.is_empty(), "{failures:#?}"); } /// Les thèmes cerclés gardent le trait de leur dessin (`design/cursors/`) : sur la diff --git a/crates/compositor/src/compositor_macos.rs b/crates/compositor/src/compositor_macos.rs index 1e59f2de1..ad4bbf28e 100644 --- a/crates/compositor/src/compositor_macos.rs +++ b/crates/compositor/src/compositor_macos.rs @@ -1417,6 +1417,23 @@ impl Compositor { } + /// Fige l'image composée dans `ann_copy` (mip 0), qu'y lisent les flous (mips dérivés, + /// `mips`) et le cristal de Prism Glow. + unsafe fn snapshot_rt(&self, cmd: &metal::CommandBufferRef, mips: bool) { + let blit = cmd.new_blit_command_encoder(); + blit.copy_from_texture( + &self.rt, 0, 0, + metal::MTLOrigin { x: 0, y: 0, z: 0 }, + metal::MTLSize { width: self.render_w as u64, height: self.render_h as u64, depth: 1 }, + &self.ann_copy, 0, 0, + metal::MTLOrigin { x: 0, y: 0, z: 0 }, + ); + if mips { + blit.generate_mipmaps(&self.ann_copy); + } + blit.end_encoding(); + } + /// Annotations : calque le plus haut, ancré sur `s_ann` — le rect écran SANS ZOOM, le /// conteneur que reçoit l'overlay web. Port de `compositor_windows::draw_annotations`. /// @@ -1425,6 +1442,9 @@ impl Compositor { /// grossir les sous-titres sous un zoom (issue #179, puis #397 sur Linux). L'arithmétique /// elle-même vit dans `frame_geometry::annotation_dst_in`, partagée par les trois backends. /// Le flou, lui, se place par `g.privacy_mask` : un masque doit rester sur ce qu'il cache. + /// + /// `privacy` : les flous de confidentialité seuls, dessinés sur le métrage avant le curseur ; + /// sinon toutes les autres annotations, le calque le plus haut. unsafe fn draw_annotations( &self, cmd: &metal::CommandBufferRef, @@ -1432,30 +1452,21 @@ impl Compositor { t: f32, s_ann: [f32; 4], g: &crate::frame_geometry::FrameGeometry, + privacy: bool, ) -> Result<()> { let Some(scene) = scene else { return Ok(()) }; - if scene.annotations.is_empty() { - return Ok(()); - } let (rw, rh) = (self.render_w as f32, self.render_h as f32); let visible = |a: &crate::scene::SceneAnnotation| { - t >= a.start_sec as f32 && t < a.end_sec as f32 + t >= a.start_sec as f32 && t < a.end_sec as f32 && (a.kind == "blur") == privacy }; + if !scene.annotations.iter().any(visible) { + return Ok(()); + } // UNE seule recopie pour toutes les annotations flou de la frame : leur lecture doit // voir l'image composée SANS les flous eux-mêmes, sinon deux zones qui se recouvrent // s'échantillonneraient l'une l'autre selon l'ordre de dessin. - if scene.annotations.iter().any(|a| a.kind == "blur" && visible(a)) { - let blit = cmd.new_blit_command_encoder(); - blit.copy_from_texture( - &self.rt, 0, 0, - metal::MTLOrigin { x: 0, y: 0, z: 0 }, - metal::MTLSize { width: rw as u64, height: rh as u64, depth: 1 }, - &self.ann_copy, 0, 0, - metal::MTLOrigin { x: 0, y: 0, z: 0 }, - ); - // Seul le mip 0 est rempli ; le GPU dérive le reste. - blit.generate_mipmaps(&self.ann_copy); - blit.end_encoding(); + if privacy { + self.snapshot_rt(cmd, true); } let enc = self.begin_pass(cmd, &self.rt, None, &self.pipeline_main)?; @@ -2401,6 +2412,11 @@ impl Compositor { enc.end_encoding(); + // --- flous de confidentialité : sur le métrage, AVANT le curseur (parité Windows). Le + // curseur reste net par-dessus, et le cristal de Prism Glow, qui réfracte l'image + // composée, n'y voit que des pixels déjà floutés. + self.draw_annotations(cmd_buf, scene_ref.as_ref(), g.source_t, g.s_ann, &g, true)?; + // --- curseur --- (parité `compositor_windows.rs`, section « curseur custom ») if let Some(track) = cursor_ref.as_ref() { let plan = crate::frame_geometry::plan_cursor( @@ -2423,6 +2439,12 @@ impl Compositor { .map(|s| s.cursor.cursor_sprites.clone()) .unwrap_or_default(); let kind = plan.cursor_type.as_deref(); + // Le cristal de Prism Glow réfracte l'image telle qu'elle est composée à cet + // instant, flous compris : sa copie en texture(5), une fois pour toutes les copies + // de la traînée (`ann_copy` est libre, les flous l'ont déjà lue). + if plan.glass { + self.snapshot_rt(cmd_buf, false); + } // L'impact des clics (mode 16, sans texture), posé sur l'écran SOUS le curseur. if !plan.impacts.is_empty() { let e = self.begin_pass(cmd_buf, &self.rt, None, &self.pipeline_main)?; @@ -2433,9 +2455,9 @@ impl Compositor { } if plan.taps <= 1 { let e = self.begin_pass(cmd_buf, &self.rt, None, &self.pipeline_main)?; - // Le cristal de Prism Glow (mode 15) réfracte l'enregistrement : texture(0) et (1). - e.set_fragment_texture(0, Some(&sy)); - e.set_fragment_texture(1, Some(&suv)); + if plan.glass { + e.set_fragment_texture(5, Some(&self.ann_copy)); + } self.draw_cur_themed( e, &sprites, @@ -2458,8 +2480,9 @@ impl Compositor { Some(metal::MTLClearColor::new(0.0, 0.0, 0.0, 0.0)), &self.pipeline_add, )?; - e.set_fragment_texture(0, Some(&sy)); - e.set_fragment_texture(1, Some(&suv)); + if plan.glass { + e.set_fragment_texture(5, Some(&self.ann_copy)); + } for k in 0..plan.taps { let f = k as f32 / (plan.taps - 1) as f32; let w = crate::frame_geometry::cursor_tap_weight(k, plan.taps); @@ -2584,8 +2607,9 @@ impl Compositor { // --- annotations : calque le plus haut, ancré sur le rect ÉCRAN SANS ZOOM --- // `s_ann`, pas `s_dst` : le zoom vit dans la boîte depuis l'issue #179, donc `s_dst` // grandit avec lui et emmenait annotations et sous-titres dans le mouvement. Le flou de - // confidentialité est l'exception : il suit le contenu, d'où `&g`. - self.draw_annotations(cmd_buf, scene_ref.as_ref(), g.source_t, g.s_ann, &g)?; + // confidentialité est l'exception : il suit le contenu, d'où `&g` ; il est passé avant le + // curseur (plus haut). + self.draw_annotations(cmd_buf, scene_ref.as_ref(), g.source_t, g.s_ann, &g, false)?; // Ni miroir RGBA ni attente ici : le miroir ne sert qu'à `readback_direct` (la // preview), et l'export ne lit jamais le RGBA — le blit pleine résolution était payé diff --git a/crates/compositor/src/compositor_windows.rs b/crates/compositor/src/compositor_windows.rs index 360f307ba..c99a0ac82 100644 --- a/crates/compositor/src/compositor_windows.rs +++ b/crates/compositor/src/compositor_windows.rs @@ -2138,6 +2138,11 @@ impl Compositor { self.ctx.PSSetShaderResources(5, Some(&[None])); } + // --- flous de confidentialité : sur le métrage, AVANT le curseur. Le curseur reste net + // par-dessus, et le cristal de Prism Glow, qui réfracte l'image composée, n'y voit que des + // pixels déjà floutés. Les autres annotations restent le calque le plus haut (plus bas). + self.draw_annotations(scene_ref.as_ref(), source_t, s_ann, &g, true); + // --- curseur custom : suit le mapping src/dst (zoom+layout), click bounce, // et flou de mouvement (parité `compositor_macos.rs` et `compositor_linux.rs`) --- if let Some(track) = cursor_ref.as_ref() { @@ -2161,10 +2166,13 @@ impl Compositor { .map(|s| s.cursor.cursor_sprites.clone()) .unwrap_or_default(); let cursor_type = plan.cursor_type.as_deref(); - // L'enregistrement en t0/t1 : le cristal de Prism Glow (mode 15) le lit sous le - // curseur pour le réfracter. Les plans y sont déjà depuis le dessin de l'écran ; on - // ne compte pas là-dessus. - self.ctx.PSSetShaderResources(0, Some(&[Some(sy.clone()), Some(suv.clone())])); + // Le cristal de Prism Glow réfracte l'image telle qu'elle est composée à cet + // instant, flous compris : sa copie en t5, une fois pour toutes les copies de la + // traînée (`ann_copy` est libre, les flous l'ont déjà lue). + if plan.glass { + self.ctx.CopySubresourceRegion(&self.ann_copy, 0, 0, 0, 0, &self.rt, 0, None); + self.ctx.PSSetShaderResources(5, Some(&[Some(self.ann_copy_srv.clone())])); + } // L'impact des clics (mode 16, sans texture), posé sur l'écran SOUS le curseur. for cb in &plan.impacts { self.draw_solid(cb); @@ -2220,6 +2228,9 @@ impl Compositor { // le dessin de la webcam qui suit juste après. self.bind_compose_state(); } + if plan.glass { + self.ctx.PSSetShaderResources(5, Some(&[None])); + } } } @@ -2338,12 +2349,13 @@ impl Compositor { // la transform, donc les annotations restent en place pendant que le contenu zoome dessous. // Ce fut `s_dst` tant que le zoom vivait dans la coupe source ; depuis l'issue #179 il vit // dans la BOÎTE, et `s_dst` emmenait annotations et sous-titres avec lui. - // Exception : le flou de confidentialité suit le contenu (`FrameGeometry::privacy_mask`), - // d'où la géométrie entière passée en plus de `s_ann`. + // Les flous de confidentialité, eux, sont passés avant le curseur (plus haut) : ils suivent + // le contenu (`FrameGeometry::privacy_mask`), d'où la géométrie entière passée en plus de + // `s_ann`. // `source_t`, la même base de temps que les zoom/speed regions : le temps SOURCE du clip, // pas le compteur de frames. C'est ce qui garde une annotation alignée sur l'image quand // une speed region répète ou saute des frames. - self.draw_annotations(scene_ref.as_ref(), source_t, s_ann, &g); + self.draw_annotations(scene_ref.as_ref(), source_t, s_ann, &g, false); Ok(()) } @@ -2355,19 +2367,23 @@ impl Compositor { /// sous-titres sous un zoom (issue #179, puis #397 sur Linux). L'arithmétique elle-même vit /// dans `frame_geometry::annotation_dst_in`, partagée par les trois backends. Le flou, lui, /// se place par `g.privacy_mask` : un masque doit rester sur ce qu'il cache. + /// + /// `privacy` : les flous de confidentialité seuls, dessinés sur le métrage avant le curseur ; + /// sinon toutes les autres annotations, le calque le plus haut. unsafe fn draw_annotations( &self, scene: Option<&Scene>, t: f32, s_ann: [f32; 4], g: &crate::frame_geometry::FrameGeometry, + privacy: bool, ) { let Some(scene) = scene else { return }; if scene.annotations.is_empty() { return; } let visible = |a: &crate::scene::SceneAnnotation| { - t >= a.start_sec as f32 && t < a.end_sec as f32 + t >= a.start_sec as f32 && t < a.end_sec as f32 && (a.kind == "blur") == privacy }; // Une seule recopie du render target pour TOUTES les annotations flou de la frame — leur // lecture doit voir l'image composée sans les flous eux-mêmes, sinon deux zones qui se diff --git a/crates/compositor/src/frame_geometry.rs b/crates/compositor/src/frame_geometry.rs index 4b872ce12..4aee32a9d 100644 --- a/crates/compositor/src/frame_geometry.rs +++ b/crates/compositor/src/frame_geometry.rs @@ -1066,9 +1066,6 @@ pub enum CursorPlacement { screen_px: [f32; 2], /// Taille de la cible de rendu en px, pour repasser des px aux 0..1 de la sortie. render_px: [f32; 2], - /// La coupe que montre le plan, en uv de la TEXTURE écran (`FrameGeometry::cut`) : de - /// quoi lire l'enregistrement sous le curseur (le cristal de Prism Glow le réfracte). - screen_uv: [f32; 4], }, } impl CursorPlacement { @@ -1083,13 +1080,12 @@ impl CursorPlacement { pub(crate) fn lerp(self, other: CursorPlacement, f: f32) -> CursorPlacement { match (self, other) { ( - CursorPlacement::Tilted { plane_pt: a, quad, center_px, screen_px, render_px, screen_uv }, + CursorPlacement::Tilted { plane_pt: a, quad, center_px, screen_px, render_px }, CursorPlacement::Tilted { plane_pt: b, quad: quad_b, center_px: center_b, screen_px: screen_b, - screen_uv: uv_b, .. }, ) => CursorPlacement::Tilted { @@ -1115,7 +1111,6 @@ impl CursorPlacement { center_px: [lerp(center_px[0], center_b[0], f), lerp(center_px[1], center_b[1], f)], screen_px: [lerp(screen_px[0], screen_b[0], f), lerp(screen_px[1], screen_b[1], f)], render_px, - screen_uv: lerp4(screen_uv, uv_b, f), }, (a, b) => { let (p, q) = (a.upright_center(), b.upright_center()); @@ -3431,6 +3426,10 @@ pub struct CursorPlan { /// L'impact des clics récents sur l'écran (mode 16), à dessiner SOUS le curseur. Vide sans /// curseur modélisé, sans clic récent ou à `clickBounce` nul. pub impacts: Vec, + /// Le curseur modélisé est le cristal de Prism Glow : il réfracte l'image déjà composée, le + /// métrage et ses flous de confidentialité, que le backend lui copie juste avant de le + /// dessiner. Seuls des pixels floutés passent ainsi à travers le verre. + pub glass: bool, } impl CursorPlan { @@ -3575,7 +3574,6 @@ pub fn plan_cursor(g: &FrameGeometry, input: &CursorPlanInput) -> Option Option CursorPlacement::Upright { @@ -3734,6 +3731,7 @@ pub fn plan_cursor(g: &FrameGeometry, input: &CursorPlanInput) -> Option Option Option { let view = ModelView::new(placement, size_px, pose, shape)?; - let CursorPlacement::Tilted { render_px: [rw, rh], screen_uv, .. } = placement else { return None }; + let CursorPlacement::Tilted { render_px: [rw, rh], .. } = placement else { return None }; let [x0, y0, x1, y1] = view.footprint()?; // Bords entiers : `local` vaut alors k + 0,5 au centre des pixels, comme le rastériseur. let (x0, y0, x1, y1) = (x0.floor(), y0.floor(), x1.ceil(), y1.ceil()); @@ -4202,7 +4199,6 @@ pub fn cursor_model_cb( mb: [view.half[0], view.half[1], view.offset[0], view.offset[1]], radius_px: shape.size[0] / shape.size[1], trail_a: [shape.sculpt as f32, shape.thick, shape.max_height, 0.0], - trail_b: screen_uv, ..Default::default() }) } @@ -4278,7 +4274,7 @@ fn cursor_impact_cb( impact: Impact, alpha: f32, ) -> Option { - let CursorPlacement::Tilted { plane_pt, quad, center_px, screen_px, render_px: [rw, rh], .. } = + let CursorPlacement::Tilted { plane_pt, quad, center_px, screen_px, render_px: [rw, rh] } = placement else { return None; @@ -7651,7 +7647,7 @@ mod tests { a: f32, clip: [f32; 4], ) -> LayerCB { - let CursorPlacement::Tilted { plane_pt, quad, center_px, screen_px, render_px, .. } = p + let CursorPlacement::Tilted { plane_pt, quad, center_px, screen_px, render_px } = p else { unreachable!() }; diff --git a/crates/compositor/src/sculpt.rs b/crates/compositor/src/sculpt.rs index 5d3cdba2f..89fa9a277 100644 --- a/crates/compositor/src/sculpt.rs +++ b/crates/compositor/src/sculpt.rs @@ -62,6 +62,11 @@ fn model_box(theme: usize, arrow: bool) -> [f32; 3] { } } +/// Le curseur sculpté que nomme la scène est de verre (Prism Glow) : il réfracte ce qui est dessous. +pub fn refracts(name: &str) -> bool { + sculpted_shape(name).is_some_and(|s| THEMES[((s.sculpt - 1) / 2) as usize] == "prism-glow") +} + /// Le curseur sculpté que nomme la scène (`"/<état>"`, cf. `resolveCursorSprites`), sous /// la forme que le mode 15 attend. `None` pour un nom inconnu : l'appelant extrude le sprite. pub fn sculpted_shape(name: &str) -> Option { @@ -225,6 +230,15 @@ mod tests { } } + /// Seul Prism Glow est de verre, flèche et main. + #[test] + fn only_prism_glow_refracts() { + for name in NAMES { + assert_eq!(refracts(name), name.starts_with("prism-glow/"), "{name}"); + } + assert!(!refracts("prism-glow/text")); + } + /// Le maillage de Prism Glow tient dans sa boîte (le shader n'y lance de rayons que là), son /// dessous est à l'épaisseur annoncée (la garde au sol en dépend) et son sommet à la hauteur. #[test] diff --git a/crates/compositor/src/shaders.hlsl b/crates/compositor/src/shaders.hlsl index 96c9a4971..37614e23d 100644 --- a/crates/compositor/src/shaders.hlsl +++ b/crates/compositor/src/shaders.hlsl @@ -14,7 +14,7 @@ cbuffer Layer : register(b0) float4 dst_prev; // dst à la frame précédente ; modes 13 et 15 : rect de clip ; mode 17 : angle du socle, rayon et recouvrement de l'ouverture, pénombre de l'ombre float4 mb; // mb.x = nombre de taps de motion blur (1 = désactivé) ; modes 15 et 17 : demi-taille du plan, translation ; mode 18 : .z = 1 si le plan est incliné, .w = 1 si son warp est projectif float4 trail_a; // mode 8 : coins TL, TR du plan à la frame précédente (px locaux, comme fx) ; mode 18 incliné : en fractions de sortie - float4 trail_b; // mode 8 : coins BR, BL du plan à la frame précédente (comme src_prev) ; mode 18 incliné : en fractions de sortie ; mode 15 : la coupe du plan en uv de l'écran (u0, v0, u1, v1), que réfracte le cristal de Prism Glow + float4 trail_b; // mode 8 : coins BR, BL du plan à la frame précédente (comme src_prev) ; mode 18 incliné : en fractions de sortie float4 trail_mb; // mode 8 : x = taps, y = force du flou de mouvement (ceux du mode 0) ; mode 18 incliné : le `mb` du mode 8 (profondeur de champ), et `color.xy` sa lampe ; 0 ailleurs }; // Mode 15 (curseur modélisé) : le détail des emplacements est dans `frame_geometry.rs`, en tête @@ -82,11 +82,6 @@ float3 sample_yuv(float2 uv) } // La même, au niveau 0 : lisible dans une boucle ou une branche (le cristal du mode 15). -float3 sample_yuv_level(float2 uv) -{ - return yuv709_limited(texY.SampleLevel(samp, uv, 0.0), texUV.SampleLevel(samp, uv, 0.0)); -} - // SDF segment à bouts ronds — la primitive des flèches d'annotation, dont les tracés SVG sont // trois segments `stroke-linecap="round"` (cf. ArrowSvgs.tsx). float sd_segment(float2 p, float2 a, float2 b) @@ -1054,8 +1049,9 @@ float s_pixel_outline(float2 p, int shape) // dessus du serti en z = 0). Le serti marine est la silhouette de l'art extrudée sous z = 0, un // champ de distance comme les autres sculptés : il donne la couverture, l'ombre portée et l'ombre // de contact. Le cristal est lancé de rayons, triangles rangés par boîtes : réfraction par canal -// (dispersion), réflexions totales internes, sortie par son fond plat vers l'enregistrement, lu -// sous le curseur (t0/t1, `trail_b`). Tables : miroir de `prism_mesh.rs` ; un triangle = quatre +// (dispersion), réflexions totales internes, sortie par son fond plat vers l'image composée sous +// le curseur, flous de confidentialité compris (sa copie en t5). Tables : miroir de `prism_mesh.rs` ; +// un triangle = quatre // float4 (v0, drapeaux), (e1, r), (e2, g), (normale sortante, b) ; drapeaux = paroi (1) + 2 × // arêtes réelles (bit k : l'arête opposée au sommet k) ; une boîte = deux float4 (coin bas, // premier triangle), (coin haut, nombre de triangles). @@ -2349,8 +2345,21 @@ float prism_edge_px(float3 p, int h, float px) return d / px; } -// L'enregistrement là où le rayon (q, d) du modèle retombe sur le plan : la vidéo, lue par la coupe -// du plan (`trail_b`, uv de la texture), en linéaire. Hors du plan, l'écran sombre du studio. +// La rotation inverse de `world_to_plane` : du repère du plan à celui de la caméra. +float3 plane_to_world(float3 v, ModelFrame f) +{ + float x = v.x * f.c.z - v.y * f.s.z; + float y = v.x * f.s.z + v.y * f.c.z; + float z = -x * f.s.y + v.z * f.c.y; + return float3(x * f.c.y + v.z * f.s.y, y * f.c.x - z * f.s.x, y * f.s.x + z * f.c.x); +} + +// Ce que montre l'image là où le rayon (q, d) du modèle retombe sur le plan : la copie de l'image +// composée faite juste avant le curseur (t5), flous de confidentialité compris, lue au pixel de +// sortie de ce point, en linéaire. Seuls des pixels déjà floutés passent donc à travers le verre. +// L'œil est en (-mb.zw, persp) du repère caméra, le rayon du pixel `local` y va vers +// (local + src.xy, -persp) : on remonte de ce point du plan à son `local`, puis à son uv de sortie. +// Hors de l'image, l'écran sombre du studio. float3 prism_screen(float3 q, float3 d, float3 nz, float hz, float3 tip, float unit, ModelFrame f) { float denom = dot(d, nz); @@ -2359,12 +2368,18 @@ float3 prism_screen(float3 q, float3 d, float3 nz, float hz, float3 tip, float u return SCULPT_SCREEN * 0.2; } float3 g = q + d * ((hz - dot(q, nz)) / denom); - float2 fp = (tip + unit * model_to_plane(g, f)).xy / (2.0 * mb.xy) + 0.5; - if (any(fp < 0.0) || any(fp > 1.0)) + float3 w = plane_to_world(tip + unit * model_to_plane(g, f), f); + float k = 1.0 - w.z / src.z; + if (k < 1e-3) + { + return SCULPT_SCREEN * 0.2; + } + float2 uv = dst.xy + ((w.xy + mb.zw) / k - src.xy) / quad_px * dst.zw; + if (any(uv < 0.0) || any(uv > 1.0)) { return SCULPT_SCREEN * 0.2; } - return pow(sample_yuv_level(lerp(trail_b.xy, trail_b.zw, fp)), 2.2); + return pow(texDof.SampleLevel(samp, uv, 0.0).rgb, 2.2); } // La couleur (sRGB, comme `model_shade`) du cristal au point `p` de la facette `h`, vu le long de diff --git a/crates/compositor/src/shaders.metal b/crates/compositor/src/shaders.metal index 34f0be51d..6456c3ff7 100644 --- a/crates/compositor/src/shaders.metal +++ b/crates/compositor/src/shaders.metal @@ -58,7 +58,7 @@ struct Layer float4 dst_prev; // dst à la frame précédente ; modes 13 et 15 : rect de clip ; mode 17 : angle du socle, rayon et recouvrement de l'ouverture, pénombre de l'ombre float4 mb; // mb.x = nombre de taps de motion blur (1 = désactivé) ; modes 15 et 17 : demi-taille du plan, translation ; mode 18 : .z = 1 si le plan est incliné, .w = 1 si son warp est projectif float4 trail_a; // mode 8 : coins TL, TR du plan à la frame précédente (px locaux, comme fx) ; mode 18 incliné : en fractions de sortie - float4 trail_b; // mode 8 : coins BR, BL du plan à la frame précédente (comme src_prev) ; mode 18 incliné : en fractions de sortie ; mode 15 : la coupe du plan en uv de l'écran (u0, v0, u1, v1), que réfracte le cristal de Prism Glow + float4 trail_b; // mode 8 : coins BR, BL du plan à la frame précédente (comme src_prev) ; mode 18 incliné : en fractions de sortie float4 trail_mb; // mode 8 : x = taps, y = force du flou de mouvement (ceux du mode 0) ; 0 ailleurs }; // Mode 15 (curseur modélisé) : le détail des emplacements est dans `frame_geometry.rs`, en tête @@ -147,14 +147,6 @@ inline float3 sample_yuv(float2 uv, return yuv709_limited(y, cbcr); } -// `sample_yuv` au niveau 0, pour les boucles à sortie anticipée (pas de dérivées). -inline float3 sample_yuv_level(float2 uv, - texture2d texY, - texture2d texUV) -{ - return yuv709_limited(texY.sample(samp, uv, level(0.0)).r, texUV.sample(samp, uv, level(0.0)).rg); -} - // SDF segment à bouts ronds — la primitive des flèches d'annotation. inline float sd_segment(float2 p, float2 a, float2 b) { @@ -553,7 +545,8 @@ inline float3 image_motion(float2 q, float time, float motion, float aspect) // ============ Curseur MODÉLISÉ (mode 15) ============ // Port ligne pour ligne de `cursor_model` (HLSL), dont les commentaires font foi ; seules // différences : `layer` et les textures arrivent en paramètres (le sprite en texture(2), son -// champ R16F en texture(4), l'enregistrement en texture(0) et (1) pour le cristal de Prism Glow), +// champ R16F en texture(4), la copie de l'image composée en texture(5) pour le cristal de Prism +// Glow), // `lerp` s'écrit `mix`, `SampleLevel` s'écrit `sample(…, level(0.0))`, un `out` s'écrit `thread &`, // et `pow` veut un exposant du type de sa base. // Constantes : miroir exact de `frame_geometry.rs` (MODEL_*) et de `sculpt.rs` (SCULPT_*). @@ -2200,9 +2193,16 @@ static float prism_edge_px(float3 p, int h, float px) return d / px; } +static float3 plane_to_world(float3 v, ModelFrame f) +{ + float x = v.x * f.c.z - v.y * f.s.z; + float y = v.x * f.s.z + v.y * f.c.z; + float z = -x * f.s.y + v.z * f.c.y; + return float3(x * f.c.y + v.z * f.s.y, y * f.c.x - z * f.s.x, y * f.s.x + z * f.c.x); +} + static float3 prism_screen(float3 q, float3 d, float3 nz, float hz, float3 tip, float unit, ModelFrame f, - constant Layer &layer, texture2d texY, - texture2d texUV) + constant Layer &layer, texture2d texFrame) { float denom = dot(d, nz); if (denom > -1e-4) @@ -2210,17 +2210,23 @@ static float3 prism_screen(float3 q, float3 d, float3 nz, float hz, float3 tip, return SCULPT_SCREEN * 0.2; } float3 g = q + d * ((hz - dot(q, nz)) / denom); - float2 fp = (tip + unit * model_to_plane(g, f)).xy / (2.0 * layer.mb.xy) + 0.5; - if (any(fp < 0.0) || any(fp > 1.0)) + float3 w = plane_to_world(tip + unit * model_to_plane(g, f), f); + float k = 1.0 - w.z / layer.src.z; + if (k < 1e-3) + { + return SCULPT_SCREEN * 0.2; + } + float2 uv = layer.dst.xy + ((w.xy + layer.mb.zw) / k - layer.src.xy) / layer.quad_px * layer.dst.zw; + if (any(uv < 0.0) || any(uv > 1.0)) { return SCULPT_SCREEN * 0.2; } - return pow(sample_yuv_level(mix(layer.trail_b.xy, layer.trail_b.zw, fp), texY, texUV), float3(2.2)); + return pow(texFrame.sample(samp, uv, level(0.0)).rgb, float3(2.2)); } static float3 prism_shade(float3 p, float3 n, float3 rd, int h, float px, float3 l, float3 fill, float3 nz, float hz, float3 tip, float unit, ModelFrame f, constant Layer &layer, - texture2d texY, texture2d texUV) + texture2d texFrame) { int shape = prism_shape(layer); float4 r3 = PRISM_TRIS[4 * h + 3]; @@ -2251,7 +2257,7 @@ static float3 prism_shade(float3 p, float3 n, float3 rd, int h, float px, float3 d = reflect(d, float3(0.0, 0.0, 1.0)); continue; } - acc += thr * dot(prism_screen(qb, d2, nz, hz, tip, unit, f, layer, texY, texUV), mask); + acc += thr * dot(prism_screen(qb, d2, nz, hz, tip, unit, f, layer, texFrame), mask); thr = 0.0; break; } @@ -2306,8 +2312,7 @@ inline float3 model_tetra(int k) static float4 cursor_model(float2 local, constant Layer &layer, texture2d texSdf, texture2d texImg, - texture2d texY, - texture2d texUV) + texture2d texFrame) { ModelFrame f; f.c = cos(layer.fx.xyz); @@ -2395,7 +2400,7 @@ static float4 cursor_model(float2 local, constant Layer &layer, float3 c; if (mat > PRISM_MAT - 0.5) { - c = prism_shade(q, n, ray, hc, t_best / dlen, l, fill, nz, hz, tip, unit, f, layer, texY, texUV); + c = prism_shade(q, n, ray, hc, t_best / dlen, l, fill, nz, hz, tip, unit, f, layer, texFrame); } else { @@ -3305,7 +3310,7 @@ fragment float4 ps_main(VSOut i [[stage_in]], { return float4(0.0, 0.0, 0.0, 0.0); } - return cursor_model(i.local, layer, texSdf, texImg, texY, texUV); + return cursor_model(i.local, layer, texSdf, texImg, texDof); } // mode 14 : CADRE DE FENÊTRE autour de l'écran, dessiné SOUS lui. Cf. commentaires HLSL. diff --git a/crates/compositor/src/vk_shaders/layer.wgsl b/crates/compositor/src/vk_shaders/layer.wgsl index 0f4d4e452..96532b6ba 100644 --- a/crates/compositor/src/vk_shaders/layer.wgsl +++ b/crates/compositor/src/vk_shaders/layer.wgsl @@ -29,12 +29,12 @@ struct Layer { dst_prev: vec4, // mode 8 : .xy = taille du plan en px AVANT projection (le rayon y vit), .z = 1 si coins hauts carres (sous un cadre), .w = 1 si warp projectif ; mode 14 : .xy = taille du plan du cadre, .z = hauteur de la barre, .w = epaisseur du filet (px du plan) ; modes 13 et 15 : rect de clip ; mode 9 : barbe 2 ; mode 10 incliné : coins TL,TR du masque ; mode 17 : (angle du socle depuis le plan en rad, rayon de l'ouverture et recouvrement de la lunette en unites du modele, penombre de l'ombre ou 0) mb: vec4, // mode 8 : [gx, gy, z_focus, k], profondeur du plan et flou (texels source) par px d'ecart, k = 0 coupe ; mode 0 : .x taps, .y force du flou, .w = 1 si coins hauts carres (sous un cadre) ; mode 5 : mb.x = aspect w/h de la sortie (fond anime) ; mode 12 : mb.y = spread de la pénombre en px ; mode 9 : mb.y = demi-épaisseur du trait en px ; mode 10 : mb.z = 1 si masque incliné, mb.w = 1 si son warp est projectif ; mode 13 : mb.x = 1 si warp projectif ; mode 14 : couleur du filet (alpha droit) ; modes 15 et 17 : .xy = demi-taille du plan dans son repere (px pour le 15, unites pour le 17), .zw = translation du plan (repere camera, px) ; mode 18 : .z = 1 si le plan est incline, .w = 1 si son warp est projectif trail_a: vec4, // mode 8 : coins TL, TR du plan a la frame precedente (px locaux, comme fx) ; mode 18 incline : en fractions de sortie - trail_b: vec4, // mode 8 : coins BR, BL du plan a la frame precedente (comme src_prev) ; mode 18 incline : en fractions de sortie ; mode 15 : la coupe du plan en uv de l'ecran (u0, v0, u1, v1), que refracte le cristal de Prism Glow + trail_b: vec4, // mode 8 : coins BR, BL du plan a la frame precedente (comme src_prev) ; mode 18 incline : en fractions de sortie trail_mb: vec4, // mode 8 : x = taps, y = force du flou de mouvement (ceux du mode 0) ; mode 18 incline : le `mb` du mode 8 (profondeur de champ), et `color.xy` sa lampe ; 0 ailleurs } @group(0) @binding(0) var layer: Layer; -@group(0) @binding(1) var texY: texture_2d; // R8Unorm, sample .r ; modes 7 et 13 : le sprite RGBA +@group(0) @binding(1) var texY: texture_2d; // R8Unorm, sample .r ; modes 7 et 13 : le sprite RGBA ; mode 15 : la copie de l'image composee @group(0) @binding(2) var texU: texture_2d; // R8Unorm, sample .r @group(0) @binding(3) var samp: sampler; // Masque de segmentation du sujet webcam, R8. Une vue 1x1 est liee quand aucun masque @@ -573,8 +573,8 @@ fn blur_webcam_bg(uv: vec2, intensity: f32, qpx: vec2, local_px: vec2< // propres et occlusion, et qui porte une ombre douce et une ombre de contact sur le plan de // l'ecran. Constantes : miroir exact de `frame_geometry.rs` (MODEL_*) et de `sculpt.rs` // (SCULPT_*), emplacements du cbuffer : `cursor_model_cb`. -// Textures : l'enregistrement reste aux bindings 1, 2 et 5 (le cristal de Prism Glow le lit sous -// le curseur) ; le sprite RGBA (alpha droit) est au binding 6 (`texDof`), son champ R16F au +// Textures : la copie de l'image composee au binding 1 (`texY`), que refracte le cristal de Prism +// Glow ; le sprite RGBA (alpha droit) est au binding 6 (`texDof`), son champ R16F au // binding 4 (`texMask`), sur le meme rect ; `color.rg` = coin du sprite dans le repere du modele, // `sprite_size()` = sa taille (w/h dans `radius_px`), `color.b` = l'ecrasement au clic. const MODEL_BEVEL: f32 = 0.045; @@ -1363,17 +1363,31 @@ fn prism_edge_px(p: vec3, h: i32, px: f32) -> f32 { return d / px; } +// La rotation inverse de `world_to_plane` : du repere du plan a celui de la camera. +fn plane_to_world(v: vec3, f: ModelFrame) -> vec3 { + let x = v.x * f.c.z - v.y * f.s.z; + let y = v.x * f.s.z + v.y * f.c.z; + let z = -x * f.s.y + v.z * f.c.y; + return vec3(x * f.c.y + v.z * f.s.y, y * f.c.x - z * f.s.x, y * f.s.x + z * f.c.x); +} + +// L'image composee la ou le rayon retombe sur le plan (cf. HLSL) : sa copie au binding 1. fn prism_screen(q: vec3, d: vec3, nz: vec3, hz: f32, tip: vec3, unit: f32, f: ModelFrame) -> vec3 { let denom = dot(d, nz); if denom > -1e-4 { return SCULPT_SCREEN * 0.2; } let g = q + d * ((hz - dot(q, nz)) / denom); - let fp = (tip + unit * model_to_plane(g, f)).xy / (2.0 * layer.mb.xy) + vec2(0.5); - if any(fp < vec2(0.0)) || any(fp > vec2(1.0)) { + let w = plane_to_world(tip + unit * model_to_plane(g, f), f); + let k = 1.0 - w.z / layer.src.z; + if k < 1e-3 { + return SCULPT_SCREEN * 0.2; + } + let uv = layer.dst.xy + ((w.xy + layer.mb.zw) / k - layer.src.xy) / layer.quad_px * layer.dst.zw; + if any(uv < vec2(0.0)) || any(uv > vec2(1.0)) { return SCULPT_SCREEN * 0.2; } - return pow(sample_yuv_level(mix(layer.trail_b.xy, layer.trail_b.zw, fp)), vec3(2.2)); + return pow(textureSampleLevel(texY, samp, uv, 0.0).rgb, vec3(2.2)); } fn prism_shade(p: vec3, n: vec3, rd: vec3, h: i32, px: f32, l: vec3, fill: vec3, diff --git a/design/cursors/3d-direction.md b/design/cursors/3d-direction.md index a18cb1ee6..1205d3174 100644 --- a/design/cursors/3d-direction.md +++ b/design/cursors/3d-direction.md @@ -14,7 +14,7 @@ their built-in art and the compositor's beveled extrusion. | Theme | Arrow model | Hand model | | --- | --- | --- | | Studio Ink | Black rim, a raised ivory band, a recessed satin black field. | Ivory glove cushion in a black rim, black ridges between the fingers. | -| Prism Glow | A glass crystal cut along the facets of its drawing, set in a thin navy rim. The recording shows through it, refracted with a slight dispersion; each facet glows faintly in its drawn colour, and a light line marks its folds. | The same crystal: a facet for each plane of the drawn hand, the fingers and thumb included. | +| Prism Glow | A glass crystal cut along the facets of its drawing, set in a thin navy rim. The picture under it shows through, refracted with a slight dispersion; each facet glows faintly in its drawn colour, and a light line marks its folds. | The same crystal: a facet for each plane of the drawn hand, the fingers and thumb included. | | Pop Coral | Cut paper: flat-topped sheets, no rounded bead or gloss. A coral sheet on a navy sheet, a yellow sheet behind them offset down-left, two yellow click dashes. | A yellow sheet on a navy sheet, the navy showing between the fingers, a coral sheet behind offset down-left, two coral click dashes. | | Pixel Candy | Simplified pixel art, one cube per pixel: a plum outline, a pink body with a pale highlight down the lit edge and a darker shade along the outline. The 3D model and the 2D sprite come from the same grid. | The same, a pixel hand: the index up, three knuckles apart, the thumb. | | Star Sprout | Glossy mint cushion framed by the navy outline of its drawing, carrying a star with a face and two leaves. | Ivory glove cushion in the same navy frame, navy ridges between the fingers, a mint cuff and the same star. | @@ -38,7 +38,10 @@ writes the mesh into `crates/compositor/src/prism_mesh.rs` and the three shaders The compositor ray-traces the crystal, its triangles grouped in bounding boxes that a ray skips when it misses them: refraction per colour channel, total internal reflection, and an exit through its flat base onto the -recording under the cursor. The rim is its silhouette extruded, a distance field +picture under the cursor. That picture is a copy of the composed frame taken just +before the cursor is drawn: the recording, then its privacy blurs, then the cursor, +sharp on top, so only blurred pixels show through the glass. The rim is its +silhouette extruded, a distance field like the other models: it gives the coverage and the shadows. All models share one close lamp: a gradient and a highlight even on flat faces, diff --git a/design/cursors/requirements.md b/design/cursors/requirements.md index 1d191897b..1cf03d987 100644 --- a/design/cursors/requirements.md +++ b/design/cursors/requirements.md @@ -29,7 +29,7 @@ their 3D models. states that do not have a dedicated theme model. Each pack adds style-specific volume: gloves with separate fingers, beveled voxels, flat cut-paper sheets, cushions set in the rim of their drawn outline, and a - faceted glass crystal that refracts the recording. + faceted glass crystal that refracts the picture under it. - Model the arrow and hand separately for every sculpted theme. Do not apply one contour, material, or roundness rule to all designs. - Preserve the cursor hotspot through hover, tilt, yaw, click, and size changes. @@ -42,7 +42,7 @@ their 3D models. | Theme | Arrow | Hand | | --- | --- | --- | | Studio Ink | Black rim, raised ivory band, recessed satin black field. | Ivory glove cushion; the black outline stays as a physical rim, with black ridges between the fingers. | -| Prism Glow | A glass crystal cut along the drawing's facets, in a thin navy rim; the recording shows through, refracted with a slight dispersion; the facets glow faintly in their drawn colours. Modelled from the 2D drawing alone, keeping its polygonal simplicity. | The same, a crystal hand. | +| Prism Glow | A glass crystal cut along the drawing's facets, in a thin navy rim; the picture under it shows through, refracted with a slight dispersion, privacy blurs included; the facets glow faintly in their drawn colours. Modelled from the 2D drawing alone, keeping its polygonal simplicity. | The same, a crystal hand. | | Pop Coral | Cut paper: a coral sheet on a navy sheet, flat tops, matte; the yellow offset becomes a flat sheet behind; the click dashes become flat pieces. | A yellow sheet on a navy sheet, flat tops, matte; the coral offset becomes a flat sheet behind; the coral click dashes become flat pieces. | | Pixel Candy | Simplified pixel art that reads at 20 px as at full size; the 3D model is one cube per pixel of the same grid as the sprite. | A pixel hand from the same kind of grid: index, three knuckles, thumb. | | Star Sprout | Puffy mint cushion; the navy outline stays as a physical rim (a navy tray and a rounded bead) around it, the star and its leaves in front, each in its own navy rim. | Puffy ivory glove in the same navy rim, with navy ridges between the fingers; separate mint cuff framed in navy; the star and leaves in front, as in `star-sprout/3d-reference.png`. | diff --git a/docs/3d-effects-v2.md b/docs/3d-effects-v2.md index 6308bed84..0f7faf447 100644 --- a/docs/3d-effects-v2.md +++ b/docs/3d-effects-v2.md @@ -294,15 +294,16 @@ leur dessin : un plateau et un jonc de la couleur du trait, un coussin de couleu (`s_paper`). Prism Glow est à part : un **cristal en maillage**, ses facettes tracées sur son seul dessin 2D (`design/cursors/prism-glow/model/`), serti dans sa silhouette extrudée, marine. Le shader le lance de rayons, triangles rangés par boîtes englobantes (un rayon saute les -boîtes qu'il rate) : réfraction par canal (une légère -dispersion), réflexions totales internes, et sortie par son fond plat sur l'enregistrement sous -le curseur ; chaque facette luit un peu de sa couleur du dessin, ses plis d'un liseré clair. +boîtes qu'il rate) : réfraction par canal (une légère dispersion), réflexions totales internes, +et sortie par son fond plat sur l'image sous le curseur ; chaque facette luit un peu de sa +couleur du dessin, ses plis d'un liseré clair. Cette image est une copie de la frame composée +prise juste avant le curseur : le métrage, puis ses flous de confidentialité, puis le curseur, +net par-dessus. Seuls des pixels déjà floutés passent donc à travers le verre. `export_compositor.py` écrit le maillage dans `prism_mesh.rs` et dans les trois shaders. Le PNG du thème reste l'art en 2D. En 3D, la scène nomme le modèle ; `sculpt.rs` en tient la boîte, qui pose le hotspot (pointe de la flèche, bout de l'index), règle la garde au sol et borne la boîte -de dessin. Emplacements du cbuffer : `trail_a` = [modèle, épaisseur sous z = 0, hauteur -au-dessus, 0], `trail_b` = la coupe du plan en uv de l'écran, que le cristal réfracte -(`cursor_model_cb`). +de dessin. Emplacement du cbuffer : `trail_a` = [modèle, épaisseur sous z = 0, hauteur +au-dessus, 0] (`cursor_model_cb`). Pixel Candy est un pixel art dessiné une fois, en grille, dans `scripts/generate-pixel-candy-voxels.mjs` : le script en tire ses PNG 2D et les tables des trois shaders, un cube par pixel plein, tous de même hauteur, colorés comme leur pixel (contour prune,