Picking; linking x y screen coordinates to scene components

What it says on the tin: a place to discuss proposed new features.
User avatar
vitefalcon
Orc
Posts: 438
Joined: Tue Sep 18, 2007 5:28 pm
Location: Seattle, USA
x 13

Re: Picking; linking x y screen coordinates to scene compone

Post by vitefalcon »

Please correct me if I'm wrong. If the picking does not require to find the exact triangle, is it not possible to use something like the SelectionBuffer example? I have modified it to do picking in Renderable level. I also modified it a bit so that it only renders the scene once in a given render frame. That way I can do picking queries a number of times in a frame, when a particular event happens (usually a mouse down / click event). Although this code renders the scene twice upon the event (mouse down/click), it helps me precisely pick an Entity from it's SubEntity level. That is a requirement in my case but I wonder, what you guys think. From my point of view, this turns out to be very-much application dependent and not necessarily a functionality that should be present in Ogre3D itself. Or maybe, this could one of those headers that are in the 'Opts' header file and should have a disclaimer notice that this functionality is not a core Ogre3D feature and should be used at their own risk.

Header

Code: Select all

#include <OgreTexture.h>

	class RenderableMaterialSwitcher;
	class RenderableSelectionRenderListener;
	/**
	 * \brief	Buffer for \c Ogre::Renderable selection.
	**/
	class _TouchscapeExport RenderableSelectionBuffer
	{
	public:

		/**
		 * \brief	Constructor.
		 * \param [in,out]	scene_manager	Manager for scene. 
		 * \param [in,out]	camera			The camera. 
		 * \param [in,out]	render_target	The render target. 
		**/
		RenderableSelectionBuffer(SceneManager& scene_manager, Camera& camera, RenderTarget& render_target);
		~RenderableSelectionBuffer();

		/**
		 * \brief	Executes the selection click action.
		 * \param	x	The x coordinate of the click in pixel screen coordinates.
		 * \param	y	The y coordinate of the click in pixel screen coordinates.
		 *
		 * \return	null if it fails, else the renderable that was picked. 
		**/
		Ogre::Renderable* onSelectionClick(int x, int y);
	private:
		void update();
		void createRTTOverlays();
		void updateBufferSize();
		void createTexture(uint width, uint height);
		void clearTexture();

		SceneManager& m_sceneManager;
		Camera& m_camera;
		RenderTarget& m_renderTarget;

		RenderableMaterialSwitcher* m_materialSwitcher;
		RenderableSelectionRenderListener* m_selectionTargetListener;

		const String m_TextureName;
		const String m_SelectionDebugMaterialName;
		TexturePtr m_texture;
		RenderTexture* m_renderTexture;
		uint8* m_buffer;
		Ogre::PixelBox* m_pixelBox;
		Overlay* m_selectionDebugOverlay;

		time_t m_updateTimestamp;
	};
Source

Code: Select all

#include <OgreMaterialManager.h>
#include <OgreRenderTarget.h>
#include <OgreRenderTargetListener.h>
#include <OgreRenderTexture.h>
#include <OgreSceneManager.h>
#include <OgreTextureManager.h>
#include <OgreMaterialManager.h>
#include <OgreSubEntity.h>
#include <OgreEntity.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreOverlayManager.h>
#include <OgreOverlayContainer.h>

#define SHOW_OVERLAY 0

