Hi Robert,

My apologies for the late response, your reply got lost in the flurry of
osg-submission posts in my inbox.

I was fiddling with my code on Sunday and I noticed that it wasn't
displaying properly if the viewer wasn't set to display on a specific
screen. Consequently, I redesigned the image resizing routines based on
your osgStereoImage example. Basically its the same as yours now but with
the slideshow functionality removed and a quad instead of a cylinder.

In light of your current response perhaps this is a good thing. The example
is very straight forward now and simply illustrates the process of
displaying stereo images. My only concern is that setting the fusion
distance is confusing as a teaching aid. The same effect could be achieved
using a post render orthographic camera as per my previous example whereas
setting the fusion distance has an effect across the entire scene.

When you get a sec have a look over this version and see if you think it's
an improvement.

Regards,

Kim.

On 10 February 2012 17:30, Robert Osfield <[email protected]> wrote:

> Hi Kim,
>
> On 7 February 2012 21:34, Kim Bale <[email protected]> wrote:
> > You're right on both counts, I can make these changes if you like.
> >
> > However, I've had second thoughts about this submission. The reason I
> wrote
> > it was because I wanted a simple command line viewer for stereo images. I
> > could have used osgStereoImage out the box if it weren't for the
> geometric
> > distortion.
> >
> > Given that the geometric distortion is only useful for a very specific
> use
> > case, I propose that I simply modify the existing example to remove the
> > geometric distortion in favour of a flat quad. This would keep the very
> > handy slideshow functionality already in osgStereoImage and make the
> example
> > more useful to others.
> >
> > Really this is what I should have done in the first place..
> >
> > What do you think?
>
> The use of the cylinder geometry was done back in 2001 to support
> rendering in a reality theatre, it was this venue that I wrote
> osgstereimage for.  It's very much niche and could be changed to be a
> simple quad now so I'd support this change if you want to dive in and
> make it.
>
> I terms of slide show functionality, present3D (now one of the
> standard OSG applications) can do 3D stereo presentations that include
> support for stereo image/video it's much more flexible and powerful
> than osgstereoimage could ever be so I wonder if the slide show
> functionality is really that necessary - this example could be focused
> purely on illustrating how to set up a subgraph to do stereo
> left/right image rendering.
>
> Robert.
> _______________________________________________
> osg-submissions mailing list
> [email protected]
>
> http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
>
/* OpenSceneGraph example, osgStereoImageViewer.
*
*  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.
*/

// This code is originally Copyright (c) 2011 Kim Bale but can be used under the
// terms described in the OSGPL.

// This example is designed to illustrate how to use the OSG as a means of displaying
// stereo images. The program accepts stereo pairs both as individual image files 
// (one for the left eye and one for right) and as a single image containing side by side
// stereo pairs.

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Group>
#include <osg/Camera>
#include <osg/TextureRectangle>
#include <osg/Geometry>
#include <osg/Image>
#include <osgDB/ReadFile>
#include <osg/TexMat>

#define LEFT_EYE_MASK 0x1
#define RIGHT_EYE_MASK 0x2

enum ImageFormat{ Single, PairLeft, PairRight };

void printUsage(void)
{
    OSG_NOTICE << std::endl;
    OSG_NOTICE << "Usage: osgStereoImageViewer [options] filename\n" << 
        "Options:\n"
        "  --left             Path to the left eye image\n" <<
        "  --right            Path to the right eye image\n" <<
        "  --pair             Path to a horizontal split stereo image e.g. jps" <<
        "  --swap             Swap left and right eyes " << 
        "  --window <x y w h> Set the position (x,y) and size (w,h) of the viewer window\n" <<
        "  --stretch          Disregard image aspect ratio and stretch image to fill screen\n" << 
        "  --screen <num>     Set the screen to use when multiple screens are present\n" <<
        "  --stereo <mode>    Set the stereo mode \n" <<
        "                     ANAGLYPHIC | QUAD_BUFFER |\n" << 
        "                     HORIZONTAL_SPLIT | VERTICAL_SPLIT |\n" <<
        "                     LEFT_EYE | RIGHT_EYE |\n" <<
        "                     HORIZONTAL_INTERLACE |\n" <<
        "                     VERTICAL_INTERLACE | CHECKERBOARD |\n" <<
        "                     ON | OFF\n";
    OSG_NOTICE << "\nExample: osgStereoImageViewer --left leftImg.jpg --right rightImg.jpg" << std::endl;
    OSG_NOTICE << std::endl;
}

