1188 lines
47 KiB
C++
1188 lines
47 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 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 <osgVolume/MultipassTechnique>
|
|
#include <osgVolume/VolumeTile>
|
|
#include <osgVolume/VolumeScene>
|
|
#include <osgVolume/VolumeSettings>
|
|
|
|
#include <osg/Geometry>
|
|
#include <osg/ValueObject>
|
|
#include <osg/io_utils>
|
|
|
|
#include <osg/Program>
|
|
#include <osg/Material>
|
|
#include <osg/CullFace>
|
|
#include <osg/Texture1D>
|
|
#include <osg/Texture2D>
|
|
#include <osg/Texture3D>
|
|
#include <osg/TexEnvFilter>
|
|
#include <osg/TransferFunction>
|
|
|
|
#include <osgDB/ReadFile>
|
|
#include <osgDB/WriteFile>
|
|
|
|
namespace osgVolume
|
|
{
|
|
|
|
osg::Image* createDownsampledImage(osg::Image* sourceImage)
|
|
{
|
|
osg::ref_ptr<osg::Image> targetImage = new osg::Image;
|
|
|
|
int s_base = sourceImage->s()/2;
|
|
int s_odd = (sourceImage->s()%2);
|
|
int s = s_base + s_odd;
|
|
|
|
int t_base = sourceImage->t()/2;
|
|
int t_odd = (sourceImage->t()%2);
|
|
int t = t_base + t_odd;
|
|
|
|
int r_base = sourceImage->r()/2;
|
|
int r_odd = (sourceImage->r()%2);
|
|
int r = r_base + r_odd;
|
|
|
|
OSG_NOTICE<<"createDownsampledImage("<<sourceImage->s()<<", "<<sourceImage->t()<<", "<<sourceImage->r()<<")"<<std::endl;
|
|
OSG_NOTICE<<" s_base = "<<s_base<<", s_odd = "<<s_odd<<", s="<<s<<std::endl;
|
|
OSG_NOTICE<<" t_base = "<<t_base<<", t_odd = "<<t_odd<<", t="<<t<<std::endl;
|
|
OSG_NOTICE<<" r_base = "<<r_base<<", r_odd = "<<r_odd<<", r="<<r<<std::endl;
|
|
|
|
targetImage->allocateImage(s, t, r, sourceImage->getPixelFormat(), sourceImage->getDataType());
|
|
|
|
int numComponents = 1;
|
|
|
|
for(int plane=0; plane<r_base; ++plane)
|
|
{
|
|
for(int row=0; row<t_base; ++row)
|
|
{
|
|
#if 0
|
|
typedef unsigned char T;
|
|
typedef unsigned short TL;
|
|
#else
|
|
typedef unsigned short T;
|
|
typedef unsigned int TL;
|
|
#endif
|
|
T* ptr_source_1 = reinterpret_cast<T*>(sourceImage->data(0,row*2, plane*2));
|
|
T* ptr_source_2 = reinterpret_cast<T*>(sourceImage->data(1,row*2, plane*2));
|
|
T* ptr_source_3 = reinterpret_cast<T*>(sourceImage->data(0,row*2+1, plane*2));
|
|
T* ptr_source_4 = reinterpret_cast<T*>(sourceImage->data(1,row*2+1, plane*2));
|
|
T* ptr_source_5 = reinterpret_cast<T*>(sourceImage->data(0,row*2, plane*2+1));
|
|
T* ptr_source_6 = reinterpret_cast<T*>(sourceImage->data(1,row*2, plane*2+1));
|
|
T* ptr_source_7 = reinterpret_cast<T*>(sourceImage->data(0,row*2+1, plane*2+1));
|
|
T* ptr_source_8 = reinterpret_cast<T*>(sourceImage->data(1,row*2+1, plane*2+1));
|
|
T* ptr_target = reinterpret_cast<T*>(targetImage->data(0,row,plane));
|
|
|
|
for(int column=0; column<s_base; ++column)
|
|
{
|
|
// average and copy across the source data.
|
|
for(int i=0; i<numComponents; ++i)
|
|
{
|
|
#if 1
|
|
TL value = static_cast<TL>(*ptr_source_1)+
|
|
static_cast<TL>(*ptr_source_2)+
|
|
static_cast<TL>(*ptr_source_3)+
|
|
static_cast<TL>(*ptr_source_4)+
|
|
static_cast<TL>(*ptr_source_5)+
|
|
static_cast<TL>(*ptr_source_6)+
|
|
static_cast<TL>(*ptr_source_7)+
|
|
static_cast<TL>(*ptr_source_8);
|
|
value /= 8;
|
|
#else
|
|
TL value = 0;
|
|
value = osg::maximum(static_cast<TL>(*ptr_source_1),value);
|
|
value = osg::maximum(static_cast<TL>(*ptr_source_2),value);
|
|
value = osg::maximum(static_cast<TL>(*ptr_source_3),value);
|
|
value = osg::maximum(static_cast<TL>(*ptr_source_4),value);
|
|
value = osg::maximum(static_cast<TL>(*ptr_source_5),value);
|
|
value = osg::maximum(static_cast<TL>(*ptr_source_6),value);
|
|
value = osg::maximum(static_cast<TL>(*ptr_source_7),value);
|
|
value = osg::maximum(static_cast<TL>(*ptr_source_8),value);
|
|
#endif
|
|
*ptr_target = static_cast<T>(value);
|
|
|
|
++ptr_target;
|
|
|
|
++ptr_source_1;
|
|
++ptr_source_2;
|
|
++ptr_source_3;
|
|
++ptr_source_4;
|
|
++ptr_source_5;
|
|
++ptr_source_6;
|
|
++ptr_source_7;
|
|
++ptr_source_8;
|
|
}
|
|
// skip to the next set of source texels
|
|
ptr_source_1 += numComponents;
|
|
ptr_source_2 += numComponents;
|
|
ptr_source_3 += numComponents;
|
|
ptr_source_4 += numComponents;
|
|
ptr_source_5 += numComponents;
|
|
ptr_source_6 += numComponents;
|
|
ptr_source_7 += numComponents;
|
|
ptr_source_8 += numComponents;
|
|
}
|
|
if (s_odd)
|
|
{
|
|
|
|
}
|
|
}
|
|
if (t_odd)
|
|
{
|
|
OSG_NOTICE<<"Need to handle odd image row"<<std::endl;
|
|
}
|
|
}
|
|
if (r_odd)
|
|
{
|
|
OSG_NOTICE<<"Need to handle odd image plane"<<std::endl;
|
|
}
|
|
|
|
return targetImage.release();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MultipassTileData
|
|
//
|
|
|
|
class RTTCameraCullCallback : public osg::NodeCallback
|
|
{
|
|
public:
|
|
|
|
RTTCameraCullCallback(MultipassTechnique::MultipassTileData* tileData, MultipassTechnique* mt):
|
|
_tileData(tileData),
|
|
_mt(mt) {}
|
|
|
|
virtual void operator()(osg::Node* /*node*/, osg::NodeVisitor* nv)
|
|
{
|
|
osgUtil::CullVisitor* cv = nv->asCullVisitor();
|
|
|
|
cv->pushProjectionMatrix(_tileData->projectionMatrix.get());
|
|
|
|
// OSG_NOTICE<<"Traversing tile subgraph for pre-render "<<_mt->getVolumeTile()->getNumChildren()<<std::endl;
|
|
|
|
_mt->getVolumeTile()->osg::Group::traverse(*cv);
|
|
|
|
cv->popProjectionMatrix();
|
|
}
|
|
|
|
protected:
|
|
|
|
virtual ~RTTCameraCullCallback() {}
|
|
|
|
osg::observer_ptr<osgVolume::MultipassTechnique::MultipassTileData> _tileData;
|
|
osg::observer_ptr<osgVolume::MultipassTechnique> _mt;
|
|
};
|
|
|
|
MultipassTechnique::MultipassTileData::MultipassTileData(osgUtil::CullVisitor* cv, MultipassTechnique* mpt):
|
|
TileData(),
|
|
multipassTechnique(mpt)
|
|
{
|
|
currentRenderingMode = mpt->computeRenderingMode();
|
|
|
|
int width = 512;
|
|
int height = 512;
|
|
|
|
osg::Viewport* viewport = cv->getCurrentRenderStage()->getViewport();
|
|
if (viewport)
|
|
{
|
|
width = static_cast<int>(viewport->width());
|
|
height = static_cast<int>(viewport->height());
|
|
}
|
|
|
|
stateset = new osg::StateSet;
|
|
|
|
eyeToTileUniform = new osg::Uniform("eyeToTile",osg::Matrixf());
|
|
stateset->addUniform(eyeToTileUniform.get());
|
|
|
|
tileToImageUniform = new osg::Uniform("tileToImage",osg::Matrixf());
|
|
stateset->addUniform(tileToImageUniform.get());
|
|
|
|
switch(currentRenderingMode)
|
|
{
|
|
case(MultipassTechnique::CUBE):
|
|
{
|
|
// no need to set up RTT Cameras;
|
|
OSG_NOTICE<<"Setting up MultipassTileData for CUBE rendering"<<std::endl;
|
|
|
|
break;
|
|
}
|
|
case(MultipassTechnique::HULL):
|
|
{
|
|
OSG_NOTICE<<"Setting up MultipassTileData for HULL rendering"<<std::endl;
|
|
setUp(frontFaceRttCamera, frontFaceDepthTexture, width, height);
|
|
frontFaceRttCamera->setName("frontFaceRttCamera");
|
|
frontFaceRttCamera->setCullCallback(new RTTCameraCullCallback(this, mpt));
|
|
frontFaceRttCamera->getOrCreateStateSet()->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
|
|
stateset->setTextureAttribute(2, frontFaceDepthTexture.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
break;
|
|
}
|
|
case(MultipassTechnique::CUBE_AND_HULL):
|
|
{
|
|
OSG_NOTICE<<"Setting up MultipassTileData for CUBE_AND_HULL rendering"<<std::endl;
|
|
setUp(frontFaceRttCamera, frontFaceDepthTexture, width, height);
|
|
frontFaceRttCamera->setName("frontFaceRttCamera");
|
|
frontFaceRttCamera->setCullCallback(new RTTCameraCullCallback(this, mpt));
|
|
frontFaceRttCamera->getOrCreateStateSet()->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
|
|
setUp(backFaceRttCamera, backFaceDepthTexture, width, height);
|
|
backFaceRttCamera->setName("backFaceRttCamera");
|
|
backFaceRttCamera->setCullCallback(new RTTCameraCullCallback(this, mpt));
|
|
backFaceRttCamera->getOrCreateStateSet()->setAttributeAndModes(new osg::CullFace(osg::CullFace::FRONT), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
|
|
stateset->setTextureAttribute(2, frontFaceDepthTexture.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
stateset->setTextureAttribute(3, backFaceDepthTexture.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void MultipassTechnique::MultipassTileData::setUp(osg::ref_ptr<osg::Camera>& camera, osg::ref_ptr<osg::Texture2D>& depthTexture, int width, int height)
|
|
{
|
|
depthTexture = new osg::Texture2D;
|
|
depthTexture->setTextureSize(width, height);
|
|
depthTexture->setInternalFormat(GL_DEPTH_COMPONENT);
|
|
depthTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
|
|
depthTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
|
|
depthTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
|
|
depthTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
|
|
depthTexture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
|
|
|
|
camera = new osg::Camera;
|
|
camera->attach(osg::Camera::DEPTH_BUFFER, depthTexture.get());
|
|
camera->setViewport(0, 0, width, height);
|
|
|
|
// clear the depth and colour bufferson each clear.
|
|
camera->setClearMask(GL_DEPTH_BUFFER_BIT);
|
|
|
|
// set the camera to render before the main camera.
|
|
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
|
|
|
// tell the camera to use OpenGL frame buffer object where supported.
|
|
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
|
|
|
camera->setReferenceFrame(osg::Transform::RELATIVE_RF);
|
|
camera->setProjectionMatrix(osg::Matrixd::identity());
|
|
camera->setViewMatrix(osg::Matrixd::identity());
|
|
}
|
|
|
|
void MultipassTechnique::MultipassTileData::update(osgUtil::CullVisitor* cv)
|
|
{
|
|
if (currentRenderingMode != multipassTechnique->computeRenderingMode())
|
|
{
|
|
OSG_NOTICE<<"Warning: need to re-structure MP setup."<<std::endl;
|
|
}
|
|
|
|
active = true;
|
|
nodePath = cv->getNodePath();
|
|
projectionMatrix = cv->getProjectionMatrix();
|
|
modelviewMatrix = cv->getModelViewMatrix();
|
|
|
|
int width = 512;
|
|
int height = 512;
|
|
|
|
osg::Viewport* viewport = cv->getCurrentRenderStage()->getViewport();
|
|
if (viewport)
|
|
{
|
|
width = static_cast<int>(viewport->width());
|
|
height = static_cast<int>(viewport->height());
|
|
}
|
|
|
|
if (frontFaceDepthTexture.valid())
|
|
{
|
|
if (frontFaceDepthTexture->getTextureWidth()!=width || frontFaceDepthTexture->getTextureHeight()!=height)
|
|
{
|
|
OSG_NOTICE<<"Need to change texture size to "<<width<<", "<<height<<std::endl;
|
|
frontFaceDepthTexture->setTextureSize(width, height);
|
|
frontFaceRttCamera->setViewport(0, 0, width, height);
|
|
if (frontFaceRttCamera->getRenderingCache())
|
|
{
|
|
frontFaceRttCamera->getRenderingCache()->releaseGLObjects(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (backFaceDepthTexture.valid())
|
|
{
|
|
if (backFaceDepthTexture->getTextureWidth()!=width || backFaceDepthTexture->getTextureHeight()!=height)
|
|
{
|
|
OSG_NOTICE<<"Need to change texture size to "<<width<<", "<<height<<std::endl;
|
|
backFaceDepthTexture->setTextureSize(width, height);
|
|
backFaceRttCamera->setViewport(0, 0, width, height);
|
|
if (backFaceRttCamera->getRenderingCache())
|
|
{
|
|
backFaceRttCamera->getRenderingCache()->releaseGLObjects(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MultipassTechnique
|
|
//
|
|
MultipassTechnique::MultipassTechnique()
|
|
{
|
|
}
|
|
|
|
MultipassTechnique::MultipassTechnique(const MultipassTechnique& fft,const osg::CopyOp& copyop):
|
|
VolumeTechnique(fft,copyop)
|
|
{
|
|
}
|
|
|
|
MultipassTechnique::~MultipassTechnique()
|
|
{
|
|
}
|
|
|
|
osg::StateSet* MultipassTechnique::createStateSet(osg::StateSet* statesetPrototype, osg::Program* programPrototype, osg::Shader* shaderToAdd1, osg::Shader* shaderToAdd2)
|
|
{
|
|
osg::ref_ptr<osg::StateSet> stateset = osg::clone(statesetPrototype, osg::CopyOp::SHALLOW_COPY);
|
|
osg::ref_ptr<osg::Program> program = osg::clone(programPrototype, osg::CopyOp::SHALLOW_COPY);
|
|
stateset->setAttribute(program.get());
|
|
if (shaderToAdd1) program->addShader(shaderToAdd1);
|
|
if (shaderToAdd2) program->addShader(shaderToAdd2);
|
|
|
|
return stateset.release();
|
|
}
|
|
|
|
MultipassTechnique::RenderingMode MultipassTechnique::computeRenderingMode()
|
|
{
|
|
bool hasHull = (_volumeTile->getNumChildren()>0);
|
|
|
|
if (!hasHull)
|
|
{
|
|
return CUBE;
|
|
}
|
|
|
|
CollectPropertiesVisitor cpv(false);
|
|
if (_volumeTile->getLayer()->getProperty())
|
|
{
|
|
_volumeTile->getLayer()->getProperty()->accept(cpv);
|
|
}
|
|
|
|
double etfValue = cpv._exteriorTransparencyFactorProperty.valid() ? cpv._exteriorTransparencyFactorProperty->getValue() : 0.0;
|
|
|
|
if (etfValue<=0.0)
|
|
{
|
|
return HULL;
|
|
}
|
|
else if (etfValue<1.0)
|
|
{
|
|
return CUBE_AND_HULL;
|
|
}
|
|
else
|
|
{
|
|
return CUBE;
|
|
}
|
|
}
|
|
|
|
|
|
void MultipassTechnique::init()
|
|
{
|
|
OSG_INFO<<"MultipassTechnique::init()"<<std::endl;
|
|
|
|
if (!_volumeTile)
|
|
{
|
|
OSG_NOTICE<<"MultipassTechnique::init(), error no volume tile assigned."<<std::endl;
|
|
return;
|
|
}
|
|
|
|
if (_volumeTile->getLayer()==0)
|
|
{
|
|
OSG_NOTICE<<"MultipassTechnique::init(), error no layer assigend to volume tile."<<std::endl;
|
|
return;
|
|
}
|
|
|
|
if (_volumeTile->getLayer()->getImage()==0)
|
|
{
|
|
OSG_NOTICE<<"MultipassTechnique::init(), error no image assigned to layer."<<std::endl;
|
|
return;
|
|
}
|
|
|
|
OSG_NOTICE<<"MultipassTechnique::init() Need to set up"<<std::endl;
|
|
|
|
CollectPropertiesVisitor cpv(false);
|
|
if (_volumeTile->getLayer()->getProperty())
|
|
{
|
|
_volumeTile->getLayer()->getProperty()->accept(cpv);
|
|
}
|
|
|
|
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
|
|
|
{
|
|
osg::Geometry* geom = new osg::Geometry;
|
|
|
|
osg::Vec3Array* coords = new osg::Vec3Array(8);
|
|
(*coords)[0] = osg::Vec3d(0.0,0.0,0.0);
|
|
(*coords)[1] = osg::Vec3d(1.0,0.0,0.0);
|
|
(*coords)[2] = osg::Vec3d(1.0,1.0,0.0);
|
|
(*coords)[3] = osg::Vec3d(0.0,1.0,0.0);
|
|
(*coords)[4] = osg::Vec3d(0.0,0.0,1.0);
|
|
(*coords)[5] = osg::Vec3d(1.0,0.0,1.0);
|
|
(*coords)[6] = osg::Vec3d(1.0,1.0,1.0);
|
|
(*coords)[7] = osg::Vec3d(0.0,1.0,1.0);
|
|
geom->setVertexArray(coords);
|
|
|
|
osg::Vec4Array* colours = new osg::Vec4Array(1);
|
|
(*colours)[0].set(1.0f,1.0f,1.0,1.0f);
|
|
geom->setColorArray(colours, osg::Array::BIND_OVERALL);
|
|
|
|
osg::DrawElementsUShort* drawElements = new osg::DrawElementsUShort(GL_QUADS);
|
|
// bottom
|
|
drawElements->push_back(3);
|
|
drawElements->push_back(2);
|
|
drawElements->push_back(1);
|
|
drawElements->push_back(0);
|
|
|
|
// bottom
|
|
drawElements->push_back(7);//7623
|
|
drawElements->push_back(6);
|
|
drawElements->push_back(2);
|
|
drawElements->push_back(3);
|
|
|
|
// left
|
|
drawElements->push_back(4);//4730
|
|
drawElements->push_back(7);
|
|
drawElements->push_back(3);
|
|
drawElements->push_back(0);
|
|
|
|
// right
|
|
drawElements->push_back(1);//1265
|
|
drawElements->push_back(2);
|
|
drawElements->push_back(6);
|
|
drawElements->push_back(5);
|
|
|
|
// front
|
|
drawElements->push_back(5);//5401
|
|
drawElements->push_back(4);
|
|
drawElements->push_back(0);
|
|
drawElements->push_back(1);
|
|
|
|
// top
|
|
drawElements->push_back(4);//4567
|
|
drawElements->push_back(5);
|
|
drawElements->push_back(6);
|
|
drawElements->push_back(7);
|
|
|
|
geom->addPrimitiveSet(drawElements);
|
|
|
|
geode->addDrawable(geom);
|
|
|
|
}
|
|
|
|
_transform = new osg::MatrixTransform;
|
|
_transform->addChild(geode.get());
|
|
|
|
// handle locators
|
|
Locator* masterLocator = _volumeTile->getLocator();
|
|
Locator* layerLocator = _volumeTile->getLayer()->getLocator();
|
|
|
|
osg::TransferFunction1D* tf = 0;
|
|
|
|
if (!masterLocator && layerLocator) masterLocator = layerLocator;
|
|
if (!layerLocator && masterLocator) layerLocator = masterLocator;
|
|
|
|
osg::Matrix geometryMatrix;
|
|
if (masterLocator)
|
|
{
|
|
geometryMatrix = masterLocator->getTransform();
|
|
_transform->setMatrix(geometryMatrix);
|
|
masterLocator->addCallback(new TransformLocatorCallback(_transform.get()));
|
|
}
|
|
|
|
osg::Matrix imageMatrix;
|
|
if (layerLocator)
|
|
{
|
|
imageMatrix = layerLocator->getTransform();
|
|
}
|
|
|
|
OSG_NOTICE<<"MultipassTechnique::init() : geometryMatrix = "<<geometryMatrix<<std::endl;
|
|
OSG_NOTICE<<"MultipassTechnique::init() : imageMatrix = "<<imageMatrix<<std::endl;
|
|
|
|
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
|
_volumeRenderStateSet = stateset;
|
|
|
|
// texture unit 0 is for main scene colour buffer
|
|
// texture unit 1 is for main scene depth buffer
|
|
// texture unit 2 is for front depth buffer of hull when required
|
|
// texture unit 3 is for back depth depth of hull when required
|
|
unsigned int volumeTextureUnit = 4;
|
|
unsigned int transferFunctionTextureUnit = 5;
|
|
|
|
|
|
// set up uniforms
|
|
{
|
|
stateset->addUniform(new osg::Uniform("colorTexture",0));
|
|
stateset->addUniform(new osg::Uniform("depthTexture",1));
|
|
|
|
stateset->setMode(GL_ALPHA_TEST,osg::StateAttribute::ON);
|
|
//stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
|
|
|
|
float alphaFuncValue = 0.1;
|
|
if (cpv._isoProperty.valid())
|
|
{
|
|
alphaFuncValue = cpv._isoProperty->getValue();
|
|
}
|
|
|
|
if (cpv._sampleRatioProperty.valid())
|
|
stateset->addUniform(cpv._sampleRatioProperty->getUniform());
|
|
else
|
|
stateset->addUniform(new osg::Uniform("SampleRatioValue",1.0f));
|
|
|
|
|
|
if (cpv._transparencyProperty.valid())
|
|
stateset->addUniform(cpv._transparencyProperty->getUniform());
|
|
else
|
|
stateset->addUniform(new osg::Uniform("TransparencyValue",1.0f));
|
|
|
|
if (cpv._exteriorTransparencyFactorProperty.valid())
|
|
stateset->addUniform(cpv._exteriorTransparencyFactorProperty->getUniform());
|
|
else
|
|
stateset->addUniform(new osg::Uniform("ExteriorTransparencyFactorValue",0.0f));
|
|
|
|
|
|
if (cpv._afProperty.valid())
|
|
stateset->addUniform(cpv._afProperty->getUniform());
|
|
else
|
|
stateset->addUniform(new osg::Uniform("AlphaFuncValue",alphaFuncValue));
|
|
|
|
|
|
if (cpv._isoProperty.valid())
|
|
stateset->addUniform(cpv._isoProperty->getUniform());
|
|
else
|
|
stateset->addUniform(new osg::Uniform("IsoSurfaceValue",alphaFuncValue));
|
|
|
|
if (cpv._tfProperty.valid())
|
|
{
|
|
tf = dynamic_cast<osg::TransferFunction1D*>(cpv._tfProperty->getTransferFunction());
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// set up 3D texture
|
|
osg::ref_ptr<osg::Image> image_3d = _volumeTile->getLayer()->getImage();
|
|
|
|
// create a downsampled image to use when rendering at a lower quality.
|
|
// osg::ref_ptr<osg::Image> downsampled_image_3d = createDownsampledImage(image_3d.get());
|
|
|
|
//image_3d = createDownsampledImage(downsampled_image_3d.get());
|
|
// image_3d = downsampled_image_3d;
|
|
|
|
osg::ref_ptr<osg::Texture3D> texture3D = new osg::Texture3D;
|
|
{
|
|
osg::Texture::InternalFormatMode internalFormatMode = osg::Texture::USE_IMAGE_DATA_FORMAT;
|
|
#define VOLUME_TYPE 2
|
|
#if VOLUME_TYPE==1
|
|
osg::Texture::FilterMode minFilter = osg::Texture::LINEAR_MIPMAP_LINEAR;
|
|
osg::Texture::FilterMode magFilter = osg::Texture::LINEAR;
|
|
#elif VOLUME_TYPE==2
|
|
osg::Texture::FilterMode minFilter = osg::Texture::LINEAR;
|
|
osg::Texture::FilterMode magFilter = osg::Texture::LINEAR;
|
|
#else
|
|
osg::Texture::FilterMode minFilter = osg::Texture::NEAREST;
|
|
osg::Texture::FilterMode magFilter = osg::Texture::NEAREST;
|
|
#endif
|
|
|
|
// set up the 3d texture itself,
|
|
// note, well set the filtering up so that mip mapping is disabled,
|
|
// gluBuild3DMipsmaps doesn't do a very good job of handled the
|
|
// imbalanced dimensions of the 256x256x4 texture.
|
|
texture3D->setResizeNonPowerOfTwoHint(false);
|
|
texture3D->setFilter(osg::Texture3D::MIN_FILTER,minFilter);
|
|
texture3D->setFilter(osg::Texture3D::MAG_FILTER, magFilter);
|
|
texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP_TO_BORDER);
|
|
texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP_TO_BORDER);
|
|
texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP_TO_BORDER);
|
|
//texture3D->setMaxAnisotropy(16.0f);
|
|
texture3D->setBorderColor(osg::Vec4(0.0,0.0,0.0,0.0));
|
|
if (image_3d->getPixelFormat()==GL_ALPHA ||
|
|
image_3d->getPixelFormat()==GL_LUMINANCE)
|
|
{
|
|
texture3D->setInternalFormatMode(osg::Texture3D::USE_USER_DEFINED_FORMAT);
|
|
texture3D->setInternalFormat(GL_INTENSITY);
|
|
}
|
|
else
|
|
{
|
|
texture3D->setInternalFormatMode(internalFormatMode);
|
|
}
|
|
texture3D->setImage(image_3d.get());
|
|
|
|
stateset->setTextureAttributeAndModes(volumeTextureUnit, texture3D.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
|
|
|
osg::ref_ptr<osg::Uniform> baseTextureSampler = new osg::Uniform("volumeTexture", int(volumeTextureUnit));
|
|
stateset->addUniform(baseTextureSampler.get());
|
|
|
|
osg::ref_ptr<osg::Uniform> volumeCellSize = new osg::Uniform("volumeCellSize", osg::Vec3(1.0f/static_cast<float>(image_3d->s()),1.0f/static_cast<float>(image_3d->t()),1.0f/static_cast<float>(image_3d->r())));
|
|
stateset->addUniform(volumeCellSize.get());
|
|
|
|
OSG_NOTICE<<"Texture Dimensions "<<image_3d->s()<<", "<<image_3d->t()<<", "<<image_3d->r()<<std::endl;
|
|
}
|
|
|
|
if (tf)
|
|
{
|
|
OSG_NOTICE<<"Setting up TransferFunction"<<std::endl;
|
|
|
|
float tfScale = 1.0f;
|
|
float tfOffset = 0.0f;
|
|
|
|
ImageLayer* imageLayer = dynamic_cast<ImageLayer*>(_volumeTile->getLayer());
|
|
if (imageLayer)
|
|
{
|
|
tfOffset = (imageLayer->getTexelOffset()[3] - tf->getMinimum()) / (tf->getMaximum() - tf->getMinimum());
|
|
tfScale = imageLayer->getTexelScale()[3] / (tf->getMaximum() - tf->getMinimum());
|
|
}
|
|
else
|
|
{
|
|
tfOffset = -tf->getMinimum() / (tf->getMaximum()-tf->getMinimum());
|
|
tfScale = 1.0f / (tf->getMaximum()-tf->getMinimum());
|
|
}
|
|
osg::ref_ptr<osg::Texture1D> tf_texture = new osg::Texture1D;
|
|
tf_texture->setImage(tf->getImage());
|
|
|
|
tf_texture->setResizeNonPowerOfTwoHint(false);
|
|
tf_texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
|
tf_texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
|
tf_texture->setWrap(osg::Texture::WRAP_R,osg::Texture::CLAMP_TO_EDGE);
|
|
|
|
stateset->setTextureAttributeAndModes(transferFunctionTextureUnit, tf_texture.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
|
stateset->addUniform(new osg::Uniform("tfTexture",int(transferFunctionTextureUnit)));
|
|
stateset->addUniform(new osg::Uniform("tfOffset",tfOffset));
|
|
stateset->addUniform(new osg::Uniform("tfScale",tfScale));
|
|
|
|
}
|
|
|
|
// creates CullFace attributes to apply to front/back StateSet configurations.
|
|
osg::ref_ptr<osg::CullFace> front_CullFace = new osg::CullFace(osg::CullFace::BACK);
|
|
osg::ref_ptr<osg::CullFace> back_CullFace = new osg::CullFace(osg::CullFace::FRONT);
|
|
|
|
osg::ref_ptr<osg::Shader> main_vertexShader = osgDB::readRefShaderFile(osg::Shader::VERTEX, "shaders/volume_multipass.vert");
|
|
if (!main_vertexShader)
|
|
{
|
|
#include "Shaders/volume_multipass_vert.cpp"
|
|
main_vertexShader = new osg::Shader(osg::Shader::VERTEX, volume_multipass_vert);
|
|
}
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Shader> computeRayColorShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_compute_ray_color.frag");
|
|
if (!computeRayColorShader)
|
|
{
|
|
#include "Shaders/volume_compute_ray_color_frag.cpp"
|
|
computeRayColorShader = new osg::Shader(osg::Shader::FRAGMENT, volume_compute_ray_color_frag);
|
|
}
|
|
|
|
// clear any previous settings
|
|
_stateSetMap.clear();
|
|
|
|
|
|
// set up the program template for rendering just the cube
|
|
osg::ref_ptr<osg::Shader> cube_main_fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_cube.frag");;
|
|
if (!cube_main_fragmentShader)
|
|
{
|
|
#include "Shaders/volume_multipass_cube_frag.cpp"
|
|
cube_main_fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, volume_multipass_cube_frag);
|
|
}
|
|
osg::ref_ptr<osg::StateSet> cube_stateset_prototype = new osg::StateSet;
|
|
osg::ref_ptr<osg::Program> cube_program_prototype = new osg::Program;
|
|
{
|
|
cube_stateset_prototype->setAttributeAndModes(back_CullFace.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
|
|
cube_program_prototype->addShader(main_vertexShader.get());
|
|
cube_program_prototype->addShader(cube_main_fragmentShader.get());
|
|
cube_program_prototype->addShader(computeRayColorShader.get());
|
|
}
|
|
|
|
|
|
// set up the program template for rendering just the hull
|
|
osg::ref_ptr<osg::Shader> hull_main_fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_hull.frag");;
|
|
if (!hull_main_fragmentShader)
|
|
{
|
|
#include "Shaders/volume_multipass_hull_frag.cpp"
|
|
hull_main_fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, volume_multipass_hull_frag);
|
|
}
|
|
osg::ref_ptr<osg::StateSet> hull_stateset_prototype = new osg::StateSet;
|
|
osg::ref_ptr<osg::Program> hull_program_prototype = new osg::Program;
|
|
{
|
|
hull_stateset_prototype->setAttributeAndModes(back_CullFace.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
hull_stateset_prototype->addUniform(new osg::Uniform("frontFaceDepthTexture",2));
|
|
|
|
hull_program_prototype->addShader(main_vertexShader.get());
|
|
hull_program_prototype->addShader(hull_main_fragmentShader.get());
|
|
hull_program_prototype->addShader(computeRayColorShader.get());
|
|
}
|
|
|
|
|
|
// set up the program template for rendering just the cube
|
|
osg::ref_ptr<osg::Shader> cube_and_hull_main_fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_cube_and_hull.frag");;
|
|
if (!cube_and_hull_main_fragmentShader)
|
|
{
|
|
#include "Shaders/volume_multipass_cube_and_hull_frag.cpp"
|
|
cube_and_hull_main_fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, volume_multipass_cube_and_hull_frag);
|
|
}
|
|
osg::ref_ptr<osg::StateSet> cube_and_hull_stateset_prototype = new osg::StateSet;
|
|
osg::ref_ptr<osg::Program> cube_and_hull_program_prototype = new osg::Program;
|
|
{
|
|
cube_and_hull_stateset_prototype->setAttributeAndModes(back_CullFace.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
cube_and_hull_stateset_prototype->addUniform(new osg::Uniform("frontFaceDepthTexture",2));
|
|
cube_and_hull_stateset_prototype->addUniform(new osg::Uniform("backFaceDepthTexture",3));
|
|
|
|
cube_and_hull_program_prototype->addShader(main_vertexShader.get());
|
|
cube_and_hull_program_prototype->addShader(cube_and_hull_main_fragmentShader.get());
|
|
cube_and_hull_program_prototype->addShader(computeRayColorShader.get());
|
|
}
|
|
|
|
// set up the rendering of the front face
|
|
{
|
|
_frontFaceStateSet = new osg::StateSet;
|
|
|
|
// cull only the bac faces so we write only the front
|
|
_frontFaceStateSet->setAttributeAndModes(front_CullFace.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
|
|
// set up the front falce
|
|
osg::ref_ptr<osg::Program> program = new osg::Program;
|
|
program->addShader(main_vertexShader.get());
|
|
_frontFaceStateSet->setAttribute(program.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
|
}
|
|
|
|
|
|
// STANDARD_SHADERS
|
|
{
|
|
// STANDARD_SHADERS without TransferFunction
|
|
{
|
|
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_standard.frag");
|
|
if (!accumulateSamplesShader)
|
|
{
|
|
#include "Shaders/volume_accumulateSamples_standard_frag.cpp"
|
|
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_frag);
|
|
}
|
|
|
|
_stateSetMap[STANDARD_SHADERS|CUBE_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[STANDARD_SHADERS|HULL_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[STANDARD_SHADERS|CUBE_AND_HULL_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
}
|
|
|
|
// STANDARD_SHADERS with TransferFunction
|
|
if (tf)
|
|
{
|
|
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_standard_tf.frag");
|
|
if (!accumulateSamplesShader)
|
|
{
|
|
#include "Shaders/volume_accumulateSamples_standard_tf_frag.cpp"
|
|
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_tf_frag);
|
|
}
|
|
|
|
_stateSetMap[STANDARD_SHADERS|CUBE_SHADERS|TF_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[STANDARD_SHADERS|HULL_SHADERS|TF_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[STANDARD_SHADERS|CUBE_AND_HULL_SHADERS|TF_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
}
|
|
}
|
|
|
|
// ISO_SHADERS
|
|
if (cpv._isoProperty.valid())
|
|
{
|
|
// ISO_SHADERS without TransferFunction
|
|
{
|
|
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_iso.frag");
|
|
if (!accumulateSamplesShader)
|
|
{
|
|
#include "Shaders/volume_accumulateSamples_iso_frag.cpp"
|
|
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_iso_frag);
|
|
}
|
|
|
|
_stateSetMap[ISO_SHADERS|CUBE_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[ISO_SHADERS|HULL_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[ISO_SHADERS|CUBE_AND_HULL_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
}
|
|
|
|
// ISO_SHADERS with TransferFunction
|
|
if (tf)
|
|
{
|
|
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_iso_tf.frag");
|
|
if (!accumulateSamplesShader)
|
|
{
|
|
#include "Shaders/volume_accumulateSamples_iso_tf_frag.cpp"
|
|
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_iso_tf_frag);
|
|
}
|
|
|
|
_stateSetMap[ISO_SHADERS|CUBE_SHADERS|TF_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[ISO_SHADERS|HULL_SHADERS|TF_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[ISO_SHADERS|CUBE_AND_HULL_SHADERS|TF_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
}
|
|
}
|
|
|
|
// MIP_SHADERS
|
|
if (cpv._mipProperty.valid())
|
|
{
|
|
// MIP_SHADERS without TransferFunction
|
|
{
|
|
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_mip.frag");
|
|
if (!accumulateSamplesShader)
|
|
{
|
|
#include "Shaders/volume_accumulateSamples_mip_frag.cpp"
|
|
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_mip_frag);
|
|
}
|
|
|
|
_stateSetMap[MIP_SHADERS|CUBE_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[MIP_SHADERS|HULL_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[MIP_SHADERS|CUBE_AND_HULL_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
}
|
|
|
|
// MIP_SHADERS with TransferFunction
|
|
if (tf)
|
|
{
|
|
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_mip_tf.frag");
|
|
if (!accumulateSamplesShader)
|
|
{
|
|
#include "Shaders/volume_accumulateSamples_mip_tf_frag.cpp"
|
|
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_mip_tf_frag);
|
|
}
|
|
|
|
_stateSetMap[MIP_SHADERS|CUBE_SHADERS|TF_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[MIP_SHADERS|HULL_SHADERS|TF_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[MIP_SHADERS|CUBE_AND_HULL_SHADERS|TF_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
}
|
|
}
|
|
|
|
// LIT_SHADERS
|
|
if (cpv._lightingProperty.valid())
|
|
{
|
|
// LIT_SHADERS without TransferFunction
|
|
{
|
|
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_lit.frag");
|
|
if (!accumulateSamplesShader)
|
|
{
|
|
#include "Shaders/volume_accumulateSamples_lit_frag.cpp"
|
|
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_lit_frag);
|
|
}
|
|
|
|
_stateSetMap[LIT_SHADERS|CUBE_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[LIT_SHADERS|HULL_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[LIT_SHADERS|CUBE_AND_HULL_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
}
|
|
|
|
// MIP_SHADERS with TransferFunction
|
|
if (tf)
|
|
{
|
|
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_lit_tf.frag");
|
|
if (!accumulateSamplesShader)
|
|
{
|
|
#include "Shaders/volume_accumulateSamples_lit_tf_frag.cpp"
|
|
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_lit_tf_frag);
|
|
}
|
|
|
|
_stateSetMap[LIT_SHADERS|CUBE_SHADERS|TF_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[LIT_SHADERS|HULL_SHADERS|TF_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
_stateSetMap[LIT_SHADERS|CUBE_AND_HULL_SHADERS|TF_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
|
|
}
|
|
}
|
|
|
|
|
|
if (cpv._sampleRatioWhenMovingProperty.valid())
|
|
{
|
|
_whenMovingStateSet = new osg::StateSet;
|
|
_whenMovingStateSet->addUniform(cpv._sampleRatioWhenMovingProperty->getUniform(), osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
|
|
}
|
|
|
|
}
|
|
|
|
void MultipassTechnique::update(osgUtil::UpdateVisitor* uv)
|
|
{
|
|
// OSG_NOTICE<<"MultipassTechnique:update(osgUtil::UpdateVisitor* uv):"<<std::endl;
|
|
if (getVolumeTile()->getNumChildren()>0)
|
|
{
|
|
getVolumeTile()->osg::Group::traverse(*uv);
|
|
}
|
|
else
|
|
{
|
|
_transform->accept(*uv);
|
|
}
|
|
}
|
|
|
|
void MultipassTechnique::backfaceSubgraphCullTraversal(osgUtil::CullVisitor* cv)
|
|
{
|
|
if (!cv) return;
|
|
|
|
cv->pushStateSet(_frontFaceStateSet.get());
|
|
|
|
if (getVolumeTile()->getNumChildren()>0)
|
|
{
|
|
getVolumeTile()->osg::Group::traverse(*cv);
|
|
}
|
|
else
|
|
{
|
|
_transform->accept(*cv);
|
|
}
|
|
|
|
cv->popStateSet();
|
|
}
|
|
|
|
class RTTBackfaceCameraCullCallback : public osg::NodeCallback
|
|
{
|
|
public:
|
|
|
|
RTTBackfaceCameraCullCallback(MultipassTechnique::MultipassTileData* tileData, MultipassTechnique* mt):
|
|
_tileData(tileData),
|
|
_mt(mt) {}
|
|
|
|
virtual void operator()(osg::Node* /*node*/, osg::NodeVisitor* nv)
|
|
{
|
|
osgUtil::CullVisitor* cv = nv->asCullVisitor();
|
|
|
|
cv->pushProjectionMatrix(_tileData->projectionMatrix.get());
|
|
|
|
_mt->backfaceSubgraphCullTraversal(cv);
|
|
|
|
cv->popProjectionMatrix();
|
|
}
|
|
|
|
protected:
|
|
|
|
virtual ~RTTBackfaceCameraCullCallback() {}
|
|
|
|
osg::observer_ptr<osgVolume::MultipassTechnique::MultipassTileData> _tileData;
|
|
osg::observer_ptr<osgVolume::MultipassTechnique> _mt;
|
|
};
|
|
|
|
class ShadingModelVisitor : public osgVolume::PropertyVisitor
|
|
{
|
|
public:
|
|
|
|
ShadingModelVisitor():
|
|
PropertyVisitor(true),
|
|
_shadingModel(VolumeSettings::Standard),
|
|
_usesTransferFunction(false) {}
|
|
|
|
virtual void apply(IsoSurfaceProperty& /*iso*/) { _shadingModel = VolumeSettings::Isosurface; }
|
|
virtual void apply(MaximumIntensityProjectionProperty& /*mip*/) { _shadingModel = VolumeSettings::MaximumIntensityProjection; }
|
|
virtual void apply(LightingProperty& /*lp*/) { _shadingModel = VolumeSettings::Light; }
|
|
virtual void apply(TransferFunctionProperty& /*tf*/) { _usesTransferFunction = true; }
|
|
virtual void apply(VolumeSettings& vs) { _shadingModel = vs.getShadingModel(); }
|
|
|
|
VolumeSettings::ShadingModel _shadingModel;
|
|
bool _usesTransferFunction;
|
|
|
|
};
|
|
|
|
void MultipassTechnique::cull(osgUtil::CullVisitor* cv)
|
|
{
|
|
std::string traversalPass;
|
|
bool rttTraversal = cv->getUserValue("VolumeSceneTraversal", traversalPass) && traversalPass=="RenderToTexture";
|
|
|
|
// OSG_NOTICE<<"MultipassTechnique::cull() traversalPass="<<traversalPass<<std::endl;
|
|
osgVolume::VolumeScene* vs = 0;
|
|
osg::NodePath& nodePath = cv->getNodePath();
|
|
for(osg::NodePath::reverse_iterator itr = nodePath.rbegin();
|
|
(itr != nodePath.rend()) && (vs == 0);
|
|
++itr)
|
|
{
|
|
vs = dynamic_cast<osgVolume::VolumeScene*>(*itr);
|
|
}
|
|
|
|
RenderingMode renderingMode = computeRenderingMode();
|
|
|
|
if (rttTraversal)
|
|
{
|
|
if (vs)
|
|
{
|
|
MultipassTileData* tileData = dynamic_cast<MultipassTileData*>(vs->tileVisited(cv, getVolumeTile()));
|
|
if (tileData)
|
|
{
|
|
switch(renderingMode)
|
|
{
|
|
case(CUBE):
|
|
{
|
|
// no work required to pre-render
|
|
break;
|
|
}
|
|
case(HULL):
|
|
{
|
|
if (tileData->frontFaceRttCamera.valid()) tileData->frontFaceRttCamera->accept(*cv);
|
|
break;
|
|
}
|
|
case(CUBE_AND_HULL):
|
|
{
|
|
// OSG_NOTICE<<"Doing pre-rendering"<<std::endl;
|
|
if (tileData->frontFaceRttCamera.valid())
|
|
{
|
|
// OSG_NOTICE<<" frontFaceRttCamera"<<std::endl;
|
|
tileData->frontFaceRttCamera->accept(*cv);
|
|
}
|
|
if (tileData->backFaceRttCamera.valid())
|
|
{
|
|
// OSG_NOTICE<<" backFaceRttCamera"<<std::endl;
|
|
tileData->backFaceRttCamera->accept(*cv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
osg::BoundingBox bb;
|
|
if (_transform.valid()) bb.expandBy(_transform->getBound());
|
|
if (getVolumeTile()->getNumChildren()>0) bb.expandBy(getVolumeTile()->getBound());
|
|
cv->updateCalculatedNearFar(*(cv->getModelViewMatrix()),bb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
int shaderMask = 0;
|
|
if (_volumeTile->getLayer()->getProperty())
|
|
{
|
|
ShadingModelVisitor smv;
|
|
|
|
_volumeTile->getLayer()->getProperty()->accept(smv);
|
|
|
|
if (smv._usesTransferFunction)
|
|
{
|
|
shaderMask |= TF_SHADERS;
|
|
}
|
|
|
|
switch(smv._shadingModel)
|
|
{
|
|
case(VolumeSettings::Isosurface): shaderMask |= ISO_SHADERS; break;
|
|
case(VolumeSettings::MaximumIntensityProjection): shaderMask |= MIP_SHADERS; break;
|
|
case(VolumeSettings::Light): shaderMask |= LIT_SHADERS; break;
|
|
default: shaderMask |= STANDARD_SHADERS; break;
|
|
}
|
|
}
|
|
|
|
switch(renderingMode)
|
|
{
|
|
case(CUBE): shaderMask |= CUBE_SHADERS; break;
|
|
case(HULL): shaderMask |= HULL_SHADERS; break;
|
|
case(CUBE_AND_HULL): shaderMask |= CUBE_AND_HULL_SHADERS; break;
|
|
}
|
|
|
|
if (vs)
|
|
{
|
|
MultipassTileData* tileData = dynamic_cast<MultipassTileData*>(vs->getTileData(cv, getVolumeTile()));
|
|
if (tileData)
|
|
{
|
|
Locator* tileLocator = _volumeTile->getLocator();
|
|
osg::Matrixd tileToEye = tileLocator->getTransform() * (*(cv->getModelViewMatrix()));
|
|
osg::Matrixd eyeToTile;
|
|
eyeToTile.invert(tileToEye);
|
|
|
|
tileData->eyeToTileUniform->set(osg::Matrixf(eyeToTile));
|
|
|
|
Locator* layerLocator = _volumeTile->getLayer()->getLocator();
|
|
if (tileLocator==layerLocator)
|
|
{
|
|
tileData->tileToImageUniform->set(osg::Matrixf::identity());
|
|
}
|
|
else
|
|
{
|
|
osg::Matrixd tileToImage(tileLocator->getTransform() * osg::Matrixd::inverse(layerLocator->getTransform()));
|
|
tileData->tileToImageUniform->set(osg::Matrixf(tileToImage));
|
|
}
|
|
|
|
|
|
// OSG_NOTICE<<"Updating texgen"<<std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
osg::ref_ptr<osg::StateSet> moving_stateset = (_whenMovingStateSet.valid() && isMoving(cv)) ? _whenMovingStateSet : 0;
|
|
|
|
if (moving_stateset.valid())
|
|
{
|
|
// OSG_NOTICE<<"Using MovingStateSet"<<std::endl;
|
|
cv->pushStateSet(moving_stateset.get());
|
|
}
|
|
|
|
osg::ref_ptr<osg::StateSet> program_stateset = _stateSetMap[shaderMask];
|
|
if (program_stateset.valid())
|
|
{
|
|
// OSG_NOTICE<<"Have program_stateset OK."<<std::endl;
|
|
cv->pushStateSet(program_stateset.get());
|
|
cv->pushStateSet(_volumeRenderStateSet.get());
|
|
|
|
switch(renderingMode)
|
|
{
|
|
case(CUBE):
|
|
// OSG_NOTICE<<"Travering Transform for CUBE rendering"<<std::endl;
|
|
_transform->accept(*cv);
|
|
break;
|
|
case(HULL):
|
|
// OSG_NOTICE<<"Travering children for HULL rendering"<<std::endl;
|
|
getVolumeTile()->osg::Group::traverse(*cv);
|
|
break;
|
|
case(CUBE_AND_HULL):
|
|
// OSG_NOTICE<<"Travering Transform for CUBE_AND_HULL rendering"<<std::endl;
|
|
_transform->accept(*cv);
|
|
//getVolumeTile()->osg::Group::traverse(*cv);
|
|
break;
|
|
}
|
|
|
|
cv->popStateSet();
|
|
cv->popStateSet();
|
|
}
|
|
else
|
|
{
|
|
OSG_NOTICE<<"Warning: No program available for required shader mask "<<shaderMask<<std::endl;
|
|
}
|
|
|
|
|
|
if (moving_stateset.valid())
|
|
{
|
|
// OSG_NOTICE<<"Using MovingStateSet"<<std::endl;
|
|
cv->popStateSet();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MultipassTechnique::cleanSceneGraph()
|
|
{
|
|
OSG_NOTICE<<"MultipassTechnique::cleanSceneGraph()"<<std::endl;
|
|
}
|
|
|
|
void MultipassTechnique::traverse(osg::NodeVisitor& nv)
|
|
{
|
|
// OSG_NOTICE<<"MultipassTechnique::traverse(osg::NodeVisitor& nv)"<<std::endl;
|
|
if (!_volumeTile) return;
|
|
|
|
// if app traversal update the frame count.
|
|
if (nv.getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR)
|
|
{
|
|
if (_volumeTile->getDirty()) _volumeTile->init();
|
|
|
|
osgUtil::UpdateVisitor* uv = nv.asUpdateVisitor();
|
|
if (uv)
|
|
{
|
|
update(uv);
|
|
return;
|
|
}
|
|
|
|
}
|
|
else if (nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR)
|
|
{
|
|
osgUtil::CullVisitor* cv = nv.asCullVisitor();
|
|
if (cv)
|
|
{
|
|
cull(cv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (_volumeTile->getDirty())
|
|
{
|
|
OSG_INFO<<"******* Doing init ***********"<<std::endl;
|
|
_volumeTile->init();
|
|
}
|
|
}
|
|
|
|
|
|
} // end of osgVolume namespace
|