irrlicht/source/Irrlicht/CTextSceneNode.cpp
engineer_apple d19d6939d4 GUIText
a) SpriteBank. error check on non existing textureNumber. Seen in broken Fonts. unified getFrameNr
b) CBillboardTextSceneNode. use getOriginalSize

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6368 dfc29bdd-3216-0410-991c-e03cc46cb475
2022-05-01 09:50:32 +00:00

523 lines
13 KiB
C++

// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "CTextSceneNode.h"
#include "ISceneManager.h"
#include "IVideoDriver.h"
#include "ICameraSceneNode.h"
#include "IGUISpriteBank.h"
#include "SMeshBuffer.h"
#include "os.h"
namespace irr
{
namespace scene
{
//! constructor
CTextSceneNode::CTextSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id,
gui::IGUIFont* font, scene::ISceneCollisionManager* coll,
const core::vector3df& position, const wchar_t* text,
video::SColor color)
: ITextSceneNode(parent, mgr, id, position), Text(text), Color(color),
Font(font), Coll(coll)
{
#ifdef _DEBUG
setDebugName("CTextSceneNode");
#endif
if (Font)
Font->grab();
setAutomaticCulling(scene::EAC_OFF);
}
//! destructor
CTextSceneNode::~CTextSceneNode()
{
if (Font)
Font->drop();
}
void CTextSceneNode::OnRegisterSceneNode()
{
if (IsVisible)
SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT);
ISceneNode::OnRegisterSceneNode();
}
//! renders the node.
void CTextSceneNode::render()
{
if (!Font || !Coll)
return;
core::position2d<s32> pos = Coll->getScreenCoordinatesFrom3DPosition(getAbsolutePosition(),
SceneManager->getActiveCamera());
core::rect<s32> r(pos, core::dimension2d<s32>(1,1));
Font->draw(Text, r, Color, true, true);
}
//! returns the axis aligned bounding box of this node
const core::aabbox3d<f32>& CTextSceneNode::getBoundingBox() const
{
return Box;
}
//! sets the text string
void CTextSceneNode::setText(const wchar_t* text)
{
Text = text;
}
//! get the text string
const wchar_t* CTextSceneNode::getText() const
{
return Text.c_str();
}
//! sets the color of the text
void CTextSceneNode::setTextColor(video::SColor color)
{
Color = color;
}
//! get the color of the text
video::SColor CTextSceneNode::getTextColor() const
{
return Color;
}
void CTextSceneNode::setFont(gui::IGUIFont* font)
{
if ( font != Font )
{
if ( font )
font->grab();
if ( Font )
Font->drop();
Font = font;
}
}
//! Get the font used to draw the text
gui::IGUIFont* CTextSceneNode::getFont() const
{
return Font;
}
//!--------------------------------- CBillboardTextSceneNode ----------------------------------------------
//! constructor
CBillboardTextSceneNode::CBillboardTextSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id,
gui::IGUIFont* font,const wchar_t* text,
const core::vector3df& position, const core::dimension2d<f32>& size,
video::SColor colorTop,video::SColor shade_bottom )
: IBillboardTextSceneNode(parent, mgr, id, position),
Font(0), ColorTop(colorTop), ColorBottom(shade_bottom), Mesh(0)
{
#ifdef _DEBUG
setDebugName("CBillboardTextSceneNode");
#endif
Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
Material.MaterialTypeParam = 1.f / 255.f;
Material.BackfaceCulling = false;
Material.Lighting = false;
Material.ZBuffer = video::ECFN_LESSEQUAL;
Material.ZWriteEnable = video::EZW_OFF;
if (font)
{
// doesn't support other font types
if (font->getType() == gui::EGFT_BITMAP)
{
Font = (gui::IGUIFontBitmap*)font;
Font->grab();
// mesh with one buffer per texture
Mesh = new SMesh();
for (u32 i=0; i<Font->getSpriteBank()->getTextureCount(); ++i)
{
SMeshBuffer *mb = new SMeshBuffer();
mb->Material = Material;
mb->Material.setTexture(0, Font->getSpriteBank()->getTexture(i));
Mesh->addMeshBuffer(mb);
mb->drop();
}
}
else
{
os::Printer::log("Sorry, CBillboardTextSceneNode does not support this font type", ELL_INFORMATION);
}
}
setText(text);
setSize(size);
setAutomaticCulling ( scene::EAC_BOX );
}
CBillboardTextSceneNode::~CBillboardTextSceneNode()
{
if (Font)
Font->drop();
if (Mesh)
Mesh->drop();
}
//! sets the text string
void CBillboardTextSceneNode::setText(const wchar_t* text)
{
if ( !Mesh )
return;
Text = text;
Symbol.clear();
// clear mesh
for (u32 j=0; j < Mesh->getMeshBufferCount(); ++j)
{
((SMeshBuffer*)Mesh->getMeshBuffer(j))->Indices.clear();
((SMeshBuffer*)Mesh->getMeshBuffer(j))->Vertices.clear();
}
if (!Font)
return;
const core::array< core::rect<s32> > &sourceRects = Font->getSpriteBank()->getPositions();
const core::array< gui::SGUISprite > &sprites = Font->getSpriteBank()->getSprites();
f32 dim[2];
f32 tex[4];
u32 i;
for ( i = 0; i != Text.size (); ++i )
{
SSymbolInfo info;
u32 spriteno = Font->getSpriteNoFromChar( &text[i] );
u32 rectno = sprites[spriteno].Frames[0].rectNumber;
u32 texno = sprites[spriteno].Frames[0].textureNumber;
const core::dimension2d<u32>& texSize = Font->getSpriteBank()->getTexture(texno)->getOriginalSize();
dim[0] = core::reciprocal((f32)texSize.Width);
dim[1] = core::reciprocal((f32)texSize.Height);
const core::rect<s32>& s = sourceRects[rectno];
// add space for letter to buffer
SMeshBuffer* buf = (SMeshBuffer*)Mesh->getMeshBuffer(texno);
u32 firstInd = buf->Indices.size();
u32 firstVert = buf->Vertices.size();
buf->Indices.set_used(firstInd + 6);
buf->Vertices.set_used(firstVert + 4);
tex[0] = (s.LowerRightCorner.X * dim[0]) + 0.5f*dim[0]; // half pixel
tex[1] = (s.LowerRightCorner.Y * dim[1]) + 0.5f*dim[1];
tex[2] = (s.UpperLeftCorner.Y * dim[1]) - 0.5f*dim[1];
tex[3] = (s.UpperLeftCorner.X * dim[0]) - 0.5f*dim[0];
buf->Vertices[firstVert+0].TCoords.set(tex[0], tex[1]);
buf->Vertices[firstVert+1].TCoords.set(tex[0], tex[2]);
buf->Vertices[firstVert+2].TCoords.set(tex[3], tex[2]);
buf->Vertices[firstVert+3].TCoords.set(tex[3], tex[1]);
buf->Vertices[firstVert+0].Color = ColorBottom;
buf->Vertices[firstVert+3].Color = ColorBottom;
buf->Vertices[firstVert+1].Color = ColorTop;
buf->Vertices[firstVert+2].Color = ColorTop;
buf->Indices[firstInd+0] = (u16)firstVert+0;
buf->Indices[firstInd+1] = (u16)firstVert+2;
buf->Indices[firstInd+2] = (u16)firstVert+1;
buf->Indices[firstInd+3] = (u16)firstVert+0;
buf->Indices[firstInd+4] = (u16)firstVert+3;
buf->Indices[firstInd+5] = (u16)firstVert+2;
wchar_t *tp = 0;
if (i>0)
tp = &Text[i-1];
info.Width = (f32)s.getWidth();
info.bufNo = texno;
info.Kerning = (f32)Font->getKerningWidth(&Text[i], tp);
info.firstInd = firstInd;
info.firstVert = firstVert;
Symbol.push_back(info);
}
}
//! get the text string
const wchar_t* CBillboardTextSceneNode::getText() const
{
return Text.c_str();
}
//! pre render event
void CBillboardTextSceneNode::OnAnimate(u32 timeMs)
{
ISceneNode::OnAnimate(timeMs);
if (!IsVisible || !Font || !Mesh)
return;
ICameraSceneNode* camera = SceneManager->getActiveCamera();
if (!camera)
return;
// TODO: Risky - if camera is later in the scene-graph then it's not yet updated here
// CBillBoardSceneNode does it different, but maybe real solution would be to enforce cameras to update earlier?
// Maybe we can also unify the code by using a common base-class or having updateMesh functionality in an animator instead.
updateMesh(camera);
// mesh uses vertices with absolute coordinates so to get a bbox for culling we have to get back to local ones.
BBox = Mesh->getBoundingBox();
core::matrix4 mat( getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE );
mat.transformBoxEx(BBox);
}
const core::aabbox3d<f32>& CBillboardTextSceneNode::getTransformedBillboardBoundingBox(const irr::scene::ICameraSceneNode* camera)
{
updateMesh(camera);
return Mesh->getBoundingBox();
}
void CBillboardTextSceneNode::updateMesh(const irr::scene::ICameraSceneNode* camera)
{
// get text width
f32 textLength = 0.f;
u32 i;
for(i=0; i!=Symbol.size(); ++i)
{
SSymbolInfo &info = Symbol[i];
textLength += info.Kerning + info.Width;
}
if (textLength<0.0f)
textLength=1.0f;
//const core::matrix4 &m = camera->getViewFrustum()->Matrices[ video::ETS_VIEW ];
// make billboard look to camera
core::vector3df pos = getAbsolutePosition();
core::vector3df campos = camera->getAbsolutePosition();
core::vector3df target = camera->getTarget();
core::vector3df up = camera->getUpVector();
core::vector3df view = target - campos;
view.normalize();
core::vector3df horizontal = up.crossProduct(view);
if ( horizontal.getLength() == 0 )
{
horizontal.set(up.Y,up.X,up.Z);
}
horizontal.normalize();
core::vector3df space = horizontal;
horizontal *= 0.5f * Size.Width;
core::vector3df vertical = horizontal.crossProduct(view);
vertical.normalize();
vertical *= 0.5f * Size.Height;
view *= -1.0f;
// center text
pos += space * (Size.Width * -0.5f);
for ( i = 0; i!= Symbol.size(); ++i )
{
SSymbolInfo &info = Symbol[i];
f32 infw = info.Width / textLength;
f32 infk = info.Kerning / textLength;
f32 w = (Size.Width * infw * 0.5f);
pos += space * w;
SMeshBuffer* buf = (SMeshBuffer*)Mesh->getMeshBuffer(info.bufNo);
buf->Vertices[info.firstVert+0].Normal = view;
buf->Vertices[info.firstVert+1].Normal = view;
buf->Vertices[info.firstVert+2].Normal = view;
buf->Vertices[info.firstVert+3].Normal = view;
buf->Vertices[info.firstVert+0].Pos = pos + (space * w) + vertical;
buf->Vertices[info.firstVert+1].Pos = pos + (space * w) - vertical;
buf->Vertices[info.firstVert+2].Pos = pos - (space * w) - vertical;
buf->Vertices[info.firstVert+3].Pos = pos - (space * w) + vertical;
pos += space * (Size.Width*infk + w);
}
// make bounding box
for (i=0; i< Mesh->getMeshBufferCount() ; ++i)
Mesh->getMeshBuffer(i)->recalculateBoundingBox();
Mesh->recalculateBoundingBox();
}
void CBillboardTextSceneNode::OnRegisterSceneNode()
{
if (IsVisible && Font && Mesh)
{
SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT);
ISceneNode::OnRegisterSceneNode();
}
}
//! render
void CBillboardTextSceneNode::render()
{
if ( !Mesh )
return;
video::IVideoDriver* driver = SceneManager->getVideoDriver();
// draw
core::matrix4 mat;
driver->setTransform(video::ETS_WORLD, mat);
for (u32 i = 0; i < Mesh->getMeshBufferCount(); ++i)
{
driver->setMaterial(Mesh->getMeshBuffer(i)->getMaterial());
driver->drawMeshBuffer(Mesh->getMeshBuffer(i));
}
if ( DebugDataVisible & scene::EDS_BBOX )
{
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
video::SMaterial m;
m.Lighting = false;
driver->setMaterial(m);
driver->draw3DBox(BBox, video::SColor(0,208,195,152));
}
}
//! returns the axis aligned bounding box of this node
const core::aabbox3d<f32>& CBillboardTextSceneNode::getBoundingBox() const
{
return BBox;
}
//! sets the size of the billboard
void CBillboardTextSceneNode::setSize(const core::dimension2d<f32>& size)
{
Size = size;
if (Size.Width == 0.0f)
Size.Width = 1.0f;
if (Size.Height == 0.0f )
Size.Height = 1.0f;
//f32 avg = (size.Width + size.Height)/6;
//BBox.MinEdge.set(-avg,-avg,-avg);
//BBox.MaxEdge.set(avg,avg,avg);
}
video::SMaterial& CBillboardTextSceneNode::getMaterial(u32 i)
{
if (Mesh && Mesh->getMeshBufferCount() > i )
return Mesh->getMeshBuffer(i)->getMaterial();
else
return Material;
}
//! returns amount of materials used by this scene node.
u32 CBillboardTextSceneNode::getMaterialCount() const
{
if (Mesh)
return Mesh->getMeshBufferCount();
else
return 0;
}
//! gets the size of the billboard
const core::dimension2d<f32>& CBillboardTextSceneNode::getSize() const
{
return Size;
}
//! Get the font used to draw the text
gui::IGUIFont* CBillboardTextSceneNode::getFont() const
{
return Font;
}
//! Set the color of all vertices of the billboard
//! \param overallColor: the color to set
void CBillboardTextSceneNode::setColor(const video::SColor & overallColor)
{
if ( !Mesh )
return;
for ( u32 i = 0; i != Text.size (); ++i )
{
const SSymbolInfo &info = Symbol[i];
SMeshBuffer* buf = (SMeshBuffer*)Mesh->getMeshBuffer(info.bufNo);
buf->Vertices[info.firstVert+0].Color = overallColor;
buf->Vertices[info.firstVert+1].Color = overallColor;
buf->Vertices[info.firstVert+2].Color = overallColor;
buf->Vertices[info.firstVert+3].Color = overallColor;
}
}
//! Set the color of the top and bottom vertices of the billboard
//! \param topColor: the color to set the top vertices
//! \param bottomColor: the color to set the bottom vertices
void CBillboardTextSceneNode::setColor(const video::SColor & topColor, const video::SColor & bottomColor)
{
if ( !Mesh )
return;
ColorBottom = bottomColor;
ColorTop = topColor;
for ( u32 i = 0; i != Text.size (); ++i )
{
const SSymbolInfo &info = Symbol[i];
SMeshBuffer* buf = (SMeshBuffer*)Mesh->getMeshBuffer(info.bufNo);
buf->Vertices[info.firstVert+0].Color = ColorBottom;
buf->Vertices[info.firstVert+3].Color = ColorBottom;
buf->Vertices[info.firstVert+1].Color = ColorTop;
buf->Vertices[info.firstVert+2].Color = ColorTop;
}
}
//! Gets the color of the top and bottom vertices of the billboard
//! \param topColor: stores the color of the top vertices
//! \param bottomColor: stores the color of the bottom vertices
void CBillboardTextSceneNode::getColor(video::SColor & topColor, video::SColor & bottomColor) const
{
topColor = ColorTop;
bottomColor = ColorBottom;
}
} // end namespace scene
} // end namespace irr