699 lines
30 KiB
Python
699 lines
30 KiB
Python
"""
|
|
地形编辑工具模块
|
|
"""
|
|
|
|
import math
|
|
import random
|
|
from panda3d.core import PNMImage
|
|
|
|
class TerrainTools:
|
|
"""
|
|
地形编辑工具类
|
|
提供完整的地形编辑功能,包括抬高、降低、平坦化、平滑、噪声等工具
|
|
"""
|
|
|
|
def __init__(self, world):
|
|
self.world = world
|
|
self.current_tool = "抬高工具"
|
|
self.radius = 10.0
|
|
self.strength = 50.0
|
|
self.operation = "添加"
|
|
self.is_editing = False
|
|
self.last_edit_pos = None
|
|
self.brush_spacing = 1.0 # 画笔间隔,用于连续编辑
|
|
|
|
# 工具参数
|
|
self.smooth_factor = 0.5
|
|
self.noise_scale = 1.0
|
|
self.erosion_factor = 0.1
|
|
self.thermal_erosion_factor = 0.05
|
|
|
|
def set_tool(self, tool_name):
|
|
"""
|
|
设置当前工具
|
|
"""
|
|
valid_tools = ["抬高工具", "降低工具", "平坦化工具", "平滑工具", "噪声工具", "侵蚀工具", "热侵蚀工具"]
|
|
if tool_name in valid_tools:
|
|
self.current_tool = tool_name
|
|
print(f"设置地形编辑工具: {tool_name}")
|
|
return True
|
|
else:
|
|
print(f"无效的工具名称: {tool_name}")
|
|
return False
|
|
|
|
def set_radius(self, radius):
|
|
"""
|
|
设置工具半径
|
|
"""
|
|
self.radius = max(0.1, min(100.0, float(radius)))
|
|
|
|
def set_strength(self, strength):
|
|
"""
|
|
设置工具强度
|
|
"""
|
|
self.strength = max(0.0, min(100.0, float(strength)))
|
|
|
|
def set_operation(self, operation):
|
|
"""
|
|
设置操作类型
|
|
"""
|
|
valid_operations = ["添加", "减去", "设置"]
|
|
if operation in valid_operations:
|
|
self.operation = operation
|
|
else:
|
|
print(f"无效的操作类型: {operation}")
|
|
|
|
def set_smooth_factor(self, factor):
|
|
"""
|
|
设置平滑因子
|
|
"""
|
|
self.smooth_factor = max(0.0, min(1.0, float(factor)))
|
|
|
|
def set_noise_scale(self, scale):
|
|
"""
|
|
设置噪声缩放
|
|
"""
|
|
self.noise_scale = max(0.1, min(10.0, float(scale)))
|
|
|
|
def set_erosion_factor(self, factor):
|
|
"""
|
|
设置侵蚀因子
|
|
"""
|
|
self.erosion_factor = max(0.0, min(1.0, float(factor)))
|
|
|
|
def set_thermal_erosion_factor(self, factor):
|
|
"""
|
|
设置热侵蚀因子
|
|
"""
|
|
self.thermal_erosion_factor = max(0.0, min(1.0, float(factor)))
|
|
|
|
def set_brush_spacing(self, spacing):
|
|
"""
|
|
设置画笔间隔
|
|
"""
|
|
self.brush_spacing = max(0.1, min(10.0, float(spacing)))
|
|
|
|
def start_editing(self, x, y):
|
|
"""
|
|
开始编辑地形
|
|
"""
|
|
self.is_editing = True
|
|
self.last_edit_pos = (x, y)
|
|
self._apply_tool(x, y)
|
|
|
|
def update_editing(self, x, y):
|
|
"""
|
|
更新编辑过程
|
|
"""
|
|
if self.is_editing:
|
|
# 检查是否需要应用工具(基于画笔间隔)
|
|
if self.last_edit_pos:
|
|
distance = math.sqrt((x - self.last_edit_pos[0])**2 + (y - self.last_edit_pos[1])**2)
|
|
if distance >= self.brush_spacing:
|
|
self._apply_tool(x, y)
|
|
self.last_edit_pos = (x, y)
|
|
else:
|
|
self._apply_tool(x, y)
|
|
self.last_edit_pos = (x, y)
|
|
|
|
def stop_editing(self):
|
|
"""
|
|
停止编辑地形
|
|
"""
|
|
self.is_editing = False
|
|
self.last_edit_pos = None
|
|
|
|
def _apply_tool(self, x, y):
|
|
"""
|
|
应用当前工具到指定位置
|
|
"""
|
|
# 获取当前选中的地形
|
|
selected_terrain = self._get_selected_terrain()
|
|
if not selected_terrain:
|
|
# 如果没有选中地形,尝试获取场景中的第一个地形
|
|
selected_terrain = self._get_first_terrain()
|
|
if not selected_terrain:
|
|
print("没有可用的地形")
|
|
return
|
|
|
|
# 根据当前工具类型应用不同的操作
|
|
if self.current_tool == "抬高工具":
|
|
self._raise_terrain(selected_terrain, x, y)
|
|
elif self.current_tool == "降低工具":
|
|
self._lower_terrain(selected_terrain, x, y)
|
|
elif self.current_tool == "平坦化工具":
|
|
self._flatten_terrain(selected_terrain, x, y)
|
|
elif self.current_tool == "平滑工具":
|
|
self._smooth_terrain(selected_terrain, x, y)
|
|
elif self.current_tool == "噪声工具":
|
|
self._noise_terrain(selected_terrain, x, y)
|
|
elif self.current_tool == "侵蚀工具":
|
|
self._erode_terrain(selected_terrain, x, y)
|
|
elif self.current_tool == "热侵蚀工具":
|
|
self._thermal_erode_terrain(selected_terrain, x, y)
|
|
|
|
def _get_selected_terrain(self):
|
|
"""
|
|
获取当前选中的地形
|
|
"""
|
|
# 如果有选择系统,获取选中的地形
|
|
if hasattr(self.world, 'selection') and self.world.selection.selectedNode:
|
|
selected_node = self.world.selection.selectedNode
|
|
# 检查选中的节点是否是地形节点
|
|
if selected_node.getTag("tree_item_type") == "TERRAIN_NODE":
|
|
# 在地形管理器中查找对应的地形信息
|
|
if hasattr(self.world, 'terrain_manager'):
|
|
for terrain_info in self.world.terrain_manager.terrains:
|
|
if terrain_info['node'] == selected_node:
|
|
return terrain_info
|
|
return None
|
|
|
|
def _get_first_terrain(self):
|
|
"""
|
|
获取场景中的第一个地形
|
|
"""
|
|
if hasattr(self.world, 'terrain_manager') and self.world.terrain_manager.terrains:
|
|
return self.world.terrain_manager.terrains[0]
|
|
return None
|
|
|
|
def _raise_terrain(self, terrain_info, x, y):
|
|
"""
|
|
抬高地形
|
|
"""
|
|
if hasattr(self.world, 'terrain_manager'):
|
|
operation = "add"
|
|
if self.operation == "减去":
|
|
operation = "subtract"
|
|
elif self.operation == "设置":
|
|
operation = "set"
|
|
|
|
self.world.terrain_manager.modifyTerrainHeight(
|
|
terrain_info, x, y, self.radius, self.strength, operation
|
|
)
|
|
|
|
def _lower_terrain(self, terrain_info, x, y):
|
|
"""
|
|
降低地形
|
|
"""
|
|
if hasattr(self.world, 'terrain_manager'):
|
|
operation = "subtract"
|
|
if self.operation == "添加":
|
|
operation = "add"
|
|
elif self.operation == "设置":
|
|
operation = "set"
|
|
|
|
self.world.terrain_manager.modifyTerrainHeight(
|
|
terrain_info, x, y, self.radius, self.strength, operation
|
|
)
|
|
|
|
def _flatten_terrain(self, terrain_info, x, y):
|
|
"""
|
|
平坦化地形
|
|
"""
|
|
if hasattr(self.world, 'terrain_manager'):
|
|
# 平坦化工具使用特殊的"set"操作
|
|
self.world.terrain_manager.modifyTerrainHeight(
|
|
terrain_info, x, y, self.radius, self.strength, "set"
|
|
)
|
|
|
|
def _smooth_terrain(self, terrain_info, x, y):
|
|
"""
|
|
平滑地形
|
|
"""
|
|
if not hasattr(self.world, 'terrain_manager'):
|
|
return
|
|
|
|
try:
|
|
terrain_node = terrain_info['node']
|
|
heightfield = terrain_info['heightfield']
|
|
|
|
if not heightfield:
|
|
print("无法获取地形高度图数据")
|
|
return
|
|
|
|
# 获取高度图尺寸
|
|
width = heightfield.getXSize()
|
|
height = heightfield.getYSize()
|
|
|
|
# 获取地形节点信息
|
|
terrain_pos = terrain_node.getPos()
|
|
terrain_scale = terrain_node.getScale()
|
|
|
|
# 坐标转换
|
|
relative_x = x - terrain_pos.getX()
|
|
relative_y = y - terrain_pos.getY()
|
|
scaled_x = relative_x / terrain_scale.getX()
|
|
scaled_y = relative_y / terrain_scale.getY()
|
|
center_offset = (width - 1) / 2
|
|
center_x = int(scaled_x + center_offset)
|
|
center_y = int(scaled_y + center_offset)
|
|
|
|
# 检查中心点是否在有效范围内
|
|
if not (0 <= center_x < width and 0 <= center_y < height):
|
|
return
|
|
|
|
# 计算有效编辑半径
|
|
effective_radius = max(1, int(self.radius / max(terrain_scale.getX(), terrain_scale.getY())))
|
|
effective_radius = min(effective_radius, width // 4, height // 4)
|
|
|
|
# 创建临时高度图用于存储平滑结果
|
|
temp_heightfield = PNMImage(width, height)
|
|
temp_heightfield.copyFrom(heightfield)
|
|
|
|
# 应用平滑算法
|
|
modified = False
|
|
for dx in range(-effective_radius, effective_radius + 1):
|
|
for dy in range(-effective_radius, effective_radius + 1):
|
|
distance = math.sqrt(dx*dx + dy*dy)
|
|
if distance <= effective_radius:
|
|
target_x = center_x + dx
|
|
target_y = center_y + dy
|
|
|
|
# 检查边界
|
|
if 0 <= target_x < width and 0 <= target_y < height:
|
|
# 计算邻域平均值
|
|
total_height = 0.0
|
|
count = 0
|
|
|
|
# 检查周围的8个邻居
|
|
for nx in range(-1, 2):
|
|
for ny in range(-1, 2):
|
|
neighbor_x = target_x + nx
|
|
neighbor_y = target_y + ny
|
|
|
|
# 检查邻居是否在边界内
|
|
if 0 <= neighbor_x < width and 0 <= neighbor_y < height:
|
|
total_height += heightfield.getRed(neighbor_x, neighbor_y)
|
|
count += 1
|
|
|
|
if count > 0:
|
|
average_height = total_height / count
|
|
current_height = heightfield.getRed(target_x, target_y)
|
|
|
|
# 应用平滑因子
|
|
new_height = current_height * (1 - self.smooth_factor) + average_height * self.smooth_factor
|
|
|
|
# 设置新高度到临时高度图
|
|
temp_heightfield.setRed(target_x, target_y, new_height)
|
|
temp_heightfield.setGreen(target_x, target_y, new_height)
|
|
temp_heightfield.setBlue(target_x, target_y, new_height)
|
|
modified = True
|
|
|
|
# 如果有修改,则更新地形
|
|
if modified:
|
|
# 更新高度图
|
|
terrain_info['heightfield'] = temp_heightfield
|
|
|
|
# 重新设置高度图并生成地形
|
|
terrain = terrain_info['terrain']
|
|
terrain.setHeightfield(temp_heightfield)
|
|
terrain.generate()
|
|
|
|
# 重新创建碰撞体
|
|
self._recreate_collision(terrain_node)
|
|
|
|
print(f"✓ 地形已平滑: 位置({x}, {y}), 半径{self.radius}")
|
|
|
|
except Exception as e:
|
|
print(f"平滑地形时出错: {e}")
|
|
|
|
def _noise_terrain(self, terrain_info, x, y):
|
|
"""
|
|
添加噪声到地形
|
|
"""
|
|
if not hasattr(self.world, 'terrain_manager'):
|
|
return
|
|
|
|
try:
|
|
terrain_node = terrain_info['node']
|
|
heightfield = terrain_info['heightfield']
|
|
|
|
if not heightfield:
|
|
print("无法获取地形高度图数据")
|
|
return
|
|
|
|
# 获取高度图尺寸
|
|
width = heightfield.getXSize()
|
|
height = heightfield.getYSize()
|
|
|
|
# 获取地形节点信息
|
|
terrain_pos = terrain_node.getPos()
|
|
terrain_scale = terrain_node.getScale()
|
|
|
|
# 坐标转换
|
|
relative_x = x - terrain_pos.getX()
|
|
relative_y = y - terrain_pos.getY()
|
|
scaled_x = relative_x / terrain_scale.getX()
|
|
scaled_y = relative_y / terrain_scale.getY()
|
|
center_offset = (width - 1) / 2
|
|
center_x = int(scaled_x + center_offset)
|
|
center_y = int(scaled_y + center_offset)
|
|
|
|
# 检查中心点是否在有效范围内
|
|
if not (0 <= center_x < width and 0 <= center_y < height):
|
|
return
|
|
|
|
# 计算有效编辑半径
|
|
effective_radius = max(1, int(self.radius / max(terrain_scale.getX(), terrain_scale.getY())))
|
|
effective_radius = min(effective_radius, width // 4, height // 4)
|
|
|
|
# 应用噪声算法
|
|
modified = False
|
|
for dx in range(-effective_radius, effective_radius + 1):
|
|
for dy in range(-effective_radius, effective_radius + 1):
|
|
distance = math.sqrt(dx*dx + dy*dy)
|
|
if distance <= effective_radius:
|
|
# 计算影响权重
|
|
weight = 1.0 - (distance / effective_radius) if effective_radius > 0 else 1.0
|
|
|
|
target_x = center_x + dx
|
|
target_y = center_y + dy
|
|
|
|
# 检查边界
|
|
if 0 <= target_x < width and 0 <= target_y < height:
|
|
# 生成噪声值
|
|
noise_value = random.uniform(-1.0, 1.0) * self.noise_scale
|
|
|
|
# 获取当前高度
|
|
current_height = heightfield.getRed(target_x, target_y)
|
|
|
|
# 应用噪声
|
|
new_height = max(0.0, min(1.0, current_height + noise_value * weight * self.strength * 0.01))
|
|
|
|
# 设置新高度
|
|
heightfield.setRed(target_x, target_y, new_height)
|
|
heightfield.setGreen(target_x, target_y, new_height)
|
|
heightfield.setBlue(target_x, target_y, new_height)
|
|
modified = True
|
|
|
|
# 如果有修改,则更新地形
|
|
if modified:
|
|
# 重新设置高度图并生成地形
|
|
terrain = terrain_info['terrain']
|
|
terrain.setHeightfield(heightfield)
|
|
terrain.generate()
|
|
|
|
# 重新创建碰撞体
|
|
self._recreate_collision(terrain_node)
|
|
|
|
print(f"✓ 地形已添加噪声: 位置({x}, {y}), 半径{self.radius}")
|
|
|
|
except Exception as e:
|
|
print(f"添加噪声到地形时出错: {e}")
|
|
|
|
def _erode_terrain(self, terrain_info, x, y):
|
|
"""
|
|
水力侵蚀地形
|
|
"""
|
|
if not hasattr(self.world, 'terrain_manager'):
|
|
return
|
|
|
|
try:
|
|
terrain_node = terrain_info['node']
|
|
heightfield = terrain_info['heightfield']
|
|
|
|
if not heightfield:
|
|
print("无法获取地形高度图数据")
|
|
return
|
|
|
|
# 获取高度图尺寸
|
|
width = heightfield.getXSize()
|
|
height = heightfield.getYSize()
|
|
|
|
# 获取地形节点信息
|
|
terrain_pos = terrain_node.getPos()
|
|
terrain_scale = terrain_node.getScale()
|
|
|
|
# 坐标转换
|
|
relative_x = x - terrain_pos.getX()
|
|
relative_y = y - terrain_pos.getY()
|
|
scaled_x = relative_x / terrain_scale.getX()
|
|
scaled_y = relative_y / terrain_scale.getY()
|
|
center_offset = (width - 1) / 2
|
|
center_x = int(scaled_x + center_offset)
|
|
center_y = int(scaled_y + center_offset)
|
|
|
|
# 检查中心点是否在有效范围内
|
|
if not (0 <= center_x < width and 0 <= center_y < height):
|
|
return
|
|
|
|
# 计算有效编辑半径
|
|
effective_radius = max(1, int(self.radius / max(terrain_scale.getX(), terrain_scale.getY())))
|
|
effective_radius = min(effective_radius, width // 4, height // 4)
|
|
|
|
# 应用简单的侵蚀算法
|
|
modified = False
|
|
for dx in range(-effective_radius, effective_radius + 1):
|
|
for dy in range(-effective_radius, effective_radius + 1):
|
|
distance = math.sqrt(dx*dx + dy*dy)
|
|
if distance <= effective_radius:
|
|
# 计算影响权重
|
|
weight = 1.0 - (distance / effective_radius) if effective_radius > 0 else 1.0
|
|
|
|
target_x = center_x + dx
|
|
target_y = center_y + dy
|
|
|
|
# 检查边界
|
|
if 0 <= target_x < width and 0 <= target_y < height:
|
|
# 简单的侵蚀模型:降低高点,填充低点
|
|
current_height = heightfield.getRed(target_x, target_y)
|
|
|
|
# 根据高度确定侵蚀效果
|
|
# 高度越高,侵蚀越强
|
|
erosion_amount = self.erosion_factor * weight * (1.0 - current_height) * self.strength * 0.01
|
|
|
|
# 应用侵蚀
|
|
new_height = max(0.0, min(1.0, current_height - erosion_amount))
|
|
|
|
# 设置新高度
|
|
heightfield.setRed(target_x, target_y, new_height)
|
|
heightfield.setGreen(target_x, target_y, new_height)
|
|
heightfield.setBlue(target_x, target_y, new_height)
|
|
modified = True
|
|
|
|
# 如果有修改,则更新地形
|
|
if modified:
|
|
# 重新设置高度图并生成地形
|
|
terrain = terrain_info['terrain']
|
|
terrain.setHeightfield(heightfield)
|
|
terrain.generate()
|
|
|
|
# 重新创建碰撞体
|
|
self._recreate_collision(terrain_node)
|
|
|
|
print(f"✓ 地形已应用侵蚀: 位置({x}, {y}), 半径{self.radius}")
|
|
|
|
except Exception as e:
|
|
print(f"侵蚀地形时出错: {e}")
|
|
|
|
def _thermal_erode_terrain(self, terrain_info, x, y):
|
|
"""
|
|
热侵蚀地形(模拟重力作用)
|
|
"""
|
|
if not hasattr(self.world, 'terrain_manager'):
|
|
return
|
|
|
|
try:
|
|
terrain_node = terrain_info['node']
|
|
heightfield = terrain_info['heightfield']
|
|
|
|
if not heightfield:
|
|
print("无法获取地形高度图数据")
|
|
return
|
|
|
|
# 获取高度图尺寸
|
|
width = heightfield.getXSize()
|
|
height = heightfield.getYSize()
|
|
|
|
# 获取地形节点信息
|
|
terrain_pos = terrain_node.getPos()
|
|
terrain_scale = terrain_node.getScale()
|
|
|
|
# 坐标转换
|
|
relative_x = x - terrain_pos.getX()
|
|
relative_y = y - terrain_pos.getY()
|
|
scaled_x = relative_x / terrain_scale.getX()
|
|
scaled_y = relative_y / terrain_scale.getY()
|
|
center_offset = (width - 1) / 2
|
|
center_x = int(scaled_x + center_offset)
|
|
center_y = int(scaled_y + center_offset)
|
|
|
|
# 检查中心点是否在有效范围内
|
|
if not (0 <= center_x < width and 0 <= center_y < height):
|
|
return
|
|
|
|
# 计算有效编辑半径
|
|
effective_radius = max(1, int(self.radius / max(terrain_scale.getX(), terrain_scale.getY())))
|
|
effective_radius = min(effective_radius, width // 4, height // 4)
|
|
|
|
# 应用热侵蚀算法
|
|
modified = False
|
|
for dx in range(-effective_radius, effective_radius + 1):
|
|
for dy in range(-effective_radius, effective_radius + 1):
|
|
distance = math.sqrt(dx*dx + dy*dy)
|
|
if distance <= effective_radius:
|
|
# 计算影响权重
|
|
weight = 1.0 - (distance / effective_radius) if effective_radius > 0 else 1.0
|
|
|
|
target_x = center_x + dx
|
|
target_y = center_y + dy
|
|
|
|
# 检查边界
|
|
if 0 <= target_x < width and 0 <= target_y < height:
|
|
# 检查周围的邻居,找到最低点
|
|
current_height = heightfield.getRed(target_x, target_y)
|
|
lowest_neighbor_height = current_height
|
|
lowest_neighbor_x = target_x
|
|
lowest_neighbor_y = target_y
|
|
|
|
# 检查8个邻居
|
|
for nx in range(-1, 2):
|
|
for ny in range(-1, 2):
|
|
if nx == 0 and ny == 0:
|
|
continue # 跳过中心点
|
|
|
|
neighbor_x = target_x + nx
|
|
neighbor_y = target_y + ny
|
|
|
|
# 检查邻居是否在边界内
|
|
if 0 <= neighbor_x < width and 0 <= neighbor_y < height:
|
|
neighbor_height = heightfield.getRed(neighbor_x, neighbor_y)
|
|
if neighbor_height < lowest_neighbor_height:
|
|
lowest_neighbor_height = neighbor_height
|
|
lowest_neighbor_x = neighbor_x
|
|
lowest_neighbor_y = neighbor_y
|
|
|
|
# 如果有更低的邻居,移动部分物质
|
|
height_diff = current_height - lowest_neighbor_height
|
|
if height_diff > 0:
|
|
# 移动物质
|
|
move_amount = min(height_diff, self.thermal_erosion_factor * weight * self.strength * 0.01)
|
|
|
|
# 降低当前点
|
|
new_height = max(0.0, current_height - move_amount)
|
|
heightfield.setRed(target_x, target_y, new_height)
|
|
heightfield.setGreen(target_x, target_y, new_height)
|
|
heightfield.setBlue(target_x, target_y, new_height)
|
|
|
|
# 提高最低邻居点
|
|
neighbor_new_height = min(1.0, lowest_neighbor_height + move_amount * 0.5)
|
|
heightfield.setRed(lowest_neighbor_x, lowest_neighbor_y, neighbor_new_height)
|
|
heightfield.setGreen(lowest_neighbor_x, lowest_neighbor_y, neighbor_new_height)
|
|
heightfield.setBlue(lowest_neighbor_x, lowest_neighbor_y, neighbor_new_height)
|
|
|
|
modified = True
|
|
|
|
# 如果有修改,则更新地形
|
|
if modified:
|
|
# 重新设置高度图并生成地形
|
|
terrain = terrain_info['terrain']
|
|
terrain.setHeightfield(heightfield)
|
|
terrain.generate()
|
|
|
|
# 重新创建碰撞体
|
|
self._recreate_collision(terrain_node)
|
|
|
|
print(f"✓ 地形已应用热侵蚀: 位置({x}, {y}), 半径{self.radius}")
|
|
|
|
except Exception as e:
|
|
print(f"热侵蚀地形时出错: {e}")
|
|
|
|
def _recreate_collision(self, terrain_node):
|
|
"""
|
|
重新创建地形碰撞体
|
|
"""
|
|
try:
|
|
from panda3d.core import BitMask32
|
|
|
|
# 移除旧的地形碰撞体
|
|
for child in terrain_node.getChildren():
|
|
if child.getName().startswith("terrain_collision_"):
|
|
child.removeNode()
|
|
|
|
# 设置地形节点的碰撞掩码
|
|
terrain_node.setCollideMask(BitMask32.bit(2))
|
|
|
|
# 为地形的每个子节点也设置碰撞掩码
|
|
for child in terrain_node.getChildren():
|
|
child.setCollideMask(BitMask32.bit(2))
|
|
|
|
except Exception as e:
|
|
print(f"重新创建碰撞体时出错: {e}")
|
|
|
|
def apply_brush(self, terrain_info, x, y, brush_data):
|
|
"""
|
|
应用画笔数据到地形
|
|
"""
|
|
if not hasattr(self.world, 'terrain_manager'):
|
|
return
|
|
|
|
try:
|
|
terrain_node = terrain_info['node']
|
|
heightfield = terrain_info['heightfield']
|
|
|
|
if not heightfield:
|
|
print("无法获取地形高度图数据")
|
|
return
|
|
|
|
# 获取高度图尺寸
|
|
width = heightfield.getXSize()
|
|
height = heightfield.getYSize()
|
|
|
|
# 获取地形节点信息
|
|
terrain_pos = terrain_node.getPos()
|
|
terrain_scale = terrain_node.getScale()
|
|
|
|
# 坐标转换
|
|
relative_x = x - terrain_pos.getX()
|
|
relative_y = y - terrain_pos.getY()
|
|
scaled_x = relative_x / terrain_scale.getX()
|
|
scaled_y = relative_y / terrain_scale.getY()
|
|
center_offset = (width - 1) / 2
|
|
center_x = int(scaled_x + center_offset)
|
|
center_y = int(scaled_y + center_offset)
|
|
|
|
# 检查中心点是否在有效范围内
|
|
if not (0 <= center_x < width and 0 <= center_y < height):
|
|
return
|
|
|
|
# 应用画笔数据
|
|
modified = False
|
|
brush_size = len(brush_data)
|
|
brush_radius = brush_size // 2
|
|
|
|
for dx in range(-brush_radius, brush_radius + 1):
|
|
for dy in range(-brush_radius, brush_radius + 1):
|
|
if 0 <= brush_radius + dx < brush_size and 0 <= brush_radius + dy < brush_size:
|
|
brush_value = brush_data[brush_radius + dy][brush_radius + dx]
|
|
|
|
target_x = center_x + dx
|
|
target_y = center_y + dy
|
|
|
|
# 检查边界
|
|
if 0 <= target_x < width and 0 <= target_y < height:
|
|
# 获取当前高度
|
|
current_height = heightfield.getRed(target_x, target_y)
|
|
|
|
# 应用画笔值
|
|
new_height = max(0.0, min(1.0, current_height + brush_value * self.strength * 0.01))
|
|
|
|
# 设置新高度
|
|
heightfield.setRed(target_x, target_y, new_height)
|
|
heightfield.setGreen(target_x, target_y, new_height)
|
|
heightfield.setBlue(target_x, target_y, new_height)
|
|
modified = True
|
|
|
|
# 如果有修改,则更新地形
|
|
if modified:
|
|
# 重新设置高度图并生成地形
|
|
terrain = terrain_info['terrain']
|
|
terrain.setHeightfield(heightfield)
|
|
terrain.generate()
|
|
|
|
# 重新创建碰撞体
|
|
self._recreate_collision(terrain_node)
|
|
|
|
print(f"✓ 应用画笔数据到地形: 位置({x}, {y})")
|
|
|
|
except Exception as e:
|
|
print(f"应用画笔数据到地形时出错: {e}") |