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 
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 } },
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);
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_BaseTex] = "baseTex"; // unused
856  uniformNames[C4SSU_OverlayTex] = "overlayTex"; // unused
857  uniformNames[C4SSU_OverlayClr] = "oc_PlayerColor";
858  uniformNames[C4SSU_LightTex] = "lightTex";
859  uniformNames[C4SSU_LightTransform] = "lightTransform";
860  uniformNames[C4SSU_NormalTex] = "normalTex"; // unused
861  uniformNames[C4SSU_AmbientTex] = "ambientTex";
862  uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
863  uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
864  uniformNames[C4SSU_MaterialAmbient] = "materialAmbient";
865  uniformNames[C4SSU_MaterialDiffuse] = "materialDiffuse";
866  uniformNames[C4SSU_MaterialSpecular] = "materialSpecular";
867  uniformNames[C4SSU_MaterialEmission] = "materialEmission";
868  uniformNames[C4SSU_MaterialShininess] = "materialShininess";
869  uniformNames[C4SSU_Bones] = "bones";
870  uniformNames[C4SSU_CullMode] = "cullMode";
871  uniformNames[C4SSU_FrameCounter] = "frameCounter";
872  for (unsigned int i = 0; i < ParameterNames.size(); ++i)
873  uniformNames[C4SSU_Count + i] = ParameterNames[i].getData();
874  uniformNames[C4SSU_Count + ParameterNames.size()] = nullptr;
875  attributeNames.resize(C4SSA_Count + 1);
876  attributeNames[C4SSA_Position] = "oc_Position";
877  attributeNames[C4SSA_Normal] = "oc_Normal";
878  attributeNames[C4SSA_TexCoord] = "oc_TexCoord";
879  attributeNames[C4SSA_Color] = "oc_Color"; // unused
880  attributeNames[C4SSA_BoneIndices0] = "oc_BoneIndices0";
881  attributeNames[C4SSA_BoneIndices1] = "oc_BoneIndices1";
882  attributeNames[C4SSA_BoneWeights0] = "oc_BoneWeights0";
883  attributeNames[C4SSA_BoneWeights1] = "oc_BoneWeights1";
884  attributeNames[C4SSA_Count] = nullptr;
885 #endif
886  // Compile the shader
887  StdCopyStrBuf name(Name);
888 #ifndef USE_CONSOLE
889  if (ssc != 0) name.Append(":");
890  if (ssc & C4SSC_LIGHT) name.Append("Light");
891  if (ssc & C4SSC_MOD2) name.Append("Mod2");
892 #endif
893  return shader.Init(name.getData(), &uniformNames[0], &attributeNames[0]);
894 }
895 
897 {
898 #ifndef USE_CONSOLE
899  if (!CompileShader(loader, Shader, 0)) return false;
900  if (!CompileShader(loader, ShaderMod2, C4SSC_MOD2)) return false;
901  if (!CompileShader(loader, ShaderLight, C4SSC_LIGHT)) return false;
902  if (!CompileShader(loader, ShaderLightMod2, C4SSC_LIGHT | C4SSC_MOD2)) return false;
903 #endif
904  return true;
905 }
906 
908 {
909 #ifndef USE_CONSOLE
910  const C4Shader* shaders[4] = {
911  &Shader,
912  &ShaderMod2,
913  &ShaderLight,
914  &ShaderLightMod2
915  };
916 
917  int index = 0;
918  if(ssc & C4SSC_MOD2) index += 1;
919  if(ssc & C4SSC_LIGHT) index += 2;
920 
921  assert(index < 4);
922  return shaders[index];
923 #else
924  return nullptr;
925 #endif
926 }
927 
928 int StdMeshMaterialProgram::GetParameterIndex(const char* name) const
929 {
930 #ifndef USE_CONSOLE
931  std::vector<StdCopyStrBuf>::const_iterator iter = std::find(ParameterNames.begin(), ParameterNames.end(), name);
932  if(iter == ParameterNames.end()) return -1;
933  return C4SSU_Count + std::distance(ParameterNames.begin(), iter);
934 #else
935  return -1;
936 #endif
937 }
938 
940 {
941  assert(TransformType == T_WAVE_XFORM);
942  const double val = fmod(WaveXForm.Frequency * t + WaveXForm.Phase, 1.0);
943  switch (WaveXForm.Wave)
944  {
945  case W_SINE: return WaveXForm.Base + WaveXForm.Amplitude*0.5*(1.0 + sin(val * 2.0 * M_PI));
946  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);
947  case W_SQUARE: if (val < 0.5) return WaveXForm.Base; else return WaveXForm.Base + WaveXForm.Amplitude;
948  case W_SAWTOOTH: return WaveXForm.Base + WaveXForm.Amplitude*val;
949  case W_INVERSE_SAWTOOTH: return WaveXForm.Base + WaveXForm.Amplitude*(1.0-val);
950  default: assert(false); return 0.0;
951  }
952 }
953 
955  : RefCount(1), Surf(Surface), Texture(*Surface->texture)
956 {
957 }
958 
960 {
961  assert(RefCount == 0);
962  delete Surf;
963 }
964 
966  : pTex(new Tex(Surface))
967 {
968 }
969 
971  : pTex(other.pTex)
972 {
973  ++pTex->RefCount;
974 }
975 
977 {
978  if(!--pTex->RefCount)
979  delete pTex;
980 }
981 
983 {
984  if(&other == this) return *this;
985 
986  if(!--pTex->RefCount)
987  delete pTex;
988 
989  pTex = other.pTex;
990  ++pTex->RefCount;
991 
992  return *this;
993 }
994 
996 {
997  TexBorderColor[0] = TexBorderColor[1] = TexBorderColor[2] = 0.0f; TexBorderColor[3] = 1.0f;
998  Filtering[0] = Filtering[1] = F_Linear; Filtering[2] = F_Point;
1003 }
1004 
1006 {
1007  std::unique_ptr<C4Surface> surface(ctx.Loader.LoadTexture(texname)); // be exception-safe
1008  if (!surface.get())
1009  ctx.Error(StdCopyStrBuf("Could not load texture '") + texname + "'");
1010 
1011  if (surface->Wdt != surface->Hgt)
1012  ctx.Error(StdCopyStrBuf("Texture '") + texname + "' is not quadratic");
1013 
1014  Textures.emplace_back(surface.release());
1015 }
1016 
1018 {
1019  Token token;
1020  StdCopyStrBuf token_name;
1021  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
1022  {
1023  if (token_name == "texture")
1024  {
1025  Textures.clear();
1026  ctx.AdvanceRequired(token_name, TOKEN_IDTF);
1027  LoadTexture(ctx, token_name.getData());
1028  }
1029  else if (token_name == "anim_texture")
1030  {
1031  Textures.clear();
1032 
1033  StdCopyStrBuf base_name;
1034  ctx.AdvanceRequired(base_name, TOKEN_IDTF);
1035 
1036  int num_frames;
1037  if (ctx.AdvanceIntOptional(num_frames))
1038  {
1039  const char* data = base_name.getData();
1040  const char* sep = strrchr(data, '.');
1041  for (int i = 0; i < num_frames; ++i)
1042  {
1043  StdCopyStrBuf buf;
1044  if (sep)
1045  buf.Format("%.*s_%d.%s", (int)(sep - data), data, i, sep+1);
1046  else
1047  buf.Format("%s_%d", data, i);
1048 
1049  LoadTexture(ctx, buf.getData());
1050  }
1051 
1052  Duration = ctx.AdvanceFloat();
1053  }
1054  else
1055  {
1056  LoadTexture(ctx, base_name.getData());
1057  while (!ctx.AdvanceFloatOptional(Duration))
1058  {
1059  ctx.AdvanceRequired(token_name, TOKEN_IDTF);
1060  LoadTexture(ctx, token_name.getData());
1061  }
1062  }
1063  }
1064  else if (token_name == "tex_address_mode")
1065  {
1066  TexAddressMode = ctx.AdvanceEnum(TexAddressModeEnumerators);
1067  }
1068  else if (token_name == "tex_border_colour")
1069  {
1070  ctx.AdvanceColor(true, TexBorderColor);
1071  }
1072  else if (token_name == "filtering")
1073  {
1074  ctx.AdvanceEnums<3, StdMeshMaterialTextureUnit::FilteringType>(FilteringEnumerators, FilteringShortcuts, Filtering);
1075  if (Filtering[0] == F_None || Filtering[1] == F_None)
1076  ctx.Error(StdCopyStrBuf("'none' is only valid for the mip filter"));
1077  if (Filtering[2] == F_Anisotropic)
1078  ctx.Error(StdCopyStrBuf("'anisotropic' is not a valid mip filter"));
1079  }
1080  else if (token_name == "colour_op")
1081  {
1082  BlendOpType ColorOp = ctx.AdvanceEnum(BlendOpEnumerators);
1083  switch (ColorOp)
1084  {
1085  case BO_Replace:
1087  break;
1088  case BO_Add:
1089  ColorOpEx = BOX_Add;
1090  break;
1091  case BO_Modulate:
1093  break;
1094  case BO_AlphaBlend:
1096  break;
1097  }
1098 
1101  }
1102  else if (token_name == "colour_op_ex")
1103  {
1104  ColorOpEx = ctx.AdvanceEnum(BlendOpExEnumerators);
1105  ColorOpSources[0] = ctx.AdvanceEnum(BlendOpSourceEnumerators);
1106  ColorOpSources[1] = ctx.AdvanceEnum(BlendOpSourceEnumerators);
1110  }
1111  else if (token_name == "alpha_op_ex")
1112  {
1113  AlphaOpEx = ctx.AdvanceEnum(BlendOpExEnumerators);
1114  AlphaOpSources[0] = ctx.AdvanceEnum(BlendOpSourceEnumerators);
1115  AlphaOpSources[1] = ctx.AdvanceEnum(BlendOpSourceEnumerators);
1119  }
1120  else if (token_name == "scroll")
1121  {
1122  Transformation trans;
1124  trans.Scroll.X = ctx.AdvanceFloat();
1125  trans.Scroll.Y = ctx.AdvanceFloat();
1126  Transformations.push_back(trans);
1127  }
1128  else if (token_name == "scroll_anim")
1129  {
1130  Transformation trans;
1132  trans.ScrollAnim.XSpeed = ctx.AdvanceFloat();
1133  trans.ScrollAnim.YSpeed = ctx.AdvanceFloat();
1134  Transformations.push_back(trans);
1135  }
1136  else if (token_name == "rotate")
1137  {
1138  Transformation trans;
1140  trans.Rotate.Angle = ctx.AdvanceFloat();
1141  Transformations.push_back(trans);
1142  }
1143  else if (token_name == "rotate_anim")
1144  {
1145  Transformation trans;
1147  trans.RotateAnim.RevsPerSec = ctx.AdvanceFloat();
1148  Transformations.push_back(trans);
1149  }
1150  else if (token_name == "scale")
1151  {
1152  Transformation trans;
1154  trans.Scale.X = ctx.AdvanceFloat();
1155  trans.Scale.Y = ctx.AdvanceFloat();
1156  Transformations.push_back(trans);
1157  }
1158  else if (token_name == "transform")
1159  {
1160  Transformation trans;
1162  for (float & i : trans.Transform.M)
1163  i = ctx.AdvanceFloat();
1164  Transformations.push_back(trans);
1165  }
1166  else if (token_name == "wave_xform")
1167  {
1168  Transformation trans;
1170  trans.WaveXForm.XForm = ctx.AdvanceEnum(XFormTypeEnumerators);
1171  trans.WaveXForm.Wave = ctx.AdvanceEnum(WaveTypeEnumerators);
1172  trans.WaveXForm.Base = ctx.AdvanceFloat();
1173  trans.WaveXForm.Frequency = ctx.AdvanceFloat();
1174  trans.WaveXForm.Phase = ctx.AdvanceFloat();
1175  trans.WaveXForm.Amplitude = ctx.AdvanceFloat();
1176  Transformations.push_back(trans);
1177  }
1178  else
1179  ctx.ErrorUnexpectedIdentifier(token_name);
1180  }
1181 
1182  if (token != TOKEN_BRACE_CLOSE)
1183  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1184 }
1185 
1186 StdMeshMaterialPass::ProgramInstance::ProgramInstance(const StdMeshMaterialProgram* program, const ShaderInstance* fragment_instance, const ShaderInstance* vertex_instance, const ShaderInstance* geometry_instance):
1187  Program(program)
1188 {
1189  // Consistency check
1190  assert(Program->GetFragmentShader() == fragment_instance->Shader);
1191  assert(Program->GetVertexShader() == vertex_instance->Shader);
1192  assert(Program->GetGeometryShader() == geometry_instance->Shader);
1193 
1194  // Load instance parameters, i.e. connect parameter values with uniform index
1195  LoadParameterRefs(fragment_instance);
1196  LoadParameterRefs(vertex_instance);
1197  LoadParameterRefs(geometry_instance);
1198 }
1199 
1200 void StdMeshMaterialPass::ProgramInstance::LoadParameterRefs(const ShaderInstance* instance)
1201 {
1202  for(const auto & NamedParameter : instance->Parameters.NamedParameters)
1203  {
1204  const int index = Program->GetParameterIndex(NamedParameter.first.getData());
1205  assert(index != -1);
1206 
1207  const std::vector<ParameterRef>::const_iterator parameter_iter =
1208  std::find_if(Parameters.begin(), Parameters.end(), [index](const ParameterRef& ref) { return ref.UniformIndex == index; });
1209  if(parameter_iter != Parameters.end())
1210  {
1211  // TODO: Check that the current parameter has the same value as the found one
1212  continue;
1213  }
1214  else
1215  {
1216  ParameterRef ref;
1217  ref.Parameter = &NamedParameter.second;
1218  ref.UniformIndex = index;
1219  Parameters.push_back(ref);
1220  }
1221  }
1222 }
1223 
1225 {
1226  Ambient[0] = Ambient[1] = Ambient[2] = 1.0f; Ambient[3] = 1.0f;
1227  Diffuse[0] = Diffuse[1] = Diffuse[2] = 1.0f; Diffuse[3] = 1.0f;
1228  Specular[0] = Specular[1] = Specular[2] = 0.0f; Specular[3] = 0.0f;
1229  Emissive[0] = Emissive[1] = Emissive[2] = 0.0f; Emissive[3] = 0.0f;
1230  Shininess = 0.0f;
1233  AlphaToCoverage = false;
1235 }
1236 
1237 void StdMeshMaterialPass::LoadShaderRef(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShaderType type)
1238 {
1239  StdStrBuf program_name, token;
1240  ctx.AdvanceRequired(program_name, TOKEN_IDTF);
1241 
1242  ShaderInstance* cur_shader;
1243  const StdMeshMaterialShader* shader;
1244  const char* shader_type_name;
1245 
1246  switch(type)
1247  {
1248  case SMMS_FRAGMENT:
1249  cur_shader = &FragmentShader;
1250  shader = ctx.Manager.GetFragmentShader(program_name.getData());
1251  shader_type_name = "fragment";
1252  break;
1253  case SMMS_VERTEX:
1254  cur_shader = &VertexShader;
1255  shader = ctx.Manager.GetVertexShader(program_name.getData());
1256  shader_type_name = "vertex";
1257  break;
1258  case SMMS_GEOMETRY:
1259  cur_shader = &GeometryShader;
1260  shader = ctx.Manager.GetGeometryShader(program_name.getData());
1261  shader_type_name = "geometry";
1262  break;
1263  default: // can't happen
1264  assert(0);
1265  return;
1266  }
1267 
1268  if(cur_shader->Shader != nullptr)
1269  ctx.Error(FormatString("There is already a %s shader in this pass", shader_type_name));
1270  if(!shader)
1271  ctx.Error(FormatString("There is no such %s shader with name %s", shader_type_name, program_name.getData()));
1272 
1273  cur_shader->Shader = shader;
1274  cur_shader->Parameters.Load(ctx);
1275 }
1276 
1278 {
1279  Token token;
1280  StdCopyStrBuf token_name;
1281  StdMeshMaterialSubLoader texture_unit_loader;
1282  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
1283  {
1284  if (token_name == "texture_unit")
1285  {
1286  texture_unit_loader.Load(ctx, TextureUnits);
1287  }
1288  else if (token_name == "ambient")
1289  {
1290  ctx.AdvanceColor(true, Ambient);
1291  }
1292  else if (token_name == "diffuse")
1293  {
1294  ctx.AdvanceColor(true, Diffuse);
1295  }
1296  else if (token_name == "specular")
1297  {
1298  Specular[0] = ctx.AdvanceFloat();
1299  Specular[1] = ctx.AdvanceFloat();
1300  Specular[2] = ctx.AdvanceFloat();
1301 
1302  // The fourth argument is optional, not the fifth:
1303  float specular3 = ctx.AdvanceFloat();
1304 
1305  float shininess;
1306  if (ctx.AdvanceFloatOptional(shininess))
1307  {
1308  Specular[3] = specular3;
1309  Shininess = shininess;
1310  }
1311  else
1312  {
1313  Shininess = specular3;
1314  }
1315  }
1316  else if (token_name == "emissive")
1317  {
1318  ctx.AdvanceColor(true, Emissive);
1319  }
1320  else if (token_name == "depth_check")
1321  {
1322  DepthCheck = ctx.AdvanceBoolean();
1323  }
1324  else if (token_name == "depth_write")
1325  {
1326  DepthWrite = ctx.AdvanceBoolean();
1327  }
1328  else if (token_name == "cull_hardware")
1329  {
1330  CullHardware = ctx.AdvanceEnum(CullHardwareEnumerators);
1331  }
1332  else if (token_name == "scene_blend")
1333  {
1334  ctx.AdvanceEnums<2, StdMeshMaterialPass::SceneBlendType>(SceneBlendEnumerators, SceneBlendShortcuts, SceneBlendFactors);
1335  }
1336  else if (token_name == "scene_blend_op")
1337  {
1338  StdStrBuf op;
1339  ctx.AdvanceRequired(op, TOKEN_IDTF);
1340  ctx.WarningNotSupported(token_name.getData());
1341  }
1342  else if (token_name == "alpha_rejection")
1343  {
1344  AlphaRejectionFunction = ctx.AdvanceEnum(DepthFunctionEnumerators);
1346  AlphaRejectionValue = ctx.AdvanceFloat() / 255.0f;
1347  }
1348  else if (token_name == "alpha_to_coverage")
1349  {
1351  }
1352  else if (token_name == "colour_write")
1353  {
1354  ctx.AdvanceBoolean();
1355  ctx.WarningNotSupported("colour_write");
1356  }
1357  else if (token_name == "depth_func")
1358  {
1359  StdStrBuf func;
1360  ctx.AdvanceRequired(func, TOKEN_IDTF);
1361  ctx.WarningNotSupported(token_name.getData());
1362  }
1363  else if (token_name == "illumination_stage")
1364  {
1365  ctx.WarningNotSupported(token_name.getData());
1366  }
1367  else if (token_name == "light_clip_planes")
1368  {
1369  ctx.AdvanceBoolean();
1370  ctx.WarningNotSupported(token_name.getData());
1371  }
1372  else if (token_name == "light_scissor")
1373  {
1374  ctx.AdvanceBoolean();
1375  ctx.WarningNotSupported(token_name.getData());
1376  }
1377  else if (token_name == "lighting")
1378  {
1379  ctx.AdvanceBoolean();
1380  ctx.WarningNotSupported(token_name.getData());
1381  }
1382  else if (token_name == "normalise_normals" || token_name == "normalize_normals")
1383  {
1384  ctx.AdvanceBoolean();
1385  ctx.WarningNotSupported(token_name.getData());
1386  }
1387  else if (token_name == "polygon_mode")
1388  {
1389  StdStrBuf mode;
1390  ctx.AdvanceRequired(mode, TOKEN_IDTF);
1391  ctx.WarningNotSupported(token_name.getData());
1392  }
1393  else if (token_name == "shading")
1394  {
1395  StdStrBuf shading;
1396  ctx.AdvanceRequired(shading, TOKEN_IDTF);
1397  ctx.WarningNotSupported(token_name.getData());
1398  }
1399  else if (token_name == "transparent_sorting")
1400  {
1401  ctx.AdvanceBoolean();
1402  ctx.WarningNotSupported(token_name.getData());
1403  }
1404  else if (token_name == "vertex_program_ref")
1405  {
1406  LoadShaderRef(ctx, SMMS_VERTEX);
1407  }
1408  else if (token_name == "fragment_program_ref")
1409  {
1410  LoadShaderRef(ctx, SMMS_FRAGMENT);
1411  }
1412  else if (token_name == "geometry_program_ref")
1413  {
1414  LoadShaderRef(ctx, SMMS_GEOMETRY);
1415  }
1416  else
1417  ctx.ErrorUnexpectedIdentifier(token_name);
1418  }
1419 
1420  if (token != TOKEN_BRACE_CLOSE)
1421  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1422 }
1423 
1425 
1427 {
1428  Token token;
1429  StdCopyStrBuf token_name;
1430  StdMeshMaterialSubLoader pass_loader;
1431  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
1432  {
1433  if (token_name == "pass")
1434  {
1435  pass_loader.Load(ctx, Passes);
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 {
1447  // Technique is opaque if one of the passes is opaque (subsequent
1448  // non-opaque passes will just depend on the opaque value drawn in
1449  // the previous pass; total result will not depend on original
1450  // frame buffer value).
1451  for(const auto & Pass : Passes)
1452  if(Pass.IsOpaque())
1453  return true;
1454  return false;
1455 }
1456 
1458 
1460 {
1461  Token token;
1462  StdCopyStrBuf token_name;
1463  StdMeshMaterialSubLoader technique_loader;
1464  while ((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF)
1465  {
1466  if (token_name == "technique")
1467  {
1468  technique_loader.Load(ctx, Techniques);
1469  }
1470  else if (token_name == "receive_shadows")
1471  {
1472  ReceiveShadows = ctx.AdvanceBoolean();
1473  }
1474  else
1475  ctx.ErrorUnexpectedIdentifier(token_name);
1476  }
1477 
1478  if (token != TOKEN_BRACE_CLOSE)
1479  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1480 }
1481 
1483 {
1484  Materials.clear();
1485 
1486  Programs.clear();
1487  FragmentShaders.clear();
1488  VertexShaders.clear();
1489  GeometryShaders.clear();
1490 }
1491 
1492 std::set<StdCopyStrBuf> StdMeshMatManager::Parse(const char* mat_script, const char* filename, StdMeshMaterialLoader& loader)
1493 {
1494  StdMeshMaterialParserCtx ctx(*this, mat_script, filename, loader);
1495 
1496  Token token;
1497  StdCopyStrBuf token_name;
1498 
1499  std::set<StdCopyStrBuf> loaded_materials;
1500 
1501  while ((token = ctx.Advance(token_name)) == TOKEN_IDTF)
1502  {
1503  if (token_name == "material")
1504  {
1505  // Read name
1506  StdCopyStrBuf material_name;
1507  ctx.AdvanceRequired(material_name, TOKEN_IDTF);
1508 
1509  // Check for uniqueness
1510  std::map<StdCopyStrBuf, StdMeshMaterial>::iterator iter = Materials.find(material_name);
1511  if (iter != Materials.end())
1512  ctx.Error(FormatString("Material with name '%s' is already defined in %s:%u", material_name.getData(), iter->second.FileName.getData(), iter->second.Line));
1513 
1514  // Check if there is a parent given
1515  Token next = ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN, TOKEN_COLON);
1516  // Read parent name, if any
1517  StdMeshMaterial* parent = nullptr;
1518  if (next == TOKEN_COLON)
1519  {
1520  // Note that if there is a parent, then it needs to be loaded
1521  // already. This currently makes only sense when its defined above
1522  // in the same material script file or in a parent definition.
1523  // We could later support material scripts in the System.ocg.
1524  StdCopyStrBuf parent_name;
1525  ctx.AdvanceRequired(parent_name, TOKEN_IDTF);
1526  ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN);
1527 
1528  iter = Materials.find(parent_name);
1529  if (iter == Materials.end())
1530  ctx.Error(StdCopyStrBuf("Parent material '") + parent_name + "' does not exist (or is not yet loaded)");
1531  parent = &iter->second;
1532  }
1533 
1534  // Copy properties from parent if one is given, otherwise
1535  // default-construct the material.
1536  StdMeshMaterial mat = parent ? StdMeshMaterial(*parent) : StdMeshMaterial();
1537 
1538  // Set/Overwrite source and name
1539  mat.Name = material_name;
1540  mat.FileName = ctx.FileName;
1541  mat.Line = ctx.Line;
1542 
1543  mat.Load(ctx);
1544 
1545  Materials[material_name] = mat;
1546 
1547 #ifndef USE_CONSOLE
1548  // To Gfxspecific setup of the material; choose working techniques
1549  if (!pDraw->PrepareMaterial(*this, loader, Materials[material_name]))
1550  {
1551  Materials.erase(material_name);
1552  ctx.Error(StdCopyStrBuf("No working technique for material '") + material_name + "'");
1553  }
1554 #endif
1555  loaded_materials.insert(material_name);
1556  }
1557  else if (token_name == "vertex_program")
1558  {
1559  LoadShader(ctx, SMMS_VERTEX);
1560  }
1561  else if (token_name == "fragment_program")
1562  {
1563  LoadShader(ctx, SMMS_FRAGMENT);
1564  }
1565  else if (token_name == "geometry_program")
1566  {
1567  LoadShader(ctx, SMMS_GEOMETRY);
1568  }
1569  else
1570  ctx.ErrorUnexpectedIdentifier(token_name);
1571  }
1572 
1573  if (token != TOKEN_EOF)
1574  ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
1575 
1576  return loaded_materials;
1577 }
1578 
1579 const StdMeshMaterial* StdMeshMatManager::GetMaterial(const char* material_name) const
1580 {
1581  std::map<StdCopyStrBuf, StdMeshMaterial>::const_iterator iter = Materials.find(StdCopyStrBuf(material_name));
1582  if (iter == Materials.end()) return nullptr;
1583  return &iter->second;
1584 }
1585 
1587 {
1588  if(update) update->Add(&*iter);
1589  Iterator next_iter = iter;
1590  ++next_iter;
1591  Materials.erase(iter.iter_);
1592  return next_iter;
1593 }
1594 
1596 {
1597  auto it = Materials.find(StdCopyStrBuf(name));
1598  if (it == Materials.end())
1599  return;
1600  if (update) update->Add(&it->second);
1601  Materials.erase(it);
1602 }
1603 
1605 {
1606  ShaderMap::const_iterator iter = FragmentShaders.find(StdCopyStrBuf(name));
1607  if(iter == FragmentShaders.end()) return nullptr;
1608  return iter->second.get();
1609 }
1610 
1612 {
1613  ShaderMap::const_iterator iter = VertexShaders.find(StdCopyStrBuf(name));
1614  if(iter == VertexShaders.end()) return nullptr;
1615  return iter->second.get();
1616 }
1617 
1619 {
1620  ShaderMap::const_iterator iter = GeometryShaders.find(StdCopyStrBuf(name));
1621  if(iter == GeometryShaders.end()) return nullptr;
1622  return iter->second.get();
1623 }
1624 
1625 const StdMeshMaterialShader* StdMeshMatManager::AddShader(const char* filename, const char* name, const char* language, StdMeshMaterialShaderType type, const char* text, uint32_t load_flags)
1626 {
1627  ShaderMap* map = nullptr;
1628  switch(type)
1629  {
1630  case SMMS_FRAGMENT:
1631  map = &FragmentShaders;
1632  break;
1633  case SMMS_VERTEX:
1634  map = &VertexShaders;
1635  break;
1636  case SMMS_GEOMETRY:
1637  map = &GeometryShaders;
1638  break;
1639  }
1640 
1641  StdCopyStrBuf name_buf(name);
1642  ShaderMap::iterator iter = map->find(name_buf);
1643 
1644  if(iter != map->end())
1645  {
1646  // Shader exists
1647  if ((load_flags & SMM_ForceReload) == SMM_ForceReload)
1648  map->erase(iter);
1649  else if ((load_flags & SMM_AcceptExisting) == SMM_AcceptExisting)
1650  return iter->second.get();
1651  else
1652  return nullptr;
1653  }
1654 
1655  std::unique_ptr<StdMeshMaterialShader> shader(new StdMeshMaterialShader(filename, name, language, type, text));
1656  std::pair<ShaderMap::iterator, bool> inserted = map->insert(std::make_pair(name_buf, std::move(shader)));
1657  assert(inserted.second == true);
1658  iter = inserted.first;
1659 
1660  return iter->second.get();
1661 }
1662 
1664 {
1665  std::tuple<const StdMeshMaterialShader*, const StdMeshMaterialShader*, const StdMeshMaterialShader*> key = std::make_tuple(fragment_shader.Shader, vertex_shader.Shader, geometry_shader.Shader);
1666  ProgramMap::iterator iter = Programs.find(key);
1667  if(iter == Programs.end())
1668  {
1669  std::unique_ptr<StdMeshMaterialProgram> program(new StdMeshMaterialProgram(name, fragment_shader.Shader, vertex_shader.Shader, geometry_shader.Shader));
1670  iter = Programs.insert(std::make_pair(key, std::move(program))).first;
1671  }
1672 
1673  StdMeshMaterialProgram& inserted_program = *iter->second;
1674 
1675  const bool fragment_added = inserted_program.AddParameterNames(fragment_shader.Parameters);
1676  const bool vertex_added = inserted_program.AddParameterNames(vertex_shader.Parameters);
1677  const bool geometry_added = inserted_program.AddParameterNames(geometry_shader.Parameters);
1678 
1679  // Re-compile the program (and assign new uniform locations if new
1680  // parameters were encountered).
1681  if(!inserted_program.IsCompiled() || fragment_added || vertex_added || geometry_added)
1682  if(!inserted_program.Compile(loader))
1683  return nullptr;
1684 
1685  return &inserted_program;
1686 }
1687 
TexAddressModeType TexAddressMode
const char * getData() const
Definition: StdBuf.h:442
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 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: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)
SceneBlendType SceneBlendFactors[2]
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
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:278
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:607
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: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 * 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< StdMeshMaterialTextureUnit > TextureUnits
bool Init(const char *szWhat, const char **szUniforms, const char **szAttributes)
Definition: C4Shader.cpp:346
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:445
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:467
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:270