```python
import bpy, math, random
from mathutils import Vector

random.seed(21)
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

# Materials
def mat(name, color, rough=.65, metal=0, emission=None):
    m = bpy.data.materials.new(name)
    m.diffuse_color = (*color, 1)
    m.use_nodes = True
    p = m.node_tree.nodes.get('Principled BSDF')
    p.inputs['Base Color'].default_value = (*color, 1)
    p.inputs['Roughness'].default_value = rough
    p.inputs['Metallic'].default_value = metal
    if emission:
        p.inputs['Emission Color'].default_value = (*color, 1)
        p.inputs['Emission Strength'].default_value = emission
    return m

wood = [mat('Honey cedar %02d'%i, (.31+i*.024, .135+i*.014, .047+i*.007)) for i in range(7)]
trim = mat('Dark oiled end grain', (.16,.067,.026))
cutwood = mat('Fresh split firewood', (.62,.36,.15))
deckm = [mat('Weathered dock cedar %d'%i, (.28+i*.024,.205+i*.018,.115+i*.013)) for i in range(5)]
roofm = [mat('Blue spruce shingles %d'%i, (.042+i*.007,.105+i*.009,.113+i*.009), .8) for i in range(6)]
stone = [mat('Chimney fieldstone %d'%i, (.27+i*.027,.28+i*.025,.26+i*.023)) for i in range(6)]
mortar = mat('Recessed charcoal mortar', (.17,.18,.16))
groundm = [mat('Meadow %d'%i, (.205+i*.011,.25+i*.012,.105+i*.006)) for i in range(6)]
earth = [mat('Earth strata %d'%i, (.19+i*.03,.115+i*.021,.063+i*.014)) for i in range(3)]
sand = mat('Wet shoreline clay', (.24,.24,.135))
water = mat('Still jade water', (.025,.205,.215), .19, .28)
water.node_tree.nodes.get('Principled BSDF').inputs['IOR'].default_value = 1.333
ripplemat = mat('Quiet water glints', (.16,.41,.40), .26, .25)
glass = mat('Warm amber window glass', (1,.48,.12), .26, emission=2.3)
iron = mat('Blackened iron', (.042,.051,.046), .35, .7)
brass = mat('Old brass', (.47,.27,.068), .28, .7)
leafm = [mat('Pine needles %d'%i, (.045+i*.019,.13+i*.027,.087+i*.013)) for i in range(5)]
goldm = [mat('Autumn birch leaves %d'%i, (.50+i*.072,.27+i*.048,.035+i*.012)) for i in range(5)]
bark = mat('Birch ivory bark', (.71,.67,.49))
grassm = [mat('Grass %d'%i, (.24+i*.047,.31+i*.037,.10+i*.012)) for i in range(4)]
lilymat = mat('Lily leaves', (.115,.30,.13), .45)
flower = mat('Water lily ivory', (.98,.80,.49), .4)
boatmat = mat('Canoe red cedar', (.39,.115,.05), .45)
rope_mat = mat('Hemp rope', (.49,.37,.19))
backmat = mat('Studio sand', (.68,.66,.57))

def mesh(name, verts, faces, material):
    me=bpy.data.meshes.new(name)
    me.from_pydata(verts, [], faces)
    me.materials.clear()
    if isinstance(material, list):
        for m in material: me.materials.append(m)
    elif material: me.materials.append(material)
    me.update()
    ob=bpy.data.objects.new(name,me)
    bpy.context.collection.objects.link(ob)
    return ob

def cube(name, loc, scale, material, bevel=0):
    bpy.ops.mesh.primitive_cube_add(size=1, location=loc)
    ob=bpy.context.object; ob.name=name
    ob.dimensions=scale
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    ob.data.materials.append(material)
    if bevel:
        mod=ob.modifiers.new('Soft hand-worked edges','BEVEL'); mod.width=bevel; mod.segments=2
        ob.modifiers.new('Weighted corner normals','WEIGHTED_NORMAL')
    return ob

def cyl(name, a, b, radius, material, vertices=10, r2=None):
    d=Vector(b)-Vector(a)
    bpy.ops.mesh.primitive_cone_add(vertices=vertices, radius1=radius, radius2=radius if r2 is None else r2, depth=d.length, location=(Vector(a)+Vector(b))/2)
    ob=bpy.context.object; ob.name=name
    ob.rotation_euler=d.to_track_quat('Z','Y').to_euler()
    ob.data.materials.append(material)
    return ob

def beam(name,a,b,width,depth,material, bevel=.015):
    ob=cube(name,(Vector(a)+Vector(b))/2,(width,depth,(Vector(b)-Vector(a)).length),material,bevel)
    ob.rotation_euler=(Vector(b)-Vector(a)).to_track_quat('Z','Y').to_euler()
    return ob

def line(name, points, radius, material, cyclic=False):
    cu=bpy.data.curves.new(name,'CURVE'); cu.dimensions='3D'
    cu.resolution_u=1; cu.bevel_depth=radius; cu.bevel_resolution=2
    sp=cu.splines.new('POLY'); sp.points.add(len(points)-1)
    for p,co in zip(sp.points,points): p.co=(*co,1)
    sp.use_cyclic_u=cyclic
    ob=bpy.data.objects.new(name,cu); bpy.context.collection.objects.link(ob)
    ob.data.materials.append(material)
    bpy.context.view_layer.objects.active=ob
    ob.select_set(True)
    bpy.ops.object.convert(target='MESH')
    ob.select_set(False)
    return ob

def rock(name, pos, scale, material=None):
    bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=1, radius=1, location=pos)
    ob=bpy.context.object; ob.name=name
    ob.scale=scale; ob.rotation_euler=(random.random()*.4,random.random()*.4,random.random()*6)
    ob.data.materials.append(material or random.choice(stone))
    bevel=ob.modifiers.new('Worn rock edges','BEVEL'); bevel.width=.045; bevel.segments=2
    ob.modifiers.new('Rock normals','WEIGHTED_NORMAL')
    return ob

# Organic terrain with a genuine recessed pond, rather than a disc laid on top.
N=112
pc=Vector((1.35,-1.5))
def inner(t):
    wav=1+.055*math.sin(3*t+.8)+.038*math.sin(7*t-1)
    return Vector((pc.x+2.85*math.cos(t)*wav, pc.y+2.02*math.sin(t)*wav))
def outer(t):
    d=Vector((math.cos(t),math.sin(t)))
    a=(d.x/6.25)**2+(d.y/5.05)**2
    b=2*(pc.x*d.x/6.25**2+pc.y*d.y/5.05**2)
    c=(pc.x/6.25)**2+(pc.y/5.05)**2-1
    r=(-b+math.sqrt(b*b-4*a*c))/(2*a)
    r*=1+.017*math.sin(9*t)+.012*math.sin(5*t)
    return pc+d*r

rings=[]
for f in [0,.08,.28,.57,.8,1]:
    ring=[]
    for i in range(N):
        t=2*math.pi*i/N
        p=inner(t).lerp(outer(t),f)
        z=.12 if f==0 else (.31 if f==.08 else .36+.026*math.sin(t*5+f*5))
        ring.append((p.x,p.y,z))
    rings.append(ring)
verts=sum(rings,[])
faces=[]
for j in range(len(rings)-1):
    for i in range(N):
        ni=(i+1)%N
        faces.extend([(j*N+i,j*N+ni,(j+1)*N+ni), (j*N+i,(j+1)*N+ni,(j+1)*N+i)])
land=mesh('Continuous meadow around inset pond',verts,faces,[sand]+groundm)
for p in land.data.polygons:
    p.material_index=0 if p.index<2*N else random.randint(1,len(groundm))
# Layered, substantial island edge
for k,(ztop,zbot,fac) in enumerate([(.36,-.04,1),(-.04,-.53,.995),(-.53,-.86,.955)]):
    vs=[]
    for z,f in [(ztop,fac),(zbot,fac-.028)]:
        for i in range(N):
            p=outer(2*math.pi*i/N)*f
            vs.append((p.x,p.y,z))
    fs=[(i,(i+1)%N,(i+1)%N+N,i+N) for i in range(N)]
    mesh('Exposed island soil layer',vs,fs,earth[2-k])
mesh('Pond basin',[(pc.x,pc.y,-.24)]+[(p[0],p[1],-.1) for p in rings[0]],[(0,i+1,(i+1)%N+1) for i in range(N)],sand)
wv=[(pc.x,pc.y,.15)]+[(p[0],p[1],.15) for p in rings[0]]
mesh('Unbroken pond surface',wv,[(0,i+1,(i+1)%N+1) for i in range(N)],water)

# Cabin stone footings and floor
cx=-2.1; xl=-3.85; xr=-.35; yf=.45; yb=3.30
cube('Stone foundation', (cx,1.85,.58),(3.65,3.05,.49),mortar,.06)
for y in [.37,3.35]:
    for i in range(9):
        cube('Foundation fieldstone',(xl+.2+i*.41,y,.58),(.385,.22,.36),random.choice(stone),.045)
for x in [xl-.045,xr+.045]:
    for j in range(7):
        cube('Foundation fieldstone',(x,.65+j*.42,.58),(.24,.39,.36),random.choice(stone),.04)
cube('Cabin wall core',(cx,1.875,1.94),(3.5,2.85,2.36),trim)
# Each course is individually modeled.
for i in range(12):
    z=.87+i*.194
    for y in [yf-.035,yb+.035]:
        cube('Horizontal cedar clapboard',(cx,y,z),(3.53,.105,.183),random.choice(wood),.018)
    for x in [xl-.035,xr+.035]:
        cube('Side cedar clapboard',(x,1.875,z),(.105,2.85,.183),random.choice(wood),.018)
for x in [xl-.04,xr+.04]:
    for y in [yf-.065,yb+.065]:
        cube('Corner timber',(x,y,1.97),(.15,.16,2.43),trim,.024)
# Gables, with tapering wooden courses
ridge=4.43; eave=3.15; slope=(ridge-eave)/1.95
for y in [yf-.06,yb+.06]:
    for i in range(7):
        z=3.16+i*.18
        half=(ridge-z)/slope
        half=min(1.76,half)
        if half>0:
            cube('Gable cedar course',(cx,y,z),(half*2,.12,.173),random.choice(wood),.012)
# Windows: front or side, built in local coordinates
def window(name, origin, width=.85, height=.92, side=False):
    # u horizontal across facade, v towards outside
    ox,oy,oz=origin
    def box(label,u,v,z,w,d,h,m,be=.01):
        if not side: return cube(name+' '+label,(ox+u,oy-v,oz+z),(w,d,h),m,be)
        return cube(name+' '+label,(ox+v,oy+u,oz+z),(d,w,h),m,be)
    box('shadow reveal',0,0,0,width+.18,.13,height+.18,trim)
    box('luminous pane',0,.078,0,width-.08,.025,height-.08,glass,.006)
    for u in [-width/2,width/2]: box('jamb',u,.12,0,.075,.13,height+.10,wood[5])
    for z in [-height/2,height/2]: box('lintel',0,.12,z,width+.13,.15,.075,wood[5])
    box('vertical mullion',0,.13,0,.044,.08,height,trim,.005)
    box('horizontal mullion',0,.13,0,width,.08,.044,trim,.005)
    box('projecting sill',0,.18,-height/2-.06,width+.23,.30,.085,wood[4])
window('Left front window',(-3.18,.33,2.06),.72,.92)
window('Right front window',(-1.10,.33,2.06),.77,.92)
window('Large pond-facing window',(-.23,1.49,2.05),1.27,1.02,True)
window('Rear window',(cx,3.44,2.0),.80,.86)
# Front door
cube('Door dark reveal',(-2.19,.30,1.74),(.91,.16,1.92),trim,.025)
for i in range(5):
    cube('Door vertical plank',(-2.53+i*.17,.195,1.72),(.16,.065,1.80),wood[(i+3)%7],.012)
for x in [-2.67,-1.71]:
    cube('Door frame',(x,.19,1.77),(.095,.16,2.04),wood[5],.015)
cube('Door lintel',(-2.19,.19,2.79),(1.05,.18,.11),wood[5],.02)
window('Door upper light',(-2.19,.135,2.18),.44,.48)
for z in [1.10,1.82]:
    cube('Forged door hinge',(-2.50,.12,z),(.24,.035,.042),iron,.01)
cyl('Door brass knob',(-1.86,.14,1.65),(-1.86,.065,1.65),.048,brass,12)

# Roof planks and overlapping individual blue-green shingles
def roof_prism(name,x1,x2,y1,y2,thick,m,extra=0):
    def zz(x): return ridge-abs(x-cx)*slope+extra
    vs=[(x1,y1,zz(x1)),(x2,y1,zz(x2)),(x2,y2,zz(x2)),(x1,y2,zz(x1))]
    vs += [(x,y,z-thick) for x,y,z in vs]
    return mesh(name,vs,[(0,1,2,3),(7,6,5,4),(0,4,5,1),(1,5,6,2),(2,6,7,3),(3,7,4,0)],m)
for sign in [-1,1]:
    a,b=sorted([cx,cx+sign*2.10])
    roof_prism('Solid roof decking',a,b,-.02,3.74,.12,trim)
    for row in range(8):
        d0=row*2.1/8
        d1=min(2.13,d0+.31)
        a,b=sorted([cx+sign*d0,cx+sign*d1])
        for j in range(10):
            y1=-.04+j*.392-(.196 if row%2 else 0)
            y2=min(3.76,y1+.38); y1=max(-.04,y1)
            if y2>y1:
                roof_prism('Overlapping cedar roof shingle',a,b,y1,y2,.05,random.choice(roofm),.035+(7-row)*.005)
for y in [-.065,3.78]:
    for sign in [-1,1]:
        beam('Gable bargeboard',(cx,y,4.49),(cx+sign*2.15,y,3.07),.14,.14,wood[2],.014)
for sign in [-1,1]:
    cube('Eave fascia',(cx+sign*2.12,1.85,3.075),(.13,3.88,.18),trim,.018)
for j in range(12):
    cyl('Ridge cap',(cx,-.08+j*.325,4.49),(cx,.245+j*.325,4.49),.10,roofm[2],8)
beam('Front gable king post',(cx,.245,3.13),(cx,.245,4.24),.095,.10,trim)
for s in [-1,1]:
    beam('Gable decorative diagonal',(cx,.24,3.16),(cx+s*1.15,.24,3.63),.085,.085,trim)

# Tall coursed fieldstone chimney, fully three dimensional
chx=-.10; chy=2.70
cube('Chimney mortar core',(chx,chy,2.68),(.72,.86,4.55),mortar,.035)
for row in range(15):
    z=.48+row*.303
    for side in [-1,1]:
        for j in range(2):
            x=chx+(j-.5)*.37
            cube('Chimney dressed face stone',(x+random.uniform(-.025,.025),chy+side*.445,z),(.35,.17,.274),random.choice(stone),.038)
        for j in range(2):
            y=chy+(j-.5)*.42
            cube('Chimney return stone',(chx+side*.39,y,z),(.17,.39,.275),random.choice(stone),.038)
cube('Chimney capstone',(chx,chy,5.02),(.98,1.09,.16),stone[4],.04)
cube('Soot-black flue',(chx,chy,5.115),(.57,.67,.045),iron,.015)
for x in [chx-.37,chx+.37]:
    cube('Flue lip',(x,chy,5.16),(.14,.88,.15),stone[2],.025)
for y in [chy-.37,chy+.37]:
    cube('Flue lip',(chx,y,5.16),(.67,.14,.15),stone[3],.025)

# Porch, stairs, and understated railings
for i in range(19):
    cube('Porch floorboard',(-4.03+i*.207,-.13,.80),(.195,1.12,.13),random.choice(deckm),.012)
for y in [-.64,.35]:
    cube('Porch rim joist',(-2.16,y,.68),(4.12,.13,.24),trim,.02)
for x in [-3.95,-.38]:
    for y in [-.58,.29]:
        cube('Porch support',(x,y,.50),(.17,.17,.58),trim,.018)
for x in [-3.99,-.26]:
    cube('Porch rail post',(x,-.59,1.22),(.125,.125,.97),wood[3],.015)
    cube('Post cap',(x,-.59,1.72),(.18,.18,.075),wood[5],.015)
    cube('Porch side handrail',(x,-.04,1.62),(.10,1.1,.09),wood[4],.018)
    for y in [-.38,-.1,.18]:
        cube('Porch baluster',(x,y,1.22),(.055,.055,.76),wood[2],.009)
for i in range(3):
    cube('Broad entry step',(-2.17,-.85-i*.26,.685-i*.125),(1.34,.31,.15),deckm[3],.018)
# Lamp beside doorway
def lantern(x,y,z):
    cube('Lantern wall mount',(x,y+.10,z+.13),(.11,.11,.26),iron,.02)
    beam('Lantern bracket',(x,y,z+.25),(x,y-.21,z+.25),.04,.04,iron)
    yy=y-.20
    cube('Lantern warm core',(x,yy,z),(.14,.12,.23),glass,.015)
    for xx in [x-.095,x+.095]:
        for y2 in [yy-.075,yy+.075]:
            cube('Lantern cage',(xx,y2,z),(.025,.025,.29),iron,.005)
    for zz in [z-.16,z+.16]:
        cube('Lantern cap',(x,yy,zz),(.24,.21,.055),iron,.018)
    bpy.ops.object.light_add(type='POINT', location=(x,yy-.12,z))
    bpy.context.object.name='Amber porch lamplight'
    bpy.context.object.data.energy=16
    bpy.context.object.data.color=(1,.48,.16)
    bpy.context.object.data.shadow_soft_size=.35
lantern(-1.65,.17,2.49)

# Dock: visible board ends, framing, posts, bolts and a rope coil
for x in [-.40,.46]:
    cube('Dock long stringer',(x,-.80,.40),(.12,3.13,.21),trim,.015)
for i in range(20):
    cube('Dock plank',(.03,.67-i*.148,.535),(1.11,.135,.115),random.choice(deckm),.013)
    for x in [-.37,.43]:
        cyl('Dock iron nail',(x,.67-i*.148,.590),(x,.67-i*.148,.599),.012,iron,6)
for y in [.50,-.72,-2.04]:
    for x in [-.48,.54]:
        cyl('Dock round piling',(x,y,-.22),(x,y,.84),.088,trim,12,.075)
        cyl('Piling cut top',(x,y,.841),(x,y,.856),.077,cutwood,12)
        for zz in [.64,.69]:
            cyl('Piling iron collar',(x,y,zz),(x,y,zz+.022),.09,iron,12)
for k in range(4):
    points=[]
    for i in range(65):
        t=i*math.tau/64
        rr=.105+k*.026
        points.append((.10+rr*math.cos(t),-.22+rr*math.sin(t),.61+k*.008))
    line('Coiled dock rope',points,.012,rope_mat,True)

# Small open canoe, moored alongside the end of the dock
bc=Vector((1.24,-2.13,.0)); angle=-.18
def bp(x,y,z):
    return (bc.x+x*math.cos(angle)-y*math.sin(angle),bc.y+x*math.sin(angle)+y*math.cos(angle),z)
n=48
vs=[]
for w,l,z in [(.38,1.08,.43),(.24,.83,.06),(.19,.76,.115),(.33,1.02,.415)]:
    for i in range(n):
        t=i*math.tau/n
        vs.append(bp(w*math.sin(t)*(0.78+.22*abs(math.sin(t))),l*math.cos(t),z))
fs=[]
for ring in range(3):
    for i in range(n):
        j=(i+1)%n
        fs.append((ring*n+i,ring*n+j,(ring+1)*n+j,(ring+1)*n+i))
for i in range(n): fs.append((i,(i+1)%n,3*n+(i+1)%n,3*n+i))
fs.append(tuple(range(2*n,3*n)))
mesh('Hollow red cedar canoe hull',vs,fs,boatmat)
line('Canoe rounded gunwale',[bp(.38*math.sin(i*math.tau/n)*(.78+.22*abs(math.sin(i*math.tau/n))),1.08*math.cos(i*math.tau/n),.438) for i in range(n)],.028,wood[5],True)
for y in [-.45,.40]:
    ob=cube('Canoe wooden seat',bp(0,y,.35),(.55,.18,.065),wood[5],.015)
    ob.rotation_euler.z=angle
for y in [-.67,-.23,.21,.65]:
    pts=[bp(.28*math.sin(t),y,.145+.24*(abs(math.sin(t))**1.6)) for t in [-math.pi/2+i*math.pi/18 for i in range(19)]]
    line('Canoe internal rib',pts,.018,trim)
line('Painter rope',[bp(0,1.06,.44),(.76,-1.27,.48),(.54,-.72,.72)],.013,rope_mat)
cyl('Oar shaft',(-.29,-1.05,.64),(.28,-2.03,.67),.026,wood[5],10)
ob=cube('Oar blade',(.36,-2.18,.67),(.15,.42,.045),cutwood,.045); ob.rotation_euler.z=-.52

# Water ripples, shore stones, and lily leaves
for x,y,rx,ry in [(2.52,-2.6,.67,.12),(2.85,-1.15,.58,.11),(.55,-2.05,.21,.08),(-.48,-.73,.19,.07),(1.1,-3.12,.40,.08),(3.27,-.4,.35,.07)]:
    for k in range(2):
        pts=[(x+(rx+k*.065)*math.cos(t),y+(ry+k*.03)*math.sin(t),.157) for t in [i*math.pi/28 for i in range(5,48)]]
        line('Fine arc on water',pts,.008,ripplemat)
for i in range(43):
    t=i*math.tau/43+random.uniform(-.045,.045)
    p=inner(t).lerp(outer(t),random.uniform(.015,.045))
    r=random.uniform(.12,.27)
    rock('Shoreline boulder',(p.x,p.y,.27),(r*1.4,r,.12+random.random()*.11))
for x,y,r in [(3.16,-2.47,.20),(3.52,-2.18,.24),(3.12,-2.03,.17),(2.95,-2.75,.16),(2.95,-.16,.18),(3.26,-.02,.16)]:
    vs=[(x,y,.177)]
    for i in range(23):
        t=.16+i*(math.tau-.32)/22
        vs.append((x+r*math.cos(t),y+r*.8*math.sin(t),.177+random.random()*.006))
    mesh('Notched lily pad',vs,[(0,i,i+1) for i in range(1,23)],lilymat)
for x,y in [(3.52,-2.18),(2.95,-.16)]:
    for j in range(7):
        t=j*math.tau/7
        ob=rock('Water lily petal',(x+.067*math.cos(t),y+.067*math.sin(t),.217),(.076,.036,.025),flower)
        ob.rotation_euler=(0,-.25,t)
    rock('Lily golden heart',(x,y,.238),(.037,.037,.025),goldm[3])

# Firewood stacked against the left side, with sawn ends
for row in range(4):
    for j in range(5-row%2):
        x=-4.09-row*.012; y=.85+j*.32+(row%2)*.13; z=.50+row*.18
        cyl('Stacked split log',(x-.27,y,z),(x+.22,y,z),.11,trim,9)
        cyl('Pale log end',(x-.278,y,z),(x-.283,y,z),.085,cutwood,9)
cube('Woodpile weather cover',(-4.13,1.5,1.23),(.74,2.0,.10),deckm[1],.025)
# Rain barrel
barx,bary=-3.62,-.10
cyl('Coopered rain barrel',(barx,bary,.86),(barx,bary,1.51),.265,wood[2],16,.25)
for zz in [.94,1.40]:
    cyl('Barrel iron hoop',(barx,bary,zz),(barx,bary,zz+.055),.272,iron,16)
cyl('Barrel lid',(barx,bary,1.515),(barx,bary,1.54),.247,cutwood,16)
for x in [-.12,0,.12]:
    line('Barrel lid seams',[(barx+x,bary-.20,1.545),(barx+x,bary+.20,1.545)],.006,trim)

# Trees with trunks and actual branching, modeled foliage in the GLB as well.
def pine(x,y,h):
    z0=.36
    cyl('Pine trunk',(x,y,z0),(x,y,z0+h*.96),.14,trim,11,.042)
    for k in range(6):
        z=z0+h*(.28+k*.115)
        rad=h*(.225-k*.025)
        for j in range(5):
            a=j*math.tau/5+k*.72
            cyl('Pine radial branch',(x,y,z+.12),(x+rad*.83*math.cos(a),y+rad*.83*math.sin(a),z-.13),.04,trim,7,.012)
        seg=13; vv=[]
        for rr,zz in [(rad*.69,z-.05),(rad,z+.09),(rad*.63,z+.40),(rad*.035,z+h*.33)]:
            for j in range(seg):
                a=j*math.tau/seg
                r=rr*random.uniform(.87,1.14)
                vv.append((x+r*math.cos(a),y+r*math.sin(a),zz+random.uniform(-.055,.055)))
        ff=[]
        for ring in range(3):
            for j in range(seg):
                nj=(j+1)%seg
                ff.extend([(ring*seg+j,ring*seg+nj,(ring+1)*seg+nj),(ring*seg+j,(ring+1)*seg+nj,(ring+1)*seg+j)])
        ff.append(tuple(range(seg-1,-1,-1)))
        ob=mesh('Layered evergreen boughs',vv,ff,leafm)
        for poly in ob.data.polygons: poly.material_index=random.choice([0,1,2,2,3,4])
    for j in range(5):
        a=j*math.tau/5
        cyl('Pine exposed root',(x,y,.47),(x+.40*math.cos(a),y+.40*math.sin(a),.36),.065,trim,7,.022)
pine(-4.54,2.90,4.42)
pine(-2.98,4.02,4.96)
pine(1.30,3.60,4.28)
pine(-5.02,-.38,3.12)

def birch(x,y,h):
    cyl('Birch trunk',(x,y,.35),(x+.13,y,h+.35),.105,bark,10,.033)
    for k in range(10):
        z=.55+k*h*.084
        # Small dark marks are geometry, not a nonportable shader.
        cyl('Birch bark scar',(x-.012,y,z),(x-.011,y,z+.032),.108,trim,10,.105)
    for k in range(9):
        a=k*2.39
        z=1.25+k*h*.062
        reach=.60+random.random()*.35
        ex=x+math.cos(a)*reach; ey=y+math.sin(a)*reach; ez=z+.65
        cyl('Birch ascending branch',(x+.06,y,z),(ex,ey,ez),.047,bark,8,.014)
        for j in range(3):
            ox=ex+random.uniform(-.30,.30); oy=ey+random.uniform(-.30,.30)
            rock('Golden birch crown',(ox,oy,ez+random.uniform(-.1,.4)),(.53,.47,.54),random.choice(goldm))
    rock('Birch crown tip',(x+.13,y,h+.28),(.65,.56,.60),goldm[3])
birch(4.20,1.35,3.15)
birch(3.13,3.30,3.63)

# Stepping stone trail, grasses, reeds and undergrowth
for i in range(8):
    x=-2.17-.20*i+.12*math.sin(i)
    y=-1.58-i*.35
    rock('Flat path stepping stone',(x,y,.39),(.30,.21,.055),stone[3+i%3])
def on_land(x,y):
    t=math.atan2(y-pc.y,x-pc.x)
    p=inner(t)
    # ellipse-scaled test handles the noncircular bank correctly
    v=Vector((x-pc.x,y-pc.y)); edge=p-pc
    pond=(v.x/2.85)**2+(v.y/2.02)**2
    limit=(edge.x/2.85)**2+(edge.y/2.02)**2
    return pond>limit*1.17 and (x/6.0)**2+(y/4.85)**2<.97
def grass_clump(x,y,z=.37, height=.23, reed=False):
    vv=[]; ff=[]
    for j in range(random.randint(5,8)):
        a=random.random()*math.tau
        h=height*random.uniform(.65,1.25)
        bx=x+random.uniform(-.055,.055); by=y+random.uniform(-.055,.055)
        w=.018 if reed else .028
        dx=math.cos(a); dy=math.sin(a)
        idx=len(vv)
        vv.extend([(bx-dy*w,by+dx*w,z),(bx+dy*w,by-dx*w,z),(bx+dx*.09,by+dy*.09,z+h)])
        ff.append((idx,idx+1,idx+2))
    mesh('Shore rushes' if reed else 'Meadow grass tuft',vv,ff,random.choice(grassm))
for i in range(270):
    x=random.uniform(-5.8,5.8); y=random.uniform(-4.6,4.6)
    if not on_land(x,y): continue
    if -4.35<x<.6 and -.95<y<3.6: continue
    if abs(x-(-2.17+.56*(y+1.58)))<.43 and y<-1.35: continue
    grass_clump(x,y,height=random.uniform(.12,.27))
for t in [.13,.26,.4,1.1,2.65,3.40,4.28,5.64]:
    p=inner(t).lerp(outer(t),.025)
    for j in range(4):
        x=p.x+random.uniform(-.13,.13); y=p.y+random.uniform(-.13,.13)
        grass_clump(x,y,.25,.46,True)
        if j<2:
            cyl('Cattail stalk',(x,y,.25),(x+.035,y,.95),.012,grassm[1],6)
            cyl('Cattail velvet head',(x+.035,y,.79),(x+.035,y,1.0),.035,trim,8)
for x,y in [(-4.70,1.1),(-4.50,3.8),(2.2,3.1),(4.65,.55),(3.7,2.4),(-.5,4.1)]:
    for j in range(4):
        rock('Low woodland shrub',(x+random.uniform(-.3,.3),y+random.uniform(-.25,.25),.52),(.32,.29,.30),leafm[2+j%3])
for i in range(22):
    x=random.uniform(-5.4,5.2); y=random.uniform(-4.2,4.3)
    if on_land(x,y) and not (-4.4<x<.8 and -1<y<3.6):
        rock('Meadow pebble',(x,y,.40),(.08,.06,.05),stone[3])

# Presentation: a softly lit, tactile miniature landscape.
cube('Matte presentation ground',(0,0,-1.02),(200,200,.20),backmat,.02)
world=bpy.data.worlds.new('Blue afternoon ambience')
bpy.context.scene.world=world
world.use_nodes=True
world.node_tree.nodes['Background'].inputs[0].default_value=(.53,.67,.78,1)
world.node_tree.nodes['Background'].inputs[1].default_value=.35

def area(name,loc,energy,color,size,target):
    bpy.ops.object.light_add(type='AREA',location=loc)
    o=bpy.context.object; o.name=name
    o.data.energy=energy; o.data.color=color; o.data.shape='DISK'; o.data.size=size
    o.rotation_euler=(Vector(target)-o.location).to_track_quat('-Z','Y').to_euler()
area('Large warm sunset softbox',(-6,-7,12),1700,(1,.79,.54),7,(0,0,0))
area('Cool sky fill',(7,-1,9),1050,(.62,.79,1),8,(0,0,1))
area('Woodland rim light',(-1,7,11),1900,(1,.86,.59),6,(0,1,2))
bpy.ops.object.camera_add(location=(10.5,-16.5,11.6))
cam=bpy.context.object; cam.name='Cabin pond presentation'
target=Vector((-.15,.0,1.35))
cam.rotation_euler=(target-cam.location).to_track_quat('-Z','Y').to_euler()
cam.data.type='ORTHO'; cam.data.ortho_scale=15.5; cam.data.lens=45
scene=bpy.context.scene; scene.camera=cam
scene.render.engine='CYCLES'
scene.cycles.samples=64
scene.cycles.use_denoising=True
scene.render.resolution_x=1536; scene.render.resolution_y=1536; scene.render.resolution_percentage=100
scene.view_settings.view_transform='AgX'
scene.view_settings.look='AgX - Medium High Contrast'
scene.render.image_settings.file_format='PNG'
scene.render.filepath='/mnt/data/cabin_pond.png'
scene.world.color=(.5,.6,.7)
# Use physical geometry throughout; bevels remain editable in the complete scene.
bpy.ops.wm.save_as_mainfile(filepath='/mnt/data/scene.blend')
```