thought
Ah, yes! `get_terrain_height` is not defined in the scope of this new session call. I must make sure that all dependent helper functions like `get_terrain_height` are defined inside the script being run.
Let's make sure that whenever I call execution blocks, I include `get_terrain_height` in them. This is very easy to do.

Let's now write a master compilation script.
This script will do EVERYTHING from scratch:
1. Re-create the terrain (so it's clean).
2. Establish the 3 houses with their multi-materials.
3. Call the Ivy generator and grow ivy on multiple walls of each of the 3 houses.
4. Call the Grass clutter scatter generator and paint the meadow.
5. Create a stone pathway or ruined fences if we want.
6. Set up the camera position and angle.
7. Set up the Cycles Renderer, lighting (warm evening sun + cool sky), and background.

Let's carefully design the Camera and Lighting:
For **Lighting**:
- Sun light: e.g. `type='SUN'`.
  - Angle: low angle, say `rotation_euler=(math.radians(72), math.radians(10), math.radians(45))`. This represents a beautiful late afternoon / sunset sun.
  - Energy: 3.5.
  - Color: warm peach/orange `(1.0, 0.75, 0.5)`. This will catch the rusty tin roofs and the grass tips beautifully.
- Ambient Light / World:
  - We can set the background world color to a soft, realistic dark-blue or grey-sky gradient, or simple sky colored light. This will make the shadows look rich and atmospheric.
  - World node setup: simple background color with strength 0.8. Color: `(0.12, 0.15, 0.23)` (cool twilight blue). The combination of orange sunlight and cool blue shadows is a classic painterly look for nature reclamation!

For the **Camera**:
- Let's place the camera at:
  - Location: `Vector((-6.5, -11.0, 3.8))`
  - Rotation: `Euler((math.radians(76), math.radians(0), math.radians(-32)))`
- Let's verify what this is aiming at:
  - The first house is positioned at `(-1.0, -1.5, height)`. This is around (X=-1.0, Y=-1.5), which is in front and slightly right of the camera.
  - The second house is at `(5.5, 6.0, height)`. This is further to the right and back.
  - The third house is at `(-8.0, 4.0, height)`. This is to the left and back.
  - This composition is absolute gold! It creates a wonderful triangular focal layout:
    - House 1 (foreground right/center).
    - House 2 (midground right).
    - House 3 (midground left).
    - The camera path or meadow runs diagonally between them. This will look like a master landscape painting.

Let's combine all of these into a single master script, execute it, and see how it looks! Let's write the whole master script, making sure all functions are defined properly.

import bpy
import math
import random
from mathutils import Vector, Euler, Matrix

# Clear all mesh and lamp objects, keep materials if they are there
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

# Clean up orphan data to keep file size lightweight
for block in bpy.data.meshes:
    if block.users == 0:
        bpy.data.meshes.remove(block)

# --- Define Terrain Math ---
def get_terrain_height(x, y):
    # Large rolling hills
    z = 1.0 * math.sin(x * 0.1) * math.cos(y * 0.1)
    # Small ridges
    z += 0.4 * math.sin(x * 0.3) * math.sin(y * 0.4)
    # Micro-undulations
    z += 0.12 * math.cos(x * 0.8 + y * 0.5)
    # Pathway/depression
    path_dist = abs(x + y * 0.5) / 10.0
    if path_dist < 1.0:
        z -= (1.0 - path_dist) * 0.6
    return z

# --- 1. RECREATE TERRAIN ---
bpy.ops.mesh.primitive_grid_add(x_subdivisions=120, y_subdivisions=120, size=50, location=(0, 0, 0))
terrain = bpy.context.object
terrain.name = "Terrain"
mat_g = bpy.data.materials.get("Ground_Moss")
if mat_g:
    terrain.data.materials.append(mat_g)

mesh = terrain.data
for vert in mesh.vertices:
    vert.co.z = get_terrain_height(vert.co.x, vert.co.y)
bpy.ops.object.shade_smooth()

# --- Define Sheet & House & Ivy & Grass Helpers ---

def create_corrugated_sheet_data(w=1.0, l=1.8, wave_h=0.025, wave_num=6):
    verts = []
    faces = []
    steps = wave_num * 4
    for j in range(2):
        y = -l/2.0 if j == 0 else l/2.0
        for i in range(steps + 1):
            t = i / steps
            x = -w/2.0 + t * w
            z = wave_h * math.sin(t * wave_num * 2.0 * math.pi)
            verts.append(Vector((x, y, z)))
    for i in range(steps):
        v1 = i
        v2 = i + 1
        v3 = v2 + (steps + 1)
        v4 = v1 + (steps + 1)
        faces.append((v1, v4, v3, v2))
    return verts, faces

def create_house_mesh(name, l=6.0, w=4.0, h=2.5, ph=3.6, missing_roof_prob=0.2, pos=Vector((0,0,0)), rot_z=0.0):
    all_verts = []
    all_faces = []
    materials_list = [bpy.data.materials.get("Weathered_Wood"), bpy.data.materials.get("Rusted_Metal")]
    all_face_mats = []

    p_w = 0.22
    p_d = 0.025

    house_mat = Matrix.Translation(pos) @ Euler((0, 0, rot_z)).to_matrix().to_4x4()

    def add_box(local_pos, size, rot, mat_idx=0):
        v_offset = len(all_verts)
        sx, sy, sz = size[0]/2.0, size[1]/2.0, size[2]/2.0
        local_verts = [
            Vector((-sx, -sy, -sz)), Vector((sx, -sy, -sz)),
            Vector((sx, sy, -sz)), Vector((-sx, sy, -sz)),
            Vector((-sx, -sy, sz)), Vector((sx, -sy, sz)),
            Vector((sx, sy, sz)), Vector((-sx, sy, sz))
        ]
        local_mat = Matrix.Translation(local_pos) @ rot.to_matrix().to_4x4()
        for lv in local_verts:
            all_verts.append(house_mat @ (local_mat @ lv))

        local_faces = [
            (0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1),
            (1, 5, 6, 2), (2, 6, 7, 3), (3, 7, 4, 0)
        ]
        for lf in local_faces:
            all_faces.append((lf[0]+v_offset, lf[1]+v_offset, lf[2]+v_offset, lf[3]+v_offset))
            all_face_mats.append(mat_idx)

    # 1. Timber posts
    post_sz = 0.12
    for cx in [-l/2.0 + post_sz/2.0, l/2.0 - post_sz/2.0]:
        for cy in [-w/2.0 + post_sz/2.0, w/2.0 - post_sz/2.0]:
            add_box(Vector((cx, cy, h/2.0)), (post_sz, post_sz, h), Euler((random.uniform(-0.02, 0.02), random.uniform(-0.02, 0.02), 0)), 0)

    # 2. Support rafters
    beam_sz = 0.08
    num_rafters = 5
    slope_angle = math.atan((ph - h) / (w / 2.0))
    slope_len = math.sqrt((w/2.0)**2 + (ph - h)**2)
    for r in range(num_rafters):
        t = r / (num_rafters - 1)
        rx = -l/2.0 + t * l
        add_box(Vector((rx, 0, h)), (beam_sz, w * 0.9, beam_sz), Euler((0,0,0)), 0)
        add_box(Vector((rx, -w/4.0, h + (ph-h)/2.0)), (beam_sz, beam_sz, slope_len), Euler((slope_angle, 0, 0)), 0)
        add_box(Vector((rx, w/4.0, h + (ph-h)/2.0)), (beam_sz, beam_sz, slope_len), Euler((-slope_angle, 0, 0)), 0)

    # 3. Front Wall (Y = -w/2)
    num_steps_x = int(l / p_w)
    for i in range(num_steps_x + 1):
        x = -l/2.0 + i * p_w
        if abs(x) < 0.6:  # door way
            continue
        if random.random() < 0.12:
            continue
        plank_h = h
        tilted_angle = 0.0
        pos_offset_z = 0.0
        if random.random() < 0.15:
            plank_h = h * random.uniform(0.12, 0.6)
            pos_offset_z = - (h - plank_h) / 2.0
        if random.random() < 0.25:
            tilted_angle = random.uniform(-0.08, 0.08)
        add_box(Vector((x, -w/2.0, h/2.0 + pos_offset_z)), (p_w * 0.95, p_d, plank_h), Euler((0.0, tilted_angle, 0.0)), 0)

    # 4. Back Wall (Y = w/2)
    for i in range(num_steps_x + 1):
        x = -l/2.0 + i * p_w
        if abs(x) < 0.6 and random.random() < 0.5:
            continue
        if random.random() < 0.12:
            continue
        plank_h = h
        tilted_angle = 0.0
        pos_offset_z = 0.0
        if random.random() < 0.15:
            plank_h = h * random.uniform(0.12, 0.6)
            pos_offset_z = - (h - plank_h) / 2.0
        if random.random() < 0.25:
            tilted_angle = random.uniform(-0.08, 0.08)
        add_box(Vector((x, w/2.0, h/2.0 + pos_offset_z)), (p_w * 0.95, p_d, plank_h), Euler((0.0, tilted_angle, 0.0)), 0)

    # 5. Left Wall (X = -l/2)
    num_steps_y = int(w / p_w)
    for i in range(num_steps_y + 1):
        y = -w/2.0 + i * p_w
        if abs(y) < 0.5: # window
            continue
        if random.random() < 0.12:
            continue
        plank_h = h
        tilted_angle = 0.0
        pos_offset_z = 0.0
        if random.random() < 0.15:
            plank_h = h * random.uniform(0.12, 0.6)
            pos_offset_z = - (h - plank_h) / 2.0
        if random.random() < 0.25:
            tilted_angle = random.uniform(-0.08, 0.08)
        add_box(Vector((-l/2.0, y, h/2.0 + pos_offset_z)), (p_w * 0.95, p_d, plank_h), Euler((tilted_angle, 0.0, math.pi/2.0)), 0)

    # 6. Right Wall (X = l/2)
    for i in range(num_steps_y + 1):
        y = -w/2.0 + i * p_w
        if abs(y) < 0.5 and random.random() < 0.5:
            continue
        if random.random() < 0.12:
            continue
        plank_h = h
        tilted_angle = 0.0
        pos_offset_z = 0.0
        if random.random() < 0.15:
            plank_h = h * random.uniform(0.12, 0.6)
            pos_offset_z = - (h - plank_h) / 2.0
        if random.random() < 0.25:
            tilted_angle = random.uniform(-0.08, 0.08)
        add_box(Vector((l/2.0, y, h/2.0 + pos_offset_z)), (p_w * 0.95, p_d, plank_h), Euler((tilted_angle, 0.0, math.pi/2.0)), 0)

    # 7. Gable walls
    p_h = 0.20
    g_h = ph - h
    for j in range(int(g_h / p_h) + 1):
        rel_h = j * p_h
        cl_rel_h = min(rel_h, g_h)
        plank_w = (1.0 - (cl_rel_h / g_h)) * w
        if plank_w < 0.1: continue
        if random.random() > 0.15:
            add_box(Vector((-l/2.0, 0, h + cl_rel_h + p_h/2.0)), (plank_w * 0.98, p_d, p_h * 0.95), Euler((0, 0, math.pi/2.0)), 0)
        if random.random() > 0.15:
            add_box(Vector((l/2.0, 0, h + cl_rel_h + p_h/2.0)), (plank_w * 0.98, p_d, p_h * 0.95), Euler((0, 0, math.pi/2.0)), 0)

    # 8. Corrugated sheets
    sheet_w = 0.95
    sheet_l = 1.35
    for slope_side in [-1, 1]:
        rot_x = slope_side * slope_angle
        cols = int((l + 0.4) / (sheet_w * 0.85)) + 1
        rows = 2

        for r in range(rows):
            for c in range(cols):
                if random.random() < missing_roof_prob:
                    continue

                t_slope = 0.25 if r == 0 else 0.75
                dist_slope = t_slope * slope_len - 0.15

                ly = slope_side * (dist_slope * math.cos(slope_angle))
                lz = h + dist_slope * math.sin(slope_angle)
                lx = -l/2.0 - 0.1 + c * (sheet_w * 0.82)

                offset_x = random.uniform(-0.02, 0.02)
                offset_y = random.uniform(-0.02, 0.02)
                offset_z = random.uniform(-0.01, 0.01)

                rot_offset_x = 0.0
                rot_offset_y = 0.0
                rot_offset_z = 0.0

                if random.random() < 0.22: # Slipped sheet
                    offset_y += slope_side * random.uniform(0.12, 0.45)
                    offset_z -= random.uniform(0.04, 0.08)
                    rot_offset_z = random.uniform(-0.18, 0.18)
                    rot_offset_x = random.uniform(-0.04, 0.04)
                elif random.random() < 0.15:
                    rot_offset_z = random.uniform(-0.05, 0.05)

                s_verts, s_faces = create_corrugated_sheet_data(w=sheet_w, l=sheet_l)
                sheet_local_pos = Vector((lx + offset_x, ly + offset_y, lz + offset_z))
                sheet_local_rot = Euler((rot_x + rot_offset_x, rot_offset_y, rot_offset_z))
                sheet_local_matrix = Matrix.Translation(sheet_local_pos) @ sheet_local_rot.to_matrix().to_4x4()

                v_offset = len(all_verts)
                for sv in s_verts:
                    all_verts.append(house_mat @ (sheet_local_matrix @ sv))
                for sf in s_faces:
                    all_faces.append((sf[0]+v_offset, sf[1]+v_offset, sf[2]+v_offset, sf[3]+v_offset))
                    all_face_mats.append(1)

    mesh = bpy.data.meshes.new(name)
    obj = bpy.data.objects.new(name, mesh)
    bpy.context.collection.objects.link(obj)

    v_tuples = [v.to_tuple() for v in all_verts]
    mesh.from_pydata(v_tuples, [], all_faces)
    mesh.update()

    for mat in materials_list:
        if mat:
            obj.data.materials.append(mat)
    for face_idx, mat_idx in enumerate(all_face_mats):
        mesh.polygons[face_idx].material_index = mat_idx

    solidify = obj.modifiers.new(name="Solidify", type='SOLIDIFY')
    solidify.thickness = 0.012

    bpy.ops.object.select_all(action='DESELECT')
    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.shade_smooth()
    return obj

def generate_house_ivy_mesh(name, l=6.0, w=4.0, h=2.5, ph=3.6, side='front', x_start=1.0, num_branches=3, pos=Vector((0,0,0)), rot_z=0.0):
    all_verts = []
    all_faces = []

    stem_r = 0.012
    leaf_sz = 0.15

    house_mat = Matrix.Translation(pos) @ Euler((0, 0, rot_z)).to_matrix().to_4x4()

    def add_stem_segment(p1, p2):
        v_offset = len(all_verts)
        dir_v = p2 - p1
        dist = dir_v.length
        if dist < 0.001: return

        center = p1 + dir_v * 0.5
        rotation = dir_v.to_track_quat('Z', 'Y').to_euler()

        sx, sy, sz = stem_r, stem_r, dist/2.0
        local_verts = [
            Vector((-sx, -sy, -sz)), Vector((sx, -sy, -sz)),
            Vector((sx, sy, -sz)), Vector((-sx, sy, -sz)),
            Vector((-sx, -sy, sz)), Vector((sx, -sy, sz)),
            Vector((sx, sy, sz)), Vector((-sx, sy, sz))
        ]
        local_mat = Matrix.Translation(center) @ rotation.to_matrix().to_4x4()
        for lv in local_verts:
            all_verts.append(house_mat @ (local_mat @ lv))

        local_faces = [
            (0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1),
            (1, 5, 6, 2), (2, 6, 7, 3), (3, 7, 4, 0)
        ]
        for lf in local_faces:
            all_faces.append((lf[0]+v_offset, lf[1]+v_offset, lf[2]+v_offset, lf[3]+v_offset))

    def add_leaf(pos_local, normal_local):
        v_offset = len(all_verts)
        sz = leaf_sz / 2.0

        up = Vector((0, 0, 1))
        tangent = normal_local.cross(up)
        if tangent.length < 0.01:
            tangent = normal_local.cross(Vector((0, 1, 0)))
        tangent.normalize()
        bitangent = normal_local.cross(tangent).normalized()

        l_verts = [
            pos_local - tangent * sz - bitangent * sz + normal_local * 0.02,
            pos_local + tangent * sz - bitangent * sz + normal_local * 0.02,
            pos_local + tangent * sz + bitangent * sz + normal_local * 0.08,
            pos_local - tangent * sz + bitangent * sz + normal_local * 0.08
        ]
        for lv in l_verts:
            all_verts.append(house_mat @ lv)
        all_faces.append((v_offset, v_offset+1, v_offset+2, v_offset+3))

    for b in range(num_branches):
        curr_x = x_start + random.uniform(-0.15, 0.15)
        curr_z = -0.1

        if side == 'front':
            curr_y = -w/2.0 - 0.02
            wall_normal = Vector((0, -1, 0))
        elif side == 'back':
            curr_y = w/2.0 + 0.02
            wall_normal = Vector((0, 1, 0))
        elif side == 'left':
            curr_x = -l/2.0 - 0.02
            curr_y = x_start
            wall_normal = Vector((-1, 0, 0))
        else:
            curr_x = l/2.0 + 0.02
            curr_y = x_start
            wall_normal = Vector((1, 0, 0))

        curr_p = Vector((curr_x, curr_y, curr_z))

        step_len = 0.16
        steps = int(h / step_len) + 5

        for s in range(steps):
            next_z = curr_p.z + step_len * random.uniform(0.7, 1.2)
            if next_z > h: break

            if side in ['front', 'back']:
                next_x = curr_p.x + random.uniform(-0.12, 0.12)
                next_y = curr_p.y + random.uniform(-0.01, 0.01)
            else:
                next_x = curr_p.x + random.uniform(-0.01, 0.01)
                next_y = curr_p.y + random.uniform(-0.12, 0.12)

            next_p = Vector((next_x, next_y, next_z))
            add_stem_segment(curr_p, next_p)

            if random.random() < 0.85:
                add_leaf(next_p, wall_normal)
                if random.random() < 0.5:
                    add_leaf(next_p + Vector((0,0,0.05)), wall_normal)
            curr_p = next_p

        if side in ['front', 'back']:
            slope_side = -1 if side == 'front' else 1
            slope_angle = math.atan((ph - h) / (w / 2.0))
            roof_normal = Vector((0, -slope_side * math.sin(slope_angle), math.cos(slope_angle)))

            roof_steps = 14
            for rs in range(roof_steps):
                dist_to_peak = abs(curr_p.y)
                if dist_to_peak < 0.1: break

                step_y = 0.18 * math.cos(slope_angle)
                next_y = curr_p.y + slope_side * step_y
                if (slope_side == -1 and next_y > 0.0) or (slope_side == 1 and next_y < 0.0):
                    next_y = 0.0

                t_slope = 1.0 - (abs(next_y) / (w/2.0))
                next_z = h + t_slope * (ph - h) + random.uniform(0.01, 0.04)
                next_x = curr_p.x + random.uniform(-0.12, 0.12)

                next_p = Vector((next_x, next_y, next_z))
                add_stem_segment(curr_p, next_p)
                add_leaf(next_p, roof_normal)
                if random.random() < 0.7:
                    add_leaf(next_p, roof_normal)
                curr_p = next_p

    mesh = bpy.data.meshes.new(name)
    obj = bpy.data.objects.new(name, mesh)
    bpy.context.collection.objects.link(obj)

    v_tuples = [v.to_tuple() for v in all_verts]
    mesh.from_pydata(v_tuples, [], all_faces)
    mesh.update()

    mat_foliage = bpy.data.materials.get("Foliage")
    if mat_foliage:
        obj.data.materials.append(mat_foliage)

    bpy.ops.object.select_all(action='DESELECT')
    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.shade_smooth()
    return obj

def generate_village_grass(name, num_clumps=2000, x_range=(-22.0, 22.0), y_range=(-22.0, 22.0)):
    all_verts = []
    all_faces = []

    b_w = 0.04
    b_h = 0.35
    angles_rad = [0.0, math.pi / 3.0, 2.0 * math.pi / 3.0]

    # House centers for obstacle avoidance
    house_centers = [Vector((-1.0, -1.5)), Vector((5.5, 6.0)), Vector((-8.0, 4.0))]

    for _ in range(num_clumps):
        rx = random.uniform(x_range[0], x_range[1])
        ry = random.uniform(y_range[0], y_range[1])
        rz = get_terrain_height(rx, ry)

        # Don't place dense grass directly under house footprint, but let some creep inside!
        in_house = False
        for hp in house_centers:
            if (Vector((rx, ry)) - hp).length < 2.5:
                if random.random() < 0.8:
                    in_house = True
                    break
        if in_house: continue

        clump_scale = random.uniform(0.65, 1.3)
        clump_rot_z = random.uniform(0.0, 2.0 * math.pi)

        clump_mat = Matrix.Translation(Vector((rx, ry, rz - 0.02))) @ Euler((random.uniform(-0.1, 0.1), random.uniform(-0.1, 0.1), clump_rot_z)).to_matrix().to_4x4()

        for angle in angles_rad:
            v_offset = len(all_verts)
            tilt = random.uniform(0.1, 0.25)
            blade_rot = Euler((0.0, tilt, angle)).to_matrix().to_4x4()

            bh = b_h * clump_scale * random.uniform(0.8, 1.25)
            bw = b_w * clump_scale * random.uniform(0.8, 1.2)

            local_verts = [
                Vector((-bw/2.0, 0, 0)), Vector((bw/2.0, 0, 0)),
                Vector((bw/4.0, 0, bh)), Vector((-bw/4.0, 0, bh))
            ]
            for lv in local_verts:
                all_verts.append(clump_mat @ (blade_rot @ lv))
            all_faces.append((v_offset, v_offset+1, v_offset+2, v_offset+3))

    mesh = bpy.data.meshes.new(name)
    obj = bpy.data.objects.new(name, mesh)
    bpy.context.collection.objects.link(obj)

    v_tuples = [v.to_tuple() for v in all_verts]
    mesh.from_pydata(v_tuples, [], all_faces)
    mesh.update()

    mat_foliage = bpy.data.materials.get("Foliage")
    if mat_foliage:
        obj.data.materials.append(mat_foliage)

    bpy.ops.object.select_all(action='DESELECT')
    obj.select_set(True)
    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.shade_smooth()
    return obj

# --- 2. ASSEMBLE HOUSES AND GROW IVY ---

# House 1 (Foreground)
h1_pos = Vector((-1.0, -1.5, get_terrain_height(-1.0, -1.5)))
create_house_mesh("House_H1", l=5.5, w=3.8, h=2.3, ph=3.5, missing_roof_prob=0.20, pos=h1_pos, rot_z=-0.2)
generate_house_ivy_mesh("Ivy_H1_front", l=5.5, w=3.8, h=2.3, ph=3.5, side='front', x_start=0.5, num_branches=5, pos=h1_pos, rot_z=-0.2)
generate_house_ivy_mesh("Ivy_H1_left", l=5.5, w=3.8, h=2.3, ph=3.5, side='left', x_start=-0.6, num_branches=4, pos=h1_pos, rot_z=-0.2)

# House 2 (Middle-ground right)
h2_pos = Vector((5.5, 6.0, get_terrain_height(5.5, 6.0)))
create_house_mesh("House_H2", l=6.2, w=4.0, h=2.4, ph=3.6, missing_roof_prob=0.35, pos=h2_pos, rot_z=1.0)
generate_house_ivy_mesh("Ivy_H2_front", l=6.2, w=4.0, h=2.4, ph=3.6, side='front', x_start=-0.8, num_branches=4, pos=h2_pos, rot_z=1.0)
generate_house_ivy_mesh("Ivy_H2_back", l=6.2, w=4.0, h=2.4, ph=3.6, side='back', x_start=0.2, num_branches=4, pos=h2_pos, rot_z=1.0)

# House 3 (Background left, highly ruined skeleton)
h3_pos = Vector((-8.0, 4.0, get_terrain_height(-8.0, 4.0)))
create_house_mesh("House_H3", l=5.0, w=3.6, h=2.2, ph=3.2, missing_roof_prob=0.75, pos=h3_pos, rot_z=-0.8)
generate_house_ivy_mesh("Ivy_H3_front", l=5.0, w=3.6, h=2.2, ph=3.2, side='front', x_start=0.0, num_branches=5, pos=h3_pos, rot_z=-0.8)
generate_house_ivy_mesh("Ivy_H3_right", l=5.0, w=3.6, h=2.2, ph=3.2, side='right', x_start=0.3, num_branches=4, pos=h3_pos, rot_z=-0.8)

# --- 3. GENERATE MEADOW SCATTER ---
generate_village_grass("Village_Grass_Field", num_clumps=2500)

# --- 4. LIGHTING & ENVIRONMENT SETUPS ---

# Cool Evening Sky / Ambient World setup
bpy.context.scene.use_nodes = True
world = bpy.context.scene.world
world.use_nodes = True
w_nodes = world.node_tree.nodes
w_links = world.node_tree.links
for n in list(w_nodes):
    w_nodes.remove(n)

w_out = w_nodes.new('ShaderNodeOutputWorld')
w_bg = w_nodes.new('ShaderNodeBackground')
w_bg.inputs['Color'].default_value = (0.10, 0.14, 0.22, 1.0) # twilight cool gray blue
w_bg.inputs['Strength'].default_value = 0.65
w_links.new(w_bg.outputs['Background'], w_out.inputs['Surface'])

# Warm Sunset Sun Light
sun_data = bpy.data.lights.new(name="Sunset_Sun", type='SUN')
sun_data.energy = 5.0
sun_data.color = (0.98, 0.72, 0.45) # rich warm peach-gold sunset color
sun_data.angle = 0.15 # softer shadows (0.15 radians is ~8 degrees spread)
sun_obj = bpy.data.objects.new(name="Sunset_Sun", object_data=sun_data)
bpy.context.collection.objects.link(sun_obj)
# Low warm angle
sun_obj.rotation_euler = (math.radians(74), math.radians(6), math.radians(48))

# --- 5. CAMERA COMPOSITION SETUP ---

cam_data = bpy.data.cameras.new(name="ContestantCam")
cam_data.lens = 28 # widescreen cinematic touch
cam_obj = bpy.data.objects.new(name="Contestant_Camera", object_data=cam_data)
bpy.context.collection.objects.link(cam_obj)

# Place camera to frame the valley beautifully
cam_obj.location = Vector((-6.8, -10.5, 3.6))
cam_obj.rotation_euler = (math.radians(77), math.radians(0), math.radians(-32))

# Set active camera
bpy.context.scene.camera = cam_obj

# --- 6. RENDER ENGINE CYCLES ENGINE SETUPS ---
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.samples = 64
bpy.context.scene.cycles.use_denoising = True

# Color Management (filmic/AgX is modern standard, high contrast for maximum painterly look)
bpy.context.scene.view_settings.view_transform = 'AgX' if hasattr(bpy.types.ViewSettings, 'view_transform') else 'Filmic'
bpy.context.scene.view_settings.look = 'High Contrast'

print("Master compilation script finished running successfully!")

Execute full master compilation scene construction including terrain, 3 cottages, 6 ivy groups, grass, lighting, and camera


Error executing code: Communication error with Blender: Code execution error: 'module' object has no attribute 'ViewSettings'