OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4DrawMeshGL.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2013-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 /* OpenGL implementation of Mesh Rendering */
18 
19 #include "C4Include.h"
21 #include "object/C4Object.h"
22 #include "graphics/C4DrawGL.h"
24 #include "lib/SHA1.h"
26 
27 #include "lib/StdMesh.h"
29 #include <locale.h>
30 #include <stdexcept>
31 
32 #ifndef USE_CONSOLE
33 
34 namespace
35 {
36  template<int category>
37  class ScopedLocale
38  {
39  // We need to make a copy of the return value of setlocale, because
40  // it's using TLS
41  std::string saved_loc;
42  public:
43  explicit ScopedLocale(const char *locale)
44  {
45  const char *old_loc = setlocale(category, locale);
46  if (old_loc == nullptr)
47  throw std::invalid_argument("Argument to setlocale was invalid");
48  saved_loc = old_loc;
49  }
50  ~ScopedLocale()
51  {
52  setlocale(category, saved_loc.c_str());
53  }
54  };
55 
57  // Shader code generation
58  // This translates the fixed function instructions in a material script
59  // to an equivalent fragment shader. The generated code can certainly
60  // be optimized more.
62  StdStrBuf Texture2DToCode(int index, bool hasTextureAnimation)
63  {
64  if (hasTextureAnimation) return FormatString("texture(oc_Texture%d, (oc_TextureMatrix%d * vec4(texcoord, 0.0, 1.0)).xy)", index, index);
65  return FormatString("texture(oc_Texture%d, texcoord)", index);
66  }
67 
68  StdStrBuf TextureUnitSourceToCode(int index, StdMeshMaterialTextureUnit::BlendOpSourceType source, const float manualColor[3], float manualAlpha, bool hasTextureAnimation)
69  {
70  switch(source)
71  {
72  case StdMeshMaterialTextureUnit::BOS_Current: return StdStrBuf("currentColor");
73  case StdMeshMaterialTextureUnit::BOS_Texture: return Texture2DToCode(index, hasTextureAnimation);
74  case StdMeshMaterialTextureUnit::BOS_Diffuse: return StdStrBuf("diffuse");
75  case StdMeshMaterialTextureUnit::BOS_Specular: return StdStrBuf("diffuse"); // TODO: Should be specular
76  case StdMeshMaterialTextureUnit::BOS_PlayerColor: return StdStrBuf("vec4(oc_PlayerColor, 1.0)");
77  case StdMeshMaterialTextureUnit::BOS_Manual: return FormatString("vec4(%f, %f, %f, %f)", manualColor[0], manualColor[1], manualColor[2], manualAlpha);
78  default: assert(false); return StdStrBuf("vec4(0.0, 0.0, 0.0, 0.0)");
79  }
80  }
81 
82  StdStrBuf TextureUnitBlendToCode(int index, StdMeshMaterialTextureUnit::BlendOpExType blend_type, const char* source1, const char* source2, float manualFactor, bool hasTextureAnimation)
83  {
84  switch(blend_type)
85  {
88  case StdMeshMaterialTextureUnit::BOX_Modulate: return FormatString("%s * %s", source1, source2);
89  case StdMeshMaterialTextureUnit::BOX_ModulateX2: return FormatString("2.0 * %s * %s", source1, source2);
90  case StdMeshMaterialTextureUnit::BOX_ModulateX4: return FormatString("4.0 * %s * %s", source1, source2);
91  case StdMeshMaterialTextureUnit::BOX_Add: return FormatString("%s + %s", source1, source2);
92  case StdMeshMaterialTextureUnit::BOX_AddSigned: return FormatString("%s + %s - 0.5", source1, source2);
93  case StdMeshMaterialTextureUnit::BOX_AddSmooth: return FormatString("%s + %s - %s*%s", source1, source2, source1, source2);
94  case StdMeshMaterialTextureUnit::BOX_Subtract: return FormatString("%s - %s", source1, source2);
95  case StdMeshMaterialTextureUnit::BOX_BlendDiffuseAlpha: return FormatString("diffuse.a * %s + (1.0 - diffuse.a) * %s", source1, source2);
96  case StdMeshMaterialTextureUnit::BOX_BlendTextureAlpha: return FormatString("%s.a * %s + (1.0 - %s.a) * %s", Texture2DToCode(index, hasTextureAnimation).getData(), source1, Texture2DToCode(index, hasTextureAnimation).getData(), source2);
97  case StdMeshMaterialTextureUnit::BOX_BlendCurrentAlpha: return FormatString("currentColor.a * %s + (1.0 - currentColor.a) * %s", source1, source2);
98  case StdMeshMaterialTextureUnit::BOX_BlendManual: return FormatString("%f * %s + (1.0 - %f) * %s", manualFactor, source1, manualFactor, source2);
99  case StdMeshMaterialTextureUnit::BOX_Dotproduct: return FormatString("vec3(4.0 * dot(%s - 0.5, %s - 0.5), 4.0 * dot(%s - 0.5, %s - 0.5), 4.0 * dot(%s - 0.5, %s - 0.5))", source1, source2, source1, source2, source1, source2); // TODO: Needs special handling for the case of alpha
100  case StdMeshMaterialTextureUnit::BOX_BlendDiffuseColor: return FormatString("diffuse.rgb * %s + (1.0 - diffuse.rgb) * %s", source1, source2);
101  default: assert(false); return StdStrBuf(source1);
102  }
103  }
104 
105  StdStrBuf TextureUnitToCode(int index, const StdMeshMaterialTextureUnit& texunit)
106  {
107  ScopedLocale<LC_NUMERIC> scoped_c_locale("C");
108  const bool hasTextureAnimation = texunit.HasTexCoordAnimation();
109 
110  StdStrBuf color_source1 = FormatString("%s.rgb", TextureUnitSourceToCode(index, texunit.ColorOpSources[0], texunit.ColorOpManualColor1, texunit.AlphaOpManualAlpha1, hasTextureAnimation).getData());
111  StdStrBuf color_source2 = FormatString("%s.rgb", TextureUnitSourceToCode(index, texunit.ColorOpSources[1], texunit.ColorOpManualColor2, texunit.AlphaOpManualAlpha2, hasTextureAnimation).getData());
112  StdStrBuf alpha_source1 = FormatString("%s.a", TextureUnitSourceToCode(index, texunit.AlphaOpSources[0], texunit.ColorOpManualColor1, texunit.AlphaOpManualAlpha1, hasTextureAnimation).getData());
113  StdStrBuf alpha_source2 = FormatString("%s.a", TextureUnitSourceToCode(index, texunit.AlphaOpSources[1], texunit.ColorOpManualColor2, texunit.AlphaOpManualAlpha2, hasTextureAnimation).getData());
114 
115  return FormatString("currentColor = vec4(%s, %s);\n", TextureUnitBlendToCode(index, texunit.ColorOpEx, color_source1.getData(), color_source2.getData(), texunit.ColorOpManualFactor, hasTextureAnimation).getData(), TextureUnitBlendToCode(index, texunit.AlphaOpEx, alpha_source1.getData(), alpha_source2.getData(), texunit.AlphaOpManualFactor, hasTextureAnimation).getData());
116  }
117 
118  StdStrBuf AlphaTestToCode(const StdMeshMaterialPass& pass)
119  {
120  ScopedLocale<LC_NUMERIC> scoped_c_locale("C");
121  switch (pass.AlphaRejectionFunction)
122  {
124  return StdStrBuf("");
126  return StdStrBuf("discard;");
128  return FormatString("if (!(fragColor.a < %f)) discard;", pass.AlphaRejectionValue);
130  return FormatString("if (!(fragColor.a <= %f)) discard;", pass.AlphaRejectionValue);
132  return FormatString("if (!(fragColor.a == %f)) discard;", pass.AlphaRejectionValue);
134  return FormatString("if (!(fragColor.a != %f)) discard;", pass.AlphaRejectionValue);
136  return FormatString("if (!(fragColor.a > %f)) discard;", pass.AlphaRejectionValue);
138  return FormatString("if (!(fragColor.a >= %f)) discard;", pass.AlphaRejectionValue);
139  default:
140  assert(false);
141  return StdStrBuf();
142  }
143  }
144 
145  // Simple helper function
146  inline GLenum OgreBlendTypeToGL(StdMeshMaterialPass::SceneBlendType blend)
147  {
148  switch(blend)
149  {
150  case StdMeshMaterialPass::SB_One: return GL_ONE;
151  case StdMeshMaterialPass::SB_Zero: return GL_ZERO;
152  case StdMeshMaterialPass::SB_DestColor: return GL_DST_COLOR;
153  case StdMeshMaterialPass::SB_SrcColor: return GL_SRC_COLOR;
154  case StdMeshMaterialPass::SB_OneMinusDestColor: return GL_ONE_MINUS_DST_COLOR;
155  case StdMeshMaterialPass::SB_OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR;
156  case StdMeshMaterialPass::SB_DestAlpha: return GL_DST_ALPHA;
157  case StdMeshMaterialPass::SB_SrcAlpha: return GL_SRC_ALPHA;
158  case StdMeshMaterialPass::SB_OneMinusDestAlpha: return GL_ONE_MINUS_DST_ALPHA;
159  case StdMeshMaterialPass::SB_OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA;
160  default: assert(false); return GL_ZERO;
161  }
162  }
163 
164  StdStrBuf GetVertexShaderCodeForPass(const StdMeshMaterialPass& pass, bool LowMaxVertexUniformCount)
165  {
166  StdStrBuf buf;
167 
168  if (!::GraphicsResource.Files.LoadEntryString("MeshVertexShader.glsl", &buf))
169  {
170  // Fall back just in case
171  buf.Copy(
172  "attribute vec3 oc_Position;\n"
173  "attribute vec3 oc_Normal;\n"
174  "attribute vec2 oc_TexCoord;\n"
175  "varying vec3 vtxNormal;\n"
176  "varying vec2 texcoord;\n"
177  "uniform mat4 projectionMatrix;\n"
178  "uniform mat4 modelviewMatrix;\n"
179  "uniform mat3 normalMatrix;\n"
180  "\n"
181  "slice(position)\n"
182  "{\n"
183  " gl_Position = projectionMatrix * modelviewMatrix * vec4(oc_Position, 1.0);\n"
184  "}\n"
185  "\n"
186  "slice(texcoord)\n"
187  "{\n"
188  " texcoord = oc_TexCoord;\n"
189  "}\n"
190  "\n"
191  "slice(normal)\n"
192  "{\n"
193  " vtxNormal = normalize(normalMatrix * oc_Normal);\n"
194  "}\n"
195  );
196  }
197 
198  if (pGL->Workarounds.ForceSoftwareTransform)
199  buf.Take(StdStrBuf("#define OC_WA_FORCE_SOFTWARE_TRANSFORM\n") + buf);
200 
201  if (LowMaxVertexUniformCount)
202  return StdStrBuf("#define OC_WA_LOW_MAX_VERTEX_UNIFORM_COMPONENTS\n") + buf;
203  else
204  return buf;
205  }
206 
207  // Note this only gets the code which inserts the slices specific for the pass
208  // -- other slices are independent from this!
209  StdStrBuf GetFragmentShaderCodeForPass(const StdMeshMaterialPass& pass, StdMeshMaterialShaderParameters& params)
210  {
211  StdStrBuf buf;
212 
213  // Produce the fragment shader... first we create one code fragment for each
214  // texture unit, and we count the number of active textures, i.e. texture
215  // units that actually use a texture.
216  unsigned int texIndex = 0;
217  StdStrBuf textureUnitCode(""), textureUnitDeclCode("");
218  for(unsigned int i = 0; i < pass.TextureUnits.size(); ++i)
219  {
220  const StdMeshMaterialTextureUnit& texunit = pass.TextureUnits[i];
221  textureUnitCode.Append(TextureUnitToCode(texIndex, texunit));
222 
223  if(texunit.HasTexture())
224  {
225  textureUnitDeclCode.Append(FormatString("uniform sampler2D oc_Texture%u;\n", texIndex).getData());
226  params.AddParameter(FormatString("oc_Texture%u", texIndex).getData(), StdMeshMaterialShaderParameter::INT).GetInt() = texIndex;
227 
228  // If the texture unit has texture coordinate transformations,
229  // add a corresponding texture matrix uniform.
230  if(texunit.HasTexCoordAnimation())
231  {
232  textureUnitDeclCode.Append(FormatString("uniform mat4 oc_TextureMatrix%u;\n", texIndex).getData());
233  params.AddParameter(FormatString("oc_TextureMatrix%u", texIndex).getData(), StdMeshMaterialShaderParameter::AUTO_TEXTURE_MATRIX).GetInt() = texIndex;
234  }
235 
236  ++texIndex;
237  }
238  }
239 
240  return FormatString(
241  "%s\n" // Texture units with active textures, only if >0 texture units
242  "uniform vec3 oc_PlayerColor;\n" // This needs to be in-sync with the naming in StdMeshMaterialProgram::CompileShader()
243  "\n"
244  "slice(texture)\n"
245  "{\n"
246  " vec4 diffuse = fragColor;\n"
247  " vec4 currentColor = diffuse;\n"
248  " %s\n"
249  " fragColor = currentColor;\n"
250  "}\n"
251  "\n"
252  "slice(finish)\n"
253  "{\n"
254  " %s\n"
255  "}\n",
256  textureUnitDeclCode.getData(),
257  textureUnitCode.getData(),
258  AlphaTestToCode(pass).getData()
259  );
260  }
261 
262  StdStrBuf GetSHA1HexDigest(const char* text, std::size_t len)
263  {
264  sha1 ctx;
265  ctx.process_bytes(text, len);
266  unsigned int digest[5];
267  ctx.get_digest(digest);
268 
269  return FormatString("%08x%08x%08x%08x%08x", digest[0], digest[1], digest[2], digest[3], digest[4]);
270  }
271 } // anonymous namespace
272 
274 {
275  // TODO: If a technique is not available, show an error message what the problem is
276 
277  // select context, if not already done
278  if (!pCurrCtx) return false;
279 
280  for (unsigned int i = 0; i < mat.Techniques.size(); ++i)
281  {
282  StdMeshMaterialTechnique& technique = mat.Techniques[i];
283  technique.Available = true;
284  for (unsigned int j = 0; j < technique.Passes.size(); ++j)
285  {
286  StdMeshMaterialPass& pass = technique.Passes[j];
287 
288  GLint max_texture_units;
289  glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
290 
291  // OpenGL 3.x guarantees at least 16 TIUs. If the above returns
292  // less it's probably a driver bug. So just keep going.
293  assert(max_texture_units >= 16);
294  max_texture_units = std::min<GLint>(max_texture_units, 16);
295 
296  unsigned int active_texture_units = 0;
297  for(unsigned int k = 0; k < pass.TextureUnits.size(); ++k)
298  if(pass.TextureUnits[k].HasTexture())
299  ++active_texture_units;
300 
301  if (active_texture_units > static_cast<unsigned int>(max_texture_units))
302  technique.Available = false;
303 
304  for (unsigned int k = 0; k < pass.TextureUnits.size(); ++k)
305  {
306  StdMeshMaterialTextureUnit& texunit = pass.TextureUnits[k];
307  for (unsigned int l = 0; l < texunit.GetNumTextures(); ++l)
308  {
309  const C4TexRef& texture = texunit.GetTexture(l);
310  glBindTexture(GL_TEXTURE_2D, texture.texName);
311  switch (texunit.TexAddressMode)
312  {
314  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
315  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
316  break;
318  glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, texunit.TexBorderColor);
319  // fallthrough
321  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
322  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
323  break;
325  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
326  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
327  break;
328  }
329 
330  switch (texunit.Filtering[0]) // min
331  {
333  technique.Available = false;
334  break;
336  switch (texunit.Filtering[2]) // mip
337  {
339  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
340  break;
342  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
343  break;
345  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
346  break;
348  technique.Available = false; // invalid
349  break;
350  }
351  break;
353  switch (texunit.Filtering[2]) // mip
354  {
356  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
357  break;
359  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
360  break;
362  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
363  break;
365  technique.Available = false; // invalid
366  break;
367  }
368  break;
370  // unsupported
371  technique.Available = false;
372  break;
373  }
374 
375  switch (texunit.Filtering[1]) // max
376  {
378  technique.Available = false; // invalid
379  break;
381  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
382  break;
384  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
385  break;
387  // unsupported
388  technique.Available = false;
389  break;
390  }
391  } // loop over textures
392  } // loop over texture units
393 
394  // Create fragment and/or vertex shader
395  // if a custom shader is not provided.
396  // Re-use existing programs if the generated
397  // code is the same (determined by SHA1 hash).
398  bool custom_shader = true;
399  if(!pass.VertexShader.Shader)
400  {
401  StdStrBuf buf = GetVertexShaderCodeForPass(pass, Workarounds.LowMaxVertexUniformCount);
402  StdStrBuf hash = GetSHA1HexDigest(buf.getData(), buf.getLength());
403  pass.VertexShader.Shader = mat_manager.AddShader("auto-generated vertex shader", hash.getData(), "glsl", SMMS_VERTEX, buf.getData(), StdMeshMatManager::SMM_AcceptExisting);
404  custom_shader = false;
405  }
406 
407  if(!pass.FragmentShader.Shader)
408  {
409  // TODO: Should use shared_params once we introduce them
410  StdStrBuf buf = GetFragmentShaderCodeForPass(pass, pass.FragmentShader.Parameters);
411  StdStrBuf hash = GetSHA1HexDigest(buf.getData(), buf.getLength());
412  pass.FragmentShader.Shader = mat_manager.AddShader("auto-generated fragment shader", hash.getData(), "glsl", SMMS_FRAGMENT, buf.getData(), StdMeshMatManager::SMM_AcceptExisting);
413  }
414 
415  // Then, link the program, and resolve parameter locations
416  StdStrBuf name(FormatString("%s:%s:%s", mat.Name.getData(), technique.Name.getData(), pass.Name.getData()));
417  const StdMeshMaterialProgram* added_program = mat_manager.AddProgram(name.getData(), loader, pass.FragmentShader, pass.VertexShader, pass.GeometryShader);
418  if(!added_program)
419  {
420  // If the program could not be compiled, try again with the LowMaxVertexUniformCount workaround.
421  // See bug #1368.
422  if (!custom_shader && !Workarounds.LowMaxVertexUniformCount)
423  {
424  StdStrBuf buf = GetVertexShaderCodeForPass(pass, true);
425  StdStrBuf hash = GetSHA1HexDigest(buf.getData(), buf.getLength());
426  pass.VertexShader.Shader = mat_manager.AddShader("auto-generated vertex shader", hash.getData(), "glsl", SMMS_VERTEX, buf.getData(), StdMeshMatManager::SMM_AcceptExisting);
427 
428  added_program = mat_manager.AddProgram(name.getData(), loader, pass.FragmentShader, pass.VertexShader, pass.GeometryShader);
429  if(added_program)
430  {
431  // If this actually work, cache the result, so we don't
432  // need to fail again next time before trying the workaround.
433  Workarounds.LowMaxVertexUniformCount = true;
434  Log(" gl: Enabling low max vertex uniform workaround");
435  }
436  }
437  }
438 
439  if (!added_program)
440  {
441  technique.Available = false;
442  }
443  else
444  {
445  std::unique_ptr<StdMeshMaterialPass::ProgramInstance> program_instance(new StdMeshMaterialPass::ProgramInstance(added_program, &pass.FragmentShader, &pass.VertexShader, &pass.GeometryShader));
446  pass.Program = std::move(program_instance);
447  }
448  }
449 
450  if (technique.Available && mat.BestTechniqueIndex == -1)
451  mat.BestTechniqueIndex = i;
452  }
453 
454  return mat.BestTechniqueIndex != -1;
455 }
456 
457 // TODO: We should add a class, C4MeshRenderer, which contains all the functions
458 // in this namespace, and avoids passing around so many parameters.
459 namespace
460 {
461  // Apply Zoom and Transformation to the current matrix stack. Return
462  // parity of the transformation.
463  bool ApplyZoomAndTransform(float ZoomX, float ZoomY, float Zoom, C4BltTransform* pTransform, StdProjectionMatrix& projection)
464  {
465  // Apply zoom
466  Translate(projection, ZoomX, ZoomY, 0.0f);
467  Scale(projection, Zoom, Zoom, 1.0f);
468  Translate(projection, -ZoomX, -ZoomY, 0.0f);
469 
470  // Apply transformation
471  if (pTransform)
472  {
473  StdProjectionMatrix transform;
474  transform(0, 0) = pTransform->mat[0];
475  transform(0, 1) = pTransform->mat[1];
476  transform(0, 2) = 0.0f;
477  transform(0, 3) = pTransform->mat[2];
478  transform(1, 0) = pTransform->mat[3];
479  transform(1, 1) = pTransform->mat[4];
480  transform(1, 2) = 0.0f;
481  transform(1, 3) = pTransform->mat[5];
482  transform(2, 0) = 0.0f;
483  transform(2, 1) = 0.0f;
484  transform(2, 2) = 1.0f;
485  transform(2, 3) = 0.0f;
486  transform(3, 0) = pTransform->mat[6];
487  transform(3, 1) = pTransform->mat[7];
488  transform(3, 2) = 0.0f;
489  transform(3, 3) = pTransform->mat[8];
490  projection *= transform;
491 
492  // Compute parity of the transformation matrix - if parity is swapped then
493  // we need to cull front faces instead of back faces.
494  const float det = transform(0,0)*transform(1,1)*transform(3,3)
495  + transform(1,0)*transform(3,1)*transform(0,3)
496  + transform(3,0)*transform(0,1)*transform(1,3)
497  - transform(0,0)*transform(3,1)*transform(1,3)
498  - transform(1,0)*transform(0,1)*transform(3,3)
499  - transform(3,0)*transform(1,1)*transform(0,3);
500 
501  return det > 0;
502  }
503 
504  return true;
505  }
506 
507  void SetStandardUniforms(C4ShaderCall& call, DWORD dwModClr, DWORD dwPlayerColor, DWORD dwBlitMode, bool cullFace, const C4FoWRegion* pFoW, const C4Rect& clipRect, const C4Rect& outRect)
508  {
509  // Draw transform
510  const float fMod[4] = {
511  ((dwModClr >> 16) & 0xff) / 255.0f,
512  ((dwModClr >> 8) & 0xff) / 255.0f,
513  ((dwModClr ) & 0xff) / 255.0f,
514  ((dwModClr >> 24) & 0xff) / 255.0f
515  };
516  call.SetUniform4fv(C4SSU_ClrMod, 1, fMod);
518 
519  // Player color
520  const float fPlrClr[3] = {
521  ((dwPlayerColor >> 16) & 0xff) / 255.0f,
522  ((dwPlayerColor >> 8) & 0xff) / 255.0f,
523  ((dwPlayerColor ) & 0xff) / 255.0f,
524  };
525  call.SetUniform3fv(C4SSU_OverlayClr, 1, fPlrClr);
526 
527  // Backface culling flag
528  call.SetUniform1f(C4SSU_CullMode, cullFace ? 0.0f : 1.0f);
529 
530  // Dynamic light
531  if(pFoW != nullptr)
532  {
534  glBindTexture(GL_TEXTURE_2D, pFoW->getSurfaceName());
535  float lightTransform[6];
536  pFoW->GetFragTransform(clipRect, outRect, lightTransform);
537  call.SetUniformMatrix2x3fv(C4SSU_LightTransform, 1, lightTransform);
538 
540  glBindTexture(GL_TEXTURE_2D, pFoW->getFoW()->Ambient.Tex);
542  float ambientTransform[6];
543  pFoW->getFoW()->Ambient.GetFragTransform(pFoW->getViewportRegion(), clipRect, outRect, ambientTransform);
544  call.SetUniformMatrix2x3fv(C4SSU_AmbientTransform, 1, ambientTransform);
545  }
546 
547  // Current frame counter
549  }
550 
551  bool ResolveAutoParameter(C4ShaderCall& call, StdMeshMaterialShaderParameter& parameter, StdMeshMaterialShaderParameter::Auto value, DWORD dwModClr, DWORD dwPlayerColor, DWORD dwBlitMode, const C4FoWRegion* pFoW, const C4Rect& clipRect)
552  {
553  // There are no auto parameters implemented yet
554  assert(false);
555  return false;
556  }
557 
558  StdProjectionMatrix ResolveAutoTextureMatrix(const StdSubMeshInstance& instance, const StdMeshMaterialTechnique& technique, unsigned int passIndex, unsigned int texUnitIndex)
559  {
560  assert(passIndex < technique.Passes.size());
561  const StdMeshMaterialPass& pass = technique.Passes[passIndex];
562 
563  assert(texUnitIndex < pass.TextureUnits.size());
564  const StdMeshMaterialTextureUnit& texunit = pass.TextureUnits[texUnitIndex];
565 
566  StdProjectionMatrix matrix = StdProjectionMatrix::Identity();
567  const double Position = instance.GetTexturePosition(passIndex, texUnitIndex);
568 
569  for (unsigned int k = 0; k < texunit.Transformations.size(); ++k)
570  {
571  const StdMeshMaterialTextureUnit::Transformation& trans = texunit.Transformations[k];
572  StdProjectionMatrix temp_matrix;
573  switch (trans.TransformType)
574  {
576  Translate(matrix, trans.Scroll.X, trans.Scroll.Y, 0.0f);
577  break;
579  Translate(matrix, trans.GetScrollX(Position), trans.GetScrollY(Position), 0.0f);
580  break;
582  Rotate(matrix, trans.Rotate.Angle, 0.0f, 0.0f, 1.0f);
583  break;
585  Rotate(matrix, trans.GetRotate(Position), 0.0f, 0.0f, 1.0f);
586  break;
588  Scale(matrix, trans.Scale.X, trans.Scale.Y, 1.0f);
589  break;
591  for (int i = 0; i < 16; ++i)
592  temp_matrix(i / 4, i % 4) = trans.Transform.M[i];
593  matrix *= temp_matrix;
594  break;
596  switch (trans.WaveXForm.XForm)
597  {
599  Translate(matrix, trans.GetWaveXForm(Position), 0.0f, 0.0f);
600  break;
602  Translate(matrix, 0.0f, trans.GetWaveXForm(Position), 0.0f);
603  break;
605  Rotate(matrix, trans.GetWaveXForm(Position), 0.0f, 0.0f, 1.0f);
606  break;
608  Scale(matrix, trans.GetWaveXForm(Position), 1.0f, 1.0f);
609  break;
611  Scale(matrix, 1.0f, trans.GetWaveXForm(Position), 1.0f);
612  break;
613  }
614  break;
615  }
616  }
617 
618  return matrix;
619  }
620 
621  struct BoneTransform
622  {
623  float m[3][4];
624  };
625 
626  std::vector<BoneTransform> CookBoneTransforms(const StdMeshInstance& mesh_instance)
627  {
628  // Cook the bone transform matrixes into something that OpenGL can use. This could be moved into RenderMeshImpl.
629  // Or, even better, we could upload them into a UBO, but Intel doesn't support them prior to Sandy Bridge.
630 
631  std::vector<BoneTransform> bones;
632  if (mesh_instance.GetBoneCount() == 0)
633  {
634 #pragma clang diagnostic ignored "-Wmissing-braces"
635  // Upload dummy bone so we don't have to do branching in the vertex shader
636  static const BoneTransform dummy_bone = {
637  1.0f, 0.0f, 0.0f, 0.0f,
638  0.0f, 1.0f, 0.0f, 0.0f,
639  0.0f, 0.0f, 1.0f, 0.0f
640  };
641  bones.push_back(dummy_bone);
642  }
643  else
644  {
645  bones.reserve(mesh_instance.GetBoneCount());
646  for (size_t bone_index = 0; bone_index < mesh_instance.GetBoneCount(); ++bone_index)
647  {
648  const StdMeshMatrix &bone = mesh_instance.GetBoneTransform(bone_index);
649  BoneTransform cooked_bone = {
650  bone(0, 0), bone(0, 1), bone(0, 2), bone(0, 3),
651  bone(1, 0), bone(1, 1), bone(1, 2), bone(1, 3),
652  bone(2, 0), bone(2, 1), bone(2, 2), bone(2, 3)
653  };
654  bones.push_back(cooked_bone);
655  }
656  }
657  return bones;
658  }
659 
660  struct PretransformedMeshVertex
661  {
662  float nx, ny, nz;
663  float x, y, z;
664  };
665 
666  void PretransformMeshVertex(PretransformedMeshVertex *out, const StdMeshVertex &in, const StdMeshInstance &mesh_instance)
667  {
668  // If the first bone assignment has a weight of 0, all others are zero
669  // as well, or the loader would have overwritten the assignment
670  if (in.bone_weight[0] == 0.0f || mesh_instance.GetBoneCount() == 0)
671  {
672  out->x = in.x;
673  out->y = in.y;
674  out->z = in.z;
675  out->nx = in.nx;
676  out->ny = in.ny;
677  out->nz = in.nz;
678  }
679  else
680  {
681  PretransformedMeshVertex vtx{ 0, 0, 0, 0, 0, 0 };
682  for (size_t i = 0; i < StdMeshVertex::MaxBoneWeightCount && in.bone_weight[i] > 0; ++i)
683  {
684  float weight = in.bone_weight[i];
685  const auto &bone = mesh_instance.GetBoneTransform(in.bone_index[i]);
686  auto vertex = weight * (bone * in);
687  vtx.nx += vertex.nx;
688  vtx.ny += vertex.ny;
689  vtx.nz += vertex.nz;
690  vtx.x += vertex.x;
691  vtx.y += vertex.y;
692  vtx.z += vertex.z;
693  }
694  *out = vtx;
695  }
696  }
697 
698  void PretransformMeshVertices(const StdMeshInstance &mesh_instance, const StdSubMeshInstance& instance, GLuint vbo)
699  {
700  assert(pGL->Workarounds.ForceSoftwareTransform);
701  glBindBuffer(GL_ARRAY_BUFFER, vbo);
702 
703  const auto &original_vertices = mesh_instance.GetSharedVertices().empty() ? instance.GetSubMesh().GetVertices() : mesh_instance.GetSharedVertices();
704  const size_t vertex_count = original_vertices.size();
705 
706  // Unmapping the buffer may fail for certain reasons, in which case we need to try again.
707  do
708  {
709  glBufferData(GL_ARRAY_BUFFER, vertex_count * sizeof(PretransformedMeshVertex), nullptr, GL_STREAM_DRAW);
710  void *map = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
711  PretransformedMeshVertex *buffer = new (map) PretransformedMeshVertex[vertex_count];
712 
713  for (size_t i = 0; i < vertex_count; ++i)
714  {
715  PretransformMeshVertex(&buffer[i], original_vertices[i], mesh_instance);
716  }
717  } while (glUnmapBuffer(GL_ARRAY_BUFFER) == GL_FALSE);
718  // Unbind the buffer so following rendering calls do not use it
719  glBindBuffer(GL_ARRAY_BUFFER, 0);
720  }
721 
722  void RenderSubMeshImpl(const StdProjectionMatrix& projectionMatrix, const StdMeshMatrix& modelviewMatrix, const StdMeshInstance& mesh_instance, const StdSubMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, const C4Rect& clipRect, const C4Rect& outRect, bool parity)
723  {
724  // Don't render with degenerate matrix
725  if (fabs(modelviewMatrix.Determinant()) < 1e-6)
726  return;
727 
728  const StdMeshMaterial& material = instance.GetMaterial();
729  assert(material.BestTechniqueIndex != -1);
730  const StdMeshMaterialTechnique& technique = material.Techniques[material.BestTechniqueIndex];
731 
732  bool using_shared_vertices = instance.GetSubMesh().GetVertices().empty();
733  GLuint vbo = mesh_instance.GetMesh().GetVBO();
734  GLuint ibo = mesh_instance.GetIBO();
735  unsigned int vaoid = mesh_instance.GetVAOID();
736  size_t vertex_buffer_offset = using_shared_vertices ? 0 : instance.GetSubMesh().GetOffsetInVBO();
737  size_t index_buffer_offset = instance.GetSubMesh().GetOffsetInIBO(); // note this is constant
738 
739  const bool ForceSoftwareTransform = pGL->Workarounds.ForceSoftwareTransform;
740  GLuint pretransform_vbo;
741 
742  std::vector<BoneTransform> bones;
743  if (!ForceSoftwareTransform)
744  {
745  bones = CookBoneTransforms(mesh_instance);
746  }
747  else
748  {
749  glGenBuffers(1, &pretransform_vbo);
750  PretransformMeshVertices(mesh_instance, instance, pretransform_vbo);
751  }
752  // Modelview matrix does not change between passes, so cache it here
753  const StdMeshMatrix normalMatrixTranspose = StdMeshMatrix::Inverse(modelviewMatrix);
754 
755  // Render each pass
756  for (unsigned int i = 0; i < technique.Passes.size(); ++i)
757  {
758  const StdMeshMaterialPass& pass = technique.Passes[i];
759 
760  if (!pass.DepthCheck)
761  glDisable(GL_DEPTH_TEST);
762 
763  glDepthMask(pass.DepthWrite ? GL_TRUE : GL_FALSE);
764 
765  if (pass.AlphaToCoverage)
766  glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
767  else
768  glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
769 
770  glFrontFace(parity ? GL_CW : GL_CCW);
771  if (mesh_instance.GetCompletion() < 1.0f)
772  {
773  // Backfaces might be visible when completion is < 1.0f since front
774  // faces might be omitted.
775  glDisable(GL_CULL_FACE);
776  }
777  else
778  {
779  switch (pass.CullHardware)
780  {
782  glEnable(GL_CULL_FACE);
783  glCullFace(GL_BACK);
784  break;
786  glEnable(GL_CULL_FACE);
787  glCullFace(GL_FRONT);
788  break;
790  glDisable(GL_CULL_FACE);
791  break;
792  }
793  }
794 
795  // Overwrite blend mode with default alpha blending when alpha in clrmod
796  // is <255. This makes sure that normal non-blended meshes can have
797  // blending disabled in their material script (which disables expensive
798  // face ordering) but when they are made translucent via clrmod
799  if (!(dwBlitMode & C4GFXBLIT_ADDITIVE))
800  {
801  if (((dwModClr >> 24) & 0xff) < 0xff) // && (!(dwBlitMode & C4GFXBLIT_MOD2)) )
802  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
803  else
804  glBlendFunc(OgreBlendTypeToGL(pass.SceneBlendFactors[0]),
805  OgreBlendTypeToGL(pass.SceneBlendFactors[1]));
806  }
807  else
808  {
809  if (((dwModClr >> 24) & 0xff) < 0xff) // && (!(dwBlitMode & C4GFXBLIT_MOD2)) )
810  glBlendFunc(GL_SRC_ALPHA, GL_ONE);
811  else
812  glBlendFunc(OgreBlendTypeToGL(pass.SceneBlendFactors[0]), GL_ONE);
813  }
814 
815  assert(pass.Program.get() != nullptr);
816 
817  // Upload all parameters to the shader
818  int ssc = 0;
819  if (dwBlitMode & C4GFXBLIT_MOD2) ssc |= C4SSC_MOD2;
820  if (pFoW != nullptr) ssc |= C4SSC_LIGHT;
821  const C4Shader* shader = pass.Program->Program->GetShader(ssc);
822  if (!shader) return;
823  C4ShaderCall call(shader);
824  call.Start();
825 
826  // Upload projection, modelview and normal matrices
827  call.SetUniformMatrix4x4(C4SSU_ProjectionMatrix, projectionMatrix);
828  call.SetUniformMatrix4x4(C4SSU_ModelViewMatrix, modelviewMatrix);
829  call.SetUniformMatrix3x3Transpose(C4SSU_NormalMatrix, normalMatrixTranspose);
830 
831 
832  // Upload material properties
838 
839  // Upload the current bone transformation matrixes (if there are any)
840  if (!ForceSoftwareTransform)
841  {
842  if (!bones.empty())
843  {
844  if (pGL->Workarounds.LowMaxVertexUniformCount)
845  glUniformMatrix3x4fv(shader->GetUniform(C4SSU_Bones), bones.size(), GL_FALSE, &bones[0].m[0][0]);
846  else
847  glUniformMatrix4x3fv(shader->GetUniform(C4SSU_Bones), bones.size(), GL_TRUE, &bones[0].m[0][0]);
848  }
849  }
850 
851  GLuint vao;
852  const bool has_vao = pGL->GetVAO(vaoid, vao);
853  glBindVertexArray(vao);
854  if (!has_vao)
855  {
856  // Bind the vertex data of the mesh
857  // Note this relies on the fact that all vertex
858  // attributes for all shaders are at the same
859  // locations.
860  // TODO: And this fails if the mesh changes
861  // from a material with texture to one without
862  // or vice versa.
863  glBindBuffer(GL_ARRAY_BUFFER, vbo);
864  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
865 #define VERTEX_OFFSET(field) reinterpret_cast<const uint8_t *>(offsetof(StdMeshVertex, field))
866  if (shader->GetAttribute(C4SSA_TexCoord) != -1)
867  glVertexAttribPointer(shader->GetAttribute(C4SSA_TexCoord), 2, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(u));
868  if (!ForceSoftwareTransform)
869  {
870  glVertexAttribPointer(shader->GetAttribute(C4SSA_Position), 3, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(x));
871  glVertexAttribPointer(shader->GetAttribute(C4SSA_Normal), 3, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(nx));
872  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_Position));
873  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_Normal));
874 
875  glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneWeights0), 4, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(bone_weight));
876  glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneWeights1), 4, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(bone_weight) + 4 * sizeof(std::remove_all_extents<decltype(StdMeshVertex::bone_weight)>::type));
877  glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneIndices0), 4, GL_SHORT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(bone_index));
878  glVertexAttribPointer(shader->GetAttribute(C4SSA_BoneIndices1), 4, GL_SHORT, GL_FALSE, sizeof(StdMeshVertex), VERTEX_OFFSET(bone_index) + 4 * sizeof(std::remove_all_extents<decltype(StdMeshVertex::bone_index)>::type));
879  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneWeights0));
880  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneWeights1));
881  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneIndices0));
882  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_BoneIndices1));
883  }
884  if (shader->GetAttribute(C4SSA_TexCoord) != -1)
885  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_TexCoord));
886 
887 #undef VERTEX_OFFSET
888  }
889 
890  if (ForceSoftwareTransform)
891  {
892  glBindBuffer(GL_ARRAY_BUFFER, pretransform_vbo);
893 #define VERTEX_OFFSET(field) reinterpret_cast<const uint8_t *>(offsetof(PretransformedMeshVertex, field))
894  glVertexAttribPointer(shader->GetAttribute(C4SSA_Position), 3, GL_FLOAT, GL_FALSE, sizeof(PretransformedMeshVertex), VERTEX_OFFSET(x));
895  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_Position));
896  glVertexAttribPointer(shader->GetAttribute(C4SSA_Normal), 3, GL_FLOAT, GL_FALSE, sizeof(PretransformedMeshVertex), VERTEX_OFFSET(nx));
897  glEnableVertexAttribArray(shader->GetAttribute(C4SSA_Normal));
898 #undef VERTEX_OFFSET
899  glBindBuffer(GL_ARRAY_BUFFER, 0);
900  }
901 
902  // Bind textures
903  for (unsigned int j = 0; j < pass.TextureUnits.size(); ++j)
904  {
905  const StdMeshMaterialTextureUnit& texunit = pass.TextureUnits[j];
906  if (texunit.HasTexture())
907  {
908  call.AllocTexUnit(-1);
909  const unsigned int Phase = instance.GetTexturePhase(i, j);
910  glBindTexture(GL_TEXTURE_2D, texunit.GetTexture(Phase).texName);
911  }
912  }
913 
914  // Set uniforms and instance parameters
915  SetStandardUniforms(call, dwModClr, dwPlayerColor, dwBlitMode, pass.CullHardware != StdMeshMaterialPass::CH_None, pFoW, clipRect, outRect);
916  for(unsigned int j = 0; j < pass.Program->Parameters.size(); ++j)
917  {
918  const int uniform = pass.Program->Parameters[j].UniformIndex;
919  if(!shader->HaveUniform(uniform)) continue; // optimized out
920 
921  const StdMeshMaterialShaderParameter* parameter = pass.Program->Parameters[j].Parameter;
922 
923  StdMeshMaterialShaderParameter auto_resolved;
925  {
926  if(!ResolveAutoParameter(call, auto_resolved, parameter->GetAuto(), dwModClr, dwPlayerColor, dwBlitMode, pFoW, clipRect))
927  continue;
928  parameter = &auto_resolved;
929  }
930 
931  switch(parameter->GetType())
932  {
934  call.SetUniformMatrix4x4(uniform, ResolveAutoTextureMatrix(instance, technique, i, parameter->GetInt()));
935  break;
937  call.SetUniform1i(uniform, parameter->GetInt());
938  break;
940  call.SetUniform1f(uniform, parameter->GetFloat());
941  break;
943  call.SetUniform2fv(uniform, 1, parameter->GetFloatv());
944  break;
946  call.SetUniform3fv(uniform, 1, parameter->GetFloatv());
947  break;
949  call.SetUniform4fv(uniform, 1, parameter->GetFloatv());
950  break;
952  call.SetUniformMatrix4x4fv(uniform, 1, parameter->GetMatrix());
953  break;
954  default:
955  assert(false);
956  break;
957  }
958  }
959 
960  pDraw->scriptUniform.Apply(call);
961 
962  size_t vertex_count = 3 * instance.GetNumFaces();
963  assert (vertex_buffer_offset % sizeof(StdMeshVertex) == 0);
964  size_t base_vertex = vertex_buffer_offset / sizeof(StdMeshVertex);
965  glDrawElementsBaseVertex(GL_TRIANGLES, vertex_count, GL_UNSIGNED_INT, reinterpret_cast<void*>(index_buffer_offset), base_vertex);
966  glBindVertexArray(0);
967  call.Finish();
968 
969  if(!pass.DepthCheck)
970  glEnable(GL_DEPTH_TEST);
971  }
972 
973  if (ForceSoftwareTransform)
974  glDeleteBuffers(1, &pretransform_vbo);
975  }
976 
977  void RenderMeshImpl(const StdProjectionMatrix& projectionMatrix, const StdMeshMatrix& modelviewMatrix, StdMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, const C4Rect& clipRect, const C4Rect& outRect, bool parity); // Needed by RenderAttachedMesh
978 
979  void RenderAttachedMesh(const StdProjectionMatrix& projectionMatrix, const StdMeshMatrix& modelviewMatrix, StdMeshInstance::AttachedMesh* attach, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, const C4Rect& clipRect, const C4Rect& outRect, bool parity)
980  {
981  const StdMeshMatrix& FinalTrans = attach->GetFinalTransformation();
982 
983  // Take the player color from the C4Object, if the attached object is not a definition
984  // This is a bit unfortunate because it requires access to C4Object which is otherwise
985  // avoided in this code. It could be replaced by virtual function calls to StdMeshDenumerator
986  C4MeshDenumerator* denumerator = dynamic_cast<C4MeshDenumerator*>(attach->ChildDenumerator);
987  if(denumerator && denumerator->GetObject())
988  {
989  dwModClr = denumerator->GetObject()->ColorMod;
990  dwBlitMode = denumerator->GetObject()->BlitMode;
991  dwPlayerColor = denumerator->GetObject()->Color;
992  }
993 
994  // TODO: Take attach transform's parity into account
995  StdMeshMatrix newModelviewMatrix = modelviewMatrix * FinalTrans;
996  RenderMeshImpl(projectionMatrix, newModelviewMatrix, *attach->Child, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, outRect, parity);
997  }
998 
999  void RenderMeshImpl(const StdProjectionMatrix& projectionMatrix, const StdMeshMatrix& modelviewMatrix, StdMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, const C4Rect& clipRect, const C4Rect& outRect, bool parity)
1000  {
1001  const StdMesh& mesh = instance.GetMesh();
1002 
1003  // Render AM_DrawBefore attached meshes
1004  StdMeshInstance::AttachedMeshIter attach_iter = instance.AttachedMeshesBegin();
1005 
1006  for (; attach_iter != instance.AttachedMeshesEnd() && ((*attach_iter)->GetFlags() & StdMeshInstance::AM_DrawBefore); ++attach_iter)
1007  RenderAttachedMesh(projectionMatrix, modelviewMatrix, *attach_iter, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, outRect, parity);
1008 
1009  // Check if we should draw in wireframe or normal mode
1010  if(dwBlitMode & C4GFXBLIT_WIREFRAME)
1011  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1012 
1013  // Render each submesh
1014  for (unsigned int i = 0; i < mesh.GetNumSubMeshes(); ++i)
1015  RenderSubMeshImpl(projectionMatrix, modelviewMatrix, instance, instance.GetSubMeshOrdered(i), dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, outRect, parity);
1016 
1017  // reset old mode to prevent rendering errors
1018  if(dwBlitMode & C4GFXBLIT_WIREFRAME)
1019  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1020 
1021  // Render non-AM_DrawBefore attached meshes
1022  for (; attach_iter != instance.AttachedMeshesEnd(); ++attach_iter)
1023  RenderAttachedMesh(projectionMatrix, modelviewMatrix, *attach_iter, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, outRect, parity);
1024  }
1025 }
1026 
1027 void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, DWORD dwPlayerColor, C4BltTransform* pTransform)
1028 {
1029  // Field of View for perspective projection, in degrees
1030  static const float FOV = 60.0f;
1031  static const float TAN_FOV = tan(FOV / 2.0f / 180.0f * M_PI);
1032 
1033  // Check mesh transformation; abort when it is degenerate.
1034  bool mesh_transform_parity = false;
1035  if (MeshTransform)
1036  {
1037  const float det = MeshTransform->Determinant();
1038  if (fabs(det) < 1e-6)
1039  return;
1040  else if (det < 0.0f)
1041  mesh_transform_parity = true;
1042  }
1043 
1044  const StdMesh& mesh = instance.GetMesh();
1045 
1046  bool parity = false;
1047 
1048  // Convert bounding box to clonk coordinate system
1049  // (TODO: We should cache this, not sure where though)
1050  const StdMeshBox& box = mesh.GetBoundingBox();
1051  StdMeshVector v1, v2;
1052  v1.x = box.x1; v1.y = box.y1; v1.z = box.z1;
1053  v2.x = box.x2; v2.y = box.y2; v2.z = box.z2;
1054 
1055  // Vector from origin of mesh to center of mesh
1056  const StdMeshVector MeshCenter = (v1 + v2)/2.0f;
1057 
1058  glEnable(GL_DEPTH_TEST);
1059  glEnable(GL_BLEND); // TODO: Shouldn't this always be enabled? - blending does not work for meshes without this though.
1060 
1061  // TODO: We ignore the additive drawing flag for meshes but instead
1062  // set the blending mode of the corresponding material. I'm not sure
1063  // how the two could be combined.
1064  // TODO: Maybe they can be combined using a pixel shader which does
1065  // ftransform() and then applies colormod, additive and mod2
1066  // on the result (with alpha blending).
1067  //int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE;
1068  //glBlendFunc(GL_SRC_ALPHA, iAdditive ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
1069 
1070  // Mesh extents
1071  const float b = fabs(v2.x - v1.x)/2.0f;
1072  const float h = fabs(v2.y - v1.y)/2.0f;
1073  const float l = fabs(v2.z - v1.z)/2.0f;
1074 
1075  // Set up projection matrix first. We do transform and Zoom with the
1076  // projection matrix, so that lighting is applied to the untransformed/unzoomed
1077  // mesh.
1078  StdProjectionMatrix projectionMatrix;
1079  if (!fUsePerspective)
1080  {
1081  // Load the orthographic projection
1082  projectionMatrix = ProjectionMatrix;
1083 
1084  if (!ApplyZoomAndTransform(ZoomX, ZoomY, Zoom, pTransform, projectionMatrix))
1085  parity = !parity;
1086 
1087  // Scale so that the mesh fits in (tx,ty,twdt,thgt)
1088  const float rx = -std::min(v1.x,v2.x) / fabs(v2.x - v1.x);
1089  const float ry = -std::min(v1.y,v2.y) / fabs(v2.y - v1.y);
1090  const float dx = tx + rx*twdt;
1091  const float dy = ty + ry*thgt;
1092 
1093  // Scale so that Z coordinate is between -1 and 1, otherwise parts of
1094  // the mesh could be clipped away by the near or far clipping plane.
1095  // Note that this only works for the projection matrix, otherwise
1096  // lighting is screwed up.
1097 
1098  // This technique might also enable us not to clear the depth buffer
1099  // after every mesh rendering - we could simply scale the first mesh
1100  // of the scene so that it's Z coordinate is between 0 and 1, scale
1101  // the second mesh that it is between 1 and 2, and so on.
1102  // This of course requires an orthogonal projection so that the
1103  // meshes don't look distorted - if we should ever decide to use
1104  // a perspective projection we need to think of something different.
1105  // Take also into account that the depth is not linear but linear
1106  // in the logarithm (if I am not mistaken), so goes as 1/z
1107 
1108  // Don't scale by Z extents since mesh might be transformed
1109  // by MeshTransformation, so use GetBoundingRadius to be safe.
1110  // Note this still fails if mesh is scaled in Z direction or
1111  // there are attached meshes.
1112  const float scz = 1.0/(mesh.GetBoundingRadius());
1113 
1114  Translate(projectionMatrix, dx, dy, 0.0f);
1115  Scale(projectionMatrix, 1.0f, 1.0f, scz);
1116  }
1117  else
1118  {
1119  // Perspective projection. This code transforms the projected
1120  // 3D model into the target area.
1121  const C4Rect clipRect = GetClipRect();
1122  projectionMatrix = StdProjectionMatrix::Identity();
1123 
1124  // Back to GL device coordinates
1125  Translate(projectionMatrix, -1.0f, 1.0f, 0.0f);
1126  Scale(projectionMatrix, 2.0f/clipRect.Wdt, -2.0f/clipRect.Hgt, 1.0f);
1127 
1128  Translate(projectionMatrix, -clipRect.x, -clipRect.y, 0.0f);
1129  if (!ApplyZoomAndTransform(ZoomX, ZoomY, Zoom, pTransform, projectionMatrix))
1130  parity = !parity;
1131 
1132  // Move to target location and compensate for 1.0f aspect
1133  float ttx = tx, tty = ty, ttwdt = twdt, tthgt = thgt;
1134  if(twdt > thgt)
1135  {
1136  tty += (thgt-twdt)/2.0;
1137  tthgt = twdt;
1138  }
1139  else
1140  {
1141  ttx += (twdt-thgt)/2.0;
1142  ttwdt = thgt;
1143  }
1144 
1145  Translate(projectionMatrix, ttx, tty, 0.0f);
1146  Scale(projectionMatrix, ((float)ttwdt)/clipRect.Wdt, ((float)tthgt)/clipRect.Hgt, 1.0f);
1147 
1148  // Return to Clonk coordinate frame
1149  Scale(projectionMatrix, clipRect.Wdt/2.0, -clipRect.Hgt/2.0, 1.0f);
1150  Translate(projectionMatrix, 1.0f, -1.0f, 0.0f);
1151 
1152  // Fix for the case when we have different aspect ratios
1153  const float ta = twdt / thgt;
1154  const float ma = b / h;
1155  if(ta <= 1 && ta/ma <= 1)
1156  Scale(projectionMatrix, std::max(ta, ta/ma), std::max(ta, ta/ma), 1.0f);
1157  else if(ta >= 1 && ta/ma >= 1)
1158  Scale(projectionMatrix, std::max(1.0f/ta, ma/ta), std::max(1.0f/ta, ma/ta), 1.0f);
1159 
1160  // Apply perspective projection. After this, x and y range from
1161  // -1 to 1, and this is mapped into tx/ty/twdt/thgt by the above code.
1162  // Aspect is 1.0f which is changed above as well.
1163  Perspective(projectionMatrix, 1.0f/TAN_FOV, 1.0f, 0.1f, 100.0f);
1164  }
1165 
1166  // Now set up the modelview matrix
1167  StdMeshMatrix modelviewMatrix;
1168  if (fUsePerspective)
1169  {
1170  // Setup camera position so that the mesh with uniform transformation
1171  // fits well into a square target (without distortion).
1172  const float EyeR = l + std::max(b/TAN_FOV, h/TAN_FOV);
1173  const StdMeshVector Eye = StdMeshVector::Translate(MeshCenter.x, MeshCenter.y, MeshCenter.z + EyeR);
1174 
1175  // Up vector is unit vector in theta direction
1176  const StdMeshVector Up = StdMeshVector::Translate(0.0f, -1.0f, 0.0f);
1177 
1178  // Fix X axis (???)
1179  modelviewMatrix = StdMeshMatrix::Scale(-1.0f, 1.0f, 1.0f);
1180 
1181  // center on mesh's bounding box, so that the mesh is really in the center of the viewport
1182  modelviewMatrix *= StdMeshMatrix::LookAt(Eye, MeshCenter, Up);
1183  }
1184  else
1185  {
1186  modelviewMatrix = StdMeshMatrix::Identity();
1187  }
1188 
1189  // Apply mesh transformation matrix
1190  if (MeshTransform)
1191  {
1192  // Apply MeshTransformation (in the Mesh's coordinate system)
1193  modelviewMatrix *= *MeshTransform;
1194  // Keep track of parity
1195  if (mesh_transform_parity) parity = !parity;
1196  }
1197 
1198  DWORD dwModClr = BlitModulated ? BlitModulateClr : 0xffffffff;
1199 
1200  const C4Rect clipRect = GetClipRect();
1201  const C4Rect outRect = GetOutRect();
1202 
1203  RenderMeshImpl(projectionMatrix, modelviewMatrix, instance, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, outRect, parity);
1204 
1205  // Reset state
1206  //glActiveTexture(GL_TEXTURE0);
1207  glDepthMask(GL_TRUE);
1208  glDisable(GL_DEPTH_TEST);
1209  glDisable(GL_CULL_FACE);
1210  glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
1211 
1212  // TODO: glScissor, so that we only clear the area the mesh covered.
1213  glClear(GL_DEPTH_BUFFER_BIT);
1214 }
1215 
1216 #endif // USE_CONSOLE
TexAddressModeType TexAddressMode
const char * getData() const
Definition: StdBuf.h:450
float y2
Definition: StdMesh.h:149
float GetBoundingRadius() const
Definition: StdMesh.h:209
void SetUniform1i(int iUniform, int iX) const
Definition: C4Shader.h:210
const StdSubMesh & GetSubMesh() const
Definition: StdMesh.h:267
void Up(T &keys)
#define z
#define C4GFXBLIT_MOD2
Definition: C4Surface.h:29
size_t GetNumFaces() const
Definition: StdMesh.h:266
CStdGLCtx * pCurrCtx
Definition: C4DrawGL.h:179
GLint GetUniform(int iUniform) const
Definition: C4Shader.h:118
C4Game Game
Definition: C4Globals.cpp:52
const StdMeshMaterial & GetMaterial() const
Definition: StdMesh.h:272
C4ScriptUniform scriptUniform
Definition: C4Draw.h:100
const StdSubMeshInstance & GetSubMeshOrdered(size_t i) const
Definition: StdMesh.h:593
const std::vector< StdMeshVertex > & GetSharedVertices() const
Definition: StdMesh.h:551
float bone_weight[MaxBoneWeightCount]
Definition: StdMeshMath.h:44
#define b
const FLOAT_RECT & getViewportRegion() const
Definition: C4FoWRegion.h:52
StdMeshMaterialShaderParameters Parameters
ShaderInstance FragmentShader
ShaderInstance GeometryShader
float x2
Definition: StdMesh.h:149
float gammaOut[3]
Definition: C4Draw.h:98
double GetBrightness() const
Definition: C4FoWAmbient.h:56
#define C4GFXBLIT_ADDITIVE
Definition: C4Surface.h:28
float z1
Definition: StdMesh.h:148
BlendOpSourceType ColorOpSources[2]
void Apply(C4ShaderCall &call)
Definition: C4Shader.cpp:830
Definition: C4Rect.h:29
SceneBlendType SceneBlendFactors[2]
const C4FoW * getFoW() const
Definition: C4FoWRegion.h:50
Denumerator * ChildDenumerator
Definition: StdMesh.h:515
GLuint GetVBO() const
Definition: StdMesh.h:214
float z2
Definition: StdMesh.h:149
void SetUniformMatrix4x4fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:259
const StdMeshMaterialShader * Shader
StdMeshInstance * Child
Definition: StdMesh.h:513
bool fUsePerspective
Definition: C4Draw.h:114
unsigned int GetVAOID() const
Definition: StdMesh.h:628
C4GraphicsResource GraphicsResource
ShaderInstance VertexShader
size_t GetNumSubMeshes() const
Definition: StdMesh.h:201
const StdMeshMatrix * MeshTransform
Definition: C4Draw.h:113
int32_t Wdt
Definition: C4Rect.h:32
const StdMeshMatrix & GetBoneTransform(size_t i) const
Definition: StdMesh.cpp:1459
int32_t FrameCounter
Definition: C4Game.h:130
virtual bool PrepareMaterial(StdMeshMatManager &mat_manager, StdMeshMaterialLoader &loader, StdMeshMaterial &mat) override
int32_t y
Definition: C4Rect.h:32
AttachedMeshIter AttachedMeshesBegin() const
Definition: StdMesh.h:586
size_t GetNumTextures() const
void SetUniformMatrix3x3Transpose(int iUniform, const StdMeshMatrix &matrix)
Definition: C4Shader.h:273
float y1
Definition: StdMesh.h:148
void Take(char *pnData)
Definition: StdBuf.h:465
const float * GetMatrix() const
void Finish()
Definition: C4Shader.cpp:707
C4Object * GetObject()
uint32_t Color
Definition: C4Object.h:120
GLuint getSurfaceName() const
static StdMeshVector Translate(float dx, float dy, float dz)
Definition: StdMeshMath.cpp:44
#define C4GFXBLIT_WIREFRAME
Definition: C4Surface.h:32
size_t GetOffsetInIBO() const
Definition: StdMesh.h:176
GLuint GetIBO() const
Definition: StdMesh.h:627
StdMeshMaterialShaderParameter & AddParameter(const char *name, StdMeshMaterialShaderParameter::Type type)
C4Draw * pDraw
Definition: C4Draw.cpp:45
bool HasTexCoordAnimation() const
bool GetVAO(unsigned int vaoid, GLuint &vao)
Definition: C4DrawGL.cpp:1023
#define VERTEX_OFFSET(field)
const float * GetFloatv() const
BlendOpSourceType AlphaOpSources[2]
void SetUniformMatrix2x3fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:244
C4FoWAmbient Ambient
Definition: C4FoW.h:115
bool HaveUniform(int iUniform) const
Definition: C4Shader.h:123
CullHardwareType CullHardware
GLint GetAttribute(int iAttribute) const
Definition: C4Shader.h:128
AttachedMeshList::const_iterator AttachedMeshIter
Definition: StdMesh.h:546
const C4TexRef & GetTexture(unsigned int i) const
DWORD BlitModulateClr
Definition: C4Draw.h:109
unsigned int texName
Definition: C4Surface.h:157
int32_t x
Definition: C4Rect.h:32
std::shared_ptr< ProgramInstance > Program
const std::vector< Vertex > & GetVertices() const
Definition: StdMesh.h:165
float GetCompletion() const
Definition: StdMesh.h:556
uint16_t bone_index[MaxBoneWeightCount]
Definition: StdMeshMath.h:45
void SetUniform3fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:234
std::vector< StdMeshMaterialPass > Passes
struct CStdGL::@10 Workarounds
bool LoadEntryString(const char *szEntryName, StdStrBuf *rBuf)
Definition: C4GroupSet.cpp:227
std::vector< StdMeshMaterialTextureUnit > TextureUnits
virtual void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, DWORD dwPlayerColor, C4BltTransform *pTransform) override
uint32_t ColorMod
Definition: C4Object.h:161
void SetUniform1f(int iUniform, float gX) const
Definition: C4Shader.h:214
double GetTexturePosition(size_t pass, size_t texunit) const
Definition: StdMesh.h:270
static const size_t MaxBoneWeightCount
Definition: StdMeshMath.h:37
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
DepthFunctionType AlphaRejectionFunction
float x1
Definition: StdMesh.h:148
void GetFragTransform(const C4Rect &clipRect, const C4Rect &outRect, float lightTransform[6]) const
GLint AllocTexUnit(int iUniform)
Definition: C4Shader.cpp:670
C4Rect GetOutRect() const
Definition: C4Draw.cpp:742
StdCopyStrBuf Name
size_t GetOffsetInVBO() const
Definition: StdMesh.h:175
const StdMeshMaterialProgram * AddProgram(const char *name, StdMeshMaterialLoader &loader, const StdMeshMaterialPass::ShaderInstance &fragment_shader, const StdMeshMaterialPass::ShaderInstance &vertex_shader, const StdMeshMaterialPass::ShaderInstance &geometry_shader)
uint32_t BlitMode
Definition: C4Object.h:162
void SetUniformMatrix4x4(int iUniform, const StdMeshMatrix &matrix)
Definition: C4Shader.h:288
size_t getLength() const
Definition: StdBuf.h:453
std::vector< StdMeshMaterialTechnique > Techniques
const StdMeshMatrix & GetFinalTransformation() const
Definition: StdMesh.h:520
CStdGL * pGL
Definition: C4DrawGL.cpp:914
int32_t Hgt
Definition: C4Rect.h:32
const StdMesh & GetMesh() const
Definition: StdMesh.h:624
C4Rect GetClipRect() const
Definition: C4Draw.cpp:733
void Start()
Definition: C4Shader.cpp:693
unsigned int GetTexturePhase(size_t pass, size_t texunit) const
Definition: StdMesh.h:269
uint32_t DWORD
StdProjectionMatrix ProjectionMatrix
Definition: C4DrawGL.h:184
const StdMeshMaterialShader * AddShader(const char *filename, const char *name, const char *language, StdMeshMaterialShaderType type, const char *text, uint32_t load_flags)
void Copy()
Definition: StdBuf.h:475
bool BlitModulated
Definition: C4Draw.h:108
const StdMeshBox & GetBoundingBox() const
Definition: StdMesh.h:208
size_t GetBoneCount() const
Definition: StdMesh.cpp:1476
void GetFragTransform(const struct FLOAT_RECT &vpRect, const C4Rect &clipRect, const C4Rect &outRect, float ambientTransform[6]) const
void SetUniform2fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:230
void SetUniform4fv(int iUniform, int iLength, const float *pVals) const
Definition: C4Shader.h:238
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
AttachedMeshIter AttachedMeshesEnd() const
Definition: StdMesh.h:587