/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commercial and non commercial applications, * as long as this copyright notice is maintained. * * This application is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void createFountainEffect(osgParticle::ModularEmitter* emitter, osgParticle::ModularProgram* program) { // Emit specific number of particles every frame osg::ref_ptr rrc = new osgParticle::RandomRateCounter; rrc->setRateRange(500, 2000); // Accelerate particles in the given gravity direction. osg::ref_ptr accel = new osgParticle::AccelOperator; accel->setToGravity(); // Multiply each particle's velocity by a damping constant. osg::ref_ptr damping = new osgParticle::DampingOperator; damping->setDamping(0.9f); // Bounce particles off objects defined by one or more domains. // Supported domains include triangle, rectangle, plane, disk and sphere. // Since a bounce always happens instantaneously, it will not work correctly with unstable delta-time. // At present, even the floating error of dt (which are applied to ParticleSystem and Operator separately) // causes wrong bounce results. Some one else may have better solutions for this. osg::ref_ptr bounce = new osgParticle::BounceOperator; bounce->setFriction(-0.05); bounce->setResilience(0.35); bounce->addDiskDomain(osg::Vec3(0.0f, 0.0f, -2.0f), osg::Z_AXIS, 8.0f); bounce->addPlaneDomain(osg::Plane(osg::Z_AXIS, 5.0f)); // Kill particles going inside/outside of specified domains. osg::ref_ptr sink = new osgParticle::SinkOperator; sink->setSinkStrategy(osgParticle::SinkOperator::SINK_OUTSIDE); sink->addSphereDomain(osg::Vec3(), 20.0f); emitter->setCounter(rrc.get()); program->addOperator(accel.get()); program->addOperator(damping.get()); program->addOperator(bounce.get()); program->addOperator(sink.get()); } const std::string OSG_DATA_FOLDER("C:/Users/User/Desktop/OpenSceneGraph/OpenSceneGraph-Data/"); int main(int argc, char** argv) { auto useVertexAttributeAliasing = false; auto useModelViewAndProjectionUniforms = true; bool useShaders = false; if (argc > 1) { auto caseNo = atoi(argv[1]); // 8 pemutations of 3 options, 0-7 // Case 4 : Black cow // Case 5 : Black cow, purple particle smoke // Case 6 : Black cow useVertexAttributeAliasing = (0x4 & caseNo)!=0; useModelViewAndProjectionUniforms = (0x2 & caseNo) != 0; useShaders = (0x1 & caseNo) != 0; } // use an ArgumentParser object to manage the program arguments. osg::ArgumentParser arguments(&argc, argv); osgViewer::Viewer viewer(arguments); std::string textureFile(OSG_DATA_FOLDER + "Images/smoke.rgb"); while (arguments.read("--texture", textureFile)) {} float pointSize = 20.0f; while (arguments.read("--point", pointSize)) {} double visibilityDistance = -1.0f; while (arguments.read("--visibility", visibilityDistance)) {} bool customShape = false; while (arguments.read("--enable-custom")) { customShape = true; } unsigned int helpType = 0; if ((helpType = arguments.readHelpType())) { arguments.getApplicationUsage()->write(std::cout, helpType); return 1; } // report any errors if they have occurred when parsing the program arguments. if (arguments.errors()) { arguments.writeErrorMessages(std::cout); return 1; } // set up the camera manipulators. { osg::ref_ptr keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator; keyswitchManipulator->addMatrixManipulator('1', "Trackball", new osgGA::TrackballManipulator()); keyswitchManipulator->addMatrixManipulator('2', "Flight", new osgGA::FlightManipulator()); keyswitchManipulator->addMatrixManipulator('3', "Drive", new osgGA::DriveManipulator()); keyswitchManipulator->addMatrixManipulator('4', "Terrain", new osgGA::TerrainManipulator()); keyswitchManipulator->addMatrixManipulator('5', "Orbit", new osgGA::OrbitManipulator()); keyswitchManipulator->addMatrixManipulator('6', "FirstPerson", new osgGA::FirstPersonManipulator()); keyswitchManipulator->addMatrixManipulator('7', "Spherical", new osgGA::SphericalManipulator()); std::string pathfile; double animationSpeed = 1.0; while (arguments.read("--speed", animationSpeed)) {} char keyForAnimationPath = '8'; while (arguments.read("-p", pathfile)) { osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile); if (apm || !apm->valid()) { apm->setTimeScale(animationSpeed); unsigned int num = keyswitchManipulator->getNumMatrixManipulators(); keyswitchManipulator->addMatrixManipulator(keyForAnimationPath, "Path", apm); keyswitchManipulator->selectMatrixManipulator(num); ++keyForAnimationPath; } } viewer.setCameraManipulator(keyswitchManipulator.get()); } // add the state manipulator viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); // add the thread model handler viewer.addEventHandler(new osgViewer::ThreadingHandler); // add the window size toggle handler viewer.addEventHandler(new osgViewer::WindowSizeHandler); // add the stats handler viewer.addEventHandler(new osgViewer::StatsHandler); // add the help handler viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); // add the record camera path handler viewer.addEventHandler(new osgViewer::RecordCameraPathHandler); // add the LOD Scale handler viewer.addEventHandler(new osgViewer::LODScaleHandler); // add the screen capture handler viewer.addEventHandler(new osgViewer::ScreenCaptureHandler); // load the data osg::ref_ptr loadedModel = osgDB::readRefNodeFiles(arguments); // any option left unread are converted into errors to write out later. arguments.reportRemainingOptionsAsUnrecognized(); // report any errors if they have occurred when parsing the program arguments. if (arguments.errors()) { arguments.writeErrorMessages(std::cout); return 1; } /*** Customize particle template and system attributes ***/ osg::ref_ptr ps = new osgParticle::ParticleSystem; ps->getDefaultParticleTemplate().setLifeTime(5.0f); if (customShape) { // osgParticle now supports making use of customized drawables. The draw() method will be executed // and display lists will be called for each particle. It is always a huge consumption of memory, and // hardly to use shaders to render them, so please be careful using this feature. ps->getDefaultParticleTemplate().setShape(osgParticle::Particle::USER); ps->getDefaultParticleTemplate().setDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(), 1.0f))); useShaders = false; } else { // The shader only supports rendering points at present. ps->getDefaultParticleTemplate().setShape(osgParticle::Particle::POINT); } // Set the visibility distance of particles, due to their Z-value in the eye coordinates. // Particles that are out of the distance (or behind the eye) will not be rendered. ps->setVisibilityDistance(visibilityDistance); if (useShaders) { // Set using local GLSL shaders to render particles. // At present, this is slightly efficient than ordinary methods. The bottlenack here seems to be the cull // traversal time. Operators go through the particle list again and again... ps->setDefaultAttributesUsingShaders(textureFile, true, 0); } else { // The default methods uses glBegin()/glEnd() pairs. Fortunately the GLBeginEndAdapter does improve the // process, which mimics the immediate mode with glDrawArrays(). ps->setDefaultAttributes(textureFile, true, false, 0); // Without the help of shaders, we have to sort particles to make the visibility distance work. Sorting is // also useful in rendering transparent particles in back-to-front order. if (visibilityDistance>0.0) ps->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); } // At last, to make the point sprite work, we have to set the points size and the sprite attribute. osg::StateSet* stateset = ps->getOrCreateStateSet(); stateset->setAttribute(new osg::Point(pointSize)); stateset->setTextureAttributeAndModes(0, new osg::PointSprite, osg::StateAttribute::ON); /*** Construct other particle system elements, including the emitter and program ***/ osg::ref_ptr emitter = new osgParticle::ModularEmitter; emitter->setParticleSystem(ps.get()); osg::ref_ptr program = new osgParticle::ModularProgram; program->setParticleSystem(ps.get()); createFountainEffect(emitter.get(), program.get()); /*** Add the entire particle system to the scene graph ***/ osg::ref_ptr parent = new osg::MatrixTransform; parent->addChild(emitter.get()); parent->addChild(program.get()); // The updater can receive particle systems as child drawables now. The addParticleSystem() method // is still usable, with which we should define another geode to contain a particle system. osg::ref_ptr updater = new osgParticle::ParticleSystemUpdater; //updater->addDrawable( ps.get() ); osg::ref_ptr root = new osg::Group; root->addChild(parent.get()); root->addChild(updater.get()); // FIXME 2010.9.19: the updater can't be a drawable; otehrwise the ParticleEffect will not work properly. why? updater->addParticleSystem(ps.get()); osg::ref_ptr geode = new osg::Geode; geode->addDrawable(ps.get()); root->addChild(geode.get()); std::string cowFile(OSG_DATA_FOLDER + "cow.osg"); auto cow = osgDB::readNodeFile(cowFile); root->addChild(cow); viewer.setSceneData(root); viewer.realize(); auto cams = std::vector(); viewer.getCameras(cams, false); for (int i = 0; i < cams.size(); i++) { if(cams.at(i)->getGraphicsContext() ) cams.at(i)->getGraphicsContext()->getState()->setUseVertexAttributeAliasing(useVertexAttributeAliasing); if (cams.at(i)->getGraphicsContext()) cams.at(i)->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(useModelViewAndProjectionUniforms); } viewer.run(); }