// Use random colours only if the debug overlay is being shown.
#if SHOW_OVERLAY
#	define USE_RANDOM_COLOURS 1
#else
#	define USE_RANDOM_COLOURS 0
#endif // SHOW_OVERLAY
	struct ColourCompare
	{
		bool operator()(const Ogre::ColourValue& a, const Ogre::ColourValue& b) const
		{
			return a.getAsBGRA() < b.getAsBGRA();
		}
	};

	class RenderableMaterialSwitcher : public Ogre::MaterialManager::Listener
	{
	public:
		RenderableMaterialSwitcher();
		virtual ~RenderableMaterialSwitcher();

		Ogre::Technique* handleSchemeNotFound(unsigned short schemeIndex, const Ogre::String& schemeName, Ogre::Material* originalMaterial, unsigned short lodIndex, const Ogre::Renderable* rend);
		Ogre::Renderable* getRenderable(const Ogre::ColourValue& colour);
		void reset();
	private:
		friend class RenderableSelectionBuffer;
		void getNextColour();

		typedef std::map<ColourValue, Ogre::Renderable*, ColourCompare> ColourMap;
		typedef ColourMap::const_iterator ColourMapConstItr;

		static const ColourValue RESET_COLOUR;

		ColourValue m_currentColour;
		Ogre::Renderable* m_lastRenderable;
		Ogre::Technique* m_lastTechnique;
		ColourMap m_colourMap;
	};

	const ColourValue RenderableMaterialSwitcher::RESET_COLOUR(0.0f, 0.0f, 0.0f);
	RenderableMaterialSwitcher::RenderableMaterialSwitcher()
		:m_lastTechnique(NULL)
		,m_currentColour(RESET_COLOUR)
	{
	}

	RenderableMaterialSwitcher::~RenderableMaterialSwitcher()
	{
	}

	Ogre::Technique* RenderableMaterialSwitcher::handleSchemeNotFound( unsigned short schemeIndex, const Ogre::String& schemeName, Ogre::Material* originalMaterial, unsigned short lodIndex, const Ogre::Renderable* rend )
	{
		if (rend)
		{
			if (m_lastRenderable != rend)
			{
				Ogre::ResourcePtr res = Ogre::MaterialManager::getSingleton().load("SelectionBuffer/PlainColour", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
				m_lastTechnique = static_cast<Ogre::MaterialPtr>(res)->getTechnique(0);

				getNextColour();

				m_lastRenderable = const_cast<Ogre::Renderable*>(rend);
				m_colourMap[m_currentColour] = m_lastRenderable;
			}
			m_lastRenderable->setCustomParameter(1, Vector4(m_currentColour.r, m_currentColour.g, m_currentColour.b, 1.0f));
			return m_lastTechnique;
		}
		return NULL;
	}

	Ogre::Renderable* RenderableMaterialSwitcher::getRenderable( const Ogre::ColourValue& colour )
	{
		if (m_colourMap.count(colour))
		{
			return m_colourMap[colour];
		}
		return NULL;
	}

	void RenderableMaterialSwitcher::getNextColour()
	{
#if USE_RANDOM_COLOURS
		m_currentColour.setAsARGB(Ogre::Math::UnitRandom()*0x00FFFFFF + 0xFF000000);
#else
		Ogre::ARGB colour = m_currentColour.getAsARGB();
		colour++;
		m_currentColour.setAsARGB(colour);
#endif // USE_RANDOM_COLOURS
	}

	void RenderableMaterialSwitcher::reset()
	{
		m_currentColour = RESET_COLOUR;
		m_lastRenderable = NULL;
		m_colourMap.clear();
	}
	class RenderableSelectionRenderListener : public Ogre::RenderTargetListener
	{
	public:
		RenderableSelectionRenderListener(RenderableMaterialSwitcher& switcher);
		~RenderableSelectionRenderListener();

		void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
		void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);

	private:
		RenderableMaterialSwitcher& m_materialListener;
	};

	RenderableSelectionRenderListener::RenderableSelectionRenderListener( RenderableMaterialSwitcher& switcher )
		:m_materialListener(switcher)
	{
	}

	RenderableSelectionRenderListener::~RenderableSelectionRenderListener()
	{
	}

	void RenderableSelectionRenderListener::preRenderTargetUpdate( const Ogre::RenderTargetEvent& evt )
	{
		Ogre::MaterialManager::getSingleton().addListener(&m_materialListener);
	}

	void RenderableSelectionRenderListener::postRenderTargetUpdate( const Ogre::RenderTargetEvent& evt )
	{
		Ogre::MaterialManager::getSingleton().removeListener(&m_materialListener);
	}
	RenderableSelectionBuffer::RenderableSelectionBuffer( SceneManager& scene_manager, Camera& camera, RenderTarget& render_target )
		:m_sceneManager(scene_manager)
		,m_camera(camera)
		,m_renderTarget(render_target)
		,m_TextureName(NameGenerator::Next("RenderableSelectionPassTex"))
		,m_SelectionDebugMaterialName(NameGenerator::Next("RenderableSelectionDebugMaterial"))
		,m_buffer(NULL)
		,m_pixelBox(NULL)
		,m_selectionDebugOverlay(NULL)
		,m_materialSwitcher(new RenderableMaterialSwitcher())
		,m_selectionTargetListener(new RenderableSelectionRenderListener(*m_materialSwitcher))
	{
		uint width = m_renderTarget.getWidth();
		uint height = m_renderTarget.getHeight();

		createTexture(width, height);

#if SHOW_OVERLAY
		createRTTOverlays();
#endif // SHOW_OVERLAY
	}

	RenderableSelectionBuffer::~RenderableSelectionBuffer()
	{
		clearTexture();
		SafeDelete(m_selectionTargetListener);
		SafeDelete(m_materialSwitcher);
#if SHOW_OVERLAY
		Ogre::MaterialManager::getSingleton().unload(m_SelectionDebugMaterialName);
#endif // SHOW_OVERLAY
	}

	__forceinline void RenderableSelectionBuffer::createTexture( uint width, uint height )
	{
		m_texture = Ogre::TextureManager::getSingleton().createManual(
			m_TextureName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
			Ogre::TEX_TYPE_2D, width, height, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);

		Ogre::HardwarePixelBufferSharedPtr pixel_buffer = m_texture->getBuffer();

		m_renderTexture = pixel_buffer->getRenderTarget();
		m_renderTexture->setAutoUpdated(false);
		m_renderTexture->setPriority(0);
		Ogre::Viewport* viewport = m_renderTexture->addViewport(&m_camera);
		viewport->setOverlaysEnabled(false);
		viewport->setClearEveryFrame(true);
		m_renderTexture->addListener(m_selectionTargetListener);
		viewport->setMaterialScheme("{45B9A135-0D74-443f-9806-2129FD7A2A96}");

		size_t buffer_size = pixel_buffer->getSizeInBytes();
		m_buffer = new uint8[buffer_size];
		m_pixelBox = new Ogre::PixelBox(pixel_buffer->getWidth(), pixel_buffer->getHeight(),
			pixel_buffer->getDepth(), pixel_buffer->getFormat(), m_buffer);
	}

	__forceinline void RenderableSelectionBuffer::clearTexture()
	{
		m_renderTexture->removeAllViewports();
		m_renderTexture->removeAllListeners();
		Ogre::TextureManager::getSingleton().unload(m_TextureName);
		m_renderTexture = NULL;
		SafeDelete(m_pixelBox);
		delete[] m_buffer;
		m_buffer = NULL;
	}

	void RenderableSelectionBuffer::createRTTOverlays()
	{
		Ogre::MaterialPtr base_white = Ogre::MaterialManager::getSingleton().getDefaultSettings();
		Ogre::MaterialPtr selection_buffer_material = base_white->clone(m_SelectionDebugMaterialName);
		Ogre::TextureUnitState* texture_unit = selection_buffer_material->getTechnique(0)->getPass(0)->createTextureUnitState(m_TextureName);

		Ogre::OverlayManager& overlay_manager = Ogre::OverlayManager::getSingleton();
		m_selectionDebugOverlay = overlay_manager.create(NameGenerator::Next("SelectionDebugOverlay"));

		Ogre::OverlayContainer* panel = static_cast<Ogre::OverlayContainer*>(overlay_manager.createOverlayElement("Panel", NameGenerator::Next("SelectionDebugPanel")));
		//panel->setMetricsMode(Ogre::GMM_PIXELS);
		panel->setPosition(0.0125, 0.0166667);
		panel->setDimensions(0.5f, 0.5f);
		panel->setMaterialName(m_SelectionDebugMaterialName);
		m_selectionDebugOverlay->add2D(panel);

		m_selectionDebugOverlay->show();
	}

	__forceinline void RenderableSelectionBuffer::update()
	{
		if (m_updateTimestamp != System::FrameTimeStamp)
		{
			updateBufferSize();

			m_materialSwitcher->reset();
			m_renderTexture->update();

			m_renderTexture->copyContentsToMemory(*m_pixelBox, Ogre::RenderTarget::FB_FRONT);

			m_updateTimestamp = System::FrameTimeStamp;
		}
	}

	void RenderableSelectionBuffer::updateBufferSize()
	{
		uint width = m_renderTarget.getWidth();
		uint height = m_renderTarget.getHeight();

		if (width != m_renderTexture->getWidth() || height != m_renderTexture->getHeight())
		{
			clearTexture();
			createTexture(width, height);
		}
	}

	Ogre::Renderable* RenderableSelectionBuffer::onSelectionClick( int x, int y )
	{
		update();

		size_t position_in_stream = m_pixelBox->getWidth() * y * 4;
		position_in_stream += x * 4;

		Ogre::ARGB argb(0);
		memcpy_s((void*)&argb, 4, m_buffer+position_in_stream, 4);
		Ogre::ColourValue colour;
		colour.setAsARGB(argb);
		colour.a = 1.0f;
		Ogre::Renderable* renderable = m_materialSwitcher->getRenderable(colour);
		return renderable;
	}
Image
Post Reply