[Solved] HardwareOcclusionQuery -- Query in RenderSingleObj

Problems building or running the engine, queries about how to use features etc.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

Unfortunately, when I incorporate this same logic in my project the Pixel Counts are not correct. Or not behaving the same way.

I converted to use Planes instead of BillboardSets and my earlier bad results.

I am suspicious that having multiple SceneManagers could be part of the problem. But I can't be certain.

I also have several queueGroupIds in use. I'm wondering if they step on each other's toes.

In the working example above I used 3 unique queueGroupIds without issues.

Maybe I should use PIX and record beginOcclusionQuery to endOcclusionQuery to diagnose the problem.

I'm a hair's width from using a shader solution. But having the working example above gives me some hope...
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

Here's a texture example where the lighting get's all funky because we have two render queues with entities that share the same texture.
Image

Again to run this overwrite your Samples\Demo_TextureFX\TextureFX.h file with this. And then run the Demo_TextureFX sample.

Code: Select all

/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2005 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

/**
    \file 
        TextureFX.h
    \brief
        Shows OGRE's ability to handle different types of texture effects.
*/


#include "ExampleApplication.h"

class TextureEffectsApplication : public ExampleApplication, RenderQueueListener
{
public:
	TextureEffectsApplication() :
		mScalingPlane_PixelCount	(0), // Hold the pixel count for
		mScrollingKnot_PixelCount	(0), // Hardware Occlusion Testing
		mWateryPlane_PixelCount		(0)  // "						 "
	{
	}

	// Implements RenderQueueListener, For Hardware Occlusion Testing
	void renderQueueStarted(uint8 queueGroupId, const std::string &invocation, bool &skipThisInvocation)
	{
		switch (queueGroupId)
		{
		case mScalingPlane_RenderQueueGroup:
			break;

		case mScrollingKnot_RenderQueueGroup:
			break;

		case mWateryPlane_RenderQueueGroup:
			break;

		default:
			// not interested
			return;
		};

		// CREATE HardwareOcclusionQuery
		Ogre::HardwareOcclusionQuery* hardwareOcclusionQuery = Root::getSingleton().getRenderSystem()->createHardwareOcclusionQuery();
		assert (hardwareOcclusionQuery);

		// Start the HardwareOcclusionQuery
		hardwareOcclusionQuery->beginOcclusionQuery();

		// Render the Item
		mSceneMgr->_renderQueueGroupObjects(mSceneMgr->getRenderQueue()->getQueueGroup(queueGroupId), Ogre::QueuedRenderableCollection::OrganisationMode::OM_PASS_GROUP);
		
		// Stop the HardwareOcclusionQuery
		hardwareOcclusionQuery->endOcclusionQuery();

		// ALREADY RENDERED
		skipThisInvocation = true;

		switch (queueGroupId)
		{
		case mScalingPlane_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mScalingPlane_PixelCount);
			break;

		case mScrollingKnot_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mScrollingKnot_PixelCount);
			break;

		case mWateryPlane_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mWateryPlane_PixelCount);
			break;
		};

		// DELETE HardwareOcclusionQuery
		Root::getSingleton().getRenderSystem()->destroyHardwareOcclusionQuery(hardwareOcclusionQuery);
		hardwareOcclusionQuery = 0;

		// SET THE DEBUG TEXT TO DISPLAY THE PIXEL COUNTS
		char buffer[250] = {0};
		sprintf(buffer,
			"ScalingPlane: [%d], WateryPlane [%d], ScrollingKnot [%d]",
			mScalingPlane_PixelCount, mWateryPlane_PixelCount, mScrollingKnot_PixelCount);
		mWindow->setDebugText(buffer);
	}

	// Implements RenderQueueListener, For Hardware Occlusion Testing
	void renderQueueEnded(uint8 queueGroupId, const std::string &invocation, bool &repeatThisInvocation)
	{
	}

