MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk::MXModel Class Reference

Loads OBJ/MXMOD meshes and uploads them to Vulkan buffers. More...

#include <mxvk/include/mxvk/mxvk_model.hpp>

Public Member Functions

void cleanup (VkDevice device)
 Release owned GPU buffers.
void compressIndices ()
 Remove duplicate vertices and remap indices.
void draw (VkCommandBuffer cmd) const
 Record one indexed draw for the full mesh.
void drawSubMesh (VkCommandBuffer cmd, size_t index) const
 Record one indexed draw for a sub-mesh.
void exportOBJ (const std::string &objPath, const std::string &mtlPath="") const
 Export the loaded model as Wavefront OBJ plus MTL.
VkBuffer indexBuffer () const
uint32_t indexCount () const
const std::vector< uint32_t > & indices () const
void load (const std::string &path, const std::string &textureManifestPath, const std::string &textureBasePath, float positionScale=1.0f)
 Parse model data and an optional texture manifest from disk.
void load (const std::string &path, float positionScale=1.0f)
 Parse model data from disk into CPU-side arrays.
const std::vector< MXMaterial > & materials () const
const std::string & mtlLibPath () const
 MXModel ()=default
 MXModel (const MXModel &)=delete
 MXModel (MXModel &&other) noexcept
MXModeloperator= (const MXModel &)=delete
MXModeloperator= (MXModel &&other) noexcept
const SubMeshsubMesh (size_t i) const
size_t subMeshCount () const
const std::vector< SubMesh > & subMeshes () const
void upload (VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue)
 Upload parsed geometry to device-local GPU buffers.
VkBuffer vertexBuffer () const
const std::vector< VKVertex > & vertices () const
 ~MXModel ()=default

Static Public Member Functions

static void exportOBJ (const std::string &modelPath, const std::string &textureManifestPath, const std::string &textureBasePath, const std::string &objPath, float positionScale=1.0f)
 Load a model/manifest pair and export it as Wavefront OBJ plus MTL.

Detailed Description

Loads OBJ/MXMOD meshes and uploads them to Vulkan buffers.

Definition at line 69 of file mxvk_model.hpp.

Constructor & Destructor Documentation

◆ MXModel() [1/3]

mxvk::MXModel::MXModel ( )
default

◆ ~MXModel()

mxvk::MXModel::~MXModel ( )
default

◆ MXModel() [2/3]

mxvk::MXModel::MXModel ( const MXModel & )
delete

◆ MXModel() [3/3]

mxvk::MXModel::MXModel ( MXModel && other)
noexcept

Definition at line 18 of file mxvk_model.cpp.

19 : verticesData(std::move(other.verticesData)),
20 indicesData(std::move(other.indicesData)),
21 subMeshList(std::move(other.subMeshList)),
22 materialList(std::move(other.materialList)),
23 mtlLibraryPath(std::move(other.mtlLibraryPath)),
24 vertexBufferHandle(other.vertexBufferHandle),
25 vertexBufferMemory(other.vertexBufferMemory),
26 indexBufferHandle(other.indexBufferHandle),
27 indexBufferMemory(other.indexBufferMemory) {
28 other.vertexBufferHandle = VK_NULL_HANDLE;
29 other.vertexBufferMemory = VK_NULL_HANDLE;
30 other.indexBufferHandle = VK_NULL_HANDLE;
31 other.indexBufferMemory = VK_NULL_HANDLE;
32 }

Member Function Documentation

◆ cleanup()

void mxvk::MXModel::cleanup ( VkDevice device)

Release owned GPU buffers.

Parameters
deviceLogical Vulkan device that owns these resources.

Definition at line 1211 of file mxvk_model.cpp.

