1
0
forked from Rowland/EG

addRender #28

Merged
Hector merged 49 commits from addRender into main 2025-10-20 08:13:49 +00:00
372 changed files with 25534 additions and 8882 deletions

54
.gitignore vendored
View File

@ -1,3 +1,57 @@
__pycache__/
*.pyc
.idea/
【模板】模型及算法与功能对应清单.docx
【模板】GY知识-产品模块架构及功能清单和主要应用场景说明.docx
1.json
2.json
3.json
4.json
6.json
功能清单.md
模型及算法与功能对应清单(完整版).md
维修系统使用说明.md
audio_demo_config.json
create_test_audio.py
demo_universal_dialog.py
Dock按钮样式统一说明.md
Dock面板标题样式线完成说明.md
exam_result_20250926_004454.json
exam_result_20250926_010628.json
exam_result_20250926_011746.json
exam_result_20250926_090330.json
exam_result_20250926_111756.json
exam_test_config.json
Figma布局优化说明.md
Figma设计分析与菜单栏优化文档.md
Figma样式优化完成说明.md
manual_tools_config.json
pm.txt
simple_menubar_test.py
test_audio.py
test_basic_audio.py
test_exam_tool_error.py
test_full_audio_playback.py
test_menu_visibility.py
test_mode_selection.py
test_mp3_audio.py
test_pygame_audio.py
test_score_config.py
test_simple_audio.py
tool_test_config.json
UI设计文档.md
UniversalMessageDialog使用说明.md
.codex/settings/kiroCodex-settings.json
data/projects.json
RenderPipelineFile/config/daytime.yaml
Resources/audio/README.md
Resources/model/a.glb
Resources/model/b.glb
Resources/model/c.glb
Resources/model/JQB_auto_converted.glb
Resources/model/Untitled.glb
Resources/models/Women_1.glb
Subjects/test_maintenance_config.json
Resources/models/Women_1.glb
Resources/models/Women_1.glb
Resources/models/Women_1.glb

6
.idea/EG.iml generated
View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10 virtualenv at ~/EG/venv (2)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">

2
.idea/misc.xml generated
View File

@ -3,5 +3,5 @@
<component name="Black">
<option name="sdkName" value="Python 3.12 (PythonProject)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (EG)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 virtualenv at ~/EG/venv (2)" project-jdk-type="Python SDK" />
</project>

Binary file not shown.

112
CLAUDE.md Normal file
View File

@ -0,0 +1,112 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
这是一个基于Panda3D的3D渲染引擎和场景编辑器集成了PyQt5界面和多种高级功能
- 3D场景编辑器模型导入、材质系统、碰撞检测
- GUI元素管理2D/3D GUI组件
- 项目管理系统(场景保存/加载、项目打包)
- Cesium地图集成
- 渲染管线增强RenderPipelineFile
## 运行和构建命令
### 启动应用程序
```bash
python Start_Run.py [project_path]
```
或者直接:
```bash
python main.py
```
### 依赖安装
```bash
# 主要依赖
pip install -r requirements/requirements.txt
# Conda环境依赖
pip install -r requirements/conda-requirements.txt
```
### 工具脚本
```bash
# 安装FBX到GLTF转换工具
./install_fbx2gltf.sh
```
## 核心架构
### 主要模块结构
```
EG/
├── main.py # 应用程序入口点
├── Start_Run.py # 启动脚本(路径配置)
├── core/ # 核心功能模块
│ ├── world.py # 3D世界核心继承Panda3DWorld
│ ├── scene_manager.py # 场景和模型管理
│ ├── selection.py # 对象选择系统
│ ├── event_handler.py # 事件处理
│ └── tool_manager.py # 工具系统
├── gui/ # GUI元素管理
│ └── gui_manager.py # 2D/3D GUI组件
├── ui/ # 用户界面
│ ├── widgets.py # 自定义Qt组件
│ ├── property_panel.py # 属性面板
│ └── interface_manager.py # 界面管理
├── scene/ # 场景相关
│ └── scene_manager.py # 场景管理器
├── project/ # 项目管理
│ └── project_manager.py # 项目生命周期
├── RenderPipelineFile/ # 渲染管线扩展
└── QPanda3D/ # Panda3D Qt集成
```
### 核心设计模式
1. **模块化架构**: 每个功能模块独立,通过管理器类协调
2. **事件驱动**: EventHandler统一处理用户交互和系统事件
3. **组件系统**: SelectionSystem、ToolManager等可插拔组件
4. **MVC分离**: UI组件、核心逻辑和数据管理分离
### 主要依赖集成
- **Panda3D 1.10.15**: 3D渲染引擎
- **PyQt5**: GUI框架
- **QPanda3D**: Panda3D的Qt集成
- **RenderPipeline**: 高级渲染功能
## 开发指南
### 添加新功能模块
1. 在对应目录下创建新的Python文件
2. 继承相应的基类如Panda3DWorld用于3D功能
3. 在main.py中集成新模块
4. 更新界面管理器以添加UI控制
### 材质和渲染
- 材质系统集成在scene/scene_manager.py
- 支持PBR材质和自定义着色器
- RenderPipelineFile提供高级渲染特性
### GUI开发
- 使用PyQt5构建主界面
- 3D GUI元素通过gui/gui_manager.py管理
- 自定义组件在ui/widgets.py中定义
## 文件约定
- Python文件使用UTF-8编码
- 中文注释和文档字符串
- 模块顶部包含功能描述注释
- 类和方法使用描述性命名
## 注意事项
- 项目依赖多个大型库Panda3D、PyQt5、RenderPipeline
- Cesium集成需要WebEngine支持
- 某些功能可能需要特定的系统配置

