32 bool extract_vertex_columns(
33 const Kakshya::VertexLayout& layout,
34 std::span<const uint8_t> raw,
36 std::vector<glm::vec3>& positions,
37 std::vector<SpatialAttribute>& attributes)
39 if (layout.stride_bytes !=
sizeof(Kakshya::Vertex)) {
43 const auto* verts =
reinterpret_cast<const Kakshya::Vertex*
>(raw.data());
45 positions.resize(vertex_count);
46 std::vector<glm::vec3> colors(vertex_count);
47 std::vector<float> scalars(vertex_count);
48 std::vector<glm::vec2> uvs(vertex_count);
49 std::vector<glm::vec3> normals(vertex_count);
50 std::vector<glm::vec3> tangents(vertex_count);
52 for (
size_t i = 0; i < vertex_count; ++i) {
53 positions[i] = verts[i].position;
54 colors[i] = verts[i].color;
55 scalars[i] = verts[i].scalar;
57 normals[i] = verts[i].normal;
58 tangents[i] = verts[i].tangent;
96 std::optional<std::vector<int32_t>> curve_vertex_counts(
98 const std::vector<uint32_t>& cluster_ids,
102 std::vector<int32_t> counts;
104 while (i < cluster_ids.size()) {
106 while (j < cluster_ids.size() && cluster_ids[j] == cluster_ids[i]) {
109 counts.push_back(
static_cast<int32_t
>(j - i));
115 if (vertex_count % 2 != 0) {
118 return std::vector<int32_t>(vertex_count / 2, 2);
124 const std::shared_ptr<Buffers::RelaxationGridBuffer>& grid,
126 std::vector<glm::vec3>& positions,
127 std::vector<uint64_t>& ids)
131 "relaxation_grid_positions: null grid");
135 const uint32_t
width = grid->get_grid_width();
136 const uint32_t
height = grid->get_grid_height();
137 const uint32_t cell_count = grid->get_cell_count();
139 positions.resize(cell_count);
140 ids.resize(cell_count);
142 for (uint32_t i = 0; i < cell_count; ++i) {
143 const uint32_t ix = i %
width;
144 const uint32_t iy = i /
width;
146 const float fx = (
static_cast<float>(ix) + 0.5F) /
static_cast<float>(
width);
147 const float fy = (
static_cast<float>(iy) + 0.5F) /
static_cast<float>(
height);
149 positions[i] = glm::vec3((fx * 2.0F - 1.0F) * extent, (fy * 2.0F - 1.0F) * extent, 0.0F);
158 const std::string& stream_name,
163 "write_operator_sample: null operator");
168 if (vertex_count == 0) {
170 "write_operator_sample: operator reports zero vertices");
178 "write_operator_sample: vertex buffer smaller than layout implies");
182 std::vector<glm::vec3> positions;
183 std::vector<SpatialAttribute> attributes;
184 if (!extract_vertex_columns(layout, raw, vertex_count, positions, attributes)) {
186 "write_operator_sample: vertex layout is not a Kakshya::Vertex record");
191 if (cluster_ids.size() != vertex_count) {
193 "write_operator_sample: build_cluster_ids() count mismatch");
198 std::optional<std::vector<int32_t>> vertex_counts_per_curve;
201 vertex_counts_per_curve = curve_vertex_counts(topology, cluster_ids, vertex_count);
202 if (!vertex_counts_per_curve) {
204 "write_operator_sample: {} vertices cannot form LINE_LIST pairs", vertex_count);
215 const size_t count = std::visit([](
const auto& v) {
return v.size(); },
values);
216 if (
count != vertex_count) {
218 "write_operator_sample: extract_vertex_attributes() '{}' count mismatch",
name);
224 .values = std::move(
values) });
227 if (vertex_counts_per_curve) {
228 return cache.
write(stream_name,
231 .positions = positions,
232 .vertex_counts_per_curve = *vertex_counts_per_curve,
233 .attributes = attributes });
236 std::vector<uint64_t> ids(vertex_count);
237 std::ranges::iota(ids, uint64_t { 0 });
240 if (!velocities.empty() && velocities.size() != vertex_count) {
242 "write_operator_sample: extract_vertex_velocities() count mismatch");
246 return cache.
write(stream_name,
249 .positions = positions,
251 .velocities = velocities,
252 .attributes = attributes });
272 bool write_network_geometry_buffer_gpu_sample(
274 const std::string& stream_name,
275 const std::shared_ptr<Buffers::NetworkGeometryBuffer>&
buffer,
279 if (vertex_count == 0) {
281 "write_network_geometry_buffer_sample: operator reports zero vertices");
286 if (layout.stride_bytes !=
sizeof(Kakshya::Vertex)) {
288 "write_network_geometry_buffer_sample: vertex layout is not a Kakshya::Vertex record");
292 std::vector<uint8_t> raw(vertex_count * layout.stride_bytes);
293 std::shared_ptr<Buffers::VKBuffer> staging;
296 std::vector<glm::vec3> positions;
297 std::vector<SpatialAttribute> attributes;
298 if (!extract_vertex_columns(layout, raw, vertex_count, positions, attributes)) {
300 "write_network_geometry_buffer_sample: vertex layout is not a Kakshya::Vertex record");
305 std::optional<std::vector<int32_t>> vertex_counts_per_curve;
309 if (structural_cluster_ids.size() != vertex_count) {
311 "write_network_geometry_buffer_sample: build_cluster_ids() count mismatch");
314 vertex_counts_per_curve = curve_vertex_counts(topology, structural_cluster_ids, vertex_count);
315 if (!vertex_counts_per_curve) {
317 "write_network_geometry_buffer_sample: {} vertices cannot form LINE_LIST pairs", vertex_count);
322 if (
buffer->has_state(
"hash_cluster_id")
323 &&
buffer->get_state_bytes(
"hash_cluster_id") == vertex_count *
sizeof(uint32_t)) {
324 std::vector<uint32_t> cluster_ids(vertex_count);
326 buffer->read_state_slot(
"hash_cluster_id"), cluster_ids.data(),
327 cluster_ids.size() *
sizeof(uint32_t), staging);
328 attributes.push_back(SpatialAttribute {
334 if (vertex_counts_per_curve) {
335 return cache.write(stream_name,
337 .topology = topology,
338 .positions = positions,
339 .vertex_counts_per_curve = *vertex_counts_per_curve,
340 .attributes = attributes });
343 std::vector<uint64_t> ids(vertex_count);
344 std::ranges::iota(ids, uint64_t { 0 });
346 return cache.write(stream_name,
349 .positions = positions,
351 .attributes = attributes });
358 const std::string& stream_name,
359 const std::shared_ptr<Buffers::NetworkGeometryBuffer>&
buffer)
363 "write_network_geometry_buffer_sample: null buffer");
370 "write_network_geometry_buffer_sample: buffer has no network");
380 "write_network_geometry_buffer_sample: network's primary operator is not a GraphicsOperator");
383 return write_network_geometry_buffer_gpu_sample(cache, stream_name,
buffer, graphics_op);
386 std::vector<std::pair<std::string, Nodes::Network::GraphicsOperator*>> graphics_ops;
388 graphics_ops.emplace_back(std::string(primary->get_type_name()), primary);
391 for (
const auto& op :
chain->operators()) {
393 graphics_ops.emplace_back(std::string(secondary->get_type_name()), secondary);
398 if (graphics_ops.empty()) {
400 "write_network_geometry_buffer_sample: network has no GraphicsOperator");
404 if (graphics_ops.size() == 1) {
408 std::unordered_map<std::string, int> seen;
409 for (
const auto& [type_name, op] : graphics_ops) {
410 int& occurrence = seen[type_name];
411 std::string set_name = stream_name;
412 set_name.append(
"_").append(type_name);
413 if (occurrence != 0) {
414 set_name.append(std::to_string(occurrence));
#define MF_ERROR(comp, ctx,...)
Core::GlobalNetworkConfig network
std::shared_ptr< NetworkGeometryBuffer > buffer
BufferProcessingChain * chain
std::vector< std::byte > values
bool write(const std::string &stream_name, const SpatialSample &sample)
Append one sample to a named stream.
Alembic-backed writer for time-sampled spatial entity state: particle systems, point clouds,...
Chain operator that declares Tendency field deformation as a compute shader rather than evaluating it...
virtual std::vector< std::pair< std::string, Kakshya::DataVariant > > extract_vertex_attributes() const
Extra named, per-vertex attributes beyond position/color/size, for an external cache/export consumer.
virtual size_t get_vertex_count() const =0
Get number of vertices (may differ from point count for topology/path)
virtual std::vector< uint32_t > build_cluster_ids() const
Per-vertex collection index, global index order.
virtual Kakshya::VertexLayout get_vertex_layout() const =0
Get vertex layout describing vertex structure.
virtual std::optional< Portal::Graphics::PrimitiveTopology > declared_topology() const
Topology this operator's vertex data is authored for, if it holds a node to ask.
virtual std::vector< glm::vec3 > extract_vertex_velocities() const
Per-vertex velocity, for a consumer that wants to write it as a native channel (e....
virtual std::span< const uint8_t > get_vertex_data() const =0
Get vertex data for GPU upload.
Operator that produces GPU-renderable geometry.
void download_from_gpu_async(const std::shared_ptr< VKBuffer > &source, void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Download from a device-local GPU buffer without stalling the graphics queue.
void download_back_buffer(const VKBufferResources::GenerationSlot &slot, void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Download a raw back_buffers slot to host memory.
bool relaxation_grid_positions(const std::shared_ptr< Buffers::RelaxationGridBuffer > &grid, float extent, std::vector< glm::vec3 > &positions, std::vector< uint64_t > &ids)
Generate the fixed grid positions and cell ids a RelaxationGridBuffer's cells occupy,...
bool write_operator_sample(SpatialCache &cache, const std::string &stream_name, const Nodes::Network::GraphicsOperator *op)
Pack one GraphicsOperator's current vertex state into a SpatialCache stream, as a Points or Curves sa...
bool write_network_geometry_buffer_sample(SpatialCache &cache, const std::string &stream_name, const std::shared_ptr< Buffers::NetworkGeometryBuffer > &buffer)
Pack a NetworkGeometryBuffer's driving network into one or more SpatialCache streams,...
@ FileIO
Filesystem I/O operations.
@ IO
Networking, file handling, streaming.
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
PrimitiveTopology
Vertex assembly primitive topology.
One named, scoped channel of already-typed values.
Portal::Graphics::PrimitiveTopology topology
One sample's worth of data for one named stream.
uint32_t stride_bytes
Total bytes per vertex (stride in Vulkan terms) e.g., 3 floats (position) + 3 floats (normal) = 24 by...
Complete description of vertex data layout in a buffer.