1
0
forked from Rowland/EG
EG/core/world.py
2025-07-24 11:37:46 +08:00

438 lines
15 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.

import math
import warnings
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
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.launch_day_time_editor()
#self.createDirectionalLight()
print("✓ 核心世界初始化完成")
def launch_day_time_editor(self):
"""启动day time editor 作为独立进程"""
import subprocess
import os
import sys
base_path = self.render_pipeline.mount_mgr.base_path
editor_path = os.path.join(base_path,"文档/EG/RenderPipelineFile/toolkit/day_time_editor/main.py")
subprocess.Popen([sys.executable,editor_path])
print("day time editor 已启动")
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()
color = LColor(1, 1, 1, 1)
mat.set_base_color(color)
mat.set_roughness(0)
mat.set_metallic(0.8)
#mat.set_normal("/home/tiger/下载/OIP.jpeg")
self.ground.set_material(mat)
# self.render_pipeline.set_effect(self.ground, "RenderPipelineFile/effects/material_blend4.yaml", {
# "parallax_mapping": False, # Not supported
# "alpha_testing": False,
# "normal_mapping": False, # The effect does its own normal mapping
# }, 100)
#
# self.ground.set_shader_input("detail_scale_factor", 4.0)
# self.ground.set_shader_input("material_0_pow", 10.0)
# self.ground.set_shader_input("material_0_add", 0.5)
# self.ground.set_shader_input("material_1_pow", 10.0)
# self.ground.set_shader_input("material_1_add", 0.5)
# self.ground.set_shader_input("material_2_pow", 10.0)
# self.ground.set_shader_input("material_2_add", 0.5)
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):
self.render_pipeline.daytime_mgr.time = time_str
print(f"当前时间设置为: {time_str}")
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):
"""启动day time editor 作为独立进程"""
import subprocess
import os
import sys
base_path = self.render_pipeline.mount_mgr.base_path
editor_path = os.path.join(base_path,"toolkit/day_time_editor/main.py")
subprocess.Popen([sys.executable,editor_path])
print("day time editor 已启动")