Hardware Occlusion Culling - Occlusion Query for Irrlicht

Announce new projects or updates of Irrlicht Engine related tools, games, and applications.
Also check the Wiki
Nadro
Posts: 1648
Joined: Sun Feb 19, 2006 9:08 am
Location: Warsaw, Poland

Hardware Occlusion Culling - Occlusion Query for Irrlicht

Post by Nadro »

Hi, This is occlusion query example for Irrlicht (support Direct3D9 and OpenGL modes). For query test I use normal Hi-Poly meshes, so FPS results isn't so good, but in next release I will use Bounding meshes for Occlusion Query Pass and FPS will be better. This culling is very useful for large levels.

First pre alpha demo for D3D9 / OpenGL from (only binaries):
http://rapidshare.com/files/136728949/O ... nQuery.zip

Pre builded Irrlicht (rev 1498) with Occlusion Query support and example demo of usage it with source code:
http://rapidshare.com/files/137400230/O ... ev1498.zip

Patches for update Irrlicht (rev 1498) to support Occlusion Query:
http://nadro.net46.net/Patches/Occlusio ... 1498.patch
http://nadro.net46.net/Patches/Occlusio ... 1498.patch
Last edited by Nadro on Fri Aug 15, 2008 12:51 pm, edited 2 times in total.
Library helping with network requests, tasks management, logger etc in desktop and mobile apps: https://github.com/GrupaPracuj/hermes
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

There's quite a delay before it seems the cull is done on the little elephant... or is it just updating the stats every second instead of every frame?

By using bounding meshes do you mean just using a bounding box? And is that the bounding box of the mesh doing the blocking? That wouldn't work very well obviously due to the elephant (and many other) mesh not being an actual box. Or do you mean you'd do bounding box tests first and then the high poly tests?

Maybe you could provide a lower poly version of the elephant for doing the tests on, but that would have to fit completely within the high poly elephant for it to not give inaccurate results.....
Image Image Image
Nadro
Posts: 1648
Joined: Sun Feb 19, 2006 9:08 am
Location: Warsaw, Poland

Post by Nadro »

