733 lines
27 KiB
Python
733 lines
27 KiB
Python
|
||
import math
|
||
import warnings
|
||
|
||
from direct.actor.Actor import Actor
|
||
|
||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||
|
||
from QPanda3D.Panda3DWorld import Panda3DWorld
|
||
from panda3d.core import (CardMaker, Vec4, Vec3, AmbientLight, DirectionalLight,
|
||
Point3, WindowProperties,Material,LColor)
|
||
from direct.showbase.ShowBaseGlobal import globalClock
|
||
from scene.scene_manager import SceneManager
|
||
|
||
class CoreWorld(Panda3DWorld):
|
||
"""核心世界功能类 - 负责基础的3D世界设置和核心功能"""
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
|
||
# 初始化基础属性
|
||
self.qtWidget = None # Qt部件引用(用于获取准确的渲染区域尺寸)
|
||
|
||
# 设置相机控制参数
|
||
#self.cameraSpeed = 200.0 # 移动速度
|
||
self.cameraSpeed=20.0
|
||
#self.cameraRotateSpeed = 40.0 # 旋转速度
|
||
self.cameraRotateSpeed = 10.0
|
||
|
||
# 鼠标控制相关变量
|
||
self.lastMouseX = 0
|
||
self.lastMouseY = 0
|
||
self.mouseRightPressed = False
|
||
|
||
# 初始化世界
|
||
self._setupCamera()
|
||
self._setupLighting()
|
||
self._setupGround()
|
||
self._loadFont()
|
||
|
||
#self.start_day_night_cycle(duration_seconds=300.0)
|
||
# self.accept("1", lambda: self.set_daytime("4:00")) # 清晨
|
||
# self.accept("2", lambda: self.set_daytime("6:00")) # 中午
|
||
# self.accept("3", lambda: self.set_daytime("8:00")) # 傍晚
|
||
# self.accept("4", lambda: self.set_daytime("10:00")) # 深夜
|
||
# self.accept("5", lambda: self.set_daytime("12:00")) # 深夜
|
||
# self.accept("6", lambda: self.set_daytime("14:00")) # 深夜
|
||
# self.accept("7", lambda: self.set_daytime("16:00")) # 深夜
|
||
# self.accept("8", lambda: self.set_daytime("18:00")) # 深夜
|
||
# self.accept("9", lambda: self.set_daytime("20:00")) # 深夜
|
||
# self.accept("0", lambda: self.set_daytime("22:00")) # 深夜
|
||
#
|
||
#self.createDirectionalLight()
|
||
#self._setYCModel()
|
||
#self._setArm1()
|
||
|
||
# 创建示例材质
|
||
#self.load_test_models_with_materials()
|
||
|
||
# 设置材质编辑器网络通信
|
||
#self.setup_material_editor_network()
|
||
|
||
# 启动材质编辑器
|
||
#self.launch_material_editor()
|
||
|
||
#self.launch_plugin_configurator()
|
||
|
||
print("✓ 核心世界初始化完成")
|
||
|
||
def _setYCModel(self):
|
||
model = self.loader.loadModel("/home/tiger/文档/Tzjyc_GLTF/tzjyc.gltf")
|
||
model.reparentTo(self.render)
|
||
model.setScale(0.25)
|
||
model.setPos(-8, 42, 0)
|
||
model.setHpr(0, 90, 0)
|
||
|
||
|
||
def _setArm1(self):
|
||
model1 = self.loader.loadModel("/home/tiger/下载/JQB1.fbx")
|
||
model2 = self.loader.loadModel("/home/tiger/下载/JQB2.fbx")
|
||
model1.reparentTo(self.render)
|
||
model1.setPos(0, 5, 0)
|
||
model1.setHpr(0, 90, 0)
|
||
model1.setScale(0.01)
|
||
model2.reparentTo(self.render)
|
||
model2.setPos(0, -5, 0)
|
||
model2.setHpr(0, 90, 0)
|
||
model2.setScale(0.01)
|
||
|
||
def _setupCamera(self):
|
||
"""设置相机位置和朝向"""
|
||
self.cam.setPos(0, -50, 20)
|
||
self.cam.lookAt(0, 0, 0)
|
||
self.camLens.setFov(80)
|
||
print("✓ 相机设置完成")
|
||
|
||
def _setupLighting(self):
|
||
"""设置基础光照系统"""
|
||
# 环境光
|
||
alight = AmbientLight('alight')
|
||
alight.setColor((0.2, 0.2, 0.2, 1))
|
||
alnp = self.render.attachNewNode(alight)
|
||
self.render.setLight(alnp)
|
||
|
||
# 定向光(模拟太阳光)
|
||
dlight = DirectionalLight('dlight')
|
||
dlight.setColor((0.8, 0.8, 0.8, 1))
|
||
dlnp = self.render.attachNewNode(dlight)
|
||
dlnp.setHpr(45, -45, 0) # 设置光照方向
|
||
self.render.setLight(dlnp)
|
||
|
||
# 保存光源引用
|
||
self.ambient_light = alnp
|
||
self.directional_light = dlnp
|
||
|
||
print("✓ 光照系统设置完成")
|
||
|
||
def _setupGround(self):
|
||
"""创建地板"""
|
||
cm = CardMaker('ground')
|
||
cm.setFrame(-50, 50, -50, 50)
|
||
|
||
# 创建地板节点
|
||
self.ground = self.render.attachNewNode(cm.generate())
|
||
self.ground.setP(-90)
|
||
self.ground.setZ(-0.1)
|
||
self.ground.setColor(0.8, 0.8, 0.8, 1)
|
||
|
||
# 创建支持贴图的材质
|
||
mat = Material()
|
||
mat.setName("GroundMaterial") # 设置材质名称,便于材质编辑器识别
|
||
color = LColor(0.7, 0.7, 0.7, 1) # 稍微暗一点的灰色
|
||
mat.set_base_color(color)
|
||
mat.set_roughness(0.5) # 设置合适的初始粗糙度
|
||
mat.set_metallic(0.1) # 设置较低的初始金属性
|
||
self.ground.set_material(mat)
|
||
|
||
# 应用默认PBR效果,确保支持贴图
|
||
try:
|
||
if hasattr(self, 'render_pipeline') and self.render_pipeline:
|
||
self.render_pipeline.set_effect(
|
||
self.ground,
|
||
"effects/default.yaml",
|
||
{
|
||
"normal_mapping": True,
|
||
"render_gbuffer": True,
|
||
"alpha_testing": False,
|
||
"parallax_mapping": False,
|
||
"render_shadow": True,
|
||
"render_envmap": True
|
||
},
|
||
50
|
||
)
|
||
print("✓ 地板PBR效果已应用")
|
||
else:
|
||
print("⚠️ RenderPipeline未初始化,地板将使用基础渲染")
|
||
except Exception as e:
|
||
print(f"⚠️ 地板PBR效果应用失败: {e}")
|
||
|
||
print("✓ 地板创建完成(支持材质贴图)")
|
||
|
||
|
||
|
||
def _loadFont(self):
|
||
"""加载中文字体"""
|
||
try:
|
||
self.chinese_font = self.loader.loadFont('/usr/share/fonts/truetype/wqy/wqy-microhei.ttc')
|
||
if not self.chinese_font:
|
||
print("警告: 无法加载中文字体,将使用默认字体")
|
||
else:
|
||
print("✓ 中文字体加载成功")
|
||
except:
|
||
print("警告: 无法加载中文字体,将使用默认字体")
|
||
self.chinese_font = None
|
||
|
||
def setQtWidget(self, widget):
|
||
"""设置Qt部件引用"""
|
||
self.qtWidget = widget
|
||
print(f"✓ 设置Qt部件引用: {widget}")
|
||
|
||
def getWindowSize(self):
|
||
"""获取准确的窗口尺寸"""
|
||
if self.qtWidget:
|
||
# 优先使用Qt部件的实际尺寸
|
||
width, height = self.qtWidget.getActualSize()
|
||
if width > 0 and height > 0:
|
||
return width, height
|
||
|
||
# 备用方案:使用Panda3D窗口尺寸
|
||
if hasattr(self, 'win') and self.win:
|
||
width = self.win.getXSize()
|
||
height = self.win.getYSize()
|
||
print(f"从Panda3D窗口获取尺寸: {width} x {height}")
|
||
return width, height
|
||
|
||
# 最后的默认值
|
||
print("使用默认窗口尺寸: 800 x 600")
|
||
return 800, 600
|
||
|
||
# ==================== 相机控制功能 ====================
|
||
|
||
def wheelForward(self, data=None):
|
||
"""处理滚轮向前滚动(前进)"""
|
||
# 获取相机的前向向量
|
||
forward = self.cam.getMat().getRow3(1)
|
||
# 计算移动距离
|
||
distance = self.cameraSpeed * globalClock.getDt()
|
||
# 更新相机位置
|
||
currentPos = self.cam.getPos()
|
||
newPos = currentPos + forward * distance
|
||
self.cam.setPos(newPos)
|
||
|
||
def wheelBackward(self, data=None):
|
||
"""处理滚轮向后滚动(后退)"""
|
||
# 获取相机的前向向量
|
||
forward = self.cam.getMat().getRow3(1)
|
||
# 计算移动距离
|
||
distance = self.cameraSpeed * globalClock.getDt()
|
||
# 更新相机位置
|
||
currentPos = self.cam.getPos()
|
||
newPos = currentPos - forward * distance
|
||
self.cam.setPos(newPos)
|
||
|
||
def moveCamera(self, x, y, z):
|
||
"""移动相机位置(垂直移动)"""
|
||
# 获取相机的上向量
|
||
upVector = self.cam.getMat().getRow3(2)
|
||
# 计算移动距离
|
||
distance = self.cameraSpeed * globalClock.getDt()
|
||
# 更新相机位置
|
||
currentPos = self.cam.getPos()
|
||
newPos = currentPos + upVector * z * distance
|
||
self.cam.setPos(newPos)
|
||
|
||
# ==================== 鼠标事件处理 ====================
|
||
|
||
def mousePressEventRight(self, evt):
|
||
"""处理鼠标右键按下事件"""
|
||
print("右键按下")
|
||
self.mouseRightPressed = True
|
||
self.lastMouseX = evt['x']
|
||
self.lastMouseY = evt['y']
|
||
|
||
def mouseReleaseEventRight(self, evt):
|
||
"""处理鼠标右键释放事件"""
|
||
print("右键释放")
|
||
self.mouseRightPressed = False
|
||
|
||
def mouseMoveEvent(self, evt):
|
||
"""处理鼠标移动事件 - 只处理相机旋转"""
|
||
if not evt:
|
||
return
|
||
|
||
if self.mouseRightPressed:
|
||
# 计算鼠标移动距离
|
||
dx = evt.get('x', 0) - self.lastMouseX
|
||
dy = evt.get('y', 0) - self.lastMouseY
|
||
|
||
# 计算旋转角度
|
||
rotateSpeed = self.cameraRotateSpeed * globalClock.getDt()
|
||
|
||
# 更新相机朝向
|
||
currentH = self.cam.getH()
|
||
currentP = self.cam.getP()
|
||
|
||
# 限制俯仰角度在-90到90度之间
|
||
newP = max(-90, min(90, currentP - dy * rotateSpeed))
|
||
|
||
self.cam.setH(currentH - dx * rotateSpeed)
|
||
self.cam.setP(newP)
|
||
|
||
# 更新鼠标位置
|
||
self.lastMouseX = evt.get('x', 0)
|
||
self.lastMouseY = evt.get('y', 0)
|
||
|
||
# ==================== 其他基础功能 ====================
|
||
|
||
def getChineseFont(self):
|
||
"""获取中文字体"""
|
||
return self.chinese_font
|
||
|
||
def getGroundNode(self):
|
||
"""获取地板节点"""
|
||
return self.ground
|
||
|
||
def getAmbientLight(self):
|
||
"""获取环境光"""
|
||
return self.ambient_light
|
||
|
||
def getDirectionalLight(self):
|
||
"""获取定向光"""
|
||
return self.directional_light
|
||
|
||
def start_day_night_cycle(self, duration_seconds=10.0):
|
||
"""让天空盒在 duration_seconds 秒内从 0:00 过渡到 24:00"""
|
||
self._cycle_start_time = self.taskMgr.globalClock.get_real_time()
|
||
self._cycle_duration = duration_seconds
|
||
self.taskMgr.add(self._day_night_cycle_task, "day_night_cycle_task")
|
||
|
||
def _day_night_cycle_task(self, task):
|
||
elapsed = self.taskMgr.globalClock.get_real_time() - self._cycle_start_time
|
||
t = (elapsed % self._cycle_duration) / self._cycle_duration # 始终在 0~1 循环
|
||
self.render_pipeline.daytime_mgr.time = 24.0 * t
|
||
return task.cont
|
||
|
||
|
||
def set_daytime(self, time_str):
|
||
"""设置时间"""
|
||
try:
|
||
if hasattr(self, 'render_pipeline') and self.render_pipeline:
|
||
self.render_pipeline.daytime_mgr.time = time_str
|
||
print(f"当前时间设置为: {time_str}")
|
||
else:
|
||
print(f"⚠️ RenderPipeline 不可用,无法设置时间: {time_str}")
|
||
except Exception as e:
|
||
print(f"设置时间失败: {e}")
|
||
|
||
def get_render_pipeline(self):
|
||
"""获取 RenderPipeline 实例"""
|
||
return getattr(self, 'render_pipeline', None)
|
||
|
||
def _setupSkybox(self):
|
||
# 加载天空盒模型
|
||
self.skybox = self.loader.loadModel("data/builtin_models/skybox/skybox.bam")
|
||
self.skybox.reparentTo(self.camera) # 绑定到相机
|
||
self.skybox.setScale(500)
|
||
self.skybox.setBin('background', 0)
|
||
self.skybox.setDepthWrite(False)
|
||
self.skybox.setLightOff()
|
||
self.skybox.setCompass() # 始终朝向固定
|
||
|
||
print("✓ 静态天空盒加载完成")
|
||
|
||
def createDirectionalLight(self):
|
||
from RenderPipelineFile.rpcore import light_manager
|
||
from panda3d.core import DirectionalLight, Vec3
|
||
dlight = DirectionalLight("1")
|
||
|
||
dlight_np = self.render.attachNewNode(dlight)
|
||
#light_manager.add_light(dlight)
|
||
|
||
dlight_np.setHpr(45,-45,0)
|
||
|
||
self.render.setLight(dlight_np)
|
||
|
||
dlight.setColor((1,1,1,1))
|
||
dlight.setShadowCaster(True,2048,2048)
|
||
|
||
print("平行光创建完成")
|
||
|
||
# dlight.direction = Vec3(0, 0, -1) # 光照方向
|
||
# dlight.fov = self.lamp_fov # 光源角度(类似手电筒)
|
||
# dlight.set_color_from_temperature(1 * 1000.0) # 色温(K)
|
||
# dlight.energy = self.half_energy # 光照强度
|
||
# dlight.radius = self.lamp_radius # 影响范围
|
||
# dlight.casts_shadows = True # 是否投射阴影
|
||
# dlight.shadow_map_resolution = 256 # 阴影分辨率
|
||
# dlight.setPos(0,0,10)
|
||
|
||
def check_material_editor_connection(self):
|
||
"""检查材质编辑器连接状态"""
|
||
try:
|
||
# 确保 RenderPipeline 已完全初始化
|
||
if not hasattr(self, 'render_pipeline') or not self.render_pipeline:
|
||
print("RenderPipeline 未初始化")
|
||
return False
|
||
|
||
# 检查网络监听器是否存在
|
||
if not hasattr(self.render_pipeline, '_listener'):
|
||
print("NetworkCommunication 监听器未初始化")
|
||
return False
|
||
|
||
from RenderPipelineFile.rpcore.util.network_communication import NetworkCommunication
|
||
import tempfile
|
||
import os
|
||
import time
|
||
|
||
# 使用唯一的测试文件名
|
||
import uuid
|
||
test_filename = f"test_materials_{uuid.uuid4().hex[:8]}.data"
|
||
temp_path = os.path.join(tempfile.gettempdir(), test_filename)
|
||
|
||
print(f"测试材质编辑器连接,文件路径: {temp_path}")
|
||
|
||
# 确保测试文件不存在
|
||
if os.path.exists(temp_path):
|
||
os.remove(temp_path)
|
||
|
||
# 发送导出命令
|
||
NetworkCommunication.send_async(
|
||
NetworkCommunication.MATERIAL_PORT,
|
||
f"dump_materials {temp_path}"
|
||
)
|
||
|
||
# 大幅增加等待时间,因为网络命令处理可能有延迟
|
||
for i in range(60): # 等待最多30秒
|
||
time.sleep(0.5)
|
||
if os.path.exists(temp_path):
|
||
# 等待文件写入完成
|
||
time.sleep(1.0)
|
||
try:
|
||
with open(temp_path, 'r') as f:
|
||
content = f.read().strip()
|
||
print(f"材质编辑器连接测试成功,文件内容: {len(content)} 字符")
|
||
os.remove(temp_path)
|
||
return True
|
||
except:
|
||
print("文件读取失败,继续等待...")
|
||
continue
|
||
|
||
if i % 20 == 0 and i > 0: # 每10秒打印一次状态
|
||
print(f"等待材质文件创建... ({i // 2}s)")
|
||
|
||
print("材质编辑器连接测试超时")
|
||
return False
|
||
|
||
except Exception as e:
|
||
print(f"材质编辑器连接测试出错: {e}")
|
||
return False
|
||
|
||
|
||
def create_material_editor_widget(self):
|
||
"""创建材质编辑器组件"""
|
||
try:
|
||
# 确保 RenderPipeline 已完全初始化
|
||
if not hasattr(self, 'render_pipeline') or not self.render_pipeline:
|
||
print("RenderPipeline 未初始化")
|
||
return None
|
||
|
||
# 检查网络连接
|
||
if not self.check_material_editor_connection():
|
||
print("无法连接到材质编辑器网络服务")
|
||
return None
|
||
|
||
# 创建材质编辑器组件
|
||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
|
||
|
||
material_widget = QWidget()
|
||
layout = QVBoxLayout()
|
||
|
||
# 添加基本的材质编辑控件
|
||
layout.addWidget(QLabel("材质编辑器"))
|
||
# 这里可以添加更多的材质编辑控件
|
||
|
||
material_widget.setLayout(layout)
|
||
return material_widget
|
||
|
||
except Exception as e:
|
||
print(f"创建材质编辑器失败: {e}")
|
||
return None
|
||
|
||
def _delayed_material_test(self, task):
|
||
"""延迟执行的材质编辑器连接测试"""
|
||
print("开始延迟材质编辑器连接测试...")
|
||
success = self.check_material_editor_connection()
|
||
if success:
|
||
print("✓ 材质编辑器连接正常")
|
||
else:
|
||
print("✗ 材质编辑器连接失败")
|
||
return task.done
|
||
|
||
def launch_day_time_editor(self):
|
||
|
||
# 检查是否已经启动
|
||
if hasattr(self, '_day_time_editor_process') and self._day_time_editor_process:
|
||
if self._day_time_editor_process.poll() is None: # 进程仍在运行
|
||
print("Day Time Editor 已经在运行")
|
||
return True
|
||
import subprocess
|
||
import os
|
||
import sys
|
||
|
||
try:
|
||
if not hasattr(self.world,'render_pipeline') or not self.world.render_pipeline:
|
||
print("错误:renderpipeline未初始化")
|
||
return False
|
||
|
||
base_path = self.world.render_pipeline.mount_mgr.base_path
|
||
editor_path = os.path.join(base_path,"toolkit/day_time_editor/main.py")
|
||
|
||
if not os.path.exists(editor_path):
|
||
print("错误文件不存在")
|
||
return False
|
||
|
||
self._day_time_editor_process = subprocess.Popen([sys.executable, editor_path])
|
||
print("Day Time Editor 已启动")
|
||
return True
|
||
except Exception as e:
|
||
print(f"启动 time editor失败")
|
||
|
||
def setup_material_editor_network(self):
|
||
"""设置材质编辑器网络通信"""
|
||
from RenderPipelineFile.rpcore.util.network_communication import NetworkCommunication
|
||
|
||
def handle_material_requests(message):
|
||
"""处理材质编辑器的网络请求"""
|
||
try:
|
||
print(f"收到材质编辑器请求: {message}")
|
||
parts = message.strip().split()
|
||
if not parts:
|
||
return
|
||
|
||
command = parts[0]
|
||
|
||
if command == "dump_materials":
|
||
# 处理导出材质列表请求
|
||
path = parts[1] if len(parts) > 1 else ""
|
||
if path:
|
||
self.export_materials_to_file(path)
|
||
elif command.startswith("update_material"):
|
||
# 处理更新材质请求
|
||
data = message[len("update_material "):].strip()
|
||
self.update_material_from_editor(data.split())
|
||
|
||
except Exception as e:
|
||
print(f"处理材质编辑器请求失败: {e}")
|
||
|
||
# 注册网络消息处理器
|
||
try:
|
||
NetworkCommunication.listen_threaded(
|
||
NetworkCommunication.MATERIAL_PORT,
|
||
handle_material_requests
|
||
)
|
||
print("✓ 材质编辑器网络通信已设置")
|
||
except Exception as e:
|
||
print(f"设置材质编辑器网络通信失败: {e}")
|
||
|
||
def export_materials_to_file(self, path):
|
||
"""导出材质列表到文件"""
|
||
try:
|
||
print(f"导出材质列表到: {path}")
|
||
materials = []
|
||
|
||
# 收集所有材质
|
||
def collect_materials(node):
|
||
if node.hasMaterial():
|
||
material = node.getMaterial()
|
||
if material:
|
||
materials.append(material)
|
||
|
||
for child in node.getChildren():
|
||
collect_materials(child)
|
||
|
||
collect_materials(self.render)
|
||
|
||
# 写入文件
|
||
with open(path, "w") as f:
|
||
for i, material in enumerate(materials):
|
||
name = f"{i}-{material.getName() or 'unnamed'}"
|
||
|
||
# 获取材质属性,使用默认值如果不存在
|
||
base_color = material.getBaseColor() if hasattr(material, 'getBaseColor') else Vec4(0.6, 0.6, 0.6, 1.0)
|
||
roughness = material.getRoughness() if hasattr(material, 'getRoughness') else 0.5
|
||
metallic = material.getMetallic() if hasattr(material, 'getMetallic') else 0.0
|
||
|
||
# 写入材质数据
|
||
f.write(("{} " * 11).format(
|
||
name,
|
||
base_color.x,
|
||
base_color.y,
|
||
base_color.z,
|
||
roughness,
|
||
0.5, # refractive_index
|
||
metallic,
|
||
0, # shading_model
|
||
1.0, # normal_strength
|
||
0.0, # param1
|
||
0.0 # param2
|
||
) + "\n")
|
||
|
||
print(f"✓ 成功导出 {len(materials)} 个材质到 {path}")
|
||
|
||
except Exception as e:
|
||
print(f"导出材质列表失败: {e}")
|
||
|
||
def update_material_from_editor(self, data):
|
||
"""从编辑器更新材质"""
|
||
try:
|
||
if len(data) < 11:
|
||
print(f"材质数据不完整: {data}")
|
||
return
|
||
|
||
name_parts = data[0].split("-")
|
||
if len(name_parts) < 2:
|
||
print(f"材质名称格式错误: {data[0]}")
|
||
return
|
||
|
||
index = int(name_parts[0])
|
||
name = "-".join(name_parts[1:])
|
||
|
||
# 收集所有材质
|
||
materials = []
|
||
def collect_materials(node):
|
||
if node.hasMaterial():
|
||
material = node.getMaterial()
|
||
if material:
|
||
materials.append(material)
|
||
|
||
for child in node.getChildren():
|
||
collect_materials(child)
|
||
|
||
collect_materials(self.render)
|
||
|
||
# 查找匹配的材质
|
||
if index < len(materials):
|
||
material = materials[index]
|
||
|
||
# 更新材质属性
|
||
material.setBaseColor(Vec4(float(data[1]), float(data[2]), float(data[3]), 1.0))
|
||
material.setRoughness(float(data[4]))
|
||
material.setMetallic(float(data[6]))
|
||
|
||
print(f"✓ 更新材质 {name}: 颜色=({data[1]}, {data[2]}, {data[3]}), 粗糙度={data[4]}, 金属度={data[6]}")
|
||
else:
|
||
print(f"未找到索引为 {index} 的材质")
|
||
|
||
except Exception as e:
|
||
print(f"更新材质失败: {e}")
|
||
|
||
def launch_material_editor(self):
|
||
"""启动材质编辑器"""
|
||
import subprocess
|
||
import os
|
||
import sys
|
||
|
||
try:
|
||
if not self.render_pipeline:
|
||
print("错误:renderpipeline未初始化")
|
||
return False
|
||
|
||
base_path = self.render_pipeline.mount_mgr.base_path
|
||
editor_path = os.path.join(base_path,"toolkit/material_editor/main.py")
|
||
|
||
if not os.path.exists(editor_path):
|
||
print("错误文件不存在")
|
||
return False
|
||
|
||
self._material_editor_process = subprocess.Popen([sys.executable, editor_path])
|
||
print("Material Editor 已启动")
|
||
return True
|
||
except Exception as e:
|
||
print(f"启动 Material editor失败")
|
||
|
||
def create_sample_materials(self):
|
||
"""创建一些示例材质供编辑器使用"""
|
||
from panda3d.core import Material, Vec4, CardMaker
|
||
|
||
# 创建几个测试几何体,每个都有不同的材质
|
||
sample_materials = [
|
||
{"name": "MetalMaterial", "color": Vec4(0.7, 0.7, 0.8, 1.0), "metallic": True, "roughness": 0.2},
|
||
{"name": "PlasticMaterial", "color": Vec4(0.8, 0.2, 0.2, 1.0), "metallic": False, "roughness": 0.8},
|
||
{"name": "GlassMaterial", "color": Vec4(0.9, 0.9, 1.0, 0.3), "metallic": False, "roughness": 0.1},
|
||
{"name": "WoodMaterial", "color": Vec4(0.6, 0.4, 0.2, 1.0), "metallic": False, "roughness": 0.7},
|
||
{"name": "TestPlaneMaterial", "color": Vec4(0.8, 0.8, 0.8, 1.0), "metallic": False, "roughness": 1.0}
|
||
]
|
||
|
||
for i, mat_data in enumerate(sample_materials):
|
||
# 创建几何体
|
||
if mat_data["name"] == "TestPlaneMaterial":
|
||
# 创建一个大的测试平面,类似Blender中的平面
|
||
cm = CardMaker('test_plane')
|
||
cm.setFrame(-5, 5, -5, 5) # 更大的平面
|
||
geom_node = self.render.attachNewNode(cm.generate())
|
||
geom_node.setPos(0, 15, 0) # 放在前面显眼位置
|
||
geom_node.setP(-90) # 水平放置
|
||
print("✓ 创建大型测试平面,适合测试粗糙度贴图")
|
||
else:
|
||
cm = CardMaker(f'sample_geom_{i}')
|
||
cm.setFrame(-1, 1, -1, 1)
|
||
geom_node = self.render.attachNewNode(cm.generate())
|
||
geom_node.setPos(i * 3 - 6, 10, 1)
|
||
geom_node.setP(-90) # 水平放置
|
||
|
||
# 创建材质并确保名称正确设置
|
||
material = Material()
|
||
material.setName(mat_data["name"]) # 明确设置材质名称
|
||
material.setBaseColor(mat_data["color"])
|
||
material.setRoughness(mat_data["roughness"])
|
||
material.setMetallic(1.0 if mat_data["metallic"] else 0.0)
|
||
|
||
# 应用材质
|
||
geom_node.setMaterial(material)
|
||
|
||
print(f"✓ 创建示例材质: {mat_data['name']}")
|
||
|
||
# 延迟一下确保材质完全创建
|
||
import time
|
||
time.sleep(0.1)
|
||
|
||
def load_test_models_with_materials(self):
|
||
"""加载一些测试模型以提供更多材质供编辑"""
|
||
try:
|
||
# 你可以在这里加载你的模型文件
|
||
# test_model = self.loader.loadModel("models/your_model.gltf")
|
||
# if test_model:
|
||
# test_model.reparentTo(self.render)
|
||
# test_model.setPos(5, 0, 0)
|
||
# test_model.setScale(1.0)
|
||
# print(f"✓ 测试模型已加载")
|
||
|
||
# 创建示例材质
|
||
self.create_sample_materials()
|
||
|
||
except Exception as e:
|
||
print(f"加载测试模型失败: {e}")
|
||
# 创建示例材质作为备选
|
||
self.create_sample_materials()
|
||
|
||
def launch_plugin_configurator(self):
|
||
"""启动材质编辑器"""
|
||
import subprocess
|
||
import os
|
||
import sys
|
||
|
||
try:
|
||
if not self.render_pipeline:
|
||
print("错误:renderpipeline未初始化")
|
||
return False
|
||
|
||
base_path = self.render_pipeline.mount_mgr.base_path
|
||
editor_path = os.path.join(base_path, "toolkit/plugin_configurator/main.py")
|
||
|
||
if not os.path.exists(editor_path):
|
||
print("错误文件不存在")
|
||
return False
|
||
|
||
self._material_editor_process = subprocess.Popen([sys.executable, editor_path])
|
||
print("plugin_configurator 已启动")
|
||
return True
|
||
except Exception as e:
|
||
print(f"启动plugin_configurator失败")
|
||
|