Загрузка данных
import bpy
import random
import math
# ============================================================
# ОЧИСТКА СЦЕНЫ
# ============================================================
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
for coll in (bpy.data.meshes, bpy.data.materials, bpy.data.lights,
bpy.data.cameras, bpy.data.curves, bpy.data.objects):
for block in list(coll):
if block.users == 0:
coll.remove(block)
# ============================================================
# МАТЕРИАЛЫ — поиск Principled BSDF по типу узла
# ============================================================
def make_mat(name, color, roughness=0.85, metallic=0.0, emission=None):
mat = bpy.data.materials.new(name=name)
mat.use_nodes = True
tree = mat.node_tree
bsdf = None
for node in tree.nodes:
if node.type == 'BSDF_PRINCIPLED':
bsdf = node
break
if bsdf is None:
bsdf = tree.nodes.new('ShaderNodeBsdfPrincipled')
out = None
for n in tree.nodes:
if n.type == 'OUTPUT_MATERIAL':
out = n
break
if out is None:
out = tree.nodes.new('ShaderNodeOutputMaterial')
tree.links.new(bsdf.outputs['BSDF'], out.inputs['Surface'])
def set_input(sock_name, value):
if sock_name in bsdf.inputs:
bsdf.inputs[sock_name].default_value = value
set_input("Base Color", (*color, 1.0))
set_input("Roughness", roughness)
set_input("Metallic", metallic)
if emission is not None:
set_input("Emission Color", (*emission, 1.0))
set_input("Emission Strength", 2.0)
return mat
MAT = {
"wood_dark": make_mat("WoodDark", (0.10, 0.05, 0.02)),
"wood_light": make_mat("WoodLight", (0.30, 0.17, 0.07)),
"roof": make_mat("Roof", (0.28, 0.10, 0.05)),
"roof_old": make_mat("RoofOld", (0.16, 0.13, 0.09)),
"ground": make_mat("Ground", (0.18, 0.14, 0.07)),
"stone": make_mat("Stone", (0.32, 0.30, 0.28)),
"leaves": make_mat("Leaves", (0.08, 0.22, 0.05)),
"trunk": make_mat("Trunk", (0.13, 0.07, 0.03)),
"water": make_mat("Water", (0.05, 0.15, 0.25), roughness=0.05),
"dirt": make_mat("Dirt", (0.22, 0.14, 0.07)),
"lamp": make_mat("Lamp", (1.0, 0.75, 0.4),
emission=(1.0, 0.6, 0.2)),
}
def assign(obj, mat):
obj.data.materials.append(mat)
return obj
# ============================================================
# ЗЕМЛЯ
# ============================================================
bpy.ops.mesh.primitive_plane_add(size=80, location=(0, 0, 0))
ground = bpy.context.object
ground.name = "Ground"
assign(ground, MAT["ground"])
# ============================================================
# ДОМ — с правильной двускатной крышей (угол 40°)
# ============================================================
def create_house(loc, rot_z=0.0, size=(3.0, 3.5, 2.4), roof_mat=None):
if roof_mat is None:
roof_mat = MAT["roof"]
x, y, z = loc
w, d, h = size
# --- Родитель ---
bpy.ops.object.empty_add(type='PLAIN_AXES', location=(x, y, z))
root = bpy.context.object
root.name = "House_Root"
root.rotation_euler = (0, 0, rot_z)
parts = []
# --- Корпус ---
bpy.ops.mesh.primitive_cube_add(size=1, location=(0, 0, h / 2))
body = bpy.context.object
body.name = "House_Body"
body.scale = (w, d, h)
assign(body, MAT["wood_light"])
parts.append(body)
# --- Двускатная крыша (правильный угол 40°) ---
angle = math.radians(40)
overhang = 0.35
half_w = w / 2 + overhang
roof_h = math.tan(angle) * half_w
slope_len = half_w / math.cos(angle)
for side in (-1, 1):
bpy.ops.mesh.primitive_cube_add(size=1)
slope = bpy.context.object
slope.name = "Roof_Slope"
slope.location = (
side * half_w / 2,
0,
h + roof_h / 2
)
slope.scale = (slope_len, d + 0.6, 0.10)
slope.rotation_euler = (0, side * angle, 0)
assign(slope, roof_mat)
parts.append(slope)
# --- Фронтоны (треугольники на торцах) ---
for side in (-1, 1):
mesh = bpy.data.meshes.new(f"Gable_{side}")
verts = [(-w / 2, 0, 0), (w / 2, 0, 0), (0, 0, roof_h)]
faces = [(0, 1, 2)]
mesh.from_pydata(verts, [], faces)
mesh.update()
gable = bpy.data.objects.new(f"Gable_{side}", mesh)
bpy.context.collection.objects.link(gable)
gable.location = (0, side * d / 2, h)
assign(gable, MAT["wood_light"])
parts.append(gable)
# --- Дверь (передняя стена +Y) ---
bpy.ops.mesh.primitive_cube_add(size=1)
door = bpy.context.object
door.name = "Door"
door.scale = (0.7, 0.06, 1.3)
door.location = (0, d / 2 + 0.03, 0.65)
assign(door, MAT["wood_dark"])
parts.append(door)
# --- Окна (боковые стены ±X) ---
for side in (-1, 1):
bpy.ops.mesh.primitive_cube_add(size=1)
win = bpy.context.object
win.name = "Window"
win.scale = (0.06, 0.5, 0.5)
win.location = (side * (w / 2 + 0.03), 0, h * 0.6)
assign(win, MAT["wood_dark"])
parts.append(win)
# --- Труба ---
bpy.ops.mesh.primitive_cube_add(size=1)
chimney = bpy.context.object
chimney.name = "Chimney"
chimney.scale = (0.35, 0.35, 1.4)
chimney.location = (-w * 0.25, 0, h + roof_h * 0.75)
assign(chimney, MAT["stone"])
parts.append(chimney)
# --- Привязка ---
for p in parts:
p.parent = root
return root
# ============================================================
# ДЕРЕВО
# ============================================================
def create_tree(loc, height=4.0, seed=None):
if seed is not None:
random.seed(seed)
x, y, z = loc
bpy.ops.mesh.primitive_cylinder_add(
vertices=8, radius=0.18, depth=height,
location=(x, y, z + height / 2))
trunk = bpy.context.object
trunk.name = "Trunk"
assign(trunk, MAT["trunk"])
for _ in range(4):
ox = random.uniform(-0.55, 0.55)
oy = random.uniform(-0.55, 0.55)
oz = height + random.uniform(-0.4, 0.7)
r = random.uniform(0.85, 1.25)
bpy.ops.mesh.primitive_uv_sphere_add(
segments=12, ring_count=8, radius=r,
location=(x + ox, y + oy, z + oz))
leaves = bpy.context.object
leaves.name = "Leaves"
assign(leaves, MAT["leaves"])
# ============================================================
# КОЛОДЕЦ
# ============================================================
def create_well(loc):
x, y, z = loc
bpy.ops.mesh.primitive_cylinder_add(vertices=24, radius=0.85, depth=0.9,
location=(x, y, z + 0.45))
ring = bpy.context.object
ring.name = "Well_Ring"
assign(ring, MAT["stone"])
bpy.ops.mesh.primitive_cylinder_add(vertices=24, radius=0.7, depth=0.05,
location=(x, y, z + 0.85))
water = bpy.context.object
water.name = "Well_Water"
assign(water, MAT["water"])
for dx in (-0.7, 0.7):
bpy.ops.mesh.primitive_cube_add(size=1, location=(x + dx, y, z + 1.5))
post = bpy.context.object
post.scale = (0.16, 0.16, 1.5)
assign(post, MAT["wood_dark"])
bpy.ops.mesh.primitive_cone_add(
vertices=4, radius1=1.15, depth=0.7,
location=(x, y, z + 2.35),
rotation=(0, 0, math.radians(45)))
roof = bpy.context.object
roof.name = "Well_Roof"
assign(roof, MAT["roof"])
# ============================================================
# ЗАБОР
# ============================================================
def create_fence(start, end, posts=12, height=1.0):
x1, y1 = start
x2, y2 = end
dx = x2 - x1
dy = y2 - y1
total_len = math.hypot(dx, dy)
angle = math.atan2(dy, dx)
for i in range(posts):
t = i / (posts - 1)
x = x1 + dx * t
y = y1 + dy * t
bpy.ops.mesh.primitive_cube_add(size=1, location=(x, y, height / 2))
post = bpy.context.object
post.name = "Fence_Post"
post.scale = (0.12, 0.12, height)
assign(post, MAT["wood_dark"])
if i < posts - 1:
t2 = (i + 0.5) / (posts - 1)
mx = x1 + dx * t2
my = y1 + dy * t2
seg_len = total_len / (posts - 1)
for zz in (height * 0.35, height * 0.75):
bpy.ops.mesh.primitive_cube_add(size=1, location=(mx, my, zz))
rail = bpy.context.object
rail.name = "Fence_Rail"
rail.scale = (seg_len, 0.07, 0.07)
rail.rotation_euler = (0, 0, angle)
assign(rail, MAT["wood_dark"])
# ============================================================
# ФОНАРЬ
# ============================================================
def create_lamp(loc, height=2.6):
x, y, z = loc
bpy.ops.mesh.primitive_cylinder_add(vertices=8, radius=0.07,
depth=height,
location=(x, y, z + height / 2))
pole = bpy.context.object
pole.name = "Lamp_Pole"
assign(pole, MAT["wood_dark"])
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.18,
location=(x, y, z + height + 0.1))
bulb = bpy.context.object
bulb.name = "Lamp_Bulb"
assign(bulb, MAT["lamp"])
bpy.ops.object.light_add(type='POINT', location=(x, y, z + height + 0.1))
light = bpy.context.object
light.name = "Lamp_Light"
light.data.energy = 40
light.data.color = (1.0, 0.7, 0.35)
light.data.shadow_soft_size = 0.4
# ============================================================
# РАССТАНОВКА
# ============================================================
# Дома
create_house(loc=(-6, 4, 0), rot_z=math.radians(15), size=(3.2, 3.6, 2.5))
create_house(loc=(5.5, -4, 0), rot_z=math.radians(-35),
size=(2.7, 3.1, 2.1), roof_mat=MAT["roof_old"])
create_house(loc=(-5, -8, 0), rot_z=math.radians(60), size=(2.9, 3.3, 2.3))
create_house(loc=(7, 4.5, 0), rot_z=math.radians(200),
size=(2.5, 3.0, 2.0), roof_mat=MAT["roof_old"])
# Деревья
tree_coords = [(-11, -3), (9, 7), (-9, -10), (11, -8), (0, 9),
(-2, -13), (8, 9), (-13, 6), (13, 1)]
for i, pos in enumerate(tree_coords):
create_tree(pos, height=random.uniform(3.5, 5.2), seed=i)
# Колодец
create_well((0, 0, 0))
# Фонари
create_lamp((-3, 3))
create_lamp((3, -3))
create_lamp((6, 6))
# Забор по периметру
create_fence((-13, -12), (13, -12), posts=16)
create_fence((13, -12), (13, 12), posts=14)
create_fence((-13, -12), (-13, 12), posts=14)
create_fence((-13, 12), (13, 12), posts=16)
# Дороги
bpy.ops.mesh.primitive_plane_add(size=1, location=(0, 0, 0.02))
road = bpy.context.object
road.name = "Road"
road.scale = (3.2, 28, 1)
road.rotation_euler = (0, 0, math.radians(12))
assign(road, MAT["dirt"])
bpy.ops.mesh.primitive_plane_add(size=1, location=(0, 0, 0.02))
road2 = bpy.context.object
road2.name = "Road2"
road2.scale = (2.6, 20, 1)
road2.rotation_euler = (0, 0, math.radians(95))
assign(road2, MAT["dirt"])
# Камни
random.seed(42)
for _ in range(30):
x = random.uniform(-12, 12)
y = random.uniform(-12, 12)
if math.hypot(x, y) < 2.5:
continue
if abs(x) < 2 or abs(y) < 2:
continue
bpy.ops.mesh.primitive_ico_sphere_add(
subdivisions=1, radius=random.uniform(0.15, 0.35),
location=(x, y, 0.08))
stone = bpy.context.object
stone.name = "Stone"
stone.scale = (1, 1, 0.5)
stone.rotation_euler = (random.uniform(0, math.pi),
random.uniform(0, math.pi),
random.uniform(0, math.pi))
assign(stone, MAT["stone"])
# ============================================================
# ОСВЕЩЕНИЕ
# ============================================================
bpy.ops.object.light_add(type='SUN', location=(12, -14, 16))
sun = bpy.context.object
sun.name = "Sun"
sun.data.energy = 3.0
sun.data.color = (1.0, 0.72, 0.45)
sun.data.angle = math.radians(3)
sun.rotation_euler = (math.radians(55), 0, math.radians(50))
bpy.ops.object.light_add(type='AREA', location=(-10, 10, 8))
fill = bpy.context.object
fill.name = "Fill"
fill.data.energy = 300
fill.data.size = 12
fill.data.color = (0.55, 0.7, 1.0)
# ============================================================
# МИР
# ============================================================
world = bpy.data.worlds.get("World")
if world is None:
world = bpy.data.worlds.new("World")
bpy.context.scene.world = world
world.use_nodes = True
bg = None
for n in world.node_tree.nodes:
if n.type == 'BACKGROUND':
bg = n
break
if bg:
bg.inputs["Color"].default_value = (0.35, 0.45, 0.6, 1.0)
bg.inputs["Strength"].default_value = 0.85
# ============================================================
# КАМЕРА
# ============================================================
bpy.ops.object.camera_add(location=(20, -20, 11))
camera = bpy.context.object
camera.name = "Camera"
camera.rotation_euler = (math.radians(63), 0, math.radians(45))
camera.data.lens = 42
bpy.context.scene.camera = camera
# ============================================================
# РЕНДЕР
# ============================================================
scene = bpy.context.scene
scene.render.engine = 'CYCLES'
scene.cycles.samples = 128
scene.cycles.use_denoising = True
scene.cycles.device = 'CPU'
scene.render.resolution_x = 1920
scene.render.resolution_y = 1080
scene.view_settings.view_transform = 'AgX'
try:
scene.view_settings.look = 'AgX - Punchy'
except Exception:
pass
print("✅ Готово! Старая деревня создана в Blender 4.5.4")
print(" F12 — рендер, Numpad 0 — вид из камеры.")