CodeSnippets Selection Buffer

From PyWiki

Jump to: navigation, search

This class is easy to use. Basicaly you just create an Instance of the SelectionBuffer class and pass it a pointer to a scenemanager an the renderTarget in wich you want to select. When you wan't to select something just call onSelectionClick (with your x and y coordinates) on your SelectionBuffer instance and it returns an Entity instance or None if no entity was found under that pixel.

The buffer works by rendering the scene again but without any materials but only plain colors. The MaterialSwitcher class saves which color belongs to which entity. The SelectionBuffer class then retrieves the correct Entity from the SceneManager.


import ctypes as ctypes
import random
 
import ogre.renderer.OGRE as og
 
 
# class to handle material switching without having to modify scene materials individually
class MaterialSwitcher( og.MaterialManager.Listener ):
    def __init__(self):
        og.MaterialManager.Listener.__init__(self)
 
        self.currentColor = og.ColourValue(0.0, 0.0, 0.0)
        self.currentColorAsVector3 = og.Vector3()
 
        self.lastEntity = ""
        self.lastTechnique = None
 
        self.colorDict = {}
 
    # takes into account that one Entity can have multiple SubEntities
    def handleSchemeNotFound(self, index, name, material, lod, subEntity):
        temp = str(type(subEntity))
        if temp == "<class 'ogre.renderer.OGRE._ogre_.SubEntity'>":
            if self.lastEntity == subEntity.getParent().getName():
                subEntity.setCustomParameter(1, og.Vector4(self.currentColor.r, self.currentColor.g, self.currentColor.b, 1.0))
                return self.lastTechnique
            else:
                self.lastTechnique = og.MaterialManager.getSingleton().load("PlainColor", og.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME).getTechnique(0)
 
                self.randomizeColor()
                subEntity.setCustomParameter(1, og.Vector4(self.currentColor.r, self.currentColor.g, self.currentColor.b, 1.0))
 
                self.lastEntity = subEntity.getParent().getName()
                self.colorDict[self.lastEntity] = self.currentColorAsVector3
                return self.lastTechnique
 
 
    def randomizeColor(self):
        r = random.randrange(1, 255)
        g = random.randrange(1, 255)
        b = random.randrange(1, 255)
        self.currentColorAsVector3 = og.Vector3(r, g, b)
        var = 1.0 / 255.0
 
        self.currentColor = og.ColourValue(r * var, g * var, b * var)
 
    def reset(self):
        self.currentColor = og.ColourValue(0.0, 0.0, 0.0)
        self.lastEntity = ""
 
# We need this attached to the depth target, otherwise we get problems with the compositor
# MaterialManager.Listener should NOT be running all the time - rather only when we're
# specifically rendering the target that needs it
class SelectionRenderListener(og.RenderTargetListener):
    def __init__(self, materialListener):
        og.RenderTargetListener.__init__(self)
        self.materialListener = materialListener
 
    def preRenderTargetUpdate(self, evt):
        og.MaterialManager.getSingleton().addListener( self.materialListener )
 
    def postRenderTargetUpdate(self, evt):
        og.MaterialManager.getSingleton().removeListener( self.materialListener )
 
 