Stats are update only when FPS are change, so this delay is not true, all is ok for it. Bounding meshes is very low poly mesh of object (all elements of it can't be higher than orginal elements of Hi Poly mesh)
Library helping with network requests, tasks management, logger etc in desktop and mobile apps: https://github.com/GrupaPracuj/hermes
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Sounds good then! Hopefully this can improve performance for a lot of people!
Image Image Image
Nadro
Posts: 1648
Joined: Sun Feb 19, 2006 9:08 am
Location: Warsaw, Poland

Post by Nadro »

Hi, I made first version of Irrlicht interface for functions usage in Hardware Occlusion Query (support Direct3D9 and OpenGL):

Code: Select all

//! Create query. Get node for identification query and bounding mesh for occlusion test.
void createQuery( scene::ISceneNode* vNode, scene::IMesh* vMesh );

//! Remove query.
void removeQuery( scene::ISceneNode* vNode );

//! Remove all queries.
void removeAllQueries();

//! Run query. Draw a mesh stored in query.
void runQuery( scene::ISceneNode* vNode );
//! Run all queries. Draw all meshes stored in all queries.
void runAllQueries();

//! Return query result.
int getQueryResult( scene::ISceneNode* vNode );

And small and easy, usefull function for clean buffers:

Code: Select all

//! Clears BackBuffer and/or ZBuffer.
void clearBuffers(bool backBuffer, bool zBuffer, SColor color);
I created *.patch files for update Irrlicht to support Hardware Occlusion Query (tested with the latest rev 1498), and I release package with precompiled Irrlicht r1498 with Occlusion Query support and example demo with source. I add links for it in first post.

Source code of example demo:

Code: Select all

#include <irrlicht.h>
#include <iostream>

using namespace irr;

using namespace core;
using namespace scene;
using namespace video;

int main()
{
    E_DRIVER_TYPE driverType;

	printf("Please select the driver you want for this example:\n"\
		" (a) Direct3D 9.0c\n (b) OpenGL 1.5\n (otherKey) exit\n\n");

    char i;
	std::cin >> i;

	switch(i)
	{
		case 'a': driverType = EDT_DIRECT3D9;break;
		case 'b': driverType = EDT_OPENGL;break;
		default: return 1;
	}

    IrrlichtDevice* device = createDevice( driverType, dimension2d<s32>(800, 600), 32,false, false, false, 0);

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();

	IMesh* mesh = smgr->getMesh("elephant.obj")->getMesh(0);
	IMeshSceneNode* node1 = smgr->addMeshSceneNode( mesh );
	IMeshSceneNode* node2 = smgr->addMeshSceneNode( mesh );

	if (node1)
	{
		node1->setMaterialFlag(EMF_LIGHTING, false);
		node1->setMaterialTexture( 0, driver->getTexture("red.jpg") );

		node1->setPosition(vector3df(0,0,0));
        node1->setScale(vector3df(1,1,1));
        node1->setOcclusionCulling( true ); // Activate occlusion query test for node1, If You need manualy hide node eg. for LOD, You have to disable it, because OC automatic set node1->setVisible(false/true); You can disable/enable OC in each momment.
        driver->createQuery(node1, mesh); // Create query for node1; in query test we will use standard hi poly mesh, because I don't have low poly bounding mesh for it.

	}

	if (node2)
	{
		node2->setMaterialFlag(EMF_LIGHTING, false);
		node2->setMaterialTexture( 0, driver->getTexture("blue.jpg") );

		node2->setPosition(vector3df(0,0,40));
        node2->setScale(vector3df(0.1,0.1,0.1));
        node2->setOcclusionCulling( true );
        driver->createQuery(node2, mesh);
	}

	ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS();
	cam->setPosition(vector3df(100,0,60));
	cam->setTarget(vector3df(3,0,37));

	int lastFPS = -1;
    SColor Color = SColor(255,100,101,140);

	while(device->run())
	{
		driver->beginScene(true, true, Color);

		// *** Occlusion Query Pass *** //

		// At first we have to render transformed camera
		cam->OnRegisterSceneNode();
        cam->OnAnimate(device->getTimer()->getTime());
        cam->render();

		driver->runAllQueries(); // Run all occlusion queries test, if node has got 'setOcclusionCulling(0)' is skipping in this test, if object is occluded: node1->setVisible(0); if not: node1->setVisible(1);

		// driver->runQuery(node1); // this is alternative run query method for node1
        // driver->runQuery(node2);

		driver->clearBuffers(true,true,Color); // We can clear BackBuffer and ZBuffer after OC Pass

		// *** End of Occlusion Query Pass *** //

		if(lastFPS == -1)
		cam->setTarget(vector3df(3,0,37)); // Set camera target in first frame

		smgr->drawAll();

		driver->endScene();

		int tris = driver->getPrimitiveCountDrawn();
		int fps = driver->getFPS();

		if (lastFPS != fps)
		{
			core::stringw str = L"Occlusion Query Demo FPS: ";
			str += fps;
			str += " Tris: ";
			str += tris;
			str += " Node 1 fragments: ";
			str += driver->getQueryResult(node1); // get visible fragments count for node
			str += " Node 2 fragments: ";
			str += driver->getQueryResult(node2);

			device->setWindowCaption(str.c_str());
			lastFPS = fps;
		}
	}
	driver->removeAllQueries(); // Remove All Queries
	// driver->removeQuery(node1); // this is alternative remove query method for node1
	// driver->removeQuery(node2);

	device->drop();

	return 0;
}
I'm waiting for Your comments and suggestions.
Library helping with network requests, tasks management, logger etc in desktop and mobile apps: https://github.com/GrupaPracuj/hermes
wyrmmage
Posts: 204
Joined: Sun Mar 16, 2008 3:12 am
Contact:

Post by wyrmmage »

that was a massive quintuple post! :D

Looks cool...I'll definitely be bookmarking this :)
-wyrmmage
Worlds at War (Current Project) - http://www.awkward-games.com
Ganadu'r, The Eternal Sage (Other Current Project) - http://rpg.naget.com
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

lmao... everyone's being fooled by the debug messages
Image Image Image
Nadro
Posts: 1648
Joined: Sun Feb 19, 2006 9:08 am
Location: Warsaw, Poland

Post by Nadro »

Ehh, when I submit my post I see error message from phpBB, so I don't update first post because I think than my post isn't submited, but now I see than this error is joke:D OK, links are ready in first post.

BTW. I forgot add in this release EAC_BOX frustum culling test in query test, but I add it in the next release.
Library helping with network requests, tasks management, logger etc in desktop and mobile apps: https://github.com/GrupaPracuj/hermes
full.metal.coder
Posts: 68
Joined: Sat May 10, 2008 11:30 am
Contact:

Post by full.metal.coder »

Link broken, please fix it (preferably with a more up to date version of your code).

I might be able to go beyond 500fps on The First King test level with proper occlusion culling... :D
Nadro
Posts: 1648
Joined: Sun Feb 19, 2006 9:08 am
Location: Warsaw, Poland

Post by Nadro »

Hi full.metal.coder,

Yes, server has got problems... and links are broken. Yes, I use in my engine this oc code with bounding meshes and I have got big speed up of my project. You have to manualy add to this source EAC_BOX frustum culling test eg. from CSceneManager.cpp (and put it in query test), because I forgot about this usefull stuff in this version.

I don't have access to stable server so I put code here:

OcclusionQuery_Include_Rev1498.patch

Code: Select all

Index: ISceneNode.h
===================================================================
--- ISceneNode.h	(revision 1498)
+++ ISceneNode.h	(working copy)
@@ -44,7 +44,8 @@
 			: RelativeTranslation(position), RelativeRotation(rotation), RelativeScale(scale),
 				Parent(0), ID(id), SceneManager(mgr), TriangleSelector(0),
 				AutomaticCullingState(EAC_BOX), IsVisible(true),
-				DebugDataVisible(EDS_OFF), IsDebugObject(false)
+				DebugDataVisible(EDS_OFF), IsDebugObject(false),
+				QueryPosition(-1), IsOcclusionCulling(0)
 		{
 			if (parent)
 				parent->addChild(this);
@@ -663,6 +664,31 @@
 			return 0; // to be implemented by derived classes
 		}
 
+		//! Set position on Qcclusion Query list.
+		void setQueryPosition( int vPosition )
+		{
+			if(vPosition >= 0)
+			QueryPosition = vPosition;
+		}
+
+		//! Get position on Qcclusion Query list.
+		int getQueryPosition()
+		{
+			return QueryPosition;
+		}
+
+		//! Set occlusion status.
+		void setOcclusionCulling( bool Value )
+		{
+			IsOcclusionCulling = Value;
+		}
+
+		//! Get occlusion status.
+		bool getOcclusionCulling()
+		{
+			return IsOcclusionCulling;
+		}
+
 	protected:
 
 		//! A clone function for the ISceneNode members.
@@ -753,6 +779,12 @@
 
 		//! Is debug object?
 		bool IsDebugObject;
+
+		//! Position on Qcclusion Query list.
+		int QueryPosition;
+
+		//! Is occluded?
+		bool IsOcclusionCulling;
 	};
 
 } // end namespace scene