1211 {
1212 if (device == VK_NULL_HANDLE) {
1213 return;
1214 }
1215
1216 const bool hadBuffers = vertexBufferHandle != VK_NULL_HANDLE || indexBufferHandle != VK_NULL_HANDLE ||
1217 vertexBufferMemory != VK_NULL_HANDLE || indexBufferMemory != VK_NULL_HANDLE;
1218 if (hadBuffers) {
1219 logMXModelStep("teardown begin", true);
1220 }
1221
1222 if (vertexBufferHandle != VK_NULL_HANDLE) {
1223 vkDestroyBuffer(device, vertexBufferHandle, nullptr);
1224 vertexBufferHandle = VK_NULL_HANDLE;
1225 }
1226 if (vertexBufferMemory != VK_NULL_HANDLE) {
1227 vkFreeMemory(device, vertexBufferMemory, nullptr);
1228 vertexBufferMemory = VK_NULL_HANDLE;
1229 }
1230
1231 if (indexBufferHandle != VK_NULL_HANDLE) {
1232 vkDestroyBuffer(device, indexBufferHandle, nullptr);
1233 indexBufferHandle = VK_NULL_HANDLE;
1234 }
1235 if (indexBufferMemory != VK_NULL_HANDLE) {
1236 vkFreeMemory(device, indexBufferMemory, nullptr);
1237 indexBufferMemory = VK_NULL_HANDLE;
1238 }
1239
1240 if (hadBuffers) {
1241 logMXModelStep("teardown complete", true);
1242 }
1243 }
void logMXModelStep(const std::string &message, bool important=false)

◆ compressIndices()

void mxvk::MXModel::compressIndices ( )

Remove duplicate vertices and remap indices.

Definition at line 685 of file mxvk_model.cpp.

685 {
686 if (verticesData.empty() || indicesData.empty()) {
687 return;
688 }
689
690 std::vector<VKVertex> uniqueVertices{};
691 uniqueVertices.reserve(verticesData.size());
692
693 std::unordered_map<VKVertex, uint32_t, VKVertexHash> vertexMap{};
694 std::vector<uint32_t> remap(verticesData.size(), 0);
695
696 for (size_t i = 0; i < verticesData.size(); ++i) {
697 const auto it = vertexMap.find(verticesData[i]);
698 if (it != vertexMap.end()) {
699 remap[i] = it->second;
700 continue;
701 }
702
703 const uint32_t nextIndex = static_cast<uint32_t>(uniqueVertices.size());
704 uniqueVertices.push_back(verticesData[i]);
705 vertexMap.emplace(verticesData[i], nextIndex);
706 remap[i] = nextIndex;
707 }
708
709 for (uint32_t &idx : indicesData) {
710 if (idx < static_cast<uint32_t>(remap.size())) {
711 idx = remap[idx];
712 }
713 }
714
715 verticesData = std::move(uniqueVertices);
716 }

◆ draw()

void mxvk::MXModel::draw ( VkCommandBuffer cmd) const

Record one indexed draw for the full mesh.

Parameters
cmdActive command buffer.

Definition at line 1245 of file mxvk_model.cpp.

1245 {
1246 if (cmd == VK_NULL_HANDLE || vertexBufferHandle == VK_NULL_HANDLE || indexBufferHandle == VK_NULL_HANDLE || indicesData.empty()) {
1247 return;
1248 }
1249
1250 const VkBuffer buffers[] = {vertexBufferHandle};
1251 const VkDeviceSize offsets[] = {0};
1252 vkCmdBindVertexBuffers(cmd, 0, 1, buffers, offsets);
1253 vkCmdBindIndexBuffer(cmd, indexBufferHandle, 0, VK_INDEX_TYPE_UINT32);
1254 vkCmdDrawIndexed(cmd, indexCount(), 1, 0, 0, 0);
1255 }
uint32_t indexCount() const

◆ drawSubMesh()

void mxvk::MXModel::drawSubMesh ( VkCommandBuffer cmd,
size_t index ) const

Record one indexed draw for a sub-mesh.

Parameters
cmdActive command buffer.
indexSub-mesh index.

Definition at line 1257 of file mxvk_model.cpp.

1257 {
1258 if (cmd == VK_NULL_HANDLE || index >= subMeshList.size() ||
1259 vertexBufferHandle == VK_NULL_HANDLE || indexBufferHandle == VK_NULL_HANDLE) {
1260 return;
1261 }
1262
1263 const VkBuffer buffers[] = {vertexBufferHandle};
1264 const VkDeviceSize offsets[] = {0};
1265 vkCmdBindVertexBuffers(cmd, 0, 1, buffers, offsets);
1266 vkCmdBindIndexBuffer(cmd, indexBufferHandle, 0, VK_INDEX_TYPE_UINT32);
1267
1268 const SubMesh &sm = subMeshList[index];
1269 if (sm.indexCount == 0) {
1270 return;
1271 }
1272
1273 vkCmdDrawIndexed(cmd, sm.indexCount, 1, sm.firstIndex, 0, 0);
1274 }

