#include "osgUtil/RenderVisitor" #include "osg/DCS" #include "osg/Geode" #include "osg/LOD" #include "osg/Billboard" #include "osg/LightSource" #include "osg/Notify" #include #include #ifndef OSG_USE_IO_DOT_H #include using namespace std; #endif using namespace osg; using namespace osgUtil; static bool g_debugging = false; ViewState::ViewState() { _matrix = NULL; _inverse = NULL; _ratio = 0.002f; _viewFrustumCullingActive=true; _smallFeatureCullingActive=true; } ViewState::~ViewState() { if (_matrix) _matrix->unref(); if (_inverse) _inverse->unref(); } bool ViewState::isCulled(const BoundingSphere& sp) { if (!sp.isValid()) return true; Vec3 delta = sp._center-_eyePoint; if (_smallFeatureCullingActive) { if (sp._radiussp._radius) { return true; } if (delta*_frustumBottomNormal>sp._radius) { return true; } if (delta*_frustumLeftNormal>sp._radius) { return true; } if (delta*_frustumRightNormal>sp._radius) { return true; } } return false; } bool ViewState::isCulled(const BoundingBox& bb) { if (!bb.isValid()) return true; if (_viewFrustumCullingActive) { unsigned int c; for(c=0;c<8;++c) { if ((bb.corner(c)-_eyePoint)*_frustumLeftNormal<=0.0f) break; } // if all corners have been checked, therefore all points are // and the far side of the left clipping plane and hence should be culled. if (c==8) return true; for(c=0;c<8;++c) { if ((bb.corner(c)-_eyePoint)*_frustumRightNormal<=0.0f) break; } // if all corners have been checked, therefore all points are // and the far side of the right clipping plane and hence should be culled. if (c==8) return true; for(c=0;c<8;++c) { if ((bb.corner(c)-_eyePoint)*_frustumTopNormal<=0.0f) break; } // if all corners have been checked, therefore all points are // and the far side of the top clipping plane and hence should be culled. if (c==8) return true; for(c=0;c<8;++c) { if ((bb.corner(c)-_eyePoint)*_frustumBottomNormal<=0.0f) break; } // if all corners have been checked, therefore all points are // and the far side of the bottom clipping plane and hence should be culled. if (c==8) return true; } return false; } RenderVisitor::RenderVisitor() { // overide the default node visitor mode. setTraverseMode(NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); _globalState=NULL; _LODBias = 1.0f; _fovy=60.0f; _aspect=1.0f; _znear=1.0f; _zfar=1000.0f; _tvs = new ViewState; _tvs->_eyePoint.set(0.0f,0.0f,1.0f); _tvs->_centerPoint.set(0.0f,0.0f,0.0f); _tvs->_lookVector.set(0.0f,0.0f,-1.0f); _tvs->_upVector.set(0.0f,1.0f,0.0f); _tvs->ref(); _cvs = _tvs; _cvs->ref(); _tsm = LOOK_VECTOR_DISTANCE; _tsm = OBJECT_EYE_POINT_DISTANCE; _viewFrustumCullingActive=true; _smallFeatureCullingActive=true; calculateClippingPlanes(); } RenderVisitor::~RenderVisitor() { reset(); // if a global geostate is attached simply unref it. if (_globalState) { _globalState->unref(); _globalState = NULL; } if (_tvs) _tvs->unref(); if (_cvs) _cvs->unref(); } void RenderVisitor::reset() { // // first unref all referenced objects and then empty the containers. // std::for_each(_viewStateStack.begin(),_viewStateStack.end(),UnrefOp()); _viewStateStack.erase(_viewStateStack.begin(),_viewStateStack.end()); if (_cvs!=_tvs) { if (_cvs) _cvs->unref(); _cvs = _tvs; _cvs->ref(); } for(std::multimap::iterator titr= _transparentGeoSets.begin(); titr!= _transparentGeoSets.end(); ++titr) { if ((*titr).second.first) (*titr).second.first->unref(); } _transparentGeoSets.erase(_transparentGeoSets.begin(),_transparentGeoSets.end()); for(std::multimap::iterator oitr= _opaqueGeoSets.begin(); oitr!= _opaqueGeoSets.end(); ++oitr) { if ((*oitr).second.first) (*oitr).second.first->unref(); } _opaqueGeoSets.erase(_opaqueGeoSets.begin(),_opaqueGeoSets.end()); _lights.erase(_lights.begin(),_lights.end()); } void RenderVisitor::setGlobalState(GeoState* global) { if (global==_globalState) return; if (_globalState) _globalState->unref(); _globalState = global; _globalState->ref(); } void RenderVisitor::setPerspective(const osg::Camera& camera) { _fovy=camera.getFieldOfViewY(); _aspect=camera.getAspectRatio(); _znear=camera.getNearPlane(); _zfar=camera.getFarPlane(); calculateClippingPlanes(); } void RenderVisitor::setPerspective(float fovy,float aspect,float znear,float zfar) { _fovy=fovy; _aspect=aspect; _znear=znear; _zfar=zfar; calculateClippingPlanes(); } void RenderVisitor::setLookAt(const Camera& camera) { setLookAt(camera.getEyePoint(),camera.getLookPoint(),camera.getUpVector()); } void RenderVisitor::setLookAt(const Vec3& eye,const Vec3& center,const Vec3& upVector) { _tvs->_eyePoint = eye; _tvs->_centerPoint = center; _tvs->_lookVector = center-eye; _tvs->_lookVector.normalize(); _tvs->_upVector = upVector; calculateClippingPlanes(); } void RenderVisitor::setLookAt(double eyeX,double eyeY,double eyeZ, double centerX,double centerY,double centerZ, double upX,double upY,double upZ) { _tvs->_eyePoint[0] = eyeX; _tvs->_eyePoint[1] = eyeY; _tvs->_eyePoint[2] = eyeZ; _tvs->_centerPoint[0] = centerX; _tvs->_centerPoint[1] = centerY; _tvs->_centerPoint[2] = centerZ; _tvs->_lookVector[0] = centerX-eyeX; _tvs->_lookVector[1] = centerY-eyeY; _tvs->_lookVector[2] = centerZ-eyeZ; _tvs->_lookVector.normalize(); _tvs->_upVector[0] = upX; _tvs->_upVector[1] = upY; _tvs->_upVector[2] = upZ; calculateClippingPlanes(); } void RenderVisitor::setCullingActive(CullingType ct,bool active) { switch(ct) { case(VIEW_FRUSTUM_CULLING):_viewFrustumCullingActive=active;break; case(SMALL_FEATURE_CULLING):_smallFeatureCullingActive=active;break; } } bool RenderVisitor::getCullingActive(CullingType ct) { switch(ct) { case(VIEW_FRUSTUM_CULLING):return _viewFrustumCullingActive; case(SMALL_FEATURE_CULLING):return _smallFeatureCullingActive; } return false; } void RenderVisitor::calculateClippingPlanes() { float half_fovy = _fovy*0.5f; float s = sinf(half_fovy*M_PI/180.0f); float c = cosf(half_fovy*M_PI/180.0f); Vec3 lv = _tvs->_lookVector; Vec3 up = _tvs->_upVector; Vec3 sv = lv ^ up; sv.normalize(); _tvs->_frustumTopNormal = -lv*s + up*c; _tvs->_frustumTopNormal.normalize(); _tvs->_frustumBottomNormal = -lv*s - up*c; _tvs->_frustumBottomNormal.normalize(); _tvs->_frustumLeftNormal = -sv*c - lv*(s*_aspect); _tvs->_frustumLeftNormal.normalize(); _tvs->_frustumRightNormal = sv*c - lv*(s*_aspect); _tvs->_frustumRightNormal.normalize(); // notify(INFO) << "_frustumTopNormal = "<<_tvs->_frustumTopNormal[0]<<"\t"<<_tvs->_frustumTopNormal[1]<<"\t"<<_tvs->_frustumTopNormal[2]<_frustumBottomNormal[1]<<"\t"<<_tvs->_frustumBottomNormal[2]<_frustumLeftNormal[1]<<"\t"<<_tvs->_frustumLeftNormal[2]<_frustumRightNormal[1]<<"\t"<<_tvs->_frustumRightNormal[2]<ref(); nvs->_viewFrustumCullingActive=_viewFrustumCullingActive; nvs->_smallFeatureCullingActive=_smallFeatureCullingActive; if (_cvs && _cvs->_matrix) { nvs->_matrix = new Matrix; nvs->_matrix->mult(matrix,*(_cvs->_matrix)); } else { nvs->_matrix = new Matrix(matrix); } nvs->_matrix->ref(); Matrix* inverse_world = new Matrix; inverse_world->ref(); inverse_world->invert(*(nvs->_matrix)); nvs->_inverse = inverse_world; nvs->_eyePoint = _tvs->_eyePoint*(*inverse_world); nvs->_centerPoint = _tvs->_centerPoint*(*inverse_world); nvs->_lookVector = nvs->_centerPoint - nvs->_eyePoint; nvs->_lookVector.normalize(); nvs->_frustumTopNormal = (_tvs->_eyePoint + _tvs->_frustumTopNormal)*(*inverse_world)-nvs->_eyePoint; nvs->_frustumTopNormal.normalize(); nvs->_frustumBottomNormal = (_tvs->_eyePoint + _tvs->_frustumBottomNormal)*(*inverse_world)-nvs->_eyePoint; nvs->_frustumBottomNormal.normalize(); nvs->_frustumLeftNormal = (_tvs->_eyePoint + _tvs->_frustumLeftNormal)*(*inverse_world)-nvs->_eyePoint; nvs->_frustumLeftNormal.normalize(); nvs->_frustumRightNormal = (_tvs->_eyePoint + _tvs->_frustumRightNormal)*(*inverse_world)-nvs->_eyePoint; nvs->_frustumRightNormal.normalize(); if (_cvs) _cvs->unref(); _cvs = nvs; if (_cvs) _cvs->ref(); _viewStateStack.push_back(nvs); } void RenderVisitor::popMatrix() { // pop the top of the view stack and unref it. ViewState* pvs = _viewStateStack.back(); _viewStateStack.pop_back(); pvs->unref(); // unref previous cvs if (_cvs) _cvs->unref(); // to new cvs and ref it. if (_viewStateStack.empty()) { _cvs = _tvs; if (_cvs) _cvs->ref(); } else { _cvs = _viewStateStack.back(); if (_cvs) _cvs->ref(); } } Matrix* RenderVisitor::getCurrentMatrix() { return _cvs->_matrix; } Matrix* RenderVisitor::getInverseCurrentMatrix() { return _cvs->_inverse; } const Vec3& RenderVisitor::getEyeLocal() { return _cvs->_eyePoint; } const Vec3& RenderVisitor::getCenterLocal() { return _cvs->_centerPoint; } const Vec3& RenderVisitor::getLookVectorLocal() { return _cvs->_lookVector; } bool RenderVisitor::isCulled(const BoundingSphere& sp) { return _cvs->isCulled(sp); } bool RenderVisitor::isCulled(const BoundingBox& bb) { return _cvs->isCulled(bb); } void RenderVisitor::apply(Node& node) { if (isCulled(node.getBound())) return; traverse(node); } void RenderVisitor::apply(Geode& node) { if (isCulled(node.getBound())) return; Matrix* matrix = getCurrentMatrix(); for(int i=0;igetBound())) continue; GeoState* gstate = gset->getGeoState(); bool isTransparent = gstate && gstate->isTransparent(); if (isTransparent) { Vec3 center; if (matrix) { center = (gset->getBound().center())*(*matrix); } else { center = gset->getBound().center(); } Vec3 delta_center = center-_tvs->_eyePoint; if (g_debugging) { notify(INFO) << "center ["<_lookVector.x()<<","<<_tvs->_lookVector.y()<<","<<_tvs->_lookVector.z()<<"]"<_lookVector*delta_center;break; case(OBJECT_EYE_POINT_DISTANCE): default: depth = delta_center.length2();break; } if (matrix) matrix->ref(); _transparentGeoSets.insert( std::pair(depth,MatrixGeoSet(matrix,gset)) ); } else { if (matrix) matrix->ref(); _opaqueGeoSets.insert( std::pair(gstate,MatrixGeoSet(matrix,gset)) ); } } } void RenderVisitor::apply(Billboard& node) { if (isCulled(node.getBound())) return; Vec3 eye_local = getEyeLocal(); for(int i=0;igetBound())) continue; Matrix local_mat; node.calcTransform(eye_local,pos,local_mat); Matrix* matrix = NULL; if (_cvs->_matrix) { matrix = new Matrix(); matrix->mult(local_mat,*(_cvs->_matrix)); } else { matrix = new Matrix(local_mat); } GeoState* gstate = gset->getGeoState(); bool isTransparent = gstate && gstate->isTransparent(); if (isTransparent) { Vec3 center; if (matrix) { center = (gset->getBound().center())*(*matrix); } else { center = gset->getBound().center(); } Vec3 delta_center = center-_tvs->_eyePoint; if (g_debugging) { notify(INFO) << "center ["<_lookVector.x()<<","<<_tvs->_lookVector.y()<<","<<_tvs->_lookVector.z()<<"]"<_lookVector*delta_center;break; case(OBJECT_EYE_POINT_DISTANCE): default: depth = delta_center.length2();break; } if (matrix) matrix->ref(); _transparentGeoSets.insert( std::pair(depth,MatrixGeoSet(matrix,gset)) ); } else { if (matrix) matrix->ref(); _opaqueGeoSets.insert( std::pair(gstate,MatrixGeoSet(matrix,gset)) ); } } } void RenderVisitor::apply(LightSource& node) { Matrix* matrix = getCurrentMatrix(); Light* light = node.getLight(); if (light) { if (matrix) matrix->ref(); _lights.insert(std::pair(matrix,light)); } } void RenderVisitor::apply(Group& node) { if (isCulled(node.getBound())) return; traverse(node); } void RenderVisitor::apply(DCS& node) { if (isCulled(node.getBound())) return; pushMatrix(*node.getMatrix()); traverse(node); popMatrix(); } void RenderVisitor::apply(Switch& node) { apply((Group&)node); } void RenderVisitor::apply(LOD& node) { if (isCulled(node.getBound())) return; int eval = node.evaluate(getEyeLocal(),_LODBias); if (eval<0) { //notify(INFO) << "culled LOD"<accept(*this); } } void RenderVisitor::apply(Scene& node) { apply((Group&)node); } bool RenderVisitor::calcNearFar(double& near_plane,double& far_plane) { if (_opaqueGeoSets.empty() && _transparentGeoSets.empty()) return false; near_plane = FLT_MAX; far_plane = -FLT_MAX; Vec3 eyePoint = getEyeLocal(); Vec3 lookVector = getLookVectorLocal(); for(OpaqueList::iterator oitr = _opaqueGeoSets.begin(); oitr != _opaqueGeoSets.end(); ++oitr) { Matrix* matrix = (oitr->second).first; GeoSet* gset = (oitr->second).second; const BoundingBox& bb = gset->getBound(); for(int c=0;c<8;++c) { float d; if (matrix) d = ((bb.corner(c)*(*matrix))-eyePoint)*lookVector; else d = (bb.corner(c)-eyePoint)*lookVector; if (dfar_plane) far_plane = d; } } for(TransparentList::iterator titr = _transparentGeoSets.begin(); titr != _transparentGeoSets.end(); ++titr) { Matrix* matrix = (titr->second).first; GeoSet* gset = (titr->second).second; const BoundingBox& bb = gset->getBound(); for(int c=0;c<8;++c) { float d; if (matrix) d = ((bb.corner(c)*(*matrix))-eyePoint)*lookVector; else d = (bb.corner(c)-eyePoint)*lookVector; if (dfar_plane) far_plane = d; } } return true; } void RenderVisitor::render() { Matrix* prevMatrix = NULL; Matrix* currMatrix = NULL; GeoState* currGeoState = NULL; GeoState* prevGeoState = NULL; if (g_debugging) notify(INFO) << "start of render"<apply(); prevGeoState=_globalState; } // apply the lists. for(LightList::iterator litr=_lights.begin(); litr!=_lights.end(); ++litr) { currMatrix = litr->first; if (currMatrix!=prevMatrix) { if (prevMatrix) glPopMatrix(); if (currMatrix) { glPushMatrix(); glMultMatrixf( (GLfloat *)(currMatrix->_mat) ); } prevMatrix = currMatrix; } (litr->second)->apply(); } // draw the opaque bin. for(std::multimap::iterator oitr= _opaqueGeoSets.begin(); oitr!= _opaqueGeoSets.end(); ++oitr) { if (g_debugging) { notify(INFO) << " drawing opaque matrix["<<(*oitr).second.first<<"]" << " geoset["<<(*oitr).second.second<<"]" << " geostate["<<(*oitr).second.second->getGeoState()<<"]"<_mat) ); } prevMatrix = currMatrix; } currGeoState = (*oitr).first; if (currGeoState!=prevGeoState) { if (currGeoState) currGeoState->apply(_globalState,prevGeoState); prevGeoState = currGeoState; } (*oitr).second.second->draw(); } // glPushAttrib( GL_DEPTH_TEST ); // glDisable( GL_DEPTH_TEST ); // render the transparent geoset in reverse order, so to render the // deepest transparent objects, relative to the look vector, first. for(std::multimap::reverse_iterator titr= _transparentGeoSets.rbegin(); titr!= _transparentGeoSets.rend(); ++titr) { if (g_debugging) { notify(INFO) << " drawing transparent matrix["<<(*titr).second.first<<"]" << " geoset["<<(*titr).second.second<<"]" << " geostate["<<(*titr).second.second->getGeoState()<<"]" << " depth["<<(*titr).first<<"]"<_mat) ); } prevMatrix = currMatrix; } currGeoState = (*titr).second.second->getGeoState(); if (currGeoState!=prevGeoState) { if (currGeoState) currGeoState->apply(_globalState,prevGeoState); prevGeoState = currGeoState; } (*titr).second.second->draw(); } // glPopAttrib(); if (currMatrix) glPopMatrix(); if (g_debugging) { notify(INFO) << "end of render"<