Files
martinfencl.eu/locenice/blender/prepare_model.py
T
2026-10-22 01:22:01 +02:00

66 lines
3.2 KiB
Python

"""Run in Blender, not system Python:
blender --background --python blender/prepare_model.py
Creates an editable .blend next to this script. Existing scene is replaced.
Keeps every configuration variant; helper collections are hidden in viewport.
"""
from pathlib import Path
import json
import bpy
ROOT=Path(__file__).resolve().parent.parent
SOURCE=ROOT/'models'/'dum.glb'
TARGET=Path(__file__).resolve().parent/'dum_locenice.blend'
if not SOURCE.is_file():
raise FileNotFoundError(SOURCE)
if TARGET.exists():
raise FileExistsError(f'Rename or remove {TARGET} before running again.')
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.gltf(filepath=str(SOURCE))
defaults=json.loads((ROOT/'data'/'catalog.json').read_text())['defaults']
scene=bpy.context.scene
scene.unit_settings.system='METRIC'
scene.unit_settings.scale_length=1.0
collections={}
for name in ['01_Exterior','02_Interior_walls','03_Kitchen_and_furniture','04_Site','05_Cutaway_alternatives','06_Standards_alternatives']:
col=bpy.data.collections.new(name);scene.collection.children.link(col);collections[name]=col
for obj in list(scene.objects):
if obj.type!='MESH':continue
props=obj
if props.get('stdHidden') or props.get('stdWhen') or props.get('stdBlind'):
key='06_Standards_alternatives'
elif props.get('interiorLayer') in ['cut','cutShell']:
key='05_Cutaway_alternatives'
elif props.get('interiorLayer')=='furniture':
key='03_Kitchen_and_furniture'
elif props.get('interiorLayer'):
key='02_Interior_walls'
elif props.get('siteLayer') or props.get('legacySite'):
key='04_Site'
else:key='01_Exterior'
for c in list(obj.users_collection):c.objects.unlink(obj)
collections[key].objects.link(obj)
# Stable ID comes from the GLB extras. Do not delete it during future editing.
obj['locenice_edit_note']='Keep loceniceId and configuration properties.'
condition=props.get('stdWhen',{})
hidden=bool(props.get('stdHidden')) or bool(props.get('stdBlind') and not defaults['blinds'])
if hasattr(condition,'items'):
hidden=hidden or any(defaults.get(k)!=v for k,v in condition.items())
obj.hide_set(hidden)
obj.hide_render=hidden
collections['05_Cutaway_alternatives'].hide_viewport=True
# Keep matching default variants visible; all alternatives remain in the file.
note=bpy.data.texts.new('READ_ME_Locenice')
note.write('''This is a visualisation mesh, not BIM. Units: metres.
The source includes multiple, mutually exclusive variants in the same location.
Use named collections to inspect them. Do not join all meshes or all materials.
loceniceId, stdWhen, stdBlind, windowNode and interiorLayer must stay on objects.
Detailed construction dimensions come from the project, not reference photos.
After exporting all variants to GLB, run tools/prepare_export.py.
Blender reorders glTF nodes, materials and textures. The included web manifest
is indexed for the supplied GLB; the preparation tool rebinds stable IDs.
A changed wall or cabinet also requires checking web collision volumes / camera presets.
''')
scene['locenice_metadata_json']=json.dumps(json.loads((ROOT/'data'/'scene.json').read_text()),ensure_ascii=False)
bpy.ops.wm.save_as_mainfile(filepath=str(TARGET))
print('Saved:',TARGET)