From 9a2a6a3340f8985fb112a12c21cc1a2cb306c9eb Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sun, 22 Jun 2025 23:21:59 +0200 Subject: [PATCH] Added Model Loading. --- .gitattributes | 2 + Assets/Models/Box.glb | 3 + Assets/Models/BoxTextured.glb | 3 + Assets/Models/BoxVertexColors.glb | 3 + Assets/Models/OrientationTest.glb | 3 + Assets/Shaders/Mesh.slang | 36 +- Blaze.vcxproj | 3 + Blaze.vcxproj.filters | 3 + Blaze/AppState.cpp | 2 +- Blaze/Blaze.cpp | 131 +++--- Blaze/BufferManager.cpp | 62 +++ Blaze/BufferManager.h | 2 + Blaze/CgltfImpl.cpp | 3 + Blaze/EntityManager.cpp | 460 +----------------- Blaze/EntityManager.h | 81 +--- Blaze/MathUtil.h | 2 + Blaze/MiscData.cpp | 20 +- Blaze/ModelLoader.cpp | 756 ++++++++++++++++++++++++++++++ Blaze/ModelLoader.h | 82 ++++ Blaze/RID.h | 7 +- PLAN.md | 8 +- 21 files changed, 1074 insertions(+), 598 deletions(-) create mode 100644 Assets/Models/Box.glb create mode 100644 Assets/Models/BoxTextured.glb create mode 100644 Assets/Models/BoxVertexColors.glb create mode 100644 Assets/Models/OrientationTest.glb create mode 100644 Blaze/CgltfImpl.cpp create mode 100644 Blaze/ModelLoader.cpp create mode 100644 Blaze/ModelLoader.h diff --git a/.gitattributes b/.gitattributes index 6c8519f..158e339 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,5 @@ *.glb filter=lfs diff=lfs merge=lfs -text *.hdr filter=lfs diff=lfs merge=lfs -text *.exr filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.gltf filter=lfs diff=lfs merge=lfs -text diff --git a/Assets/Models/Box.glb b/Assets/Models/Box.glb new file mode 100644 index 0000000..5445f06 --- /dev/null +++ b/Assets/Models/Box.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed52f7192b8311d700ac0ce80644e3852cd01537e4d62241b9acba023da3d54e +size 1664 diff --git a/Assets/Models/BoxTextured.glb b/Assets/Models/BoxTextured.glb new file mode 100644 index 0000000..6524740 --- /dev/null +++ b/Assets/Models/BoxTextured.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d055d2d56a492d1b9302de6e733046b625cf51e5f2a3090bd3a8c11acc93c17 +size 6540 diff --git a/Assets/Models/BoxVertexColors.glb b/Assets/Models/BoxVertexColors.glb new file mode 100644 index 0000000..c6394ac --- /dev/null +++ b/Assets/Models/BoxVertexColors.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c48227f33b0ba2fbcf23b98ebf60d1c8ae0c6e6c5281e0aa3cc58affee10382 +size 1924 diff --git a/Assets/Models/OrientationTest.glb b/Assets/Models/OrientationTest.glb new file mode 100644 index 0000000..9713071 --- /dev/null +++ b/Assets/Models/OrientationTest.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a91cf448f37de06ab61bc615e123692dd29ac185e6c69e1fdbf1cf53e41045b2 +size 38920 diff --git a/Assets/Shaders/Mesh.slang b/Assets/Shaders/Mesh.slang index 118c001..7f8a565 100644 --- a/Assets/Shaders/Mesh.slang +++ b/Assets/Shaders/Mesh.slang @@ -1,10 +1,12 @@ import Bindless; struct VertexOut { - float4 outPosition : SV_Position; - float4 screenPosition : ScreenPosition; - float4 vertexColor : CoarseColor; - float2 texCoord0 : TexCoord0; + float4 outPosition : SV_Position; + float4 screenPosition : ScreenPosition; + float4 normal : CoarseNormal; + float2 texCoord0 : TexCoord0; + float2 texCoord1 : TexCoord1; + float4 vertexColor0 : VertexColor; }; struct CameraData { @@ -17,6 +19,10 @@ uniform ParameterBlock camera; struct PerInstanceData { float4x4 transform; Sampler2D.RID textureID; + uint _padding; + float metallic; + float roughness; + float4 baseColor; } [[vk::push_constant]] @@ -26,27 +32,37 @@ uniform ConstantBuffer pcb; VertexOut VertexMain( uint vertexId: SV_VertexID, float3 position, - float3 color, + float3 normal, float2 texCoord0, + float2 texCoord1, + float4 vertexColor0, ) { VertexOut output; output.outPosition = mul(camera.proj, mul(camera.view, mul(pcb.transform, float4(position, 1.0f)))); output.screenPosition = mul(camera.proj, mul(camera.view, mul(pcb.transform, float4(position, 1.0f)))); - output.vertexColor = float4(color, 1.0f); - output.texCoord0 = texCoord0 * 2.0f; + output.normal = mul(pcb.transform, float4(normalize(normal.rgb), 0.0f)); + output.texCoord0 = texCoord0; + output.texCoord1 = texCoord1; + output.vertexColor0 = vertexColor0; return output; } [shader("fragment")] float4 FragmentMain( float4 interpolatePosition : ScreenPosition, - float4 interpolatedColors : CoarseColor, + float4 interpolatedNormal : CoarseNormal, float2 uv0 : TexCoord0, + float2 uv1 : TexCoord1, + float4 interpolatedColor : VertexColor, ) : SV_Target0 { + + let N = interpolatedNormal.xyz; + let L = dot(normalize(N), float3(1.0f, 1.0f, -1.0f)); + if (let texture = pcb.textureID) { - return float4(texture.Sample(uv0).rgb, 1.0f) * interpolatedColors; + return float4(texture.Sample(uv0).rgb, 1.0f) * pcb.baseColor * interpolatedColor * L; } else { - return interpolatedColors; + return pcb.baseColor * interpolatedColor * L; } } diff --git a/Blaze.vcxproj b/Blaze.vcxproj index f5d9d1c..b786585 100644 --- a/Blaze.vcxproj +++ b/Blaze.vcxproj @@ -180,6 +180,7 @@ + @@ -189,11 +190,13 @@ + + diff --git a/Blaze.vcxproj.filters b/Blaze.vcxproj.filters index 5183898..995b499 100644 --- a/Blaze.vcxproj.filters +++ b/Blaze.vcxproj.filters @@ -133,6 +133,9 @@ Source Files + + Source Files\HeaderOnlyImpl + diff --git a/Blaze/AppState.cpp b/Blaze/AppState.cpp index 0082860..33e4620 100644 --- a/Blaze/AppState.cpp +++ b/Blaze/AppState.cpp @@ -53,7 +53,7 @@ AppState* AppState_Create( GlobalMemory* memory, uint32_t const width, uint32_t return nullptr; } - EntityManager* entityManager = EntityManager_Create( memory, renderDevice, 10 ); + EntityManager* entityManager = EntityManager_Create( memory, renderDevice, 1000 ); if ( !entityManager ) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "EntityManager failed to init" ); diff --git a/Blaze/Blaze.cpp b/Blaze/Blaze.cpp index 036378e..85a43cc 100644 --- a/Blaze/Blaze.cpp +++ b/Blaze/Blaze.cpp @@ -24,6 +24,8 @@ #include "MiscData.h" #include "RenderDevice.h" +#include "ModelLoader.h" + constexpr uint32_t WIDTH = 1280; constexpr uint32_t HEIGHT = 720; constexpr uint32_t NUM_FRAMES = 3; @@ -42,59 +44,11 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** ) *appstate = AppState_Create( &Blaze::Global::g_Memory, WIDTH, HEIGHT ); if ( !*appstate ) return SDL_APP_FAILURE; - AppState& appState = *static_cast( *appstate ); - - // TODO: Integrate this - - // TL----TR - // | \ | - // | \ | - // | \ | - // BL----BR - // - // BL -> BR -> TL - // TL -> BR -> TR - - std::array vertices = { - // Bottom Left - Vertex{ - .position = { -1.0f, -1.0f, 0.0f }, - .color = { 0.0f, 0.0f, 1.0f }, - .texCoord0 = { 0.0f, 0.0f }, - }, - // Bottom Right - Vertex{ - .position = { 1.0f, -1.0f, 0.0f }, - .color = { 1.0f, 0.0f, 0.0f }, - .texCoord0 = { 1.0f, 0.0f }, - }, - // Top Left - Vertex{ - .position = { -1.0f, 1.0f, 0.0f }, - .color = { 0.0f, 1.0f, 0.0f }, - .texCoord0 = { 0.0f, 1.0f }, - }, - // Top Right - Vertex{ - .position = { 1.0f, 1.0f, 0.0f }, - .color = { 1.0f, 1.0f, 0.0f }, - .texCoord0 = { 1.0f, 1.0f }, - } - }; - - Transform modelTransform = { - .position = { 0.0f, 0.0f, 2.0f }, - .scale = 1.0f, - .rotation = DirectX::XMQuaternionRotationAxis( DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ), 0.0f ), - }; - - for ( int i = -1; i <= 1; ++i ) - { - modelTransform.position.x = static_cast( i ); - appState.entityManager->createEntity( - modelTransform, vertices, i > 0 ? "Assets/Textures/wall.jpg" : "Assets/Textures/container2.png" ); - } + AppState& appState = *static_cast( *appstate ); + Entity const* entity = + LoadModel( appState.renderDevice, appState.entityManager, "Assets/Models/OrientationTest.glb" ); + ASSERT( entity ); return SDL_APP_CONTINUE; } @@ -133,11 +87,13 @@ SDL_AppResult SDL_AppIterate( void* appstate ) for ( Entity& entity : entityManager.iter() ) { - entity.transform().rotation = DirectX::XMQuaternionMultiply( + if ( not entity.isRoot() ) continue; + + entity.transform.rotation = DirectX::XMQuaternionMultiply( DirectX::XMQuaternionRotationAxis( DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ), DirectX::XMConvertToRadians( 60.0f ) * static_cast( deltaTime ) ), - entity.transform().rotation ); + entity.transform.rotation ); } uint32_t currentImageIndex; @@ -251,41 +207,66 @@ SDL_AppResult SDL_AppIterate( void* appstate ) vkCmdBindDescriptorSets( cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 1, 1, &misc.descriptorSet, 0, nullptr ); - std::function drawEntity = - [&]( Entity const& entity, DirectX::XMMATRIX const& parent ) + std::function drawEntity = + [&]( Entity const& entity, DirectX::XMMATRIX const& parent, Model const* current ) { - Transform const& localTransform = entity.transform(); + Transform const& localTransform = entity.transform; DirectX::XMMATRIX worldTransform; { - auto [x, y, z] = localTransform.position; - auto scale = localTransform.scale; - worldTransform = DirectX::XMMatrixScaling( scale, scale, scale ) * - DirectX::XMMatrixRotationQuaternion( localTransform.rotation ) * - DirectX::XMMatrixTranslation( x, y, z ) * parent; + worldTransform = + DirectX::XMMatrixAffineTransformation( + localTransform.scale, DirectX::XMVectorZero(), localTransform.rotation, localTransform.translation ) * + parent; } - VkBuffer const vertexBuffer = - renderDevice.bufferManager->fetchBuffer( entity.mesh().buffer ).value_or( nullptr ); - if ( vertexBuffer ) + if ( not entity.model.isNull() ) { + VkBuffer const vertexBuffer = renderDevice.bufferManager->fetchBuffer( entity.model.vertexBuffer ).value(); + + VkBuffer const indexBuffer = renderDevice.bufferManager->fetchBuffer( entity.model.indexBuffer ).value(); VkDeviceSize constexpr offset = 0; vkCmdBindVertexBuffers( cmd, 0, 1, &vertexBuffer, &offset ); + vkCmdBindIndexBuffer( cmd, indexBuffer, offset, VK_INDEX_TYPE_UINT32 ); } vkCmdPushConstants( cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform ); - vkCmdPushConstants( - cmd, - misc.pipelineLayout, - VK_SHADER_STAGE_ALL_GRAPHICS, - sizeof worldTransform, - sizeof entity.material().texture, - &entity.material().texture ); - vkCmdDraw( cmd, entity.mesh().vertexCount, 1, 0, 0 ); + + if ( not entity.modelMesh.isNull() ) + { + ASSERT( current ); + for ( Primitive const& primitive : std::span{ current->primitives.data() + entity.modelMesh.primitiveStart, + entity.modelMesh.primitiveCount } ) + { + byte const* materialData = nullptr; + if ( primitive.material != UINT32_MAX ) + { + Material const* mat = ¤t->materials[primitive.material]; + materialData = reinterpret_cast( mat ); + materialData += Material::GPU_DATA_OFFSET; + } + else + { + materialData = reinterpret_cast( &DEFAULT_MATERIAL ); + materialData += Material::GPU_DATA_OFFSET; + } + + vkCmdPushConstants( + cmd, + misc.pipelineLayout, + VK_SHADER_STAGE_ALL_GRAPHICS, + sizeof worldTransform, + Material::GPU_DATA_SIZE, + materialData ); + + vkCmdDrawIndexed( cmd, primitive.indexCount, 1, primitive.indexStart, primitive.vertexOffset, 0 ); + // vkCmdDrawIndexed( cmd, primitive.count, 1, primitive.start, 0 ); + } + } for ( Entity& child : entity.children() ) { - drawEntity( child, worldTransform ); + drawEntity( child, worldTransform, entity.model.isNull() ? current : &entity.model ); } }; @@ -296,7 +277,7 @@ SDL_AppResult SDL_AppIterate( void* appstate ) continue; } - drawEntity( entity, DirectX::XMMatrixIdentity() ); + drawEntity( entity, DirectX::XMMatrixIdentity(), nullptr ); } } vkCmdEndRendering( cmd ); diff --git a/Blaze/BufferManager.cpp b/Blaze/BufferManager.cpp index b31df30..1bb429e 100644 --- a/Blaze/BufferManager.cpp +++ b/Blaze/BufferManager.cpp @@ -123,6 +123,68 @@ std::optional BufferManager::createVertexBuffer( size_t const size ) return std::move( *reinterpret_cast( &index ) ); } +std::optional BufferManager::createIndexBuffer( size_t size ) +{ + if ( m_freeList.empty() ) + { + return std::nullopt; + } + + Buffer* bufferSlot = reinterpret_cast( m_freeList.popFront() ); + ++m_count; + + ASSERT( m_pRenderDevice ); + RenderDevice const& renderDevice = *m_pRenderDevice; + + + VkBufferCreateInfo const bufferCreateInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = size, + .usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + + VmaAllocationCreateInfo constexpr allocationCreateInfo = { + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, + .usage = VMA_MEMORY_USAGE_AUTO, + .requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + .preferredFlags = 0, + .memoryTypeBits = 0, + .pool = nullptr, + .pUserData = nullptr, + .priority = 1.0f, + }; + + VmaAllocationInfo allocationInfo; + VkBuffer indexBuffer; + VmaAllocation indexBufferAllocation; + + VK_CHECK( vmaCreateBuffer( + renderDevice.gpuAllocator, + &bufferCreateInfo, + &allocationCreateInfo, + &indexBuffer, + &indexBufferAllocation, + &allocationInfo ) ); + + // NOTE: bufferSlot preserves index between uses. + uint32_t index = bufferSlot->index; + new ( bufferSlot ) Buffer{ + .buffer = indexBuffer, + .allocation = indexBufferAllocation, + .mappedData = static_cast( allocationInfo.pMappedData ), + .size = size, + .index = index, + }; + + // NOTE: Memory hackery to create BufferID; + return std::move( *reinterpret_cast( &index ) ); +} + void BufferManager::freeBuffer( BufferID&& rid ) { if ( not isValidID( rid ) ) return; diff --git a/Blaze/BufferManager.h b/Blaze/BufferManager.h index cf2f0d1..c861ca5 100644 --- a/Blaze/BufferManager.h +++ b/Blaze/BufferManager.h @@ -56,6 +56,8 @@ public: std::optional createVertexBuffer( size_t size ); + std::optional createIndexBuffer( size_t size ); + void freeBuffer( BufferID&& rid ); DEPRECATE_JULY_2025 diff --git a/Blaze/CgltfImpl.cpp b/Blaze/CgltfImpl.cpp new file mode 100644 index 0000000..b0cf325 --- /dev/null +++ b/Blaze/CgltfImpl.cpp @@ -0,0 +1,3 @@ + +#define CGLTF_IMPLEMENTATION +#include \ No newline at end of file diff --git a/Blaze/EntityManager.cpp b/Blaze/EntityManager.cpp index a9f14ee..d48a4c8 100644 --- a/Blaze/EntityManager.cpp +++ b/Blaze/EntityManager.cpp @@ -5,8 +5,6 @@ #include "GlobalMemory.h" #include "RenderDevice.h" -#include - #include "Frame.h" #include "TextureManager.h" @@ -102,458 +100,48 @@ EntitySiblingIterable Entity::children() const return { m_firstChild }; } -Entity::Entity( Transform const& transform, Mesh&& mesh, Material&& material ) - : m_transform{ transform } - , m_mesh{ std::forward( mesh ) } - , m_material{ std::forward( material ) } +Entity::Entity( Transform const& transform ) + : transform{ transform } + , model{} + , modelMesh{} , m_parent{ nullptr } , m_firstChild{ nullptr } , m_prevSibling{ nullptr } , m_nextSibling{ nullptr } - , m_padding0{ 0 } + , m_flags{ 0 } {} -Entity* EntityManager::createEntity( - Transform const& transform, std::span const vertices, const char* textureFile ) +Entity* EntityManager::createEntity( Transform const& transform ) { - ASSERT( pRenderDevice ); - RenderDevice& renderDevice = *pRenderDevice; + ASSERT( count < capacity ); - Mesh mesh; - { - mesh.vertexCount = static_cast( vertices.size() ); + Entity& entity = entities[count++]; + new ( &entity ) Entity{ transform }; - auto vertexBuffer = renderDevice.bufferManager->createVertexBuffer( vertices.size_bytes() ); - if ( not vertexBuffer ) - { - return nullptr; - } - mesh.buffer = std::move( vertexBuffer.value() ); - - renderDevice.bufferManager->writeToBuffer( mesh.buffer, vertices ); - } - - Material material; - { - VkSampler sampler; - - uint32_t width; - uint32_t height; - uint32_t numChannels = 4; - stbi_uc* textureData; - { - int w; - int h; - int nc; - int requestedChannels = static_cast( numChannels ); - - textureData = stbi_load( textureFile, &w, &h, &nc, requestedChannels ); - ASSERT( nc <= requestedChannels ); - - if ( not textureData ) - { - renderDevice.bufferManager->freeBuffer( std::move( mesh.buffer ) ); - SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%s", stbi_failure_reason() ); - return nullptr; - } - - width = static_cast( w ); - height = static_cast( h ); - } - - VkSamplerCreateInfo constexpr samplerCreateInfo = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .mipLodBias = 0.0, - .anisotropyEnable = true, - .maxAnisotropy = 1.0f, - .compareEnable = false, - .compareOp = VK_COMPARE_OP_NEVER, - .minLod = 0.0f, - .maxLod = VK_LOD_CLAMP_NONE, - .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - .unnormalizedCoordinates = false, - }; - - VK_CHECK( vkCreateSampler( renderDevice.device, &samplerCreateInfo, nullptr, &sampler ) ); - - auto textureOpt = renderDevice.textureManager->createTexture( { width, height, 1 }, sampler ); - if ( not textureOpt ) - { - renderDevice.bufferManager->freeBuffer( std::move( mesh.buffer ) ); - SDL_LogError( SDL_LOG_CATEGORY_ERROR, "%s", stbi_failure_reason() ); - stbi_image_free( textureData ); - return nullptr; - } - - TextureID texture = std::move( textureOpt.value() ); - VkImage textureImage = renderDevice.textureManager->fetchImage( texture ).value(); - - // Staging Buffer Create - VkBuffer stagingBuffer; - VmaAllocation stagingAllocation; - { - VkBufferCreateInfo const stagingBufferCreateInfo = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = static_cast( width ) * height * numChannels * sizeof( textureData[0] ), - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - }; - - VmaAllocationCreateInfo constexpr stagingAllocationCreateInfo = { - .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, - .usage = VMA_MEMORY_USAGE_AUTO, - .requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, - .preferredFlags = 0, - .memoryTypeBits = 0, - .pool = nullptr, - .pUserData = nullptr, - .priority = 1.0f, - }; - - VmaAllocationInfo allocationInfo; - - VK_CHECK( vmaCreateBuffer( - renderDevice.gpuAllocator, - &stagingBufferCreateInfo, - &stagingAllocationCreateInfo, - &stagingBuffer, - &stagingAllocation, - &allocationInfo ) ); - - if ( allocationInfo.pMappedData ) - { - memcpy( allocationInfo.pMappedData, textureData, stagingBufferCreateInfo.size ); - } - } - - // All data is copied to stagingBuffer, don't need this. - stbi_image_free( textureData ); - - // Staging -> Texture transfer - { - Frame& frameInUse = renderDevice.frames[renderDevice.frameIndex]; - - // This should just pass. - VK_CHECK( vkWaitForFences( renderDevice.device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, INT64_MAX ) ); - - // Reset Frame - VK_CHECK( vkResetFences( renderDevice.device, 1, &frameInUse.frameReadyToReuse ) ); - VK_CHECK( vkResetCommandPool( renderDevice.device, frameInUse.commandPool, 0 ) ); - - VkCommandBufferBeginInfo constexpr beginInfo = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .pNext = nullptr, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - .pInheritanceInfo = nullptr, - }; - - uint32_t mipLevels = TextureManager::calculateRequiredMipLevels( width, height, 1 ); - - VkImageSubresourceRange const subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = mipLevels, - .baseArrayLayer = 0, - .layerCount = 1, - }; - - VkImageMemoryBarrier2 const creationToTransferImageBarrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, - .srcAccessMask = VK_ACCESS_2_NONE, - .dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = renderDevice.textureManager->fetchImage( texture ).value(), - .subresourceRange = subresourceRange, - }; - - VkDependencyInfo const creationToTransferDependency = { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = 0, - .memoryBarrierCount = 0, - .pMemoryBarriers = nullptr, - .bufferMemoryBarrierCount = 0, - .pBufferMemoryBarriers = nullptr, - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &creationToTransferImageBarrier, - }; - - std::array transferToReadyImageBarriers{ - // transferToReadyImageBarrier - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = textureImage, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = mipLevels-1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }, - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = textureImage, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = mipLevels-1, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - } - }; - - VkDependencyInfo const transferToReadyDependency = { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = 0, - .memoryBarrierCount = 0, - .pMemoryBarriers = nullptr, - .bufferMemoryBarrierCount = 0, - .pBufferMemoryBarriers = nullptr, - .imageMemoryBarrierCount = static_cast( transferToReadyImageBarriers.size() ), - .pImageMemoryBarriers = transferToReadyImageBarriers.data(), - }; - - VkImageSubresourceRange const mipLevelSubresource = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }; - - std::array prepareNextMipLevelBarriers{ - // prepareNextMipLevelSrcImageBarrier - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = textureImage, - .subresourceRange = mipLevelSubresource, - }, - // prepareNextMipLevelDstImageBarrier - VkImageMemoryBarrier2{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = textureImage, - .subresourceRange = mipLevelSubresource, - } - }; - - VkDependencyInfo const prepareNextMipLevelDependency = { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pNext = nullptr, - .dependencyFlags = 0, - .memoryBarrierCount = 0, - .pMemoryBarriers = nullptr, - .bufferMemoryBarrierCount = 0, - .pBufferMemoryBarriers = nullptr, - .imageMemoryBarrierCount = static_cast( prepareNextMipLevelBarriers.size() ), - .pImageMemoryBarriers = prepareNextMipLevelBarriers.data(), - }; - - - vkBeginCommandBuffer( frameInUse.commandBuffer, &beginInfo ); - { - VkImageSubresourceLayers imageSubresourceLayers = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }; - - // TODO: Ensure `bufferRowLength` and `bufferImageHeight` are not required. - VkBufferImageCopy copyRegion = { - .bufferOffset = 0, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource = imageSubresourceLayers, - .imageOffset = { 0, 0, 0 }, - .imageExtent = { width, height, 1 } - }; - - // Start - vkCmdPipelineBarrier2( frameInUse.commandBuffer, &creationToTransferDependency ); - - // Staging -> Image L0 - vkCmdCopyBufferToImage( - frameInUse.commandBuffer, - stagingBuffer, - textureImage, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - ©Region ); - - prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = 0; - prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = 1; - - int32_t mipSrcWidth = static_cast( width ); - int32_t mipSrcHeight = static_cast( height ); - int32_t mipDstWidth = std::max( mipSrcWidth / 2, 1 ); - int32_t mipDstHeight = std::max( mipSrcHeight / 2, 1 ); - - VkImageSubresourceLayers constexpr mipSubresourceLayers = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }; - - VkImageBlit2 imageBlit = { - .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, - .pNext = nullptr, - .srcSubresource = mipSubresourceLayers, - .srcOffsets = { { 0, 0, 0 }, { mipSrcWidth, mipSrcHeight, 1 } }, - .dstSubresource = mipSubresourceLayers, - .dstOffsets = { { 0, 0, 0 }, { mipDstWidth, mipDstHeight, 1 } }, - }; - - imageBlit.srcSubresource.mipLevel = 0; - imageBlit.dstSubresource.mipLevel = 1; - imageBlit.srcOffsets[1].x = mipSrcWidth; - imageBlit.srcOffsets[1].y = mipSrcHeight; - imageBlit.dstOffsets[1].x = mipDstWidth; - imageBlit.dstOffsets[1].y = mipDstHeight; - - VkBlitImageInfo2 blitInfo = { - .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, - .pNext = nullptr, - .srcImage = textureImage, - .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .dstImage = textureImage, - .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .regionCount = 1, - .pRegions = &imageBlit, - .filter = VK_FILTER_LINEAR, - }; - - // MipMapping - for ( uint32_t dstMipLevel = 1; dstMipLevel < mipLevels; ++dstMipLevel ) - { - vkCmdPipelineBarrier2( frameInUse.commandBuffer, &prepareNextMipLevelDependency ); - vkCmdBlitImage2( frameInUse.commandBuffer, &blitInfo ); - - // Prep for NEXT iteration - - mipSrcWidth = mipDstWidth; - mipSrcHeight = mipDstHeight; - mipDstWidth = std::max( mipSrcWidth / 2, 1 ); - mipDstHeight = std::max( mipSrcHeight / 2, 1 ); - - imageBlit.srcSubresource.mipLevel = dstMipLevel; - imageBlit.dstSubresource.mipLevel = dstMipLevel + 1; - imageBlit.srcOffsets[1].x = mipSrcWidth; - imageBlit.srcOffsets[1].y = mipSrcHeight; - imageBlit.dstOffsets[1].x = mipDstWidth; - imageBlit.dstOffsets[1].y = mipDstHeight; - - // Prep current mip level as source - prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = dstMipLevel; - prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = dstMipLevel + 1; - } - - // End - vkCmdPipelineBarrier2( frameInUse.commandBuffer, &transferToReadyDependency ); - } - vkEndCommandBuffer( frameInUse.commandBuffer ); - - VkSubmitInfo submitInfo = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .pNext = nullptr, - .waitSemaphoreCount = 0, - .pWaitSemaphores = nullptr, - .pWaitDstStageMask = nullptr, - .commandBufferCount = 1, - .pCommandBuffers = &frameInUse.commandBuffer, - .signalSemaphoreCount = 0, - .pSignalSemaphores = nullptr, - }; - VK_CHECK( vkQueueSubmit( renderDevice.directQueue, 1, &submitInfo, frameInUse.frameReadyToReuse ) ); - - // Do not reset this. Else, the frame will never be available to the main loop. - VK_CHECK( vkWaitForFences( renderDevice.device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, UINT64_MAX ) ); - - renderDevice.frameIndex = ( renderDevice.frameIndex + 1 ) % renderDevice.getNumFrames(); - } - - vmaDestroyBuffer( renderDevice.gpuAllocator, stagingBuffer, stagingAllocation ); - - material = { - sampler, - std::move( texture ), - }; - } - - Entity& alloc = entities[count++]; - alloc = Entity( transform, std::move( mesh ), std::move( material ) ); - - return &alloc; + return &entity; } void EntityManager::destroyEntity( Entity* entity ) { ASSERT( entity ); - if ( not entity->isInit() ) return; VkDevice const device = pRenderDevice->device; - vkDestroySampler( device, Take( entity->material().sampler ), nullptr ); + if ( not entity->model.isNull() ) + { + for ( auto& material : entity->model.materials ) + { + vkDestroySampler( device, Take( material.sampler ), nullptr ); + pRenderDevice->textureManager->freeTexture( std::move( material.texture ) ); + } - pRenderDevice->textureManager->freeTexture( std::move( entity->material().texture ) ); - pRenderDevice->bufferManager->freeBuffer( std::move( entity->mesh().buffer ) ); + pRenderDevice->bufferManager->freeBuffer( std::move( entity->model.vertexBuffer ) ); + pRenderDevice->bufferManager->freeBuffer( std::move( entity->model.indexBuffer ) ); + entity->model.primitives.clear(); + entity->model.materials.clear(); + } + + entity->modelMesh = { 0, 0 }; } void EntityManager::destroy() diff --git a/Blaze/EntityManager.h b/Blaze/EntityManager.h index 5e2aed4..2c5771a 100644 --- a/Blaze/EntityManager.h +++ b/Blaze/EntityManager.h @@ -8,37 +8,18 @@ #include "VulkanHeader.h" // TODO: Remove this dependency #include "BufferManager.h" +#include "ModelLoader.h" #include "TextureManager.h" struct Entity; struct RenderDevice; struct GlobalMemory; -struct Vertex -{ - DirectX::XMFLOAT3 position; - DirectX::XMFLOAT3 color; - DirectX::XMFLOAT2 texCoord0; -}; - struct Transform { - DirectX::XMFLOAT3 position; - float scale; + DirectX::XMVECTOR translation; DirectX::XMVECTOR rotation; -}; - -struct Mesh -{ - BufferID buffer; - uint32_t vertexCount; -}; - -struct Material -{ - VkSampler sampler; // TODO: Reuse - TextureID texture; - uint32_t padding0; // FIXME: Wasting space. + DirectX::XMVECTOR scale; }; struct EntitySiblingIterable @@ -60,52 +41,18 @@ struct EntitySiblingIterable struct Entity { + Transform transform; + Model model; + ModelMesh modelMesh; + private: - Transform m_transform; - Mesh m_mesh; - Material m_material; - Entity* m_parent; // TODO: Switch to EntityIndex. - Entity* m_firstChild; - Entity* m_prevSibling; - Entity* m_nextSibling; - uint64_t m_padding0; // FIXME: Wasting space. + Entity* m_parent; // TODO: Switch to EntityIndex. + Entity* m_firstChild; + Entity* m_prevSibling; + Entity* m_nextSibling; + uint64_t m_flags; // FIXME: Wasting space. public: - [[nodiscard]] Transform& transform() - { - return m_transform; - } - - [[nodiscard]] Transform const& transform() const - { - return m_transform; - } - - [[nodiscard]] Mesh& mesh() - { - return m_mesh; - } - - [[nodiscard]] Mesh const& mesh() const - { - return m_mesh; - } - - [[nodiscard]] Material& material() - { - return m_material; - } - - [[nodiscard]] Material const& material() const - { - return m_material; - } - - [[nodiscard]] bool isInit() const - { - return m_mesh.buffer or m_material.texture; - } - [[nodiscard]] bool isRoot() const { return not m_parent; @@ -132,7 +79,7 @@ public: [[nodiscard]] EntitySiblingIterable children() const; - Entity( Transform const& transform, Mesh&& mesh, Material&& material ); + explicit Entity( Transform const& transform ); }; struct EntityManager @@ -173,7 +120,7 @@ struct EntityManager } // Make Entities return ID, make it a sparse indexing system. - Entity* createEntity( Transform const& transform, std::span vertices, const char* textureFile ); + Entity* createEntity( Transform const& transform ); void destroyEntity( Entity* entity ); diff --git a/Blaze/MathUtil.h b/Blaze/MathUtil.h index 5f0923c..4606f8d 100644 --- a/Blaze/MathUtil.h +++ b/Blaze/MathUtil.h @@ -2,6 +2,8 @@ #include +using byte = std::byte; + template T Clamp( T const val, T const minVal, T const maxVal ) { diff --git a/Blaze/MiscData.cpp b/Blaze/MiscData.cpp index ce8b288..6d1a787 100644 --- a/Blaze/MiscData.cpp +++ b/Blaze/MiscData.cpp @@ -60,7 +60,7 @@ bool MiscData::init( RenderDevice const& renderDevice ) VkPushConstantRange const pushConstantRange = { .stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS, .offset = 0, - .size = sizeof( DirectX::XMMATRIX ) + sizeof( uint32_t ), + .size = sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE }; std::array const descriptorSetLayouts = { @@ -118,7 +118,7 @@ bool MiscData::init( RenderDevice const& renderDevice ) .location = 1, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, - .offset = offsetof( Vertex, color ), + .offset = offsetof( Vertex, normal ), }, VkVertexInputAttributeDescription{ .location = 2, @@ -126,6 +126,18 @@ bool MiscData::init( RenderDevice const& renderDevice ) .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof( Vertex, texCoord0 ), }, + VkVertexInputAttributeDescription{ + .location = 3, + .binding = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof( Vertex, texCoord1 ), + }, + VkVertexInputAttributeDescription{ + .location = 4, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = offsetof( Vertex, color0 ), + }, }; VkPipelineVertexInputStateCreateInfo const vertexInputState = { @@ -142,7 +154,7 @@ bool MiscData::init( RenderDevice const& renderDevice ) .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .primitiveRestartEnable = VK_FALSE, }; @@ -277,7 +289,7 @@ bool MiscData::init( RenderDevice const& renderDevice ) // Camera { - cameraPosition = DirectX::XMVectorSet( 0.0f, 0.0f, -4.0f, 1.0f ); + cameraPosition = DirectX::XMVectorSet( 0.0f, 20.0f, -20.0f, 1.0f ); cameraTarget = DirectX::XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f ); cameraUp = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 1.0f ); cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraPosition, cameraTarget, cameraUp ); diff --git a/Blaze/ModelLoader.cpp b/Blaze/ModelLoader.cpp new file mode 100644 index 0000000..4907468 --- /dev/null +++ b/Blaze/ModelLoader.cpp @@ -0,0 +1,756 @@ +#include "ModelLoader.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "EntityManager.h" +#include "Frame.h" +#include "GlobalMemory.h" +#include "MacroUtils.h" +#include "MathUtil.h" + +// TODO: Cache materials while loading. +uint32_t ProcessMaterial( RenderDevice* renderDevice, Model* model, cgltf_material const& material ) +{ + ASSERT( material.has_pbr_metallic_roughness ); + + DirectX::XMFLOAT4 const baseColorFactor = DirectX::XMFLOAT4{ material.pbr_metallic_roughness.base_color_factor }; + + VkSampler sampler = nullptr; + TextureID baseColorTexture; + + if ( material.pbr_metallic_roughness.base_color_texture.texture ) + { + cgltf_image* baseColorImage = material.pbr_metallic_roughness.base_color_texture.texture->image; + + { + + byte* data; + if ( baseColorImage->buffer_view->data ) + { + data = static_cast( baseColorImage->buffer_view->data ); + } + else + { + data = static_cast( baseColorImage->buffer_view->buffer->data ) + baseColorImage->buffer_view->offset; + } + size_t size = baseColorImage->buffer_view->size; + + uint32_t width; + uint32_t height; + uint32_t numChannels = 4; + stbi_uc* textureData; + { + int w; + int h; + int nc; + int requestedChannels = static_cast( numChannels ); + + textureData = stbi_load_from_memory( + reinterpret_cast( data ), static_cast( size ), &w, &h, &nc, requestedChannels ); + ASSERT( nc <= requestedChannels ); + + if ( not textureData ) + { + return UINT32_MAX; + } + + width = static_cast( w ); + height = static_cast( h ); + } + + VkSamplerCreateInfo constexpr samplerCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipLodBias = 0.0, + .anisotropyEnable = true, + .maxAnisotropy = 1.0f, + .compareEnable = false, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = VK_LOD_CLAMP_NONE, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + .unnormalizedCoordinates = false, + }; + + VK_CHECK( vkCreateSampler( renderDevice->device, &samplerCreateInfo, nullptr, &sampler ) ); + + auto textureOpt = renderDevice->textureManager->createTexture( { width, height, 1 }, sampler ); + if ( not textureOpt ) + { + return UINT32_MAX; + } + + baseColorTexture = std::move( textureOpt.value() ); + VkImage textureImage = renderDevice->textureManager->fetchImage( baseColorTexture ).value(); + + // Staging Buffer Create + VkBuffer stagingBuffer; + VmaAllocation stagingAllocation; + { + VkBufferCreateInfo const stagingBufferCreateInfo = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = static_cast( width ) * height * numChannels * sizeof( textureData[0] ), + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + + VmaAllocationCreateInfo constexpr stagingAllocationCreateInfo = { + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, + .usage = VMA_MEMORY_USAGE_AUTO, + .requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + .preferredFlags = 0, + .memoryTypeBits = 0, + .pool = nullptr, + .pUserData = nullptr, + .priority = 1.0f, + }; + + VmaAllocationInfo allocationInfo; + + VK_CHECK( vmaCreateBuffer( + renderDevice->gpuAllocator, + &stagingBufferCreateInfo, + &stagingAllocationCreateInfo, + &stagingBuffer, + &stagingAllocation, + &allocationInfo ) ); + + if ( allocationInfo.pMappedData ) + { + memcpy( allocationInfo.pMappedData, textureData, stagingBufferCreateInfo.size ); + } + } + + // All data is copied to stagingBuffer, don't need this. + stbi_image_free( textureData ); + + // Staging -> Texture transfer + { + Frame& frameInUse = renderDevice->frames[renderDevice->frameIndex]; + + // This should just pass. + VK_CHECK( vkWaitForFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, INT64_MAX ) ); + + // Reset Frame + VK_CHECK( vkResetFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse ) ); + VK_CHECK( vkResetCommandPool( renderDevice->device, frameInUse.commandPool, 0 ) ); + + VkCommandBufferBeginInfo constexpr beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = nullptr, + }; + + uint32_t mipLevels = TextureManager::calculateRequiredMipLevels( width, height, 1 ); + + VkImageSubresourceRange const subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = mipLevels, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + VkImageMemoryBarrier2 const creationToTransferImageBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, + .srcAccessMask = VK_ACCESS_2_NONE, + .dstStageMask = VK_PIPELINE_STAGE_2_COPY_BIT, + .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = renderDevice->textureManager->fetchImage( baseColorTexture ).value(), + .subresourceRange = subresourceRange, + }; + + VkDependencyInfo const creationToTransferDependency = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = 0, + .memoryBarrierCount = 0, + .pMemoryBarriers = nullptr, + .bufferMemoryBarrierCount = 0, + .pBufferMemoryBarriers = nullptr, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &creationToTransferImageBarrier, + }; + + std::array transferToReadyImageBarriers{ + // transferToReadyImageBarrier + VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + .dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = textureImage, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = mipLevels-1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }, + VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, + .dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = textureImage, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = mipLevels-1, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + } + }; + + VkDependencyInfo const transferToReadyDependency = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = 0, + .memoryBarrierCount = 0, + .pMemoryBarriers = nullptr, + .bufferMemoryBarrierCount = 0, + .pBufferMemoryBarriers = nullptr, + .imageMemoryBarrierCount = static_cast( transferToReadyImageBarriers.size() ), + .pImageMemoryBarriers = transferToReadyImageBarriers.data(), + }; + + VkImageSubresourceRange const mipLevelSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + std::array prepareNextMipLevelBarriers{ + // prepareNextMipLevelSrcImageBarrier + VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = textureImage, + .subresourceRange = mipLevelSubresource, + }, + // prepareNextMipLevelDstImageBarrier + VkImageMemoryBarrier2{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT, + .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_BLIT_BIT, + .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = textureImage, + .subresourceRange = mipLevelSubresource, + } + }; + + VkDependencyInfo const prepareNextMipLevelDependency = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = 0, + .memoryBarrierCount = 0, + .pMemoryBarriers = nullptr, + .bufferMemoryBarrierCount = 0, + .pBufferMemoryBarriers = nullptr, + .imageMemoryBarrierCount = static_cast( prepareNextMipLevelBarriers.size() ), + .pImageMemoryBarriers = prepareNextMipLevelBarriers.data(), + }; + + + vkBeginCommandBuffer( frameInUse.commandBuffer, &beginInfo ); + { + VkImageSubresourceLayers imageSubresourceLayers = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + // TODO: Ensure `bufferRowLength` and `bufferImageHeight` are not required. + VkBufferImageCopy copyRegion = { + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = imageSubresourceLayers, + .imageOffset = { 0, 0, 0 }, + .imageExtent = { width, height, 1 } + }; + + // Start + vkCmdPipelineBarrier2( frameInUse.commandBuffer, &creationToTransferDependency ); + + // Staging -> Image L0 + vkCmdCopyBufferToImage( + frameInUse.commandBuffer, + stagingBuffer, + textureImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ©Region ); + + prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = 0; + prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = 1; + + int32_t mipSrcWidth = static_cast( width ); + int32_t mipSrcHeight = static_cast( height ); + int32_t mipDstWidth = std::max( mipSrcWidth / 2, 1 ); + int32_t mipDstHeight = std::max( mipSrcHeight / 2, 1 ); + + VkImageSubresourceLayers constexpr mipSubresourceLayers = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + VkImageBlit2 imageBlit = { + .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, + .pNext = nullptr, + .srcSubresource = mipSubresourceLayers, + .srcOffsets = { { 0, 0, 0 }, { mipSrcWidth, mipSrcHeight, 1 } }, + .dstSubresource = mipSubresourceLayers, + .dstOffsets = { { 0, 0, 0 }, { mipDstWidth, mipDstHeight, 1 } }, + }; + + imageBlit.srcSubresource.mipLevel = 0; + imageBlit.dstSubresource.mipLevel = 1; + imageBlit.srcOffsets[1].x = mipSrcWidth; + imageBlit.srcOffsets[1].y = mipSrcHeight; + imageBlit.dstOffsets[1].x = mipDstWidth; + imageBlit.dstOffsets[1].y = mipDstHeight; + + VkBlitImageInfo2 blitInfo = { + .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, + .pNext = nullptr, + .srcImage = textureImage, + .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .dstImage = textureImage, + .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .regionCount = 1, + .pRegions = &imageBlit, + .filter = VK_FILTER_LINEAR, + }; + + // MipMapping + for ( uint32_t dstMipLevel = 1; dstMipLevel < mipLevels; ++dstMipLevel ) + { + vkCmdPipelineBarrier2( frameInUse.commandBuffer, &prepareNextMipLevelDependency ); + vkCmdBlitImage2( frameInUse.commandBuffer, &blitInfo ); + + // Prep for NEXT iteration + + mipSrcWidth = mipDstWidth; + mipSrcHeight = mipDstHeight; + mipDstWidth = std::max( mipSrcWidth / 2, 1 ); + mipDstHeight = std::max( mipSrcHeight / 2, 1 ); + + imageBlit.srcSubresource.mipLevel = dstMipLevel; + imageBlit.dstSubresource.mipLevel = dstMipLevel + 1; + imageBlit.srcOffsets[1].x = mipSrcWidth; + imageBlit.srcOffsets[1].y = mipSrcHeight; + imageBlit.dstOffsets[1].x = mipDstWidth; + imageBlit.dstOffsets[1].y = mipDstHeight; + + // Prep current mip level as source + prepareNextMipLevelBarriers[0].subresourceRange.baseMipLevel = dstMipLevel; + prepareNextMipLevelBarriers[1].subresourceRange.baseMipLevel = dstMipLevel + 1; + } + + // End + vkCmdPipelineBarrier2( frameInUse.commandBuffer, &transferToReadyDependency ); + } + vkEndCommandBuffer( frameInUse.commandBuffer ); + + VkSubmitInfo submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &frameInUse.commandBuffer, + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, + }; + VK_CHECK( vkQueueSubmit( renderDevice->directQueue, 1, &submitInfo, frameInUse.frameReadyToReuse ) ); + + // Do not reset this. Else, the frame will never be available to the main loop. + VK_CHECK( vkWaitForFences( renderDevice->device, 1, &frameInUse.frameReadyToReuse, VK_TRUE, UINT64_MAX ) ); + + renderDevice->frameIndex = ( renderDevice->frameIndex + 1 ) % renderDevice->getNumFrames(); + } + + vmaDestroyBuffer( renderDevice->gpuAllocator, stagingBuffer, stagingAllocation ); + } + } + + float const metallic = material.pbr_metallic_roughness.metallic_factor; + float const roughness = material.pbr_metallic_roughness.roughness_factor; + + uint32_t const materialIdx = static_cast( model->materials.size() ); + model->materials.push_back( { sampler, std::move( baseColorTexture ), {}, roughness, metallic, baseColorFactor } ); + + return materialIdx; +} + +ModelMesh ProcessMesh( + RenderDevice* renderDevice, + Model* model, + std::pmr::vector* pVertices, + std::pmr::vector* pIndices, + cgltf_mesh const& mesh ) +{ + using namespace std::string_view_literals; + + uint32_t const primitiveStart = static_cast( model->primitives.size() ); + uint32_t const primitiveCount = static_cast( mesh.primitives_count ); + + cgltf_primitive const* primitives = mesh.primitives; + for ( uint32_t primitiveIndex = 0; primitiveIndex < mesh.primitives_count; ++primitiveIndex ) + { + // VertexStart is per-primitive + int32_t const vertexStart = static_cast( pVertices->size() ); + + cgltf_primitive const& primitive = primitives[primitiveIndex]; + + ASSERT( primitive.type == cgltf_primitive_type_triangles ); + + // Index Buffer + size_t const indexStart = pIndices->size(); + size_t const indexCount = cgltf_accessor_unpack_indices( primitive.indices, nullptr, sizeof pIndices->at( 0 ), 0 ); + ASSERT( indexCount > 0 ); + pIndices->resize( indexStart + indexCount ); + cgltf_accessor_unpack_indices( + primitive.indices, pIndices->data() + indexStart, sizeof pIndices->at( 0 ), indexCount ); + + // Material + + uint32_t materialIdx = UINT32_MAX; + if ( primitive.material ) + { + materialIdx = ProcessMaterial( renderDevice, model, *primitive.material ); + } + + model->primitives.push_back( Primitive{ + .indexStart = static_cast( indexStart ), + .indexCount = static_cast( indexCount ), + .material = materialIdx, + .vertexOffset = vertexStart, + } ); + + cgltf_attribute const* attributes = primitive.attributes; + for ( uint32_t attribIndex = 0; attribIndex < primitive.attributes_count; ++attribIndex ) + { + if ( "POSITION"sv == attributes[attribIndex].name ) + { + cgltf_attribute const& positionAttr = attributes[attribIndex]; + ASSERT( positionAttr.data->component_type == cgltf_component_type_r_32f ); + ASSERT( positionAttr.data->type == cgltf_type_vec3 ); + + std::pmr::vector positions{ pVertices->get_allocator() }; + + size_t const floatCount = cgltf_accessor_unpack_floats( positionAttr.data, nullptr, 0 ); + positions.resize( floatCount / 3 ); + cgltf_accessor_unpack_floats( + positionAttr.data, reinterpret_cast( positions.data() ), floatCount ); + + // Guaranteed to have space for these vertices. + pVertices->resize( vertexStart + positions.size() ); + + auto vertexIter = pVertices->begin() + vertexStart; + for ( DirectX::XMFLOAT3 const& position : positions ) + { + vertexIter->position = position; + ++vertexIter; + } + } + if ( "NORMAL"sv == attributes[attribIndex].name ) + { + cgltf_attribute const& normalAttr = attributes[attribIndex]; + ASSERT( normalAttr.data->component_type == cgltf_component_type_r_32f ); + ASSERT( normalAttr.data->type == cgltf_type_vec3 ); + + std::pmr::vector normals{ pVertices->get_allocator() }; + + size_t const floatCount = cgltf_accessor_unpack_floats( normalAttr.data, nullptr, 0 ); + normals.resize( floatCount / 3 ); + cgltf_accessor_unpack_floats( normalAttr.data, reinterpret_cast( normals.data() ), floatCount ); + + // Guaranteed to have space for these vertices. + pVertices->resize( vertexStart + normals.size() ); + + auto vertexIter = pVertices->begin() + vertexStart; + for ( DirectX::XMFLOAT3 const& normal : normals ) + { + vertexIter->normal = normal; + ++vertexIter; + } + } + if ( "TEXCOORD_0"sv == attributes[attribIndex].name ) + { + cgltf_attribute const& texCoordAttr = attributes[attribIndex]; + ASSERT( texCoordAttr.data->component_type == cgltf_component_type_r_32f ); + ASSERT( texCoordAttr.data->type == cgltf_type_vec2 ); + + std::pmr::vector texCoords{ pVertices->get_allocator() }; + + size_t const floatCount = cgltf_accessor_unpack_floats( texCoordAttr.data, nullptr, 0 ); + texCoords.resize( floatCount / 2 ); + cgltf_accessor_unpack_floats( + texCoordAttr.data, reinterpret_cast( texCoords.data() ), floatCount ); + + // Guaranteed to have space for these vertices. + pVertices->resize( vertexStart + texCoords.size() ); + + auto vertexIter = pVertices->begin() + vertexStart; + for ( DirectX::XMFLOAT2 const& texCoord : texCoords ) + { + vertexIter->texCoord0 = texCoord; + ++vertexIter; + } + } + if ( "TEXCOORD_1"sv == attributes[attribIndex].name ) + { + cgltf_attribute const& texCoordAttr = attributes[attribIndex]; + ASSERT( texCoordAttr.data->component_type == cgltf_component_type_r_32f ); + ASSERT( texCoordAttr.data->type == cgltf_type_vec2 ); + + std::pmr::vector texCoords{ pVertices->get_allocator() }; + + size_t const floatCount = cgltf_accessor_unpack_floats( texCoordAttr.data, nullptr, 0 ); + texCoords.resize( floatCount / 2 ); + cgltf_accessor_unpack_floats( + texCoordAttr.data, reinterpret_cast( texCoords.data() ), floatCount ); + + // Guaranteed to have space for these vertices. + pVertices->resize( vertexStart + texCoords.size() ); + + auto vertexIter = pVertices->begin() + vertexStart; + for ( DirectX::XMFLOAT2 const& texCoord : texCoords ) + { + vertexIter->texCoord1 = texCoord; + ++vertexIter; + } + } + if ( "COLOR_0"sv == attributes[attribIndex].name ) + { + cgltf_attribute const& colorAttr = attributes[attribIndex]; + ASSERT( colorAttr.data->component_type == cgltf_component_type_r_32f ); + ASSERT( colorAttr.data->type == cgltf_type_vec3 or colorAttr.data->type == cgltf_type_vec4 ); + + if ( colorAttr.data->type == cgltf_type_vec3 ) + { + std::pmr::vector colors{ pVertices->get_allocator() }; + + size_t const floatCount = cgltf_accessor_unpack_floats( colorAttr.data, nullptr, 0 ); + colors.resize( floatCount / 3 ); + cgltf_accessor_unpack_floats( colorAttr.data, reinterpret_cast( colors.data() ), floatCount ); + + // Guaranteed to have space for these vertices. + pVertices->resize( vertexStart + colors.size() ); + + auto vertexIter = pVertices->begin() + vertexStart; + for ( DirectX::XMFLOAT3 const& color : colors ) + { + vertexIter->color0 = { color.x, color.y, color.z, 1.0f }; + ++vertexIter; + } + } + else // Since only two options + { + std::pmr::vector colors{ pVertices->get_allocator() }; + + size_t const floatCount = cgltf_accessor_unpack_floats( colorAttr.data, nullptr, 0 ); + colors.resize( floatCount / 4 ); + cgltf_accessor_unpack_floats( colorAttr.data, reinterpret_cast( colors.data() ), floatCount ); + + // Guaranteed to have space for these vertices. + pVertices->resize( vertexStart + colors.size() ); + + auto vertexIter = pVertices->begin() + vertexStart; + for ( DirectX::XMFLOAT4 const& color : colors ) + { + vertexIter->color0 = color; + ++vertexIter; + } + } + } + // TODO: Grab other attributes. + } + } + + return { primitiveStart, primitiveCount }; +} + +Entity* ProcessNode( + RenderDevice* renderDevice, + EntityManager* entityManager, + Model* model, + std::pmr::vector* vertices, + std::pmr::vector* indices, + cgltf_node const& node ) +{ + DirectX::XMVECTOR vTranslation; + DirectX::XMVECTOR qRotation; + DirectX::XMVECTOR vScale; + if ( node.has_matrix ) + { + DirectX::XMMATRIX const mat = DirectX::XMMATRIX{ node.matrix }; + ASSERT( DirectX::XMMatrixDecompose( &vScale, &qRotation, &vTranslation, mat ) ); + } + else + { + vTranslation = node.has_translation + ? DirectX::XMVectorSet( node.translation[0], node.translation[1], node.translation[2], 1.0f ) + : DirectX::XMVectorZero(); + qRotation = node.has_rotation + ? DirectX::XMVectorSet( node.rotation[0], node.rotation[1], node.rotation[2], node.rotation[3] ) + : DirectX::XMQuaternionIdentity(); + vScale = node.has_scale ? DirectX::XMVectorSet( node.scale[0], node.scale[1], node.scale[2], 1.0f ) + : DirectX::XMVectorSplatOne(); + } + + auto tx = Transform{ + .translation = vTranslation, + .rotation = qRotation, + .scale = vScale, + }; + Entity* entity = entityManager->createEntity( tx ); + + if ( node.mesh ) + { + entity->modelMesh = ProcessMesh( renderDevice, model, vertices, indices, *node.mesh ); + } + + for ( uint32_t childIdx = 0; childIdx < node.children_count; ++childIdx ) + { + entity->addChild( ProcessNode( renderDevice, entityManager, model, vertices, indices, *node.children[childIdx] ) ); + } + + return entity; +} + + +Entity* LoadModel( RenderDevice* renderDevice, EntityManager* entityManager, const char* filename ) +{ + cgltf_data* gltfModel = nullptr; + cgltf_options options = {}; + cgltf_result result = cgltf_parse_file( &options, filename, &gltfModel ); + + if ( result != cgltf_result_success ) + { + SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "%s failed to load", filename ); + cgltf_free( gltfModel ); + + return nullptr; + } + + result = cgltf_validate( gltfModel ); + + if ( result != cgltf_result_success ) + { + SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "%s is invalid.", filename ); + cgltf_free( gltfModel ); + + return nullptr; + } + + result = cgltf_load_buffers( &options, gltfModel, filename ); + + if ( result != cgltf_result_success ) + { + SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "%s buffers failed to load.", filename ); + cgltf_free( gltfModel ); + + return nullptr; + } + + Entity* entity = entityManager->createEntity( { + .translation = DirectX::XMVectorZero(), + .rotation = DirectX::XMQuaternionIdentity(), + .scale = DirectX::XMVectorSplatOne(), + } ); + + // Output data + std::pmr::vector vertices; + std::pmr::vector indices; + + cgltf_scene const* currentScene = gltfModel->scene; + for ( uint32_t nodeIdx = 0; nodeIdx < currentScene->nodes_count; ++nodeIdx ) + { + entity->addChild( ProcessNode( + renderDevice, entityManager, &entity->model, &vertices, &indices, *currentScene->nodes[nodeIdx] ) ); + } + + auto vertexBuffer = renderDevice->bufferManager->createVertexBuffer( vertices.size() * sizeof vertices[0] ); + if ( not vertexBuffer ) + { + return nullptr; + } + entity->model.vertexBuffer = std::move( vertexBuffer.value() ); + + renderDevice->bufferManager->writeToBuffer( entity->model.vertexBuffer, std::span{ vertices } ); + + auto indexBuffer = renderDevice->bufferManager->createIndexBuffer( indices.size() * sizeof indices[0] ); + if ( not indexBuffer ) + { + return nullptr; + } + entity->model.indexBuffer = std::move( indexBuffer.value() ); + + renderDevice->bufferManager->writeToBuffer( entity->model.indexBuffer, std::span{ indices } ); + + cgltf_free( gltfModel ); + return entity; +} diff --git a/Blaze/ModelLoader.h b/Blaze/ModelLoader.h new file mode 100644 index 0000000..f2d549d --- /dev/null +++ b/Blaze/ModelLoader.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +#include "BufferManager.h" +#include "TextureManager.h" + +struct RenderDevice; +struct EntityManager; +struct Entity; +struct GlobalMemory; + +struct Vertex +{ + DirectX::XMFLOAT3 position = { 0.0f, 0.0f, 0.0f }; + DirectX::XMFLOAT3 normal = { 1.0f, 1.0f, 1.0f }; + DirectX::XMFLOAT2 texCoord0 = { 0.0f, 0.0f }; + DirectX::XMFLOAT2 texCoord1 = { 0.0f, 0.0f }; + DirectX::XMFLOAT4 color0 = { 1.0f, 1.0f, 1.0f, 1.0f }; +}; + +struct Primitive +{ + uint32_t indexStart; + uint32_t indexCount; + uint32_t material; + int32_t vertexOffset; +}; + +struct ModelMesh +{ + uint32_t primitiveStart = 0; + uint32_t primitiveCount = 0; + + [[nodiscard]] bool isNull() const + { + return primitiveCount == 0; + } +}; + +struct Material +{ + constexpr static size_t GPU_DATA_OFFSET = sizeof( VkSampler ); + constexpr static size_t GPU_DATA_SIZE = + sizeof( TextureID ) + sizeof( uint32_t ) + 2 * sizeof( float ) + sizeof( DirectX::XMFLOAT4 ); + + VkSampler sampler; // TODO: Reuse + // To copy directly. + TextureID texture; + uint32_t padding0; // FIXME: Wasting space. + float roughness = 1.0f; + float metallic = 1.0f; + DirectX::XMFLOAT4 baseColor = { 1.0f, 1.0f, 1.0f, 1.0f }; + + [[nodiscard]] bool isNull() const + { + return texture.isNull() or sampler; + } +}; + +static_assert( sizeof( Material ) == Material::GPU_DATA_OFFSET + Material::GPU_DATA_SIZE ); + +static constexpr Material DEFAULT_MATERIAL = {}; + +struct Model +{ + std::pmr::monotonic_buffer_resource mem; + + BufferID vertexBuffer; + BufferID indexBuffer; + std::pmr::vector materials; + std::pmr::vector primitives; + + [[nodiscard]] bool isNull() const + { + return vertexBuffer.isNull(); + } +}; + +Entity* LoadModel( RenderDevice* renderDevice, EntityManager* entityManager, const char* filename ); diff --git a/Blaze/RID.h b/Blaze/RID.h index cc4774e..7a9537b 100644 --- a/Blaze/RID.h +++ b/Blaze/RID.h @@ -20,7 +20,12 @@ public: // Move allowed RID( RID&& other ) noexcept; - RID& operator=( RID&& other ) noexcept; + RID& operator=( RID&& other ) noexcept; + + [[nodiscard]] bool isNull() const + { + return m_index == 0; + } static RID null() { diff --git a/PLAN.md b/PLAN.md index b7aa37b..e4c8d80 100644 --- a/PLAN.md +++ b/PLAN.md @@ -17,10 +17,10 @@ - [X] Create a Triangle - [X] Create pipeline - [X] Draw -- [ ] Create a Box - - [ ] Create Vertex buffer - - [ ] Load texture - - [ ] Draw +- [X] Create a Box + - [X] Create Vertex buffer + - [X] Load texture + - [X] Draw - [ ] Refactor ## Features