protected:

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mScalingPlane_RenderQueueGroup = 1;

	// Pixel Count
	unsigned int mScalingPlane_PixelCount;

    void createScalingPlane()
    {
        // Set up a material for the plane

        // Create a prefab plane
        Entity *planeEnt = mSceneMgr->createEntity("Plane", SceneManager::PT_PLANE);
        // Give the plane a texture
        planeEnt->setMaterialName("Examples/TextureEffect1");

		// For Hardware Occlusion Testing
		planeEnt->setRenderQueueGroup(mScalingPlane_RenderQueueGroup);

        SceneNode* node = 
            mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(-250,-40,-100));

        node->attachObject(planeEnt);
    }

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mScrollingKnot_RenderQueueGroup = 2;

	// Pixel Count
	unsigned int mScrollingKnot_PixelCount;

    void createScrollingKnot()
    {
        Entity *ent = mSceneMgr->createEntity("knot", "knot.mesh");


        ent->setMaterialName("Examples/TextureEffect2");

		// For Hardware Occlusion Testing
		ent->setRenderQueueGroup(mScrollingKnot_RenderQueueGroup);

        // Add entity to the root scene node
        SceneNode* node = 
            mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(200,50,150));

        node->attachObject(ent);

    }

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mWateryPlane_RenderQueueGroup = 3;

	// Pixel Count
	unsigned int mWateryPlane_PixelCount;

    void createWateryPlane()
    {
        // Create a prefab plane
        Entity *planeEnt = mSceneMgr->createEntity("WaterPlane", SceneManager::PT_PLANE);

		// For Hardware Occlusion Testing
		planeEnt->setRenderQueueGroup(mWateryPlane_RenderQueueGroup);

        // Give the plane a texture
        planeEnt->setMaterialName("Examples/TextureEffect3");

        mSceneMgr->getRootSceneNode()->attachObject(planeEnt);
    }

	void createImposterPlane()
    {
        // Create a prefab plane
        Entity *ent = mSceneMgr->createEntity("ImposterPlane", SceneManager::PT_PLANE);

        // Give the plane a texture
        ent->setMaterialName("Examples/TextureEffect2");

		ent->setRenderQueueGroup(0);

		// Add entity to the root scene node
        SceneNode* node = 
            mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(0,0,-250));
		assert (node);

		node->setScale(5, 5, 0);
		node->_updateBounds();

        node->attachObject(ent);
    }

    // Just override the mandatory create scene method
    void createScene(void)
    {

        // Set ambient light
        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

        // Create a point light
        Light* l = mSceneMgr->createLight("MainLight");
        // Accept default settings: point light, white diffuse, just set position
        // NB I could attach the light to a SceneNode if I wanted it to move automatically with
        //  other objects, but I don't
        l->setPosition(20,80,50);

		// Added for Hardware Occlusion Testing
		mSceneMgr->addRenderQueueListener(this);

        createScalingPlane();
        createScrollingKnot();
        createWateryPlane();
		createImposterPlane();


        // Set up a material for the skydome
        MaterialPtr skyMat = MaterialManager::getSingleton().create("SkyMat",
            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
        // Perform no dynamic lighting on the sky
        skyMat->setLightingEnabled(false);
        // Use a cloudy sky
        TextureUnitState* t = skyMat->getTechnique(0)->getPass(0)->createTextureUnitState("clouds.jpg");
        // Scroll the clouds
        t->setScrollAnimation(0.15, 0);

        // System will automatically set no depth write

        // Create a skydome
        mSceneMgr->setSkyDome(true, "SkyMat", -5, 2);
    }

};
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

Again to run the code snippet above overwrite your Samples\Demo_TextureFX\TextureFX.h file with the code snippet. And then run the Demo_TextureFX sample.
User avatar
Lee04
Minaton
Posts: 945
Joined: Mon Jul 05, 2004 4:06 pm
Location: Sweden
x 1

Post by Lee04 »

Hmmm I think the demos here are toy demos, right?

A complex scene and this won't work. Because on how Ogre renders scenes are not compaible with the what the use of hardware occlusion query requires.

HOQ requires scenes render sorted on depth.
While Ogre sorts the rendered scene based on materials.
So if you have a deep complex scene with many different materials
and use standard Ogre rendering you will get a random result on your pixel count. Thats why I have done a special rendering plug-in for Ogre that does a first pass just for getting hardware occlusion qyuery and early z out working like it should.

