1 //===============================================================================================================
2 //Example 3 - Trees and Bushes
3 //---------------------------------------------------------------------------------------------------------------
4 // This example demonstrates the use of PagedGeometry to display trees and bushes with different view ranges.
5 // Instructions: Move around with the arrow/WASD keys, hold SHIFT to move faster, and hold SPACE to fly.
6 // HINT: Search this source for "[NOTE]" to find important code and comments related to PagedGeometry.
7 //===============================================================================================================
8 #define AppTitle "PagedGeometry Example 3 - Trees and Bushes"
9
10 //Include windows/Ogre/OIS headers
11 #include "PagedGeometryConfig.h"
12 #include <Ogre.h>
13 #ifdef OIS_USING_DIR
14 # include "OIS/OIS.h"
15 #else
16 # include "OIS.h"
17 #endif //OIS_USING_DIR
18 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
19 #include <windows.h>
20 #endif
21 using namespace Ogre;
22
23
24 //Include PagedGeometry headers that will be needed
25 #include "PagedGeometry.h"
26 #include "BatchPage.h"
27 #include "ImpostorPage.h"
28 #include "TreeLoader2D.h"
29
30 //Include "HeightFunction.h", a header that provides some useful functions for quickly and easily
31 //getting the height of the terrain at a given point.
32 #include "HeightFunction.h"
33 //[NOTE] Remember that this "HeightFunction.h" file is not related to the PagedGeometry library itself
34 //in any way. It's simply a utility that's included with all these examples to make getting the terrain
35 //height easy. You can use it in your games/applications if you want, although if you're using a
36 //collision/physics library with a faster alternate, you may use that instead.
37
38 //PagedGeometry's classes and functions are under the "Forests" namespace
39 using namespace Forests;
40
41 //Demo world class
42 //[NOTE] The main PagedGeometry-related sections of this class are load() and
43 //render. These functions setup and use PagedGeometry in the scene.
44 class World
45 {
46 public:
47 World();
48 ~World();
49
50 void load(); //Loads the 3D scene
51 void unload(); //Unloads the 3D scene cleanly
52 void run(); //Runs the simulation
53
54 private:
55 void render(); //Renders a single frame, updating PagedGeometry and Ogre
56 void processInput(); //Accepts keyboard and mouse input, allowing you to move around in the world
57
58 bool running; //A flag which, when set to false, will terminate a simulation started with run()
59
60 //Various pointers to Ogre objects are stored here:
61 Root *root;
62 RenderWindow *window;
63 Viewport *viewport;
64 SceneManager *sceneMgr;
65 Camera *camera;
66
67 //OIS input objects
68 OIS::InputManager *inputManager;
69 OIS::Keyboard *keyboard;
70 OIS::Mouse *mouse;
71
72 //Variables used to keep track of the camera's rotation/etc.
73 Radian camPitch, camYaw;
74
75 //Pointers to PagedGeometry class instances:
76 PagedGeometry *trees, *bushes;
77 };
78
79 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
WinMain(HINSTANCE hInst,HINSTANCE hPrevInstance,LPSTR strCmdLine,INT nCmdShow)80 INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR strCmdLine, INT nCmdShow)
81 #else
82 int main(int argc, char *argv[])
83 #endif
84 {
85 //Initialize Ogre
86 Root *root = new Ogre::Root("");
87
88 //Load appropriate plugins
89 //[NOTE] PagedGeometry needs the CgProgramManager plugin to compile shaders
90 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
91 #ifdef _DEBUG
92 root->loadPlugin("Plugin_CgProgramManager_d");
93 root->loadPlugin("Plugin_OctreeSceneManager_d");
94 root->loadPlugin("RenderSystem_Direct3D9_d");
95 root->loadPlugin("RenderSystem_GL_d");
96 #else
97 root->loadPlugin("Plugin_CgProgramManager");
98 root->loadPlugin("Plugin_OctreeSceneManager");
99 root->loadPlugin("RenderSystem_Direct3D9");
100 root->loadPlugin("RenderSystem_GL");
101 #endif
102 #else
103 root->loadPlugin("Plugin_CgProgramManager");
104 root->loadPlugin("Plugin_OctreeSceneManager");
105 root->loadPlugin("RenderSystem_GL");
106 #endif
107
108 //Show Ogre's default config dialog to let the user setup resolution, etc.
109 bool result = root->showConfigDialog();
110
111 //If the user clicks OK, continue
112 if (result) {
113 World myWorld;
114 myWorld.load(); //Load world
115 myWorld.run(); //Display world
116 }
117
118 //Shut down Ogre
119 delete root;
120
121 return 0;
122 }
123
World()124 World::World()
125 {
126 //Setup Ogre::Root and the scene manager
127 root = Root::getSingletonPtr();
128 window = root->initialise(true, AppTitle);
129 sceneMgr = root->createSceneManager(ST_EXTERIOR_CLOSE);
130
131 //Initialize the camera and viewport
132 camera = sceneMgr->createCamera("MainCamera");
133 viewport = window->addViewport(camera);
134 viewport->setBackgroundColour(ColourValue(0.47f, 0.67f, 0.96f)); //Blue sky background color
135 camera->setAspectRatio(Real(viewport->getActualWidth()) / Real(viewport->getActualHeight()));
136 camera->setNearClipDistance(1.0f);
137 camera->setFarClipDistance(2000.0f);
138
139 //Set up lighting
140 Light *light = sceneMgr->createLight("Sun");
141 light->setType(Light::LT_DIRECTIONAL);
142 light->setDirection(Vector3(0.0f, -0.5f, 1.0f));
143 sceneMgr->setAmbientLight(ColourValue(1, 1, 1));
144
145 //Load media (trees, grass, etc.)
146 ResourceGroupManager::getSingleton().addResourceLocation("media/trees", "FileSystem");
147 ResourceGroupManager::getSingleton().addResourceLocation("media/terrains", "FileSystem");
148 ResourceGroupManager::getSingleton().addResourceLocation("media/grass", "FileSystem");
149 ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
150
151 //Initialize OIS
152 using namespace OIS;
153 size_t windowHnd;
154 window->getCustomAttribute("WINDOW", &windowHnd);
155 inputManager = InputManager::createInputSystem(windowHnd);
156
157 keyboard = (Keyboard*)inputManager->createInputObject(OISKeyboard, false);
158 mouse = (Mouse*)inputManager->createInputObject(OISMouse, false);
159
160 //Reset camera orientation
161 camPitch = 0;
162 camYaw = 0;
163 }
164
~World()165 World::~World()
166 {
167 //Shut down OIS
168 inputManager->destroyInputObject(keyboard);
169 inputManager->destroyInputObject(mouse);
170 OIS::InputManager::destroyInputSystem(inputManager);
171
172 unload();
173 }
174
175
176 //[NOTE] In addition to some Ogre setup, this function configures PagedGeometry in the scene.
load()177 void World::load()
178 {
179 //-------------------------------------- LOAD TERRAIN --------------------------------------
180 //Setup the fog up to 500 units away
181 sceneMgr->setFog(FOG_LINEAR, viewport->getBackgroundColour(), 0, 100, 700);
182
183 //Load the terrain
184 sceneMgr->setWorldGeometry("terrain.cfg");
185
186 //Start off with the camera at the center of the terrain
187 camera->setPosition(700, 100, 700);
188
189 //-------------------------------------- LOAD TREES --------------------------------------
190 //Create and configure a new PagedGeometry instance for trees
191 trees = new PagedGeometry(camera, 80);
192 trees->addDetailLevel<BatchPage>(150, 50);
193 trees->addDetailLevel<ImpostorPage>(500, 50);
194
195 //Create a new TreeLoader2D object
196 TreeLoader2D *treeLoader = new TreeLoader2D(trees, TBounds(0, 0, 1500, 1500));
197 trees->setPageLoader(treeLoader);
198
199 //Supply the height function to TreeLoader2D so it can calculate tree Y values
200 HeightFunction::initialize(sceneMgr);
201 treeLoader->setHeightFunction(&HeightFunction::getTerrainHeight);
202
203 //Load a tree entity
204 Entity *myTree = sceneMgr->createEntity("Tree", "tree2.mesh");
205
206 //Randomly place 10,000 copies of the tree on the terrain
207 Vector3 position = Vector3::ZERO;
208 Radian yaw;
209 Real scale;
210 for (int i = 0; i < 10000; i++){
211 yaw = Degree(Math::RangeRandom(0, 360));
212 position.x = Math::RangeRandom(0, 1500);
213 position.z = Math::RangeRandom(0, 1500);
214 scale = Math::RangeRandom(0.5f, 0.6f);
215
216 treeLoader->addTree(myTree, position, yaw, scale);
217 }
218
219 //-------------------------------------- LOAD BUSHES --------------------------------------
220 //Create and configure a new PagedGeometry instance for bushes
221 bushes = new PagedGeometry(camera, 50);
222 bushes->addDetailLevel<BatchPage>(80, 50);
223
224 //Create a new TreeLoader2D object for the bushes
225 TreeLoader2D *bushLoader = new TreeLoader2D(bushes, TBounds(0, 0, 1500, 1500));
226 bushes->setPageLoader(bushLoader);
227
228 //Supply the height function to TreeLoader2D so it can calculate tree Y values
229 HeightFunction::initialize(sceneMgr);
230 bushLoader->setHeightFunction(&HeightFunction::getTerrainHeight);
231
232 //Load a bush entity
233 Entity *myBush = sceneMgr->createEntity("Bush", "Bush.mesh");
234
235 //Randomly place 30,000 copies of the bush on the terrain
236 for (int i = 0; i < 30000; i++){
237 yaw = Degree(Math::RangeRandom(0, 360));
238 position.x = Math::RangeRandom(0, 1500);
239 position.z = Math::RangeRandom(0, 1500);
240 scale = Math::RangeRandom(0.7f, 0.8f);
241
242 bushLoader->addTree(myBush, position, yaw, scale);
243 }
244 }
245
unload()246 void World::unload()
247 {
248 //[NOTE] Always remember to delete any PageLoader(s) and PagedGeometry instances in order to avoid memory leaks.
249
250 //Delete the TreeLoader2D instances
251 delete trees->getPageLoader();
252 delete bushes->getPageLoader();
253
254 //Delete the PagedGeometry instances
255 delete trees;
256 delete bushes;
257
258 //Also delete the tree/bush entities
259 sceneMgr->destroyEntity("Tree");
260 sceneMgr->destroyEntity("Bush");
261 }
262
run()263 void World::run()
264 {
265 //Render loop
266 running = true;
267 while(running)
268 {
269 //Handle windows events
270 WindowEventUtilities::messagePump();
271
272 //Update frame
273 processInput();
274 render();
275
276 //Exit immediately if the window is closed
277 if (window->isClosed())
278 break;
279 }
280 }
281
render()282 void World::render()
283 {
284 //[NOTE] PagedGeometry::update() is called every frame to keep LODs, etc. up-to-date
285 trees->update();
286 bushes->update();
287
288 //Render the scene with Ogre
289 root->renderOneFrame();
290 }
291
processInput()292 void World::processInput()
293 {
294 using namespace OIS;
295 static Ogre::Timer timer;
296 static unsigned long lastTime = 0;
297 unsigned long currentTime = timer.getMilliseconds();
298
299 //Calculate the amount of time passed since the last frame
300 Real timeScale = (currentTime - lastTime) * 0.001f;
301 if (timeScale < 0.001f)
302 timeScale = 0.001f;
303 lastTime = currentTime;
304
305 //Get the current state of the keyboard and mouse
306 keyboard->capture();
307 mouse->capture();
308
309 //Always exit if ESC is pressed
310 if (keyboard->isKeyDown(KC_ESCAPE))
311 running = false;
312
313 //Reload the scene if R is pressed
314 static bool reloadedLast = false;
315 if (keyboard->isKeyDown(KC_R) && !reloadedLast){
316 unload();
317 load();
318 reloadedLast = true;
319 }
320 else {
321 reloadedLast = false;
322 }
323
324 //Get mouse movement
325 const OIS::MouseState &ms = mouse->getMouseState();
326
327 //Update camera rotation based on the mouse
328 camYaw += Radian(-ms.X.rel / 200.0f);
329 camPitch += Radian(-ms.Y.rel / 200.0f);
330 camera->setOrientation(Quaternion::IDENTITY);
331 camera->pitch(camPitch);
332 camera->yaw(camYaw);
333
334 //Allow the camera to move around with the arrow/WASD keys
335 Ogre::Vector3 trans(0, 0, 0);
336 if (keyboard->isKeyDown(KC_UP) || keyboard->isKeyDown(KC_W))
337 trans.z = -1;
338 if (keyboard->isKeyDown(KC_DOWN) || keyboard->isKeyDown(KC_S))
339 trans.z = 1;
340 if (keyboard->isKeyDown(KC_RIGHT) || keyboard->isKeyDown(KC_D))
341 trans.x = 1;
342 if (keyboard->isKeyDown(KC_LEFT) || keyboard->isKeyDown(KC_A))
343 trans.x = -1;
344 if (keyboard->isKeyDown(KC_PGUP) || keyboard->isKeyDown(KC_E))
345 trans.y = 1;
346 if (keyboard->isKeyDown(KC_PGDOWN) || keyboard->isKeyDown(KC_Q))
347 trans.y = -1;
348
349 //Shift = speed boost
350 if (keyboard->isKeyDown(KC_LSHIFT) || keyboard->isKeyDown(KC_RSHIFT))
351 trans *= 2;
352
353 trans *= 100;
354 camera->moveRelative(trans * timeScale);
355
356 //Make sure the camera doesn't go under the terrain
357 Ogre::Vector3 camPos = camera->getPosition();
358 float terrY = HeightFunction::getTerrainHeight(camPos.x, camPos.z);
359 if (camPos.y < terrY + 2 || !keyboard->isKeyDown(KC_SPACE)){ //Space = fly
360 camPos.y = terrY + 2;
361 camera->setPosition(camPos);
362 }
363 }
364