Integrated detailed near clipping plane calculation into osgUtil::CullVisitor,

submitted by Sasa Bistroviae.
This commit is contained in:
Robert Osfield 2001-12-16 22:20:26 +00:00
parent 786dfea3c8
commit f5873a82c5
4 changed files with 521 additions and 20 deletions

10
NEWS
View File

@ -16,7 +16,7 @@ OSG News (most significant items from ChangeLog)
library which provides support for true type font rendering, as textured,
polygonal, pixmap, bitmap and 3D fonts.
An new osg::Matrix class has been implemented which cleans its interface.
We now have new osg::Matrix implementation which cleans up its interface.
Also all angular paramters are now based on radians rather than degrees
as before (and as in Performer), this has been done to be more consistent
with the basic trignometric functions and standard maths conventions.
@ -39,10 +39,10 @@ OSG News (most significant items from ChangeLog)
src/Demos/osgcluster has been added to demonstrate how easy it is
to set up a graphics cluster across a standard networked PC's.
There have been many minor code changes to fix bugs, bottlenecks or
hinderances to extensibility have been addressed, there are too many
to list, but it does represent a significant step towards API maturity
and a beta release which is now not far away.
There have been many minor code changes to fix bugs and address
performance bottlenecks or to improve extensibility was, representing
a significant step towards API maturity and a beta release which is
now not far away.
9th August 2001 - osg-0.8.42.tar.gz

View File