Lee04
Ph.D. student in game development
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

Lee04 wrote:Hmmm I think the demos here are toy demos, right?

A complex scene and this won't work. Because on how Ogre renders scenes are not compaible with the what the use of hardware occlusion query requires.

HOQ requires scenes render sorted on depth.
While Ogre sorts the rendered scene based on materials.
So if you have a deep complex scene with many different materials
and use standard Ogre rendering you will get a random result on your pixel count. Thats why I have done a special rendering plug-in for Ogre that does a first pass just for getting hardware occlusion qyuery and early z out working like it should.

Lee04
Care to share your source code? Because that's exactly what I'm seeing random pixel values. Because I've done some things. Bad things. I've changed the sort order by using depth functions, used depth biases, added particle systems, and added billboard sets on top of that. Plus there's a lot of sharing materials going on. So I'll need to use whatever solution you have.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

Lee04 is sellling that plugin.. :wink:
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

Re-implementing another Ogre::SceneManager doesn't seem like a difficult task.

Let's see, I'd need to make a CustomSceneManager class.

CustomSceneManager.h

Code: Select all

/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2005 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

/**
    \file 
        CustomSceneManager.h
    \brief
        Shows OGRE's ability to handle a different scene manager.
*/


#ifndef __CUSTOM_SCENE_MANAGER__H__
#define	__CUSTOM_SCENE_MANAGER__H__

#include <OgreNoMemoryMacros.h>
#include <OgreSceneManagerEnumerator.h>
#include <OgreMemoryMacros.h>

class CustomSceneManagerFactory : public Ogre::SceneManagerFactory
{
protected:
	void initMetaData(void) const;
public:
	CustomSceneManagerFactory() {}
	~CustomSceneManagerFactory() {}
	/// Factory type name
	static const Ogre::String FACTORY_TYPE_NAME;
	Ogre::SceneManager* createInstance(const Ogre::String& instanceName);
	void destroyInstance(Ogre::SceneManager* instance);
};

class CustomSceneManager : public Ogre::SceneManager
{
public:
	CustomSceneManager(const Ogre::String& name);
	~CustomSceneManager();
	const Ogre::String& getTypeName(void) const;
};

#endif	// __CUSTOM_SCENE_MANAGER__H__
Since Ogre::SceneManager is an abstract class, we need to provide a little implementation.

CustomSceneManager.cpp

Code: Select all

/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2005 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

/**
    \file 
        CustomSceneManager.cpp
    \brief
        Shows OGRE's ability to handle a different scene manager.
*/


#include "CustomSceneManager.h"

void CustomSceneManagerFactory::initMetaData(void) const
{
	mMetaData.typeName = FACTORY_TYPE_NAME;
	mMetaData.description = "The custom scene manager";
	mMetaData.sceneTypeMask = Ogre::ST_GENERIC;
	mMetaData.worldGeometrySupported = false;
}

const Ogre::String CustomSceneManagerFactory::FACTORY_TYPE_NAME = "CustomSceneManager";

Ogre::SceneManager* CustomSceneManagerFactory::createInstance(const Ogre::String& instanceName)
{
	return new CustomSceneManager(instanceName);
}

void CustomSceneManagerFactory::destroyInstance(Ogre::SceneManager* instance)
{
	delete instance;
}

CustomSceneManager::CustomSceneManager(const Ogre::String& name)
	: Ogre::SceneManager(name)
{
}

CustomSceneManager::~CustomSceneManager()
{
}

const Ogre::String& CustomSceneManager::getTypeName(void) const
{
	return CustomSceneManagerFactory::FACTORY_TYPE_NAME;
}
And then we need to modify the Texture Demo again to add the CustomSceneManagerFactory.
TextureFX.h

Code: Select all

/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2005 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

/**
    \file 
        TextureFX.h
    \brief
        Shows OGRE's ability to handle different types of texture effects.
*/


#include "ExampleApplication.h"
#include "CustomSceneManager.h"

