]> git.mxchange.org Git - flightgear.git/commitdiff
panoramic spherical distortion
authorTim Moore <timoore33@gmail.com>
Mon, 22 Nov 2010 08:10:40 +0000 (09:10 +0100)
committerTim Moore <timoore33@gmail.com>
Sun, 28 Nov 2010 22:29:20 +0000 (23:29 +0100)
This supports the panoramic distortion option, as found in osgViewer, in
the camera file syntax of flightgear.

docs-mini/README.multiscreen
src/Main/CameraGroup.cxx
src/Main/CameraGroup.hxx

index 9954f13f9b62bd4d9d61836e07f8a16e26c1cd2e..042bbac8495384d328b72d4541a6df0b6e995efa 100644 (file)
@@ -133,6 +133,30 @@ window, camera, or gui tags.
   This specifies an orthographic view. The parameters are the sames as
   the frustum node's.
 
+  texture
+  This tag indicates that the camera renders to a texture instead of the
+  framebuffer. For now the following tags are supported, but obviously
+  different  texture formats should be specified too. 
+   name - string
+   The name of the texture. This can be referred to by other cameras.
+   width, height - double
+   The dimensions of the texture
+
+  panoramic-distortion
+  This tag cause the camera to create distortion geometry that
+  corrects for projection onto a spherical screen. It is equivalent to
+  the --panoramic-sd option to osgviewer.
+
+   texture - string
+   The name of a texture, created by another camera, that will be
+   rendered on the distortion correction geometry.
+
+   radius - double
+   Radius of string
+
+   collar - double
+   size of screen collar.
+
  gui
  This is a special camera node that displays the 2D GUI.
 
@@ -313,3 +337,61 @@ accounted for.
     </rendering>
   </sim>
 </PropertyList>
+
+This example renders the scene for projection onto a spherical screen.
+
+<PropertyList>
+  <sim>
+    <rendering>
+      <camera-group>
+        <camera>
+          <window>
+            <name type="string">main</name>
+            <host-name type="string"></host-name>
+            <display>0</display>
+            <screen>0</screen>
+            <!-- <fullscreen type = "bool">true</fullscreen>-->
+            <width>1024</width>
+            <height>768</height>
+          </window>
+          <view>
+            <heading-deg type = "double">0</heading-deg>
+          </view>
+          <frustum>
+            <top>0.133</top>
+            <bottom>-0.133</bottom>
+            <left>-.1668</left>
+            <right>.1668</right>
+            <near>0.4</near>
+            <far>120000.0</far>
+          </frustum>
+          <texture>
+            <name>mainview</name>
+            <width>1024</width>
+            <height>768</height>
+          </texture>
+        </camera>
+        <camera>
+          <window><name>main</name></window>
+          <ortho>
+            <top>768</top>
+            <bottom>0</bottom>
+            <left>0</left>
+            <right>1024</right>
+            <near>-1.0</near>
+            <far>1.0</far>
+          </ortho>
+          <panoramic-spherical>
+            <texture>mainview</texture>
+          </panoramic-spherical>
+        </camera>
+        <gui>
+          <window>
+            <name type="string">main</name>
+          </window>
+        </gui>
+      </camera-group>
+    </rendering>
+  </sim>
+</PropertyList>
index 0fb2f2e5663038859be4a27884bd745a87d3f54e..ae4cf48dfcc42e69ec483db7c5d3a83d6c43f01a 100644 (file)
 
 #include <osg/Camera>
 #include <osg/GraphicsContext>
+#include <osg/io_utils>
 #include <osg/Math>
 #include <osg/Matrix>
+#include <osg/Notify>
+#include <osg/Program>
 #include <osg/Quat>
+#include <osg/TexMat>
 #include <osg/Vec3d>
 #include <osg/Viewport>
 
