main.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. /** Example 022 Material Viewer
  2. This example can be used to play around with material settings and watch the results.
  3. Only the default non-shader materials are used in here.
  4. You have a node with a mesh, one dynamic light and global ambient light to play around with.
  5. You can move the light with cursor-keys and +/-.
  6. You can move the camera while left-mouse button is clicked.
  7. */
  8. // TODO: Should be possible to set all material values by the GUI.
  9. // For now just change the defaultMaterial in CApp::init for the rest.
  10. // TODO: Allow users to switch between a sphere and a box mesh.
  11. #include <irrlicht.h>
  12. #include "driverChoice.h"
  13. #include "exampleHelper.h"
  14. #include "main.h"
  15. using namespace irr;
  16. #ifdef _MSC_VER
  17. #pragma comment(lib, "Irrlicht.lib")
  18. #endif
  19. /*
  20. Variables within the empty namespace are globals which are restricted to this file.
  21. */
  22. namespace
  23. {
  24. // For the gui id's
  25. enum EGUI_IDS
  26. {
  27. GUI_ID_OPEN_TEXTURE = 1,
  28. GUI_ID_QUIT,
  29. GUI_ID_MAX
  30. };
  31. // Name used in texture selection to clear the textures on the node
  32. const core::stringw CLEAR_TEXTURE = L"CLEAR texture";
  33. // some useful color constants
  34. const video::SColor SCOL_BLACK = video::SColor(255, 0, 0, 0);
  35. const video::SColor SCOL_BLUE = video::SColor(255, 0, 0, 255);
  36. const video::SColor SCOL_CYAN = video::SColor(255, 0, 255, 255);
  37. const video::SColor SCOL_GRAY = video::SColor(255, 128,128, 128);
  38. const video::SColor SCOL_GREEN = video::SColor(255, 0, 255, 0);
  39. const video::SColor SCOL_MAGENTA = video::SColor(255, 255, 0, 255);
  40. const video::SColor SCOL_RED = video::SColor(255, 255, 0, 0);
  41. const video::SColor SCOL_YELLOW = video::SColor(255, 255, 255, 0);
  42. const video::SColor SCOL_WHITE = video::SColor(255, 255, 255, 255);
  43. }; // namespace
  44. /*
  45. Returns a new unique number on each call.
  46. */
  47. s32 makeUniqueId()
  48. {
  49. static int unique = GUI_ID_MAX;
  50. ++unique;
  51. return unique;
  52. }
  53. /*
  54. Find out which vertex-type is needed for the given material type.
  55. */
  56. video::E_VERTEX_TYPE getVertexTypeForMaterialType(video::E_MATERIAL_TYPE materialType)
  57. {
  58. using namespace video;
  59. switch ( materialType )
  60. {
  61. case EMT_SOLID:
  62. return EVT_STANDARD;
  63. case EMT_SOLID_2_LAYER:
  64. return EVT_STANDARD;
  65. case EMT_LIGHTMAP:
  66. case EMT_LIGHTMAP_ADD:
  67. case EMT_LIGHTMAP_M2:
  68. case EMT_LIGHTMAP_M4:
  69. case EMT_LIGHTMAP_LIGHTING:
  70. case EMT_LIGHTMAP_LIGHTING_M2:
  71. case EMT_LIGHTMAP_LIGHTING_M4:
  72. return EVT_2TCOORDS;
  73. case EMT_DETAIL_MAP:
  74. return EVT_2TCOORDS;
  75. case EMT_SPHERE_MAP:
  76. return EVT_STANDARD;
  77. case EMT_REFLECTION_2_LAYER:
  78. return EVT_2TCOORDS;
  79. case EMT_TRANSPARENT_ADD_COLOR:
  80. return EVT_STANDARD;
  81. case EMT_TRANSPARENT_ALPHA_CHANNEL:
  82. return EVT_STANDARD;
  83. case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
  84. return EVT_STANDARD;
  85. case EMT_TRANSPARENT_VERTEX_ALPHA:
  86. return EVT_STANDARD;
  87. case EMT_TRANSPARENT_REFLECTION_2_LAYER:
  88. return EVT_2TCOORDS;
  89. case EMT_NORMAL_MAP_SOLID:
  90. case EMT_NORMAL_MAP_TRANSPARENT_ADD_COLOR:
  91. case EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA:
  92. case EMT_PARALLAX_MAP_SOLID:
  93. case EMT_PARALLAX_MAP_TRANSPARENT_ADD_COLOR:
  94. case EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA:
  95. return EVT_TANGENTS;
  96. case EMT_ONETEXTURE_BLEND:
  97. return EVT_STANDARD;
  98. case EMT_FORCE_32BIT:
  99. return EVT_STANDARD;
  100. }
  101. return EVT_STANDARD;
  102. }
  103. /*
  104. Custom GUI-control to edit colorvalues.
  105. */
  106. // Constructor
  107. CColorControl::CColorControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, IGUIElement* parent, s32 id)
  108. : gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect< s32 >(pos, pos+core::dimension2d<s32>(80, 75)))
  109. , DirtyFlag(true)
  110. , Color(0)
  111. , ColorStatic(0)
  112. , EditAlpha(0)
  113. , EditRed(0)
  114. , EditGreen(0)
  115. , EditBlue(0)
  116. {
  117. using namespace gui;
  118. ButtonSetId = makeUniqueId();
  119. const core::rect< s32 > rectControls(0,0,AbsoluteRect.getWidth(),AbsoluteRect.getHeight() );
  120. IGUIStaticText * groupElement = guiEnv->addStaticText (L"", rectControls, true, false, this, -1, false);
  121. groupElement->setNotClipped(true);
  122. guiEnv->addStaticText (text, core::rect<s32>(0,0,80,15), false, false, groupElement, -1, false);
  123. EditAlpha = addEditForNumbers(guiEnv, core::position2d<s32>(0,15), L"a", -1, groupElement );
  124. EditRed = addEditForNumbers(guiEnv, core::position2d<s32>(0,30), L"r", -1, groupElement );
  125. EditGreen = addEditForNumbers(guiEnv, core::position2d<s32>(0,45), L"g", -1, groupElement );
  126. EditBlue = addEditForNumbers(guiEnv, core::position2d<s32>(0,60), L"b", -1, groupElement );
  127. ColorStatic = guiEnv->addStaticText (L"", core::rect<s32>(60,15,80,75), true, false, groupElement, -1, true);
  128. guiEnv->addButton (core::rect<s32>(60,35,80,50), groupElement, ButtonSetId, L"set");
  129. setEditsFromColor(Color);
  130. }
  131. // event receiver
  132. bool CColorControl::OnEvent(const SEvent &event)
  133. {
  134. if ( event.EventType != EET_GUI_EVENT )
  135. return false;
  136. if ( event.GUIEvent.Caller->getID() == ButtonSetId && event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
  137. {
  138. Color = getColorFromEdits();
  139. setEditsFromColor(Color);
  140. }
  141. return false;
  142. }
  143. // set the color values
  144. void CColorControl::setColor(const video::SColor& col)
  145. {
  146. DirtyFlag = true;
  147. Color = col;
  148. setEditsFromColor(Color);
  149. }
  150. // Add a staticbox for a description + an editbox so users can enter numbers
  151. gui::IGUIEditBox* CColorControl::addEditForNumbers(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, s32 id, gui::IGUIElement * parent)
  152. {
  153. using namespace gui;
  154. core::rect< s32 > rect(pos, pos+core::dimension2d<s32>(10, 15));
  155. guiEnv->addStaticText (text, rect, false, false, parent, -1, false);
  156. rect += core::position2d<s32>( 20, 0 );
  157. rect.LowerRightCorner.X += 20;
  158. gui::IGUIEditBox* edit = guiEnv->addEditBox(L"0", rect, true, parent, id);
  159. return edit;
  160. }
  161. // Get the color value from the editfields
  162. video::SColor CColorControl::getColorFromEdits() const
  163. {
  164. video::SColor col;
  165. if (EditAlpha)
  166. {
  167. u32 alpha = core::strtoul10(core::stringc(EditAlpha->getText()).c_str());
  168. if (alpha > 255)
  169. alpha = 255;
  170. col.setAlpha(alpha);
  171. }
  172. if (EditRed)
  173. {
  174. u32 red = core::strtoul10(core::stringc(EditRed->getText()).c_str());
  175. if (red > 255)
  176. red = 255;
  177. col.setRed(red);
  178. }
  179. if (EditGreen)
  180. {
  181. u32 green = core::strtoul10(core::stringc(EditGreen->getText()).c_str());
  182. if (green > 255)
  183. green = 255;
  184. col.setGreen(green);
  185. }
  186. if (EditBlue)
  187. {
  188. u32 blue = core::strtoul10(core::stringc(EditBlue->getText()).c_str());
  189. if (blue > 255)
  190. blue = 255;
  191. col.setBlue(blue);
  192. }
  193. return col;
  194. }
  195. // Fill the editfields with the value for the given color
  196. void CColorControl::setEditsFromColor(video::SColor col)
  197. {
  198. DirtyFlag = true;
  199. if ( EditAlpha )
  200. EditAlpha->setText( core::stringw(col.getAlpha()).c_str() );
  201. if ( EditRed )
  202. EditRed->setText( core::stringw(col.getRed()).c_str() );
  203. if ( EditGreen )
  204. EditGreen->setText( core::stringw(col.getGreen()).c_str() );
  205. if ( EditBlue )
  206. EditBlue->setText( core::stringw(col.getBlue()).c_str() );
  207. if ( ColorStatic )
  208. ColorStatic->setBackgroundColor(col);
  209. }
  210. /*
  211. Custom GUI-control for to edit all colors typically used in materials and lights
  212. */
  213. // Constructor
  214. CTypicalColorsControl::CTypicalColorsControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, bool hasEmissive, IGUIElement* parent, s32 id)
  215. : gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(60,250)))
  216. , ControlAmbientColor(0), ControlDiffuseColor(0), ControlSpecularColor(0), ControlEmissiveColor(0)
  217. {
  218. ControlAmbientColor = new CColorControl( guiEnv, core::position2d<s32>(0, 0), L"Ambient", this);
  219. ControlDiffuseColor = new CColorControl( guiEnv, core::position2d<s32>(0, 75), L"Diffuse", this );
  220. ControlSpecularColor = new CColorControl( guiEnv, core::position2d<s32>(0, 150), L"Specular", this );
  221. if ( hasEmissive )
  222. {
  223. ControlEmissiveColor = new CColorControl( guiEnv, core::position2d<s32>(0, 225), L"Emissive", this );
  224. }
  225. }
  226. // Destructor
  227. CTypicalColorsControl::~CTypicalColorsControl()
  228. {
  229. ControlAmbientColor->drop();
  230. ControlDiffuseColor->drop();
  231. if ( ControlEmissiveColor )
  232. ControlEmissiveColor->drop();
  233. ControlSpecularColor->drop();
  234. }
  235. // Set the color values to those within the material
  236. void CTypicalColorsControl::setColorsToMaterialColors(const video::SMaterial & material)
  237. {
  238. ControlAmbientColor->setColor(material.AmbientColor);
  239. ControlDiffuseColor->setColor(material.DiffuseColor);
  240. ControlEmissiveColor->setColor(material.EmissiveColor);
  241. ControlSpecularColor->setColor(material.SpecularColor);
  242. }
  243. // Update all changed colors in the material
  244. void CTypicalColorsControl::updateMaterialColors(video::SMaterial & material) const
  245. {
  246. if ( ControlAmbientColor->isDirty() )
  247. material.AmbientColor = ControlAmbientColor->getColor();
  248. if ( ControlDiffuseColor->isDirty() )
  249. material.DiffuseColor = ControlDiffuseColor->getColor();
  250. if ( ControlEmissiveColor->isDirty() )
  251. material.EmissiveColor = ControlEmissiveColor->getColor();
  252. if ( ControlSpecularColor->isDirty() )
  253. material.SpecularColor = ControlSpecularColor->getColor();
  254. }
  255. // Set the color values to those from the light data
  256. void CTypicalColorsControl::setColorsToLightDataColors(const video::SLight & lightData)
  257. {
  258. ControlAmbientColor->setColor(lightData.AmbientColor.toSColor());
  259. ControlDiffuseColor->setColor(lightData.DiffuseColor.toSColor());
  260. ControlSpecularColor->setColor(lightData.SpecularColor.toSColor());
  261. }
  262. // Update all changed colors in the light data
  263. void CTypicalColorsControl::updateLightColors(video::SLight & lightData) const
  264. {
  265. if ( ControlAmbientColor->isDirty() )
  266. lightData.AmbientColor = video::SColorf( ControlAmbientColor->getColor() );
  267. if ( ControlDiffuseColor->isDirty() )
  268. lightData.DiffuseColor = video::SColorf( ControlDiffuseColor->getColor() );
  269. if ( ControlSpecularColor->isDirty() )
  270. lightData.SpecularColor = video::SColorf(ControlSpecularColor->getColor() );
  271. }
  272. // To reset the dirty flags
  273. void CTypicalColorsControl::resetDirty()
  274. {
  275. ControlAmbientColor->resetDirty();
  276. ControlDiffuseColor->resetDirty();
  277. ControlSpecularColor->resetDirty();
  278. if ( ControlEmissiveColor )
  279. ControlEmissiveColor->resetDirty();
  280. }
  281. /*
  282. GUI-Control to offer a selection of available textures.
  283. */
  284. CTextureControl::CTextureControl(gui::IGUIEnvironment* guiEnv, video::IVideoDriver * driver, const core::position2d<s32> & pos, IGUIElement* parent, s32 id)
  285. : gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(150,15)))
  286. , DirtyFlag(true), ComboTexture(0)
  287. {
  288. core::rect<s32> rectCombo(0, 0, AbsoluteRect.getWidth(),AbsoluteRect.getHeight());
  289. ComboTexture = guiEnv->addComboBox (rectCombo, this);
  290. updateTextures(driver);
  291. }
  292. bool CTextureControl::OnEvent(const SEvent &event)
  293. {
  294. if ( event.EventType != EET_GUI_EVENT )
  295. return false;
  296. if ( event.GUIEvent.Caller == ComboTexture && event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
  297. {
  298. DirtyFlag = true;
  299. }
  300. return false;
  301. }
  302. // Workaround for a problem with comboboxes.
  303. // We have to get in front when the combobox wants to get in front or combobox-list might be drawn below other elements.
  304. bool CTextureControl::bringToFront(IGUIElement* element)
  305. {
  306. bool result = gui::IGUIElement::bringToFront(element);
  307. if ( Parent && element == ComboTexture )
  308. result &= Parent->bringToFront(this);
  309. return result;
  310. }
  311. // return selected texturename (if any, otherwise 0)
  312. const wchar_t * CTextureControl::getSelectedTextureName() const
  313. {
  314. s32 selected = ComboTexture->getSelected();
  315. if ( selected < 0 )
  316. return 0;
  317. return ComboTexture->getItem(selected);
  318. }
  319. void CTextureControl::selectTextureByName(const irr::core::stringw& name)
  320. {
  321. for (u32 i=0; i< ComboTexture->getItemCount(); ++i)
  322. {
  323. if ( name == ComboTexture->getItem(i))
  324. {
  325. ComboTexture->setSelected(i);
  326. DirtyFlag = true;
  327. return;
  328. }
  329. }
  330. }
  331. // Put the names of all currently loaded textures in a combobox
  332. void CTextureControl::updateTextures(video::IVideoDriver * driver)
  333. {
  334. s32 oldSelected = ComboTexture->getSelected();
  335. s32 selectNew = -1;
  336. core::stringw oldTextureName;
  337. if ( oldSelected >= 0 )
  338. {
  339. oldTextureName = ComboTexture->getItem(oldSelected);
  340. }
  341. ComboTexture->clear();
  342. for ( u32 i=0; i < driver->getTextureCount(); ++i )
  343. {
  344. video::ITexture * texture = driver->getTextureByIndex(i);
  345. core::stringw name( texture->getName() );
  346. ComboTexture->addItem( name.c_str() );
  347. if ( !oldTextureName.empty() && selectNew < 0 && name == oldTextureName )
  348. selectNew = i;
  349. }
  350. // add another name which can be used to clear the texture
  351. ComboTexture->addItem( CLEAR_TEXTURE.c_str() );
  352. if ( CLEAR_TEXTURE == oldTextureName )
  353. selectNew = ComboTexture->getItemCount()-1;
  354. if ( selectNew >= 0 )
  355. ComboTexture->setSelected(selectNew);
  356. DirtyFlag = true;
  357. }
  358. /*
  359. Control which allows setting some of the material values for a meshscenenode
  360. */
  361. void CMaterialControl::init(scene::IMeshSceneNode* node, IrrlichtDevice * device, const core::position2d<s32> & pos, const wchar_t * description)
  362. {
  363. if ( Initialized || !node || !device) // initializing twice or with invalid data not allowed
  364. return;
  365. Driver = device->getVideoDriver ();
  366. gui::IGUIEnvironment* guiEnv = device->getGUIEnvironment();
  367. //scene::ISceneManager* smgr = device->getSceneManager();
  368. const video::SMaterial & material = node->getMaterial(0);
  369. s32 top = pos.Y;
  370. // Description
  371. guiEnv->addStaticText(description, core::rect<s32>(pos.X, top, pos.X+60, top+15), false, false, 0, -1, false);
  372. top += 15;
  373. // Control for material type
  374. core::rect<s32> rectCombo(pos.X, top, 150, top+15);
  375. top += 15;
  376. ComboMaterial = guiEnv->addComboBox (rectCombo);
  377. for ( int i=0; i <= (int)video::EMT_ONETEXTURE_BLEND; ++i )
  378. {
  379. ComboMaterial->addItem( core::stringw(video::sBuiltInMaterialTypeNames[i]).c_str() );
  380. }
  381. ComboMaterial->setSelected( (s32)material.MaterialType );
  382. // Control to enable/disabling material lighting
  383. core::rect<s32> rectBtn(core::position2d<s32>(pos.X, top), core::dimension2d<s32>(100, 15));
  384. top += 15;
  385. ButtonLighting = guiEnv->addButton (rectBtn, 0, -1, L"Lighting");
  386. ButtonLighting->setIsPushButton(true);
  387. ButtonLighting->setPressed(material.Lighting);
  388. core::rect<s32> rectInfo( rectBtn.LowerRightCorner.X, rectBtn.UpperLeftCorner.Y, rectBtn.LowerRightCorner.X+40, rectBtn.UpperLeftCorner.Y+15 );
  389. InfoLighting = guiEnv->addStaticText(L"", rectInfo, true, false );
  390. InfoLighting->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER );
  391. // Controls for colors
  392. TypicalColorsControl = new CTypicalColorsControl(guiEnv, core::position2d<s32>(pos.X, top), true, guiEnv->getRootGUIElement());
  393. top += 300;
  394. TypicalColorsControl->setColorsToMaterialColors(material);
  395. // Controls for selecting the material textures
  396. guiEnv->addStaticText(L"Textures", core::rect<s32>(pos.X, top, pos.X+60, top+15), false, false, 0, -1, false);
  397. top += 15;
  398. for (irr::u32 i=0; i<irr::video::MATERIAL_MAX_TEXTURES; ++i)
  399. {
  400. TextureControls[i] = new CTextureControl(guiEnv, Driver, core::position2di(pos.X, top), guiEnv->getRootGUIElement());
  401. top += 15;
  402. }
  403. Initialized = true;
  404. }
  405. void CMaterialControl::update(scene::IMeshSceneNode* sceneNode, scene::IMeshSceneNode* sceneNode2T, scene::IMeshSceneNode* sceneNodeTangents)
  406. {
  407. if ( !Initialized )
  408. return;
  409. video::SMaterial & material = sceneNode->getMaterial(0);
  410. video::SMaterial & material2T = sceneNode2T->getMaterial(0);
  411. video::SMaterial & materialTangents = sceneNodeTangents->getMaterial(0);
  412. s32 selectedMaterial = ComboMaterial->getSelected();
  413. if ( selectedMaterial >= (s32)video::EMT_SOLID && selectedMaterial <= (s32)video::EMT_ONETEXTURE_BLEND)
  414. {
  415. // Show the node which has a mesh to work with the currently selected material
  416. video::E_VERTEX_TYPE vertexType = getVertexTypeForMaterialType((video::E_MATERIAL_TYPE)selectedMaterial);
  417. switch ( vertexType )
  418. {
  419. case video::EVT_STANDARD:
  420. material.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
  421. sceneNode->setVisible(true);
  422. sceneNode2T->setVisible(false);
  423. sceneNodeTangents->setVisible(false);
  424. break;
  425. case video::EVT_2TCOORDS:
  426. material2T.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
  427. sceneNode->setVisible(false);
  428. sceneNode2T->setVisible(true);
  429. sceneNodeTangents->setVisible(false);
  430. break;
  431. case video::EVT_TANGENTS:
  432. materialTangents.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
  433. sceneNode->setVisible(false);
  434. sceneNode2T->setVisible(false);
  435. sceneNodeTangents->setVisible(true);
  436. break;
  437. }
  438. }
  439. // Always update materials of all nodes, otherwise the tool is confusing to use.
  440. updateMaterial(material);
  441. updateMaterial(material2T);
  442. updateMaterial(materialTangents);
  443. if ( ButtonLighting->isPressed() )
  444. InfoLighting->setText(L"is on");
  445. else
  446. InfoLighting->setText(L"is off");
  447. TypicalColorsControl->resetDirty();
  448. for (irr::u32 i=0; i<irr::video::MATERIAL_MAX_TEXTURES; ++i)
  449. TextureControls[i]->resetDirty();
  450. }
  451. void CMaterialControl::updateTextures()
  452. {
  453. for (irr::u32 i=0; i<irr::video::MATERIAL_MAX_TEXTURES; ++i)
  454. TextureControls[i]->updateTextures(Driver);
  455. }
  456. void CMaterialControl::selectTextures(const irr::core::stringw& name)
  457. {
  458. for (irr::u32 i=0; i<irr::video::MATERIAL_MAX_TEXTURES; ++i)
  459. TextureControls[i]->selectTextureByName(name);
  460. }
  461. bool CMaterialControl::isLightingEnabled() const
  462. {
  463. return ButtonLighting && ButtonLighting->isPressed();
  464. }
  465. void CMaterialControl::updateMaterial(video::SMaterial & material)
  466. {
  467. TypicalColorsControl->updateMaterialColors(material);
  468. material.Lighting = ButtonLighting->isPressed();
  469. for (irr::u32 i=0; i<irr::video::MATERIAL_MAX_TEXTURES; ++i)
  470. {
  471. if ( TextureControls[i]->isDirty() )
  472. {
  473. material.TextureLayer[i].Texture = Driver->getTexture( io::path(TextureControls[i]->getSelectedTextureName()) );
  474. }
  475. }
  476. }
  477. /*
  478. Control to allow setting the color values of a lightscenenode.
  479. */
  480. void CLightNodeControl::init(scene::ILightSceneNode* node, gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description)
  481. {
  482. if ( Initialized || !node || !guiEnv) // initializing twice or with invalid data not allowed
  483. return;
  484. guiEnv->addStaticText(description, core::rect<s32>(pos.X, pos.Y, pos.X+70, pos.Y+15), false, false, 0, -1, false);
  485. TypicalColorsControl = new CTypicalColorsControl(guiEnv, core::position2d<s32>(pos.X, pos.Y+15), false, guiEnv->getRootGUIElement());
  486. const video::SLight & lightData = node->getLightData();
  487. TypicalColorsControl->setColorsToLightDataColors(lightData);
  488. Initialized = true;
  489. }
  490. void CLightNodeControl::update(scene::ILightSceneNode* node)
  491. {
  492. if ( !Initialized )
  493. return;
  494. video::SLight & lightData = node->getLightData();
  495. TypicalColorsControl->updateLightColors(lightData);
  496. }
  497. /*
  498. Main application class
  499. */
  500. /*
  501. Event handler
  502. */
  503. bool CApp::OnEvent(const SEvent &event)
  504. {
  505. if (event.EventType == EET_GUI_EVENT)
  506. {
  507. gui::IGUIEnvironment* env = Device->getGUIEnvironment();
  508. switch(event.GUIEvent.EventType)
  509. {
  510. case gui::EGET_MENU_ITEM_SELECTED:
  511. {
  512. gui::IGUIContextMenu* menu = (gui::IGUIContextMenu*)event.GUIEvent.Caller;
  513. s32 id = menu->getItemCommandId(menu->getSelectedItem());
  514. switch(id)
  515. {
  516. case GUI_ID_OPEN_TEXTURE: // File -> Open Texture
  517. env->addFileOpenDialog(L"Please select a texture file to open");
  518. break;
  519. case GUI_ID_QUIT: // File -> Quit
  520. setRunning(false);
  521. break;
  522. }
  523. }
  524. break;
  525. case gui::EGET_FILE_SELECTED:
  526. {
  527. // load the model file, selected in the file open dialog
  528. gui::IGUIFileOpenDialog* dialog =
  529. (gui::IGUIFileOpenDialog*)event.GUIEvent.Caller;
  530. loadTexture(io::path(dialog->getFileName()).c_str());
  531. }
  532. break;
  533. default:
  534. break;
  535. }
  536. }
  537. else if (event.EventType == EET_KEY_INPUT_EVENT)
  538. {
  539. KeysPressed[event.KeyInput.Key] = event.KeyInput.PressedDown;
  540. }
  541. else if (event.EventType == EET_MOUSE_INPUT_EVENT)
  542. {
  543. if (!MousePressed && event.MouseInput.isLeftPressed())
  544. {
  545. gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
  546. if ( guiEnv->getHovered() == guiEnv->getRootGUIElement() ) // Click on background
  547. {
  548. MousePressed = true;
  549. MouseStart.X = event.MouseInput.X;
  550. MouseStart.Y = event.MouseInput.Y;
  551. }
  552. }
  553. else if (MousePressed && !event.MouseInput.isLeftPressed())
  554. {
  555. MousePressed = false;
  556. }
  557. }
  558. return false;
  559. }
  560. // Application initialization
  561. // returns true when it was successful initialized, otherwise false.
  562. bool CApp::init(int argc, char *argv[])
  563. {
  564. // ask user for driver
  565. Config.DriverType=driverChoiceConsole();
  566. if (Config.DriverType==video::EDT_COUNT)
  567. return false;
  568. // create the device with the settings from our config
  569. Device = createDevice(Config.DriverType, Config.ScreenSize);
  570. if (!Device)
  571. return false;
  572. Device->setWindowCaption( core::stringw(video::DRIVER_TYPE_NAMES[Config.DriverType]).c_str() );
  573. Device->setEventReceiver(this);
  574. scene::ISceneManager* smgr = Device->getSceneManager();
  575. video::IVideoDriver * driver = Device->getVideoDriver ();
  576. gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
  577. MeshManipulator = smgr->getMeshManipulator();
  578. // set a nicer font
  579. gui::IGUISkin* skin = guiEnv->getSkin();
  580. gui::IGUIFont* font = guiEnv->getFont(getExampleMediaPath() + "fonthaettenschweiler.bmp");
  581. if (font)
  582. skin->setFont(font);
  583. // remove some alpha value because it makes those menus harder to read otherwise
  584. video::SColor col3dHighLight( skin->getColor(gui::EGDC_APP_WORKSPACE) );
  585. col3dHighLight.setAlpha(255);
  586. video::SColor colHighLight( col3dHighLight );
  587. skin->setColor(gui::EGDC_HIGH_LIGHT, colHighLight );
  588. skin->setColor(gui::EGDC_3D_HIGH_LIGHT, col3dHighLight );
  589. // Add some textures which are useful to test material settings
  590. createDefaultTextures(driver);
  591. // create a menu
  592. gui::IGUIContextMenu * menuBar = guiEnv->addMenu();
  593. menuBar->addItem(L"File", -1, true, true);
  594. gui::IGUIContextMenu* subMenuFile = menuBar->getSubMenu(0);
  595. subMenuFile->addItem(L"Open texture ...", GUI_ID_OPEN_TEXTURE);
  596. subMenuFile->addSeparator();
  597. subMenuFile->addItem(L"Quit", GUI_ID_QUIT);
  598. // a static camera
  599. Camera = smgr->addCameraSceneNode (0, core::vector3df(0, 40, -40),
  600. core::vector3df(0, 10, 0),
  601. -1);
  602. // default material
  603. video::SMaterial defaultMaterial;
  604. defaultMaterial.Shininess = 20.f;
  605. // add the nodes which are used to show the materials
  606. SceneNode = smgr->addCubeSceneNode (30.0f, 0, -1,
  607. core::vector3df(0, 0, 0),
  608. core::vector3df(0.f, 45.f, 0.f),
  609. core::vector3df(1.0f, 1.0f, 1.0f));
  610. SceneNode->getMaterial(0) = defaultMaterial;
  611. const s32 controlsTop = 20;
  612. MeshMaterialControl = new CMaterialControl();
  613. MeshMaterialControl->init( SceneNode, Device, core::position2d<s32>(10,controlsTop), L"Material" );
  614. MeshMaterialControl->selectTextures(core::stringw("CARO_A8R8G8B8")); // set a useful default texture
  615. // create nodes with other vertex types
  616. scene::IMesh * mesh2T = MeshManipulator->createMeshWith2TCoords(SceneNode->getMesh());
  617. SceneNode2T = smgr->addMeshSceneNode(mesh2T, 0, -1, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
  618. mesh2T->drop();
  619. scene::IMesh * meshTangents = MeshManipulator->createMeshWithTangents(SceneNode->getMesh(), false, false, false);
  620. SceneNodeTangents = smgr->addMeshSceneNode(meshTangents, 0, -1
  621. , SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
  622. meshTangents->drop();
  623. // add one light
  624. NodeLight = smgr->addLightSceneNode(0, core::vector3df(0, 0, -40),
  625. video::SColorf(1.0f, 1.0f, 1.0f),
  626. 35.0f);
  627. LightControl = new CLightNodeControl();
  628. LightControl->init(NodeLight, guiEnv, core::position2d<s32>(550,controlsTop), L"Dynamic light" );
  629. // one large cube around everything. That's mainly to make the light more obvious.
  630. scene::IMeshSceneNode* backgroundCube = smgr->addCubeSceneNode (200.0f, 0, -1, core::vector3df(0, 0, 0),
  631. core::vector3df(45, 0, 0),
  632. core::vector3df(1.0f, 1.0f, 1.0f));
  633. backgroundCube->getMaterial(0).BackfaceCulling = false; // we are within the cube, so we have to disable backface culling to see it
  634. backgroundCube->getMaterial(0).EmissiveColor.set(255,50,50,50); // we keep some self lighting to keep texts visible
  635. // Add a the mesh vertex color control
  636. guiEnv->addStaticText(L"Mesh", core::rect<s32>(200, controlsTop, 270, controlsTop+15), false, false, 0, -1, false);
  637. ControlVertexColors = new CColorControl( guiEnv, core::position2d<s32>(200, controlsTop+15), L"Vertex colors", guiEnv->getRootGUIElement());
  638. video::S3DVertex * vertices = (video::S3DVertex *)SceneNode->getMesh()->getMeshBuffer(0)->getVertices();
  639. if ( vertices )
  640. {
  641. ControlVertexColors->setColor(vertices[0].Color);
  642. }
  643. // Add a control for ambient light
  644. GlobalAmbient = new CColorControl( guiEnv, core::position2d<s32>(550, 300), L"Global ambient", guiEnv->getRootGUIElement());
  645. GlobalAmbient->setColor( smgr->getAmbientLight().toSColor() );
  646. return true;
  647. }
  648. /*
  649. Update one frame
  650. */
  651. bool CApp::update()
  652. {
  653. using namespace irr;
  654. video::IVideoDriver* videoDriver = Device->getVideoDriver();
  655. if ( !Device->run() )
  656. return false;
  657. // Figure out delta time since last frame
  658. ITimer * timer = Device->getTimer();
  659. u32 newTick = timer->getRealTime();
  660. f32 deltaTime = RealTimeTick > 0 ? f32(newTick-RealTimeTick)/1000.f : 0.f; // in seconds
  661. RealTimeTick = newTick;
  662. if ( Device->isWindowActive() || Config.RenderInBackground )
  663. {
  664. gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
  665. scene::ISceneManager* smgr = Device->getSceneManager();
  666. gui::IGUISkin * skin = guiEnv->getSkin();
  667. // update our controls
  668. MeshMaterialControl->update(SceneNode, SceneNode2T, SceneNodeTangents);
  669. LightControl->update(NodeLight);
  670. // Update vertices
  671. if ( ControlVertexColors->isDirty() )
  672. {
  673. MeshManipulator->setVertexColors (SceneNode->getMesh(), ControlVertexColors->getColor());
  674. MeshManipulator->setVertexColors (SceneNode2T->getMesh(), ControlVertexColors->getColor());
  675. MeshManipulator->setVertexColors (SceneNodeTangents->getMesh(), ControlVertexColors->getColor());
  676. ControlVertexColors->resetDirty();
  677. }
  678. // update ambient light settings
  679. if ( GlobalAmbient->isDirty() )
  680. {
  681. smgr->setAmbientLight( GlobalAmbient->getColor() );
  682. GlobalAmbient->resetDirty();
  683. }
  684. // Let the user move the light around
  685. const float zoomSpeed = 10.f * deltaTime;
  686. const float rotationSpeed = 100.f * deltaTime;
  687. if ( KeysPressed[KEY_PLUS] || KeysPressed[KEY_ADD])
  688. ZoomOut(NodeLight, zoomSpeed);
  689. if ( KeysPressed[KEY_MINUS] || KeysPressed[KEY_SUBTRACT])
  690. ZoomOut(NodeLight, -zoomSpeed);
  691. if ( KeysPressed[KEY_RIGHT])
  692. RotateHorizontal(NodeLight, rotationSpeed);
  693. if ( KeysPressed[KEY_LEFT])
  694. RotateHorizontal(NodeLight, -rotationSpeed);
  695. UpdateRotationAxis(NodeLight, LightRotationAxis);
  696. if ( KeysPressed[KEY_UP])
  697. RotateAroundAxis(NodeLight, rotationSpeed, LightRotationAxis);
  698. if ( KeysPressed[KEY_DOWN])
  699. RotateAroundAxis(NodeLight, -rotationSpeed, LightRotationAxis);
  700. // Let the user move the camera around
  701. if (MousePressed)
  702. {
  703. gui::ICursorControl* cursorControl = Device->getCursorControl();
  704. const core::position2d<s32>& mousePos = cursorControl->getPosition ();
  705. RotateHorizontal(Camera, rotationSpeed * (MouseStart.X - mousePos.X));
  706. RotateAroundAxis(Camera, rotationSpeed * (mousePos.Y - MouseStart.Y), CameraRotationAxis);
  707. MouseStart = mousePos;
  708. }
  709. // draw everything
  710. video::SColor bkColor( skin->getColor(gui::EGDC_APP_WORKSPACE) );
  711. videoDriver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, bkColor);
  712. smgr->drawAll();
  713. guiEnv->drawAll();
  714. if ( MeshMaterialControl->isLightingEnabled() )
  715. {
  716. // draw a line from the light to the target
  717. video::SMaterial lineMaterial;
  718. lineMaterial.Lighting = false;
  719. videoDriver->setMaterial(lineMaterial);
  720. videoDriver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
  721. videoDriver->draw3DLine(NodeLight->getAbsolutePosition(), SceneNode->getAbsolutePosition());
  722. }
  723. videoDriver->endScene();
  724. }
  725. // be nice
  726. Device->sleep( 5 );
  727. return true;
  728. }
  729. // Close down the application
  730. void CApp::quit()
  731. {
  732. IsRunning = false;
  733. delete LightControl;
  734. LightControl = NULL;
  735. delete MeshMaterialControl;
  736. MeshMaterialControl = NULL;
  737. if ( ControlVertexColors )
  738. {
  739. ControlVertexColors->drop();
  740. ControlVertexColors = NULL;
  741. }
  742. if ( GlobalAmbient )
  743. {
  744. GlobalAmbient->drop();
  745. GlobalAmbient = NULL;
  746. }
  747. if ( Device )
  748. {
  749. Device->closeDevice();
  750. Device->drop();
  751. Device = NULL;
  752. }
  753. }
  754. // Create some useful textures.
  755. void CApp::createDefaultTextures(video::IVideoDriver * driver)
  756. {
  757. const u32 width = 256;
  758. const u32 height = 256;
  759. video::IImage * imageA8R8G8B8 = driver->createImage (video::ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
  760. if ( !imageA8R8G8B8 )
  761. return;
  762. const u32 pitch = imageA8R8G8B8->getPitch();
  763. // Some nice square-pattern with 9 typical colors
  764. // Note that the function put readability over speed, you shouldn't use setPixel at runtime but for initialization it's nice.
  765. for ( u32 y = 0; y < height; ++ y )
  766. {
  767. for ( u32 x = 0; x < pitch; ++x )
  768. {
  769. if ( y < height/3 )
  770. {
  771. if ( x < width/3 )
  772. imageA8R8G8B8->setPixel (x, y, SCOL_BLACK);
  773. else if ( x < 2*width/3 )
  774. imageA8R8G8B8->setPixel (x, y, SCOL_BLUE);
  775. else
  776. imageA8R8G8B8->setPixel (x, y, SCOL_CYAN);
  777. }
  778. else if ( y < 2*height/3 )
  779. {
  780. if ( x < width/3 )
  781. imageA8R8G8B8->setPixel (x, y, SCOL_GRAY);
  782. else if ( x < 2*width/3 )
  783. imageA8R8G8B8->setPixel (x, y, SCOL_GREEN);
  784. else
  785. imageA8R8G8B8->setPixel (x, y, SCOL_MAGENTA);
  786. }
  787. else
  788. {
  789. if ( x < width/3 )
  790. imageA8R8G8B8->setPixel (x, y, SCOL_RED);
  791. else if ( x < 2*width/3 )
  792. imageA8R8G8B8->setPixel (x, y, SCOL_YELLOW);
  793. else
  794. imageA8R8G8B8->setPixel (x, y, SCOL_WHITE);
  795. }
  796. }
  797. }
  798. driver->addTexture (io::path("CARO_A8R8G8B8"), imageA8R8G8B8);
  799. // all white
  800. imageA8R8G8B8->fill(SCOL_WHITE);
  801. driver->addTexture (io::path("WHITE_A8R8G8B8"), imageA8R8G8B8);
  802. // all black
  803. imageA8R8G8B8->fill(SCOL_BLACK);
  804. driver->addTexture (io::path("BLACK_A8R8G8B8"), imageA8R8G8B8);
  805. // gray-scale
  806. for ( u32 y = 0; y < height; ++ y )
  807. {
  808. for ( u32 x = 0; x < pitch; ++x )
  809. {
  810. imageA8R8G8B8->setPixel (x, y, video::SColor(y, x,x,x) );
  811. }
  812. }
  813. driver->addTexture (io::path("GRAYSCALE_A8R8G8B8"), imageA8R8G8B8);
  814. imageA8R8G8B8->drop();
  815. }
  816. // Load a texture and make sure nodes know it when more textures are available.
  817. void CApp::loadTexture(const io::path &name)
  818. {
  819. Device->getVideoDriver()->getTexture(name);
  820. MeshMaterialControl->updateTextures();
  821. }
  822. void CApp::RotateHorizontal(irr::scene::ISceneNode* node, irr::f32 angle)
  823. {
  824. if ( node )
  825. {
  826. core::vector3df pos(node->getPosition());
  827. core::vector2df dir(pos.X, pos.Z);
  828. dir.rotateBy(angle);
  829. pos.X = dir.X;
  830. pos.Z = dir.Y;
  831. node->setPosition(pos);
  832. }
  833. }
  834. void CApp::RotateAroundAxis(irr::scene::ISceneNode* node, irr::f32 angle, const irr::core::vector3df& axis)
  835. {
  836. if ( node )
  837. {
  838. // TOOD: yeah, doesn't rotate around top/bottom yet. Fixes welcome.
  839. core::vector3df pos(node->getPosition());
  840. core::matrix4 mat;
  841. mat.setRotationAxisRadians (core::degToRad(angle), axis);
  842. mat.rotateVect(pos);
  843. node->setPosition(pos);
  844. }
  845. }
  846. void CApp::ZoomOut(irr::scene::ISceneNode* node, irr::f32 units)
  847. {
  848. if ( node )
  849. {
  850. core::vector3df pos(node->getPosition());
  851. irr::f32 len = pos.getLength() + units;
  852. pos.setLength(len);
  853. node->setPosition(pos);
  854. }
  855. }
  856. void CApp::UpdateRotationAxis(irr::scene::ISceneNode* node, irr::core::vector3df& axis)
  857. {
  858. // Find a perpendicular axis to the x,z vector. If none found (vector straight up/down) continue to use the existing one.
  859. core::vector3df pos(node->getPosition());
  860. if ( !core::equals(pos.X, 0.f) || !core::equals(pos.Z, 0.f) )
  861. {
  862. axis.X = -pos.Z;
  863. axis.Z = pos.X;
  864. axis.normalize();
  865. }
  866. }
  867. /*
  868. Short main as most is done in classes.
  869. */
  870. int main(int argc, char *argv[])
  871. {
  872. CApp APP;
  873. if ( !APP.init(argc, argv) )
  874. {
  875. printf("init failed\n");
  876. APP.quit();
  877. return 1;
  878. }
  879. APP.setRunning(true);
  880. /*
  881. main application loop
  882. */
  883. while(APP.isRunning())
  884. {
  885. if ( !APP.update() )
  886. break;
  887. }
  888. APP.quit();
  889. return 0;
  890. }
  891. /*
  892. **/