Preserve homestead context and garden-centred Blender design
This commit is contained in:
100
source/samford-retreat-garage/optimize_playback.py
Normal file
100
source/samford-retreat-garage/optimize_playback.py
Normal file
@@ -0,0 +1,100 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user