Preserve homestead context and garden-centred Blender design

This commit is contained in:
2026-09-10 20:52:43 +10:00
commit c1369d9ed7
321 changed files with 17843 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
import bpy,bmesh,math,random,os
from mathutils import Vector
random.seed(523)
OUT=os.path.dirname(os.path.abspath(__file__))
s=bpy.context.scene
# Ensure all closed construction solids and the atmosphere have outward-facing surfaces.
fixed=0
for o in bpy.data.objects:
if o.type=='MESH' and len(o.data.vertices)==8 and len(o.data.polygons)==6:
bm=bmesh.new(); bm.from_mesh(o.data); bmesh.ops.recalc_face_normals(bm,faces=list(bm.faces)); bm.to_mesh(o.data); bm.free(); fixed+=1
print('SOLID_NORMALS_CHECKED',fixed,flush=True)
def ground(x,y): return -.024*y+.004*x+.12*math.sin(x*.19)*math.cos(y*.14)+.055*math.sin(x*.72+y*.32)
# Put both tanks on the north-west side of the sleeping module, visible from the main camera.
prefixes=('2000 mm dark green poly rainwater tank','Poly tank','Tank service lid','Accessible tank outlet','Gravel tank pad')
for o in bpy.data.objects:
if not o.name.startswith(prefixes): continue
center=o.matrix_world@(sum((Vector(v) for v in o.bound_box),Vector())/8)
shift=Vector((0,4.1,0)) if center.y>-9 else Vector((-2.3,6.4,0))
shift.z=ground(center.x+shift.x,center.y+shift.y)-ground(center.x,center.y)
o.location+=shift
# Loose gravel fragments soften the hard geometry of unsealed paths.
vs=[]; fs=[]
routes=[([(1,14),(0,9),(0,7),(-3,6.2),(-7,5),(-9,1),(-10,-3),(-13,-4.6)],1.5), ([(0,7),(4,7.6),(8,8.4),(14,8),(19,7),(20,2)],1.1), ([(20,2),(20,-4.6),(18.2,-5.7)],1)]
for points,width in routes:
for a,b in zip(points,points[1:]):
a,b=Vector(a),Vector(b); delta=b-a; side=Vector((-delta.y,delta.x)).normalized()
for _ in range(int(delta.length*135)):
p=a+delta*random.random()+side*random.uniform(-width*.62,width*.62); x,y=p; z=ground(x,y)+.04; r=random.uniform(.013,.04); k=len(vs)
vs.extend([(x-r,y-r,z),(x+r,y-r*.5,z),(x+r*.7,y+r,z),(x-r*.7,y+r*.7,z),(x,y,z+r*.8)])
fs.extend([(k,k+1,k+4),(k+1,k+2,k+4),(k+2,k+3,k+4),(k+3,k,k+4)])
me=bpy.data.meshes.new('Loose gravel stones'); me.from_pydata(vs,[],fs); me.materials.append(bpy.data.materials['Loose local gravel - fine stones']); ob=bpy.data.objects.new('Scattered loose gravel and irregular path edges',me); bpy.data.collections['07 Terrain and paths'].objects.link(ob)
# Modest golden light; a low-density atmosphere scatters it through the canopy.
s.world.node_tree.nodes['Background'].inputs['Strength'].default_value=.6
s.view_settings.exposure=.3
sun=bpy.data.objects['Warm early morning sun from north-east']; sun.data.energy=3.0
v=next(n for n in bpy.data.materials['Very light canopy haze'].node_tree.nodes if n.type=='PRINCIPLED_VOLUME'); v.inputs['Density'].default_value=.0012
for area in [a for screen in bpy.data.screens for a in screen.areas if a.type=='VIEW_3D']:
area.spaces.active.shading.type='SOLID'; area.spaces.active.shading.color_type='MATERIAL'; area.spaces.active.region_3d.view_perspective='CAMERA'
bpy.data.objects['Subtle atmosphere over hillside'].hide_set(True)
# hide_set affects only the interactive viewport; the atmosphere remains enabled for render.
s.render.resolution_percentage=100; s.cycles.samples=96; s.render.filepath=os.path.join(OUT,'samford-modular-final.png')
bpy.ops.wm.save_as_mainfile(filepath=os.path.join(OUT,'samford-modular-home.blend'))
s.cycles.device='CPU'; s.cycles.samples=12; s.render.resolution_percentage=40; s.render.filepath=os.path.join(OUT,'samford-modular-light-check.png')
bpy.ops.render.render(write_still=True)
print('LIGHTING_CHECK_COMPLETE',flush=True)