class SelectionBuffer():
    # sceneManager is the ogre.SceneManager wich this buffer belongs to
    # renderTarget is the ogre.RenderTarget in wich you want to select
    def __init__(self, sceneManager,  renderTarget):
        self.sceneMgr = sceneManager
        self.camera = sceneManager.getCamera("MainCam")
 
        self.renderTarget = renderTarget
 
        # This is the material listener - Note: it is controlled by a seperate
        # RenderTargetListener, not applied globally to all targets
        self.materialSwitchListener = MaterialSwitcher()
 
        self.selectionTargetListener = SelectionRenderListener( self.materialSwitchListener )
 
        width = self.renderTarget.getWidth()
        height = self.renderTarget.getHeight()
 
        self.texture = og.TextureManager.getSingleton().createManual("SelectionPassTex", 
                                                                    og.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME, 
                                                                    og.TEX_TYPE_2D, 
                                                                    width, 
                                                                    height, 
                                                                    0, og.PixelFormat.PF_R8G8B8, og.TU_RENDERTARGET)
 
        self.renderTexture = self.texture.getBuffer().getRenderTarget()
        self.renderTexture.setAutoUpdated(False)
        self.renderTexture.setPriority(0)                                                         
        self.renderTexture.addViewport( self.camera )
        self.renderTexture.getViewport(0).setOverlaysEnabled(False)
        self.renderTexture.getViewport(0).setClearEveryFrame(True)
        self.renderTexture.addListener( self.selectionTargetListener )
        self.renderTexture.getViewport(0).setMaterialScheme("aa")
 
        self.createRTTOverlays()
 
    def update(self):
        self.updateBufferSize()
 
        self.renderTexture.update()        
        self.materialSwitchListener.reset()
 
        pixelBuffer = self.texture.getBuffer()
        bufferSize = pixelBuffer.getSizeInBytes()
        #buffersize2 = self.renderTexture.getWidth()*self.renderTexture.getHeight()*4
 
        storageclass = ctypes.c_uint8 * (bufferSize)
        self.buffer = storageclass()
 
        VoidPointer = og.castAsVoidPtr(ctypes.addressof(self.buffer))
 
        self.pBox = og.PixelBox(pixelBuffer.getWidth(), pixelBuffer.getHeight(),pixelBuffer.getDepth(), pixelBuffer.getFormat(), VoidPointer)
        self.renderTexture.copyContentsToMemory(self.pBox, og.RenderTarget.FrameBuffer.FB_FRONT)
 
 
    def updateBufferSize(self):
        width = self.renderTarget.getWidth()
        height = self.renderTarget.getHeight()
        needsSizeUpdate = False
 
        if width is not self.renderTexture.getWidth():
            needsSizeUpdate = True
        if height is not self.renderTexture.getHeight():
            needsSizeUpdate = True            
 
        if needsSizeUpdate:
            og.TextureManager.getSingleton().unload("SelectionPassTex")
 
            self.texture = og.TextureManager.getSingleton().createManual("SelectionPassTex", 
                                                            og.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME, 
                                                            og.TEX_TYPE_2D, 
                                                            width, 
                                                            height, 
                                                            0, og.PixelFormat.PF_R8G8B8, og.TU_RENDERTARGET)
 
            self.renderTexture = self.texture.getBuffer().getRenderTarget()
            self.renderTexture.setAutoUpdated(False)
            self.renderTexture.setPriority(0)                                                         
            self.renderTexture.addViewport( self.camera )
            self.renderTexture.getViewport(0).setOverlaysEnabled(False)
            self.renderTexture.getViewport(0).setClearEveryFrame(True)
            self.renderTexture.addListener( self.selectionTargetListener )
            self.renderTexture.getViewport(0).setMaterialScheme("aa")
        else:
            return
 
    def onSelectionClick(self, x, y):
        self.update()
 
        posInStream = (self.pBox.getWidth() * y - 1)*4
        posInStream += x*4
 
        colVec = og.Vector3(self.buffer[int(posInStream) + 2], self.buffer[int(posInStream)+1], self.buffer[int(posInStream)])
 
        for key in self.materialSwitchListener.colorDict:
            if self.materialSwitchListener.colorDict[key] == colVec:
	      return self.sceneMgr.getEntity(key)
 
        return None
 
    def createRTTOverlays(self):
        baseWhite = og.MaterialManager.getSingletonPtr().getByName("Lockenwickler_Pivot_X")
        SelectionBufferTexture = baseWhite.clone("SelectionDebugMaterial")
        textureUnit = SelectionBufferTexture.getTechnique(0).getPass(0).createTextureUnitState()
 
        textureUnit.setTextureName("SelectionPassTex")
 
 
        overlayManager = og.OverlayManager.getSingleton()
        # Create an overlay
        self.mDebugOverlay = overlayManager.create("OverlayName")
 
        # Create a panel
        panel = overlayManager.createOverlayElement("Panel", "PanelName0")
        panel.setMetricsMode(og.GMM_PIXELS)
        panel.setPosition(10, 10)
        panel.setDimensions(400, 280)
        panel.setMaterialName("SelectionDebugMaterial") 
        self.mDebugOverlay.add2D(panel)
 
        self.mDebugOverlay.show()

And here's how to use it:

self.selectionBuffer = SelectionBuffer(self.sceneManager, self.ogreRoot.getRenderTarget("OgreMainWin"))
ent = self.selectionBuffer.onSelectionClick(screenX, screenY) #maybe None or an Entity

And you will need this material for it to work correctly, put this in a .material file:

vertex_program PlainColor_VS cg
{
	source PlainColor.cg
	entry_point main_plain_color_vp
	profiles vs_1_1 arbvp1
 
	default_params
	{
		param_named_auto worldViewProj worldviewproj_matrix		
	}
 
}
 
fragment_program PlainColor_PS cg			
{
	source PlainColor.cg		
	entry_point main_plain_color_fp	
	profiles ps_1_1 arbfp1
 
	default_params
	{
		param_named inColor float4 1 1 1 1
	}
}	
 
material PlainColor
{
        // Material has one technique
	technique					
	{
                // This technique has one pass
		pass					
		{
                        // Make this pass use the vertex shader defined above
			vertex_program_ref PlainColor_VS	
			{
			}
                        // Make this pass use the pixel shader defined above
			fragment_program_ref PlainColor_PS	
			{
			      param_named_auto inColor custom 1
			}
		}
	}
}

PlainColor.cg

void main_plain_color_vp(
		// Vertex Inputs
		float4 position		: POSITION,	// Vertex position in model space
		float2 texCoord0	: TEXCOORD0,	// Texture UV set 0
 
		// Outputs
		out float4 oPosition	: POSITION,	// Transformed vertex position
		out float2 uv0		: TEXCOORD0,	// UV0
 
		// Model Level Inputs
		uniform float4x4 worldViewProj)
{
	// Calculate output position
	oPosition = mul(worldViewProj, position);
 
	// Simply copy the input vertex UV to the output
	uv0 = texCoord0;
}
 
void main_plain_color_fp(
		// Pixel Inputs
		float2 uv0		: TEXCOORD0,	// UV interpolated for current pixel	 
		// Outputs
		out float4 color	: COLOR,	// Output color we want to write
		uniform float4 inColor
		)
{	 
	color = inColor;
}
Personal tools