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 : }
|