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

def clear():
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)
    for b in (bpy.data.meshes, bpy.data.materials, bpy.data.lights, bpy.data.cameras, bpy.data.images, bpy.data.worlds):
        for i in list(b):
            b.remove(i)

clear()
random.seed(7)

def new_mat(name):
    m = bpy.data.materials.new(name)
    m.use_nodes = True
    return m

def bsdf(mat):
    return mat.node_tree.nodes["Principled BSDF"]

# ---------------- WORLD ----------------
world = bpy.data.worlds.new("DarkRoom")
bpy.context.scene.world = world
world.use_nodes = True
bg = world.node_tree.nodes["Background"]
bg.inputs[0].default_value = (0.008, 0.006, 0.007, 1)
bg.inputs[1].default_value = 0.4

# ---------------- TABLE ----------------
bpy.ops.mesh.primitive_cube_add(size=6, location=(0, 0, -0.15))
table = bpy.context.object
table.name = "TableTop"
table.scale = (1.5, 1.0, 0.08)
bpy.ops.object.shade_smooth()
mat_table = new_mat("WoodTable")
b = bsdf(mat_table)
b.inputs["Base Color"].default_value = (0.05, 0.025, 0.015, 1)
b.inputs["Roughness"].default_value = 0.35
table.data.materials.append(mat_table)

# Backdrop wall (near black, subtle)
bpy.ops.mesh.primitive_plane_add(size=20, location=(0, 3.2, 5))
wall = bpy.context.object
wall.rotation_euler = (math.radians(90), 0, 0)
mat_wall = new_mat("Wall")
b = bsdf(mat_wall)
b.inputs["Base Color"].default_value = (0.01, 0.01, 0.012, 1)
b.inputs["Roughness"].default_value = 0.9
wall.data.materials.append(mat_wall)

# ---------------- CLOTH DRAPES (Cornell nod) ----------------
def make_cloth(name, loc, rot, scale, color, subdiv=4):
    bpy.ops.mesh.primitive_plane_add(size=2, location=loc)
    obj = bpy.context.object
    obj.name = name
    obj.rotation_euler = rot
    obj.scale = scale
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.subdivide(number_cuts=subdiv)
    bpy.ops.object.mode_set(mode='OBJECT')
    # add fold structure via displace-ish manual vertex push using a wave modifier baked
    disp = obj.modifiers.new("SubD", 'SUBSURF')
    disp.levels = 2
    # random fold noise using Displace with a procedural texture
    tex = bpy.data.textures.new(name+"Tex", 'CLOUDS')
    tex.noise_scale = 0.4
    dispmod = obj.modifiers.new("Disp", 'DISPLACE')
    dispmod.texture = tex
    dispmod.strength = 0.18
    dispmod.mid_level = 0.5
    m = new_mat(name+"Mat")
    b = bsdf(m)
    b.inputs["Base Color"].default_value = color
    b.inputs["Roughness"].default_value = 0.95
    b.inputs["Sheen Weight"].default_value = 0.4
    obj.data.materials.append(m)
    return obj

# Red cloth - left, draped over table edge
red_cloth = make_cloth("RedVelvet", (-1.9, -0.3, 0.05), (0.15, 0.05, 0.0), (1.1, 1.3, 1), (0.30, 0.02, 0.03, 1))
# Green cloth - right, facing red
green_cloth = make_cloth("GreenVelvet", (1.9, 0.2, 0.05), (-0.1, -0.05, 0.0), (1.1, 1.2, 1), (0.03, 0.13, 0.05, 1))
# Neutral grey table runner between them, center
runner = make_cloth("GreyRunner", (0, -0.1, 0.05), (0.02, 0.0, 0.0), (1.4, 1.6, 1), (0.55, 0.53, 0.50, 1))

# ---------------- TEAPOT (Utah Teapot, built from primitives) ----------------
teapot_grp = bpy.data.collections.new("UtahTeapot")
bpy.context.scene.collection.children.link(teapot_grp)

def link_to(obj, coll):
    for c in obj.users_collection:
        c.objects.unlink(obj)
    coll.objects.link(obj)

# Body
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.55, location=(-0.5, 0.6, 0.28))
body = bpy.context.object
body.name = "TeapotBody"
body.scale = (1.0, 1.0, 0.62)
bpy.ops.object.shade_smooth()
link_to(body, teapot_grp)

