/////////////////////////////////////////////////////////////////////////// // // Realistic rain real-time simulation program // // Pierre Rousseau // /////////////////////////////////////////////////////////////////////////// // // Main camera, and external events (mouse/keyboard) handling // /////////////////////////////////////////////////////////////////////////// #include "RainCloseDropsParticles.h" #include "RainFrameListener.h" #include using namespace std; extern float startPos[3]; // don't go below the ground with this extern RaySceneQuery* raySceneQuery; RainFrameListener::RainFrameListener(RenderWindow* win, Camera* cam, SceneNode* ViewNode, SceneNode* CameraNode, CloseDropsParticles *syst) : ExampleFrameListener(win, cam) { mViewNode = ViewNode; mCameraNode = CameraNode; mRainSystem = syst; timer = 0.0; mWindow->setAutoUpdated(false); Overlay * mRainOverlay = OverlayManager::getSingleton().getByName("RainOverlay"); mRainOverlay->show(); showDebugOverlay(false); // useless as we defined our own overlay } void RainFrameListener::updateStats(void) { static String currFps = "FPS : "; // update stats when necessary try { OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("RainCurrFps"); const RenderTarget::FrameStats& stats = mWindow->getStatistics(); guiCurr->setCaption(currFps + StringConverter::toString(stats.lastFPS)); } catch(...) { // ignore } } void RainFrameListener::displayMessage(String text) { try { OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("RainCurrFps"); guiCurr->setCaption(text); } catch(...) { // ignore } } bool RainFrameListener::frameStarted(const FrameEvent& evt) { static bool usePrimaryPositionTexture = true; static bool capture = false; static bool stopDrop = false; static int numScreenshot=0; static bool firstFrame = true; static bool showOverlay = true; Real moveFactor; #ifdef SNOW static float fallSpeed = 0.1; static float windForce = 0.02; #else static float fallSpeed = 0.7; static float windForce = 0.0; #endif if (firstFrame) { mRainSystem->definePosTextureAndBackup(); firstFrame = false; // give initial values to the shader parameters Vector3 camPos = mViewNode->getWorldPosition(); Vector3 viewPos( (camPos.x - PARTICLES_MIN_X) / float(PARTICLES_MAX_X - PARTICLES_MIN_X), (camPos.y - PARTICLES_MIN_Y) / float(PARTICLES_MAX_Y - PARTICLES_MIN_Y), (camPos.z - PARTICLES_MIN_Z) / float(PARTICLES_MAX_Z - PARTICLES_MIN_Z) ); Vector3 viewDir(0.0, 0.0, 1.0); Vector3 fallVect(windForce, -fallSpeed, 0.0); Vector3 boxMin(VIS_PARTICLE_BOX_MIN_X, VIS_PARTICLE_BOX_MIN_Y, VIS_PARTICLE_BOX_MIN_Z); Vector3 boxMax(VIS_PARTICLE_BOX_MAX_X, VIS_PARTICLE_BOX_MAX_Y, VIS_PARTICLE_BOX_MAX_Z); Vector3 lastMove( 0.0, 0.0, 0.0 ); Vector3 randomMove((rand() % 100 ) / 1000.0 - 0.05, (rand() % 100 ) / 1000.0 - 0.05, (rand() % 100 ) / 1000.0 - 0.05); mRainSystem->updateShaderParameters(0.015, mRainSystem->mRainWidth, mRainSystem->mRainStreaksHeight, mRainSystem->getLightManagerPtr()->getLightCount(), viewPos, viewDir, fallVect, boxMin, boxMax, lastMove, randomMove, 1); // the control overlay is for debugging purposes : uncomment if you wish to see a dynamic texture, such as the particle's position texture. //mRainSystem->makeControlOverlay(); } else { // set the active position texture mRainSystem->showPositionBillboards(); mRainSystem->updatePositionTexture(usePrimaryPositionTexture); mRainSystem->hidePositionBillboards(); usePrimaryPositionTexture = !usePrimaryPositionTexture; //update random factor mRainSystem->updateRandomMove(Vector3((rand() % 100 ) / 1000.0 - 0.05, (rand() % 100 ) / 1000.0 - 0.05, (rand() % 100 ) / 1000.0 - 0.05)); } if (capture) { moveFactor = evt.timeSinceLastFrame * 10.0; // set framerate to 30 fps mRainSystem->updateTimeSinceLastFrame(1.0 / 30.0); } else { // tell the stat overlay to update updateStats(); // update time since last frame mRainSystem->updateTimeSinceLastFrame(evt.timeSinceLastFrame); moveFactor = evt.timeSinceLastFrame * 80.0; } timer -= evt.timeSinceLastFrame; mInputDevice->capture(); ///////////////////////////////////////////////// ///////////////////////////////////////////////// // translations ///////////////////////////////////////////////// ///////////////////////////////////////////////// // save the previous position Vector3 oldViewPos = mViewNode->getWorldPosition(); if (mInputDevice->isKeyDown(Ogre::KC_UP) || mInputDevice->isKeyDown(Ogre::KC_NUMPAD8)) { mViewNode->translate(0.0, 0.0, -moveFactor, Ogre::Node::TS_LOCAL); } if (mInputDevice->isKeyDown(Ogre::KC_DOWN) || mInputDevice->isKeyDown(Ogre::KC_NUMPAD5)) { mViewNode->translate(0.0, 0.0, moveFactor, Ogre::Node::TS_LOCAL); } // keep the feets on the ground static Ray updateRay; updateRay.setOrigin(mViewNode->getPosition()); updateRay.setDirection(Vector3::NEGATIVE_UNIT_Y); raySceneQuery->setRay(updateRay); raySceneQuery->setQueryMask(0x80000000); RaySceneQueryResult& qryResult = raySceneQuery->execute(); RaySceneQueryResult::iterator i = qryResult.begin(); if (i != qryResult.end() && i->worldFragment) { SceneQuery::WorldFragment* wf = i->worldFragment; mViewNode->setPosition(mViewNode->getPosition().x, i->worldFragment->singleIntersection.y + HAUTEUR_OBSERVATEUR, mViewNode->getPosition().z); } // in case the user moved, update his position Vector3 camPos = mViewNode->getWorldPosition(); mRainSystem->updateViewPos(camPos); ///////////////////////////////////////////////// ///////////////////////////////////////////////// // rotations ///////////////////////////////////////////////// ///////////////////////////////////////////////// Real rotX = -mInputDevice->getMouseRelativeX(); Real rotY = -mInputDevice->getMouseRelativeY(); Vector3 oldViewDir = mViewNode->getOrientation().zAxis(); mViewNode->yaw(Ogre::Radian(rotX * evt.timeSinceLastFrame / (capture ? 100.0 : 1.0)), Ogre::Node::TS_WORLD); mCameraNode->pitch(-Ogre::Radian(rotY * evt.timeSinceLastFrame / (capture ? 100.0 : 1.0)), Ogre::Node::TS_LOCAL); // get the orientation for the update shader Vector3 newViewDir = mViewNode->getOrientation().zAxis(); Vector3 lastMove( (camPos.x - oldViewPos.x) / float(VIS_PARTICLE_BOX_MAX_X - VIS_PARTICLE_BOX_MIN_X) - (newViewDir.x - oldViewDir.x)/2.0, (camPos.y - oldViewPos.y) / float(VIS_PARTICLE_BOX_MAX_Y - VIS_PARTICLE_BOX_MIN_Y), (camPos.z - oldViewPos.z) / float(VIS_PARTICLE_BOX_MAX_Z - VIS_PARTICLE_BOX_MIN_Z) - (newViewDir.z - oldViewDir.z)/2.0 ); mRainSystem->updateLastMove(lastMove); mRainSystem->updateViewDir(newViewDir); ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// // // react to user input (keyboard) // ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// // pause the system if (mInputDevice->isKeyDown(KC_SPACE) && timer <= 0) { stopDrop = ! stopDrop; if (stopDrop) displayMessage("Pause"); else displayMessage(" "); timer = 0.5; // update fall vector mRainSystem->updateFallVector(Vector3(stopDrop?0.0:windForce, stopDrop?0.0:-fallSpeed, 0.0)); } // modifying particle size if (mInputDevice->isKeyDown(KC_ADD) && timer <= 0) { mRainSystem->mRainWidth += 0.05; displayMessage("Radius : " + StringConverter::toString(mRainSystem->mRainWidth * 5.0f)); std::cout << "Radius : " << mRainSystem->mRainWidth * 5.0f << std::endl; #ifndef SNOW if (mRainSystem->mRainWidth <= 0.901) { String nom_masque="masques/masque_" + StringConverter::toString(mRainSystem->mRainWidth * 5.0f) + "_.tga"; static_cast(MaterialManager::getSingleton().getByName(RAIN_MATERIAL_NAME))->getTechnique (0)->getPass (0)->getTextureUnitState (2)->setTextureName(nom_masque); } #endif timer = 0.1; // update the size mRainSystem->updateRainBilboardSideLength(mRainSystem->mRainWidth); } if (mInputDevice->isKeyDown(KC_SUBTRACT) && timer <= 0) { if (mRainSystem->mRainWidth > 0.051) { mRainSystem->mRainWidth -= 0.05; displayMessage("Radius : " + StringConverter::toString(mRainSystem->mRainWidth * 5.0f)); std::cout << "Radius : " << mRainSystem->mRainWidth * 5.0f << std::endl; #ifndef SNOW if (mRainSystem->mRainWidth <= 0.9 && mRainSystem->mRainWidth >= 0.14) { String nom_masque="masques/masque_" + StringConverter::toString(mRainSystem->mRainWidth * 5.0f) + "_.tga"; static_cast(MaterialManager::getSingleton().getByName(RAIN_MATERIAL_NAME))->getTechnique (0)->getPass (0)->getTextureUnitState (2)->setTextureName(nom_masque); } #endif } timer = 0.1; // update the size mRainSystem->updateRainBilboardSideLength(mRainSystem->mRainWidth); } // press 'S' to accelerate, and 'Q' to slow down if (mInputDevice->isKeyDown(KC_S)) { displayMessage("Accelerating"); fallSpeed += 0.002; std::cout << "fallSpeed : " << fallSpeed << std::endl; // update fall vector mRainSystem->updateFallVector(Vector3(stopDrop?0.0:windForce, stopDrop?0.0:-fallSpeed, 0.0)); } if (mInputDevice->isKeyDown(KC_Q)) { if (fallSpeed > 0.05) { displayMessage("Slowing"); fallSpeed -= 0.002; } std::cout << "fallSpeed : " << fallSpeed << std::endl; // update fall vector mRainSystem->updateFallVector(Vector3(stopDrop?0.0:windForce, stopDrop?0.0:-fallSpeed, 0.0)); } // wind handling ('W' key) if (mInputDevice->isKeyDown(KC_W)) { if (mInputDevice->isKeyDown(KC_LSHIFT) || mInputDevice->isKeyDown(KC_RSHIFT)) { displayMessage("Decreasing wind"); windForce -= 0.001; } else { displayMessage("Increasing wind"); windForce += 0.001; } std::cout << "wind : " << windForce << std::endl; // update fall vector mRainSystem->updateFallVector(Vector3(stopDrop?0.0:windForce, stopDrop?0.0:-fallSpeed, 0.0)); } // erase text if (mInputDevice->isKeyDown(KC_E)) { displayMessage(" "); } // show / hide the calimero overlay if (mInputDevice->isKeyDown(KC_O) && timer <= 0) { showOverlay = ! showOverlay; if (showOverlay) OverlayManager::getSingleton().getByName("RainOverlay")->show(); else OverlayManager::getSingleton().getByName("RainOverlay")->hide(); timer = 0.5; } // we can capture a screenshot mWindow->update(); if (mInputDevice->isKeyDown(KC_SYSRQ) && timer <= 0) { String tmp; String padding = (numScreenshot<10) ? "000" : ( (numScreenshot<100) ? "00" : ((numScreenshot<1000) ? "0" : "" ) ) ; tmp = "screenshot_" + padding; tmp += StringConverter::toString(numScreenshot++) + ".jpg"; mWindow->writeContentsToFile(tmp); timer = 0.5; } // on enregistre if (mInputDevice->isKeyDown(Ogre::KC_C) && timer <= 0) { displayMessage(" "); capture = !capture; timer = 0.5; } if (capture) { String tmp; String padding = (numScreenshot<10) ? "000" : ( (numScreenshot<100) ? "00" : ((numScreenshot<1000) ? "0" : "" ) ) ; tmp = "Capture_" + padding; tmp += StringConverter::toString(numScreenshot++) + ".jpg"; mWindow->writeContentsToFile(tmp); } // end of program if(mInputDevice->isKeyDown(Ogre::KC_ESCAPE)) return false; return true; } bool RainFrameListener::frameEnded(const FrameEvent& evt) { return ExampleFrameListener::frameEnded(evt); }