OpenClonk
StdMeshMaterial.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) 2009-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 #include "C4Include.h"
19 #include "lib/StdMeshMaterial.h"
20 
21 #include "lib/StdMeshUpdate.h"
22 #include "graphics/C4DrawGL.h"
23 
24 #ifdef WITH_GLIB
25 #include <glib.h>
26 #endif
27 
28 namespace
29 {
30  // String <-> Enum assocation
31  template<typename EnumType>
32  struct Enumerator
33  {
34  const char* Name;
35  EnumType Value;
36  };
37 
38  // Define a name for a sequence of enums
39  template<int Num, typename EnumType>
40  struct EnumeratorShortcut
41  {
42  const char* Name;
43  EnumType Values[Num];
44  };
45 
46  const Enumerator<StdMeshMaterialShaderParameter::Auto> ShaderParameterAutoEnumerators[] =
47  {
48  { nullptr, static_cast<StdMeshMaterialShaderParameter::Auto>(0) }
49  };
50 
51  const Enumerator<StdMeshMaterialTextureUnit::TexAddressModeType> TexAddressModeEnumerators[] =
52  {
57  { nullptr, static_cast<StdMeshMaterialTextureUnit::TexAddressModeType>(0) }
58  };
59 
60  const Enumerator<StdMeshMaterialTextureUnit::FilteringType> FilteringEnumerators[] =
61  {
66  { nullptr, static_cast<StdMeshMaterialTextureUnit::FilteringType>(0) }
67  };
68 
69  const EnumeratorShortcut<3, StdMeshMaterialTextureUnit::FilteringType> FilteringShortcuts[] =
70  {
73  { "trilinear", { StdMeshMaterialTextureUnit::F_Linear, StdMeshMaterialTextureUnit::F_Linear, StdMeshMaterialTextureUnit::F_Linear } },
74  { "anisotropic", { StdMeshMaterialTextureUnit::F_Anisotropic, StdMeshMaterialTextureUnit::F_Anisotropic, StdMeshMaterialTextureUnit::F_Linear } },
75  { nullptr, { static_cast<StdMeshMaterialTextureUnit::FilteringType>(0), static_cast<StdMeshMaterialTextureUnit::FilteringType>(0), static_cast<StdMeshMaterialTextureUnit::FilteringType>(0) } }
76  };
77 
78  const Enumerator<StdMeshMaterialTextureUnit::BlendOpType> BlendOpEnumerators[] =
79  {
84  { nullptr, static_cast<StdMeshMaterialTextureUnit::BlendOpType>(0) }
85  };
86 
87  const Enumerator<StdMeshMaterialTextureUnit::BlendOpExType> BlendOpExEnumerators[] =
88  {
98  { "blend_diffuse_alpha", StdMeshMaterialTextureUnit::BOX_BlendDiffuseAlpha },
99  { "blend_texture_alpha", StdMeshMaterialTextureUnit::BOX_BlendTextureAlpha },
100  { "blend_current_alpha", StdMeshMaterialTextureUnit::BOX_BlendCurrentAlpha },
103  { "blend_diffuse_colour", StdMeshMaterialTextureUnit::BOX_BlendDiffuseColor },
104  { nullptr, static_cast<StdMeshMaterialTextureUnit::BlendOpExType>(0) }
105  };
106 
107  const Enumerator<StdMeshMaterialTextureUnit::BlendOpSourceType> BlendOpSourceEnumerators[] =
108  {
109  { "src_current", StdMeshMaterialTextureUnit::BOS_Current },
110  { "src_texture", StdMeshMaterialTextureUnit::BOS_Texture },
111  { "src_diffuse", StdMeshMaterialTextureUnit::BOS_Diffuse },
112  { "src_specular", StdMeshMaterialTextureUnit::BOS_Specular },
113  { "src_player_color", StdMeshMaterialTextureUnit::BOS_PlayerColor },
114  { "src_player_colour", StdMeshMaterialTextureUnit::BOS_PlayerColor },
115  { "src_manual", StdMeshMaterialTextureUnit::BOS_Manual },
116  { nullptr, static_cast<StdMeshMaterialTextureUnit::BlendOpSourceType>(0) }
117  };
118 
119  const Enumerator<StdMeshMaterialTextureUnit::Transformation::XFormType> XFormTypeEnumerators[] =
120  {
126  { nullptr, static_cast<StdMeshMaterialTextureUnit::Transformation::XFormType>(0) }
127  };
128 
129  const Enumerator<StdMeshMaterialTextureUnit::Transformation::WaveType> WaveTypeEnumerators[] =
130  {
136  { nullptr, static_cast<StdMeshMaterialTextureUnit::Transformation::WaveType>(0) }
137  };
138 
139  const Enumerator<StdMeshMaterialPass::CullHardwareType> CullHardwareEnumerators[] =
140  {
141  { "clockwise", StdMeshMaterialPass::CH_Clockwise },
142  { "anticlockwise", StdMeshMaterialPass::CH_CounterClockwise },
143  { "none", StdMeshMaterialPass::CH_None },
144  { nullptr, static_cast<StdMeshMaterialPass::CullHardwareType>(0) }
145  };
146 
147  const Enumerator<StdMeshMaterialPass::SceneBlendType> SceneBlendEnumerators[] =
148  {
149  { "one", StdMeshMaterialPass::SB_One },
150  { "zero", StdMeshMaterialPass::SB_Zero },
151  { "dest_colour", StdMeshMaterialPass::SB_DestColor },
152  { "src_colour", StdMeshMaterialPass::SB_SrcColor },
153  { "one_minus_dest_colour", StdMeshMaterialPass::SB_OneMinusDestColor },
154  { "one_minus_src_colour", StdMeshMaterialPass::SB_OneMinusSrcColor },
155  { "dest_alpha", StdMeshMaterialPass::SB_DestAlpha },
156  { "src_alpha", StdMeshMaterialPass::SB_SrcAlpha },
157  { "one_minus_dest_alpha", StdMeshMaterialPass::SB_OneMinusDestAlpha },
158  { "one_minus_src_alpha", StdMeshMaterialPass::SB_OneMinusSrcAlpha },
159  { nullptr, static_cast<StdMeshMaterialPass::SceneBlendType>(0) }
160  };
161 
162  const EnumeratorShortcut<2, StdMeshMaterialPass::SceneBlendType> SceneBlendShortcuts[] =
163  {
168  { nullptr, { static_cast<StdMeshMaterialPass::SceneBlendType>(0), static_cast<StdMeshMaterialPass::SceneBlendType>(0) } }
169  };
170 
171  const Enumerator<StdMeshMaterialPass::DepthFunctionType> DepthFunctionEnumerators[] =
172  {
173  { "always_fail", StdMeshMaterialPass::DF_AlwaysFail },
174  { "always_pass", StdMeshMaterialPass::DF_AlwaysPass },
175  { "less", StdMeshMaterialPass::DF_Less },
176  { "less_equal", StdMeshMaterialPass::DF_LessEqual },
177  { "equal", StdMeshMaterialPass::DF_Equal },
178  { "not_equal", StdMeshMaterialPass::DF_NotEqual },
179  { "greater_equal", StdMeshMaterialPass::DF_GreaterEqual },
180  { "greater", StdMeshMaterialPass::DF_Greater },
181  { nullptr, static_cast<StdMeshMaterialPass::DepthFunctionType>(0) }
182  };
183 }
184 
185 StdMeshMaterialError::StdMeshMaterialError(const StdStrBuf& message, const char* file, unsigned int line)
186 {
187  Buf.Format("%s:%u: %s", file, line, message.getData());
188 }
189 
190 enum Token
191 {
197 };
198 
200 {
201 public:
202  StdMeshMaterialParserCtx(StdMeshMatManager& manager, const char* mat_script, const char* filename, StdMeshMaterialLoader& loader);
203 
204  void SkipWhitespace();
205  Token Peek(StdStrBuf& name);
206  Token Advance(StdStrBuf& name);
207  Token AdvanceNonEOF(StdStrBuf& name);
208  Token AdvanceRequired(StdStrBuf& name, Token expect);
209  Token AdvanceRequired(StdStrBuf& name, Token expect1, Token expect2);
210  int AdvanceInt();
211  bool AdvanceIntOptional(int& value);
212  float AdvanceFloat();
213  bool AdvanceFloatOptional(float& value);
214  void AdvanceColor(bool with_alpha, float Color[4]);
215  bool AdvanceBoolean();
216  template<typename EnumType> EnumType AdvanceEnum(const Enumerator<EnumType>* enumerators);
217  template<int Num, typename EnumType> void AdvanceEnums(const Enumerator<EnumType>* enumerators, EnumType enums[Num]);
218  template<int Num, typename EnumType> void AdvanceEnums(const Enumerator<EnumType>* enumerators, const EnumeratorShortcut<Num, EnumType>* shortcuts, EnumType enums[Num]);
219  void Error(const StdStrBuf& message);
220  void ErrorUnexpectedIdentifier(const StdStrBuf& identifier);
221  void WarningNotSupported(const char* identifier);
222 
223  // Current parsing data
224  unsigned int Line;
225  const char* Script;
226 
230 };
231 
233 {
234 public:
236 
237  template<typename SubT> void Load(StdMeshMaterialParserCtx& ctx, std::vector<SubT>& vec);
238 private:
239  unsigned int CurIndex{0u};
240 };
241 
242 StdMeshMaterialParserCtx::StdMeshMaterialParserCtx(StdMeshMatManager& manager, const char* mat_script, const char* filename, StdMeshMaterialLoader& loader):
243  Line(1), Script(mat_script), Manager(manager), FileName(filename), Loader(loader)
244 {
245 }
246 
248 {
249  while (isspace(*Script))
250  {
251  if (*Script == '\n') ++Line;
252  ++Script;
253  }
254 
255  if (*Script == '/')
256  {
257  if (*(Script+1) == '/')
258  {
259  Script += 2;
260  while (*Script != '\n' && *Script != '\0')
261  ++Script;
262  SkipWhitespace();
263  }
264  else if (*Script == '*')
265  {
266  for (Script += 2; *Script != '\0'; ++Script)
267  if (*Script == '*' && *(Script+1) == '/')
268  break;
269 
270  if (*Script == '*')
271  {
272  Script += 2;
273  SkipWhitespace();
274  }
275  }
276  }
277 }
278 
280 {
281  SkipWhitespace();
282 
283  const char* before = Script;
284  Token tok = Advance(name);
285  Script = before;
286  return tok;
287 }
288 
290 {
291  SkipWhitespace();
292 
293  switch (*Script)
294  {
295  case '\0':
296  name.Clear();
297  return TOKEN_EOF;
298  case '{':
299  ++Script;
300  name = "{";
301  return TOKEN_BRACE_OPEN;
302  case '}':
303  ++Script;
304  name = "}";
305  return TOKEN_BRACE_CLOSE;
306  case ':':
307  ++Script;
308  name = ":";
309  return TOKEN_COLON;
310  default:
311  const char* begin = Script;
312  // Advance to next whitespace
313  do { ++Script; }
314  while (!isspace(*Script) && *Script != '{' && *Script != '}' && *Script != ':');
315  name.Copy(begin, Script - begin);
316  return TOKEN_IDTF;
317  }
318 }
319 
321 {
322  Token token = Advance(name);
323  if (token == TOKEN_EOF) Error(StdCopyStrBuf("Unexpected end of file"));
324  return token;
325 }
326 
328 {
329  Token token = AdvanceNonEOF(name);
330  // TODO: Explain what was actually expected
331  if (token != expect) Error(StdCopyStrBuf("'") + name + "' unexpected");
332  return token;
333 }
334 
336 {
337  Token token = AdvanceNonEOF(name);
338  // TODO: Explain what was actually expected
339  if (token != expect1 && token != expect2)
340  Error(StdStrBuf("'") + name + "' unexpected");
341  return token;
342 }
343 
345 {
346  StdStrBuf buf;
348  int i;
349 #ifdef WITH_GLIB
350  char* end;
351  i = g_ascii_strtoll(buf.getData(), &end, 10);
352  if (*end != '\0')
353 #else
354  if (!(std::istringstream(buf.getData()) >> i))
355 #endif
356  Error(StdStrBuf("Integer value expected"));
357 
358  return i;
359 }
360 
362 {
363  StdStrBuf buf;
364  Token tok = Peek(buf);
365 
366  if (tok == TOKEN_IDTF && isdigit(buf[0]))
367  {
368  value = AdvanceInt();
369  return true;
370  }
371 
372  return false;
373 }
374 
376 {
377  StdStrBuf buf;
379  float f;
380 #ifdef WITH_GLIB
381  char* end;
382  f = g_ascii_strtod(buf.getData(), &end);
383  if (*end != '\0')
384 #else
385  if (!(std::istringstream(buf.getData()) >> f))
386 #endif
387  Error(StdStrBuf("Floating point value expected"));
388  return f;
389 }
390 
392 {
393  StdStrBuf buf;
394  Token tok = Peek(buf);
395 
396  if (tok == TOKEN_IDTF && isdigit(buf[0]))
397  {
398  value = AdvanceFloat();
399  return true;
400  }
401 
402  return false;
403 }
404 
405 void StdMeshMaterialParserCtx::AdvanceColor(bool with_alpha, float Color[4])
406 {
407  Color[0] = AdvanceFloat();
408  Color[1] = AdvanceFloat();
409  Color[2] = AdvanceFloat();
410  if (with_alpha) AdvanceFloatOptional(Color[3]);
411 }
412 
414 {
415  StdCopyStrBuf buf;
417  if (buf == "on") return true;
418  if (buf == "off") return false;
419  Error(StdCopyStrBuf("Expected either 'on' or 'off', but not '") + buf + "'");
420  return false; // Never reached
421 }
422 
423 template<typename EnumType>
424 EnumType StdMeshMaterialParserCtx::AdvanceEnum(const Enumerator<EnumType>* enumerators)
425 {
426  StdCopyStrBuf buf;
428 
429  for (const Enumerator<EnumType>* cur = enumerators; cur->Name; ++cur)
430  if (buf == cur->Name)
431  return cur->Value;
432 
434  return EnumType(); // avoid compiler warning
435 }
436 
437 template<int Num, typename EnumType>
438 void StdMeshMaterialParserCtx::AdvanceEnums(const Enumerator<EnumType>* enumerators, EnumType enums[Num])
439 {
440  for (int i = 0; i < Num; ++i)
441  enums[i] = AdvanceEnum(enumerators);
442 }
443 
444 template<int Num, typename EnumType>
445 void StdMeshMaterialParserCtx::AdvanceEnums(const Enumerator<EnumType>* enumerators, const EnumeratorShortcut<Num, EnumType>* shortcuts, EnumType enums[Num])
446 {
447  StdCopyStrBuf buf;
449 
450  const Enumerator<EnumType>* cenum;
451  const EnumeratorShortcut<Num, EnumType>* cshort;
452 
453  for (cenum = enumerators; cenum->Name; ++cenum)
454  if (buf == cenum->Name)
455  break;
456  for (cshort = shortcuts; cshort->Name; ++cshort)
457  if (buf == cshort->Name)
458  break;
459 
460  if (!cenum->Name && !cshort->Name)
461  {
463  }
464  else if (!cenum->Name && cshort->Name)
465  {
466  for (int i = 0; i < Num; ++i)
467  enums[i] = cshort->Values[i];
468  }
469  else if (cenum->Name && (!cshort->Name || Num == 1))
470  {
471  enums[0] = cenum->Value;
472  for (int i = 1; i < Num; ++i)
473  enums[i] = AdvanceEnum(enumerators);
474  }
475  else
476  {
477  // Both enumerator and shortcut are possible, determine by look-ahead
478  const Enumerator<EnumType>* cenum2 = nullptr;
479  Token tok = Peek(buf);
480  if (tok == TOKEN_IDTF)
481  {
482  for (cenum2 = enumerators; cenum2->Name; ++cenum2)
483  if (buf == cenum2->Name)
484  break;
485  }
486 
487  if (cenum2 && cenum2->Name)
488  {
489  // The next item is an enumerator, so load as enumerators
490  enums[0] = cenum->Value;
491  for (int i = 1; i < Num; ++i)
492  enums[i] = AdvanceEnum(enumerators);
493  }
494  else
495  {
496  // The next item is something else, so load the shortcut
497  for (int i = 0; i < Num; ++i)
498  enums[i] = cshort->Values[i];
499  }
500  }
501 }
502 
504 {
505  throw StdMeshMaterialError(message, FileName.getData(), Line);
506 }
507 
509 {
510  Error(StdCopyStrBuf("Unexpected identifier: '") + identifier + "'");
511 }
512 
514 {
515  DebugLogF(R"(%s:%d: Warning: "%s" is not supported!)", FileName.getData(), Line, identifier);
516 }
517 
519 
520 template<typename SubT>
522 {
523  std::vector<unsigned int> indices;
524 
525  StdCopyStrBuf token_name;
526  Token tok = ctx.AdvanceRequired(token_name, TOKEN_IDTF, TOKEN_BRACE_OPEN);
527  if(tok == TOKEN_BRACE_OPEN)
528  {
529  // Unnamed section, name by running index
530  indices.push_back(CurIndex);
531  assert(CurIndex <= vec.size());
532  if(CurIndex == vec.size())
533  {
534  vec.push_back(SubT());
535  vec.back().Name.Format("%u", CurIndex);
536  }
537 
538  ++CurIndex;
539  }
540  else
541  {
542  unsigned int size_before = indices.size();
543  for(unsigned int i = 0; i < vec.size(); ++i)
544  if(SWildcardMatchEx(vec[i].Name.getData(), token_name.getData()))
545  indices.push_back(i);
546 
547  // Only add new SubSection if no wildcard was given
548  if(indices.size() == size_before)
549  {
550  if(std::strchr(token_name.getData(), '*') == nullptr && std::strchr(token_name.getData(), '?') == nullptr)
551  {
552  indices.push_back(vec.size());
553  vec.push_back(SubT());
554  vec.back().Name = token_name;
555  }
556  }
557 
558  ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN);
559  }
560 
561  if(indices.empty())
562  {
563  // Section is not used, parse anyway to advance script position
564  // This can happen if there is inheritance by a non-matching wildcard
565  SubT().Load(ctx);
566  }
567  else
568  {
569  // Parse section multiple times in case there is more than one match.
570  // Not particularly elegant but working.
571  for(unsigned int i = 0; i < indices.size()-1; ++i)
572  {
573  unsigned int old_line = ctx.Line;
574  const char* old_pos = ctx.Script;
575  vec[indices[i]].Load(ctx);
576  ctx.Line = old_line;
577  ctx.Script = old_pos;
578  }
579 
580  vec[indices.back()].Load(ctx);
581  }
582 }
583 
585 {
586  StdStrBuf token_name;
587  StdStrBuf name, language;
588  ctx.AdvanceRequired(name, TOKEN_IDTF);
589  ctx.AdvanceRequired(language, TOKEN_IDTF);
590  ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN);
591 
592  Token token;
593  StdCopyStrBuf source, code, syntax;
594  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
595  {
596  if(token_name == "source")
597  {
598  ctx.AdvanceRequired(source, TOKEN_IDTF);
599  code = ctx.Loader.LoadShaderCode(source.getData());
600  if(code.getLength() == 0)
601  ctx.Error(StdCopyStrBuf("Could not load shader code from '") + source + "'");
602  }
603  else if(token_name == "syntax")
604  {
605  ctx.AdvanceRequired(syntax, TOKEN_IDTF);
606  }
607  else
608  {
609  ctx.ErrorUnexpectedIdentifier(token_name);
610  }
611  }
612 
613  if (token != TOKEN_BRACE_CLOSE)
614  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
615 
616  ctx.Manager.AddShader(source.getData(), name.getData(), language.getData(), type, code.getData(), StdMeshMatManager::SMM_ForceReload);
617 }
618 
620 
622  type(type)
623 {
624  if(type == MATRIX_4X4)
625  matrix = new float[16];
626 }
627 
629 {
630  CopyDeep(other);
631 }
632 
634 {
635  Move(std::move(other));
636 }
637 
639 {
640  if(type == MATRIX_4X4)
641  delete[] matrix;
642 }
643 
645 {
646  if(this == &other) return *this;
647 
648  if(type == MATRIX_4X4)
649  delete[] matrix;
650 
651  CopyDeep(other);
652  return *this;
653 }
654 
656 {
657  if(this == &other) return *this;
658 
659  if(type == MATRIX_4X4)
660  delete[] matrix;
661 
662  Move(std::move(other));
663  return *this;
664 }
665 
667 {
668  StdMeshMaterialShaderParameter other(type);
669  Move(std::move(other));
670 }
671 
672 void StdMeshMaterialShaderParameter::CopyShallow(const StdMeshMaterialShaderParameter& other)
673 {
674  type = other.type;
675 
676  switch(type)
677  {
678  case AUTO:
679  a = other.a;
680  break;
681  case AUTO_TEXTURE_MATRIX:
682  case INT:
683  i = other.i;
684  break;
685  case FLOAT4:
686  f[3] = other.f[3];
687  case FLOAT3:
688  f[2] = other.f[2];
689  case FLOAT2:
690  f[1] = other.f[1];
691  case FLOAT:
692  f[0] = other.f[0];
693  break;
694  case MATRIX_4X4:
695  matrix = other.matrix;
696  break;
697  default:
698  assert(false);
699  break;
700  }
701 }
702 
703 void StdMeshMaterialShaderParameter::CopyDeep(const StdMeshMaterialShaderParameter& other)
704 {
705  CopyShallow(other);
706 
707  if(type == MATRIX_4X4)
708  {
709  matrix = new float[16];
710  for(int i = 0; i < 16; ++i)
711  matrix[i] = other.matrix[i];
712  }
713 }
714 
715 void StdMeshMaterialShaderParameter::Move(StdMeshMaterialShaderParameter &&other)
716 {
717  CopyShallow(other);
718  other.type = FLOAT;
719 }
720 
722 
723 StdMeshMaterialShaderParameter StdMeshMaterialShaderParameters::LoadConstParameter(StdMeshMaterialParserCtx& ctx)
724 {
725  StdStrBuf type_name;
726  ctx.AdvanceRequired(type_name, TOKEN_IDTF);
727  if(type_name == "int")
728  {
730  param.GetInt() = ctx.AdvanceInt();
731  return param;
732  }
733  else if(type_name == "float")
734  {
736  param.GetFloat() = ctx.AdvanceFloat();
737  return param;
738  }
739  else if(type_name == "float2")
740  {
742  param.GetFloatv()[0] = ctx.AdvanceFloat();
743  param.GetFloatv()[1] = ctx.AdvanceFloat();
744  return param;
745  }
746  else if(type_name == "float3")
747  {
749  param.GetFloatv()[0] = ctx.AdvanceFloat();
750  param.GetFloatv()[1] = ctx.AdvanceFloat();
751  param.GetFloatv()[2] = ctx.AdvanceFloat();
752  return param;
753  }
754  else if(type_name == "float4")
755  {
757  param.GetFloatv()[0] = ctx.AdvanceFloat();
758  param.GetFloatv()[1] = ctx.AdvanceFloat();
759  param.GetFloatv()[2] = ctx.AdvanceFloat();
760  param.GetFloatv()[3] = ctx.AdvanceFloat();
761  return param;
762  }
763  else
764  {
765  ctx.Error(FormatString(R"(Invalid type: "%s")", type_name.getData()));
767  }
768 }
769 
770 StdMeshMaterialShaderParameter StdMeshMaterialShaderParameters::LoadAutoParameter(StdMeshMaterialParserCtx& ctx)
771 {
773  param.GetAuto() = ctx.AdvanceEnum(ShaderParameterAutoEnumerators);
774  return param;
775 }
776 
778 {
779  StdStrBuf token_name;
780  ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN);
781 
782  Token token;
783  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
784  {
785  if(token_name == "param_named")
786  {
787  StdStrBuf param_name;
788  ctx.AdvanceRequired(param_name, TOKEN_IDTF);
789  NamedParameters.push_back(std::make_pair(StdCopyStrBuf(param_name), LoadConstParameter(ctx)));
790  }
791  else if(token_name == "param_named_auto")
792  {
793  StdStrBuf param_name;
794  ctx.AdvanceRequired(param_name, TOKEN_IDTF);
795  NamedParameters.push_back(std::make_pair(StdCopyStrBuf(param_name), LoadAutoParameter(ctx)));
796  }
797  else
798  {
799  ctx.ErrorUnexpectedIdentifier(token_name);
800  }
801  }
802 
803  if (token != TOKEN_BRACE_CLOSE)
804  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
805 }
806 
808 {
809  NamedParameters.push_back(std::make_pair(StdCopyStrBuf(name), StdMeshMaterialShaderParameter(type)));
810  return NamedParameters.back().second;
811 }
812 
813 StdMeshMaterialProgram::StdMeshMaterialProgram(const char* name, const StdMeshMaterialShader* fragment_shader, const StdMeshMaterialShader* vertex_shader, const StdMeshMaterialShader* geometry_shader):
814  Name(name), FragmentShader(fragment_shader), VertexShader(vertex_shader), GeometryShader(geometry_shader)
815 {
816  assert(FragmentShader != nullptr);
817  assert(VertexShader != nullptr);
818  // Geometry shader is optional (and not even implemented at the moment!)
819 }
820 
822 {
823  // TODO: This is O(n^2) -- not optimal!
824  bool added = false;
825  for (const auto & NamedParameter : parameters.NamedParameters)
826  {
827  const std::vector<StdCopyStrBuf>::const_iterator iter = std::find(ParameterNames.begin(), ParameterNames.end(), NamedParameter.first);
828  if (iter == ParameterNames.end())
829  {
830  ParameterNames.push_back(NamedParameter.first);
831  added = true;
832  }
833  }
834 
835  return added;
836 }
837 
838 bool StdMeshMaterialProgram::CompileShader(StdMeshMaterialLoader& loader, C4Shader& shader, int ssc)
839 {
840  // Add standard slices
841  loader.AddShaderSlices(shader, ssc);
842  // Add our slices
843  shader.AddVertexSlices(VertexShader->GetFilename(), VertexShader->GetCode(), VertexShader->GetFilename());
844  shader.AddFragmentSlices(FragmentShader->GetFilename(), FragmentShader->GetCode(), FragmentShader->GetFilename());
845  // Construct the list of uniforms
846  std::vector<const char*> uniformNames;
847  std::vector<const char*> attributeNames;
848 #ifndef USE_CONSOLE
849  uniformNames.resize(C4SSU_Count + ParameterNames.size() + 1);
850  uniformNames[C4SSU_ProjectionMatrix] = "projectionMatrix";
851  uniformNames[C4SSU_ModelViewMatrix] = "modelviewMatrix";
852  uniformNames[C4SSU_NormalMatrix] = "normalMatrix";
853  uniformNames[C4SSU_ClrMod] = "clrMod";
854  uniformNames[C4SSU_Gamma] = "gamma";
855  uniformNames[C4SSU_Resolution] = "resolution";
856  uniformNames[C4SSU_BaseTex] = "baseTex"; // unused
857  uniformNames[C4SSU_OverlayTex] = "overlayTex"; // unused
858  uniformNames[C4SSU_OverlayClr] = "oc_PlayerColor";
859  uniformNames[C4SSU_LightTex] = "lightTex";
860  uniformNames[C4SSU_LightTransform] = "lightTransform";
861  uniformNames[C4SSU_NormalTex] = "normalTex"; // unused
862  uniformNames[C4SSU_AmbientTex] = "ambientTex";
863  uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
864  uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
865  uniformNames[C4SSU_MaterialAmbient] = "materialAmbient";
866  uniformNames[C4SSU_MaterialDiffuse] = "materialDiffuse";
867  uniformNames[C4SSU_MaterialSpecular] = "materialSpecular";
868  uniformNames[C4SSU_MaterialEmission] = "materialEmission";
869  uniformNames[C4SSU_MaterialShininess] = "materialShininess";
870  uniformNames[C4SSU_Bones] = "bones";
871  uniformNames[C4SSU_CullMode] = "cullMode";
872  uniformNames[C4SSU_FrameCounter] = "frameCounter";
873  for (unsigned int i = 0; i < ParameterNames.size(); ++i)
874  uniformNames[C4SSU_Count + i] = ParameterNames[i].getData();
875  uniformNames[C4SSU_Count + ParameterNames.size()] = nullptr;
876  attributeNames.resize(C4SSA_Count + 1);
877  attributeNames[C4SSA_Position] = "oc_Position";
878  attributeNames[C4SSA_Normal] = "oc_Normal";
879  attributeNames[C4SSA_TexCoord] = "oc_TexCoord";
880  attributeNames[C4SSA_Color] = "oc_Color"; // unused
881  attributeNames[C4SSA_BoneIndices0] = "oc_BoneIndices0";
882  attributeNames[C4SSA_BoneIndices1] = "oc_BoneIndices1";
883  attributeNames[C4SSA_BoneWeights0] = "oc_BoneWeights0";
884  attributeNames[C4SSA_BoneWeights1] = "oc_BoneWeights1";
885  attributeNames[C4SSA_Count] = nullptr;
886 #endif
887  // Compile the shader
888  StdCopyStrBuf name(Name);
889 #ifndef USE_CONSOLE
890  if (ssc != 0) name.Append(":");
891  if (ssc & C4SSC_LIGHT) name.Append("Light");
892  if (ssc & C4SSC_MOD2) name.Append("Mod2");
893 #endif
894  return shader.Init(name.getData(), &uniformNames[0], &attributeNames[0]);
895 }
896 
898 {
899 #ifndef USE_CONSOLE
900  if (!CompileShader(loader, Shader, 0)) return false;
901  if (!CompileShader(loader, ShaderMod2, C4SSC_MOD2)) return false;
902  if (!CompileShader(loader, ShaderLight, C4SSC_LIGHT)) return false;
903  if (!CompileShader(loader, ShaderLightMod2, C4SSC_LIGHT | C4SSC_MOD2)) return false;
904 #endif
905  return true;
906 }
907 
909 {
910 #ifndef USE_CONSOLE
911  const C4Shader* shaders[4] = {
912  &Shader,
913  &ShaderMod2,
914  &ShaderLight,
915  &ShaderLightMod2
916  };
917 
918  int index = 0;
919  if(ssc & C4SSC_MOD2) index += 1;
920  if(ssc & C4SSC_LIGHT) index += 2;
921 
922  assert(index < 4);
923  return shaders[index];
924 #else
925  return nullptr;
926 #endif
927 }
928 
929 int StdMeshMaterialProgram::GetParameterIndex(const char* name) const
930 {
931 #ifndef USE_CONSOLE
932  std::vector<StdCopyStrBuf>::const_iterator iter = std::find(ParameterNames.begin(), ParameterNames.end(), name);
933  if(iter == ParameterNames.end()) return -1;
934  return C4SSU_Count + std::distance(ParameterNames.begin(), iter);
935 #else
936  return -1;
937 #endif
938 }
939 
941 {
942  assert(TransformType == T_WAVE_XFORM);
943  const double val = fmod(WaveXForm.Frequency * t + WaveXForm.Phase, 1.0);
944  switch (WaveXForm.Wave)
945  {
946  case W_SINE: return WaveXForm.Base + WaveXForm.Amplitude*0.5*(1.0 + sin(val * 2.0 * M_PI));
947  case W_TRIANGLE: if (val < 0.5) return WaveXForm.Base + WaveXForm.Amplitude*2.0*val; else return WaveXForm.Base + WaveXForm.Amplitude*2.0*(1.0 - val);
948  case W_SQUARE: if (val < 0.5) return WaveXForm.Base; else return WaveXForm.Base + WaveXForm.Amplitude;
949  case W_SAWTOOTH: return WaveXForm.Base + WaveXForm.Amplitude*val;
950  case W_INVERSE_SAWTOOTH: return WaveXForm.Base + WaveXForm.Amplitude*(1.0-val);
951  default: assert(false); return 0.0;
952  }
953 }
954 
956  : RefCount(1), Surf(Surface), Texture(*Surface->texture)
957 {
958 }
959 
961 {
962  assert(RefCount == 0);
963  delete Surf;
964 }
965 
967  : pTex(new Tex(Surface))
968 {
969 }
970 
972  : pTex(other.pTex)
973 {
974  ++pTex->RefCount;
975 }
976 
978 {
979  if(!--pTex->RefCount)
980  delete pTex;
981 }
982 
984 {
985  if(&other == this) return *this;
986 
987  if(!--pTex->RefCount)
988  delete pTex;
989 
990  pTex = other.pTex;
991  ++pTex->RefCount;
992 
993  return *this;
994 }
995 
997 {
998  TexBorderColor[0] = TexBorderColor[1] = TexBorderColor[2] = 0.0f; TexBorderColor[3] = 1.0f;
999  Filtering[0] = Filtering[1] = F_Linear; Filtering[2] = F_Point;
1004 }
1005 
1007 {
1008  std::unique_ptr<C4Surface> surface(ctx.Loader.LoadTexture(texname)); // be exception-safe
1009  if (!surface.get())
1010  ctx.Error(StdCopyStrBuf("Could not load texture '") + texname + "'");
1011 
1012  if (surface->Wdt != surface->Hgt)
1013  ctx.Error(StdCopyStrBuf("Texture '") + texname + "' is not quadratic");
1014 
1015  Textures.emplace_back(surface.release());
1016 }
1017 
1019 {
1020  Token token;
1021  StdCopyStrBuf token_name;
1022  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
1023  {
1024  if (token_name == "texture")
1025  {
1026  Textures.clear();
1027  ctx.AdvanceRequired(token_name, TOKEN_IDTF);
1028  LoadTexture(ctx, token_name.getData());
1029  }
1030  else if (token_name == "anim_texture")
1031  {
1032  Textures.clear();
1033 
1034  StdCopyStrBuf base_name;
1035  ctx.AdvanceRequired(base_name, TOKEN_IDTF);
1036 
1037  int num_frames;
1038  if (ctx.AdvanceIntOptional(num_frames))
1039  {
1040  const char* data = base_name.getData();
1041  const char* sep = strrchr(data, '.');
1042  for (int i = 0; i < num_frames; ++i)
1043  {
1044  StdCopyStrBuf buf;
1045  if (sep)
1046  buf.Format("%.*s_%d.%s", (int)(sep - data), data, i, sep+1);
1047  else
1048  buf.Format("%s_%d", data, i);
1049 
1050  LoadTexture(ctx, buf.getData());
1051  }
1052 
1053  Duration = ctx.AdvanceFloat();
1054  }
1055  else
1056  {
1057  LoadTexture(ctx, base_name.getData());
1058  while (!ctx.AdvanceFloatOptional(Duration))
1059  {
1060  ctx.AdvanceRequired(token_name, TOKEN_IDTF);
1061  LoadTexture(ctx, token_name.getData());
1062  }
1063  }
1064  }
1065  else if (token_name == "tex_address_mode")
1066  {
1067  TexAddressMode = ctx.AdvanceEnum(TexAddressModeEnumerators);
1068  }
1069  else if (token_name == "tex_border_colour")
1070  {
1071  ctx.AdvanceColor(true, TexBorderColor);
1072  }
1073  else if (token_name == "filtering")
1074  {
1075  ctx.AdvanceEnums<3, StdMeshMaterialTextureUnit::FilteringType>(FilteringEnumerators, FilteringShortcuts, Filtering);
1076  if (Filtering[0] == F_None || Filtering[1] == F_None)
1077  ctx.Error(StdCopyStrBuf("'none' is only valid for the mip filter"));
1078  if (Filtering[2] == F_Anisotropic)
1079  ctx.Error(StdCopyStrBuf("'anisotropic' is not a valid mip filter"));
1080  }
1081  else if (token_name == "colour_op")
1082  {
1083  BlendOpType ColorOp = ctx.AdvanceEnum(BlendOpEnumerators);
1084  switch (ColorOp)
1085  {
1086  case BO_Replace:
1088  break;
1089  case BO_Add:
1090  ColorOpEx = BOX_Add;
1091  break;
1092  case BO_Modulate:
1094  break;
1095  case BO_AlphaBlend:
1097  break;
1098  }
1099 
1102  }
1103  else if (token_name == "colour_op_ex")
1104  {
1105  ColorOpEx = ctx.AdvanceEnum(BlendOpExEnumerators);
1106  ColorOpSources[0] = ctx.AdvanceEnum(BlendOpSourceEnumerators);
1107  ColorOpSources[1] = ctx.AdvanceEnum(BlendOpSourceEnumerators);
1111  }
1112  else if (token_name == "alpha_op_ex")
1113  {
1114  AlphaOpEx = ctx.AdvanceEnum(BlendOpExEnumerators);
1115  AlphaOpSources[0] = ctx.AdvanceEnum(BlendOpSourceEnumerators);
1116  AlphaOpSources[1] = ctx.AdvanceEnum(BlendOpSourceEnumerators);
1120  }
1121  else if (token_name == "scroll")
1122  {
1123  Transformation trans;
1125  trans.Scroll.X = ctx.AdvanceFloat();
1126  trans.Scroll.Y = ctx.AdvanceFloat();
1127  Transformations.push_back(trans);
1128  }
1129  else if (token_name == "scroll_anim")
1130  {
1131  Transformation trans;
1133  trans.ScrollAnim.XSpeed = ctx.AdvanceFloat();
1134  trans.ScrollAnim.YSpeed = ctx.AdvanceFloat();
1135  Transformations.push_back(trans);
1136  }
1137  else if (token_name == "rotate")
1138  {
1139  Transformation trans;
1141  trans.Rotate.Angle = ctx.AdvanceFloat();
1142  Transformations.push_back(trans);
1143  }
1144  else if (token_name == "rotate_anim")
1145  {
1146  Transformation trans;
1148  trans.RotateAnim.RevsPerSec = ctx.AdvanceFloat();
1149  Transformations.push_back(trans);
1150  }
1151  else if (token_name == "scale")
1152  {
1153  Transformation trans;
1155  trans.Scale.X = ctx.AdvanceFloat();
1156  trans.Scale.Y = ctx.AdvanceFloat();
1157  Transformations.push_back(trans);
1158  }
1159  else if (token_name == "transform")
1160  {
1161  Transformation trans;
1163  for (float & i : trans.Transform.M)
1164  i = ctx.AdvanceFloat();
1165  Transformations.push_back(trans);
1166  }
1167  else if (token_name == "wave_xform")
1168  {
1169  Transformation trans;
1171  trans.WaveXForm.XForm = ctx.AdvanceEnum(XFormTypeEnumerators);
1172  trans.WaveXForm.Wave = ctx.AdvanceEnum(WaveTypeEnumerators);
1173  trans.WaveXForm.Base = ctx.AdvanceFloat();
1174  trans.WaveXForm.Frequency = ctx.AdvanceFloat();
1175  trans.WaveXForm.Phase = ctx.AdvanceFloat();
1176  trans.WaveXForm.Amplitude = ctx.AdvanceFloat();
1177  Transformations.push_back(trans);
1178  }
1179  else
1180  ctx.ErrorUnexpectedIdentifier(token_name);
1181  }
1182 
1183  if (token != TOKEN_BRACE_CLOSE)
1184  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1185 }
1186 
1187 StdMeshMaterialPass::ProgramInstance::ProgramInstance(const StdMeshMaterialProgram* program, const ShaderInstance* fragment_instance, const ShaderInstance* vertex_instance, const ShaderInstance* geometry_instance):
1188  Program(program)
1189 {
1190  // Consistency check
1191  assert(Program->GetFragmentShader() == fragment_instance->Shader);
1192  assert(Program->GetVertexShader() == vertex_instance->Shader);
1193  assert(Program->GetGeometryShader() == geometry_instance->Shader);
1194 
1195  // Load instance parameters, i.e. connect parameter values with uniform index
1196  LoadParameterRefs(fragment_instance);
1197  LoadParameterRefs(vertex_instance);
1198  LoadParameterRefs(geometry_instance);
1199 }
1200 
1201 void StdMeshMaterialPass::ProgramInstance::LoadParameterRefs(const ShaderInstance* instance)
1202 {
1203  for(const auto & NamedParameter : instance->Parameters.NamedParameters)
1204  {
1205  const int index = Program->GetParameterIndex(NamedParameter.first.getData());
1206  assert(index != -1);
1207 
1208  const std::vector<ParameterRef>::const_iterator parameter_iter =
1209  std::find_if(Parameters.begin(), Parameters.end(), [index](const ParameterRef& ref) { return ref.UniformIndex == index; });
1210  if(parameter_iter != Parameters.end())
1211  {
1212  // TODO: Check that the current parameter has the same value as the found one
1213  continue;
1214  }
1215  else
1216  {
1217  ParameterRef ref;
1218  ref.Parameter = &NamedParameter.second;
1219  ref.UniformIndex = index;
1220  Parameters.push_back(ref);
1221  }
1222  }
1223 }
1224 
1226 {
1227  Ambient[0] = Ambient[1] = Ambient[2] = 1.0f; Ambient[3] = 1.0f;
1228  Diffuse[0] = Diffuse[1] = Diffuse[2] = 1.0f; Diffuse[3] = 1.0f;
1229  Specular[0] = Specular[1] = Specular[2] = 0.0f; Specular[3] = 0.0f;
1230  Emissive[0] = Emissive[1] = Emissive[2] = 0.0f; Emissive[3] = 0.0f;
1231  Shininess = 0.0f;
1234  AlphaToCoverage = false;
1236 }
1237 
1238 void StdMeshMaterialPass::LoadShaderRef(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShaderType type)
1239 {
1240  StdStrBuf program_name, token;
1241  ctx.AdvanceRequired(program_name, TOKEN_IDTF);
1242 
1243  ShaderInstance* cur_shader;
1244  const StdMeshMaterialShader* shader;
1245  const char* shader_type_name;
1246 
1247  switch(type)
1248  {
1249  case SMMS_FRAGMENT:
1250  cur_shader = &FragmentShader;
1251  shader = ctx.Manager.GetFragmentShader(program_name.getData());
1252  shader_type_name = "fragment";
1253  break;
1254  case SMMS_VERTEX:
1255  cur_shader = &VertexShader;
1256  shader = ctx.Manager.GetVertexShader(program_name.getData());
1257  shader_type_name = "vertex";
1258  break;
1259  case SMMS_GEOMETRY:
1260  cur_shader = &GeometryShader;
1261  shader = ctx.Manager.GetGeometryShader(program_name.getData());
1262  shader_type_name = "geometry";
1263  break;
1264  default: // can't happen
1265  assert(0);
1266  return;
1267  }
1268 
1269  if(cur_shader->Shader != nullptr)
1270  ctx.Error(FormatString("There is already a %s shader in this pass", shader_type_name));
1271  if(!shader)
1272  ctx.Error(FormatString("There is no such %s shader with name %s", shader_type_name, program_name.getData()));
1273 
1274  cur_shader->Shader = shader;
1275  cur_shader->Parameters.Load(ctx);
1276 }
1277 
1279 {
1280  Token token;
1281  StdCopyStrBuf token_name;
1282  StdMeshMaterialSubLoader texture_unit_loader;
1283  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
1284  {
1285  if (token_name == "texture_unit")
1286  {
1287  texture_unit_loader.Load(ctx, TextureUnits);
1288  }
1289  else if (token_name == "ambient")
1290  {
1291  ctx.AdvanceColor(true, Ambient);
1292  }
1293  else if (token_name == "diffuse")
1294  {
1295  ctx.AdvanceColor(true, Diffuse);
1296  }
1297  else if (token_name == "specular")
1298  {
1299  Specular[0] = ctx.AdvanceFloat();
1300  Specular[1] = ctx.AdvanceFloat();
1301  Specular[2] = ctx.AdvanceFloat();
1302 
1303  // The fourth argument is optional, not the fifth:
1304  float specular3 = ctx.AdvanceFloat();
1305 
1306  float shininess;
1307  if (ctx.AdvanceFloatOptional(shininess))
1308  {
1309  Specular[3] = specular3;
1310  Shininess = shininess;
1311  }
1312  else
1313  {
1314  Shininess = specular3;
1315  }
1316  }
1317  else if (token_name == "emissive")
1318  {
1319  ctx.AdvanceColor(true, Emissive);
1320  }
1321  else if (token_name == "depth_check")
1322  {
1323  DepthCheck = ctx.AdvanceBoolean();
1324  }
1325  else if (token_name == "depth_write")
1326  {
1327  DepthWrite = ctx.AdvanceBoolean();
1328  }
1329  else if (token_name == "cull_hardware")
1330  {
1331  CullHardware = ctx.AdvanceEnum(CullHardwareEnumerators);
1332  }
1333  else if (token_name == "scene_blend")
1334  {
1335  ctx.AdvanceEnums<2, StdMeshMaterialPass::SceneBlendType>(SceneBlendEnumerators, SceneBlendShortcuts, SceneBlendFactors);
1336  }
1337  else if (token_name == "scene_blend_op")
1338  {
1339  StdStrBuf op;
1340  ctx.AdvanceRequired(op, TOKEN_IDTF);
1341  ctx.WarningNotSupported(token_name.getData());
1342  }
1343  else if (token_name == "alpha_rejection")
1344  {
1345  AlphaRejectionFunction = ctx.AdvanceEnum(DepthFunctionEnumerators);
1347  AlphaRejectionValue = ctx.AdvanceFloat() / 255.0f;
1348  }
1349  else if (token_name == "alpha_to_coverage")
1350  {
1352  }
1353  else if (token_name == "colour_write")
1354  {
1355  ctx.AdvanceBoolean();
1356  ctx.WarningNotSupported("colour_write");
1357  }
1358  else if (token_name == "depth_func")
1359  {
1360  StdStrBuf func;
1361  ctx.AdvanceRequired(func, TOKEN_IDTF);
1362  ctx.WarningNotSupported(token_name.getData());
1363  }
1364  else if (token_name == "illumination_stage")
1365  {
1366  ctx.WarningNotSupported(token_name.getData());
1367  }
1368  else if (token_name == "light_clip_planes")
1369  {
1370  ctx.AdvanceBoolean();
1371  ctx.WarningNotSupported(token_name.getData());
1372  }
1373  else if (token_name == "light_scissor")
1374  {
1375  ctx.AdvanceBoolean();
1376  ctx.WarningNotSupported(token_name.getData());
1377  }
1378  else if (token_name == "lighting")
1379  {
1380  ctx.AdvanceBoolean();
1381  ctx.WarningNotSupported(token_name.getData());
1382  }
1383  else if (token_name == "normalise_normals" || token_name == "normalize_normals")
1384  {
1385  ctx.AdvanceBoolean();
1386  ctx.WarningNotSupported(token_name.getData());
1387  }
1388  else if (token_name == "polygon_mode")
1389  {
1390  StdStrBuf mode;
1391  ctx.AdvanceRequired(mode, TOKEN_IDTF);
1392  ctx.WarningNotSupported(token_name.getData());
1393  }
1394  else if (token_name == "shading")
1395  {
1396  StdStrBuf shading;
1397  ctx.AdvanceRequired(shading, TOKEN_IDTF);
1398  ctx.WarningNotSupported(token_name.getData());
1399  }
1400  else if (token_name == "transparent_sorting")
1401  {
1402  ctx.AdvanceBoolean();
1403  ctx.WarningNotSupported(token_name.getData());
1404  }
1405  else if (token_name == "vertex_program_ref")
1406  {
1407  LoadShaderRef(ctx, SMMS_VERTEX);
1408  }
1409  else if (token_name == "fragment_program_ref")
1410  {
1411  LoadShaderRef(ctx, SMMS_FRAGMENT);
1412  }
1413  else if (token_name == "geometry_program_ref")
1414  {
1415  LoadShaderRef(ctx, SMMS_GEOMETRY);
1416  }
1417  else
1418  ctx.ErrorUnexpectedIdentifier(token_name);
1419  }
1420 
1421  if (token != TOKEN_BRACE_CLOSE)
1422  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1423 }
1424 
1426 
1428 {
1429  Token token;
1430  StdCopyStrBuf token_name;
1431  StdMeshMaterialSubLoader pass_loader;
1432  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
1433  {
1434  if (token_name == "pass")
1435  {
1436  pass_loader.Load(ctx, Passes);
1437  }
1438  else
1439  ctx.ErrorUnexpectedIdentifier(token_name);
1440  }
1441 
1442  if (token != TOKEN_BRACE_CLOSE)
1443  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1444 }
1445 
1447 {
1448  // Technique is opaque if one of the passes is opaque (subsequent
1449  // non-opaque passes will just depend on the opaque value drawn in
1450  // the previous pass; total result will not depend on original
1451  // frame buffer value).
1452  for(const auto & Pass : Passes)
1453  if(Pass.IsOpaque())
1454  return true;
1455  return false;
1456 }
1457 
1459 
1461 {
1462  Token token;
1463  StdCopyStrBuf token_name;
1464  StdMeshMaterialSubLoader technique_loader;
1465  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
1466  {
1467  if (token_name == "technique")
1468  {
1469  technique_loader.Load(ctx, Techniques);
1470  }
1471  else if (token_name == "receive_shadows")
1472  {
1473  ReceiveShadows = ctx.AdvanceBoolean();
1474  }
1475  else
1476  ctx.ErrorUnexpectedIdentifier(token_name);
1477  }
1478 
1479  if (token != TOKEN_BRACE_CLOSE)
1480  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1481 }
1482 
1484 {
1485  Materials.clear();
1486 
1487  Programs.clear();
1488  FragmentShaders.clear();
1489  VertexShaders.clear();
1490  GeometryShaders.clear();
1491 }
1492 
1493 std::set<StdCopyStrBuf> StdMeshMatManager::Parse(const char* mat_script, const char* filename, StdMeshMaterialLoader& loader)
1494 {
1495  StdMeshMaterialParserCtx ctx(*this, mat_script, filename, loader);
1496 
1497  Token token;
1498  StdCopyStrBuf token_name;
1499 
1500  std::set<StdCopyStrBuf> loaded_materials;
1501 
1502  while ((token = ctx.Advance(token_name)) == TOKEN_IDTF)
1503  {
1504  if (token_name == "material")
1505  {
1506  // Read name
1507  StdCopyStrBuf material_name;
1508  ctx.AdvanceRequired(material_name, TOKEN_IDTF);
1509 
1510  // Check for uniqueness
1511  std::map<StdCopyStrBuf, StdMeshMaterial>::iterator iter = Materials.find(material_name);
1512  if (iter != Materials.end())
1513  ctx.Error(FormatString("Material with name '%s' is already defined in %s:%u", material_name.getData(), iter->second.FileName.getData(), iter->second.Line));
1514 
1515  // Check if there is a parent given
1516  Token next = ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN, TOKEN_COLON);
1517  // Read parent name, if any
1518  StdMeshMaterial* parent = nullptr;
1519  if (next == TOKEN_COLON)
1520  {
1521  // Note that if there is a parent, then it needs to be loaded
1522  // already. This currently makes only sense when its defined above
1523  // in the same material script file or in a parent definition.
1524  // We could later support material scripts in the System.ocg.
1525  StdCopyStrBuf parent_name;
1526  ctx.AdvanceRequired(parent_name, TOKEN_IDTF);
1527  ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN);
1528 
1529  iter = Materials.find(parent_name);
1530  if (iter == Materials.end())
1531  ctx.Error(StdCopyStrBuf("Parent material '") + parent_name + "' does not exist (or is not yet loaded)");
1532  parent = &iter->second;
1533  }
1534 
1535  // Copy properties from parent if one is given, otherwise
1536  // default-construct the material.
1537  StdMeshMaterial mat = parent ? StdMeshMaterial(*parent) : StdMeshMaterial();
1538 
1539  // Set/Overwrite source and name
1540  mat.Name = material_name;
1541  mat.FileName = ctx.FileName;
1542  mat.Line = ctx.Line;
1543 
1544  mat.Load(ctx);
1545 
1546  Materials[material_name] = mat;
1547 
1548 #ifndef USE_CONSOLE
1549  // To Gfxspecific setup of the material; choose working techniques
1550  if (!pDraw->PrepareMaterial(*this, loader, Materials[material_name]))
1551  {
1552  Materials.erase(material_name);
1553  ctx.Error(StdCopyStrBuf("No working technique for material '") + material_name + "'");
1554  }
1555 #endif
1556  loaded_materials.insert(material_name);
1557  }
1558  else if (token_name == "vertex_program")
1559  {
1560  LoadShader(ctx, SMMS_VERTEX);
1561  }
1562  else if (token_name == "fragment_program")
1563  {
1564  LoadShader(ctx, SMMS_FRAGMENT);
1565  }
1566  else if (token_name == "geometry_program")
1567  {
1568  LoadShader(ctx, SMMS_GEOMETRY);
1569  }
1570  else
1571  ctx.ErrorUnexpectedIdentifier(token_name);
1572  }
1573 
1574  if (token != TOKEN_EOF)
1575  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1576 
1577  return loaded_materials;
1578 }
1579 
1580 const StdMeshMaterial* StdMeshMatManager::GetMaterial(const char* material_name) const
1581 {
1582  std::map<StdCopyStrBuf, StdMeshMaterial>::const_iterator iter = Materials.find(StdCopyStrBuf(material_name));
1583  if (iter == Materials.end()) return nullptr;
1584  return &iter->second;
1585 }
1586 
1588 {
1589  if(update) update->Add(&*iter);
1590  Iterator next_iter = iter;
1591  ++next_iter;
1592  Materials.erase(iter.iter_);
1593  return next_iter;
1594 }
1595 
1597 {
1598  auto it = Materials.find(StdCopyStrBuf(name));
1599  if (it == Materials.end())
1600  return;
1601  if (update) update->Add(&it->second);
1602  Materials.erase(it);
1603 }
1604 
1606 {
1607  ShaderMap::const_iterator iter = FragmentShaders.find(StdCopyStrBuf(name));
1608  if(iter == FragmentShaders.end()) return nullptr;
1609  return iter->second.get();
1610 }
1611 
1613 {
1614  ShaderMap::const_iterator iter = VertexShaders.find(StdCopyStrBuf(name));
1615  if(iter == VertexShaders.end()) return nullptr;
1616  return iter->second.get();
1617 }
1618 
1620 {
1621  ShaderMap::const_iterator iter = GeometryShaders.find(StdCopyStrBuf(name));
1622  if(iter == GeometryShaders.end()) return nullptr;
1623  return iter->second.get();
1624 }
1625 
1626 const StdMeshMaterialShader* StdMeshMatManager::AddShader(const char* filename, const char* name, const char* language, StdMeshMaterialShaderType type, const char* text, uint32_t load_flags)
1627 {
1628  ShaderMap* map = nullptr;
1629  switch(type)
1630  {
1631  case SMMS_FRAGMENT:
1632  map = &FragmentShaders;
1633  break;
1634  case SMMS_VERTEX:
1635  map = &VertexShaders;
1636  break;
1637  case SMMS_GEOMETRY:
1638  map = &GeometryShaders;
1639  break;
1640  }
1641 
1642  StdCopyStrBuf name_buf(name);
1643  ShaderMap::iterator iter = map->find(name_buf);
1644 
1645  if(iter != map->end())
1646  {
1647  // Shader exists
1648  if ((load_flags & SMM_ForceReload) == SMM_ForceReload)
1649  map->erase(iter);
1650  else if ((load_flags & SMM_AcceptExisting) == SMM_AcceptExisting)
1651  return iter->second.get();
1652  else
1653  return nullptr;
1654  }
1655 
1656  std::unique_ptr<StdMeshMaterialShader> shader(new StdMeshMaterialShader(filename, name, language, type, text));
1657  std::pair<ShaderMap::iterator, bool> inserted = map->insert(std::make_pair(name_buf, std::move(shader)));
1658  assert(inserted.second == true);
1659  iter = inserted.first;
1660 
1661  return iter->second.get();
1662 }
1663 
1665 {
1666  std::tuple<const StdMeshMaterialShader*, const StdMeshMaterialShader*, const StdMeshMaterialShader*> key = std::make_tuple(fragment_shader.Shader, vertex_shader.Shader, geometry_shader.Shader);
1667  ProgramMap::iterator iter = Programs.find(key);
1668  if(iter == Programs.end())
1669  {
1670  std::unique_ptr<StdMeshMaterialProgram> program(new StdMeshMaterialProgram(name, fragment_shader.Shader, vertex_shader.Shader, geometry_shader.Shader));
1671  iter = Programs.insert(std::make_pair(key, std::move(program))).first;
1672  }
1673 
1674  StdMeshMaterialProgram& inserted_program = *iter->second;
1675 
1676  const bool fragment_added = inserted_program.AddParameterNames(fragment_shader.Parameters);
1677  const bool vertex_added = inserted_program.AddParameterNames(vertex_shader.Parameters);
1678  const bool geometry_added = inserted_program.AddParameterNames(geometry_shader.Parameters);
1679 
1680  // Re-compile the program (and assign new uniform locations if new
1681  // parameters were encountered).
1682  if(!inserted_program.IsCompiled() || fragment_added || vertex_added || geometry_added)
1683  if(!inserted_program.Compile(loader))
1684  return nullptr;
1685 
1686  return &inserted_program;
1687 }
1688 
TexAddressModeType TexAddressMode
void AddFragmentSlices(const char *szWhat, const char *szText, const char *szSource="", int iFileTime=0)
Definition: C4Shader.cpp:85
void LoadTexture(StdMeshMaterialParserCtx &ctx, const char *texname)
void Load(StdMeshMaterialParserCtx &ctx)
StdMeshMaterialParserCtx(StdMeshMatManager &manager, const char *mat_script, const char *filename, StdMeshMaterialLoader &loader)
const StdMeshMaterialShader * GetGeometryShader() const
Token AdvanceRequired(StdStrBuf &name, Token expect)
EnumType AdvanceEnum(const Enumerator< EnumType > *enumerators)
void Load(StdMeshMaterialParserCtx &ctx)
StdCopyStrBuf FileName
void Clear()
Definition: StdBuf.h:466
StdMeshMaterialShaderParameters Parameters
ShaderInstance FragmentShader
void AdvanceColor(bool with_alpha, float Color[4])
virtual bool PrepareMaterial(StdMeshMatManager &mat_manager, StdMeshMaterialLoader &loader, StdMeshMaterial &mat)=0
void AddVertexSlices(const char *szWhat, const char *szText, const char *szSource="", int iFileTime=0)
Definition: C4Shader.cpp:80
void ErrorUnexpectedIdentifier(const StdStrBuf &identifier)
ShaderInstance GeometryShader
BlendOpSourceType ColorOpSources[2]
void Error(const StdStrBuf &message)
const float * GetFloatv() const
SceneBlendType SceneBlendFactors[2]
const StdMeshMaterial * GetMaterial(const char *material_name) const
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
const StdMeshMaterialShader * Shader
std::vector< ParameterRef > Parameters
#define a
ShaderInstance VertexShader
Token AdvanceNonEOF(StdStrBuf &name)
StdMeshMatManager & Manager
virtual void AddShaderSlices(C4Shader &shader, int ssc)=0
bool AdvanceFloatOptional(float &value)
virtual C4Surface * LoadTexture(const char *filename)=0
std::vector< Transformation > Transformations
virtual StdStrBuf LoadShaderCode(const char *filename)=0
TexPtr & operator=(const TexPtr &other)
StdMeshMaterialShaderParameter & operator=(const StdMeshMaterialShaderParameter &other)
void LoadShader(StdMeshMaterialParserCtx &ctx, StdMeshMaterialShaderType type)
void Load(StdMeshMaterialParserCtx &ctx)
bool SWildcardMatchEx(const char *szString, const char *szWildcard)
Definition: Standard.cpp:629
StdMeshMaterialShaderType
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
const char * GetCode() const
Token Advance(StdStrBuf &name)
std::set< StdCopyStrBuf > Parse(const char *mat_script, const char *filename, StdMeshMaterialLoader &loader)
void AdvanceEnums(const Enumerator< EnumType > *enumerators, EnumType enums[Num])
bool AdvanceIntOptional(int &value)
int GetParameterIndex(const char *name) const
StdMeshMaterialShaderParameter & AddParameter(const char *name, StdMeshMaterialShaderParameter::Type type)
C4Draw * pDraw
Definition: C4Draw.cpp:42
StdMeshMaterialProgram(const char *name, const StdMeshMaterialShader *fragment_shader, const StdMeshMaterialShader *vertex_shader, const StdMeshMaterialShader *geometry_shader)
BlendOpSourceType AlphaOpSources[2]
void WarningNotSupported(const char *identifier)
const StdMeshMaterialShader * GetVertexShader() const
StdMeshMaterialError(const StdStrBuf &message, const char *file, unsigned int line)
const char * getData() const
Definition: StdBuf.h:442
CullHardwareType CullHardware
bool Compile(StdMeshMaterialLoader &loader)
Token Peek(StdStrBuf &name)
std::vector< StdMeshMaterialTextureUnit > TextureUnits
const StdMeshMaterialShader * GetVertexShader(const char *name) const
bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes)
Definition: C4Shader.cpp:346
bool AddParameterNames(const StdMeshMaterialShaderParameters &parameters)
const C4Shader * GetShader(int ssc) const
void Load(StdMeshMaterialParserCtx &ctx, std::vector< SubT > &vec)
unsigned int Line
const char * GetFilename() const
const StdMeshMaterialShader * GetFragmentShader(const char *name) const
void Load(StdMeshMaterialParserCtx &ctx)
StdMeshMaterialLoader & Loader
void Load(StdMeshMaterialParserCtx &ctx)
DepthFunctionType AlphaRejectionFunction
const StdMeshMaterialShader * GetGeometryShader(const char *name) const
StdCopyStrBuf Name
const StdMeshMaterialProgram * AddProgram(const char *name, StdMeshMaterialLoader &loader, const StdMeshMaterialPass::ShaderInstance &fragment_shader, const StdMeshMaterialPass::ShaderInstance &vertex_shader, const StdMeshMaterialPass::ShaderInstance &geometry_shader)
std::vector< std::pair< StdCopyStrBuf, StdMeshMaterialShaderParameter > > NamedParameters
ProgramInstance(const StdMeshMaterialProgram *program, const ShaderInstance *fragment_instance, const ShaderInstance *vertex_instance, const ShaderInstance *geometry_instance)
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
size_t getLength() const
Definition: StdBuf.h:445
StdMeshMatManager MeshMaterialManager
bool DebugLogF(const char *strMessage ...)
Definition: C4Log.cpp:288
const StdMeshMaterialShader * GetFragmentShader() const
void Remove(const StdStrBuf &name, class StdMeshMaterialUpdate *update)
const StdMeshMaterialShaderParameter * Parameter
const StdMeshMaterialProgram *const Program
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270