diff options
Diffstat (limited to 'libraries/irrlicht-1.8/examples/26.OcclusionQuery/main.cpp')
-rw-r--r-- | libraries/irrlicht-1.8/examples/26.OcclusionQuery/main.cpp | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/libraries/irrlicht-1.8/examples/26.OcclusionQuery/main.cpp b/libraries/irrlicht-1.8/examples/26.OcclusionQuery/main.cpp new file mode 100644 index 0000000..17f64b2 --- /dev/null +++ b/libraries/irrlicht-1.8/examples/26.OcclusionQuery/main.cpp | |||
@@ -0,0 +1,213 @@ | |||
1 | /** Example 026 OcclusionQuery | ||
2 | |||
3 | This Tutorial shows how to speed up rendering by use of the | ||
4 | OcclusionQuery feature. The usual rendering tries to avoid rendering of | ||
5 | scene nodes by culling those nodes which are outside the visible area, the | ||
6 | view frustum. However, this technique does not cope with occluded objects | ||
7 | which are still in the line of sight, but occluded by some larger object | ||
8 | between the object and the eye (camera). Occlusion queries check exactly that. | ||
9 | The queries basically measure the number of pixels that a previous render | ||
10 | left on the screen. | ||
11 | Since those pixels cannot be recognized at the end of a rendering anymore, | ||
12 | the pixel count is measured directly when rendering. Thus, one needs to render | ||
13 | the occluder (the object in front) first. This object needs to write to the | ||
14 | z-buffer in order to become a real occluder. Then the node is rendered and in | ||
15 | case a z-pass happens, i.e. the pixel is written to the framebuffer, the pixel | ||
16 | is counted in the query. | ||
17 | The result of a query is the number of pixels which got through. One can, based | ||
18 | on this number, judge if the scene node is visible enough to be rendered, or if | ||
19 | the node should be removed in the next round. Also note that the number of | ||
20 | pixels is a safe over approximation in general. The pixels might be overdrawn | ||
21 | later on, and the GPU tries to avoid inaccuracies which could lead to false | ||
22 | negatives in the queries. | ||
23 | |||
24 | As you might have recognized already, we had to render the node to get the | ||
25 | numbers. So where's the benefit, you might say. There are several ways where | ||
26 | occlusion queries can help. It is often a good idea to just render the bbox | ||
27 | of the node instead of the actual mesh. This is really fast and is a safe over | ||
28 | approximation. If you need a more exact render with the actual geometry, it's | ||
29 | a good idea to render with just basic solid material. Avoid complex shaders | ||
30 | and state changes through textures. There's no need while just doing the | ||
31 | occlusion query. At least if the render is not used for the actual scene. This | ||
32 | is the third way to optimize occlusion queries. Just check the queries every | ||
33 | 5th or 10th frame, or even less frequent. This depends on the movement speed | ||
34 | of the objects and camera. | ||
35 | */ | ||
36 | |||
37 | #ifdef _MSC_VER | ||
38 | // We'll also define this to stop MSVC complaining about sprintf(). | ||
39 | #define _CRT_SECURE_NO_WARNINGS | ||
40 | #pragma comment(lib, "Irrlicht.lib") | ||
41 | #endif | ||
42 | |||
43 | #include <irrlicht.h> | ||
44 | #include "driverChoice.h" | ||
45 | |||
46 | using namespace irr; | ||
47 | |||
48 | /* | ||
49 | We need keyboard input events to switch some parameters | ||
50 | */ | ||
51 | class MyEventReceiver : public IEventReceiver | ||
52 | { | ||
53 | public: | ||
54 | // This is the one method that we have to implement | ||
55 | virtual bool OnEvent(const SEvent& event) | ||
56 | { | ||
57 | // Remember whether each key is down or up | ||
58 | if (event.EventType == irr::EET_KEY_INPUT_EVENT) | ||
59 | KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown; | ||
60 | |||
61 | return false; | ||
62 | } | ||
63 | |||
64 | // This is used to check whether a key is being held down | ||
65 | virtual bool IsKeyDown(EKEY_CODE keyCode) const | ||
66 | { | ||
67 | return KeyIsDown[keyCode]; | ||
68 | } | ||
69 | |||
70 | MyEventReceiver() | ||
71 | { | ||
72 | for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i) | ||
73 | KeyIsDown[i] = false; | ||
74 | } | ||
75 | |||
76 | private: | ||
77 | // We use this array to store the current state of each key | ||
78 | bool KeyIsDown[KEY_KEY_CODES_COUNT]; | ||
79 | }; | ||
80 | |||
81 | |||
82 | /* | ||
83 | We create an irr::IrrlichtDevice and the scene nodes. One occluder, one | ||
84 | occluded. The latter is a complex sphere, which has many triangles. | ||
85 | */ | ||
86 | int main() | ||
87 | { | ||
88 | // ask user for driver | ||
89 | video::E_DRIVER_TYPE driverType=driverChoiceConsole(); | ||
90 | if (driverType==video::EDT_COUNT) | ||
91 | return 1; | ||
92 | |||
93 | // create device | ||
94 | MyEventReceiver receiver; | ||
95 | |||
96 | IrrlichtDevice* device = createDevice(driverType, | ||
97 | core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver); | ||
98 | |||
99 | if (device == 0) | ||
100 | return 1; // could not create selected driver. | ||
101 | |||
102 | video::IVideoDriver* driver = device->getVideoDriver(); | ||
103 | scene::ISceneManager* smgr = device->getSceneManager(); | ||
104 | |||
105 | smgr->getGUIEnvironment()->addStaticText(L"Press Space to hide occluder.", core::recti(10,10, 200,50)); | ||
106 | |||
107 | /* | ||
108 | Create the node to be occluded. We create a sphere node with high poly count. | ||
109 | */ | ||
110 | scene::ISceneNode * node = smgr->addSphereSceneNode(10, 64); | ||
111 | if (node) | ||
112 | { | ||
113 | node->setPosition(core::vector3df(0,0,60)); | ||
114 | node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp")); | ||
115 | node->setMaterialFlag(video::EMF_LIGHTING, false); | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | Now we create another node, the occluder. It's a simple plane. | ||
120 | */ | ||
121 | scene::ISceneNode* plane = smgr->addMeshSceneNode(smgr->addHillPlaneMesh( | ||
122 | "plane", core::dimension2df(10,10), core::dimension2du(2,2)), 0, -1, | ||
123 | core::vector3df(0,0,20), core::vector3df(270,0,0)); | ||
124 | |||
125 | if (plane) | ||
126 | { | ||
127 | plane->setMaterialTexture(0, driver->getTexture("../../media/t351sml.jpg")); | ||
128 | plane->setMaterialFlag(video::EMF_LIGHTING, false); | ||
129 | plane->setMaterialFlag(video::EMF_BACK_FACE_CULLING, true); | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | Here we create the occlusion query. Because we don't have a plain mesh scene node | ||
134 | (ESNT_MESH or ESNT_ANIMATED_MESH), we pass the base geometry as well. Instead, | ||
135 | we could also pass a simpler mesh or the bounding box. But we will use a time | ||
136 | based method, where the occlusion query renders to the frame buffer and in case | ||
137 | of success (occlusion), the mesh is not drawn for several frames. | ||
138 | */ | ||
139 | driver->addOcclusionQuery(node, ((scene::IMeshSceneNode*)node)->getMesh()); | ||
140 | |||
141 | /* | ||
142 | We have done everything, just a camera and draw it. We also write the | ||
143 | current frames per second and the name of the driver to the caption of the | ||
144 | window to examine the render speedup. | ||
145 | We also store the time for measuring the time since the last occlusion query ran | ||
146 | and store whether the node should be visible in the next frames. | ||
147 | */ | ||
148 | smgr->addCameraSceneNode(); | ||
149 | int lastFPS = -1; | ||
150 | u32 timeNow = device->getTimer()->getTime(); | ||
151 | bool nodeVisible=true; | ||
152 | |||
153 | while(device->run()) | ||
154 | { | ||
155 | plane->setVisible(!receiver.IsKeyDown(irr::KEY_SPACE)); | ||
156 | |||
157 | driver->beginScene(true, true, video::SColor(255,113,113,133)); | ||
158 | /* | ||
159 | First, we draw the scene, possibly without the occluded element. This is necessary | ||
160 | because we need the occluder to be drawn first. You can also use several scene | ||
161 | managers to collect a number of possible occluders in a separately rendered | ||
162 | scene. | ||
163 | */ | ||
164 | node->setVisible(nodeVisible); | ||
165 | smgr->drawAll(); | ||
166 | smgr->getGUIEnvironment()->drawAll(); | ||
167 | |||
168 | /* | ||
169 | Once in a while, here every 100 ms, we check the visibility. We run the queries, | ||
170 | update the pixel value, and query the result. Since we already rendered the node | ||
171 | we render the query invisible. The update is made blocking, as we need the result | ||
172 | immediately. If you don't need the result immediately, e.g. because you have other | ||
173 | things to render, you can call the update non-blocking. This gives the GPU more | ||
174 | time to pass back the results without flushing the render pipeline. | ||
175 | If the update was called non-blocking, the result from getOcclusionQueryResult is | ||
176 | either the previous value, or 0xffffffff if no value has been generated at all, yet. | ||
177 | The result is taken immediately as visibility flag for the node. | ||
178 | */ | ||
179 | if (device->getTimer()->getTime()-timeNow>100) | ||
180 | { | ||
181 | driver->runAllOcclusionQueries(false); | ||
182 | driver->updateAllOcclusionQueries(); | ||
183 | nodeVisible=driver->getOcclusionQueryResult(node)>0; | ||
184 | timeNow=device->getTimer()->getTime(); | ||
185 | } | ||
186 | |||
187 | driver->endScene(); | ||
188 | |||
189 | int fps = driver->getFPS(); | ||
190 | |||
191 | if (lastFPS != fps) | ||
192 | { | ||
193 | core::stringw tmp(L"OcclusionQuery Example ["); | ||
194 | tmp += driver->getName(); | ||
195 | tmp += L"] fps: "; | ||
196 | tmp += fps; | ||
197 | |||
198 | device->setWindowCaption(tmp.c_str()); | ||
199 | lastFPS = fps; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | In the end, delete the Irrlicht device. | ||
205 | */ | ||
206 | device->drop(); | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | That's it. Compile and play around with the program. | ||
213 | **/ | ||