View File

@ -70,6 +70,14 @@ class Panda3DWorld(ShowBase):
loadPrcFileData("", f"win-size {width} {height}")
loadPrcFileData("", "win-fixed-size #f") # 允许窗口调整大小
# 🚀 VR性能优化配置
loadPrcFileData("", "prefer-single-buffer true") # 减少缓冲区交换开销
loadPrcFileData("", "gl-force-flush false") # 避免强制glFlush导致的性能损失
loadPrcFileData("", "sync-video false") # 禁用默认VSync让OpenVR控制
loadPrcFileData("", "support-stencil false") # 禁用不必要的模板缓冲区
loadPrcFileData("", "clock-mode non-real-time") # 禁用Panda3D帧率控制让OpenVR控制
# loadPrcFileData("", "gl-debug true") # 调试时可启用OpenGL调试
if (is_fullscreen):
loadPrcFileData("", "fullscreen #t")
@ -134,7 +142,7 @@ class Panda3DWorld(ShowBase):
#render_pipeline.set_camera(self.cam)
#添加渲染效果<EFBFBD><EFBFBD>
#添加渲染效果
#self.cam = self.render_pipeline._showbase.cam
#self.camNode = self.cam.node()
#self.camLens = self.camNode.get_lens()
@ -152,6 +160,19 @@ class Panda3DWorld(ShowBase):
self.mouse_controller = CustomMouseController(self)
self.mouse_controller.setUp()
# 添加错误处理钩子
self.accept("transform_state_error", self._handle_transform_error)
def _handle_transform_error(self):
"""处理TransformState相关的错误"""
try:
from panda3d.core import TransformState, RenderState
TransformState.clear_cache()
RenderState.clear_cache()
print("已清理TransformState和RenderState缓存")
except Exception as e:
print(f"清理缓存时出错: {e}")
def render_pipeline(self):
"""获取 RenderPipeline 实例"""
return self._render_pipeline
@ -190,4 +211,4 @@ def resize_buffer(self, width: int, height: int):
self.camLens.set_film_size(width, height) # 或 set_aspect_ratio(width / height)
# 强制更新窗口(有时在 Qt 内嵌时需要)
self.graphicsEngine.open_windows()
self.graphicsEngine.open_windows()

View File

