EG/plugins/user/terrain_editor/vegetation/vegetation_system.py
2025-12-12 16:16:15 +08:00

1152 lines
49 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
地形植被系统
提供完整的植被分布、生长模拟、LOD控制、生态模拟等功能
"""
from panda3d.core import NodePath, Vec3, Point3, BitMask32
from panda3d.core import Texture, TextureStage, Material
import random
import math
import json
import os
import numpy as np
class VegetationSystem:
"""
地形植被系统类
提供完整的植被分布、生长模拟、LOD控制、生态模拟等功能
"""
def __init__(self, world):
self.world = world
self.vegetation_instances = [] # 存储植被实例
self.vegetation_types = {} # 存储植被类型定义
self.density_maps = {} # 存储密度图
self.lod_settings = {} # 存储LOD设置
self.ecosystem = {} # 存储生态系统信息
self.growth_simulator = None # 生长模拟器
self.wind_effect = { # 风力效果参数
'strength': 0.5,
'direction': Vec3(1, 0, 0),
'speed': 1.0,
'turbulence': 0.2
}
def define_vegetation_type(self, name, model_path, scale_range=(1.0, 1.0),
rotation_range=(0, 360), color_variation=(0.8, 1.2),
growth_params=None, ecosystem_role='general'):
"""
定义植被类型
growth_params: 生长参数字典
ecosystem_role: 生态角色 ('tree', 'shrub', 'grass', 'flower', 'general')
"""
self.vegetation_types[name] = {
'model_path': model_path,
'scale_range': scale_range,
'rotation_range': rotation_range,
'color_variation': color_variation,
'instances': [],
'growth_params': growth_params or {
'max_height': 10.0,
'growth_rate': 0.1,
'lifespan': 100.0,
'seed_production': 0.5,
'water_requirement': 0.5,
'sunlight_requirement': 0.7
},
'ecosystem_role': ecosystem_role,
'seasonal_variations': {
'spring': {'color_shift': (0.1, 0.2, 0.0), 'scale_factor': 1.1},
'summer': {'color_shift': (0.0, 0.0, 0.0), 'scale_factor': 1.0},
'autumn': {'color_shift': (0.3, 0.2, 0.0), 'scale_factor': 0.9},
'winter': {'color_shift': (0.1, 0.1, 0.1), 'scale_factor': 0.8}
}
}
print(f"定义植被类型: {name} (生态角色: {ecosystem_role})")
def load_vegetation_models(self):
"""
预加载所有植被模型
"""
for name, veg_type in self.vegetation_types.items():
try:
model = self.world.loader.loadModel(veg_type['model_path'])
if model:
veg_type['model'] = model
print(f"加载植被模型: {name}")
else:
print(f"无法加载植被模型: {name}")
except Exception as e:
print(f"加载植被模型 {name} 时出错: {e}")
def generate_vegetation(self, terrain_info, vegetation_config, density=0.1,
distribution_method='random'):
"""
在地形上生成植被
vegetation_config: 植被配置字典,指定不同类型植被的分布参数
distribution_method: 分布方法 ('random', 'clustered', 'ecosystem')
"""
try:
terrain_node = terrain_info['node']
heightfield = terrain_info['heightfield']
if not heightfield:
print("无法获取地形高度图数据")
return False
width = heightfield.getXSize()
height = heightfield.getYSize()
# 获取地形节点信息
terrain_pos = terrain_node.getPos()
terrain_scale = terrain_node.getScale()
# 根据分布方法选择生成策略
if distribution_method == 'ecosystem':
return self._generate_ecosystem_vegetation(terrain_info, vegetation_config, density)
elif distribution_method == 'clustered':
return self._generate_clustered_vegetation(terrain_info, vegetation_config, density)
else:
return self._generate_random_vegetation(terrain_info, vegetation_config, density)
except Exception as e:
print(f"生成植被时出错: {e}")
return False
def _generate_random_vegetation(self, terrain_info, vegetation_config, density):
"""
随机生成植被
"""
try:
terrain_node = terrain_info['node']
heightfield = terrain_info['heightfield']
width = heightfield.getXSize()
height = heightfield.getYSize()
terrain_pos = terrain_node.getPos()
terrain_scale = terrain_node.getScale()
# 为每种植被类型生成实例
total_instances = 0
for veg_type_name, veg_params in vegetation_config.items():
if veg_type_name not in self.vegetation_types:
print(f"未定义的植被类型: {veg_type_name}")
continue
veg_type = self.vegetation_types[veg_type_name]
# 获取环境限制条件
min_height = veg_params.get('min_height', 0.0)
max_height = veg_params.get('max_height', 1.0)
slope_threshold = veg_params.get('slope_threshold', 45.0)
moisture_preference = veg_params.get('moisture_preference', 0.5)
# 计算植被数量
total_points = int(width * height * density * veg_params.get('density_factor', 1.0))
# 生成植被实例
instances_created = 0
attempts = 0
max_attempts = total_points * 10 # 防止无限循环
while instances_created < total_points and attempts < max_attempts:
attempts += 1
# 随机选择位置
x = random.randint(0, width - 1)
y = random.randint(0, height - 1)
# 获取高度值
height_value = heightfield.getRed(x, y)
# 检查高度限制
if height_value < min_height or height_value > max_height:
continue
# 检查坡度
if self._calculate_slope(heightfield, x, y) > slope_threshold:
continue
# 检查湿度条件(简化模拟)
moisture = self._calculate_moisture(heightfield, x, y)
if abs(moisture - moisture_preference) > 0.3:
continue
# 计算世界坐标
center_offset = (width - 1) / 2
world_x = (x - center_offset) * terrain_scale.getX() + terrain_pos.getX()
world_y = (y - center_offset) * terrain_scale.getY() + terrain_pos.getY()
world_z = height_value * terrain_scale.getZ() + terrain_pos.getZ()
# 创建植被实例
if self._create_vegetation_instance(veg_type_name, Point3(world_x, world_y, world_z)):
instances_created += 1
total_instances += instances_created
print(f"植被类型 {veg_type_name} 生成了 {instances_created} 个实例")
print(f"随机生成植被完成,共创建 {total_instances} 个实例")
return True
except Exception as e:
print(f"随机生成植被时出错: {e}")
return False
def _generate_clustered_vegetation(self, terrain_info, vegetation_config, density):
"""
簇状生成植被
"""
try:
terrain_node = terrain_info['node']
heightfield = terrain_info['heightfield']
width = heightfield.getXSize()
height = heightfield.getYSize()
terrain_pos = terrain_node.getPos()
terrain_scale = terrain_node.getScale()
# 为每种植被类型生成簇
total_instances = 0
for veg_type_name, veg_params in vegetation_config.items():
if veg_type_name not in self.vegetation_types:
continue
# 计算簇的数量
cluster_count = int((width * height * density * veg_params.get('density_factor', 1.0)) / 10)
for _ in range(cluster_count):
# 随机选择簇中心
center_x = random.randint(0, width - 1)
center_y = random.randint(0, height - 1)
center_height = heightfield.getRed(center_x, center_y)
# 确定簇大小
cluster_size = random.randint(3, 10)
# 在簇内生成植被
for _ in range(cluster_size):
# 在簇中心附近随机分布
offset_x = random.randint(-5, 5)
offset_y = random.randint(-5, 5)
x = max(0, min(width - 1, center_x + offset_x))
y = max(0, min(height - 1, center_y + offset_y))
# 计算世界坐标
center_offset = (width - 1) / 2
world_x = (x - center_offset) * terrain_scale.getX() + terrain_pos.getX()
world_y = (y - center_offset) * terrain_scale.getY() + terrain_pos.getY()
world_z = heightfield.getRed(x, y) * terrain_scale.getZ() + terrain_pos.getZ()
# 创建植被实例
if self._create_vegetation_instance(veg_type_name, Point3(world_x, world_y, world_z)):
total_instances += 1
print(f"簇状生成植被完成,共创建 {total_instances} 个实例")
return True
except Exception as e:
print(f"簇状生成植被时出错: {e}")
return False
def _generate_ecosystem_vegetation(self, terrain_info, vegetation_config, density):
"""
生态系统方式生成植被
"""
try:
terrain_node = terrain_info['node']
heightfield = terrain_info['heightfield']
width = heightfield.getXSize()
height = heightfield.getYSize()
terrain_pos = terrain_node.getPos()
terrain_scale = terrain_node.getScale()
# 创建生态位地图
niche_map = self._create_niche_map(heightfield)
# 为每种植被类型根据生态位生成
total_instances = 0
for veg_type_name, veg_params in vegetation_config.items():
if veg_type_name not in self.vegetation_types:
continue
veg_type = self.vegetation_types[veg_type_name]
ecosystem_role = veg_type['ecosystem_role']
# 根据生态角色确定分布偏好
if ecosystem_role == 'tree':
preferred_niches = ['high_ground', 'flat_area']
elif ecosystem_role == 'shrub':
preferred_niches = ['slope', 'transition']
elif ecosystem_role == 'grass':
preferred_niches = ['flat_area', 'low_ground']
else:
preferred_niches = ['any']
# 计算植被数量
total_points = int(width * height * density * veg_params.get('density_factor', 1.0))
# 根据生态位偏好生成植被
instances_created = 0
attempts = 0
max_attempts = total_points * 20
while instances_created < total_points and attempts < max_attempts:
attempts += 1
# 根据生态位偏好选择位置
x, y = self._select_ecosystem_position(niche_map, preferred_niches)
if x is None or y is None:
continue
# 检查环境条件
height_value = heightfield.getRed(x, y)
slope = self._calculate_slope(heightfield, x, y)
# 根据植被类型检查条件
if not self._check_environment_conditions(veg_type_name, height_value, slope):
continue
# 计算世界坐标
center_offset = (width - 1) / 2
world_x = (x - center_offset) * terrain_scale.getX() + terrain_pos.getX()
world_y = (y - center_offset) * terrain_scale.getY() + terrain_pos.getY()
world_z = height_value * terrain_scale.getZ() + terrain_pos.getZ()
# 创建植被实例
if self._create_vegetation_instance(veg_type_name, Point3(world_x, world_y, world_z)):
instances_created += 1
total_instances += instances_created
print(f"生态系统生成植被类型 {veg_type_name}: {instances_created} 个实例")
print(f"生态系统生成植被完成,共创建 {total_instances} 个实例")
return True
except Exception as e:
print(f"生态系统生成植被时出错: {e}")
return False
def _create_niche_map(self, heightfield):
"""
创建生态位地图
"""
try:
width = heightfield.getXSize()
height = heightfield.getYSize()
niche_map = {}
for x in range(width):
for y in range(height):
# 分析位置特征
slope = self._calculate_slope(heightfield, x, y)
height_value = heightfield.getRed(x, y)
# 确定生态位
if slope > 30:
niche = 'steep_slope'
elif slope > 15:
niche = 'slope'
elif height_value > 0.7:
niche = 'high_ground'
elif height_value < 0.3:
niche = 'low_ground'
else:
niche = 'flat_area'
# 检查是否为过渡区域
if self._is_transition_zone(heightfield, x, y):
niche = 'transition'
niche_map[(x, y)] = niche
return niche_map
except Exception as e:
print(f"创建生态位地图时出错: {e}")
return {}
def _is_transition_zone(self, heightfield, x, y):
"""
检查是否为过渡区域
"""
try:
width = heightfield.getXSize()
height = heightfield.getYSize()
if x < 2 or x >= width - 2 or y < 2 or y >= height - 2:
return False
# 计算周围点的高度差异
center_height = heightfield.getRed(x, y)
height_diff = 0
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if dx == 0 and dy == 0:
continue
neighbor_height = heightfield.getRed(x + dx, y + dy)
height_diff += abs(neighbor_height - center_height)
# 如果高度差异较大,则为过渡区域
return height_diff > 0.1
except:
return False
def _select_ecosystem_position(self, niche_map, preferred_niches):
"""
根据生态位偏好选择位置
"""
try:
if not niche_map:
return None, None
# 如果偏好'any',随机选择
if 'any' in preferred_niches:
positions = list(niche_map.keys())
if positions:
return random.choice(positions)
return None, None
# 根据偏好选择
preferred_positions = []
for pos, niche in niche_map.items():
if niche in preferred_niches:
preferred_positions.append(pos)
if preferred_positions:
return random.choice(preferred_positions)
# 如果没有偏好位置,随机选择
positions = list(niche_map.keys())
if positions:
return random.choice(positions)
return None, None
except:
return None, None
def _check_environment_conditions(self, veg_type_name, height_value, slope):
"""
检查环境条件是否适合植被生长
"""
try:
if veg_type_name not in self.vegetation_types:
return True
veg_type = self.vegetation_types[veg_type_name]
growth_params = veg_type['growth_params']
# 检查高度限制
max_height = growth_params.get('max_height', 100.0)
if height_value * 100 > max_height: # 假设高度值0-1对应0-100米
return False
# 检查坡度限制
max_slope = growth_params.get('max_slope', 45.0)
if slope > max_slope:
return False
return True
except:
return True
def _calculate_slope(self, heightfield, x, y):
"""
计算指定点的坡度
"""
try:
width = heightfield.getXSize()
height = heightfield.getYSize()
# 获取周围点的高度
heights = []
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
nx, ny = x + dx, y + dy
if 0 <= nx < width and 0 <= ny < height:
heights.append(heightfield.getRed(nx, ny))
else:
heights.append(heightfield.getRed(x, y))
# 计算X和Y方向的梯度
dx = heights[2] - heights[0] # 右侧点 - 左侧点
dy = heights[6] - heights[0] # 下方点 - 上方点
# 计算坡度(角度)
slope = math.degrees(math.atan(math.sqrt(dx*dx + dy*dy)))
return slope
except:
return 0.0
def _calculate_moisture(self, heightfield, x, y):
"""
计算指定点的湿度(简化模拟)
"""
try:
# 简化的湿度计算:低洼地区湿度较高
width = heightfield.getXSize()
height = heightfield.getYSize()
# 计算周围点的平均高度
total_height = 0
count = 0
for dx in [-2, -1, 0, 1, 2]:
for dy in [-2, -1, 0, 1, 2]:
nx, ny = x + dx, y + dy
if 0 <= nx < width and 0 <= ny < height:
total_height += heightfield.getRed(nx, ny)
count += 1
if count > 0:
avg_height = total_height / count
current_height = heightfield.getRed(x, y)
# 低洼地区湿度较高
moisture = max(0.0, min(1.0, 0.5 + (avg_height - current_height) * 2))
return moisture
return 0.5
except:
return 0.5
def _create_vegetation_instance(self, veg_type_name, position):
"""
创建植被实例
"""
try:
if veg_type_name not in self.vegetation_types:
return None
veg_type = self.vegetation_types[veg_type_name]
# 检查模型是否已加载
if 'model' not in veg_type:
# 尝试加载模型
try:
model = self.world.loader.loadModel(veg_type['model_path'])
if model:
veg_type['model'] = model
else:
print(f"无法加载植被模型: {veg_type['model_path']}")
return None
except Exception as e:
print(f"加载植被模型时出错: {e}")
return None
# 克隆模型创建实例
instance = veg_type['model'].copyTo(self.world.render)
instance.setPos(position)
# 应用随机缩放
scale_min, scale_max = veg_type['scale_range']
scale = random.uniform(scale_min, scale_max)
instance.setScale(scale)
# 应用随机旋转
rot_min, rot_max = veg_type['rotation_range']
rotation = random.uniform(rot_min, rot_max)
instance.setH(rotation)
# 应用颜色变化
color_min, color_max = veg_type['color_variation']
color_factor = random.uniform(color_min, color_max)
# 应用季节性颜色变化
season_color = self._get_seasonal_color(veg_type_name)
if season_color:
# 这里可以应用颜色变化到模型材质
pass
# 设置标签
instance.setTag("vegetation_type", veg_type_name)
instance.setTag("is_scene_element", "1")
instance.setTag("tree_item_type", "VEGETATION_NODE")
instance.setTag("creation_time", str(self.world.globalClock.getFrameTime()))
# 添加碰撞体
instance.setCollideMask(BitMask32.bit(3)) # 使用第3位作为植被碰撞掩码
# 初始化生长状态
creation_time = self.world.globalClock.getFrameTime()
# 保存实例信息
instance_info = {
'node': instance,
'type': veg_type_name,
'position': position,
'scale': scale,
'rotation': rotation,
'creation_time': creation_time,
'age': 0.0,
'health': 1.0,
'growth_stage': 0, # 0=seedling, 1=sapling, 2=mature, 3=elderly
'last_update': creation_time,
'wind_offset': Vec3(0, 0, 0)
}
self.vegetation_instances.append(instance_info)
veg_type['instances'].append(instance_info)
return instance_info
except Exception as e:
print(f"创建植被实例时出错: {e}")
return None
def _get_seasonal_color(self, veg_type_name):
"""
获取季节性颜色变化
"""
try:
if veg_type_name in self.vegetation_types:
veg_type = self.vegetation_types[veg_type_name]
# 这里可以根据当前季节返回相应的颜色变化
# 简化实现,返回默认值
return veg_type['seasonal_variations'].get('summer', {'color_shift': (0, 0, 0)})
return None
except:
return None
def remove_vegetation_instance(self, instance_info):
"""
移除植被实例
"""
try:
if instance_info in self.vegetation_instances:
# 从全局列表中移除
self.vegetation_instances.remove(instance_info)
# 从类型列表中移除
veg_type_name = instance_info['type']
if veg_type_name in self.vegetation_types:
if instance_info in self.vegetation_types[veg_type_name]['instances']:
self.vegetation_types[veg_type_name]['instances'].remove(instance_info)
# 移除节点
if instance_info['node'] and not instance_info['node'].isEmpty():
instance_info['node'].removeNode()
print("移除植被实例")
return True
except Exception as e:
print(f"移除植被实例时出错: {e}")
return False
def clear_all_vegetation(self):
"""
清除所有植被
"""
try:
# 移除所有实例节点
for instance_info in self.vegetation_instances:
if instance_info['node'] and not instance_info['node'].isEmpty():
instance_info['node'].removeNode()
# 清空数据结构
self.vegetation_instances = []
# 清空各类型实例列表
for veg_type in self.vegetation_types.values():
veg_type['instances'] = []
print("清除所有植被")
return True
except Exception as e:
print(f"清除所有植被时出错: {e}")
return False
def set_vegetation_lod(self, lod_settings):
"""
设置植被LOD
lod_settings: LOD设置字典包含距离阈值和简化策略
"""
self.lod_settings = lod_settings
print("设置植被LOD")
def update_vegetation_lod(self, camera_pos):
"""
更新植被LOD
"""
try:
for instance_info in self.vegetation_instances:
node = instance_info['node']
if node and not node.isEmpty():
# 计算到相机的距离
distance = (node.getPos() - camera_pos).length()
# 根据距离应用LOD策略
if distance > self.lod_settings.get('far_distance', 100):
# 远距离时隐藏
node.hide()
elif distance > self.lod_settings.get('medium_distance', 50):
# 中距离时显示简化版本
node.show()
self._apply_medium_lod(node)
else:
# 近距离时显示完整版本
node.show()
self._apply_full_lod(node)
except Exception as e:
print(f"更新植被LOD时出错: {e}")
def _apply_medium_lod(self, node):
"""
应用中距离LOD
"""
try:
# 减少多边形数量的简化版本
# 这里可以实现模型简化或使用公告板
# 简化实现:使用缩放来模拟简化效果
original_scale = node.getScale()
node.setScale(original_scale * 0.8)
except:
pass
def _apply_full_lod(self, node):
"""
应用完整LOD
"""
# 恢复完整细节
pass
def simulate_growth(self, time_step=1.0):
"""
模拟植被生长
"""
try:
current_time = self.world.globalClock.getFrameTime()
for instance_info in self.vegetation_instances:
node = instance_info['node']
if node and not node.isEmpty():
# 获取植被类型信息
veg_type_name = instance_info['type']
if veg_type_name not in self.vegetation_types:
continue
veg_type = self.vegetation_types[veg_type_name]
growth_params = veg_type['growth_params']
# 计算生长时间
elapsed_time = current_time - instance_info['last_update']
instance_info['last_update'] = current_time
# 更新年龄
instance_info['age'] += elapsed_time
# 计算生长率(受健康状况影响)
growth_rate = growth_params.get('growth_rate', 0.1) * instance_info['health']
# 生长模拟
if instance_info['growth_stage'] == 0: # seedling
# 幼苗阶段:快速增长
new_scale = node.getScale() * (1.0 + growth_rate * elapsed_time * 0.1)
node.setScale(new_scale)
# 检查是否进入下一阶段
if instance_info['age'] > 5.0: # 5秒后进入sapling阶段
instance_info['growth_stage'] = 1
elif instance_info['growth_stage'] == 1: # sapling
# 树苗阶段:中等速度增长
new_scale = node.getScale() * (1.0 + growth_rate * elapsed_time * 0.05)
node.setScale(new_scale)
# 检查是否进入下一阶段
if instance_info['age'] > 20.0: # 20秒后进入mature阶段
instance_info['growth_stage'] = 2
elif instance_info['growth_stage'] == 2: # mature
# 成熟阶段:缓慢增长
new_scale = node.getScale() * (1.0 + growth_rate * elapsed_time * 0.01)
node.setScale(new_scale)
# 检查是否进入下一阶段
lifespan = growth_params.get('lifespan', 100.0)
if instance_info['age'] > lifespan * 0.8: # 80%寿命后进入elderly阶段
instance_info['growth_stage'] = 3
elif instance_info['growth_stage'] == 3: # elderly
# 老年阶段:可能开始衰退
if random.random() < 0.001: # 小概率开始衰退
instance_info['health'] -= 0.01 * elapsed_time
if instance_info['health'] < 0:
instance_info['health'] = 0
# 缩放可能减小
health_factor = max(0.5, instance_info['health'])
new_scale = node.getScale() * (1.0 - (1.0 - health_factor) * 0.001 * elapsed_time)
node.setScale(new_scale)
# 更新实例信息
instance_info['scale'] = node.getScale()
except Exception as e:
print(f"模拟植被生长时出错: {e}")
def apply_wind_effect(self, wind_strength=None, wind_direction=None, wind_speed=None):
"""
应用风力效果
"""
try:
# 更新风力参数
if wind_strength is not None:
self.wind_effect['strength'] = wind_strength
if wind_direction is not None:
self.wind_effect['direction'] = wind_direction
if wind_speed is not None:
self.wind_effect['speed'] = wind_speed
current_time = self.world.globalClock.getFrameTime()
for instance_info in self.vegetation_instances:
node = instance_info['node']
if node and not node.isEmpty():
# 计算风力影响
time_factor = current_time * self.wind_effect['speed']
# 添加湍流效果
turbulence = math.sin(time_factor * 3) * self.wind_effect['turbulence']
# 计算摆动量
sway_amount = math.sin(time_factor) * self.wind_effect['strength'] * 10
sway_amount += turbulence * 5
# 根据植被类型调整摆动(大树摆动较小,小草摆动较大)
veg_type_name = instance_info['type']
if veg_type_name in self.vegetation_types:
veg_type = self.vegetation_types[veg_type_name]
scale_factor = node.getScale().length() # 简化的大尺度因子
sway_amount *= max(0.1, 1.0 / scale_factor) # 大树摆动小
# 应用摆动效果
original_rotation = instance_info['rotation']
new_h = original_rotation + sway_amount
node.setH(new_h)
# 添加轻微的位置偏移
sway_offset = Vec3(
math.cos(time_factor * 2) * self.wind_effect['strength'] * 0.1,
math.sin(time_factor * 2) * self.wind_effect['strength'] * 0.1,
0
)
instance_info['wind_offset'] = sway_offset
except Exception as e:
print(f"应用风力效果时出错: {e}")
def simulate_ecosystem_interactions(self, time_step=1.0):
"""
模拟生态系统相互作用
"""
try:
# 模拟植物间的竞争、繁殖、死亡等生态过程
current_time = self.world.globalClock.getFrameTime()
# 检查植物健康状况
for instance_info in self.vegetation_instances[:]: # 使用切片复制避免修改列表时的问题
node = instance_info['node']
if not node or node.isEmpty():
continue
# 检查年龄相关死亡
veg_type_name = instance_info['type']
if veg_type_name in self.vegetation_types:
veg_type = self.vegetation_types[veg_type_name]
lifespan = veg_type['growth_params'].get('lifespan', 100.0)
if instance_info['age'] > lifespan:
# 植物死亡
if random.random() < 0.01: # 每帧1%概率死亡
print(f"植物死亡: {veg_type_name}")
self.remove_vegetation_instance(instance_info)
continue
# 检查健康状况
if instance_info['health'] <= 0:
print(f"植物因健康状况不佳死亡: {veg_type_name}")
self.remove_vegetation_instance(instance_info)
continue
# 模拟繁殖(简化)
if instance_info['growth_stage'] >= 2: # 成熟植物才能繁殖
growth_params = veg_type['growth_params']
seed_production = growth_params.get('seed_production', 0.5)
if random.random() < seed_production * 0.001: # 繁殖概率
self._attempt_reproduction(instance_info)
except Exception as e:
print(f"模拟生态系统相互作用时出错: {e}")
def _attempt_reproduction(self, parent_instance):
"""
尝试繁殖
"""
try:
# 在父植物附近生成新植物
parent_pos = parent_instance['position']
# 随机生成在附近位置
offset_range = 5.0 # 5米范围内
new_x = parent_pos.getX() + random.uniform(-offset_range, offset_range)
new_y = parent_pos.getY() + random.uniform(-offset_range, offset_range)
new_z = parent_pos.getZ() # 高度保持一致
# 创建新植物实例
veg_type_name = parent_instance['type']
self._create_vegetation_instance(veg_type_name, Point3(new_x, new_y, new_z))
except Exception as e:
print(f"尝试繁殖时出错: {e}")
def save_vegetation_data(self, terrain_info, output_path):
"""
保存植被数据
"""
try:
# 收集植被数据
vegetation_data = {
'vegetation_types': {},
'instances': [],
'ecosystem_settings': {
'wind_effect': self.wind_effect
}
}
# 保存植被类型定义
for name, veg_type in self.vegetation_types.items():
vegetation_data['vegetation_types'][name] = {
'model_path': veg_type['model_path'],
'scale_range': veg_type['scale_range'],
'rotation_range': veg_type['rotation_range'],
'color_variation': veg_type['color_variation'],
'growth_params': veg_type['growth_params'],
'ecosystem_role': veg_type['ecosystem_role']
}
# 保存实例信息
for instance_info in self.vegetation_instances:
instance_data = {
'type': instance_info['type'],
'position': [instance_info['position'].x,
instance_info['position'].y,
instance_info['position'].z],
'scale': [instance_info['scale'].x,
instance_info['scale'].y,
instance_info['scale'].z],
'rotation': instance_info['rotation'],
'age': instance_info['age'],
'health': instance_info['health'],
'growth_stage': instance_info['growth_stage'],
'creation_time': instance_info['creation_time']
}
vegetation_data['instances'].append(instance_data)
# 确保输出目录存在
output_dir = os.path.dirname(output_path)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# 写入JSON文件
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(vegetation_data, f, indent=2, ensure_ascii=False)
print(f"植被数据保存成功: {output_path}")
return True
except Exception as e:
print(f"保存植被数据时出错: {e}")
return False
def load_vegetation_data(self, terrain_info, input_path):
"""
加载植被数据
"""
try:
if not os.path.exists(input_path):
print(f"植被数据文件不存在: {input_path}")
return False
# 读取JSON文件
with open(input_path, 'r', encoding='utf-8') as f:
vegetation_data = json.load(f)
# 清除现有植被
self.clear_all_vegetation()
# 加载植被类型定义
if 'vegetation_types' in vegetation_data:
for name, veg_def in vegetation_data['vegetation_types'].items():
self.define_vegetation_type(
name=name,
model_path=veg_def['model_path'],
scale_range=veg_def.get('scale_range', (1.0, 1.0)),
rotation_range=veg_def.get('rotation_range', (0, 360)),
color_variation=veg_def.get('color_variation', (0.8, 1.2)),
growth_params=veg_def.get('growth_params'),
ecosystem_role=veg_def.get('ecosystem_role', 'general')
)
# 加载实例
if 'instances' in vegetation_data:
for instance_data in vegetation_data['instances']:
position = Point3(instance_data['position'][0],
instance_data['position'][1],
instance_data['position'][2])
# 创建实例
instance_info = self._create_vegetation_instance(instance_data['type'], position)
if instance_info:
# 恢复实例状态
if 'scale' in instance_data:
scale_vec = instance_data['scale']
if len(scale_vec) == 3:
instance_info['node'].setScale(scale_vec[0], scale_vec[1], scale_vec[2])
else:
instance_info['node'].setScale(scale_vec[0])
if 'age' in instance_data:
instance_info['age'] = instance_data['age']
if 'health' in instance_data:
instance_info['health'] = instance_data['health']
if 'growth_stage' in instance_data:
instance_info['growth_stage'] = instance_data['growth_stage']
if 'creation_time' in instance_data:
instance_info['creation_time'] = instance_data['creation_time']
# 恢复生态系统设置
if 'ecosystem_settings' in vegetation_data:
eco_settings = vegetation_data['ecosystem_settings']
if 'wind_effect' in eco_settings:
self.wind_effect = eco_settings['wind_effect']
print(f"植被数据加载成功: {input_path}")
return True
except Exception as e:
print(f"加载植被数据时出错: {e}")
return False
def get_vegetation_stats(self):
"""
获取植被统计信息
"""
stats = {
'total_instances': len(self.vegetation_instances),
'vegetation_types': {},
'ecosystem_stats': {
'wind_strength': self.wind_effect['strength'],
'wind_speed': self.wind_effect['speed']
},
'growth_stages': {
'seedling': 0,
'sapling': 0,
'mature': 0,
'elderly': 0
}
}
# 统计植被类型
for name, veg_type in self.vegetation_types.items():
stats['vegetation_types'][name] = {
'count': len(veg_type['instances']),
'model_path': veg_type['model_path'],
'ecosystem_role': veg_type['ecosystem_role']
}
# 统计生长阶段
for instance_info in self.vegetation_instances:
stage = instance_info['growth_stage']
if stage == 0:
stats['growth_stages']['seedling'] += 1
elif stage == 1:
stats['growth_stages']['sapling'] += 1
elif stage == 2:
stats['growth_stages']['mature'] += 1
elif stage == 3:
stats['growth_stages']['elderly'] += 1
return stats
def paint_vegetation(self, terrain_info, brush_position, brush_radius,
vegetation_type, density=1.0):
"""
绘制植被(笔刷工具)
"""
try:
terrain_node = terrain_info['node']
heightfield = terrain_info['heightfield']
if not heightfield:
print("无法获取地形高度图数据")
return False
width = heightfield.getXSize()
height = heightfield.getYSize()
# 获取地形节点信息
terrain_pos = terrain_node.getPos()
terrain_scale = terrain_node.getScale()
# 计算笔刷区域
center_offset = (width - 1) / 2
brush_x = int((brush_position.getX() - terrain_pos.getX()) / terrain_scale.getX() + center_offset)
brush_y = int((brush_position.getY() - terrain_pos.getY()) / terrain_scale.getY() + center_offset)
brush_pixels = int(brush_radius / max(terrain_scale.getX(), terrain_scale.getY()))
# 在笔刷区域内生成植被
instances_created = 0
for dx in range(-brush_pixels, brush_pixels + 1):
for dy in range(-brush_pixels, brush_pixels + 1):
# 检查是否在笔刷范围内
distance = math.sqrt(dx*dx + dy*dy)
if distance <= brush_pixels:
# 计算实际位置
x = brush_x + dx
y = brush_y + dy
# 检查边界
if 0 <= x < width and 0 <= y < height:
# 根据密度决定是否创建
if random.random() < density:
# 计算世界坐标
world_x = (x - center_offset) * terrain_scale.getX() + terrain_pos.getX()
world_y = (y - center_offset) * terrain_scale.getY() + terrain_pos.getY()
world_z = heightfield.getRed(x, y) * terrain_scale.getZ() + terrain_pos.getZ()
# 创建植被实例
if self._create_vegetation_instance(vegetation_type, Point3(world_x, world_y, world_z)):
instances_created += 1
print(f"绘制植被完成,创建了 {instances_created} 个实例")
return True
except Exception as e:
print(f"绘制植被时出错: {e}")
return False
def update_all_systems(self, time_delta):
"""
更新所有植被系统
"""
try:
# 更新生长模拟
self.simulate_growth(time_delta)
# 更新风力效果
self.apply_wind_effect()
# 更新生态系统相互作用
self.simulate_ecosystem_interactions(time_delta)
# 更新LOD如果已设置相机位置
# 这里需要从外部传入相机位置
# self.update_vegetation_lod(camera_pos)
except Exception as e:
print(f"更新植被系统时出错: {e}")