345 lines
10 KiB
C++
345 lines
10 KiB
C++
#include <osg/Geometry>
|
|
#include <osg/Impostor>
|
|
#include <osg/Material>
|
|
#include <osg/MatrixTransform>
|
|
#include <osg/Quat>
|
|
#include <osg/Geode>
|
|
|
|
#include <osgUtil/InsertImpostorsVisitor>
|
|
|
|
#include <osgDB/ReadFile>
|
|
|
|
#include <osgProducer/Viewer>
|
|
|
|
#include "TestManipulator.h"
|
|
|
|
|
|
#include <iostream>
|
|
#include <list>
|
|
|
|
// container storing all house nodes
|
|
typedef osg::ref_ptr<osg::Node> NodePtr;
|
|
typedef std::list<NodePtr> NodeContainer;
|
|
typedef NodeContainer::iterator NodeIterator;
|
|
|
|
NodeContainer nodes;
|
|
|
|
//
|
|
osg::Group * Root = 0;
|
|
|
|
const int HOUSES_SIZE = 25000; // total number of houses
|
|
double XDim = 5000.0f; // area dimension +/- XDim
|
|
double ZDim = 5000.0f; // area dimension +/- YDim
|
|
|
|
int GridX = 20; // number of grids in x direction
|
|
int GridY = 20; // number of grids in y direction
|
|
|
|
bool UseImpostor = true; // use impostor (or do not use)
|
|
|
|
float Threshold = 3000.0f; // distance where impostor are shown
|
|
|
|
// create houses and store nodes in container
|
|
void CreateHouses()
|
|
{
|
|
int i;
|
|
|
|
GLubyte indices[48] = {
|
|
0, 2, 1,
|
|
3, 2, 0,
|
|
0, 4, 7,
|
|
7, 3, 0,
|
|
0, 1, 5,
|
|
5, 4, 0,
|
|
1, 6, 5,
|
|
2, 6, 1,
|
|
2, 3, 7,
|
|
2, 7, 6,
|
|
4, 8, 7,
|
|
5, 6, 9,
|
|
4, 5, 8,
|
|
8, 5, 9,
|
|
6, 7, 8,
|
|
8, 9, 6
|
|
};
|
|
|
|
// use the same color, normal and indices for all houses.
|
|
osg::Vec4Array* colors = new osg::Vec4Array(1);
|
|
(*colors)[0] = osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
// normals
|
|
osg::Vec3Array * normals = new osg::Vec3Array(16);
|
|
(*normals)[0] = osg::Vec3( 0.0f, -0.0f, -1.0f);
|
|
(*normals)[1] = osg::Vec3( 0.0f, -0.0f, -1.0f);
|
|
(*normals)[2] = osg::Vec3( 0.0f, -1.0f, 0.0f);
|
|
(*normals)[3] = osg::Vec3( 0.0f, -1.0f, 0.0f);
|
|
(*normals)[4] = osg::Vec3( 1.0f, -0.0f, 0.0f);
|
|
(*normals)[5] = osg::Vec3( 1.0f, -0.0f, 0.0f);
|
|
(*normals)[6] = osg::Vec3( 0.0f, 1.0f, 0.0f);
|
|
(*normals)[7] = osg::Vec3( 0.0f, 1.0f, 0.0f);
|
|
(*normals)[8] = osg::Vec3(-1.0f, -0.0f, 0.0f);
|
|
(*normals)[9] = osg::Vec3(-1.0f, -0.0f, 0.0f);
|
|
(*normals)[10] = osg::Vec3( 0.0f, -0.928477f, 0.371391f);
|
|
(*normals)[11] = osg::Vec3( 0.0f, 0.928477f, 0.371391f);
|
|
(*normals)[12] = osg::Vec3( 0.707107f, 0.0f, 0.707107f);
|
|
(*normals)[13] = osg::Vec3( 0.707107f, 0.0f, 0.707107f);
|
|
(*normals)[14] = osg::Vec3(-0.707107f, 0.0f, 0.707107f);
|
|
(*normals)[15] = osg::Vec3(-0.707107f, 0.0f, 0.707107f);
|
|
|
|
// coordIndices
|
|
osg::UByteArray* coordIndices = new osg::UByteArray(48,indices);
|
|
|
|
// share the primtive set.
|
|
osg::PrimitiveSet* primitives = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,0,48);
|
|
|
|
for (int q = 0; q < HOUSES_SIZE; q++)
|
|
{
|
|
float xPos = ((static_cast<double> (rand()) /
|
|
static_cast<double> (RAND_MAX))
|
|
* 2.0 * XDim) - XDim;
|
|
|
|
float yPos = ((static_cast<double> (rand()) /
|
|
static_cast<double> (RAND_MAX))
|
|
* 2 * ZDim) - ZDim;
|
|
|
|
float scale = 10.0f;
|
|
|
|
osg::Vec3 offset(xPos,yPos,0.0f);
|
|
|
|
// coords
|
|
osg::Vec3Array* coords = new osg::Vec3Array(10);
|
|
(*coords)[0] = osg::Vec3( 0.5f, -0.7f, 0.0f);
|
|
(*coords)[1] = osg::Vec3( 0.5f, 0.7f, 0.0f);
|
|
(*coords)[2] = osg::Vec3(-0.5f, 0.7f, 0.0f);
|
|
(*coords)[3] = osg::Vec3(-0.5f, -0.7f, 0.0f);
|
|
(*coords)[4] = osg::Vec3( 0.5f, -0.7f, 1.0f);
|
|
(*coords)[5] = osg::Vec3( 0.5f, 0.7f, 1.0f);
|
|
(*coords)[6] = osg::Vec3(-0.5f, 0.7f, 1.0f);
|
|
(*coords)[7] = osg::Vec3(-0.5f, -0.7f, 1.0f);
|
|
(*coords)[8] = osg::Vec3( 0.0f, -0.5f, 1.5f);
|
|
(*coords)[9] = osg::Vec3( 0.0f, 0.5f, 1.5f);
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
(*coords)[i] = (*coords)[i] * scale + offset;
|
|
}
|
|
|
|
|
|
// create geometry
|
|
osg::Geometry * geometry = new osg::Geometry();
|
|
|
|
geometry->addPrimitiveSet(primitives);
|
|
|
|
geometry->setVertexArray(coords);
|
|
geometry->setVertexIndices(coordIndices);
|
|
|
|
geometry->setColorArray(colors);
|
|
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
|
|
|
|
geometry->setNormalArray(normals);
|
|
geometry->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
|
|
|
|
osg::Geode * geode = new osg::Geode();
|
|
geode->addDrawable(geometry);
|
|
|
|
nodes.push_back(geode);
|
|
}
|
|
}
|
|
|
|
void LayoutAsGrid()
|
|
{
|
|
// calculate bounding box
|
|
osg::BoundingBox bbox;
|
|
for (NodeIterator node = nodes.begin(); node != nodes.end(); ++node)
|
|
bbox.expandBy((*node)->getBound());
|
|
|
|
// setup grid information
|
|
osg::Group ** groups = new osg::Group*[GridX * GridY];
|
|
int i;
|
|
for (i = 0; i < GridX * GridY; i++)
|
|
groups[i] = new osg::Group();
|
|
|
|
float xGridStart = bbox.xMin();
|
|
float xGridSize = (bbox.xMax() - bbox.xMin()) / GridX;
|
|
|
|
float yGridStart = bbox.yMin();
|
|
float yGridSize = (bbox.yMax() - bbox.yMin()) / GridY;
|
|
|
|
// arrange buildings into right grid
|
|
for (NodeIterator nodeIter = nodes.begin(); nodeIter != nodes.end(); ++nodeIter)
|
|
{
|
|
osg::Node * node = nodeIter->get();
|
|
osg::Vec3 center = node->getBound().center();
|
|
|
|
int x = (int)floor((center.x() - xGridStart) / xGridSize);
|
|
int z = (int)floor((center.y() - yGridStart) / yGridSize);
|
|
|
|
groups[z * GridX + x]->addChild(node);
|
|
}
|
|
|
|
// add nodes to building root
|
|
for (i = 0; i < GridX * GridY; i++)
|
|
{
|
|
osg::StateSet * stateset = new osg::StateSet();
|
|
|
|
osg::Material * material = new osg::Material();
|
|
osg::Vec4 color = osg::Vec4(
|
|
0.5f + (static_cast<double> (rand()) / (2.0*static_cast<double> (RAND_MAX))),
|
|
0.5f + (static_cast<double> (rand()) / (2.0*static_cast<double> (RAND_MAX))),
|
|
0.5f + (static_cast<double> (rand()) / ( 2.0*static_cast<double>(RAND_MAX))),
|
|
1.0f);
|
|
|
|
material->setAmbient(osg::Material::FRONT_AND_BACK, color);
|
|
material->setDiffuse(osg::Material::FRONT_AND_BACK, color);
|
|
stateset->setAttributeAndModes(material, osg::StateAttribute::ON);
|
|
|
|
groups[i]->setStateSet(stateset);
|
|
|
|
if (UseImpostor)
|
|
{
|
|
osg::Impostor * impostor = new osg::Impostor();
|
|
impostor->setImpostorThreshold(static_cast<float> (Threshold));
|
|
impostor->addChild(groups[i]);
|
|
impostor->setRange(0, 0.0f, 1e7f);
|
|
impostor->setCenter(groups[i]->getBound().center());
|
|
Root->addChild(impostor);
|
|
}
|
|
else
|
|
{
|
|
Root->addChild(groups[i]);
|
|
}
|
|
}
|
|
|
|
delete[] groups;
|
|
}
|
|
|
|
|
|
int main( int argc, char **argv )
|
|
{
|
|
// use an ArgumentParser object to manage the program arguments.
|
|
osg::ArgumentParser arguments(&argc,argv);
|
|
|
|
// set up the usage document, in case we need to print out how to use this program.
|
|
arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use impostors - hierachical image cache.");
|
|
arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
|
|
arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
|
|
|
|
// construct the viewer.
|
|
osgProducer::Viewer viewer(arguments);
|
|
|
|
// set up the value with sensible default event handlers.
|
|
viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
|
|
|
|
// add local test manipulator more suitable for testing impostors.
|
|
unsigned int pos = viewer.addCameraManipulator(new TestManipulator);
|
|
viewer.selectCameraManipulator(pos);
|
|
|
|
// get details on keyboard and mouse bindings used by the viewer.
|
|
viewer.getUsage(*arguments.getApplicationUsage());
|
|
|
|
// if user request help write it out to cout.
|
|
if (arguments.read("-h") || arguments.read("--help"))
|
|
{
|
|
arguments.getApplicationUsage()->write(std::cout);
|
|
return 1;
|
|
}
|
|
|
|
// any option left unread are converted into errors to write out later.
|
|
arguments.reportRemainingOptionsAsUnrecognized();
|
|
|
|
// report any errors if they have occured when parsing the program aguments.
|
|
if (arguments.errors())
|
|
{
|
|
arguments.writeErrorMessages(std::cout);
|
|
return 1;
|
|
}
|
|
|
|
|
|
// load the nodes from the commandline arguments.
|
|
osg::Node* model = osgDB::readNodeFiles(arguments);
|
|
if (model)
|
|
{
|
|
// the osgUtil::InsertImpostorsVisitor used lower down to insert impostors
|
|
// only operators on subclass of Group's, if the model top node is not
|
|
// a group then it won't be able to insert an impostor. We therefore
|
|
// manually insert an impostor above the model.
|
|
if (dynamic_cast<osg::Group*>(model)==0)
|
|
{
|
|
const osg::BoundingSphere& bs = model->getBound();
|
|
if (bs.valid())
|
|
{
|
|
|
|
osg::Impostor* impostor = new osg::Impostor;
|
|
|
|
// standard LOD settings
|
|
impostor->addChild(model);
|
|
impostor->setRange(0,0.0f,1e7f);
|
|
impostor->setCenter(bs.center());
|
|
|
|
// impostor specfic settings.
|
|
impostor->setImpostorThresholdToBound(5.0f);
|
|
|
|
model = impostor;
|
|
|
|
}
|
|
}
|
|
|
|
// we insert an impostor node above the model, so we keep a handle
|
|
// on the rootnode of the model, the is required since the
|
|
// InsertImpostorsVisitor can add a new root in automatically and
|
|
// we would know about it, other than by following the parent path
|
|
// up from model. This is really what should be done, but I'll pass
|
|
// on it right now as it requires a getRoots() method to be added to
|
|
// osg::Node, and we're about to make a release so no new features!
|
|
osg::Group* rootnode = new osg::Group;
|
|
rootnode->addChild(model);
|
|
|
|
|
|
// now insert impostors in the model using the InsertImpostorsVisitor.
|
|
osgUtil::InsertImpostorsVisitor ov;
|
|
|
|
// traverse the model and collect all osg::Group's and osg::LOD's.
|
|
// however, don't traverse the rootnode since we want to keep it as
|
|
// the start of traversal, otherwise the insertImpostor could insert
|
|
// and Impostor above the current root, making it nolonger a root!
|
|
model->accept(ov);
|
|
|
|
// insert the Impostors above groups and LOD's
|
|
ov.insertImpostors();
|
|
}
|
|
else
|
|
{
|
|
// no user model so we'll create our own world.
|
|
model = Root = new osg::Group();
|
|
CreateHouses();
|
|
LayoutAsGrid();
|
|
}
|
|
|
|
// add model to viewer.
|
|
viewer.setSceneData(model);
|
|
|
|
// register trackball, flight and drive.
|
|
|
|
// create the windows and run the threads.
|
|
viewer.realize();
|
|
|
|
while( !viewer.done() )
|
|
{
|
|
// wait for all cull and draw threads to complete.
|
|
viewer.sync();
|
|
|
|
// update the scene by traversing it with the the update visitor which will
|
|
// call all node update callbacks and animations.
|
|
viewer.update();
|
|
|
|
// fire off the cull and draw traversals of the scene.
|
|
viewer.frame();
|
|
|
|
}
|
|
|
|
// wait for all cull and draw threads to complete.
|
|
viewer.sync();
|
|
|
|
|
|
return 0;
|
|
}
|