class TextureEffectsApplication : public ExampleApplication, RenderQueueListener
{
public:
	TextureEffectsApplication() :
		mScalingPlane_PixelCount	(0), // Hold the pixel count for
		mScrollingKnot_PixelCount	(0), // Hardware Occlusion Testing
		mWateryPlane_PixelCount		(0)  // "						 "
	{
	}

	// Implements RenderQueueListener, For Hardware Occlusion Testing
	void renderQueueStarted(uint8 queueGroupId, const std::string &invocation, bool &skipThisInvocation)
	{
		switch (queueGroupId)
		{
		case mScalingPlane_RenderQueueGroup:
			break;

		case mScrollingKnot_RenderQueueGroup:
			break;

		case mWateryPlane_RenderQueueGroup:
			break;

		default:
			// not interested
			return;
		};

		// CREATE HardwareOcclusionQuery
		Ogre::HardwareOcclusionQuery* hardwareOcclusionQuery = Root::getSingleton().getRenderSystem()->createHardwareOcclusionQuery();
		assert (hardwareOcclusionQuery);

		// Start the HardwareOcclusionQuery
		hardwareOcclusionQuery->beginOcclusionQuery();

		// Render the Item
		mSceneMgr->_renderQueueGroupObjects(mSceneMgr->getRenderQueue()->getQueueGroup(queueGroupId), Ogre::QueuedRenderableCollection::OrganisationMode::OM_PASS_GROUP);
		
		// Stop the HardwareOcclusionQuery
		hardwareOcclusionQuery->endOcclusionQuery();

		// ALREADY RENDERED
		skipThisInvocation = true;

		switch (queueGroupId)
		{
		case mScalingPlane_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mScalingPlane_PixelCount);
			break;

		case mScrollingKnot_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mScrollingKnot_PixelCount);
			break;

		case mWateryPlane_RenderQueueGroup:
			// GET THE PIXEL COUNT
			hardwareOcclusionQuery->pullOcclusionQuery(&mWateryPlane_PixelCount);
			break;
		};

		// DELETE HardwareOcclusionQuery
		Root::getSingleton().getRenderSystem()->destroyHardwareOcclusionQuery(hardwareOcclusionQuery);
		hardwareOcclusionQuery = 0;

		// SET THE DEBUG TEXT TO DISPLAY THE PIXEL COUNTS
		char buffer[250] = {0};
		sprintf(buffer,
			"ScalingPlane: [%d], WateryPlane [%d], ScrollingKnot [%d]",
			mScalingPlane_PixelCount, mWateryPlane_PixelCount, mScrollingKnot_PixelCount);
		mWindow->setDebugText(buffer);
	}

	// Implements RenderQueueListener, For Hardware Occlusion Testing
	void renderQueueEnded(uint8 queueGroupId, const std::string &invocation, bool &repeatThisInvocation)
	{
	}

