227 lines
6.8 KiB
Python
227 lines
6.8 KiB
Python
"""
|
|
VR Effects Manager - 为VR场景中的模型自动应用RenderPipeline Effects
|
|
|
|
主要功能:
|
|
1. 扫描VR场景中的所有模型节点
|
|
2. 自动应用RenderPipeline的默认effect配置
|
|
3. 设置正确的shader tags以确保模型能正确渲染到GBuffer
|
|
4. 支持批量应用和单个模型应用
|
|
"""
|
|
|
|
from panda3d.core import NodePath, GeomNode
|
|
|
|
|
|
class VREffectsManager:
|
|
"""VR场景的RenderPipeline Effects管理器"""
|
|
|
|
def __init__(self, pipeline):
|
|
"""
|
|
初始化VR Effects Manager
|
|
|
|
Args:
|
|
pipeline: RenderPipeline实例
|
|
"""
|
|
self.pipeline = pipeline
|
|
self.applied_models = set() # 记录已应用effects的模型
|
|
|
|
# 默认的effect配置
|
|
self.default_effect_config = {
|
|
"normal_mapping": True, # 法线贴图
|
|
"render_gbuffer": True, # 渲染到GBuffer
|
|
"alpha_testing": False, # Alpha测试
|
|
"parallax_mapping": False, # 视差贴图(性能考虑,VR中默认关闭)
|
|
"render_shadow": True, # 投射阴影
|
|
"render_envmap": True, # 环境映射
|
|
}
|
|
|
|
def apply_effects_to_scene(self, scene_root):
|
|
"""
|
|
为场景根节点下的所有模型应用effects
|
|
|
|
Args:
|
|
scene_root: 场景根节点(通常是render或其子节点)
|
|
|
|
Returns:
|
|
int: 应用effects的模型数量
|
|
"""
|
|
if not self.pipeline:
|
|
print("⚠️ RenderPipeline未初始化,无法应用effects")
|
|
return 0
|
|
|
|
print("🎨 开始为VR场景应用RenderPipeline Effects...")
|
|
count = 0
|
|
|
|
# 递归遍历所有子节点
|
|
for node in scene_root.findAllMatches("**/+GeomNode"):
|
|
if self._should_apply_effect(node):
|
|
if self.apply_effect_to_model(node):
|
|
count += 1
|
|
|
|
print(f"✅ 为 {count} 个模型应用了RenderPipeline Effects")
|
|
return count
|
|
|
|
def apply_effect_to_model(self, model_node, effect_config=None):
|
|
"""
|
|
为单个模型节点应用RenderPipeline effect
|
|
|
|
Args:
|
|
model_node: 模型的NodePath
|
|
effect_config: 自定义effect配置(可选,默认使用default_effect_config)
|
|
|
|
Returns:
|
|
bool: 是否成功应用
|
|
"""
|
|
if not isinstance(model_node, NodePath):
|
|
print(f"⚠️ 无效的模型节点: {model_node}")
|
|
return False
|
|
|
|
# 使用提供的配置或默认配置
|
|
config = effect_config or self.default_effect_config
|
|
|
|
try:
|
|
# 应用RenderPipeline effect
|
|
self.pipeline.set_effect(
|
|
model_node,
|
|
"effects/default.yaml",
|
|
config,
|
|
sort=50 # 默认排序值
|
|
)
|
|
|
|
# 设置RenderPipeline shader tag
|
|
# 这个tag告诉RenderPipeline这个模型需要通过GBuffer渲染
|
|
model_node.setTag("RenderPipeline", "1")
|
|
|
|
# 递归设置所有子节点的tag
|
|
for child in model_node.findAllMatches("**/+GeomNode"):
|
|
child.setTag("RenderPipeline", "1")
|
|
|
|
# 记录已应用的模型
|
|
model_id = id(model_node.node())
|
|
self.applied_models.add(model_id)
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"❌ 应用effect失败: {model_node.getName()} - {e}")
|
|
return False
|
|
|
|
def apply_effect_to_model_simple(self, model_node):
|
|
"""
|
|
为模型应用简化版effect(只设置tags,不调用set_effect)
|
|
|
|
适用于某些特殊情况,例如:
|
|
- 模型已经有自定义shader
|
|
- 只需要确保模型能渲染到GBuffer
|
|
- 避免覆盖现有的shader配置
|
|
|
|
Args:
|
|
model_node: 模型的NodePath
|
|
|
|
Returns:
|
|
bool: 是否成功
|
|
"""
|
|
try:
|
|
model_node.setTag("RenderPipeline", "1")
|
|
|
|
# 递归设置所有子节点
|
|
for child in model_node.findAllMatches("**/+GeomNode"):
|
|
child.setTag("RenderPipeline", "1")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"❌ 设置tags失败: {model_node.getName()} - {e}")
|
|
return False
|
|
|
|
def _should_apply_effect(self, node):
|
|
"""
|
|
判断是否应该为节点应用effect
|
|
|
|
Args:
|
|
node: 待检查的节点
|
|
|
|
Returns:
|
|
bool: 是否应该应用effect
|
|
"""
|
|
# 跳过已应用的模型
|
|
model_id = id(node.node())
|
|
if model_id in self.applied_models:
|
|
return False
|
|
|
|
# 跳过没有几何数据的节点
|
|
if not node.node().getNumGeoms() > 0:
|
|
return False
|
|
|
|
# 跳过特殊标记的节点(例如:UI元素、调试几何体等)
|
|
# 检查节点是否有"skip_pipeline"标签
|
|
if node.hasTag("skip_pipeline"):
|
|
return False
|
|
|
|
# 跳过隐藏的节点
|
|
if node.isHidden():
|
|
return False
|
|
|
|
return True
|
|
|
|
def apply_effects_to_new_models(self, model_list):
|
|
"""
|
|
为新添加的模型列表批量应用effects
|
|
|
|
Args:
|
|
model_list: 模型NodePath列表
|
|
|
|
Returns:
|
|
int: 成功应用effects的模型数量
|
|
"""
|
|
count = 0
|
|
for model in model_list:
|
|
if self.apply_effect_to_model(model):
|
|
count += 1
|
|
|
|
print(f"✅ 为 {count}/{len(model_list)} 个新模型应用了effects")
|
|
return count
|
|
|
|
def update_effect_config(self, new_config):
|
|
"""
|
|
更新默认effect配置
|
|
|
|
Args:
|
|
new_config: 新的配置字典(会合并到现有配置)
|
|
"""
|
|
self.default_effect_config.update(new_config)
|
|
print(f"✓ 更新effect配置: {new_config}")
|
|
|
|
def get_applied_models_count(self):
|
|
"""获取已应用effects的模型数量"""
|
|
return len(self.applied_models)
|
|
|
|
def clear_applied_models_cache(self):
|
|
"""清空已应用模型的缓存(用于场景重置等情况)"""
|
|
self.applied_models.clear()
|
|
print("✓ 已清空effects应用缓存")
|
|
|
|
|
|
def setup_vr_model_effects(world, vr_root=None):
|
|
"""
|
|
便捷函数:为VR场景设置RenderPipeline Effects
|
|
|
|
Args:
|
|
world: CoreWorld实例(需要有render_pipeline属性)
|
|
vr_root: VR场景根节点(可选,默认使用world.render)
|
|
|
|
Returns:
|
|
VREffectsManager实例,或None(如果失败)
|
|
"""
|
|
if not hasattr(world, 'render_pipeline') or not world.render_pipeline:
|
|
print("⚠️ RenderPipeline未初始化")
|
|
return None
|
|
|
|
# 创建Effects Manager
|
|
effects_mgr = VREffectsManager(world.render_pipeline)
|
|
|
|
# 应用effects到场景
|
|
scene_root = vr_root or world.render
|
|
effects_mgr.apply_effects_to_scene(scene_root)
|
|
|
|
return effects_mgr
|