libmx2 2.2.1
C++20 multimedia and game-engine library (SDL2 / OpenGL / Vulkan)
Loading...
Searching...
No Matches
vk_model.hpp
Go to the documentation of this file.
1/**
2 * @file vk_model.hpp
3 * @brief Vulkan 3-D model loader and GPU buffer manager.
4 *
5 * Parses a Wavefront OBJ-format mesh, uploads vertex and index data to
6 * device-local Vulkan buffers, and provides a draw call that records
7 * bind/draw commands into a command buffer.
8 */
9#ifndef _VK_MODEL_MX_H__
10#define _VK_MODEL_MX_H__
11
12#include "config.h"
13
14#ifdef WITH_MOLTEN
15#include <vulkan/vulkan.h>
16#else
17#include "volk.h"
18#endif
19
20#include "exception.hpp"
21#include <vector>
22#include <string>
23#include <fstream>
24#include <sstream>
25#include <stdexcept>
26#include <cstring>
27#include <unordered_map>
28#include <functional>
29#include <iostream>
30#include "util.hpp"
31
32namespace mx {
33
34/**
35 * @struct VKVertex
36 * @brief Per-vertex data uploaded to the Vulkan vertex buffer.
37 */
38 struct VKVertex {
39 float pos[3]; ///< 3-D position (x, y, z).
40 float texCoord[2]; ///< UV texture coordinates.
41 float normal[3]; ///< Surface normal vector.
42
43 /**
44 * @brief Byte-wise equality comparison for deduplication.
45 * @param other Vertex to compare against.
46 * @return @c true if every field is identical.
47 */
48 bool operator==(const VKVertex &other) const {
49 return std::memcmp(this, &other, sizeof(VKVertex)) == 0;
50 }
51 };
52
53/**
54 * @struct VKVertexHash
55 * @brief Hash functor for VKVertex, suitable for use in unordered_map.
56 *
57 * Combines std::hash<float> results using a Boost-style seed mix to
58 * produce a stable hash of all vertex fields.
59 */
60 struct VKVertexHash {
61 /**
62 * @brief Compute the hash of a VKVertex.
63 * @param v Vertex to hash.
64 * @return Hash value.
65 */
66 std::size_t operator()(const VKVertex &v) const {
67 std::size_t seed = 0;
68 auto hc = [&seed](float val) {
69 std::hash<float> h;
70 seed ^= h(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
71 };
72 for (int i = 0; i < 3; ++i) hc(v.pos[i]);
73 for (int i = 0; i < 2; ++i) hc(v.texCoord[i]);
74 for (int i = 0; i < 3; ++i) hc(v.normal[i]);
75 return seed;
76 }
77 };
78
79
80 /* Load textures per sub-mesh
81for (size_t i = 0; i < model.subMeshCount(); ++i) {
82 uint32_t texIdx = model.subMesh(i).textureIndex;
83 // bind texture[texIdx] to descriptor set
84 model.drawSubMesh(cmd, i);
85}*/
86
87/**
88 * @struct SubMesh
89 * @brief A range of indices within the shared index buffer, with its own texture.
90 */
91 struct SubMesh {
92 uint32_t firstIndex = 0; ///< Offset into the index buffer.
93 uint32_t indexCount = 0; ///< Number of indices in this sub-mesh.
94 uint32_t textureIndex = 0; ///< Texture slot for this sub-mesh.
95 std::string materialName; ///< Material name from OBJ usemtl directive.
96 };
97
98/**
99 * @struct MXMaterial
100 * @brief Parsed Wavefront MTL material properties.
101 */
102 struct MXMaterial {
103 std::string name; ///< Material name (newmtl).
104 float ka[3] = {0.2f, 0.2f, 0.2f}; ///< Ambient colour.
105 float kd[3] = {0.8f, 0.8f, 0.8f}; ///< Diffuse colour.
106 float ks[3] = {0.0f, 0.0f, 0.0f}; ///< Specular colour.
107 float ns = 30.0f; ///< Specular exponent.
108 float d = 1.0f; ///< Opacity (dissolve).
109 int illum = 2; ///< Illumination model.
110 std::string map_kd; ///< Diffuse texture filename.
111 };
112
113/**
114 * @class MXModel
115 * @brief Vulkan mesh loader and GPU resource manager.
116 *
117 * Loads a Wavefront OBJ file, optionally deduplicates vertices
118 * (compressIndices), uploads the geometry to device-local Vulkan buffers,
119 * and provides a draw() helper that records indexed draw commands.
120 * Supports multiple sub-meshes (groups) with separate texture indices.
121 */
122 class MXModel {
123 public:
124 /** @brief Default constructor. */
125 MXModel() = default;
126 /** @brief Destructor. */
127 ~MXModel() = default;
128
129 MXModel(const MXModel&) = delete;
130 MXModel& operator=(const MXModel&) = delete;
131 MXModel(MXModel&&) = delete;
133
134 /**
135 * @brief Parse a model file (.mxmod, .mxmod.z, or .obj) and populate the vertex/index vectors.
136 * @param path Path to the model file.
137 * @param positionScale Uniform scale applied to all vertex positions.
138 */
139 void load(const std::string &path, float positionScale = 1.0f);
140
141 /**
142 * @brief Upload parsed geometry to device-local Vulkan buffers.
143 * @param device Logical device.
144 * @param physicalDevice Physical device (for memory type queries).
145 * @param commandPool Command pool for staging commands.
146 * @param graphicsQueue Queue used to submit staging commands.
147 */
148 void upload(VkDevice device, VkPhysicalDevice physicalDevice,
149 VkCommandPool commandPool, VkQueue graphicsQueue);
150
151 /**
152 * @brief Free all Vulkan buffer resources.
153 * @param device Logical device that owns the buffers.
154 */
155 void cleanup(VkDevice device);
156
157 /**
158 * @brief Record a bind + indexed draw into a command buffer.
159 * @param cmd Active Vulkan command buffer (must be in recording state).
160 */
161 void draw(VkCommandBuffer cmd) const;
162
163 /**
164 * @brief Deduplicate vertices and rebuild the index list.
165 *
166 * Reduces GPU bandwidth by removing duplicate VKVertex entries and
167 * rewriting the index buffer to reference the canonical copies.
168 */
169 void compressIndices();
170
171 /** @return Read-only reference to the vertex array. */
172 const std::vector<VKVertex>& vertices() const { return vertices_; }
173 /** @return Read-only reference to the index array. */
174 const std::vector<uint32_t>& indices() const { return indices_; }
175 /** @return Number of indices in the index buffer. */
176 uint32_t indexCount() const { return static_cast<uint32_t>(indices_.size()); }
177
178 /** @return Vulkan vertex buffer handle. */
179 VkBuffer vertexBuffer() const { return vertexBuffer_; }
180 /** @return Vulkan index buffer handle. */
181 VkBuffer indexBuffer() const { return indexBuffer_; }
182 /** @return Texture sub-mesh index encoded in the OBJ file (first sub-mesh). */
183 uint32_t textureIndex() const { return textureIndex_; }
184
185 /** @return Number of sub-meshes (groups) in the model. */
186 size_t subMeshCount() const { return subMeshes_.size(); }
187 /** @return Read-only reference to a sub-mesh by index. */
188 const SubMesh& subMesh(size_t i) const { return subMeshes_[i]; }
189 /** @return Read-only reference to all sub-meshes. */
190 const std::vector<SubMesh>& subMeshes() const { return subMeshes_; }
191
192 /**
193 * @brief Draw a single sub-mesh (binds vertex/index buffers and issues indexed draw).
194 * @param cmd Active command buffer.
195 * @param index Sub-mesh index.
196 */
197 void drawSubMesh(VkCommandBuffer cmd, size_t index) const;
198
199 /** @return Read-only reference to parsed MTL materials. */
200 const std::vector<MXMaterial>& materials() const { return materials_; }
201 /** @return Path to the MTL library referenced by the OBJ, or empty. */
202 const std::string& mtlLibPath() const { return mtlLibPath_; }
203
204 private:
205 std::vector<VKVertex> vertices_; ///< Parsed vertex data.
206 std::vector<uint32_t> indices_; ///< Parsed index data.
207 uint32_t textureIndex_ = 0; ///< Sub-mesh texture index.
208 std::vector<SubMesh> subMeshes_; ///< Sub-mesh ranges (one per group).
209 std::vector<MXMaterial> materials_; ///< Parsed MTL materials.
210 std::string mtlLibPath_; ///< Path to .mtl file from OBJ mtllib.
211
212 VkBuffer vertexBuffer_ = VK_NULL_HANDLE;
213 VkDeviceMemory vertexBufferMemory_ = VK_NULL_HANDLE;
214 VkBuffer indexBuffer_ = VK_NULL_HANDLE;
215 VkDeviceMemory indexBufferMemory_ = VK_NULL_HANDLE;
216
217 /** @brief Allocate a Vulkan buffer with the specified usage and memory flags. */
218 static void createBuffer(VkDevice device, VkPhysicalDevice physicalDevice,
219 VkDeviceSize size, VkBufferUsageFlags usage,
220 VkMemoryPropertyFlags properties,
221 VkBuffer &buffer, VkDeviceMemory &bufferMemory);
222
223 /** @brief Find a memory type index satisfying typeFilter and properties. */
224 static uint32_t findMemoryType(VkPhysicalDevice physicalDevice,
225 uint32_t typeFilter, VkMemoryPropertyFlags properties);
226
227 /** @brief Copy bytes from one Vulkan buffer to another via a staging command. */
228 static void copyBuffer(VkDevice device, VkCommandPool commandPool,
229 VkQueue graphicsQueue,
230 VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
231
232 /** @brief Load a Wavefront OBJ file. */
233 void loadOBJ(const std::string &path, float positionScale);
234
235 /** @brief Load an MXMOD format file. */
236 void loadMXMOD(const std::string &path, float positionScale);
237
238 /** @brief Parse a Wavefront .mtl file and populate materials_. */
239 void loadMTL(const std::string &path);
240 };
241}
242
243#endif
std::string mtlLibPath_
Path to .mtl file from OBJ mtllib.
Definition vk_model.hpp:210
uint32_t indexCount() const
Definition vk_model.hpp:176
const std::vector< VKVertex > & vertices() const
Definition vk_model.hpp:172
static void createBuffer(VkDevice device, VkPhysicalDevice physicalDevice, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory)
Allocate a Vulkan buffer with the specified usage and memory flags.
Definition vk_model.cpp:478
void compressIndices()
Deduplicate vertices and rebuild the index list.
Definition vk_model.cpp:19
std::vector< uint32_t > indices_
Parsed index data.
Definition vk_model.hpp:206
VkBuffer vertexBuffer() const
Definition vk_model.hpp:179
std::vector< SubMesh > subMeshes_
Sub-mesh ranges (one per group).
Definition vk_model.hpp:208
VkDeviceMemory indexBufferMemory_
Definition vk_model.hpp:215
void loadOBJ(const std::string &path, float positionScale)
Load a Wavefront OBJ file.
Definition vk_model.cpp:53
std::vector< VKVertex > vertices_
Parsed vertex data.
Definition vk_model.hpp:205
const std::string & mtlLibPath() const
Definition vk_model.hpp:202
const std::vector< MXMaterial > & materials() const
Definition vk_model.hpp:200
uint32_t textureIndex() const
Definition vk_model.hpp:183
void drawSubMesh(VkCommandBuffer cmd, size_t index) const
Draw a single sub-mesh (binds vertex/index buffers and issues indexed draw).
Definition vk_model.cpp:459
static uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties)
Find a memory type index satisfying typeFilter and properties.
Definition vk_model.cpp:506
void loadMXMOD(const std::string &path, float positionScale)
Load an MXMOD format file.
Definition vk_model.cpp:217
void upload(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue)
Upload parsed geometry to device-local Vulkan buffers.
Definition vk_model.cpp:369
~MXModel()=default
Destructor.
const std::vector< uint32_t > & indices() const
Definition vk_model.hpp:174
MXModel(MXModel &&)=delete
MXModel & operator=(const MXModel &)=delete
MXModel(const MXModel &)=delete
const std::vector< SubMesh > & subMeshes() const
Definition vk_model.hpp:190
VkBuffer vertexBuffer_
Definition vk_model.hpp:212
void draw(VkCommandBuffer cmd) const
Record a bind + indexed draw into a command buffer.
Definition vk_model.cpp:446
uint32_t textureIndex_
Sub-mesh texture index.
Definition vk_model.hpp:207
VkBuffer indexBuffer_
Definition vk_model.hpp:214
VkBuffer indexBuffer() const
Definition vk_model.hpp:181
MXModel()=default
Default constructor.
std::vector< MXMaterial > materials_
Parsed MTL materials.
Definition vk_model.hpp:209
void cleanup(VkDevice device)
Free all Vulkan buffer resources.
Definition vk_model.cpp:430
void load(const std::string &path, float positionScale=1.0f)
Parse a model file (.mxmod, .mxmod.z, or .obj) and populate the vertex/index vectors.
Definition vk_model.cpp:45
size_t subMeshCount() const
Definition vk_model.hpp:186
const SubMesh & subMesh(size_t i) const
Definition vk_model.hpp:188
VkDeviceMemory vertexBufferMemory_
Definition vk_model.hpp:213
MXModel & operator=(MXModel &&)=delete
void loadMTL(const std::string &path)
Parse a Wavefront .mtl file and populate materials_.
Definition vk_model.cpp:554
static void copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue graphicsQueue, VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
Copy bytes from one Vulkan buffer to another via a staging command.
Definition vk_model.cpp:519
Exception type used throughout libmx.
Definition cfg.cpp:11
Parsed Wavefront MTL material properties.
Definition model.hpp:45
float ks[3]
Specular colour.
Definition model.hpp:49
int illum
Illumination model.
Definition model.hpp:52
float ka[3]
Ambient colour.
Definition model.hpp:47
float ns
Specular exponent.
Definition model.hpp:50
std::string map_kd
Diffuse texture filename.
Definition model.hpp:53
float d
Opacity (dissolve).
Definition model.hpp:51
float kd[3]
Diffuse colour.
Definition model.hpp:48
std::string name
Material name (newmtl).
Definition model.hpp:46
A range of indices within the shared index buffer, with its own texture.
Definition vk_model.hpp:91
std::string materialName
Material name from OBJ usemtl directive.
Definition vk_model.hpp:95
uint32_t textureIndex
Texture slot for this sub-mesh.
Definition vk_model.hpp:94
uint32_t indexCount
Number of indices in this sub-mesh.
Definition vk_model.hpp:93
uint32_t firstIndex
Offset into the index buffer.
Definition vk_model.hpp:92
Hash functor for VKVertex, suitable for use in unordered_map.
Definition vk_model.hpp:60
std::size_t operator()(const VKVertex &v) const
Compute the hash of a VKVertex.
Definition vk_model.hpp:66
Per-vertex data uploaded to the Vulkan vertex buffer.
Definition vk_model.hpp:38
bool operator==(const VKVertex &other) const
Byte-wise equality comparison for deduplication.
Definition vk_model.hpp:48
float normal[3]
Surface normal vector.
Definition vk_model.hpp:41
float texCoord[2]
UV texture coordinates.
Definition vk_model.hpp:40
float pos[3]
3-D position (x, y, z).
Definition vk_model.hpp:39
General SDL utility helpers, file I/O, and compression utilities.