CodeSnippets Selection Buffer
From PyWiki
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; }
