1 // Copyright (C) 2006-2012 Nikolaus Gebhardt / Thomas Alten
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #ifndef __I_Q3_LEVEL_SHADER_H_INCLUDED__
6 #define __I_Q3_LEVEL_SHADER_H_INCLUDED__
7 
8 #include "irrArray.h"
9 #include "fast_atof.h"
10 #include "IFileSystem.h"
11 #include "IVideoDriver.h"
12 #include "coreutil.h"
13 
14 namespace irr
15 {
16 namespace scene
17 {
18 namespace quake3
19 {
20 
21 	static core::stringc irrEmptyStringc("");
22 
23 	//! Hold the different Mesh Types used for getMesh
24 	enum eQ3MeshIndex
25 	{
26 		E_Q3_MESH_GEOMETRY = 0,
27 		E_Q3_MESH_ITEMS,
28 		E_Q3_MESH_BILLBOARD,
29 		E_Q3_MESH_FOG,
30 		E_Q3_MESH_UNRESOLVED,
31 		E_Q3_MESH_SIZE
32 	};
33 
34 	/*! used to customize Quake3 BSP Loader
35 	*/
36 
37 	struct Q3LevelLoadParameter
38 	{
Q3LevelLoadParameterQ3LevelLoadParameter39 		Q3LevelLoadParameter ()
40 			:defaultLightMapMaterial ( video::EMT_LIGHTMAP_M4 ),
41 			defaultModulate ( video::EMFN_MODULATE_4X ),
42 			defaultFilter ( video::EMF_BILINEAR_FILTER ),
43 			patchTesselation ( 8 ),
44 			verbose ( 0 ),
45 			startTime ( 0 ), endTime ( 0 ),
46 			mergeShaderBuffer ( 1 ),
47 			cleanUnResolvedMeshes ( 1 ),
48 			loadAllShaders ( 0 ),
49 			loadSkyShader ( 0 ),
50 			alpharef ( 1 ),
51 			swapLump ( 0 ),
52 	#ifdef __BIG_ENDIAN__
53 			swapHeader ( 1 )
54 	#else
55 			swapHeader ( 0 )
56 	#endif
57 			{
58 				memcpy ( scriptDir, "scripts\x0", 8 );
59 			}
60 
61 		video::E_MATERIAL_TYPE defaultLightMapMaterial;
62 		video::E_MODULATE_FUNC defaultModulate;
63 		video::E_MATERIAL_FLAG defaultFilter;
64 		s32 patchTesselation;
65 		s32 verbose;
66 		u32 startTime;
67 		u32 endTime;
68 		s32 mergeShaderBuffer;
69 		s32 cleanUnResolvedMeshes;
70 		s32 loadAllShaders;
71 		s32 loadSkyShader;
72 		s32 alpharef;
73 		s32 swapLump;
74 		s32 swapHeader;
75 		c8 scriptDir [ 64 ];
76 	};
77 
78 	// some useful typedefs
79 	typedef core::array< core::stringc > tStringList;
80 	typedef core::array< video::ITexture* > tTexArray;
81 
82 	// string helper.. TODO: move to generic files
isEqual(const core::stringc & string,u32 & pos,const c8 * list[],u16 listSize)83 	inline s16 isEqual ( const core::stringc &string, u32 &pos, const c8 *list[], u16 listSize )
84 	{
85 		const char * in = string.c_str () + pos;
86 
87 		for ( u16 i = 0; i != listSize; ++i )
88 		{
89 			if (string.size() < pos)
90 				return -2;
91 			u32 len = (u32) strlen ( list[i] );
92 			if (string.size() < pos+len)
93 				continue;
94 			if ( in [len] != 0 && in [len] != ' ' )
95 				continue;
96 			if ( strncmp ( in, list[i], len ) )
97 				continue;
98 
99 			pos += len + 1;
100 			return (s16) i;
101 		}
102 		return -2;
103 	}
104 
getAsFloat(const core::stringc & string,u32 & pos)105 	inline f32 getAsFloat ( const core::stringc &string, u32 &pos )
106 	{
107 		const char * in = string.c_str () + pos;
108 
109 		f32 value = 0.f;
110 		pos += (u32) ( core::fast_atof_move ( in, value ) - in ) + 1;
111 		return value;
112 	}
113 
114 	//! get a quake3 vector translated to irrlicht position (x,-z,y )
getAsVector3df(const core::stringc & string,u32 & pos)115 	inline core::vector3df getAsVector3df ( const core::stringc &string, u32 &pos )
116 	{
117 		core::vector3df v;
118 
119 		v.X = getAsFloat ( string, pos );
120 		v.Z = getAsFloat ( string, pos );
121 		v.Y = getAsFloat ( string, pos );
122 
123 		return v;
124 	}
125 
126 
127 	/*
128 		extract substrings
129 	*/
getAsStringList(tStringList & list,s32 max,const core::stringc & string,u32 & startPos)130 	inline void getAsStringList ( tStringList &list, s32 max, const core::stringc &string, u32 &startPos )
131 	{
132 		list.clear ();
133 
134 		s32 finish = 0;
135 		s32 endPos;
136 		do
137 		{
138 			endPos = string.findNext ( ' ', startPos );
139 			if ( endPos == -1 )
140 			{
141 				finish = 1;
142 				endPos = string.size();
143 			}
144 
145 			list.push_back ( string.subString ( startPos, endPos - startPos ) );
146 			startPos = endPos + 1;
147 
148 			if ( list.size() >= (u32) max )
149 				finish = 1;
150 
151 		} while ( !finish );
152 
153 	}
154 
155 	//! A blend function for a q3 shader.
156 	struct SBlendFunc
157 	{
SBlendFuncSBlendFunc158 		SBlendFunc ( video::E_MODULATE_FUNC mod )
159 			: type ( video::EMT_SOLID ), modulate ( mod ),
160 				param0( 0.f ),
161 			isTransparent ( 0 ) {}
162 
163 		video::E_MATERIAL_TYPE type;
164 		video::E_MODULATE_FUNC modulate;
165 
166 		f32 param0;
167 		u32 isTransparent;
168 	};
169 
170 	// parses the content of Variable cull
getCullingFunction(const core::stringc & cull)171 	inline bool getCullingFunction ( const core::stringc &cull )
172 	{
173 		if ( cull.size() == 0 )
174 			return true;
175 
176 		bool ret = true;
177 		static const c8 * funclist[] = { "none", "disable", "twosided" };
178 
179 		u32 pos = 0;
180 		switch ( isEqual ( cull, pos, funclist, 3 ) )
181 		{
182 			case 0:
183 			case 1:
184 			case 2:
185 				ret = false;
186 				break;
187 		}
188 		return ret;
189 	}
190 
191 	// parses the content of Variable depthfunc
192 	// return a z-test
getDepthFunction(const core::stringc & string)193 	inline u8 getDepthFunction ( const core::stringc &string )
194 	{
195 		u8 ret = video::ECFN_LESSEQUAL;
196 
197 		if ( string.size() == 0 )
198 			return ret;
199 
200 		static const c8 * funclist[] = { "lequal","equal" };
201 
202 		u32 pos = 0;
203 		switch ( isEqual ( string, pos, funclist, 2 ) )
204 		{
205 			case 0:
206 				ret = video::ECFN_LESSEQUAL;
207 				break;
208 			case 1:
209 				ret = video::ECFN_EQUAL;
210 				break;
211 		}
212 		return ret;
213 	}
214 
215 
216 	/*!
217 		parses the content of Variable blendfunc,alphafunc
218 		it also make a hint for rendering as transparent or solid node.
219 
220 		we assume a typical quake scene would look like this..
221 		1) Big Static Mesh ( solid )
222 		2) static scene item ( may use transparency ) but rendered in the solid pass
223 		3) additional transparency item in the transparent pass
224 
225 		it's not 100% accurate! it just empirical..
226 	*/
getBlendFunc(const core::stringc & string,SBlendFunc & blendfunc)227 	inline static void getBlendFunc ( const core::stringc &string, SBlendFunc &blendfunc )
228 	{
229 		if ( string.size() == 0 )
230 			return;
231 
232 		// maps to E_BLEND_FACTOR
233 		static const c8 * funclist[] =
234 		{
235 			"gl_zero",
236 			"gl_one",
237 			"gl_dst_color",
238 			"gl_one_minus_dst_color",
239 			"gl_src_color",
240 			"gl_one_minus_src_color",
241 			"gl_src_alpha",
242 			"gl_one_minus_src_alpha",
243 			"gl_dst_alpha",
244 			"gl_one_minus_dst_alpha",
245 			"gl_src_alpha_sat",
246 
247 			"add",
248 			"filter",
249 			"blend",
250 
251 			"ge128",
252 			"gt0",
253 		};
254 
255 
256 		u32 pos = 0;
257 		s32 srcFact = isEqual ( string, pos, funclist, 16 );
258 
259 		if ( srcFact < 0 )
260 			return;
261 
262 		u32 resolved = 0;
263 		s32 dstFact = isEqual ( string, pos, funclist, 16 );
264 
265 		switch ( srcFact )
266 		{
267 			case video::EBF_ZERO:
268 				switch ( dstFact )
269 				{
270 					// gl_zero gl_src_color == gl_dst_color gl_zero
271 					case video::EBF_SRC_COLOR:
272 						blendfunc.type = video::EMT_ONETEXTURE_BLEND;
273 						blendfunc.param0 = video::pack_textureBlendFunc ( video::EBF_DST_COLOR, video::EBF_ZERO, blendfunc.modulate );
274 						blendfunc.isTransparent = 1;
275 						resolved = 1;
276 						break;
277 				} break;
278 
279 			case video::EBF_ONE:
280 				switch ( dstFact )
281 				{
282 					// gl_one gl_zero
283 					case video::EBF_ZERO:
284 						blendfunc.type = video::EMT_SOLID;
285 						blendfunc.isTransparent = 0;
286 						resolved = 1;
287 						break;
288 
289 					// gl_one gl_one
290 					case video::EBF_ONE:
291 						blendfunc.type = video::EMT_TRANSPARENT_ADD_COLOR;
292 						blendfunc.isTransparent = 1;
293 						resolved = 1;
294 						break;
295 				} break;
296 
297 			case video::EBF_SRC_ALPHA:
298 				switch ( dstFact )
299 				{
300 					// gl_src_alpha gl_one_minus_src_alpha
301 					case video::EBF_ONE_MINUS_SRC_ALPHA:
302 						blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
303 						blendfunc.param0 = 1.f/255.f;
304 						blendfunc.isTransparent = 1;
305 						resolved = 1;
306 						break;
307 				} break;
308 
309 			case 11:
310 				// add
311 				blendfunc.type = video::EMT_TRANSPARENT_ADD_COLOR;
312 				blendfunc.isTransparent = 1;
313 				resolved = 1;
314 				break;
315 			case 12:
316 				// filter = gl_dst_color gl_zero or gl_zero gl_src_color
317 				blendfunc.type = video::EMT_ONETEXTURE_BLEND;
318 				blendfunc.param0 = video::pack_textureBlendFunc ( video::EBF_DST_COLOR, video::EBF_ZERO, blendfunc.modulate );
319 				blendfunc.isTransparent = 1;
320 				resolved = 1;
321 				break;
322 			case 13:
323 				// blend = gl_src_alpha gl_one_minus_src_alpha
324 				blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
325 				blendfunc.param0 = 1.f/255.f;
326 				blendfunc.isTransparent = 1;
327 				resolved = 1;
328 				break;
329 			case 14:
330 				// alphafunc ge128
331 				blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
332 				blendfunc.param0 = 0.5f;
333 				blendfunc.isTransparent = 1;
334 				resolved = 1;
335 				break;
336 			case 15:
337 				// alphafunc gt0
338 				blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
339 				blendfunc.param0 = 1.f / 255.f;
340 				blendfunc.isTransparent = 1;
341 				resolved = 1;
342 				break;
343 
344 		}
345 
346 		// use the generic blender
347 		if ( 0 == resolved )
348 		{
349 			blendfunc.type = video::EMT_ONETEXTURE_BLEND;
350 			blendfunc.param0 = video::pack_textureBlendFunc (
351 					(video::E_BLEND_FACTOR) srcFact,
352 					(video::E_BLEND_FACTOR) dstFact,
353 					blendfunc.modulate);
354 
355 			blendfunc.isTransparent = 1;
356 		}
357 	}
358 
359 	// random noise [-1;1]
360 	struct Noiser
361 	{
getNoiser362 		static f32 get ()
363 		{
364 			static u32 RandomSeed = 0x69666966;
365 			RandomSeed = (RandomSeed * 3631 + 1);
366 
367 			f32 value = ( (f32) (RandomSeed & 0x7FFF ) * (1.0f / (f32)(0x7FFF >> 1) ) ) - 1.f;
368 			return value;
369 		}
370 	};
371 
372 	enum eQ3ModifierFunction
373 	{
374 		TCMOD				= 0,
375 		DEFORMVERTEXES		= 1,
376 		RGBGEN				= 2,
377 		TCGEN				= 3,
378 		MAP					= 4,
379 		ALPHAGEN			= 5,
380 
381 		FUNCTION2			= 0x10,
382 		SCROLL				= FUNCTION2 + 1,
383 		SCALE				= FUNCTION2 + 2,
384 		ROTATE				= FUNCTION2 + 3,
385 		STRETCH				= FUNCTION2 + 4,
386 		TURBULENCE			= FUNCTION2 + 5,
387 		WAVE				= FUNCTION2 + 6,
388 
389 		IDENTITY			= FUNCTION2 + 7,
390 		VERTEX				= FUNCTION2 + 8,
391 		TEXTURE				= FUNCTION2 + 9,
392 		LIGHTMAP			= FUNCTION2 + 10,
393 		ENVIRONMENT			= FUNCTION2 + 11,
394 		DOLLAR_LIGHTMAP		= FUNCTION2 + 12,
395 		BULGE				= FUNCTION2 + 13,
396 		AUTOSPRITE			= FUNCTION2 + 14,
397 		AUTOSPRITE2			= FUNCTION2 + 15,
398 		TRANSFORM			= FUNCTION2 + 16,
399 		EXACTVERTEX			= FUNCTION2 + 17,
400 		CONSTANT			= FUNCTION2 + 18,
401 		LIGHTINGSPECULAR	= FUNCTION2 + 19,
402 		MOVE				= FUNCTION2 + 20,
403 		NORMAL				= FUNCTION2 + 21,
404 		IDENTITYLIGHTING	= FUNCTION2 + 22,
405 
406 		WAVE_MODIFIER_FUNCTION	= 0x30,
407 		SINUS				= WAVE_MODIFIER_FUNCTION + 1,
408 		COSINUS				= WAVE_MODIFIER_FUNCTION + 2,
409 		SQUARE				= WAVE_MODIFIER_FUNCTION + 3,
410 		TRIANGLE			= WAVE_MODIFIER_FUNCTION + 4,
411 		SAWTOOTH			= WAVE_MODIFIER_FUNCTION + 5,
412 		SAWTOOTH_INVERSE	= WAVE_MODIFIER_FUNCTION + 6,
413 		NOISE				= WAVE_MODIFIER_FUNCTION + 7,
414 
415 
416 		UNKNOWN				= -2
417 
418 	};
419 
420 	struct SModifierFunction
421 	{
SModifierFunctionSModifierFunction422 		SModifierFunction ()
423 			: masterfunc0 ( UNKNOWN ), masterfunc1( UNKNOWN ), func ( SINUS ),
424 			tcgen( TEXTURE ), rgbgen ( IDENTITY ), alphagen ( UNKNOWN ),
425 			base ( 0 ), amp ( 1 ), phase ( 0 ), frequency ( 1 ),
426 			wave ( 1 ),
427 			x ( 0 ), y ( 0 ), z( 0 ), count( 0 ) {}
428 
429 		// "tcmod","deformvertexes","rgbgen", "tcgen"
430 		eQ3ModifierFunction masterfunc0;
431 		// depends
432 		eQ3ModifierFunction masterfunc1;
433 		// depends
434 		eQ3ModifierFunction func;
435 
436 		eQ3ModifierFunction tcgen;
437 		eQ3ModifierFunction rgbgen;
438 		eQ3ModifierFunction alphagen;
439 
440 		union
441 		{
442 			f32 base;
443 			f32 bulgewidth;
444 		};
445 
446 		union
447 		{
448 			f32 amp;
449 			f32 bulgeheight;
450 		};
451 
452 		f32 phase;
453 
454 		union
455 		{
456 			f32 frequency;
457 			f32 bulgespeed;
458 		};
459 
460 		union
461 		{
462 			f32 wave;
463 			f32 div;
464 		};
465 
466 		f32 x;
467 		f32 y;
468 		f32 z;
469 		u32 count;
470 
evaluateSModifierFunction471 		f32 evaluate ( f32 dt ) const
472 		{
473 			// phase in 0 and 1..
474 			f32 x = core::fract( (dt + phase ) * frequency );
475 			f32 y = 0.f;
476 
477 			switch ( func )
478 			{
479 				case SINUS:
480 					y = sinf ( x * core::PI * 2.f );
481 					break;
482 				case COSINUS:
483 					y = cosf ( x * core::PI * 2.f );
484 					break;
485 				case SQUARE:
486 					y = x < 0.5f ? 1.f : -1.f;
487 					break;
488 				case TRIANGLE:
489 					y = x < 0.5f ? ( 4.f * x ) - 1.f : ( -4.f * x ) + 3.f;
490 					break;
491 				case SAWTOOTH:
492 					y = x;
493 					break;
494 				case SAWTOOTH_INVERSE:
495 					y = 1.f - x;
496 					break;
497 				case NOISE:
498 					y = Noiser::get();
499 					break;
500 				default:
501 					break;
502 			}
503 
504 			return base + ( y * amp );
505 		}
506 
507 
508 	};
509 
getMD3Normal(u32 i,u32 j)510 	inline core::vector3df getMD3Normal ( u32 i, u32 j )
511 	{
512 		const f32 lng = i * 2.0f * core::PI / 255.0f;
513 		const f32 lat = j * 2.0f * core::PI / 255.0f;
514 		return core::vector3df(cosf ( lat ) * sinf ( lng ),
515 				sinf ( lat ) * sinf ( lng ),
516 				cosf ( lng ));
517 	}
518 
519 	//
getModifierFunc(SModifierFunction & fill,const core::stringc & string,u32 & pos)520 	inline void getModifierFunc ( SModifierFunction& fill, const core::stringc &string, u32 &pos )
521 	{
522 		if ( string.size() == 0 )
523 			return;
524 
525 		static const c8 * funclist[] =
526 		{
527 			"sin","cos","square",
528 			"triangle", "sawtooth","inversesawtooth", "noise"
529 		};
530 
531 		fill.func = (eQ3ModifierFunction) isEqual ( string,pos, funclist,7 );
532 		fill.func = fill.func == UNKNOWN ? SINUS : (eQ3ModifierFunction) ((u32) fill.func + WAVE_MODIFIER_FUNCTION + 1);
533 
534 		fill.base = getAsFloat ( string, pos );
535 		fill.amp = getAsFloat ( string, pos );
536 		fill.phase = getAsFloat ( string, pos );
537 		fill.frequency = getAsFloat ( string, pos );
538 	}
539 
540 
541 	// name = "a b c .."
542 	struct SVariable
543 	{
544 		core::stringc name;
545 		core::stringc content;
546 
nameSVariable547 		SVariable ( const c8 * n, const c8 *c = 0 ) : name ( n ), content (c) {}
~SVariableSVariable548 		virtual ~SVariable () {}
549 
clearSVariable550 		void clear ()
551 		{
552 			name = "";
553 			content = "";
554 		}
555 
isValidSVariable556 		s32 isValid () const
557 		{
558 			return name.size();
559 		}
560 
561 		bool operator == ( const SVariable &other ) const
562 		{
563 			return 0 == strcmp ( name.c_str(), other.name.c_str () );
564 		}
565 
566 		bool operator < ( const SVariable &other ) const
567 		{
568 			return 0 > strcmp ( name.c_str(), other.name.c_str () );
569 		}
570 
571 	};
572 
573 
574 	// string database. "a" = "Hello", "b" = "1234.6"
575 	struct SVarGroup
576 	{
SVarGroupSVarGroup577 		SVarGroup () { Variable.setAllocStrategy ( core::ALLOC_STRATEGY_SAFE ); }
~SVarGroupSVarGroup578 		virtual ~SVarGroup () {}
579 
580 		u32 isDefined ( const c8 * name, const c8 * content = 0 ) const
581 		{
582 			for ( u32 i = 0; i != Variable.size (); ++i )
583 			{
584 				if ( 0 == strcmp ( Variable[i].name.c_str(), name ) &&
585 					(  0 == content || strstr ( Variable[i].content.c_str(), content ) )
586 					)
587 				{
588 					return i + 1;
589 				}
590 			}
591 			return 0;
592 		}
593 
594 		// searches for Variable name and returns is content
595 		// if Variable is not found a reference to an Empty String is returned
getSVarGroup596 		const core::stringc &get( const c8 * name ) const
597 		{
598 			SVariable search ( name );
599 			s32 index = Variable.linear_search ( search );
600 			if ( index < 0 )
601 				return irrEmptyStringc;
602 
603 			return Variable [ index ].content;
604 		}
605 
606 		// set the Variable name
607 		void set ( const c8 * name, const c8 * content = 0 )
608 		{
609 			u32 index = isDefined ( name, 0 );
610 			if ( 0 == index )
611 			{
612 				Variable.push_back ( SVariable ( name, content ) );
613 			}
614 			else
615 			{
616 				Variable [ index ].content = content;
617 			}
618 		}
619 
620 
621 		core::array < SVariable > Variable;
622 	};
623 
624 	//! holding a group a variable
625 	struct SVarGroupList: public IReferenceCounted
626 	{
SVarGroupListSVarGroupList627 		SVarGroupList ()
628 		{
629 			VariableGroup.setAllocStrategy ( core::ALLOC_STRATEGY_SAFE );
630 		}
~SVarGroupListSVarGroupList631 		virtual ~SVarGroupList () {}
632 
633 		core::array < SVarGroup > VariableGroup;
634 	};
635 
636 
637 	//! A Parsed Shader Holding Variables ordered in Groups
638 	struct IShader
639 	{
IShaderIShader640 		IShader ()
641 			: ID ( 0 ), VarGroup ( 0 )  {}
~IShaderIShader642 		virtual ~IShader () {}
643 
644 		void operator = (const IShader &other )
645 		{
646 			ID = other.ID;
647 			VarGroup = other.VarGroup;
648 			name = other.name;
649 		}
650 
651 		bool operator == (const IShader &other ) const
652 		{
653 			return 0 == strcmp ( name.c_str(), other.name.c_str () );
654 			//return name == other.name;
655 		}
656 
657 		bool operator < (const IShader &other ) const
658 		{
659 			return strcmp ( name.c_str(), other.name.c_str () ) < 0;
660 			//return name < other.name;
661 		}
662 
getGroupSizeIShader663 		u32 getGroupSize () const
664 		{
665 			if ( 0 == VarGroup )
666 				return 0;
667 			return VarGroup->VariableGroup.size ();
668 		}
669 
getGroupIShader670 		const SVarGroup * getGroup ( u32 stage ) const
671 		{
672 			if ( 0 == VarGroup || stage >= VarGroup->VariableGroup.size () )
673 				return 0;
674 
675 			return &VarGroup->VariableGroup [ stage ];
676 		}
677 
678 		// id
679 		s32 ID;
680 		SVarGroupList *VarGroup; // reference
681 
682 		// Shader: shader name ( also first variable in first Vargroup )
683 		// Entity: classname ( variable in Group(1) )
684 		core::stringc name;
685 	};
686 
687 	typedef IShader IEntity;
688 
689 	typedef core::array < IEntity > tQ3EntityList;
690 
691 	/*
692 		dump shader like original layout, regardless of internal data holding
693 		no recursive folding..
694 	*/
dumpVarGroup(core::stringc & dest,const SVarGroup * group,s32 stack)695 	inline void dumpVarGroup ( core::stringc &dest, const SVarGroup * group, s32 stack )
696 	{
697 		core::stringc buf;
698 		s32 i;
699 
700 
701 		if ( stack > 0 )
702 		{
703 			buf = "";
704 			for ( i = 0; i < stack - 1; ++i )
705 				buf += '\t';
706 
707 			buf += "{\n";
708 			dest.append ( buf );
709 		}
710 
711 		for ( u32 g = 0; g != group->Variable.size(); ++g )
712 		{
713 			buf = "";
714 			for ( i = 0; i < stack; ++i )
715 				buf += '\t';
716 
717 			buf += group->Variable[g].name;
718 			buf += " ";
719 			buf += group->Variable[g].content;
720 			buf += "\n";
721 			dest.append ( buf );
722 		}
723 
724 		if ( stack > 1 )
725 		{
726 			buf = "";
727 			for ( i = 0; i < stack - 1; ++i )
728 				buf += '\t';
729 
730 			buf += "}\n";
731 			dest.append ( buf );
732 		}
733 
734 	}
735 
736 	/*!
737 		dump a Shader or an Entity
738 	*/
739 	inline core::stringc & dumpShader ( core::stringc &dest, const IShader * shader, bool entity = false )
740 	{
741 		if ( 0 == shader )
742 			return dest;
743 
744 		const SVarGroup * group;
745 
746 		const u32 size = shader->VarGroup->VariableGroup.size ();
747 		for ( u32 i = 0; i != size; ++i )
748 		{
749 			group = &shader->VarGroup->VariableGroup[ i ];
750 			dumpVarGroup ( dest, group, core::clamp( (int)i, 0, 2 ) );
751 		}
752 
753 		if ( !entity )
754 		{
755 			if ( size <= 1 )
756 			{
757 				dest.append ( "{\n" );
758 			}
759 			dest.append ( "}\n" );
760 		}
761 		return dest;
762 	}
763 
764 
765 	/*
766 		quake3 doesn't care much about tga & jpg
767 		load one or multiple files stored in name started at startPos to the texture array textures
768 		if texture is not loaded 0 will be added ( to find missing textures easier)
769 	*/
getTextures(tTexArray & textures,const core::stringc & name,u32 & startPos,io::IFileSystem * fileSystem,video::IVideoDriver * driver)770 	inline void getTextures(tTexArray &textures,
771 				const core::stringc &name, u32 &startPos,
772 				io::IFileSystem *fileSystem,
773 				video::IVideoDriver* driver)
774 	{
775 		static const char* extension[] =
776 		{
777 			".jpg",
778 			".jpeg",
779 			".png",
780 			".dds",
781 			".tga",
782 			".bmp",
783 			".pcx"
784 		};
785 
786 		tStringList stringList;
787 		getAsStringList(stringList, -1, name, startPos);
788 
789 		textures.clear();
790 
791 		io::path loadFile;
792 		for ( u32 i = 0; i!= stringList.size (); ++i )
793 		{
794 			video::ITexture* texture = 0;
795 			for (u32 g = 0; g != 7 ; ++g)
796 			{
797 				core::cutFilenameExtension ( loadFile, stringList[i] );
798 
799 				if ( loadFile == "$whiteimage" )
800 				{
801 					texture = driver->getTexture( "$whiteimage" );
802 					if ( 0 == texture )
803 					{
804 						core::dimension2du s ( 2, 2 );
805 						u32 image[4] = { 0xFFFFFFFF, 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF };
806 						video::IImage* w = driver->createImageFromData ( video::ECF_A8R8G8B8, s,&image );
807 						texture = driver->addTexture( "$whiteimage", w );
808 						w->drop ();
809 					}
810 
811 				}
812 				else
813 				if ( loadFile == "$redimage" )
814 				{
815 					texture = driver->getTexture( "$redimage" );
816 					if ( 0 == texture )
817 					{
818 						core::dimension2du s ( 2, 2 );
819 						u32 image[4] = { 0xFFFF0000, 0xFFFF0000,0xFFFF0000,0xFFFF0000 };
820 						video::IImage* w = driver->createImageFromData ( video::ECF_A8R8G8B8, s,&image );
821 						texture = driver->addTexture( "$redimage", w );
822 						w->drop ();
823 					}
824 				}
825 				else
826 				if ( loadFile == "$blueimage" )
827 				{
828 					texture = driver->getTexture( "$blueimage" );
829 					if ( 0 == texture )
830 					{
831 						core::dimension2du s ( 2, 2 );
832 						u32 image[4] = { 0xFF0000FF, 0xFF0000FF,0xFF0000FF,0xFF0000FF };
833 						video::IImage* w = driver->createImageFromData ( video::ECF_A8R8G8B8, s,&image );
834 						texture = driver->addTexture( "$blueimage", w );
835 						w->drop ();
836 					}
837 				}
838 				else
839 				if ( loadFile == "$checkerimage" )
840 				{
841 					texture = driver->getTexture( "$checkerimage" );
842 					if ( 0 == texture )
843 					{
844 						core::dimension2du s ( 2, 2 );
845 						u32 image[4] = { 0xFFFFFFFF, 0xFF000000,0xFF000000,0xFFFFFFFF };
846 						video::IImage* w = driver->createImageFromData ( video::ECF_A8R8G8B8, s,&image );
847 						texture = driver->addTexture( "$checkerimage", w );
848 						w->drop ();
849 					}
850 				}
851 				else
852 				if ( loadFile == "$lightmap" )
853 				{
854 					texture = 0;
855 				}
856 				else
857 				{
858 					loadFile.append ( extension[g] );
859 				}
860 
861 				if ( fileSystem->existFile ( loadFile ) )
862 				{
863 					texture = driver->getTexture( loadFile );
864 					if ( texture )
865 						break;
866 					texture = 0;
867 				}
868 			}
869 			// take 0 Texture
870 			textures.push_back(texture);
871 		}
872 	}
873 
874 
875 	//! Manages various Quake3 Shader Styles
876 	class IShaderManager : public IReferenceCounted
877 	{
878 	};
879 
880 } // end namespace quake3
881 } // end namespace scene
882 } // end namespace irr
883 
884 #endif
885 
886