1 /* Copyright (C) 2022 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
19 #include "renderer/InstancingModelRenderer.h"
21 #include "graphics/Color.h"
22 #include "graphics/LightEnv.h"
23 #include "graphics/Model.h"
24 #include "graphics/ModelDef.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"
36 struct IModelDef
: public CModelDefRPrivate
38 /// Static per-CModel vertex array
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
48 /// The number of UVs is determined by the model
49 std::vector
<VertexArray::Attribute
> m_UVs
;
51 /// Indices are the same for all models, so share them
52 VertexIndexArray m_IndexArray
;
54 IModelDef(const CModelDefPtr
& mdef
, bool gpuSkinning
, bool calculateTangents
);
58 IModelDef::IModelDef(const CModelDefPtr
& mdef
, bool gpuSkinning
, bool calculateTangents
)
59 : m_IndexArray(false), m_Array(Renderer::Backend::GL::CBuffer::Type::VERTEX
, false)
61 size_t numVertices
= mdef
->GetNumVertices();
63 m_Position
.format
= Renderer::Backend::Format::R32G32B32_SFLOAT
;
64 m_Array
.AddAttribute(&m_Position
);
66 m_Normal
.format
= Renderer::Backend::Format::R32G32B32_SFLOAT
;
67 m_Array
.AddAttribute(&m_Normal
);
69 m_UVs
.resize(mdef
->GetNumUVsPerVertex());
70 for (size_t i
= 0; i
< mdef
->GetNumUVsPerVertex(); i
++)
72 m_UVs
[i
].format
= Renderer::Backend::Format::R32G32_SFLOAT
;
73 m_Array
.AddAttribute(&m_UVs
[i
]);
78 // We can't use a lot of bones because it costs uniform memory. Recommended
79 // number of bones per model is 32.
80 // Add 1 to NumBones because of the special 'root' bone.
81 if (mdef
->GetNumBones() + 1 > 64)
82 LOGERROR("Model '%s' has too many bones %zu/64", mdef
->GetName().string8().c_str(), mdef
->GetNumBones() + 1);
83 ENSURE(mdef
->GetNumBones() + 1 <= 64);
85 m_BlendJoints
.format
= Renderer::Backend::Format::R8G8B8A8_UINT
;
86 m_Array
.AddAttribute(&m_BlendJoints
);
88 m_BlendWeights
.format
= Renderer::Backend::Format::R8G8B8A8_UNORM
;
89 m_Array
.AddAttribute(&m_BlendWeights
);
92 if (calculateTangents
)
94 // Generate tangents for the geometry:-
96 m_Tangent
.format
= Renderer::Backend::Format::R32G32B32A32_SFLOAT
;
97 m_Array
.AddAttribute(&m_Tangent
);
99 // floats per vertex; position + normal + tangent + UV*sets [+ GPUskinning]
100 int numVertexAttrs
= 3 + 3 + 4 + 2 * mdef
->GetNumUVsPerVertex();
106 // the tangent generation can increase the number of vertices temporarily
107 // so reserve a bit more memory to avoid reallocations in GenTangents (in most cases)
108 std::vector
<float> newVertices
;
109 newVertices
.reserve(numVertexAttrs
* numVertices
* 2);
111 // Generate the tangents
112 ModelRenderer::GenTangents(mdef
, newVertices
, gpuSkinning
);
114 // how many vertices do we have after generating tangents?
115 int newNumVert
= newVertices
.size() / numVertexAttrs
;
117 std::vector
<int> remapTable(newNumVert
);
118 std::vector
<float> vertexDataOut(newNumVert
* numVertexAttrs
);
120 // re-weld the mesh to remove duplicated vertices
121 int numVertices2
= WeldMesh(&remapTable
[0], &vertexDataOut
[0],
122 &newVertices
[0], newNumVert
, numVertexAttrs
);
124 // Copy the model data to graphics memory:-
126 m_Array
.SetNumberOfVertices(numVertices2
);
129 VertexArrayIterator
<CVector3D
> Position
= m_Position
.GetIterator
<CVector3D
>();
130 VertexArrayIterator
<CVector3D
> Normal
= m_Normal
.GetIterator
<CVector3D
>();
131 VertexArrayIterator
<CVector4D
> Tangent
= m_Tangent
.GetIterator
<CVector4D
>();
133 VertexArrayIterator
<u8
[4]> BlendJoints
;
134 VertexArrayIterator
<u8
[4]> BlendWeights
;
137 BlendJoints
= m_BlendJoints
.GetIterator
<u8
[4]>();
138 BlendWeights
= m_BlendWeights
.GetIterator
<u8
[4]>();
141 // copy everything into the vertex array
142 for (int i
= 0; i
< numVertices2
; i
++)
144 int q
= numVertexAttrs
* i
;
146 Position
[i
] = CVector3D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2]);
149 Normal
[i
] = CVector3D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2]);
152 Tangent
[i
] = CVector4D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2],
153 vertexDataOut
[q
+ 3]);
158 for (size_t j
= 0; j
< 4; ++j
)
160 BlendJoints
[i
][j
] = (u8
)vertexDataOut
[q
+ 0 + 2 * j
];
161 BlendWeights
[i
][j
] = (u8
)vertexDataOut
[q
+ 1 + 2 * j
];
166 for (size_t j
= 0; j
< mdef
->GetNumUVsPerVertex(); j
++)
168 VertexArrayIterator
<float[2]> UVit
= m_UVs
[j
].GetIterator
<float[2]>();
169 UVit
[i
][0] = vertexDataOut
[q
+ 0 + 2 * j
];
170 UVit
[i
][1] = vertexDataOut
[q
+ 1 + 2 * j
];
174 // upload vertex data
176 m_Array
.FreeBackingStore();
178 m_IndexArray
.SetNumberOfVertices(mdef
->GetNumFaces() * 3);
179 m_IndexArray
.Layout();
181 VertexArrayIterator
<u16
> Indices
= m_IndexArray
.GetIterator();
185 // reindex geometry and upload index
186 for (size_t j
= 0; j
< mdef
->GetNumFaces(); ++j
)
188 Indices
[idxidx
++] = remapTable
[j
* 3 + 0];
189 Indices
[idxidx
++] = remapTable
[j
* 3 + 1];
190 Indices
[idxidx
++] = remapTable
[j
* 3 + 2];
193 m_IndexArray
.Upload();
194 m_IndexArray
.FreeBackingStore();
198 // Upload model without calculating tangents:-
200 m_Array
.SetNumberOfVertices(numVertices
);
203 VertexArrayIterator
<CVector3D
> Position
= m_Position
.GetIterator
<CVector3D
>();
204 VertexArrayIterator
<CVector3D
> Normal
= m_Normal
.GetIterator
<CVector3D
>();
206 ModelRenderer::CopyPositionAndNormals(mdef
, Position
, Normal
);
208 for (size_t i
= 0; i
< mdef
->GetNumUVsPerVertex(); i
++)
210 VertexArrayIterator
<float[2]> UVit
= m_UVs
[i
].GetIterator
<float[2]>();
211 ModelRenderer::BuildUV(mdef
, UVit
, i
);
216 VertexArrayIterator
<u8
[4]> BlendJoints
= m_BlendJoints
.GetIterator
<u8
[4]>();
217 VertexArrayIterator
<u8
[4]> BlendWeights
= m_BlendWeights
.GetIterator
<u8
[4]>();
218 for (size_t i
= 0; i
< numVertices
; ++i
)
220 const SModelVertex
& vtx
= mdef
->GetVertices()[i
];
221 for (size_t j
= 0; j
< 4; ++j
)
223 BlendJoints
[i
][j
] = vtx
.m_Blend
.m_Bone
[j
];
224 BlendWeights
[i
][j
] = (u8
)(255.f
* vtx
.m_Blend
.m_Weight
[j
]);
230 m_Array
.FreeBackingStore();
232 m_IndexArray
.SetNumberOfVertices(mdef
->GetNumFaces()*3);
233 m_IndexArray
.Layout();
234 ModelRenderer::BuildIndices(mdef
, m_IndexArray
.GetIterator());
235 m_IndexArray
.Upload();
236 m_IndexArray
.FreeBackingStore();
241 struct InstancingModelRendererInternals
245 bool calculateTangents
;
247 /// Previously prepared modeldef
248 IModelDef
* imodeldef
;
250 /// Index base for imodeldef
251 u8
* imodeldefIndexBase
;
255 // Construction and Destruction
256 InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning
, bool calculateTangents
)
258 m
= new InstancingModelRendererInternals
;
259 m
->gpuSkinning
= gpuSkinning
;
260 m
->calculateTangents
= calculateTangents
;
264 InstancingModelRenderer::~InstancingModelRenderer()
270 // Build modeldef data if necessary - we have no per-CModel data
271 CModelRData
* InstancingModelRenderer::CreateModelData(const void* key
, CModel
* model
)
273 CModelDefPtr mdef
= model
->GetModelDef();
274 IModelDef
* imodeldef
= (IModelDef
*)mdef
->GetRenderData(m
);
277 ENSURE(model
->IsSkinned());
279 ENSURE(!model
->IsSkinned());
283 imodeldef
= new IModelDef(mdef
, m
->gpuSkinning
, m
->calculateTangents
);
284 mdef
->SetRenderData(m
, imodeldef
);
287 return new CModelRData(key
);
291 void InstancingModelRenderer::UpdateModelData(CModel
* UNUSED(model
), CModelRData
* UNUSED(data
), int UNUSED(updateflags
))
293 // We have no per-CModel data
297 // Setup one rendering pass.
298 void InstancingModelRenderer::BeginPass()
302 // Cleanup rendering pass.
303 void InstancingModelRenderer::EndPass(
304 Renderer::Backend::GL::CDeviceCommandContext
* UNUSED(deviceCommandContext
))
308 // Prepare UV coordinates for this modeldef
309 void InstancingModelRenderer::PrepareModelDef(
310 Renderer::Backend::GL::CDeviceCommandContext
* deviceCommandContext
,
311 Renderer::Backend::GL::CShaderProgram
* UNUSED(shader
), const CModelDef
& def
)
313 m
->imodeldef
= (IModelDef
*)def
.GetRenderData(m
);
315 ENSURE(m
->imodeldef
);
316 m
->imodeldef
->m_Array
.UploadIfNeeded(deviceCommandContext
);
317 m
->imodeldef
->m_IndexArray
.UploadIfNeeded(deviceCommandContext
);
319 deviceCommandContext
->SetIndexBuffer(m
->imodeldef
->m_IndexArray
.GetBuffer());
321 const uint32_t stride
= m
->imodeldef
->m_Array
.GetStride();
322 const uint32_t firstVertexOffset
= m
->imodeldef
->m_Array
.GetOffset() * stride
;
324 deviceCommandContext
->SetVertexAttributeFormat(
325 Renderer::Backend::VertexAttributeStream::POSITION
,
326 m
->imodeldef
->m_Position
.format
,
327 firstVertexOffset
+ m
->imodeldef
->m_Position
.offset
, stride
, 0);
328 deviceCommandContext
->SetVertexAttributeFormat(
329 Renderer::Backend::VertexAttributeStream::NORMAL
,
330 m
->imodeldef
->m_Normal
.format
,
331 firstVertexOffset
+ m
->imodeldef
->m_Normal
.offset
, stride
, 0);
333 constexpr size_t MAX_UV
= 2;
334 for (size_t uv
= 0; uv
< std::min(MAX_UV
, def
.GetNumUVsPerVertex()); ++uv
)
336 const Renderer::Backend::VertexAttributeStream stream
=
337 static_cast<Renderer::Backend::VertexAttributeStream
>(
338 static_cast<int>(Renderer::Backend::VertexAttributeStream::UV0
) + uv
);
339 deviceCommandContext
->SetVertexAttributeFormat(
340 stream
, m
->imodeldef
->m_UVs
[uv
].format
,
341 firstVertexOffset
+ m
->imodeldef
->m_UVs
[uv
].offset
, stride
, 0);
344 // GPU skinning requires extra attributes to compute positions/normals.
347 deviceCommandContext
->SetVertexAttributeFormat(
348 Renderer::Backend::VertexAttributeStream::UV2
,
349 m
->imodeldef
->m_BlendJoints
.format
,
350 firstVertexOffset
+ m
->imodeldef
->m_BlendJoints
.offset
, stride
, 0);
351 deviceCommandContext
->SetVertexAttributeFormat(
352 Renderer::Backend::VertexAttributeStream::UV3
,
353 m
->imodeldef
->m_BlendWeights
.format
,
354 firstVertexOffset
+ m
->imodeldef
->m_BlendWeights
.offset
, stride
, 0);
357 if (m
->calculateTangents
)
359 deviceCommandContext
->SetVertexAttributeFormat(
360 Renderer::Backend::VertexAttributeStream::UV4
,
361 m
->imodeldef
->m_Tangent
.format
,
362 firstVertexOffset
+ m
->imodeldef
->m_Tangent
.offset
, stride
, 0);
365 deviceCommandContext
->SetVertexBuffer(0, m
->imodeldef
->m_Array
.GetBuffer());
370 void InstancingModelRenderer::RenderModel(
371 Renderer::Backend::GL::CDeviceCommandContext
* deviceCommandContext
,
372 Renderer::Backend::GL::CShaderProgram
* shader
, CModel
* model
, CModelRData
* UNUSED(data
))
374 const CModelDefPtr
& mdldef
= model
->GetModelDef();
378 // Bind matrices for current animation state.
379 // Add 1 to NumBones because of the special 'root' bone.
380 shader
->Uniform(str_skinBlendMatrices
, mdldef
->GetNumBones() + 1, model
->GetAnimatedBoneMatrices());
384 const size_t numberOfFaces
= mdldef
->GetNumFaces();
386 deviceCommandContext
->DrawIndexedInRange(
387 m
->imodeldef
->m_IndexArray
.GetOffset(), numberOfFaces
* 3, 0, m
->imodeldef
->m_Array
.GetNumberOfVertices() - 1);
390 g_Renderer
.m_Stats
.m_DrawCalls
++;
391 g_Renderer
.m_Stats
.m_ModelTris
+= numberOfFaces
;