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: 0 175 0.0 %
Date: 2022-03-08 13:03:03 Functions: 0 10 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 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 "lib/ogl.h"
      26             : #include "maths/Vector3D.h"
      27             : #include "maths/Vector4D.h"
      28             : #include "ps/CLogger.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             : 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             :     /// Indices are the same for all models, so share them
      52             :     VertexIndexArray m_IndexArray;
      53             : 
      54             :     IModelDef(const CModelDefPtr& mdef, bool gpuSkinning, bool calculateTangents);
      55             : };
      56             : 
      57             : 
      58           0 : IModelDef::IModelDef(const CModelDefPtr& mdef, bool gpuSkinning, bool calculateTangents)
      59           0 :     : m_IndexArray(false), m_Array(Renderer::Backend::GL::CBuffer::Type::VERTEX, false)
      60             : {
      61           0 :     size_t numVertices = mdef->GetNumVertices();
      62             : 
      63           0 :     m_Position.type = GL_FLOAT;
      64           0 :     m_Position.elems = 3;
      65           0 :     m_Array.AddAttribute(&m_Position);
      66             : 
      67           0 :     m_Normal.type = GL_FLOAT;
      68           0 :     m_Normal.elems = 3;
      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].type = GL_FLOAT;
      75           0 :         m_UVs[i].elems = 2;
      76           0 :         m_Array.AddAttribute(&m_UVs[i]);
      77             :     }
      78             : 
      79           0 :     if (gpuSkinning)
      80             :     {
      81             :         // We can't use a lot of bones because it costs uniform memory. Recommended
      82             :         // number of bones per model is 32.
      83             :         // Add 1 to NumBones because of the special 'root' bone.
      84           0 :         if (mdef->GetNumBones() + 1 > 64)
      85           0 :             LOGERROR("Model '%s' has too many bones %zu/64", mdef->GetName().string8().c_str(), mdef->GetNumBones() + 1);
      86           0 :         ENSURE(mdef->GetNumBones() + 1 <= 64);
      87             : 
      88           0 :         m_BlendJoints.type = GL_UNSIGNED_BYTE;
      89           0 :         m_BlendJoints.elems = 4;
      90           0 :         m_Array.AddAttribute(&m_BlendJoints);
      91             : 
      92           0 :         m_BlendWeights.type = GL_UNSIGNED_BYTE;
      93           0 :         m_BlendWeights.elems = 4;
      94           0 :         m_Array.AddAttribute(&m_BlendWeights);
      95             :     }
      96             : 
      97           0 :     if (calculateTangents)
      98             :     {
      99             :         // Generate tangents for the geometry:-
     100             : 
     101           0 :         m_Tangent.type = GL_FLOAT;
     102           0 :         m_Tangent.elems = 4;
     103           0 :         m_Array.AddAttribute(&m_Tangent);
     104             : 
     105             :         // floats per vertex; position + normal + tangent + UV*sets [+ GPUskinning]
     106           0 :         int numVertexAttrs = 3 + 3 + 4 + 2 * mdef->GetNumUVsPerVertex();
     107           0 :         if (gpuSkinning)
     108             :         {
     109           0 :             numVertexAttrs += 8;
     110             :         }
     111             : 
     112             :         // the tangent generation can increase the number of vertices temporarily
     113             :         // so reserve a bit more memory to avoid reallocations in GenTangents (in most cases)
     114           0 :         std::vector<float> newVertices;
     115           0 :         newVertices.reserve(numVertexAttrs * numVertices * 2);
     116             : 
     117             :         // Generate the tangents
     118           0 :         ModelRenderer::GenTangents(mdef, newVertices, gpuSkinning);
     119             : 
     120             :         // how many vertices do we have after generating tangents?
     121           0 :         int newNumVert = newVertices.size() / numVertexAttrs;
     122             : 
     123           0 :         std::vector<int> remapTable(newNumVert);
     124           0 :         std::vector<float> vertexDataOut(newNumVert * numVertexAttrs);
     125             : 
     126             :         // re-weld the mesh to remove duplicated vertices
     127           0 :         int numVertices2 = WeldMesh(&remapTable[0], &vertexDataOut[0],
     128           0 :                     &newVertices[0], newNumVert, numVertexAttrs);
     129             : 
     130             :         // Copy the model data to graphics memory:-
     131             : 
     132           0 :         m_Array.SetNumberOfVertices(numVertices2);
     133           0 :         m_Array.Layout();
     134             : 
     135           0 :         VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
     136           0 :         VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>();
     137           0 :         VertexArrayIterator<CVector4D> Tangent = m_Tangent.GetIterator<CVector4D>();
     138             : 
     139           0 :         VertexArrayIterator<u8[4]> BlendJoints;
     140           0 :         VertexArrayIterator<u8[4]> BlendWeights;
     141           0 :         if (gpuSkinning)
     142             :         {
     143           0 :             BlendJoints = m_BlendJoints.GetIterator<u8[4]>();
     144           0 :             BlendWeights = m_BlendWeights.GetIterator<u8[4]>();
     145             :         }
     146             : 
     147             :         // copy everything into the vertex array
     148           0 :         for (int i = 0; i < numVertices2; i++)
     149             :         {
     150           0 :             int q = numVertexAttrs * i;
     151             : 
     152           0 :             Position[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]);
     153           0 :             q += 3;
     154             : 
     155           0 :             Normal[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]);
     156           0 :             q += 3;
     157             : 
     158           0 :             Tangent[i] = CVector4D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2],
     159           0 :                     vertexDataOut[q + 3]);
     160           0 :             q += 4;
     161             : 
     162           0 :             if (gpuSkinning)
     163             :             {
     164           0 :                 for (size_t j = 0; j < 4; ++j)
     165             :                 {
     166           0 :                     BlendJoints[i][j] = (u8)vertexDataOut[q + 0 + 2 * j];
     167           0 :                     BlendWeights[i][j] = (u8)vertexDataOut[q + 1 + 2 * j];
     168             :                 }
     169           0 :                 q += 8;
     170             :             }
     171             : 
     172           0 :             for (size_t j = 0; j < mdef->GetNumUVsPerVertex(); j++)
     173             :             {
     174           0 :                 VertexArrayIterator<float[2]> UVit = m_UVs[j].GetIterator<float[2]>();
     175           0 :                 UVit[i][0] = vertexDataOut[q + 0 + 2 * j];
     176           0 :                 UVit[i][1] = vertexDataOut[q + 1 + 2 * j];
     177             :             }
     178             :         }
     179             : 
     180             :         // upload vertex data
     181           0 :         m_Array.Upload();
     182           0 :         m_Array.FreeBackingStore();
     183             : 
     184           0 :         m_IndexArray.SetNumberOfVertices(mdef->GetNumFaces() * 3);
     185           0 :         m_IndexArray.Layout();
     186             : 
     187           0 :         VertexArrayIterator<u16> Indices = m_IndexArray.GetIterator();
     188             : 
     189             :         size_t idxidx = 0;
     190             : 
     191             :         // reindex geometry and upload index
     192           0 :         for (size_t j = 0; j < mdef->GetNumFaces(); ++j)
     193             :         {
     194           0 :             Indices[idxidx++] = remapTable[j * 3 + 0];
     195           0 :             Indices[idxidx++] = remapTable[j * 3 + 1];
     196           0 :             Indices[idxidx++] = remapTable[j * 3 + 2];
     197             :         }
     198             : 
     199           0 :         m_IndexArray.Upload();
     200           0 :         m_IndexArray.FreeBackingStore();
     201             :     }
     202             :     else
     203             :     {
     204             :         // Upload model without calculating tangents:-
     205             : 
     206           0 :         m_Array.SetNumberOfVertices(numVertices);
     207           0 :         m_Array.Layout();
     208             : 
     209           0 :         VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
     210           0 :         VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>();
     211             : 
     212           0 :         ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal);
     213             : 
     214           0 :         for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
     215             :         {
     216           0 :             VertexArrayIterator<float[2]> UVit = m_UVs[i].GetIterator<float[2]>();
     217           0 :             ModelRenderer::BuildUV(mdef, UVit, i);
     218             :         }
     219             : 
     220           0 :         if (gpuSkinning)
     221             :         {
     222           0 :             VertexArrayIterator<u8[4]> BlendJoints = m_BlendJoints.GetIterator<u8[4]>();
     223           0 :             VertexArrayIterator<u8[4]> BlendWeights = m_BlendWeights.GetIterator<u8[4]>();
     224           0 :             for (size_t i = 0; i < numVertices; ++i)
     225             :             {
     226           0 :                 const SModelVertex& vtx = mdef->GetVertices()[i];
     227           0 :                 for (size_t j = 0; j < 4; ++j)
     228             :                 {
     229           0 :                     BlendJoints[i][j] = vtx.m_Blend.m_Bone[j];
     230           0 :                     BlendWeights[i][j] = (u8)(255.f * vtx.m_Blend.m_Weight[j]);
     231             :                 }
     232             :             }
     233             :         }
     234             : 
     235           0 :         m_Array.Upload();
     236           0 :         m_Array.FreeBackingStore();
     237             : 
     238           0 :         m_IndexArray.SetNumberOfVertices(mdef->GetNumFaces()*3);
     239           0 :         m_IndexArray.Layout();
     240           0 :         ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator());
     241           0 :         m_IndexArray.Upload();
     242           0 :         m_IndexArray.FreeBackingStore();
     243             :     }
     244           0 : }
     245             : 
     246             : 
     247             : struct InstancingModelRendererInternals
     248             : {
     249             :     bool gpuSkinning;
     250             : 
     251             :     bool calculateTangents;
     252             : 
     253             :     /// Previously prepared modeldef
     254             :     IModelDef* imodeldef;
     255             : 
     256             :     /// Index base for imodeldef
     257             :     u8* imodeldefIndexBase;
     258             : };
     259             : 
     260             : 
     261             : // Construction and Destruction
     262           0 : InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning, bool calculateTangents)
     263             : {
     264           0 :     m = new InstancingModelRendererInternals;
     265           0 :     m->gpuSkinning = gpuSkinning;
     266           0 :     m->calculateTangents = calculateTangents;
     267           0 :     m->imodeldef = 0;
     268           0 : }
     269             : 
     270           0 : InstancingModelRenderer::~InstancingModelRenderer()
     271             : {
     272           0 :     delete m;
     273           0 : }
     274           0 : 
     275             : 
     276           0 : // Build modeldef data if necessary - we have no per-CModel data
     277           0 : CModelRData* InstancingModelRenderer::CreateModelData(const void* key, CModel* model)
     278           0 : {
     279             :     CModelDefPtr mdef = model->GetModelDef();
     280           0 :     IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m);
     281           0 : 
     282             :     if (m->gpuSkinning)
     283             :         ENSURE(model->IsSkinned());
     284             :     else
     285           0 :         ENSURE(!model->IsSkinned());
     286             : 
     287           0 :     if (!imodeldef)
     288           0 :     {
     289             :         imodeldef = new IModelDef(mdef, m->gpuSkinning, m->calculateTangents);
     290           0 :         mdef->SetRenderData(m, imodeldef);
     291           0 :     }
     292             : 
     293           0 :     return new CModelRData(key);
     294             : }
     295           0 : 
     296             : 
     297           0 : void InstancingModelRenderer::UpdateModelData(CModel* UNUSED(model), CModelRData* UNUSED(data), int UNUSED(updateflags))
     298           0 : {
     299             :     // We have no per-CModel data
     300             : }
     301           0 : 
     302             : 
     303             : // Setup one rendering pass.
     304             : void InstancingModelRenderer::BeginPass(int streamflags)
     305           0 : {
     306             :     ENSURE(streamflags == (streamflags & (STREAM_POS|STREAM_NORMAL|STREAM_UV0|STREAM_UV1)));
     307             : }
     308           0 : 
     309             : // Cleanup rendering pass.
     310             : void InstancingModelRenderer::EndPass(
     311             :     Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
     312           0 :     int UNUSED(streamflags))
     313             : {
     314           0 :     CVertexBuffer::Unbind(deviceCommandContext);
     315           0 : }
     316             : 
     317             : 
     318           0 : // Prepare UV coordinates for this modeldef
     319             : void InstancingModelRenderer::PrepareModelDef(
     320             :     Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
     321             :     const CShaderProgramPtr& shader, int streamflags, const CModelDef& def)
     322           0 : {
     323           0 :     m->imodeldef = (IModelDef*)def.GetRenderData(m);
     324             : 
     325             :     ENSURE(m->imodeldef);
     326             : 
     327           0 :     u8* base = m->imodeldef->m_Array.Bind(deviceCommandContext);
     328             :     GLsizei stride = (GLsizei)m->imodeldef->m_Array.GetStride();
     329             : 
     330             :     m->imodeldef->m_IndexArray.UploadIfNeeded(deviceCommandContext);
     331           0 :     deviceCommandContext->SetIndexBuffer(m->imodeldef->m_IndexArray.GetBuffer());
     332             : 
     333           0 :     if (streamflags & STREAM_POS)
     334             :     {
     335           0 :         shader->VertexPointer(
     336           0 :             Renderer::Backend::Format::R32G32B32_SFLOAT, stride,
     337             :             base + m->imodeldef->m_Position.offset);
     338           0 :     }
     339           0 : 
     340             :     if (streamflags & STREAM_NORMAL)
     341           0 :     {
     342             :         shader->NormalPointer(
     343           0 :             Renderer::Backend::Format::R32G32B32_SFLOAT, stride,
     344             :             base + m->imodeldef->m_Normal.offset);
     345           0 :     }
     346             : 
     347             :     if (m->calculateTangents)
     348           0 :     {
     349             :         shader->VertexAttribPointer(
     350           0 :             str_a_tangent, Renderer::Backend::Format::R32G32B32A32_SFLOAT,
     351             :             GL_FALSE, stride, base + m->imodeldef->m_Tangent.offset);
     352           0 :     }
     353             : 
     354             :     // The last UV set is STREAM_UV3
     355           0 :     for (size_t uv = 0; uv < 4; ++uv)
     356             :         if (streamflags & (STREAM_UV0 << uv))
     357           0 :         {
     358             :             if (def.GetNumUVsPerVertex() >= uv + 1)
     359           0 :             {
     360             :                 shader->TexCoordPointer(
     361             :                     GL_TEXTURE0 + uv, Renderer::Backend::Format::R32G32_SFLOAT, stride,
     362             :                     base + m->imodeldef->m_UVs[uv].offset);
     363           0 :             }
     364           0 :             else
     365             :                 ONCE(LOGERROR("Model '%s' has no UV%d set.", def.GetName().string8().c_str(), uv));
     366           0 :         }
     367             : 
     368           0 :     // GPU skinning requires extra attributes to compute positions/normals
     369             :     if (m->gpuSkinning)
     370           0 :     {
     371             :         shader->VertexAttribPointer(
     372             :             str_a_skinJoints, Renderer::Backend::Format::R8G8B8A8_UINT, GL_FALSE,
     373           0 :             stride, base + m->imodeldef->m_BlendJoints.offset);
     374             :         shader->VertexAttribPointer(
     375             :             str_a_skinWeights, Renderer::Backend::Format::R8G8B8A8_UNORM, GL_TRUE,
     376             :             stride, base + m->imodeldef->m_BlendWeights.offset);
     377           0 :     }
     378             : 
     379           0 :     shader->AssertPointersBound();
     380             : }
     381           0 : 
     382           0 : 
     383             : // Render one model
     384           0 : void InstancingModelRenderer::RenderModel(
     385             :     Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
     386             :     const CShaderProgramPtr& shader, int UNUSED(streamflags), CModel* model, CModelRData* UNUSED(data))
     387           0 : {
     388           0 :     const CModelDefPtr& mdldef = model->GetModelDef();
     389             : 
     390             :     if (m->gpuSkinning)
     391             :     {
     392           0 :         // Bind matrices for current animation state.
     393             :         // Add 1 to NumBones because of the special 'root' bone.
     394             :         // HACK: NVIDIA drivers return uniform name with "[0]", Intel Windows drivers without;
     395             :         // try uploading both names since one of them should work, and this is easier than
     396           0 :         // canonicalising the uniform names in CShaderProgramGLSL
     397             :         shader->Uniform(str_skinBlendMatrices_0, mdldef->GetNumBones() + 1, model->GetAnimatedBoneMatrices());
     398           0 :         shader->Uniform(str_skinBlendMatrices, mdldef->GetNumBones() + 1, model->GetAnimatedBoneMatrices());
     399             :     }
     400             : 
     401             :     // Render the lot.
     402             :     const size_t numberOfFaces = mdldef->GetNumFaces();
     403             : 
     404             :     deviceCommandContext->DrawIndexedInRange(
     405           0 :         m->imodeldef->m_IndexArray.GetOffset(), numberOfFaces * 3, 0, m->imodeldef->m_Array.GetNumberOfVertices() - 1);
     406           0 : 
     407             :     // Bump stats.
     408             :     g_Renderer.m_Stats.m_DrawCalls++;
     409             :     g_Renderer.m_Stats.m_ModelTris += numberOfFaces;
     410           0 : 
     411             : }

Generated by: LCOV version 1.13