@@ -148,7 +152,18 @@ CameraInfo* CameraGroup::addCamera(unsigned flags, Camera* camera,
         // resized; if the the viewport isn't copied here, it gets updated
         // twice and ends up with the wrong value.
         farCamera->setViewport(simgear::clone(camera->getViewport()));
-        _viewer->addSlave(farCamera, view, projection, useMasterSceneData);
+        farCamera->setDrawBuffer(camera->getDrawBuffer());
+        farCamera->setReadBuffer(camera->getReadBuffer());
+        farCamera->setRenderTargetImplementation(
+            camera->getRenderTargetImplementation());
+        const Camera::BufferAttachmentMap& bufferMap
+            = camera->getBufferAttachmentMap();
+        if (bufferMap.count(Camera::COLOR_BUFFER) != 0) {
+            farCamera->attach(
+                Camera::COLOR_BUFFER,
+                bufferMap.find(Camera::COLOR_BUFFER)->second._texture);
+        }
+        _viewer->addSlave(farCamera, projection, view, useMasterSceneData);
         installCullVisitor(farCamera);
         info->farCamera = farCamera;
         info->farSlaveIndex = _viewer->getNumSlaves() - 1;
@@ -156,7 +171,7 @@ CameraInfo* CameraGroup::addCamera(unsigned flags, Camera* camera,
         camera->setCullMask(camera->getCullMask() & ~simgear::BACKGROUND_BIT);
         camera->setClearMask(GL_DEPTH_BUFFER_BIT);
     }
-    _viewer->addSlave(camera, view, projection, useMasterSceneData);
+    _viewer->addSlave(camera, projection, view, useMasterSceneData);
     installCullVisitor(camera);
     info->camera = camera;
     info->slaveIndex = _viewer->getNumSlaves() - 1;
@@ -297,6 +312,199 @@ void buildViewport(flightgear::CameraInfo* info, SGPropertyNode* viewportNode,
 namespace flightgear
 {
 
+// Mostly copied from osg's osgViewer/View.cpp
+
+static osg::Geometry* createParoramicSphericalDisplayDistortionMesh(
+    const Vec3& origin, const Vec3& widthVector, const Vec3& heightVector,
+    double sphere_radius, double collar_radius,
+    Image* intensityMap = 0, const Matrix& projectorMatrix = Matrix())
+{
+    osg::Vec3d center(0.0,0.0,0.0);
+    osg::Vec3d eye(0.0,0.0,0.0);
+
+    double distance = sqrt(sphere_radius*sphere_radius - collar_radius*collar_radius);
+    bool flip = false;
+    bool texcoord_flip = false;
+
+    osg::Vec3d projector = eye - osg::Vec3d(0.0,0.0, distance);
+
+
+    OSG_INFO<<"createParoramicSphericalDisplayDistortionMesh : Projector position = "<<projector<<std::endl;
+    OSG_INFO<<"createParoramicSphericalDisplayDistortionMesh : distance = "<<distance<<std::endl;
+
+    // create the quad to visualize.
+    osg::Geometry* geometry = new osg::Geometry();
+
+    geometry->setSupportsDisplayList(false);
+
+    osg::Vec3 xAxis(widthVector);
+    float width = widthVector.length();
+    xAxis /= width;
+
+    osg::Vec3 yAxis(heightVector);
+    float height = heightVector.length();
+    yAxis /= height;
+
+    int noSteps = 160;
+
+    osg::Vec3Array* vertices = new osg::Vec3Array;
+    osg::Vec2Array* texcoords0 = new osg::Vec2Array;
+    osg::Vec2Array* texcoords1 = intensityMap==0 ? new osg::Vec2Array : 0;
+    osg::Vec4Array* colors = new osg::Vec4Array;
+
+    osg::Vec3 bottom = origin;
+    osg::Vec3 dx = xAxis*(width/((float)(noSteps-2)));
+    osg::Vec3 dy = yAxis*(height/((float)(noSteps-1)));
+
+    osg::Vec3 top = origin + yAxis*height;
+
+    osg::Vec3 screenCenter = origin + widthVector*0.5f + heightVector*0.5f;
+    float screenRadius = heightVector.length() * 0.5f;
+
+    geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
+
+    for(int i=0;i<noSteps;++i)
+    {
+        osg::Vec3 cursor = bottom+dy*(float)i;
+        for(int j=0;j<noSteps;++j)
+        {
+            osg::Vec2 texcoord(double(i)/double(noSteps-1), double(j)/double(noSteps-1));
+            double theta = texcoord.x() * 2.0 * osg::PI;
+            double phi = (1.0-texcoord.y()) * osg::PI;
+
+            if (texcoord_flip) texcoord.y() = 1.0f - texcoord.y();
+
+            osg::Vec3 pos(sin(phi)*sin(theta), sin(phi)*cos(theta), cos(phi));
+            pos = pos*projectorMatrix;
+
+            double alpha = atan2(pos.x(), pos.y());
+            if (alpha<0.0) alpha += 2.0*osg::PI;
+
+            double beta = atan2(sqrt(pos.x()*pos.x() + pos.y()*pos.y()), pos.z());
+            if (beta<0.0) beta += 2.0*osg::PI;
+
+            double gamma = atan2(sqrt(double(pos.x()*pos.x() + pos.y()*pos.y())), double(pos.z()+distance));
+            if (gamma<0.0) gamma += 2.0*osg::PI;
+
+
+            osg::Vec3 v = screenCenter + osg::Vec3(sin(alpha)*gamma*2.0/osg::PI, -cos(alpha)*gamma*2.0/osg::PI, 0.0f)*screenRadius;
+
+            if (flip)
+                vertices->push_back(osg::Vec3(v.x(), top.y()-(v.y()-origin.y()),v.z()));
+            else
+                vertices->push_back(v);
+
+            texcoords0->push_back( texcoord );
+
+            osg::Vec2 texcoord1(alpha/(2.0*osg::PI), 1.0f - beta/osg::PI);
+            if (intensityMap)
+            {
+                colors->push_back(intensityMap->getColor(texcoord1));
+            }
+            else
+            {
+                colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
+                if (texcoords1) texcoords1->push_back( texcoord1 );
+            }
+
+
+        }
+    }
+
+
+    // pass the created vertex array to the points geometry object.
+    geometry->setVertexArray(vertices);
+
+    geometry->setColorArray(colors);
+    geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
+
+    geometry->setTexCoordArray(0,texcoords0);
+    if (texcoords1) geometry->setTexCoordArray(1,texcoords1);
+
+    osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
+    geometry->addPrimitiveSet(elements);
+
+    for(int i=0;i<noSteps-1;++i)
+    {
+        for(int j=0;j<noSteps-1;++j)
+        {
+            int i1 = j+(i+1)*noSteps;
+            int i2 = j+(i)*noSteps;
+            int i3 = j+1+(i)*noSteps;
+            int i4 = j+1+(i+1)*noSteps;
+
+            osg::Vec3& v1 = (*vertices)[i1];
+            osg::Vec3& v2 = (*vertices)[i2];
+            osg::Vec3& v3 = (*vertices)[i3];
+            osg::Vec3& v4 = (*vertices)[i4];
+
+            if ((v1-screenCenter).length()>screenRadius) continue;
+            if ((v2-screenCenter).length()>screenRadius) continue;
+            if ((v3-screenCenter).length()>screenRadius) continue;
+            if ((v4-screenCenter).length()>screenRadius) continue;
+
+            elements->push_back(i1);
+            elements->push_back(i2);
+            elements->push_back(i3);
+
+            elements->push_back(i1);
+            elements->push_back(i3);
+            elements->push_back(i4);
+        }
+    }
+
+    return geometry;
+}
+
+void CameraGroup::buildDistortionCamera(const SGPropertyNode* psNode,
+                                        Camera* camera)
+{
+    const SGPropertyNode* texNode = psNode->getNode("texture");
+    if (!texNode) {
+        // error
+        return;
+    }
+    string texName = texNode->getStringValue();
+    TextureMap::iterator itr = _textureTargets.find(texName);
+    if (itr == _textureTargets.end()) {
+        // error
+        return;
+    }
+    Viewport* viewport = camera->getViewport();
+    float width = viewport->width();
+    float height = viewport->height();
+    TextureRectangle* texRect = itr->second.get();
+    double radius = psNode->getDoubleValue("radius", 1.0);
+    double collar = psNode->getDoubleValue("collar", 0.45);
+    Geode* geode = new Geode();
+    geode->addDrawable(createParoramicSphericalDisplayDistortionMesh(
+                           Vec3(0.0f,0.0f,0.0f), Vec3(width,0.0f,0.0f),
+                           Vec3(0.0f,height,0.0f), radius, collar));
+
+    // new we need to add the texture to the mesh, we do so by creating a
+    // StateSet to contain the Texture StateAttribute.
+    StateSet* stateset = geode->getOrCreateStateSet();
+    stateset->setTextureAttributeAndModes(0, texRect, StateAttribute::ON);
+    stateset->setMode(GL_LIGHTING, StateAttribute::OFF);
+
+    TexMat* texmat = new TexMat;
+    texmat->setScaleByTextureRectangleSize(true);
+    stateset->setTextureAttributeAndModes(0, texmat, osg::StateAttribute::ON);
+#if 0
+    if (!applyIntensityMapAsColours && intensityMap)
+    {
+        stateset->setTextureAttributeAndModes(1, new osg::Texture2D(intensityMap), osg::StateAttribute::ON);
+    }
+#endif
+    // add subgraph to render
+    camera->addChild(geode);
+    camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    camera->setClearColor(osg::Vec4(0.0, 0.0, 0.0, 1.0));
+    camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
+    camera->setCullingMode(osg::CullSettings::NO_CULLING);
+    camera->setName("DistortionCorrectionCamera");
+}
+
 CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode)
 {
     WindowBuilder *wBuild = WindowBuilder::getWindowBuilder();
@@ -390,13 +598,43 @@ CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode)
         double sheary = cameraNode->getDoubleValue("shear-y", 0);
         pOff.makeTranslate(-shearx, -sheary, 0);
     }
-    CameraInfo* info = addCamera(cameraFlags, camera, pOff, vOff);
+    const SGPropertyNode* textureNode = cameraNode->getNode("texture");
+    if (textureNode) {
+        string texName = textureNode->getStringValue("name");
+        int tex_width = textureNode->getIntValue("width");
+        int tex_height = textureNode->getIntValue("height");
+        TextureRectangle* texture = new TextureRectangle;
+
+        texture->setTextureSize(tex_width, tex_height);
+        texture->setInternalFormat(GL_RGB);
+        texture->setFilter(Texture::MIN_FILTER, Texture::LINEAR);
+        texture->setFilter(Texture::MAG_FILTER, Texture::LINEAR);
+        texture->setWrap(Texture::WRAP_S, Texture::CLAMP_TO_EDGE);
+        texture->setWrap(Texture::WRAP_T, Texture::CLAMP_TO_EDGE);
+        camera->setDrawBuffer(GL_FRONT);
+        camera->setReadBuffer(GL_FRONT);
+        camera->setRenderTargetImplementation(Camera::FRAME_BUFFER_OBJECT);
+        camera->attach(Camera::COLOR_BUFFER, texture);
+        _textureTargets[texName] = texture;
+    } else {
+        camera->setDrawBuffer(GL_BACK);
+        camera->setReadBuffer(GL_BACK);
+    }
+    const SGPropertyNode* psNode = cameraNode->getNode("panoramic-spherical");
+    bool useMasterSceneGraph = !psNode;
+    CameraInfo* info = addCamera(cameraFlags, camera, vOff, pOff,
+                                 useMasterSceneGraph);
     // If a viewport isn't set on the camera, then it's hard to dig it
     // out of the SceneView objects in the viewer, and the coordinates
     // of mouse events are somewhat bizzare.
     SGPropertyNode* viewportNode = cameraNode->getNode("viewport", true);
     buildViewport(info, viewportNode, window->gc->getTraits());
     updateCameras(info);
+    // Distortion camera needs the viewport which is created by addCamera().
+    if (psNode) {
+        info->flags = info->flags | VIEW_ABSOLUTE;
+        buildDistortionCamera(psNode, camera);
+    }
     return info;
 }
 
index 79d0d33d4a0d7ddf84433769d44b842eea3e87f3..1e18e28e1172702ebd12e8c7c2a826844cc23b1a 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef CAMERAGROUP_HXX
 #define CAMERAGROUP_HXX 1
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -24,6 +25,7 @@
 #include <osg/ref_ptr>
 #include <osg/Referenced>
 #include <osg/Node>
+#include <osg/TextureRectangle>
 
 // For osgUtil::LineSegmentIntersector::Intersections, which is a typedef.
 #include <osgUtil/LineSegmentIntersector>
@@ -178,6 +180,9 @@ public:
     /** Update camera properties after a resize event.
      */
     void resized();
+
+    void buildDistortionCamera(const SGPropertyNode* psNode,
+                               osg::Camera* camera);
 protected:
     CameraList _cameras;
     osg::ref_ptr<osgViewer::Viewer> _viewer;
@@ -186,6 +191,8 @@ protected:
     float _zNear;
     float _zFar;
     float _nearField;
+    typedef std::map<std::string, osg::ref_ptr<osg::TextureRectangle> > TextureMap;
+    TextureMap _textureTargets;
 };
 
 }