LCOV - code coverage report
Current view: top level - source/renderer - InstancingModelRenderer.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 163 0.6 %
Date: 2023-01-19 00:18:29 Functions: 2 13 15.4 %

          Line data    Source code
       1             : /* Copyright (C) 2023 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : #include "renderer/InstancingModelRenderer.h"
      20             : 
      21             : #include "graphics/Color.h"
      22             : #include "graphics/LightEnv.h"
      23             : #include "graphics/Model.h"
      24             : #include "graphics/ModelDef.h"
      25             : #include "maths/Vector3D.h"
      26             : #include "maths/Vector4D.h"
      27             : #include "ps/CLogger.h"
      28             : #include "ps/containers/StaticVector.h"
      29             : #include "ps/CStrInternStatic.h"
      30             : #include "renderer/Renderer.h"
      31             : #include "renderer/RenderModifiers.h"
      32             : #include "renderer/VertexArray.h"
      33             : #include "third_party/mikktspace/weldmesh.h"
      34             : 
      35             : 
      36           0 : struct IModelDef : public CModelDefRPrivate
      37             : {
      38             :     /// Static per-CModel vertex array
      39             :     VertexArray m_Array;
      40             : 
      41             :     /// Position and normals are static
      42             :     VertexArray::Attribute m_Position;
      43             :     VertexArray::Attribute m_Normal;
      44             :     VertexArray::Attribute m_Tangent;
      45             :     VertexArray::Attribute m_BlendJoints; // valid iff gpuSkinning == true
      46             :     VertexArray::Attribute m_BlendWeights; // valid iff gpuSkinning == true
      47             : 
      48             :     /// The number of UVs is determined by the model
      49             :     std::vector<VertexArray::Attribute> m_UVs;
      50             : 
      51             :     Renderer::Backend::IVertexInputLayout* m_VertexInputLayout = nullptr;
      52             : 
      53             :     /// Indices are the same for all models, so share them
      54             :     VertexIndexArray m_IndexArray;
      55             : 
      56             :     IModelDef(const CModelDefPtr& mdef, bool gpuSkinning, bool calculateTangents);
      57             : };
      58             : 
      59             : 
      60           0 : IModelDef::IModelDef(const CModelDefPtr& mdef, bool gpuSkinning, bool calculateTangents)
      61           0 :     : m_IndexArray(false), m_Array(Renderer::Backend::IBuffer::Type::VERTEX, false)
      62             : {
      63           0 :     size_t numVertices = mdef->GetNumVertices();
      64             : 
      65           0 :     m_Position.format = Renderer::Backend::Format::R32G32B32_SFLOAT;
      66           0 :     m_Array.AddAttribute(&m_Position);
      67             : 
      68           0 :     m_Normal.format = Renderer::Backend::Format::R32G32B32_SFLOAT;
      69           0 :     m_Array.AddAttribute(&m_Normal);
      70             : 
      71           0 :     m_UVs.resize(mdef->GetNumUVsPerVertex());
      72           0 :     for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
      73             :     {
      74           0 :         m_UVs[i].format = Renderer::Backend::Format::R32G32_SFLOAT;
      75           0 :         m_Array.AddAttribute(&m_UVs[i]);
      76             :     }
      77             : 
      78           0 :     if (gpuSkinning)
      79             :     {
      80             :         // We can't use a lot of bones because it costs uniform memory. Recommended
      81             :         // number of bones per model is 32.
      82             :         // Add 1 to NumBones because of the special 'root' bone.
      83           0 :         if (mdef->GetNumBones() + 1 > 64)
      84           0 :             LOGERROR("Model '%s' has too many bones %zu/64", mdef->GetName().string8().c_str(), mdef->GetNumBones() + 1);
      85           0 :         ENSURE(mdef->GetNumBones() + 1 <= 64);
      86             : 
      87           0 :         m_BlendJoints.format = Renderer::Backend::Format::R8G8B8A8_UINT;
      88           0 :         m_Array.AddAttribute(&m_BlendJoints);
      89             : 
      90           0 :         m_BlendWeights.format = Renderer::Backend::Format::R8G8B8A8_UNORM;
      91           0 :         m_Array.AddAttribute(&m_BlendWeights);
      92             :     }
      93             : 
      94           0 :     if (calculateTangents)
      95             :     {
      96             :         // Generate tangents for the geometry:-
      97             : 
      98           0 :         m_Tangent.format = Renderer::Backend::Format::R32G32B32A32_SFLOAT;
      99           0 :         m_Array.AddAttribute(&m_Tangent);
     100             : 
     101             :         // floats per vertex; position + normal + tangent + UV*sets [+ GPUskinning]
     102           0 :         int numVertexAttrs = 3 + 3 + 4 + 2 * mdef->GetNumUVsPerVertex();
     103           0 :         if (gpuSkinning)
     104             :         {
     105           0 :             numVertexAttrs += 8;
     106             :         }
     107             : 
     108             :         // the tangent generation can increase the number of vertices temporarily
     109             :         // so reserve a bit more memory to avoid reallocations in GenTangents (in most cases)
     110           0 :         std::vector<float> newVertices;
     111           0 :         newVertices.reserve(numVertexAttrs * numVertices * 2);
     112             : 
     113             :         // Generate the tangents
     114           0 :         ModelRenderer::GenTangents(mdef, newVertices, gpuSkinning);
     115             : 
     116             :         // how many vertices do we have after generating tangents?
     117           0 :         int newNumVert = newVertices.size() / numVertexAttrs;
     118             : 
     119           0 :         std::vector<int> remapTable(newNumVert);
     120           0 :         std::vector<float> vertexDataOut(newNumVert * numVertexAttrs);
     121             : 
     122             :         // re-weld the mesh to remove duplicated vertices
     123           0 :         int numVertices2 = WeldMesh(&remapTable[0], &vertexDataOut[0],
     124           0 :                     &newVertices[0], newNumVert, numVertexAttrs);
     125             : 
     126             :         // Copy the model data to graphics memory:-
     127             : 
     128           0 :         m_Array.SetNumberOfVertices(numVertices2);
     129           0 :         m_Array.Layout();
     130             : 
     131           0 :         VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
     132           0 :         VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>();
     133           0 :         VertexArrayIterator<CVector4D> Tangent = m_Tangent.GetIterator<CVector4D>();
     134             : 
     135           0 :         VertexArrayIterator<u8[4]> BlendJoints;
     136           0 :         VertexArrayIterator<u8[4]> BlendWeights;
     137           0 :         if (gpuSkinning)
     138             :         {
     139           0 :             BlendJoints = m_BlendJoints.GetIterator<u8[4]>();
     140           0 :             BlendWeights = m_BlendWeights.GetIterator<u8[4]>();
     141             :         }
     142             : 
     143             :         // copy everything into the vertex array
     144           0 :         for (int i = 0; i < numVertices2; i++)
     145             :         {
     146           0 :             int q = numVertexAttrs * i;
     147             : 
     148           0 :             Position[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]);
     149           0 :             q += 3;
     150             : 
     151           0 :             Normal[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]);
     152           0 :             q += 3;
     153             : 
     154           0 :             Tangent[i] = CVector4D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2],
     155           0 :                     vertexDataOut[q + 3]);
     156           0 :             q += 4;
     157             : 
     158           0 :             if (gpuSkinning)
     159             :             {
     160           0 :                 for (size_t j = 0; j < 4; ++j)
     161             :                 {
     162           0 :                     BlendJoints[i][j] = (u8)vertexDataOut[q + 0 + 2 * j];
     163           0 :                     BlendWeights[i][j] = (u8)vertexDataOut[q + 1 + 2 * j];
     164             :                 }
     165           0 :                 q += 8;
     166             :             }
     167             : 
     168           0 :             for (size_t j = 0; j < mdef->GetNumUVsPerVertex(); j++)
     169             :             {
     170           0 :                 VertexArrayIterator<float[2]> UVit = m_UVs[j].GetIterator<float[2]>();
     171           0 :                 UVit[i][0] = vertexDataOut[q + 0 + 2 * j];
     172           0 :                 UVit[i][1] = vertexDataOut[q + 1 + 2 * j];
     173             :             }
     174             :         }
     175             : 
     176             :         // upload vertex data
     177           0 :         m_Array.Upload();
     178           0 :         m_Array.FreeBackingStore();
     179             : 
     180           0 :         m_IndexArray.SetNumberOfVertices(mdef->GetNumFaces() * 3);
     181           0 :         m_IndexArray.Layout();
     182             : 
     183           0 :         VertexArrayIterator<u16> Indices = m_IndexArray.GetIterator();
     184             : 
     185           0 :         size_t idxidx = 0;
     186             : 
     187             :         // reindex geometry and upload index
     188           0 :         for (size_t j = 0; j < mdef->GetNumFaces(); ++j)
     189             :         {
     190           0 :             Indices[idxidx++] = remapTable[j * 3 + 0];
     191           0 :             Indices[idxidx++] = remapTable[j * 3 + 1];
     192           0 :             Indices[idxidx++] = remapTable[j * 3 + 2];
     193             :         }
     194             : 
     195           0 :         m_IndexArray.Upload();
     196           0 :         m_IndexArray.FreeBackingStore();
     197             :     }
     198             :     else
     199             :     {
     200             :         // Upload model without calculating tangents:-
     201             : 
     202           0 :         m_Array.SetNumberOfVertices(numVertices);
     203           0 :         m_Array.Layout();
     204             : 
     205           0 :         VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
     206           0 :         VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>();
     207             : 
     208           0 :         ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal);
     209             : 
     210           0 :         for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
     211             :         {
     212           0 :             VertexArrayIterator<float[2]> UVit = m_UVs[i].GetIterator<float[2]>();
     213           0 :             ModelRenderer::BuildUV(mdef, UVit, i);
     214             :         }
     215             : 
     216           0 :         if (gpuSkinning)
     217             :         {
     218           0 :             VertexArrayIterator<u8[4]> BlendJoints = m_BlendJoints.GetIterator<u8[4]>();
     219           0 :             VertexArrayIterator<u8[4]> BlendWeights = m_BlendWeights.GetIterator<u8[4]>();
     220           0 :             for (size_t i = 0; i < numVertices; ++i)
     221             :             {
     222           0 :                 const SModelVertex& vtx = mdef->GetVertices()[i];
     223           0 :                 for (size_t j = 0; j < 4; ++j)
     224             :                 {
     225           0 :                     BlendJoints[i][j] = vtx.m_Blend.m_Bone[j];
     226           0 :                     BlendWeights[i][j] = (u8)(255.f * vtx.m_Blend.m_Weight[j]);
     227             :                 }
     228             :             }
     229             :         }
     230             : 
     231           0 :         m_Array.Upload();
     232           0 :         m_Array.FreeBackingStore();
     233             : 
     234           0 :         m_IndexArray.SetNumberOfVertices(mdef->GetNumFaces()*3);
     235           0 :         m_IndexArray.Layout();
     236           0 :         ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator());
     237           0 :         m_IndexArray.Upload();
     238           0 :         m_IndexArray.FreeBackingStore();
     239             :     }
     240             : 
     241           0 :     const uint32_t stride = m_Array.GetStride();
     242           0 :     constexpr size_t MAX_UV = 2;
     243             : 
     244             :     PS::StaticVector<Renderer::Backend::SVertexAttributeFormat, 5 + MAX_UV> attributes{
     245             :         {Renderer::Backend::VertexAttributeStream::POSITION,
     246           0 :             m_Position.format, m_Position.offset, stride,
     247             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     248             :         {Renderer::Backend::VertexAttributeStream::NORMAL,
     249           0 :             m_Normal.format, m_Normal.offset, stride,
     250             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     251           0 :     };
     252             : 
     253           0 :     for (size_t uv = 0; uv < std::min(MAX_UV, mdef->GetNumUVsPerVertex()); ++uv)
     254             :     {
     255           0 :         const Renderer::Backend::VertexAttributeStream stream =
     256           0 :             static_cast<Renderer::Backend::VertexAttributeStream>(
     257             :                 static_cast<int>(Renderer::Backend::VertexAttributeStream::UV0) + uv);
     258           0 :         attributes.push_back({
     259           0 :             stream, m_UVs[uv].format, m_UVs[uv].offset, stride,
     260             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0});
     261             :     }
     262             : 
     263             :     // GPU skinning requires extra attributes to compute positions/normals.
     264           0 :     if (gpuSkinning)
     265             :     {
     266           0 :         attributes.push_back({
     267             :             Renderer::Backend::VertexAttributeStream::UV2,
     268           0 :             m_BlendJoints.format, m_BlendJoints.offset, stride,
     269             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0});
     270           0 :         attributes.push_back({
     271             :             Renderer::Backend::VertexAttributeStream::UV3,
     272           0 :             m_BlendWeights.format, m_BlendWeights.offset, stride,
     273             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0});
     274             :     }
     275             : 
     276           0 :     if (calculateTangents)
     277             :     {
     278           0 :         attributes.push_back({
     279             :             Renderer::Backend::VertexAttributeStream::UV4,
     280           0 :             m_Tangent.format, m_Tangent.offset, stride,
     281             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0});
     282             :     }
     283             : 
     284           0 :     m_VertexInputLayout = g_Renderer.GetVertexInputLayout({attributes.begin(), attributes.end()});
     285           0 : }
     286             : 
     287             : struct InstancingModelRendererInternals
     288             : {
     289             :     bool gpuSkinning;
     290             : 
     291             :     bool calculateTangents;
     292             : 
     293             :     /// Previously prepared modeldef
     294             :     IModelDef* imodeldef;
     295             : 
     296             :     /// Index base for imodeldef
     297             :     u8* imodeldefIndexBase;
     298             : };
     299             : 
     300             : 
     301             : // Construction and Destruction
     302           0 : InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning, bool calculateTangents)
     303             : {
     304           0 :     m = new InstancingModelRendererInternals;
     305           0 :     m->gpuSkinning = gpuSkinning;
     306           0 :     m->calculateTangents = calculateTangents;
     307           0 :     m->imodeldef = 0;
     308           0 : }
     309             : 
     310           0 : InstancingModelRenderer::~InstancingModelRenderer()
     311             : {
     312           0 :     delete m;
     313           0 : }
     314             : 
     315             : 
     316             : // Build modeldef data if necessary - we have no per-CModel data
     317           0 : CModelRData* InstancingModelRenderer::CreateModelData(const void* key, CModel* model)
     318             : {
     319           0 :     CModelDefPtr mdef = model->GetModelDef();
     320           0 :     IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m);
     321             : 
     322           0 :     if (m->gpuSkinning)
     323           0 :         ENSURE(model->IsSkinned());
     324             :     else
     325           0 :         ENSURE(!model->IsSkinned());
     326             : 
     327           0 :     if (!imodeldef)
     328             :     {
     329           0 :         imodeldef = new IModelDef(mdef, m->gpuSkinning, m->calculateTangents);
     330           0 :         mdef->SetRenderData(m, imodeldef);
     331             :     }
     332             : 
     333           0 :     return new CModelRData(key);
     334             : }
     335             : 
     336             : 
     337           0 : void InstancingModelRenderer::UpdateModelData(CModel* UNUSED(model), CModelRData* UNUSED(data), int UNUSED(updateflags))
     338             : {
     339             :     // We have no per-CModel data
     340           0 : }
     341             : 
     342           0 : void InstancingModelRenderer::UploadModelData(
     343             :     Renderer::Backend::IDeviceCommandContext* UNUSED(deviceCommandContext),
     344             :     CModel* UNUSED(model), CModelRData* UNUSED(data))
     345             : {
     346             :     // Data uploaded once during creation as we don't update it dynamically.
     347           0 : }
     348             : 
     349             : // Prepare UV coordinates for this modeldef
     350           0 : void InstancingModelRenderer::PrepareModelDef(
     351             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     352             :     const CModelDef& def)
     353             : {
     354           0 :     m->imodeldef = (IModelDef*)def.GetRenderData(m);
     355           0 :     ENSURE(m->imodeldef);
     356             : 
     357           0 :     deviceCommandContext->SetVertexInputLayout(m->imodeldef->m_VertexInputLayout);
     358             : 
     359           0 :     deviceCommandContext->SetIndexBuffer(m->imodeldef->m_IndexArray.GetBuffer());
     360             : 
     361           0 :     const uint32_t stride = m->imodeldef->m_Array.GetStride();
     362           0 :     const uint32_t firstVertexOffset = m->imodeldef->m_Array.GetOffset() * stride;
     363             : 
     364           0 :     deviceCommandContext->SetVertexBuffer(
     365           0 :         0, m->imodeldef->m_Array.GetBuffer(), firstVertexOffset);
     366           0 : }
     367             : 
     368             : 
     369             : // Render one model
     370           0 : void InstancingModelRenderer::RenderModel(
     371             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     372             :     Renderer::Backend::IShaderProgram* shader, CModel* model, CModelRData* UNUSED(data))
     373             : {
     374           0 :     const CModelDefPtr& mdldef = model->GetModelDef();
     375             : 
     376           0 :     if (m->gpuSkinning)
     377             :     {
     378             :         // Bind matrices for current animation state.
     379             :         // Add 1 to NumBones because of the special 'root' bone.
     380           0 :         deviceCommandContext->SetUniform(
     381           0 :             shader->GetBindingSlot(str_skinBlendMatrices),
     382             :             PS::span<const float>(
     383           0 :                 model->GetAnimatedBoneMatrices()[0]._data,
     384           0 :                 model->GetAnimatedBoneMatrices()[0].AsFloatArray().size() * (mdldef->GetNumBones() + 1)));
     385             :     }
     386             : 
     387             :     // Render the lot.
     388           0 :     const size_t numberOfFaces = mdldef->GetNumFaces();
     389             : 
     390           0 :     deviceCommandContext->DrawIndexedInRange(
     391           0 :         m->imodeldef->m_IndexArray.GetOffset(), numberOfFaces * 3, 0, m->imodeldef->m_Array.GetNumberOfVertices() - 1);
     392             : 
     393             :     // Bump stats.
     394           0 :     g_Renderer.m_Stats.m_DrawCalls++;
     395           0 :     g_Renderer.m_Stats.m_ModelTris += numberOfFaces;
     396           3 : }

Generated by: LCOV version 1.13