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"
18 #include "C4ForbidLibraryCompilation.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 {
53 { "wrap", StdMeshMaterialTextureUnit::AM_Wrap },
54 { "clamp", StdMeshMaterialTextureUnit::AM_Clamp },
55 { "mirror", StdMeshMaterialTextureUnit::AM_Mirror },
56 { "border", StdMeshMaterialTextureUnit::AM_Border },
57 { nullptr, static_cast<StdMeshMaterialTextureUnit::TexAddressModeType>(0) }
58 };
59
60 const Enumerator<StdMeshMaterialTextureUnit::FilteringType> FilteringEnumerators[] =
61 {
62 { "none", StdMeshMaterialTextureUnit::F_None },
63 { "point", StdMeshMaterialTextureUnit::F_Point },
64 { "linear", StdMeshMaterialTextureUnit::F_Linear },
65 { "anisotropic", StdMeshMaterialTextureUnit::F_Anisotropic },
66 { nullptr, static_cast<StdMeshMaterialTextureUnit::FilteringType>(0) }
67 };
68
69 const EnumeratorShortcut<3, StdMeshMaterialTextureUnit::FilteringType> FilteringShortcuts[] =
70 {
71 { "none", { StdMeshMaterialTextureUnit::F_Point, StdMeshMaterialTextureUnit::F_Point, StdMeshMaterialTextureUnit::F_None } },
72 { "bilinear", { StdMeshMaterialTextureUnit::F_Linear, StdMeshMaterialTextureUnit::F_Linear, StdMeshMaterialTextureUnit::F_Point } },
73 { "trilinear", { StdMeshMaterialTextureUnit::F_Linear, StdMeshMaterialTextureUnit::F_Linear, StdMeshMaterialTextureUnit::F_Linear } },
74 { "anisotropic", { StdMeshMaterialTextureUnit::F_Anisotropic, StdMeshMaterialTextureUnit::F_Anisotropic, StdMeshMaterialTextureUnit::F_Linear } },
75 { nullptr, { static_cast<StdMeshMaterialTextureUnit::FilteringType>(0), static_cast<StdMeshMaterialTextureUnit::FilteringType>(0), static_cast<StdMeshMaterialTextureUnit::FilteringType>(0) } }
76 };
77
78 const Enumerator<StdMeshMaterialTextureUnit::BlendOpType> BlendOpEnumerators[] =
79 {
80 { "replace", StdMeshMaterialTextureUnit::BO_Replace },
81 { "add", StdMeshMaterialTextureUnit::BO_Add },
82 { "modulate", StdMeshMaterialTextureUnit::BO_Modulate },
83 { "alpha_blend", StdMeshMaterialTextureUnit::BO_AlphaBlend },
84 { nullptr, static_cast<StdMeshMaterialTextureUnit::BlendOpType>(0) }
85 };
86
87 const Enumerator<StdMeshMaterialTextureUnit::BlendOpExType> BlendOpExEnumerators[] =
88 {
89 { "source1", StdMeshMaterialTextureUnit::BOX_Source1 },
90 { "source2", StdMeshMaterialTextureUnit::BOX_Source2 },
91 { "modulate", StdMeshMaterialTextureUnit::BOX_Modulate },
92 { "modulate_x2", StdMeshMaterialTextureUnit::BOX_ModulateX2 },
93 { "modulate_x4", StdMeshMaterialTextureUnit::BOX_ModulateX4 },
94 { "add", StdMeshMaterialTextureUnit::BOX_Add },
95 { "add_signed", StdMeshMaterialTextureUnit::BOX_AddSigned },
96 { "add_smooth", StdMeshMaterialTextureUnit::BOX_AddSmooth },
97 { "subtract", StdMeshMaterialTextureUnit::BOX_Subtract },
98 { "blend_diffuse_alpha", StdMeshMaterialTextureUnit::BOX_BlendDiffuseAlpha },
99 { "blend_texture_alpha", StdMeshMaterialTextureUnit::BOX_BlendTextureAlpha },
100 { "blend_current_alpha", StdMeshMaterialTextureUnit::BOX_BlendCurrentAlpha },
101 { "blend_manual", StdMeshMaterialTextureUnit::BOX_BlendManual },
102 { "dotproduct", StdMeshMaterialTextureUnit::BOX_Dotproduct },
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 {
121 { "scroll_x", StdMeshMaterialTextureUnit::Transformation::XF_SCROLL_X },
122 { "scroll_y", StdMeshMaterialTextureUnit::Transformation::XF_SCROLL_Y },
123 { "rotate", StdMeshMaterialTextureUnit::Transformation::XF_ROTATE },
124 { "scale_x", StdMeshMaterialTextureUnit::Transformation::XF_SCALE_X },
125 { "scale_y", StdMeshMaterialTextureUnit::Transformation::XF_SCALE_Y },
126 { nullptr, static_cast<StdMeshMaterialTextureUnit::Transformation::XFormType>(0) }
127 };
128
129 const Enumerator<StdMeshMaterialTextureUnit::Transformation::WaveType> WaveTypeEnumerators[] =
130 {
131 { "sine", StdMeshMaterialTextureUnit::Transformation::W_SINE },
132 { "triangle", StdMeshMaterialTextureUnit::Transformation::W_TRIANGLE },
133 { "square", StdMeshMaterialTextureUnit::Transformation::W_SQUARE },
134 { "sawtooth", StdMeshMaterialTextureUnit::Transformation::W_SAWTOOTH },
135 { "inverse_sawtooth", StdMeshMaterialTextureUnit::Transformation::W_INVERSE_SAWTOOTH },
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 {
164 { "add", { StdMeshMaterialPass::SB_One, StdMeshMaterialPass::SB_One } },
165 { "modulate", { StdMeshMaterialPass::SB_DestColor, StdMeshMaterialPass::SB_Zero } },
166 { "colour_blend", { StdMeshMaterialPass::SB_SrcColor, StdMeshMaterialPass::SB_OneMinusSrcColor } },
167 { "alpha_blend", { StdMeshMaterialPass::SB_SrcAlpha, StdMeshMaterialPass::SB_OneMinusSrcAlpha } },
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
StdMeshMaterialError(const StdStrBuf & message,const char * file,unsigned int line)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 {
192 TOKEN_IDTF,
193 TOKEN_BRACE_OPEN,
194 TOKEN_BRACE_CLOSE,
195 TOKEN_COLON,
196 TOKEN_EOF
197 };
198
199 class StdMeshMaterialParserCtx
200 {
201 public:
202 StdMeshMaterialParserCtx(StdMeshMatManager& manager, const char* mat_script, const char* filename, StdMeshMaterialLoader& loader);
203
204 void SkipWhitespace();
205 Token Peek(StdStrBuf& name);
206 Token Advance(StdStrBuf& name);
207 Token AdvanceNonEOF(StdStrBuf& name);
208 Token AdvanceRequired(StdStrBuf& name, Token expect);
209 Token AdvanceRequired(StdStrBuf& name, Token expect1, Token expect2);
210 int AdvanceInt();
211 bool AdvanceIntOptional(int& value);
212 float AdvanceFloat();
213 bool AdvanceFloatOptional(float& value);
214 void AdvanceColor(bool with_alpha, float Color[4]);
215 bool AdvanceBoolean();
216 template<typename EnumType> EnumType AdvanceEnum(const Enumerator<EnumType>* enumerators);
217 template<int Num, typename EnumType> void AdvanceEnums(const Enumerator<EnumType>* enumerators, EnumType enums[Num]);
218 template<int Num, typename EnumType> void AdvanceEnums(const Enumerator<EnumType>* enumerators, const EnumeratorShortcut<Num, EnumType>* shortcuts, EnumType enums[Num]);
219 void Error(const StdStrBuf& message);
220 void ErrorUnexpectedIdentifier(const StdStrBuf& identifier);
221 void WarningNotSupported(const char* identifier);
222
223 // Current parsing data
224 unsigned int Line;
225 const char* Script;
226
227 StdMeshMatManager& Manager;
228 StdCopyStrBuf FileName;
229 StdMeshMaterialLoader& Loader;
230 };
231
232 class StdMeshMaterialSubLoader
233 {
234 public:
235 StdMeshMaterialSubLoader();
236
237 template<typename SubT> void Load(StdMeshMaterialParserCtx& ctx, std::vector<SubT>& vec);
238 private:
239 unsigned int CurIndex{0u};
240 };
241
StdMeshMaterialParserCtx(StdMeshMatManager & manager,const char * mat_script,const char * filename,StdMeshMaterialLoader & loader)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
SkipWhitespace()247 void StdMeshMaterialParserCtx::SkipWhitespace()
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
Peek(StdStrBuf & name)279 Token StdMeshMaterialParserCtx::Peek(StdStrBuf& name)
280 {
281 SkipWhitespace();
282
283 const char* before = Script;
284 Token tok = Advance(name);
285 Script = before;
286 return tok;
287 }
288
Advance(StdStrBuf & name)289 Token StdMeshMaterialParserCtx::Advance(StdStrBuf& name)
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
AdvanceNonEOF(StdStrBuf & name)320 Token StdMeshMaterialParserCtx::AdvanceNonEOF(StdStrBuf& name)
321 {
322 Token token = Advance(name);
323 if (token == TOKEN_EOF) Error(StdCopyStrBuf("Unexpected end of file"));
324 return token;
325 }
326
AdvanceRequired(StdStrBuf & name,Token expect)327 Token StdMeshMaterialParserCtx::AdvanceRequired(StdStrBuf& name, Token expect)
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
AdvanceRequired(StdStrBuf & name,Token expect1,Token expect2)335 Token StdMeshMaterialParserCtx::AdvanceRequired(StdStrBuf& name, Token expect1, Token expect2)
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
AdvanceInt()344 int StdMeshMaterialParserCtx::AdvanceInt()
345 {
346 StdStrBuf buf;
347 AdvanceRequired(buf, TOKEN_IDTF);
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
AdvanceIntOptional(int & value)361 bool StdMeshMaterialParserCtx::AdvanceIntOptional(int& value)
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
AdvanceFloat()375 float StdMeshMaterialParserCtx::AdvanceFloat()
376 {
377 StdStrBuf buf;
378 AdvanceRequired(buf, TOKEN_IDTF);
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
AdvanceFloatOptional(float & value)391 bool StdMeshMaterialParserCtx::AdvanceFloatOptional(float& value)
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
AdvanceColor(bool with_alpha,float Color[4])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
AdvanceBoolean()413 bool StdMeshMaterialParserCtx::AdvanceBoolean()
414 {
415 StdCopyStrBuf buf;
416 AdvanceRequired(buf, TOKEN_IDTF);
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>
AdvanceEnum(const Enumerator<EnumType> * enumerators)424 EnumType StdMeshMaterialParserCtx::AdvanceEnum(const Enumerator<EnumType>* enumerators)
425 {
426 StdCopyStrBuf buf;
427 AdvanceRequired(buf, TOKEN_IDTF);
428
429 for (const Enumerator<EnumType>* cur = enumerators; cur->Name; ++cur)
430 if (buf == cur->Name)
431 return cur->Value;
432
433 ErrorUnexpectedIdentifier(buf);
434 return EnumType(); // avoid compiler warning
435 }
436
437 template<int Num, typename EnumType>
AdvanceEnums(const Enumerator<EnumType> * enumerators,EnumType enums[Num])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>
AdvanceEnums(const Enumerator<EnumType> * enumerators,const EnumeratorShortcut<Num,EnumType> * shortcuts,EnumType enums[Num])445 void StdMeshMaterialParserCtx::AdvanceEnums(const Enumerator<EnumType>* enumerators, const EnumeratorShortcut<Num, EnumType>* shortcuts, EnumType enums[Num])
446 {
447 StdCopyStrBuf buf;
448 AdvanceRequired(buf, TOKEN_IDTF);
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 {
462 ErrorUnexpectedIdentifier(buf);
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
Error(const StdStrBuf & message)503 void StdMeshMaterialParserCtx::Error(const StdStrBuf& message)
504 {
505 throw StdMeshMaterialError(message, FileName.getData(), Line);
506 }
507
ErrorUnexpectedIdentifier(const StdStrBuf & identifier)508 void StdMeshMaterialParserCtx::ErrorUnexpectedIdentifier(const StdStrBuf& identifier)
509 {
510 Error(StdCopyStrBuf("Unexpected identifier: '") + identifier + "'");
511 }
512
WarningNotSupported(const char * identifier)513 void StdMeshMaterialParserCtx::WarningNotSupported(const char* identifier)
514 {
515 DebugLogF(R"(%s:%d: Warning: "%s" is not supported!)", FileName.getData(), Line, identifier);
516 }
517
518 StdMeshMaterialSubLoader::StdMeshMaterialSubLoader() = default;
519
520 template<typename SubT>
Load(StdMeshMaterialParserCtx & ctx,std::vector<SubT> & vec)521 void StdMeshMaterialSubLoader::Load(StdMeshMaterialParserCtx& ctx, std::vector<SubT>& vec)
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
LoadShader(StdMeshMaterialParserCtx & ctx,StdMeshMaterialShaderType type)584 void LoadShader(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShaderType type)
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
619 StdMeshMaterialShaderParameter::StdMeshMaterialShaderParameter() = default;
620
StdMeshMaterialShaderParameter(Type type)621 StdMeshMaterialShaderParameter::StdMeshMaterialShaderParameter(Type type):
622 type(type)
623 {
624 if(type == MATRIX_4X4)
625 matrix = new float[16];
626 }
627
StdMeshMaterialShaderParameter(const StdMeshMaterialShaderParameter & other)628 StdMeshMaterialShaderParameter::StdMeshMaterialShaderParameter(const StdMeshMaterialShaderParameter& other)
629 {
630 CopyDeep(other);
631 }
632
StdMeshMaterialShaderParameter(StdMeshMaterialShaderParameter && other)633 StdMeshMaterialShaderParameter::StdMeshMaterialShaderParameter(StdMeshMaterialShaderParameter &&other)
634 {
635 Move(std::move(other));
636 }
637
~StdMeshMaterialShaderParameter()638 StdMeshMaterialShaderParameter::~StdMeshMaterialShaderParameter()
639 {
640 if(type == MATRIX_4X4)
641 delete[] matrix;
642 }
643
operator =(const StdMeshMaterialShaderParameter & other)644 StdMeshMaterialShaderParameter& StdMeshMaterialShaderParameter::operator=(const StdMeshMaterialShaderParameter& other)
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
operator =(StdMeshMaterialShaderParameter && other)655 StdMeshMaterialShaderParameter& StdMeshMaterialShaderParameter::operator=(StdMeshMaterialShaderParameter &&other)
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
SetType(Type type)666 void StdMeshMaterialShaderParameter::SetType(Type type)
667 {
668 StdMeshMaterialShaderParameter other(type);
669 Move(std::move(other));
670 }
671
CopyShallow(const StdMeshMaterialShaderParameter & other)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
CopyDeep(const StdMeshMaterialShaderParameter & other)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
Move(StdMeshMaterialShaderParameter && other)715 void StdMeshMaterialShaderParameter::Move(StdMeshMaterialShaderParameter &&other)
716 {
717 CopyShallow(other);
718 other.type = FLOAT;
719 }
720
721 StdMeshMaterialShaderParameters::StdMeshMaterialShaderParameters() = default;
722
LoadConstParameter(StdMeshMaterialParserCtx & ctx)723 StdMeshMaterialShaderParameter StdMeshMaterialShaderParameters::LoadConstParameter(StdMeshMaterialParserCtx& ctx)
724 {
725 StdStrBuf type_name;
726 ctx.AdvanceRequired(type_name, TOKEN_IDTF);
727 if(type_name == "int")
728 {
729 StdMeshMaterialShaderParameter param(StdMeshMaterialShaderParameter::INT);
730 param.GetInt() = ctx.AdvanceInt();
731 return param;
732 }
733 else if(type_name == "float")
734 {
735 StdMeshMaterialShaderParameter param(StdMeshMaterialShaderParameter::FLOAT);
736 param.GetFloat() = ctx.AdvanceFloat();
737 return param;
738 }
739 else if(type_name == "float2")
740 {
741 StdMeshMaterialShaderParameter param(StdMeshMaterialShaderParameter::FLOAT2);
742 param.GetFloatv()[0] = ctx.AdvanceFloat();
743 param.GetFloatv()[1] = ctx.AdvanceFloat();
744 return param;
745 }
746 else if(type_name == "float3")
747 {
748 StdMeshMaterialShaderParameter param(StdMeshMaterialShaderParameter::FLOAT2);
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 {
756 StdMeshMaterialShaderParameter param(StdMeshMaterialShaderParameter::FLOAT2);
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()));
766 return StdMeshMaterialShaderParameter();
767 }
768 }
769
LoadAutoParameter(StdMeshMaterialParserCtx & ctx)770 StdMeshMaterialShaderParameter StdMeshMaterialShaderParameters::LoadAutoParameter(StdMeshMaterialParserCtx& ctx)
771 {
772 StdMeshMaterialShaderParameter param(StdMeshMaterialShaderParameter::AUTO);
773 param.GetAuto() = ctx.AdvanceEnum(ShaderParameterAutoEnumerators);
774 return param;
775 }
776
Load(StdMeshMaterialParserCtx & ctx)777 void StdMeshMaterialShaderParameters::Load(StdMeshMaterialParserCtx& ctx)
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
AddParameter(const char * name,StdMeshMaterialShaderParameter::Type type)807 StdMeshMaterialShaderParameter& StdMeshMaterialShaderParameters::AddParameter(const char* name, StdMeshMaterialShaderParameter::Type type)
808 {
809 NamedParameters.push_back(std::make_pair(StdCopyStrBuf(name), StdMeshMaterialShaderParameter(type)));
810 return NamedParameters.back().second;
811 }
812
StdMeshMaterialProgram(const char * name,const StdMeshMaterialShader * fragment_shader,const StdMeshMaterialShader * vertex_shader,const StdMeshMaterialShader * geometry_shader)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
AddParameterNames(const StdMeshMaterialShaderParameters & parameters)821 bool StdMeshMaterialProgram::AddParameterNames(const StdMeshMaterialShaderParameters& parameters)
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
CompileShader(StdMeshMaterialLoader & loader,C4Shader & shader,int ssc)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
Compile(StdMeshMaterialLoader & loader)896 bool StdMeshMaterialProgram::Compile(StdMeshMaterialLoader& loader)
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
GetShader(int ssc) const907 const C4Shader* StdMeshMaterialProgram::GetShader(int ssc) const
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
GetParameterIndex(const char * name) const928 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
GetWaveXForm(double t) const939 double StdMeshMaterialTextureUnit::Transformation::GetWaveXForm(double t) const
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
Tex(C4Surface * Surface)954 StdMeshMaterialTextureUnit::Tex::Tex(C4Surface* Surface)
955 : RefCount(1), Surf(Surface), Texture(*Surface->texture)
956 {
957 }
958
~Tex()959 StdMeshMaterialTextureUnit::Tex::~Tex()
960 {
961 assert(RefCount == 0);
962 delete Surf;
963 }
964
TexPtr(C4Surface * Surface)965 StdMeshMaterialTextureUnit::TexPtr::TexPtr(C4Surface* Surface)
966 : pTex(new Tex(Surface))
967 {
968 }
969
TexPtr(const TexPtr & other)970 StdMeshMaterialTextureUnit::TexPtr::TexPtr(const TexPtr& other)
971 : pTex(other.pTex)
972 {
973 ++pTex->RefCount;
974 }
975
~TexPtr()976 StdMeshMaterialTextureUnit::TexPtr::~TexPtr()
977 {
978 if(!--pTex->RefCount)
979 delete pTex;
980 }
981
operator =(const TexPtr & other)982 StdMeshMaterialTextureUnit::TexPtr& StdMeshMaterialTextureUnit::TexPtr::operator=(const TexPtr& other)
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
StdMeshMaterialTextureUnit()995 StdMeshMaterialTextureUnit::StdMeshMaterialTextureUnit()
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;
999 ColorOpSources[0] = BOS_Current; ColorOpSources[1] = BOS_Texture;
1000 AlphaOpSources[0] = BOS_Current; AlphaOpSources[1] = BOS_Texture;
1001 ColorOpManualColor1[0] = ColorOpManualColor1[1] = ColorOpManualColor1[2] = AlphaOpManualAlpha1 = 0.0f;
1002 ColorOpManualColor2[0] = ColorOpManualColor2[1] = ColorOpManualColor2[2] = AlphaOpManualAlpha2 = 0.0f;
1003 }
1004
LoadTexture(StdMeshMaterialParserCtx & ctx,const char * texname)1005 void StdMeshMaterialTextureUnit::LoadTexture(StdMeshMaterialParserCtx& ctx, const char* texname)
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
Load(StdMeshMaterialParserCtx & ctx)1017 void StdMeshMaterialTextureUnit::Load(StdMeshMaterialParserCtx& ctx)
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:
1086 ColorOpEx = BOX_Source1;
1087 break;
1088 case BO_Add:
1089 ColorOpEx = BOX_Add;
1090 break;
1091 case BO_Modulate:
1092 ColorOpEx = BOX_Modulate;
1093 break;
1094 case BO_AlphaBlend:
1095 ColorOpEx = BOX_BlendTextureAlpha;
1096 break;
1097 }
1098
1099 ColorOpSources[0] = BOS_Texture;
1100 ColorOpSources[1] = BOS_Current;
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);
1107 if (ColorOpEx == BOX_BlendManual) ColorOpManualFactor = ctx.AdvanceFloat();
1108 if (ColorOpSources[0] == BOS_Manual) ctx.AdvanceColor(false, ColorOpManualColor1);
1109 if (ColorOpSources[1] == BOS_Manual) ctx.AdvanceColor(false, ColorOpManualColor2);
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);
1116 if (AlphaOpEx == BOX_BlendManual) AlphaOpManualFactor = ctx.AdvanceFloat();
1117 if (AlphaOpSources[0] == BOS_Manual) AlphaOpManualAlpha1 = ctx.AdvanceFloat();
1118 if (AlphaOpSources[1] == BOS_Manual) AlphaOpManualAlpha2 = ctx.AdvanceFloat();
1119 }
1120 else if (token_name == "scroll")
1121 {
1122 Transformation trans;
1123 trans.TransformType = Transformation::T_SCROLL;
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;
1131 trans.TransformType = Transformation::T_SCROLL_ANIM;
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;
1139 trans.TransformType = Transformation::T_ROTATE;
1140 trans.Rotate.Angle = ctx.AdvanceFloat();
1141 Transformations.push_back(trans);
1142 }
1143 else if (token_name == "rotate_anim")
1144 {
1145 Transformation trans;
1146 trans.TransformType = Transformation::T_ROTATE_ANIM;
1147 trans.RotateAnim.RevsPerSec = ctx.AdvanceFloat();
1148 Transformations.push_back(trans);
1149 }
1150 else if (token_name == "scale")
1151 {
1152 Transformation trans;
1153 trans.TransformType = Transformation::T_SCALE;
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;
1161 trans.TransformType = Transformation::T_TRANSFORM;
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;
1169 trans.TransformType = Transformation::T_WAVE_XFORM;
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
ProgramInstance(const StdMeshMaterialProgram * program,const ShaderInstance * fragment_instance,const ShaderInstance * vertex_instance,const ShaderInstance * geometry_instance)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
LoadParameterRefs(const ShaderInstance * instance)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
StdMeshMaterialPass()1224 StdMeshMaterialPass::StdMeshMaterialPass()
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;
1231 SceneBlendFactors[0] = SB_One; SceneBlendFactors[1] = SB_Zero;
1232 AlphaRejectionFunction = DF_AlwaysPass; AlphaRejectionValue = 0.0f;
1233 AlphaToCoverage = false;
1234 VertexShader.Shader = FragmentShader.Shader = GeometryShader.Shader = nullptr;
1235 }
1236
LoadShaderRef(StdMeshMaterialParserCtx & ctx,StdMeshMaterialShaderType type)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
Load(StdMeshMaterialParserCtx & ctx)1277 void StdMeshMaterialPass::Load(StdMeshMaterialParserCtx& ctx)
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);
1345 if (AlphaRejectionFunction != DF_AlwaysFail && AlphaRejectionFunction != DF_AlwaysPass)
1346 AlphaRejectionValue = ctx.AdvanceFloat() / 255.0f;
1347 }
1348 else if (token_name == "alpha_to_coverage")
1349 {
1350 AlphaToCoverage = ctx.AdvanceBoolean();
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
1424 StdMeshMaterialTechnique::StdMeshMaterialTechnique() = default;
1425
Load(StdMeshMaterialParserCtx & ctx)1426 void StdMeshMaterialTechnique::Load(StdMeshMaterialParserCtx& ctx)
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
IsOpaque() const1445 bool StdMeshMaterialTechnique::IsOpaque() const
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
1457 StdMeshMaterial::StdMeshMaterial() = default;
1458
Load(StdMeshMaterialParserCtx & ctx)1459 void StdMeshMaterial::Load(StdMeshMaterialParserCtx& ctx)
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
Clear()1482 void StdMeshMatManager::Clear()
1483 {
1484 Materials.clear();
1485
1486 Programs.clear();
1487 FragmentShaders.clear();
1488 VertexShaders.clear();
1489 GeometryShaders.clear();
1490 }
1491
Parse(const char * mat_script,const char * filename,StdMeshMaterialLoader & loader)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
GetMaterial(const char * material_name) const1579 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
Remove(const Iterator & iter,StdMeshMaterialUpdate * update)1586 StdMeshMatManager::Iterator StdMeshMatManager::Remove(const Iterator& iter, StdMeshMaterialUpdate* update)
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
Remove(const StdStrBuf & name,StdMeshMaterialUpdate * update)1595 void StdMeshMatManager::Remove(const StdStrBuf &name, StdMeshMaterialUpdate *update)
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
GetFragmentShader(const char * name) const1604 const StdMeshMaterialShader* StdMeshMatManager::GetFragmentShader(const char* name) const
1605 {
1606 ShaderMap::const_iterator iter = FragmentShaders.find(StdCopyStrBuf(name));
1607 if(iter == FragmentShaders.end()) return nullptr;
1608 return iter->second.get();
1609 }
1610
GetVertexShader(const char * name) const1611 const StdMeshMaterialShader* StdMeshMatManager::GetVertexShader(const char* name) const
1612 {
1613 ShaderMap::const_iterator iter = VertexShaders.find(StdCopyStrBuf(name));
1614 if(iter == VertexShaders.end()) return nullptr;
1615 return iter->second.get();
1616 }
1617
GetGeometryShader(const char * name) const1618 const StdMeshMaterialShader* StdMeshMatManager::GetGeometryShader(const char* name) const
1619 {
1620 ShaderMap::const_iterator iter = GeometryShaders.find(StdCopyStrBuf(name));
1621 if(iter == GeometryShaders.end()) return nullptr;
1622 return iter->second.get();
1623 }
1624
AddShader(const char * filename,const char * name,const char * language,StdMeshMaterialShaderType type,const char * text,uint32_t load_flags)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
AddProgram(const char * name,StdMeshMaterialLoader & loader,const StdMeshMaterialPass::ShaderInstance & fragment_shader,const StdMeshMaterialPass::ShaderInstance & vertex_shader,const StdMeshMaterialPass::ShaderInstance & geometry_shader)1663 const StdMeshMaterialProgram* StdMeshMatManager::AddProgram(const char* name, StdMeshMaterialLoader& loader, const StdMeshMaterialPass::ShaderInstance& fragment_shader, const StdMeshMaterialPass::ShaderInstance& vertex_shader, const StdMeshMaterialPass::ShaderInstance& geometry_shader)
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
1688 StdMeshMatManager MeshMaterialManager;
1689