osg::Geode* createImagePlane( ImageFormat format, osg::TextureRectangle* texture, float frustumWidth, float frustumHeight, bool stretchToFill )
{
    int imgWidth  = (format!=Single?texture->getTextureWidth()/2:texture->getTextureWidth());;
    int imgHeight = texture->getTextureHeight();

    int left = 0;
    int right = 0;
    int bottom = 0;
    int top = imgHeight;

    if(format==Single){
        right = imgWidth;
    }
    else if(format==PairLeft){
        right = imgWidth;
    }
    else if(format==PairRight){
        left = imgWidth; 
        right = imgWidth*2;
    }

    float quadWidth  = 0;
    float quadHeight = 0;
    float imageRatio = imgHeight / (float)imgWidth;

    if(stretchToFill)
    {
        quadWidth = frustumWidth;
        quadHeight = frustumHeight;
    }
    else if(imgWidth > imgHeight)
    {
        if( frustumWidth*imageRatio > frustumHeight ){
            quadHeight = frustumHeight;
            quadWidth = quadHeight/imageRatio;
        }
        else{
            quadWidth = frustumWidth;
            quadHeight = quadWidth*imageRatio;
        }  
    }
    else{
        if(frustumHeight/imageRatio > frustumWidth){
            quadWidth = frustumWidth;
            quadHeight = quadWidth*imageRatio;
        }
        else{
            quadHeight = frustumHeight;
            quadWidth = quadHeight/imageRatio;
        }
    }

    osg::Vec3 corner( -quadWidth*0.5f, 1.f, -quadHeight*0.5f );
    osg::Vec3 width( quadWidth, 0, 0 );
    osg::Vec3 height( 0, 0, quadHeight );
    osg::Geometry* planeGeometry = osg::createTexturedQuadGeometry( corner, width, height, left, bottom, right, top); 

    osg::Geode* planeGeode = new osg::Geode;
    planeGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0,texture);
    planeGeode->addDrawable(planeGeometry);
    return planeGeode;
}

osg::TextureRectangle* createTexture( const std::string& imageFile )
{
    osg::ref_ptr<osg::Image> image = osgDB::readImageFile(imageFile);

    if(!image){
        OSG_NOTICE << "ERROR: Could not load image: " << imageFile << std::endl << std::endl;
        return NULL;
    }

    if(!image) return NULL;

    osg::TextureRectangle* textureRect = new osg::TextureRectangle(image);
    textureRect->setTextureWidth(image->s());
    textureRect->setTextureHeight(image->t());
    textureRect->setWrap(osg::Texture2D::WRAP_S,osg::Texture::CLAMP_TO_EDGE);
    textureRect->setWrap(osg::Texture2D::WRAP_T,osg::Texture::CLAMP_TO_EDGE);

    return textureRect;
}

osg::Group* createLeftRightImagePlanes( const std::string& pairFile, 
                                        const std::string& leftFile, const std::string& rightFile,
                                        float frustumWidth, float frustumHeight,
                                        bool stretchToFill, bool swapEyes )
{
    osg::Geode* leftEyePlane = NULL;
    osg::Geode* rightEyePlane = NULL;

    if( !pairFile.empty() )
    {
        OSG_NOTICE << "Image pair:    " << pairFile << std::endl;
        osg::ref_ptr<osg::TextureRectangle> pairTexture = createTexture( pairFile );

        if( !pairTexture.valid() )
            return NULL;

        leftEyePlane  = createImagePlane(PairLeft,  pairTexture, frustumWidth, frustumHeight, stretchToFill );
        rightEyePlane = createImagePlane(PairRight, pairTexture, frustumWidth, frustumHeight, stretchToFill );
    }
    else
    {
        OSG_NOTICE << "Left image:    " << leftFile << std::endl;
        OSG_NOTICE << "Right image:   " << rightFile << std::endl;
        osg::ref_ptr<osg::TextureRectangle> leftTexture  = createTexture( leftFile );
        osg::ref_ptr<osg::TextureRectangle> rightTexture = createTexture( rightFile );

        if( !leftTexture.valid() || !rightTexture.valid() ){
            return NULL;
        }

        if( leftTexture->getTextureWidth()  != rightTexture->getTextureWidth() || 
            leftTexture->getTextureHeight() != rightTexture->getTextureHeight() )
        {
            OSG_NOTICE << "ERROR: Left and right eye images must be the same dimension\n" << std::endl;
            return NULL;      
        }

        leftEyePlane  = createImagePlane( Single, leftTexture,  frustumWidth, frustumHeight, stretchToFill );
        rightEyePlane = createImagePlane( Single, rightTexture, frustumWidth, frustumHeight, stretchToFill );
    }

    // Assign masks to each plane so that they appear in the correct left and right eye cull pass.
    leftEyePlane->setNodeMask( swapEyes ? RIGHT_EYE_MASK : LEFT_EYE_MASK );
    rightEyePlane->setNodeMask( swapEyes ? LEFT_EYE_MASK : RIGHT_EYE_MASK );

    osg::Group* planeGroup = new osg::Group;
    planeGroup->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
    planeGroup->getOrCreateStateSet()->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
    planeGroup->addChild( leftEyePlane );
    planeGroup->addChild( rightEyePlane );

    return planeGroup;
}

