// Per-eye scene renderer — M2 gate 4 core, generalized for M3 step 2. // // Draws textured quads into a caller-provided render target. The geometry // is ONE unit quad (1x1, centered, z=0, UV origin top-left); every draw // takes a full MVP so callers place any number of world quads per pass // (multi-monitor layout). The M2 single-test-quad record() remains as a // convenience wrapper. Owns only PSO/geometry/test-card texture. // // Matrix convention: row-major storage, column-vector math (clip = M * v), // HLSL side declared row_major. World = +Y up, -Z forward (OpenVR). #pragma once #include #include #include #include namespace sauna { // Default world quad (meters at z = -2.0 m) — the M2 test-card placement, // and the angular-size anchor for the M3 monitor layout (primary monitor // width maps to kSceneQuadW). constexpr float kSceneQuadW = 2.1333f, kSceneQuadH = 1.2f; constexpr float kSceneQuadZ = -2.0f; // Model matrix (row-major) mapping the unit quad to a w x h meter world // rectangle centered at (cx, cy, z). inline void MakeQuadModel(float w, float h, float cx, float cy, float z, float out[16]) { for (int i = 0; i < 16; i++) out[i] = 0; out[0] = w; out[5] = h; out[10] = 1; out[3] = cx; out[7] = cy; out[11] = z; out[15] = 1; } // Yawed variant (curved monitor arrangement): quad centered at (cx, cy, // cz), rotated about world +Y by azimuth az — az = 0 faces -Z exactly // like MakeQuadModel; positive az places/faces the quad to the user's // right. right = (cos az, 0, sin az), up = +Y, normal toward the origin. inline void MakeQuadModelYaw(float w, float h, float cx, float cy, float cz, float az, float out[16]) { const float ca = std::cos(az), sa = std::sin(az); for (int i = 0; i < 16; i++) out[i] = 0; out[0] = w * ca; out[3] = cx; out[5] = h; out[7] = cy; out[8] = w * sa; out[10] = 1; out[11] = cz; out[15] = 1; } class SceneRenderer { public: // One-shot init: builds root signature, PSO, quad geometry, and the // procedural test-card texture (uploads via an internal one-shot queue). bool init(ID3D12Device* dev, DXGI_FORMAT rtFormat); // Records commands rendering the scene into rtv (assumed RENDER_TARGET // state). Clears first. viewProj per the convention above. void record(ID3D12GraphicsCommandList* list, D3D12_CPU_DESCRIPTOR_HANDLE rtv, uint32_t width, uint32_t height, const float viewProj[16]); // Same, but the quad samples a caller-provided texture (M3 capture path: // desktop content from a FrameSource) instead of the test card. srv must // live in srvHeap; the texture must be in PIXEL_SHADER_RESOURCE (or // promotable COMMON) state. void record(ID3D12GraphicsCommandList* list, D3D12_CPU_DESCRIPTOR_HANDLE rtv, uint32_t width, uint32_t height, const float viewProj[16], ID3D12DescriptorHeap* srvHeap, D3D12_GPU_DESCRIPTOR_HANDLE srv); // Multi-quad path (M3 step 2): begin the pass once (clear + pipeline // state), then draw any number of quads. mvp = viewProj * model, where // model maps the unit quad (1x1 centered at origin, z=0) to its world // rectangle — see MakeQuadModel. void beginPass(ID3D12GraphicsCommandList* list, D3D12_CPU_DESCRIPTOR_HANDLE rtv, uint32_t width, uint32_t height); void drawQuad(ID3D12GraphicsCommandList* list, const float mvp[16], ID3D12DescriptorHeap* srvHeap, D3D12_GPU_DESCRIPTOR_HANDLE srv); void drawQuad(ID3D12GraphicsCommandList* list, const float mvp[16]); // card private: Microsoft::WRL::ComPtr rootSig_; Microsoft::WRL::ComPtr pso_; Microsoft::WRL::ComPtr vb_; Microsoft::WRL::ComPtr texture_; Microsoft::WRL::ComPtr srvHeap_; D3D12_VERTEX_BUFFER_VIEW vbv_{}; }; } // namespace sauna