diff --git a/core/vr_manager.py b/core/vr_manager.py index dbd9f59d..f16cb340 100644 --- a/core/vr_manager.py +++ b/core/vr_manager.py @@ -92,6 +92,9 @@ class VRManager(DirectObject): # VR提交策略 - 基于参考实现 self.submit_together = True # 是否在right_cb中同时提交左右眼 + # 帧同步标记 - 修复ATW闪烁 + self._poses_updated_this_frame = False + # VR手柄控制器 self.left_controller = None self.right_controller = None @@ -389,20 +392,16 @@ class VRManager(DirectObject): self.last_fps_time = current_time # 优化的VR更新顺序: - # 1. 立即调用 waitGetPoses 获取最新的姿态数据 - # 这确保我们使用最新数据而不是上一帧的数据 - self._wait_get_poses() + # 注意:WaitGetPoses现在在渲染回调中调用,避免ATW双重预测 + # 这里仅更新非关键的跟踪设备和交互系统 - # 2. 更新相机位置(使用刚获取的最新姿态数据) - self._update_camera_poses() - - # 3. 更新手柄和其他跟踪设备 + # 1. 更新手柄和其他跟踪设备 self.update_tracked_devices() - # 4. 更新VR动作状态 + # 2. 更新VR动作状态 self.action_manager.update_actions() - # 5. 更新VR交互系统 + # 3. 更新VR交互系统 self.interaction_manager.update() # 注意:纹理提交现在通过渲染回调自动处理 @@ -466,6 +465,59 @@ class VRManager(DirectObject): # 记录姿态失败次数 self.pose_failures += 1 + def _wait_get_poses_immediate(self): + """立即获取VR姿态 - 修复ATW闪烁的关键方法""" + try: + if not self.vr_compositor or not self.poses: + return + + # 使用0预测时间来避免双重预测,立即获取当前姿态 + # 这是修复ATW闪烁的关键:在渲染前立即获取,避免时序错误 + result = self.vr_compositor.waitGetPoses(self.poses, None) + + # 检查姿态数据的有效性 + valid_poses = 0 + + # 更新HMD姿态(设备0通常是头显) + if len(self.poses) > 0 and self.poses[0].bPoseIsValid: + valid_poses += 1 + else: + # 如果HMD姿态无效,不要频繁输出错误信息 + if not hasattr(self, '_hmd_invalid_warning_shown'): + print("⚠️ HMD姿态数据无效(立即模式)") + self._hmd_invalid_warning_shown = True + + # 更新控制器姿态 + self.controller_poses.clear() + for device_id in range(1, min(len(self.poses), openvr.k_unMaxTrackedDeviceCount)): + if self.poses[device_id].bPoseIsValid: + device_class = self.vr_system.getTrackedDeviceClass(device_id) + if device_class == openvr.TrackedDeviceClass_Controller: + controller_matrix = self.poses[device_id].mDeviceToAbsoluteTracking + self.controller_poses[device_id] = self._convert_openvr_matrix_to_panda(controller_matrix) + valid_poses += 1 + + # 调试信息 - 仅在第一次成功时输出 + if not hasattr(self, '_immediate_mode_logged') and valid_poses > 0: + print(f"✅ 立即姿态获取模式启用 - 有效姿态数: {valid_poses}") + self._immediate_mode_logged = True + + except Exception as e: + # 限制错误输出频率 + if not hasattr(self, '_last_immediate_error_frame'): + self._last_immediate_error_frame = 0 + + if self.frame_count - self._last_immediate_error_frame > 300: # 每5秒最多输出一次错误 + print(f"立即姿态获取失败: {e}") + self._last_immediate_error_frame = self.frame_count + + self.pose_failures += 1 + + def _reset_frame_flag(self, task): + """重置帧标记 - 确保下一帧可以更新姿态""" + self._poses_updated_this_frame = False + return task.done + def _update_tracking_data(self): """更新VR追踪数据""" try: @@ -840,26 +892,53 @@ class VRManager(DirectObject): print("========================") def left_cb(self, cbdata): - """左眼渲染回调 - 基于参考实现""" - # 执行实际的渲染工作 - cbdata.upcall() - # 根据提交策略决定是否立即提交 - if not self.submit_together: - # 分别提交模式:左眼渲染完成后立即提交 - self.submit_texture(openvr.Eye_Left, self.vr_left_texture) + """左眼渲染回调 - 修复ATW闪烁问题""" + try: + # 在渲染开始前立即获取最新姿态,避免ATW双重预测 + if not hasattr(self, '_poses_updated_this_frame') or not self._poses_updated_this_frame: + self._wait_get_poses_immediate() + self._update_camera_poses() + self._poses_updated_this_frame = True + # 在帧结束时重置标记 + self.world.taskMgr.doMethodLater(0.001, self._reset_frame_flag, "reset_frame_flag") + + # 执行实际的渲染工作 + cbdata.upcall() + + # 根据提交策略决定是否立即提交 + if not self.submit_together: + # 分别提交模式:左眼渲染完成后立即提交 + self.submit_texture(openvr.Eye_Left, self.vr_left_texture) + + except Exception as e: + print(f"左眼渲染回调错误: {e}") def right_cb(self, cbdata): - """右眼渲染回调 - 基于参考实现""" - # 执行实际的渲染工作 - cbdata.upcall() - # 根据提交策略决定提交方式 - if self.submit_together: - # 同时提交模式:右眼渲染完成后同时提交左右眼 - self.submit_texture(openvr.Eye_Left, self.vr_left_texture) - self.submit_texture(openvr.Eye_Right, self.vr_right_texture) - else: - # 分别提交模式:只提交右眼 - self.submit_texture(openvr.Eye_Right, self.vr_right_texture) + """右眼渲染回调 - 修复ATW闪烁问题""" + try: + # 在渲染开始前立即获取最新姿态,避免ATW双重预测 + # 由于左右眼都会调用回调,确保每帧只调用一次WaitGetPoses + if not hasattr(self, '_poses_updated_this_frame') or not self._poses_updated_this_frame: + self._wait_get_poses_immediate() + self._update_camera_poses() + self._poses_updated_this_frame = True + # 在帧结束时重置标记 + self.world.taskMgr.doMethodLater(0.001, self._reset_frame_flag, "reset_frame_flag") + + # 执行实际的渲染工作 + cbdata.upcall() + + # 根据提交策略决定提交方式 + if self.submit_together: + # 同时提交模式:右眼渲染完成后同时提交左右眼 + self.submit_texture(openvr.Eye_Left, self.vr_left_texture) + self.submit_texture(openvr.Eye_Right, self.vr_right_texture) + else: + # 分别提交模式:只提交右眼 + self.submit_texture(openvr.Eye_Right, self.vr_right_texture) + + except Exception as e: + print(f"右眼渲染回调错误: {e}") def submit_texture(self, eye, texture): """提交纹理到VR - 基于参考实现,增强调试信息""" diff --git a/参考文件/panda3d-openvr b/参考文件/panda3d-openvr new file mode 160000 index 00000000..3f956789 --- /dev/null +++ b/参考文件/panda3d-openvr @@ -0,0 +1 @@ +Subproject commit 3f9567897552df6c10078bc124795101cf478f91