MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MeshBuffer.hpp
Go to the documentation of this file.
1#pragma once
2
6
8class MeshWriterNode;
9}
10
11namespace MayaFlux::Core {
12class VKImage;
13}
14
15namespace MayaFlux::Buffers {
16
17class MeshProcessor;
18class RenderProcessor;
19
20/**
21 * @class MeshBuffer
22 * @brief VKBuffer subclass that owns a MeshData and manages its GPU upload.
23 *
24 * MeshBuffer is to indexed triangle geometry what TextureBuffer is to pixel
25 * data: it owns the CPU-side storage, creates a dedicated processor for
26 * GPU upload, and exposes setup_rendering() to attach a RenderProcessor.
27 *
28 * The VKBuffer base manages the vertex Vulkan buffer (Usage::VERTEX).
29 * MeshProcessor allocates and owns a separate Usage::INDEX VKBuffer and
30 * links both into VKBufferResources via set_index_resources(), making the
31 * indexed draw path available to RenderProcessor transparently.
32 *
33 * Two independent dirty flags govern re-upload:
34 * - m_vertices_dirty: set when vertex bytes change (deformation, morph, etc.)
35 * - m_indices_dirty: set when connectivity changes (topology mutation)
36 *
37 * Typical usage:
38 * @code
39 * auto buf = std::make_shared<MeshBuffer>(mesh_data);
40 * buf->setup_rendering({ .target_window = window });
41 * // Register with graphics subsystem — MeshProcessor uploads on first cycle.
42 * @endcode
43 *
44 * Deformation:
45 * @code
46 * buf->set_vertex_data(new_bytes); // marks vertices dirty
47 * // MeshProcessor re-uploads vertex buffer on next graphics cycle.
48 * @endcode
49 */
50class MAYAFLUX_API MeshBuffer : public VKBuffer {
51public:
52 /**
53 * @brief Construct from an owning MeshData.
54 *
55 * The VKBuffer base is sized to vertex_bytes. MeshProcessor allocates
56 * the index buffer separately on on_attach.
57 *
58 * @param data Loaded or generated mesh. Must be valid (is_valid() == true).
59 */
60 explicit MeshBuffer(Kakshya::MeshData data);
61
62 /**
63 * @brief Construct from a MeshWriterNode.
64 *
65 * The node is polled each graphics cycle. compute_frame() is called
66 * on the node, and if its data changed, set_mesh_data() is called
67 * internally before MeshProcessor uploads.
68 */
69 MeshBuffer(std::shared_ptr<Nodes::GpuSync::MeshWriterNode> node);
70
71 ~MeshBuffer() override = default;
72
73 /**
74 * @brief Create and attach MeshProcessor as the default processor.
75 */
76 void setup_processors(ProcessingToken token) override;
77
78 /**
79 * @brief Attach a RenderProcessor targeting the given window.
80 *
81 * Always uses triangle.vert.spv / triangle.frag.spv unless overridden.
82 * Topology is always TRIANGLE_LIST.
83 */
84 void setup_rendering(const RenderConfig& config);
85
86 // -------------------------------------------------------------------------
87 // CPU-side data access
88 // -------------------------------------------------------------------------
89
90 /**
91 * @brief Produce a non-owning MeshAccess view over the internal MeshData.
92 */
93 [[nodiscard]] std::optional<Kakshya::MeshAccess> access() const
94 {
95 return m_mesh_data.access();
96 }
97
98 [[nodiscard]] uint32_t get_vertex_count() const noexcept
99 {
100 return m_mesh_data.vertex_count();
101 }
102
103 [[nodiscard]] uint32_t get_index_count() const noexcept
104 {
105 const auto* ib = std::get_if<std::vector<uint32_t>>(
106 &m_mesh_data.index_variant);
107 return ib ? static_cast<uint32_t>(ib->size()) : 0;
108 }
109
110 [[nodiscard]] uint32_t get_face_count() const noexcept
111 {
112 return m_mesh_data.face_count();
113 }
114
115 [[nodiscard]] const Kakshya::VertexLayout& get_vertex_layout_ref() const noexcept
116 {
117 return m_mesh_data.layout;
118 }
119
120 [[nodiscard]] const std::optional<Kakshya::RegionGroup>& get_submeshes() const noexcept
121 {
122 return m_mesh_data.submeshes;
123 }
124
125 [[nodiscard]] std::shared_ptr<Nodes::GpuSync::MeshWriterNode> get_node() const
126 {
127 return m_node;
128 }
129
130 // -------------------------------------------------------------------------
131 // Mutation (deformation path)
132 // -------------------------------------------------------------------------
133
134 /**
135 * @brief Replace vertex bytes entirely and mark vertices dirty.
136 *
137 * Intended for deformation: caller writes new interleaved bytes (same
138 * stride as the original layout) and the next graphics cycle re-uploads.
139 * Does not change index data or layout.
140 *
141 * @param bytes Raw interleaved vertex bytes. Size must be a multiple of
142 * m_mesh_data.layout.stride_bytes.
143 */
144 void set_vertex_data(std::span<const uint8_t> bytes);
145
146 /**
147 * @brief Replace index data entirely and mark indices dirty.
148 *
149 * For topology mutation. Size must be a multiple of 3.
150 *
151 * @param indices New triangle index list.
152 */
153 void set_index_data(std::span<const uint32_t> indices);
154
155 /**
156 * @brief Replace both vertex and index data atomically and mark both dirty.
157 */
158 void set_mesh_data(Kakshya::MeshData data);
159
160 // -------------------------------------------------------------------------
161 // Dirty flag queries (read by MeshProcessor)
162 // -------------------------------------------------------------------------
163
164 [[nodiscard]] bool vertices_dirty() const noexcept
165 {
166 return m_vertices_dirty.load(std::memory_order_acquire);
167 }
168
169 [[nodiscard]] bool indices_dirty() const noexcept
170 {
171 return m_indices_dirty.load(std::memory_order_acquire);
172 }
173
174 void clear_vertices_dirty() noexcept
175 {
176 m_vertices_dirty.store(false, std::memory_order_release);
177 }
178
179 void clear_indices_dirty() noexcept
180 {
181 m_indices_dirty.store(false, std::memory_order_release);
182 }
183
184 // -------------------------------------------------------------------------
185 // Processor / renderer access
186 // -------------------------------------------------------------------------
187
188 [[nodiscard]] std::shared_ptr<MeshProcessor> get_mesh_processor() const
189 {
190 return m_mesh_processor;
191 }
192
193 /**
194 * @brief Bind a diffuse texture to the render processor, if present.
195 *
196 * The binding name must match a descriptor slot in the shader. If the
197 * render processor is not yet created, the texture will be bound on
198 * creation if the default_texture_binding matches.
199 */
200 void bind_diffuse_texture(
201 std::shared_ptr<Core::VKImage> image,
202 std::string_view binding_name = "diffuseTex");
203
204 /* Check if a diffuse texture is bound. */
205 [[nodiscard]] bool has_diffuse_texture() const noexcept { return m_diffuse_texture != nullptr; }
206
207 /* Diffuse texture is optional, so may return nullptr. */
208 [[nodiscard]] std::shared_ptr<Core::VKImage> get_diffuse_texture() const noexcept { return m_diffuse_texture; }
209
210private:
211 friend class MeshProcessor;
212
214 std::shared_ptr<Nodes::GpuSync::MeshWriterNode> m_node;
215 std::shared_ptr<Core::VKImage> m_diffuse_texture;
216 std::string m_diffuse_binding { "diffuseTex" };
217
218 std::atomic<bool> m_vertices_dirty { true };
219 std::atomic<bool> m_indices_dirty { true };
220
221 std::shared_ptr<MeshProcessor> m_mesh_processor;
222};
223
224} // namespace MayaFlux::Buffers
IO::ImageData image
Definition Decoder.cpp:57
uint32_t get_face_count() const noexcept
std::shared_ptr< Nodes::GpuSync::MeshWriterNode > get_node() const
const Kakshya::VertexLayout & get_vertex_layout_ref() const noexcept
std::shared_ptr< Core::VKImage > m_diffuse_texture
bool has_diffuse_texture() const noexcept
std::shared_ptr< Core::VKImage > get_diffuse_texture() const noexcept
~MeshBuffer() override=default
const std::optional< Kakshya::RegionGroup > & get_submeshes() const noexcept
std::optional< Kakshya::MeshAccess > access() const
Produce a non-owning MeshAccess view over the internal MeshData.
std::shared_ptr< Nodes::GpuSync::MeshWriterNode > m_node
void clear_indices_dirty() noexcept
std::shared_ptr< MeshProcessor > m_mesh_processor
std::shared_ptr< MeshProcessor > get_mesh_processor() const
bool indices_dirty() const noexcept
bool vertices_dirty() const noexcept
uint32_t get_vertex_count() const noexcept
uint32_t get_index_count() const noexcept
void clear_vertices_dirty() noexcept
Kakshya::MeshData m_mesh_data
VKBuffer subclass that owns a MeshData and manages its GPU upload.
Default processor for MeshBuffer: handles CPU-to-GPU upload of vertex and index data with selective d...
Vulkan-backed buffer wrapper used in processing chains.
Definition VKBuffer.hpp:67
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
Owning CPU-side representation of a loaded or generated mesh.
Definition MeshData.hpp:33
Complete description of vertex data layout in a buffer.
Unified rendering configuration for graphics buffers.