protected:

	// To create a custom scene manager you need a factory
	CustomSceneManagerFactory* mCustomSceneManagerFactory;

	// For custom scene manager
	virtual void chooseSceneManager(void)
    {
		if (!mCustomSceneManagerFactory)
		{
			// Create custom scene manager factory
			mCustomSceneManagerFactory = new CustomSceneManagerFactory();
			assert (mCustomSceneManagerFactory);

			// Allows creation of Custom Scene Managers
			Root::getSingleton().addSceneManagerFactory(mCustomSceneManagerFactory); //only do this once
		}

        // Create the SceneManager, in this case a custom one
        mSceneMgr = mRoot->createSceneManager(ST_GENERIC, "CustomSceneManager");
    }

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mScalingPlane_RenderQueueGroup = 1;

	// Pixel Count
	unsigned int mScalingPlane_PixelCount;

    void createScalingPlane()
    {
        // Set up a material for the plane

        // Create a prefab plane
        Entity *planeEnt = mSceneMgr->createEntity("Plane", SceneManager::PT_PLANE);
        // Give the plane a texture
        planeEnt->setMaterialName("Examples/TextureEffect1");

		// For Hardware Occlusion Testing
		planeEnt->setRenderQueueGroup(mScalingPlane_RenderQueueGroup);

        SceneNode* node = 
            mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(-250,-40,-100));

        node->attachObject(planeEnt);
    }

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mScrollingKnot_RenderQueueGroup = 2;

	// Pixel Count
	unsigned int mScrollingKnot_PixelCount;

    void createScrollingKnot()
    {
        Entity *ent = mSceneMgr->createEntity("knot", "knot.mesh");


        ent->setMaterialName("Examples/TextureEffect2");

		// For Hardware Occlusion Testing
		ent->setRenderQueueGroup(mScrollingKnot_RenderQueueGroup);

        // Add entity to the root scene node
        SceneNode* node = 
            mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(200,50,150));

        node->attachObject(ent);

    }

	// Hardware Occlusion Testing CONSTANT
	static const unsigned int mWateryPlane_RenderQueueGroup = 3;

	// Pixel Count
	unsigned int mWateryPlane_PixelCount;

    void createWateryPlane()
    {
        // Create a prefab plane
        Entity *planeEnt = mSceneMgr->createEntity("WaterPlane", SceneManager::PT_PLANE);

		// For Hardware Occlusion Testing
		planeEnt->setRenderQueueGroup(mWateryPlane_RenderQueueGroup);

        // Give the plane a texture
        planeEnt->setMaterialName("Examples/TextureEffect3");

        mSceneMgr->getRootSceneNode()->attachObject(planeEnt);
    }

	void createImposterPlane()
    {
        // Create a prefab plane
        Entity *ent = mSceneMgr->createEntity("ImposterPlane", SceneManager::PT_PLANE);

        // Give the plane a texture
        ent->setMaterialName("Examples/TextureEffect2");

		ent->setRenderQueueGroup(0);

		// Add entity to the root scene node
        SceneNode* node = 
            mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(0,0,-250));
		assert (node);

		node->setScale(5, 5, 0);
		node->_updateBounds();

        node->attachObject(ent);
    }

    // Just override the mandatory create scene method
    void createScene(void)
    {

        // Set ambient light
        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

        // Create a point light
        Light* l = mSceneMgr->createLight("MainLight");
        // Accept default settings: point light, white diffuse, just set position
        // NB I could attach the light to a SceneNode if I wanted it to move automatically with
        //  other objects, but I don't
        l->setPosition(20,80,50);

		// Added for Hardware Occlusion Testing
		mSceneMgr->addRenderQueueListener(this);

        createScalingPlane();
        createScrollingKnot();
        createWateryPlane();
		createImposterPlane();


        // Set up a material for the skydome
        MaterialPtr skyMat = MaterialManager::getSingleton().create("SkyMat",
            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
        // Perform no dynamic lighting on the sky
        skyMat->setLightingEnabled(false);
        // Use a cloudy sky
        TextureUnitState* t = skyMat->getTechnique(0)->getPass(0)->createTextureUnitState("clouds.jpg");
        // Scroll the clouds
        t->setScrollAnimation(0.15, 0);

        // System will automatically set no depth write

        // Create a skydome
        mSceneMgr->setSkyDome(true, "SkyMat", -5, 2);
    }

};
Remaining Logic
Now I just need to provide a little logic in our CustomSceneManager to sort objects in DECENDING DISTANCE ORDER, instead of SORTED BY RENDER QUEUE ID or material or whatever...

(To be continued...)
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

I've been reading the source comments and looking deeper in the Ogre::SceneManager source.

Is it possible to do HOQ queries around a SpecialCaseRenderQueue? I had just been assigning unused values for the RenderingQueueId.

I guess that's what I was trying earlier anyway. When the pixel values were seemingly random. Most likely because other objects were sneaking into Hardware Occlusion Query test.

But I'm starting to see the Ogre rendering system is more complex:
http://www.ogre3d.org/phpBB2/viewtopic. ... enderqueue

Digging deeper... Down the rabbit hole...
User avatar
Lee04
Minaton
Posts: 945
Joined: Mon Jul 05, 2004 4:06 pm
Location: Sweden
x 1

Post by Lee04 »

That rabbit hole is deep.
Specially different kind of shadows...
Ph.D. student in game development
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

