diff --git a/docs/gallery/anvil/index.html b/docs/gallery/anvil/index.html index c730f27..da9f14a 100644 --- a/docs/gallery/anvil/index.html +++ b/docs/gallery/anvil/index.html @@ -253,7 +253,7 @@

anvil

anvil render

Rendered headless by the example itself — click to zoom.

-
witnesses Recomputed: 1052 tris, two materials with 102 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.750×0.406×0.690 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 134 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.
+
witnesses Recomputed: 1696 tris, two materials with 274 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.746×0.410×0.682 m, LOD ratios in band (5.2 COLLAPSE more aggressive on LOD2), convex collider 162 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.
blender --background --python showcase/anvil/anvil.py --
@@ -264,7 +264,7 @@

anvil

Composes skills mesh-editing-and-bmesh, bake-high-to-low, depsgraph-and-evaluated-data, engine-export-presets, and snippets bake_normal_high_to_low.py, setup_bake_target_image.py, lod_chain.py / decimate_to_budget.py, convex_hull_collider.py, export_preset_unity.py (helpers copied, not imported as a package).

Budgets

Declared as named constants; every gate recomputes from the mesh, materials, UVs, evaluated LOD, collider, or export file.

-

| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) | | --- | --- | --- | | Base triangles | 980–1140 | 1052 / 1052 / 1052 | | LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 | | LOD2 ratio | 0.10–0.35 of base | 0.2186 / 0.2186 / 0.2110 | | Materials | exactly 2 distinct, ≥24 metal faces | 2 slots, 102 metal | | UVs | in 0..1, AABB overlap ≤ 1e-5 | in range, overlap 0 | | Outer AABB | (0.750, 0.406, 0.690) m ± 0.01 | (0.7500, 0.4060, 0.6900), zmin 0 | | Collider tris | ≤ 180 | 134 | | Export | written, size > 0 | 84092 / 84092 / 84088 bytes |

+

| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) | | --- | --- | --- | | Base triangles | 1620–1780 | 1696 / 1696 / 1696 | | LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 | | LOD2 ratio | 0.10–0.35 of base | 0.2193 / 0.2193 / 0.2182 | | Materials | exactly 2 distinct, ≥24 metal faces | 2 slots, 274 metal | | UVs | in 0..1, AABB overlap ≤ 1e-5 | in range, overlap 0 | | Outer AABB | (0.746, 0.410, 0.682) m ± 0.01 | (0.7460, 0.4095, 0.6820), zmin 0 | | Collider tris | ≤ 180 | 162 | | Export | written, size > 0 | 130852 / 130852 / 130840 bytes |

DECIMATE COLLAPSE triangle counts are not identical across series — 5.2.1 is more aggressive on LOD2. The gate is a ratio band, not an exact count. Bake pixels are stochastic; the gate is has_data plus operator FINISHED, not byte-identity. Construction uses no RNG. Export byte counts differ by 4 B on 5.2.1 (glTF serializer), not a gated axis.

--skip-decimate skips the LOD DECIMATE stage so LOD1 ratio is 1.0 and exit 9 fires. That is the named budget the falsifier violates.

Run

@@ -333,9 +333,9 @@

Source

HEEL_L = 0.11 BBOX_TOL = 0.01 -OUTER_SIZE = (0.750, 0.406, 0.690) -BASE_TRIS_MIN = 980 -BASE_TRIS_MAX = 1140 +OUTER_SIZE = (0.746, 0.410, 0.682) +BASE_TRIS_MIN = 1620 +BASE_TRIS_MAX = 1780 LOD1_RATIO_MIN = 0.32 LOD1_RATIO_MAX = 0.62 LOD2_RATIO_MIN = 0.10 @@ -461,6 +461,37 @@

Source

