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