main.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. /** Example 011 Per-Pixel Lighting
  2. This tutorial shows how to use one of the built in more complex materials in
  3. irrlicht: Per pixel lighted surfaces using normal maps and parallax mapping. It
  4. will also show how to use fog and moving particle systems. And don't panic: You
  5. do not need any experience with shaders to use these materials in Irrlicht.
  6. At first, we need to include all headers and do the stuff we always do, like in
  7. nearly all other tutorials.
  8. */
  9. #include <irrlicht.h>
  10. #include "driverChoice.h"
  11. #include "exampleHelper.h"
  12. using namespace irr;
  13. #ifdef _MSC_VER
  14. #pragma comment(lib, "Irrlicht.lib")
  15. #endif
  16. /*
  17. For this example, we need an event receiver, to make it possible for the user
  18. to switch between the three available material types. In addition, the event
  19. receiver will create some small GUI window which displays what material is
  20. currently being used. There is nothing special done in this class, so maybe you
  21. want to skip reading it.
  22. */
  23. class MyEventReceiver : public IEventReceiver
  24. {
  25. public:
  26. MyEventReceiver(scene::ISceneNode* room,scene::ISceneNode* earth,
  27. gui::IGUIEnvironment* env, video::IVideoDriver* driver)
  28. {
  29. // store pointer to room so we can change its drawing mode
  30. Room = room;
  31. Earth = earth;
  32. Driver = driver;
  33. // set a nicer font
  34. gui::IGUISkin* skin = env->getSkin();
  35. gui::IGUIFont* font = env->getFont(getExampleMediaPath() + "fonthaettenschweiler.bmp");
  36. if (font)
  37. skin->setFont(font);
  38. // add window and listbox
  39. gui::IGUIWindow* window = env->addWindow(
  40. core::rect<s32>(460,375,630,470), false, L"Use 'E' + 'R' to change");
  41. ListBox = env->addListBox(
  42. core::rect<s32>(2,22,165,88), window);
  43. ListBox->addItem(L"Diffuse");
  44. ListBox->addItem(L"Bump mapping");
  45. ListBox->addItem(L"Parallax mapping");
  46. ListBox->setSelected(1);
  47. // create problem text
  48. ProblemText = env->addStaticText(
  49. L"Your hardware or this renderer is not able to use the "\
  50. L"needed shaders for this material. Using fall back materials.",
  51. core::rect<s32>(150,20,470,80));
  52. ProblemText->setOverrideColor(video::SColor(100,255,255,255));
  53. // set start material (prefer parallax mapping if available)
  54. video::IMaterialRenderer* renderer =
  55. Driver->getMaterialRenderer(video::EMT_PARALLAX_MAP_SOLID);
  56. if (renderer && renderer->getRenderCapability() == 0)
  57. ListBox->setSelected(2);
  58. // set the material which is selected in the listbox
  59. setMaterial();
  60. }
  61. bool OnEvent(const SEvent& event)
  62. {
  63. // check if user presses the key 'E' or 'R'
  64. if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
  65. !event.KeyInput.PressedDown && Room && ListBox)
  66. {
  67. // change selected item in listbox
  68. int sel = ListBox->getSelected();
  69. if (event.KeyInput.Key == irr::KEY_KEY_R)
  70. ++sel;
  71. else
  72. if (event.KeyInput.Key == irr::KEY_KEY_E)
  73. --sel;
  74. else
  75. return false;
  76. if (sel > 2) sel = 0;
  77. if (sel < 0) sel = 2;
  78. ListBox->setSelected(sel);
  79. // set the material which is selected in the listbox
  80. setMaterial();
  81. }
  82. return false;
  83. }
  84. private:
  85. // sets the material of the room mesh the the one set in the
  86. // list box.
  87. void setMaterial()
  88. {
  89. video::E_MATERIAL_TYPE type = video::EMT_SOLID;
  90. // change material setting
  91. switch(ListBox->getSelected())
  92. {
  93. case 0: type = video::EMT_SOLID;
  94. break;
  95. case 1: type = video::EMT_NORMAL_MAP_SOLID;
  96. break;
  97. case 2: type = video::EMT_PARALLAX_MAP_SOLID;
  98. break;
  99. }
  100. Room->setMaterialType(type);
  101. // change material setting
  102. switch(ListBox->getSelected())
  103. {
  104. case 0: type = video::EMT_TRANSPARENT_VERTEX_ALPHA;
  105. break;
  106. case 1: type = video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA;
  107. break;
  108. case 2: type = video::EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA;
  109. break;
  110. }
  111. Earth->setMaterialType(type);
  112. /*
  113. We need to add a warning if the materials will not be able to
  114. be displayed 100% correctly. This is no problem, they will be
  115. rendered using fall back materials, but at least the user
  116. should know that it would look better on better hardware. We
  117. simply check if the material renderer is able to draw at full
  118. quality on the current hardware. The
  119. IMaterialRenderer::getRenderCapability() returns 0 if this is
  120. the case.
  121. */
  122. video::IMaterialRenderer* renderer = Driver->getMaterialRenderer(type);
  123. // display some problem text when problem
  124. if (!renderer || renderer->getRenderCapability() != 0)
  125. ProblemText->setVisible(true);
  126. else
  127. ProblemText->setVisible(false);
  128. }
  129. private:
  130. gui::IGUIStaticText* ProblemText;
  131. gui::IGUIListBox* ListBox;
  132. scene::ISceneNode* Room;
  133. scene::ISceneNode* Earth;
  134. video::IVideoDriver* Driver;
  135. };
  136. /*
  137. Now for the real fun. We create an Irrlicht Device and start to setup the scene.
  138. */
  139. int main()
  140. {
  141. // ask user for driver
  142. video::E_DRIVER_TYPE driverType=driverChoiceConsole();
  143. if (driverType==video::EDT_COUNT)
  144. return 1;
  145. // create device
  146. IrrlichtDevice* device = createDevice(driverType,
  147. core::dimension2d<u32>(640, 480));
  148. if (device == 0)
  149. return 1; // could not create selected driver.
  150. /*
  151. Before we start with the interesting stuff, we do some simple things:
  152. Store pointers to the most important parts of the engine (video driver,
  153. scene manager, gui environment) to safe us from typing too much, add an
  154. irrlicht engine logo to the window and a user controlled first person
  155. shooter style camera. Also, we let the engine know that it should store
  156. all textures in 32 bit. This necessary because for parallax mapping, we
  157. need 32 bit textures.
  158. */
  159. video::IVideoDriver* driver = device->getVideoDriver();
  160. scene::ISceneManager* smgr = device->getSceneManager();
  161. gui::IGUIEnvironment* env = device->getGUIEnvironment();
  162. driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
  163. const io::path mediaPath = getExampleMediaPath();
  164. // add irrlicht logo
  165. env->addImage(driver->getTexture(mediaPath + "irrlichtlogo3.png"),
  166. core::position2d<s32>(10,10));
  167. // add camera
  168. scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
  169. camera->setPosition(core::vector3df(-200,200,-200));
  170. // disable mouse cursor
  171. device->getCursorControl()->setVisible(false);
  172. switchToMayaCamera(device);
  173. /*
  174. Because we want the whole scene to look a little bit scarier, we add
  175. some fog to it. This is done by a call to IVideoDriver::setFog(). There
  176. you can set various fog settings. In this example, we use pixel fog,
  177. because it will work well with the materials we'll use in this example.
  178. Please note that you will have to set the material flag EMF_FOG_ENABLE
  179. to 'true' in every scene node which should be affected by this fog.
  180. */
  181. driver->setFog(video::SColor(0,138,125,81), video::EFT_FOG_LINEAR, 250, 1000, .003f, true, false);
  182. /*
  183. To be able to display something interesting, we load a mesh from a .3ds
  184. file which is a room I modeled with anim8or. It is the same room as
  185. from the specialFX example. Maybe you remember from that tutorial, I am
  186. no good modeler at all and so I totally messed up the texture mapping
  187. in this model, but we can simply repair it with the
  188. IMeshManipulator::makePlanarTextureMapping() method.
  189. */
  190. scene::IAnimatedMesh* roomMesh = smgr->getMesh(mediaPath + "room.3ds");
  191. scene::ISceneNode* room = 0;
  192. scene::ISceneNode* earth = 0;
  193. if (roomMesh)
  194. {
  195. // The room mesh doesn't have proper texture mapping on the
  196. // floor, so we can recreate the mapping on runtime.
  197. smgr->getMeshManipulator()->makePlanarTextureMapping(
  198. roomMesh->getMesh(0), 0.003f);
  199. /*
  200. Now for the first exciting thing: If we successfully loaded the
  201. mesh we need to apply textures to it. Because we want this room
  202. to be displayed with a very cool material, we have to do a
  203. little bit more than just set the textures. Instead of only
  204. loading a color map as usual, we also load a height map which
  205. is simply a grayscale texture. From this height map, we create
  206. a normal map which we will set as second texture of the room.
  207. If you already have a normal map, you could directly set it,
  208. but I simply didn't find a nice normal map for this texture.
  209. The normal map texture is being generated by the
  210. makeNormalMapTexture method of the VideoDriver. The second
  211. parameter specifies the height of the heightmap. If you set it
  212. to a bigger value, the map will look more rocky.
  213. */
  214. video::ITexture* normalMap =
  215. driver->getTexture(mediaPath + "rockwall_height.bmp");
  216. if (normalMap)
  217. driver->makeNormalMapTexture(normalMap, 9.0f);
  218. /*
  219. But just setting color and normal map is not everything. The
  220. material we want to use needs some additional information per
  221. vertex like tangents and binormals. Because we are too lazy to
  222. calculate that information now, we let Irrlicht do this for us.
  223. That's why we call IMeshManipulator::createMeshWithTangents().
  224. It creates a mesh copy with tangents and binormals from another
  225. mesh. After we've done that, we simply create a standard
  226. mesh scene node with this mesh copy, set color and normal map
  227. and adjust some other material settings. Note that we set
  228. EMF_FOG_ENABLE to true to enable fog in the room.
  229. */
  230. scene::IMesh* tangentMesh = smgr->getMeshManipulator()->
  231. createMeshWithTangents(roomMesh->getMesh(0));
  232. room = smgr->addMeshSceneNode(tangentMesh);
  233. room->setMaterialTexture(0,
  234. driver->getTexture(mediaPath + "rockwall.jpg"));
  235. room->setMaterialTexture(1, normalMap);
  236. // Stones don't glitter.. (but specular highlight for EMT_SOLID)
  237. //room->getMaterial(0).SpecularColor.set(0,0,0,0);
  238. //room->getMaterial(0).Shininess = 0.f;
  239. room->setMaterialFlag(video::EMF_FOG_ENABLE, true);
  240. room->setMaterialType(video::EMT_PARALLAX_MAP_SOLID);
  241. // adjust height for parallax effect
  242. room->getMaterial(0).MaterialTypeParam = 1.f / 64.f;
  243. // drop mesh because we created it with a create.. call.
  244. tangentMesh->drop();
  245. }
  246. /*
  247. After we've created a room shaded by per pixel lighting, we add a
  248. sphere into it with the same material, but we'll make it transparent.
  249. In addition, because the sphere looks somehow like a familiar planet,
  250. we make it rotate. The procedure is similar as before. The difference
  251. is that we are loading the mesh from an .x file which already contains
  252. a color map so we do not need to load it manually. But the sphere is a
  253. little bit too small for our needs, so we scale it by the factor 50.
  254. */
  255. // add earth sphere
  256. scene::IAnimatedMesh* earthMesh = smgr->getMesh(mediaPath + "earth.x");
  257. if (earthMesh)
  258. {
  259. //perform various tasks with the mesh manipulator
  260. scene::IMeshManipulator *manipulator = smgr->getMeshManipulator();
  261. // create mesh copy with tangent information from original earth.x mesh
  262. scene::IMesh* tangentSphereMesh =
  263. manipulator->createMeshWithTangents(earthMesh->getMesh(0));
  264. // set the alpha value of all vertices to 200
  265. manipulator->setVertexColorAlpha(tangentSphereMesh, 200);
  266. // scale the mesh by factor 50
  267. core::matrix4 m;
  268. m.setScale ( core::vector3df(50,50,50) );
  269. manipulator->transform( tangentSphereMesh, m );
  270. earth = smgr->addMeshSceneNode(tangentSphereMesh);
  271. earth->setPosition(core::vector3df(-70,130,45));
  272. // load heightmap, create normal map from it and set it
  273. video::ITexture* earthNormalMap = driver->getTexture(mediaPath + "earthbump.jpg");
  274. if (earthNormalMap)
  275. {
  276. driver->makeNormalMapTexture(earthNormalMap, 20.0f);
  277. earth->setMaterialTexture(1, earthNormalMap);
  278. earth->setMaterialType(video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA);
  279. }
  280. // adjust material settings
  281. earth->setMaterialFlag(video::EMF_FOG_ENABLE, true);
  282. // add rotation animator
  283. scene::ISceneNodeAnimator* anim =
  284. smgr->createRotationAnimator(core::vector3df(0,0.1f,0));
  285. earth->addAnimator(anim);
  286. anim->drop();
  287. // drop mesh because we created it with a create.. call.
  288. tangentSphereMesh->drop();
  289. }
  290. /*
  291. Per pixel lighted materials only look cool when there are moving
  292. lights. So we add some. And because moving lights alone are so boring,
  293. we add billboards to them, and a whole particle system to one of them.
  294. We start with the first light which is red and has only the billboard
  295. attached.
  296. */
  297. // add light 1 (more green)
  298. scene::ILightSceneNode* light1 =
  299. smgr->addLightSceneNode(0, core::vector3df(0,0,0),
  300. video::SColorf(0.5f, 1.0f, 0.5f, 0.0f), 400.0f);
  301. // add fly circle animator to light 1
  302. scene::ISceneNodeAnimator* anim =
  303. smgr->createFlyCircleAnimator (core::vector3df(50,300,0),190.0f, -0.003f);
  304. light1->addAnimator(anim);
  305. anim->drop();
  306. // attach billboard to the light
  307. scene::IBillboardSceneNode* bill =
  308. smgr->addBillboardSceneNode(light1, core::dimension2d<f32>(60, 60));
  309. bill->setMaterialFlag(video::EMF_LIGHTING, false);
  310. bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
  311. bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
  312. bill->setMaterialTexture(0, driver->getTexture(mediaPath + "particlegreen.jpg"));
  313. /*
  314. Now the same again, with the second light. The difference is that we
  315. add a particle system to it too. And because the light moves, the
  316. particles of the particle system will follow. If you want to know more
  317. about how particle systems are created in Irrlicht, take a look at the
  318. SpecialFX example. Maybe you will have noticed that we only add 2
  319. lights, this has a simple reason: The low end version of this material
  320. was written in ps1.1 and vs1.1, which doesn't allow more lights. You
  321. could add a third light to the scene, but it won't be used to shade the
  322. walls. But of course, this will change in future versions of Irrlicht
  323. where higher versions of pixel/vertex shaders will be implemented too.
  324. */
  325. // add light 2 (red)
  326. scene::ISceneNode* light2 =
  327. smgr->addLightSceneNode(0, core::vector3df(0,0,0),
  328. video::SColorf(1.0f, 0.2f, 0.2f, 0.0f), 400.0f);
  329. // add fly circle animator to light 2
  330. anim = smgr->createFlyCircleAnimator(core::vector3df(0,150,0), 200.0f,
  331. 0.001f, core::vector3df(0.2f, 0.9f, 0.f));
  332. light2->addAnimator(anim);
  333. anim->drop();
  334. // attach billboard to light
  335. bill = smgr->addBillboardSceneNode(light2, core::dimension2d<f32>(120, 120));
  336. bill->setMaterialFlag(video::EMF_LIGHTING, false);
  337. bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
  338. bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
  339. bill->setMaterialTexture(0, driver->getTexture(mediaPath + "particlered.bmp"));
  340. // add particle system
  341. scene::IParticleSystemSceneNode* ps =
  342. smgr->addParticleSystemSceneNode(false, light2);
  343. // create and set emitter
  344. scene::IParticleEmitter* em = ps->createBoxEmitter(
  345. core::aabbox3d<f32>(-3,0,-3,3,1,3),
  346. core::vector3df(0.0f,0.03f,0.0f),
  347. 80,100,
  348. video::SColor(10,255,255,255), video::SColor(10,255,255,255),
  349. 400,1100);
  350. em->setMinStartSize(core::dimension2d<f32>(30.0f, 40.0f));
  351. em->setMaxStartSize(core::dimension2d<f32>(30.0f, 40.0f));
  352. ps->setEmitter(em);
  353. em->drop();
  354. // create and set affector
  355. scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
  356. ps->addAffector(paf);
  357. paf->drop();
  358. // adjust some material settings
  359. ps->setMaterialFlag(video::EMF_LIGHTING, false);
  360. ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
  361. ps->setMaterialTexture(0, driver->getTexture(mediaPath + "fireball.bmp"));
  362. ps->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
  363. MyEventReceiver receiver(room, earth, env, driver);
  364. device->setEventReceiver(&receiver);
  365. /*
  366. Finally, draw everything. That's it.
  367. */
  368. int lastFPS = -1;
  369. while(device->run())
  370. if (device->isWindowActive())
  371. {
  372. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0));
  373. smgr->drawAll();
  374. env->drawAll();
  375. driver->endScene();
  376. int fps = driver->getFPS();
  377. if (lastFPS != fps)
  378. {
  379. core::stringw str = L"Per pixel lighting example - Irrlicht Engine [";
  380. str += driver->getName();
  381. str += "] FPS:";
  382. str += fps;
  383. device->setWindowCaption(str.c_str());
  384. lastFPS = fps;
  385. }
  386. }
  387. device->drop();
  388. return 0;
  389. }
  390. /*
  391. **/