main.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /** Example 012 Terrain Rendering
  2. This tutorial will briefly show how to use the terrain renderer of Irrlicht. It
  3. will also show the terrain renderer triangle selector to be able to do
  4. collision detection with terrain.
  5. Note that the Terrain Renderer in Irrlicht is based on Spintz'
  6. GeoMipMapSceneNode, lots of thanks go to him. DeusXL provided a new elegant
  7. simple solution for building larger area on small heightmaps -> terrain
  8. smoothing.
  9. In the beginning there is nothing special. We include the needed header files
  10. and create an event listener to listen if the user presses certain keys.
  11. */
  12. #include <irrlicht.h>
  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. class MyEventReceiver : public IEventReceiver
  20. {
  21. public:
  22. MyEventReceiver(scene::ISceneNode* terrain, scene::ISceneNode* skybox, scene::ISceneNode* skydome) :
  23. Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true), showDebug(false)
  24. {
  25. Skybox->setVisible(showBox);
  26. Skydome->setVisible(!showBox);
  27. }
  28. bool OnEvent(const SEvent& event)
  29. {
  30. // check if user presses the key 'W' or 'D'
  31. if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
  32. {
  33. switch (event.KeyInput.Key)
  34. {
  35. case irr::KEY_KEY_W: // switch wire frame mode
  36. Terrain->setMaterialFlag(video::EMF_WIREFRAME,
  37. !Terrain->getMaterial(0).Wireframe);
  38. Terrain->setMaterialFlag(video::EMF_POINTCLOUD, false);
  39. return true;
  40. case irr::KEY_KEY_P: // switch point cloud mode
  41. Terrain->setMaterialFlag(video::EMF_POINTCLOUD,
  42. !Terrain->getMaterial(0).PointCloud);
  43. Terrain->setMaterialFlag(video::EMF_WIREFRAME, false);
  44. return true;
  45. case irr::KEY_KEY_D: // toggle detail map
  46. Terrain->setMaterialType(
  47. Terrain->getMaterial(0).MaterialType == video::EMT_SOLID ?
  48. video::EMT_DETAIL_MAP : video::EMT_SOLID);
  49. return true;
  50. case irr::KEY_KEY_S: // toggle skies
  51. showBox=!showBox;
  52. Skybox->setVisible(showBox);
  53. Skydome->setVisible(!showBox);
  54. return true;
  55. case irr::KEY_KEY_X: // toggle debug information
  56. showDebug=!showDebug;
  57. Terrain->setDebugDataVisible(showDebug?scene::EDS_BBOX_ALL:scene::EDS_OFF);
  58. return true;
  59. default:
  60. break;
  61. }
  62. }
  63. return false;
  64. }
  65. private:
  66. scene::ISceneNode* Terrain;
  67. scene::ISceneNode* Skybox;
  68. scene::ISceneNode* Skydome;
  69. bool showBox;
  70. bool showDebug;
  71. };
  72. /*
  73. The start of the main function starts like in most other example. We ask the
  74. user for the desired renderer and start it up. This time with the advanced
  75. parameter handling.
  76. */
  77. int main()
  78. {
  79. // ask user for driver
  80. video::E_DRIVER_TYPE driverType=driverChoiceConsole();
  81. if (driverType==video::EDT_COUNT)
  82. return 1;
  83. // create device with full flexibility over creation parameters
  84. // you can add more parameters if desired, check irr::SIrrlichtCreationParameters
  85. irr::SIrrlichtCreationParameters params;
  86. params.DriverType=driverType;
  87. params.WindowSize=core::dimension2d<u32>(640, 480);
  88. IrrlichtDevice* device = createDeviceEx(params);
  89. if (device == 0)
  90. return 1; // could not create selected driver.
  91. /*
  92. First, we add standard stuff to the scene: A nice irrlicht engine
  93. logo, a small help text, a user controlled camera, and we disable
  94. the mouse cursor.
  95. */
  96. video::IVideoDriver* driver = device->getVideoDriver();
  97. scene::ISceneManager* smgr = device->getSceneManager();
  98. gui::IGUIEnvironment* env = device->getGUIEnvironment();
  99. driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
  100. const io::path mediaPath = getExampleMediaPath();
  101. // add irrlicht logo
  102. env->addImage(driver->getTexture(mediaPath + "irrlichtlogo3.png"),
  103. core::position2d<s32>(10,10));
  104. //set other font
  105. env->getSkin()->setFont(env->getFont(mediaPath + "fontlucida.png"));
  106. // add some help text (let's ignore 'P' and 'X' which are more about debugging)
  107. env->addStaticText(
  108. L"Press 'W' to change wireframe mode\nPress 'D' to toggle detail map\nPress 'S' to toggle skybox/skydome",
  109. core::rect<s32>(10,421,250,475), true, true, 0, -1, true);
  110. // add camera
  111. scene::ICameraSceneNode* camera =
  112. smgr->addCameraSceneNodeFPS(0,100.0f,1.2f);
  113. camera->setPosition(core::vector3df(2700*2,255*2,2600*2));
  114. camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
  115. camera->setFarValue(42000.0f);
  116. // disable mouse cursor
  117. device->getCursorControl()->setVisible(false);
  118. /*
  119. Here comes the terrain renderer scene node: We add it just like any
  120. other scene node to the scene using
  121. ISceneManager::addTerrainSceneNode(). The first parameter is a
  122. file name to the heightmap we use. A heightmap is simply a gray scale
  123. texture. The terrain renderer loads it and creates the 3D terrain from
  124. it.
  125. To make the terrain look bigger, we change it's scale factor to
  126. (40, 4.4, 40). Because we don't have any dynamic lights in the
  127. scene, we switch off the lighting, and we set the file
  128. terrain-texture.jpg as texture for the terrain and detailmap3.jpg as
  129. second texture, called detail map. At last, we set the scale values for
  130. the texture: The first texture will be repeated only one time over the
  131. whole terrain, and the second one (detail map) 20 times.
  132. */
  133. // add terrain scene node
  134. scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
  135. mediaPath + "terrain-heightmap.bmp",
  136. 0, // parent node
  137. -1, // node id
  138. core::vector3df(0.f, 0.f, 0.f), // position
  139. core::vector3df(0.f, 0.f, 0.f), // rotation
  140. core::vector3df(40.f, 4.4f, 40.f), // scale
  141. video::SColor ( 255, 255, 255, 255 ), // vertexColor
  142. 5, // maxLOD
  143. scene::ETPS_17, // patchSize
  144. 4 // smoothFactor
  145. );
  146. terrain->setMaterialFlag(video::EMF_LIGHTING, false);
  147. terrain->setMaterialTexture(0,
  148. driver->getTexture(mediaPath + "terrain-texture.jpg"));
  149. terrain->setMaterialTexture(1,
  150. driver->getTexture(mediaPath + "detailmap3.jpg"));
  151. terrain->setMaterialType(video::EMT_DETAIL_MAP);
  152. terrain->scaleTexture(1.0f, 20.0f);
  153. /*
  154. To be able to do collision with the terrain, we create a triangle selector.
  155. If you want to know what triangle selectors do, just take a look into the
  156. collision tutorial. The terrain triangle selector works together with the
  157. terrain. To demonstrate this, we create a collision response animator
  158. and attach it to the camera, so that the camera will not be able to fly
  159. through the terrain.
  160. */
  161. // create triangle selector for the terrain
  162. scene::ITriangleSelector* selector
  163. = smgr->createTerrainTriangleSelector(terrain, 0);
  164. terrain->setTriangleSelector(selector);
  165. // create collision response animator and attach it to the camera
  166. scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
  167. selector, camera, core::vector3df(60,100,60),
  168. core::vector3df(0,0,0),
  169. core::vector3df(0,50,0));
  170. selector->drop();
  171. camera->addAnimator(anim);
  172. anim->drop();
  173. /* If you need access to the terrain data you can also do this directly via the following code fragment.
  174. */
  175. scene::CDynamicMeshBuffer* buffer = new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
  176. terrain->getMeshBufferForLOD(*buffer, 0);
  177. video::S3DVertex2TCoords* data = (video::S3DVertex2TCoords*)buffer->getVertexBuffer().getData();
  178. // Work on data or get the IndexBuffer with a similar call.
  179. (void)data; // disable unused variable warnings
  180. buffer->drop(); // When done drop the buffer again.
  181. /*
  182. To make the user be able to switch between normal and wireframe mode,
  183. we create an instance of the event receiver from above and let Irrlicht
  184. know about it. In addition, we add the skybox which we already used in
  185. lots of Irrlicht examples and a skydome, which is shown mutually
  186. exclusive with the skybox by pressing 'S'.
  187. */
  188. // create skybox and skydome
  189. driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
  190. scene::ISceneNode* skybox=smgr->addSkyBoxSceneNode(
  191. driver->getTexture(mediaPath + "irrlicht2_up.jpg"),
  192. driver->getTexture(mediaPath + "irrlicht2_dn.jpg"),
  193. driver->getTexture(mediaPath + "irrlicht2_lf.jpg"),
  194. driver->getTexture(mediaPath + "irrlicht2_rt.jpg"),
  195. driver->getTexture(mediaPath + "irrlicht2_ft.jpg"),
  196. driver->getTexture(mediaPath + "irrlicht2_bk.jpg"));
  197. scene::ISceneNode* skydome=smgr->addSkyDomeSceneNode(driver->getTexture(mediaPath + "skydome.jpg"),16,8,0.95f,2.0f);
  198. driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
  199. // create event receiver
  200. MyEventReceiver receiver(terrain, skybox, skydome);
  201. device->setEventReceiver(&receiver);
  202. /*
  203. That's it, draw everything.
  204. */
  205. int lastFPS = -1;
  206. while(device->run())
  207. if (device->isWindowActive())
  208. {
  209. driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0));
  210. smgr->drawAll();
  211. env->drawAll();
  212. driver->endScene();
  213. // display frames per second in window title
  214. int fps = driver->getFPS();
  215. if (lastFPS != fps)
  216. {
  217. core::stringw str = L"Terrain Renderer - Irrlicht Engine [";
  218. str += driver->getName();
  219. str += "] FPS:";
  220. str += fps;
  221. // Also print terrain height of current camera position
  222. // We can use camera position because terrain is located at coordinate origin
  223. str += " Height: ";
  224. str += terrain->getHeight(camera->getAbsolutePosition().X,
  225. camera->getAbsolutePosition().Z);
  226. device->setWindowCaption(str.c_str());
  227. lastFPS = fps;
  228. }
  229. }
  230. device->drop();
  231. return 0;
  232. }
  233. /*
  234. Now you know how to use terrain in Irrlicht.
  235. **/