Files
NODEDC_MISSION_CORE/tools/rover-scene/export_rover.py
T

50 lines
4.0 KiB
Python

"""Immutable-source evaluated geometry export, owner-selected complete v020."""
import bpy,json,hashlib,struct
from pathlib import Path
from mathutils import Vector,Matrix
root=Path.cwd();source=Path(bpy.data.filepath)
donor=root.parent/'NODEDC_ENGINE_INFRA/nodedc-source/public/3dassetnode/model.glb'
with donor.open('rb') as f:
f.read(12);n,t=struct.unpack('<II',f.read(8));donor_json=json.loads(f.read(n))
# Same physical PBR parameters as the drone's untextured metal/rubber/steel.
material_rules={'metal':1,'rubber':2,'steel':6,'paint':3}
materials={}
for kind,index in material_rules.items():
spec=donor_json['materials'][index]['pbrMetallicRoughness'];m=bpy.data.materials.new('NodeDC '+kind);m.use_nodes=True
bs=m.node_tree.nodes.get('Principled BSDF');bs.inputs['Base Color'].default_value=spec.get('baseColorFactor',[1,1,1,1]);bs.inputs['Metallic'].default_value=spec.get('metallicFactor',1);bs.inputs['Roughness'].default_value=spec.get('roughnessFactor',1);materials[kind]=m
source_scene=bpy.context.scene;source_scene.frame_set(1);dg=bpy.context.evaluated_depsgraph_get()
instances=[]
for inst in dg.object_instances:
o=inst.object;src=o.original;cols=[c.name for c in src.users_collection]
if o.type not in {'MESH','CURVE'} or src.hide_render:continue
if any(c.hide_render for c in src.users_collection) or any(('STUDIO' in c or 'BOOLEAN' in c or 'DATUM' in c) for c in cols):continue
# Exclude standalone instancing source collections; their placed instances survive.
if not inst.is_instance and not src.visible_get():continue
mesh=bpy.data.meshes.new_from_object(o,preserve_all_data_layers=True,depsgraph=dg)
names=[slot.material.name.lower() if slot.material else '' for slot in o.material_slots]
for idx in range(len(mesh.materials)):
name=names[idx] if idx<len(names) else ''
kind='rubber' if any(k in name for k in ['rubber','gasket','polymer','elastomer','jacket','asa']) else 'steel' if any(k in name for k in ['zinc','steel','brass','spring']) else 'paint' if any(k in name for k in ['paint','coated','black','charcoal']) else 'metal'
mesh.materials[idx]=materials[kind]
if not mesh.materials:mesh.materials.append(materials['metal'])
instances.append((src.name,mesh,inst.matrix_world.copy(),inst.is_instance))
scene=bpy.data.scenes.new('Mission Core rover export');bpy.context.window.scene=scene
objects=[]
for name,mesh,matrix,_ in instances:
o=bpy.data.objects.new(name,mesh);scene.collection.objects.link(o);o.matrix_world=matrix;objects.append(o)
bpy.context.view_layer.update()
lo=Vector((min((o.matrix_world@Vector(v))[i] for o in objects for v in o.bound_box) for i in range(3)))
hi=Vector((max((o.matrix_world@Vector(v))[i] for o in objects for v in o.bound_box) for i in range(3)))
shift=Vector((-(lo.x+hi.x)/2,-(lo.y+hi.y)/2,-lo.z))
for o in objects:o.matrix_world=Matrix.Translation(shift)@o.matrix_world
source_object_count=len(objects)
bpy.ops.object.select_all(action='SELECT')
bpy.context.view_layer.objects.active=objects[0]
bpy.ops.object.join()
joined=bpy.context.object
for uv in list(joined.data.uv_layers):joined.data.uv_layers.remove(uv)
out=root/'apps/control-station/public/rover-scene/dcd006-v020.glb'
bpy.ops.export_scene.gltf(filepath=str(out),export_format='GLB',use_selection=True,use_active_scene=True,export_animations=False,export_cameras=False,export_lights=False,export_extras=False,export_yup=True,export_apply=False,export_texcoords=False,export_attributes=False,export_vertex_color='NONE')
manifest={'source':source.name,'source_sha256':hashlib.sha256(source.read_bytes()).hexdigest(),'model':'DCD-006 v020 complete rover','source_object_count':source_object_count,'render_mesh_count':1,'original_bounds':[list(lo),list(hi)],'dimensions_m':list(hi-lo),'translation_m':list(shift),'glb_bytes':out.stat().st_size,'glb_sha256':hashlib.sha256(out.read_bytes()).hexdigest(),'material_source_sha256':hashlib.sha256(donor.read_bytes()).hexdigest(),'material_rules':material_rules,'source_saved':False}
(out.parent/'dcd006-v020.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2)+'\n');print(json.dumps(manifest))