This tutorial will walk through a short OpenFrames program, Tutorial2.cpp, that loads and displays models in file formats accepted by OpenSceneGraph, OpenFrames' underlying graphics package. The first step is to include the header files that are needed by the program-
#include <OpenFrames/FrameManager.hpp> #include <OpenFrames/Model.hpp> #include <OpenFrames/WindowProxy.hpp> #include <osg/ArgumentParser> #include <osgDB/FileNameUtils>
The need for each of these is discussed below.
At the top of the main()
we next set up an "OpenFrame::WindoxProxy"-
// Create the window interface. unsigned int x = 30, y = 30; unsigned int winWidth = 800, winHeight = 600; unsigned int numRows = 1, numCols = 1; osg::ref_ptr<OpenFrames::WindowProxy> myWindow = new OpenFrames::WindowProxy( x, y, winWidth, winHeight, numRows, numCols );
A WindowProxy is a platform-independent entity that allows the display of OpenFrames output on any of several operating systems. The x and y parameters specify the location of the window's corner on the screen. The window width and height parameters are self-explanatory.
Next, the 'root reference frame' is created-
// Create the root reference frame that will hold all specified models. osg::ref_ptr<OpenFrames::ReferenceFrame> rootFrame = new OpenFrames::ReferenceFrame( "Root" ); rootFrame->showAxes( OpenFrames::ReferenceFrame::NO_AXES ); rootFrame->showAxesLabels( OpenFrames::ReferenceFrame::NO_AXES ); rootFrame->showNameLabel( false );
As mentioned, the foundation of OpenFrames is a graphics package called OpenSceneGraph (OSG). In OSG, everything in a scene is logically arranged in an tree-type graph structure called a "scenegraph". All nodes are children of the top-most (root) node, or of one of its children, or one of its children's children, etc. In this tutorial, all models that the program loads will become children of the node we are calling "rootFrame".
Now the program creates a "frame manager" for the scene-
// Create a frame manager for the scene. osg::ref_ptr<OpenFrames::FrameManager> myFrmMgr = new OpenFrames::FrameManager; myFrmMgr->setFrame( rootFrame );
OpenFrames is multi-threaded, for high performance, and a frame manager coordinates competing threads' requests for the same system resources.
The final setup step specifies the placement of the scene on-screen-
// Now place the scene contained in myFrmMgr at the given location. myWindow->setScene( myFrmMgr, 0, 0 );
The following operations pertain to the view window and its content. This operation specifies the first part of the window's title-
// Set up the window title. std::string windowName = "OpenFrames Viewer:";
The names of any models that are loaded will be appended to this initial string.
The following code now loops through the input arguments and attempts to load them-
// Try to load user-specified files/models. for ( int i=1; i<argc; ++i ) { OpenFrames::Model *theModel = new OpenFrames::Model( "Model", 0.5, 0.5, 0.5, 0.9 ); if ( !theModel->setModel( argv[i] ) ) { continue; } // Add the model's name, sans any extension, to the window's title/name. std::string fname = osgDB::getSimpleFileName( argv[i] ); std::string fname_noext = osgDB::getNameLessAllExtensions( fname ); theModel->setName( fname_noext ); windowName += " " + fname_noext; // Reset model pivot so that its origin coincides with the root scene origin. theModel->setModelPivot( 0.0, 0.0, 0.0 ); // Add the model to the scene. rootFrame->addChild( theModel ); // Create a view for the model. OpenFrames::View *modelView = new OpenFrames::View( rootFrame, theModel ); myWindow->getGridPosition(0, 0)->addView( modelView ); }
The setModel( argv[i] ) )
near the top of the loop attempts to load a model specified by the i'th input argument. If successful, the model's name (without any extension) is appended to the window title, the model's pivot point is set and the model is added as a child of the scenegraph's root node. In the last step of the loop a "view" is created for the model; this is discussed further below.
Below the loop, a check is done to make sure that at least one model was loaded-
if ( 0 == rootFrame->getNumChildren() ) { OSG_WARN << std::endl << "No models loaded, exiting." << std::endl; return 1; }
If none were loaded, the program, as can be seen, prints a warning message and exits early. On the other hand, if one or more models were loaded, this code is executed-
myWindow->setWindowName( windowName ); myWindow->startThread(); // Start window animation. myWindow->join(); // Wait for window animation to finish.
This figure shows the results of executing the command, tutorial2 cow.osg
-
(For clarity, the root reference frame has been slightly tilted and zoomed in, using mouse controls that are part of OSG; for details of scene manipulation with the mouse see the OpenSceneGraph Quick Start Guide.)
Running the program with two models, OSG's cow and Cessna, gives this result-
Again, the scene has been tilted and slightly zoomed-in for clarity. Notice that the cow is in the middle of the Cessna; this is because their origins have been placed at the same point: the origin of the scene. The two models therefore overlap. Notice also that two sets of reference axes are now present: a small set near the cow and a larger set surrounding the Cessna. Every model is loaded into a reference frame that by default is visible. (The frame can be made invisible as well.) The size of the frame is proportional to the model size, accounting for the differing sizes visible in the image.
This multi-model image affords an opportunity to explain "views", mentioned above. When running the tutorial with two models, try pressing the "v" key. The image will alternate between different views of the models. This occurs because separate default views were made at the bottom of the for loop. Each view shows that particular model centered on-screen . Pressing the 'v' key cycles between these views. This functionality is part of OpenSceneGraph and is therefore available to OpenFrames developers.
Congratulations, you have finished OpenFrames Tutorial #2.