Added Model Loading.
This commit is contained in:
parent
dfdc0dd6d6
commit
9a2a6a3340
|
|
@ -3,3 +3,5 @@
|
||||||
*.glb filter=lfs diff=lfs merge=lfs -text
|
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||||
*.hdr filter=lfs diff=lfs merge=lfs -text
|
*.hdr filter=lfs diff=lfs merge=lfs -text
|
||||||
*.exr 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
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -3,8 +3,10 @@ import Bindless;
|
||||||
struct VertexOut {
|
struct VertexOut {
|
||||||
float4 outPosition : SV_Position;
|
float4 outPosition : SV_Position;
|
||||||
float4 screenPosition : ScreenPosition;
|
float4 screenPosition : ScreenPosition;
|
||||||
float4 vertexColor : CoarseColor;
|
float4 normal : CoarseNormal;
|
||||||
float2 texCoord0 : TexCoord0;
|
float2 texCoord0 : TexCoord0;
|
||||||
|
float2 texCoord1 : TexCoord1;
|
||||||
|
float4 vertexColor0 : VertexColor;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CameraData {
|
struct CameraData {
|
||||||
|
|
@ -17,6 +19,10 @@ uniform ParameterBlock<CameraData> camera;
|
||||||
struct PerInstanceData {
|
struct PerInstanceData {
|
||||||
float4x4 transform;
|
float4x4 transform;
|
||||||
Sampler2D.RID textureID;
|
Sampler2D.RID textureID;
|
||||||
|
uint _padding;
|
||||||
|
float metallic;
|
||||||
|
float roughness;
|
||||||
|
float4 baseColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[vk::push_constant]]
|
[[vk::push_constant]]
|
||||||
|
|
@ -26,27 +32,37 @@ uniform ConstantBuffer<PerInstanceData> pcb;
|
||||||
VertexOut VertexMain(
|
VertexOut VertexMain(
|
||||||
uint vertexId: SV_VertexID,
|
uint vertexId: SV_VertexID,
|
||||||
float3 position,
|
float3 position,
|
||||||
float3 color,
|
float3 normal,
|
||||||
float2 texCoord0,
|
float2 texCoord0,
|
||||||
|
float2 texCoord1,
|
||||||
|
float4 vertexColor0,
|
||||||
) {
|
) {
|
||||||
VertexOut output;
|
VertexOut output;
|
||||||
output.outPosition = mul(camera.proj, mul(camera.view, mul(pcb.transform, float4(position, 1.0f))));
|
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.screenPosition = mul(camera.proj, mul(camera.view, mul(pcb.transform, float4(position, 1.0f))));
|
||||||
output.vertexColor = float4(color, 1.0f);
|
output.normal = mul(pcb.transform, float4(normalize(normal.rgb), 0.0f));
|
||||||
output.texCoord0 = texCoord0 * 2.0f;
|
output.texCoord0 = texCoord0;
|
||||||
|
output.texCoord1 = texCoord1;
|
||||||
|
output.vertexColor0 = vertexColor0;
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
[shader("fragment")]
|
[shader("fragment")]
|
||||||
float4 FragmentMain(
|
float4 FragmentMain(
|
||||||
float4 interpolatePosition : ScreenPosition,
|
float4 interpolatePosition : ScreenPosition,
|
||||||
float4 interpolatedColors : CoarseColor,
|
float4 interpolatedNormal : CoarseNormal,
|
||||||
float2 uv0 : TexCoord0,
|
float2 uv0 : TexCoord0,
|
||||||
|
float2 uv1 : TexCoord1,
|
||||||
|
float4 interpolatedColor : VertexColor,
|
||||||
) : SV_Target0 {
|
) : SV_Target0 {
|
||||||
|
|
||||||
|
let N = interpolatedNormal.xyz;
|
||||||
|
let L = dot(normalize(N), float3(1.0f, 1.0f, -1.0f));
|
||||||
|
|
||||||
if (let texture = pcb.textureID) {
|
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 {
|
} else {
|
||||||
return interpolatedColors;
|
return pcb.baseColor * interpolatedColor * L;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@
|
||||||
<ClInclude Include="Blaze\MacroUtils.h" />
|
<ClInclude Include="Blaze\MacroUtils.h" />
|
||||||
<ClInclude Include="Blaze\MathUtil.h" />
|
<ClInclude Include="Blaze\MathUtil.h" />
|
||||||
<ClInclude Include="Blaze\MiscData.h" />
|
<ClInclude Include="Blaze\MiscData.h" />
|
||||||
|
<ClInclude Include="Blaze\ModelLoader.h" />
|
||||||
<ClInclude Include="Blaze\RenderDevice.h" />
|
<ClInclude Include="Blaze\RenderDevice.h" />
|
||||||
<ClInclude Include="Blaze\RID.h" />
|
<ClInclude Include="Blaze\RID.h" />
|
||||||
<ClInclude Include="Blaze\TextureManager.h" />
|
<ClInclude Include="Blaze\TextureManager.h" />
|
||||||
|
|
@ -189,11 +190,13 @@
|
||||||
<ClCompile Include="Blaze\AppState.cpp" />
|
<ClCompile Include="Blaze\AppState.cpp" />
|
||||||
<ClCompile Include="Blaze\Blaze.cpp" />
|
<ClCompile Include="Blaze\Blaze.cpp" />
|
||||||
<ClCompile Include="Blaze\BufferManager.cpp" />
|
<ClCompile Include="Blaze\BufferManager.cpp" />
|
||||||
|
<ClCompile Include="Blaze\CgltfImpl.cpp" />
|
||||||
<ClCompile Include="Blaze\EntityManager.cpp" />
|
<ClCompile Include="Blaze\EntityManager.cpp" />
|
||||||
<ClCompile Include="Blaze\Frame.cpp" />
|
<ClCompile Include="Blaze\Frame.cpp" />
|
||||||
<ClCompile Include="Blaze\FreeList.cpp" />
|
<ClCompile Include="Blaze\FreeList.cpp" />
|
||||||
<ClCompile Include="Blaze\GlobalMemory.cpp" />
|
<ClCompile Include="Blaze\GlobalMemory.cpp" />
|
||||||
<ClCompile Include="Blaze\MiscData.cpp" />
|
<ClCompile Include="Blaze\MiscData.cpp" />
|
||||||
|
<ClCompile Include="Blaze\ModelLoader.cpp" />
|
||||||
<ClCompile Include="Blaze\RenderDevice.cpp" />
|
<ClCompile Include="Blaze\RenderDevice.cpp" />
|
||||||
<ClCompile Include="Blaze\StbImpl.cpp" />
|
<ClCompile Include="Blaze\StbImpl.cpp" />
|
||||||
<ClCompile Include="Blaze\TextureManager.cpp" />
|
<ClCompile Include="Blaze\TextureManager.cpp" />
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,9 @@
|
||||||
<ClCompile Include="Blaze\ModelLoader.cpp">
|
<ClCompile Include="Blaze\ModelLoader.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Blaze\CgltfImpl.cpp">
|
||||||
|
<Filter>Source Files\HeaderOnlyImpl</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Image Include="Assets\Textures\container2.png">
|
<Image Include="Assets\Textures\container2.png">
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ AppState* AppState_Create( GlobalMemory* memory, uint32_t const width, uint32_t
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityManager* entityManager = EntityManager_Create( memory, renderDevice, 10 );
|
EntityManager* entityManager = EntityManager_Create( memory, renderDevice, 1000 );
|
||||||
if ( !entityManager )
|
if ( !entityManager )
|
||||||
{
|
{
|
||||||
SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "EntityManager failed to init" );
|
SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "EntityManager failed to init" );
|
||||||
|
|
|
||||||
119
Blaze/Blaze.cpp
119
Blaze/Blaze.cpp
|
|
@ -24,6 +24,8 @@
|
||||||
#include "MiscData.h"
|
#include "MiscData.h"
|
||||||
#include "RenderDevice.h"
|
#include "RenderDevice.h"
|
||||||
|
|
||||||
|
#include "ModelLoader.h"
|
||||||
|
|
||||||
constexpr uint32_t WIDTH = 1280;
|
constexpr uint32_t WIDTH = 1280;
|
||||||
constexpr uint32_t HEIGHT = 720;
|
constexpr uint32_t HEIGHT = 720;
|
||||||
constexpr uint32_t NUM_FRAMES = 3;
|
constexpr uint32_t NUM_FRAMES = 3;
|
||||||
|
|
@ -44,57 +46,9 @@ SDL_AppResult SDL_AppInit( void** appstate, int, char** )
|
||||||
|
|
||||||
AppState& appState = *static_cast<AppState*>( *appstate );
|
AppState& appState = *static_cast<AppState*>( *appstate );
|
||||||
|
|
||||||
// TODO: Integrate this
|
Entity const* entity =
|
||||||
|
LoadModel( appState.renderDevice, appState.entityManager, "Assets/Models/OrientationTest.glb" );
|
||||||
// TL----TR
|
ASSERT( entity );
|
||||||
// | \ |
|
|
||||||
// | \ |
|
|
||||||
// | \ |
|
|
||||||
// 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<float>( i );
|
|
||||||
appState.entityManager->createEntity(
|
|
||||||
modelTransform, vertices, i > 0 ? "Assets/Textures/wall.jpg" : "Assets/Textures/container2.png" );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return SDL_APP_CONTINUE;
|
return SDL_APP_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
@ -133,11 +87,13 @@ SDL_AppResult SDL_AppIterate( void* appstate )
|
||||||
|
|
||||||
for ( Entity& entity : entityManager.iter() )
|
for ( Entity& entity : entityManager.iter() )
|
||||||
{
|
{
|
||||||
entity.transform().rotation = DirectX::XMQuaternionMultiply(
|
if ( not entity.isRoot() ) continue;
|
||||||
|
|
||||||
|
entity.transform.rotation = DirectX::XMQuaternionMultiply(
|
||||||
DirectX::XMQuaternionRotationAxis(
|
DirectX::XMQuaternionRotationAxis(
|
||||||
DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ),
|
DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ),
|
||||||
DirectX::XMConvertToRadians( 60.0f ) * static_cast<float>( deltaTime ) ),
|
DirectX::XMConvertToRadians( 60.0f ) * static_cast<float>( deltaTime ) ),
|
||||||
entity.transform().rotation );
|
entity.transform.rotation );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t currentImageIndex;
|
uint32_t currentImageIndex;
|
||||||
|
|
@ -251,41 +207,66 @@ SDL_AppResult SDL_AppIterate( void* appstate )
|
||||||
vkCmdBindDescriptorSets(
|
vkCmdBindDescriptorSets(
|
||||||
cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 1, 1, &misc.descriptorSet, 0, nullptr );
|
cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, misc.pipelineLayout, 1, 1, &misc.descriptorSet, 0, nullptr );
|
||||||
|
|
||||||
std::function<void( Entity const&, DirectX::XMMATRIX const& )> drawEntity =
|
std::function<void( Entity const&, DirectX::XMMATRIX const&, Model const* )> drawEntity =
|
||||||
[&]( Entity const& entity, DirectX::XMMATRIX const& parent )
|
[&]( Entity const& entity, DirectX::XMMATRIX const& parent, Model const* current )
|
||||||
{
|
{
|
||||||
Transform const& localTransform = entity.transform();
|
Transform const& localTransform = entity.transform;
|
||||||
DirectX::XMMATRIX worldTransform;
|
DirectX::XMMATRIX worldTransform;
|
||||||
{
|
{
|
||||||
auto [x, y, z] = localTransform.position;
|
worldTransform =
|
||||||
auto scale = localTransform.scale;
|
DirectX::XMMatrixAffineTransformation(
|
||||||
worldTransform = DirectX::XMMatrixScaling( scale, scale, scale ) *
|
localTransform.scale, DirectX::XMVectorZero(), localTransform.rotation, localTransform.translation ) *
|
||||||
DirectX::XMMatrixRotationQuaternion( localTransform.rotation ) *
|
parent;
|
||||||
DirectX::XMMatrixTranslation( x, y, z ) * parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VkBuffer const vertexBuffer =
|
if ( not entity.model.isNull() )
|
||||||
renderDevice.bufferManager->fetchBuffer( entity.mesh().buffer ).value_or( nullptr );
|
|
||||||
if ( vertexBuffer )
|
|
||||||
{
|
{
|
||||||
|
VkBuffer const vertexBuffer = renderDevice.bufferManager->fetchBuffer( entity.model.vertexBuffer ).value();
|
||||||
|
|
||||||
|
VkBuffer const indexBuffer = renderDevice.bufferManager->fetchBuffer( entity.model.indexBuffer ).value();
|
||||||
VkDeviceSize constexpr offset = 0;
|
VkDeviceSize constexpr offset = 0;
|
||||||
vkCmdBindVertexBuffers( cmd, 0, 1, &vertexBuffer, &offset );
|
vkCmdBindVertexBuffers( cmd, 0, 1, &vertexBuffer, &offset );
|
||||||
|
vkCmdBindIndexBuffer( cmd, indexBuffer, offset, VK_INDEX_TYPE_UINT32 );
|
||||||
}
|
}
|
||||||
|
|
||||||
vkCmdPushConstants(
|
vkCmdPushConstants(
|
||||||
cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform );
|
cmd, misc.pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof worldTransform, &worldTransform );
|
||||||
|
|
||||||
|
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<byte const*>( mat );
|
||||||
|
materialData += Material::GPU_DATA_OFFSET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
materialData = reinterpret_cast<byte const*>( &DEFAULT_MATERIAL );
|
||||||
|
materialData += Material::GPU_DATA_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
vkCmdPushConstants(
|
vkCmdPushConstants(
|
||||||
cmd,
|
cmd,
|
||||||
misc.pipelineLayout,
|
misc.pipelineLayout,
|
||||||
VK_SHADER_STAGE_ALL_GRAPHICS,
|
VK_SHADER_STAGE_ALL_GRAPHICS,
|
||||||
sizeof worldTransform,
|
sizeof worldTransform,
|
||||||
sizeof entity.material().texture,
|
Material::GPU_DATA_SIZE,
|
||||||
&entity.material().texture );
|
materialData );
|
||||||
vkCmdDraw( cmd, entity.mesh().vertexCount, 1, 0, 0 );
|
|
||||||
|
vkCmdDrawIndexed( cmd, primitive.indexCount, 1, primitive.indexStart, primitive.vertexOffset, 0 );
|
||||||
|
// vkCmdDrawIndexed( cmd, primitive.count, 1, primitive.start, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for ( Entity& child : entity.children() )
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawEntity( entity, DirectX::XMMatrixIdentity() );
|
drawEntity( entity, DirectX::XMMatrixIdentity(), nullptr );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vkCmdEndRendering( cmd );
|
vkCmdEndRendering( cmd );
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,68 @@ std::optional<BufferID> BufferManager::createVertexBuffer( size_t const size )
|
||||||
return std::move( *reinterpret_cast<BufferID*>( &index ) );
|
return std::move( *reinterpret_cast<BufferID*>( &index ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<BufferID> BufferManager::createIndexBuffer( size_t size )
|
||||||
|
{
|
||||||
|
if ( m_freeList.empty() )
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer* bufferSlot = reinterpret_cast<Buffer*>( 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<std::byte*>( allocationInfo.pMappedData ),
|
||||||
|
.size = size,
|
||||||
|
.index = index,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: Memory hackery to create BufferID;
|
||||||
|
return std::move( *reinterpret_cast<BufferID*>( &index ) );
|
||||||
|
}
|
||||||
|
|
||||||
void BufferManager::freeBuffer( BufferID&& rid )
|
void BufferManager::freeBuffer( BufferID&& rid )
|
||||||
{
|
{
|
||||||
if ( not isValidID( rid ) ) return;
|
if ( not isValidID( rid ) ) return;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ public:
|
||||||
|
|
||||||
std::optional<BufferID> createVertexBuffer( size_t size );
|
std::optional<BufferID> createVertexBuffer( size_t size );
|
||||||
|
|
||||||
|
std::optional<BufferID> createIndexBuffer( size_t size );
|
||||||
|
|
||||||
void freeBuffer( BufferID&& rid );
|
void freeBuffer( BufferID&& rid );
|
||||||
|
|
||||||
DEPRECATE_JULY_2025
|
DEPRECATE_JULY_2025
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
#define CGLTF_IMPLEMENTATION
|
||||||
|
#include <cgltf.h>
|
||||||
|
|
@ -5,8 +5,6 @@
|
||||||
#include "GlobalMemory.h"
|
#include "GlobalMemory.h"
|
||||||
#include "RenderDevice.h"
|
#include "RenderDevice.h"
|
||||||
|
|
||||||
#include <stb_image.h>
|
|
||||||
|
|
||||||
#include "Frame.h"
|
#include "Frame.h"
|
||||||
#include "TextureManager.h"
|
#include "TextureManager.h"
|
||||||
|
|
||||||
|
|
@ -102,458 +100,48 @@ EntitySiblingIterable Entity::children() const
|
||||||
return { m_firstChild };
|
return { m_firstChild };
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity::Entity( Transform const& transform, Mesh&& mesh, Material&& material )
|
Entity::Entity( Transform const& transform )
|
||||||
: m_transform{ transform }
|
: transform{ transform }
|
||||||
, m_mesh{ std::forward<Mesh>( mesh ) }
|
, model{}
|
||||||
, m_material{ std::forward<Material>( material ) }
|
, modelMesh{}
|
||||||
, m_parent{ nullptr }
|
, m_parent{ nullptr }
|
||||||
, m_firstChild{ nullptr }
|
, m_firstChild{ nullptr }
|
||||||
, m_prevSibling{ nullptr }
|
, m_prevSibling{ nullptr }
|
||||||
, m_nextSibling{ nullptr }
|
, m_nextSibling{ nullptr }
|
||||||
, m_padding0{ 0 }
|
, m_flags{ 0 }
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Entity* EntityManager::createEntity(
|
Entity* EntityManager::createEntity( Transform const& transform )
|
||||||
Transform const& transform, std::span<Vertex> const vertices, const char* textureFile )
|
|
||||||
{
|
{
|
||||||
ASSERT( pRenderDevice );
|
ASSERT( count < capacity );
|
||||||
RenderDevice& renderDevice = *pRenderDevice;
|
|
||||||
|
|
||||||
Mesh mesh;
|
Entity& entity = entities[count++];
|
||||||
{
|
new ( &entity ) Entity{ transform };
|
||||||
mesh.vertexCount = static_cast<uint32_t>( vertices.size() );
|
|
||||||
|
|
||||||
auto vertexBuffer = renderDevice.bufferManager->createVertexBuffer( vertices.size_bytes() );
|
return &entity;
|
||||||
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<int>( 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<uint32_t>( w );
|
|
||||||
height = static_cast<uint32_t>( 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<VkDeviceSize>( 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<uint32_t>( 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<uint32_t>( 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<int32_t>( width );
|
|
||||||
int32_t mipSrcHeight = static_cast<int32_t>( 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityManager::destroyEntity( Entity* entity )
|
void EntityManager::destroyEntity( Entity* entity )
|
||||||
{
|
{
|
||||||
ASSERT( entity );
|
ASSERT( entity );
|
||||||
if ( not entity->isInit() ) return;
|
|
||||||
|
|
||||||
VkDevice const device = pRenderDevice->device;
|
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->model.vertexBuffer ) );
|
||||||
pRenderDevice->bufferManager->freeBuffer( std::move( entity->mesh().buffer ) );
|
pRenderDevice->bufferManager->freeBuffer( std::move( entity->model.indexBuffer ) );
|
||||||
|
entity->model.primitives.clear();
|
||||||
|
entity->model.materials.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
entity->modelMesh = { 0, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityManager::destroy()
|
void EntityManager::destroy()
|
||||||
|
|
|
||||||
|
|
@ -8,37 +8,18 @@
|
||||||
#include "VulkanHeader.h"
|
#include "VulkanHeader.h"
|
||||||
// TODO: Remove this dependency
|
// TODO: Remove this dependency
|
||||||
#include "BufferManager.h"
|
#include "BufferManager.h"
|
||||||
|
#include "ModelLoader.h"
|
||||||
#include "TextureManager.h"
|
#include "TextureManager.h"
|
||||||
|
|
||||||
struct Entity;
|
struct Entity;
|
||||||
struct RenderDevice;
|
struct RenderDevice;
|
||||||
struct GlobalMemory;
|
struct GlobalMemory;
|
||||||
|
|
||||||
struct Vertex
|
|
||||||
{
|
|
||||||
DirectX::XMFLOAT3 position;
|
|
||||||
DirectX::XMFLOAT3 color;
|
|
||||||
DirectX::XMFLOAT2 texCoord0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Transform
|
struct Transform
|
||||||
{
|
{
|
||||||
DirectX::XMFLOAT3 position;
|
DirectX::XMVECTOR translation;
|
||||||
float scale;
|
|
||||||
DirectX::XMVECTOR rotation;
|
DirectX::XMVECTOR rotation;
|
||||||
};
|
DirectX::XMVECTOR scale;
|
||||||
|
|
||||||
struct Mesh
|
|
||||||
{
|
|
||||||
BufferID buffer;
|
|
||||||
uint32_t vertexCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Material
|
|
||||||
{
|
|
||||||
VkSampler sampler; // TODO: Reuse
|
|
||||||
TextureID texture;
|
|
||||||
uint32_t padding0; // FIXME: Wasting space.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EntitySiblingIterable
|
struct EntitySiblingIterable
|
||||||
|
|
@ -60,52 +41,18 @@ struct EntitySiblingIterable
|
||||||
|
|
||||||
struct Entity
|
struct Entity
|
||||||
{
|
{
|
||||||
|
Transform transform;
|
||||||
|
Model model;
|
||||||
|
ModelMesh modelMesh;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Transform m_transform;
|
|
||||||
Mesh m_mesh;
|
|
||||||
Material m_material;
|
|
||||||
Entity* m_parent; // TODO: Switch to EntityIndex.
|
Entity* m_parent; // TODO: Switch to EntityIndex.
|
||||||
Entity* m_firstChild;
|
Entity* m_firstChild;
|
||||||
Entity* m_prevSibling;
|
Entity* m_prevSibling;
|
||||||
Entity* m_nextSibling;
|
Entity* m_nextSibling;
|
||||||
uint64_t m_padding0; // FIXME: Wasting space.
|
uint64_t m_flags; // FIXME: Wasting space.
|
||||||
|
|
||||||
public:
|
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
|
[[nodiscard]] bool isRoot() const
|
||||||
{
|
{
|
||||||
return not m_parent;
|
return not m_parent;
|
||||||
|
|
@ -132,7 +79,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] EntitySiblingIterable children() const;
|
[[nodiscard]] EntitySiblingIterable children() const;
|
||||||
|
|
||||||
Entity( Transform const& transform, Mesh&& mesh, Material&& material );
|
explicit Entity( Transform const& transform );
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EntityManager
|
struct EntityManager
|
||||||
|
|
@ -173,7 +120,7 @@ struct EntityManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make Entities return ID, make it a sparse indexing system.
|
// Make Entities return ID, make it a sparse indexing system.
|
||||||
Entity* createEntity( Transform const& transform, std::span<Vertex> vertices, const char* textureFile );
|
Entity* createEntity( Transform const& transform );
|
||||||
|
|
||||||
void destroyEntity( Entity* entity );
|
void destroyEntity( Entity* entity );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
using byte = std::byte;
|
||||||
|
|
||||||
template <std::totally_ordered T>
|
template <std::totally_ordered T>
|
||||||
T Clamp( T const val, T const minVal, T const maxVal )
|
T Clamp( T const val, T const minVal, T const maxVal )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
|
||||||
VkPushConstantRange const pushConstantRange = {
|
VkPushConstantRange const pushConstantRange = {
|
||||||
.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS,
|
.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS,
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.size = sizeof( DirectX::XMMATRIX ) + sizeof( uint32_t ),
|
.size = sizeof( DirectX::XMMATRIX ) + Material::GPU_DATA_SIZE
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array const descriptorSetLayouts = {
|
std::array const descriptorSetLayouts = {
|
||||||
|
|
@ -118,7 +118,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
|
||||||
.location = 1,
|
.location = 1,
|
||||||
.binding = 0,
|
.binding = 0,
|
||||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||||
.offset = offsetof( Vertex, color ),
|
.offset = offsetof( Vertex, normal ),
|
||||||
},
|
},
|
||||||
VkVertexInputAttributeDescription{
|
VkVertexInputAttributeDescription{
|
||||||
.location = 2,
|
.location = 2,
|
||||||
|
|
@ -126,6 +126,18 @@ bool MiscData::init( RenderDevice const& renderDevice )
|
||||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||||
.offset = offsetof( Vertex, texCoord0 ),
|
.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 = {
|
VkPipelineVertexInputStateCreateInfo const vertexInputState = {
|
||||||
|
|
@ -142,7 +154,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
|
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||||
.primitiveRestartEnable = VK_FALSE,
|
.primitiveRestartEnable = VK_FALSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -277,7 +289,7 @@ bool MiscData::init( RenderDevice const& renderDevice )
|
||||||
|
|
||||||
// Camera
|
// 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 );
|
cameraTarget = DirectX::XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f );
|
||||||
cameraUp = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 1.0f );
|
cameraUp = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 1.0f );
|
||||||
cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraPosition, cameraTarget, cameraUp );
|
cameraData.viewMatrix = DirectX::XMMatrixLookAtLH( cameraPosition, cameraTarget, cameraUp );
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,756 @@
|
||||||
|
#include "ModelLoader.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <DirectXMath.h>
|
||||||
|
#include <SDL3/SDL_log.h>
|
||||||
|
#include <cgltf.h>
|
||||||
|
#include <stb_image.h>
|
||||||
|
|
||||||
|
#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<byte*>( baseColorImage->buffer_view->data );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = static_cast<byte*>( 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<int>( numChannels );
|
||||||
|
|
||||||
|
textureData = stbi_load_from_memory(
|
||||||
|
reinterpret_cast<stbi_uc const*>( data ), static_cast<int>( size ), &w, &h, &nc, requestedChannels );
|
||||||
|
ASSERT( nc <= requestedChannels );
|
||||||
|
|
||||||
|
if ( not textureData )
|
||||||
|
{
|
||||||
|
return UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
width = static_cast<uint32_t>( w );
|
||||||
|
height = static_cast<uint32_t>( 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<VkDeviceSize>( 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<uint32_t>( 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<uint32_t>( 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<int32_t>( width );
|
||||||
|
int32_t mipSrcHeight = static_cast<int32_t>( 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<uint32_t>( 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<Vertex>* pVertices,
|
||||||
|
std::pmr::vector<uint32_t>* pIndices,
|
||||||
|
cgltf_mesh const& mesh )
|
||||||
|
{
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
uint32_t const primitiveStart = static_cast<uint32_t>( model->primitives.size() );
|
||||||
|
uint32_t const primitiveCount = static_cast<uint32_t>( 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<int32_t>( 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<uint32_t>( indexStart ),
|
||||||
|
.indexCount = static_cast<uint32_t>( 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<DirectX::XMFLOAT3> 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<cgltf_float*>( 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<DirectX::XMFLOAT3> 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<cgltf_float*>( 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<DirectX::XMFLOAT2> 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<cgltf_float*>( 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<DirectX::XMFLOAT2> 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<cgltf_float*>( 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<DirectX::XMFLOAT3> 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<cgltf_float*>( 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<DirectX::XMFLOAT4> 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<cgltf_float*>( 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<Vertex>* vertices,
|
||||||
|
std::pmr::vector<uint32_t>* 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<Vertex> vertices;
|
||||||
|
std::pmr::vector<uint32_t> 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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DirectXMath.h>
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<Material> materials;
|
||||||
|
std::pmr::vector<Primitive> primitives;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isNull() const
|
||||||
|
{
|
||||||
|
return vertexBuffer.isNull();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Entity* LoadModel( RenderDevice* renderDevice, EntityManager* entityManager, const char* filename );
|
||||||
|
|
@ -22,6 +22,11 @@ public:
|
||||||
RID( RID&& other ) noexcept;
|
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()
|
static RID null()
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
|
|
|
||||||
8
PLAN.md
8
PLAN.md
|
|
@ -17,10 +17,10 @@
|
||||||
- [X] Create a Triangle
|
- [X] Create a Triangle
|
||||||
- [X] Create pipeline
|
- [X] Create pipeline
|
||||||
- [X] Draw
|
- [X] Draw
|
||||||
- [ ] Create a Box
|
- [X] Create a Box
|
||||||
- [ ] Create Vertex buffer
|
- [X] Create Vertex buffer
|
||||||
- [ ] Load texture
|
- [X] Load texture
|
||||||
- [ ] Draw
|
- [X] Draw
|
||||||
- [ ] Refactor
|
- [ ] Refactor
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue