From 1b66dcfa8bd1f2deddb3e406653a3553c5549bf5 Mon Sep 17 00:00:00 2001 From: s-ol Date: Sat, 27 Mar 2021 10:12:40 +0100 Subject: C API for GLTFLoader --- AssetLoader/include/GLTFLoader.hpp | 442 ++++++++++++++++++++++++++++ AssetLoader/include/GLTFResourceManager.hpp | 161 ++++++++++ 2 files changed, 603 insertions(+) create mode 100644 AssetLoader/include/GLTFLoader.hpp create mode 100644 AssetLoader/include/GLTFResourceManager.hpp (limited to 'AssetLoader/include') diff --git a/AssetLoader/include/GLTFLoader.hpp b/AssetLoader/include/GLTFLoader.hpp new file mode 100644 index 0000000..83b9a4d --- /dev/null +++ b/AssetLoader/include/GLTFLoader.hpp @@ -0,0 +1,442 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../DiligentCore/Graphics/GraphicsEngine/interface/RenderDevice.h" +#include "../../../DiligentCore/Graphics/GraphicsEngine/interface/DeviceContext.h" +#include "../../../DiligentCore/Common/interface/RefCntAutoPtr.hpp" +#include "../../../DiligentCore/Common/interface/AdvancedMath.hpp" +#include "GLTFLoader.h" +#include "GLTFResourceManager.hpp" + +namespace tinygltf +{ + +class Node; +class Model; + +} // namespace tinygltf + +namespace Diligent +{ + +namespace GLTF +{ + +struct Material +{ + Material() noexcept + { + TextureIds.fill(-1); + } + + enum PBR_WORKFLOW + { + PBR_WORKFLOW_METALL_ROUGH = 0, + PBR_WORKFLOW_SPEC_GLOSS + }; + + // Material attributes packed in a shader-friendly format + struct ShaderAttribs + { + float4 BaseColorFactor = float4{1, 1, 1, 1}; + float4 EmissiveFactor = float4{1, 1, 1, 1}; + float4 SpecularFactor = float4{1, 1, 1, 1}; + + int Workflow = PBR_WORKFLOW_METALL_ROUGH; + float BaseColorUVSelector = -1; + float PhysicalDescriptorUVSelector = -1; + float NormalUVSelector = -1; + + float OcclusionUVSelector = -1; + float EmissiveUVSelector = -1; + float BaseColorSlice = 0; + float PhysicalDescriptorSlice = 0; + + float NormalSlice = 0; + float OcclusionSlice = 0; + float EmissiveSlice = 0; + float MetallicFactor = 1; + + float RoughnessFactor = 1; + int AlphaMode = GLTF_MAT_ALPHA_MODE_OPAQUE; + float AlphaCutoff = 0.5f; + float Dummy0; + + // When texture atlas is used, UV scale and bias applied to + // each texture coordinate set + float4 BaseColorUVScaleBias = float4{1, 1, 0, 0}; + float4 PhysicalDescriptorUVScaleBias = float4{1, 1, 0, 0}; + float4 NormalUVScaleBias = float4{1, 1, 0, 0}; + float4 OcclusionUVScaleBias = float4{1, 1, 0, 0}; + float4 EmissiveUVScaleBias = float4{1, 1, 0, 0}; + }; + static_assert(sizeof(ShaderAttribs) % 16 == 0, "ShaderAttribs struct must be 16-byte aligned"); + ShaderAttribs Attribs; + + bool DoubleSided = false; + + enum TEXTURE_ID + { + // Base color for metallic-roughness workflow or + // diffuse color for specular-glossinees workflow + TEXTURE_ID_BASE_COLOR = 0, + + // Metallic-roughness or specular-glossinees map + TEXTURE_ID_PHYSICAL_DESC, + + TEXTURE_ID_NORMAL_MAP, + TEXTURE_ID_OCCLUSION, + TEXTURE_ID_EMISSIVE, + TEXTURE_ID_NUM_TEXTURES + }; + // Texture indices in Model.Textures array + std::array TextureIds = {}; +}; + + +struct Primitive +{ + const Uint32 FirstIndex; + const Uint32 IndexCount; + const Uint32 VertexCount; + const Uint32 MaterialId; + + const BoundBox BB; + + Primitive(Uint32 _FirstIndex, + Uint32 _IndexCount, + Uint32 _VertexCount, + Uint32 _MaterialId, + const float3& BBMin, + const float3& BBMax) : + FirstIndex{_FirstIndex}, + IndexCount{_IndexCount}, + VertexCount{_VertexCount}, + MaterialId{_MaterialId}, + BB{BBMin, BBMax} + { + } + + Primitive(Primitive&&) = default; + + bool HasIndices() const + { + return IndexCount > 0; + } +}; + + + +struct Mesh +{ + std::vector Primitives; + + BoundBox BB; + + // There may be no primitives in the mesh, in which + // case the bounding box will be invalid. + bool IsValidBB() const + { + return !Primitives.empty(); + } + + struct TransformData + { + float4x4 matrix; + std::vector jointMatrices; + }; + + TransformData Transforms; + + Mesh(const float4x4& matrix); +}; + + +struct Node; +struct Skin +{ + std::string Name; + Node* pSkeletonRoot = nullptr; + std::vector InverseBindMatrices; + std::vector Joints; +}; + +struct Camera +{ + enum class Projection + { + Unknown, + Perspective, + Orthographic + } Type = Projection::Unknown; + + std::string Name; + + struct PerspectiveAttribs + { + float AspectRatio; + float YFov; + float ZNear; + float ZFar; + }; + struct OrthographicAttribs + { + float XMag; + float YMag; + float ZNear; + float ZFar; + }; + union + { + PerspectiveAttribs Perspective = {}; + OrthographicAttribs Orthographic; + }; + + float4x4 matrix; +}; + +struct Node +{ + std::string Name; + Node* Parent = nullptr; + Uint32 Index; + + std::vector> Children; + + float4x4 Matrix; + std::unique_ptr pMesh; + std::unique_ptr pCamera; + Skin* pSkin = nullptr; + Int32 SkinIndex = -1; + float3 Translation; + float3 Scale = float3{1, 1, 1}; + Quaternion Rotation; + BoundBox BVH; + BoundBox AABB; + + bool IsValidBVH = false; + + float4x4 LocalMatrix() const; + float4x4 GetMatrix() const; + void UpdateTransforms(); +}; + + +struct AnimationChannel +{ + enum PATH_TYPE + { + TRANSLATION, + ROTATION, + SCALE + }; + PATH_TYPE PathType; + Node* pNode = nullptr; + Uint32 SamplerIndex = static_cast(-1); +}; + + +struct AnimationSampler +{ + enum INTERPOLATION_TYPE + { + LINEAR, + STEP, + CUBICSPLINE + }; + INTERPOLATION_TYPE Interpolation; + std::vector Inputs; + std::vector OutputsVec4; +}; + +struct Animation +{ + std::string Name; + std::vector Samplers; + std::vector Channels; + + float Start = std::numeric_limits::max(); + float End = std::numeric_limits::min(); +}; + +struct GLTF_TextureCacheType +{ + std::mutex TexturesMtx; + + std::unordered_map> Textures; +}; + +class Model : public IGLTFModel +{ +public: + struct VertexBasicAttribs + { + float3 pos; + float3 normal; + float2 uv0; + float2 uv1; + }; + + struct VertexSkinAttribs + { + float4 joint0; + float4 weight0; + }; + + Uint32 IndexCount = 0; + + /// Transformation matrix that transforms unit cube [0,1]x[0,1]x[0,1] into + /// axis-aligned bounding box in model space. + float4x4 AABBTransform; + + std::vector> Nodes; + std::vector LinearNodes; + + std::vector> Skins; + + std::vector> TextureSamplers; + std::vector Materials; + std::vector Animations; + std::vector Extensions; + + struct Dimensions + { + float3 min = float3{+FLT_MAX, +FLT_MAX, +FLT_MAX}; + float3 max = float3{-FLT_MAX, -FLT_MAX, -FLT_MAX}; + } dimensions; + + Model(IRenderDevice* pDevice, + IDeviceContext* pContext, + const IGLTFModelCreateInfo& CI); + + ~Model(); + + virtual void UpdateAnimation(Uint32 index, float time) override final; + + virtual void PrepareGPUResources(IRenderDevice* pDevice, IDeviceContext* pCtx) override final; + + virtual bool IsGPUDataInitialized() const override final + { + return GPUDataInitialized.load(); + } + + virtual void Transform(const float* Matrix) override final; + + virtual IBuffer* GetBuffer(GLTF_BUFFER_ID BuffId) override final + { + return Buffers[BuffId].pBuffer; + } + + virtual ITexture* GetTexture(Uint32 Index) override final + { + return Textures[Index].pTexture; + } + + virtual Uint32 GetFirstIndexLocation() const override final + { + auto& IndBuff = Buffers[GLTF_BUFFER_ID_INDEX]; + VERIFY(!IndBuff.pSuballocation || IndBuff.pSuballocation->GetOffset() % sizeof(Uint32) == 0, + "Allocation offset is not multiple of sizeof(Uint32)"); + return IndBuff.pSuballocation ? + static_cast(IndBuff.pSuballocation->GetOffset() / sizeof(Uint32)) : + 0; + } + + virtual Uint32 GetBaseVertex() const override final + { + auto& VertBuff = Buffers[GLTF_BUFFER_ID_VERTEX_BASIC_ATTRIBS]; + VERIFY(!VertBuff.pSuballocation || VertBuff.pSuballocation->GetOffset() % sizeof(VertexBasicAttribs) == 0, + "Allocation offset is not multiple of sizeof(VertexAttribs0)"); + return VertBuff.pSuballocation ? + static_cast(VertBuff.pSuballocation->GetOffset() / sizeof(VertexBasicAttribs)) : + 0; + } + +private: + void LoadFromFile(IRenderDevice* pDevice, + IDeviceContext* pContext, + const IGLTFModelCreateInfo& CI); + + void LoadNode(Node* parent, + const tinygltf::Node& gltf_node, + uint32_t nodeIndex, + const tinygltf::Model& gltf_model, + std::vector& IndexData, + std::vector& VertexBasicData, + std::vector* pVertexSkinData); + + void LoadSkins(const tinygltf::Model& gltf_model); + + void LoadTextures(IRenderDevice* pDevice, + const tinygltf::Model& gltf_model, + const std::string& BaseDir, + GLTF_TextureCacheType* pTextureCache, + ResourceManager* pResourceMgr); + + void LoadTextureSamplers(IRenderDevice* pDevice, const tinygltf::Model& gltf_model); + void LoadMaterials(const tinygltf::Model& gltf_model); + void LoadAnimations(const tinygltf::Model& gltf_model); + void CalculateBoundingBox(Node* node, const Node* parent); + void CalculateSceneDimensions(); + Node* FindNode(Node* parent, Uint32 index); + Node* NodeFromIndex(uint32_t index); + + std::atomic_bool GPUDataInitialized{false}; + + struct BufferInfo + { + RefCntAutoPtr pBuffer; + RefCntAutoPtr pSuballocation; + }; + std::array Buffers; + + struct TextureInfo + { + RefCntAutoPtr pTexture; + RefCntAutoPtr pAtlasSuballocation; + + bool IsValid() const + { + return pTexture || pAtlasSuballocation; + } + }; + std::vector Textures; +}; + +} // namespace GLTF + +} // namespace Diligent diff --git a/AssetLoader/include/GLTFResourceManager.hpp b/AssetLoader/include/GLTFResourceManager.hpp new file mode 100644 index 0000000..a65f9e1 --- /dev/null +++ b/AssetLoader/include/GLTFResourceManager.hpp @@ -0,0 +1,161 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include +#include +#include +#include + +#include "../../../DiligentCore/Graphics/GraphicsEngine/interface/RenderDevice.h" +#include "../../../DiligentCore/Graphics/GraphicsEngine/interface/DeviceContext.h" +#include "../../../DiligentCore/Common/interface/RefCntAutoPtr.hpp" +#include "../../../DiligentCore/Common/interface/ObjectBase.hpp" +#include "../../../DiligentCore/Graphics/GraphicsTools/interface/BufferSuballocator.h" +#include "../../../DiligentCore/Graphics/GraphicsTools/interface/DynamicTextureAtlas.h" + + +namespace Diligent +{ + +namespace GLTF +{ + +/// GLTF resource manager +class ResourceManager final : public ObjectBase +{ +public: + using TBase = ObjectBase; + + struct CreateInfo + { + const BufferSuballocatorCreateInfo* BuffSuballocators = nullptr; // [NumBuffSuballocators] + const DynamicTextureAtlasCreateInfo* TexAtlases = nullptr; // [NumTexAtlases] + + Uint32 NumBuffSuballocators = 0; + Uint32 NumTexAtlases = 0; + + DynamicTextureAtlasCreateInfo DefaultAtlasDesc; + }; + + static RefCntAutoPtr Create(IRenderDevice* pDevice, + const CreateInfo& CI); + + RefCntAutoPtr AllocateBufferSpace(Uint32 BufferIndex, + Uint32 Size, + Uint32 Alignment) + { + RefCntAutoPtr pSuballoc; + m_BufferSuballocators[BufferIndex]->Allocate(Size, Alignment, &pSuballoc); + return pSuballoc; + } + + RefCntAutoPtr AllocateTextureSpace(TEXTURE_FORMAT Fmt, + Uint32 Width, + Uint32 Height, + const char* CacheId = nullptr, + IObject* pUserData = nullptr); + + RefCntAutoPtr FindAllocation(const char* CacheId); + + Uint32 GetTextureVersion() + { + Uint32 Version = 0; + + std::lock_guard Lock{m_AtlasesMtx}; + for (auto atlas_it : m_Atlases) + Version += atlas_it.second->GetVersion(); + + return Version; + } + + Uint32 GetBufferVersion(Uint32 Index) const + { + return m_BufferSuballocators[Index]->GetVersion(); + } + + IBuffer* GetBuffer(Uint32 Index, IRenderDevice* pDevice, IDeviceContext* pContext) + { + return m_BufferSuballocators[Index]->GetBuffer(pDevice, pContext); + } + + ITexture* GetTexture(TEXTURE_FORMAT Fmt, IRenderDevice* pDevice, IDeviceContext* pContext) + { + decltype(m_Atlases)::iterator cache_it; // NB: can't initialize it without locking the mutex + { + std::lock_guard Lock{m_AtlasesMtx}; + cache_it = m_Atlases.find(Fmt); + if (cache_it == m_Atlases.end()) + return nullptr; + } + + return cache_it->second->GetTexture(pDevice, pContext); + } + + // NB: can't return reference here! + TextureDesc GetAtlasDesc(TEXTURE_FORMAT Fmt) + { + { + std::lock_guard Lock{m_AtlasesMtx}; + + auto cache_it = m_Atlases.find(Fmt); + if (cache_it != m_Atlases.end()) + return cache_it->second->GetAtlasDesc(); + } + + // Atlas is not present in the map - use default description + TextureDesc Desc = m_DefaultAtlasDesc.Desc; + Desc.Format = Fmt; + return Desc; + } + +private: + template + friend class Diligent::MakeNewRCObj; + + ResourceManager(IReferenceCounters* pRefCounters, + IRenderDevice* pDevice, + const CreateInfo& CI); + + std::vector> m_BufferSuballocators; + + DynamicTextureAtlasCreateInfo m_DefaultAtlasDesc; + const std::string m_DefaultAtlasName; + + using AtlasesHashMapType = std::unordered_map, std::hash>; + std::mutex m_AtlasesMtx; + AtlasesHashMapType m_Atlases; + + using TexAllocationsHashMapType = std::unordered_map>; + std::mutex m_TexAllocationsMtx; + TexAllocationsHashMapType m_TexAllocations; +}; + +} // namespace GLTF + +} // namespace Diligent -- cgit v1.2.3