@@ -760,3 +792,4 @@
 
 #endif
 
+
Index: IVideoDriver.h
===================================================================
--- IVideoDriver.h	(revision 1498)
+++ IVideoDriver.h	(working copy)
@@ -25,11 +25,14 @@
 {
 	class IAttributes;
 	class IReadFile;
+	class ISceneNode;
 } // end namespace io
 namespace scene
 {
 	class IMeshBuffer;
 	class IMeshManipulator;
+	class IMesh;
+	class ISceneNode;
 } // end namespace scene
 
 namespace video
@@ -917,6 +920,9 @@
 		*/
 		virtual void clearZBuffer() = 0;
 
+		//! Clears BackBuffer and/or ZBuffer.
+		virtual void clearBuffers(bool backBuffer, bool zBuffer, SColor color) = 0;
+
 		//! Make a screenshot of the last rendered frame.
 		/** \return An image created from the last rendered frame. */
 		virtual IImage* createScreenShot() = 0;
@@ -957,6 +963,24 @@
 		//! Returns the graphics card vendor name.
 		virtual core::stringc getVendorInfo() = 0;
 
+		//! Create query. Get node for identification query and bounding mesh for occlusion test.
+		virtual void createQuery( scene::ISceneNode* vNode, scene::IMesh* vMesh ) = 0;
+
+		//! Remove query.
+		virtual void removeQuery( scene::ISceneNode* vNode ) = 0;
+
+		//! Remove all queries.
+		virtual void removeAllQueries() = 0;
+
+		//! Run query. Draw a mesh stored in query.
+		virtual void runQuery( scene::ISceneNode* vNode ) = 0;
+
+		//! Run all queries. Draw all meshes stored in all queries.
+		virtual void runAllQueries() = 0;
+
+		//! Return query result.
+		virtual int getQueryResult( scene::ISceneNode* vNode ) = 0;
+
 	};
 
 } // end namespace video
OcclusionQuery_Source_Rev1498.patch

Code: Select all

Index: CD3D9Driver.cpp
===================================================================
--- CD3D9Driver.cpp	(revision 1498)
+++ CD3D9Driver.cpp	(working copy)
@@ -2410,6 +2410,26 @@
 }
 
 
+//! Clears BackBuffer and/or ZBuffer.
+void CD3D9Driver::clearBuffers(bool backBuffer, bool zBuffer, SColor color)
+{
+	if (backBuffer || zBuffer)
+	{
+		DWORD flags = 0;
+
+		if (backBuffer)
+			flags |= D3DCLEAR_TARGET;
+
+		if (zBuffer)
+			flags |= D3DCLEAR_ZBUFFER;
+
+		HRESULT hr = pID3DDevice->Clear( 0, NULL, flags, color.color, 1.0, 0);
+		if (FAILED(hr))
+			os::Printer::log("CD3D9Driver clearBuffers() failed.", ELL_WARNING);
+	}
+}
+
+
 //! Returns an image created from the last rendered frame.
 IImage* CD3D9Driver::createScreenShot()
 {
@@ -2519,6 +2539,147 @@
 }
 
 
