import bpy, random, math, os from mathutils import Vector random.seed(119) OUT=os.path.dirname(os.path.abspath(__file__)) s=bpy.context.scene # Layer individual leaves into substantial evergreen canopies instead of sparse twigs. for ob in list(bpy.data.objects): if 'individual canopy leaves' not in ob.name: continue old=ob.data verts=[tuple(v.co) for v in old.vertices]; faces=[tuple(p.vertices) for p in old.polygons]; inds=[p.material_index for p in old.polygons] original=list(verts) for k in range(0,len(original),5): center=Vector(original[k+4]); index=inds[(k//5)*4] for repeat in range(6): offset=Vector((random.uniform(-.36,.36),random.uniform(-.36,.36),random.uniform(-.23,.23))) a=random.uniform(-1,1); scale=random.uniform(.95,1.35); j=len(verts) for v in original[k:k+5]: d=(Vector(v)-center)*scale d=Vector((d.x*math.cos(a)-d.y*math.sin(a),d.x*math.sin(a)+d.y*math.cos(a),d.z)) verts.append(tuple(center+offset+d)) faces.extend([(j,j+1,j+4),(j+1,j+2,j+4),(j+2,j+3,j+4),(j+3,j,j+4)]); inds.extend([index]*4) me=bpy.data.meshes.new(ob.name+' full layered foliage'); me.from_pydata(verts,[],faces); me.update() for m in old.materials: me.materials.append(m) for p,mi in zip(me.polygons,inds): p.material_index=mi ob.data=me; bpy.data.meshes.remove(old) print('CANOPY',ob.name,len(verts),flush=True) # Move two foreground eucalyptus trees aside to leave greenhouse and beds visible. for ob in bpy.data.collections['09 Eucalyptus and fruit trees'].objects: if not ob.data or ob.type!='MESH': continue center=sum((Vector(b) for b in ob.bound_box),Vector())/8 center=ob.matrix_world@center if center.x>26 and center.y>5: ob.location.x+=17 # Soft blue ambient sky with a warm north-east morning sun. world=s.world; nodes=world.node_tree.nodes; bg=nodes.get('Background') for link in list(bg.inputs['Color'].links): world.node_tree.links.remove(link) bg.inputs['Color'].default_value=(.38,.49,.64,1); bg.inputs['Strength'].default_value=.45 s.view_settings.exposure=0 sun=bpy.data.objects['Warm early morning sun from north-east']; sun.data.energy=2.3; sun.data.color=(1,.78,.53) sun.location=(35,40,23); sun.rotation_euler=(Vector((-15,-20,0))-sun.location).to_track_quat('-Z','Y').to_euler() # Broad overlapping, wooded ridgelines complete the landscape beyond the site. collection=bpy.data.collections['07 Terrain and paths'] for row,(y,base,color) in enumerate([(-58,9,(.13,.21,.16)),(-87,17,(.20,.29,.26)),(-125,23,(.29,.37,.36))]): mat=bpy.data.materials.new('Distant wooded ridge '+str(row)); mat.diffuse_color=(*color,1); mat.use_nodes=True; p=mat.node_tree.nodes.get('Principled BSDF'); p.inputs['Base Color'].default_value=(*color,1); p.inputs['Roughness'].default_value=1 vs=[]; fs=[] for j in range(9): yy=y+(j-4)*7 for i in range(81): x=-180+i*4.5; crest=base+4*math.sin(x*.048+row)+2.2*math.sin(x*.105+row*2) z=crest*max(0,1-((j-4)/4.5)**2)-2 vs.append((x,yy,z)) for j in range(8): for i in range(80): k=j*81+i; fs.append((k,k+1,k+82,k+81)) me=bpy.data.meshes.new('Ridgeline terrain'); me.from_pydata(vs,[],fs); me.materials.append(mat); ob=bpy.data.objects.new('Layered subtropical ridgeline',me); collection.objects.link(ob) for p in me.polygons: p.use_smooth=True # Tone down new timber and roof glare; retain warm structure and silver-grey cladding. for name in ['Silvered hardwood weatherboard - X grain','Silvered hardwood weatherboard - Y grain']: m=bpy.data.materials[name]; ramp=next(n for n in m.node_tree.nodes if n.type=='VALTORGB') ramp.color_ramp.elements[0].color=(.115,.12,.105,1); ramp.color_ramp.elements[1].color=(.30,.30,.26,1) bpy.data.objects['NE aerial - complete adaptable compound'].data.lens=48 s.render.resolution_x=2560; s.render.resolution_y=1440; s.render.resolution_percentage=100; s.render.filepath=os.path.join(OUT,'samford-modular-final.png'); s.cycles.samples=96 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-refined-draft.png') bpy.ops.render.render(write_still=True) print('REFINED_DRAFT_COMPLETE',flush=True)