Files
martinfencl.eu/locenice2/blender/prepare_model.py
T
martin.fencl ba7790daba
continuous-integration/drone/push Build is passing
feat: Add legacy shaders, model I/O, and project panel functionality
- Introduced `legacy-shaders.js` containing vertex and fragment shaders for rendering.
- Implemented `model-io.js` for parsing and encoding GLB files, ensuring compatibility with glTF 2.0.
- Created `project-panel.js` to manage project resources and survey overlays, enhancing user interaction.
- Added `solar.js` for calculating solar positions based on UTC time, latitude, and longitude.
- Developed `three-renderer.js` as a standard backend for Three.js, managing scene rendering and material synchronization.
- Included a new GLB model `dum.glb` for testing and demonstration purposes.
- Established `data_checks.mjs` for validating model data and ensuring integrity of assets.
- Created `prepare_export.py` to facilitate Blender GLB exports, maintaining material and node references.
- Added `vendor_three.py` to download and configure a local version of Three.js for the project.
2026-10-22 01:04:21 +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)