#include "xe_model.hpp" #include "xe_utils.hpp" #define TINYOBJLOADER_IMPLEMENTATION #include "xe_obj_loader.hpp" #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include namespace std { template<> struct hash { size_t operator()(xe::Model::Vertex const &vertex) const { size_t seed = 0; xe::hashCombine(seed, vertex.position, vertex.normal, vertex.uv); return seed; } }; } namespace xe { Model::Model(Device &device, const Model::Builder &builder) : xeDevice{device} { createVertexBuffers(builder.vertices); createIndexBuffers(builder.indices); } Model::~Model() {} std::unique_ptr Model::createModelFromFile(Device &device, const std::string &filepath) { Builder builder{}; builder.loadModel(filepath); return std::make_unique(device, builder); } void Model::createVertexBuffers(const std::vector &vertices) { vertexCount = static_cast(vertices.size()); assert(vertexCount >= 3 && "Vertex count must be atleast 3"); VkDeviceSize bufferSize = sizeof(vertices[0]) * vertexCount; uint32_t vertexSize = sizeof(vertices[0]); Buffer stagingBuffer { xeDevice, vertexSize, vertexCount, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT }; stagingBuffer.map(); stagingBuffer.writeToBuffer((void *)vertices.data()); vertexBuffer = std::make_unique( xeDevice, vertexSize, vertexCount, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ); xeDevice.copyBuffer(stagingBuffer.getBuffer(), vertexBuffer->getBuffer(), bufferSize); } void Model::createIndexBuffers(const std::vector &indices) { indexCount = static_cast(indices.size()); hasIndexBuffer = indexCount > 0; if (!hasIndexBuffer) { return; } VkDeviceSize bufferSize = sizeof(indices[0]) * indexCount; uint32_t indexSize = sizeof(indices[0]); Buffer stagingBuffer { xeDevice, indexSize, indexCount, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT }; stagingBuffer.map(); stagingBuffer.writeToBuffer((void *)indices.data()); indexBuffer = std::make_unique( xeDevice, indexSize, indexCount, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ); xeDevice.copyBuffer(stagingBuffer.getBuffer(), indexBuffer->getBuffer(), bufferSize); } void Model::bind(VkCommandBuffer commandBuffer) { VkBuffer buffers[] = {vertexBuffer->getBuffer()}; VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets); if (hasIndexBuffer) { vkCmdBindIndexBuffer(commandBuffer, indexBuffer->getBuffer(), 0, VK_INDEX_TYPE_UINT32); } } void Model::draw(VkCommandBuffer commandBuffer) { if (hasIndexBuffer) { vkCmdDrawIndexed(commandBuffer, indexCount, 1, 0, 0, 0); } else { vkCmdDraw(commandBuffer, vertexCount, 1, 0, 0); } } std::vector Model::Vertex::getBindingDescriptions() { std::vector bindingDescriptions(1); bindingDescriptions[0].binding = 0; bindingDescriptions[0].stride = sizeof(Vertex); bindingDescriptions[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; return bindingDescriptions; } std::vector Model::Vertex::getAttributeDescriptions() { std::vector attributeDescptions{}; attributeDescptions.push_back({0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, position)}); attributeDescptions.push_back({1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, color)}); attributeDescptions.push_back({2, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)}); attributeDescptions.push_back({3, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)}); return attributeDescptions; } void Model::Builder::loadModel(const std::string &filepath) { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string warn, err; if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filepath.c_str())) { throw std::runtime_error(warn + err); } vertices.clear(); indices.clear(); std::unordered_map uniqueVertices{}; for (const auto &shape : shapes) { for (const auto &index : shape.mesh.indices) { Vertex vertex{}; if(index.vertex_index >= 0) { vertex.position = { attrib.vertices[3 * index.vertex_index + 0], attrib.vertices[3 * index.vertex_index + 1], attrib.vertices[3 * index.vertex_index + 2] }; vertex.color = { attrib.colors[3 * index.vertex_index + 0], attrib.colors[3 * index.vertex_index + 1], attrib.colors[3 * index.vertex_index + 2] }; } if(index.normal_index >= 0) { vertex.normal = { attrib.normals[3 * index.normal_index + 0], attrib.normals[3 * index.normal_index + 1], attrib.normals[3 * index.normal_index + 2] }; } if(index.texcoord_index >= 0) { vertex.uv = { attrib.texcoords[2 * index.texcoord_index + 0], attrib.texcoords[2 * index.texcoord_index + 1], }; } if (uniqueVertices.count(vertex) == 0) { uniqueVertices[vertex] = static_cast(vertices.size()); vertices.push_back(vertex); } indices.push_back(uniqueVertices[vertex]); } } } }