Someone had mentioned an Ogre add-on was experimenting with HOQ, which project was that?
User avatar
Lee04
Minaton
Posts: 945
Joined: Mon Jul 05, 2004 4:06 pm
Location: Sweden
x 1

Post by Lee04 »

Pageing landscapes I belive but I don't know hwo general the HOQ usability for general HOQ things besides for the landscape.
Ph.D. student in game development
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

The simplest answer is always the best solution. I got occlusion working. Check it out:
Image

I just got something working. I just implemented RenderSingle on an extended interface of DefaultSceneManager. I do the query at the beginning and end of the method. The pixel counts are correct now. It's amazing.

Right now it works for a single object, but it's relatively simple to add a list of objects to test for.

Code: Select all

class CustomSceneManager : public Ogre::SceneManager
{
	friend class StarElement;

public:
	CustomSceneManager(const Ogre::String& name);
	~CustomSceneManager();
	const Ogre::String& getTypeName(void) const;

	/** Internal utility method for rendering a single object. 
    @remarks
        Assumes that the pass has already been set up.
    @param rend The renderable to issue to the pipeline
    @param pass The pass which is being used
    @param doLightIteration If true, this method will issue the renderable to
        the pipeline possibly multiple times, if the pass indicates it should be
        done once per light
    @param manualLightList Only applicable if doLightIteration is false, this
        method allows you to pass in a previously determined set of lights
        which will be used for a single render of this object.
    */
	virtual void renderSingleObject(const Ogre::Renderable* rend, const Ogre::Pass* pass, 
		bool doLightIteration, const Ogre::LightList* manualLightList = 0);
};

Code: Select all

#include <OgreNoMemoryMacros.h>
#include "OgreCamera.h"
#include "OgreString.h"
#include "OgreSceneNode.h"
#include "OgrePlane.h"
#include "OgreQuaternion.h"
#include "OgreColourValue.h"
#include "OgreCommon.h"
#include "OgreSceneQuery.h"
#include "OgreAutoParamDataSource.h"
#include "OgreAnimationState.h"
#include "OgreRenderQueue.h"
#include "OgreRenderQueueSortingGrouping.h"
#include "OgreRenderSystem.h"
#include "OgreRectangle2D.h"
#include "OgrePixelFormat.h"
#include "OgreRoot.h"
#include <OgreHardwareOcclusionQuery.h>
#include "OgreResourceGroupManager.h"
#include "OgreTexture.h"
#include <OgreMemoryMacros.h>

Ogre::Renderable* g_InspectRenderable = 0;
unsigned int g_PixelCount = 0;
...

Code: Select all