+//! Create query.
+void CD3D9Driver::createQuery( scene::ISceneNode* vNode, scene::IMesh* vMesh )
+{
+	if(!vNode || !vMesh)
+	return;
+
+	if(vNode->getQueryPosition() != -1)
+	return;
+
+	int Position = Query.size();
+	vNode->setQueryPosition(Position);
+
+	Query.push_back(0);
+	QueryResult.push_back(0);
+	QueryNode.push_back(vNode);
+	QueryMesh.push_back(vMesh);
+
+	pID3DDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &Query[Position] );
+}
+
+//! Remove query.
+void CD3D9Driver::removeQuery( scene::ISceneNode* vNode )
+{
+	if(!vNode)
+	return;
+
+	int Position = vNode->getQueryPosition();
+
+	if(Position == -1)
+	return;
+
+	for(int current = Position + 1; current < Query.size(); current++)
+	{
+		QueryNode[current]->setQueryPosition(QueryNode[current]->getQueryPosition() - 1);
+	}
+
+	if( Query[Position] != NULL )
+	Query[Position]->Release();
+
+	Query.erase(Position);
+	QueryResult.erase(Position);
+	QueryNode.erase(Position);
+	QueryMesh.erase(Position);
+}
+
+//! Remove all queries.
+void CD3D9Driver::removeAllQueries()
+{
+	for(int current = 0; current < Query.size(); current++)
+	{
+		if( Query[current] != NULL )
+		Query[current]->Release();
+	}
+
+	Query.clear();
+	QueryResult.clear();
+	QueryNode.clear();
+	QueryMesh.clear();
+}
+
+//! Run query.
+void CD3D9Driver::runQuery( scene::ISceneNode* vNode )
+{
+	if(!vNode)
+	return;
+
+	int Position = vNode->getQueryPosition();
+
+	if(Position == -1)
+	return;
+
+	if(QueryNode[Position]->getOcclusionCulling() && QueryMesh[Position])
+	{
+		setTransform(video::ETS_WORLD, QueryNode[Position]->getRelativeTransformation());
+		Query[Position]->Issue( D3DISSUE_BEGIN );
+		{
+			for(int current = 0; current < QueryMesh[Position]->getMeshBufferCount(); current++)
+			drawMeshBuffer(QueryMesh[Position]->getMeshBuffer(current));
+		}
+		Query[Position]->Issue( D3DISSUE_END );
+
+		while( Query[Position]->GetData( (void*)&QueryResult[Position], sizeof(DWORD), D3DGETDATA_FLUSH) == S_FALSE )
+		{
+		}
+
+		if(QueryResult[Position] < 1)
+		{
+			QueryNode[Position]->setVisible(false);
+		}
+		else
+		{
+			QueryNode[Position]->setVisible(true);
+		}
+	}
+}
+
+//! Run all queries.
+void CD3D9Driver::runAllQueries()
+{
+	for(int current = 0; current < Query.size(); current++)
+	{
+		if(QueryNode[current])
+		{
+			if(QueryNode[current]->getOcclusionCulling() && QueryMesh[current])
+			{
+				setTransform(video::ETS_WORLD, QueryNode[current]->getRelativeTransformation());
+				Query[current]->Issue( D3DISSUE_BEGIN );
+				{
+					for(int scurrent = 0; scurrent < QueryMesh[current]->getMeshBufferCount(); scurrent++)
+					drawMeshBuffer(QueryMesh[current]->getMeshBuffer(scurrent));
+				}
+				Query[current]->Issue( D3DISSUE_END );
+
+				while( Query[current]->GetData( (void*)&QueryResult[current], sizeof(DWORD), D3DGETDATA_FLUSH) == S_FALSE )
+				{
+				}
+
+				if(QueryResult[current] < 1)
+				{
+					QueryNode[current]->setVisible(false);
+				}
+				else
+				{
+					QueryNode[current]->setVisible(true);
+				}
+			}
+		}
+	}
+}
+
+//! Return query result.
+int CD3D9Driver::getQueryResult( scene::ISceneNode* vNode )
+{
+	if(!vNode)
+	return -1;
+
+	int Position = vNode->getQueryPosition();
+	return QueryResult[Position];
+}
+
+
 } // end namespace video
 } // end namespace irr
 
Index: CD3D9Driver.h
===================================================================
--- CD3D9Driver.h	(revision 1498)
+++ CD3D9Driver.h	(working copy)
@@ -202,6 +202,9 @@
 		//! Clears the ZBuffer.
 		virtual void clearZBuffer();
 
+		//! Clears BackBuffer and/or ZBuffer.
+		virtual void clearBuffers(bool backBuffer, bool zBuffer, SColor color);
+
 		//! Returns an image created from the last rendered frame.
 		virtual IImage* createScreenShot();
 
@@ -221,6 +224,25 @@
 		//! Returns the graphics card vendor name.
 		virtual core::stringc getVendorInfo() {return vendorName;};
 
+		//! Create query.
+		virtual void createQuery( scene::ISceneNode* vNode, scene::IMesh* vMesh );
+
+		//! Remove query.
+		virtual void removeQuery( scene::ISceneNode* vNode );
+
+		//! Remove all queries.
+		virtual void removeAllQueries();
+
+		//! Run query.
+		virtual void runQuery( scene::ISceneNode* vNode );
+
+		//! Run all queries.
+		virtual void runAllQueries();
+
+		//! Return query result.
+		virtual int getQueryResult( scene::ISceneNode* vNode );
+
+
 	private:
 
 		// enumeration for rendering modes such as 2d and 3d for minizing the switching of renderStates.
@@ -326,6 +348,11 @@
 		SColorf AmbientLight;
 
 		core::stringc vendorName;
+
+		core::array<IDirect3DQuery9*> Query;
+		core::array<int> QueryResult;
+		core::array<scene::ISceneNode*> QueryNode;
+		core::array<scene::IMesh*> QueryMesh;
 	};
 
 
Index: CNullDriver.cpp
===================================================================
--- CNullDriver.cpp	(revision 1498)
+++ CNullDriver.cpp	(working copy)
@@ -1813,6 +1813,11 @@
 }
 
 
+void CNullDriver::clearBuffers(bool backBuffer, bool zBuffer, SColor color)
+{
+}
+
+
 //! Returns a pointer to the mesh manipulator.
 scene::IMeshManipulator* CNullDriver::getMeshManipulator()
 {
@@ -1871,7 +1876,38 @@
 	// not necessary
 }
 
+//! Create query.
+void CNullDriver::createQuery( scene::ISceneNode* vNode, scene::IMesh* vMesh )
+{
+}
 
