MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VertexLayout.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "NDData.hpp"
4
5namespace MayaFlux::Kakshya {
6
7/**
8 * @struct VertexAttributeLayout
9 * @brief Semantic description of a single vertex attribute
10 *
11 * Describes one component of vertex data without exposing Vulkan types.
12 * The modality encodes everything needed (3D position, 2D texture coords, etc.)
13 */
15 /**
16 * Semantic type of this attribute
17 * e.g., VERTEX_POSITIONS_3D → vec3, TEXTURE_COORDS_2D → vec2
18 */
20
21 /**
22 * Byte offset of this attribute within one vertex
23 * e.g., position at 0, normal at 12, color at 24
24 */
25 uint32_t offset_in_vertex = 0;
26
27 /**
28 * Optional name for debugging/introspection
29 * e.g., "position", "normal", "texCoord"
30 */
31 std::string name;
32};
33
34/**
35 * @struct VertexLayout
36 * @brief Complete description of vertex data layout in a buffer
37 *
38 * Fully semantic and backend-agnostic. Portal layer translates to Vulkan.
39 * Derived from buffer's modality and dimensions.
40 */
42 /**
43 * Total number of vertices in this buffer
44 */
45 uint32_t vertex_count = 0;
46
47 /**
48 * Total bytes per vertex (stride in Vulkan terms)
49 * e.g., 3 floats (position) + 3 floats (normal) = 24 bytes
50 */
51 uint32_t stride_bytes = 0;
52
53 /**
54 * All attributes that make up one vertex
55 * Ordered by shader location (0, 1, 2, ...)
56 */
57 std::vector<VertexAttributeLayout> attributes;
58
59 /**
60 * @brief Word offset of the attribute matching the given modality, if any.
61 * @param modality Attribute kind to search for, e.g.
62 * DataModality::VERTEX_POSITIONS_3D or ::VERTEX_COLORS_RGB.
63 * @return Byte offset divided by 4, or nullopt when no attribute of that
64 * modality is present or its offset is not word-aligned.
65 *
66 * The alignment check exists for GPU code addressing this record as a
67 * flat float/uint array rather than through a struct: GpuFieldOperator
68 * and the particle spatial hash both index by word, not byte.
69 */
70 [[nodiscard]] std::optional<uint32_t> find_word_offset(DataModality modality) const
71 {
72 for (const auto& attr : attributes) {
73 if (attr.component_modality == modality) {
74 if (attr.offset_in_vertex % 4 != 0) {
75 return std::nullopt;
76 }
77 return attr.offset_in_vertex / 4;
78 }
79 }
80 return std::nullopt;
81 }
82
83 /**
84 * @brief Helper: compute stride from attributes if not explicitly set
85 */
87 {
88 if (stride_bytes == 0 && !attributes.empty()) {
89 uint32_t max_offset = 0;
90 uint32_t last_size = 0;
91
92 for (const auto& attr : attributes) {
93 uint32_t attr_size = modality_size_bytes(attr.component_modality);
94 max_offset = std::max(max_offset, attr.offset_in_vertex);
95 if (attr.offset_in_vertex == max_offset) {
96 last_size = attr_size;
97 }
98 }
99
100 stride_bytes = max_offset + last_size;
101 }
102 }
103
104 /**
105 * @brief Factory: layout for PointVertex (position, color, size, uv, normal, tangent)
106 *
107 * Matches PointVertex field order exactly (60 bytes):
108 * loc 0 offset 0 VERTEX_POSITIONS_3D position
109 * loc 1 offset 12 VERTEX_COLORS_RGB color
110 * loc 2 offset 24 UNKNOWN size
111 * loc 3 offset 28 TEXTURE_COORDS_2D uv
112 * loc 4 offset 36 VERTEX_NORMALS_3D normal
113 * loc 5 offset 48 VERTEX_TANGENTS_3D tangent
114 *
115 * @param stride Override stride (default: sizeof(PointVertex) == 60)
116 */
117 static VertexLayout for_points(uint32_t stride = 60)
118 {
119 VertexLayout layout;
120 layout.stride_bytes = stride;
121
122 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_POSITIONS_3D,
123 .offset_in_vertex = 0,
124 .name = "position" });
125
126 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_COLORS_RGB,
127 .offset_in_vertex = 12,
128 .name = "color" });
129
130 layout.attributes.push_back({ .component_modality = DataModality::SCALAR_F32,
131 .offset_in_vertex = 24,
132 .name = "size" });
133
134 layout.attributes.push_back({ .component_modality = DataModality::TEXTURE_COORDS_2D,
135 .offset_in_vertex = 28,
136 .name = "uv" });
137
138 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_NORMALS_3D,
139 .offset_in_vertex = 36,
140 .name = "normal" });
141
142 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_TANGENTS_3D,
143 .offset_in_vertex = 48,
144 .name = "tangent" });
145
146 return layout;
147 }
148
149 /**
150 * @brief Factory: layout for LineVertex (position, color, thickness, uv, normal, tangent)
151 *
152 * Matches LineVertex field order exactly (60 bytes):
153 * loc 0 offset 0 VERTEX_POSITIONS_3D position
154 * loc 1 offset 12 VERTEX_COLORS_RGB color
155 * loc 2 offset 24 UNKNOWN thickness
156 * loc 3 offset 28 TEXTURE_COORDS_2D uv
157 * loc 4 offset 36 VERTEX_NORMALS_3D normal
158 * loc 5 offset 48 VERTEX_TANGENTS_3D tangent
159 *
160 * @param stride Override stride (default: sizeof(LineVertex) == 60)
161 */
162 static VertexLayout for_lines(uint32_t stride = 60)
163 {
164 VertexLayout layout;
165 layout.stride_bytes = stride;
166
167 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_POSITIONS_3D,
168 .offset_in_vertex = 0,
169 .name = "position" });
170
171 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_COLORS_RGB,
172 .offset_in_vertex = 12,
173 .name = "color" });
174
175 layout.attributes.push_back({ .component_modality = DataModality::SCALAR_F32,
176 .offset_in_vertex = 24,
177 .name = "thickness" });
178
179 layout.attributes.push_back({ .component_modality = DataModality::TEXTURE_COORDS_2D,
180 .offset_in_vertex = 28,
181 .name = "uv" });
182
183 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_NORMALS_3D,
184 .offset_in_vertex = 36,
185 .name = "normal" });
186
187 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_TANGENTS_3D,
188 .offset_in_vertex = 48,
189 .name = "tangent" });
190 return layout;
191 }
192
193 /**
194 * @brief Factory: layout for MeshVertex (position, color, weight, uv, normal, tangent)
195 *
196 * Matches MeshVertex field order exactly (60 bytes):
197 * loc 0 offset 0 VERTEX_POSITIONS_3D position
198 * loc 1 offset 12 VERTEX_COLORS_RGB color
199 * loc 2 offset 24 SCALAR_F32 weight
200 * loc 3 offset 28 TEXTURE_COORDS_2D uv
201 * loc 4 offset 36 VERTEX_NORMALS_3D normal
202 * loc 5 offset 48 VERTEX_TANGENTS_3D tangent
203 *
204 * Identical offset table to for_points() and for_lines().
205 * FieldOperator processes all three types with the same constants.
206 *
207 * @param stride Override stride (default: sizeof(MeshVertex) == 60)
208 */
209 static VertexLayout for_meshes(uint32_t stride = 60)
210 {
211 VertexLayout layout;
212 layout.stride_bytes = stride;
213
214 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_POSITIONS_3D,
215 .offset_in_vertex = 0,
216 .name = "position" });
217
218 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_COLORS_RGB,
219 .offset_in_vertex = 12,
220 .name = "color" });
221
222 layout.attributes.push_back({ .component_modality = DataModality::SCALAR_F32,
223 .offset_in_vertex = 24,
224 .name = "weight" });
225
226 layout.attributes.push_back({ .component_modality = DataModality::TEXTURE_COORDS_2D,
227 .offset_in_vertex = 28,
228 .name = "uv" });
229
230 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_NORMALS_3D,
231 .offset_in_vertex = 36,
232 .name = "normal" });
233
234 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_TANGENTS_3D,
235 .offset_in_vertex = 48,
236 .name = "tangent" });
237
238 return layout;
239 }
240
241 /**
242 * @brief Factory: layout for raw vertex data with common attributes
243 *
244 * Matches PointVertex/LineVertex/MeshVertex field order exactly (60 bytes):
245 * loc 0 offset 0 VERTEX_POSITIONS_3D position
246 * loc 1 offset 12 VERTEX_COLORS_RGB color
247 * loc 2 offset 24 SCALAR_F32 scalar
248 * loc 3 offset 28 TEXTURE_COORDS_2D uv
249 * loc 4 offset 36 VERTEX_NORMALS_3D normal
250 * loc 5 offset 48 VERTEX_TANGENTS_3D tangent
251 *
252 * Use when vertex data is pre-packed or doesn't fit standard structs.
253 *
254 * @param stride Override stride (default: 60 bytes for these attributes)
255 */
256 static VertexLayout for_raw(uint32_t stride = 60)
257 {
258 VertexLayout layout;
259 layout.stride_bytes = stride;
260 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_POSITIONS_3D,
261 .offset_in_vertex = 0,
262 .name = "position" });
263 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_COLORS_RGB,
264 .offset_in_vertex = 12,
265 .name = "color" });
266 layout.attributes.push_back({ .component_modality = DataModality::SCALAR_F32,
267 .offset_in_vertex = 24,
268 .name = "scalar" });
269 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_NORMALS_3D,
270 .offset_in_vertex = 36,
271 .name = "normal" });
272 layout.attributes.push_back({ .component_modality = DataModality::VERTEX_TANGENTS_3D,
273 .offset_in_vertex = 48,
274 .name = "tangent" });
275 return layout;
276 }
277
278 /**
279 * @brief Factory: Create layout for textured quad primitives (position, texcoord).
280 * @param vertex_count Number of vertices in the buffer (default: 4).
281 * @return VertexLayout configured for Nodes::TextureQuadVertex.
282 */
284 {
285 VertexLayout layout;
286 layout.vertex_count = vertex_count;
287 layout.stride_bytes = static_cast<uint32_t>(sizeof(glm::vec3) + sizeof(glm::vec2)); // 20
288
289 layout.attributes.push_back(VertexAttributeLayout {
291 .offset_in_vertex = 0,
292 .name = "position" });
293
294 layout.attributes.push_back(VertexAttributeLayout {
296 .offset_in_vertex = static_cast<uint32_t>(sizeof(glm::vec3)),
297 .name = "texcoord" });
298
299 return layout;
300 }
301
302private:
303 /**
304 * Get size in bytes for a given modality
305 * Mirrors VKBuffer::get_format() logic
306 */
308 {
309 switch (mod) {
314 return sizeof(glm::vec3); // 12
315
317 return sizeof(glm::vec2); // 8
318
320 return sizeof(glm::vec4); // 16
321
324 return sizeof(double); // 8
325
326 default:
327 return 4; // Conservative default
328 }
329 }
330};
331
332} // namespace MayaFlux::Buffers
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:164
@ AUDIO_MULTICHANNEL
Multi-channel audio.
@ SCALAR_F32
Single-channel float data.
std::string name
Optional name for debugging/introspection e.g., "position", "normal", "texCoord".
uint32_t offset_in_vertex
Byte offset of this attribute within one vertex e.g., position at 0, normal at 12,...
DataModality component_modality
Semantic type of this attribute e.g., VERTEX_POSITIONS_3D → vec3, TEXTURE_COORDS_2D → vec2.
Semantic description of a single vertex attribute.
static VertexLayout for_raw(uint32_t stride=60)
Factory: layout for raw vertex data with common attributes.
uint32_t stride_bytes
Total bytes per vertex (stride in Vulkan terms) e.g., 3 floats (position) + 3 floats (normal) = 24 by...
uint32_t vertex_count
Total number of vertices in this buffer.
static VertexLayout for_lines(uint32_t stride=60)
Factory: layout for LineVertex (position, color, thickness, uv, normal, tangent)
static VertexLayout for_textured_quad(uint32_t vertex_count=4)
Factory: Create layout for textured quad primitives (position, texcoord).
static uint32_t modality_size_bytes(DataModality mod)
Get size in bytes for a given modality Mirrors VKBuffer::get_format() logic.
void compute_stride()
Helper: compute stride from attributes if not explicitly set.
static VertexLayout for_meshes(uint32_t stride=60)
Factory: layout for MeshVertex (position, color, weight, uv, normal, tangent)
std::optional< uint32_t > find_word_offset(DataModality modality) const
Word offset of the attribute matching the given modality, if any.
std::vector< VertexAttributeLayout > attributes
All attributes that make up one vertex Ordered by shader location (0, 1, 2, ...)
static VertexLayout for_points(uint32_t stride=60)
Factory: layout for PointVertex (position, color, size, uv, normal, tangent)
Complete description of vertex data layout in a buffer.