391 lines
14 KiB
Python
391 lines
14 KiB
Python
"""
|
||
地形图层系统
|
||
提供完整的地形图层管理功能,支持多层纹理混合、Alpha混合等高级功能
|
||
"""
|
||
|
||
from panda3d.core import Texture, TextureStage, PNMImage, Filename
|
||
import os
|
||
|
||
class TerrainLayerSystem:
|
||
"""
|
||
地形图层系统类
|
||
提供完整的地形图层管理功能
|
||
"""
|
||
|
||
def __init__(self, world):
|
||
self.world = world
|
||
self.terrain_layers = {} # 存储地形图层信息
|
||
|
||
def create_layered_terrain(self, terrain_node, layer_configs):
|
||
"""
|
||
创建分层地形
|
||
layer_configs: 图层配置列表,每个配置包含纹理路径、混合贴图等信息
|
||
"""
|
||
try:
|
||
# 清除现有的地形纹理
|
||
terrain_node.clearTexture()
|
||
|
||
# 为每个图层创建纹理阶段
|
||
for i, layer_config in enumerate(layer_configs):
|
||
# 加载漫反射纹理
|
||
if 'diffuse' in layer_config:
|
||
diffuse_texture = self._load_texture(layer_config['diffuse'])
|
||
if diffuse_texture:
|
||
diffuse_stage = TextureStage(f"diffuse_{i}")
|
||
diffuse_stage.setSort(i * 10) # 设置排序
|
||
terrain_node.setTexture(diffuse_stage, diffuse_texture)
|
||
|
||
# 加载法线纹理
|
||
if 'normal' in layer_config:
|
||
normal_texture = self._load_texture(layer_config['normal'])
|
||
if normal_texture:
|
||
normal_stage = TextureStage(f"normal_{i}")
|
||
normal_stage.setMode(TextureStage.M_normal) # 设置为法线贴图模式
|
||
normal_stage.setSort(i * 10 + 1)
|
||
terrain_node.setTexture(normal_stage, normal_texture)
|
||
|
||
# 加载高光纹理
|
||
if 'specular' in layer_config:
|
||
specular_texture = self._load_texture(layer_config['specular'])
|
||
if specular_texture:
|
||
specular_stage = TextureStage(f"specular_{i}")
|
||
specular_stage.setMode(TextureStage.M_specular) # 设置为高光贴图模式
|
||
specular_stage.setSort(i * 10 + 2)
|
||
terrain_node.setTexture(specular_stage, specular_texture)
|
||
|
||
# 加载混合纹理(控制图层混合)
|
||
if 'blend' in layer_config:
|
||
blend_texture = self._load_texture(layer_config['blend'])
|
||
if blend_texture:
|
||
blend_stage = TextureStage(f"blend_{i}")
|
||
blend_stage.setMode(TextureStage.M_blend) # 设置为混合模式
|
||
blend_stage.setSort(i * 10 + 3)
|
||
terrain_node.setTexture(blend_stage, blend_texture)
|
||
|
||
# 加载Alpha纹理(控制透明度)
|
||
if 'alpha' in layer_config:
|
||
alpha_texture = self._load_texture(layer_config['alpha'])
|
||
if alpha_texture:
|
||
alpha_stage = TextureStage(f"alpha_{i}")
|
||
alpha_stage.setMode(TextureStage.M_modulate) # 设置为调制模式
|
||
alpha_stage.setSort(i * 10 + 4)
|
||
terrain_node.setTexture(alpha_stage, alpha_texture)
|
||
|
||
# 保存图层信息
|
||
self.terrain_layers[terrain_node] = layer_configs
|
||
|
||
print(f"为地形创建 {len(layer_configs)} 个图层系统")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"创建分层地形时出错: {e}")
|
||
return False
|
||
|
||
def add_layer(self, terrain_node, layer_config, layer_index=None):
|
||
"""
|
||
为地形添加新图层
|
||
"""
|
||
try:
|
||
if terrain_node not in self.terrain_layers:
|
||
# 如果地形还没有图层系统,创建一个新的
|
||
self.terrain_layers[terrain_node] = []
|
||
|
||
layers = self.terrain_layers[terrain_node]
|
||
|
||
# 确定插入位置
|
||
if layer_index is None:
|
||
layer_index = len(layers)
|
||
|
||
# 插入新图层
|
||
layers.insert(layer_index, layer_config)
|
||
|
||
# 重新应用所有图层
|
||
return self.create_layered_terrain(terrain_node, layers)
|
||
|
||
except Exception as e:
|
||
print(f"添加图层时出错: {e}")
|
||
return False
|
||
|
||
def remove_layer(self, terrain_node, layer_index):
|
||
"""
|
||
移除地形的指定图层
|
||
"""
|
||
try:
|
||
if terrain_node not in self.terrain_layers:
|
||
print("地形没有图层系统")
|
||
return False
|
||
|
||
layers = self.terrain_layers[terrain_node]
|
||
|
||
if layer_index < 0 or layer_index >= len(layers):
|
||
print("图层索引超出范围")
|
||
return False
|
||
|
||
# 移除图层
|
||
layers.pop(layer_index)
|
||
|
||
# 重新应用所有图层
|
||
return self.create_layered_terrain(terrain_node, layers)
|
||
|
||
except Exception as e:
|
||
print(f"移除图层时出错: {e}")
|
||
return False
|
||
|
||
def update_layer(self, terrain_node, layer_index, layer_config):
|
||
"""
|
||
更新地形图层
|
||
"""
|
||
try:
|
||
if terrain_node not in self.terrain_layers:
|
||
print("地形没有图层系统")
|
||
return False
|
||
|
||
layers = self.terrain_layers[terrain_node]
|
||
|
||
if layer_index < 0 or layer_index >= len(layers):
|
||
print("图层索引超出范围")
|
||
return False
|
||
|
||
# 更新图层配置
|
||
layers[layer_index] = layer_config
|
||
|
||
# 重新应用所有图层
|
||
return self.create_layered_terrain(terrain_node, layers)
|
||
|
||
except Exception as e:
|
||
print(f"更新图层时出错: {e}")
|
||
return False
|
||
|
||
def get_layer_info(self, terrain_node, layer_index):
|
||
"""
|
||
获取图层信息
|
||
"""
|
||
try:
|
||
if terrain_node not in self.terrain_layers:
|
||
print("地形没有图层系统")
|
||
return None
|
||
|
||
layers = self.terrain_layers[terrain_node]
|
||
|
||
if layer_index < 0 or layer_index >= len(layers):
|
||
print("图层索引超出范围")
|
||
return None
|
||
|
||
return layers[layer_index]
|
||
|
||
except Exception as e:
|
||
print(f"获取图层信息时出错: {e}")
|
||
return None
|
||
|
||
def blend_layers(self, terrain_node, blend_map_path):
|
||
"""
|
||
使用混合图混合所有图层
|
||
"""
|
||
try:
|
||
if terrain_node not in self.terrain_layers:
|
||
print("地形没有图层系统")
|
||
return False
|
||
|
||
# 加载混合图
|
||
blend_texture = self._load_texture(blend_map_path)
|
||
if not blend_texture:
|
||
print("无法加载混合图")
|
||
return False
|
||
|
||
# 应用混合图到所有图层
|
||
blend_stage = TextureStage("global_blend")
|
||
blend_stage.setMode(TextureStage.M_blend)
|
||
blend_stage.setSort(1000) # 确保在最后应用
|
||
terrain_node.setTexture(blend_stage, blend_texture)
|
||
|
||
print(f"应用全局混合图: {blend_map_path}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"混合图层时出错: {e}")
|
||
return False
|
||
|
||
def generate_blend_map(self, terrain_info, layer_weights, output_path):
|
||
"""
|
||
生成图层混合图
|
||
layer_weights: 每个图层的权重列表
|
||
"""
|
||
try:
|
||
heightfield = terrain_info['heightfield']
|
||
if not heightfield:
|
||
print("无法获取地形高度图数据")
|
||
return False
|
||
|
||
width = heightfield.getXSize()
|
||
height = heightfield.getYSize()
|
||
|
||
# 创建混合图
|
||
blend_image = PNMImage(width, height, 4) # RGBA格式
|
||
|
||
# 根据高度和权重生成混合图
|
||
for y in range(height):
|
||
for x in range(width):
|
||
height_value = heightfield.getRed(x, y)
|
||
|
||
# 根据高度值分配权重
|
||
weights = self._calculate_layer_weights(height_value, layer_weights)
|
||
|
||
# 设置RGBA值
|
||
blend_image.setRed(x, y, weights[0] if len(weights) > 0 else 0)
|
||
blend_image.setGreen(x, y, weights[1] if len(weights) > 1 else 0)
|
||
blend_image.setBlue(x, y, weights[2] if len(weights) > 2 else 0)
|
||
blend_image.setAlpha(x, y, weights[3] if len(weights) > 3 else 0)
|
||
|
||
# 保存混合图
|
||
output_dir = os.path.dirname(output_path)
|
||
if not os.path.exists(output_dir):
|
||
os.makedirs(output_dir)
|
||
|
||
success = blend_image.write(Filename.fromOsSpecific(output_path))
|
||
if success:
|
||
print(f"混合图生成成功: {output_path}")
|
||
return True
|
||
else:
|
||
print(f"混合图生成失败: {output_path}")
|
||
return False
|
||
|
||
except Exception as e:
|
||
print(f"生成混合图时出错: {e}")
|
||
return False
|
||
|
||
def _calculate_layer_weights(self, height_value, layer_weights):
|
||
"""
|
||
根据高度值计算图层权重
|
||
"""
|
||
# 简单的高度分区方法
|
||
weights = [0.0] * len(layer_weights)
|
||
|
||
if height_value < 0.3: # 低地
|
||
weights[0] = 1.0
|
||
elif height_value < 0.6: # 中等地
|
||
weights[1] = 1.0
|
||
elif height_value < 0.8: # 高地
|
||
weights[2] = 1.0
|
||
else: # 山峰
|
||
weights[3] = 1.0 if len(weights) > 3 else 0
|
||
|
||
return weights
|
||
|
||
def _load_texture(self, texture_path):
|
||
"""
|
||
加载纹理
|
||
"""
|
||
try:
|
||
if not os.path.exists(texture_path):
|
||
print(f"纹理文件不存在: {texture_path}")
|
||
return None
|
||
|
||
texture = self.world.loader.loadTexture(texture_path)
|
||
if texture:
|
||
print(f"纹理加载成功: {texture_path}")
|
||
return texture
|
||
else:
|
||
print(f"纹理加载失败: {texture_path}")
|
||
return None
|
||
except Exception as e:
|
||
print(f"加载纹理时出错: {e}")
|
||
return None
|
||
|
||
def create_texture_array(self, texture_paths, array_name):
|
||
"""
|
||
创建纹理数组
|
||
"""
|
||
try:
|
||
# 加载所有纹理
|
||
textures = []
|
||
for path in texture_paths:
|
||
texture = self._load_texture(path)
|
||
if texture:
|
||
textures.append(texture)
|
||
|
||
if not textures:
|
||
print("没有成功加载任何纹理")
|
||
return None
|
||
|
||
# 创建纹理数组
|
||
# 注意:Panda3D的纹理数组创建比较复杂,这里只是一个概念实现
|
||
print(f"创建纹理数组: {array_name},包含 {len(textures)} 个纹理")
|
||
return textures # 临时返回纹理列表
|
||
|
||
except Exception as e:
|
||
print(f"创建纹理数组时出错: {e}")
|
||
return None
|
||
|
||
def apply_texture_array(self, terrain_node, texture_array, array_name):
|
||
"""
|
||
应用纹理数组到地形
|
||
"""
|
||
try:
|
||
if not texture_array:
|
||
print("纹理数组为空")
|
||
return False
|
||
|
||
# 为每个纹理创建纹理阶段
|
||
for i, texture in enumerate(texture_array):
|
||
texture_stage = TextureStage(f"{array_name}_{i}")
|
||
texture_stage.setSort(i)
|
||
terrain_node.setTexture(texture_stage, texture)
|
||
|
||
print(f"应用纹理数组: {array_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"应用纹理数组时出错: {e}")
|
||
return False
|
||
|
||
def save_layer_config(self, terrain_node, config_path):
|
||
"""
|
||
保存图层配置到文件
|
||
"""
|
||
try:
|
||
if terrain_node not in self.terrain_layers:
|
||
print("地形没有图层系统")
|
||
return False
|
||
|
||
import json
|
||
|
||
# 保存图层配置
|
||
layers = self.terrain_layers[terrain_node]
|
||
|
||
# 确保输出目录存在
|
||
output_dir = os.path.dirname(config_path)
|
||
if not os.path.exists(output_dir):
|
||
os.makedirs(output_dir)
|
||
|
||
with open(config_path, 'w', encoding='utf-8') as f:
|
||
json.dump(layers, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"图层配置保存成功: {config_path}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"保存图层配置时出错: {e}")
|
||
return False
|
||
|
||
def load_layer_config(self, terrain_node, config_path):
|
||
"""
|
||
从文件加载图层配置
|
||
"""
|
||
try:
|
||
if not os.path.exists(config_path):
|
||
print(f"图层配置文件不存在: {config_path}")
|
||
return False
|
||
|
||
import json
|
||
|
||
# 加载图层配置
|
||
with open(config_path, 'r', encoding='utf-8') as f:
|
||
layers = json.load(f)
|
||
|
||
# 应用图层配置
|
||
self.terrain_layers[terrain_node] = layers
|
||
return self.create_layered_terrain(terrain_node, layers)
|
||
|
||
except Exception as e:
|
||
print(f"加载图层配置时出错: {e}")
|
||
return False |