+//! Remove query.
+void CNullDriver::removeQuery( scene::ISceneNode* vNode )
+{
+}
+
+//! Remove all queries.
+void CNullDriver::removeAllQueries()
+{
+}
+
+//! Run query.
+void CNullDriver::runQuery( scene::ISceneNode* vNode )
+{
+}
+
+//! Run all queries.
+void CNullDriver::runAllQueries()
+{
+}
+
+//! Return query result.
+int CNullDriver::getQueryResult( scene::ISceneNode* vNode )
+{
+	return -1;
+}
+
+
 } // end namespace
 } // end namespace
 
Index: CNullDriver.h
===================================================================
--- CNullDriver.h	(revision 1498)
+++ CNullDriver.h	(working copy)
@@ -14,6 +14,8 @@
 #include "irrMap.h"
 #include "IAttributes.h"
 #include "IMeshBuffer.h"
+#include "IMesh.h"
+#include "ISceneNode.h"
 #include "CFPSCounter.h"
 #include "S3DVertex.h"
 #include "SLight.h"
@@ -456,6 +458,9 @@
 		//! Clears the ZBuffer.
 		virtual void clearZBuffer();
 
+		//! Clears BackBuffer and/or ZBuffer.
+		virtual void clearBuffers(bool backBuffer, bool zBuffer, SColor color);
+
 		//! Returns an image created from the last rendered frame.
 		virtual IImage* createScreenShot();
 
@@ -490,6 +495,24 @@
 		//! Returns the graphics card vendor name.
 		virtual core::stringc getVendorInfo() {return "Not available on this driver.";};
 
+		//! Create query.
+		virtual void createQuery( scene::ISceneNode* vNode, scene::IMesh* vMesh );
+
+		//! Remove query.
+		virtual void removeQuery( scene::ISceneNode* vNode );
+
+		//! Remove all queries.
+		virtual void removeAllQueries();
+
+		//! Run query.
+		virtual void runQuery( scene::ISceneNode* vNode );
+
+		//! Run all queries.
+		virtual void runAllQueries();
+
+		//! Return query result.
+		virtual int getQueryResult( scene::ISceneNode* vNode );
+
 	protected:
 
 		//! deletes all textures
Index: COpenGLDriver.cpp
===================================================================
--- COpenGLDriver.cpp	(revision 1498)
+++ COpenGLDriver.cpp	(working copy)
@@ -2603,6 +2603,33 @@
 }
 
 
+//! Clears BackBuffer and/or ZBuffer.
+void COpenGLDriver::clearBuffers(bool backBuffer, bool zBuffer, SColor color)
+{
+	if (backBuffer || zBuffer)
+	{
+		GLbitfield mask = 0;
+
+		if (backBuffer)
+		{
+			const f32 inv = 1.0f / 255.0f;
+			glClearColor(color.getRed() * inv, color.getGreen() * inv,
+					color.getBlue() * inv, color.getAlpha() * inv);
+
+			mask |= GL_COLOR_BUFFER_BIT;
+		}
+
+		if (zBuffer)
+		{
+			glDepthMask(GL_TRUE);
+			mask |= GL_DEPTH_BUFFER_BIT;
+		}
+
+		glClear(mask);
+	}
+}
+
+
 //! Returns an image created from the last rendered frame.
 IImage* COpenGLDriver::createScreenShot()
 {
@@ -2707,6 +2734,141 @@
 }
 
 
+//! Create query.
+void COpenGLDriver::createQuery( scene::ISceneNode* vNode, scene::IMesh* vMesh )
+{
+	if(!vNode || !vMesh)
+	return;
+
+	if(vNode->getQueryPosition() != -1)
+	return;
+
+	int Position = Query.size();
+	vNode->setQueryPosition(Position);
+
+	Query.push_back(0);
+	QueryResult.push_back(0);
+	QueryNode.push_back(vNode);
+	QueryMesh.push_back(vMesh);
+
+	extGlGenQueries( 1, &Query[Position] );
+}
+
+//! Remove query.
+void COpenGLDriver::removeQuery( scene::ISceneNode* vNode )
+{
+	if(!vNode)
+	return;
+
+	int Position = vNode->getQueryPosition();
+
+	if(Position == -1)
+	return;
+
+	for(int current = Position + 1; current < Query.size(); current++)
+	{
+		QueryNode[current]->setQueryPosition(QueryNode[current]->getQueryPosition() - 1);
+	}
+
+	extGlDeleteQueries( 1, &Query[Position] );
+
+	Query.erase(Position);
+	QueryResult.erase(Position);
+	QueryNode.erase(Position);
+	QueryMesh.erase(Position);
+}
+
+//! Remove all queries.
+void COpenGLDriver::removeAllQueries()
+{
+	for(int current = 0; current < Query.size(); current++)
+	{
+		extGlDeleteQueries( 1, &Query[current] );
+	}
+
+	Query.clear();
+	QueryResult.clear();
+	QueryNode.clear();
+	QueryMesh.clear();
+}
+
+//! Run query.
+void COpenGLDriver::runQuery( scene::ISceneNode* vNode )
+{
+	if(!vNode)
+	return;
+
+	int Position = vNode->getQueryPosition();
+
+	if(Position == -1)
+	return;
+
+	if(QueryNode[Position]->getOcclusionCulling() && QueryMesh[Position])
+	{
+		setTransform(video::ETS_WORLD, QueryNode[Position]->getRelativeTransformation());
+		extGlBeginQuery( GL_SAMPLES_PASSED_ARB, Query[Position] );
+		{
+			for(int current = 0; current < QueryMesh[Position]->getMeshBufferCount(); current++)
+			drawMeshBuffer(QueryMesh[Position]->getMeshBuffer(current));
+		}
+		extGlEndQuery( GL_SAMPLES_PASSED_ARB );
+
+		extGlGetQueryObjectuiv( Query[Position], GL_QUERY_RESULT_ARB, (GLuint*)&QueryResult[Position] );
+
+		if(QueryResult[Position] < 1)
+		{
+			QueryNode[Position]->setVisible(false);
+		}
+		else
+		{
+			QueryNode[Position]->setVisible(true);
+		}
+	}
+}
+
+//! Run all queries.
+void COpenGLDriver::runAllQueries()
+{
+	for(int current = 0; current < Query.size(); current++)
+	{
+		if(QueryNode[current])
+		{
+			if(QueryNode[current]->getOcclusionCulling() && QueryMesh[current])
+			{
+				setTransform(video::ETS_WORLD, QueryNode[current]->getRelativeTransformation());
+				extGlBeginQuery( GL_SAMPLES_PASSED_ARB, Query[current] );
+				{
+					for(int scurrent = 0; scurrent < QueryMesh[current]->getMeshBufferCount(); scurrent++)
+					drawMeshBuffer(QueryMesh[current]->getMeshBuffer(scurrent));
+				}
+				extGlEndQuery( GL_SAMPLES_PASSED_ARB );
+
+				extGlGetQueryObjectuiv( Query[current], GL_QUERY_RESULT_ARB, (GLuint*)&QueryResult[current] );
+
+				if(QueryResult[current] < 1)
+				{
+					QueryNode[current]->setVisible(false);
+				}
+				else
+				{
+					QueryNode[current]->setVisible(true);
+				}
+			}
+		}
+	}
+}
+
+//! Return query result.
+int COpenGLDriver::getQueryResult( scene::ISceneNode* vNode )
+{
+	if(!vNode)
+	return -1;
+
+	int Position = vNode->getQueryPosition();
+	return QueryResult[Position];
+}
+
+
 } // end namespace
 } // end namespace
 
