/* -*-c++-*- osgVisual - Copyright (C) 2009-2010 Torben Dannhauer * * This library is based on OpenSceneGraph, 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. * * osgVisual requires for some proprietary modules a license from the correspondig manufacturer. * You have to aquire licenses for all used proprietary modules. * * 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 using namespace osgVisual; visual_distortion::visual_distortion(osgViewer::Viewer* viewer_, osg::ArgumentParser& arguments_) : viewer(viewer_), arguments(arguments_) { OSG_NOTIFY (osg::ALWAYS ) << "visual_distortion instantiated." << std::endl; initialized = false; distortionEnabled = false; parser = new CameraConfigParser(); distortedGraph = NULL; cleanGraph = NULL; distortMapTexture = NULL; blendMapTexture = NULL; } visual_distortion::~visual_distortion(void) { OSG_NOTIFY (osg::ALWAYS ) << "visual_distortion destroyed." << std::endl; } bool visual_distortion::processCommandlineparameters() { arguments.getApplicationUsage()->addCommandLineOption("-C ","Setup the channel name, this is the preset for blendmap, distortionmap and camera configuration file."); arguments.getApplicationUsage()->addCommandLineOption("--fbo","Use Frame Buffer Object for render to texture, where supported."); arguments.getApplicationUsage()->addCommandLineOption("--fb","Use FrameBuffer for render to texture."); arguments.getApplicationUsage()->addCommandLineOption("--pbuffer","Use Pixel Buffer for render to texture, where supported."); arguments.getApplicationUsage()->addCommandLineOption("--window","Use a seperate Window for render to texture."); arguments.getApplicationUsage()->addCommandLineOption("--width","Set the width of the render to texture."); arguments.getApplicationUsage()->addCommandLineOption("--height","Set the height of the render to texture."); arguments.getApplicationUsage()->addCommandLineOption("--texture-rectangle","Use osg::TextureRectangle for doing the render to texure to."); arguments.getApplicationUsage()->addCommandLineOption("--distortionmap","Set a distortion map file name."); arguments.getApplicationUsage()->addCommandLineOption("--blendmap","Set a blend map file name."); arguments.getApplicationUsage()->addCommandLineOption("--shaderDistortion","Use OpenGL shader for distortion and blending."); arguments.getApplicationUsage()->addKeyboardMouseBinding("d", "Toggle Distortion."); // Read out Distortion CMDLine arguments tex_width = 2048; tex_height = 2048; while (arguments.read("--width", tex_width)) {} while (arguments.read("--height", tex_height)) {} renderImplementation = osg::Camera::FRAME_BUFFER_OBJECT; while (arguments.read("--fbo")) { renderImplementation = osg::Camera::FRAME_BUFFER_OBJECT; } while (arguments.read("--pbuffer")) { renderImplementation = osg::Camera::PIXEL_BUFFER; } while (arguments.read("--pbuffer-rtt")) { renderImplementation = osg::Camera::PIXEL_BUFFER_RTT; } while (arguments.read("--fb")) { renderImplementation = osg::Camera::FRAME_BUFFER; } while (arguments.read("--window")) { renderImplementation = osg::Camera::SEPERATE_WINDOW; } useTextureRectangle = false; while (arguments.read("--texture-rectangle")) { useTextureRectangle = true; } useShaderDistortion = false; while (arguments.read("--shaderDistortion")) { useShaderDistortion = true; } useHDR = false; while (arguments.read("--hdr")) { useHDR = true; } distortMapFileName = "..\\resources\\distortion\\distort_distDisabled.bmp"; while (arguments.read("--distortionmap", distortMapFileName)) { } blendMapFileName = "..\\resources\\distortion\\blend_distDisabled.bmp"; while (arguments.read("--blendmap", blendMapFileName)) { } std::string pre_cfg("..\\resources\\distortion\\view_"); std::string pre_distortion("..\\resources\\distortion\\distort_"); std::string pre_blend("..\\resources\\distortion\\blend_"); std::string post_cfg(".cfg"); std::string post_distortion(".bmp"); std::string post_blend(".bmp"); std::string channelname; while (arguments.read("-C",channelname)) { OSG_NOTIFY(osg::INFO) << "channelname:" << channelname << std::endl; // load channel config parser->parseConfigFile((pre_cfg+channelname+post_cfg).data()); // load channel blendmap blendMapFileName = (pre_blend+channelname+post_blend).data(); // load channel distortionmap distortMapFileName = (pre_distortion+channelname+post_distortion).data(); // set channel frustum if( parser->isConfigParsed()) viewer->getCamera()->setProjectionMatrixAsFrustum((parser->getFrustumDataset())[0], (parser->getFrustumDataset())[1], (parser->getFrustumDataset())[2], (parser->getFrustumDataset())[3], (parser->getFrustumDataset())[4], (parser->getFrustumDataset())[5]); else OSG_NOTIFY(osg::WARN) << "WARNING: Unable to parse Frustum values from '" << pre_cfg<addChild( this ); // add the keyboard handler for toggle distortion kBEventHandler = new ToggleDistortionKBEventHandler( this ); viewer->addEventHandler( kBEventHandler ); cleanGraph = subgraph; distortedGraph = createPreRenderSubGraph( subgraph, clearColor ); // Create and install updateCallback (to get called for copying the main cameras view matrixes to the PRE_RENDER camera) // -- must be called _AFTER_ createPreRenderSubGraph() (necessary because sceneCamera is set by createPreRenderSubGraph()) updateCallback = new distortionUpdateCallback( viewer, sceneCamera ); this->setUpdateCallback( updateCallback ); // Note down state flags.. initialized = true; distortionEnabled = true; distortionEnabled = true; viewer->setSceneData( distortedGraph ); return distortedGraph; } void visual_distortion::shutdown() { if(initialized) { // Remove this Node from scenegraph cleanGraph->removeChild( this ); // Remove update callback this->removeUpdateCallback( updateCallback ); updateCallback = NULL; OSG_NOTIFY (osg::ALWAYS ) << "visual_distortion shutdown complete." << std::endl; } } void visual_distortion::distortionUpdateCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) { // Copy Main Camera's matrixes to the PRE_RENDER Camera. sceneCamera->setViewMatrix( viewer->getCamera()->getViewMatrix() ); sceneCamera->setProjectionMatrix( viewer->getCamera()->getProjectionMatrix() ); } void visual_distortion::toggleDistortion() { // Toggle //// If not Distorted: enabled distortion if( !distortionEnabled ) { distortionEnabled = true; viewer->setSceneData( distortedGraph ); } else // .. otherwise disable distortion { distortionEnabled = false; viewer->setSceneData( cleanGraph ); } } osg::Group* visual_distortion::createPreRenderSubGraph(osg::Group* subgraph, const osg::Vec4& clearColor ) { if (!subgraph) return 0; // create a group to contain the flag and the pre rendering camera. osg::Group* parent = new osg::Group; // texture to render to and to use for rendering of flag. osg::Texture* texture = 0; if (useTextureRectangle) { osg::TextureRectangle* textureRect = new osg::TextureRectangle; textureRect->setTextureSize(tex_width, tex_height); textureRect->setInternalFormat(GL_RGBA); textureRect->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); textureRect->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); texture = textureRect; } else { osg::Texture2D* texture2D = new osg::Texture2D; texture2D->setTextureSize(tex_width, tex_height); texture2D->setInternalFormat(GL_RGBA); texture2D->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); texture2D->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); texture = texture2D; } if (useHDR) { texture->setInternalFormat(GL_RGBA16F_ARB); texture->setSourceFormat(GL_RGBA); texture->setSourceType(GL_FLOAT); } // load a distortion map texture file if ( osgDB::fileExists( distortMapFileName ) ) distortMapTexture = loadTexture(distortMapFileName); else { OSG_NOTIFY(osg::FATAL) << "ERROR: Distortionmap file'" << distortMapFileName << "' not found! Please change the channelname, filename or copy the desired file to the applications root folder!" << std::endl; exit(-1); } // load a blend map texture file if ( osgDB::fileExists( blendMapFileName ) ) blendMapTexture = loadTexture(blendMapFileName); else { OSG_NOTIFY(osg::FATAL) << "ERROR: Blendmap file'" << blendMapFileName << "' not found! Please change the channelname, filename or copy the desired file to the applications root folder!" << std::endl; exit(-1); } // set up the plane to render the rendered view. { // create the quad to visualize. osg::Geometry* polyGeom = new osg::Geometry(); polyGeom->setSupportsDisplayList(false); osg::Vec3 origin(0.0f,0.0f,0.0f); osg::Vec3 xAxis(1.0f,0.0f,0.0f); osg::Vec3 yAxis(0.0f,1.0f,0.0f); osg::Vec3 zAxis(0.0f,0.0f,1.0f); float height = 1024.0f; float width = 1280.0f; int noSteps = 128; if (useShaderDistortion) noSteps = 2; osg::Vec3Array* vertices = new osg::Vec3Array; osg::Vec2Array* texcoords = new osg::Vec2Array; osg::Vec2Array* texcoords2 = useShaderDistortion?NULL:(new osg::Vec2Array); osg::Vec4Array* colors = new osg::Vec4Array; osg::Vec3 bottom = origin; osg::Vec3 dx = xAxis*(width/((float)(noSteps-1))); osg::Vec3 dy = yAxis*(height/((float)(noSteps-1))); osg::Vec2 bottom_texcoord(0.0f,0.0f); osg::Vec2 dx_texcoord(1.0f/(float)(noSteps-1),0.0f); osg::Vec2 dy_texcoord(0.0f,1.0f/(float)(noSteps-1)); osg::Vec3 cursor = bottom; osg::Vec2 texcoord = bottom_texcoord; int i,j; for(i=0;ipush_back(cursor); if (distortMapTexture && !useShaderDistortion) { osg::Vec2 imgcoord = osg::Vec2((distortMapTexture->getImage(0)->s()-1) * texcoord.x(), (distortMapTexture->getImage(0)->t()-1) * texcoord.y()); unsigned char* pPixel = &distortMapTexture->getImage(0)->data()[(int)imgcoord.y()*distortMapTexture->getImage(0)->getRowSizeInBytes()+ (int)imgcoord.x()*distortMapTexture->getImage(0)->getPixelSizeInBits()/8]; texcoords->push_back(osg::Vec2(((float)pPixel[2] / 255.0f + (pPixel[0] % 16)) / 16.0f, 1.0f - ((float)pPixel[1] / 255.0f + (pPixel[0] / 16)) / 16.0f)); texcoords2->push_back(osg::Vec2(texcoord.x(), texcoord.y())); } else texcoords->push_back(osg::Vec2(texcoord.x(), texcoord.y())); colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); cursor += dx; texcoord += dx_texcoord; } } // pass the created vertex array to the points geometry object. polyGeom->setVertexArray(vertices); polyGeom->setColorArray(colors); polyGeom->setColorBinding(osg::Geometry::BIND_PER_VERTEX); polyGeom->setTexCoordArray(0,texcoords); if (!useShaderDistortion) polyGeom->setTexCoordArray(2,texcoords2); for(i=0;ipush_back(j+(i+1)*noSteps); elements->push_back(j+(i)*noSteps); } polyGeom->addPrimitiveSet(elements); } // new we need to add the texture to the Drawable, we do so by creating a // StateSet to contain the Texture StateAttribute. osg::StateSet* stateset = polyGeom->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); osg::Geode* geode = new osg::Geode(); geode->addDrawable(polyGeom); if (useShaderDistortion) { // apply the distortion map texture if (distortMapTexture) geode->getOrCreateStateSet()->setTextureAttributeAndModes(1, distortMapTexture, osg::StateAttribute::ON); } // apply the blend map texture if (blendMapTexture) geode->getOrCreateStateSet()->setTextureAttributeAndModes(2, blendMapTexture, osg::StateAttribute::ON); // set up the camera to render the textured quad osg::Camera* camera = new osg::Camera; // just inherit the main cameras view camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); camera->setViewMatrix(osg::Matrix::identity()); //camera->setProjectionMatrixAsOrtho2D(0,viewer->getCamera()->getViewport()->width(),0,viewer->getCamera()->getViewport()->height()); camera->setProjectionMatrixAsOrtho2D(0,1280,0,1024); // set the camera to render before the main camera. camera->setRenderOrder(osg::Camera::POST_RENDER, 200); // only clear the depth buffer camera->setClearMask(0); // add subgraph to render camera->addChild(geode); if (useShaderDistortion) { // create shaders for distortion osg::StateSet* ss = geode->getOrCreateStateSet(); osg::Program* distortProgram = new osg::Program; distortProgram->setName( "distortion" ); osg::Shader* distortVertObj = new osg::Shader( osg::Shader::VERTEX ); osg::Shader* distortFragObj = new osg::Shader( osg::Shader::FRAGMENT ); if (loadShaderSource( distortVertObj, "shader.vert" ) && loadShaderSource( distortFragObj, "shader.frag" ) && distortMapTexture) { distortProgram->addShader( distortFragObj ); distortProgram->addShader( distortVertObj ); ss->addUniform( new osg::Uniform("textureImage", 0) ); ss->addUniform( new osg::Uniform("textureDistort", 1) ); ss->addUniform( new osg::Uniform("textureBlend", 2) ); ss->setAttributeAndModes(distortProgram, osg::StateAttribute::ON); } else { OSG_NOTIFY(osg::FATAL) << "##################################" << std::endl << "## Unable to activate ShaderDistortion!" << std::endl << "## ABORT" << std::endl << "##################################" << std::endl; exit(0); } } parent->addChild(camera); } // then create the camera node to do the render to texture { osg::Camera* camera = new osg::Camera; // set up the background color and clear mask. camera->setClearColor(clearColor); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // just inherit the main cameras view /* ABSOLUTE_RF required to make intersections possible. Disadvantage of ABOLUTE_RF : the maincameras view matrix and projection matrix has to copied to the PRE_RENDER camera by our self. Therefore an update callback is installed. */ camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); camera->setProjectionMatrix(osg::Matrixd::identity()); camera->setViewMatrix(osg::Matrixd::identity()); // set viewport camera->setViewport(0,0,tex_width,tex_height); // set the camera to render before the main camera. camera->setRenderOrder(osg::Camera::PRE_RENDER, 0); sceneCamera = camera; // tell the camera to use OpenGL frame buffer object where supported. camera->setRenderTargetImplementation(renderImplementation); // attach the texture and use it as the color buffer. camera->attach(osg::Camera::COLOR_BUFFER, texture); // No Multisampling/Antialiasing //camera->attach(osg::Camera::COLOR_BUFFER, texture, 0, 0, false, 4, 4); // 4x Multisampling/Antialiasing // add subgraph to render camera->addChild(subgraph); parent->addChild(camera); } return parent; } bool visual_distortion::loadShaderSource( osg::Shader* shader, const std::string& fileName ) { std::string foundFileName = osgDB::findDataFile(fileName); if (foundFileName.length() != 0 ) { return shader->loadShaderSourceFromFile( foundFileName.c_str() ); } OSG_NOTIFY(osg::WARN) << "File \"" << fileName << "\" not found." << std::endl; return false; } osg::Texture* visual_distortion::loadTexture( const std::string& fileName ) { std::string foundFileName = osgDB::findDataFile(fileName); if (foundFileName.length() != 0 ) { // load distortion map texture file osg::Image* image = osgDB::readImageFile(foundFileName); if (image) { osg::Texture2D* texture = new osg::Texture2D; texture->setImage(image); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE); texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE); return texture; } } OSG_NOTIFY(osg::WARN) << "File \"" << fileName << "\" not found." << std::endl; return NULL; }