Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
InstancingModelRenderer.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 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 /*
19  * Implementation of InstancingModelRenderer
20  */
21 
22 #include "precompiled.h"
23 
24 #include "lib/ogl.h"
25 #include "maths/Vector3D.h"
26 #include "maths/Vector4D.h"
27 
28 #include "ps/CLogger.h"
29 
30 #include "graphics/Color.h"
31 #include "graphics/LightEnv.h"
32 #include "graphics/Model.h"
33 #include "graphics/ModelDef.h"
34 
36 #include "renderer/Renderer.h"
38 #include "renderer/VertexArray.h"
39 
41 
42 
43 ///////////////////////////////////////////////////////////////////////////////////////////////
44 // InstancingModelRenderer implementation
45 
47 {
48  /// Static per-CModel vertex array
50 
51  /// Position and normals are static
55  VertexArray::Attribute m_BlendJoints; // valid iff gpuSkinning == true
56  VertexArray::Attribute m_BlendWeights; // valid iff gpuSkinning == true
57 
58  /// The number of UVs is determined by the model
59  std::vector<VertexArray::Attribute> m_UVs;
60 
61  /// Indices are the same for all models, so share them
63 
64  IModelDef(const CModelDefPtr& mdef, bool gpuSkinning, bool calculateTangents);
65 };
66 
67 
68 IModelDef::IModelDef(const CModelDefPtr& mdef, bool gpuSkinning, bool calculateTangents)
69  : m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW)
70 {
71  size_t numVertices = mdef->GetNumVertices();
72 
73  m_Position.type = GL_FLOAT;
74  m_Position.elems = 3;
76 
77  m_Normal.type = GL_FLOAT;
78  m_Normal.elems = 3;
80 
81  m_UVs.resize(mdef->GetNumUVsPerVertex());
82  for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
83  {
84  m_UVs[i].type = GL_FLOAT;
85  m_UVs[i].elems = 2;
87  }
88 
89  if (gpuSkinning)
90  {
91  m_BlendJoints.type = GL_UNSIGNED_BYTE;
92  m_BlendJoints.elems = 4;
94 
95  m_BlendWeights.type = GL_UNSIGNED_BYTE;
98  }
99 
100  if (calculateTangents)
101  {
102  // Generate tangents for the geometry:-
103 
104  m_Tangent.type = GL_FLOAT;
105  m_Tangent.elems = 4;
107 
108  // floats per vertex; position + normal + tangent + UV*sets [+ GPUskinning]
109  int numVertexAttrs = 3 + 3 + 4 + 2 * mdef->GetNumUVsPerVertex();
110  if (gpuSkinning)
111  {
112  numVertexAttrs += 8;
113  }
114 
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);
119 
120  // Generate the tangents
121  ModelRenderer::GenTangents(mdef, newVertices, gpuSkinning);
122 
123  // how many vertices do we have after generating tangents?
124  int newNumVert = newVertices.size() / numVertexAttrs;
125 
126  std::vector<int> remapTable(newNumVert);
127  std::vector<float> vertexDataOut(newNumVert * numVertexAttrs);
128 
129  // re-weld the mesh to remove duplicated vertices
130  int numVertices2 = WeldMesh(&remapTable[0], &vertexDataOut[0],
131  &newVertices[0], newNumVert, numVertexAttrs);
132 
133  // Copy the model data to graphics memory:-
134 
135  m_Array.SetNumVertices(numVertices2);
136  m_Array.Layout();
137 
141 
142  VertexArrayIterator<u8[4]> BlendJoints;
143  VertexArrayIterator<u8[4]> BlendWeights;
144  if (gpuSkinning)
145  {
146  BlendJoints = m_BlendJoints.GetIterator<u8[4]>();
147  BlendWeights = m_BlendWeights.GetIterator<u8[4]>();
148  }
149 
150  // copy everything into the vertex array
151  for (int i = 0; i < numVertices2; i++)
152  {
153  int q = numVertexAttrs * i;
154 
155  Position[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]);
156  q += 3;
157 
158  Normal[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]);
159  q += 3;
160 
161  Tangent[i] = CVector4D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2],
162  vertexDataOut[q + 3]);
163  q += 4;
164 
165  if (gpuSkinning)
166  {
167  for (size_t j = 0; j < 4; ++j)
168  {
169  BlendJoints[i][j] = (u8)vertexDataOut[q + 0 + 2 * j];
170  BlendWeights[i][j] = (u8)vertexDataOut[q + 1 + 2 * j];
171  }
172  q += 8;
173  }
174 
175  for (size_t j = 0; j < mdef->GetNumUVsPerVertex(); j++)
176  {
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];
180  }
181  }
182 
183  // upload vertex data
184  m_Array.Upload();
186 
187  m_IndexArray.SetNumVertices(mdef->GetNumFaces() * 3);
189 
191 
192  size_t idxidx = 0;
193 
194  // reindex geometry and upload index
195  for (size_t j = 0; j < mdef->GetNumFaces(); ++j)
196  {
197  Indices[idxidx++] = remapTable[j * 3 + 0];
198  Indices[idxidx++] = remapTable[j * 3 + 1];
199  Indices[idxidx++] = remapTable[j * 3 + 2];
200  }
201 
204  }
205  else
206  {
207  // Upload model without calculating tangents:-
208 
209  m_Array.SetNumVertices(numVertices);
210  m_Array.Layout();
211 
214 
215  ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal);
216 
217  for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
218  {
219  VertexArrayIterator<float[2]> UVit = m_UVs[i].GetIterator<float[2]>();
220  ModelRenderer::BuildUV(mdef, UVit, i);
221  }
222 
223  if (gpuSkinning)
224  {
227  for (size_t i = 0; i < numVertices; ++i)
228  {
229  const SModelVertex& vtx = mdef->GetVertices()[i];
230  for (size_t j = 0; j < 4; ++j)
231  {
232  BlendJoints[i][j] = vtx.m_Blend.m_Bone[j];
233  BlendWeights[i][j] = (u8)(255.f * vtx.m_Blend.m_Weight[j]);
234  }
235  }
236  }
237 
238  m_Array.Upload();
240 
241  m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3);
246  }
247 }
248 
249 
251 {
253 
255 
256  /// Previously prepared modeldef
258 
259  /// Index base for imodeldef
261 };
262 
263 
264 // Construction and Destruction
265 InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning, bool calculateTangents)
266 {
268  m->gpuSkinning = gpuSkinning;
269  m->calculateTangents = calculateTangents;
270  m->imodeldef = 0;
271 }
272 
274 {
275  delete m;
276 }
277 
278 
279 // Build modeldef data if necessary - we have no per-CModel data
281 {
282  CModelDefPtr mdef = model->GetModelDef();
283  IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m);
284 
285  if (m->gpuSkinning)
286  ENSURE(model->IsSkinned());
287  else
288  ENSURE(!model->IsSkinned());
289 
290  if (!imodeldef)
291  {
292  imodeldef = new IModelDef(mdef, m->gpuSkinning, m->calculateTangents);
293  mdef->SetRenderData(m, imodeldef);
294  }
295 
296  return new CModelRData(key);
297 }
298 
299 
301 {
302  // We have no per-CModel data
303 }
304 
305 
306 // Setup one rendering pass.
308 {
309  ENSURE(streamflags == (streamflags & (STREAM_POS|STREAM_NORMAL|STREAM_UV0|STREAM_UV1)));
310 }
311 
312 // Cleanup rendering pass.
314 {
316 }
317 
318 
319 // Prepare UV coordinates for this modeldef
320 void InstancingModelRenderer::PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def)
321 {
322  m->imodeldef = (IModelDef*)def.GetRenderData(m);
323 
324  ENSURE(m->imodeldef);
325 
326  u8* base = m->imodeldef->m_Array.Bind();
327  GLsizei stride = (GLsizei)m->imodeldef->m_Array.GetStride();
328 
330 
331  if (streamflags & STREAM_POS)
332  shader->VertexPointer(3, GL_FLOAT, stride, base + m->imodeldef->m_Position.offset);
333 
334  if (streamflags & STREAM_NORMAL)
335  shader->NormalPointer(GL_FLOAT, stride, base + m->imodeldef->m_Normal.offset);
336 
337  if (m->calculateTangents)
338  shader->VertexAttribPointer(str_a_tangent, 4, GL_FLOAT, GL_TRUE, stride, base + m->imodeldef->m_Tangent.offset);
339 
340  if (streamflags & STREAM_UV0)
341  shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m->imodeldef->m_UVs[0].offset);
342 
343  if ((streamflags & STREAM_UV1) && def.GetNumUVsPerVertex() >= 2)
344  shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, base + m->imodeldef->m_UVs[1].offset);
345 
346  // GPU skinning requires extra attributes to compute positions/normals
347  if (m->gpuSkinning)
348  {
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);
351  }
352 
353  shader->AssertPointersBound();
354 }
355 
356 
357 // Render one model
359 {
360  CModelDefPtr mdldef = model->GetModelDef();
361 
362  if (m->gpuSkinning)
363  {
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());
371  }
372 
373  // render the lot
374  size_t numFaces = mdldef->GetNumFaces();
375 
376  if (!g_Renderer.m_SkipSubmit)
377  {
378  // Draw with DrawRangeElements where available, since it might be more efficient
379 #if CONFIG2_GLES
380  glDrawElements(GL_TRIANGLES, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase);
381 #else
382  pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)m->imodeldef->m_Array.GetNumVertices()-1,
383  (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase);
384 #endif
385  }
386 
387  // bump stats
388  g_Renderer.m_Stats.m_DrawCalls++;
389  g_Renderer.m_Stats.m_ModelTris += numFaces;
390 
391 }
void UpdateModelData(CModel *model, CModelRData *data, int updateflags)
UpdateModelData: Calculate per-model data for each frame.
#define u8
Definition: types.h:39
static void BuildUV(const CModelDefPtr &mdef, const VertexArrayIterator< float[2]> &UV, int UVset)
BuildUV: Copy UV coordinates into the given vertex array.
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
VertexArray::Attribute m_BlendWeights
CModelRData * CreateModelData(const void *key, CModel *model)
CreateModelData: Create internal data for one model.
VertexArray::Attribute m_Position
Position and normals are static.
VertexArrayIterator< u16 > GetIterator() const
Gets the iterator over the (only) attribute in this array, i.e. a u16.
static void CopyPositionAndNormals(const CModelDefPtr &mdef, const VertexArrayIterator< CVector3D > &Position, const VertexArrayIterator< CVector3D > &Normal)
CopyPositionAndNormals: Copy unanimated object-space vertices and normals into the given vertex array...
IModelDef * imodeldef
Previously prepared modeldef.
float m_Weight[SIZE]
Definition: ModelDef.h:91
InstancingModelRenderer(bool gpuSkinning, bool calculateTangents)
VertexArray::Attribute m_Tangent
void AddAttribute(Attribute *attr)
Definition: VertexArray.cpp:75
void FreeBackingStore()
VertexArray m_Array
Static per-CModel vertex array.
A VertexArray that is specialised to handle 16-bit array indices.
Definition: VertexArray.h:212
#define g_Renderer
Definition: Renderer.h:61
const CMatrix3D * GetAnimatedBoneMatrices()
Definition: Model.h:195
u8 m_Bone[SIZE]
Definition: ModelDef.h:89
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
void BeginPass(int streamflags)
BeginPass: Setup global OpenGL state for this ModelVertexRenderer.
InstancingModelRendererInternals * m
bool IsSkinned()
Return whether this is a skinned/skeletal model.
Definition: Model.h:192
void RenderModel(const CShaderProgramPtr &shader, int streamflags, CModel *model, CModelRData *data)
RenderModel: Invoke the rendering commands for the given model.
pthread_key_t key
Definition: wpthread.cpp:140
static void BuildIndices(const CModelDefPtr &mdef, const VertexArrayIterator< u16 > &Indices)
BuildIndices: Create the indices array for the given CModelDef.
void SetNumVertices(size_t num)
Definition: VertexArray.cpp:64
IModelDef(const CModelDefPtr &mdef, bool gpuSkinning, bool calculateTangents)
int WeldMesh(int *piRemapTable, float *pfVertexDataOut, const float pfVertexDataIn[], const int iNrVerticesIn, const int iFloatsPerVert)
Copyright (C) 2011 by Morten S.
Definition: weldmesh.cpp:43
VertexArrayIterator< T > GetIterator() const
void PrepareModelDef(const CShaderProgramPtr &shader, int streamflags, const CModelDef &def)
PrepareModelDef: Setup OpenGL state for rendering of models that use the given CModelDef object as ba...
static void GenTangents(const CModelDefPtr &mdef, std::vector< float > &newVertices, bool gpuSkinning)
GenTangents: Generate tangents for the given CModelDef.
static size_t model
Definition: x86_x64.cpp:211
std::vector< VertexArray::Attribute > m_UVs
The number of UVs is determined by the model.
VertexArray::Attribute m_Normal
Class CModelRData: Render data that is maintained per CModel.
Definition: ModelRenderer.h:70
VertexArray::Attribute m_BlendJoints
SVertexBlend m_Blend
Definition: ModelDef.h:110
u8 * imodeldefIndexBase
Index base for imodeldef.
Definition: Model.h:50
const CModelDefPtr & GetModelDef()
Definition: Model.h:99
static void Unbind()
Unbind any currently-bound buffer, so glVertexPointer etc calls will not attempt to use it...
shared_ptr< CShaderProgram > CShaderProgramPtr
size_t GetStride() const
Definition: VertexArray.h:169
CModelDefRPrivate * GetRenderData(const void *key) const
Definition: ModelDef.cpp:465
size_t GetNumUVsPerVertex() const
Definition: ModelDef.h:170
size_t GetNumVertices() const
Definition: VertexArray.h:168
boost::shared_ptr< CModelDef > CModelDefPtr
Definition: MeshManager.h:27
void EndPass(int streamflags)
EndPass: Cleanup OpenGL state set up by BeginPass.
VertexIndexArray m_IndexArray
Indices are the same for all models, so share them.