VR-Forces Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
exampleGLDrawingPlugin

Table of Contents

Overview

This example plugin creates a new driver that demonstrates how to use OpenGL draw calls from a subclass of osg::Drawable that gets added to the normalized 2D camera of all DtOsgChannels.

Expected Result

exampleGLDrawingPlugin.png
GL Drawing Plugin Result

Example details

Make a DtReticleDrawable which is subclassed from osg::Drawable:

// This subclass of osg::Drawable has a drawImplementation that can draw a
// 2D green reticle overlay or a tri-color 3D cube
class DtCustomDrawable : public osg::Drawable

Override drawImplementation to make some OpenGL calls:

virtual void drawImplementation(osg::RenderInfo& state) const
{
if (myIsReticleEnabled)
renderReticle(state);
if (myIsCubeEnabled)
renderCube(state);
}
virtual void renderReticle(osg::RenderInfo& state) const
{
// Access the viewport to determine the dimensions and aspect ratio of the
// current channel
osg::Viewport* viewport = state.getCurrentCamera()->getViewport();
if (!viewport)
{
return;
}
int width = viewport->width();
int height = viewport->height();
float aspect = viewport->width() / viewport->height();
// Push the current OpenGL state for the things that will be modified
glPushMatrix();
glPushAttrib(GL_LINE_BIT);
// Update the projection matrix for the current aspect ratio and to base
// the coordinates on OpenGL (which uses -1,-1 .. 1, 1) instead of the
// normalized overlay coordinates in DtOsgChannel (which uses 0,0 .. 1,1)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (width >= height)
{
gluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0);
}
else
{
gluOrtho2D(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect);
}
// Default thicker lines to 10 but if the window is smaller (like with
// an inset view) use 1% of the screen width as the line width so they
// don't get drawn so fat as to hide the proper shape of the reticle.
int lineWidth = 10;
if ((width * .01) < lineWidth)
{
lineWidth = width * .01;
}
glLineWidth(lineWidth);
// Draw the circle
glBegin(GL_LINE_LOOP);
glColor3f(0.0f, 1.0f, 0.0f);
float radius = 0.5;
float centerX = 0.0;
float centerY = 0.0;
for (int i=0; i < 360; i++)
{
float radians = i * M_DEG2RAD;
glVertex2f(centerX + cos(radians)*radius,
centerY + sin(radians)*radius);
}
glEnd();

Instantiate a DtReticleDrawable when the driver starts up and put it into an osg::Geode so it can be added to cameras:

