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