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"
25 #include "maths/Vector3D.h"
26 #include "maths/Vector4D.h"
27 #include "ps/CLogger.h"
28 #include "ps/CStrInternStatic.h"
29 #include "renderer/Renderer.h"
30 #include "renderer/RenderModifiers.h"
31 #include "renderer/VertexArray.h"
32 #include "third_party/mikktspace/weldmesh.h"
35 struct IModelDef
: public CModelDefRPrivate
37 /// Static per-CModel vertex array
40 /// Position and normals are static
41 VertexArray::Attribute m_Position
;
42 VertexArray::Attribute m_Normal
;
43 VertexArray::Attribute m_Tangent
;
44 VertexArray::Attribute m_BlendJoints
; // valid iff gpuSkinning == true
45 VertexArray::Attribute m_BlendWeights
; // valid iff gpuSkinning == true
47 /// The number of UVs is determined by the model
48 std::vector
<VertexArray::Attribute
> m_UVs
;
50 /// Indices are the same for all models, so share them
51 VertexIndexArray m_IndexArray
;
53 IModelDef(const CModelDefPtr
& mdef
, bool gpuSkinning
, bool calculateTangents
);
57 IModelDef::IModelDef(const CModelDefPtr
& mdef
, bool gpuSkinning
, bool calculateTangents
)
58 : m_IndexArray(false), m_Array(Renderer::Backend::IBuffer::Type::VERTEX
, false)
60 size_t numVertices
= mdef
->GetNumVertices();
62 m_Position
.format
= Renderer::Backend::Format::R32G32B32_SFLOAT
;
63 m_Array
.AddAttribute(&m_Position
);
65 m_Normal
.format
= Renderer::Backend::Format::R32G32B32_SFLOAT
;
66 m_Array
.AddAttribute(&m_Normal
);
68 m_UVs
.resize(mdef
->GetNumUVsPerVertex());
69 for (size_t i
= 0; i
< mdef
->GetNumUVsPerVertex(); i
++)
71 m_UVs
[i
].format
= Renderer::Backend::Format::R32G32_SFLOAT
;
72 m_Array
.AddAttribute(&m_UVs
[i
]);
77 // We can't use a lot of bones because it costs uniform memory. Recommended
78 // number of bones per model is 32.
79 // Add 1 to NumBones because of the special 'root' bone.
80 if (mdef
->GetNumBones() + 1 > 64)
81 LOGERROR("Model '%s' has too many bones %zu/64", mdef
->GetName().string8().c_str(), mdef
->GetNumBones() + 1);
82 ENSURE(mdef
->GetNumBones() + 1 <= 64);
84 m_BlendJoints
.format
= Renderer::Backend::Format::R8G8B8A8_UINT
;
85 m_Array
.AddAttribute(&m_BlendJoints
);
87 m_BlendWeights
.format
= Renderer::Backend::Format::R8G8B8A8_UNORM
;
88 m_Array
.AddAttribute(&m_BlendWeights
);
91 if (calculateTangents
)
93 // Generate tangents for the geometry:-
95 m_Tangent
.format
= Renderer::Backend::Format::R32G32B32A32_SFLOAT
;
96 m_Array
.AddAttribute(&m_Tangent
);
98 // floats per vertex; position + normal + tangent + UV*sets [+ GPUskinning]
99 int numVertexAttrs
= 3 + 3 + 4 + 2 * mdef
->GetNumUVsPerVertex();
105 // the tangent generation can increase the number of vertices temporarily
106 // so reserve a bit more memory to avoid reallocations in GenTangents (in most cases)
107 std::vector
<float> newVertices
;
108 newVertices
.reserve(numVertexAttrs
* numVertices
* 2);
110 // Generate the tangents
111 ModelRenderer::GenTangents(mdef
, newVertices
, gpuSkinning
);
113 // how many vertices do we have after generating tangents?
114 int newNumVert
= newVertices
.size() / numVertexAttrs
;
116 std::vector
<int> remapTable(newNumVert
);
117 std::vector
<float> vertexDataOut(newNumVert
* numVertexAttrs
);
119 // re-weld the mesh to remove duplicated vertices
120 int numVertices2
= WeldMesh(&remapTable
[0], &vertexDataOut
[0],
121 &newVertices
[0], newNumVert
, numVertexAttrs
);
123 // Copy the model data to graphics memory:-
125 m_Array
.SetNumberOfVertices(numVertices2
);
128 VertexArrayIterator
<CVector3D
> Position
= m_Position
.GetIterator
<CVector3D
>();
129 VertexArrayIterator
<CVector3D
> Normal
= m_Normal
.GetIterator
<CVector3D
>();
130 VertexArrayIterator
<CVector4D
> Tangent
= m_Tangent
.GetIterator
<CVector4D
>();
132 VertexArrayIterator
<u8
[4]> BlendJoints
;
133 VertexArrayIterator
<u8
[4]> BlendWeights
;
136 BlendJoints
= m_BlendJoints
.GetIterator
<u8
[4]>();
137 BlendWeights
= m_BlendWeights
.GetIterator
<u8
[4]>();
140 // copy everything into the vertex array
141 for (int i
= 0; i
< numVertices2
; i
++)
143 int q
= numVertexAttrs
* i
;
145 Position
[i
] = CVector3D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2]);
148 Normal
[i
] = CVector3D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2]);
151 Tangent
[i
] = CVector4D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2],
152 vertexDataOut
[q
+ 3]);
157 for (size_t j
= 0; j
< 4; ++j
)
159 BlendJoints
[i
][j
] = (u8
)vertexDataOut
[q
+ 0 + 2 * j
];
160 BlendWeights
[i
][j
] = (u8
)vertexDataOut
[q
+ 1 + 2 * j
];
165 for (size_t j
= 0; j
< mdef
->GetNumUVsPerVertex(); j
++)
167 VertexArrayIterator
<float[2]> UVit
= m_UVs
[j
].GetIterator
<float[2]>();
168 UVit
[i
][0] = vertexDataOut
[q
+ 0 + 2 * j
];
169 UVit
[i
][1] = vertexDataOut
[q
+ 1 + 2 * j
];
173 // upload vertex data
175 m_Array
.FreeBackingStore();
177 m_IndexArray
.SetNumberOfVertices(mdef
->GetNumFaces() * 3);
178 m_IndexArray
.Layout();
180 VertexArrayIterator
<u16
> Indices
= m_IndexArray
.GetIterator();
184 // reindex geometry and upload index
185 for (size_t j
= 0; j
< mdef
->GetNumFaces(); ++j
)
187 Indices
[idxidx
++] = remapTable
[j
* 3 + 0];
188 Indices
[idxidx
++] = remapTable
[j
* 3 + 1];
189 Indices
[idxidx
++] = remapTable
[j
* 3 + 2];
192 m_IndexArray
.Upload();
193 m_IndexArray
.FreeBackingStore();
197 // Upload model without calculating tangents:-
199 m_Array
.SetNumberOfVertices(numVertices
);
202 VertexArrayIterator
<CVector3D
> Position
= m_Position
.GetIterator
<CVector3D
>();
203 VertexArrayIterator
<CVector3D
> Normal
= m_Normal
.GetIterator
<CVector3D
>();
205 ModelRenderer::CopyPositionAndNormals(mdef
, Position
, Normal
);
207 for (size_t i
= 0; i
< mdef
->GetNumUVsPerVertex(); i
++)
209 VertexArrayIterator
<float[2]> UVit
= m_UVs
[i
].GetIterator
<float[2]>();
210 ModelRenderer::BuildUV(mdef
, UVit
, i
);
215 VertexArrayIterator
<u8
[4]> BlendJoints
= m_BlendJoints
.GetIterator
<u8
[4]>();
216 VertexArrayIterator
<u8
[4]> BlendWeights
= m_BlendWeights
.GetIterator
<u8
[4]>();
217 for (size_t i
= 0; i
< numVertices
; ++i
)
219 const SModelVertex
& vtx
= mdef
->GetVertices()[i
];
220 for (size_t j
= 0; j
< 4; ++j
)
222 BlendJoints
[i
][j
] = vtx
.m_Blend
.m_Bone
[j
];
223 BlendWeights
[i
][j
] = (u8
)(255.f
* vtx
.m_Blend
.m_Weight
[j
]);
229 m_Array
.FreeBackingStore();
231 m_IndexArray
.SetNumberOfVertices(mdef
->GetNumFaces()*3);
232 m_IndexArray
.Layout();
233 ModelRenderer::BuildIndices(mdef
, m_IndexArray
.GetIterator());
234 m_IndexArray
.Upload();
235 m_IndexArray
.FreeBackingStore();
240 struct InstancingModelRendererInternals
244 bool calculateTangents
;
246 /// Previously prepared modeldef
247 IModelDef
* imodeldef
;
249 /// Index base for imodeldef
250 u8
* imodeldefIndexBase
;
254 // Construction and Destruction
255 InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning
, bool calculateTangents
)
257 m
= new InstancingModelRendererInternals
;
258 m
->gpuSkinning
= gpuSkinning
;
259 m
->calculateTangents
= calculateTangents
;
263 InstancingModelRenderer::~InstancingModelRenderer()
269 // Build modeldef data if necessary - we have no per-CModel data
270 CModelRData
* InstancingModelRenderer::CreateModelData(const void* key
, CModel
* model
)
272 CModelDefPtr mdef
= model
->GetModelDef();
273 IModelDef
* imodeldef
= (IModelDef
*)mdef
->GetRenderData(m
);
276 ENSURE(model
->IsSkinned());
278 ENSURE(!model
->IsSkinned());
282 imodeldef
= new IModelDef(mdef
, m
->gpuSkinning
, m
->calculateTangents
);
283 mdef
->SetRenderData(m
, imodeldef
);
286 return new CModelRData(key
);
290 void InstancingModelRenderer::UpdateModelData(CModel
* UNUSED(model
), CModelRData
* UNUSED(data
), int UNUSED(updateflags
))
292 // We have no per-CModel data
295 void InstancingModelRenderer::UploadModelData(
296 Renderer::Backend::IDeviceCommandContext
* UNUSED(deviceCommandContext
),
297 CModel
* UNUSED(model
), CModelRData
* UNUSED(data
))
299 // Data uploaded once during creation as we don't update it dynamically.
302 // Prepare UV coordinates for this modeldef
303 void InstancingModelRenderer::PrepareModelDef(
304 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
,
305 const CModelDef
& def
)
307 m
->imodeldef
= (IModelDef
*)def
.GetRenderData(m
);
308 ENSURE(m
->imodeldef
);
310 deviceCommandContext
->SetIndexBuffer(m
->imodeldef
->m_IndexArray
.GetBuffer());
312 const uint32_t stride
= m
->imodeldef
->m_Array
.GetStride();
313 const uint32_t firstVertexOffset
= m
->imodeldef
->m_Array
.GetOffset() * stride
;
315 deviceCommandContext
->SetVertexAttributeFormat(
316 Renderer::Backend::VertexAttributeStream::POSITION
,
317 m
->imodeldef
->m_Position
.format
,
318 m
->imodeldef
->m_Position
.offset
, stride
,
319 Renderer::Backend::VertexAttributeRate::PER_VERTEX
, 0);
320 deviceCommandContext
->SetVertexAttributeFormat(
321 Renderer::Backend::VertexAttributeStream::NORMAL
,
322 m
->imodeldef
->m_Normal
.format
,
323 m
->imodeldef
->m_Normal
.offset
, stride
,
324 Renderer::Backend::VertexAttributeRate::PER_VERTEX
, 0);
326 constexpr size_t MAX_UV
= 2;
327 for (size_t uv
= 0; uv
< std::min(MAX_UV
, def
.GetNumUVsPerVertex()); ++uv
)
329 const Renderer::Backend::VertexAttributeStream stream
=
330 static_cast<Renderer::Backend::VertexAttributeStream
>(
331 static_cast<int>(Renderer::Backend::VertexAttributeStream::UV0
) + uv
);
332 deviceCommandContext
->SetVertexAttributeFormat(
333 stream
, m
->imodeldef
->m_UVs
[uv
].format
,
334 m
->imodeldef
->m_UVs
[uv
].offset
, stride
,
335 Renderer::Backend::VertexAttributeRate::PER_VERTEX
, 0);
338 // GPU skinning requires extra attributes to compute positions/normals.
341 deviceCommandContext
->SetVertexAttributeFormat(
342 Renderer::Backend::VertexAttributeStream::UV2
,
343 m
->imodeldef
->m_BlendJoints
.format
,
344 m
->imodeldef
->m_BlendJoints
.offset
, stride
,
345 Renderer::Backend::VertexAttributeRate::PER_VERTEX
, 0);
346 deviceCommandContext
->SetVertexAttributeFormat(
347 Renderer::Backend::VertexAttributeStream::UV3
,
348 m
->imodeldef
->m_BlendWeights
.format
,
349 m
->imodeldef
->m_BlendWeights
.offset
, stride
,
350 Renderer::Backend::VertexAttributeRate::PER_VERTEX
, 0);
353 if (m
->calculateTangents
)
355 deviceCommandContext
->SetVertexAttributeFormat(
356 Renderer::Backend::VertexAttributeStream::UV4
,
357 m
->imodeldef
->m_Tangent
.format
,
358 m
->imodeldef
->m_Tangent
.offset
, stride
,
359 Renderer::Backend::VertexAttributeRate::PER_VERTEX
, 0);
362 deviceCommandContext
->SetVertexBuffer(
363 0, m
->imodeldef
->m_Array
.GetBuffer(), firstVertexOffset
);
368 void InstancingModelRenderer::RenderModel(
369 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
,
370 Renderer::Backend::IShaderProgram
* shader
, CModel
* model
, CModelRData
* UNUSED(data
))
372 const CModelDefPtr
& mdldef
= model
->GetModelDef();
376 // Bind matrices for current animation state.
377 // Add 1 to NumBones because of the special 'root' bone.
378 deviceCommandContext
->SetUniform(
379 shader
->GetBindingSlot(str_skinBlendMatrices
),
380 PS::span
<const float>(
381 model
->GetAnimatedBoneMatrices()[0]._data
,
382 model
->GetAnimatedBoneMatrices()[0].AsFloatArray().size() * (mdldef
->GetNumBones() + 1)));
386 const size_t numberOfFaces
= mdldef
->GetNumFaces();
388 deviceCommandContext
->DrawIndexedInRange(
389 m
->imodeldef
->m_IndexArray
.GetOffset(), numberOfFaces
* 3, 0, m
->imodeldef
->m_Array
.GetNumberOfVertices() - 1);
392 g_Renderer
.m_Stats
.m_DrawCalls
++;
393 g_Renderer
.m_Stats
.m_ModelTris
+= numberOfFaces
;