virtual bool onStart()
{
// Create the nodes that need to be added to the scene
myReticle = new DtCustomDrawable();
myReticle->myIsReticleEnabled = true;
myReticle->myIsCubeEnabled = false;
myReticleGeode = new osg::Geode();
myReticleGeode->setName("Reticle Geode");
myReticleGeode->addDrawable(myReticle);

Next make sure it's added to all the current channels:

To get the reticle geode added to any new channels, hook up signal_channelCreated from the drivers constructor:

mySignalConnections += DtChannelManagerSignaler::instance(

And then add it from the callback:

void slot_channelCreated(DtChannelManager* mgr, DtChannel* channel)
{
DtOsgChannel* osgChannel = dynamic_cast<DtOsgChannel*>(channel);
if (!osgChannel || !myReticleGeode || !myCubeGeode)
{
return;
}
// Add myReticleGeode as a child of this new channel so it can be rendered every frame
osgChannel->normalizedOverlayCamera()->addChild(myReticleGeode);

Building the Example

VR-Vantage includes pre-built versions of the example plug-in. To build it yourself, follow the instructions at Building VR-Vantage Examples, Applications, and Plug-ins.

Running the Example

This example is a plug-in. You can run it by running ./bin64/exampleGLDrawingPlugin_stealth.bat (on Windows) or ./bin64/exampleGLDrawingPlugin_stealth.sh (on Linux). For more information about running examples, please see Running Applications and Examples.

Example Source Files


exampleGLDrawingPlugin.cxx

/******************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
******************************************************************************/
#ifdef _WIN32
#ifdef EXAMPLEGLDRAWINGPLUGIN_EXPORTS
#define DT_DLL_EXAMPLEGLDRAWINGPLUGIN __declspec ( dllexport )
#else
#define DT_DLL_EXAMPLEGLDRAWINGPLUGIN __declspec ( dllimport )
#endif
#else
#define DT_DLL_EXAMPLEGLDRAWINGPLUGIN
#endif
#define DT_DE_PLUGIN_EXPORT_MACRO DT_DLL_EXAMPLEGLDRAWINGPLUGIN
// Declare & export the plugin initialization function (bool initDeModule(DtDe* de))
#include <vrvCore/DtDe.h>
#include <matrix/vlMath.h>
#include <osg/Drawable>
#include <osg/Geode>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <boost/bind/bind.hpp>
// Use the VR-Vantage namespace. All classes in VR-Vantage are in this namespace.
using namespace makVrv;
static vrvSignalsLib::connection postInitializeConnection;
// NOTE: For this example to work in Virtual Reality, the normalizedOverlayCamera should be switched to just overlayCamera
// This subclass of osg::Drawable has a drawImplementation that can draw a
// 2D green reticle overlay or a tri-color 3D cube
class DtCustomDrawable : public osg::Drawable
{
public:
DtCustomDrawable()
{
myIsReticleEnabled = false;
myIsCubeEnabled = false;
// set to false to make sure drawImplementation is called every frame
setSupportsDisplayList(false);
setUseDisplayList(false);
};
DtCustomDrawable(const DtCustomDrawable& drawable,
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) :
osg::Drawable(drawable,copyop)
{
myIsReticleEnabled = drawable.myIsReticleEnabled;
myIsCubeEnabled = drawable.myIsCubeEnabled;
// set to false to make sure drawImplementation is called every frame
setSupportsDisplayList(false);
setUseDisplayList(false);
}
virtual ~DtCustomDrawable()
{
};
META_Object(test, DtCustomDrawable)
virtual void drawImplementation(osg::RenderInfo& state) const
{
if (myIsReticleEnabled)
renderReticle(state);
if (myIsCubeEnabled)
renderCube(state);
}
virtual void renderReticle(osg::RenderInfo& state) const
{
// Access the viewport to determine the dimensions and aspect ratio of the
// current channel
osg::Viewport* viewport = state.getCurrentCamera()->getViewport();
if (!viewport)
{
return;
}
int width = viewport->width();
int height = viewport->height();
float aspect = viewport->width() / viewport->height();
// Push the current OpenGL state for the things that will be modified
glPushMatrix();
glPushAttrib(GL_LINE_BIT);
// Update the projection matrix for the current aspect ratio and to base
// the coordinates on OpenGL (which uses -1,-1 .. 1, 1) instead of the
// normalized overlay coordinates in DtOsgChannel (which uses 0,0 .. 1,1)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (width >= height)
{
gluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0);
}
else
{
gluOrtho2D(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect);
}
// Default thicker lines to 10 but if the window is smaller (like with
// an inset view) use 1% of the screen width as the line width so they
// don't get drawn so fat as to hide the proper shape of the reticle.
int lineWidth = 10;
if ((width * .01) < lineWidth)
{
lineWidth = width * .01;
}
glLineWidth(lineWidth);
// Draw the circle
glBegin(GL_LINE_LOOP);
glColor3f(0.0f, 1.0f, 0.0f);
float radius = 0.5;
float centerX = 0.0;
float centerY = 0.0;
for (int i=0; i < 360; i++)
{
float radians = i * M_DEG2RAD;
glVertex2f(centerX + cos(radians)*radius,
centerY + sin(radians)*radius);
}
glEnd();
// Draw the thick lines
glBegin(GL_LINES);
glColor3f(0.0f, 1.0f, 0.0f);
float extra = 0.06;
glVertex2f(centerX - (radius + extra), centerY);
glVertex2f(centerX - radius/2, centerY);
glVertex2f(centerX + (radius + extra), centerY);
glVertex2f(centerX + radius/2, centerY);
glVertex2f(centerX, centerY - (radius + extra));
glVertex2f(centerX, centerY - radius/2);
glVertex2f(centerX, centerY + (radius + extra));
glVertex2f(centerX, centerY + radius/2);
glEnd();
// Draw the thin lined inner crosshair
glLineWidth(2.0);
glBegin(GL_LINES);
glVertex2f(centerX - radius/2, centerY);
glVertex2f(centerX + radius/2, centerY);
glVertex2f(centerX, centerY - radius/2);
glVertex2f(centerX, centerY + radius/2);
glEnd();
// Clean up the OpenGL state
glPopAttrib();
glPopMatrix();
}
virtual void renderCube(osg::RenderInfo& renderInfo) const
{
// Because VR-Vantage uses revers z, the depth test needs to be backwards compared to what is typically used.
glPushAttrib(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER);
// We don't want to use normals, lights, or materials, so turn off all lighting calculations.
//
// NOTE: This call does not seem to make a difference. It is likely that the currently bound GLSL shader does
// not implement this part of the fixed function pipeline.
glPushAttrib(GL_LIGHTING_BIT);
glDisable(GL_LIGHTING);
// Set the projection matrix for the OpenGL fixed function pipeline to match what OSG is using
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
osg::Matrixd projMat = renderInfo.getState()->getProjectionMatrix();
glLoadMatrixd( projMat.ptr() );
// We are going to render relative to the observer, so use the identity matrix for the model view matrix.
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// If we wanted to put something into world space, this matrix would be needed...
//osg::Matrixd modelViewMat = renderInfo.getState()->getModelViewMatrix();
// Render a cube a fixed distance in front of the observer
float distance = 500.0f;
glTranslatef(0.0, 0.0, -distance);
// Add some arbitrary rotations each frame to animate the cube
static int frame = 0;
++frame;
float rotDeg = (float)frame / 10.0f;
glRotatef(rotDeg, 0.0f, 1.0f, 0.0f);
glRotatef(rotDeg, 1.0f, 0.0f, 0.0f);
// The cube defined below is 1 unit in size, scale it up so it can be seen in the scene
float size = 100.0f;
glScalef(size, size, size);
// front
glColor3f(0.0f, 0.0f, 1.0f);
glBegin(GL_QUADS);
glVertex3f( 0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glVertex3f( 0.5f, -0.5f, 0.5f);
glEnd();
// back
glColor3f(0.0f, 0.0f, 1.0f);
glBegin(GL_QUADS);
glVertex3f( 0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f( 0.5f, -0.5f, -0.5f);
glEnd();
// right
glColor3f(1.0f, 0.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f( 0.5f, 0.5f, 0.5f);
glVertex3f( 0.5f, 0.5f, -0.5f);
glVertex3f( 0.5f, -0.5f, -0.5f);
glVertex3f( 0.5f, -0.5f, 0.5f);
glEnd();
// left
glColor3f(1.0f, 0.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f( -0.5f, 0.5f, 0.5f);
glVertex3f( -0.5f, 0.5f, -0.5f);
glVertex3f( -0.5f, -0.5f, -0.5f);
glVertex3f( -0.5f, -0.5f, 0.5f);
glEnd();
// top
glColor3f(0.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f( 0.5f, 0.5f, 0.5f);
glVertex3f( 0.5f, 0.5f, -0.5f);
glVertex3f( -0.5f, 0.5f, -0.5f);
glVertex3f( -0.5f, 0.5f, 0.5f);
glEnd();
// bottom
glColor3f(0.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f( 0.5f, -0.5f, 0.5f);
glVertex3f( 0.5f, -0.5f, -0.5f);
glVertex3f( -0.5f, -0.5f, -0.5f);
glVertex3f( -0.5f, -0.5f, 0.5f);
glEnd();
// Put the OpenGL state back to what it was
glPopAttrib(); // lighting
glPopAttrib(); // depth test
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
// Publicly accessible member variables that should be used to enable/disable the
// rendering of the 2D reticle and 3D cube
bool myIsReticleEnabled;
bool myIsCubeEnabled;
};
// This driver creates and owns the example driver.
class DtExampleGLDrawingDriver : public makVrv::DtDriver
{
public:
DtExampleGLDrawingDriver( DtAgentManager& am )
: makVrv::DtDriver( am, "DtExampleGLDrawingDriver" )
{
// Hook up slot_channelCreated so it gets calls whenever a new channel is
// created.
mySignalConnections += DtChannelManagerSignaler::instance(
myAgentManager.de()).signal_channelCreated.connect(
boost::bind(&DtExampleGLDrawingDriver::slot_channelCreated, this,
boost::placeholders::_1, boost::placeholders::_2));
}
virtual ~DtExampleGLDrawingDriver()
{
}
virtual const std::string& className() const
{
static std::string name = "DtExampleGLDrawingDriver";
return name;
}
void slot_channelCreated(DtChannelManager* mgr, DtChannel* channel)
{
DtOsgChannel* osgChannel = dynamic_cast<DtOsgChannel*>(channel);
if (!osgChannel || !myReticleGeode || !myCubeGeode)
{
return;
}
// Add myReticleGeode as a child of this new channel so it can be rendered every frame
osgChannel->normalizedOverlayCamera()->addChild(myReticleGeode);
// Add myCubeGeode as a child of this new channel so it can be rendered every frame
osgChannel->sceneCamera()->addChild(myCubeGeode);
}
virtual bool onStart()
{
// Create the nodes that need to be added to the scene
myReticle = new DtCustomDrawable();
myReticle->myIsReticleEnabled = true;
myReticle->myIsCubeEnabled = false;
myReticleGeode = new osg::Geode();
myReticleGeode->setName("Reticle Geode");
myReticleGeode->addDrawable(myReticle);
myCube = new DtCustomDrawable();
myCube->myIsReticleEnabled = false;
myCube->myIsCubeEnabled = true;
myCubeGeode = new osg::Geode();
myCubeGeode->setName("Cube Geode");
myCubeGeode->addDrawable(myCube);
// Set up a known scenegraph state set that can be used for our OpenGL rendering.
osg::StateSet* ss = myReticleGeode->getOrCreateStateSet();
ss->setAttributeAndModes(new osg::Program(), osg::StateAttribute::OFF);
ss->setTextureMode(0, GL_TEXTURE_1D, osg::StateAttribute::OFF);
ss->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
ss->setTextureMode(0, GL_TEXTURE_3D, osg::StateAttribute::OFF);
ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
ss->setMode(GL_BLEND, osg::StateAttribute::OFF);
ss->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
// Assign the same State Set properties to the cube geode
myCubeGeode->setStateSet(ss);
// Add myReticleGeode and myCubeGeode to the appropriate cameras for each channel. Do this by iterating
// over all windows, then all channels, to find the cameras.
DtWindowManager::WindowList wins = myAgentManager.de().display()->windowManager().windows();
DtWindowManager::WindowList::iterator winIter = wins.begin();
for(winIter; winIter != wins.end(); ++winIter)
{
DtChannelManager::ChannelMap channels = (*winIter)->channelManager().channels();
DtChannelManager::ChannelMap::iterator chanIter=channels.begin();
for(chanIter; chanIter!=channels.end(); ++chanIter)
{
DtOsgChannel* osgChannel = dynamic_cast<DtOsgChannel*>(chanIter->second);
if (osgChannel)
{
// An overlay camera that is rendered after the 3D scene has been rendered
//osgChannel->overlayCamera()->addChild(myReticleGeode);
osgChannel->normalizedOverlayCamera()->addChild(myReticleGeode);
// The 3D camera that renders the scene
osgChannel->sceneCamera()->addChild(myCubeGeode);
}
}
}
// onStart() complete
return true;
}
virtual bool onStop()
{
return true;
}
virtual bool onTick()
{
return true;
}
protected:
osg::ref_ptr<osg::Geode> myReticleGeode;
makVrv::DtSignalConnectionManager mySignalConnections;
};
{
// Create DtExampleDriver.
DtExampleGLDrawingDriver* driver = new DtExampleGLDrawingDriver(de.agentManager());
// The driver is now owned by the display engine. Do not delete it.
de.driverManager().addDriver(driver);
// Start the driver, creating the agent.
de.driverManager().startDriver(driver);
}
void init(DtDe& de)
{
// Ensure that init only gets called once
// (not strictly necessary here, but this is good practice in general)
// We only want to create the driver if we are running in master mode.
if (de.isInMasterMode())
{
// The accessory must be created after the DE's initialization is
// finished, to make sure all the objects it uses exist.
postInitializeConnection = de.signal_postInitialize.connect(boost::bind(
&installDriver, std::ref(de) ));
}
}
{
// Setup the plug-in. Normally, the init function and functionality
// should be in its own library.
init(*de);
return true;
}

[<< Examples] [Home] [Top of Page]


Document ID: Generated on Thu Oct 23 22:29:17 EDT 2025 from SVN revision 280951
Copyright © 2005-2024 MAK Technologies. All Rights Reserved (www.mak.com)