1 #ifndef DONUT_GRAPHICS_MESH_HPP
2 #define DONUT_GRAPHICS_MESH_HPP
15 #include <type_traits>
27 std::is_same_v<T, u32> ||
28 std::is_same_v<T, float> ||
29 std::is_same_v<T, vec2> ||
30 std::is_same_v<T, vec3> ||
31 std::is_same_v<T, vec4> ||
32 std::is_same_v<T, mat2> ||
33 std::is_same_v<T, mat3> ||
34 std::is_same_v<T, mat4>;
81 class MeshStatePreserver {
83 [[nodiscard]] MeshStatePreserver() noexcept;
84 ~MeshStatePreserver();
86 MeshStatePreserver(const MeshStatePreserver&) = delete;
87 MeshStatePreserver(MeshStatePreserver&&) = delete;
88 MeshStatePreserver& operator=(const MeshStatePreserver&) = delete;
89 MeshStatePreserver& operator=(MeshStatePreserver&&) = delete;
92 std::int32_t vertexArrayBinding = 0;
93 std::int32_t arrayBufferBinding = 0;
96 void bindVertexArray(
Handle handle);
97 void bindArrayBuffer(
Handle handle);
98 void bindElementArrayBuffer(
Handle handle);
99 void enableVertexAttribArray(std::uint32_t index);
100 void vertexAttribDivisor(std::uint32_t index, std::uint32_t divisor);
101 void vertexAttribPointerUint(std::uint32_t index, std::
size_t count, std::
size_t stride, std::uintptr_t offset);
102 void vertexAttribPointerFloat(std::uint32_t index, std::
size_t count, std::
size_t stride, std::uintptr_t offset);
103 void bufferArrayBufferData(std::
size_t size, const
void* data,
MeshBufferUsage usage);
104 void bufferElementArrayBufferData(std::
size_t size, const
void* data,
MeshBufferUsage usage);
106 template <
bool IsInstance>
107 inline
void enableVertexAttribute(std::uint32_t index) {
108 enableVertexAttribArray(index);
109 if constexpr (IsInstance) {
110 vertexAttribDivisor(index, 1);
114 template <
bool IsInstance,
typename T>
115 [[nodiscard]]
inline std::uint32_t setupVertexAttribute(std::uint32_t index, std::size_t stride, std::uintptr_t offset) {
116 if constexpr (std::is_same_v<T, std::uint32_t>) {
117 enableVertexAttribute<IsInstance>(index);
118 vertexAttribPointerUint(index++, 1, stride, offset);
119 }
else if constexpr (std::is_same_v<T, float>) {
120 enableVertexAttribute<IsInstance>(index);
121 vertexAttribPointerFloat(index++, 1, stride, offset);
122 }
else if constexpr (std::is_same_v<T, vec2>) {
123 enableVertexAttribute<IsInstance>(index);
124 vertexAttribPointerFloat(index++, 2, stride, offset);
125 }
else if constexpr (std::is_same_v<T, vec3>) {
126 enableVertexAttribute<IsInstance>(index);
127 vertexAttribPointerFloat(index++, 3, stride, offset);
128 }
else if constexpr (std::is_same_v<T, vec4>) {
129 enableVertexAttribute<IsInstance>(index);
130 vertexAttribPointerFloat(index++, 4, stride, offset);
131 }
else if constexpr (std::is_same_v<T, mat2>) {
132 enableVertexAttribute<IsInstance>(index);
133 vertexAttribPointerFloat(index++, 2, stride, offset);
134 enableVertexAttribute<IsInstance>(index);
135 vertexAttribPointerFloat(index++, 2, stride, offset +
sizeof(
float) * 2);
136 }
else if constexpr (std::is_same_v<T, mat3>) {
137 enableVertexAttribute<IsInstance>(index);
138 vertexAttribPointerFloat(index++, 3, stride, offset);
139 enableVertexAttribute<IsInstance>(index);
140 vertexAttribPointerFloat(index++, 3, stride, offset +
sizeof(
float) * 3);
141 enableVertexAttribute<IsInstance>(index);
142 vertexAttribPointerFloat(index++, 3, stride, offset +
sizeof(
float) * 6);
143 }
else if constexpr (std::is_same_v<T, mat4>) {
144 enableVertexAttribute<IsInstance>(index);
145 vertexAttribPointerFloat(index++, 4, stride, offset);
146 enableVertexAttribute<IsInstance>(index);
147 vertexAttribPointerFloat(index++, 4, stride, offset +
sizeof(
float) * 4);
148 enableVertexAttribute<IsInstance>(index);
149 vertexAttribPointerFloat(index++, 4, stride, offset +
sizeof(
float) * 8);
150 enableVertexAttribute<IsInstance>(index);
151 vertexAttribPointerFloat(index++, 4, stride, offset +
sizeof(
float) * 12);
153 throw std::invalid_argument{
"Invalid vertex attribute type!"};
158 template <
typename Tuple>
159 struct is_vertex_attributes : std::false_type {};
161 template <
typename... Ts>
162 struct is_vertex_attributes<std::tuple<Ts...>> : std::bool_constant<(vertex_attribute<std::remove_cvref_t<Ts>> && ...)> {};
164 template <
typename Tuple>
165 inline constexpr
bool is_vertex_attributes_v = is_vertex_attributes<Tuple>::value;
174 template <
typename T>
176 std::is_aggregate_v<T> &&
177 std::is_standard_layout_v<T> &&
178 detail::is_vertex_attributes_v<decltype(reflection::fields(std::declval<T>()))>;
190 template <
typename T>
192 std::is_same_v<T, NoIndex> ||
193 std::is_same_v<T, u8> ||
194 std::is_same_v<T, u16> ||
195 std::is_same_v<T, u32>;
207 template <
typename T>
209 std::is_aggregate_v<T> &&
210 std::is_standard_layout_v<T> &&
211 detail::is_vertex_attributes_v<decltype(reflection::fields(std::declval<T>()))>;
225 template <
typename Vertex,
typename Index = NoIndex,
typename Instance = NoInstance>
228 static_assert(mesh_vertex<Vertex>,
"Mesh template parameter \"Vertex\" must be a valid vertex type.");
229 static_assert(mesh_index<Index>,
"Mesh template parameter \"Index\" must be a valid index type.");
230 static_assert(mesh_instance<Instance>,
"Mesh template parameter \"Instance\" must be a valid instance type.");
233 static constexpr
bool IS_INDEXED = !std::is_same_v<Index, NoIndex>;
236 static constexpr
bool IS_INSTANCED = !std::is_same_v<Instance, NoInstance>;
245 Mesh(
MeshBufferUsage verticesUsage, std::span<const Vertex> vertices) requires(!IS_INDEXED && !IS_INSTANCED) {
246 const detail::MeshStatePreserver preserver{};
247 detail::bindVertexArray(vao.get());
248 bufferVertexData(verticesUsage, vertices, 0);
262 const detail::MeshStatePreserver preserver{};
263 detail::bindVertexArray(vao.get());
264 bufferVertexData(verticesUsage, vertices, 0);
265 bufferIndexData(indicesUsage, indices);
280 const detail::MeshStatePreserver preserver{};
281 detail::bindVertexArray(vao.get());
282 bufferVertexData(verticesUsage, vertices, 0);
283 bufferInstanceData(instancesUsage, instances,
static_cast<std::uint32_t
>(reflection::aggregate_size_v<Vertex>));
300 std::span<const Instance> instances) requires(IS_INDEXED && IS_INSTANCED) {
301 const detail::MeshStatePreserver preserver{};
302 detail::bindVertexArray(vao.get());
303 bufferVertexData(verticesUsage, vertices, 0);
304 bufferIndexData(indicesUsage, indices);
305 bufferInstanceData(instancesUsage, instances,
static_cast<std::uint32_t
>(reflection::aggregate_size_v<Vertex>));
318 const detail::MeshStatePreserver preserver{};
319 detail::bindVertexArray(vao.get());
320 detail::bindArrayBuffer(vbo.get());
321 detail::bufferArrayBufferData(
sizeof(Vertex) * vertices.size(), vertices.data(), verticesUsage);
337 const detail::MeshStatePreserver preserver{};
338 detail::bindVertexArray(vao.get());
339 detail::bindArrayBuffer(vbo.get());
340 detail::bufferArrayBufferData(
sizeof(Vertex) * vertices.size(), vertices.data(), verticesUsage);
341 detail::bindElementArrayBuffer(ebo.get());
342 detail::bufferElementArrayBufferData(
sizeof(Index) * indices.size(), indices.data(), indicesUsage);
402 void bufferVertexData(
MeshBufferUsage usage, std::span<const Vertex> vertices, std::uint32_t attributeOffset) {
403 static_assert(std::is_aggregate_v<Vertex>,
"Vertex type must be an aggregate type!");
404 static_assert(std::is_standard_layout_v<Vertex>,
"Vertex type must have standard layout!");
405 detail::bindArrayBuffer(vbo.get());
406 detail::bufferArrayBufferData(
sizeof(Vertex) * vertices.size(), vertices.data(), usage);
407 Vertex dummyVertex{};
409 const std::byte*
const basePointer =
reinterpret_cast<const std::byte*
>(std::addressof(dummyVertex));
410 const std::byte*
const attributePointer =
reinterpret_cast<const std::byte*
>(std::addressof(dummyField));
411 const std::uintptr_t offset =
static_cast<std::uintptr_t
>(attributePointer - basePointer);
412 attributeOffset = detail::setupVertexAttribute<false, T>(attributeOffset,
sizeof(Vertex), offset);
416 void bufferIndexData(
MeshBufferUsage usage, std::span<const Index> indices) requires(IS_INDEXED) {
417 detail::bindElementArrayBuffer(ebo.get());
418 detail::bufferElementArrayBufferData(
sizeof(Index) * indices.size(), indices.data(), usage);
421 void bufferInstanceData(
MeshBufferUsage usage, std::span<const Instance> instances, std::uint32_t attributeOffset) requires(IS_INSTANCED) {
422 static_assert(std::is_aggregate_v<Instance>,
"Instance type must be an aggregate type!");
423 static_assert(std::is_standard_layout_v<Instance>,
"Instance type must have standard layout!");
424 detail::bindArrayBuffer(ibo.get());
425 detail::bufferArrayBufferData(
sizeof(Instance) * instances.size(), instances.data(), usage);
426 Instance dummyInstance{};
428 const std::byte*
const basePointer =
reinterpret_cast<const std::byte*
>(std::addressof(dummyInstance));
429 const std::byte*
const attributePointer =
reinterpret_cast<const std::byte*
>(std::addressof(dummyField));
430 const std::uintptr_t offset =
static_cast<std::uintptr_t
>(attributePointer - basePointer);
431 attributeOffset = detail::setupVertexAttribute<true, T>(attributeOffset,
sizeof(Instance), offset);
437 [[no_unique_address]] std::conditional_t<IS_INDEXED, Buffer, NoIndex> ebo{};
438 [[no_unique_address]] std::conditional_t<IS_INSTANCED, Buffer, NoInstance> ibo{};
Generic abstraction of a GPU vertex array object and its associated buffers.
Definition: Mesh.hpp:226
Mesh(MeshBufferUsage verticesUsage, MeshBufferUsage indicesUsage, std::span< const Vertex > vertices, std::span< const Index > indices) requires(IS_INDEXED &&!IS_INSTANCED)
Constructor for meshes that have a vertex buffer and an index buffer.
Definition: Mesh.hpp:261
Mesh(MeshBufferUsage verticesUsage, MeshBufferUsage instancesUsage, std::span< const Vertex > vertices, std::span< const Instance > instances) requires(!IS_INDEXED &&IS_INSTANCED)
Constructor for meshes that have a vertex buffer and an instance buffer.
Definition: Mesh.hpp:278
Handle getIndexBuffer() const noexcept requires(IS_INDEXED)
Get an opaque handle to the GPU representation of the index buffer.
Definition: Mesh.hpp:369
Handle get() const noexcept
Get an opaque handle to the GPU representation of the vertex array.
Definition: Mesh.hpp:397
Handle getVertexBuffer() const noexcept
Get an opaque handle to the GPU representation of the vertex buffer.
Definition: Mesh.hpp:355
void setVertices(MeshBufferUsage verticesUsage, std::span< const Vertex > vertices) noexcept requires(!IS_INDEXED)
Set the contents of the vertex buffer.
Definition: Mesh.hpp:317
Handle getInstanceBuffer() const noexcept requires(IS_INSTANCED)
Get an opaque handle to the GPU representation of the instance buffer.
Definition: Mesh.hpp:383
Mesh(MeshBufferUsage verticesUsage, MeshBufferUsage indicesUsage, MeshBufferUsage instancesUsage, std::span< const Vertex > vertices, std::span< const Index > indices, std::span< const Instance > instances) requires(IS_INDEXED &&IS_INSTANCED)
Constructor for meshes that have a vertex buffer, an index buffer and an instance buffer.
Definition: Mesh.hpp:299
void setVertices(MeshBufferUsage verticesUsage, MeshBufferUsage indicesUsage, std::span< const Vertex > vertices, std::span< const Index > indices) noexcept requires(IS_INDEXED)
Set the contents of the vertex and index buffers.
Definition: Mesh.hpp:336
Mesh(MeshBufferUsage verticesUsage, std::span< const Vertex > vertices) requires(!IS_INDEXED &&!IS_INSTANCED)
Constructor for meshes that only have a vertex buffer.
Definition: Mesh.hpp:245
concept vertex_attribute
Concept that checks if a type is a valid vertex attribute.
Definition: Mesh.hpp:26
concept mesh_vertex
Concept that checks if a type is a valid vertex type.
Definition: Mesh.hpp:175
std::uint32_t Handle
Generic GPU resource handle.
Definition: Handle.hpp:11
MeshBufferUsage
Hint to the graphics driver implementation regarding the intended access pattern of a particular GPU ...
Definition: Mesh.hpp:44
MeshPrimitiveType
Specification of which kind of graphical primitive is defined by an associated sequence of vertices i...
Definition: Mesh.hpp:60
@ TRIANGLE_STRIP
Each point, except the first two, forms a filled triangle with the previous two points.
@ POINTS
Individual points.
@ LINE_LOOP
Each point forms a line segment to the previous point, where the last point connects back to the firs...
@ TRIANGLES
Each consecutive triple of points forms an individual filled triangle.
@ LINES
Each consecutive pair of points forms an individual line segment.
@ LINE_STRIP
Each point, except the first, forms a line segment to the previous point.
concept mesh_instance
Concept that checks if a type is a valid instance type.
Definition: Mesh.hpp:208
concept mesh_index
Concept that checks if a type is a valid index type.
Definition: Mesh.hpp:191
MeshIndexType
Specification of which type of indices is used in the index buffer of a particular Mesh.
Definition: Mesh.hpp:73
@ U8
Unsigned 8-bit integer.
@ U32
Unsigned 32-bit integer.
@ U16
Unsigned 16-bit integer.
constexpr auto fields(auto &&aggregate) noexcept
Get a tuple of references to each of the fields of an aggregate.
Definition: reflection.hpp:86
constexpr void forEach(auto &&tuple, auto fn)
Execute a function once for each element in a given tuple, sequentially.
Definition: reflection.hpp:207
Tag type for specifying that a Mesh does not have an index buffer.
Definition: Mesh.hpp:183
Tag type for specifying that a Mesh does not have an instance buffer.
Definition: Mesh.hpp:200