◆ exportOBJ() [1/2]

void mxvk::MXModel::exportOBJ ( const std::string & modelPath,
const std::string & textureManifestPath,
const std::string & textureBasePath,
const std::string & objPath,
float positionScale = 1.0f )
static

Load a model/manifest pair and export it as Wavefront OBJ plus MTL.

Definition at line 888 of file mxvk_model.cpp.

892 {
893 MXModel model{};
894 model.load(modelPath, textureManifestPath, textureBasePath, positionScale);
895 model.exportOBJ(objPath);
896 }
MXModel()=default

◆ exportOBJ() [2/2]

void mxvk::MXModel::exportOBJ ( const std::string & objPath,
const std::string & mtlPath = "" ) const

Export the loaded model as Wavefront OBJ plus MTL.

Parameters
objPathOutput .obj path.
mtlPathOptional output .mtl path. Defaults to objPath with .mtl extension.

Definition at line 765 of file mxvk_model.cpp.

765 {
766 if (objPath.empty()) {
767 throw mxvk::Exception("MXModel::exportOBJ objPath is empty");
768 }
769 if (verticesData.empty() || indicesData.empty()) {
770 throw mxvk::Exception("MXModel::exportOBJ requires loaded geometry");
771 }
772
773 const std::filesystem::path objOutputPath(objPath);
774 const std::filesystem::path mtlOutputPath = mtlPath.empty()
775 ? objOutputPath.parent_path() / objOutputPath.stem().concat(".mtl")
776 : std::filesystem::path(mtlPath);
777
778 if (!objOutputPath.parent_path().empty()) {
779 std::filesystem::create_directories(objOutputPath.parent_path());
780 }
781 if (!mtlOutputPath.parent_path().empty()) {
782 std::filesystem::create_directories(mtlOutputPath.parent_path());
783 }
784
785 std::ofstream objFile(objOutputPath);
786 if (!objFile.is_open()) {
787 throw mxvk::Exception("MXModel::exportOBJ failed to open OBJ file: " + objOutputPath.string());
788 }
789
790 std::ofstream mtlFile(mtlOutputPath);
791 if (!mtlFile.is_open()) {
792 throw mxvk::Exception("MXModel::exportOBJ failed to open MTL file: " + mtlOutputPath.string());
793 }
794
795 objFile << "# Exported from MXVK MXModel\n";
796 objFile << "mtllib " << mtlReferencePath(objOutputPath.string(), mtlOutputPath.string()) << "\n\n";
797
798 for (const VKVertex &vertex : verticesData) {
799 objFile << "v " << vertex.pos[0] << ' ' << vertex.pos[1] << ' ' << vertex.pos[2] << '\n';
800 }
801 objFile << '\n';
802
803 for (const VKVertex &vertex : verticesData) {
804 objFile << "vt " << vertex.texCoord[0] << ' ' << vertex.texCoord[1] << '\n';
805 }
806 objFile << '\n';
807
808 for (const VKVertex &vertex : verticesData) {
809 objFile << "vn " << vertex.normal[0] << ' ' << vertex.normal[1] << ' ' << vertex.normal[2] << '\n';
810 }
811 objFile << '\n';
812
813 std::vector<MXMaterial> outputMaterials = materialList;
814 for (size_t i = 0; i < outputMaterials.size(); ++i) {
815 if (outputMaterials[i].name.empty()) {
816 outputMaterials[i].name = "material_" + std::to_string(i);
817 }
818 }
819
820 const auto hasOutputMaterial = [&outputMaterials](const std::string &name) {
821 return std::any_of(outputMaterials.begin(), outputMaterials.end(), [&name](const MXMaterial &material) {
822 return material.name == name;
823 });
824 };
825
826 const auto ensureOutputMaterial = [&outputMaterials, &hasOutputMaterial](const std::string &name) {
827 if (hasOutputMaterial(name)) {
828 return;
829 }
830 MXMaterial material{};
831 material.name = name;
832 outputMaterials.push_back(material);
833 };
834
835 if (subMeshList.empty()) {
836 ensureOutputMaterial("material_0");
837 } else {
838 for (const SubMesh &sm : subMeshList) {
839 const std::string materialName = !sm.materialName.empty()
840 ? sm.materialName
841 : materialNameForTextureIndex(sm.textureIndex, outputMaterials);
842 ensureOutputMaterial(materialName);
843 }
844 }
845
846 for (const MXMaterial &material : outputMaterials) {
847 MXMaterial outputMaterial = material;
848 outputMaterial.map_kd = mtlTextureReferencePath(mtlOutputPath, outputMaterial.map_kd);
849 writeMTLMaterial(mtlFile, outputMaterial);
850 }
851
852 const auto emitFaces = [&](uint32_t firstIndex, uint32_t indexCount, const std::string &materialName) {
853 if (firstIndex > indicesData.size() || indexCount > indicesData.size() - firstIndex) {
854 throw mxvk::Exception("MXModel::exportOBJ submesh index range is out of bounds");
855 }
856 if ((indexCount % 3U) != 0U) {
857 throw mxvk::Exception("MXModel::exportOBJ only triangle index ranges can be exported");
858 }
859
860 objFile << "usemtl " << materialName << '\n';
861 for (uint32_t i = 0; i < indexCount; i += 3U) {
862 objFile << "f";
863 for (uint32_t j = 0; j < 3U; ++j) {
864 const uint32_t vertexIndex = indicesData[firstIndex + i + j];
865 if (vertexIndex >= static_cast<uint32_t>(verticesData.size())) {
866 throw mxvk::Exception("MXModel::exportOBJ vertex index is out of bounds");
867 }
868 const uint32_t objIndex = vertexIndex + 1U;
869 objFile << ' ' << objIndex << '/' << objIndex << '/' << objIndex;
870 }
871 objFile << '\n';
872 }
873 objFile << '\n';
874 };
875
876 if (subMeshList.empty()) {
877 emitFaces(0, static_cast<uint32_t>(indicesData.size()), "material_0");
878 } else {
879 for (const SubMesh &sm : subMeshList) {
880 const std::string materialName = !sm.materialName.empty()
881 ? sm.materialName
882 : materialNameForTextureIndex(sm.textureIndex, materialList);
883 emitFaces(sm.firstIndex, sm.indexCount, materialName);
884 }
885 }
886 }
std::string materialNameForTextureIndex(uint32_t textureIndex, const std::vector< MXMaterial > &materials)
void writeMTLMaterial(std::ostream &out, const MXMaterial &material)
std::string mtlTextureReferencePath(const std::filesystem::path &mtlPath, const std::string &texturePath)
std::string mtlReferencePath(const std::string &objPath, const std::string &mtlPath)