void CustomSceneManager::renderSingleObject(const Ogre::Renderable* rend, const Ogre::Pass* pass, 
											bool doLightIteration, const Ogre::LightList* manualLightList)
{
	/// <!-- START OF CUSTOM CODE -->
	///
	/// CREATE HardwareOcclusionQuery
	///
	static Ogre::HardwareOcclusionQuery* hardwareOcclusionQuery;

	if (g_InspectRenderable == rend)
	{
		hardwareOcclusionQuery = Ogre::Root::getSingleton().getRenderSystem()->createHardwareOcclusionQuery();
		assert (hardwareOcclusionQuery);

		///
		/// Start the HardwareOcclusionQuery
		///
		hardwareOcclusionQuery->beginOcclusionQuery();
	}
	/// <!-- END OF CUSTOM CODE -->

    unsigned short numMatrices;
	static Ogre::RenderOperation ro;
    static Ogre::LightList localLightList;

    // Set up rendering operation
    // I know, I know, const_cast is nasty but otherwise it requires all internal
    // state of the Renderable assigned to the rop to be mutable
    const_cast<Ogre::Renderable*>(rend)->getRenderOperation(ro);
    ro.srcRenderable = rend;

    // Set world transformation
    rend->getWorldTransforms(mTempXform);
    numMatrices = rend->getNumWorldTransforms();
    if (numMatrices > 1)
    {
        mDestRenderSystem->_setWorldMatrices(mTempXform, numMatrices);
    }
    else
    {
        mDestRenderSystem->_setWorldMatrix(*mTempXform);
    }

    // Issue view / projection changes if any
    useRenderableViewProjMode(rend);

    if (!mSuppressRenderStateChanges)
    {
        bool passSurfaceAndLightParams = true;

        if (pass->isProgrammable())
        {
            // Tell auto params object about the renderable change
            mAutoParamDataSource.setCurrentRenderable(rend);
            pass->_updateAutoParamsNoLights(mAutoParamDataSource);
            if (pass->hasVertexProgram())
            {
                passSurfaceAndLightParams = pass->getVertexProgram()->getPassSurfaceAndLightStates();
            }
        }

        // Reissue any texture gen settings which are dependent on view matrix
		Ogre::Pass::ConstTextureUnitStateIterator texIter =  pass->getTextureUnitStateIterator();
        size_t unit = 0;
        while(texIter.hasMoreElements())
        {
			Ogre::TextureUnitState* pTex = texIter.getNext();
            if (pTex->hasViewRelativeTextureCoordinateGeneration())
            {
                mDestRenderSystem->_setTextureUnitSettings(unit, *pTex);
            }
            ++unit;
        }

        // Sort out normalisation
        mDestRenderSystem->setNormaliseNormals(rend->getNormaliseNormals());

        // Set up the solid / wireframe override
		// Precedence is Camera, Object, Material
		// Camera might not override object if not overrideable
		Ogre::PolygonMode reqMode = pass->getPolygonMode();
		if (rend->getPolygonModeOverrideable())
		{
			Ogre::PolygonMode camPolyMode = mCameraInProgress->getPolygonMode();
			// check camera detial only when render detail is overridable
			if (reqMode > camPolyMode)
			{
				// only downgrade detail; if cam says wireframe we don't go up to solid
				reqMode = camPolyMode;
			}
		}
		mDestRenderSystem->_setPolygonMode(reqMode);

		mDestRenderSystem->setClipPlanes(rend->getClipPlanes());

		if (doLightIteration)
		{
			// Here's where we issue the rendering operation to the render system
			// Note that we may do this once per light, therefore it's in a loop
			// and the light parameters are updated once per traversal through the
			// loop
			const Ogre::LightList& rendLightList = rend->getLights();
			bool iteratePerLight = pass->getIteratePerLight();
			size_t numIterations = iteratePerLight ? rendLightList.size() : 1;
			const Ogre::LightList* pLightListToUse;
			for (size_t i = 0; i < numIterations; ++i)
			{
				// Determine light list to use
				if (iteratePerLight)
				{
					// Change the only element of local light list to be
					// the light at index i
					localLightList.clear();
					// Check whether we need to filter this one out
					if (pass->getRunOnlyForOneLightType() && 
						pass->getOnlyLightType() != rendLightList[i]->getType())
					{
						// Skip
						continue;
					}

					localLightList.push_back(rendLightList[i]);
					pLightListToUse = &localLightList;
				}
				else
				{
					// Use complete light list
					pLightListToUse = &rendLightList;
				}


				// Do we need to update GPU program parameters?
				if (pass->isProgrammable())
				{
					// Update any automatic gpu params for lights
					// Other bits of information will have to be looked up
					mAutoParamDataSource.setCurrentLightList(pLightListToUse);
					pass->_updateAutoParamsLightsOnly(mAutoParamDataSource);
					// NOTE: We MUST bind parameters AFTER updating the autos
					// TEST
					if (pass->hasVertexProgram())
					{
						mDestRenderSystem->bindGpuProgramParameters(Ogre::GPT_VERTEX_PROGRAM, 
							pass->getVertexProgramParameters());
					}
					if (pass->hasFragmentProgram())
					{
						mDestRenderSystem->bindGpuProgramParameters(Ogre::GPT_FRAGMENT_PROGRAM, 
							pass->getFragmentProgramParameters());
					}
				}
				// Do we need to update light states? 
				// Only do this if fixed-function vertex lighting applies
				if (pass->getLightingEnabled() && passSurfaceAndLightParams)
				{
					mDestRenderSystem->_useLights(*pLightListToUse, pass->getMaxSimultaneousLights());
				}
				// issue the render op		
				// nfz: check for gpu_multipass
				mDestRenderSystem->setCurrentPassIterationCount(pass->getPassIterationCount());
				mDestRenderSystem->_render(ro);
			} // possibly iterate per light
		}
		else // no automatic light processing
		{
			// Do we need to update GPU program parameters?
			if (pass->isProgrammable())
			{
				// Do we have a manual light list?
				if (manualLightList)
				{
					// Update any automatic gpu params for lights
					mAutoParamDataSource.setCurrentLightList(manualLightList);
					pass->_updateAutoParamsLightsOnly(mAutoParamDataSource);
				}

				if (pass->hasVertexProgram())
				{
					mDestRenderSystem->bindGpuProgramParameters(Ogre::GPT_VERTEX_PROGRAM, 
						pass->getVertexProgramParameters());
				}
				if (pass->hasFragmentProgram())
				{
					mDestRenderSystem->bindGpuProgramParameters(Ogre::GPT_FRAGMENT_PROGRAM, 
						pass->getFragmentProgramParameters());
				}
			}

			// Use manual lights if present, and not using vertex programs that don't use fixed pipeline
			if (manualLightList && 
				pass->getLightingEnabled() && passSurfaceAndLightParams)
			{
				mDestRenderSystem->_useLights(*manualLightList, pass->getMaxSimultaneousLights());
			}
			// issue the render op		
			// nfz: set up multipass rendering
			mDestRenderSystem->setCurrentPassIterationCount(pass->getPassIterationCount());
			mDestRenderSystem->_render(ro);
		}

	}
	else // mSuppressRenderStateChanges
	{
		// Just render
		mDestRenderSystem->setCurrentPassIterationCount(1);
		mDestRenderSystem->_render(ro);
	}
	
    // Reset view / projection changes if any
    resetViewProjMode();
	
	/// <!-- START OF CUSTOM CODE -->
	if (g_InspectRenderable == rend)
	{
		///
		/// Stop the HardwareOcclusionQuery
		///
		hardwareOcclusionQuery->endOcclusionQuery();

		///
		/// GET THE PIXEL COUNT
		///
		hardwareOcclusionQuery->pullOcclusionQuery(&g_PixelCount);

		///
		/// DELETE HardwareOcclusionQuery
		///
		Ogre::Root::getSingleton().getRenderSystem()->destroyHardwareOcclusionQuery(hardwareOcclusionQuery);
		hardwareOcclusionQuery = 0;
	}
	/// <!-- END OF CUSTOM CODE -->
}
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