@ -12,6 +12,7 @@ from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from direct.task.TaskManagerGlobal import taskMgr
# Panda imports
from panda3d.core import Texture, WindowProperties, CallbackGraphicsWindow
@ -32,24 +33,34 @@ class QPanda3DSynchronizer(QTimer):
self.setInterval(int(dt))
self.timeout.connect(self.tick)
# def tick(self):
# taskMgr.step()
# self.qPanda3DWidget.update()
def tick(self):
try:
# 在渲染前清理可能损坏的TransformState对象
from panda3d.core import TransformState
TransformState.clear_cache()
taskMgr.step()
self.qPanda3DWidget.update()
except AssertionError as e:
if "has_mat()" in str(e):
print("⚠️ 检测到变换矩阵错误,跳过此帧")
# 继续运行而不是崩溃
# 专门处理 TransformState has_mat() 断言错误
if "has_mat" in str(e):
print(f"警告: 检测到TransformState断言错误已静默处理: {e}")
# 尝试恢复渲染状态
try:
# 强制清理缓存并重试
from panda3d.core import TransformState, RenderState
TransformState.clear_cache()
RenderState.clear_cache()
taskMgr.step()
self.qPanda3DWidget.update()
except:
pass
else:
# 重新抛出其他断言错误
raise
except Exception as e:
print(f"❌ 渲染循环错误: {e}")
import traceback
traceback.print_exc()
# 静默处理其他所有异常
print(f"警告: 检测到异常,已静默处理: {e}")
def get_panda_key_modifiers(evt):
@ -248,7 +259,7 @@ class QPanda3DWidget(QWidget):
#
# data = tex.getRamImage().getData()
# width = tex.getXSize()
# height = tex.getYSize()
# height = tex.getYSize()
# expected_len = width * height * 4
#
# if len(data) != expected_len:
@ -341,10 +352,4 @@ class QPanda3DWidget(QWidget):
print(f"Panda3D 窗口尺寸已同步为: {adjusted_width} x {adjusted_height}")
except Exception as e:
print(f"同步 Panda3D 窗口尺寸失败: {str(e)}")
print(f"同步 Panda3D 窗口尺寸失败: {str(e)}")

File diff suppressed because one or more lines are too long

View File

@ -351,7 +351,11 @@ class RenderPipeline(RPObject):
continue
material = state.get_attrib(MaterialAttrib).get_material()
shading_model = material.emission.x
if material.emission is not None:
shading_model = material.emission.x
else:
shading_model = 0.0
# SHADING_MODEL_TRANSPARENT
if shading_model == 3:

View File

@ -60,7 +60,7 @@ class MovementController(object):
self.keyboard_hpr_speed = 0.4
self.use_hpr = False
self.smoothness = 6.0
self.bobbing_amount = 1.5
self.bobbing_amount = 0.0
self.bobbing_speed = 0.5
def set_initial_position(self, pos, target):
@ -138,8 +138,8 @@ class MovementController(object):
self.showbase.accept("j", self.print_position)
# mouse
self.showbase.accept("mouse1", self.set_mouse_enabled, [True])
self.showbase.accept("mouse1-up", self.set_mouse_enabled, [False])
self.showbase.accept("mouse3", self.set_mouse_enabled, [True])
self.showbase.accept("mouse3-up", self.set_mouse_enabled, [False])
# arrow mouse navigation
self.showbase.accept("arrow_up", self.set_hpr_movement, [1, 1])

View File

@ -1,8 +0,0 @@
*.pyc
*.pyd
*.blend1
03-Forest
06-Map
*.blend1
*.blend2

View File

@ -1,28 +0,0 @@
"""
This is an alternative possibility of initializing the RenderPipeline, which
makes it possible to construct the ShowBase object manually
"""
import sys
from direct.showbase.ShowBase import ShowBase
# Insert the pipeline path to the system path, this is required to be
# able to import the pipeline classes. In case you placed the render
# pipeline in a subfolder of your project, you have to adjust this.
sys.path.insert(0, "../../RenderPipeline")
sys.path.insert(0, "../../")
# Import render pipeline classes
from rpcore import RenderPipeline
# Construct and create the pipeline
render_pipeline = RenderPipeline()
render_pipeline.pre_showbase_init()
# Construct and create the ShowBase
base = ShowBase()
render_pipeline.create(base)
base.run()