Index: COpenGLDriver.h
===================================================================
--- COpenGLDriver.h	(revision 1498)
+++ COpenGLDriver.h	(working copy)
@@ -307,6 +307,9 @@
 		//! Clears the ZBuffer.
 		virtual void clearZBuffer();
 
+		//! Clears BackBuffer and/or ZBuffer.
+		virtual void clearBuffers(bool backBuffer, bool zBuffer, SColor color);
+
 		//! Returns an image created from the last rendered frame.
 		virtual IImage* createScreenShot();
 
@@ -330,6 +333,24 @@
 		//! Returns the graphics card vendor name.
 		virtual core::stringc getVendorInfo() {return vendorName;};
 
+		//! Create query.
+		virtual void createQuery( scene::ISceneNode* vNode, scene::IMesh* vMesh );
+
+		//! Remove query.
+		virtual void removeQuery( scene::ISceneNode* vNode );
+
+		//! Remove all queries.
+		virtual void removeAllQueries();
+
+		//! Run query.
+		virtual void runQuery( scene::ISceneNode* vNode );
+
+		//! Run all queries.
+		virtual void runAllQueries();
+
+		//! Return query result.
+		virtual int getQueryResult( scene::ISceneNode* vNode );
+
 	private:
 
 		void uploadClipPlane(u32 index);
@@ -392,6 +413,11 @@
 		//! Color buffer format
 		ECOLOR_FORMAT ColorFormat;
 
+		core::array<GLuint> Query;
+		core::array<int> QueryResult;
+		core::array<scene::ISceneNode*> QueryNode;
+		core::array<scene::IMesh*> QueryMesh;
+
 		#ifdef _IRR_WINDOWS_API_
 			HDC HDc; // Private GDI Device Context
 			HWND Window;
Index: COpenGLExtensionHandler.cpp
===================================================================
--- COpenGLExtensionHandler.cpp	(revision 1498)
+++ COpenGLExtensionHandler.cpp	(working copy)
@@ -47,7 +47,9 @@
 	pGlRenderbufferStorageEXT(0), pGlFramebufferRenderbufferEXT(0),
 	pGlGenBuffersARB(0), pGlBindBufferARB(0), pGlBufferDataARB(0), pGlDeleteBuffersARB(0),
 	pGlBufferSubDataARB(0), pGlGetBufferSubDataARB(0), pGlMapBufferARB(0), pGlUnmapBufferARB(0),
-	pGlIsBufferARB(0), pGlGetBufferParameterivARB(0), pGlGetBufferPointervARB(0)
+	pGlIsBufferARB(0), pGlGetBufferParameterivARB(0), pGlGetBufferPointervARB(0),
+	pGlGenQueriesARB(0), pGlDeleteQueriesARB(0), pGlIsQueryARB(0), pGlBeginQueryARB(0), pGlEndQueryARB(0),
+	pGlGetQueryivARB(0), pGlGetQueryObjectivARB(0), pGlGetQueryObjectuivARB(0)
 
 
 #endif // _IRR_OPENGL_USE_EXTPOINTER_
@@ -177,6 +179,15 @@
 	pGlGetBufferParameterivARB= (PFNGLGETBUFFERPARAMETERIVARBPROC) wglGetProcAddress("glGetBufferParameterivARB");
 	pGlGetBufferPointervARB= (PFNGLGETBUFFERPOINTERVARBPROC) wglGetProcAddress("glGetBufferPointervARB");
 
