Note: This code is hack of OgreBorderPanelOverlayElement and the OgreTextAreaOverlayElement. As such it may contain unnecessary code from these or may lack code which ogre needs. Use with caution!
Also uses a lot of trigonometry functions so probably not the fastest method.
Table of contents
PieSliceOverlayElement
PieSliceOverlayElement.h
#ifndef _PieSliceOverlayElement_H__ #define _PieSliceOverlayElement_H__ #include "OgreOverlayElement.h" #include "OgreOverlayElementFactory.h" #include "OgreFont.h" namespace Ogre { class _OgreExport PieSliceOverlayElement : public OverlayElement { public: /** Constructor. */ PieSliceOverlayElement(const String& name); virtual ~PieSliceOverlayElement(); virtual void initialise(void); void setStartAngle( Real angle ); Real getStartAngle() const; void setEndAngle( Real angle ); Real getEndAngle() const; /** See OverlayElement. */ virtual const String& getTypeName(void) const; /** See Renderable. */ void getRenderOperation(RenderOperation& op); /** Overridden from OverlayElement */ void setMaterialName(const String& matName); /** Overridden from OverlayElement */ void setMetricsMode(GuiMetricsMode gmm); /** Overridden from OverlayElement */ void _update(void); //----------------------------------------------------------------------------------------- /** Command object for setting the Start Angle. @see ParamCommand */ class CmdStartAngle : public ParamCommand { public: String doGet( const void* target ) const; void doSet( void* target, const String& val ); }; //----------------------------------------------------------------------------------------- /** Command object for setting the End Angle. @see ParamCommand */ class CmdEndAngle : public ParamCommand { public: String doGet( const void* target ) const; void doSet( void* target, const String& val ); }; protected: /// Flag indicating if this panel should be visual or just group things bool mTransparent; /// Render operation RenderOperation mRenderOp; /// Method for setting up base parameters for this class void addBaseParameters(void); static String msTypeName; static CmdStartAngle msCmdStartAngle; static CmdEndAngle msCmdEndAngle; /// Pie Slice angles Real mStartAngle; Real mEndAngle; /// Inherited function virtual void updatePositionGeometry(); /// Inherited function virtual void updateTextureGeometry(); }; /** Factory for creating PieSliceOverlayElement instances. */ class _OgreExport PieSliceOverlayElementFactory: public OverlayElementFactory { public: /** See OverlayElementFactory */ OverlayElement* createOverlayElement(const String& instanceName) { return new PieSliceOverlayElement(instanceName); } /** See OverlayElementFactory */ const String& getTypeName(void) const { static String name = "PieSlice"; return name; } }; } #endif
PieSliceOverlayElement.cpp
#include "OgreStableHeaders.h" #include "PieSliceOverlayElement.h" #include "OgreRoot.h" #include "OgreLogManager.h" #include "OgreOverlayManager.h" #include "OgreHardwareBufferManager.h" #include "OgreHardwareVertexBuffer.h" #include "OgreException.h" #include "OgreStringConverter.h" #include "OgreRenderSystem.h" #include "OgreMath.h" namespace Ogre { //--------------------------------------------------------------------- String PieSliceOverlayElement::msTypeName = "PieSlice"; PieSliceOverlayElement::CmdStartAngle PieSliceOverlayElement::msCmdStartAngle; PieSliceOverlayElement::CmdEndAngle PieSliceOverlayElement::msCmdEndAngle; //--------------------------------------------------------------------- #define POS_TEX_BINDING 0 //--------------------------------------------------------------------- PieSliceOverlayElement::PieSliceOverlayElement(const String& name) : OverlayElement(name) { mTransparent = false; mStartAngle = 0.0f; mEndAngle = Math::TWO_PI; if (createParamDictionary("PieSliceOverlayElement")) { addBaseParameters(); } } void PieSliceOverlayElement::initialise(void) { if (!mInitialised) { // Set up the render op // Combine positions and texture coords since they tend to change together // since character sizes are different mRenderOp.vertexData = new VertexData(); mRenderOp.vertexData->vertexCount = 5; // 8 cells, can't necessarily share vertices cos // texcoords may differ mRenderOp.vertexData->vertexStart = 0; size_t offset = 0; // Positions VertexDeclaration* decl = mRenderOp.vertexData->vertexDeclaration; decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT3, VES_POSITION); offset += VertexElement::getTypeSize(VET_FLOAT3); // Texcoords decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); offset += VertexElement::getTypeSize(VET_FLOAT2); // Vertex buffer position and tex HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton(). createVertexBuffer( decl->getVertexSize(POS_TEX_BINDING), mRenderOp.vertexData->vertexCount, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); VertexBufferBinding* bind = mRenderOp.vertexData->vertexBufferBinding; bind->setBinding(POS_TEX_BINDING, vbuf); mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST; mRenderOp.useIndexes = true; // Index data mRenderOp.indexData = new IndexData(); mRenderOp.indexData->indexCount = 4 * 3; mRenderOp.indexData->indexStart = 0; mRenderOp.indexData->indexBuffer = HardwareBufferManager::getSingleton(). createIndexBuffer( HardwareIndexBuffer::IT_16BIT, mRenderOp.indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY); ushort* pIdx = static_cast<ushort*>( mRenderOp.indexData->indexBuffer->lock( 0, mRenderOp.indexData->indexBuffer->getSizeInBytes(), HardwareBuffer::HBL_DISCARD) ); /* 0-----1 |\ /| | \ / | | 2 | | / \ | |/ \| 4-----3 */ *pIdx++ = 0; *pIdx++ = 2; *pIdx++ = 1; *pIdx++ = 2; *pIdx++ = 3; *pIdx++ = 1; *pIdx++ = 3; *pIdx++ = 2; *pIdx++ = 4; *pIdx++ = 4; *pIdx++ = 2; *pIdx++ = 0; mRenderOp.indexData->indexBuffer->unlock(); mInitialised = true; } } void PieSliceOverlayElement::updatePositionGeometry() { /* +-----+ +-----+ |\ 0 /| |\B B/| | \ / | |D\A/D| |3 : 1| | C:C | | / \ | |D/ \D| |/ 2 \| |/D D\| +-----+ +-----+ */ int startSeg, endSeg; Radian startAngle, endAngle,tempAngle; Radian aA,aB,aC,aD; Real sideC; Real left, right, top, bottom, centerX, centerY; left = _getDerivedLeft() * 2 - 1; right = left + (mWidth * 2); top = -((_getDerivedTop() * 2) - 1); bottom = top - (mHeight * 2); centerX = left + (right - left)/2; centerY = top + (bottom - top)/2; aB = Math::ATan((top - bottom)/(right - left)); aA = Radian(Math::PI) - aB -aB; aD = Radian(Math::HALF_PI) - aB; aC = Radian(Math::PI) - aA; sideC = Math::Sqrt(Math::Sqr(bottom - top) + Math::Sqr(right - left))/2.0f; startAngle = Radian(mStartAngle) + (aA/2.0); if (startAngle >= Radian(Math::TWO_PI)) startAngle -= Radian(Math::TWO_PI); endAngle = Radian(mEndAngle) + (aA/2.0); if (endAngle >= Radian(Math::TWO_PI)) endAngle -= Radian(Math::TWO_PI); if (startAngle < aA) startSeg = 0; else { if (startAngle < aA+aC) startSeg = 1; else { if (startAngle < aA+aC+aA) startSeg = 2; else { startSeg = 3; } } } if (endAngle < aA) endSeg = 0; else { if (endAngle < aA+aC) endSeg = 1; else { if (endAngle < aA+aC+aA) endSeg = 2; else { endSeg = 3; } } } // Set up the render op // Combine positions and texture coords since they tend to change together // since character sizes are different mRenderOp.vertexData = new VertexData(); if (startAngle>=endAngle) { if (startSeg == endSeg) mRenderOp.vertexData->vertexCount = 7; else { mRenderOp.vertexData->vertexCount = 3; int currentSeg = startSeg; while (currentSeg != endSeg) { mRenderOp.vertexData->vertexCount++; currentSeg++; currentSeg = currentSeg%4; } } } else { if (startSeg == endSeg) mRenderOp.vertexData->vertexCount = 3; else { mRenderOp.vertexData->vertexCount = 3; int currentSeg = startSeg; while (currentSeg != endSeg) { mRenderOp.vertexData->vertexCount++; currentSeg++; currentSeg = currentSeg%4; } } } mRenderOp.vertexData->vertexStart = 0; size_t offset = 0; // Positions VertexDeclaration* decl = mRenderOp.vertexData->vertexDeclaration; decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT3, VES_POSITION); offset += VertexElement::getTypeSize(VET_FLOAT3); // Texcoords decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); offset += VertexElement::getTypeSize(VET_FLOAT2); // Vertex buffer position and tex HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton(). createVertexBuffer( decl->getVertexSize(POS_TEX_BINDING), mRenderOp.vertexData->vertexCount, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); VertexBufferBinding* bind = mRenderOp.vertexData->vertexBufferBinding; bind->setBinding(POS_TEX_BINDING, vbuf); float* pPos = static_cast<float*>( vbuf->lock(HardwareBuffer::HBL_DISCARD) ); // Use the furthest away depth value, since materials should have depth-check off // This initialised the depth buffer for any 3D objects in front Real zValue = Root::getSingleton().getRenderSystem()->getMaximumDepthInputValue(); *pPos++ = centerX; *pPos++ = centerY; *pPos++ = zValue; *pPos++ = 0.5; *pPos++ = 0.5; // calc start values here Real startX,startY,startU,startV,sideA; Radian angle; switch(startSeg) { case 0 : { angle = startAngle; sideA = (Math::Sin(angle)*sideC)/Math::Sin(Radian(Math::PI)-angle-aB); startX = sideA+left; startY = top; startU = sideA/(right - left); startV = 0.0; break; } case 1 : { angle = startAngle-aA; sideA = (Math::Sin(angle)*sideC)/Math::Sin(Radian(Math::PI)-angle-aD); startX = right; startY = top-sideA; startU = 1.0; startV = sideA/(top-bottom); break; } case 2 : { angle = startAngle-aA-aC; sideA = (Math::Sin(angle)*sideC)/Math::Sin(Radian(Math::PI)-angle-aB); startX = right-sideA; startY = bottom; startU = 1.0-(sideA/(right - left)); startV = 1.0; break; } case 3 : { angle = startAngle-aA-aC-aA; sideA = (Math::Sin(angle)*sideC)/Math::Sin(Radian(Math::PI)-angle-aD); startX = left; startY = bottom+sideA; startU = 0.0; startV = 1.0-(sideA/(top-bottom)); break; } } *pPos++ = startX; *pPos++ = startY; *pPos++ = zValue; *pPos++ = startU; *pPos++ = startV; int currentSeg = startSeg; int segCount = 1; bool largeSlice = false; if(startAngle >= endAngle) largeSlice = true; while (currentSeg != endSeg || largeSlice) { largeSlice = false; switch (currentSeg) { case 0 : { *pPos++ = right; *pPos++ = top; *pPos++ = zValue; *pPos++ = 1.0; *pPos++ = 0.0; break; } case 1 : { *pPos++ = right; *pPos++ = bottom; *pPos++ = zValue; *pPos++ = 1.0; *pPos++ = 1.0; break; } case 2 : { *pPos++ = left; *pPos++ = bottom; *pPos++ = zValue; *pPos++ = 0.0; *pPos++ = 1.0; break; } case 3 : { *pPos++ = left; *pPos++ = top; *pPos++ = zValue; *pPos++ = 0.0; *pPos++ = 0.0; break; } } currentSeg++; currentSeg = currentSeg % 4; segCount++; } // calc start values here Real endX,endY,endU,endV; switch(endSeg) { case 0 : { angle = endAngle; sideA = (Math::Sin(angle)*sideC)/Math::Sin(Radian(Math::PI)-angle-aB); endX = sideA+left; endY = top; endU = sideA/(right - left); endV = 0.0; break; } case 1 : { angle = endAngle-aA; sideA = (Math::Sin(angle)*sideC)/Math::Sin(Radian(Math::PI)-angle-aD); endX = right; endY = top-sideA; endU = 1.0; endV = sideA/(top-bottom); break; } case 2 : { angle = endAngle-aA-aC; sideA = (Math::Sin(angle)*sideC)/Math::Sin(Radian(Math::PI)-angle-aB); endX = right-sideA; endY = bottom; endU = 1.0-(sideA/(right - left)); endV = 1.0; break; } case 3 : { angle = endAngle-aA-aC-aA; sideA = (Math::Sin(angle)*sideC)/Math::Sin(Radian(Math::PI)-angle-aD); endX = left; endY = bottom+sideA; endU = 0.0; endV = 1.0-(sideA/(top-bottom)); break; } } *pPos++ = endX; *pPos++ = endY; *pPos++ = zValue; *pPos++ = endU; *pPos++ = endV; vbuf->unlock(); mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST; mRenderOp.useIndexes = true; // Index data mRenderOp.indexData = new IndexData(); mRenderOp.indexData->indexCount = (segCount+1)*3; mRenderOp.indexData->indexStart = 0; mRenderOp.indexData->indexBuffer = HardwareBufferManager::getSingleton(). createIndexBuffer( HardwareIndexBuffer::IT_16BIT, mRenderOp.indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY); ushort* pIdx = static_cast<ushort*>( mRenderOp.indexData->indexBuffer->lock( 0, mRenderOp.indexData->indexBuffer->getSizeInBytes(), HardwareBuffer::HBL_DISCARD) ); /* 5-6-1-2 |\ /| | \ / | | 0 | | / \ | |/ \| 4-----3 */ if (segCount>0) { *pIdx++ = 0; *pIdx++ = 2; *pIdx++ = 1; } if (segCount>1) { *pIdx++ = 0; *pIdx++ = 3; *pIdx++ = 2; } if (segCount>2) { *pIdx++ = 0; *pIdx++ = 4; *pIdx++ = 3; } if (segCount>3) { *pIdx++ = 0; *pIdx++ = 5; *pIdx++ = 4; } if (segCount>4) { *pIdx++ = 0; *pIdx++ = 6; *pIdx++ = 5; } mRenderOp.indexData->indexBuffer->unlock(); } void PieSliceOverlayElement::updateTextureGeometry() { // Nothing to do, we combine positions and textures } void PieSliceOverlayElement::setStartAngle( Real angle ) { mStartAngle = angle; mGeomPositionsOutOfDate = true; } Real PieSliceOverlayElement::getStartAngle() const { return mStartAngle; } void PieSliceOverlayElement::setEndAngle( Real angle ) { mEndAngle = angle; mGeomPositionsOutOfDate = true; } Real PieSliceOverlayElement::getEndAngle() const { return mEndAngle; } //--------------------------------------------------------------------- PieSliceOverlayElement::~PieSliceOverlayElement() { delete mRenderOp.vertexData; } //--------------------------------------------------------------------- const String& PieSliceOverlayElement::getTypeName(void) const { return msTypeName; } //--------------------------------------------------------------------- void PieSliceOverlayElement::getRenderOperation(RenderOperation& op) { op = mRenderOp; } //--------------------------------------------------------------------- void PieSliceOverlayElement::setMaterialName(const String& matName) { OverlayElement::setMaterialName(matName); } //--------------------------------------------------------------------- void PieSliceOverlayElement::addBaseParameters(void) { OverlayElement::addBaseParameters(); ParamDictionary* dict = getParamDictionary(); dict->addParameter(ParameterDef("start_angle", "Sets the start angle." , PT_REAL), &msCmdStartAngle); dict->addParameter(ParameterDef("end_angle", "Sets the end angle." , PT_REAL), &msCmdEndAngle); } //----------------------------------------------------------------------- void PieSliceOverlayElement::setMetricsMode(GuiMetricsMode gmm) { Real vpWidth, vpHeight; vpWidth = (Real) (OverlayManager::getSingleton().getViewportWidth()); vpHeight = (Real) (OverlayManager::getSingleton().getViewportHeight()); OverlayElement::setMetricsMode(gmm); } //----------------------------------------------------------------------- void PieSliceOverlayElement::_update(void) { Real vpWidth, vpHeight; vpWidth = (Real) (OverlayManager::getSingleton().getViewportWidth()); vpHeight = (Real) (OverlayManager::getSingleton().getViewportHeight()); if (mMetricsMode != GMM_RELATIVE && (OverlayManager::getSingleton().hasViewportChanged() || mGeomPositionsOutOfDate)) { // Recalc character size mGeomPositionsOutOfDate = true; } OverlayElement::_update(); } //--------------------------------------------------------------------------------------------- // StartAngle command object // String PieSliceOverlayElement::CmdStartAngle::doGet( const void* target ) const { return StringConverter::toString( static_cast< const PieSliceOverlayElement* >( target )->getStartAngle() ); } void PieSliceOverlayElement::CmdStartAngle::doSet( void* target, const String& val ) { static_cast< PieSliceOverlayElement* >( target )->setStartAngle( StringConverter::parseReal( val ) ); } //--------------------------------------------------------------------------------------------- // EndAngle command object // String PieSliceOverlayElement::CmdEndAngle::doGet( const void* target ) const { return StringConverter::toString( static_cast< const PieSliceOverlayElement* >( target )->getEndAngle() ); } void PieSliceOverlayElement::CmdEndAngle::doSet( void* target, const String& val ) { static_cast< PieSliceOverlayElement* >( target )->setEndAngle( StringConverter::parseReal( val ) ); } }
Usage
Create factory.
bool MyApplication::setup( void ) { mRoot = new Root(); mPieSliceFactory = new PieSliceOverlayElementFactory(); OverlayManager::getSingleton().addOverlayElementFactory(mPieSliceFactory); . . .
Then create a normal overlay element, passing parameter for angles. NOTE: Angles need to be in radian.
Overlay* PieSliceOverlay = OverlayManager::getSingleton().create("PieSliceOverlay"); PSpecialOverlay->setZOrder(2); NewPanel = static_cast<OverlayContainer*>(OverlayManager::getSingleton().createOverlayElement ("PieSlice", "Minority")); NewPanel->setMaterialName("PieChart/Blue"); NewPanel->setPosition(0.1f,0.1f); NewPanel->setWidth(0.8f); NewPanel->setHeight(0.8f); NewPanel->setParameter("start_angle", StringConverter::toString(Math::fDeg2Rad * 0.0f)); NewPanel->setParameter("end_angle", StringConverter::toString(Math::fDeg2Rad * 60.0f)); PSpecialOverlay->add2D(NewPanel); NewPanel = static_cast<OverlayContainer*>(OverlayManager::getSingleton().createOverlayElement ("PieSlice", "Magority")); NewPanel->setMaterialName("PieChart/Red"); NewPanel->setPosition(0.1f,0.1f); NewPanel->setWidth(0.8f); NewPanel->setHeight(0.8f); NewPanel->setParameter("start_angle", StringConverter::toString(Math::fDeg2Rad * 60.0f)); NewPanel->setParameter("end_angle", StringConverter::toString(Math::fDeg2Rad * 360.0f)); PSpecialOverlay->add2D(NewPanel); PSpecialOverlay->show();