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