irrlicht/source/Irrlicht/CBillboardSceneNode.cpp
cutealien 91f281229b Fix: Make CBillboardSceneNode bounding-box large enough to fit the billboard inside.
It still won't work yet for scaled boundingboxes (or parents being scaled). 
But at least it's now large enough for typical unscaled boundingboxes.
Before it was always too small - even for the simplest quadratic billboard case seen without rotation.
Now it's always a bit too large, but that's way less of a problem (collisions still work and culling simply happens a bit less often, but not too often which is way worse)

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6431 dfc29bdd-3216-0410-991c-e03cc46cb475
2022-09-29 14:12:12 +00:00

322 lines
8.6 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 "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<f32>& 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<video::S3DVertex>& 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<f32>& CBillboardSceneNode::getBoundingBox() const
{
// Really wrong when scaled (as the node does not scale it's vertices - maybe it should?)
return BBoxSafe;
}
const core::aabbox3d<f32>& CBillboardSceneNode::getTransformedBillboardBoundingBox(const irr::scene::ICameraSceneNode* camera)
{
updateMesh(camera);
return Buffer->BoundingBox;
}
void CBillboardSceneNode::setSize(const core::dimension2d<f32>& 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<f32>& 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_