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