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