@ -24,6 +24,8 @@
#include <map>
#include <vector>
#include <osg/Vec3>
namespace osgUtil {
/**
@ -182,6 +184,16 @@ class OSGUTIL_EXPORT CullVisitor : public osg::NodeVisitor
const float getCalculatedFarPlane() const { return _calculated_zfar; }
//SandB
/**sets the flag for detailed culling*/
void setDetailedCulling(bool detailed) {_detailedCulling = detailed;}
/**gets the status of detailed culling*/
const bool getDetailedCulling() const {return _detailedCulling;}
/**calculates unit directions of vectors that are intersections of cameras' clipping planes*/
void calcClippingDirections() const;
protected:
/** prevent unwanted copy construction.*/
@ -232,6 +244,24 @@ class OSGUTIL_EXPORT CullVisitor : public osg::NodeVisitor
void updateCalculatedNearFar(const osg::BoundingBox& bb);
void updateCalculatedNearFar(const osg::Vec3& pos);
//SandB added: ////////////////////////////////////////////////////////////////////////////////
/**updates near and far clipping values for case of detailed culling*/
void updateCalculatedNearFar(osg::Drawable* pDrawable);
/**calculates near for "global" vertex in scene*/
double calculateZNear(const osg::Vec3& position, const osg::Vec3& eye, const osg::Vec3& look);
///////////////////////////////////////////////////////////////////////////////////////////////
/**unit vector of direction along intersection of cameras left and top clipping planes in "global" coordinates*/
mutable osg::Vec3 _LeftUp;
/**unit vector of direction along intersection of cameras right and top clipping planes in "global" coordinates*/
mutable osg::Vec3 _RightUp;
/**unit vector of direction along intersection of cameras left and down clipping planes in "global" coordinates*/
mutable osg::Vec3 _LeftDown;
/**unit vector of direction along intersection of cameras right and down clipping planes in "global" coordinates*/
mutable osg::Vec3 _RightDown;
/** Add a drawable to current render graph.*/
@ -272,6 +302,8 @@ class OSGUTIL_EXPORT CullVisitor : public osg::NodeVisitor
* to generate the impostor texture. */
osg::ImpostorSprite* createImpostorSprite(osg::Impostor& node);
bool _detailedCulling;
typedef std::vector< osg::ref_ptr<CullViewState> > CullViewStateStack;
CullViewStateStack _viewStateStack;
osg::ref_ptr<CullViewState> _tvs;

View File

@ -7,6 +7,10 @@
#include <osg/TexEnv>
#include <osg/AlphaFunc>
#include <osg/LineSegment>
#include <osg/GeoSet>
#include <osgUtil/CullVisitor>
#include <osgUtil/RenderToTextureStage>
@ -18,7 +22,7 @@
using namespace osg;
using namespace osgUtil;
#define DEG2RAD(x) ((x)*M_PI/180.0)
#define DEG2RAD(x) ((x)*M_PI/180.0)
static bool g_debugging2 = false;
@ -71,6 +75,308 @@ class PrintVisitor : public NodeVisitor
int _step;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SandB change to this
struct TriangleViewFrustumIntersect
{
//members .................
//the clipping volume, so that triangle vertices can be shecked if inside
osg::ClippingVolume _cv;
//map serves not to have mulitple entries of same vertices
std::map<osg::Vec3, bool> _listVectors;
//transformation matrix
const osg::Matrix* _t_mat;
//value needed to set up triangles properly
double _current_near;
//eye point of camera
osg::Vec3 _eye;
osg::Vec3 _LeftUp;
osg::Vec3 _LeftDown;
osg::Vec3 _RightUp;
osg::Vec3 _RightDown;
//constructor
TriangleViewFrustumIntersect(
const osg::ClippingVolume& clip_vol,
const osg::Matrix* matr,
double current_near,
const osg::Vec3& eyePoint,
const osg::Vec3& LeftUp,
const osg::Vec3& LeftDown,
const osg::Vec3& RightUp,
const osg::Vec3& RightDown)
{
_cv = clip_vol;
_t_mat = matr;
_current_near = current_near;
_eye = eyePoint;
_LeftUp = LeftUp;
_LeftDown = LeftDown;
_RightUp = RightUp;
_RightDown = RightDown;
}
//pretty much the copy of IntersectVisitor intersect() function
int intersect_linesegment_and_triangle(
osg::Vec3& to_return,
const osg::LineSegment& ls,
const osg::Vec3& vertex1,
const osg::Vec3& vertex2,
const osg::Vec3& vertex3);
void intersect_triangle(const osg::Vec3& vert1, const osg::Vec3& vert2, const osg::Vec3& vert3);
//and crucial:
void operator() (const osg::Vec3& vert1, const osg::Vec3& vert2, const osg::Vec3& vert3)
{
intersect_triangle(vert1, vert2, vert3);
}
};
//SandB added: pretty much copy of the IntersectVisitor intersection of traingle function
int TriangleViewFrustumIntersect::intersect_linesegment_and_triangle(osg::Vec3& to_return,
const osg::LineSegment& ls,
const osg::Vec3& v1,
const osg::Vec3& v2,
const osg::Vec3& v3)
{
if(v1 == v2 || v1 == v3 || v2 == v3) return -1;
osg::Vec3 _s = ls.start();
osg::Vec3 _d = ls.end() - ls.start();
float _length = _d.length();
_d /= _length;
osg::Vec3 v12 = v2 - v1;
osg::Vec3 n12 = v12^_d;
float ds12 = (_s-v1)*n12;
float d312 = (v3-v1)*n12;
if (d312>=0.0f)
{
if (ds12<0.0f) return 3;
if (ds12>d312) return 3;
}
else // d312 < 0
{
if (ds12>0.0f) return 3;
if (ds12<d312) return 3;
}
osg::Vec3 v23 = v3-v2;
osg::Vec3 n23 = v23^_d;
float ds23 = (_s-v2)*n23;
float d123 = (v1-v2)*n23;
if (d123>=0.0f)
{
if (ds23<0.0f) return 3;
if (ds23>d123) return 3;
}
else // d123 < 0
{
if (ds23>0.0f) return 3;
if (ds23<d123) return 3;
}
osg::Vec3 v31 = v1-v3;
osg::Vec3 n31 = v31^_d;
float ds31 = (_s-v3)*n31;
float d231 = (v2-v3)*n31;
if (d231>=0.0f)
{
if (ds31<0.0f) return 3;
if (ds31>d231) return 3;
}
else // d231 < 0
{
if (ds31>0.0f) return 3;
if (ds31<d231) return 3;
}
float r3 = ds12/d312;
float r1 = ds23/d123;
float r2 = ds31/d231;
to_return = v1*r1+v2*r2+v3*r3;
float d = (to_return-_s)*_d;
if (d<0.0f) return 1;
if (d>_length) return 2;
return 0;
}
void TriangleViewFrustumIntersect::intersect_triangle(const osg::Vec3& vert1, const osg::Vec3& vert2, const osg::Vec3& vert3)
{
//if we have vertices in "transformed" coordinates, transform them to "global" coordinates
osg::Vec3 v1, v2, v3;
if(_t_mat)
{
v1 = vert1*(*_t_mat);
v2 = vert2*(*_t_mat);
v3 = vert3*(*_t_mat);
}
else
{
v1 = vert1;
v2 = vert2;
v3 = vert3;
}
//construct positions of truncated clipping volume corners
osg::Vec3 UpLeft(_eye + _LeftUp * _current_near);
osg::Vec3 DownLeft(_eye + _LeftDown*_current_near);
osg::Vec3 UpRight(_eye + _RightUp*_current_near);
osg::Vec3 DownRight(_eye + _RightDown*_current_near);
//construct truncation "back plane"
osg::Plane back_plane(DownLeft, DownRight, UpRight);//CCW, to have normal where it should be
//add this plane to clipping volume
_cv.add(back_plane);
//check if all three triangle vertices are contained in truncated clipping volume
unsigned int check = 0;
//check if all three triangle vertices are behind truncation ("back") plane
unsigned int check2 = 0;
if(back_plane.distance(v1) <= 0.0)
check2 |= 1;
else if(_cv.contains(v1)) //can not be contained if behind
{
_listVectors[v1] = true;
check |= 1;
}
if(back_plane.distance(v2)<=0.0)
check2 |= 2;
else if(_cv.contains(v2))
{
_listVectors[v2] = true;
check |= 2;
}
if(back_plane.distance(v3) <= 0.0)
check2 |= 4;
else if(_cv.contains(v3))
{
_listVectors[v3] = true;
check |= 4;
}
if(check2 == 7)
{
//all three traingle vertices are behind truncation plane so no need to check them
//for heavily tesselated situation, htis is where most of tries will end
return;
}
if(check != 7)
{
//just if it happens that all three are contained in truncated clipping volume, no need to do extra calculation
//(and they already are added to candidate vertices))
//at least one of the trianngle vertices is not contained in clipping volume, so extra checks are necessary
//"working" variable
osg::Vec3 returned;
//construct line segment of two triangle vertices and check if they intersect any clipping plane
//but within correct clipping plane triangle
osg::LineSegment s12(v1, v2);
//left triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpLeft, DownLeft) == 0)
_listVectors[returned] = true;
//up triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpLeft, UpRight) == 0)
_listVectors[returned] = true;
//right triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpRight, DownRight) == 0)
_listVectors[returned] = true;
//bottom triangled
if(intersect_linesegment_and_triangle(returned, s12, _eye, DownLeft, DownRight) == 0)
_listVectors[returned] = true;
//now for second edge of triangle
s12.set(v2, v3);
//left triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpLeft, DownLeft) == 0)
_listVectors[returned] = true;
//up triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpLeft, UpRight) == 0)
_listVectors[returned] = true;
//right triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpRight, DownRight) == 0)
_listVectors[returned] = true;
//bottom triangled
if(intersect_linesegment_and_triangle(returned, s12, _eye, DownLeft, DownRight) == 0)
_listVectors[returned] = true;
s12.set(v3, v1);
//left triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpLeft, DownLeft) == 0)
_listVectors[returned] = true;
//up triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpLeft, UpRight) == 0)
_listVectors[returned] = true;
//right triangle
if(intersect_linesegment_and_triangle(returned, s12, _eye, UpRight, DownRight) == 0)
_listVectors[returned] = true;
//bottom triangled
if(intersect_linesegment_and_triangle(returned, s12, _eye, DownLeft, DownRight) == 0)
_listVectors[returned] = true;
//we still have possibility of camera being above huge triangle, so it is possible that clipping volume
//intersects this triangle thus giving coordinates relevant for determination of near plane
s12.set(_eye, UpLeft);
if(intersect_linesegment_and_triangle(returned, s12, v1, v2, v3) == 0)
_listVectors[returned] = true;
s12.set(_eye, DownLeft);
if(intersect_linesegment_and_triangle(returned, s12, v1, v2, v3) == 0)
_listVectors[returned] = true;
s12.set(_eye, UpRight);
if(intersect_linesegment_and_triangle(returned, s12, v1, v2, v3) == 0)
_listVectors[returned] = true;
s12.set(_eye, DownRight);
if(intersect_linesegment_and_triangle(returned, s12, v1, v2, v3) == 0)
_listVectors[returned] = true;
}
}
CullVisitor::CullVisitor()
{
@ -109,6 +415,9 @@ CullVisitor::CullVisitor()
_numFramesToKeepImpostorSprites = 10;
_impostorSpriteManager = new ImpostorSpriteManager;
//SandB change
_detailedCulling = false;
}
@ -144,10 +453,10 @@ void CullVisitor::reset()
_currentReuseRenderLeafIndex = 0;
for(RenderLeafList::iterator itr=_reuseRenderLeafList.begin();
itr!=_reuseRenderLeafList.end();
++itr)
itr!=_reuseRenderLeafList.end();
++itr)
{
(*itr)->reset();
(*itr)->reset();
}
}
@ -168,8 +477,8 @@ void CullVisitor::setCamera(const Camera& camera)
_tvs->_upVector = camera.getUpVector_Model();
_tvs->_bbCornerFar = (_tvs->_lookVector.x()>=0?1:0) |
(_tvs->_lookVector.y()>=0?2:0) |
(_tvs->_lookVector.z()>=0?4:0);
(_tvs->_lookVector.y()>=0?2:0) |
(_tvs->_lookVector.z()>=0?4:0);
_tvs->_bbCornerNear = (~_tvs->_bbCornerFar)&7;
@ -247,8 +556,8 @@ void CullVisitor::pushCullViewState(const Matrix* matrix)
nvs->_bbCornerFar = (nvs->_lookVector.x()>=0?1:0) |
(nvs->_lookVector.y()>=0?2:0) |
(nvs->_lookVector.z()>=0?4:0);
(nvs->_lookVector.y()>=0?2:0) |
(nvs->_lookVector.z()>=0?4:0);
nvs->_bbCornerNear = (~nvs->_bbCornerFar)&7;
@ -275,6 +584,135 @@ void CullVisitor::popCullViewState()
}
double CullVisitor::calculateZNear(const osg::Vec3& position, const osg::Vec3& eye, const osg::Vec3& look)
{
//note: the candidate points are always in "global" coordinates
return (position - eye)*look;
}
void CullVisitor::calcClippingDirections() const
{
//need to calculate intersections of clipping planes
osg::Vec3 t_up = _camera->getUpVector();
osg::Vec3 t_side = _camera->getSideVector();
double t_VFOV_2 = _camera->calc_fovy() * M_PI / 360.0;//half of vertical FOV in radians
//we need to pitch up the cameras up vector for angle that is half fovy,
osg::Vec3 pitched_up_up = t_up * osg::Matrix::rotate(t_VFOV_2, t_side.x(), t_side.y(), t_side.z());
//we need also pitched down cameras up vector
osg::Vec3 pitched_down_up = t_up * osg::Matrix::rotate(-t_VFOV_2, t_side.x(), t_side.y(), t_side.z());
//we need either left and right or up and down planes of clipping volume (their normals better said)
osg::Vec4 temp_plane = _cvs.get()->_clippingVolume.getPlaneList()[0].asVec4();//take left
osg::Vec3 left(temp_plane.x(), temp_plane.y(), temp_plane.z());
temp_plane = _cvs.get()->_clippingVolume.getPlaneList()[1].asVec4();//take right
osg::Vec3 right(temp_plane.x(), temp_plane.y(), temp_plane.z());
//now, the line from eye along intersecion of left and up clipping planes is cross product of properly pitched up "up" vector and left
//clipping plane normal
_LeftUp = pitched_up_up^left; _LeftUp.normalize();//upper left line of clipping volume
_LeftDown = pitched_down_up^left; _LeftDown.normalize();//lower left line of clipping volume
_RightUp = right^pitched_up_up; _RightUp.normalize();//upper right line of clipping volume
_RightDown = right^pitched_down_up; _RightDown.normalize();//lower right line of clipping volume
}
void CullVisitor::updateCalculatedNearFar(osg::Drawable* pDrawable)
{
//new philosophy, to have detailed checking
//do all the same as non-detailed update near and far
const BoundingBox& bb = pDrawable->getBound();
const osg::Vec3& eyePoint = _tvs->_eyePoint; // note world eye point.
const osg::Vec3& lookVector = _tvs->_lookVector; // world look vector.
float d_near,d_far;
if (_cvs->_matrix.valid())
{
const osg::Matrix& matrix = *(_cvs->_matrix);
// calculate the offset from the eye in local coords then transform
// the offset into world and then compare against the world look vector.
d_near = ((bb.corner(_cvs->_bbCornerNear)*matrix) - eyePoint)*lookVector;
d_far = ((bb.corner(_cvs->_bbCornerFar)*matrix) - eyePoint)*lookVector;
}
else
{
d_near = (bb.corner(_cvs->_bbCornerNear)-eyePoint)*lookVector;
d_far = (bb.corner(_cvs->_bbCornerFar)-eyePoint)*lookVector;
}
//this is where difference arises: check if near is less than zero:
if(d_near >= 0.0)
{
//this is the same as before (non detailed:
if(d_near <= d_far)
{
if(d_near < _calculated_znear) _calculated_znear = d_near;
if(d_far > _calculated_zfar) _calculated_zfar = d_far;
}
else
{
if ( !EQUAL_F(d_near, d_far) )
{
osg::notify(osg::WARN)<<"Warning: CullVisitor::updateCalculatedNearFar(.) near>far in range calculation,"<<endl;
osg::notify(osg::WARN)<<" correcting by swapping values d_near="<<d_near<<" dfar="<<d_far<<endl;
}
// note, need to reverse the d_near/d_far association because they are
// the wrong way around...
if (d_far<_calculated_znear) _calculated_znear = d_far;
if (d_near>_calculated_zfar) _calculated_zfar = d_near;
}
}
else if(d_far > 0.0)
{
//SandB change
//we need to determine what has to be checked: everything that is actually behind current near clipping
//plane needs not be rechecked
double current_near = _camera->right()/_camera->zNear();//this is tan (HFOV/2)
current_near = sqrt(1.0 + current_near*current_near);//his is 1 / cos(HFOV/2)
if(_calculated_znear != FLT_MAX)//just in case this is the very first entry (i.e. the first bounding box contained eyePoint of camera
current_near = _calculated_znear * current_near;//this is side of triangle ...
else if(_calculated_zfar != -FLT_MAX)
current_near = _calculated_zfar * current_near;
else current_near = 10000.0;//something must be put
//construct functor: needs clipping volume, matrix, and current near, while some members for speed are kept in CullVisitor since
//they need be calculated only once per frame
TriangleViewFrustumIntersect ti(_cvs->_clippingVolume,
_cvs->_matrix.get(), current_near, eyePoint,
_LeftUp,_LeftDown,_RightUp,_RightDown);
//this is ok, since the GeoSets are the ones we are really interested in here
osg::GeoSet* p_gset = (osg::GeoSet*) pDrawable;
for_each_triangle(*p_gset, ti);//that's it, all triangles of this geoset have been checked out
//now, take the smallest positive near from candidate coordinates
std::map<osg::Vec3, bool>::iterator it = ti._listVectors.begin();
double calc_znear = 0.0;
for(; it != ti._listVectors.end(); ++it)
{
calc_znear = calculateZNear(it->first, eyePoint, lookVector);//determine near produced by this coordinate
if(calc_znear > 0.0 && calc_znear < _calculated_znear) _calculated_znear = calc_znear;//just to make sure , but should not be negative here
//since we intersect triangles nad line segments
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CullVisitor::updateCalculatedNearFar(const osg::BoundingBox& bb)
{
@ -406,8 +844,19 @@ void CullVisitor::apply(Geode& node)
const BoundingBox &bb =drawable->getBound();
if (isCulled(bb,mode)) continue;
updateCalculatedNearFar(bb);
//SandB change:
// updateCalculatedNearFar(bb);
if(_detailedCulling)
{
updateCalculatedNearFar(drawable);
}
else
{
updateCalculatedNearFar(bb);
}
//end of SandB change
// push the geoset's state on the geostate stack.
StateSet* stateset = drawable->getStateSet();
@ -486,7 +935,19 @@ void CullVisitor::apply(Billboard& node)
// need to modify isCulled to handle the billboard offset.
// if (isCulled(drawable->getBound())) continue;
//SandB change:
updateCalculatedNearFar(pos);
/*
if(_detailedCulling)
{
updateCalculatedNearFar(drawable);
}
else
{
updateCalculatedNearFar(pos);
}
//end of SandB change
*/
Matrix* billboard_matrix = createOrReuseMatrix();
node.calcTransform(eye_local,pos,*billboard_matrix);
@ -1070,7 +1531,7 @@ ImpostorSprite* CullVisitor::createImpostorSprite(Impostor& node)
{
// deal with projection issue move the top and right points
// onto the near plane.
float ratio = znear/(center_world-eye_world).length();
float ratio = znear/(center_world-eye_world).length();
top *= ratio;
right *= ratio;
camera->setFrustum(-right,right,-top,top,znear,zfar);
@ -1211,8 +1672,8 @@ ImpostorSprite* CullVisitor::createImpostorSprite(Impostor& node)
{
// deal with projection issue by moving the coorners of the quad
// towards the eye point.
float ratio = width/(center_local-eye_local).length();
float one_minus_ratio = 1.0f-ratio;
float ratio = width/(center_local-eye_local).length();
float one_minus_ratio = 1.0f-ratio;
Vec3 eye_local_ratio = eye_local*ratio;
controlcoords[0] = coords[0]*one_minus_ratio + eye_local_ratio;

View File

@ -174,6 +174,11 @@ void SceneView::cull()
_cullVisitor->setViewport(_viewport.get());
_cullVisitor->setEarthSky(NULL); // reset earth sky on each frame.
// SandB
//now make it compute "clipping directions" needed for detailed culling
if(_cullVisitor->getDetailedCulling())
_cullVisitor->calcClippingDirections();//only once pre frame
_renderStage->reset();
_renderStage->setViewport(_viewport.get());
@ -251,8 +256,11 @@ void SceneView::cull()
// if required clamp the near plane to prevent negative or near zero
// near planes.
float min_near_plane = _far_plane*0.0005f;
if (_near_plane<min_near_plane) _near_plane=min_near_plane;
if(!_cullVisitor->getDetailedCulling())
{
float min_near_plane = _far_plane*0.0005f;
if (_near_plane<min_near_plane) _near_plane=min_near_plane;
}
}
else
{