main.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /** Example 008 SpecialFX
  2. This tutorial describes how to do special effects. It shows how to use stencil
  3. buffer shadows, the particle system, billboards, dynamic light, and the water
  4. surface scene node.
  5. We start like in some tutorials before. Please note that this time, the
  6. 'shadows' flag in createDevice() is set to true, for we want to have a dynamic
  7. shadow cast from an animated character. If this example runs too slow,
  8. set it to false. The Irrlicht Engine also checks if your hardware doesn't
  9. support the stencil buffer, and then disables shadows by itself.
  10. */
  11. #include <irrlicht.h>
  12. #include <iostream>
  13. #include "driverChoice.h"
  14. #include "exampleHelper.h"
  15. using namespace irr;
  16. #ifdef _MSC_VER
  17. #pragma comment(lib, "Irrlicht.lib")
  18. #endif
  19. int main()
  20. {
  21. // ask if user would like shadows
  22. char i = 'y';
  23. printf("Please press 'y' if you want to use realtime shadows.\n");
  24. std::cin >> i;
  25. const bool shadows = (i == 'y');
  26. // ask user for driver
  27. video::E_DRIVER_TYPE driverType=driverChoiceConsole();
  28. if (driverType==video::EDT_COUNT)
  29. return 1;
  30. /*
  31. Create device and exit if creation failed. We make the stencil flag
  32. optional to avoid slow screen modes for runs without shadows.
  33. */
  34. IrrlichtDevice *device =
  35. createDevice(driverType, core::dimension2d<u32>(640, 480),
  36. 16, false, shadows);
  37. if (device == 0)
  38. return 1; // could not create selected driver.
  39. video::IVideoDriver* driver = device->getVideoDriver();
  40. scene::ISceneManager* smgr = device->getSceneManager();
  41. const io::path mediaPath = getExampleMediaPath();
  42. /*
  43. For our environment, we load a .3ds file. It is a small room I modeled
  44. with Anim8or and exported into the 3ds format because the Irrlicht
  45. Engine does not support the .an8 format. I am a very bad 3d graphic
  46. artist, and so the texture mapping is not very nice in this model.
  47. Luckily I am a better programmer than artist, and so the Irrlicht
  48. Engine is able to create a cool texture mapping for me: Just use the
  49. mesh manipulator and create a planar texture mapping for the mesh. If
  50. you want to see the mapping I made with Anim8or, uncomment this line. I
  51. also did not figure out how to set the material right in Anim8or, it
  52. has a specular light color which I don't really like. I'll switch it
  53. off too with this code.
  54. */
  55. scene::IAnimatedMesh* mesh = smgr->getMesh(mediaPath + "room.3ds");
  56. if ( !mesh )
  57. {
  58. printf("Can't find model room.3ds in media path");
  59. return 1;
  60. }
  61. smgr->getMeshManipulator()->makePlanarTextureMapping(mesh->getMesh(0), 0.004f);
  62. scene::ISceneNode* node = 0;
  63. node = smgr->addAnimatedMeshSceneNode(mesh);
  64. node->setMaterialTexture(0, driver->getTexture(mediaPath + "wall.jpg"));
  65. node->getMaterial(0).SpecularColor.set(0,0,0,0);
  66. /*
  67. Now, for the first special effect: Animated water. It works like this:
  68. The WaterSurfaceSceneNode takes a mesh as input and makes it wave like
  69. a water surface. And if we let this scene node use a nice material like
  70. the EMT_REFLECTION_2_LAYER, it looks really cool. We are doing this
  71. with the next few lines of code. As input mesh, we create a hill plane
  72. mesh, without hills. But any other mesh could be used for this, you
  73. could even use the room.3ds (which would look really strange) if you
  74. want to.
  75. */
  76. mesh = smgr->addHillPlaneMesh( "myHill",
  77. core::dimension2d<f32>(20,20),
  78. core::dimension2d<u32>(40,40), 0, 0,
  79. core::dimension2d<f32>(0,0),
  80. core::dimension2d<f32>(10,10));
  81. node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0), 3.0f, 300.0f, 30.0f);
  82. node->setPosition(core::vector3df(0,7,0));
  83. node->setMaterialTexture(0, driver->getTexture(mediaPath + "stones.jpg"));
  84. node->setMaterialTexture(1, driver->getTexture(mediaPath + "water.jpg"));
  85. node->setMaterialType(video::EMT_REFLECTION_2_LAYER);
  86. /*
  87. The second special effect is very basic, I bet you saw it already in
  88. some Irrlicht Engine demos: A transparent billboard combined with a
  89. dynamic light. We simply create a light scene node, let it fly around,
  90. and to make it look more cool, we attach a billboard scene node to it.
  91. */
  92. // create light
  93. scene::ILightSceneNode * lightNode = smgr->addLightSceneNode(0, core::vector3df(0,0,0),
  94. video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 800.0f);
  95. scene::ISceneNodeAnimator* anim = 0;
  96. anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f, 0.0005f);
  97. lightNode ->addAnimator(anim);
  98. anim->drop();
  99. // attach billboard to light
  100. node = smgr->addBillboardSceneNode(lightNode, core::dimension2d<f32>(50, 50));
  101. node->setMaterialFlag(video::EMF_LIGHTING, false);
  102. node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
  103. node->setMaterialTexture(0, driver->getTexture(mediaPath + "particlewhite.bmp"));
  104. /*
  105. The next special effect is a lot more interesting: A particle system.
  106. The particle system in the Irrlicht Engine is quite modular and
  107. extensible, but yet easy to use. There is a particle system scene node
  108. into which you can put a particle emitter, which makes particles come out
  109. of nothing. These emitters are quite flexible and usually have lots of
  110. parameters like direction, amount, and color of the particles they
  111. create.
  112. There are different emitters, for example a point emitter which lets
  113. particles pop out at a fixed point. If the particle emitters available
  114. in the engine are not enough for you, you can easily create your own
  115. ones, you'll simply have to create a class derived from the
  116. IParticleEmitter interface and attach it to the particle system using
  117. setEmitter(). In this example we create a box particle emitter, which
  118. creates particles randomly inside a box. The parameters define the box,
  119. direction of the particles, minimal and maximal new particles per
  120. second, color, and minimal and maximal lifetime of the particles.
  121. Because only with emitters particle system would be a little bit
  122. boring, there are particle affectors which modify particles while
  123. they fly around. Affectors can be added to a particle system for
  124. simulating additional effects like gravity or wind.
  125. The particle affector we use in this example is an affector which
  126. modifies the color of the particles: It lets them fade out. Like the
  127. particle emitters, additional particle affectors can also be
  128. implemented by you, simply derive a class from IParticleAffector and
  129. add it with addAffector().
  130. After we set a nice material to the particle system, we have a cool
  131. looking camp fire. By adjusting material, texture, particle emitter,
  132. and affector parameters, it is also easily possible to create smoke,
  133. rain, explosions, snow, and so on.
  134. */
  135. // create a particle system
  136. scene::IParticleSystemSceneNode* ps =
  137. smgr->addParticleSystemSceneNode(false);
  138. if (ps)
  139. {
  140. scene::IParticleEmitter* em = ps->createBoxEmitter(
  141. core::aabbox3d<f32>(-7,0,-7,7,1,7), // emitter size
  142. core::vector3df(0.0f,0.06f,0.0f), // initial direction
  143. 80,100, // emit rate
  144. video::SColor(0,255,255,255), // darkest color
  145. video::SColor(0,255,255,255), // brightest color
  146. 800,2000,0, // min and max age, angle
  147. core::dimension2df(10.f,10.f), // min size
  148. core::dimension2df(20.f,20.f)); // max size
  149. ps->setEmitter(em); // this grabs the emitter
  150. em->drop(); // so we can drop it here without deleting it
  151. scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
  152. ps->addAffector(paf); // same goes for the affector
  153. paf->drop();
  154. ps->setPosition(core::vector3df(-70,60,40));
  155. ps->setScale(core::vector3df(2,2,2));
  156. ps->setMaterialFlag(video::EMF_LIGHTING, false);
  157. ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
  158. ps->setMaterialTexture(0, driver->getTexture(mediaPath + "fire.bmp"));
  159. ps->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
  160. }
  161. /*
  162. Next we add a volumetric light node, which adds a glowing fake area light to
  163. the scene. Like with the billboards and particle systems we also assign a
  164. texture for the desired effect, though this time we'll use a texture animator
  165. to create the illusion of a magical glowing area effect.
  166. */
  167. scene::IVolumeLightSceneNode * n = smgr->addVolumeLightSceneNode(0, -1,
  168. 32, // Subdivisions on U axis
  169. 32, // Subdivisions on V axis
  170. video::SColor(0, 255, 255, 255), // foot color
  171. video::SColor(0, 0, 0, 0)); // tail color
  172. if (n)
  173. {
  174. n->setScale(core::vector3df(56.0f, 56.0f, 56.0f));
  175. n->setPosition(core::vector3df(-120,50,40));
  176. // load textures for animation
  177. core::array<video::ITexture*> textures;
  178. for (s32 g=7; g > 0; --g)
  179. {
  180. core::stringc tmp(mediaPath);
  181. tmp += "portal";
  182. tmp += g;
  183. tmp += ".bmp";
  184. video::ITexture* t = driver->getTexture( tmp.c_str() );
  185. textures.push_back(t);
  186. }
  187. // create texture animator
  188. scene::ISceneNodeAnimator* glow = smgr->createTextureAnimator(textures, 150);
  189. // add the animator
  190. n->addAnimator(glow);
  191. // drop the animator because it was created with a create() function
  192. glow->drop();
  193. }
  194. /*
  195. As our last special effect, we want a dynamic shadow be cast from an
  196. animated character. For this we load a DirectX .x model and place it
  197. into our world. For creating the shadow, we simply need to call
  198. addShadowVolumeSceneNode(). The color of shadows is only adjustable
  199. globally for all shadows, by calling ISceneManager::setShadowColor().
  200. Voila, here is our dynamic shadow.
  201. Because the character is a little bit too small for this scene, we make
  202. it bigger using setScale(). And because the character is lighted by a
  203. dynamic light, we need to normalize the normals to make the lighting on
  204. it correct. This is always necessary if the scale of a dynamic lighted
  205. model is not (1,1,1). Otherwise it would get too dark or too bright
  206. because the normals will be scaled too.
  207. */
  208. // add animated character
  209. mesh = smgr->getMesh(mediaPath + "dwarf.x");
  210. if (!mesh) // some Linux distributions might not have dwarf due to license
  211. {
  212. mesh = smgr->addArrowMesh("no_dwarf", video::SColor(0xFFFFFFFF), video::SColor(0xFFFFFFFF), 4, 8, 80.f, 50.f, 5.f, 15.f);
  213. }
  214. scene::IAnimatedMeshSceneNode* anode = 0;
  215. anode = smgr->addAnimatedMeshSceneNode(mesh);
  216. anode->setPosition(core::vector3df(-50,20,-60));
  217. anode->setAnimationSpeed(15);
  218. /*
  219. Shadows still have to be drawn even then the node causing them is not visible itself.
  220. We have to disable culling if the node is animated or it's transformations change
  221. as otherwise the shadow is not updated correctly.
  222. If you have many objects and this becomes a speed problem you will have to figure
  223. out some manual culling (for exampling hiding all objects beyond a certain distance).
  224. */
  225. anode->setAutomaticCulling(scene::EAC_OFF);
  226. // add shadow
  227. anode->addShadowVolumeSceneNode();
  228. smgr->setShadowColor(video::SColor(150,0,0,0));
  229. // make the model a bit bigger
  230. anode->setScale(core::vector3df(2,2,2));
  231. // because of the scaling we have to normalize its normals for correct lighting
  232. anode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
  233. // let the dwarf slowly rotate around it's y axis
  234. scene::ISceneNodeAnimator* ra = smgr->createRotationAnimator(irr::core::vector3df(0, 0.1f, 0));
  235. anode->addAnimator(ra);
  236. ra->drop();
  237. /*
  238. Finally we simply have to draw everything, that's all.
  239. */
  240. scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
  241. camera->setPosition(core::vector3df(-50,50,-150));
  242. camera->setFarValue(10000.0f); // this increase a shadow visible range.
  243. // disable mouse cursor
  244. device->getCursorControl()->setVisible(false);
  245. switchToMayaCamera(device);
  246. s32 lastFPS = -1;
  247. while(device->run())
  248. if (device->isWindowActive())
  249. {
  250. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0));
  251. smgr->drawAll();
  252. driver->endScene();
  253. const s32 fps = driver->getFPS();
  254. if (lastFPS != fps)
  255. {
  256. core::stringw str = L"Irrlicht Engine - SpecialFX example [";
  257. str += driver->getName();
  258. str += "] FPS:";
  259. str += fps;
  260. device->setWindowCaption(str.c_str());
  261. lastFPS = fps;
  262. }
  263. }
  264. device->drop();
  265. return 0;
  266. }
  267. /*
  268. **/