MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Scissor.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "ViewTransform.hpp"
4
6
7namespace MayaFlux::Kinesis {
8
9/**
10 * @struct Scissor
11 * @brief NDC clip region for a draw, resolved to a pixel rect against a
12 * live framebuffer size wherever it is applied.
13 *
14 * Not a rendering-backend type and not Forma-specific: it lives in Kinesis
15 * because anything with a spatial footprint can produce one. A 2D bounds
16 * region converts directly; anything exposing bounds() (Portal::Forma's
17 * Mapped<T>, Collapsible, Scrollable, Entry, EntryGroup, and so on)
18 * converts through the same HasBounds mechanism place() already uses; a
19 * 3D world-space AABB3D converts by projecting through a ViewTransform.
20 * Portal::Graphics::RenderConfig holds one as std::optional<Scissor>, and
21 * Buffers::RenderProcessor resolves it to a backend rect (Vulkan's
22 * vk::Rect2D) fresh every frame.
23 *
24 * bounds are NDC (matches AABB2D convention: x/y in [-1,1], center origin,
25 * +Y up), so one Scissor stays correct across a resize; nothing here is
26 * resolved to pixels until the frame that draws it.
27 */
28struct Scissor {
29 AABB2D bounds { .min = glm::vec2(-1.F), .max = glm::vec2(1.F) };
30
31 [[nodiscard]] bool operator==(const Scissor& other) const noexcept
32 {
33 return bounds.min == other.bounds.min && bounds.max == other.bounds.max;
34 }
35
36 /// @brief Construct directly from an NDC AABB2D.
37 [[nodiscard]] static Scissor from(AABB2D ndc_bounds) noexcept
38 {
39 return { .bounds = ndc_bounds };
40 }
41
42 /// @brief Construct from anything exposing bounds(): Mapped<T>, Collapsible, Scrollable, Entry, EntryGroup, and so on.
43 template <HasBounds T>
44 [[nodiscard]] static Scissor from(const T& anchor) noexcept
45 {
46 return { .bounds = bounds_of(anchor) };
47 }
48
49 /**
50 * @brief Project a world-space AABB3D through a ViewTransform and take
51 * the NDC bounding box of its projected corners.
52 *
53 * Projects all 8 corners through projection * view, divides by
54 * clip-space w, and takes the min/max of the resulting NDC x/y,
55 * clamped to [-1,1]. A corner behind the eye (w <= 0) is skipped rather
56 * than let it fold the bounding box onto itself, so an object crossing
57 * the near plane still resolves from whichever corners are in front of
58 * the camera. A box with no corner in front of the camera at all
59 * returns the full-screen Scissor: there is nothing visible to clip to,
60 * and the caller almost certainly should not be drawing it this frame
61 * regardless of what the scissor rect says.
62 */
63 [[nodiscard]] static Scissor from(const AABB3D& world_bounds, const ViewTransform& view) noexcept
64 {
65 const glm::mat4 view_projection = view.projection * view.view;
66
67 glm::vec2 lo { 1.F, 1.F };
68 glm::vec2 hi { -1.F, -1.F };
69 bool any_in_front = false;
70
71 for (int i = 0; i < 8; ++i) {
72 const glm::vec3 corner {
73 (i & 1) ? world_bounds.max.x : world_bounds.min.x,
74 (i & 2) ? world_bounds.max.y : world_bounds.min.y,
75 (i & 4) ? world_bounds.max.z : world_bounds.min.z,
76 };
77
78 const glm::vec4 clip = view_projection * glm::vec4(corner, 1.F);
79 if (clip.w <= 0.F)
80 continue;
81
82 const glm::vec2 ndc { clip.x / clip.w, clip.y / clip.w };
83 lo = glm::min(lo, ndc);
84 hi = glm::max(hi, ndc);
85 any_in_front = true;
86 }
87
88 if (!any_in_front)
89 return {};
90
91 return { .bounds = {
92 .min = glm::clamp(lo, glm::vec2(-1.F), glm::vec2(1.F)),
93 .max = glm::clamp(hi, glm::vec2(-1.F), glm::vec2(1.F)),
94 } };
95 }
96};
97
98} // namespace MayaFlux::Kinesis
AABB2D bounds_of(const T &obj)
Extract the anchor region from any HasBounds object.
Definition Bounds.hpp:659
Axis-aligned bounding rectangle in a 2D coordinate space.
Definition Bounds.hpp:63
Axis-aligned bounding box in 3D world space.
Definition Bounds.hpp:188
static Scissor from(const T &anchor) noexcept
Construct from anything exposing bounds(): Mapped<T>, Collapsible, Scrollable, Entry,...
Definition Scissor.hpp:44
static Scissor from(AABB2D ndc_bounds) noexcept
Construct directly from an NDC AABB2D.
Definition Scissor.hpp:37
bool operator==(const Scissor &other) const noexcept
Definition Scissor.hpp:31
static Scissor from(const AABB3D &world_bounds, const ViewTransform &view) noexcept
Project a world-space AABB3D through a ViewTransform and take the NDC bounding box of its projected c...
Definition Scissor.hpp:63
NDC clip region for a draw, resolved to a pixel rect against a live framebuffer size wherever it is a...
Definition Scissor.hpp:28
View and projection matrices as a named push constant slot.