""" 地形植被系统 提供完整的植被分布、生长模拟、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}")