View File

@ -1,28 +0,0 @@
"""
This is an alternative possibility of initializing the RenderPipeline, which
uses the (deprecated!) DirectStart interface. This should not be used anymore,
except for fast prototyping.
"""
import sys
# Insert the pipeline path to the system path, this is required to be
# able to import the pipeline classes. In case you placed the render
# pipeline in a subfolder of your project, you have to adjust this.
sys.path.insert(0, "../../")
sys.path.insert(0, "../../RenderPipeline")
# Import render pipeline classes
from rpcore import RenderPipeline
# Construct and create the pipeline
render_pipeline = RenderPipeline()
render_pipeline.pre_showbase_init()
# Import (deprecated!) DirectStart interface
import direct.directbase.DirectStart
render_pipeline.create(base)
base.run()

View File

@ -1,39 +0,0 @@
"""
Simplest possible application using the render pipeline.
This sample will not show any fancy rendering output, but you can base your own
applications on this skeleton.
This is the preferred way of initializing the pipeline, however you can find
alternative ways in the other included files.
"""
import sys
from direct.showbase.ShowBase import ShowBase
class Application(ShowBase):
def __init__(self):
# Notice that you must not call ShowBase.__init__ (or super), the
# render pipeline does that for you. If this is unconvenient for you,
# have a look at the other initialization possibilities.
# Insert the pipeline path to the system path, this is required to be
# able to import the pipeline classes. In case you placed the render
# pipeline in a subfolder of your project, you have to adjust this.
sys.path.insert(0, "../../")
sys.path.insert(0, "../../RenderPipeline")
# Import the main render pipeline class
from rpcore import RenderPipeline
# Construct and create the pipeline
self.render_pipeline = RenderPipeline()
self.render_pipeline.create(self)
# Done! You can start setting up your application stuff as regular now.
Application().run()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 KiB

View File

@ -1,72 +0,0 @@
"""
Material Demo
This demonstrates the various materials the pipeline supports.
It is also a reference scene, for testing BRDF changes.
"""
from __future__ import print_function
import os
import sys
from panda3d.core import Vec3, load_prc_file_data
from direct.showbase.ShowBase import ShowBase
# Change to the current directory
os.chdir(os.path.dirname(os.path.realpath(__file__)))
# Insert the pipeline path to the system path, this is required to be
# able to import the pipeline classes
pipeline_path = "../../"
# Just a special case for my development setup, so I don't accidentally
# commit a wrong path. You can remove this in your own programs.
if not os.path.isfile(os.path.join(pipeline_path, "setup.py")):
pipeline_path = "../../RenderPipeline/"
sys.path.insert(0, pipeline_path)
# Import the render pipeline class
from rpcore import RenderPipeline
# This is a helper class for better camera movement - see below.
from rpcore.util.movement_controller import MovementController
class Application(ShowBase):
def __init__(self):
# Setup window size and title
load_prc_file_data("", """
# win-size 1600 900
window-title Render Pipeline - Material Sample
""")
# Construct the render pipeline
self.render_pipeline = RenderPipeline()
self.render_pipeline.create(self)
self.render_pipeline.daytime_mgr.time = "19:17"
# self.render_pipeline.daytime_mgr.time = "12:00"
# Load the scene
model = self.loader.load_model("scene/TestScene.bam")
model.reparent_to(self.render)
self.render_pipeline.prepare_scene(model)
# Enable parallax mapping on the floor
# self.render_pipeline.set_effect(
# model.find("**/FloorPlane"),
# "effects/default.yaml", {"parallax_mapping": True}, 100)
# Initialize movement controller, this is a convenience class
# to provide an improved camera control compared to Panda3Ds default
# mouse controller.
self.controller = MovementController(self)
self.controller.set_initial_position_hpr(
Vec3(-17.2912578583, -13.290019989, 6.88211250305),
Vec3(-39.7285499573, -14.6770210266, 0.0))
self.controller.setup()
Application().run()

View File

@ -1 +0,0 @@
*.blend

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

View File

