forked from Mirrorlandia_minetest/irrlicht
91f281229b
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
322 lines
8.6 KiB
C++
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_
|