Examples Gallery
GitHub

depsgraph-export

The depsgraph lifetime contract — evaluated_get().to_mesh() paired with to_mesh_clear() — measured against an OBJ export of the same object.

Rendered headless by the example itself — click to zoom.

witnesses Exports ship evaluated geometry: the exported vertex count equals the subsurf-applied count and is strictly greater than the base mesh.
blender --background --python examples/depsgraph-export/depsgraph_export.py --

A runnable example that proves modifiers actually ship in exports and demonstrates the depsgraph-and-evaluated-data 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.

What it witnesses: the evaluated_getto_meshto_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.

Run

# Cheap correctness check (writes an OBJ to a temp path, asserts the counts) — the CI check:
blender --background --python depsgraph_export.py --

# Write the exported OBJ to a specific path:
blender --background --python depsgraph_export.py -- --output remeshed.obj

It exits non-zero on failure (modifier not applied, or exported count ≠ evaluated count). The blender-smoke workflow runs this check on Blender 4.5 LTS and 5.1: base 8 → evaluated/exported 98 vertices with a 2-level SUBSURF.

Source

examples/depsgraph-export/depsgraph_export.py View on GitHub →
"""Candidate B: depsgraph-evaluated export (SCRATCH).

Witnesses the depsgraph lifetime contract AND that modifiers actually ship in exports. 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.
"""
import bpy, bmesh, sys, os, argparse

def main():
    argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
    p = argparse.ArgumentParser()
    p.add_argument("--output", default=None, help="optional: write the exported OBJ here (else a temp path)")
    args = p.parse_args(argv)

    bpy.ops.wm.read_factory_settings(use_empty=True)
    me = bpy.data.meshes.new("Cube"); bm = bmesh.new(); bmesh.ops.create_cube(bm, size=2.0); bm.to_mesh(me); bm.free()
    obj = bpy.data.objects.new("Cube", me); bpy.context.collection.objects.link(obj)
    obj.modifiers.new("ss", 'SUBSURF').levels = 2
    base = len(obj.data.vertices)

    # depsgraph lifetime contract: evaluate, read, then release with to_mesh_clear
    dg = bpy.context.evaluated_depsgraph_get()
    ev = obj.evaluated_get(dg)
    em = ev.to_mesh()
    eval_vcount = len(em.vertices)
    ev.to_mesh_clear()  # must be paired; releases the temporary mesh

    import tempfile
    out = args.output or os.path.join(tempfile.gettempdir(), "depsgraph_export.obj")
    os.makedirs(os.path.dirname(os.path.abspath(out)) or ".", exist_ok=True)
    # obj_export writes the evaluated (modifier-applied) geometry by default
    bpy.ops.wm.obj_export(filepath=out, export_selected_objects=False)
    if not (os.path.exists(out) and os.path.getsize(out) > 0):
        print("ERROR: no OBJ written", file=sys.stderr); return 4
    exported = 0
    with open(out) as f:
        for line in f:
            if line.startswith("v "): exported += 1

    print(f"base_vcount={base} eval_vcount={eval_vcount} exported_vcount={exported}")
    if not (eval_vcount > base):
        print("ERROR: evaluated mesh did not apply the modifier", file=sys.stderr); return 3
    if exported != eval_vcount:
        print(f"ERROR: export ({exported}) != evaluated ({eval_vcount}); modifier did not ship",
              file=sys.stderr); return 5
    print("depsgraph-export OK")
    return 0

if __name__ == "__main__":
    try:
        sys.exit(main())
    except Exception as e:
        import traceback; traceback.print_exc(); print(f"FATAL: {e}", file=sys.stderr); sys.exit(1)