101 lines
6.7 KiB
Python
101 lines
6.7 KiB
Python
import bpy,json,time,statistics,os
|
|
from mathutils import Vector
|
|
OUT=os.path.dirname(os.path.abspath(__file__))
|
|
s=bpy.context.scene
|
|
original=list(s.objects)
|
|
visible=[o for o in original if o.visible_get()]
|
|
render_flags={o:o.hide_render for o in original}
|
|
render_settings=(s.render.engine,s.render.resolution_percentage,s.render.filepath)
|
|
def preview(tag,objects):
|
|
for o in s.objects:o.hide_render=o not in objects
|
|
s.render.engine='BLENDER_WORKBENCH';s.render.resolution_percentage=40
|
|
s.display.shading.light='STUDIO';s.display.shading.color_type='MATERIAL'
|
|
s.display.shading.show_shadows=False;s.display.shading.show_cavity=False
|
|
s.display.shading.show_specular_highlight=False
|
|
times=[]
|
|
for f in [1,60,2460]:
|
|
s.frame_set(f);s.render.filepath=os.path.join(OUT,tag+('-garage' if f==2460 else '')+'.png')
|
|
t=time.perf_counter();bpy.ops.render.render(write_still=f!=60);times.append(time.perf_counter()-t)
|
|
return times
|
|
base_objects={o for o in visible if o.display_type not in {'WIRE','BOUNDS'}}
|
|
baseline=preview('performance-before',base_objects)
|
|
for o,flag in render_flags.items():o.hide_render=flag
|
|
s.render.engine=render_settings[0]
|
|
s.frame_set(1)
|
|
fast=bpy.data.collections.new('00 FAST TOUR - lightweight viewport');s.collection.children.link(fast)
|
|
fast.hide_render=True
|
|
def combined(name,objects):
|
|
verts=[];faces=[];indices=[];smooth=[];materials=[];mat_map={}
|
|
for o in objects:
|
|
me=o.data;is_leaf='individual canopy leaves' in o.name
|
|
mi=[]
|
|
for mat in me.materials:
|
|
if mat not in mat_map:
|
|
mat_map[mat]=len(materials);materials.append(mat)
|
|
mi.append(mat_map[mat])
|
|
def material_idx(i):return mi[i] if mi and i<len(mi) else 0
|
|
if is_leaf:
|
|
# Keep one complete leaf in four. Flatten its four tiny triangles
|
|
# into one quad and enlarge slightly to preserve canopy coverage.
|
|
assert len(me.vertices)%5==0 and len(me.polygons)==len(me.vertices)//5*4
|
|
for k in range(0,len(me.vertices),20):
|
|
c=sum((me.vertices[k+j].co for j in range(4)),Vector())/4
|
|
off=len(verts)
|
|
verts.extend(tuple(o.matrix_world@(c+(me.vertices[k+j].co-c)*1.55)) for j in range(4))
|
|
faces.append((off,off+1,off+2,off+3));indices.append(material_idx(me.polygons[k//5*4].material_index));smooth.append(False)
|
|
else:
|
|
off=len(verts);verts.extend(tuple(o.matrix_world@v.co) for v in me.vertices)
|
|
faces.extend(tuple(off+i for i in p.vertices) for p in me.polygons)
|
|
indices.extend(material_idx(p.material_index) for p in me.polygons)
|
|
smooth.extend(p.use_smooth for p in me.polygons)
|
|
if not faces:return
|
|
me=bpy.data.meshes.new(name);me.from_pydata(verts,[],faces);me.update()
|
|
for m in materials:me.materials.append(m)
|
|
me.polygons.foreach_set('material_index',indices);me.polygons.foreach_set('use_smooth',smooth)
|
|
ob=bpy.data.objects.new(name,me);fast.objects.link(ob)
|
|
ob['Purpose']='Lightweight static tour display. Original editable geometry is retained in the DETAIL collection and used for renders.'
|
|
ob['Source objects']=len(objects)
|
|
return ob
|
|
seen=set()
|
|
for col in list(bpy.data.collections):
|
|
if col==fast:continue
|
|
obs=[o for o in col.objects if o in base_objects and o.type=='MESH' and o not in seen and not o.animation_data and not o.parent]
|
|
if not obs:continue
|
|
combined('FAST | '+col.name,obs);seen.update(obs)
|
|
# Originals remain fully editable, with all materials and render visibility intact.
|
|
# Globally disabling their collection affects only viewport evaluation.
|
|
detail=bpy.data.collections.new('DETAIL - full geometry for renders');s.collection.children.link(detail)
|
|
for col in list(s.collection.children):
|
|
if col in {fast,detail} or col.name.startswith('22 PLAY'):continue
|
|
s.collection.children.unlink(col);detail.children.link(col)
|
|
detail.hide_viewport=True
|
|
for screen in bpy.data.screens:
|
|
for a in screen.areas:
|
|
if a.type=='VIEW_3D':
|
|
sp=a.spaces.active;sp.shading.type='SOLID';sp.shading.light='STUDIO';sp.shading.color_type='MATERIAL'
|
|
sp.shading.show_cavity=False;sp.shading.show_shadows=False;sp.shading.show_specular_highlight=False
|
|
sp.overlay.show_overlays=False;sp.region_3d.view_perspective='CAMERA'
|
|
s.sync_mode='FRAME_DROP';s.cycles.preview_samples=8
|
|
s['Viewport performance']='FAST TOUR provides simplified static meshes for navigation. Detailed geometry is retained under DETAIL, disabled only in the viewport. Final Cycles renders automatically use DETAIL and omit FAST TOUR.'
|
|
s['Editing full detail']='In the Outliner filter menu show Disable in Viewports (monitor icon). Enable DETAIL and disable FAST TOUR to edit the full model. Return to FAST TOUR for playback.'
|
|
fast_objects=set(fast.objects)|{o for o in original if o.type in {'CAMERA','FONT'} and o in base_objects}
|
|
# Make preview-only proxies available to the independent Workbench test.
|
|
fast.hide_render=False
|
|
optimized=preview('performance-after',fast_objects)
|
|
fast.hide_render=True
|
|
for o,flag in render_flags.items():o.hide_render=flag
|
|
for o in fast.objects:o.hide_render=False
|
|
s.render.engine=render_settings[0];s.render.resolution_percentage=render_settings[1];s.render.filepath=render_settings[2]
|
|
s.frame_set(1);bpy.context.view_layer.update()
|
|
times=[]
|
|
for f in range(1,97):
|
|
t=time.perf_counter();s.frame_set(f);bpy.context.view_layer.update();times.append(time.perf_counter()-t)
|
|
now=[o for o in s.objects if o.visible_get()]
|
|
report={'before_visible_objects':len(visible),'after_visible_objects':len(now),'before_visible_faces':sum(len(o.data.polygons) for o in visible if o.type=='MESH'),'after_visible_faces':sum(len(o.data.polygons) for o in now if o.type=='MESH'),'workbench_before_seconds':baseline,'workbench_after_seconds':optimized,'after_frame_update_ms_median':statistics.median(times)*1000,'original_render_visibility_preserved':all(o.hide_render==v for o,v in render_flags.items()),'original_objects_preserved':all(o.name in s.objects for o in original),'fast_geometry_excluded_from_renders':fast.hide_render,'detailed_geometry_disabled_only_in_viewport':detail.hide_viewport and not detail.hide_render}
|
|
assert report['original_render_visibility_preserved'] and report['original_objects_preserved'] and report['fast_geometry_excluded_from_renders'] and report['detailed_geometry_disabled_only_in_viewport']
|
|
json.dump(report,open(os.path.join(OUT,'performance-checks.json'),'w'),indent=2)
|
|
s.frame_set(1);s.camera=bpy.data.objects['PLAY TOUR camera']
|
|
filename='samford-solarpunk-smooth-tour.blend' if bpy.data.objects.get('TESLA MODEL Y - pearl white flowing body') else 'samford-retreat-smooth-tour.blend'
|
|
bpy.ops.wm.save_as_mainfile(filepath=os.path.join(OUT,filename))
|
|
print('PERFORMANCE_RESULT '+json.dumps(report),flush=True)
|