# Lid
bpy.ops.mesh.primitive_cone_add(radius1=0.30, radius2=0.05, depth=0.22, location=(-0.5, 0.6, 0.62))
lid = bpy.context.object
lid.name = "TeapotLid"
bpy.ops.object.shade_smooth()
link_to(lid, teapot_grp)
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.05, location=(-0.5, 0.6, 0.76))
knob = bpy.context.object
knob.name = "TeapotKnob"
bpy.ops.object.shade_smooth()
link_to(knob, teapot_grp)

# Spout (tapered curve using cone + bend via simple angled cylinder chain)
bpy.ops.mesh.primitive_cone_add(radius1=0.14, radius2=0.045, depth=0.7, location=(-1.05, 0.6, 0.42))
spout = bpy.context.object
spout.name = "TeapotSpout"
spout.rotation_euler = (0, math.radians(65), 0)
bpy.ops.object.shade_smooth()
link_to(spout, teapot_grp)

# Handle (torus arc)
bpy.ops.mesh.primitive_torus_add(major_radius=0.28, minor_radius=0.05, location=(0.05, 0.6, 0.35))
handle = bpy.context.object
handle.name = "TeapotHandle"
handle.rotation_euler = (0, math.radians(90), 0)
handle.scale = (1, 0.75, 1)
bpy.ops.object.shade_smooth()
link_to(handle, teapot_grp)

mat_teapot = new_mat("CeramicTeapot")
b = bsdf(mat_teapot)
b.inputs["Base Color"].default_value = (0.62, 0.58, 0.48, 1)
b.inputs["Roughness"].default_value = 0.22
b.inputs["Metallic"].default_value = 0.0
b.inputs["Specular IOR Level"].default_value = 0.6
b.inputs["Coat Weight"].default_value = 1.0
b.inputs["Coat Roughness"].default_value = 0.08
for o in teapot_grp.objects:
    o.data.materials.append(mat_teapot)

# ---------------- STANFORD BUNNY (crouched, approximated with metaballs) ----------------
mb_data = bpy.data.metaballs.new("BunnyMeta")
mb_obj = bpy.data.objects.new("BunnyMeta", mb_data)
bpy.context.scene.collection.objects.link(mb_obj)
mb_data.resolution = 0.06
mb_data.render_resolution = 0.035
loc0 = Vector((0.75, -0.15, 0.0))

def add_mb(pos, radius, stiffness=1.5, mtype='BALL', size2=None):
    e = mb_data.elements.new()
    e.co = pos
    e.radius = radius
    e.stiffness = stiffness
    e.type = mtype
    if size2:
        e.size_x, e.size_y, e.size_z = size2
    return e

# body - crouched egg shape
add_mb(loc0 + Vector((0, 0, 0.16)), 0.30)
add_mb(loc0 + Vector((0.05, 0.15, 0.22)), 0.26)
add_mb(loc0 + Vector((0.0, -0.15, 0.20)), 0.22)
# head
add_mb(loc0 + Vector((0.02, 0.32, 0.30)), 0.18)
# haunches lower (crouched legs tucked)
add_mb(loc0 + Vector((0.1, -0.05, 0.06)), 0.22)
add_mb(loc0 + Vector((-0.12, -0.05, 0.06)), 0.20)
# ears folded back
add_mb(loc0 + Vector((0.10, 0.30, 0.42)), 0.09)
add_mb(loc0 + Vector((-0.06, 0.28, 0.44)), 0.09)
# tail bump
add_mb(loc0 + Vector((0.0, -0.32, 0.14)), 0.08)

mat_bunny = new_mat("BunnyStone")
b = bsdf(mat_bunny)
b.inputs["Base Color"].default_value = (0.72, 0.68, 0.60, 1)
b.inputs["Roughness"].default_value = 0.55
mb_data.materials.append(mat_bunny) if hasattr(mb_data, "materials") else None
mb_obj.data.materials.append(mat_bunny)