+	// occlusion query
+	pGlGenQueriesARB = (PFNGLGENQUERIESARBPROC) wglGetProcAddress("glGenQueriesARB");
+	pGlDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) wglGetProcAddress("glDeleteQueriesARB");
+	pGlIsQueryARB = (PFNGLISQUERYARBPROC) wglGetProcAddress("glIsQueryARB");
+	pGlBeginQueryARB = (PFNGLBEGINQUERYARBPROC) wglGetProcAddress("glBeginQueryARB");
+	pGlEndQueryARB = (PFNGLENDQUERYARBPROC) wglGetProcAddress("glEndQueryARB");
+	pGlGetQueryivARB = (PFNGLGETQUERYIVARBPROC) wglGetProcAddress("glGetQueryivARB");
+	pGlGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) wglGetProcAddress("glGetQueryObjectivARB");
+	pGlGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) wglGetProcAddress("glGetQueryObjectuivARB");
 
 	// vsync extension
 	wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC) wglGetProcAddress("wglSwapIntervalEXT");
@@ -384,8 +395,32 @@
 
 	pGlGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC)
 	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetBufferPointervARB"));
+ 
+	pGlGenQueriesARB = (PFNGLGENQUERIESARBPROC)
+	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGenQueriesARB"));
+ 
+	pGlDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC)
+	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glDeleteQueriesARB"));
 
+	pGlIsQueryARB = (PFNGLISQUERYARBPROC)
+	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glIsQueryARB"));
 
+	pGlBeginQueryARB = (PFNGLBEGINQUERYARBPROC)
+	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glBeginQueryARB"));
+
+	pGlEndQueryARB = (PFNGLENDQUERYARBPROC)
+	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glEndQueryARB"));
+
+	pGlGetQueryivARB = (PFNGLGETQUERYIVARBPROC)
+	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetQueryivARB"));
+
+	pGlGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC)
+	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetQueryObjectivARB"));
+
+	pGlGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC)
+	IRR_OGL_LOAD_EXTENSION(reinterpret_cast<const GLubyte*>("glGetQueryObjectuivARB"));
+
+
 	#endif // _IRR_OPENGL_USE_EXTPOINTER_
 #endif // _IRR_WINDOWS_API_
 
Index: COpenGLExtensionHandler.h
===================================================================
--- COpenGLExtensionHandler.h	(revision 1498)
+++ COpenGLExtensionHandler.h	(working copy)
@@ -763,7 +763,17 @@
 	void extGlGetBufferParameteriv (GLenum target, GLenum pname, GLint *params);
 	void extGlGetBufferPointerv (GLenum target, GLenum pname, GLvoid **params);
 
+	// occlusion query
+	void extGlGenQueries (GLsizei n, GLuint *ids);
+	void extGlDeleteQueries (GLsizei n, const GLuint *ids);
+	GLboolean extGlIsQuery (GLuint id);
+	void extGlBeginQuery (GLenum target, GLuint id);
+	void extGlEndQuery (GLenum target);
+	void extGlGetQueryiv (GLenum target, GLenum pname, GLint *params);
+	void extGlGetQueryObjectiv (GLuint id, GLenum pname, GLint *params);
+	void extGlGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params);
 
+
 	protected:
 	// the global feature array
 	bool FeatureAvailable[IRR_OpenGL_Feature_Count];
@@ -831,6 +841,14 @@
 		PFNGLISBUFFERARBPROC pGlIsBufferARB;
 		PFNGLGETBUFFERPARAMETERIVARBPROC pGlGetBufferParameterivARB;
 		PFNGLGETBUFFERPOINTERVARBPROC pGlGetBufferPointervARB;
+		PFNGLGENQUERIESARBPROC pGlGenQueriesARB;
+		PFNGLDELETEQUERIESARBPROC pGlDeleteQueriesARB;
+		PFNGLISQUERYARBPROC pGlIsQueryARB;
+		PFNGLBEGINQUERYARBPROC pGlBeginQueryARB;
+		PFNGLENDQUERYARBPROC pGlEndQueryARB;
+		PFNGLGETQUERYIVARBPROC pGlGetQueryivARB;
+		PFNGLGETQUERYOBJECTIVARBPROC pGlGetQueryObjectivARB;
+		PFNGLGETQUERYOBJECTUIVARBPROC pGlGetQueryObjectuivARB;
 
 
 
@@ -1505,11 +1523,107 @@
 #endif
 }
 
+inline void COpenGLExtensionHandler::extGlGenQueries (GLsizei n, GLuint *ids)
+{
+#ifdef _IRR_OPENGL_USE_EXTPOINTER_
+	if (pGlGenQueriesARB)
+		pGlGenQueriesARB(n, ids);
+#elif defined(GL_ARB_occlusion_query)
+	glGenQueriesARB(n, ids);
+#else
+	os::Printer::log("glGenQueriesARB not supported", ELL_ERROR);
+#endif
+}
 
