Skip to content

Commit 784cda1

Browse files
TMHSDigitalclaude
andauthored
fix(examples): redesign car-mirror-symmetry as a smooth-shaded stylized hatchback (#260)
* fix(examples): redesign car-mirror-symmetry as a smooth-shaded stylized hatchback The gallery review flagged a pink streak on the hood, sticker headlamps, a faceted body that read unfinished, and dull windows. The streak was investigated first: a camera ray cast maps it to one authored face (station 4, ring segment 5) on the mirrored half at |x| 0.11-0.38, far from the plane, with every weld check passing. It was not a seam defect but a sliver facet where the old ring's greenhouse points collapsed onto the hood, flat-shaded and facing the key almost head on (n . -key = 0.967). The loft is rebuilt: 52 placed stations x 13-point rings that lerp a hood layout into a cabin layout (no slivers), round arches seated on the wheels, flared arch cladding, a character line, and smooth shading with sharp edges by angle and material set on the authored half (Mirror only in the stack, so every count stays closed-form). Lamps, grille, door mirrors and handles are chrome-bezelled pods seated on the skin by BVH ray cast; glass ramps toward steel blue at grazing angles; wheels carry five recessed spokes. The grille is authored as a half across the plane and welded by the merge, so the part check now takes a per-part weld count (2n - weld, on-plane verts == weld) and falsifies both ways (merge off -> exit 14, end point off the plane -> exit 13). The render path now runs the shared framing and asset-quality gates, remapped to 17 and 18 because 10 and 11 are already check codes here. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> * fix(examples): car-mirror-symmetry asset sheet against the new reference set Adds the car's SELECT row to scripts/asset_sheet.py (body, wheels and the fitted parts; stage excluded) and regenerates the asset sheet with the committed tool against the reference set pinned in CLAUDE.md after #257: collision-hull-proxy, socket-attach-points, vertex-color-ao, wheelbarrow, apothecary-shelf. The old-car panel from the scratch sheet is dropped. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> --------- Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent f300041 commit 784cda1

11 files changed

Lines changed: 981 additions & 432 deletions

File tree

‎README.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,17 +1047,17 @@ smoothed render inverts on sight if the panel binding does.
10471047
</tr>
10481048
<tr>
10491049
<td width="46%" valign="middle">
1050-
<a href="examples/car-mirror-symmetry/"><img src="examples/car-mirror-symmetry/preview.webp" alt="Car mirror symmetry: a red low-poly hatchback on a dark studio floor, light windshield band and white headlamp pair — lofted as one half and completed by the Mirror modifier evaluated through the depsgraph, proving 2n-c counts, exact negated-X partners, a welded watertight centerline, and wheels mirrored about origins on the plane" /></a>
1050+
<a href="examples/car-mirror-symmetry/"><img src="examples/car-mirror-symmetry/preview.webp" alt="Car mirror symmetry: a red stylized hatchback on a dark studio floor, tinted glass, black pillars and arch cladding, spoked wheels, and a matched headlamp and door-mirror pair either side of one centered grille — lofted as one half and completed by the Mirror modifier evaluated through the depsgraph, proving 2n-c counts, exact negated-X partners, a welded watertight centerline, and wheels mirrored about origins on the plane" /></a>
10511051
</td>
10521052
<td valign="middle">
10531053

10541054
### [car-mirror-symmetry](examples/car-mirror-symmetry/)
10551055

10561056
The Mirror + depsgraph contract: the datablock holds only the authored half
1057-
(126/231/106, 28 centerline verts) while the evaluated mesh is the welded
1057+
(676/1289/614, 104 centerline verts) while the evaluated mesh is the welded
10581058
whole — exactly `2n − c` verts, watertight with Euler 2, every vertex paired
1059-
at negated X (deviation 0.0). Wheels and lamps mirror about object origins
1060-
parked **on** the symmetry plane — offset the data, never the object.
1059+
at negated X (deviation 0.0). Wheels, lamps, grille, mirrors and handles mirror
1060+
about object origins parked **on** the symmetry plane — offset the data, never the object.
10611061
Companion to [`depsgraph-export`](examples/depsgraph-export/) (evaluated-vs-
10621062
original) and [`shape-key-blend`](examples/shape-key-blend/) (non-destructive
10631063
evaluation). Break the mirror and the render is literally half a car.
56.4 KB
Loading
3.73 KB
Loading

‎docs/gallery/car-mirror-symmetry/index.html‎

Lines changed: 462 additions & 201 deletions
Large diffs are not rendered by default.
1.91 KB
Loading

‎docs/gallery/index.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -772,11 +772,11 @@ <h2><a href="soccer-ball-goldberg/">soccer-ball-goldberg</a></h2>
772772
</article>
773773
<article class="card" data-tags="modifiers depsgraph">
774774
<a class="card-media" href="car-mirror-symmetry/" tabindex="-1" aria-hidden="true">
775-
<img src="assets/car-mirror-symmetry-hero.webp" alt="A red low-poly hatchback with white headlights, modelled as one mirrored half." loading="lazy" decoding="async" />
775+
<img src="assets/car-mirror-symmetry-hero.webp" alt="A red stylized hatchback in front three-quarter view with tinted glass, black pillars, spoked wheels, and matched headlamps and door mirrors either side of one centered grille." loading="lazy" decoding="async" />
776776
</a>
777777
<div class="card-body">
778778
<h2><a href="car-mirror-symmetry/">car-mirror-symmetry</a></h2>
779-
<p class="teaches">A generic hatchback lofted as one half (14 stations, 9-point rings) and completed by the Mirror modifier, evaluated through the depsgraph. Wheels and lamps mirror about object origins parked on the symmetry plane.</p>
779+
<p class="teaches">A stylized hatchback lofted as one half (52 stations, 13-point rings) and completed by the Mirror modifier, evaluated through the depsgraph. Wheels, lamps, grille, door mirrors and handles mirror about object origins parked on the symmetry plane; the grille is authored as a half and welded on it.</p>
780780
<p class="witnesses"><span class="tag">witnesses</span> Mirror is a modifier, not applied data: the original keeps the authored half while the evaluated mesh is the welded, watertight, exactly symmetric whole.</p>
781781
<a class="card-link" href="car-mirror-symmetry/">View example<span class="sr-only"> car-mirror-symmetry</span> <span aria-hidden="true">&rarr;</span></a>
782782
</div>

‎examples/car-mirror-symmetry/README.md‎

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,84 @@
11
# Car Mirror Symmetry
22

3-
A runnable example that builds a generic hatchback as **one half** — a loft of
4-
14 stations, each a 9-point half-ring from the bottom centerline out to the
5-
roof centerline — and completes it with a **Mirror modifier**, evaluated
3+
A runnable example that builds a stylized hatchback as **one half** — a loft
4+
of 52 stations, each a 13-point half-ring from the bottom centerline out to
5+
the roof centerline — and completes it with a **Mirror modifier**, evaluated
66
through the depsgraph, following
77
[`depsgraph-and-evaluated-data`](../../skills/depsgraph-and-evaluated-data/SKILL.md).
8-
Wheels and lamps are separate objects mirrored the idiomatic way: the object
9-
origin sits **on** the symmetry plane and the mesh data is offset — mirror
10-
mirrors about the object's own origin, so you offset the data, never the
11-
object.
8+
Wheels, headlamps, taillamps, grille, door mirrors and door handles are
9+
separate objects mirrored the idiomatic way: the object origin sits **on**
10+
the symmetry plane and the mesh data is offset — mirror mirrors about the
11+
object's own origin, so you offset the data, never the object. The grille is
12+
the one part authored *across* the plane: a half profile whose end points lie
13+
exactly on `x = 0`, welded into one grille by the merge.
1214

1315
**What it witnesses:** the original datablock keeps only the authored half
1416
while the depsgraph carries the mirrored whole, and both directions are
1517
closed forms:
1618

17-
- **Original holds the half.** Body datablock is exactly 126 verts / 231
18-
edges / 106 faces (14 × 9, loft closed forms), with exactly 28 centerline
19-
verts (probe: modifier applied into data → exit 3, datablock reads
20-
`(224, 434, 212)`).
21-
- **Evaluated is the welded whole.** Exactly `2n − c = 224` verts (probe:
22-
`use_mirror_merge = False` → exit 5, **252** verts — the doubled seam;
23-
probe: one centerline vert pulled off the plane → exit 4, **27 ≠ 28**
24-
welded; probe: mirror axis off → exit 5, **126** — the half-car). The
19+
- **Original holds the half.** Body datablock is exactly 676 verts / 1289
20+
edges / 614 faces (52 × 13, loft closed forms), with exactly 104
21+
centerline verts (probe: modifier applied into data → exit 3, datablock
22+
reads `(1248, 2474, 1228)`).
23+
- **Evaluated is the welded whole.** Exactly `2n − c = 1248` verts (probe:
24+
`use_mirror_merge = False` → exit 5, **1352** verts — the doubled seam;
25+
probe: one centerline vert pulled off the plane → exit 4, **103 ≠ 104**
26+
welded; probe: mirror axis off → exit 5, **676** — the half-car). The
2527
evaluated shell is watertight (every edge borders 2 faces) with Euler
2628
characteristic **2** — the two halves weld into a topological sphere.
2729
- **Exact ±X partners.** Every evaluated vertex has a partner at negated X:
2830
measured deviation **0.000e+00** (mirror copies exactly; tol 2e-5 guards
2931
float32), evaluated bbox symmetric (`min.x == −max.x`).
30-
- **Mirrored parts.** Wheels (96/81 half → 192/162 evaluated) and lamps
31-
(8/6 → 16/12) each double across the plane with partner deviation 0.0 —
32-
and each object origin reads `x == 0`.
32+
- **Mirrored parts.** Offset parts double exactly: wheels 390/361 →
33+
780/722, headlamp 64/50 → 128/100, taillamp and each handle 48/34 →
34+
96/68, door mirror 80/66 → 160/132, partner deviation 0.0, every origin
35+
`x == 0` (probe: headlamp origin at `x = 0.01` → exit 12; probe: front
36+
wheel's mirror axis off → exit 14, `(390, 361)`). The split grille
37+
authors 36 verts with 8 on the plane and must evaluate to `2n − 8 = 64`
38+
with exactly 8 on-plane verts (probe: grille merge off → exit 14, **72**
39+
verts / 16 on the plane — two grilles; probe: one grille end point moved
40+
to `x = 0.004`, beyond the merge threshold → exit 13, 7 ≠ 8 on the plane).
3341

3442
**Why the checks target the modifier, not the base:** mutating a non-plane
3543
base vertex cannot break the ±X pairing — the evaluated set is always
3644
`half ∪ mirror(half)`, symmetric by construction. Realistic failures live in
3745
the modifier (merge off, axis off, modifier applied into data, origin off
3846
the plane), which is what the probes break.
3947

40-
**Version witness:** check output is byte-identical on Blender 4.5.11 LTS and
41-
5.1.2. Mirror, `evaluated_get` / `to_mesh` / `to_mesh_clear` (no argument —
42-
passing the mesh raises TypeError on both), and `TRACK_TO` constraint
43-
behavior are stable across the pair; only the EEVEE engine id is
44-
version-gated.
48+
**Version witness:** check output is byte-identical on Blender 4.5.11 LTS,
49+
5.1.2 and 5.2.1 LTS. Mirror, `evaluated_get` / `to_mesh` / `to_mesh_clear`
50+
(no argument — passing the mesh raises TypeError on both), `BVHTree`, and
51+
`TRACK_TO` constraint behavior are stable across them; only the EEVEE
52+
engine id is version-gated.
4553

4654
The render is the proof: hide the mirror and the still is literally half a
47-
car — halved windshield and hood at the centerline, one headlamp. Render
48-
notes: the loft is faceted by design (flat shading, no bevel modifier — the
49-
modifier stack is Mirror only, so counts stay closed-form); the window band
50-
is glass by construction class (steepest roof-rise slope is the windshield,
51-
steepest drop the rear window, ring segment 5 the side windows), and the
52-
glass is dielectric — metallic glass mirrors the key light and renders the
53-
windshield as a hot salmon slab. The camera stands on the key light's side
54-
of the car: from the other side it saw only the flank the key cannot reach,
55-
and the whole visible side rendered near-black. The car is mirror-symmetric,
56-
so the composition is the same either way. The bright streak along the cowl
57-
below the windshield is the key's highlight in a crease that faces it.
55+
car — one headlamp, one door mirror, half a grille, a hood cut open at the
56+
centerline. The camera stands on the key light's side, high enough to look
57+
down the hood centerline so both halves read at once. The weld itself is
58+
not a pixel witness — the hood and roof are nearly flat across the plane,
59+
so an unwelded seam renders within 12/255 of the welded one; the `2n − c`
60+
count is what catches it.
61+
62+
Render notes: the loft is smooth-shaded with sharp edges set on the
63+
authored half by dihedral angle (35°) and material border — no bevel or
64+
weighted-normal modifier, so the stack stays Mirror only and every count
65+
stays closed-form. Boundary edges on the plane take the angle to their own
66+
mirror image, `acos(1 − 2nₓ²)`, so the centerline shades smooth. The
67+
previous faceted loft had a thin pink-white streak on the hood by the
68+
windshield: not a seam (it sat at `|x|` 0.11–0.38, on the mirrored half, and
69+
all weld checks passed) but a sliver facet — the loft's greenhouse points
70+
collapsing onto the hood — flat-shaded and facing the key almost head on
71+
(`n · −key = 0.967`). The redesigned ring lerps a hood layout into a cabin
72+
layout, so no such sliver exists. Panel classes are by construction
73+
position: underbody, rocker and arch cladding are trim; side glass is the
74+
sill-to-glass-top segment under the roof, broken by a trim B-pillar; the
75+
drip rail becomes the A-pillar; windshield and hatch glass are the roof
76+
bands where the cabin factor ramps. The glass is a dark dielectric whose
77+
base colour ramps toward steel blue at grazing angles (Layer Weight →
78+
Color Ramp) — on a near-black stage there is little to reflect, and flat
79+
dark glass read as dull paint. Lamps, grille, mirrors and handles are
80+
closed superellipse pods seated on the skin by a `BVHTree` ray cast, with a
81+
chrome bezel and an inset lens rather than flat boxes.
5882

5983
## Run
6084

@@ -90,10 +114,12 @@ against it.
90114
| 10 | Mirror partner deviation above tolerance |
91115
| 11 | Evaluated bbox not symmetric about X |
92116
| 12 | Mirrored-part origin off the plane |
93-
| 13 | Mirrored-part datablock is not the authored half |
94-
| 14 | Mirrored-part evaluated counts did not double |
117+
| 13 | Mirrored-part datablock is not the authored half (verts, faces, on-plane verts) |
118+
| 14 | Mirrored-part evaluated counts ≠ `2n − weld` / `2f`, or on-plane verts ≠ weld |
95119
| 15 | Mirrored-part partner check failed |
96120
| 16 | Mirrored-part evaluated mesh stayed on one side |
121+
| 17 | Gallery framing violation (`--output` path only; `gallery_framing` returns 10, remapped) |
122+
| 18 | Asset-quality floor violation (`--output` path only; `gallery_asset_quality` returns 11, remapped) |
97123

98124
The `blender-smoke` workflow runs the check on Blender 5.2 LTS and 4.5 LTS
99125
(5.1 on the weekly cron, the `needs-5.1` PR label, or manual dispatch).

0 commit comments

Comments
 (0)