EG/plugins/user/terrain_editor/materials/terrain_layer_system.py
2025-10-30 11:46:41 +08:00

391 lines
14 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.

"""
地形图层系统
提供完整的地形图层管理功能支持多层纹理混合、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