◆ indexBuffer()

VkBuffer mxvk::MXModel::indexBuffer ( ) const
inlinenodiscard

Definition at line 151 of file mxvk_model.hpp.

151{ return indexBufferHandle; }

◆ indexCount()

uint32_t mxvk::MXModel::indexCount ( ) const
inlinenodiscard

Definition at line 148 of file mxvk_model.hpp.

148{ return static_cast<uint32_t>(indicesData.size()); }

◆ indices()

const std::vector< uint32_t > & mxvk::MXModel::indices ( ) const
inlinenodiscard

Definition at line 147 of file mxvk_model.hpp.

147{ return indicesData; }

◆ load() [1/2]

void mxvk::MXModel::load ( const std::string & path,
const std::string & textureManifestPath,
const std::string & textureBasePath,
float positionScale = 1.0f )

Parse model data and an optional texture manifest from disk.

Parameters
pathPath to .obj, .mxmod, or .mxmod.z model.
textureManifestPathOptional .txt/.tex or MTL-like texture manifest.
textureBasePathBase path used to resolve relative texture paths.
positionScaleUniform scale applied to positions.

Definition at line 755 of file mxvk_model.cpp.

758 {
759 load(path, positionScale);
760 if (!textureManifestPath.empty()) {
761 loadTextureManifest(textureManifestPath, textureBasePath);
762 }
763 }
void load(const std::string &path, float positionScale=1.0f)
Parse model data from disk into CPU-side arrays.

