MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Survey.cpp
Go to the documentation of this file.
1#include "Survey.hpp"
2
21
22namespace MayaFlux::Nexus {
23
24namespace {
25
26 std::optional<Kakshya::MeshData> snapshot_mesh(const std::shared_ptr<Buffers::VKBuffer>& buffer)
27 {
28 if (const auto mesh = std::dynamic_pointer_cast<Buffers::MeshBuffer>(buffer)) {
29 return mesh->get_mesh_data();
30 }
31
32 if (const auto network_buffer = std::dynamic_pointer_cast<Buffers::MeshNetworkBuffer>(buffer)) {
33 const auto network = network_buffer->get_network();
34 return network ? network->get_mesh_data() : std::nullopt;
35 }
36
37 if (const auto geometry = std::dynamic_pointer_cast<Buffers::GeometryBuffer>(buffer)) {
38 const auto node = std::dynamic_pointer_cast<Nodes::GpuSync::MeshWriterNode>(
39 geometry->get_geometry_node());
40 return node ? node->get_mesh_data() : std::nullopt;
41 }
42
43 if (const auto compute = std::dynamic_pointer_cast<Buffers::ComputeMeshBuffer>(buffer)) {
44 return Buffers::download_compute_mesh(compute);
45 }
46
47 return std::nullopt;
48 }
49
50 size_t position_offset(const Kakshya::VertexLayout& layout)
51 {
52 const auto it = std::ranges::find_if(layout.attributes,
53 [](const auto& attribute) {
54 return attribute.component_modality == Kakshya::DataModality::VERTEX_POSITIONS_3D;
55 });
56 return it != layout.attributes.end() ? it->offset_in_vertex : 0;
57 }
58
59 std::span<const std::byte> vertex_bytes(const Kakshya::MeshData& mesh)
60 {
61 const auto* vertices = std::get_if<std::vector<uint8_t>>(&mesh.vertex_variant);
62 return vertices ? std::as_bytes(std::span(*vertices)) : std::span<const std::byte> {};
63 }
64
65 std::optional<std::vector<glm::vec3>> decode_positions(
66 std::span<const uint8_t> bytes,
67 const Kakshya::VertexLayout& layout)
68 {
69 std::vector<Kakshya::DataVariant> channels;
70 Kakshya::VertexInsertion insertion(channels);
71 if (!insertion.insert_interleaved(bytes, layout)) {
72 return std::nullopt;
73 }
74
75 const auto it = std::ranges::find_if(layout.attributes,
76 [](const auto& attribute) {
77 return attribute.component_modality == Kakshya::DataModality::VERTEX_POSITIONS_3D;
78 });
79 if (it == layout.attributes.end()) {
80 return std::nullopt;
81 }
82
83 auto* positions = std::get_if<std::vector<glm::vec3>>(
84 &channels.at(static_cast<size_t>(it - layout.attributes.begin())));
85 return positions ? std::optional<std::vector<glm::vec3>> { std::move(*positions) } : std::nullopt;
86 }
87
88 struct Cloud {
89 std::vector<glm::vec3> positions;
90 std::vector<uint32_t> clusters;
91 };
92
93 std::optional<Cloud> network_cloud(const std::shared_ptr<Buffers::NetworkGeometryBuffer>& buffer)
94 {
95 const auto network = buffer->get_network();
96 const auto* graphics = network
97 ? dynamic_cast<const Nodes::Network::GraphicsOperator*>(network->get_operator())
98 : nullptr;
99 if (!graphics) {
100 return std::nullopt;
101 }
102
103 const auto layout = graphics->get_vertex_layout();
104 const auto chain = network->get_operator_chain();
105
106 std::optional<std::vector<glm::vec3>> positions;
107 if (chain && chain->find<Nodes::Network::GpuFieldOperator>() && buffer->is_initialized()) {
108 std::vector<uint8_t> bytes(graphics->get_vertex_count() * layout.stride_bytes);
109 std::shared_ptr<Buffers::VKBuffer> staging;
110 Buffers::download_from_gpu_async(buffer, bytes.data(), bytes.size(), staging);
111 positions = decode_positions(bytes, layout);
112 } else {
113 positions = decode_positions(graphics->get_vertex_data(), layout);
114 }
115
116 if (!positions) {
117 return std::nullopt;
118 }
119 return Cloud { .positions = std::move(*positions), .clusters = graphics->build_cluster_ids() };
120 }
121
122 bool field_driven(const std::shared_ptr<Buffers::VKBuffer>& buffer)
123 {
124 const auto chain = buffer->get_processing_chain();
125 return chain
126 && (std::dynamic_pointer_cast<Buffers::VertexFieldProcessor>(chain->get_postprocessor(buffer))
127 || chain->get_processor<Buffers::VertexFieldProcessor>(buffer));
128 }
129
130 std::optional<Cloud> node_cloud(const std::shared_ptr<Buffers::GeometryBuffer>& buffer)
131 {
132 const auto node = buffer->get_geometry_node();
133 if (!node || std::dynamic_pointer_cast<Nodes::GpuSync::MeshWriterNode>(node)) {
134 return std::nullopt;
135 }
136
137 const auto layout = node->get_vertex_layout();
138 if (!layout) {
139 return std::nullopt;
140 }
141
142 std::optional<std::vector<glm::vec3>> positions;
143 if (buffer->is_initialized() && field_driven(buffer)) {
144 std::vector<uint8_t> bytes(static_cast<size_t>(node->get_vertex_count()) * layout->stride_bytes);
145 std::shared_ptr<Buffers::VKBuffer> staging;
146 Buffers::download_from_gpu_async(buffer, bytes.data(), bytes.size(), staging);
147 positions = decode_positions(bytes, *layout);
148 } else {
149 positions = decode_positions(node->get_vertex_data(), *layout);
150 }
151
152 if (!positions) {
153 return std::nullopt;
154 }
155 return Cloud { .positions = std::move(*positions), .clusters = {} };
156 }
157
158 std::optional<Cloud> cloud_of(const std::shared_ptr<Buffers::VKBuffer>& buffer)
159 {
160 if (const auto network_buffer = std::dynamic_pointer_cast<Buffers::NetworkGeometryBuffer>(buffer)) {
161 return network_cloud(network_buffer);
162 }
163 if (const auto geometry = std::dynamic_pointer_cast<Buffers::GeometryBuffer>(buffer)) {
164 return node_cloud(geometry);
165 }
166 return std::nullopt;
167 }
168
169 std::optional<Kinesis::AABB3D> node_bounds(const std::shared_ptr<Nodes::GpuSync::GeometryWriterNode>& node)
170 {
171 if (const auto mesh = std::dynamic_pointer_cast<Nodes::GpuSync::MeshWriterNode>(node)) {
172 const auto& vertices = mesh->get_mesh_vertices();
173 return vertices.empty() ? std::nullopt : std::optional<Kinesis::AABB3D> { Kinesis::aabb(std::span(vertices)) };
174 }
175
176 const auto layout = node->get_vertex_layout();
177 const auto positions = layout ? decode_positions(node->get_vertex_data(), *layout) : std::nullopt;
178 return positions && !positions->empty() ? std::optional<Kinesis::AABB3D> { Kinesis::aabb(*positions) } : std::nullopt;
179 }
180
181 std::optional<Kinesis::AABB3D> instance_bounds(const std::shared_ptr<Buffers::InstanceNetworkBuffer>& buffer)
182 {
183 const auto network = buffer->get_network();
184 if (!network) {
185 return std::nullopt;
186 }
187
188 std::optional<Kinesis::AABB3D> total;
189 for (uint32_t i = 0; i < network->slot_count(); ++i) {
190 const auto& slot = network->get_slot(i);
191 const auto local = slot.node ? node_bounds(slot.node) : std::nullopt;
192 if (!local) {
193 continue;
194 }
195
196 for (int corner = 0; corner < 8; ++corner) {
197 const glm::vec3 point {
198 (corner & 1) != 0 ? local->max.x : local->min.x,
199 (corner & 2) != 0 ? local->max.y : local->min.y,
200 (corner & 4) != 0 ? local->max.z : local->min.z,
201 };
202 const glm::vec3 placed = glm::vec3(slot.transform * glm::vec4(point, 1.0F));
203 total = total
204 ? Kinesis::AABB3D { .min = glm::min(total->min, placed), .max = glm::max(total->max, placed) }
205 : Kinesis::AABB3D { .min = placed, .max = placed };
206 }
207 }
208 return total;
209 }
210
211}
212
213std::optional<glm::vec3> read_anchor(
214 const std::shared_ptr<Buffers::VKBuffer>& buf,
215 const std::optional<uint32_t>& index)
216{
217 if (const auto cloud = cloud_of(buf); cloud && !cloud->positions.empty()) {
218 if (!index) {
219 return Kinesis::centroid(cloud->positions);
220 }
221 return *index < cloud->positions.size()
222 ? std::optional<glm::vec3> { cloud->positions.at(*index) }
223 : std::nullopt;
224 }
225
226 if (const auto instances = std::dynamic_pointer_cast<Buffers::InstanceNetworkBuffer>(buf);
227 instances && !index) {
228 const auto bounds = instance_bounds(instances);
229 return bounds ? std::optional<glm::vec3> { bounds->center() } : std::nullopt;
230 }
231
232 const auto mesh = snapshot_mesh(buf);
233 if (!mesh) {
234 return std::nullopt;
235 }
236
237 const auto bytes = vertex_bytes(*mesh);
238 if (bytes.empty()) {
239 return std::nullopt;
240 }
241
242 const size_t stride = mesh->layout.stride_bytes;
243 const size_t offset = position_offset(mesh->layout);
244
245 if (index) {
246 return Kinesis::position_at(bytes, stride, offset, *index);
247 }
248
249 if (const auto* faces = std::get_if<std::vector<uint32_t>>(&mesh->index_variant);
250 faces && !faces->empty()) {
251 return Kinesis::surface_centroid(bytes, stride, offset, *faces);
252 }
253 return Kinesis::centroid(bytes, stride, offset);
254}
255
256std::optional<Kinesis::AABB3D> read_bounds(
257 const std::shared_ptr<Buffers::VKBuffer>& buf,
258 const std::optional<uint32_t>& collection)
259{
260 if (const auto volume = std::dynamic_pointer_cast<Buffers::VolumeGridBuffer>(buf)) {
261 return volume->get_bounds();
262 }
263
264 if (const auto network_buffer = std::dynamic_pointer_cast<Buffers::NetworkGeometryBuffer>(buf)) {
265 const auto cloud = network_cloud(network_buffer);
266 if (!cloud || cloud->positions.empty()) {
267 return std::nullopt;
268 }
269 if (!collection) {
270 return Kinesis::aabb(cloud->positions);
271 }
272
273 std::vector<glm::vec3> chosen;
274 const size_t count = std::min(cloud->positions.size(), cloud->clusters.size());
275 for (size_t i = 0; i < count; ++i) {
276 if (cloud->clusters.at(i) == *collection) {
277 chosen.push_back(cloud->positions.at(i));
278 }
279 }
280 return chosen.empty() ? std::nullopt : std::optional<Kinesis::AABB3D> { Kinesis::aabb(chosen) };
281 }
282
283 if (const auto geometry = std::dynamic_pointer_cast<Buffers::GeometryBuffer>(buf)) {
284 if (const auto cloud = node_cloud(geometry); cloud && !cloud->positions.empty()) {
285 return Kinesis::aabb(cloud->positions);
286 }
287 }
288
289 if (const auto instances = std::dynamic_pointer_cast<Buffers::InstanceNetworkBuffer>(buf)) {
290 return instance_bounds(instances);
291 }
292
293 const auto mesh = snapshot_mesh(buf);
294 const auto* vertices = mesh ? std::get_if<std::vector<uint8_t>>(&mesh->vertex_variant) : nullptr;
295 if (!vertices) {
296 return std::nullopt;
297 }
298
299 const auto positions = decode_positions(*vertices, mesh->layout);
300 return positions && !positions->empty() ? std::optional<Kinesis::AABB3D> { Kinesis::aabb(*positions) } : std::nullopt;
301}
302
303glm::mat4 read_placement(const std::shared_ptr<Buffers::VKBuffer>& buf)
304{
305 const auto proc = buf->get_render_processor();
306 if (!proc) {
307 return glm::mat4(1.0F);
308 }
309 if (const auto& source = proc->get_geometry_transform_source()) {
310 return source();
311 }
312 if (const auto& fixed = proc->get_geometry_transform()) {
313 return *fixed;
314 }
315 return glm::mat4(1.0F);
316}
317
318} // namespace MayaFlux::Nexus
uint64_t offset
Definition Composite.cpp:10
Core::GlobalNetworkConfig network
Definition Config.cpp:43
Core::GlobalGraphicsConfig graphics
Definition Config.cpp:41
std::shared_ptr< NetworkGeometryBuffer > buffer
BufferProcessingChain * chain
uint32_t position_offset
uint32_t total
std::vector< glm::vec3 > positions
std::vector< glm::vec3 > positions
Definition Survey.cpp:89
std::vector< uint32_t > clusters
Definition Survey.cpp:90
glm::ivec3 min
glm::ivec3 max
uint32_t index
Definition VKDevice.cpp:122
size_t count
uint32_t channels
std::optional< Kakshya::MeshData > download_compute_mesh(const std::shared_ptr< ComputeMeshBuffer > &buffer)
Download a ComputeMeshBuffer's current live geometry to CPU.
glm::mat4 read_placement(const std::shared_ptr< Buffers::VKBuffer > &buf)
Where a buffer is drawn: the geometry transform of its primary render processor.
Definition Survey.cpp:303
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
std::optional< Kinesis::AABB3D > read_bounds(const std::shared_ptr< Buffers::VKBuffer > &buf, const std::optional< uint32_t > &collection)
Bounds of a buffer's geometry in its own stored coordinates.
Definition Survey.cpp:256
Form< glm::vec2 > point(glm::vec3 color, float size, float hit_radius)
Geometry function for a positioned point in NDC space.