1 /* Copyright (C) 2012 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/>.
19 * Implementation of InstancingModelRenderer
22 #include "precompiled.h"
25 #include "maths/Vector3D.h"
26 #include "maths/Vector4D.h"
28 #include "ps/CLogger.h"
30 #include "graphics/Color.h"
31 #include "graphics/LightEnv.h"
32 #include "graphics/Model.h"
33 #include "graphics/ModelDef.h"
35 #include "renderer/InstancingModelRenderer.h"
36 #include "renderer/Renderer.h"
37 #include "renderer/RenderModifiers.h"
38 #include "renderer/VertexArray.h"
40 #include "third_party/mikktspace/weldmesh.h"
43 ///////////////////////////////////////////////////////////////////////////////////////////////
44 // InstancingModelRenderer implementation
46 struct IModelDef
: public CModelDefRPrivate
48 /// Static per-CModel vertex array
51 /// Position and normals are static
52 VertexArray::Attribute m_Position
;
53 VertexArray::Attribute m_Normal
;
54 VertexArray::Attribute m_Tangent
;
55 VertexArray::Attribute m_BlendJoints
; // valid iff gpuSkinning == true
56 VertexArray::Attribute m_BlendWeights
; // valid iff gpuSkinning == true
58 /// The number of UVs is determined by the model
59 std::vector
<VertexArray::Attribute
> m_UVs
;
61 /// Indices are the same for all models, so share them
62 VertexIndexArray m_IndexArray
;
64 IModelDef(const CModelDefPtr
& mdef
, bool gpuSkinning
, bool calculateTangents
);
68 IModelDef::IModelDef(const CModelDefPtr
& mdef
, bool gpuSkinning
, bool calculateTangents
)
69 : m_IndexArray(GL_STATIC_DRAW
), m_Array(GL_STATIC_DRAW
)
71 size_t numVertices
= mdef
->GetNumVertices();
73 m_Position
.type
= GL_FLOAT
;
75 m_Array
.AddAttribute(&m_Position
);
77 m_Normal
.type
= GL_FLOAT
;
79 m_Array
.AddAttribute(&m_Normal
);
81 m_UVs
.resize(mdef
->GetNumUVsPerVertex());
82 for (size_t i
= 0; i
< mdef
->GetNumUVsPerVertex(); i
++)
84 m_UVs
[i
].type
= GL_FLOAT
;
86 m_Array
.AddAttribute(&m_UVs
[i
]);
91 m_BlendJoints
.type
= GL_UNSIGNED_BYTE
;
92 m_BlendJoints
.elems
= 4;
93 m_Array
.AddAttribute(&m_BlendJoints
);
95 m_BlendWeights
.type
= GL_UNSIGNED_BYTE
;
96 m_BlendWeights
.elems
= 4;
97 m_Array
.AddAttribute(&m_BlendWeights
);
100 if (calculateTangents
)
102 // Generate tangents for the geometry:-
104 m_Tangent
.type
= GL_FLOAT
;
106 m_Array
.AddAttribute(&m_Tangent
);
108 // floats per vertex; position + normal + tangent + UV*sets [+ GPUskinning]
109 int numVertexAttrs
= 3 + 3 + 4 + 2 * mdef
->GetNumUVsPerVertex();
115 // the tangent generation can increase the number of vertices temporarily
116 // so reserve a bit more memory to avoid reallocations in GenTangents (in most cases)
117 std::vector
<float> newVertices
;
118 newVertices
.reserve(numVertexAttrs
* numVertices
* 2);
120 // Generate the tangents
121 ModelRenderer::GenTangents(mdef
, newVertices
, gpuSkinning
);
123 // how many vertices do we have after generating tangents?
124 int newNumVert
= newVertices
.size() / numVertexAttrs
;
126 std::vector
<int> remapTable(newNumVert
);
127 std::vector
<float> vertexDataOut(newNumVert
* numVertexAttrs
);
129 // re-weld the mesh to remove duplicated vertices
130 int numVertices2
= WeldMesh(&remapTable
[0], &vertexDataOut
[0],
131 &newVertices
[0], newNumVert
, numVertexAttrs
);
133 // Copy the model data to graphics memory:-
135 m_Array
.SetNumVertices(numVertices2
);
138 VertexArrayIterator
<CVector3D
> Position
= m_Position
.GetIterator
<CVector3D
>();
139 VertexArrayIterator
<CVector3D
> Normal
= m_Normal
.GetIterator
<CVector3D
>();
140 VertexArrayIterator
<CVector4D
> Tangent
= m_Tangent
.GetIterator
<CVector4D
>();
142 VertexArrayIterator
<u8
[4]> BlendJoints
;
143 VertexArrayIterator
<u8
[4]> BlendWeights
;
146 BlendJoints
= m_BlendJoints
.GetIterator
<u8
[4]>();
147 BlendWeights
= m_BlendWeights
.GetIterator
<u8
[4]>();
150 // copy everything into the vertex array
151 for (int i
= 0; i
< numVertices2
; i
++)
153 int q
= numVertexAttrs
* i
;
155 Position
[i
] = CVector3D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2]);
158 Normal
[i
] = CVector3D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2]);
161 Tangent
[i
] = CVector4D(vertexDataOut
[q
+ 0], vertexDataOut
[q
+ 1], vertexDataOut
[q
+ 2],
162 vertexDataOut
[q
+ 3]);
167 for (size_t j
= 0; j
< 4; ++j
)
169 BlendJoints
[i
][j
] = (u8
)vertexDataOut
[q
+ 0 + 2 * j
];
170 BlendWeights
[i
][j
] = (u8
)vertexDataOut
[q
+ 1 + 2 * j
];
175 for (size_t j
= 0; j
< mdef
->GetNumUVsPerVertex(); j
++)
177 VertexArrayIterator
<float[2]> UVit
= m_UVs
[j
].GetIterator
<float[2]>();
178 UVit
[i
][0] = vertexDataOut
[q
+ 0 + 2 * j
];
179 UVit
[i
][1] = vertexDataOut
[q
+ 1 + 2 * j
];
183 // upload vertex data
185 m_Array
.FreeBackingStore();
187 m_IndexArray
.SetNumVertices(mdef
->GetNumFaces() * 3);
188 m_IndexArray
.Layout();
190 VertexArrayIterator
<u16
> Indices
= m_IndexArray
.GetIterator();
194 // reindex geometry and upload index
195 for (size_t j
= 0; j
< mdef
->GetNumFaces(); ++j
)
197 Indices
[idxidx
++] = remapTable
[j
* 3 + 0];
198 Indices
[idxidx
++] = remapTable
[j
* 3 + 1];
199 Indices
[idxidx
++] = remapTable
[j
* 3 + 2];
202 m_IndexArray
.Upload();
203 m_IndexArray
.FreeBackingStore();
207 // Upload model without calculating tangents:-
209 m_Array
.SetNumVertices(numVertices
);
212 VertexArrayIterator
<CVector3D
> Position
= m_Position
.GetIterator
<CVector3D
>();
213 VertexArrayIterator
<CVector3D
> Normal
= m_Normal
.GetIterator
<CVector3D
>();
215 ModelRenderer::CopyPositionAndNormals(mdef
, Position
, Normal
);
217 for (size_t i
= 0; i
< mdef
->GetNumUVsPerVertex(); i
++)
219 VertexArrayIterator
<float[2]> UVit
= m_UVs
[i
].GetIterator
<float[2]>();
220 ModelRenderer::BuildUV(mdef
, UVit
, i
);
225 VertexArrayIterator
<u8
[4]> BlendJoints
= m_BlendJoints
.GetIterator
<u8
[4]>();
226 VertexArrayIterator
<u8
[4]> BlendWeights
= m_BlendWeights
.GetIterator
<u8
[4]>();
227 for (size_t i
= 0; i
< numVertices
; ++i
)
229 const SModelVertex
& vtx
= mdef
->GetVertices()[i
];
230 for (size_t j
= 0; j
< 4; ++j
)
232 BlendJoints
[i
][j
] = vtx
.m_Blend
.m_Bone
[j
];
233 BlendWeights
[i
][j
] = (u8
)(255.f
* vtx
.m_Blend
.m_Weight
[j
]);
239 m_Array
.FreeBackingStore();
241 m_IndexArray
.SetNumVertices(mdef
->GetNumFaces()*3);
242 m_IndexArray
.Layout();
243 ModelRenderer::BuildIndices(mdef
, m_IndexArray
.GetIterator());
244 m_IndexArray
.Upload();
245 m_IndexArray
.FreeBackingStore();
250 struct InstancingModelRendererInternals
254 bool calculateTangents
;
256 /// Previously prepared modeldef
257 IModelDef
* imodeldef
;
259 /// Index base for imodeldef
260 u8
* imodeldefIndexBase
;
264 // Construction and Destruction
265 InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning
, bool calculateTangents
)
267 m
= new InstancingModelRendererInternals
;
268 m
->gpuSkinning
= gpuSkinning
;
269 m
->calculateTangents
= calculateTangents
;
273 InstancingModelRenderer::~InstancingModelRenderer()
279 // Build modeldef data if necessary - we have no per-CModel data
280 CModelRData
* InstancingModelRenderer::CreateModelData(const void* key
, CModel
* model
)
282 CModelDefPtr mdef
= model
->GetModelDef();
283 IModelDef
* imodeldef
= (IModelDef
*)mdef
->GetRenderData(m
);
286 ENSURE(model
->IsSkinned());
288 ENSURE(!model
->IsSkinned());
292 imodeldef
= new IModelDef(mdef
, m
->gpuSkinning
, m
->calculateTangents
);
293 mdef
->SetRenderData(m
, imodeldef
);
296 return new CModelRData(key
);
300 void InstancingModelRenderer::UpdateModelData(CModel
* UNUSED(model
), CModelRData
* UNUSED(data
), int UNUSED(updateflags
))
302 // We have no per-CModel data
306 // Setup one rendering pass.
307 void InstancingModelRenderer::BeginPass(int streamflags
)
309 ENSURE(streamflags
== (streamflags
& (STREAM_POS
|STREAM_NORMAL
|STREAM_UV0
|STREAM_UV1
)));
312 // Cleanup rendering pass.
313 void InstancingModelRenderer::EndPass(int UNUSED(streamflags
))
315 CVertexBuffer::Unbind();
319 // Prepare UV coordinates for this modeldef
320 void InstancingModelRenderer::PrepareModelDef(const CShaderProgramPtr
& shader
, int streamflags
, const CModelDef
& def
)
322 m
->imodeldef
= (IModelDef
*)def
.GetRenderData(m
);
324 ENSURE(m
->imodeldef
);
326 u8
* base
= m
->imodeldef
->m_Array
.Bind();
327 GLsizei stride
= (GLsizei
)m
->imodeldef
->m_Array
.GetStride();
329 m
->imodeldefIndexBase
= m
->imodeldef
->m_IndexArray
.Bind();
331 if (streamflags
& STREAM_POS
)
332 shader
->VertexPointer(3, GL_FLOAT
, stride
, base
+ m
->imodeldef
->m_Position
.offset
);
334 if (streamflags
& STREAM_NORMAL
)
335 shader
->NormalPointer(GL_FLOAT
, stride
, base
+ m
->imodeldef
->m_Normal
.offset
);
337 if (m
->calculateTangents
)
338 shader
->VertexAttribPointer(str_a_tangent
, 4, GL_FLOAT
, GL_TRUE
, stride
, base
+ m
->imodeldef
->m_Tangent
.offset
);
340 if (streamflags
& STREAM_UV0
)
341 shader
->TexCoordPointer(GL_TEXTURE0
, 2, GL_FLOAT
, stride
, base
+ m
->imodeldef
->m_UVs
[0].offset
);
343 if ((streamflags
& STREAM_UV1
) && def
.GetNumUVsPerVertex() >= 2)
344 shader
->TexCoordPointer(GL_TEXTURE1
, 2, GL_FLOAT
, stride
, base
+ m
->imodeldef
->m_UVs
[1].offset
);
346 // GPU skinning requires extra attributes to compute positions/normals
349 shader
->VertexAttribPointer(str_a_skinJoints
, 4, GL_UNSIGNED_BYTE
, GL_FALSE
, stride
, base
+ m
->imodeldef
->m_BlendJoints
.offset
);
350 shader
->VertexAttribPointer(str_a_skinWeights
, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, stride
, base
+ m
->imodeldef
->m_BlendWeights
.offset
);
353 shader
->AssertPointersBound();
358 void InstancingModelRenderer::RenderModel(const CShaderProgramPtr
& shader
, int UNUSED(streamflags
), CModel
* model
, CModelRData
* UNUSED(data
))
360 CModelDefPtr mdldef
= model
->GetModelDef();
364 // Bind matrices for current animation state.
365 // Add 1 to NumBones because of the special 'root' bone.
366 // HACK: NVIDIA drivers return uniform name with "[0]", Intel Windows drivers without;
367 // try uploading both names since one of them should work, and this is easier than
368 // canonicalising the uniform names in CShaderProgramGLSL
369 shader
->Uniform(str_skinBlendMatrices_0
, mdldef
->GetNumBones() + 1, model
->GetAnimatedBoneMatrices());
370 shader
->Uniform(str_skinBlendMatrices
, mdldef
->GetNumBones() + 1, model
->GetAnimatedBoneMatrices());
374 size_t numFaces
= mdldef
->GetNumFaces();
376 if (!g_Renderer
.m_SkipSubmit
)
378 // Draw with DrawRangeElements where available, since it might be more efficient
380 glDrawElements(GL_TRIANGLES
, (GLsizei
)numFaces
*3, GL_UNSIGNED_SHORT
, m
->imodeldefIndexBase
);
382 pglDrawRangeElementsEXT(GL_TRIANGLES
, 0, (GLuint
)m
->imodeldef
->m_Array
.GetNumVertices()-1,
383 (GLsizei
)numFaces
*3, GL_UNSIGNED_SHORT
, m
->imodeldefIndexBase
);
388 g_Renderer
.m_Stats
.m_DrawCalls
++;
389 g_Renderer
.m_Stats
.m_ModelTris
+= numFaces
;