// 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 "IrrCompileConfig.h" #ifdef _IRR_COMPILE_WITH_BILLBOARD_SCENENODE_ #include "CBillboardSceneNode.h" #include "IVideoDriver.h" #include "ISceneManager.h" #include "ICameraSceneNode.h" #include "os.h" namespace irr { namespace scene { //! constructor CBillboardSceneNode::CBillboardSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, const core::vector3df& position, const core::dimension2d& size, video::SColor colorTop, video::SColor colorBottom) : IBillboardSceneNode(parent, mgr, id, position) , Buffer(new SMeshBuffer()) { #ifdef _DEBUG setDebugName("CBillboardSceneNode"); #endif setSize(size); Buffer->Vertices.set_used(4); Buffer->Indices.set_used(6); Buffer->Indices[0] = 0; Buffer->Indices[1] = 2; Buffer->Indices[2] = 1; Buffer->Indices[3] = 0; Buffer->Indices[4] = 3; Buffer->Indices[5] = 2; Buffer->Vertices[0].TCoords.set(1.0f, 1.0f); Buffer->Vertices[0].Color = colorBottom; Buffer->Vertices[1].TCoords.set(1.0f, 0.0f); Buffer->Vertices[1].Color = colorTop; Buffer->Vertices[2].TCoords.set(0.0f, 0.0f); Buffer->Vertices[2].Color = colorTop; Buffer->Vertices[3].TCoords.set(0.0f, 1.0f); Buffer->Vertices[3].Color = colorBottom; } CBillboardSceneNode::~CBillboardSceneNode() { Buffer->drop(); } //! pre render event void CBillboardSceneNode::OnRegisterSceneNode() { if (IsVisible) SceneManager->registerNodeForRendering(this); ISceneNode::OnRegisterSceneNode(); } //! render void CBillboardSceneNode::render() { video::IVideoDriver* driver = SceneManager->getVideoDriver(); ICameraSceneNode* camera = SceneManager->getActiveCamera(); if (!camera || !driver) return; // make billboard look to camera updateMesh(camera); driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); driver->setMaterial(Buffer->Material); driver->drawMeshBuffer(Buffer); if (DebugDataVisible & scene::EDS_BBOX) { driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); video::SMaterial m; m.Lighting = false; driver->setMaterial(m); driver->draw3DBox(BBoxSafe, video::SColor(0,208,195,152)); } } void CBillboardSceneNode::updateMesh(const irr::scene::ICameraSceneNode* camera) { // billboard looks toward 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 topHorizontal = horizontal * 0.5f * TopEdgeWidth; horizontal *= 0.5f * Size.Width; // pointing down! core::vector3df vertical = horizontal.crossProduct(view); vertical.normalize(); vertical *= 0.5f * Size.Height; view *= -1.0f; core::array& vertices = Buffer->Vertices; for (s32 i=0; i<4; ++i) vertices[i].Normal = view; /* Vertices are: 2--1 |\ | | \| 3--0 */ vertices[0].Pos = pos + horizontal + vertical; vertices[1].Pos = pos + topHorizontal - vertical; vertices[2].Pos = pos - topHorizontal - vertical; vertices[3].Pos = pos - horizontal + vertical; Buffer->setDirty(EBT_VERTEX); Buffer->recalculateBoundingBox(); } //! returns the axis aligned bounding box of this node const core::aabbox3d& CBillboardSceneNode::getBoundingBox() const { // Really wrong when scaled (as the node does not scale it's vertices - maybe it should?) return BBoxSafe; } const core::aabbox3d& CBillboardSceneNode::getTransformedBillboardBoundingBox(const irr::scene::ICameraSceneNode* camera) { updateMesh(camera); return Buffer->BoundingBox; } void CBillboardSceneNode::setSize(const core::dimension2d& size) { Size = size; if (core::equals(Size.Width, 0.0f)) Size.Width = 1.0f; TopEdgeWidth = Size.Width; if (core::equals(Size.Height, 0.0f)) Size.Height = 1.0f; const f32 extent = 0.5f*sqrt(Size.Width*Size.Width + Size.Height*Size.Height); BBoxSafe.MinEdge.set(-extent,-extent,-extent); BBoxSafe.MaxEdge.set(extent,extent,extent); } void CBillboardSceneNode::setSize(f32 height, f32 bottomEdgeWidth, f32 topEdgeWidth) { Size.set(bottomEdgeWidth, height); TopEdgeWidth = topEdgeWidth; if (core::equals(Size.Height, 0.0f)) Size.Height = 1.0f; if (core::equals(Size.Width, 0.f) && core::equals(TopEdgeWidth, 0.f)) { Size.Width = 1.0f; TopEdgeWidth = 1.0f; } const f32 extent = 0.5f*sqrt(Size.Width*Size.Width + Size.Height*Size.Height); BBoxSafe.MinEdge.set(-extent,-extent,-extent); BBoxSafe.MaxEdge.set(extent,extent,extent); } video::SMaterial& CBillboardSceneNode::getMaterial(u32 i) { return Buffer->Material; } //! returns amount of materials used by this scene node. u32 CBillboardSceneNode::getMaterialCount() const { return 1; } //! gets the size of the billboard const core::dimension2d& CBillboardSceneNode::getSize() const { return Size; } //! Gets the widths of the top and bottom edges of the billboard. void CBillboardSceneNode::getSize(f32& height, f32& bottomEdgeWidth, f32& topEdgeWidth) const { height = Size.Height; bottomEdgeWidth = Size.Width; topEdgeWidth = TopEdgeWidth; } //! Writes attributes of the scene node. void CBillboardSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const { IBillboardSceneNode::serializeAttributes(out, options); out->addFloat("Width", Size.Width); out->addFloat("TopEdgeWidth", TopEdgeWidth); out->addFloat("Height", Size.Height); out->addColor("Shade_Top", Buffer->Vertices[1].Color); out->addColor("Shade_Down", Buffer->Vertices[0].Color); } //! Reads attributes of the scene node. void CBillboardSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) { IBillboardSceneNode::deserializeAttributes(in, options); Size.Width = in->getAttributeAsFloat("Width"); Size.Height = in->getAttributeAsFloat("Height"); if (in->existsAttribute("TopEdgeWidth")) { TopEdgeWidth = in->getAttributeAsFloat("TopEdgeWidth"); if (Size.Width != TopEdgeWidth) setSize(Size.Height, Size.Width, TopEdgeWidth); } else setSize(Size); Buffer->Vertices[1].Color = in->getAttributeAsColor("Shade_Top"); Buffer->Vertices[0].Color = in->getAttributeAsColor("Shade_Down"); Buffer->Vertices[2].Color = Buffer->Vertices[1].Color; Buffer->Vertices[3].Color = Buffer->Vertices[0].Color; } //! Set the color of all vertices of the billboard //! \param overallColor: the color to set void CBillboardSceneNode::setColor(const video::SColor& overallColor) { for(u32 vertex = 0; vertex < 4; ++vertex) Buffer->Vertices[vertex].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 CBillboardSceneNode::setColor(const video::SColor& topColor, const video::SColor& bottomColor) { Buffer->Vertices[0].Color = bottomColor; Buffer->Vertices[1].Color = topColor; Buffer->Vertices[2].Color = topColor; Buffer->Vertices[3].Color = bottomColor; } //! Gets the color of the top and bottom vertices of the billboard //! \param[out] topColor: stores the color of the top vertices //! \param[out] bottomColor: stores the color of the bottom vertices void CBillboardSceneNode::getColor(video::SColor& topColor, video::SColor& bottomColor) const { bottomColor = Buffer->Vertices[0].Color; topColor = Buffer->Vertices[1].Color; } //! Creates a clone of this scene node and its children. ISceneNode* CBillboardSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager) { if (!newParent) newParent = Parent; if (!newManager) newManager = SceneManager; CBillboardSceneNode* nb = new CBillboardSceneNode(newParent, newManager, ID, RelativeTranslation, Size); nb->cloneMembers(this, newManager); nb->Buffer->Material = Buffer->Material; nb->Size = Size; nb->TopEdgeWidth = this->TopEdgeWidth; video::SColor topColor,bottomColor; getColor(topColor,bottomColor); nb->setColor(topColor,bottomColor); if ( newParent ) nb->drop(); return nb; } } // end namespace scene } // end namespace irr #endif // _IRR_COMPILE_WITH_BILLBOARD_SCENENODE_