return verts +def add_rim(bm, loc, major, minor, mat_idx, euler=(0.0, 0.0, 0.0)): + n_major = 14 + n_minor = 7 + rings = [] + for i in range(n_major): + u = i * (2.0 * math.pi / n_major) + ring = [] + for j in range(n_minor): + v = j * (2.0 * math.pi / n_minor) + x = (major + minor * math.cos(v)) * math.cos(u) + y = (major + minor * math.cos(v)) * math.sin(u) + z = minor * math.sin(v) + ring.append(bm.verts.new((x, y, z))) + rings.append(ring) + bm.verts.ensure_lookup_table() + for i in range(n_major): + i2 = (i + 1) % n_major + for j in range(n_minor): + j2 = (j + 1) % n_minor + face = bm.faces.new( + (rings[i][j], rings[i2][j], rings[i2][j2], rings[i][j2]) + ) + face.material_index = mat_idx + verts = [v for ring in rings for v in ring] + rot = Euler(euler).to_matrix() + origin = Vector(loc) + for v in verts: + v.co = rot @ v.co + origin + return verts + + def pack_uvs(bm, margin=0.08): uv = bm.loops.layers.uv.new("UVMap") faces = list(bm.faces) @@ -511,17 +542,23 @@

Source

metal = [] wood.extend( - add_cyl( - bm, (0.0, 0.0, STUMP_H / 2.0), STUMP_R, STUMP_H, 14, WOOD_IDX, + add_cone( + bm, + (0.0, 0.0, STUMP_H / 2.0), + STUMP_R + 0.010, + STUMP_R - 0.018, + STUMP_H, + 20, + WOOD_IDX, ) ) wood.extend( add_cyl( bm, - (0.0, 0.0, STUMP_H + 0.012), - STUMP_R - 0.012, - 0.024, - 14, + (0.0, 0.0, STUMP_H + 0.010), + STUMP_R - 0.022, + 0.020, + 20, WOOD_IDX, ) ) @@ -540,44 +577,45 @@

Source

for f in ret.get("faces") or []: f.material_index = WOOD_IDX - for z in (0.055, STUMP_H - 0.055): + for z in (0.048, STUMP_H - 0.042): metal.extend( - add_cyl( - bm, - (0.0, 0.0, z), - STUMP_R + 0.008, - 0.028, - 16, - METAL_IDX, - ) + add_rim(bm, (0.0, 0.0, z), STUMP_R + 0.004, 0.011, METAL_IDX) ) z0 = STUMP_H metal.extend( add_box( bm, - (0.0, 0.0, z0 + 0.028), - (FOOT_L, FOOT_W, 0.056), + (0.0, 0.0, z0 + 0.024), + (0.38, 0.168, 0.048), + METAL_IDX, + ) + ) + metal.extend( + add_box( + bm, + (0.0, 0.0, z0 + 0.066), + (0.30, 0.132, 0.038), METAL_IDX, ) ) metal.extend( add_box( bm, - (0.0, 0.0, z0 + 0.056 + BODY_H / 2.0), - (WAIST_L, WAIST_W, BODY_H), + (0.0, 0.0, z0 + 0.148), + (0.176, 0.070, 0.130), METAL_IDX, ) ) metal.extend( add_box( bm, - (0.0, 0.0, z0 + 0.056 + BODY_H + 0.028), - (FACE_L * 0.90, FACE_W * 0.88, 0.056), + (0.0, 0.0, z0 + 0.248), + (0.34, 0.112, 0.052), METAL_IDX, ) ) - face_z = z0 + 0.056 + BODY_H + 0.056 + FACE_T / 2.0 + face_z = z0 + 0.248 + 0.026 + FACE_T / 2.0 metal.extend( add_box( bm, @@ -587,15 +625,15 @@

Source

) ) - horn_z = face_z - 0.018 + horn_z = face_z - 0.012 metal.extend( add_cone( bm, - (FACE_L / 2.0 + HORN_L / 2.0 - 0.02, 0.0, horn_z), - 0.052, - 0.016, + (FACE_L / 2.0 + HORN_L / 2.0 - 0.028, 0.0, horn_z), + 0.056, + 0.014, HORN_L, - 12, + 16, METAL_IDX, euler=(0.0, math.pi / 2.0, 0.0), ) @@ -603,34 +641,34 @@

Source

metal.extend( add_box( bm, - (FACE_L / 2.0 - 0.01, 0.0, horn_z - 0.028), - (0.09, FACE_W * 0.92, 0.040), + (FACE_L / 2.0 - 0.018, 0.0, horn_z - 0.018), + (0.10, FACE_W * 0.90, 0.048), METAL_IDX, ) ) metal.extend( add_box( bm, - (-FACE_L / 2.0 - HEEL_L / 2.0 + 0.02, 0.0, face_z - 0.008), - (HEEL_L, FACE_W * 0.92, FACE_T * 0.82), + (-FACE_L / 2.0 - HEEL_L / 2.0 + 0.016, 0.0, face_z - 0.006), + (HEEL_L, FACE_W * 0.90, FACE_T * 0.78), METAL_IDX, ) ) metal.extend( add_box( bm, - (FACE_L * 0.22, 0.0, face_z + FACE_T / 2.0 - 0.006), - (0.034, 0.034, 0.014), + (FACE_L * 0.18, 0.0, face_z + FACE_T / 2.0 - 0.005), + (0.032, 0.032, 0.012), METAL_IDX, ) ) metal.extend( add_cyl( bm, - (FACE_L * 0.32, 0.0, face_z + FACE_T / 2.0 - 0.004), + (FACE_L * 0.30, 0.0, face_z + FACE_T / 2.0 - 0.004), 0.009, 0.012, - 8, + 10, METAL_IDX, ) ) @@ -808,7 +846,7 @@

Source

low = build_anvil_mesh("AnvilLow", bevel_offset=0.005, bevel_segments=2) high = build_anvil_mesh("AnvilHigh", bevel_offset=0.005, bevel_segments=4) wood = principled("AnvilStump", (0.34, 0.18, 0.07, 1.0), 0.0, 0.62) - metal = principled("AnvilSteel", (0.18, 0.185, 0.195, 1.0), 1.0, 0.24) + metal = principled("AnvilSteel", (0.44, 0.45, 0.47, 1.0), 1.0, 0.30) assign_slots(low, wood, metal) assign_slots(high, wood, metal) @@ -957,7 +995,7 @@

Source

ob.hide_render = True ob.hide_viewport = True - low.rotation_euler.z = math.radians(-22.0) + low.rotation_euler.z = math.radians(-8.0) low.rotation_euler.x = math.radians(0.0) floor_me = bpy.data.meshes.new("Floor") @@ -1004,10 +1042,10 @@

Source

cam_data = bpy.data.cameras.new("Cam") cam_data.lens = 50.0 cam = bpy.data.objects.new("Cam", cam_data) - cam.location = (1.36, -1.68, 0.93) + cam.location = (0.70, -2.20, 1.08) scene.collection.objects.link(cam) aim = bpy.data.objects.new("Aim", None) - aim.location = (0.0, 0.0, 0.36) + aim.location = (0.0, 0.0, 0.40) scene.collection.objects.link(aim) con = cam.constraints.new("TRACK_TO") con.target = aim diff --git a/docs/gallery/assets/anvil-hero.webp b/docs/gallery/assets/anvil-hero.webp index 0a706f6..3caed77 100644 Binary files a/docs/gallery/assets/anvil-hero.webp and b/docs/gallery/assets/anvil-hero.webp differ diff --git a/docs/gallery/assets/hand-pump-hero.webp b/docs/gallery/assets/hand-pump-hero.webp index 4902358..aaa9171 100644 Binary files a/docs/gallery/assets/hand-pump-hero.webp and b/docs/gallery/assets/hand-pump-hero.webp differ diff --git a/docs/gallery/assets/signpost-hero.webp b/docs/gallery/assets/signpost-hero.webp index 0d00ef7..b2727e0 100644 Binary files a/docs/gallery/assets/signpost-hero.webp and b/docs/gallery/assets/signpost-hero.webp differ diff --git a/docs/gallery/assets/water-trough-hero.webp b/docs/gallery/assets/water-trough-hero.webp index 58a2d57..2395fd8 100644 Binary files a/docs/gallery/assets/water-trough-hero.webp and b/docs/gallery/assets/water-trough-hero.webp differ diff --git a/docs/gallery/hand-pump/index.html b/docs/gallery/hand-pump/index.html index 3b395d2..e1c27d2 100644 --- a/docs/gallery/hand-pump/index.html +++ b/docs/gallery/hand-pump/index.html @@ -4,18 +4,18 @@ hand-pump — Examples — Blender Developer Tools - + - + - +