History: PieSliceOverlay
Source of version: 4 (current)
Copy to clipboard
__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.
{maketoc}
!PieSliceOverlayElement
!!PieSliceOverlayElement.h
{CODE(wrap="1", colors="c++")}#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{CODE}
!!PieSliceOverlayElement.cpp
{CODE(wrap="1", colors="c++")}#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 ) );
}
}{CODE}
!!Usage
Create factory.
{CODE(wrap="1", colors="c++")}bool MyApplication::setup( void ) {
mRoot = new Root();
mPieSliceFactory = new PieSliceOverlayElementFactory();
OverlayManager::getSingleton().addOverlayElementFactory(mPieSliceFactory);
.
.
.{CODE}
Then create a normal overlay element, passing parameter for angles. NOTE: Angles need to be in radian.
{CODE(wrap="1", colors="c++")} 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();{CODE}