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