Combines three powerful stereoscopic tools into one comprehensive package: 1. Core Stereo Generation - Depth-based stereo conversion with GPU acceleration - Advanced fill techniques (Polylines, GPU Warp, Hybrid Edge, etc.) - Edge-aware depth blurring for artifact reduction - Side-by-Side, Top-Bottom, and Red-Cyan Anaglyph output formats 2. Native VR Viewing (Optional - requires PyOpenXR) - Auto-launch directly into VR headset without browser - Support for all VR headsets (Quest, Vive, Index, WMR) - Multiple projection types (Flat, Curved, 180° Dome, 360° Sphere) - Image and video playback support 3. StereoDiffusion AI (Optional - requires diffusers) - AI-powered stereo generation using diffusion models - DDIM inversion with null-text optimization - Bilateral Neighbor Attention for stereo consistency - Works with ComfyUI MODEL/CLIP/VAE or HuggingFace models Changes: - Added native_viewer/ module with PyOpenXR implementation - Added native_nodes.py with 3 VR viewer nodes - Added stereodiffusion_nodes.py with AI stereo generation - Added supporting modules: model_wrappers, model_loader, inversion, stereo_utils, diffusion_utils - Updated __init__.py with graceful optional dependency handling - Consolidated all requirements into single requirements.txt - Comprehensive README with setup guides and troubleshooting - Added NOTICE and licenses/ for proper attribution The package now offers 6+ nodes total with modular installation options. Users can install just the base, or add VR viewing and/or AI generation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
109 lines
5.1 KiB
Python
109 lines
5.1 KiB
Python
"""
|
|
GLFW context provider for OpenXR with visible window
|
|
"""
|
|
|
|
from .constants import PYOPENXR_AVAILABLE
|
|
|
|
if PYOPENXR_AVAILABLE:
|
|
from xr.utils.gl.glfw_util import GLFWOffscreenContextProvider
|
|
import glfw
|
|
|
|
class GLFWVisibleContextProvider(GLFWOffscreenContextProvider):
|
|
"""
|
|
GLFW context provider that creates a VISIBLE window for keyboard input.
|
|
Extends the offscreen provider but overrides window creation.
|
|
"""
|
|
def __init__(self):
|
|
# print("🔍 [Context] Starting GLFWVisibleContextProvider.__init__")
|
|
|
|
# Don't call parent __init__ - we'll do our own initialization
|
|
# Initialize GLFW if not already done
|
|
# print("🔍 [Context] Calling glfw.init()...")
|
|
init_result = glfw.init()
|
|
# print(f"🔍 [Context] glfw.init() returned: {init_result}")
|
|
if not init_result:
|
|
raise RuntimeError("Failed to initialize GLFW")
|
|
|
|
# Set window hints for visible window
|
|
# print("🔍 [Context] Setting window hints...")
|
|
glfw.window_hint(glfw.VISIBLE, True) # Make window VISIBLE (different from parent)
|
|
glfw.window_hint(glfw.DOUBLEBUFFER, False)
|
|
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
|
|
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 1)
|
|
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
|
|
glfw.window_hint(glfw.RESIZABLE, False)
|
|
glfw.window_hint(glfw.FLOATING, True) # Keep on top
|
|
|
|
# Create a small visible window for controls
|
|
# print("🔍 [Context] Creating GLFW window...")
|
|
self._window = glfw.create_window(400, 300, "VR Video Controls", None, None)
|
|
# print(f"🔍 [Context] Window created: {self._window}")
|
|
if not self._window:
|
|
glfw.terminate()
|
|
raise RuntimeError("Failed to create visible GLFW window")
|
|
|
|
# Make context current
|
|
# print("🔍 [Context] Making context current...")
|
|
glfw.make_context_current(self._window)
|
|
current_context = glfw.get_current_context()
|
|
# print(f"🔍 [Context] Current context after make_current: {current_context}")
|
|
# print(f"🔍 [Context] Context matches window: {current_context == self._window}")
|
|
glfw.swap_interval(0)
|
|
# print("🔍 [Context] GLFWVisibleContextProvider.__init__ complete")
|
|
|
|
def make_current(self) -> None:
|
|
"""Activate this OpenGL context for subsequent GL calls."""
|
|
# print(f"🔍 [Context] make_current() called for window: {self._window}")
|
|
if not self._window:
|
|
# print("🔍 [Context] ⚠️ WARNING: Window is None in make_current()!")
|
|
raise RuntimeError("Cannot make context current - window is None")
|
|
glfw.make_context_current(self._window)
|
|
current = glfw.get_current_context()
|
|
# print(f"🔍 [Context] Context after make_current: {current}")
|
|
|
|
def done_current(self) -> None:
|
|
"""Unbind this context from the current thread."""
|
|
# print("🔍 [Context] done_current() called")
|
|
glfw.make_context_current(None)
|
|
|
|
def destroy(self) -> None:
|
|
"""Tear down the window (called by OpenGLGraphics.destroy())"""
|
|
# print("🔍 [Context] destroy() called")
|
|
if self._window:
|
|
glfw.destroy_window(self._window)
|
|
self._window = None
|
|
# DON'T terminate GLFW here - let __exit__ do that
|
|
|
|
def __enter__(self):
|
|
# print("🔍 [Context] __enter__ called")
|
|
current_context = glfw.get_current_context()
|
|
# print(f"🔍 [Context] Current context in __enter__: {current_context}")
|
|
# print(f"🔍 [Context] Context matches window: {current_context == self._window}")
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
# print("🔍 [Context] __exit__ called")
|
|
if self._window:
|
|
# print(f"🔍 [Context] Window exists: {self._window}")
|
|
current_context = glfw.get_current_context()
|
|
# print(f"🔍 [Context] Current context before unbind: {current_context}")
|
|
|
|
# Unbind context before destroying window
|
|
# print("🔍 [Context] Unbinding context...")
|
|
glfw.make_context_current(None)
|
|
current_context = glfw.get_current_context()
|
|
# print(f"🔍 [Context] Current context after unbind: {current_context}")
|
|
|
|
# print("🔍 [Context] Destroying window...")
|
|
glfw.destroy_window(self._window)
|
|
self._window = None
|
|
# print("🔍 [Context] Window destroyed")
|
|
|
|
# Terminate GLFW to ensure clean state for next run
|
|
# print("🔍 [Context] Terminating GLFW...")
|
|
glfw.terminate()
|
|
# print("🔍 [Context] GLFW terminated - clean slate for next run")
|
|
else:
|
|
# Stub class when PyOpenXR is not available
|
|
GLFWVisibleContextProvider = None
|