3#include "components/PathUtils.hpp"
4#include "glm/common.hpp"
22 log(
"VulkanMesh created");
25 VulkanMesh::~VulkanMesh() {
26 log(
"Destroying VulkanMesh");
27 vkDeviceWaitIdle(m_r_context.device);
29 for (
auto& submesh : m_submeshBuffers) {
30 if (submesh.vertexBuffer != VK_NULL_HANDLE) {
31 vmaDestroyBuffer(m_r_context.allocator, submesh.vertexBuffer, submesh.vertexAlloc);
32 submesh.vertexBuffer = VK_NULL_HANDLE;
34 if (submesh.indexBuffer != VK_NULL_HANDLE) {
35 vmaDestroyBuffer(m_r_context.allocator, submesh.indexBuffer, submesh.indexAlloc);
36 submesh.indexBuffer = VK_NULL_HANDLE;
42 char* dstPtr =
static_cast<char*
>(dst);
43 const char* srcPtr =
static_cast<const char*
>(src);
45 size_t dstAddr =
reinterpret_cast<size_t>(dstPtr);
46 size_t misalignment = dstAddr & 15;
47 size_t head = (misalignment == 0) ? 0 : (16 - misalignment);
49 if (head > sizeBytes) head = sizeBytes;
52 std::memcpy(dstPtr, srcPtr, head);
58 size_t vecSize =
sizeof(__m128i);
59 size_t loops = sizeBytes / vecSize;
61 auto* dst128 =
reinterpret_cast<__m128i*
>(dstPtr);
62 auto* src128 =
reinterpret_cast<const __m128i*
>(srcPtr);
64 for (
size_t i = 0; i < loops; ++i) {
65 __m128i data = _mm_loadu_si128(src128 + i);
66 _mm_stream_si128(dst128 + i, data);
69 size_t bytesHandled = loops * vecSize;
70 size_t tail = sizeBytes - bytesHandled;
73 std::memcpy(dstPtr + bytesHandled, srcPtr + bytesHandled, tail);
80 log(
"Uploading mesh with %zu submeshes", meshData.submeshes.size());
82 m_submeshBuffers.reserve(meshData.submeshes.size());
83 m_submeshTextures.reserve(meshData.submeshes.size());
84 m_cpuSubmeshData = meshData.submeshes;
86 for (
auto& srcSubmesh : m_cpuSubmeshData) {
89 VkBufferCreateInfo bufferInfo{};
90 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
91 bufferInfo.size = srcSubmesh.vertices.size() *
sizeof(
Vertex);
92 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
94 VmaAllocationCreateInfo allocInfo{};
95 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
96 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo,
97 &buffers.vertexBuffer, &buffers.vertexAlloc,
nullptr);
100 vmaMapMemory(m_r_context.allocator, buffers.vertexAlloc, &data);
101 StreamToGPU(data, srcSubmesh.vertices.data(), bufferInfo.size);
102 vmaUnmapMemory(m_r_context.allocator, buffers.vertexAlloc);
104 bufferInfo.size = srcSubmesh.indices.size() *
sizeof(uint32_t);
105 bufferInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
106 vmaCreateBuffer(m_r_context.allocator, &bufferInfo, &allocInfo,
107 &buffers.indexBuffer, &buffers.indexAlloc,
nullptr);
109 vmaMapMemory(m_r_context.allocator, buffers.indexAlloc, &data);
110 StreamToGPU(data, srcSubmesh.indices.data(), bufferInfo.size);
111 vmaUnmapMemory(m_r_context.allocator, buffers.indexAlloc);
113 buffers.indexCount =
static_cast<uint32_t
>(srcSubmesh.indices.size());
115 m_submeshBuffers.push_back(buffers);
116 m_submeshTextures.push_back(srcSubmesh.texturePath);
118 for (
size_t i = 0; i < srcSubmesh.indices.size(); i += 3) {
119 const auto& p0 = srcSubmesh.vertices[srcSubmesh.indices[i+0]].position;
120 const auto& p1 = srcSubmesh.vertices[srcSubmesh.indices[i+1]].position;
121 const auto& p2 = srcSubmesh.vertices[srcSubmesh.indices[i+2]].position;
122 srcSubmesh.triangleCenters.push_back((p0 + p1 + p2) / 3.0f);
125 log(
"Uploaded submesh: %zu vertices, %u indices, texture: '%s'",
126 srcSubmesh.vertices.size(), buffers.indexCount,
127 srcSubmesh.texturePath.c_str());
131 void VulkanMesh::extractTransparentTriangles(
132 const glm::mat4& modelMatrix,
133 const glm::vec3& cameraPos,
137 std::vector<TransparentTriangle>& outTriangles
139 glm::mat3 rotationScaleMatrix = glm::mat3(modelMatrix);
140 glm::vec3 translationVector = glm::vec3(modelMatrix[3]);
142 for (uint32_t submeshIndex = 0; submeshIndex < m_cpuSubmeshData.size(); ++submeshIndex) {
143 const auto& submesh = m_cpuSubmeshData[submeshIndex];
144 const size_t indexCount = submesh.indices.size();
145 const glm::vec3* centers = submesh.triangleCenters.data();
147 for (uint32_t i = 0; i < indexCount; i += 3) {
148 glm::vec3 centerWorld = rotationScaleMatrix * centers[i/3] + translationVector;
150 glm::vec3 d = centerWorld - cameraPos;
151 float distanceSq = glm::dot(d, d);
153 outTriangles.push_back({
168 VkPipelineLayout pipelineLayout,
172 uint32_t submeshIndex,
173 glm::mat4 modelMatrix,
178 const auto& buffers = m_submeshBuffers[submeshIndex];
179 const auto& textureName = m_submeshTextures[submeshIndex];
182 uint32_t lightsDynamicOffset = modelIndex *
static_cast<uint32_t
>(
sizeof(
SceneLightsUBO));
183 uint32_t dynamicOffsets[1] = { lightsDynamicOffset };
186 vkCmdBindDescriptorSets(
188 VK_PIPELINE_BIND_POINT_GRAPHICS,
203 if (m_r_context.supportsBindlessTextures) {
207 if(submeshChanged || modelChanged){
209 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
210 pipelineLayout, 1, 1, &texSet,
215 bool needPush = modelChanged;
216 if (m_r_context.supportsBindlessTextures && submeshChanged) {
222 modelPush.model = modelMatrix;
223 modelPush.color = mc.color;
224 modelPush.textureID = textureIndex;
226 if (!resources.
textureExists(textureName) && !textureName.empty()) {
227 log(LogLevel::WARNING,
"Missing texture...");
233 VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
240 VkBuffer vertexBuffers[] = {buffers.vertexBuffer};
241 VkDeviceSize offsets[] = {0};
242 vkCmdBindVertexBuffers(cmd, 0, 1, vertexBuffers, offsets);
243 vkCmdBindIndexBuffer(cmd, buffers.indexBuffer, 0, VK_INDEX_TYPE_UINT32);
249 std::string currentTexture =
"";
251 uint32_t lightsDynamicOffset = modelIndex *
static_cast<uint32_t
>(
sizeof(
SceneLightsUBO));
252 uint32_t dynamicOffsets[1] = { lightsDynamicOffset };
255 vkCmdBindDescriptorSets(
257 VK_PIPELINE_BIND_POINT_GRAPHICS,
266 for (
size_t i = 0; i < m_submeshBuffers.size(); i++) {
267 const auto& buffers = m_submeshBuffers[i];
268 std::string textureName =
"";
269 uint32_t textureIndex = 0;
270 if(mc.textureOverrides.contains(i)){
274 if(textureIndex == 0){
278 textureName = m_submeshTextures[i];
285 SDL_LogError(SDL_LOG_CATEGORY_RENDER,
286 "Invalid texture index %u for '%s' (Max: %u)",
291 const bool textureExists = !textureName.empty() && resources.
textureExists(textureName);
295 if (!textureExists) {
296 textureName =
"default";
301 if (m_r_context.supportsBindlessTextures) {
303 modelPush.color = mc.color;
304 modelPush.model = modelMatrix;
305 modelPush.textureID = textureIndex;
310 VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
317 if (currentTexture != textureName) {
319 vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &texSet, 0,
nullptr);
320 currentTexture = textureName;
324 modelPush.color = mc.color;
325 modelPush.model = modelMatrix;
327 vkCmdPushConstants(cmd, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(
PushConstants), &modelPush);
330 VkBuffer vertexBuffers[] = {buffers.vertexBuffer};
331 VkDeviceSize offsets[] = {0};
332 vkCmdBindVertexBuffers(cmd, 0, 1, vertexBuffers, offsets);
333 vkCmdBindIndexBuffer(cmd, buffers.indexBuffer, 0, VK_INDEX_TYPE_UINT32);
335 vkCmdDrawIndexed(cmd, buffers.indexCount, 1, 0, 0, 0);
This file defines MeshData struct and all other structs needed for it.
This file defines VulkanMesh class.
void StreamToGPU(void *dst, const void *src, size_t sizeBytes)
Helper function to stream data to GPU memory.
VulkanMesh(VulkanContext &context)
Constructor for VulkanMesh class.
void draw(VkCommandBuffer cmd, VkPipelineLayout pipelineLayout, VulkanResources &resources, uint32_t frameIndex, uint32_t modelIndex, glm::mat4 modelMatrix, const MeshComponent &mc) const
Draws the mesh to the screen.
void upload(const MeshData &meshData)
Uploads mesh data to the GPU.
void bindAndDrawBatched(VkCommandBuffer cmd, VkPipelineLayout pipelineLayout, VulkanResources &resources, uint32_t frameIndex, uint32_t modelIndex, uint32_t submeshIndex, glm::mat4 modelMatrix, bool modelChanged, bool submeshChanged, const MeshComponent &mc) const
Batch-optimized draw call for sorted transparent triangles.
This class manages resources like textures, descriptor sets, and uniform buffers.
uint32_t getTextureIndex(const std::string &name)
Returns the index of a texture with the given name.
bool textureExists(const std::string &name) const
Returns if a texture with the given name exists.
VkDescriptorSet getTextureDescriptorSet(uint32_t frameIndex, uint32_t textureIndex) const
Returns a descriptor set for the given frame index and texture index.
VkDescriptorSet getDescriptorSet(uint32_t frameIndex) const
Returns the descriptor set for the given frame index.
const uint32_t MAX_TEXTURES
uint32_t Entity
Type alias representing a unique entity identifier in the ECS system.
void VEX_EXPORT log(const char *fmt,...)
Logs a formatted message.
std::string VEX_EXPORT GetAssetPath(const std::string &relativePath)
Resolves the absolute path of an asset based on the current build configuration.
Struct containing raw meshData, mesh id, texture paths and material properties. It just template and ...
Mesh data structure for loading and managing mesh data.
Push constants, holds model information.
Scene lights uniform buffer object.
Vertex structure for mesh data.
Struct holding all vulkan data, like device, surface, swapchain, images, views, and more.
struct used to hold buffers and allocations for each submesh in single object.