Skip to content

Commit 8a69765

Browse files
TMHSDigitalclaude
andcommitted
fix(examples): redesign socket-attach-points as a cobalt survey drone with carbon-weave arms
The old hero read as flat grey, and at 0.881 x-fill its rotor tips reached the card crop. Everything changed is on the render path; geometry, sockets and checks are untouched. - Two-tone livery: a gloss cobalt fuselage shell (complement of the orange mount pads, so the sockets pop), off-white arm fairings and spinners (spinners were orange and competed with the pads), graphite deck and trim. - Booms get a procedural twill weave: an object-space checker at two tones and roughnesses, under a clear coat (Coat Weight when the input exists). - The fuselage shell is smooth-shaded with set_sharp_from_angle(38 deg), so its loft bands read as a moulded part and box edges stay crisp. - Camera pulled back along its aim ray by 1.14x. Lighting: a small close key (60 W / 0.9 m) keeps its falloff on the airframe. The floor is the backdrop under this down-looking camera, so the warm wedge now pools on the floor behind the drone. Floor base darkened to 0.020. Evidence (live runs, local): - E:\Blender-Developer-Tools\.scratch\blender-5.2.1-windows-x64\blender.exe (Blender 5.2.1 LTS): check-only exit 0, sockets=7 modules=7 parts=14 matrix_dev=1.788e-07 normal_dev=1.794e-07 seat_dev=0 apply_dev=2.235e-07 repose_dev=2.384e-07. --probe: probe_no_mpi worst_socket_jump=0.690128 m (the falsifier's measured breakage, unchanged). - ...\blender-4.5.11-windows-x64\blender.exe (Blender 4.5.11 LTS): check-only exit 0, identical numbers. - Framing: fill x=0.741 y=0.672, margins l=0.131 r=0.128 b=0.172 t=0.156. Asset floors: materials n=32, edge90=0.027, no default names (aq_ok). - Contact sheet (docs/gallery/contact-sheets/socket-attach-points-contact-sheet.webp): candidate luma 0.247 / stage 0.208 / warmth +0.041 / sat 0.168, against armature-bend 0.279/0.213/-0.428/0.144, damped-track-aim 0.151/0.145/+0.277/0.231 and bmesh-gear 0.236/0.177/+0.303/0.250. Stage is within the set. Saturation is up from 0.119 (white draft), and warmth is positive but below the gold calibration tiles. - Asset sheet (docs/gallery/asset-sheets/socket-attach-points.webp): beside collision-hull-proxy, vertex-color-ao, wheelbarrow and apothecary-shelf, the drone is not the least-designed object. Verdict: pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 663fa03 commit 8a69765

8 files changed

Lines changed: 73 additions & 25 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ by an independent SAT scan, and a measured min island distance of
11811181
</tr>
11821182
<tr>
11831183
<td width="46%" valign="middle">
1184-
<a href="examples/socket-attach-points/"><img src="examples/socket-attach-points/preview.webp" alt="Socket attach points: a dark carbon survey quadcopter hovering on a studio stage, orange mount pads ringing each canted rotor, a gimbal camera pod under the belly and a sensor mast on the deck — every module seated exactly on its named socket" /></a>
1184+
<a href="examples/socket-attach-points/"><img src="examples/socket-attach-points/preview.webp" alt="Socket attach points: a cobalt-and-graphite survey quadcopter with carbon-weave arms hovering over a studio stage, orange mount pads ringing each canted rotor, a gimbal camera pod under the belly and a sensor mast on the deck — every module seated exactly on its named socket" /></a>
11851185
</td>
11861186
<td valign="middle">
11871187

15.4 KB
Loading
12 KB
Loading
-2.08 KB
Loading

‎examples/gallery.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@
602602
"name": "socket-attach-points",
603603
"dir": "examples/socket-attach-points",
604604
"teaches": "A survey drone whose named SKT_ empties are the spawn contract: modules parented with an identity local transform seat exactly on their mount pads.",
605-
"alt": "A dark quadcopter drone with orange motor mounts, propellers and attached antenna and camera parts.",
605+
"alt": "A cobalt-and-graphite survey quadcopter with carbon-weave arms hovering over a dark stage, orange mount pads ringing each rotor, with a sensor mast and belly camera pod seated on their sockets.",
606606
"witnessesFix": "7 socket world matrices within 1.788e-07 of the authored transform, socket +Z == the pad's Newell normal (1.794e-07), seating offset 0.0; the no-parent-inverse probe jumps 0.690128 m.",
607607
"hero": "docs/gallery/assets/socket-attach-points-hero.webp",
608608
"preview": "examples/socket-attach-points/preview.webp",

‎examples/socket-attach-points/README.md‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,13 @@ socket drift (exit 8).
107107
5.1.2 — same counts, same measured deviations to every printed digit, and the
108108
same framing numbers on the render path.
109109

110-
**Render as proof:** the drone on the dark stage with every module seated, the
111-
orange mount pads reading as the socket signature. The falsification variant
110+
**Render as proof:** the drone hovering over the dark stage with every module
111+
seated, the orange mount pads reading as the socket signature. The livery is
112+
two-tone — a gloss cobalt fuselage shell (the pads' complement, so the sockets
113+
pop), off-white arm fairings and spinners, graphite deck and trim — and the
114+
booms carry a procedural twill carbon weave under a clear coat. The camera
115+
looks down at the airframe, so the floor is the backdrop: a small close key
116+
keeps its falloff on the drone and the warm wedge pools on the floor behind it. The falsification variant
112117
(`--falsify`) parents the sockets without `matrix_parent_inverse`: the pads
113118
are left bare, the rotors and mast vanish from the airframe, and the camera
114119
pod and battery hang in space near the top of frame. A broken matrix is not a
@@ -124,8 +129,9 @@ blender --background --python socket_attach_points.py -- --probe
124129
```
125130

126131
The `--output` render path additionally gates framing via
127-
`examples/gallery_framing.py` (fill **0.881x**, margins
128-
**0.066/0.053/0.122/0.106**, no edge touched) and the asset floors via
132+
`examples/gallery_framing.py` (fill **0.741x**, margins
133+
**0.131/0.128/0.172/0.156**, no edge touched — pulled back from 0.881x so the
134+
rotor tips clear gallery card crops) and the asset floors via
129135
`examples/gallery_asset_quality.py` (32 materials, `edge90` **0.027**, no
130136
default names). `--falsify` is a diagnostic, not a gallery hero: it
131137
measures and prints framing but does not enforce the Layer 1 band —
11 KB
Loading

‎examples/socket-attach-points/socket_attach_points.py‎

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -912,13 +912,17 @@ def make_material(name, rgb, rough=0.45, metallic=0.6, emit=None, estr=0.0):
912912
# One entry per material slot name used by the Part builders above:
913913
# (base rgb, roughness, metallic, emission rgb or None, emission strength).
914914
SLOT_MATS = {
915-
"Carbon": ((0.044, 0.049, 0.060), 0.62, 0.22, None, 0.0),
916-
"Deck": ((0.058, 0.067, 0.080), 0.52, 0.55, None, 0.0),
917-
"Canopy": ((0.020, 0.048, 0.062), 0.14, 0.30, None, 0.0),
918-
"Vent": ((0.066, 0.074, 0.086), 0.58, 0.70, None, 0.0),
919-
"Trim": ((0.046, 0.052, 0.062), 0.72, 0.30, None, 0.0),
920-
"Boom": ((0.040, 0.045, 0.055), 0.40, 0.60, None, 0.0),
921-
"Fairing": ((0.050, 0.056, 0.068), 0.64, 0.45, None, 0.0),
915+
# two-tone survey livery: a gloss cobalt fuselage shell with off-white arm fairings and spinners
916+
# over a graphite deck and trim, so the airframe separates from the dark
917+
# stage and the orange mount pads read against both tones
918+
"Carbon": ((0.030, 0.120, 0.360), 0.26, 0.00, None, 0.0),
919+
"Deck": ((0.050, 0.056, 0.066), 0.44, 0.55, None, 0.0),
920+
"Canopy": ((0.012, 0.030, 0.042), 0.08, 0.30, None, 0.0),
921+
"Vent": ((0.030, 0.033, 0.040), 0.50, 0.70, None, 0.0),
922+
"Trim": ((0.036, 0.040, 0.048), 0.46, 0.30, None, 0.0),
923+
# booms are woven carbon fibre (see WEAVE_SLOTS), not flat grey
924+
"Boom": ((0.022, 0.024, 0.028), 0.30, 0.20, None, 0.0),
925+
"Fairing": ((0.600, 0.610, 0.600), 0.30, 0.00, None, 0.0),
922926
"Motor": ((0.150, 0.158, 0.172), 0.26, 0.92, None, 0.0),
923927
"MotorFin": ((0.108, 0.114, 0.126), 0.30, 0.90, None, 0.0),
924928
"NavFwd": ((0.060, 0.140, 0.090), 0.30, 0.10, (0.30, 1.00, 0.55), 2.6),
@@ -931,7 +935,7 @@ def make_material(name, rgb, rough=0.45, metallic=0.6, emit=None, estr=0.0):
931935
"SocketPad": ((0.640, 0.250, 0.045), 0.34, 0.40, None, 0.0),
932936
"Fixing": ((0.170, 0.176, 0.188), 0.24, 0.95, None, 0.0),
933937
"Hub": ((0.120, 0.126, 0.138), 0.28, 0.90, None, 0.0),
934-
"Spinner": ((0.520, 0.200, 0.038), 0.26, 0.55, None, 0.0),
938+
"Spinner": ((0.600, 0.610, 0.600), 0.28, 0.00, None, 0.0),
935939
"Blade": ((0.072, 0.076, 0.086), 0.26, 0.40, None, 0.0),
936940
"PodYoke": ((0.096, 0.102, 0.114), 0.34, 0.85, None, 0.0),
937941
"PodBall": ((0.038, 0.052, 0.062), 0.22, 0.45, None, 0.0),
@@ -950,11 +954,40 @@ def make_material(name, rgb, rough=0.45, metallic=0.6, emit=None, estr=0.0):
950954

951955
_mat_cache = {}
952956

957+
# slots that get a procedural 2x2 twill weave: alternating tows at two
958+
# roughnesses and tones, under a clear coat, in object space so the weave
959+
# follows each boom rather than the world
960+
WEAVE_SLOTS = {"Boom"}
961+
962+
963+
def add_weave(mat, rgb, rough):
964+
nt = mat.node_tree
965+
b = nt.nodes["Principled BSDF"]
966+
tc = nt.nodes.new("ShaderNodeTexCoord")
967+
chk = nt.nodes.new("ShaderNodeTexChecker")
968+
chk.inputs["Scale"].default_value = 180.0
969+
chk.inputs["Color1"].default_value = (*rgb, 1.0)
970+
chk.inputs["Color2"].default_value = (*(c * 2.6 for c in rgb), 1.0)
971+
nt.links.new(tc.outputs["Object"], chk.inputs["Vector"])
972+
nt.links.new(chk.outputs["Color"], b.inputs["Base Color"])
973+
mr = nt.nodes.new("ShaderNodeMapRange")
974+
mr.inputs["To Min"].default_value = rough + 0.18
975+
mr.inputs["To Max"].default_value = rough - 0.08
976+
nt.links.new(chk.outputs["Fac"], mr.inputs["Value"])
977+
nt.links.new(mr.outputs["Result"], b.inputs["Roughness"])
978+
coat = b.inputs.get("Coat Weight")
979+
if coat is not None:
980+
coat.default_value = 0.8
981+
b.inputs["Coat Roughness"].default_value = 0.06
982+
953983

954984
def mat_for_slot(slot):
955985
if slot not in _mat_cache:
956986
rgb, rough, metal, emit, estr = SLOT_MATS[slot]
957-
_mat_cache[slot] = make_material(slot, rgb, rough, metal, emit, estr)
987+
mat = make_material(slot, rgb, rough, metal, emit, estr)
988+
if slot in WEAVE_SLOTS:
989+
add_weave(mat, rgb, rough)
990+
_mat_cache[slot] = mat
958991
return _mat_cache[slot]
959992

960993

@@ -975,7 +1008,7 @@ def build_studio(sc):
9751008
bm.to_mesh(floor_me)
9761009
finally:
9771010
bm.free()
978-
fmat = make_material("Studio", (0.030, 0.032, 0.037), rough=0.7, metallic=0.0)
1011+
fmat = make_material("Studio", (0.020, 0.021, 0.025), rough=0.7, metallic=0.0)
9791012
floor_me.materials.append(fmat)
9801013
floor = bpy.data.objects.new("Floor", floor_me)
9811014
sc.collection.objects.link(floor)
@@ -1004,14 +1037,16 @@ def light(name, loc, energy, size, col, rot):
10041037
return ob
10051038

10061039
# VISUAL-STYLE Layer 2 rig, energies scaled to a 0.86 m subject
1007-
light("Key", (-1.30, -0.80, 2.30), 150.0, 1.4, (1.0, 0.96, 0.9), (26, 0, -58))
1008-
light("Fill", (1.70, -1.00, 0.90), 14.0, 2.6, (0.75, 0.85, 1.0), (66, 0, 54))
1009-
light("Rim", (-0.35, 1.60, 1.40), 55.0, 1.2, (0.6, 0.78, 1.0), (-50, 0, 196))
1010-
# Wedge sits between subject and wall, aimed at the wall, not the drone.
1011-
# Draft 3 put it high and hot: the pool blew to white and was clipped by
1012-
# the top-right corner. Low, larger and softer keeps the pool contained
1013-
# behind the subject where it lifts the silhouette.
1014-
light("Wedge", (0.10, 3.30, 0.34), 70.0, 3.0, (1.0, 0.76, 0.5), (-88, 0, 182))
1040+
# Key is small and close so its falloff stays on the airframe: the old
1041+
# 150 W / 1.4 m key washed the whole floor to mid grey under the
1042+
# three-quarter-down camera (the white livery needs far less light)
1043+
light("Key", (-1.00, -0.70, 1.90), 60.0, 0.9, (1.0, 0.95, 0.88), (30, 0, -55))
1044+
light("Fill", (1.70, -1.00, 0.90), 8.0, 2.6, (0.75, 0.85, 1.0), (66, 0, 54))
1045+
light("Rim", (-0.35, 1.60, 1.40), 45.0, 1.2, (0.6, 0.78, 1.0), (-50, 0, 196))
1046+
# The camera looks down at the drone, so the floor is the backdrop: the
1047+
# warm wedge pools on the floor behind and under the airframe, where its
1048+
# shadow and the pool frame the silhouette (the back wall barely shows)
1049+
light("Wedge", (-0.10, 1.10, 1.70), 65.0, 1.6, (1.0, 0.56, 0.26), (-18, 0, 180))
10151050
return floor, wall
10161051

10171052

@@ -1030,6 +1065,13 @@ def render_still(path, engine, falsify=False):
10301065
hero = parts + list(modules.values())
10311066
for ob in hero:
10321067
bind_materials(ob)
1068+
# render-only shading: the lofted fuselage shell reads as a moulded part,
1069+
# smooth across its loft bands, while box edges stay crisp. Positions are
1070+
# untouched, so nothing the socket checks measure changes.
1071+
hull = next(ob for ob in hero if ob.data.name.endswith("Hull"))
1072+
for poly in hull.data.polygons:
1073+
poly.use_smooth = True
1074+
hull.data.set_sharp_from_angle(angle=math.radians(38.0))
10331075
floor, wall = build_studio(sc)
10341076

10351077
cam_data = bpy.data.cameras.new("Cam")
@@ -1038,7 +1080,7 @@ def render_still(path, engine, falsify=False):
10381080
# three-quarter from above: the deck, the canted rotor pads and the belly
10391081
# pod all read from here; a level side-on view (draft 2) flattened the
10401082
# airframe into a silhouette
1041-
cam.location = (1.28, -1.42, 1.44)
1083+
cam.location = (1.445, -1.612, 1.558)
10421084
sc.collection.objects.link(cam)
10431085
aim = bpy.data.objects.new("Aim", None)
10441086
aim.location = (0.10, -0.05, 0.60)

0 commit comments

Comments
 (0)