~baltix/+junk/irrlicht-test

« back to all changes in this revision

Viewing changes to examples/26.OcclusionQuery/main.cpp

  • Committer: Mantas Kriaučiūnas
  • Date: 2011-07-18 13:06:25 UTC
  • Revision ID: mantas@akl.lt-20110718130625-c5pvifp61e7kj1ol
Included whole irrlicht SVN libraries to work around launchpad recipe issue with quilt, see https://answers.launchpad.net/launchpad/+question/165193

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 frane, 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 oyu 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
**/