forked from Rowland/EG
太阳方位角及高度角可自由调整
This commit is contained in:
parent
d40cce3ece
commit
62aa0f6fbd
2
.idea/AugmentWebviewStateStore.xml
generated
2
.idea/AugmentWebviewStateStore.xml
generated
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
RenderPipelineFile/data/gui/Default_Rough.png
Normal file
BIN
RenderPipelineFile/data/gui/Default_Rough.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
RenderPipelineFile/data/gui/black.jpg
Normal file
BIN
RenderPipelineFile/data/gui/black.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 272 KiB |
@ -86,7 +86,8 @@ class RenderPipeline(RPObject):
|
||||
self._applied_effects = []
|
||||
self._pre_showbase_initialized = False
|
||||
self._first_frame = None
|
||||
self.set_loading_screen_image("/$$rp/data/gui/loading_screen_bg.txo")
|
||||
#self.set_loading_screen_image("/$$rp/data/gui/loading_screen_bg.txo")
|
||||
self.set_loading_screen_image("/$$rp/data/gui/Default_Rough.png")
|
||||
|
||||
def load_settings(self, path):
|
||||
""" Loads the pipeline configuration from a given filename. Usually
|
||||
|
||||
@ -71,8 +71,8 @@ class DisplayShaderBuilder(object): # pylint: disable=too-few-public-methods
|
||||
# Build actual shader
|
||||
built = """
|
||||
#version 400
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
#pragma include "render_pipeline_base.inc.glsl"
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
in vec2 texcoord;
|
||||
out vec3 result;
|
||||
uniform int mipmap;
|
||||
|
||||
@ -127,9 +127,20 @@ class NetworkCommunication(RPObject):
|
||||
be an update of a plugin setting """
|
||||
if cmd.startswith("setval "):
|
||||
parts = cmd.split()
|
||||
if len(parts) < 3:
|
||||
self.warn("Invalid setval command format. Expected: setval plugin.setting value")
|
||||
return
|
||||
|
||||
setting_parts = parts[1].split(".")
|
||||
self._pipeline.plugin_mgr.on_setting_changed(
|
||||
setting_parts[0], setting_parts[1], parts[2])
|
||||
if len(setting_parts) < 2:
|
||||
self.warn("Invalid setting format. Expected: plugin.setting")
|
||||
return
|
||||
|
||||
try:
|
||||
self._pipeline.plugin_mgr.on_setting_changed(
|
||||
setting_parts[0], setting_parts[1], parts[2])
|
||||
except Exception as e:
|
||||
self.warn("Error handling setting change:", str(e))
|
||||
else:
|
||||
self.warn("Recieved unkown plugin command:", cmd)
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
// This file includes defines and functions used for the scattering
|
||||
|
||||
|
||||
@ -9,6 +7,8 @@
|
||||
|
||||
#pragma include "render_pipeline_base.inc.glsl"
|
||||
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
|
||||
// These can be overridden depending on if the shader is a compute shader
|
||||
// or a regular fragment shader
|
||||
|
||||
@ -97,6 +97,11 @@ class PluginConfigurator(QMainWindow, Ui_MainWindow):
|
||||
update_thread = Thread(target=self.update_thread, args=())
|
||||
update_thread.start()
|
||||
|
||||
# 启动通信文件监听线程
|
||||
comm_thread = Thread(target=self.communication_thread, args=())
|
||||
comm_thread.daemon = True # 设置为守护线程,主程序退出时自动结束
|
||||
comm_thread.start()
|
||||
|
||||
def closeEvent(self, event): # noqa
|
||||
event.accept()
|
||||
import os
|
||||
@ -486,6 +491,84 @@ class PluginConfigurator(QMainWindow, Ui_MainWindow):
|
||||
|
||||
self.lst_plugins.setCurrentRow(0)
|
||||
|
||||
def communication_thread(self):
|
||||
"""监听来自主程序的通信文件"""
|
||||
import json
|
||||
import tempfile
|
||||
|
||||
comm_dir = os.path.join(tempfile.gettempdir(), "daytime_editor_comm")
|
||||
comm_file = os.path.join(comm_dir, "sun_azimuth_command.json")
|
||||
|
||||
print("🔄 Day Time Editor通信监听已启动")
|
||||
print(f" 监听文件: {comm_file}")
|
||||
|
||||
last_timestamp = 0
|
||||
|
||||
while True:
|
||||
try:
|
||||
if os.path.exists(comm_file):
|
||||
with open(comm_file, 'r') as f:
|
||||
command = json.load(f)
|
||||
|
||||
# 检查是否是新命令(避免重复执行)
|
||||
if command.get('timestamp', 0) > last_timestamp:
|
||||
last_timestamp = command.get('timestamp', 0)
|
||||
|
||||
if command.get('command') == 'set_sun_azimuth':
|
||||
azimuth_value = command.get('value', 0)
|
||||
print(f"📨 收到Sun Azimuth命令: {azimuth_value}°")
|
||||
|
||||
# 在主线程中执行UI更新
|
||||
QTimer.singleShot(0, partial(self.apply_sun_azimuth, azimuth_value))
|
||||
|
||||
time.sleep(0.1) # 100ms检查一次
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 通信监听错误: {e}")
|
||||
time.sleep(1) # 出错时等待1秒再重试
|
||||
|
||||
def apply_sun_azimuth(self, azimuth_degrees):
|
||||
"""应用Sun Azimuth值到Day Time Editor"""
|
||||
try:
|
||||
print(f"🌞 正在应用Sun Azimuth: {azimuth_degrees}°")
|
||||
|
||||
# 查找Sun Azimuth相关的控件
|
||||
# 这里需要根据实际的UI结构来实现
|
||||
# 先尝试找到可能的控件
|
||||
|
||||
# 方法1:查找包含"azimuth"的控件
|
||||
for widget_name in dir(self):
|
||||
widget = getattr(self, widget_name, None)
|
||||
if widget and hasattr(widget, 'setValue'):
|
||||
if 'azimuth' in widget_name.lower() or 'sun' in widget_name.lower():
|
||||
print(f" 尝试设置控件: {widget_name}")
|
||||
try:
|
||||
widget.setValue(azimuth_degrees)
|
||||
print(f"✅ 成功设置 {widget_name} = {azimuth_degrees}°")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f" 设置失败: {e}")
|
||||
|
||||
# 方法2:查找表格中的Sun Azimuth设置
|
||||
if hasattr(self, 'table_plugin_settings'):
|
||||
table = self.table_plugin_settings
|
||||
for row in range(table.rowCount()):
|
||||
item = table.item(row, 0) # 第一列通常是设置名称
|
||||
if item and 'azimuth' in item.text().lower():
|
||||
# 找到Sun Azimuth行,设置第二列的值
|
||||
value_item = table.item(row, 1)
|
||||
if value_item:
|
||||
value_item.setText(str(azimuth_degrees))
|
||||
print(f"✅ 在表格中设置Sun Azimuth = {azimuth_degrees}°")
|
||||
return
|
||||
|
||||
print("⚠️ 未找到Sun Azimuth控件,请检查UI结构")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 应用Sun Azimuth失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# Start application
|
||||
app = QApplication(sys.argv)
|
||||
qt_register_fonts()
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -1,13 +1,16 @@
|
||||
|
||||
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世界设置和核心功能"""
|
||||
@ -49,6 +52,7 @@ class CoreWorld(Panda3DWorld):
|
||||
#
|
||||
#self.createDirectionalLight()
|
||||
#self._setYCModel()
|
||||
#self._setArm1()
|
||||
|
||||
# 创建示例材质
|
||||
#self.load_test_models_with_materials()
|
||||
@ -70,6 +74,19 @@ class CoreWorld(Panda3DWorld):
|
||||
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)
|
||||
@ -288,8 +305,19 @@ class CoreWorld(Panda3DWorld):
|
||||
|
||||
|
||||
def set_daytime(self, time_str):
|
||||
self.render_pipeline.daytime_mgr.time = time_str
|
||||
print(f"当前时间设置为: {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):
|
||||
# 加载天空盒模型
|
||||
|
||||
1
main.py
1
main.py
@ -509,7 +509,6 @@ class MyWorld(CoreWorld):
|
||||
def listAllScripts(self):
|
||||
"""列出所有脚本信息"""
|
||||
return self.script_manager.list_all_scripts()
|
||||
|
||||
# ==================== VR系统功能代理 ====================
|
||||
|
||||
# VR系统控制方法 - 代理到vr_manager
|
||||
|
||||
Binary file not shown.
@ -15,6 +15,7 @@ from panda3d.core import (
|
||||
from panda3d.egg import EggData, EggVertexPool
|
||||
from direct.actor.Actor import Actor
|
||||
from QPanda3D.Panda3DWorld import get_render_pipeline
|
||||
from scene.animation_manager import AnimationManager
|
||||
|
||||
class SceneManager:
|
||||
"""场景管理器 - 统一管理场景中的所有元素"""
|
||||
@ -31,6 +32,9 @@ class SceneManager:
|
||||
self.Spotlight = []
|
||||
self.Pointlight = []
|
||||
|
||||
# 初始化动画管理器
|
||||
self.animation_manager = AnimationManager(world)
|
||||
|
||||
print("✓ 场景管理系统初始化完成")
|
||||
|
||||
# ==================== 模型导入和处理 ====================
|
||||
@ -106,6 +110,64 @@ class SceneManager:
|
||||
print(f"导入模型失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def importAnimatedFBX(self, filepath, scale=0.01, auto_play=True):
|
||||
"""导入带动画的FBX模型(使用assimp)
|
||||
|
||||
Args:
|
||||
filepath: FBX文件路径
|
||||
scale: 缩放比例(默认0.01,从厘米转换到米)
|
||||
auto_play: 是否自动播放第一个动画
|
||||
|
||||
Returns:
|
||||
Actor对象,如果加载失败返回None
|
||||
"""
|
||||
try:
|
||||
print(f"\n=== 导入动画FBX模型: {filepath} ===")
|
||||
|
||||
# 使用动画管理器加载FBX
|
||||
actor = self.animation_manager.load_fbx_with_animations(filepath, scale)
|
||||
|
||||
if actor:
|
||||
# 设置模型名称
|
||||
model_name = os.path.basename(filepath)
|
||||
actor.setName(model_name)
|
||||
|
||||
# 调整模型位置到地面
|
||||
self._adjustModelToGround(actor)
|
||||
|
||||
# 设置碰撞检测
|
||||
self.setupCollision(actor)
|
||||
|
||||
# 添加文件标签
|
||||
actor.setTag("file", model_name)
|
||||
actor.setTag("is_animated_model", "1")
|
||||
|
||||
# 添加到模型列表
|
||||
self.models.append(actor)
|
||||
|
||||
# 自动播放第一个动画
|
||||
if auto_play:
|
||||
available_anims = self.animation_manager.get_available_animations(actor)
|
||||
if available_anims:
|
||||
first_anim = available_anims[0]
|
||||
self.animation_manager.play_animation(actor, first_anim, loop=True)
|
||||
print(f"🎬 自动播放动画: {first_anim}")
|
||||
|
||||
# 更新场景树
|
||||
self.updateSceneTree()
|
||||
|
||||
print(f"=== 动画FBX模型导入成功: {model_name} ===\n")
|
||||
return actor
|
||||
else:
|
||||
print("❌ 动画FBX模型导入失败")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"导入动画FBX模型失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def _applyMaterialsToModel(self, model):
|
||||
"""递归应用材质到模型的所有GeomNode"""
|
||||
def apply_material(node_path, depth=0):
|
||||
@ -425,9 +487,21 @@ class SceneManager:
|
||||
except Exception as e:
|
||||
print(f"异步加载模型失败: {str(e)}")
|
||||
|
||||
def loadAnimatedModel(self, model_path, anims=None):
|
||||
"""加载带动画的模型"""
|
||||
def loadAnimatedModel(self, model_path, anims=None, auto_play=True):
|
||||
"""加载带动画的模型
|
||||
|
||||
Args:
|
||||
model_path: 模型文件路径
|
||||
anims: 动画字典,格式为 {"动画名": "动画文件路径"}
|
||||
auto_play: 是否自动播放第一个动画
|
||||
"""
|
||||
try:
|
||||
print(f"🎬 加载动画模型: {model_path}")
|
||||
|
||||
# 如果没有指定动画,尝试自动检测
|
||||
if anims is None:
|
||||
anims = self._detectAnimations(model_path)
|
||||
|
||||
# 创建Actor对象
|
||||
actor = Actor(model_path, anims)
|
||||
if actor:
|
||||
@ -436,12 +510,34 @@ class SceneManager:
|
||||
# 设置碰撞检测
|
||||
self.setupCollision(actor)
|
||||
|
||||
# 获取可用的动画列表
|
||||
available_anims = actor.getAnimNames()
|
||||
print(f"📋 检测到动画: {available_anims}")
|
||||
|
||||
# 自动播放第一个动画
|
||||
if auto_play and available_anims:
|
||||
first_anim = available_anims[0]
|
||||
actor.loop(first_anim)
|
||||
print(f"▶️ 自动播放动画: {first_anim}")
|
||||
|
||||
# 添加动画控制标签
|
||||
actor.setTag("animated", "true")
|
||||
actor.setTag("current_anim", first_anim)
|
||||
actor.setTag("available_anims", str(available_anims))
|
||||
|
||||
# 调整模型位置(让它站在地面上)
|
||||
self._adjustModelPosition(actor)
|
||||
|
||||
self.models.append(actor)
|
||||
# 更新场景树
|
||||
self.updateSceneTree()
|
||||
|
||||
print(f"✅ 动画模型加载完成: {actor.getName()}")
|
||||
return actor
|
||||
except Exception as e:
|
||||
print(f"加载动画模型失败: {str(e)}")
|
||||
print(f"❌ 加载动画模型失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
# ==================== 材质和几何体处理 ====================
|
||||
|
||||
133
scripts/R_P.py
Normal file
133
scripts/R_P.py
Normal file
@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 30.0 # 最大旋转角度(相对于初始角度)
|
||||
self.direction = 1
|
||||
self.current_offset = 0.0 # 当前相对于初始角度的偏移
|
||||
self.initial_angle = None # 模型的初始角度
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"最大旋转角度: ±{self.max_angle}度")
|
||||
|
||||
# 记录模型的初始角度
|
||||
if self.gameObject:
|
||||
initial_hpr = self.gameObject.getHpr()
|
||||
self.initial_angle = initial_hpr.getZ() # 记录Z轴的初始角度
|
||||
self.log(f"模型初始角度: {self.initial_angle}度")
|
||||
self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
else:
|
||||
self.log("⚠️ 无法获取游戏对象,使用默认初始角度0")
|
||||
self.initial_angle = 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_rotating or self.initial_angle is None:
|
||||
return
|
||||
|
||||
# 计算角度变化量
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_offset += delta_angle
|
||||
|
||||
# 如果超出角度范围,则反向并限制在边界
|
||||
if self.current_offset > self.max_angle:
|
||||
self.current_offset = self.max_angle
|
||||
self.direction *= -1
|
||||
elif self.current_offset < -self.max_angle:
|
||||
self.current_offset = -self.max_angle
|
||||
self.direction *= -1
|
||||
|
||||
# 计算最终角度(初始角度 + 偏移量)
|
||||
final_angle = self.initial_angle + self.current_offset
|
||||
|
||||
# 设置新的旋转(只改变Z轴,保持其他不变)
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), final_angle, current_hpr.getZ())
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("旋转脚本停止")
|
||||
|
||||
# ==================== 控制方法 ====================
|
||||
|
||||
def set_max_angle(self, new_max_angle):
|
||||
"""
|
||||
设置新的最大旋转角度
|
||||
|
||||
Args:
|
||||
new_max_angle: 新的最大角度值
|
||||
"""
|
||||
self.max_angle = new_max_angle
|
||||
self.log(f"最大旋转角度已设置为: ±{self.max_angle}度")
|
||||
if self.initial_angle is not None:
|
||||
self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
|
||||
def set_rotation_speed(self, new_speed):
|
||||
"""
|
||||
设置新的旋转速度
|
||||
|
||||
Args:
|
||||
new_speed: 新的旋转速度(度/秒)
|
||||
"""
|
||||
self.rotation_speed_y = new_speed
|
||||
self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def pause_rotation(self):
|
||||
"""暂停旋转"""
|
||||
self.is_rotating = False
|
||||
self.log("旋转已暂停")
|
||||
|
||||
def resume_rotation(self):
|
||||
"""恢复旋转"""
|
||||
self.is_rotating = True
|
||||
self.log("旋转已恢复")
|
||||
|
||||
def reset_to_initial_angle(self):
|
||||
"""重置到初始角度"""
|
||||
if self.initial_angle is not None and self.gameObject:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), self.initial_angle)
|
||||
self.current_offset = 0.0
|
||||
self.direction = 1
|
||||
self.log(f"已重置到初始角度: {self.initial_angle}度")
|
||||
|
||||
def get_current_info(self):
|
||||
"""获取当前旋转信息"""
|
||||
if self.gameObject and self.initial_angle is not None:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
current_angle = current_hpr.getZ()
|
||||
self.log("=== 当前旋转信息 ===")
|
||||
self.log(f"初始角度: {self.initial_angle}度")
|
||||
self.log(f"当前角度: {current_angle}度")
|
||||
self.log(f"偏移量: {self.current_offset}度")
|
||||
self.log(f"最大角度: ±{self.max_angle}度")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}")
|
||||
self.log("=== 信息结束 ===")
|
||||
else:
|
||||
self.log("无法获取旋转信息")
|
||||
133
scripts/R_R.py
Normal file
133
scripts/R_R.py
Normal file
@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 30.0 # 最大旋转角度(相对于初始角度)
|
||||
self.direction = 1
|
||||
self.current_offset = 0.0 # 当前相对于初始角度的偏移
|
||||
self.initial_angle = None # 模型的初始角度
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"最大旋转角度: ±{self.max_angle}度")
|
||||
|
||||
# 记录模型的初始角度
|
||||
if self.gameObject:
|
||||
initial_hpr = self.gameObject.getHpr()
|
||||
self.initial_angle = initial_hpr.getZ() # 记录Z轴的初始角度
|
||||
self.log(f"模型初始角度: {self.initial_angle}度")
|
||||
self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
else:
|
||||
self.log("⚠️ 无法获取游戏对象,使用默认初始角度0")
|
||||
self.initial_angle = 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
if not self.is_rotating or self.initial_angle is None:
|
||||
return
|
||||
|
||||
# 计算角度变化量
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_offset += delta_angle
|
||||
|
||||
# 如果超出角度范围,则反向并限制在边界
|
||||
if self.current_offset > self.max_angle:
|
||||
self.current_offset = self.max_angle
|
||||
self.direction *= -1
|
||||
elif self.current_offset < -self.max_angle:
|
||||
self.current_offset = -self.max_angle
|
||||
self.direction *= -1
|
||||
|
||||
# 计算最终角度(初始角度 + 偏移量)
|
||||
final_angle = self.initial_angle + self.current_offset
|
||||
|
||||
# 设置新的旋转(只改变Z轴,保持其他不变)
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), final_angle)
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("旋转脚本停止")
|
||||
|
||||
# ==================== 控制方法 ====================
|
||||
|
||||
def set_max_angle(self, new_max_angle):
|
||||
"""
|
||||
设置新的最大旋转角度
|
||||
|
||||
Args:
|
||||
new_max_angle: 新的最大角度值
|
||||
"""
|
||||
self.max_angle = new_max_angle
|
||||
self.log(f"最大旋转角度已设置为: ±{self.max_angle}度")
|
||||
if self.initial_angle is not None:
|
||||
self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
|
||||
def set_rotation_speed(self, new_speed):
|
||||
"""
|
||||
设置新的旋转速度
|
||||
|
||||
Args:
|
||||
new_speed: 新的旋转速度(度/秒)
|
||||
"""
|
||||
self.rotation_speed_y = new_speed
|
||||
self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def pause_rotation(self):
|
||||
"""暂停旋转"""
|
||||
self.is_rotating = False
|
||||
self.log("旋转已暂停")
|
||||
|
||||
def resume_rotation(self):
|
||||
"""恢复旋转"""
|
||||
self.is_rotating = True
|
||||
self.log("旋转已恢复")
|
||||
|
||||
def reset_to_initial_angle(self):
|
||||
"""重置到初始角度"""
|
||||
if self.initial_angle is not None and self.gameObject:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), self.initial_angle)
|
||||
self.current_offset = 0.0
|
||||
self.direction = 1
|
||||
self.log(f"已重置到初始角度: {self.initial_angle}度")
|
||||
|
||||
def get_current_info(self):
|
||||
"""获取当前旋转信息"""
|
||||
if self.gameObject and self.initial_angle is not None:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
current_angle = current_hpr.getZ()
|
||||
self.log("=== 当前旋转信息 ===")
|
||||
self.log(f"初始角度: {self.initial_angle}度")
|
||||
self.log(f"当前角度: {current_angle}度")
|
||||
self.log(f"偏移量: {self.current_offset}度")
|
||||
self.log(f"最大角度: ±{self.max_angle}度")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}")
|
||||
self.log("=== 信息结束 ===")
|
||||
else:
|
||||
self.log("无法获取旋转信息")
|
||||
215
scripts/Rotate_H_Script.py
Normal file
215
scripts/Rotate_H_Script.py
Normal file
@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 30.0 # 最大旋转角度(相对于初始角度)
|
||||
self.direction = 1
|
||||
self.current_offset = 0.0 # 当前相对于初始角度的偏移
|
||||
self.initial_angle = None # 模型的初始角度
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
# 机器人式停顿参数
|
||||
self.pause_duration = 0.5 # 停顿时间(秒)
|
||||
self.current_pause_time = 0.0 # 当前停顿计时
|
||||
self.is_paused = False # 是否正在停顿
|
||||
self.robot_mode = True # 是否启用机器人模式
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("机器人旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"最大旋转角度: ±{self.max_angle}度")
|
||||
self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}")
|
||||
if self.robot_mode:
|
||||
self.log(f"停顿时间: {self.pause_duration}秒")
|
||||
|
||||
# 记录模型的初始角度
|
||||
if self.gameObject:
|
||||
initial_hpr = self.gameObject.getHpr()
|
||||
self.initial_angle = initial_hpr.getY() # 记录Y轴的初始角度(Pitch)
|
||||
self.log(f"模型初始角度: {self.initial_angle}度")
|
||||
self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
else:
|
||||
self.log("⚠️ 无法获取游戏对象,使用默认初始角度0")
|
||||
self.initial_angle = 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新 - 机器人式旋转"""
|
||||
if not self.is_rotating or self.initial_angle is None:
|
||||
return
|
||||
|
||||
# 如果正在停顿中
|
||||
if self.is_paused:
|
||||
self.current_pause_time += dt
|
||||
if self.current_pause_time >= self.pause_duration:
|
||||
# 停顿结束,继续旋转
|
||||
self.is_paused = False
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"停顿结束,继续旋转,方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
return
|
||||
|
||||
# 计算角度变化量
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_offset += delta_angle
|
||||
|
||||
# 检查是否到达边界
|
||||
reached_boundary = False
|
||||
if self.current_offset > self.max_angle:
|
||||
self.current_offset = self.max_angle
|
||||
self.direction *= -1
|
||||
reached_boundary = True
|
||||
elif self.current_offset < -self.max_angle:
|
||||
self.current_offset = -self.max_angle
|
||||
self.direction *= -1
|
||||
reached_boundary = True
|
||||
|
||||
# 如果到达边界且启用机器人模式,开始停顿
|
||||
if reached_boundary and self.robot_mode:
|
||||
self.is_paused = True
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"到达边界 ({self.current_offset}°),开始停顿 {self.pause_duration}秒")
|
||||
|
||||
# 计算最终角度(初始角度 + 偏移量)
|
||||
final_angle = self.initial_angle + self.current_offset
|
||||
|
||||
# 设置新的旋转(只改变Y轴,保持其他不变)
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(final_angle, current_hpr.getY(), current_hpr.getZ())
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("机器人旋转脚本停止")
|
||||
|
||||
# ==================== 机器人模式控制方法 ====================
|
||||
|
||||
def set_robot_mode(self, enabled=True):
|
||||
"""
|
||||
启用或禁用机器人模式
|
||||
|
||||
Args:
|
||||
enabled: 是否启用机器人模式
|
||||
"""
|
||||
self.robot_mode = enabled
|
||||
self.log(f"机器人模式: {'开启' if enabled else '关闭'}")
|
||||
if not enabled:
|
||||
self.is_paused = False # 如果禁用机器人模式,立即结束停顿
|
||||
|
||||
def set_pause_duration(self, duration):
|
||||
"""
|
||||
设置停顿时间
|
||||
|
||||
Args:
|
||||
duration: 停顿时间(秒)
|
||||
"""
|
||||
self.pause_duration = max(0.1, duration) # 最小0.1秒
|
||||
self.log(f"停顿时间已设置为: {self.pause_duration}秒")
|
||||
|
||||
def set_max_angle(self, new_max_angle):
|
||||
"""
|
||||
设置新的最大旋转角度
|
||||
|
||||
Args:
|
||||
new_max_angle: 新的最大角度值
|
||||
"""
|
||||
self.max_angle = new_max_angle
|
||||
self.log(f"最大旋转角度已设置为: ±{self.max_angle}度")
|
||||
if self.initial_angle is not None:
|
||||
self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
|
||||
def set_rotation_speed(self, new_speed):
|
||||
"""
|
||||
设置新的旋转速度
|
||||
|
||||
Args:
|
||||
new_speed: 新的旋转速度(度/秒)
|
||||
"""
|
||||
self.rotation_speed_y = new_speed
|
||||
self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def pause_rotation(self):
|
||||
"""暂停旋转"""
|
||||
self.is_rotating = False
|
||||
self.log("旋转已暂停")
|
||||
|
||||
def resume_rotation(self):
|
||||
"""恢复旋转"""
|
||||
self.is_rotating = True
|
||||
self.is_paused = False # 同时结束停顿状态
|
||||
self.log("旋转已恢复")
|
||||
|
||||
def reset_to_initial_angle(self):
|
||||
"""重置到初始角度"""
|
||||
if self.initial_angle is not None and self.gameObject:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), self.initial_angle, current_hpr.getZ())
|
||||
self.current_offset = 0.0
|
||||
self.direction = 1
|
||||
self.is_paused = False
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"已重置到初始角度: {self.initial_angle}度")
|
||||
|
||||
def get_current_info(self):
|
||||
"""获取当前旋转信息"""
|
||||
if self.gameObject and self.initial_angle is not None:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
current_angle = current_hpr.getY()
|
||||
self.log("=== 机器人旋转信息 ===")
|
||||
self.log(f"初始角度: {self.initial_angle}度")
|
||||
self.log(f"当前角度: {current_angle}度")
|
||||
self.log(f"偏移量: {self.current_offset}度")
|
||||
self.log(f"最大角度: ±{self.max_angle}度")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}")
|
||||
self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}")
|
||||
if self.robot_mode:
|
||||
self.log(f"停顿时间: {self.pause_duration}秒")
|
||||
self.log(f"当前状态: {'停顿中' if self.is_paused else '旋转中'}")
|
||||
if self.is_paused:
|
||||
remaining_time = self.pause_duration - self.current_pause_time
|
||||
self.log(f"剩余停顿时间: {remaining_time:.1f}秒")
|
||||
self.log("=== 信息结束 ===")
|
||||
else:
|
||||
self.log("无法获取旋转信息")
|
||||
|
||||
# ==================== 预设配置方法 ====================
|
||||
|
||||
def set_slow_robot_mode(self):
|
||||
"""预设:慢速机器人模式"""
|
||||
self.set_rotation_speed(15.0)
|
||||
self.set_pause_duration(1.0)
|
||||
self.set_robot_mode(True)
|
||||
self.log("已设置为慢速机器人模式")
|
||||
|
||||
def set_fast_robot_mode(self):
|
||||
"""预设:快速机器人模式"""
|
||||
self.set_rotation_speed(45.0)
|
||||
self.set_pause_duration(0.3)
|
||||
self.set_robot_mode(True)
|
||||
self.log("已设置为快速机器人模式")
|
||||
|
||||
def set_smooth_mode(self):
|
||||
"""预设:平滑模式(非机器人)"""
|
||||
self.set_robot_mode(False)
|
||||
self.set_rotation_speed(30.0)
|
||||
self.log("已设置为平滑旋转模式")
|
||||
740
scripts/Rotate_P.py
Normal file
740
scripts/Rotate_P.py
Normal file
@ -0,0 +1,740 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Rotate_P - 自定义脚本,支持动态挂载其他脚本到模型或子物体
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
class RotateP(ScriptBase):
|
||||
"""自定义脚本类 - 支持脚本挂载功能"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# 在这里初始化您的变量
|
||||
self.mounted_scripts = {} # 存储已挂载的脚本 {object_name: [script_names]}
|
||||
self.world = None # 世界对象引用
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用 - 当此脚本被挂载时自动执行挂载其他脚本"""
|
||||
self.log("Rotate_P 脚本开始运行!")
|
||||
|
||||
# 获取世界对象引用
|
||||
if hasattr(self.script_manager, 'world'):
|
||||
self.world = self.script_manager.world
|
||||
self.log("✓ 获取世界对象引用成功")
|
||||
|
||||
# 当此脚本被挂载时,自动执行脚本挂载操作
|
||||
self.auto_mount_scripts_on_start()
|
||||
else:
|
||||
self.log("⚠️ 无法获取世界对象引用")
|
||||
|
||||
def auto_mount_scripts_on_start(self):
|
||||
"""
|
||||
当Rotate_P脚本被挂载时自动执行的脚本挂载操作
|
||||
在这里定义要自动挂载的脚本和目标对象
|
||||
"""
|
||||
self.log("=== 开始自动挂载脚本 ===")
|
||||
|
||||
# 获取当前脚本所挂载的对象
|
||||
current_object = self.gameObject
|
||||
current_object_name = current_object.getName() if current_object else "Unknown"
|
||||
self.log(f"当前脚本挂载在对象: {current_object_name}")
|
||||
|
||||
# 显示当前对象信息
|
||||
self.get_current_object_info()
|
||||
|
||||
# ==================== 配置区域 ====================
|
||||
# 请根据您的需求选择以下配置之一,或自定义配置
|
||||
|
||||
# 配置1:机器人式旋转演示(有停顿)
|
||||
self.setup_rotation_demo(max_angle=45.0, rotation_speed=25.0, robot_mode=True, pause_duration=0.8)
|
||||
|
||||
# 配置2:工业机器人演示(取消注释以使用)
|
||||
# self.setup_industrial_robot_demo()
|
||||
|
||||
# 配置3:伺服电机演示(取消注释以使用)
|
||||
# self.setup_servo_motor_demo()
|
||||
|
||||
# 配置4:机器人手臂演示(取消注释以使用)
|
||||
# self.setup_robot_arm_demo()
|
||||
|
||||
# 配置5:为指定对象挂载机器人旋转脚本(取消注释并修改)
|
||||
# self.mount_robot_rotator("arm_joint", max_angle=60.0, rotation_speed=15.0, pause_duration=1.0)
|
||||
# self.mount_robot_rotator("gripper", max_angle=30.0, rotation_speed=20.0, pause_duration=0.5)
|
||||
|
||||
# 配置6:平滑旋转(无停顿)(取消注释以使用)
|
||||
# self.mount_smooth_rotator("wheel", max_angle=180.0, rotation_speed=90.0)
|
||||
|
||||
# 配置5:自定义挂载配置(取消注释并修改)
|
||||
# auto_mount_configs = [
|
||||
# ("your_model_name", "RotatorScript"), # 将RotatorScript挂载到指定模型
|
||||
# ("child_object_name", "MoverScript"), # 将MoverScript挂载到子物体
|
||||
# ("another_object", "ScalerScript"), # 将ScalerScript挂载到另一个对象
|
||||
# ]
|
||||
# if auto_mount_configs:
|
||||
# success_count = self.batch_mount_scripts(auto_mount_configs)
|
||||
# self.log(f"✓ 自定义挂载完成,成功挂载 {success_count} 个脚本")
|
||||
|
||||
# ==================== 配置区域结束 ====================
|
||||
|
||||
# 显示使用说明
|
||||
self.show_usage_examples()
|
||||
|
||||
# 列出已挂载的脚本
|
||||
self.list_mounted_scripts()
|
||||
|
||||
def mount_script_to_current_object(self, script_name):
|
||||
"""
|
||||
将脚本挂载到当前对象(即Rotate_P脚本所在的对象)
|
||||
|
||||
Args:
|
||||
script_name: 要挂载的脚本名称
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
if not self.gameObject:
|
||||
self.log("错误: 无法获取当前对象")
|
||||
return False
|
||||
|
||||
return self.mount_script_to_object(self.gameObject, script_name)
|
||||
|
||||
def auto_mount_to_all_children(self, script_name):
|
||||
"""
|
||||
自动将脚本挂载到当前对象的所有子物体
|
||||
|
||||
Args:
|
||||
script_name: 要挂载的脚本名称
|
||||
|
||||
Returns:
|
||||
int: 成功挂载的子物体数量
|
||||
"""
|
||||
if not self.gameObject:
|
||||
self.log("错误: 无法获取当前对象")
|
||||
return 0
|
||||
|
||||
# 获取所有子对象
|
||||
children = self.gameObject.getChildren()
|
||||
success_count = 0
|
||||
|
||||
self.log(f"开始为当前对象的 {len(children)} 个子物体挂载脚本 '{script_name}'")
|
||||
|
||||
for child in children:
|
||||
if self.mount_script_to_object(child, script_name):
|
||||
success_count += 1
|
||||
|
||||
self.log(f"✓ 成功为 {success_count}/{len(children)} 个子物体挂载脚本 '{script_name}'")
|
||||
return success_count
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
# 在这里编写更新逻辑
|
||||
pass
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("脚本被销毁")
|
||||
|
||||
# ==================== 脚本挂载功能 ====================
|
||||
|
||||
def mount_script_to_object(self, target_object, script_name, script_config=None):
|
||||
"""
|
||||
将脚本挂载到指定对象上
|
||||
|
||||
Args:
|
||||
target_object: 目标对象(Panda3D NodePath)
|
||||
script_name: 要挂载的脚本名称(字符串)
|
||||
script_config: 脚本配置参数(字典),可选
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
if not self.world:
|
||||
self.log("错误: 世界对象引用不可用")
|
||||
return False
|
||||
|
||||
try:
|
||||
# 使用世界对象的addScript方法挂载脚本
|
||||
success = self.world.addScript(target_object, script_name)
|
||||
|
||||
if success:
|
||||
# 记录挂载信息
|
||||
obj_name = target_object.getName()
|
||||
if obj_name not in self.mounted_scripts:
|
||||
self.mounted_scripts[obj_name] = []
|
||||
self.mounted_scripts[obj_name].append(script_name)
|
||||
|
||||
# 如果提供了配置参数,尝试配置脚本
|
||||
if script_config and script_name == "RotatorScript":
|
||||
self.configure_rotator_script(target_object, script_config)
|
||||
|
||||
self.log(f"✓ 成功将脚本 '{script_name}' 挂载到对象 '{obj_name}'")
|
||||
return True
|
||||
else:
|
||||
self.log(f"✗ 挂载脚本 '{script_name}' 失败")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"✗ 挂载脚本时出错: {str(e)}")
|
||||
return False
|
||||
|
||||
def configure_rotator_script(self, target_object, config):
|
||||
"""
|
||||
配置已挂载的RotatorScript脚本
|
||||
|
||||
Args:
|
||||
target_object: 目标对象
|
||||
config: 配置字典,可包含 max_angle, rotation_speed, robot_mode, pause_duration 等
|
||||
"""
|
||||
try:
|
||||
# 获取对象上的RotatorScript
|
||||
scripts = self.world.getScripts(target_object)
|
||||
for script_component in scripts:
|
||||
if script_component.script_name == "RotatorScript":
|
||||
script_instance = script_component.script_instance
|
||||
|
||||
# 配置最大角度
|
||||
if 'max_angle' in config:
|
||||
script_instance.set_max_angle(config['max_angle'])
|
||||
|
||||
# 配置旋转速度
|
||||
if 'rotation_speed' in config:
|
||||
script_instance.set_rotation_speed(config['rotation_speed'])
|
||||
|
||||
# 配置机器人模式
|
||||
if 'robot_mode' in config:
|
||||
script_instance.set_robot_mode(config['robot_mode'])
|
||||
|
||||
# 配置停顿时间
|
||||
if 'pause_duration' in config:
|
||||
script_instance.set_pause_duration(config['pause_duration'])
|
||||
|
||||
# 预设模式
|
||||
if 'preset_mode' in config:
|
||||
preset = config['preset_mode']
|
||||
if preset == 'slow_robot':
|
||||
script_instance.set_slow_robot_mode()
|
||||
elif preset == 'fast_robot':
|
||||
script_instance.set_fast_robot_mode()
|
||||
elif preset == 'smooth':
|
||||
script_instance.set_smooth_mode()
|
||||
|
||||
self.log(f"✓ 已配置 RotatorScript: {config}")
|
||||
break
|
||||
except Exception as e:
|
||||
self.log(f"✗ 配置 RotatorScript 时出错: {str(e)}")
|
||||
|
||||
def unmount_script_from_object(self, target_object, script_name):
|
||||
"""
|
||||
从指定对象卸载脚本
|
||||
|
||||
Args:
|
||||
target_object: 目标对象(Panda3D NodePath)
|
||||
script_name: 要卸载的脚本名称(字符串)
|
||||
|
||||
Returns:
|
||||
bool: 卸载是否成功
|
||||
"""
|
||||
if not self.world:
|
||||
self.log("错误: 世界对象引用不可用")
|
||||
return False
|
||||
|
||||
try:
|
||||
# 使用世界对象的removeScript方法卸载脚本
|
||||
success = self.world.removeScript(target_object, script_name)
|
||||
|
||||
if success:
|
||||
# 更新挂载记录
|
||||
obj_name = target_object.getName()
|
||||
if obj_name in self.mounted_scripts and script_name in self.mounted_scripts[obj_name]:
|
||||
self.mounted_scripts[obj_name].remove(script_name)
|
||||
if not self.mounted_scripts[obj_name]: # 如果列表为空,删除键
|
||||
del self.mounted_scripts[obj_name]
|
||||
|
||||
self.log(f"✓ 成功从对象 '{obj_name}' 卸载脚本 '{script_name}'")
|
||||
return True
|
||||
else:
|
||||
self.log(f"✗ 卸载脚本 '{script_name}' 失败")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"✗ 卸载脚本时出错: {str(e)}")
|
||||
return False
|
||||
|
||||
def mount_script_by_name(self, object_name, script_name):
|
||||
"""
|
||||
通过对象名称挂载脚本
|
||||
|
||||
Args:
|
||||
object_name: 目标对象名称(字符串)
|
||||
script_name: 要挂载的脚本名称(字符串)
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
if not self.world:
|
||||
self.log("错误: 世界对象引用不可用")
|
||||
return False
|
||||
|
||||
# 查找对象
|
||||
target_object = self.find_object_by_name(object_name)
|
||||
if not target_object:
|
||||
self.log(f"✗ 未找到名为 '{object_name}' 的对象")
|
||||
return False
|
||||
|
||||
return self.mount_script_to_object(target_object, script_name)
|
||||
|
||||
def find_object_by_name(self, object_name):
|
||||
"""
|
||||
通过名称查找对象
|
||||
|
||||
Args:
|
||||
object_name: 对象名称(字符串)
|
||||
|
||||
Returns:
|
||||
NodePath: 找到的对象,如果未找到则返回None
|
||||
"""
|
||||
if not self.world:
|
||||
return None
|
||||
|
||||
# 首先尝试使用世界的查找方法
|
||||
if hasattr(self.world, 'findModelByName'):
|
||||
obj = self.world.findModelByName(object_name)
|
||||
if obj:
|
||||
return obj
|
||||
|
||||
# 如果没有找到,尝试在render下搜索
|
||||
if hasattr(self.world, 'render'):
|
||||
return self.world.render.find(f"**/{object_name}")
|
||||
|
||||
return None
|
||||
|
||||
def get_available_scripts(self):
|
||||
"""
|
||||
获取所有可用的脚本列表
|
||||
|
||||
Returns:
|
||||
list: 可用脚本名称列表
|
||||
"""
|
||||
if not self.world:
|
||||
self.log("错误: 世界对象引用不可用")
|
||||
return []
|
||||
|
||||
try:
|
||||
return self.world.getAvailableScripts()
|
||||
except Exception as e:
|
||||
self.log(f"✗ 获取可用脚本列表时出错: {str(e)}")
|
||||
return []
|
||||
|
||||
def get_scripts_on_object(self, target_object):
|
||||
"""
|
||||
获取对象上已挂载的脚本
|
||||
|
||||
Args:
|
||||
target_object: 目标对象(Panda3D NodePath)
|
||||
|
||||
Returns:
|
||||
list: 脚本组件列表
|
||||
"""
|
||||
if not self.world:
|
||||
self.log("错误: 世界对象引用不可用")
|
||||
return []
|
||||
|
||||
try:
|
||||
return self.world.getScripts(target_object)
|
||||
except Exception as e:
|
||||
self.log(f"✗ 获取对象脚本时出错: {str(e)}")
|
||||
return []
|
||||
|
||||
def list_mounted_scripts(self):
|
||||
"""
|
||||
列出所有已挂载的脚本信息
|
||||
"""
|
||||
self.log("=== 已挂载脚本列表 ===")
|
||||
if not self.mounted_scripts:
|
||||
self.log("暂无已挂载的脚本")
|
||||
return
|
||||
|
||||
for obj_name, script_names in self.mounted_scripts.items():
|
||||
self.log(f"对象 '{obj_name}': {', '.join(script_names)}")
|
||||
|
||||
# ==================== 便捷方法 ====================
|
||||
|
||||
def mount_rotator_script(self, object_name):
|
||||
"""
|
||||
便捷方法:挂载旋转脚本到指定对象
|
||||
|
||||
Args:
|
||||
object_name: 目标对象名称
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
return self.mount_script_by_name(object_name, "RotatorScript")
|
||||
|
||||
def mount_mover_script(self, object_name):
|
||||
"""
|
||||
便捷方法:挂载移动脚本到指定对象
|
||||
|
||||
Args:
|
||||
object_name: 目标对象名称
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
return self.mount_script_by_name(object_name, "MoverScript")
|
||||
|
||||
def mount_scaler_script(self, object_name):
|
||||
"""
|
||||
便捷方法:挂载缩放脚本到指定对象
|
||||
|
||||
Args:
|
||||
object_name: 目标对象名称
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
return self.mount_script_by_name(object_name, "ScalerScript")
|
||||
|
||||
# ==================== 示例使用方法 ====================
|
||||
|
||||
def example_mount_scripts(self):
|
||||
"""
|
||||
示例:如何使用脚本挂载功能
|
||||
在start()方法中调用此方法来演示脚本挂载
|
||||
"""
|
||||
self.log("=== 脚本挂载示例 ===")
|
||||
|
||||
# 示例1:挂载旋转脚本到特定对象
|
||||
# self.mount_rotator_script("your_model_name")
|
||||
|
||||
# 示例2:挂载多个脚本到同一个对象
|
||||
# self.mount_script_by_name("your_model_name", "RotatorScript")
|
||||
# self.mount_script_by_name("your_model_name", "MoverScript")
|
||||
|
||||
# 示例3:查找并挂载脚本到子物体
|
||||
# child_object = self.find_object_by_name("child_object_name")
|
||||
# if child_object:
|
||||
# self.mount_script_to_object(child_object, "RotatorScript")
|
||||
|
||||
# 示例4:列出可用脚本
|
||||
available_scripts = self.get_available_scripts()
|
||||
self.log(f"可用脚本: {available_scripts}")
|
||||
|
||||
# 示例5:列出已挂载的脚本
|
||||
self.list_mounted_scripts()
|
||||
|
||||
def auto_mount_to_children(self, parent_object_name, script_name):
|
||||
"""
|
||||
自动将脚本挂载到指定父对象的所有子物体
|
||||
|
||||
Args:
|
||||
parent_object_name: 父对象名称
|
||||
script_name: 要挂载的脚本名称
|
||||
|
||||
Returns:
|
||||
int: 成功挂载的子物体数量
|
||||
"""
|
||||
if not self.world:
|
||||
self.log("错误: 世界对象引用不可用")
|
||||
return 0
|
||||
|
||||
# 查找父对象
|
||||
parent_object = self.find_object_by_name(parent_object_name)
|
||||
if not parent_object:
|
||||
self.log(f"✗ 未找到父对象 '{parent_object_name}'")
|
||||
return 0
|
||||
|
||||
# 获取所有子对象
|
||||
children = parent_object.getChildren()
|
||||
success_count = 0
|
||||
|
||||
for child in children:
|
||||
if self.mount_script_to_object(child, script_name):
|
||||
success_count += 1
|
||||
|
||||
self.log(f"✓ 成功为 {success_count}/{len(children)} 个子物体挂载脚本 '{script_name}'")
|
||||
return success_count
|
||||
|
||||
def batch_mount_scripts(self, object_script_pairs):
|
||||
"""
|
||||
批量挂载脚本
|
||||
|
||||
Args:
|
||||
object_script_pairs: 对象-脚本对列表,格式:[(object_name, script_name), ...]
|
||||
|
||||
Returns:
|
||||
int: 成功挂载的数量
|
||||
"""
|
||||
success_count = 0
|
||||
|
||||
for object_name, script_name in object_script_pairs:
|
||||
if self.mount_script_by_name(object_name, script_name):
|
||||
success_count += 1
|
||||
|
||||
self.log(f"✓ 批量挂载完成: {success_count}/{len(object_script_pairs)} 个脚本挂载成功")
|
||||
return success_count
|
||||
|
||||
# ==================== 预设配置方法 ====================
|
||||
|
||||
def setup_rotation_demo(self, max_angle=30.0, rotation_speed=30.0, robot_mode=True, pause_duration=0.5):
|
||||
"""
|
||||
预设配置:旋转演示
|
||||
为当前对象及其子物体挂载旋转脚本
|
||||
|
||||
Args:
|
||||
max_angle: 最大旋转角度(相对于初始角度)
|
||||
rotation_speed: 旋转速度(度/秒)
|
||||
robot_mode: 是否启用机器人模式
|
||||
pause_duration: 停顿时间(秒)
|
||||
"""
|
||||
self.log("=== 设置旋转演示 ===")
|
||||
|
||||
# 配置参数
|
||||
config = {
|
||||
'max_angle': max_angle,
|
||||
'rotation_speed': rotation_speed,
|
||||
'robot_mode': robot_mode,
|
||||
'pause_duration': pause_duration
|
||||
}
|
||||
|
||||
# 为当前对象挂载旋转脚本
|
||||
self.mount_script_to_current_object_with_config("RotatorScript", config)
|
||||
|
||||
# 为所有子物体挂载旋转脚本
|
||||
self.auto_mount_to_all_children_with_config("RotatorScript", config)
|
||||
|
||||
def setup_animation_combo(self):
|
||||
"""
|
||||
预设配置:动画组合
|
||||
为对象挂载多种动画脚本
|
||||
"""
|
||||
self.log("=== 设置动画组合 ===")
|
||||
|
||||
# 为当前对象挂载多个脚本
|
||||
scripts_to_mount = ["RotatorScript", "MoverScript", "ScalerScript"]
|
||||
|
||||
for script_name in scripts_to_mount:
|
||||
self.mount_script_to_current_object(script_name)
|
||||
|
||||
def setup_child_animation(self, script_name="RotatorScript"):
|
||||
"""
|
||||
预设配置:子物体动画
|
||||
只为子物体挂载脚本,不影响父对象
|
||||
|
||||
Args:
|
||||
script_name: 要挂载的脚本名称,默认为RotatorScript
|
||||
"""
|
||||
self.log(f"=== 设置子物体动画 ({script_name}) ===")
|
||||
self.auto_mount_to_all_children(script_name)
|
||||
|
||||
def setup_specific_objects(self, object_names, script_name="RotatorScript"):
|
||||
"""
|
||||
预设配置:为指定的对象列表挂载脚本
|
||||
|
||||
Args:
|
||||
object_names: 对象名称列表
|
||||
script_name: 要挂载的脚本名称
|
||||
"""
|
||||
self.log(f"=== 为指定对象挂载脚本 ({script_name}) ===")
|
||||
|
||||
for obj_name in object_names:
|
||||
self.mount_script_by_name(obj_name, script_name)
|
||||
|
||||
def setup_robot_arm_demo(self):
|
||||
"""
|
||||
预设配置:机器人手臂演示
|
||||
为不同关节设置不同的机器人旋转参数
|
||||
"""
|
||||
self.log("=== 设置机器人手臂演示 ===")
|
||||
|
||||
# 为当前对象设置慢速机器人模式
|
||||
config = {'preset_mode': 'slow_robot'}
|
||||
self.mount_script_to_current_object_with_config("RotatorScript", config)
|
||||
|
||||
# 为子物体设置快速机器人模式
|
||||
config = {'preset_mode': 'fast_robot'}
|
||||
self.auto_mount_to_all_children_with_config("RotatorScript", config)
|
||||
|
||||
def setup_industrial_robot_demo(self):
|
||||
"""
|
||||
预设配置:工业机器人演示
|
||||
模拟工业机器人的精确停顿动作
|
||||
"""
|
||||
self.log("=== 设置工业机器人演示 ===")
|
||||
|
||||
# 工业机器人配置:慢速、长停顿
|
||||
config = {
|
||||
'max_angle': 45.0,
|
||||
'rotation_speed': 15.0,
|
||||
'robot_mode': True,
|
||||
'pause_duration': 1.2
|
||||
}
|
||||
|
||||
self.mount_script_to_current_object_with_config("RotatorScript", config)
|
||||
self.auto_mount_to_all_children_with_config("RotatorScript", config)
|
||||
|
||||
def setup_servo_motor_demo(self):
|
||||
"""
|
||||
预设配置:伺服电机演示
|
||||
模拟伺服电机的快速精确定位
|
||||
"""
|
||||
self.log("=== 设置伺服电机演示 ===")
|
||||
|
||||
# 伺服电机配置:快速、短停顿
|
||||
config = {
|
||||
'max_angle': 60.0,
|
||||
'rotation_speed': 50.0,
|
||||
'robot_mode': True,
|
||||
'pause_duration': 0.2
|
||||
}
|
||||
|
||||
self.mount_script_to_current_object_with_config("RotatorScript", config)
|
||||
self.auto_mount_to_all_children_with_config("RotatorScript", config)
|
||||
|
||||
# ==================== 使用示例和说明 ====================
|
||||
|
||||
def show_usage_examples(self):
|
||||
"""
|
||||
显示使用示例和说明
|
||||
"""
|
||||
self.log("=== Rotate_P 脚本使用说明 ===")
|
||||
self.log("此脚本被挂载时会自动执行脚本挂载操作")
|
||||
self.log("")
|
||||
self.log("可用的预设配置方法:")
|
||||
self.log("1. setup_rotation_demo() - 为当前对象和子物体挂载旋转脚本")
|
||||
self.log("2. setup_animation_combo() - 为当前对象挂载多种动画脚本")
|
||||
self.log("3. setup_child_animation() - 只为子物体挂载脚本")
|
||||
self.log("4. setup_specific_objects() - 为指定对象挂载脚本")
|
||||
self.log("")
|
||||
self.log("修改 auto_mount_scripts_on_start() 方法来自定义自动挂载行为")
|
||||
self.log("=== 使用说明结束 ===")
|
||||
|
||||
def get_current_object_info(self):
|
||||
"""
|
||||
获取当前对象的详细信息
|
||||
"""
|
||||
if not self.gameObject:
|
||||
self.log("错误: 无法获取当前对象")
|
||||
return
|
||||
|
||||
obj_name = self.gameObject.getName()
|
||||
children = self.gameObject.getChildren()
|
||||
child_names = [child.getName() for child in children]
|
||||
|
||||
self.log("=== 当前对象信息 ===")
|
||||
self.log(f"对象名称: {obj_name}")
|
||||
self.log(f"子物体数量: {len(children)}")
|
||||
if child_names:
|
||||
self.log(f"子物体名称: {', '.join(child_names)}")
|
||||
else:
|
||||
self.log("无子物体")
|
||||
self.log("=== 对象信息结束 ===")
|
||||
|
||||
# ==================== 带配置的便捷方法 ====================
|
||||
|
||||
def mount_script_to_current_object_with_config(self, script_name, config=None):
|
||||
"""
|
||||
将脚本挂载到当前对象并配置参数
|
||||
|
||||
Args:
|
||||
script_name: 脚本名称
|
||||
config: 配置参数字典
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
if not self.gameObject:
|
||||
self.log("错误: 无法获取当前对象")
|
||||
return False
|
||||
|
||||
return self.mount_script_to_object(self.gameObject, script_name, config)
|
||||
|
||||
def auto_mount_to_all_children_with_config(self, script_name, config=None):
|
||||
"""
|
||||
自动将脚本挂载到当前对象的所有子物体并配置参数
|
||||
|
||||
Args:
|
||||
script_name: 要挂载的脚本名称
|
||||
config: 配置参数字典
|
||||
|
||||
Returns:
|
||||
int: 成功挂载的子物体数量
|
||||
"""
|
||||
if not self.gameObject:
|
||||
self.log("错误: 无法获取当前对象")
|
||||
return 0
|
||||
|
||||
# 获取所有子对象
|
||||
children = self.gameObject.getChildren()
|
||||
success_count = 0
|
||||
|
||||
self.log(f"开始为当前对象的 {len(children)} 个子物体挂载脚本 '{script_name}'")
|
||||
if config:
|
||||
self.log(f"使用配置: {config}")
|
||||
|
||||
for child in children:
|
||||
if self.mount_script_to_object(child, script_name, config):
|
||||
success_count += 1
|
||||
|
||||
self.log(f"✓ 成功为 {success_count}/{len(children)} 个子物体挂载脚本 '{script_name}'")
|
||||
return success_count
|
||||
|
||||
def mount_rotator_with_custom_angle(self, object_name, max_angle=45.0, rotation_speed=20.0, robot_mode=True, pause_duration=0.5):
|
||||
"""
|
||||
便捷方法:挂载自定义角度的旋转脚本
|
||||
|
||||
Args:
|
||||
object_name: 目标对象名称
|
||||
max_angle: 最大旋转角度
|
||||
rotation_speed: 旋转速度
|
||||
robot_mode: 是否启用机器人模式
|
||||
pause_duration: 停顿时间(秒)
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
config = {
|
||||
'max_angle': max_angle,
|
||||
'rotation_speed': rotation_speed,
|
||||
'robot_mode': robot_mode,
|
||||
'pause_duration': pause_duration
|
||||
}
|
||||
|
||||
target_object = self.find_object_by_name(object_name)
|
||||
if not target_object:
|
||||
self.log(f"✗ 未找到名为 '{object_name}' 的对象")
|
||||
return False
|
||||
|
||||
return self.mount_script_to_object(target_object, "RotatorScript", config)
|
||||
|
||||
def mount_robot_rotator(self, object_name, max_angle=30.0, rotation_speed=20.0, pause_duration=0.8):
|
||||
"""
|
||||
便捷方法:挂载机器人式旋转脚本
|
||||
|
||||
Args:
|
||||
object_name: 目标对象名称
|
||||
max_angle: 最大旋转角度
|
||||
rotation_speed: 旋转速度
|
||||
pause_duration: 停顿时间
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
return self.mount_rotator_with_custom_angle(object_name, max_angle, rotation_speed, True, pause_duration)
|
||||
|
||||
def mount_smooth_rotator(self, object_name, max_angle=45.0, rotation_speed=30.0):
|
||||
"""
|
||||
便捷方法:挂载平滑旋转脚本(无停顿)
|
||||
|
||||
Args:
|
||||
object_name: 目标对象名称
|
||||
max_angle: 最大旋转角度
|
||||
rotation_speed: 旋转速度
|
||||
|
||||
Returns:
|
||||
bool: 挂载是否成功
|
||||
"""
|
||||
return self.mount_rotator_with_custom_angle(object_name, max_angle, rotation_speed, False, 0.0)
|
||||
215
scripts/Rotate_P_Script.py
Normal file
215
scripts/Rotate_P_Script.py
Normal file
@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 25.0 # 最大旋转角度(相对于初始角度)
|
||||
self.direction = 1
|
||||
self.current_offset = 0.0 # 当前相对于初始角度的偏移
|
||||
self.initial_angle = None # 模型的初始角度
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
# 机器人式停顿参数
|
||||
self.pause_duration = 0.5 # 停顿时间(秒)
|
||||
self.current_pause_time = 0.0 # 当前停顿计时
|
||||
self.is_paused = False # 是否正在停顿
|
||||
self.robot_mode = True # 是否启用机器人模式
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("机器人旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"最大旋转角度: ±{self.max_angle}度")
|
||||
self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}")
|
||||
if self.robot_mode:
|
||||
self.log(f"停顿时间: {self.pause_duration}秒")
|
||||
|
||||
# 记录模型的初始角度
|
||||
if self.gameObject:
|
||||
initial_hpr = self.gameObject.getHpr()
|
||||
self.initial_angle = initial_hpr.getY() # 记录Y轴的初始角度(Pitch)
|
||||
self.log(f"模型初始角度: {self.initial_angle}度")
|
||||
self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
else:
|
||||
self.log("⚠️ 无法获取游戏对象,使用默认初始角度0")
|
||||
self.initial_angle = 0.0
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新 - 机器人式旋转"""
|
||||
if not self.is_rotating or self.initial_angle is None:
|
||||
return
|
||||
|
||||
# 如果正在停顿中
|
||||
if self.is_paused:
|
||||
self.current_pause_time += dt
|
||||
if self.current_pause_time >= self.pause_duration:
|
||||
# 停顿结束,继续旋转
|
||||
self.is_paused = False
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"停顿结束,继续旋转,方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
return
|
||||
|
||||
# 计算角度变化量
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_offset += delta_angle
|
||||
|
||||
# 检查是否到达边界
|
||||
reached_boundary = False
|
||||
if self.current_offset > self.max_angle:
|
||||
self.current_offset = self.max_angle
|
||||
self.direction *= -1
|
||||
reached_boundary = True
|
||||
elif self.current_offset < -self.max_angle:
|
||||
self.current_offset = -self.max_angle
|
||||
self.direction *= -1
|
||||
reached_boundary = True
|
||||
|
||||
# 如果到达边界且启用机器人模式,开始停顿
|
||||
if reached_boundary and self.robot_mode:
|
||||
self.is_paused = True
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"到达边界 ({self.current_offset}°),开始停顿 {self.pause_duration}秒")
|
||||
|
||||
# 计算最终角度(初始角度 + 偏移量)
|
||||
final_angle = self.initial_angle + self.current_offset
|
||||
|
||||
# 设置新的旋转(只改变Y轴,保持其他不变)
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), final_angle, current_hpr.getZ())
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("机器人旋转脚本停止")
|
||||
|
||||
# ==================== 机器人模式控制方法 ====================
|
||||
|
||||
def set_robot_mode(self, enabled=True):
|
||||
"""
|
||||
启用或禁用机器人模式
|
||||
|
||||
Args:
|
||||
enabled: 是否启用机器人模式
|
||||
"""
|
||||
self.robot_mode = enabled
|
||||
self.log(f"机器人模式: {'开启' if enabled else '关闭'}")
|
||||
if not enabled:
|
||||
self.is_paused = False # 如果禁用机器人模式,立即结束停顿
|
||||
|
||||
def set_pause_duration(self, duration):
|
||||
"""
|
||||
设置停顿时间
|
||||
|
||||
Args:
|
||||
duration: 停顿时间(秒)
|
||||
"""
|
||||
self.pause_duration = max(0.1, duration) # 最小0.1秒
|
||||
self.log(f"停顿时间已设置为: {self.pause_duration}秒")
|
||||
|
||||
def set_max_angle(self, new_max_angle):
|
||||
"""
|
||||
设置新的最大旋转角度
|
||||
|
||||
Args:
|
||||
new_max_angle: 新的最大角度值
|
||||
"""
|
||||
self.max_angle = new_max_angle
|
||||
self.log(f"最大旋转角度已设置为: ±{self.max_angle}度")
|
||||
if self.initial_angle is not None:
|
||||
self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°")
|
||||
|
||||
def set_rotation_speed(self, new_speed):
|
||||
"""
|
||||
设置新的旋转速度
|
||||
|
||||
Args:
|
||||
new_speed: 新的旋转速度(度/秒)
|
||||
"""
|
||||
self.rotation_speed_y = new_speed
|
||||
self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def pause_rotation(self):
|
||||
"""暂停旋转"""
|
||||
self.is_rotating = False
|
||||
self.log("旋转已暂停")
|
||||
|
||||
def resume_rotation(self):
|
||||
"""恢复旋转"""
|
||||
self.is_rotating = True
|
||||
self.is_paused = False # 同时结束停顿状态
|
||||
self.log("旋转已恢复")
|
||||
|
||||
def reset_to_initial_angle(self):
|
||||
"""重置到初始角度"""
|
||||
if self.initial_angle is not None and self.gameObject:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
self.gameObject.setHpr(current_hpr.getX(), self.initial_angle, current_hpr.getZ())
|
||||
self.current_offset = 0.0
|
||||
self.direction = 1
|
||||
self.is_paused = False
|
||||
self.current_pause_time = 0.0
|
||||
self.log(f"已重置到初始角度: {self.initial_angle}度")
|
||||
|
||||
def get_current_info(self):
|
||||
"""获取当前旋转信息"""
|
||||
if self.gameObject and self.initial_angle is not None:
|
||||
current_hpr = self.gameObject.getHpr()
|
||||
current_angle = current_hpr.getY()
|
||||
self.log("=== 机器人旋转信息 ===")
|
||||
self.log(f"初始角度: {self.initial_angle}度")
|
||||
self.log(f"当前角度: {current_angle}度")
|
||||
self.log(f"偏移量: {self.current_offset}度")
|
||||
self.log(f"最大角度: ±{self.max_angle}度")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}")
|
||||
self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}")
|
||||
self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}")
|
||||
if self.robot_mode:
|
||||
self.log(f"停顿时间: {self.pause_duration}秒")
|
||||
self.log(f"当前状态: {'停顿中' if self.is_paused else '旋转中'}")
|
||||
if self.is_paused:
|
||||
remaining_time = self.pause_duration - self.current_pause_time
|
||||
self.log(f"剩余停顿时间: {remaining_time:.1f}秒")
|
||||
self.log("=== 信息结束 ===")
|
||||
else:
|
||||
self.log("无法获取旋转信息")
|
||||
|
||||
# ==================== 预设配置方法 ====================
|
||||
|
||||
def set_slow_robot_mode(self):
|
||||
"""预设:慢速机器人模式"""
|
||||
self.set_rotation_speed(15.0)
|
||||
self.set_pause_duration(1.0)
|
||||
self.set_robot_mode(True)
|
||||
self.log("已设置为慢速机器人模式")
|
||||
|
||||
def set_fast_robot_mode(self):
|
||||
"""预设:快速机器人模式"""
|
||||
self.set_rotation_speed(45.0)
|
||||
self.set_pause_duration(0.3)
|
||||
self.set_robot_mode(True)
|
||||
self.log("已设置为快速机器人模式")
|
||||
|
||||
def set_smooth_mode(self):
|
||||
"""预设:平滑模式(非机器人)"""
|
||||
self.set_robot_mode(False)
|
||||
self.set_rotation_speed(30.0)
|
||||
self.log("已设置为平滑旋转模式")
|
||||
56
scripts/Rotate_R_Script.py
Normal file
56
scripts/Rotate_R_Script.py
Normal file
@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
旋转脚本 - 让对象持续旋转
|
||||
"""
|
||||
|
||||
from core.script_system import ScriptBase
|
||||
|
||||
|
||||
class RotatorScript(ScriptBase):
|
||||
"""旋转脚本类"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒)
|
||||
self.max_angle = 15.0
|
||||
self.direction = 1
|
||||
self.current_angle = 0.0
|
||||
self.is_rotating = True # 是否正在旋转
|
||||
|
||||
def start(self):
|
||||
"""脚本开始时调用"""
|
||||
self.log("旋转脚本启动!")
|
||||
self.log(f"旋转速度: {self.rotation_speed_y}度/秒")
|
||||
|
||||
def update(self, dt):
|
||||
"""每帧更新"""
|
||||
|
||||
delta_angle = self.rotation_speed_y * dt * self.direction
|
||||
self.current_angle += delta_angle
|
||||
|
||||
# 如果超出角度范围,则反向
|
||||
if self.current_angle > self.max_angle:
|
||||
self.current_angle = self.max_angle
|
||||
self.direction *= -1
|
||||
elif self.current_angle < -self.max_angle:
|
||||
self.current_angle = -self.max_angle
|
||||
self.direction *= -1
|
||||
|
||||
# 设置新的旋转(只改变Z轴,保持其他不变)
|
||||
base_hpr = self.gameObject.getHpr()
|
||||
new_r = self.current_angle
|
||||
self.gameObject.setHpr(base_hpr.getX(), base_hpr.getY(), new_r)
|
||||
|
||||
# if not self.is_rotating:
|
||||
# return
|
||||
#
|
||||
# # 获取当前旋转并应用增量
|
||||
# current_hpr = self.gameObject.getHpr()
|
||||
# new_r = current_hpr.getZ() + self.rotation_speed_y * dt
|
||||
# self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r)
|
||||
|
||||
def on_destroy(self):
|
||||
"""脚本销毁时调用"""
|
||||
self.log("旋转脚本停止")
|
||||
BIN
scripts/__pycache__/BouncerScript.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/BouncerScript.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/ColorChangerScript.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/ColorChangerScript.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/ComboAnimatorScript.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/ComboAnimatorScript.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/FollowerScript.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/FollowerScript.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/MoverScript.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/MoverScript.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/R_P.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/R_P.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/R_R.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/R_R.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/Rotate_H_Script.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/Rotate_H_Script.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/Rotate_P.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/Rotate_P.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/Rotate_P_Script.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/Rotate_P_Script.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/Rotate_R_Script.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/Rotate_R_Script.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/RotatorScript.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/RotatorScript.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/ScalerScript.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/ScalerScript.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/TestMover.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/TestMover.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/TestRotator.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/TestRotator.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/TestScaler.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/TestScaler.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/example_script.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/example_script.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scripts/__pycache__/test_quick_script.cpython-312.pyc
Normal file
BIN
scripts/__pycache__/test_quick_script.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -5,6 +5,7 @@ from typing import Hashable
|
||||
from PyQt5.QtWidgets import (QLabel, QLineEdit, QDoubleSpinBox, QPushButton,
|
||||
QTreeWidget, QTreeWidgetItem, QMenu,QCheckBox)
|
||||
from PyQt5.QtCore import Qt
|
||||
from deploy_libs.unicodedata import normalize
|
||||
from panda3d.core import Vec3, Vec4, transpose
|
||||
|
||||
|
||||
@ -175,6 +176,8 @@ class PropertyPanelManager:
|
||||
zScale.valueChanged.connect(lambda v: model.setScale(model.getScale().getX(), model.getScale().getY(), v))
|
||||
self._propertyLayout.addRow("缩放 Z:", zScale)
|
||||
|
||||
self._addSunAzimuthPanel()
|
||||
|
||||
material_title = QLabel("材质属性")
|
||||
material_title.setStyleSheet("color: #FF6B6B;font-weight:bold;font-size:14px;margin-top:10px;")
|
||||
self._propertyLayout.addRow(material_title)
|
||||
@ -398,10 +401,10 @@ class PropertyPanelManager:
|
||||
fovSpinBox.valueChanged.connect(lambda v:self._updateLightFOV(light_object,v))
|
||||
self._propertyLayout.addRow("视野角度:",fovSpinBox)
|
||||
|
||||
shadowCheckBox = QCheckBox()
|
||||
shadowCheckBox.setChecked(light_object.casts_shadows)
|
||||
shadowCheckBox.stateChanged.connect(lambda state:self._updateLightCastsShadows(light_object,state==2))
|
||||
self._propertyLayout.addRow("投射阴影:",shadowCheckBox)
|
||||
# shadowCheckBox = QCheckBox()
|
||||
# shadowCheckBox.setChecked(light_object.casts_shadows)
|
||||
# shadowCheckBox.stateChanged.connect(lambda state:self._updateLightCastsShadows(light_object,state==2))
|
||||
# self._propertyLayout.addRow("投射阴影:",shadowCheckBox)
|
||||
|
||||
current_scale = model.getScale()
|
||||
|
||||
@ -862,6 +865,10 @@ class PropertyPanelManager:
|
||||
self._addMaterialPresetPanel(material)
|
||||
#self._addColorSpacePanel(material)
|
||||
|
||||
# 添加太阳方位角控制面板(只在第一个材质时添加,避免重复)
|
||||
# if i == 0:
|
||||
# self._addSunAzimuthPanel()
|
||||
|
||||
|
||||
# 分隔线
|
||||
if i < len(materials) - 1:
|
||||
@ -1260,22 +1267,35 @@ class PropertyPanelManager:
|
||||
if node and material:
|
||||
print(f"正在为节点 {node.getName()} 应用漫反射贴图")
|
||||
|
||||
# 确保启用PBR效果(简单可靠版本)
|
||||
# 检查是否有金属性贴图,选择合适的PBR效果
|
||||
print("🔧 检查金属性贴图并选择合适的PBR效果...")
|
||||
has_metallic = self._hasMetallicTexture(node)
|
||||
needs_alpha = self._needsAlphaTesting(node)
|
||||
|
||||
if has_metallic:
|
||||
print("✅ 检测到金属性贴图,使用支持金属性的PBR效果")
|
||||
effect_file = "effects/pbr_with_metallic.yaml"
|
||||
else:
|
||||
print("✅ 没有金属性贴图,使用默认PBR效果")
|
||||
effect_file = "effects/default.yaml"
|
||||
|
||||
print(f"🔍 透明度测试: {'需要' if needs_alpha else '不需要'}")
|
||||
|
||||
try:
|
||||
self.world.render_pipeline.set_effect(
|
||||
node,
|
||||
"effects/default.yaml",
|
||||
effect_file,
|
||||
{
|
||||
"normal_mapping": True, # 启用法线映射支持
|
||||
"render_gbuffer": True,
|
||||
"alpha_testing": True, # 启用透明度测试支持透明贴图
|
||||
"alpha_testing": needs_alpha, # 根据是否需要透明度决定
|
||||
"parallax_mapping": False,
|
||||
"render_shadow": True,
|
||||
"render_envmap": True
|
||||
},
|
||||
100
|
||||
)
|
||||
print("✅ 默认PBR效果已应用(支持透明度)")
|
||||
print(f"✅ {effect_file} 效果已应用(支持透明度和金属性)")
|
||||
except Exception as e:
|
||||
print(f"⚠️ PBR效果应用失败: {e}")
|
||||
|
||||
@ -1378,23 +1398,35 @@ class PropertyPanelManager:
|
||||
print("⚠️ 没有漫反射贴图,创建白色纹理确保法线映射正常工作...")
|
||||
self._createWhiteDiffuseTexture(node)
|
||||
|
||||
# 使用默认PBR效果,强制启用法线映射
|
||||
print("🔧 应用默认PBR效果(强制启用法线映射)...")
|
||||
# 检查是否有金属性贴图,选择合适的PBR效果
|
||||
print("🔧 检查金属性贴图并选择合适的PBR效果...")
|
||||
has_metallic = self._hasMetallicTexture(node)
|
||||
needs_alpha = self._needsAlphaTesting(node)
|
||||
|
||||
if has_metallic:
|
||||
print("✅ 检测到金属性贴图,使用支持金属性的PBR效果")
|
||||
effect_file = "effects/pbr_with_metallic.yaml"
|
||||
else:
|
||||
print("✅ 没有金属性贴图,使用默认PBR效果")
|
||||
effect_file = "effects/default.yaml"
|
||||
|
||||
print(f"🔍 透明度测试: {'需要' if needs_alpha else '不需要'}")
|
||||
|
||||
try:
|
||||
self.world.render_pipeline.set_effect(
|
||||
node,
|
||||
"effects/default.yaml",
|
||||
effect_file,
|
||||
{
|
||||
"normal_mapping": True, # 强制启用法线映射
|
||||
"render_gbuffer": True,
|
||||
"alpha_testing": False,
|
||||
"alpha_testing": needs_alpha, # 根据是否需要透明度决定
|
||||
"parallax_mapping": False,
|
||||
"render_shadow": True,
|
||||
"render_envmap": True
|
||||
},
|
||||
100
|
||||
)
|
||||
print("✅ 默认PBR效果已应用(法线映射已启用)")
|
||||
print(f"✅ {effect_file} 效果已应用(法线映射已启用)")
|
||||
except Exception as e:
|
||||
print(f"⚠️ PBR效果应用失败: {e}")
|
||||
|
||||
@ -3427,3 +3459,452 @@ class PropertyPanelManager:
|
||||
except Exception as e:
|
||||
print(f"⚠️ 检查透明度需求失败: {e}")
|
||||
return True # 出错时默认启用透明度测试,更安全
|
||||
|
||||
def _addSunAzimuthPanel(self):
|
||||
"""添加太阳方位角控制面板"""
|
||||
try:
|
||||
from PyQt5.QtWidgets import QLabel, QSlider, QSpinBox, QHBoxLayout, QWidget
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
# 添加分隔线
|
||||
separator = QLabel("─" * 30)
|
||||
separator.setStyleSheet("color: lightgray;")
|
||||
self._propertyLayout.addRow(separator)
|
||||
|
||||
# 标题
|
||||
title_label = QLabel("☀️ 太阳光照控制")
|
||||
title_label.setStyleSheet("font-weight: bold; color: #FFA500; font-size: 12px;")
|
||||
self._propertyLayout.addRow(title_label)
|
||||
|
||||
# 太阳方位角控制
|
||||
azimuth_widget = QWidget()
|
||||
azimuth_layout = QHBoxLayout(azimuth_widget)
|
||||
azimuth_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# 滑块控制 (0-360度)
|
||||
self.sun_azimuth_slider = QSlider(Qt.Horizontal)
|
||||
self.sun_azimuth_slider.setMinimum(0)
|
||||
self.sun_azimuth_slider.setMaximum(360)
|
||||
self.sun_azimuth_slider.setValue(180) # 默认值180度
|
||||
self.sun_azimuth_slider.setTickPosition(QSlider.TicksBelow)
|
||||
self.sun_azimuth_slider.setTickInterval(45) # 每45度一个刻度
|
||||
|
||||
# 数值显示框
|
||||
self.sun_azimuth_spinbox = QSpinBox()
|
||||
self.sun_azimuth_spinbox.setMinimum(0)
|
||||
self.sun_azimuth_spinbox.setMaximum(360)
|
||||
self.sun_azimuth_spinbox.setValue(180)
|
||||
self.sun_azimuth_spinbox.setSuffix("°")
|
||||
self.sun_azimuth_spinbox.setFixedWidth(80)
|
||||
|
||||
# 连接信号
|
||||
self.sun_azimuth_slider.valueChanged.connect(self._onSunAzimuthSliderChanged)
|
||||
self.sun_azimuth_spinbox.valueChanged.connect(self._onSunAzimuthSpinboxChanged)
|
||||
|
||||
azimuth_layout.addWidget(self.sun_azimuth_slider)
|
||||
azimuth_layout.addWidget(self.sun_azimuth_spinbox)
|
||||
|
||||
self._propertyLayout.addRow("太阳方位角:", azimuth_widget)
|
||||
|
||||
# 太阳高度角控制
|
||||
altitude_widget = QWidget()
|
||||
altitude_layout = QHBoxLayout(altitude_widget)
|
||||
altitude_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# 滑块控制 (0-90度)
|
||||
self.sun_altitude_slider = QSlider(Qt.Horizontal)
|
||||
self.sun_altitude_slider.setMinimum(0)
|
||||
self.sun_altitude_slider.setMaximum(90)
|
||||
self.sun_altitude_slider.setValue(45) # 默认值45度
|
||||
self.sun_altitude_slider.setTickPosition(QSlider.TicksBelow)
|
||||
self.sun_altitude_slider.setTickInterval(15) # 每15度一个刻度
|
||||
|
||||
# 数值显示框
|
||||
self.sun_altitude_spinbox = QSpinBox()
|
||||
self.sun_altitude_spinbox.setMinimum(0)
|
||||
self.sun_altitude_spinbox.setMaximum(90)
|
||||
self.sun_altitude_spinbox.setValue(45)
|
||||
self.sun_altitude_spinbox.setSuffix("°")
|
||||
self.sun_altitude_spinbox.setFixedWidth(80)
|
||||
|
||||
# 连接信号
|
||||
self.sun_altitude_slider.valueChanged.connect(self._onSunAltitudeSliderChanged)
|
||||
self.sun_altitude_spinbox.valueChanged.connect(self._onSunAltitudeSpinboxChanged)
|
||||
|
||||
altitude_layout.addWidget(self.sun_altitude_slider)
|
||||
altitude_layout.addWidget(self.sun_altitude_spinbox)
|
||||
|
||||
self._propertyLayout.addRow("太阳高度角:", altitude_widget)
|
||||
|
||||
# 添加预设按钮
|
||||
preset_widget = QWidget()
|
||||
preset_layout = QHBoxLayout(preset_widget)
|
||||
preset_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# 预设按钮
|
||||
sunrise_btn = QPushButton("🌅 日出")
|
||||
sunrise_btn.setFixedWidth(60)
|
||||
sunrise_btn.clicked.connect(lambda: self._setSunPreset("sunrise"))
|
||||
|
||||
noon_btn = QPushButton("☀️ 正午")
|
||||
noon_btn.setFixedWidth(60)
|
||||
noon_btn.clicked.connect(lambda: self._setSunPreset("noon"))
|
||||
|
||||
sunset_btn = QPushButton("🌇 日落")
|
||||
sunset_btn.setFixedWidth(60)
|
||||
sunset_btn.clicked.connect(lambda: self._setSunPreset("sunset"))
|
||||
|
||||
midnight_btn = QPushButton("🌙 午夜")
|
||||
midnight_btn.setFixedWidth(60)
|
||||
midnight_btn.clicked.connect(lambda: self._setSunPreset("midnight"))
|
||||
|
||||
preset_layout.addWidget(sunrise_btn)
|
||||
preset_layout.addWidget(noon_btn)
|
||||
preset_layout.addWidget(sunset_btn)
|
||||
preset_layout.addWidget(midnight_btn)
|
||||
|
||||
self._propertyLayout.addRow("快速设置:", preset_widget)
|
||||
|
||||
print("✅ 太阳控制面板已添加")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 添加太阳方位角控制面板失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _onSunAzimuthSliderChanged(self, value):
|
||||
"""滑块值改变时的回调"""
|
||||
try:
|
||||
# 同步到数值框
|
||||
self.sun_azimuth_spinbox.blockSignals(True)
|
||||
self.sun_azimuth_spinbox.setValue(value)
|
||||
self.sun_azimuth_spinbox.blockSignals(False)
|
||||
|
||||
# 应用到Day Time Editor
|
||||
self._applySunAzimuth_new(value)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 滑块值改变处理失败: {e}")
|
||||
|
||||
def _onSunAzimuthSpinboxChanged(self, value):
|
||||
"""数值框值改变时的回调"""
|
||||
try:
|
||||
# 同步到滑块
|
||||
self.sun_azimuth_slider.blockSignals(True)
|
||||
self.sun_azimuth_slider.setValue(value)
|
||||
self.sun_azimuth_slider.blockSignals(False)
|
||||
|
||||
# 应用到Day Time Editor
|
||||
self._applySunAzimuth_new(value)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 数值框值改变处理失败: {e}")
|
||||
|
||||
def _onSunAltitudeSliderChanged(self, value):
|
||||
"""太阳高度角滑块值改变时的回调"""
|
||||
try:
|
||||
# 同步到数值框
|
||||
self.sun_altitude_spinbox.blockSignals(True)
|
||||
self.sun_altitude_spinbox.setValue(value)
|
||||
self.sun_altitude_spinbox.blockSignals(False)
|
||||
|
||||
# 应用太阳高度角
|
||||
self._applySunAltitude(value)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 太阳高度角滑块值改变处理失败: {e}")
|
||||
|
||||
def _onSunAltitudeSpinboxChanged(self, value):
|
||||
"""太阳高度角数值框值改变时的回调"""
|
||||
try:
|
||||
# 同步到滑块
|
||||
self.sun_altitude_slider.blockSignals(True)
|
||||
self.sun_altitude_slider.setValue(value)
|
||||
self.sun_altitude_slider.blockSignals(False)
|
||||
|
||||
# 应用太阳高度角
|
||||
self._applySunAltitude(value)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 太阳高度角数值框值改变处理失败: {e}")
|
||||
|
||||
def _setSunPreset(self, preset_name):
|
||||
"""设置太阳预设位置"""
|
||||
try:
|
||||
presets = {
|
||||
"sunrise": (90, 45), # 东方,低角度
|
||||
"noon": (180, 90), # 南方,天顶
|
||||
"sunset": (270, 45), # 西方,低角度
|
||||
"midnight": (0, 0) # 北方,地平线
|
||||
}
|
||||
|
||||
if preset_name in presets:
|
||||
azimuth, altitude = presets[preset_name]
|
||||
|
||||
# 更新滑块和数值框
|
||||
self.sun_azimuth_slider.blockSignals(True)
|
||||
self.sun_azimuth_spinbox.blockSignals(True)
|
||||
self.sun_altitude_slider.blockSignals(True)
|
||||
self.sun_altitude_spinbox.blockSignals(True)
|
||||
|
||||
self.sun_azimuth_slider.setValue(azimuth)
|
||||
self.sun_azimuth_spinbox.setValue(azimuth)
|
||||
self.sun_altitude_slider.setValue(altitude)
|
||||
self.sun_altitude_spinbox.setValue(altitude)
|
||||
|
||||
self.sun_azimuth_slider.blockSignals(False)
|
||||
self.sun_azimuth_spinbox.blockSignals(False)
|
||||
self.sun_altitude_slider.blockSignals(False)
|
||||
self.sun_altitude_spinbox.blockSignals(False)
|
||||
|
||||
# 应用设置 - 优先使用Day Time Editor
|
||||
azimuth_success = self._updateDayTimeEditorSetting("scattering", "sun_azimuth", azimuth)
|
||||
altitude_success = self._updateDayTimeEditorSetting("scattering", "sun_altitude", altitude)
|
||||
|
||||
if azimuth_success and altitude_success:
|
||||
print(f"✅ 通过Day Time Editor设置太阳预设 {preset_name}: 方位角{azimuth}°, 高度角{altitude}°")
|
||||
elif hasattr(self.world, 'sun_controller'):
|
||||
self.world.sun_controller.set_sun_position(azimuth, altitude)
|
||||
print(f"✅ 通过太阳控制器设置预设 {preset_name}: 方位角{azimuth}°, 高度角{altitude}°")
|
||||
elif hasattr(self.world, 'setSunPosition'):
|
||||
self.world.setSunPosition(azimuth, altitude)
|
||||
print(f"✅ 通过主程序方法设置预设 {preset_name}: 方位角{azimuth}°, 高度角{altitude}°")
|
||||
else:
|
||||
# 分别应用方位角和高度角
|
||||
self._applySunAzimuth_new(azimuth)
|
||||
self._applySunAltitude(altitude)
|
||||
print(f"✅ 设置太阳预设 {preset_name}: 方位角{azimuth}°, 高度角{altitude}°")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 设置太阳预设失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _applySunAzimuth_new(self, azimuth_value):
|
||||
"""应用太阳方位角 - 直接控制Day Time Editor"""
|
||||
try:
|
||||
# 方法1: 直接控制Day Time Editor中的sun_azimuth节点
|
||||
success = self._updateDayTimeEditorSetting("scattering", "sun_azimuth", azimuth_value)
|
||||
if success:
|
||||
print(f"✅ 通过Day Time Editor设置方位角: {azimuth_value}°")
|
||||
return
|
||||
|
||||
# 方法2: 使用我们的太阳控制器作为备用
|
||||
if hasattr(self.world, 'sun_controller'):
|
||||
self.world.sun_controller.set_sun_azimuth(azimuth_value)
|
||||
print(f"✅ 通过太阳控制器设置方位角: {azimuth_value}°")
|
||||
return
|
||||
|
||||
# 方法3: 直接调用主程序的太阳控制方法
|
||||
if hasattr(self.world, 'setSunAzimuth'):
|
||||
self.world.setSunAzimuth(azimuth_value)
|
||||
print(f"✅ 通过主程序方法设置方位角: {azimuth_value}°")
|
||||
return
|
||||
|
||||
print("⚠️ 所有方位角设置方法都不可用")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 应用太阳方位角失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _applySunAltitude(self, altitude_value):
|
||||
"""应用太阳高度角 - 直接控制Day Time Editor"""
|
||||
try:
|
||||
# 方法1: 直接控制Day Time Editor中的sun_altitude节点
|
||||
success = self._updateDayTimeEditorSetting("scattering", "sun_altitude", altitude_value)
|
||||
if success:
|
||||
print(f"✅ 通过Day Time Editor设置高度角: {altitude_value}°")
|
||||
return
|
||||
|
||||
# 方法2: 使用我们的太阳控制器作为备用
|
||||
if hasattr(self.world, 'sun_controller'):
|
||||
self.world.sun_controller.set_sun_altitude(altitude_value)
|
||||
print(f"✅ 通过太阳控制器设置高度角: {altitude_value}°")
|
||||
return
|
||||
|
||||
# 方法3: 直接调用主程序的太阳控制方法
|
||||
if hasattr(self.world, 'setSunAltitude'):
|
||||
self.world.setSunAltitude(altitude_value)
|
||||
print(f"✅ 通过主程序方法设置高度角: {altitude_value}°")
|
||||
return
|
||||
|
||||
print("⚠️ 所有高度角设置方法都不可用")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 应用太阳高度角失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _updateDayTimeEditorSetting(self, plugin_name, setting_name, value):
|
||||
"""直接更新Day Time Editor中的设置节点值"""
|
||||
try:
|
||||
# 检查是否有RenderPipeline和插件管理器
|
||||
if not hasattr(self.world, 'render_pipeline') or not self.world.render_pipeline:
|
||||
print("⚠️ RenderPipeline未初始化")
|
||||
return False
|
||||
|
||||
pipeline = self.world.render_pipeline
|
||||
if not hasattr(pipeline, 'plugin_mgr') or not pipeline.plugin_mgr:
|
||||
print("⚠️ 插件管理器未初始化")
|
||||
return False
|
||||
|
||||
plugin_mgr = pipeline.plugin_mgr
|
||||
|
||||
# 检查插件是否存在
|
||||
if plugin_name not in plugin_mgr.instances:
|
||||
print(f"⚠️ 插件 '{plugin_name}' 不存在")
|
||||
return False
|
||||
|
||||
# 检查Day Time设置是否存在
|
||||
if plugin_name not in plugin_mgr.day_settings:
|
||||
print(f"⚠️ 插件 '{plugin_name}' 没有Day Time设置")
|
||||
return False
|
||||
|
||||
day_settings = plugin_mgr.day_settings[plugin_name]
|
||||
if setting_name not in day_settings:
|
||||
print(f"⚠️ 设置 '{setting_name}' 在插件 '{plugin_name}' 中不存在")
|
||||
return False
|
||||
|
||||
setting_handle = day_settings[setting_name]
|
||||
|
||||
# 根据设置类型转换值
|
||||
if setting_name == "sun_azimuth":
|
||||
# 方位角:度数转换为0-1范围
|
||||
normalized_value = (value % 360) / 360.0
|
||||
elif setting_name == "sun_altitude":
|
||||
# 高度角:度数转换为0-1范围
|
||||
normalized_value = max(0, min(90, value)) / 90.0
|
||||
else:
|
||||
# 其他设置直接使用原值
|
||||
normalized_value = value
|
||||
|
||||
# 获取当前时间(如果Day Time Editor正在运行)
|
||||
current_time = getattr(self, '_current_daytime', 0.5) # 默认中午12点
|
||||
|
||||
# 更新设置的曲线值
|
||||
if hasattr(setting_handle, 'curves') and setting_handle.curves:
|
||||
# 清除现有的控制点,设置单一值
|
||||
setting_handle.curves[0].set_single_value(normalized_value)
|
||||
print(f"✅ 更新Day Time设置: {plugin_name}.{setting_name} = {value} (归一化: {normalized_value:.3f})")
|
||||
|
||||
# 保存设置到配置文件
|
||||
try:
|
||||
plugin_mgr.save_daytime_overrides("/$$rpconfig/daytime.yaml")
|
||||
print("✅ Day Time设置已保存到配置文件")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 保存配置文件失败: {e}")
|
||||
|
||||
# 通知Day Time Editor重新加载配置(如果正在运行)
|
||||
try:
|
||||
from RenderPipelineFile.rpcore.util.network_communication import NetworkCommunication
|
||||
NetworkCommunication.send_async(NetworkCommunication.DAYTIME_PORT, "loadconf")
|
||||
print("✅ 已通知Day Time Editor重新加载配置")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 通知Day Time Editor失败: {e}")
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"⚠️ 设置 '{setting_name}' 没有可用的曲线")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 更新Day Time Editor设置失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def _applySunAzimuth(self, azimuth_degrees):
|
||||
"""应用太阳方位角到Day Time Editor中的Sun Azimuth节点"""
|
||||
try:
|
||||
if not hasattr(self, 'world') or not self.world:
|
||||
print("⚠️ World对象不存在")
|
||||
return
|
||||
|
||||
# Day Time Editor是独立进程,需要通过进程间通信控制
|
||||
print(f"🌞 尝试控制Day Time Editor中的Sun Azimuth: {azimuth_degrees}°")
|
||||
|
||||
# 方法1:通过文件通信
|
||||
success = self._sendSunAzimuthViaFile(azimuth_degrees)
|
||||
if success:
|
||||
return
|
||||
|
||||
# 方法2:尝试直接控制RenderPipeline的daytime_mgr(如果存在)
|
||||
# success = self._controlLocalDaytimeManager(azimuth_degrees)
|
||||
# if success:
|
||||
# return
|
||||
|
||||
# 方法3:通过socket通信(如果实现了)
|
||||
# success = self._sendSunAzimuthViaSocket(azimuth_degrees)
|
||||
# if success:
|
||||
# return
|
||||
|
||||
print("⚠️ 无法控制Day Time Editor中的Sun Azimuth节点")
|
||||
print(" 原因:Day Time Editor运行在独立进程中")
|
||||
print(" 建议:")
|
||||
print(" 1. 直接在Day Time Editor窗口中调整Sun Azimuth")
|
||||
print(" 2. 或者实现进程间通信机制")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 应用太阳方位角失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def _sendSunAzimuthViaFile(self, azimuth_degrees):
|
||||
"""通过文件通信发送Sun Azimuth值到Day Time Editor"""
|
||||
try:
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
# 创建通信文件
|
||||
comm_dir = os.path.join(tempfile.gettempdir(), "daytime_editor_comm")
|
||||
os.makedirs(comm_dir, exist_ok=True)
|
||||
|
||||
comm_file = os.path.join(comm_dir, "sun_azimuth_command.json")
|
||||
|
||||
command = {
|
||||
"command": "set_sun_azimuth",
|
||||
"value": azimuth_degrees,
|
||||
"timestamp": __import__('time').time()
|
||||
}
|
||||
|
||||
with open(comm_file, 'w') as f:
|
||||
json.dump(command, f)
|
||||
|
||||
print(f"📁 已通过文件发送Sun Azimuth命令: {azimuth_degrees}°")
|
||||
print(f" 通信文件: {comm_file}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 文件通信失败: {e}")
|
||||
return False
|
||||
|
||||
# def _controlLocalDaytimeManager(self, azimuth_degrees):
|
||||
# """尝试控制本地的RenderPipeline daytime_mgr"""
|
||||
# try:
|
||||
# # 检查RenderPipeline的daytime_mgr
|
||||
# if hasattr(self.world, 'render_pipeline') and hasattr(self.world.render_pipeline, 'daytime_mgr'):
|
||||
# daytime_mgr = self.world.render_pipeline.daytime_mgr
|
||||
# print("✅ 找到 render_pipeline.daytime_mgr")
|
||||
#
|
||||
# # 尝试设置sun_azimuth
|
||||
# if hasattr(daytime_mgr, 'sun_azimuth'):
|
||||
# daytime_mgr.sun_azimuth = azimuth_degrees
|
||||
# print(f"☀️ 本地Sun Azimuth已设置为: {azimuth_degrees}°")
|
||||
# return True
|
||||
# elif hasattr(daytime_mgr, 'set_sun_azimuth'):
|
||||
# daytime_mgr.set_sun_azimuth(azimuth_degrees)
|
||||
# print(f"☀️ 本地Sun Azimuth已设置为: {azimuth_degrees}°")
|
||||
# return True
|
||||
# else:
|
||||
# print("🔍 本地daytime_mgr可用属性:")
|
||||
# for attr in dir(daytime_mgr):
|
||||
# if 'sun' in attr.lower() or 'azimuth' in attr.lower():
|
||||
# print(f" • {attr}")
|
||||
#
|
||||
# return False
|
||||
#
|
||||
# except Exception as e:
|
||||
# print(f"⚠️ 控制本地daytime_mgr失败: {e}")
|
||||
# return False
|
||||
|
||||
Loading…
Reference in New Issue
Block a user