// 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 pos = Coll->getScreenCoordinatesFrom3DPosition(getAbsolutePosition(), SceneManager->getActiveCamera()); core::rect r(pos, core::dimension2d(1,1)); Font->draw(Text, r, Color, true, true); } //! returns the axis aligned bounding box of this node const core::aabbox3d& 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& 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; igetSpriteBank()->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 > &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; dim[0] = core::reciprocal ( (f32) Font->getSpriteBank()->getTexture(texno)->getSize().Width ); dim[1] = core::reciprocal ( (f32) Font->getSpriteBank()->getTexture(texno)->getSize().Height ); const core::rect& 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& 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& CBillboardTextSceneNode::getBoundingBox() const { return BBox; } //! sets the size of the billboard void CBillboardTextSceneNode::setSize(const core::dimension2d& 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& 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