# ---------------- SUZANNE ----------------
bpy.ops.mesh.primitive_monkey_add(size=0.55, location=(-1.55, -0.55, 0.30))
suz = bpy.context.object
suz.name = "Suzanne"
suz.rotation_euler = (math.radians(90), 0, math.radians(35))
bpy.ops.object.shade_smooth()
mat_suz = new_mat("BronzeSuzanne")
b = bsdf(mat_suz)
b.inputs["Base Color"].default_value = (0.25, 0.16, 0.06, 1)
b.inputs["Metallic"].default_value = 1.0
b.inputs["Roughness"].default_value = 0.35
suz.data.materials.append(mat_suz)

# small pedestal for suzanne
bpy.ops.mesh.primitive_cylinder_add(radius=0.22, depth=0.12, location=(-1.55, -0.55, 0.0))
ped = bpy.context.object
ped.name = "SuzPedestal"
bpy.ops.object.shade_smooth()
mat_ped = new_mat("StonePedestal")
b = bsdf(mat_ped)
b.inputs["Base Color"].default_value = (0.15, 0.14, 0.13, 1)
b.inputs["Roughness"].default_value = 0.7
ped.data.materials.append(mat_ped)

# ---------------- REFLECTIVE CHECKERED SPHERE ----------------
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.32, segments=64, ring_count=32, location=(1.65, 0.55, 0.32))
sph = bpy.context.object
sph.name = "ChromeCheckerSphere"
bpy.ops.object.shade_smooth()
mat_sph = new_mat("ChromeChecker")
mat_sph.use_nodes = True
nt = mat_sph.node_tree
nodes, links = nt.nodes, nt.links
principled = nodes["Principled BSDF"]
tex_coord = nodes.new("ShaderNodeTexCoord")
mapping = nodes.new("ShaderNodeMapping")
mapping.inputs['Scale'].default_value = (6, 6, 6)
checker = nodes.new("ShaderNodeTexChecker")
checker.inputs['Color1'].default_value = (0.02, 0.02, 0.02, 1)
checker.inputs['Color2'].default_value = (0.85, 0.82, 0.75, 1)
links.new(tex_coord.outputs['Generated'], mapping.inputs['Vector'])
links.new(mapping.outputs['Vector'], checker.inputs['Vector'])
links.new(checker.outputs['Color'], principled.inputs['Base Color'])
principled.inputs['Metallic'].default_value = 0.9
principled.inputs['Roughness'].default_value = 0.05
sph.data.materials.append(mat_sph)

# ---------------- SUPPORTING PROPS: candlestick + fruit ----------------
# candlestick base
bpy.ops.mesh.primitive_cylinder_add(radius=0.10, depth=0.06, location=(0.0, 0.85, 0.03))
cbase = bpy.context.object
cbase.name = "CandleBase"
bpy.ops.object.shade_smooth()
# candle stem
bpy.ops.mesh.primitive_cylinder_add(radius=0.03, depth=0.55, location=(0.0, 0.85, 0.33))
cstem = bpy.context.object
cstem.name = "CandleStem"
bpy.ops.object.shade_smooth()
# candle body (the actual candle on top)
bpy.ops.mesh.primitive_cylinder_add(radius=0.045, depth=0.30, location=(0.0, 0.85, 0.75))
candle = bpy.context.object
candle.name = "Candle"
bpy.ops.object.shade_smooth()
# flame glow small
bpy.ops.mesh.primitive_cone_add(radius1=0.02, radius2=0.001, depth=0.06, location=(0.0, 0.85, 0.93))
flame = bpy.context.object
flame.name = "Flame"

mat_brass = new_mat("PatinaBrass")
b = bsdf(mat_brass)
b.inputs["Base Color"].default_value = (0.20, 0.14, 0.05, 1)
b.inputs["Metallic"].default_value = 1.0
b.inputs["Roughness"].default_value = 0.45
for o in (cbase, cstem):
    o.data.materials.append(mat_brass)

mat_wax = new_mat("Wax")
b = bsdf(mat_wax)
b.inputs["Base Color"].default_value = (0.85, 0.80, 0.65, 1)
b.inputs["Roughness"].default_value = 0.4
candle.data.materials.append(mat_wax)

mat_flame = new_mat("FlameEmit")
mat_flame.use_nodes = True
em = mat_flame.node_tree.nodes.new("ShaderNodeEmission")
em.inputs["Color"].default_value = (1.0, 0.55, 0.15, 1)
em.inputs["Strength"].default_value = 25.0
out = mat_flame.node_tree.nodes["Material Output"]
mat_flame.node_tree.links.new(em.outputs[0], out.inputs[0])
flame.data.materials.append(mat_flame)