+inline void COpenGLExtensionHandler::extGlDeleteQueries (GLsizei n, const GLuint *ids)
+{
+#ifdef _IRR_OPENGL_USE_EXTPOINTER_
+	if (pGlDeleteQueriesARB)
+		pGlDeleteQueriesARB(n, ids);
+#elif defined(GL_ARB_occlusion_query)
+	glDeleteQueriesARB(n, ids);
+#else
+	os::Printer::log("glDeleteQueriesARB not supported", ELL_ERROR);
+#endif
 }
+
+inline GLboolean COpenGLExtensionHandler::extGlIsQuery (GLuint id)
+{
+#ifdef _IRR_OPENGL_USE_EXTPOINTER_
+	if (pGlIsQueryARB)
+		pGlIsQueryARB(id);
+#elif defined(GL_ARB_occlusion_query)
+	glIsQueryARB(id);
+#else
+	os::Printer::log("glIsQueryARB not supported", ELL_ERROR);
+#endif
 }
 
+inline void COpenGLExtensionHandler::extGlBeginQuery (GLenum target, GLuint id)
+{
+#ifdef _IRR_OPENGL_USE_EXTPOINTER_
+	if (pGlBeginQueryARB)
+		pGlBeginQueryARB(target, id);
+#elif defined(GL_ARB_occlusion_query)
+	glBeginQueryARB(target, id);
+#else
+	os::Printer::log("glBeginQueryARB not supported", ELL_ERROR);
 #endif
+}
 
+inline void COpenGLExtensionHandler::extGlEndQuery (GLenum target)
+{
+#ifdef _IRR_OPENGL_USE_EXTPOINTER_
+	if (pGlEndQueryARB)
+		pGlEndQueryARB(target);
+#elif defined(GL_ARB_occlusion_query)
+	glEndQueryARB(target);
+#else
+	os::Printer::log("glEndQueryARB not supported", ELL_ERROR);
 #endif
+}
 
+inline void COpenGLExtensionHandler::extGlGetQueryiv (GLenum target, GLenum pname, GLint *params)
+{
+#ifdef _IRR_OPENGL_USE_EXTPOINTER_
+	if (pGlGetQueryivARB)
+		pGlGetQueryivARB(target, pname, params);
+#elif defined(GL_ARB_occlusion_query)
+	glGetQueryivARB(target, pname, params);
+#else
+	os::Printer::log("glGetQueryivARB not supported", ELL_ERROR);
+#endif
+}
+
+inline void COpenGLExtensionHandler::extGlGetQueryObjectiv (GLuint id, GLenum pname, GLint *params)
+{
+#ifdef _IRR_OPENGL_USE_EXTPOINTER_
+	if (pGlGetQueryObjectivARB)
+		pGlGetQueryObjectivARB(id, pname, params);
+#elif defined(GL_ARB_occlusion_query)
+	glGetQueryObjectivARB(id, pname, params);
+#else
+	os::Printer::log("glGetQueryObjectivARB not supported", ELL_ERROR);
+#endif
+}
+
+inline void COpenGLExtensionHandler::extGlGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params)
+{
+#ifdef _IRR_OPENGL_USE_EXTPOINTER_
+	if (pGlGetQueryObjectuivARB)
+		pGlGetQueryObjectuivARB(id, pname, params);
+#elif defined(GL_ARB_occlusion_query)
+	glGetQueryObjectuivARB(id, pname, params);
+#else
+	os::Printer::log("glGetQueryObjectuivARB not supported", ELL_ERROR);
+#endif
+}
+
+
+}
+}
+
+#endif
+
+#endif
+
Library helping with network requests, tasks management, logger etc in desktop and mobile apps: https://github.com/GrupaPracuj/hermes
full.metal.coder
Posts: 68
Joined: Sat May 10, 2008 11:30 am
Contact:

Post by full.metal.coder »

thanks a lot.

I've already updated my working copy now I got to implement a smart culling algorithm. I've found a couple of resources after a quick web search but I did not find any implementation so far, let alone a killer(tm) one...

http://http.developer.nvidia.com/GPUGem ... ter06.html
http://http.developer.nvidia.com/GPUGem ... _ch29.html

I guess I'll have to search more and experiment a bit.
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

Wow that's great nadro. I'm glad someone finally finished this, and hopefully it can be included in the next Irrlicht release.
TheQuestion = 2B || !2B
torleif
Posts: 188
Joined: Mon Jun 30, 2008 4:53 am

Post by torleif »

Halifax wrote:Wow that's great nadro. I'm glad someone finally finished this, and hopefully it can be included in the next Irrlicht release.
+1

I'm all for this, under the impression of "lightning fast game engine" seems to make you assume there was already culling in the engine
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

Hmm, one thing I would like to note though is the naming scheme. You name the functions runAllQueries, createQuery, etc. But you have to remember, there are more queries that OpenGL, and D3D, contain that aren't occlusion queries.

So you might just want to rename that, or the devs can rename it if they accept it into the engine. Just a note.
TheQuestion = 2B || !2B
Nadro
Posts: 1648
Joined: Sun Feb 19, 2006 9:08 am
Location: Warsaw, Poland

Post by Nadro »

Hi, I can't replay quickly because I change my PC. Yes this code isn't prepare for implementation in engine. Of course all core system is good, but user interface maybe will be better if We can will disable/enable occlusion per material E_MATERIAL_FLAG? end rename runQuery to runOcclusionQuery etc?
Library helping with network requests, tasks management, logger etc in desktop and mobile apps: https://github.com/GrupaPracuj/hermes
Post Reply