""" 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