350 lines
9.6 KiB
C++
350 lines
9.6 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
|
|
*
|
|
* This library is open source and may be redistributed and/or modified under
|
|
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
|
|
* (at your option) any later version. The full license is in LICENSE file
|
|
* included with this distribution, and on the openscenegraph.org website.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* OpenSceneGraph Public License for more details.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
|
|
#include <osg/Billboard>
|
|
|
|
using namespace osg;
|
|
|
|
#define square(x) ((x)*(x))
|
|
|
|
Billboard::Billboard()
|
|
{
|
|
_mode = AXIAL_ROT;
|
|
_axis.set(0.0f,0.0f,1.0f);
|
|
//_normal.set(0.0f,-1.0f,0.0f);
|
|
setNormal(Vec3(0.0f,-1.0f,0.0f));
|
|
updateCache();
|
|
}
|
|
|
|
Billboard::Billboard(const Billboard& billboard,const CopyOp& copyop):
|
|
Geode(billboard,copyop),
|
|
_mode(billboard._mode),
|
|
_axis(billboard._axis),
|
|
_normal(billboard._normal),
|
|
_positionList(billboard._positionList),
|
|
_cachedMode(billboard._cachedMode),
|
|
_side(billboard._side)
|
|
{
|
|
setNormal(_normal);
|
|
}
|
|
|
|
Billboard::~Billboard()
|
|
{
|
|
}
|
|
|
|
void Billboard::setMode(Mode mode)
|
|
{
|
|
_mode = mode;
|
|
_cachedMode = CACHE_DIRTY;
|
|
updateCache();
|
|
}
|
|
|
|
void Billboard::setAxis(const Vec3& axis)
|
|
{
|
|
_axis = axis;
|
|
_axis.normalize();
|
|
updateCache();
|
|
}
|
|
|
|
void Billboard::setNormal(const Vec3& normal)
|
|
{
|
|
_normal = normal;
|
|
_normal.normalize();
|
|
updateCache();
|
|
|
|
// Build rotation from normal to z-axis,
|
|
// for use with POINT_ROT_EYE
|
|
Vec3 z(0.0, 0.0, 1.0);
|
|
Vec3 cp(z^_normal);
|
|
float dot = z*_normal;
|
|
float cp_len = cp.length();
|
|
if (cp_len != 0.0f)
|
|
{
|
|
cp /= cp_len;
|
|
float rotation_cp = acosf(dot);
|
|
Matrix rotateNormalToZ;
|
|
_rotateNormalToZAxis.makeRotate(-rotation_cp, cp);
|
|
}
|
|
else
|
|
_rotateNormalToZAxis.makeIdentity();
|
|
}
|
|
|
|
void Billboard::updateCache()
|
|
{
|
|
if (_mode==AXIAL_ROT)
|
|
{
|
|
if (_axis==Vec3(1.0f,0.0,0.0f) && _normal==Vec3(0.0f,-1.0,0.0f)) _cachedMode = AXIAL_ROT_X_AXIS;
|
|
else if (_axis==Vec3(0.0f,1.0,0.0f) && _normal==Vec3(1.0f, 0.0,0.0f)) _cachedMode = AXIAL_ROT_Y_AXIS;
|
|
else if (_axis==Vec3(0.0f,0.0,1.0f) && _normal==Vec3(0.0f,-1.0,0.0f)) _cachedMode = AXIAL_ROT_Z_AXIS;
|
|
else _cachedMode = AXIAL_ROT;
|
|
}
|
|
else if( _mode == POINT_ROT_WORLD )
|
|
{
|
|
if(_axis==Vec3(0.0f, 0.0, 1.0f) && _normal==Vec3(0.0f, -1.0f, 0.0f)) _cachedMode = POINT_ROT_WORLD_Z_AXIS;
|
|
else _cachedMode = _mode;
|
|
|
|
}
|
|
else _cachedMode = _mode;
|
|
|
|
_side = _axis^_normal;
|
|
_side.normalize();
|
|
}
|
|
|
|
bool Billboard::addDrawable(Drawable *gset)
|
|
{
|
|
if (Geode::addDrawable(gset))
|
|
{
|
|
Vec3 pos(0.0f,0.0f,0.0f);
|
|
while (_positionList.size()<_children.size())
|
|
{
|
|
_positionList.push_back(pos);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool Billboard::addDrawable(Drawable *gset,const Vec3& pos)
|
|
{
|
|
if (Geode::addDrawable(gset))
|
|
{
|
|
while (_positionList.size()<_children.size())
|
|
{
|
|
_positionList.push_back(pos);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool Billboard::removeDrawable( Drawable *gset )
|
|
{
|
|
PositionList::iterator pitr = _positionList.begin();
|
|
for (NodeList::iterator itr=_children.begin();
|
|
itr!=_children.end();
|
|
++itr,++pitr)
|
|
{
|
|
if (itr->get()==gset)
|
|
{
|
|
// note ref_ptr<> automatically handles decrementing gset's reference count.
|
|
_children.erase(itr);
|
|
_positionList.erase(pitr);
|
|
dirtyBound();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Billboard::computeMatrix(Matrix& modelview, const Vec3& eye_local, const Vec3& pos_local) const
|
|
{
|
|
//Vec3 up_local(matrix(0,1),matrix(1,1),matrix(2,1));
|
|
|
|
Matrix matrix;
|
|
|
|
Vec3 ev(eye_local-pos_local);
|
|
switch(_cachedMode)
|
|
{
|
|
case(AXIAL_ROT_Z_AXIS):
|
|
{
|
|
|
|
ev.z() = 0.0f;
|
|
float ev_length = ev.length();
|
|
if (ev_length>0.0f)
|
|
{
|
|
//float rotation_zrotation_z = atan2f(ev.x(),ev.y());
|
|
//mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f);
|
|
float inv = 1.0f/ev_length;
|
|
float s = ev.x()*inv;
|
|
float c = -ev.y()*inv;
|
|
matrix(0,0) = c;
|
|
matrix(1,0) = -s;
|
|
matrix(0,1) = s;
|
|
matrix(1,1) = c;
|
|
}
|
|
break;
|
|
}
|
|
case(AXIAL_ROT_Y_AXIS):
|
|
{
|
|
ev.y() = 0.0f;
|
|
float ev_length = ev.length();
|
|
if (ev_length>0.0f)
|
|
{
|
|
//float rotation_zrotation_z = atan2f(ev.x(),ev.y());
|
|
//mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f);
|
|
float inv = 1.0f/ev_length;
|
|
float s = -ev.z()*inv;
|
|
float c = ev.x()*inv;
|
|
matrix(0,0) = c;
|
|
matrix(2,0) = s;
|
|
matrix(0,2) = -s;
|
|
matrix(2,2) = c;
|
|
}
|
|
break;
|
|
}
|
|
case(AXIAL_ROT_X_AXIS):
|
|
{
|
|
ev.x() = 0.0f;
|
|
float ev_length = ev.length();
|
|
if (ev_length>0.0f)
|
|
{
|
|
|
|
//float rotation_zrotation_z = atan2f(ev.x(),ev.y());
|
|
//mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f);
|
|
float inv = 1.0f/ev_length;
|
|
float s = -ev.z()*inv;
|
|
float c = -ev.y()*inv;
|
|
matrix(1,1) = c;
|
|
matrix(2,1) = -s;
|
|
matrix(1,2) = s;
|
|
matrix(2,2) = c;
|
|
}
|
|
break;
|
|
}
|
|
case(AXIAL_ROT): // need to implement
|
|
{
|
|
float ev_side = ev*_side;
|
|
float ev_normal = ev*_normal;
|
|
float rotation = atan2f(ev_side,ev_normal);
|
|
matrix.makeRotate(rotation,_axis);
|
|
break;
|
|
}
|
|
case(POINT_ROT_WORLD):
|
|
{
|
|
float ev_len = ev.length();
|
|
if (ev_len != 0.0f)
|
|
{
|
|
ev /= ev_len;
|
|
|
|
Vec3 cp(ev^_normal);
|
|
float dot = ev*_normal;
|
|
|
|
float cp_len = cp.length();
|
|
if (cp_len != 0.0f)
|
|
{
|
|
cp /= cp_len;
|
|
|
|
float rotation_cp = acosf(dot);
|
|
matrix.makeRotate(-inRadians(rotation_cp),cp[0],cp[1],cp[2]);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case(POINT_ROT_EYE):
|
|
{
|
|
float ev_len = ev.length();
|
|
if (ev_len != 0.0f)
|
|
{
|
|
ev /= ev_len;
|
|
|
|
Vec3 up(modelview(0,1), modelview(1,1), modelview(2,1));
|
|
Vec3 right(up^ev);
|
|
right.normalize();
|
|
up = ev^right;
|
|
up.normalize();
|
|
|
|
matrix(0,0) = right.x();
|
|
matrix(0,1) = right.y();
|
|
matrix(0,2) = right.z();
|
|
matrix(1,0) = up.x();
|
|
matrix(1,1) = up.y();
|
|
matrix(1,2) = up.z();
|
|
matrix(2,0) = ev.x();
|
|
matrix(2,1) = ev.y();
|
|
matrix(2,2) = ev.z();
|
|
|
|
matrix.preMult(_rotateNormalToZAxis);
|
|
}
|
|
break;
|
|
}
|
|
case(POINT_ROT_WORLD_Z_AXIS):
|
|
{
|
|
// float rotation_about_z = atan2( -ev.y(), ev.x() );
|
|
// float xy_distance = sqrt( ev.x()*ev.x() + ev.y()*ev.y() );
|
|
// float rotation_from_xy = atan2( xy_distance, -ev.z() );
|
|
|
|
Vec2 about_z( -ev.y(), ev.x() );
|
|
if( about_z.normalize() == 0.0f ) about_z.x() = 1.0f;
|
|
float xy_distance = sqrt( ev.x()*ev.x() + ev.y()*ev.y() );
|
|
Vec2 from_xy( xy_distance, -ev.z() );
|
|
if( from_xy.normalize() == 0.0f ) from_xy.x() = 1.0f;
|
|
|
|
matrix(0,0) = about_z.x();
|
|
matrix(0,1) = about_z.y();
|
|
matrix(1,0) = -about_z.y()*from_xy.x();
|
|
matrix(1,1) = about_z.x()*from_xy.x();
|
|
matrix(1,2) = from_xy.y();
|
|
matrix(2,0) = about_z.y()*from_xy.y();
|
|
matrix(2,1) = -about_z.x()*from_xy.y();
|
|
matrix(2,2) = from_xy.x();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
matrix.setTrans(pos_local);
|
|
|
|
modelview.preMult(matrix);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
BoundingSphere Billboard::computeBound() const
|
|
{
|
|
int i;
|
|
int ngsets = _children.size();
|
|
|
|
if( ngsets == 0 ) return BoundingSphere();
|
|
|
|
BoundingSphere bsphere;
|
|
bsphere._center.set(0.0f,0.0f,0.0f);
|
|
|
|
for( i = 0; i < ngsets; i++ )
|
|
{
|
|
const Drawable* drawable = _children[i].valid() ? _children[i]->asDrawable() : 0;
|
|
if (drawable)
|
|
{
|
|
const BoundingBox& bbox = drawable->getBoundingBox();
|
|
|
|
bsphere._center += bbox.center();
|
|
bsphere._center += _positionList[i];
|
|
}
|
|
}
|
|
|
|
bsphere._center /= (float)(ngsets);
|
|
|
|
float maxd = 0.0;
|
|
for( i = 0; i < ngsets; ++i )
|
|
{
|
|
const Drawable* drawable = _children[i].valid() ? _children[i]->asDrawable() : 0;
|
|
if (drawable)
|
|
{
|
|
const BoundingBox& bbox = drawable->getBoundingBox();
|
|
Vec3 local_center = bsphere._center-_positionList[i];
|
|
for(unsigned int c=0;c<8;++c)
|
|
{
|
|
float d = (bbox.corner(c)-local_center).length2();
|
|
if( d > maxd ) maxd = d;
|
|
}
|
|
}
|
|
}
|
|
bsphere._radius = sqrtf(maxd);
|
|
|
|
return bsphere;
|
|
}
|