glutCyberle
- Beginning
- Setup of the glut Window
- Setup of the Event Broker
- Enable Sound, Textures and Javascript
- Text support
- Enable user centric features
- Creating the view for OpenGL rendering
- Controlling the Viewpoint
- Integrating the Mouse as a Pointing Device
- Start Simulation
- Callbacks
This tutorial explains how to create a simple VRML-Browser with the Open ActiveWrl classes. The example code shows how the implementation is done in combination with the glut library for windows handling. But anyhow it should be possible to work with OAW similar in combination with other window libraries. The full source code can be found in the glutCyberle-Folder. In the following the main parts of the source-code is explained in more detail. (complete source code of this example). To run this example call the executable with the path of a VRML-File as an argument.
Beginning
Because some of the objects are needed in the callback function they are declared as global variables. All classes of the Open ActiveWrl framework belong to the namespace OAW. The roles of these objects will be explained below in more detail in the following section. We use an OAW::CybGLWorldC object because we want to render it�s state with OpenGL.
///////////////////////////////////////////
//Global declaration of needed objects
OAW::LocalEventBrokerC* pEB;
OAW::CybGLWorldC world;
OAW::CybGLChannelC* pChan;
OAW::Cyb2DViewPointingDeviceC* pPointer;
OAW::CybMonoProjectionViewC* pView;
OAW::CybViewpointSensorC* pViewpointSensor;
OAW::CybWinMouseVPCtrlC* pWinMouseVPCtrl;
These objects are connected among each other through the following relations:
The main function starts with the initialisation of a glut window. The initialisation of the glut window can be done as usual. There are declared some callback functions into the glut event loop. The mouse and motion functions are used to implement navigation with the windows mouse and to integrate the windows mouse as a pointing device. The appLoop function is used to link the glut event loop to the Event Broker simulation loop.
/////////////////////////////////////////
// setup the glut window
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize (windowSizeX, windowSizeY);
glutInitWindowPosition (0, 0);
glutCreateWindow (argv[0]);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutPassiveMotionFunc(passive_motion);
glutKeyboardFunc(keyboard);
glutIdleFunc(appLoop);
The next step is to setup the core object of every Open ActiveWrl simulation. In this case we use the OAW::LocalEventBrokerC object, because we only want to build a standalone browser. To make this browser component available for a parallel distributed simulation, we simply need to create a OAW::SockEventBrokerC object. But how to set up a parallel and distributed simulation will be explained in a separate tutorial. After the Event Broker object has been created we will set callback functions to receive the swap and update events from the Event Broker. The swap event is needed to provide the component with a swap-lock and the update event is needed to perform the update of the components state. In the last line of this code section the event broker is acquaint to the world model. The world model needs the event broker during initialisation to get information about status of the different interfaces. So the Event Broker should set to world object before any VRML-File is loaded.
////////////////////////////////////////////////////////////
//Creation and Initialization of the Eventbroker object.
pEB = new OAW::LocalEventBrokerC();
//Set callback functions for swap and update in the simulation loop
pEB->SetSwapCallbackFunc(swapFunc);
pEB->SetUpdateCallbackFunc(updateFunc);
//Acquaint Event Broker to the world model
world.SetEventBroker(pEB);
Some options for the browser are implemented by using libraries from other open source projects. These include sound, image textures, rendering of text and the support of javascript scripts. The functionalities of these libraries are encapsulated by objects. If you want to integrate the options supported by these libraries, you simply create the appropriate object and acquaint it to world object, like it is done in the code section below. If these objects are created you will also have to link the appropriate libraries to your code. For sound support OAW uses OpenAL so the libraries OpenAL32.lib and ALut.lib are needed. We installed OpenAL until now only on Windows platforms. So the CybALSoundManagerC class is not included into the makefiles for the OAW libraries. But if you installed OpenAL on an Linux platform it should be easy to include this class into the makefile and integrate this options into your OAW-projects.For Javascript Monzilla�s SpiderMonkey Javascript Engine is used and you need the js32.lib (js32.a) library for linking and on MS Windows platforms js32.dll in the execution path of your operating system. To read image textures from disc we use jpeg.lib (jpeg.a), png.lib(png.a) and zlib.lib(zlib.a). To have no trouble with different versions it is recommended to download the libraries through the OAW web page.
/////////////////////////////////
// Enable some options
//enable sound
#ifndef _LINUX
OAW::CybALSoundManagerC man(&world);
world.m_pSoundManager = &man;
#endif
//enable javascript support
OAW::CybJSEngineC jsEngine;
world.m_pJSEngine = &jsEngine;
//enable image textures
OAW::CybGLTextureManagerC texMgr(&world);
world.m_pTexManager = &texMgr;
In general to switch on text rendering support is as simple as described in the section above. Again you simply need to create a font manager object. But unfortunately the names of the files of true type fonts are not standardized in respect to font name an family. Because we do not know how to read this information from the .tff files (if anbody knows please contact us), OAW expects standarized names for the *.ttf files. For example Arial_b.ttf for Ariel Bold and Arial_i.ttf for ARIEL italic. Further the OAW framework needs for components that do not use OpenGL rendering the bounding box informations about the different fonts. These information is provided in separate *.met files, which has to be generated. For this OAW provides the program FontMetric.exe that has to be started in the font directory and produces these files for every *.ttf file. One ready fonts directory with *.ttf and *.met files from usual used fonts is provided on the OAW download page. The path to the font directory has to be set within the program like it is done in the code section below. For text rendering with OpenGL OAW uses the FTGL project which in turn uses the freetype libraries.
//enable text rendering
OAW::CybFTGLFontManagerC fontMgr;
world.m_pFontManager = &fontMgr;
OAW::SetFontFolder("c:/data/Fonts");
To enable user centric features like billboards, there has to be created an instance of an user from the CybUserC class, which is acquainted to the world model. This user can also be tracked. How this is done is explained in the tutorial �user centric projections�.
//Enable user centric features like billboards
OAW::CybUserC user;
world.SetUser(&user);
The rendering is done very similar to performer through a channel object that traverses the scene graph of the world object.
////////////////////////////////////////////////////////////
//Create and initialize a channel for OpenGL-rendering.
pChan = new OAW::CybGLChannelC();
pChan->SetWorld(&world);
In general the OpenGL rendering can be done simply by using the Process-function of the channel object. But to parameterise the channel object in a more comfortable way for rendering user centric projection screens, OAW provides different view classes. One of them is OAW::CybMonoProjectionViewC. This class can also be used for passive stereo parallel multi screen projection screen rendering. There is a special tutorial on this topic. The viewing class is parameterised by screen size and distance of the projection screen to the origin of the laboratory system. If not specified otherwise it is assumed that the user is sitting in the origin of the laboratory system. In our case we declared these as parameters as global variables at the top of the file. The measure of these parameters is meter. So in this case we assume a monitor 0.4m wide 0.3m high and the user sitting in a distance of 0.4m in front of the monitor. At the end the view is added to the world model.
// viewing geometry parameters;
long windowSizeX = 1024;
long windowSizeY = 756;
double viewSizeX = 0.4;
double viewSizeY = 0.3;
double zDist = 0.4;
..
///////////////////////////////////////////////
//Create a view for a mono projection screen
pView = new OAW::CybMonoProjectionViewC(viewSizeX,viewSizeY);
//Set the Offset of the display in the laboratory system.
pView->SetDisplayOffset(OAW::VectorC(0,0,-zDist));
//Set the OpenGL channel for rendering
pView->SetGraphicChannel(pChan);
//Add this view to the model
world.AddView(pView);
The control of the viewpoint is done though a viewpoint interface. Within the OAW framework the instance of an interface can be assigned as active to one specific host in a parallel simulation running on a cluster. By this the viewpoint can be controlled by an specific host and is been synchronized by the Event Broker seamlessly. Therefore the viewpoint is registered to the Event Broker. Further the viewpoint sensor is acquaint to the world object, to give scripts the ability to access the viewpoint through the scene authoring interface. And last but not least the viewpoint sensor is acquaint to the view object to update the projection parameters if the viewpoint is moving.
////////////////////////////////////////////////////////////
//Create a viewpoint sensor for navigation
pViewpointSensor = new OAW::CybViewpointSensorC(&world);
//Set a default viewpoint position
OAW::VectorC vppos(0,0,5)
pViewpointSensor->SetViewpointPos(vppos,CYB_VPACCESS_BROWSER);
//Register interface of viewpoint sensor to the eventbroker.
pEB->AsignInterface("default_viewpointsensor",pViewpointSensor);
//Acquaint viewpoint sensor to world for appropriate access to the //viewpoint through the SAI
world.SetViewpointSensor(pViewpointSensor);
//acquaint viewpoint sensor to view for appropriate update of projection //parameters
pView->SetViewpointSensor(pViewpointSensor);
To control position and orientation of the viewpoint by mouse movements there exits in OAW some kind of adapter that translates mouse events to transform position and orientation of the viewpoint. The mouse events have to be feed to this object through the mouse events callback functions of the glut event loop (see below).
//Create an apdapter to transform mouse events to viewpoint motion.
pWinMouseVPCtrl = new OAW::CybWinMouseVPCtrlC(&world);
//
pWinMouseVPCtrl->SetViewpointSensor(pViewpointSensor);
For the interaction with the VRML-scene content a pointing device is needed. In the case of a simple VRML-browser we use the 2D mouse as a pointing device. Again OAW provides a class for this. In the case of immersive virtual environments we prefer to implement the pointing devices as PROTOs through a intersection service in the SAI. There is a special tutorial on this topic. But in this case we implement the pointing device within the browser. Again the pointing device is an instance of an distributed interface for synchronizing the events it triggers. Therefore the pointing device is registered similar to the viewpoint sensor to the Event Broker. Further the pointing device is added to the world model to assure a correct routing of it�s events in the event execution model of VRML.
////////////////
//Create 2DView pointingdevice for a windowsmouse.
pPointer = new OAW::Cyb2DViewPointingDeviceC(&world,pChan);
//Set state callback function of the pointer to control cursor appearance.
pPointer->SetStateCallback(pointerCallback,0);
//register interface of pointer to the eventbroker.
pEB->AsignInterface("default_pointingdevice",pPointer);
//Register this Pointing device to the world object for proper routing
world.AddPointingDevice(pPointer);
To start the simulation we have to load the VRML-File to the world model. After the file has been loaded we give this feedback to the Event Broker. After this the simulation can be started by calling the glutMainLoop function. The connection to the Event Broker is done through the idle callback function which is explained in the next section
//////////////////////////////////////////////
//Initialize world by the wrl-file.
OAW::StringC initData;
initData = "c:/data/wrl/floops/s52/s52.wrl";
world.LoadFromFile(initData);
//////////////////////////////////////////////
//Give EventBroker feedback about that the Initialization
//by the VRML-File is finished.
pEB->InitializationReady();
glutMainLoop();
Glut Callbacks
Through the glut callback functions the simulation is linked to the Event Broker event processing loop and the glut windows events. The glutIdleFunc is used to call the ProcessEvents function of the Event Broker which advise him to synchronize with other components and to call it�s update and swap callback functions. If the simulation is stopped by some component, the ProcessEvents functions returns nil and the simulation component exits.
//
void appLoop(void){
if(!pEB->ProcessEvents()){
world.Unload();
delete pEB;
delete pPointer;
delete pChan;
delete pView;
delete pViewpointSensor;
delete pWinMouseVPCtrl;
exit(0);
}
}
The callback functions for the mouse events are used to set these events to the pointing device and the mouse viewpoint control object.
void mouse(int button, int state, int x, int y)
{
pPointer->SetButtonState(!state);
int oawbutton =0;
if(button==GLUT_LEFT_BUTTON){oawbutton=1;}
if(button==GLUT_MIDDLE_BUTTON){oawbutton=2;}
if(button==GLUT_RIGHT_BUTTON){oawbutton=3;}
pWinMouseVPCtrl->Button(oawbutton,!state,x,y);
}
void motion(int x, int y)
{
pWinMouseVPCtrl->Motion(x,y);
pPointer->SetCursor(double(x)/windowSizeX,1.-double(y)/windowSizeY);
}
void passive_motion(int x, int y)
{
pPointer->SetCursor(double(x)/windowSizeX,1.-double(y)/windowSizeY);
}
The keyboard event is used to exit the browser if the ESC-button has been pressed. This has to be done by calling the Quit function of the Event Broker to assure that in the case of a parallel simulation also all other components exit appropriately.
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27:
pEB->Quit();
break;
}
}
OAW Callbacks
The pointer callback is used to change the appearance of the cursor appropriate to the state of the pointer device
//
void pointerCallback(void* ,short state){
if(state==OAW::CybPointingDeviceC::TOUCH){
glutSetCursor(GLUT_CURSOR_CROSSHAIR);
}else{
glutSetCursor(GLUT_CURSOR_RIGHT_ARROW);
}
}
The swapFunc callback provides the swap lock of the OpenGL rendering. The updateFunc callback is used to update world model, view and the mouse viewpoint ctrl. The mouse viewpoint control object needs to be updated to perform the movement of the viewpoint.
void swapFunc(void*){
glutSwapBuffers();
}
void updateFunc(void*){
world.Update();
pWinMouseVPCtrl->Update();
pView->Update();
glutPostRedisplay();
}
The glutPostRedisplay function makes the glut framwork to call the display callback function. Here does does the channel render the state of the VRML world.
void display(void){
printFrameRate();
pChan->ClearBuffers();
pChan->ClearTransformStack();
pChan->Process(world.GetGroup(),CYB_TRAV_DRAW);
}