MayaFlux 1.0.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 /**
99 * @brief Read-only access to the owning MeshData.
100 *
101 * For a caller that needs an owning copy (e.g. IO::ModelWriter, which
102 * takes MeshData rather than a non-owning view since it must outlive the
103 * synchronous export call). Prefer access() when a view suffices.
104 */
105 [[nodiscard]] const Kakshya::MeshData& get_mesh_data() const noexcept
106 {
107 return m_mesh_data;
108 }
109
110 [[nodiscard]] uint32_t get_vertex_count() const noexcept
111 {
112 return m_mesh_data.vertex_count();
113 }
114
115 [[nodiscard]] uint32_t get_index_count() const noexcept
116 {
117 const auto* ib = std::get_if<std::vector<uint32_t>>(
118 &m_mesh_data.index_variant);
119 return ib ? static_cast<uint32_t>(ib->size()) : 0;
120 }
121
122 [[nodiscard]] uint32_t get_face_count() const noexcept
123 {
124 return m_mesh_data.face_count();
125 }
126
127 [[nodiscard]] const Kakshya::VertexLayout& get_vertex_layout_ref() const noexcept
128 {
129 return m_mesh_data.layout;
130 }
131
132 [[nodiscard]] const std::optional<Kakshya::RegionGroup>& get_submeshes() const noexcept
133 {
134 return m_mesh_data.submeshes;
135 }
136
137 [[nodiscard]] std::shared_ptr<Nodes::GpuSync::MeshWriterNode> get_node() const
138 {
139 return m_node;
140 }
141
142 // -------------------------------------------------------------------------
143 // Mutation (deformation path)
144 // -------------------------------------------------------------------------
145
146 /**
147 * @brief Replace vertex bytes entirely and mark vertices dirty.
148 *
149 * Intended for deformation: caller writes new interleaved bytes (same
150 * stride as the original layout) and the next graphics cycle re-uploads.
151 * Does not change index data or layout.
152 *
153 * @param bytes Raw interleaved vertex bytes. Size must be a multiple of
154 * m_mesh_data.layout.stride_bytes.
155 */
156 void set_vertex_data(std::span<const uint8_t> bytes);
157
158 /**
159 * @brief Replace index data entirely and mark indices dirty.
160 *
161 * For topology mutation. Size must be a multiple of 3.
162 *
163 * @param indices New triangle index list.
164 */
165 void set_index_data(std::span<const uint32_t> indices);
166
167 /**
168 * @brief Replace both vertex and index data atomically and mark both dirty.
169 */
170 void set_mesh_data(Kakshya::MeshData data);
171
172 // -------------------------------------------------------------------------
173 // Dirty flag queries (read by MeshProcessor)
174 // -------------------------------------------------------------------------
175
176 [[nodiscard]] bool vertices_dirty() const noexcept
177 {
178 return m_vertices_dirty.load(std::memory_order_acquire);
179 }
180
181 [[nodiscard]] bool indices_dirty() const noexcept
182 {
183 return m_indices_dirty.load(std::memory_order_acquire);
184 }
185
186 void clear_vertices_dirty() noexcept
187 {
188 m_vertices_dirty.store(false, std::memory_order_release);
189 }
190
191 void clear_indices_dirty() noexcept
192 {
193 m_indices_dirty.store(false, std::memory_order_release);
194 }
195
196 // -------------------------------------------------------------------------
197 // Processor / renderer access
198 // -------------------------------------------------------------------------
199
200 [[nodiscard]] std::shared_ptr<MeshProcessor> get_mesh_processor() const
201 {
202 return m_mesh_processor;
203 }
204
205 /**
206 * @brief Bind a diffuse texture to the render processor, if present.
207 *
208 * The binding name must match a descriptor slot in the shader. If the
209 * render processor is not yet created, the texture will be bound on
210 * creation if the default_texture_binding matches.
211 */
212 void bind_diffuse_texture(
213 std::shared_ptr<Core::VKImage> image,
214 std::string_view binding_name = "diffuseTex");
215
216 /* Check if a diffuse texture is bound. */
217 [[nodiscard]] bool has_diffuse_texture() const noexcept { return m_diffuse_texture != nullptr; }
218
219 /* Diffuse texture is optional, so may return nullptr. */
220 [[nodiscard]] std::shared_ptr<Core::VKImage> get_diffuse_texture() const noexcept { return m_diffuse_texture; }
221
222private:
223 friend class MeshProcessor;
224
226 std::shared_ptr<Nodes::GpuSync::MeshWriterNode> m_node;
227 std::shared_ptr<Core::VKImage> m_diffuse_texture;
228 std::string m_diffuse_binding { "diffuseTex" };
229
230 std::atomic<bool> m_vertices_dirty { true };
231 std::atomic<bool> m_indices_dirty { true };
232
233 std::shared_ptr<MeshProcessor> m_mesh_processor;
234};
235
236} // namespace MayaFlux::Buffers
IO::ImageData image
Definition Decoder.cpp:47
std::vector< uint32_t > indices
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
const Kakshya::MeshData & get_mesh_data() const noexcept
Read-only access to the owning MeshData.
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:76
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.