1 /** Example 022 Material Viewer
2 
3 This example can be used to play around with material settings and watch the results.
4 Only the default non-shader materials are used in here.
5 
6 You have two nodes to make it easier to see which difference your settings will make.
7 Additionally you have one lightscenenode and you can set the global ambient values.
8 */
9 
10 #include <irrlicht.h>
11 #include "driverChoice.h"
12 
13 using namespace irr;
14 
15 #ifdef _MSC_VER
16 #pragma comment(lib, "Irrlicht.lib")
17 #endif
18 
19 /*
20 	Variables within the empty namespace are globals which are restricted to this file.
21 */
22 namespace
23 {
24 	const wchar_t* const DriverTypeNames[] =
25 	{
26 		L"NULL",
27 		L"SOFTWARE",
28 		L"BURNINGSVIDEO",
29 		L"DIRECT3D8",
30 		L"DIRECT3D9",
31 		L"OPENGL",
32 		0,
33 	};
34 
35 	// For the gui id's
36 	enum EGUI_IDS
37 	{
38 		GUI_ID_OPEN_TEXTURE = 1,
39 		GUI_ID_QUIT,
40 		GUI_ID_MAX
41 	};
42 
43 	// Name used in texture selection to clear the textures on the node
44 	const core::stringw CLEAR_TEXTURE = L"CLEAR texture";
45 
46 	// some useful color constants
47 	const video::SColor SCOL_BLACK     = video::SColor(255, 0,   0,   0);
48 	const video::SColor SCOL_BLUE      = video::SColor(255, 0,   0,  255);
49 	const video::SColor SCOL_CYAN      = video::SColor(255, 0,  255, 255);
50 	const video::SColor SCOL_GRAY      = video::SColor(255, 128,128, 128);
51 	const video::SColor SCOL_GREEN     = video::SColor(255, 0,  255,  0);
52 	const video::SColor SCOL_MAGENTA   = video::SColor(255, 255, 0,  255);
53 	const video::SColor SCOL_RED       = video::SColor(255, 255, 0,   0);
54 	const video::SColor SCOL_YELLOW    = video::SColor(255, 255, 255, 0);
55 	const video::SColor SCOL_WHITE     = video::SColor(255, 255, 255, 255);
56 };	// namespace
57 
58 /*
59 	Returns a new unique number on each call.
60 */
makeUniqueId()61 s32 makeUniqueId()
62 {
63 	static int unique = GUI_ID_MAX;
64 	++unique;
65 	return unique;
66 }
67 
68 /*
69 	Find out which vertex-type is needed for the given material type.
70 */
getVertexTypeForMaterialType(video::E_MATERIAL_TYPE materialType)71 video::E_VERTEX_TYPE getVertexTypeForMaterialType(video::E_MATERIAL_TYPE materialType)
72 {
73 	using namespace video;
74 
75 	switch ( materialType )
76 	{
77 		case EMT_SOLID:
78 			return EVT_STANDARD;
79 
80 		case EMT_SOLID_2_LAYER:
81 			return EVT_STANDARD;
82 
83 		case EMT_LIGHTMAP:
84 		case EMT_LIGHTMAP_ADD:
85 		case EMT_LIGHTMAP_M2:
86 		case EMT_LIGHTMAP_M4:
87 		case EMT_LIGHTMAP_LIGHTING:
88 		case EMT_LIGHTMAP_LIGHTING_M2:
89 		case EMT_LIGHTMAP_LIGHTING_M4:
90 			return EVT_2TCOORDS;
91 
92 		case EMT_DETAIL_MAP:
93 			return EVT_2TCOORDS;
94 
95 		case EMT_SPHERE_MAP:
96 			return EVT_STANDARD;
97 
98 		case EMT_REFLECTION_2_LAYER:
99 			return EVT_2TCOORDS;
100 
101 		case EMT_TRANSPARENT_ADD_COLOR:
102 			return EVT_STANDARD;
103 
104 		case EMT_TRANSPARENT_ALPHA_CHANNEL:
105 			return EVT_STANDARD;
106 
107 		case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
108 			return EVT_STANDARD;
109 
110 		case EMT_TRANSPARENT_VERTEX_ALPHA:
111 			return EVT_STANDARD;
112 
113 		case EMT_TRANSPARENT_REFLECTION_2_LAYER:
114 			return EVT_2TCOORDS;
115 
116 		case EMT_NORMAL_MAP_SOLID:
117 		case EMT_NORMAL_MAP_TRANSPARENT_ADD_COLOR:
118 		case EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA:
119 		case EMT_PARALLAX_MAP_SOLID:
120 		case EMT_PARALLAX_MAP_TRANSPARENT_ADD_COLOR:
121 		case EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA:
122 			return EVT_TANGENTS;
123 
124 		case EMT_ONETEXTURE_BLEND:
125 			return EVT_STANDARD;
126 
127 		case EMT_FORCE_32BIT:
128 			return EVT_STANDARD;
129 	}
130 	return EVT_STANDARD;
131 }
132 
133 /*
134 	Custom GUI-control to edit colorvalues.
135 */
136 class CColorControl : public gui::IGUIElement
137 {
138 public:
139 	// Constructor
CColorControl(gui::IGUIEnvironment * guiEnv,const core::position2d<s32> & pos,const wchar_t * text,IGUIElement * parent,s32 id=-1)140 	CColorControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, IGUIElement* parent, s32 id=-1 )
141 		: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect< s32 >(pos, pos+core::dimension2d<s32>(80, 75)))
142 		, DirtyFlag(true)
143 		, ColorStatic(0)
144 		, EditAlpha(0)
145 		, EditRed(0)
146 		, EditGreen(0)
147 		, EditBlue(0)
148 	{
149 		using namespace gui;
150 		ButtonSetId = makeUniqueId();
151 
152 		const core::rect< s32 > rectControls(0,0,AbsoluteRect.getWidth(),AbsoluteRect.getHeight() );
153 		IGUIStaticText * groupElement =	guiEnv->addStaticText (L"", rectControls, true, false, this, -1, false);
154 		groupElement->setNotClipped(true);
155 
156 		guiEnv->addStaticText (text, core::rect<s32>(0,0,80,15), false, false, groupElement, -1, false);
157 
158 		EditAlpha = addEditForNumbers(guiEnv, core::position2d<s32>(0,15), L"a", -1, groupElement );
159 		EditRed = addEditForNumbers(guiEnv, core::position2d<s32>(0,30), L"r", -1, groupElement );
160 		EditGreen = addEditForNumbers(guiEnv, core::position2d<s32>(0,45), L"g", -1, groupElement );
161 		EditBlue = addEditForNumbers(guiEnv, core::position2d<s32>(0,60), L"b", -1, groupElement );
162 
163 		ColorStatic = guiEnv->addStaticText (L"", core::rect<s32>(60,15,80,75), true, false, groupElement, -1, true);
164 
165 		guiEnv->addButton (core::rect<s32>(60,35,80,50), groupElement, ButtonSetId, L"set");
166 		SetEditsFromColor(Color);
167 	}
168 
169 	// event receiver
OnEvent(const SEvent & event)170 	virtual bool OnEvent(const SEvent &event)
171 	{
172 		if ( event.EventType != EET_GUI_EVENT )
173 			return false;
174 
175 		if ( event.GUIEvent.Caller->getID() == ButtonSetId && event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
176 		{
177 			Color = GetColorFromEdits();
178 			SetEditsFromColor(Color);
179 		}
180 
181 		return false;
182 	}
183 
184 	// set the color values
setColor(const video::SColor & col)185 	void setColor(const video::SColor& col)
186 	{
187 		DirtyFlag = true;
188 		Color = col;
189 		SetEditsFromColor(Color);
190 	}
191 
192 	// get the color values
getColor() const193 	const video::SColor& getColor() const
194 	{
195 		return Color;
196 	}
197 
198 	// To reset the dirty flag
resetDirty()199 	void resetDirty()
200 	{
201 		DirtyFlag = false;
202 	}
203 
204 	// when the color was changed the dirty flag is set
isDirty() const205 	bool isDirty() const
206 	{
207 		return DirtyFlag;
208 	};
209 
210 protected:
211 
212 	// Add a staticbox for a description + an editbox so users can enter numbers
addEditForNumbers(gui::IGUIEnvironment * guiEnv,const core::position2d<s32> & pos,const wchar_t * text,s32 id,gui::IGUIElement * parent)213 	gui::IGUIEditBox* addEditForNumbers(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, s32 id, gui::IGUIElement * parent)
214 	{
215 		using namespace gui;
216 
217 		core::rect< s32 > rect(pos, pos+core::dimension2d<s32>(10, 15));
218 		guiEnv->addStaticText (text, rect, false, false, parent, -1, false);
219 		rect += core::position2d<s32>( 20, 0 );
220 		rect.LowerRightCorner.X += 20;
221 		gui::IGUIEditBox* edit = guiEnv->addEditBox(L"0", rect, true, parent, id);
222 		return edit;
223 	}
224 
225 	// Get the color value from the editfields
GetColorFromEdits()226 	video::SColor GetColorFromEdits()
227 	{
228 		video::SColor col;
229 
230 		if (EditAlpha)
231 		{
232 			u32 alpha = core::strtoul10(core::stringc(EditAlpha->getText()).c_str());
233 			if (alpha > 255)
234 				alpha = 255;
235 			col.setAlpha(alpha);
236 		}
237 
238 		if (EditRed)
239 		{
240 			u32 red = core::strtoul10(core::stringc(EditRed->getText()).c_str());
241 			if (red > 255)
242 				red = 255;
243 			col.setRed(red);
244 		}
245 
246 		if (EditGreen)
247 		{
248 			u32 green = core::strtoul10(core::stringc(EditGreen->getText()).c_str());
249 			if (green > 255)
250 				green = 255;
251 			col.setGreen(green);
252 		}
253 
254 		if (EditBlue)
255 		{
256 			u32 blue = core::strtoul10(core::stringc(EditBlue->getText()).c_str());
257 			if (blue > 255)
258 				blue = 255;
259 			col.setBlue(blue);
260 		}
261 
262 		return col;
263 	}
264 
265 	// Fill the editfields with the value for the given color
SetEditsFromColor(video::SColor col)266 	void SetEditsFromColor(video::SColor col)
267 	{
268 		DirtyFlag = true;
269 		if ( EditAlpha )
270 			EditAlpha->setText( core::stringw(col.getAlpha()).c_str() );
271 		if ( EditRed )
272 			EditRed->setText( core::stringw(col.getRed()).c_str() );
273 		if ( EditGreen )
274 			EditGreen->setText( core::stringw(col.getGreen()).c_str() );
275 		if ( EditBlue )
276 			EditBlue->setText( core::stringw(col.getBlue()).c_str() );
277 		if ( ColorStatic )
278 			ColorStatic->setBackgroundColor(col);
279 	}
280 
281 private:
282 
283 	bool DirtyFlag;
284 	video::SColor Color;
285 	s32 ButtonSetId;
286 	gui::IGUIStaticText * ColorStatic;
287 	gui::IGUIEditBox * EditAlpha;
288 	gui::IGUIEditBox * EditRed;
289 	gui::IGUIEditBox * EditGreen;
290 	gui::IGUIEditBox * EditBlue;
291 };
292 
293 /*
294 	Custom GUI-control for to edit all colors typically used in materials and lights
295 */
296 class CAllColorsControl : public gui::IGUIElement
297 {
298 public:
299 	// Constructor
CAllColorsControl(gui::IGUIEnvironment * guiEnv,const core::position2d<s32> & pos,const wchar_t * description,bool hasEmissive,IGUIElement * parent,s32 id=-1)300 	CAllColorsControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description, bool hasEmissive, IGUIElement* parent, s32 id=-1)
301 		: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(60,250)))
302 		, ControlAmbientColor(0), ControlDiffuseColor(0), ControlSpecularColor(0), ControlEmissiveColor(0)
303 	{
304 		core::rect<s32> rect(0, 0, 60, 15);
305 		guiEnv->addStaticText (description, rect, false, false, this, -1, false);
306 		createColorControls(guiEnv, core::position2d<s32>(0, 15), hasEmissive);
307 	}
308 
309 	// Destructor
~CAllColorsControl()310 	virtual ~CAllColorsControl()
311 	{
312 		ControlAmbientColor->drop();
313 		ControlDiffuseColor->drop();
314 		if ( ControlEmissiveColor )
315 			ControlEmissiveColor->drop();
316 		ControlSpecularColor->drop();
317 	}
318 
319 	// Set the color values to those within the material
setColorsToMaterialColors(const video::SMaterial & material)320 	void setColorsToMaterialColors(const video::SMaterial & material)
321 	{
322 		ControlAmbientColor->setColor(material.AmbientColor);
323 		ControlDiffuseColor->setColor(material.DiffuseColor);
324 		ControlEmissiveColor->setColor(material.EmissiveColor);
325 		ControlSpecularColor->setColor(material.SpecularColor);
326 	}
327 
328 	// Update all changed colors in the material
updateMaterialColors(video::SMaterial & material)329 	void updateMaterialColors(video::SMaterial & material)
330 	{
331 		if ( ControlAmbientColor->isDirty() )
332 			material.AmbientColor = ControlAmbientColor->getColor();
333 		if ( ControlDiffuseColor->isDirty() )
334 			material.DiffuseColor = ControlDiffuseColor->getColor();
335 		if ( ControlEmissiveColor->isDirty() )
336 			material.EmissiveColor = ControlEmissiveColor->getColor();
337 		if ( ControlSpecularColor->isDirty() )
338 			material.SpecularColor = ControlSpecularColor->getColor();
339 	}
340 
341 	// Set the color values to those from the light data
setColorsToLightDataColors(const video::SLight & lightData)342 	void setColorsToLightDataColors(const video::SLight & lightData)
343 	{
344 		ControlAmbientColor->setColor(lightData.AmbientColor.toSColor());
345 		ControlAmbientColor->setColor(lightData.DiffuseColor.toSColor());
346 		ControlAmbientColor->setColor(lightData.SpecularColor.toSColor());
347 	}
348 
349 	// Update all changed colors in the light data
updateLightColors(video::SLight & lightData)350 	void updateLightColors(video::SLight & lightData)
351 	{
352 		if ( ControlAmbientColor->isDirty() )
353 			lightData.AmbientColor = video::SColorf( ControlAmbientColor->getColor() );
354 		if ( ControlDiffuseColor->isDirty() )
355 			lightData.DiffuseColor = video::SColorf( ControlDiffuseColor->getColor() );
356 		if ( ControlSpecularColor->isDirty() )
357 			lightData.SpecularColor = video::SColorf(ControlSpecularColor->getColor() );
358 	}
359 
360 	// To reset the dirty flags
resetDirty()361 	void resetDirty()
362 	{
363 		ControlAmbientColor->resetDirty();
364 		ControlDiffuseColor->resetDirty();
365 		ControlSpecularColor->resetDirty();
366 		if ( ControlEmissiveColor )
367 			ControlEmissiveColor->resetDirty();
368 	}
369 
370 protected:
createColorControls(gui::IGUIEnvironment * guiEnv,const core::position2d<s32> & pos,bool hasEmissive)371 	void createColorControls(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, bool hasEmissive)
372 	{
373 		ControlAmbientColor = new CColorControl( guiEnv, pos, L"ambient", this);
374 		ControlDiffuseColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 75), L"diffuse", this );
375 		ControlSpecularColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 150), L"specular", this );
376 		if ( hasEmissive )
377 		{
378 			ControlEmissiveColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 225), L"emissive", this );
379 		}
380 	}
381 
382 private:
383 	CColorControl*	ControlAmbientColor;
384 	CColorControl*	ControlDiffuseColor;
385 	CColorControl*	ControlSpecularColor;
386 	CColorControl*	ControlEmissiveColor;
387 };
388 
389 /*
390 	GUI-Control to offer a selection of available textures.
391 */
392 class CTextureControl : public gui::IGUIElement
393 {
394 public:
CTextureControl(gui::IGUIEnvironment * guiEnv,video::IVideoDriver * driver,const core::position2d<s32> & pos,IGUIElement * parent,s32 id=-1)395 	CTextureControl(gui::IGUIEnvironment* guiEnv, video::IVideoDriver * driver, const core::position2d<s32> & pos, IGUIElement* parent, s32 id=-1)
396 	: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(100,15)))
397 	, DirtyFlag(true), ComboTexture(0)
398 	{
399 		core::rect<s32> rectCombo(0, 0, AbsoluteRect.getWidth(),AbsoluteRect.getHeight());
400 		ComboTexture = guiEnv->addComboBox (rectCombo, this);
401 		updateTextures(driver);
402 	}
403 
OnEvent(const SEvent & event)404 	virtual bool OnEvent(const SEvent &event)
405 	{
406 		if ( event.EventType != EET_GUI_EVENT )
407 			return false;
408 
409 		if ( event.GUIEvent.Caller == ComboTexture && event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
410 		{
411 			DirtyFlag = true;
412 		}
413 
414 		return false;
415 	}
416 
417 	// Workaround for a problem with comboboxes.
418 	// We have to get in front when the combobox wants to get in front or combobox-list might be drawn below other elements.
bringToFront(IGUIElement * element)419 	virtual bool bringToFront(IGUIElement* element)
420 	{
421 		bool result = gui::IGUIElement::bringToFront(element);
422 		if ( Parent && element == ComboTexture )
423 			result &= Parent->bringToFront(this);
424 		return result;
425 	}
426 
427 	// return selected texturename (if any, otherwise 0)
getSelectedTextureName() const428 	const wchar_t * getSelectedTextureName() const
429 	{
430 		s32 selected = ComboTexture->getSelected();
431 		if ( selected < 0 )
432 			return 0;
433 		return ComboTexture->getItem(selected);
434 	}
435 
436 	// reset the dirty flag
resetDirty()437 	void resetDirty()
438 	{
439 		DirtyFlag = false;
440 	}
441 
442 	// when the texture was changed the dirty flag is set
isDirty() const443 	bool isDirty() const
444 	{
445 		return DirtyFlag;
446 	};
447 
448 	// Put the names of all currently loaded textures in a combobox
updateTextures(video::IVideoDriver * driver)449 	void updateTextures(video::IVideoDriver * driver)
450 	{
451 		s32 oldSelected = ComboTexture->getSelected();
452 		s32 selectNew = -1;
453 		core::stringw oldTextureName;
454 		if ( oldSelected >= 0 )
455 		{
456 			oldTextureName = ComboTexture->getItem(oldSelected);
457 		}
458 		ComboTexture->clear();
459 		for ( u32 i=0; i < driver->getTextureCount(); ++i )
460 		{
461 			video::ITexture * texture = driver->getTextureByIndex(i);
462 			core::stringw name( texture->getName() );
463 			ComboTexture->addItem( name.c_str() );
464 			if ( !oldTextureName.empty() && selectNew < 0 && name == oldTextureName )
465 				selectNew = i;
466 		}
467 
468 		// add another name which can be used to clear the texture
469 		ComboTexture->addItem( CLEAR_TEXTURE.c_str() );
470 		if ( CLEAR_TEXTURE == oldTextureName )
471 			selectNew = ComboTexture->getItemCount()-1;
472 
473 		if ( selectNew >= 0 )
474 			ComboTexture->setSelected(selectNew);
475 
476 		DirtyFlag = true;
477 	}
478 
479 private:
480 	bool DirtyFlag;
481 	gui::IGUIComboBox * ComboTexture;
482 };
483 
484 /*
485 	Control which allows setting some of the material values for a meshscenenode
486 */
487 struct SMeshNodeControl
488 {
489 	// constructor
SMeshNodeControlSMeshNodeControl490 	SMeshNodeControl()
491 		: Initialized(false), Driver(0), MeshManipulator(0), SceneNode(0), SceneNode2T(0), SceneNodeTangents(0)
492 		, AllColorsControl(0), ButtonLighting(0), InfoLighting(0), ComboMaterial(0), TextureControl1(0), TextureControl2(0), ControlVertexColors(0)
493 	{
494 	}
495 
496 	// Destructor
~SMeshNodeControlSMeshNodeControl497 	virtual ~SMeshNodeControl()
498 	{
499 		if ( TextureControl1 )
500 			TextureControl1->drop();
501 		if ( TextureControl2 )
502 			TextureControl2->drop();
503 		if ( ControlVertexColors )
504 			ControlVertexColors->drop();
505 		if ( AllColorsControl )
506 			AllColorsControl->drop();
507 	}
508 
initSMeshNodeControl509 	void init(scene::IMeshSceneNode* node, IrrlichtDevice * device, const core::position2d<s32> & pos, const wchar_t * description)
510 	{
511 		if ( Initialized || !node || !device) // initializing twice or with invalid data not allowed
512 			return;
513 
514 		Driver = device->getVideoDriver ();
515 		gui::IGUIEnvironment* guiEnv = device->getGUIEnvironment();
516 		scene::ISceneManager* smgr = device->getSceneManager();
517 		MeshManipulator = smgr->getMeshManipulator();
518 
519 		SceneNode = node;
520 		scene::IMeshManipulator * meshManip = smgr->getMeshManipulator();
521 
522 		scene::IMesh * mesh2T = meshManip->createMeshWith2TCoords(node->getMesh());
523 		SceneNode2T = smgr->addMeshSceneNode(mesh2T, 0, -1, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
524 		mesh2T->drop();
525 
526 		scene::IMesh * meshTangents = meshManip->createMeshWithTangents(node->getMesh(), false, false, false);
527 		SceneNodeTangents = smgr->addMeshSceneNode(meshTangents, 0, -1
528 											, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
529 		meshTangents->drop();
530 
531 		video::SMaterial & material = SceneNode->getMaterial(0);
532 		material.Lighting = true;
533 		AllColorsControl = new CAllColorsControl(guiEnv, pos, description, true, guiEnv->getRootGUIElement());
534 		AllColorsControl->setColorsToMaterialColors(material);
535 
536 		core::rect<s32> rectBtn(pos + core::position2d<s32>(0, 320), core::dimension2d<s32>(60, 15));
537 		ButtonLighting = guiEnv->addButton (rectBtn, 0, -1, L"Lighting");
538 		ButtonLighting->setIsPushButton(true);
539 		ButtonLighting->setPressed(material.Lighting);
540 		core::rect<s32> rectInfo( rectBtn.LowerRightCorner.X, rectBtn.UpperLeftCorner.Y, rectBtn.LowerRightCorner.X+40, rectBtn.UpperLeftCorner.Y+15 );
541 		InfoLighting = guiEnv->addStaticText(L"", rectInfo, true, false );
542 		InfoLighting->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER );
543 
544 		core::rect<s32> rectCombo(pos.X, rectBtn.LowerRightCorner.Y, pos.X+100, rectBtn.LowerRightCorner.Y+15);
545 		ComboMaterial = guiEnv->addComboBox (rectCombo);
546 		for ( int i=0; i <= (int)video::EMT_ONETEXTURE_BLEND; ++i )
547 		{
548 			ComboMaterial->addItem( core::stringw(video::sBuiltInMaterialTypeNames[i]).c_str() );
549 		}
550 		ComboMaterial->setSelected( (s32)material.MaterialType );
551 
552 		core::position2d<s32> posTex(rectCombo.UpperLeftCorner.X,rectCombo.LowerRightCorner.Y);
553 		TextureControl1 = new CTextureControl(guiEnv, Driver, posTex, guiEnv->getRootGUIElement());
554 		posTex.Y += 15;
555 		TextureControl2 = new CTextureControl(guiEnv, Driver, posTex, guiEnv->getRootGUIElement());
556 
557 		core::position2d<s32> posVertexColors( posTex.X, posTex.Y + 15);
558 		ControlVertexColors = new CColorControl( guiEnv, posVertexColors, L"Vertex colors", guiEnv->getRootGUIElement());
559 
560 		video::S3DVertex * vertices =  (video::S3DVertex *)node->getMesh()->getMeshBuffer(0)->getVertices();
561 		if ( vertices )
562 		{
563 			ControlVertexColors->setColor(vertices[0].Color);
564 		}
565 
566 		Initialized = true;
567 	}
568 
updateSMeshNodeControl569 	void update()
570 	{
571 		if ( !Initialized )
572 			return;
573 
574 		video::SMaterial & material = SceneNode->getMaterial(0);
575 		video::SMaterial & material2T = SceneNode2T->getMaterial(0);
576 		video::SMaterial & materialTangents = SceneNodeTangents->getMaterial(0);
577 
578 		s32 selectedMaterial = ComboMaterial->getSelected();
579 		if ( selectedMaterial >= (s32)video::EMT_SOLID && selectedMaterial <= (s32)video::EMT_ONETEXTURE_BLEND)
580 		{
581 			video::E_VERTEX_TYPE vertexType = getVertexTypeForMaterialType((video::E_MATERIAL_TYPE)selectedMaterial);
582 			switch ( vertexType )
583 			{
584 				case video::EVT_STANDARD:
585 					material.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
586 					SceneNode->setVisible(true);
587 					SceneNode2T->setVisible(false);
588 					SceneNodeTangents->setVisible(false);
589 					break;
590 				case video::EVT_2TCOORDS:
591 					material2T.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
592 					SceneNode->setVisible(false);
593 					SceneNode2T->setVisible(true);
594 					SceneNodeTangents->setVisible(false);
595 					break;
596 				case video::EVT_TANGENTS:
597 					materialTangents.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
598 					SceneNode->setVisible(false);
599 					SceneNode2T->setVisible(false);
600 					SceneNodeTangents->setVisible(true);
601 					break;
602 			}
603 		}
604 
605 		updateMaterial(material);
606 		updateMaterial(material2T);
607 		updateMaterial(materialTangents);
608 
609 		if ( ButtonLighting->isPressed() )
610 			InfoLighting->setText(L"on");
611 		else
612 			InfoLighting->setText(L"off");
613 
614 		AllColorsControl->resetDirty();
615 		TextureControl1->resetDirty();
616 		TextureControl2->resetDirty();
617 		ControlVertexColors->resetDirty();
618 	}
619 
updateTexturesSMeshNodeControl620 	void updateTextures()
621 	{
622 		TextureControl1->updateTextures(Driver);
623 		TextureControl2->updateTextures(Driver);
624 	}
625 
626 protected:
627 
updateMaterialSMeshNodeControl628 	void updateMaterial(video::SMaterial & material)
629 	{
630 		AllColorsControl->updateMaterialColors(material);
631 		material.Lighting = ButtonLighting->isPressed();
632 		if ( TextureControl1->isDirty() )
633 		{
634 			material.TextureLayer[0].Texture = Driver->getTexture( io::path(TextureControl1->getSelectedTextureName()) );
635 		}
636 		if ( TextureControl2->isDirty() )
637 		{
638 			material.TextureLayer[1].Texture = Driver->getTexture( io::path(TextureControl2->getSelectedTextureName()) );
639 		}
640 		if ( ControlVertexColors->isDirty() )
641 		{
642 			MeshManipulator->setVertexColors (SceneNode->getMesh(), ControlVertexColors->getColor());
643 			MeshManipulator->setVertexColors (SceneNode2T->getMesh(), ControlVertexColors->getColor());
644 			MeshManipulator->setVertexColors (SceneNodeTangents->getMesh(), ControlVertexColors->getColor());
645 		}
646 	}
647 
648 	bool Initialized;
649 	video::IVideoDriver * 		Driver;
650 	scene::IMeshManipulator* 	MeshManipulator;
651 	scene::IMeshSceneNode* 		SceneNode;
652 	scene::IMeshSceneNode* 		SceneNode2T;
653 	scene::IMeshSceneNode* 		SceneNodeTangents;
654 	CAllColorsControl* 			AllColorsControl;
655 	gui::IGUIButton * 			ButtonLighting;
656 	gui::IGUIStaticText* 		InfoLighting;
657 	gui::IGUIComboBox * 		ComboMaterial;
658 	CTextureControl* 			TextureControl1;
659 	CTextureControl* 			TextureControl2;
660 	CColorControl*				ControlVertexColors;
661 };
662 
663 /*
664 	Control to allow setting the color values of a lightscenenode.
665 */
666 struct SLightNodeControl
667 {
668 	// constructor
SLightNodeControlSLightNodeControl669 	SLightNodeControl() : Initialized(false), SceneNode(0), AllColorsControl(0)
670 	{
671 	}
672 
~SLightNodeControlSLightNodeControl673 	virtual ~SLightNodeControl()
674 	{
675 		if ( AllColorsControl )
676 			AllColorsControl->drop();
677 	}
678 
initSLightNodeControl679 	void init(scene::ILightSceneNode* node, gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description)
680 	{
681 		if ( Initialized || !node || !guiEnv) // initializing twice or with invalid data not allowed
682 			return;
683 		SceneNode = node;
684 		AllColorsControl = new CAllColorsControl(guiEnv, pos, description, false, guiEnv->getRootGUIElement());
685 		const video::SLight & lightData = SceneNode->getLightData();
686 		AllColorsControl->setColorsToLightDataColors(lightData);
687 		Initialized = true;
688 	}
689 
updateSLightNodeControl690 	void update()
691 	{
692 		if ( !Initialized )
693 			return;
694 
695 		video::SLight & lightData = SceneNode->getLightData();
696 		AllColorsControl->updateLightColors(lightData);
697 	}
698 
699 protected:
700 	bool Initialized;
701 	scene::ILightSceneNode* SceneNode;
702 	CAllColorsControl* AllColorsControl;
703 };
704 
705 /*
706 	Application configuration
707 */
708 struct SConfig
709 {
SConfigSConfig710 	SConfig()
711 	: RenderInBackground(true)
712 	, DriverType(video::EDT_BURNINGSVIDEO)
713 	, ScreenSize(640, 480)
714 	{
715 	}
716 
717 	bool RenderInBackground;
718 	video::E_DRIVER_TYPE DriverType;
719 	core::dimension2d<u32> ScreenSize;
720 };
721 
722 /*
723 	Main application class
724 */
725 class CApp : public IEventReceiver
726 {
727 	friend int main(int argc, char *argv[]);
728 
729 public:
730 	// constructor
CApp()731 	CApp()
732 	: IsRunning(false)
733 	, Device(0)
734 	, Camera(0)
735 	, GlobalAmbient(0)
736 	{
737 	}
738 
739 	// destructor
~CApp()740 	~CApp()
741 	{
742 	}
743 
744 	// stop running - will quit at end of mainloop
stopApp()745 	void stopApp()
746 	{
747 		IsRunning = false;
748 	}
749 
750 	// Event handler
OnEvent(const SEvent & event)751 	virtual bool OnEvent(const SEvent &event)
752 	{
753 		if (event.EventType == EET_GUI_EVENT)
754 		{
755 			gui::IGUIEnvironment* env = Device->getGUIEnvironment();
756 
757 			switch(event.GUIEvent.EventType)
758 			{
759 				case gui::EGET_MENU_ITEM_SELECTED:
760 				{
761 					gui::IGUIContextMenu* menu = (gui::IGUIContextMenu*)event.GUIEvent.Caller;
762 					s32 id = menu->getItemCommandId(menu->getSelectedItem());
763 
764 					switch(id)
765 					{
766 						case GUI_ID_OPEN_TEXTURE: // File -> Open Texture
767 							env->addFileOpenDialog(L"Please select a texture file to open");
768 						break;
769 						case GUI_ID_QUIT: // File -> Quit
770 							stopApp();
771 						break;
772 					}
773 				}
774 				break;
775 
776 				case gui::EGET_FILE_SELECTED:
777 				{
778 					// load the model file, selected in the file open dialog
779 					gui::IGUIFileOpenDialog* dialog =
780 						(gui::IGUIFileOpenDialog*)event.GUIEvent.Caller;
781 					loadTexture(io::path(dialog->getFileName()).c_str());
782 				}
783 				break;
784 
785 				default:
786 				break;
787 			}
788 		}
789 
790 		return false;
791 	}
792 
793 protected:
794 
795 	// Application initialization
796 	// returns true when it was successful initialized, otherwise false.
init(int argc,char * argv[])797 	bool init(int argc, char *argv[])
798 	{
799 		// ask user for driver
800 		Config.DriverType=driverChoiceConsole();
801 		if (Config.DriverType==video::EDT_COUNT)
802 			return false;
803 
804 		// create the device with the settings from our config
805 		Device = createDevice(Config.DriverType, Config.ScreenSize);
806 		if (!Device)
807 			return false;
808 		Device->setWindowCaption( DriverTypeNames[Config.DriverType] );
809 		Device->setEventReceiver(this);
810 
811 		scene::ISceneManager* smgr = Device->getSceneManager();
812 		video::IVideoDriver * driver = Device->getVideoDriver ();
813 		gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
814 
815 		// set a nicer font
816 		gui::IGUISkin* skin = guiEnv->getSkin();
817 		gui::IGUIFont* font = guiEnv->getFont("../../media/fonthaettenschweiler.bmp");
818 		if (font)
819 			skin->setFont(font);
820 
821 		// remove some alpha value because it makes those menus harder to read otherwise
822 		video::SColor col3dHighLight( skin->getColor(gui::EGDC_APP_WORKSPACE) );
823 		col3dHighLight.setAlpha(255);
824 		video::SColor colHighLight( col3dHighLight );
825 		skin->setColor(gui::EGDC_HIGH_LIGHT, colHighLight );
826 		skin->setColor(gui::EGDC_3D_HIGH_LIGHT, col3dHighLight );
827 
828 		// Add some textures which are useful to test material settings
829 		createDefaultTextures(driver);
830 
831 		// create a menu
832 		gui::IGUIContextMenu * menuBar = guiEnv->addMenu();
833 		menuBar->addItem(L"File", -1, true, true);
834 
835 		gui::IGUIContextMenu* subMenuFile = menuBar->getSubMenu(0);
836 		subMenuFile->addItem(L"Open texture ...", GUI_ID_OPEN_TEXTURE);
837 		subMenuFile->addSeparator();
838 		subMenuFile->addItem(L"Quit", GUI_ID_QUIT);
839 
840 		// a static camera
841 		Camera = smgr->addCameraSceneNode (0, core::vector3df(0, 0, 0),
842 											core::vector3df(0, 0, 100),
843 											-1);
844 
845 		// add the nodes which are used to show the materials
846 		scene::IMeshSceneNode* nodeL = smgr->addCubeSceneNode (30.0f, 0, -1,
847 										   core::vector3df(-35, 0, 100),
848 										   core::vector3df(0, 0, 0),
849 										   core::vector3df(1.0f, 1.0f, 1.0f));
850 		NodeLeft.init( nodeL, Device, core::position2d<s32>(10,20), L"left node" );
851 
852 		scene::IMeshSceneNode* nodeR = smgr->addCubeSceneNode (30.0f, 0, -1,
853 										   core::vector3df(35, 0, 100),
854 										   core::vector3df(0, 0, 0),
855 										   core::vector3df(1.0f, 1.0f, 1.0f));
856 		NodeRight.init( nodeR, Device, core::position2d<s32>(530,20), L"right node" );
857 
858 		// add one light
859 		scene::ILightSceneNode* nodeLight = smgr->addLightSceneNode(0, core::vector3df(0, 0, 0),
860 														video::SColorf(1.0f, 1.0f, 1.0f),
861 														100.0f);
862 		LightControl.init(nodeLight, guiEnv, core::position2d<s32>(270,20), L"light" );
863 
864 		// one large cube around everything. That's mainly to make the light more obvious.
865 		scene::IMeshSceneNode* backgroundCube = smgr->addCubeSceneNode (200.0f, 0, -1, core::vector3df(0, 0, 0),
866 										   core::vector3df(45, 0, 0),
867 										   core::vector3df(1.0f, 1.0f, 1.0f));
868 		backgroundCube->getMaterial(0).BackfaceCulling = false;	 		// we are within the cube, so we have to disable backface culling to see it
869 		backgroundCube->getMaterial(0).EmissiveColor.set(255,50,50,50);	// we keep some self lighting to keep texts visible
870 
871 		// set the ambient light value
872 		GlobalAmbient = new CColorControl( guiEnv, core::position2d<s32>(270, 300), L"global ambient", guiEnv->getRootGUIElement());
873 		GlobalAmbient->setColor( smgr->getAmbientLight().toSColor() );
874 
875 		return true;
876 	}
877 
878 	// Update one frame
update()879 	bool update()
880 	{
881 		using namespace irr;
882 
883 		video::IVideoDriver* videoDriver =  Device->getVideoDriver();
884 		if ( !Device->run() )
885 			return false;
886 
887 		if ( Device->isWindowActive() || Config.RenderInBackground )
888 		{
889 			gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
890 			scene::ISceneManager* smgr = Device->getSceneManager();
891 			gui::IGUISkin * skin = guiEnv->getSkin();
892 
893 			// update our controls
894 			NodeLeft.update();
895 			NodeRight.update();
896 			LightControl.update();
897 
898 			// update ambient light settings
899 			if ( GlobalAmbient->isDirty() )
900 			{
901 				smgr->setAmbientLight( GlobalAmbient->getColor() );
902 				GlobalAmbient->resetDirty();
903 			}
904 
905 			// draw everything
906 			video::SColor bkColor( skin->getColor(gui::EGDC_APP_WORKSPACE) );
907 			videoDriver->beginScene(true, true, bkColor);
908 
909 			smgr->drawAll();
910 			guiEnv->drawAll();
911 
912 			videoDriver->endScene();
913 		}
914 
915 		return true;
916 	}
917 
918 	// Run the application. Our main loop.
run()919 	void run()
920 	{
921 		IsRunning = true;
922 
923 		if ( !Device )
924 			return;
925 
926 		// main application loop
927 		while(IsRunning)
928 		{
929 			if ( !update() )
930 				break;
931 
932 			Device->sleep( 5 );
933 		}
934 	}
935 
936 	// Close down the application
quit()937 	void quit()
938 	{
939 		IsRunning = false;
940 		GlobalAmbient->drop();
941 		GlobalAmbient = NULL;
942 		if ( Device )
943 		{
944 			Device->closeDevice();
945 			Device->drop();
946 			Device = NULL;
947 		}
948 	}
949 
950 	// Create some useful textures.
951 	// Note that the function put readability over speed, you shouldn't use setPixel at runtime but for initialization it's nice.
createDefaultTextures(video::IVideoDriver * driver)952 	void createDefaultTextures(video::IVideoDriver * driver)
953 	{
954 		const u32 width = 256;
955 		const u32 height = 256;
956 		video::IImage * imageA8R8G8B8 = driver->createImage (video::ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
957 		if ( !imageA8R8G8B8 )
958 			return;
959 		const u32 pitch = imageA8R8G8B8->getPitch();
960 
961 		// some nice square-pattern with 9 typical colors
962 		for ( u32 y = 0; y < height; ++ y )
963 		{
964 			for ( u32 x = 0; x < pitch; ++x )
965 			{
966 				if ( y < height/3 )
967 				{
968 					if ( x < width/3 )
969 						imageA8R8G8B8->setPixel (x, y, SCOL_BLACK);
970 					else if ( x < 2*width/3 )
971 						imageA8R8G8B8->setPixel (x, y, SCOL_BLUE);
972 					else
973 						imageA8R8G8B8->setPixel (x, y, SCOL_CYAN);
974 				}
975 				else if ( y < 2*height/3 )
976 				{
977 					if ( x < width/3 )
978 						imageA8R8G8B8->setPixel (x, y, SCOL_GRAY);
979 					else if ( x < 2*width/3 )
980 						imageA8R8G8B8->setPixel (x, y, SCOL_GREEN);
981 					else
982 						imageA8R8G8B8->setPixel (x, y, SCOL_MAGENTA);
983 				}
984 				else
985 				{
986 					if ( x < width/3 )
987 						imageA8R8G8B8->setPixel (x, y, SCOL_RED);
988 					else if ( x < 2*width/3 )
989 						imageA8R8G8B8->setPixel (x, y, SCOL_YELLOW);
990 					else
991 						imageA8R8G8B8->setPixel (x, y, SCOL_WHITE);
992 				}
993 			}
994 		}
995 		driver->addTexture (io::path("CARO_A8R8G8B8"), imageA8R8G8B8);
996 
997 		// all white
998 		imageA8R8G8B8->fill(SCOL_WHITE);
999 		driver->addTexture (io::path("WHITE_A8R8G8B8"), imageA8R8G8B8);
1000 
1001 		// all black
1002 		imageA8R8G8B8->fill(SCOL_BLACK);
1003 		driver->addTexture (io::path("BLACK_A8R8G8B8"), imageA8R8G8B8);
1004 
1005 		// gray-scale
1006 		for ( u32 y = 0; y < height; ++ y )
1007 		{
1008 			for ( u32 x = 0; x < pitch; ++x )
1009 			{
1010 				imageA8R8G8B8->setPixel (x, y, video::SColor(y, x,x,x) );
1011 			}
1012 		}
1013 		driver->addTexture (io::path("GRAYSCALE_A8R8G8B8"), imageA8R8G8B8);
1014 
1015 		imageA8R8G8B8->drop();
1016 	}
1017 
1018 	// Load a texture and make sure nodes know it when more textures are available.
loadTexture(const io::path & name)1019 	void loadTexture(const io::path &name)
1020 	{
1021 		Device->getVideoDriver()->getTexture(name);
1022 		NodeLeft.updateTextures();
1023 		NodeRight.updateTextures();
1024 	}
1025 
1026 private:
1027 	SConfig						Config;
1028 	volatile bool				IsRunning;
1029 	IrrlichtDevice * 			Device;
1030 	scene::ICameraSceneNode *	Camera;
1031 	SMeshNodeControl			NodeLeft;
1032 	SMeshNodeControl			NodeRight;
1033 	SLightNodeControl			LightControl;
1034 	CColorControl *				GlobalAmbient;
1035 };
1036 
1037 /*
1038   A very short main as we do everything else in classes.
1039 */
main(int argc,char * argv[])1040 int main(int argc, char *argv[])
1041 {
1042 	CApp APP;
1043 
1044 	if ( !APP.init(argc, argv) )
1045 	{
1046 		printf("init failed\n");
1047 		return 1;
1048 	}
1049 
1050 	APP.run();
1051 	APP.quit();
1052 
1053 	return 0;
1054 }
1055 
1056 /*
1057 **/
1058