◆ load() [2/2]

void mxvk::MXModel::load ( const std::string & path,
float positionScale = 1.0f )

Parse model data from disk into CPU-side arrays.

Parameters
pathPath to .obj, .mxmod, or .mxmod.z model.
positionScaleUniform scale applied to positions.

Definition at line 718 of file mxvk_model.cpp.

718 {
719 if (path.empty()) {
720 throw mxvk::Exception("MXModel::load path is empty");
721 }
722
723 logMXModelStep("load begin: " + path, true);
724
725 if (path.ends_with(".obj")) {
726 loadOBJ(path, positionScale);
727 logMXModelStep("load complete (.obj): vertices=" + std::to_string(verticesData.size()) +
728 ", indices=" + std::to_string(indicesData.size()) +
729 ", submeshes=" + std::to_string(subMeshList.size()),
730 true);
731 return;
732 }
733
734 if (path.ends_with(".mxmod")) {
735 loadMXMOD(path, positionScale);
736 logMXModelStep("load complete (.mxmod): vertices=" + std::to_string(verticesData.size()) +
737 ", indices=" + std::to_string(indicesData.size()) +
738 ", submeshes=" + std::to_string(subMeshList.size()),
739 true);
740 return;
741 }
742
743 if (path.ends_with(".mxmod.z")) {
744 loadMXMODZ(path, positionScale);
745 logMXModelStep("load complete (.mxmod.z): vertices=" + std::to_string(verticesData.size()) +
746 ", indices=" + std::to_string(indicesData.size()) +
747 ", submeshes=" + std::to_string(subMeshList.size()),
748 true);
749 return;
750 }
751
752 throw mxvk::Exception("MXModel::load unsupported file format: " + path);
753 }

◆ materials()

const std::vector< MXMaterial > & mxvk::MXModel::materials ( ) const
inlinenodiscard

Definition at line 157 of file mxvk_model.hpp.

157{ return materialList; }

◆ mtlLibPath()

const std::string & mxvk::MXModel::mtlLibPath ( ) const
inlinenodiscard

Definition at line 158 of file mxvk_model.hpp.

158{ return mtlLibraryPath; }

◆ operator=() [1/2]

MXModel & mxvk::MXModel::operator= ( const MXModel & )
delete

◆ operator=() [2/2]

MXModel & mxvk::MXModel::operator= ( MXModel && other)
noexcept

Definition at line 34 of file mxvk_model.cpp.

34 {
35 if (this == &other) {
36 return *this;
37 }
38
39 verticesData = std::move(other.verticesData);
40 indicesData = std::move(other.indicesData);
41 subMeshList = std::move(other.subMeshList);
42 materialList = std::move(other.materialList);
43 mtlLibraryPath = std::move(other.mtlLibraryPath);
44 vertexBufferHandle = other.vertexBufferHandle;
45 vertexBufferMemory = other.vertexBufferMemory;
46 indexBufferHandle = other.indexBufferHandle;
47 indexBufferMemory = other.indexBufferMemory;
48
49 other.vertexBufferHandle = VK_NULL_HANDLE;
50 other.vertexBufferMemory = VK_NULL_HANDLE;
51 other.indexBufferHandle = VK_NULL_HANDLE;
52 other.indexBufferMemory = VK_NULL_HANDLE;
53 return *this;
54 }

◆ subMesh()

const SubMesh & mxvk::MXModel::subMesh ( size_t i) const
inlinenodiscard

Definition at line 154 of file mxvk_model.hpp.

154{ return subMeshList[i]; }

◆ subMeshCount()

size_t mxvk::MXModel::subMeshCount ( ) const
inlinenodiscard

Definition at line 153 of file mxvk_model.hpp.

153{ return subMeshList.size(); }

◆ subMeshes()

const std::vector< SubMesh > & mxvk::MXModel::subMeshes ( ) const
inlinenodiscard

Definition at line 155 of file mxvk_model.hpp.

