## Context The application is a USD Layer Manager built with ImGui (docking enabled) and OpenGL on Windows (WGL). The existing OpenGL context is initialized with `#version 130` (GLSL) and a legacy WGL pixel format (32-bit color, 24-bit depth, 8-bit stencil). The current "Viewport" panel in `Application::RenderUI()` is a placeholder displaying static text. The OpenUSD v25.08 SDK is available with UsdGeom and UsdShade APIs for scene traversal. The CMake build system uses `GLOB_RECURSE` for source collection, so new `.cpp`/`.h` files under `src/` are automatically compiled. ## Goals / Non-Goals **Goals:** - Render USD mesh geometry in the existing Viewport ImGui panel using OpenGL - Provide interactive orbit/pan/zoom camera control via mouse input captured from the viewport region - Render a ground grid and axis gizmo for spatial orientation - Use FBO-based rendering so the 3D output composites cleanly with the ImGui docking layout - Support basic materials (diffuse color from UsdShade or primvar color) for visual differentiation **Non-Goals:** - Hydra / HdRenderer integration (deferred — requires significant additional infrastructure) - Advanced shading (PBR, transparency, shadows, ambient occlusion) - Selection highlighting or picking (future change) - Animation playback - Multi-viewport or offscreen rendering to file ## Decisions ### 1. FBO-to-ImGui-Image rendering pipeline **Decision**: Render the USD scene to an OpenGL Framebuffer Object, then display the attached color texture via `ImGui::Image()`. **Rationale**: The ImGui docking system controls window layouts. Direct `glViewport` calls over the entire framebuffer would conflict with ImGui's rendering. FBO rendering produces a texture that ImGui can composite at the correct position and size. This is the standard pattern for 3D viewports in ImGui applications. **Alternative considered**: Render directly to the default framebuffer with `glViewport` positioned over the viewport panel area. Rejected because it requires manual z-ordering with ImGui draw calls and breaks when docked windows overlap. ### 2. Direct UsdGeom traversal instead of Hydra **Decision**: Traverse the UsdStage prim tree, extract `UsdGeomMesh` points/faceVertexCounts/faceVertexIndices/normals, and upload to OpenGL VBOs/VAOs for drawing. **Rationale**: Hydra requires creating an `HdEngine`, `HdRenderDelegate`, and `HdRenderPass` infrastructure. For an initial viewport that draws meshes with basic color, direct traversal is simpler, has fewer dependencies, and gives us full control over the draw loop. Hydra can be integrated later as a modular swap. **Alternative considered**: Use `UsdImagingGL` for immediate rendering. Rejected because UsdImagingGL is deprecated in favor of Hydra in recent USD versions and pulls in heavy imaging dependencies. ### 3. Camera model: Orbit camera with arcball control **Decision**: Implement a turntable-style orbit camera. Left-drag orbits around a focal point, middle-drag pans, scroll-wheel zooms. **Rationale**: Turntable cameras are the standard for DCC applications (Maya, Blender's default). Users familiar with 3D tools expect this interaction model. The camera stores: eye position, focal point, up vector, and computes a view matrix. **Alternative considered**: Arcball camera (trackball rotation). More flexible but less predictable for typical CAD/DCC workflows. Can be added as an option later. ### 4. OpenGL GLSL #version 130 compatibility **Decision**: Use GLSL 130 shaders with the existing OpenGL context. Use `gl_Vertex`/`gl_ModelViewProjectionMatrix`-style built-ins where needed, or explicit `in`/`out` with `#version 130` compatible syntax. **Rationale**: The ImGui backend is initialized with `#version 130`. Creating a modern core-profile context would require changing the WGL initialization, potentially breaking ImGui rendering. Staying on the compatibility profile is lowest risk. **Alternative considered**: Upgrade to OpenGL 3.3+ core profile. Rejected because it requires WGL context creation changes and may break existing ImGui OpenGL3 backend behavior. ### 5. Class decomposition **Decision**: Create the following classes: - `ViewportPanel` (src/ui/) — owns the FBO, renderer, and camera; handles ImGui window and mouse input routing - `UsdSceneRenderer` (src/core/) — traverses USD stage, extracts geometry, manages OpenGL buffers, and issues draw calls - `ViewportCamera` (src/core/) — stores camera parameters, computes view/projection matrices, handles orbit/pan/zoom **Rationale**: Separates concerns — rendering logic is independent of UI, camera logic is independent of both. Each class can be tested and developed in isolation. ## Risks / Trade-offs - **[Legacy OpenGL limitations]** → Stuck with compatibility profile until WGL context is upgraded. Mitigation: GLSL 130 is sufficient for basic mesh rendering; upgrade path is documented. - **[Large scene performance]** → Direct traversal loads all geometry into VBOs upfront. Mitigation: Add frustum culling and LOD in a future iteration; for now, limit to scenes that fit in GPU memory. - **[No Hydra = no procedural rendering]** → Complex procedurals (point instancers, curves) won't render correctly. Mitigation: Document supported prim types; add Hydra path later. - **[FBO resize on dock change]** → Viewport panel size changes when docked/undocked. Mitigation: `ViewportPanel` queries `ImGui::GetContentRegionAvail()` each frame and resizes the FBO when dimensions change.