Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,15 +440,16 @@ semver on LTS builds (`"4.5.11 LTS"`), and that a Mesh reference dies at
</tr>
<tr>
<td width="46%" valign="middle">
<a href="examples/depsgraph-export/"><img src="examples/depsgraph-export/preview.webp" alt="Depsgraph-evaluated export: a teal base cube beside the smooth teal sphere of its subdivision-surface evaluated form - one shared material, two silhouettes - on a dark studio floor with a warm light pool behind" /></a>
<a href="examples/depsgraph-export/"><img src="examples/depsgraph-export/preview.webp" alt="Depsgraph-evaluated export: the sparse control cage of a game controller drawn as orange wire and vertex beads over faint blue facets, beside the smooth subdivided cobalt controller with sticks, d-pad and colored face buttons that the OBJ export contains, on a dark studio floor with a warm light pool behind" /></a>
</td>
<td valign="middle">

### [depsgraph-export](examples/depsgraph-export/)

A depsgraph-evaluated export — builds a cube with `SUBSURF`, measures the evaluated mesh via
`evaluated_get().to_mesh()` / `to_mesh_clear()`, and asserts `wm.obj_export` ships the
modifier-applied geometry (exported vertex count == evaluated > base).
A depsgraph-evaluated export — builds a game controller whose shell is a 90-vertex quad cage
under `SUBSURF`, measures every mesh via `evaluated_get().to_mesh()` / `to_mesh_clear()`,
asserts the evaluated shell matches the Catmull-Clark closed form (1,410 vertices), and asserts
`wm.obj_export` ships the modifier-applied geometry (exported vertex count == evaluated > base).

</td>
</tr>
Expand Down
Binary file added docs/gallery/asset-sheets/depsgraph-export.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/assets/depsgraph-export-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
581 changes: 467 additions & 114 deletions docs/gallery/depsgraph-export/index.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/gallery/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,12 @@ <h2><a href="gn-sdf-remesh/">gn-sdf-remesh</a></h2>
</article>
<article class="card" data-tags="depsgraph export">
<a class="card-media" href="depsgraph-export/" tabindex="-1" aria-hidden="true">
<img src="assets/depsgraph-export-hero.webp" alt="A teal cube outlined by its twelve edges beside a faceted teal sphere covered in a dense pale wire grid, on a dark studio floor." loading="lazy" decoding="async" />
<img src="assets/depsgraph-export-hero.webp" alt="A game controller&#x27;s sparse control cage in orange wire and vertex beads beside the smooth subdivided cobalt controller with sticks and colored buttons, on a dark studio floor." loading="lazy" decoding="async" />
</a>
<div class="card-body">
<h2><a href="depsgraph-export/">depsgraph-export</a></h2>
<p class="teaches">The depsgraph lifetime contract — evaluated_get().to_mesh() paired with to_mesh_clear() — measured against an OBJ export of the same object.</p>
<p class="witnesses"><span class="tag">witnesses</span> Exports ship evaluated geometry: the exported vertex count equals the subsurf-applied count and is strictly greater than the base mesh.</p>
<p class="witnesses"><span class="tag">witnesses</span> Exports ship evaluated geometry: the controller shell subdivides to exactly its Catmull-Clark closed form, and the exported vertex count equals the subsurf-applied count, strictly greater than the base mesh.</p>
<a class="card-link" href="depsgraph-export/">View example<span class="sr-only"> depsgraph-export</span> <span aria-hidden="true">&rarr;</span></a>
</div>
</article>
Expand Down
38 changes: 29 additions & 9 deletions examples/depsgraph-export/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,32 @@