155{ return subMeshList; }

◆ upload()

void mxvk::MXModel::upload ( VkDevice device,
VkPhysicalDevice physicalDevice,
VkCommandPool commandPool,
VkQueue graphicsQueue )

Upload parsed geometry to device-local GPU buffers.

Parameters
deviceLogical Vulkan device.
physicalDevicePhysical Vulkan device.
commandPoolCommand pool used for transfer command buffer allocation.
graphicsQueueQueue used to submit transfer commands.

Definition at line 1149 of file mxvk_model.cpp.

1150 {
1151 if (device == VK_NULL_HANDLE || physicalDevice == VK_NULL_HANDLE ||
1152 commandPool == VK_NULL_HANDLE || graphicsQueue == VK_NULL_HANDLE) {
1153 throw mxvk::Exception("MXModel::upload requires valid Vulkan handles");
1154 }
1155 if (verticesData.empty() || indicesData.empty()) {
1156 throw mxvk::Exception("MXModel::upload requires loaded geometry");
1157 }
1158
1159 logMXModelStep("upload begin", true);
1160
1161 cleanup(device);
1162
1163 const VkDeviceSize vertexBufferSize = sizeof(VKVertex) * verticesData.size();
1164 const VkDeviceSize indexBufferSize = sizeof(uint32_t) * indicesData.size();
1165
1166 VkBuffer stagingVertexBuffer = VK_NULL_HANDLE;
1167 VkDeviceMemory stagingVertexMemory = VK_NULL_HANDLE;
1168 createBuffer(device, physicalDevice, vertexBufferSize,
1169 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1170 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1171 stagingVertexBuffer, stagingVertexMemory);
1172
1173 VkBuffer stagingIndexBuffer = VK_NULL_HANDLE;
1174 VkDeviceMemory stagingIndexMemory = VK_NULL_HANDLE;
1175 createBuffer(device, physicalDevice, indexBufferSize,
1176 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1177 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1178 stagingIndexBuffer, stagingIndexMemory);
1179
1180 void *vertexData = nullptr;
1181 vkMapMemory(device, stagingVertexMemory, 0, vertexBufferSize, 0, &vertexData);
1182 std::memcpy(vertexData, verticesData.data(), static_cast<size_t>(vertexBufferSize));
1183 vkUnmapMemory(device, stagingVertexMemory);
1184
1185 void *indexData = nullptr;
1186 vkMapMemory(device, stagingIndexMemory, 0, indexBufferSize, 0, &indexData);
1187 std::memcpy(indexData, indicesData.data(), static_cast<size_t>(indexBufferSize));
1188 vkUnmapMemory(device, stagingIndexMemory);
1189
1190 createBuffer(device, physicalDevice, vertexBufferSize,
1191 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
1192 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1193 vertexBufferHandle, vertexBufferMemory);
1194
1195 createBuffer(device, physicalDevice, indexBufferSize,
1196 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
1197 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1198 indexBufferHandle, indexBufferMemory);
1199
1200 copyBuffer(device, commandPool, graphicsQueue, stagingVertexBuffer, vertexBufferHandle, vertexBufferSize);
1201 copyBuffer(device, commandPool, graphicsQueue, stagingIndexBuffer, indexBufferHandle, indexBufferSize);
1202
1203 vkDestroyBuffer(device, stagingVertexBuffer, nullptr);
1204 vkFreeMemory(device, stagingVertexMemory, nullptr);
1205 vkDestroyBuffer(device, stagingIndexBuffer, nullptr);
1206 vkFreeMemory(device, stagingIndexMemory, nullptr);
1207
1208 logMXModelStep("upload complete", true);
1209 }
void cleanup(VkDevice device)
Release owned GPU buffers.

◆ vertexBuffer()

VkBuffer mxvk::MXModel::vertexBuffer ( ) const
inlinenodiscard

Definition at line 150 of file mxvk_model.hpp.

150{ return vertexBufferHandle; }

◆ vertices()

const std::vector< VKVertex > & mxvk::MXModel::vertices ( ) const
inlinenodiscard

Definition at line 146 of file mxvk_model.hpp.

146{ return verticesData; }

The documentation for this class was generated from the following files: