Files

112 lines
4.4 KiB
Python

import bpy
import bmesh
import mathutils
import random
import math
class SolidTreeGenerator:
def __init__(self, levels=5, length=3.0, radius=0.3):
self.levels = levels
self.base_length = length
self.base_radius = radius
# Хранилище для данных: (start_pos, end_pos, radius_start, radius_end)
self.branches_data = []
def calculate_tree(self, start_pos, direction, length, radius, level):
if level <= 0 or length < 0.1:
return
end_pos = start_pos + direction * length
# Сохраняем данные сегмента
self.branches_data.append({
'start': start_pos.copy(),
'end': end_pos.copy(),
'r_start': radius,
'r_end': radius * 0.7
})
# 1. Основной ствол (продолжение)
trunk_dir = (direction + self.get_random_vector(0.1)).normalized()
self.calculate_tree(end_pos, trunk_dir, length * 0.8, radius * 0.7, level - 1)
# 2. Боковые ветки (ветвление)
if level > 1:
num_sides = random.randint(2, 3) # Минимум 2 ветки для видимости
for _ in range(num_sides):
# Создаем вектор, сильно отклоненный от ствола (30-60 градусов)
axis = self.get_random_vector(1.0).normalized()
angle = math.radians(random.uniform(30, 60))
side_dir = direction.copy()
side_dir.rotate(mathutils.Quaternion(axis, angle))
# Боковые ветки короче
self.calculate_tree(end_pos, side_dir, length * 0.6, radius * 0.5, level - 1)
def get_random_vector(self, intensity):
return mathutils.Vector((
random.uniform(-intensity, intensity),
random.uniform(-intensity, intensity),
random.uniform(-intensity, intensity)
))
def build_mesh(self):
mesh = bpy.data.meshes.new("TreeMesh")
obj = bpy.data.objects.new("Tree", mesh)
bpy.context.collection.objects.link(obj)
bm = bmesh.new()
skin_layer = bm.verts.layers.skin.verify()
# Словарь для предотвращения дублирования вершин в одной точке
# Ключ - кортеж координат, Значение - объект вершины BMesh
vert_map = {}
for b in self.branches_data:
# Превращаем координаты в кортежи для словаря
s_key = tuple(round(v, 4) for v in b['start'])
e_key = tuple(round(v, 4) for v in b['end'])
# Получаем или создаем начальную вершину
if s_key not in vert_map:
v_start = bm.verts.new(b['start'])
v_start[skin_layer].radius = (b['r_start'], b['r_start'])
vert_map[s_key] = v_start
else:
v_start = vert_map[s_key]
# Получаем или создаем конечную вершину
if e_key not in vert_map:
v_end = bm.verts.new(b['end'])
v_end[skin_layer].radius = (b['r_end'], b['r_end'])
vert_map[e_key] = v_end
else:
v_end = vert_map[e_key]
# Создаем ребро, если его еще нет
if not bm.edges.get((v_start, v_end)):
bm.edges.new((v_start, v_end))
# Находим корень (самую нижнюю точку) и помечаем его
root_v = min(bm.verts, key=lambda v: v.co.z)
root_v[skin_layer].use_root = True
bm.to_mesh(mesh)
bm.free()
# Модификаторы
obj.modifiers.new(name="Skin", type='SKIN')
sub = obj.modifiers.new(name="Subdiv", type='SUBSURF')
sub.levels = 1 # Для начала 1, чтобы не тормозило
# Очистка сцены
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
# Запуск
generator = SolidTreeGenerator(levels=5, length=3.0, radius=0.4)
generator.calculate_tree(mathutils.Vector((0,0,0)), mathutils.Vector((0,0,1)), 3.0, 0.4, 5)
generator.build_mesh()