I just realized another key thing for making this work is your material script.

Here are the sample values that I used:

Code: Select all

material star_1_element_blank
{
	technique
	{
		pass
		{
			lighting off
			scene_blend src_alpha dest_alpha
			depth_write off

			cull_hardware none
			cull_software none

			texture_unit
			{
				tex_address_mode clamp
				texture star_1_element_blank.png
			}
		}
	}
}
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

I should also mention that you should disabling culling:

Code: Select all

         cull_hardware none
         cull_software none 
This helps when the Billboard is off the screen the pixel count will be zero. Without this, if the billboard gets culled it won't update the pixel count.
User avatar
syedhs
Silver Sponsor
Silver Sponsor
Posts: 2703
Joined: Mon Aug 29, 2005 3:24 pm
Location: Kuala Lumpur, Malaysia
x 51

Post by syedhs »

Good for you :) I noticed your posting last time but it was not get solved fast but finally you manage it. Maybe you can get this WIKIed? A plus would be the intro of why you need Hardware Occlusion Query.
tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

tgraupmann
Gnoll
Posts: 696
Joined: Sun Feb 20, 2005 5:28 am

Post by tgraupmann »

tgraupmann wrote:Alright, step one create the page:
http://www.ogre3d.org/wiki/index.php/Ha ... sion_Query
Alright I got around to improving the HOQ documentation.
big_o
Goblin
Posts: 279
Joined: Sun Feb 19, 2006 1:08 am

Post by big_o »

Looks very good! Glad to see that you got it working. :)

This is something I might be interested in down the road.