flame_light = bpy.data.lights.new("FlameLight", 'POINT')
flame_light.energy = 12
flame_light.color = (1.0, 0.6, 0.25)
flame_light_obj = bpy.data.objects.new("FlameLightObj", flame_light)
flame_light_obj.location = (0.0, 0.85, 0.92)
bpy.context.scene.collection.objects.link(flame_light_obj)

# fruit - apples (simple sculptural spheres)
def make_apple(loc, color):
    bpy.ops.mesh.primitive_uv_sphere_add(radius=0.16, location=loc)
    ap = bpy.context.object
    ap.scale = (1.0, 1.0, 0.95)
    bpy.ops.object.shade_smooth()
    m = new_mat("AppleMat")
    b = bsdf(m)
    b.inputs["Base Color"].default_value = color
    b.inputs["Roughness"].default_value = 0.3
    ap.data.materials.append(m)
    bpy.ops.mesh.primitive_cylinder_add(radius=0.012, depth=0.08, location=(loc[0], loc[1], loc[2]+0.16))
    stem = bpy.context.object
    mstem = new_mat("StemMat")
    bstem = bsdf(mstem)
    bstem.inputs["Base Color"].default_value = (0.12, 0.08, 0.03, 1)
    stem.data.materials.append(mstem)
    return ap

make_apple((0.95, -0.55, 0.14), (0.45, 0.03, 0.03, 1))
make_apple((1.15, -0.35, 0.14), (0.4, 0.28, 0.02, 1))
make_apple((0.75, -0.65, 0.14), (0.30, 0.35, 0.03, 1))

# ---------------- LIGHTING (single warm shaft, chiaroscuro) ----------------
sun_area = bpy.data.lights.new("WindowShaft", 'AREA')
sun_area.shape = 'RECTANGLE'
sun_area.size = 1.2
sun_area.size_y = 2.6
sun_area.energy = 450
sun_area.color = (1.0, 0.85, 0.6)
sun_obj = bpy.data.objects.new("WindowShaftObj", sun_area)
sun_obj.location = (-2.6, -1.4, 2.8)
sun_obj.rotation_euler = (math.radians(55), math.radians(-25), math.radians(35))
bpy.context.scene.collection.objects.link(sun_obj)

fill = bpy.data.lights.new("FillDim", 'AREA')
fill.size = 3.0
fill.energy = 15
fill.color = (0.4, 0.45, 0.55)
fill_obj = bpy.data.objects.new("FillDimObj", fill)
fill_obj.location = (2.5, 2.0, 2.5)
fill_obj.rotation_euler = (math.radians(50), 0, math.radians(-140))
bpy.context.scene.collection.objects.link(fill_obj)

# ---------------- CAMERA ----------------
cam_data = bpy.data.cameras.new("MainCam")
cam_data.lens = 50
cam_obj = bpy.data.objects.new("MainCam", cam_data)
bpy.context.scene.collection.objects.link(cam_obj)
cam_obj.location = (0.3, -4.6, 1.55)
cam_obj.rotation_euler = (math.radians(80), 0, math.radians(4))
bpy.context.scene.camera = cam_obj

# Aim camera roughly at teapot area
target = Vector((-0.2, 0.3, 0.35))
direction = target - cam_obj.location
rot_quat = direction.to_track_quat('-Z', 'Y')
cam_obj.rotation_euler = rot_quat.to_euler()

# ---------------- RENDER SETTINGS ----------------
scene = bpy.context.scene
scene.render.engine = 'CYCLES'
scene.cycles.samples = 64
scene.render.resolution_x = 1536
scene.render.resolution_y = 1536
scene.view_settings.view_transform = 'Filmic' if 'Filmic' in [t.name for t in scene.view_settings.view_transform_choices] if hasattr(scene.view_settings, 'view_transform_choices') else 'Standard'
try:
    scene.view_settings.view_transform = 'Filmic'
except Exception:
    pass
scene.view_settings.exposure = 0.2

# ---------------- SAVE ----------------
import bpy as _bpy
try:
    _bpy.ops.wm.save_as_mainfile(filepath="//dutch_cg_still_life.blend")
except Exception:
    pass
```