/* OpenSceneGraph example, osganimate. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include class ControlPoints : public osg::Referenced { public: ControlPoints(): bottom_left(-1.0,-1.0), bottom_right(1.0,-1.0), top_left(-1.0,1.0), top_right(1.0,1.0) {} void reset() { bottom_left.set(-1.0,-1.0); bottom_right.set(1.0,-1.0); top_left.set(-1.0,1.0); top_right.set(1.0,1.0); } ControlPoints& operator = (const ControlPoints& rhs) { if (&rhs==this) return *this; bottom_left = rhs.bottom_left; bottom_right = rhs.bottom_right; top_left = rhs.top_left; top_right = rhs.top_right; return *this; } osg::Vec2d bottom_left; osg::Vec2d bottom_right; osg::Vec2d top_left; osg::Vec2d top_right; }; class Keystone : public osg::Referenced { public: Keystone(): translate(0.0,0.0), shear(0.0,0.0), scale(1.0,1.0), taper(1.0,1.0), angle(0.0) { } double angleBetweenVectors(const osg::Vec2d& v1, const osg::Vec2d& v2) const { osg::Vec2d v3(-v2.y(), v2.x()); double p1 = v1*v2; double p2 = v1*v3; double a = atan2(p2, p1); return a; } osg::Vec2d rotateVector(osg::Vec2d& v, double s, double c) const { return osg::Vec2d(v.x()*c-v.y()*s, v.y()*c+v.x()*s); } void updateKeystone(ControlPoints cp) { // compute translation translate = (cp.bottom_left+cp.bottom_right+cp.top_left+cp.top_right)*0.25; // adjust control points to fit translation cp.top_left -= translate; cp.top_right -= translate; cp.bottom_right -= translate; cp.bottom_left -= translate; osg::Vec2d mid_left = (cp.top_left+cp.bottom_left)*0.5; osg::Vec2d mid_right = (cp.top_right+cp.bottom_right)*0.5; osg::Vec2d mid_top = (cp.top_left+cp.top_right)*0.5; osg::Vec2d mid_bottom = (cp.bottom_left+cp.bottom_right)*0.5; shear.x() = (mid_top.x()-mid_bottom.x())/(mid_top.y()-mid_bottom.y()); shear.y() = (mid_right.y()-mid_left.y())/(mid_right.x()-mid_left.x()); cp.top_left.x() += cp.top_left.y() * shear.x(); cp.top_left.y() += cp.top_left.x() * shear.y(); cp.top_right.x() += cp.top_right.y() * shear.x(); cp.top_right.y() += cp.top_right.x() * shear.y(); cp.bottom_left.x() += cp.bottom_left.y() * shear.x(); cp.bottom_left.y() += cp.bottom_left.x() * shear.y(); cp.bottom_right.x() += cp.bottom_right.y() * shear.x(); cp.bottom_right.y() += cp.bottom_right.x() * shear.y(); #if 0 angle = (angleBetweenVectors(cp.top_left, osg::Vec2d(-1.0,1.0)) + angleBetweenVectors(cp.top_right, osg::Vec2d(1.0,1.0)) + angleBetweenVectors(cp.bottom_right, osg::Vec2d(1.0,-1.0)) + angleBetweenVectors(cp.bottom_left, osg::Vec2d(-1.0,-1.0)))*0.25; #endif OSG_NOTICE<<"cp.top_left="< geode = new osg::Geode; osg::ref_ptr geometry = new osg::Geometry; geode->addDrawable(geometry.get()); osg::ref_ptr colours = new osg::Vec4Array; colours->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); geometry->setColorArray(colours.get()); geometry->setColorBinding(osg::Geometry::BIND_OVERALL); osg::ref_ptr vertices = new osg::Vec3Array; geometry->setVertexArray(vertices.get()); // border line { unsigned int vi = vertices->size(); vertices->push_back(origin); vertices->push_back(origin+widthVector); vertices->push_back(origin+widthVector+heightVector); vertices->push_back(origin+heightVector); geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINE_LOOP, vi, 4)); } // cross lines { unsigned int vi = vertices->size(); vertices->push_back(origin); vertices->push_back(origin+widthVector+heightVector); vertices->push_back(origin+heightVector); vertices->push_back(origin+widthVector); geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINES, vi, 4)); } // vertices lines { unsigned int vi = vertices->size(); osg::Vec3 dv = widthVector/6.0; osg::Vec3 bv = origin+dv; osg::Vec3 tv = bv+heightVector; for(unsigned int i=0; i<5; ++i) { vertices->push_back(bv); vertices->push_back(tv); bv += dv; tv += dv; } geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINES, vi, 10)); } // horizontal lines { unsigned int vi = vertices->size(); osg::Vec3 dv = heightVector/6.0; osg::Vec3 bv = origin+dv; osg::Vec3 tv = bv+widthVector; for(unsigned int i=0; i<5; ++i) { vertices->push_back(bv); vertices->push_back(tv); bv += dv; tv += dv; } geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINES, vi, 10)); } geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); return geode.release(); } struct KeystoneUpdateCallback : public osg::Drawable::UpdateCallback { KeystoneUpdateCallback(Keystone* keystone=0):_keystone(keystone) {} KeystoneUpdateCallback(const KeystoneUpdateCallback&, const osg::CopyOp&) {} META_Object(osg,KeystoneUpdateCallback); /** do customized update code.*/ virtual void update(osg::NodeVisitor*, osg::Drawable* drawable) { update(dynamic_cast(drawable)); } void update(osg::Geometry* geometry) { if (!geometry) return; osg::Vec3Array* vertices = dynamic_cast(geometry->getVertexArray()); if (!vertices) return; double screenDistance = osg::DisplaySettings::instance()->getScreenDistance(); double screenWidth = osg::DisplaySettings::instance()->getScreenWidth(); double screenHeight = osg::DisplaySettings::instance()->getScreenHeight(); double tr = _keystone->taper.x(); double r_right = sqrt(tr); double r_left = r_right/tr; double sx = _keystone->scale.x(); double sy = _keystone->scale.y(); double tx = _keystone->translate.x(); double ty = _keystone->translate.y(); double shx = _keystone->shear.x(); double shy = _keystone->shear.y(); osg::Matrixd pm; pm.postMultRotate(osg::Quat(_keystone->angle, osg::Vec3d(0.0,0.0,1.0))); (*vertices)[0] = osg::Vec3(-screenWidth*sx*0.5*r_left + tx*screenWidth*0.5*r_left + shx*screenHeight, screenHeight*sy*0.5 + ty*screenHeight*0.5 - shy*screenWidth*0.5, -screenDistance*r_left) * pm; (*vertices)[1] = osg::Vec3(screenWidth*sx*0.5*r_right + tx*screenWidth*0.5*r_right + shx*screenHeight, screenHeight*sy*0.5 + ty*screenHeight*0.5 + shy*screenWidth*0.5, -screenDistance*r_right) * pm; (*vertices)[2] = osg::Vec3(screenWidth*sx*0.5*r_right + tx*screenWidth*0.5*r_right - shx*screenHeight,-screenHeight*sy*0.5 + ty*screenHeight*0.5 + shy*screenWidth*0.5, -screenDistance*r_right) * pm; (*vertices)[3] = osg::Vec3(-screenWidth*sx*0.5*r_left + tx*screenWidth*0.5*r_left - shx*screenHeight,-screenHeight*sy*0.5 + ty*screenHeight*0.5 - shy*screenWidth*0.5, -screenDistance*r_left) * pm; geometry->dirtyBound(); } osg::ref_ptr _keystone; }; osg::Geometry* createKeystoneDistortionMesh(Keystone* cp) { osg::ref_ptr geometry = new osg::Geometry; geometry->setUseDisplayList(false); osg::ref_ptr kuc = new KeystoneUpdateCallback(cp); geometry->setUpdateCallback(kuc.get()); osg::ref_ptr colours = new osg::Vec4Array; colours->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); geometry->setColorArray(colours.get()); geometry->setColorBinding(osg::Geometry::BIND_OVERALL); osg::ref_ptr vertices = new osg::Vec3Array; geometry->setVertexArray(vertices.get()); unsigned int vi = vertices->size(); vertices->resize(4); osg::ref_ptr texcoords = new osg::Vec2Array; geometry->setTexCoordArray(0, texcoords.get()); texcoords->push_back(osg::Vec2(0.0f,1.0f)); texcoords->push_back(osg::Vec2(1.0f,1.0f)); texcoords->push_back(osg::Vec2(1.0f,0.0f)); texcoords->push_back(osg::Vec2(0.0f,0.0f)); geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, vi, 4)); geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); kuc->update(geometry.get()); return geometry.release(); } void setUpViewForKeystone(osgViewer::View* view, Keystone* keystone) { int screenNum = 0; osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface(); if (!wsi) { OSG_NOTICE<<"Error, no WindowSystemInterface available, cannot create windows."<getScreenResolution(si, width, height); width/=2; height/=2; osg::ref_ptr traits = new osg::GraphicsContext::Traits; traits->hostName = si.hostName; traits->displayNum = si.displayNum; traits->screenNum = si.screenNum; traits->x = 0; traits->y = 0; traits->width = width; traits->height = height; traits->windowDecoration = false; traits->doubleBuffer = true; traits->sharedContext = 0; osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get()); if (!gc) { OSG_NOTICE<<"GraphicsWindow has not been created successfully."<setTextureSize(tex_width, tex_height); texture->setInternalFormat(GL_RGB); texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR); texture->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP_TO_EDGE); #if 0 osg::Camera::RenderTargetImplementation renderTargetImplementation = osg::Camera::SEPERATE_WINDOW; GLenum buffer = GL_FRONT; #else osg::Camera::RenderTargetImplementation renderTargetImplementation = osg::Camera::FRAME_BUFFER_OBJECT; GLenum buffer = GL_FRONT; #endif // front face { osg::ref_ptr camera = new osg::Camera; camera->setName("Render to texture camera"); camera->setGraphicsContext(gc.get()); camera->setViewport(new osg::Viewport(0,0,camera_width, camera_height)); camera->setDrawBuffer(buffer); camera->setReadBuffer(buffer); camera->setAllowEventFocus(false); // tell the camera to use OpenGL frame buffer object where supported. camera->setRenderTargetImplementation(renderTargetImplementation); // attach the texture and use it as the color buffer. camera->attach(osg::Camera::COLOR_BUFFER, texture); view->addSlave(camera.get(), osg::Matrixd(), osg::Matrixd()); } // distortion correction set up. { double screenDistance = osg::DisplaySettings::instance()->getScreenDistance(); double screenWidth = osg::DisplaySettings::instance()->getScreenWidth(); double screenHeight = osg::DisplaySettings::instance()->getScreenHeight(); double fovy = osg::RadiansToDegrees(2.0*atan2(screenHeight/2.0,screenDistance)); double aspectRatio = screenWidth/screenHeight; osg::Geode* geode = new osg::Geode(); geode->addDrawable(createKeystoneDistortionMesh(keystone)); // new we need to add the texture to the mesh, we do so by creating a // StateSet to contain the Texture StateAttribute. osg::StateSet* stateset = geode->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON); stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF); osg::TexMat* texmat = new osg::TexMat; texmat->setScaleByTextureRectangleSize(true); stateset->setTextureAttributeAndModes(0, texmat, osg::StateAttribute::ON); osg::ref_ptr camera = new osg::Camera; camera->setGraphicsContext(gc.get()); camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT ); camera->setClearColor( osg::Vec4(0.0,0.0,0.0,1.0) ); camera->setViewport(new osg::Viewport(0, 0, width, height)); GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT; camera->setDrawBuffer(buffer); camera->setReadBuffer(buffer); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setAllowEventFocus(false); camera->setInheritanceMask(camera->getInheritanceMask() & ~osg::CullSettings::CLEAR_COLOR & ~osg::CullSettings::COMPUTE_NEAR_FAR_MODE); //camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); camera->setViewMatrix(osg::Matrix::identity()); camera->setProjectionMatrixAsPerspective(fovy, aspectRatio, 0.1, 1000.0); // add subgraph to render camera->addChild(geode); camera->setName("DistortionCorrectionCamera"); view->addSlave(camera.get(), osg::Matrixd(), osg::Matrixd(), false); } } int main( int argc, char **argv ) { osg::ArgumentParser arguments(&argc,argv); // initialize the viewer. osgViewer::Viewer viewer(arguments); osg::ref_ptr keystone = new Keystone; double distance, width, view_angle; if (arguments.read("-p",distance, width, view_angle)) { keystone->taper.x() = (2.0 + (width/distance) * sin(osg::inDegrees(view_angle))) / (2.0 - (width/distance) * sin(osg::inDegrees(view_angle))); //scale.x() = 1.0/cos(osg::inDegrees(view_angle)); OSG_NOTICE<<"scale "<scale<taper<angle)) { OSG_NOTICE<<"angle = "<angle<angle = osg::inDegrees(keystone->angle); } if (arguments.read("-t",keystone->translate.x(), keystone->translate.y())) { OSG_NOTICE<<"translate = "<translate<scale.x(), keystone->scale.y())) { OSG_NOTICE<<"scale = "<scale<taper.x(), keystone->taper.y())) { OSG_NOTICE<<"taper = "<taper< model = osgDB::readNodeFiles(arguments); if (!model) { OSG_NOTICE<<"No models loaded, please specify a model file on the command line"< group = new osg::Group; group->addChild(model.get()); double screenWidth = osg::DisplaySettings::instance()->getScreenWidth(); double screenHeight = osg::DisplaySettings::instance()->getScreenHeight(); double screenDistance = osg::DisplaySettings::instance()->getScreenDistance(); double fovy = osg::RadiansToDegrees(2.0*atan2(screenHeight/2.0,screenDistance)); double aspectRatio = screenWidth/screenHeight; osg::ref_ptr camera = new osg::Camera; camera->setClearMask(0x0); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setRenderOrder(osg::Camera::NESTED_RENDER); camera->setProjectionMatrixAsPerspective(fovy, aspectRatio, 0.1, 1000.0); camera->addChild(createGrid(osg::Vec3(-screenWidth*0.5, -screenHeight*0.5, -screenDistance), osg::Vec3(screenWidth, 0.0, 0.0), osg::Vec3(0.0, screenHeight, 0.0), osg::Vec4(1.0,0.0,0.0,1.0))); viewer.setSceneData(group.get()); // add the state manipulator viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); // add the stats handler viewer.addEventHandler(new osgViewer::StatsHandler); // add camera manipulator viewer.setCameraManipulator(new osgGA::TrackballManipulator()); if (!oldStyleKeystone) { setUpViewForKeystone(&viewer, keystone); } viewer.realize(); viewer.getCamera()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); osg::Matrixd original_pm = viewer.getCamera()->getProjectionMatrix(); osg::Matrixd original_grid_pm = camera->getProjectionMatrix(); group->addChild(camera.get()); // Add keystone handler viewer.addEventHandler(new KeystoneHandler(keystone)); while(!viewer.done()) { viewer.advance(); viewer.eventTraversal(); viewer.updateTraversal(); if (oldStyleKeystone && keystone.valid()) { viewer.getCamera()->setProjectionMatrix(original_pm * keystone->computeKeystoneMatrix()); camera->setProjectionMatrix(original_grid_pm * keystone->computeKeystoneMatrix()); } viewer.renderingTraversals(); } return 0; }