PieSliceOverlay         A plugin overlay element for creating pie slices.

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.

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();