@ -1,315 +0,0 @@
"""
Roaming Ralph Sample (modified)
This is the default roaming ralph sample, with the render pipeline.
Using the render pipeline is only the matter of a few lines, which have
been explicitely marked.
NOTICE: Since this is a straight copy of the standard roaming ralph sample,
this attempts to keep as close to the original code to make it easier
to see where to load the render pipeline.
If you find a bug/suggestion in this code, then you should report
that to the sample included in Panda3D, and not this code.
(and yeah, this code could surely be written in a much nicer way)
"""
import os
import sys
from panda3d.core import CollisionTraverser, CollisionNode
from panda3d.core import CollisionHandlerQueue, CollisionRay
from panda3d.core import AmbientLight, DirectionalLight
from panda3d.core import PandaNode, NodePath, TextNode
from panda3d.core import Vec3, Vec4, BitMask32, load_prc_file_data
from direct.gui.OnscreenText import OnscreenText
from direct.actor.Actor import Actor
from direct.showbase.ShowBase import ShowBase
# Switch into the current directory
os.chdir(os.path.realpath(os.path.dirname(__file__)))
SPEED = 0.5
# Function to put instructions on the screen.
def addInstructions(pos, msg):
return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1),
pos=(-0.9, pos - 0.2), align=TextNode.ALeft, scale=.035)
class World(ShowBase):
def __init__(self):
# Setup window size, title and so on
load_prc_file_data("", """
win-size 1600 900
window-title Render Pipeline - Roaming Ralph Demo
""")
# ------ Begin of render pipeline code ------
# Insert the pipeline path to the system path, this is required to be
# able to import the pipeline classes
pipeline_path = "../../"
# Just a special case for my development setup, so I don't accidentally
# commit a wrong path. You can remove this in your own programs.
if not os.path.isfile(os.path.join(pipeline_path, "setup.py")):
pipeline_path = "../../RenderPipeline/"
sys.path.insert(0, pipeline_path)
from rpcore import RenderPipeline, SpotLight
self.render_pipeline = RenderPipeline()
self.render_pipeline.create(self)
# ------ End of render pipeline code, thats it! ------
# Set time of day
self.render_pipeline.daytime_mgr.time = "7:40"
# Use a special effect for rendering the scene, this is because the
# roaming ralph model has no normals or valid materials
self.render_pipeline.set_effect(render, "scene-effect.yaml", {}, sort=250)
self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, "cam-left":0, "cam-right":0}
self.speed = 1.0
base.win.setClearColor(Vec4(0,0,0,1))
# Post the instructions
self.inst1 = addInstructions(0.95, "[ESC] Quit")
self.inst4 = addInstructions(0.90, "[W] Run Ralph Forward")
self.inst4 = addInstructions(0.85, "[S] Run Ralph Backward")
self.inst2 = addInstructions(0.80, "[A] Rotate Ralph Left")
self.inst3 = addInstructions(0.75, "[D] Rotate Ralph Right")
self.inst6 = addInstructions(0.70, "[Left Arrow] Rotate Camera Left")
self.inst7 = addInstructions(0.65, "[Right Arrow] Rotate Camera Right")
# Set up the environment
#
# This environment model contains collision meshes. If you look
# in the egg file, you will see the following:
#
# <Collide> { Polyset keep descend }
#
# This tag causes the following mesh to be converted to a collision
# mesh -- a mesh which is optimized for collision, not rendering.
# It also keeps the original mesh, so there are now two copies ---
# one optimized for rendering, one for collisions.
self.environ = loader.loadModel("resources/world")
self.environ.reparentTo(render)
self.environ.setPos(0,0,0)
# Remove wall nodes
self.environ.find("**/wall").remove_node()
# Create the main character, Ralph
self.ralph = Actor("resources/ralph",
{"run":"resources/ralph-run",
"walk":"resources/ralph-walk"})
self.ralph.reparentTo(render)
self.ralph.setScale(.2)
self.ralph.setPos(Vec3(-110.9, 29.4, 1.8))
# Create a floater object. We use the "floater" as a temporary
# variable in a variety of calculations.
self.floater = NodePath(PandaNode("floater"))
self.floater.reparentTo(render)
# Accept the control keys for movement and rotation
self.accept("escape", sys.exit)
self.accept("a", self.setKey, ["left",1])
self.accept("d", self.setKey, ["right",1])
self.accept("w", self.setKey, ["forward",1])
self.accept("s", self.setKey, ["backward",1])
self.accept("arrow_left", self.setKey, ["cam-left",1])
self.accept("arrow_right", self.setKey, ["cam-right",1])
self.accept("a-up", self.setKey, ["left",0])
self.accept("d-up", self.setKey, ["right",0])
self.accept("w-up", self.setKey, ["forward",0])
self.accept("s-up", self.setKey, ["backward",0])
self.accept("arrow_left-up", self.setKey, ["cam-left",0])
self.accept("arrow_right-up", self.setKey, ["cam-right",0])
self.accept("=", self.adjustSpeed, [0.25])
self.accept("+", self.adjustSpeed, [0.25])
self.accept("-", self.adjustSpeed, [-0.25])
taskMgr.add(self.move,"moveTask")
# Game state variables
self.isMoving = False
# Set up the camera
base.disableMouse()
base.camera.setPos(self.ralph.getX() + 10,self.ralph.getY() + 10, 2)
base.camLens.setFov(80)
# We will detect the height of the terrain by creating a collision
# ray and casting it downward toward the terrain. One ray will
# start above ralph's head, and the other will start above the camera.
# A ray may hit the terrain, or it may hit a rock or a tree. If it
# hits the terrain, we can detect the height. If it hits anything
# else, we rule that the move is illegal.
self.cTrav = CollisionTraverser()
self.ralphGroundRay = CollisionRay()
self.ralphGroundRay.setOrigin(0,0,1000)
self.ralphGroundRay.setDirection(0,0,-1)
self.ralphGroundCol = CollisionNode('ralphRay')
self.ralphGroundCol.addSolid(self.ralphGroundRay)
self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
self.ralphGroundHandler = CollisionHandlerQueue()
self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)
self.camGroundRay = CollisionRay()
self.camGroundRay.setOrigin(0,0,1000)
self.camGroundRay.setDirection(0,0,-1)
self.camGroundCol = CollisionNode('camRay')
self.camGroundCol.addSolid(self.camGroundRay)
self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
self.camGroundCol.setIntoCollideMask(BitMask32.allOff())
self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol)
self.camGroundHandler = CollisionHandlerQueue()
self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)
# Uncomment this line to see the collision rays
#self.ralphGroundColNp.show()
#self.camGroundColNp.show()
# Uncomment this line to show a visual representation of the
# collisions occuring
#self.cTrav.showCollisions(render)
# Create some lighting
ambientLight = AmbientLight("ambientLight")
ambientLight.setColor(Vec4(.3, .3, .3, 1))
directionalLight = DirectionalLight("directionalLight")
directionalLight.setDirection(Vec3(-5, -5, -5))
directionalLight.setColor(Vec4(1, 1, 1, 1))
directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
render.setLight(render.attachNewNode(ambientLight))
render.setLight(render.attachNewNode(directionalLight))
#Records the state of the arrow keys
def setKey(self, key, value):
self.keyMap[key] = value
# Adjust movement speed
def adjustSpeed(self, delta):
newSpeed = self.speed + delta
if 0 <= newSpeed <= 3:
self.speed = newSpeed
# Accepts arrow keys to move either the player or the menu cursor,
# Also deals with grid checking and collision detection
def move(self, task):
# If the camera-left key is pressed, move camera left.
# If the camera-right key is pressed, move camera right.
base.camera.lookAt(self.ralph)
if (self.keyMap["cam-left"]!=0):
base.camera.setX(base.camera, +20 * globalClock.getDt())
if (self.keyMap["cam-right"]!=0):
base.camera.setX(base.camera, -20 * globalClock.getDt())
# save ralph's initial position so that we can restore it,
# in case he falls off the map or runs into something.
startpos = self.ralph.getPos()
# If a move-key is pressed, move ralph in the specified direction.
if (self.keyMap["left"]!=0):
self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt())
elif (self.keyMap["right"]!=0):
self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt())
if (self.keyMap["forward"]!=0):
self.ralph.setY(self.ralph, -25 * self.speed * globalClock.getDt())
elif (self.keyMap["backward"]!=0):
self.ralph.setY(self.ralph, 25 * self.speed * globalClock.getDt())
# If ralph is moving, loop the run animation.
# If he is standing still, stop the animation.
if (self.keyMap["forward"]!=0) or (self.keyMap["backward"]!=0) or \
(self.keyMap["left"]!=0) or (self.keyMap["right"]!=0):
if self.isMoving is False:
self.ralph.loop("run")
self.isMoving = True
else:
if self.isMoving:
self.ralph.stop()
self.ralph.pose("walk",5)
self.isMoving = False
# If the camera is too far from ralph, move it closer.
# If the camera is too close to ralph, move it farther.
camvec = self.ralph.getPos() - base.camera.getPos()
camvec.setZ(0)
camdist = camvec.length()
camvec.normalize()
if (camdist > 10.0):
base.camera.setPos(base.camera.getPos() + camvec*(camdist-10))
camdist = 10.0
if (camdist < 5.0):
base.camera.setPos(base.camera.getPos() - camvec*(5-camdist))
camdist = 5.0
# Now check for collisions.
self.cTrav.traverse(render)
# Adjust ralph's Z coordinate. If ralph's ray hit terrain,
# update his Z. If it hit anything else, or didn't hit anything, put
# him back where he was last frame.
entries = []
for i in range(self.ralphGroundHandler.getNumEntries()):
entry = self.ralphGroundHandler.getEntry(i)
entries.append(entry)
if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
self.ralph.setZ(entries[0].getSurfacePoint(render).getZ())
else:
self.ralph.setPos(startpos)
# Keep the camera at one foot above the terrain,
# or two feet above ralph, whichever is greater.
entries = []
for i in range(self.camGroundHandler.getNumEntries()):
entry = self.camGroundHandler.getEntry(i)
entries.append(entry)
if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
base.camera.setZ(entries[0].getSurfacePoint(render).getZ()+1.0)
if (base.camera.getZ() < self.ralph.getZ() + 2.0):
base.camera.setZ(self.ralph.getZ() + 2.0)
# The camera should look in ralph's direction,
# but it should also try to stay horizontal, so look at
# a floater which hovers above ralph's head.
self.floater.setPos(self.ralph.getPos())
self.floater.setZ(self.ralph.getZ() + 2.0)
base.camera.lookAt(self.floater)
return task.cont
w = World().run()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -1,13 +0,0 @@
# Special effect file for rendering the roaming ralph scene, since roaming
# Ralph has no normals or material information
fragment:
material:
m.shading_model = SHADING_MODEL_DEFAULT;
m.basecolor = texture(p3d_Texture0, texcoord).xyz;
m.roughness = 0.9;
m.specular_ior = 1.51;
m.metallic = 0;
m.normal = vOutput.normal;
m.shading_model_param0 = 0.0; // unused

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -1,147 +0,0 @@
"""
Lights sample
This sample shows how to setup multiple lights and load them from a .bam file.
"""
# Disable the "xxx has no yyy member" error, pylint seems to be unable to detect
# the properties of a nodepath
# pylint: disable=no-member
from __future__ import print_function
import os
import sys
import math
from random import randint
from panda3d.core import Vec3, load_prc_file_data, Material
from direct.showbase.ShowBase import ShowBase
from direct.interval.IntervalGlobal import Sequence
# Switch into the current directory
os.chdir(os.path.realpath(os.path.dirname(__file__)))
class MainApp(ShowBase):
def __init__(self):
# Setup window size and title
load_prc_file_data("", """
win-size 900 600
window-title Render Pipeline - Lights demo
""")
# ------ Begin of render pipeline code ------
# Insert the pipeline path to the system path, this is required to be
# able to import the pipeline classes
pipeline_path = "../../"
# Just a special case for my development setup, so I don't accidentally
# commit a wrong path. You can remove this in your own programs.
if not os.path.isfile(os.path.join(pipeline_path, "setup.py")):
pipeline_path = "../../RenderPipeline/"
sys.path.insert(0, pipeline_path)
from rpcore import RenderPipeline, SpotLight
self.render_pipeline = RenderPipeline()
self.render_pipeline.create(self)
# Import the movement controller, this is a convenience class
# to provide an improved camera control compared to Panda3Ds default
# mouse controller.
from rpcore.util.movement_controller import MovementController
# ------ End of render pipeline code, thats it! ------
# Set time of day
self.render_pipeline.daytime_mgr.time = "5:20"
# Configuration variables
self.half_energy = 5000
self.lamp_fov = 70
self.lamp_radius = 10
# Load the scene
model = self.loader.load_model("scene/Scene.bam")
model.reparent_to(self.render)
# Animate balls, this is for testing the motion blur
blend_type = "noBlend"
np = model.find("**/MBRotate")
np.hprInterval(1.5, Vec3(360, 360, 0), Vec3(0, 0, 0), blendType=blend_type).loop()
np = model.find("**/MBUpDown")
np_pos = np.get_pos() - Vec3(0, 0, 2)
Sequence(
np.posInterval(0.15, np_pos + Vec3(0, 0, 6), np_pos, blendType=blend_type),
np.posInterval(0.15, np_pos, np_pos + Vec3(0, 0, 6), blendType=blend_type)).loop()
np = model.find("**/MBFrontBack")
np_pos = np.get_pos() - Vec3(0, 0, 2)
Sequence(
np.posInterval(0.15, np_pos + Vec3(0, 6, 0), np_pos, blendType=blend_type),
np.posInterval(0.15, np_pos, np_pos + Vec3(0, 6, 0), blendType=blend_type)).loop()
np = model.find("**/MBScale")
Sequence(
np.scaleInterval(0.2, Vec3(1.5), Vec3(1), blendType=blend_type),
np.scaleInterval(0.2, Vec3(1), Vec3(1.5), blendType=blend_type)).loop()
# Generate temperature lamps
# This shows how to procedurally create lamps. In this case, we
# base the lights positions on empties created in blender.
self._lights = []
light_key = lambda light: int(light.get_name().split("LampLum")[-1])
lumlamps = sorted(model.find_all_matches("**/LampLum*"), key=light_key)
for lumlamp in lumlamps:
lum = float(lumlamp.get_name()[len("LampLum"):])
light = SpotLight()
light.direction = (0, -1.5, -1)
light.fov = self.lamp_fov
light.set_color_from_temperature(lum * 1000.0)
light.energy = self.half_energy
light.pos = lumlamp.get_pos(self.render)
light.radius = self.lamp_radius
light.casts_shadows = False
light.shadow_map_resolution = 256
self.render_pipeline.add_light(light)
# Put Pandas on the edges
if lumlamp in lumlamps[0:2] + lumlamps[-2:]:
panda = self.loader.load_model("panda")
panda.reparent_to(self.render)
panda_mat = Material("default")
panda_mat.emission = 0
panda.set_material(panda_mat)
panda.set_pos(light.pos)
panda.set_z(0.65)
panda.set_h(180 + randint(-60, 60))
panda.set_scale(0.2)
panda.set_y(panda.get_y() - 3.0)
self._lights.append(light)
self.render_pipeline.prepare_scene(model)
# Init movement controller
self.controller = MovementController(self)
self.controller.set_initial_position(Vec3(23.9, 42.5, 13.4), Vec3(23.8, 33.4, 10.8))
self.controller.setup()
self.addTask(self.update, "update")
def update(self, task):
""" Update method """
frame_time = self.taskMgr.globalClock.get_frame_time()
# Make the lights glow
for i, light in enumerate(self._lights):
brightness = math.sin(0.4 * i + frame_time)
light.energy = max(0, self.half_energy / 2.0 + brightness * self.half_energy)
return task.cont
MainApp().run()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 MiB

Some files were not shown because too many files have changed in this diff Show More