/* Copyright 2019 Diligent Graphics LLC * * 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 * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. * * 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. */ #include #include #include "DXSDKMeshLoader.h" #include "DataBlobImpl.h" #include "RefCntAutoPtr.h" #include "FileWrapper.h" #include "TextureUtilities.h" #include "GraphicsAccessories.h" namespace Diligent { //-------------------------------------------------------------------------------------- bool DXSDKMesh::CreateFromFile( const char* szFileName ) { FileWrapper File; File.Open(FileOpenAttribs{szFileName}); if (!File) { LOG_ERROR("Failed to open SDK Mesh file ", szFileName); return false; } RefCntAutoPtr pFileData(MakeNewRCObj()(0)); File->Read(pFileData); File.Close(); auto res = CreateFromMemory( reinterpret_cast(pFileData->GetDataPtr()), static_cast(pFileData->GetSize())); return res; } void DXSDKMesh::ComputeBoundingBoxes() { for( Uint32 i = 0; i < m_pMeshHeader->NumMeshes; i++ ) { auto& Mesh = m_pMeshArray[i]; float3 Min(+FLT_MAX, +FLT_MAX, +FLT_MAX); float3 Max(-FLT_MAX, -FLT_MAX, -FLT_MAX); const auto& VertexData = m_pVertexBufferArray[ Mesh.VertexBuffers[0] ]; auto* PosDecl = VertexData.Decl; while (PosDecl->Stream != 0xFF && PosDecl->Usage != DXSDKMESH_VERTEX_SEMANTIC_POSITION) ++PosDecl; VERIFY(PosDecl->Stream != 0xFF, "Position semantic not found in this buffer"); VERIFY(PosDecl->Type == DXSDKMESH_VERTEX_DATA_TYPE_FLOAT3, "Vertex is expected to be a 3-component float vector"); auto IndexType = GetIndexType(i); const auto* Vertices = GetRawVerticesAt(Mesh.VertexBuffers[0]); const auto* Indices = GetRawIndicesAt(Mesh.IndexBuffer); auto Stride = GetVertexStride(Mesh.VertexBuffers[0]); for(Uint32 subsetIdx = 0; subsetIdx < Mesh.NumSubsets; ++subsetIdx) { auto& Subset = m_pSubsetArray[ Mesh.pSubsets[subsetIdx] ]; for(Uint32 v = 0; v < Subset.IndexCount; ++v) { Uint32 Index = IndexType == IT_16BIT ? reinterpret_cast(Indices)[Subset.IndexStart + v] : reinterpret_cast(Indices)[Subset.IndexStart + v]; const float3& Vertex = reinterpret_cast(Vertices[Index * Stride + PosDecl->Offset]); Min = std::min(Min, Vertex); Max = std::max(Max, Vertex); } } Mesh.BoundingBoxCenter = (Max + Min) * 0.5; Mesh.BoundingBoxExtents = (Max - Min); } } bool DXSDKMesh::CreateFromMemory( Uint8* pData, Uint32 DataUint8s ) { m_StaticMeshData.resize(DataUint8s); memcpy(m_StaticMeshData.data(), pData, DataUint8s); // Pointer fixup auto* pStaticMeshData = m_StaticMeshData.data(); m_pMeshHeader = reinterpret_cast (pStaticMeshData); m_pVertexBufferArray = reinterpret_cast(pStaticMeshData + m_pMeshHeader->VertexStreamHeadersOffset); m_pIndexBufferArray = reinterpret_cast (pStaticMeshData + m_pMeshHeader->IndexStreamHeadersOffset); m_pMeshArray = reinterpret_cast (pStaticMeshData + m_pMeshHeader->MeshDataOffset); m_pSubsetArray = reinterpret_cast (pStaticMeshData + m_pMeshHeader->SubsetDataOffset); m_pFrameArray = reinterpret_cast (pStaticMeshData + m_pMeshHeader->FrameDataOffset); m_pMaterialArray = reinterpret_cast (pStaticMeshData + m_pMeshHeader->MaterialDataOffset); for( Uint32 i = 0; i < m_pMeshHeader->NumMaterials; i++ ) { auto& Mat = m_pMaterialArray[i]; Mat.pDiffuseTexture = nullptr; Mat.pNormalTexture = nullptr; Mat.pSpecularTexture = nullptr; Mat.pDiffuseRV = nullptr; Mat.pNormalRV = nullptr; Mat.pSpecularRV = nullptr; } // Setup subsets for( Uint32 i = 0; i < m_pMeshHeader->NumMeshes; i++ ) { m_pMeshArray[i].pSubsets = reinterpret_cast(pStaticMeshData + m_pMeshArray[i].SubsetOffset); m_pMeshArray[i].pFrameInfluences = reinterpret_cast(pStaticMeshData + m_pMeshArray[i].FrameInfluenceOffset); } // error condition if( m_pMeshHeader->Version != DXSDKMESH_FILE_VERSION ) { LOG_ERROR("Unexpected SDK mesh file version"); return false; } // Setup buffer data pointer Uint8* pBufferData = m_StaticMeshData.data() + m_pMeshHeader->HeaderSize + m_pMeshHeader->NonBufferDataSize; // Get the start of the buffer data Uint64 BufferDataStart = m_pMeshHeader->HeaderSize + m_pMeshHeader->NonBufferDataSize; // Create VBs m_ppVertices.resize(m_pMeshHeader->NumVertexBuffers); for( Uint32 i = 0; i < m_pMeshHeader->NumVertexBuffers; i++ ) { m_ppVertices[i] = reinterpret_cast(pBufferData + ( m_pVertexBufferArray[i].DataOffset - BufferDataStart)); } // Create IBs m_ppIndices.resize(m_pMeshHeader->NumIndexBuffers); for( Uint32 i = 0; i < m_pMeshHeader->NumIndexBuffers; i++ ) { m_ppIndices[i] = reinterpret_cast( pBufferData + ( m_pIndexBufferArray[i].DataOffset - BufferDataStart ) ); } ComputeBoundingBoxes(); return true; } static void LoadTexture(IRenderDevice* pDevice, const Char* ResourceDirectory, const Char* Name, bool IsSRGB, ITexture** ppTexture, ITextureView** ppSRV, std::vector& Barriers) { std::string FullPath = ResourceDirectory; if (!FullPath.empty() && FullPath.back() != FileSystem::GetSlashSymbol()) FullPath.push_back(FileSystem::GetSlashSymbol()); FullPath.append(Name); if (FileSystem::FileExists(FullPath.c_str())) { TextureLoadInfo LoadInfo; LoadInfo.IsSRGB = IsSRGB; CreateTextureFromFile(FullPath.c_str(), LoadInfo, pDevice, ppTexture); if (*ppTexture != nullptr) { *ppSRV = (*ppTexture)->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); (*ppSRV)->AddRef(); } else { LOG_ERROR("Failed to load texture ", Name); } Barriers.emplace_back(*ppTexture, RESOURCE_STATE_UNKNOWN, RESOURCE_STATE_SHADER_RESOURCE, true); } } void DXSDKMesh::LoadGPUResources(const Char* ResourceDirectory, IRenderDevice* pDevice, IDeviceContext* pDeviceCtx) { std::vector Barriers; for( Uint32 i = 0; i < m_pMeshHeader->NumMaterials; i++ ) { auto& Mat = m_pMaterialArray[i]; if (Mat.DiffuseTexture[0] != 0) { LoadTexture(pDevice, ResourceDirectory, Mat.DiffuseTexture, true, &Mat.pDiffuseTexture, &Mat.pDiffuseRV, Barriers); } if (Mat.NormalTexture[0] != 0) { LoadTexture(pDevice, ResourceDirectory, Mat.NormalTexture, false, &Mat.pNormalTexture, &Mat.pNormalRV, Barriers); } if (Mat.SpecularTexture[0] != 0) { LoadTexture(pDevice, ResourceDirectory, Mat.SpecularTexture, false, &Mat.pSpecularTexture, &Mat.pSpecularRV, Barriers); } } m_VertexBuffers.resize(m_pMeshHeader->NumVertexBuffers); for( Uint32 i = 0; i < m_pMeshHeader->NumVertexBuffers; i++ ) { const auto& VBArr = m_pVertexBufferArray[i]; std::stringstream ss; ss << "DXSDK Mesh vertex buffer #" << i; std::string VBName = ss.str(); BufferDesc VBDesc; VBDesc.Name = VBName.c_str(); VBDesc.Usage = USAGE_STATIC; VBDesc.uiSizeInBytes = static_cast(VBArr.NumVertices * VBArr.StrideUint8s); VBDesc.BindFlags = BIND_VERTEX_BUFFER; BufferData InitData{GetRawVerticesAt(i), static_cast(VBArr.SizeUint8s)}; pDevice->CreateBuffer(VBDesc, &InitData, &m_VertexBuffers[i]); Barriers.emplace_back(m_VertexBuffers[i], RESOURCE_STATE_UNKNOWN, RESOURCE_STATE_VERTEX_BUFFER, true); } // Create IBs m_IndexBuffers.resize(m_pMeshHeader->NumIndexBuffers); for( Uint32 i = 0; i < m_pMeshHeader->NumIndexBuffers; i++ ) { const auto& IBArr = m_pIndexBufferArray[i]; std::stringstream ss; ss << "DXSDK Mesh index buffer #" << i; std::string IBName = ss.str(); BufferDesc IBDesc; IBDesc.Name = IBName.c_str(); IBDesc.Usage = USAGE_STATIC; IBDesc.uiSizeInBytes = static_cast(IBArr.NumIndices * (IBArr.IndexType == IT_16BIT ? 2 : 4)); IBDesc.BindFlags = BIND_INDEX_BUFFER; BufferData InitData{GetRawIndicesAt(i), static_cast(IBArr.SizeUint8s)}; pDevice->CreateBuffer(IBDesc, &InitData, &m_IndexBuffers[i]); Barriers.emplace_back(m_IndexBuffers[i], RESOURCE_STATE_UNKNOWN, RESOURCE_STATE_INDEX_BUFFER, true); } pDeviceCtx->TransitionResourceStates(static_cast(Barriers.size()), Barriers.data()); } //-------------------------------------------------------------------------------------- DXSDKMesh::~DXSDKMesh() { Destroy(); } //-------------------------------------------------------------------------------------- bool DXSDKMesh::Create( const Char* szFileName ) { return CreateFromFile( szFileName ); } //-------------------------------------------------------------------------------------- bool DXSDKMesh::Create( Uint8* pData, Uint32 DataUint8s ) { return CreateFromMemory( pData, DataUint8s ); } //-------------------------------------------------------------------------------------- void DXSDKMesh::Destroy() { for( Uint32 i = 0; i < m_pMeshHeader->NumMaterials; i++ ) { auto& Mat = m_pMaterialArray[i]; if (Mat.pDiffuseTexture) Mat.pDiffuseTexture->Release(); if (Mat.pNormalTexture) Mat.pNormalTexture->Release(); if (Mat.pSpecularTexture) Mat.pSpecularTexture->Release(); if (Mat.pDiffuseRV) Mat.pDiffuseRV->Release(); if (Mat.pNormalRV) Mat.pNormalRV->Release(); if (Mat.pSpecularRV) Mat.pSpecularRV->Release(); } m_VertexBuffers.clear(); m_IndexBuffers.clear(); m_StaticMeshData.clear(); //delete[] m_pAdjacencyIndexBufferArray; m_pAdjacencyIndexBufferArray = nullptr; //delete[] m_pAnimationData; m_pAnimationData = nullptr; //delete[] m_pBindPoseFrameMatrices; m_pBindPoseFrameMatrices = nullptr; //delete[] m_pTransformedFrameMatrices; m_pTransformedFrameMatrices = nullptr; //delete[] m_pWorldPoseFrameMatrices; m_pWorldPoseFrameMatrices = nullptr; m_ppVertices.clear(); m_ppIndices.clear(); m_pMeshHeader = nullptr; m_pVertexBufferArray = nullptr; m_pIndexBufferArray = nullptr; m_pMeshArray = nullptr; m_pSubsetArray = nullptr; m_pFrameArray = nullptr; m_pMaterialArray = nullptr; //m_pAnimationHeader = nullptr; //m_pAnimationFrameData = nullptr; } //-------------------------------------------------------------------------------------- PRIMITIVE_TOPOLOGY DXSDKMesh::GetPrimitiveType( DXSDKMESH_PRIMITIVE_TYPE PrimType ) { switch( PrimType ) { case PT_TRIANGLE_LIST: return PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; case PT_TRIANGLE_STRIP: return PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; case PT_LINE_LIST: return PRIMITIVE_TOPOLOGY_LINE_LIST; case PT_LINE_STRIP: return PRIMITIVE_TOPOLOGY_UNDEFINED; case PT_POINT_LIST: return PRIMITIVE_TOPOLOGY_POINT_LIST; case PT_TRIANGLE_LIST_ADJ: UNEXPECTED("Unsupported primitive topolgy type"); return PRIMITIVE_TOPOLOGY_UNDEFINED;// PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ; case PT_TRIANGLE_STRIP_ADJ: UNEXPECTED("Unsupported primitive topolgy type"); return PRIMITIVE_TOPOLOGY_UNDEFINED;// PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ; case PT_LINE_LIST_ADJ: UNEXPECTED("Unsupported primitive topolgy type"); return PRIMITIVE_TOPOLOGY_UNDEFINED;// PRIMITIVE_TOPOLOGY_LINELIST_ADJ; case PT_LINE_STRIP_ADJ: UNEXPECTED("Unsupported primitive topolgy type"); return PRIMITIVE_TOPOLOGY_UNDEFINED;// D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ; default: UNEXPECTED("Unknown primitive topolgy type"); return PRIMITIVE_TOPOLOGY_UNDEFINED; } } //-------------------------------------------------------------------------------------- VALUE_TYPE DXSDKMesh::GetIBFormat( Uint32 iMesh )const { switch( m_pIndexBufferArray[ m_pMeshArray[ iMesh ].IndexBuffer ].IndexType ) { case IT_16BIT: return VT_UINT16; case IT_32BIT: return VT_UINT32; default: UNEXPECTED("Unexpected IB format"); return VT_UINT32; } } }