A runnable example that proves **modifiers actually ship in exports** and demonstrates the
[`depsgraph-and-evaluated-data`](../../skills/depsgraph-and-evaluated-data/SKILL.md) lifetime
contract. It builds a cube with a SUBSURF modifier, measures the evaluated mesh via
`evaluated_get().to_mesh()` (paired with `to_mesh_clear()`), exports through `wm.obj_export`,
and asserts the exported vertex count equals the **evaluated** (modifier-applied) count and is
strictly greater than the base mesh.
contract. It builds a game controller whose shell is a sparse quad control cage (90 vertices)
under a level-2 `SUBSURF` modifier, with the sticks, d-pad, face buttons and bumpers modeled
as ordinary parts parented to it. It measures every mesh via `evaluated_get().to_mesh()`
(each paired with `to_mesh_clear()`), exports the scene through `wm.obj_export`, and asserts:

- the evaluated shell has exactly the vertex count the Catmull-Clark closed form predicts
from the cage's own topology — per level `V' = V + E + F`, `E' = 2E + S`, `F' = S`
(S = face corners) — which for this cage (V=90, E=176, F=88, S=352) is **1,410**;
- the exported OBJ vertex count equals the summed **evaluated** counts of every mesh
(3,666), not the summed base counts (2,346).

**What it witnesses:** the `evaluated_get` → `to_mesh` → `to_mesh_clear` contract, and that
`wm.obj_export` writes the depsgraph-evaluated geometry (so modifiers are baked into the
export) rather than the unmodified base mesh.

## The render

Left: the shell datablock as the `.blend` stores it — its 90-vertex control cage drawn as
orange wire with a bead on every vertex, over faint blue facets. Right: the same object as the
depsgraph evaluates it and the OBJ contains it — the smooth subdivided cobalt controller with
its controls. If the export shipped the base mesh, the right-hand piece would be the blocky
cage on the left.

Both pieces lean toward the camera on low satin display stands (render-only, added after the
check and export have run).

## Run

```bash
Expand All @@ -20,7 +37,7 @@ blender --background --python depsgraph_export.py --
# Falsifier: apply_modifiers=False. Must exit non-zero (export ≠ evaluated).
blender --background --python depsgraph_export.py -- --unevaluated

# Also render a still of base vs evaluated (EEVEE on a GPU host; cycles on GPU-less hosts):
# Also render a still of cage vs evaluated (EEVEE on a GPU host; cycles on GPU-less hosts):
blender --background --python depsgraph_export.py -- --output depsgraph.png
blender --background --python depsgraph_export.py -- --output depsgraph.png --engine cycles

Expand All @@ -31,24 +48,27 @@ blender --background --python depsgraph_export.py -- --obj exported.obj
## Exit codes

Per-script sequential checks. `9` is a valid check code; there is no rule
against it. `10` is the shared framing helper.
against it. `10` and `11` are the shared framing and asset-quality helpers.

| Code | Meaning |
| --- | --- |
| 0 | Success |
| 1 | Uncaught exception (FATAL wrapper) |
| 2 | argparse / usage |
| 3 | Evaluated mesh did not apply the modifier |
| 3 | Evaluated shell did not apply the modifier (evaluated count not above the cage) |
| 4 | No OBJ written |
| 5 | Export vert count ≠ evaluated (`--unevaluated` lands here) |
| 6 | `--output` produced no file |
| 7 | Evaluated shell count ≠ Catmull-Clark closed form |
| 10 | Gallery framing violation |
| 11 | Asset-quality floor violation (render path only) |

`--obj` is a path selector, not a falsifier.

The `blender-smoke` workflow runs the check on Blender 5.2 LTS and 4.5 LTS
(5.1 on the weekly cron, the `needs-5.1` PR label, or manual dispatch).
Smoke does not pass `--output`, `--obj`, or `--unevaluated`.


The `--output` render path additionally measures framing against the Layer 1 band via `examples/gallery_framing.py` (exit 10 on violation) before writing the still.
The `--output` render path additionally measures framing against the Layer 1 band via
`examples/gallery_framing.py` (exit 10) and the asset-quality floors on the shipped
controller via `examples/gallery_asset_quality.py` (exit 11) before writing the still.
Loading
Loading