MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Attachment.cpp
Go to the documentation of this file.
1#include "Attachment.hpp"
2
3#include "Survey.hpp"
4
9
10#include <glm/gtc/matrix_transform.hpp>
11
12namespace MayaFlux::Nexus {
13
15 std::function<glm::mat4()> base;
16 std::vector<std::shared_ptr<glm::mat4>> layers;
17};
18
19namespace {
20
21 struct StackedSource {
22 std::shared_ptr<PlacementStack> stack;
23
24 glm::mat4 operator()() const
25 {
26 glm::mat4 placed = stack->base ? stack->base() : glm::mat4(1.0F);
27 for (const auto& layer : stack->layers) {
28 placed = *layer * placed;
29 }
30 return placed;
31 }
32 };
33
34 std::shared_ptr<PlacementStack> stack_of(const std::shared_ptr<Buffers::RenderProcessor>& proc)
35 {
36 const auto& source = proc->get_geometry_transform_source();
37 const auto* stacked = source ? source.target<StackedSource>() : nullptr;
38 return stacked ? stacked->stack : nullptr;
39 }
40
41 glm::quat frame(const std::optional<glm::quat>& orientation)
42 {
43 return orientation.value_or(glm::quat(1.0F, 0.0F, 0.0F, 0.0F));
44 }
45
46 std::vector<std::shared_ptr<Buffers::RenderProcessor>> render_processors(
47 const std::shared_ptr<Buffers::VKBuffer>& buf)
48 {
49 std::vector<std::shared_ptr<Buffers::RenderProcessor>> processors;
50 if (auto primary = buf->get_render_processor()) {
51 processors.push_back(std::move(primary));
52 }
53 for (auto& proc : buf->get_additional_render_processors()) {
54 processors.push_back(std::move(proc));
55 }
56 return processors;
57 }
58
59 void place(Attachment& attachment, const std::shared_ptr<Buffers::RenderProcessor>& proc)
60 {
61 const bool known = std::ranges::any_of(attachment.followed,
62 [&proc](const auto& entry) { return entry.first == proc; });
63 if (known) {
64 return;
65 }
66
67 auto stack = stack_of(proc);
68 if (!stack) {
69 stack = std::make_shared<PlacementStack>();
70 stack->base = proc->get_geometry_transform_source();
71 if (!stack->base) {
72 if (const auto& fixed = proc->get_geometry_transform()) {
73 stack->base = [m = *fixed] { return m; };
74 }
75 }
76 proc->set_geometry_transform_source(StackedSource { stack });
77 }
78
79 stack->layers.push_back(attachment.transform);
80 attachment.followed.emplace_back(proc, std::move(stack));
81 }
82
83}
84
86 std::shared_ptr<Buffers::VKBuffer> buf,
87 const AttachConfig& config,
88 const std::optional<glm::quat>& orientation)
89{
90 Attachment attachment {
91 .buf = std::move(buf),
92 .config = config,
93 .transform = std::make_shared<glm::mat4>(1.0F),
94 };
95
96 if (const auto anchor = read_anchor(attachment.buf, config.index)) {
97 attachment.anchor = *anchor;
98 } else {
100 "Attachment anchors at the buffer's local origin: no geometry could be read from it");
101 }
102
103 attachment.applied = attachment.anchor + frame(orientation) * config.offset;
104 return attachment;
105}
106
108 Attachment& attachment,
109 std::optional<glm::vec3>& position,
110 const std::optional<glm::quat>& orientation)
111{
112 const glm::quat rotation = frame(orientation);
113 glm::vec3 placed = glm::vec3(*attachment.transform * glm::vec4(attachment.anchor, 1.0F));
114
115 if (position && *position != attachment.applied) {
116 const glm::vec3 delta = *position - attachment.applied;
117 if (attachment.config.on_move == AttachConfig::OnMove::Carry) {
118 *attachment.transform = glm::translate(glm::mat4(1.0F), delta) * *attachment.transform;
119 placed += delta;
120 } else {
121 attachment.config.offset += glm::inverse(rotation) * delta;
122 }
123 }
124
125 position = placed + rotation * attachment.config.offset;
126 attachment.applied = *position;
127}
128
130 Attachment& attachment,
131 std::optional<glm::vec3>& position,
132 const std::optional<glm::quat>& orientation)
133{
134 const glm::quat rotation = frame(orientation);
135
136 if (const auto anchor = read_anchor(attachment.buf, attachment.config.index)) {
137 attachment.anchor = *anchor;
138 } else {
140 "recenter kept the previous anchor: no geometry could be read from the buffer");
141 }
142
143 const glm::vec3 placed = glm::vec3(*attachment.transform * glm::vec4(attachment.anchor, 1.0F));
144
145 if (position) {
146 attachment.config.offset = glm::inverse(rotation) * (*position - placed);
147 } else {
148 position = placed + rotation * attachment.config.offset;
149 }
150 attachment.applied = *position;
151}
152
153void apply_attachment(Attachment& attachment, const InfluenceContext& ctx)
154{
155 const glm::quat rotation = frame(ctx.orientation);
156
158 attachment.anchor,
159 ctx.position - rotation * attachment.config.offset,
160 rotation);
161
162 place_attachment(attachment);
163}
164
166{
167 for (const auto& proc : render_processors(attachment.buf)) {
168 place(attachment, proc);
169 }
170}
171
173{
174 for (auto& [proc, stack] : attachment.followed) {
175 std::erase(stack->layers, attachment.transform);
176 if (stack->layers.empty() && stack_of(proc) == stack) {
177 proc->set_geometry_transform_source(std::move(stack->base));
178 }
179 }
180 attachment.followed.clear();
181 *attachment.transform = glm::mat4(1.0F);
182}
183
184} // namespace MayaFlux::Nexus
#define MF_WARN(comp, ctx,...)
std::shared_ptr< PlacementStack > stack
std::optional< glm::vec3 > position
Definition Decoder.cpp:93
@ Init
Engine/subsystem initialization.
@ Runtime
General runtime operations (default fallback)
@ Nexus
Spatial indexing and scheduling for user-defined behaviour.
glm::mat4 pivot_transform(const glm::vec3 &pivot, const glm::vec3 &target, const glm::quat &rotation)
Rigid transform that rotates about a local pivot and lands the pivot on a world point.
AABB2D place(AABB2D anchor, Side side, float w, float h, float gap=0.F, Align align=Align::Center) noexcept
Compute a region of size (w, h) positioned relative to anchor.
Definition Bounds.hpp:579
std::optional< glm::vec3 > read_anchor(const std::shared_ptr< Buffers::VKBuffer > &buf, const std::optional< uint32_t > &index)
Anchor of a buffer's geometry in its own stored coordinates.
Definition Survey.cpp:213
void apply_attachment(Attachment &attachment, const InfluenceContext &ctx)
Rebuild the transform from ctx and place every render processor of the buffer.
void recenter_attachment(Attachment &attachment, std::optional< glm::vec3 > &position, const std::optional< glm::quat > &orientation)
Read the buffer's geometry again and move the anchor to match.
void place_attachment(Attachment &attachment)
Place every render processor of the buffer that is not placed yet.
void follow_attachment(Attachment &attachment, std::optional< glm::vec3 > &position, const std::optional< glm::quat > &orientation)
Set position from the buffer, or fold an outside move into the attachment.
void release_attachment(Attachment &attachment)
Give every placed render processor back the geometry transform it had.
Attachment make_attachment(std::shared_ptr< Buffers::VKBuffer > buf, const AttachConfig &config, const std::optional< glm::quat > &orientation)
Build an attachment to buf.
Source source()
Begin a Source chain.
Definition Plot.hpp:127
std::optional< uint32_t > index
Vertex whose position anchors the Agent.
glm::vec3 offset
Offset of the Agent from the anchor, in the Agent's own frame.
@ Carry
The buffer is moved along with the Agent.
How an Agent attaches to a buffer that already exists.
std::shared_ptr< Buffers::VKBuffer > buf
std::shared_ptr< glm::mat4 > transform
std::vector< std::pair< std::shared_ptr< Buffers::RenderProcessor >, std::shared_ptr< PlacementStack > > > followed
State of one Agent attached to an existing buffer.
glm::vec3 position
Position of the influence point in world space.
Definition Influence.hpp:18
std::optional< glm::quat > orientation
Optional orientation of the influencer. Local +Z is its forward direction.
Definition Influence.hpp:30
Data passed to an Emitter or Agent influence function on each commit.
Definition Influence.hpp:17
std::vector< std::shared_ptr< glm::mat4 > > layers
std::function< glm::mat4()> base
The placements layered on one render processor, kept by its geometry transform source.