int main(int argc, char* argv[])
{
    osg::ArgumentParser args(&argc,argv);
    
    osg::DisplaySettings::instance()->readCommandLine(args);
    // Set stereo on by default
    osg::DisplaySettings::instance()->setStereo(true);

    if( args.read("--help") || args.read("-h") )
    {
        printUsage();
        return 1;
    }

    std::string filePair;
    while( args.read("--pair", filePair) ){};

    std::string fileLeft;
    while( args.read("--left", fileLeft) ){};

    std::string fileRight;
    while( args.read("--right", fileRight) ){};
    
    bool stretchImage = args.find("--stretch") != -1;
    OSG_NOTICE << "Stretch Image: " << (stretchImage?"ON":"OFF") << std::endl;

    bool swapEyes = args.find("--swap") != -1;
    OSG_NOTICE << "Swap Eyes: " << (swapEyes?"ON":"OFF") << std::endl;

    if( filePair.empty() && fileLeft.empty() && fileRight.empty() )
    {
        OSG_NOTICE << "No images files specified. Using default files instead." << std::endl;
        fileLeft  = "images/dog_left_eye.jpg";
        fileRight = "images/dog_right_eye.jpg";
    }

    // Check if a screen is specified.
    // this will affect our frustum calculations.
    bool isScreenSelected = false;
    int pos = args.find("--screen");
    const char* ptr;

    // check for ---screen in args
    if( pos >= 0 && args.argc() > pos+1 ){
        isScreenSelected = atoi(args[pos+1]) >= 0;
    }
    // check for OSG_SCREEN in environment vars
    else if( (ptr = getenv("OSG_SCREEN")) != 0 ){
        isScreenSelected = atoi(ptr) >= 0;
    }

    osgViewer::Viewer viewer(args);
    viewer.addEventHandler( new osgViewer::WindowSizeHandler );
    viewer.getCamera()->setCullMask(0xffffffff);
    viewer.getCamera()->setCullMaskLeft( LEFT_EYE_MASK );
    viewer.getCamera()->setCullMaskRight( RIGHT_EYE_MASK );
    viewer.getCamera()->setClearColor( osg::Vec4(0,0,0,1) );

    // Since the planes are one unit away from the eye point we set the fusion distance 
    // (zero parallax distance) to 1.0 so we get a stereo effect from the imagery
    // and not the planes.
    viewer.setFusionDistance( osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE, 1.f );
  
    double left,right,top,bottom,near,far;
    viewer.getCamera()->getProjectionMatrixAsFrustum(left,right,bottom,top,near,far);

    float frustumWidth  = (float)(right - left);
    float frustumHeight = (float)(top - bottom);

    // Special case for spanning imagery across multiple monitors
    // When multiple monitors are used and we're not in HORIZONTAL_SPLIT multi-monitor mode,
    // the perspective frustum changes to accommodate the additional horizontal space.
    // The routine below works out the changes to the frustum width.
    osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
    if (!wsi) return 1;

    osg::GraphicsContext::ScreenIdentifier si;
    si.readDISPLAY();

    unsigned int numScreens = wsi->getNumScreens(si);

    bool isStereoSplitScreen = numScreens==2 && !isScreenSelected && 
        osg::DisplaySettings::instance()->getStereoMode()==osg::DisplaySettings::HORIZONTAL_SPLIT;

    if( !isStereoSplitScreen && !isScreenSelected )
    {
        frustumWidth = 0.f;
        for(unsigned i=0;i<numScreens;++i)
        {
            si.screenNum = i;
            unsigned int width, height;
            wsi->getScreenResolution(si, width, height);
            double aspectRatio = double(width) / double(height);
            frustumWidth += frustumHeight * aspectRatio;
        }
    }

    // Create the planes for the left and right images
    osg::ref_ptr<osg::Group> sceneGroup = 
        createLeftRightImagePlanes(filePair, fileLeft, fileRight, frustumWidth, frustumHeight, stretchImage, swapEyes);

    // If scene group is NULL then we have had an issue loading the images so exit.
    if( !sceneGroup.valid() )
        return 1;

    osg::Matrix homePosition = 
        osg::Matrix::lookAt( osg::Vec3(0.f,0.f,0.f),osg::Vec3(0.f,1.f,0.f),osg::Vec3(0.f,0.f,1.f) );

    // Set the view matrix as we're not using a camera manipulator
    viewer.getCamera()->setViewMatrix( homePosition );
    viewer.setSceneData(sceneGroup);
    viewer.realize();

    // Switch off the cursor
    osgViewer::Viewer::Windows windows;
    viewer.getWindows(windows);
    for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
        itr != windows.end();
        ++itr)
    {
        (*itr)->useCursor(false);
    }

    while( !viewer.done() )
    {
        viewer.frame();
    }

	return 0;
}

_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to