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