Files
spatial-lab/tests/blender_roundtrip.py

86 lines
3.8 KiB
Python

"""Integration test in real Blender, including geometry edits, pruning and undo.
blender -b --factory-startup --python tests/blender_roundtrip.py
Writes a verified demonstration and report to out/.
"""
import importlib.util
import sys
import tempfile
from pathlib import Path
import bpy
root = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(root))
from spatial_lab.examples import demo_project
from spatial_lab.project import Project, write_json
spec = importlib.util.spec_from_file_location('bridge', root / 'blender/bridge.py')
bridge = importlib.util.module_from_spec(spec)
spec.loader.exec_module(bridge)
scene = bridge.create_scene('Spatial Lab / Braided atrium')
bpy.context.window.scene = scene
with tempfile.TemporaryDirectory() as temporary:
path = Path(temporary) / 'study.json'
write_json(path, demo_project())
project = Project(path)
original = project.build()
bridge.import_bundle(original, scene)
original_check = bridge.verify_bundle(original, scene)
assert original_check['passed'], original_check
obj = next(o for o in scene.objects if o.get('spatial_lab_id') == 'braided-galleries/0000/main')
pointer = obj.as_pointer()
original_material = obj.material_slots[0].material
modifier = obj.modifiers.new('Artist bevel', 'BEVEL')
artist_material = bpy.data.materials.new('Artist material')
obj.material_slots[0].material = artist_material
marker = bpy.data.objects.new('Unrelated object', None)
scene.collection.objects.link(marker)
changed = project.apply([
{'action': 'set_parameter', 'name': 'width', 'value': 1.8},
{'action': 'set_parameter', 'name': 'levels', 'value': 2},
])
assert changed['geometries'] != original['geometries']
updated = bridge.import_bundle(changed, scene)
changed_check = bridge.verify_bundle(changed, scene)
assert changed_check['passed'], changed_check
assert updated['created'] == 0 and updated['updated'] == 16 and len(updated['removed']) == 1
assert obj.as_pointer() == pointer and obj.modifiers.get(modifier.name).as_pointer() == modifier.as_pointer()
assert obj.material_slots[0].material == artist_material and marker.name in scene.objects
restored = project.travel('undo')
assert restored == original
bridge.import_bundle(restored, scene)
undo_check = bridge.verify_bundle(restored, scene)
assert undo_check['passed'], undo_check
assert obj.as_pointer() == pointer and obj.material_slots[0].material == artist_material
obj.modifiers.remove(modifier)
obj.material_slots[0].material = original_material
bpy.data.objects.remove(marker, do_unlink=True)
bpy.data.materials.remove(artist_material)
out = root / 'out'
write_json(out / 'braided_atrium.bundle.json', restored)
write_json(out / 'blender_verification.json', {
'blender_version': bpy.app.version_string,
'initial': original_check, 'edited': changed_check, 'undo': undo_check,
'changed_import': updated,
'preserved_object_identity_modifiers_materials_and_unrelated_objects': True,
'undo_restored_identical_bundle': True,
})
from mathutils import Vector
target = Vector((0, 0, 4.5))
rotation = (target - Vector((45, -55, 35))).to_track_quat('-Z', 'Y')
for screen in bpy.data.screens:
for area in screen.areas:
if area.type == 'VIEW_3D':
view = area.spaces.active
bridge.enum_set(view.shading, 'type', 'SOLID')
bridge.enum_set(view.shading, 'color_type', 'MATERIAL')
view.region_3d.view_location = target
view.region_3d.view_distance = 62
view.region_3d.view_rotation = rotation
bpy.ops.wm.save_as_mainfile(filepath=str(out / 'Braided_Atrium.blend'))
print('PASS: changed geometry, pruned instance, preserved artist work, exact undo, Blender parity.')