#include #include #include #include #include #include #include #include #include #include #include #include #include #include // for the grid data.. #include "../osghangglide/terrain_coords.h" osg::Vec3 wind(1.0f,0.0f,0.0f); osg::AnimationPath* createAnimationPath(const osg::Vec3& center,float radius,double looptime) { // set up the animation path osg::AnimationPath* animationPath = new osg::AnimationPath; animationPath->setLoopMode(osg::AnimationPath::LOOP); int numSamples = 40; float yaw = 0.0f; float yaw_delta = 2.0f*osg::PI/((float)numSamples-1.0f); float roll = osg::inDegrees(30.0f); double time=0.0f; double time_delta = looptime/(double)numSamples; for(int i=0;iinsert(time,osg::AnimationPath::ControlPoint(position,rotation)); yaw += yaw_delta; time += time_delta; } return animationPath; } osg::Node* createMovingModel(const osg::Vec3& center, float radius) { float animationLength = 10.0f; osg::AnimationPath* animationPath = createAnimationPath(center,radius,animationLength); osg::Group* model = new osg::Group; osg::Node* glider = osgDB::readNodeFile("glider.osg"); if (glider) { const osg::BoundingSphere& bs = glider->getBound(); float size = radius/bs.radius()*0.3f; osg::MatrixTransform* positioned = new osg::MatrixTransform; positioned->setDataVariance(osg::Object::STATIC); positioned->setMatrix(osg::Matrix::translate(-bs.center())* osg::Matrix::scale(size,size,size)* osg::Matrix::rotate(osg::inDegrees(-90.0f),0.0f,0.0f,1.0f)); positioned->addChild(glider); osg::PositionAttitudeTransform* xform = new osg::PositionAttitudeTransform; xform->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0,0.5)); xform->addChild(positioned); model->addChild(xform); } osg::Node* cessna = osgDB::readNodeFile("cessna.osg"); if (cessna) { const osg::BoundingSphere& bs = cessna->getBound(); float size = radius/bs.radius()*0.3f; osg::MatrixTransform* positioned = new osg::MatrixTransform; positioned->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); positioned->setDataVariance(osg::Object::STATIC); positioned->setMatrix(osg::Matrix::translate(-bs.center())* osg::Matrix::scale(size,size,size)* osg::Matrix::rotate(osg::inDegrees(180.0f),0.0f,0.0f,1.0f)); //positioned->addChild(cessna); positioned->addChild(cessna); osg::MatrixTransform* xform = new osg::MatrixTransform; xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0f,1.0)); xform->addChild(positioned); model->addChild(xform); } return model; } osg::Vec3 computeTerrainIntersection(osg::Node* subgraph,float x,float y) { osgUtil::IntersectVisitor iv; osg::ref_ptr segDown = new osg::LineSegment; const osg::BoundingSphere& bs = subgraph->getBound(); float zMax = bs.center().z()+bs.radius(); float zMin = bs.center().z()-bs.radius(); segDown->set(osg::Vec3(x,y,zMin),osg::Vec3(x,y,zMax)); iv.addLineSegment(segDown.get()); subgraph->accept(iv); if (iv.hits()) { osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segDown.get()); if (!hitList.empty()) { osg::Vec3 ip = hitList.front().getWorldIntersectPoint(); return ip; } } return osg::Vec3(x,y,0.0f); } ////////////////////////////////////////////////////////////////////////////// // MAIN SCENE GRAPH BUILDING FUNCTION ////////////////////////////////////////////////////////////////////////////// void build_world(osg::Group *root) { osg::Geode* terrainGeode = new osg::Geode; // create terrain { osg::StateSet* stateset = new osg::StateSet(); osg::Image* image = osgDB::readImageFile("Images/lz.rgb"); if (image) { osg::Texture2D* texture = new osg::Texture2D; texture->setImage(image); stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON); } terrainGeode->setStateSet( stateset ); float size = 1000; // 10km; float scale = size/39.0f; // 10km; float z_scale = scale*3.0f; osg::HeightField* grid = new osg::HeightField; grid->allocate(38,39); grid->setXInterval(scale); grid->setYInterval(scale); for(unsigned int r=0;r<39;++r) { for(unsigned int c=0;c<38;++c) { grid->setHeight(c,r,z_scale*vertex[r+c*39][2]); } } terrainGeode->addDrawable(new osg::ShapeDrawable(grid)); root->addChild(terrainGeode); } // create particle effects { osg::Vec3 position = computeTerrainIntersection(terrainGeode,100.0f,100.0f); osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, 10.0f); osgParticle::ExplosionDebriEffect* explosionDebri = new osgParticle::ExplosionDebriEffect(position, 10.0f); osgParticle::SmokeEffect* smoke = new osgParticle::SmokeEffect(position, 10.0f); osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, 10.0f); explosion->setWind(wind); explosionDebri->setWind(wind); smoke->setWind(wind); fire->setWind(wind); root->addChild(explosion); root->addChild(explosionDebri); root->addChild(smoke); root->addChild(fire); } // create particle effects { osg::Vec3 position = computeTerrainIntersection(terrainGeode,200.0f,100.0f); osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, 1.0f); osgParticle::ExplosionDebriEffect* explosionDebri = new osgParticle::ExplosionDebriEffect(position, 1.0f); osgParticle::SmokeEffect* smoke = new osgParticle::SmokeEffect(position, 1.0f); osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, 1.0f); explosion->setWind(wind); explosionDebri->setWind(wind); smoke->setWind(wind); fire->setWind(wind); root->addChild(explosion); root->addChild(explosionDebri); root->addChild(smoke); root->addChild(fire); } // create the moving models. { root->addChild(createMovingModel(osg::Vec3(500.0f,500.0f,500.0f),100.0f)); } } // class to handle events with a pick class PickHandler : public osgGA::GUIEventHandler { public: PickHandler() {} bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa) { switch(ea.getEventType()) { case(osgGA::GUIEventAdapter::PUSH): { osgProducer::Viewer* viewer = dynamic_cast(&aa); pick(viewer,ea); } return false; default: return false; } } void pick(osgProducer::Viewer* viewer, const osgGA::GUIEventAdapter& ea) { osg::Group* root = dynamic_cast(viewer->getSceneData()); if (!root) return; osgUtil::IntersectVisitor::HitList hlist; if (viewer->computeIntersections(ea.getX(),ea.getY(),hlist)) { osgUtil::Hit& hit = hlist.front(); bool handleMovingModels = false; const osg::NodePath& nodePath = hit.getNodePath(); for(osg::NodePath::const_iterator nitr=nodePath.begin(); nitr!=nodePath.end(); ++nitr) { const osg::Transform* transform = dynamic_cast(*nitr); if (transform) { if (transform->getDataVariance()==osg::Object::DYNAMIC) handleMovingModels=true; } } osg::Vec3 position = handleMovingModels ? hit.getLocalIntersectPoint() : hit.getWorldIntersectPoint(); float scale = 20.0f * ((float)rand() / (float)RAND_MAX); float intensity = handleMovingModels ? 5.0f : 1.0f; osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, scale, intensity); osgParticle::ExplosionDebriEffect* explosionDebri = new osgParticle::ExplosionDebriEffect(position, scale, intensity); osgParticle::SmokeEffect* smoke = new osgParticle::SmokeEffect(position, scale, intensity); osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, scale, intensity); explosion->setWind(wind); explosionDebri->setWind(wind); smoke->setWind(wind); fire->setWind(wind); osg::Group* effectsGroup = new osg::Group; effectsGroup->addChild(explosion); effectsGroup->addChild(explosionDebri); effectsGroup->addChild(smoke); effectsGroup->addChild(fire); if (handleMovingModels) { // insert particle effects alongside the hit node, therefore able to track that nodes movement, // however, this does require us to insert the ParticleSystem itself into the root of the scene graph // seperately from the the main particle effects group which contains the emitters and programs. // the follow code block implements this, note the path for handling particle effects which arn't attached to // moving models is easy - just a single line of code! // tell the effects not to attach to the particle system locally for rendering, as we'll handle add it into the // scene graph ourselves. explosion->setUseLocalParticleSystem(false); explosionDebri->setUseLocalParticleSystem(false); smoke->setUseLocalParticleSystem(false); fire->setUseLocalParticleSystem(false); // find a place to insert the particle effects group alongside the hit node. // there are two possible ways that this can be done, either insert it into // a pre-existing group along side the hit node, or if no pre existing group // is found then this needs to be inserted above the hit node, and then the // particle effect can be inserted into this. osg::ref_ptr hitGeode = hit.getGeode(); osg::Node::ParentList parents = hitGeode->getParents(); osg::Group* insertGroup = 0; unsigned int numGroupsFound = 0; for(osg::Node::ParentList::iterator itr=parents.begin(); itr!=parents.end(); ++itr) { if (typeid(*(*itr))==typeid(osg::Group)) { ++numGroupsFound; insertGroup = *itr; } } if (numGroupsFound==parents.size() && numGroupsFound==1 && insertGroup) { osg::notify(osg::INFO)<<"PickHandler::pick(,) hit node's parent is a single osg::Group so we can simple the insert the particle effects group here."<addChild(effectsGroup); } else { osg::notify(osg::INFO)<<"PickHandler::pick(,) hit node doesn't have an appropriate osg::Group node to insert particle effects into, inserting a new osg::Group."<replaceChild(hit.getGeode(),insertGroup); } insertGroup->addChild(hitGeode.get()); insertGroup->addChild(effectsGroup); } // finally insert the particle systems into a Geode and attach to the root of the scene graph so the particle system // can be rendered. osg::Geode* geode = new osg::Geode; geode->addDrawable(explosion->getParticleSystem()); geode->addDrawable(explosionDebri->getParticleSystem()); geode->addDrawable(smoke->getParticleSystem()); geode->addDrawable(fire->getParticleSystem()); root->addChild(geode); } else { // when we don't have moving models we can simple insert the particle effect into the root of the scene graph osg::notify(osg::INFO)<<"PickHandler::pick(,) adding particle effects to root node."<addChild(effectsGroup); } #if 0 osg::Geode* geode = new osg::Geode; geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(position,scale))); group->addChild(geode); #endif } } protected: virtual ~PickHandler() {} }; ////////////////////////////////////////////////////////////////////////////// // main() ////////////////////////////////////////////////////////////////////////////// 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 of particle systems."); arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] image_file_left_eye image_file_right_eye"); 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); // get details on keyboard and mouse bindings used by the viewer. viewer.getUsage(*arguments.getApplicationUsage()); // register the pick handler viewer.getEventHandlerList().push_front(new PickHandler()); // 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; } osg::Group *root = new osg::Group; build_world(root); osgUtil::Optimizer optimizer; optimizer.optimize(root); // add a viewport to the viewer and attach the scene graph. viewer.setSceneData(root); // 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 before exit. viewer.sync(); return 0; }