|
8 | 8 | data itself — (x, y, z) -> (x, z, -y) on disk — with no node rotation. |
9 | 9 | The check parses the exported .gltf JSON and asserts the POSITION accessor |
10 | 10 | bounds equal the axis-converted evaluated bounding box, and that the node |
11 | | - carries neither rotation nor scale. Exporting with ``export_yup=False`` |
12 | | - writes raw Z-up data that every engine will display lying on its back. |
| 11 | + carries neither rotation nor scale. ``--no-yup`` exports with |
| 12 | + ``export_yup=False`` and still runs that bbox check, so the +Y-up |
| 13 | + conversion fails. That is the falsifier (``--same-axis`` in |
| 14 | + export-preset-axis). Exporting with ``export_yup=False`` writes raw Z-up |
| 15 | + data that every engine will display lying on its back. |
13 | 16 | 2. Modifiers ship evaluated geometry. ``export_apply=True`` applies the |
14 | 17 | crate's bevel modifier: the re-imported mesh matches the |
15 | 18 | depsgraph-evaluated mesh, not the base cage. With ``export_apply=False`` |
|
32 | 35 | check. Pass --output to also render a still: |
33 | 36 |
|
34 | 37 | blender --background --python gltf_export_roundtrip.py -- # check only |
| 38 | + blender --background --python gltf_export_roundtrip.py -- --no-yup # must fail |
35 | 39 | blender --background --python gltf_export_roundtrip.py -- --output c.png # + render |
36 | 40 | """ |
37 | 41 | import bpy, bmesh, sys, os, math, json, struct, shutil, tempfile, argparse |
@@ -244,7 +248,7 @@ def accessor_floats(idx, ncomp): |
244 | 248 | # --------------------------------------------------------------------------- |
245 | 249 | # The check. Distinct exit codes per contract; measured maxima printed on success. |
246 | 250 | # --------------------------------------------------------------------------- |
247 | | -def check(crate): |
| 251 | +def check(crate, export_kwargs): |
248 | 252 | # contract 0 (version guard): every kwarg we rely on still exists. |
249 | 253 | exp_props = {p.identifier for p in bpy.ops.export_scene.gltf.get_rna_type().properties} |
250 | 254 | imp_props = {p.identifier for p in bpy.ops.import_scene.gltf.get_rna_type().properties} |
@@ -285,7 +289,7 @@ def check(crate): |
285 | 289 | tmp = tempfile.mkdtemp(prefix="gltf_roundtrip_") |
286 | 290 | try: |
287 | 291 | path = os.path.join(tmp, "crate.gltf").replace("\\", "/") |
288 | | - bpy.ops.export_scene.gltf(filepath=path, **EXPORT_KWARGS) |
| 292 | + bpy.ops.export_scene.gltf(filepath=path, **export_kwargs) |
289 | 293 |
|
290 | 294 | # contract 1 (on disk): +Y-up is baked into vertex data, no node transform |
291 | 295 | g, acc_floats = read_gltf(path) |
@@ -592,13 +596,18 @@ def main(): |
592 | 596 | p.add_argument("--output", default=None, help="optional: render a still PNG here") |
593 | 597 | p.add_argument("--engine", default="eevee", choices=("eevee", "cycles"), |
594 | 598 | help="render engine for --output (cycles for GPU-less hosts)") |
| 599 | + p.add_argument("--no-yup", action="store_true", |
| 600 | + help="export with export_yup=False (must fail)") |
595 | 601 | args = p.parse_args(argv) |
596 | 602 |
|
597 | 603 | bpy.ops.wm.read_factory_settings(use_empty=True) |
598 | 604 | crate = build_crate() |
599 | 605 | for m in make_materials(): |
600 | 606 | crate.data.materials.append(m) |
601 | | - code = check(crate) |
| 607 | + kwargs = dict(EXPORT_KWARGS) |
| 608 | + if args.no_yup: |
| 609 | + kwargs["export_yup"] = False |
| 610 | + code = check(crate, kwargs) |
602 | 611 | if code: |
603 | 612 | return code |
604 | 613 |
|
|
0 commit comments