Lagrangian Liquid Simulation
Master Thesis project on simulation of liquids using Lagrangian approach and SPH
src/Simulation.cpp
Go to the documentation of this file.
00001 
00002 
00003 
00004 #include <iostream>
00005 #include <QString>
00006 #include "math.h"
00007 
00008 #include "ngl/Matrix.h"
00009 #include "ngl/Transformation.h"
00010 #include "ngl/NGLInit.h"
00011 #include "ngl/Obj.h"
00012 #include "ngl/Material.h"
00013 
00014 #include "Simulation.h"
00015 #include "Configuration.h"
00016 
00017 #include "CacheItem.h"
00018 
00019 
00020 Simulation::Simulation
00021                 (
00022                    QWidget* _parent
00023                 ) :
00024                 QGLWidget(_parent)
00025 {
00026     // set this widget to have the initial keyboard focus
00027     setFocusPolicy(Qt::StrongFocus);
00028     setFocus();
00029 
00030     //initialise simulation
00031     initialiseSimulation();
00032 }
00033 
00034 void Simulation::initialiseSimulation()
00035 {
00036     //initialise mouse rotation values
00037     m_rotate=false;
00038     m_spinXFace=-20;
00039     m_spinYFace=-40;
00040 
00041     //initialise mouse movement values
00042     m_move=false;
00043     m_moveX=0;
00044     m_moveY=0;
00045 
00046     //init framerate and mouse move sensitivity
00047     ngl::Real timestep = 0;
00048     Configuration::initialiseWindow(m_frameRate, m_mouseMoveSensitivity, timestep, m_fpsTimerEnabled, m_cacheEnabled, m_cacheSamplingInterval, m_cacheAutomaticFlushEnabled, m_cacheAutomaticFlushInterval, m_cacheExportRBDEnabled, m_cacheExportBoundaryEnabled, m_cachePath);
00049 
00050     //create camera
00051     createCamera();
00052 
00053     //create light
00054     createLight();
00055 
00056     //initialise our shader library to \0null
00057     m_shaderLibrary = 0;
00058 
00059     //create the fluid solver
00060     m_solver = new Solver();
00061 
00062     //create the environment
00063     m_environment = new Environment();
00064 
00065     //create the integration
00066     m_integration = new Integration(SEMI_IMPLICIT_EULER, timestep);
00067 
00068     //create the simulation cache
00069     m_cache = new Cache();
00070 
00071     //start main timer
00072     m_mainSimulationTimer = startTimer(m_frameRate);
00073 
00074     //initialise fps timer
00075     m_fpsTimer = new QTimer(this);
00076     connect(m_fpsTimer, SIGNAL(timeout()), this, SLOT(processFPS()));
00077     m_fpsTimer->setInterval(1000);
00078     m_frameCounter = 0;
00079     updateFPSTimer();
00080 
00081     //initialise cache timer
00082     m_simulationCacheTimer = new QTimer(this);
00083     connect(m_simulationCacheTimer, SIGNAL(timeout()), this, SLOT(processSimulationCache()));
00084     m_simulationCacheFrameNumber = -1;
00085 
00086     //initialise cache autoFlush timer
00087     m_simulationCacheAutoFlushTimer = new QTimer(this);
00088     connect(m_simulationCacheAutoFlushTimer, SIGNAL(timeout()), this, SLOT(processSimulationCacheAutoFlush()));
00089 
00090     toggleCache();
00091     toggleAutomaticCacheFlush();
00092 
00093     //hide help by default
00094     m_drawHelp = false;
00095 
00096     //initial camera z value
00097     m_lastCamZ = m_camera.getEye().m_z;
00098 }
00099 
00100 void Simulation::deleteSimulationObjects()
00101 {
00102     //kill timers
00103     killTimer(m_mainSimulationTimer);
00104 
00105     //delete all the current objects associated with the simulation
00106     delete m_shaderLibrary;
00107     delete m_solver;
00108     delete m_environment;
00109     delete m_integration;
00110 }
00111 
00112 Simulation::~Simulation()
00113 {
00114     //clean up ngl
00115     std::cout << "\nNGL Cleanup" << std::endl;
00116 
00117     ngl::NGLInit* Init = ngl::NGLInit::instance();
00118     Init->NGLQuit();
00119     std::cout << "NGL shut down safely\n" << std::endl;
00120 
00121     //delete all current objects
00122     deleteSimulationObjects();
00123 }
00124 
00125 void Simulation::initializeGL()
00126 {
00127     //init glew
00128     ngl::NGLInit* Init = ngl::NGLInit::instance();
00129     Init->initGlew();
00130 
00131     // enable depth testing for drawing
00132     glEnable(GL_DEPTH_TEST);
00133 
00134     //initialise shaders
00135     m_shaderLibrary = new ShaderLibrary();
00136     m_shaderLibrary->updateViewProjection(&m_camera);
00137 
00138     //initialise fluid drawing
00139     m_solver->initialiseDrawing();
00140 
00141     //initialise environment drawing
00142     m_environment->loadGeometry();
00143 
00144     //create vbo for plane grid
00145     ngl::VBOPrimitives *prim=ngl::VBOPrimitives::instance();
00146     prim->createVBOQuadPlane("plane",20,20,1,1,ngl::Vector(0,1,0));
00147 
00148     //create vbo for cube fluid plane [10 x 10 grid]
00149     prim->createVBOQuadPlane("fluidPlaneLayer", 9, 9, 9, 9, ngl::Vector(0, 1, 0));
00150 }
00151 
00152 void Simulation::resizeGL
00153                 (
00154                     const int _w,
00155                     const int _h
00156                 )
00157 {
00158     //set the viewport
00159     glViewport(0, 0, _w, _h);
00160 
00161     //calculate the aspect ratio
00162     m_aspect = (float)_w / _h;
00163 
00164     //update camera aspect
00165     m_camera.setAspect(m_aspect);
00166 
00167     //update shaders ProjectionMatrix
00168     m_shaderLibrary->updateProjection(&m_camera);
00169 }
00170 
00171 void Simulation::paintGL()
00172 {
00173     // clear the screen and depth buffer
00174     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00175     glClearColor(0.4, 0.4, 0.4, 1);
00176 
00177     //update tx stack with global mouse movements
00178     ngl::Transformation trans;
00179     trans.setRotation(m_spinXFace, m_spinYFace, 0);
00180     m_transformStack.setGlobal(trans);
00181 
00182     m_shaderLibrary->useShader("Light");
00183 
00184     //visualise fluid state
00185     m_solver->render(m_transformStack, m_shaderLibrary, "Light");
00186 
00187     //draw environment
00188     m_environment->draw(m_transformStack, m_shaderLibrary, "Light", "Light");
00189 
00190     //draw grid and reference origin
00191     ngl::VBOPrimitives *prim=ngl::VBOPrimitives::instance();
00192 
00193     //draw solver hose marker if enabled
00194     if (m_solver->getDrawHoseMarker())
00195     {
00196         m_transformStack.pushTransform();
00197         {
00198             m_transformStack.getCurrentTransform().setPosition(m_solver->getHoseCenter().m_x, m_solver->getHoseCenter().m_y, m_solver->getHoseCenter().m_z);
00199             m_transformStack.getCurrentTransform().setScale(0.3,0.3,0.3);
00200 
00201             //pass vertex info to shader
00202             m_shaderLibrary->updateModel("Light", m_transformStack.getCurrAndGlobal().getMatrix(), true);
00203 
00204             //pass color info to shader
00205             m_shaderLibrary->updateColor("Light", ngl::Colour(1, 1, 1), false);
00206 
00207             prim->draw("cube");
00208         }
00209         m_transformStack.popTransform();
00210     }
00211 
00212     //display stats menu
00213     displayHelp();
00214 }
00215 
00216 void Simulation::displayHelp()
00217 {
00218     //display help menu
00219     if (m_drawHelp == true)
00220     {
00221         // first we need to fallback to the fixed functionality GL pipeline
00222         glUseProgram(0);
00223 
00224         //create font
00225         QFont font;
00226         font.setBold(false);
00227         font.setPointSize(12);
00228         glColor3f(1,1,0);
00229 
00230         //display debug and stats info
00231         QString text;
00232    }
00233 
00234 }
00235 
00236 void Simulation::timerEvent(QTimerEvent* _event)
00237 {
00238     //main simulation timer
00239     if (_event->timerId() == m_mainSimulationTimer)
00240     {
00241         //update fluid state if fluid's auto move is enabled
00242         if (m_solver->getEnableAutoFluidUpdate())
00243         {
00244             //update fluid
00245             m_solver->updateFluid(m_environment, m_integration);
00246 
00247             //refresh display
00248             updateGL();
00249 
00250             //update frame counter for fps
00251             if (m_fpsTimerEnabled) m_frameCounter++;
00252         }
00253     }
00254 }
00255 
00256 void Simulation::keyPress(QKeyEvent* _event )
00257 {
00258     switch (_event->key())
00259     {
00260         //debug info
00261 //        case Qt::Key_D : { m_drawHelp ^=true; break; }
00262 
00263         //single step fluid update
00264         case Qt::Key_W : { m_solver->updateFluid(m_environment, m_integration); break; }
00265 
00266         //toggle timer to update fluid
00267         case Qt::Key_Q : { m_solver->toggleEnableAutoFluidUpdate(); updateFPSTimer(); break; }
00268 
00269         //inject particles in fluid solver
00270         case Qt::Key_A : { m_solver->injectParticles(); break; }
00271 
00272         //toggle to next hoseable fluid
00273         case Qt::Key_S : { m_solver->toggleNextHoseableFluid(); break; }
00274 
00275         //toggle cache timer
00276         case Qt::Key_C :
00277         {
00278             m_cacheEnabled = !m_cacheEnabled;
00279             toggleCache();
00280 
00281             break;
00282         }
00283 
00284         default : break;
00285     }
00286 
00287     // re-draw GL
00288     updateGL();
00289 }
00290 
00291 void Simulation::mouseMoveEvent (QMouseEvent* _event)
00292 {
00293     //as the left mouse is still pressed and it moves, we get the x and y spin value
00294     if(m_rotate && _event->buttons() == Qt::LeftButton)
00295     {
00296         m_spinYFace = ( m_spinYFace + (_event->x() - m_origX) ) % 360 ;
00297         m_spinXFace = ( m_spinXFace + (_event->y() - m_origY) ) % 360 ;
00298         m_origX = _event->x();
00299         m_origY = _event->y();
00300 
00301         // re-draw GL
00302         updateGL();
00303     }
00304 
00305     //move camera in the (x,y) plane
00306     if(m_move && _event->buttons() == Qt::RightButton)
00307     {
00308         m_moveX = _event->x() - m_origMoveX;
00309         m_moveY = _event->y() - m_origMoveY;
00310         m_origMoveX = _event->x();
00311         m_origMoveY = _event->y();
00312 
00313         //move camera eye n look by (dx, dy)
00314         m_camera.move(m_moveX / m_mouseMoveSensitivity, m_moveY / m_mouseMoveSensitivity, 0);
00315 
00316         //update view projection matrices of camera
00317         m_shaderLibrary->updateViewProjection(&m_camera);
00318 
00319         // re-draw GL
00320         updateGL();
00321     }
00322 }
00323 
00324 void Simulation::mousePressEvent (QMouseEvent* _event)
00325 {
00326     //keep track of origin of x,y movement for rotation
00327     if(_event->button() == Qt::LeftButton)
00328     {
00329         m_origX = _event->x();
00330         m_origY = _event->y();
00331         m_rotate =true;
00332     }
00333 
00334     //keep track of origin of x,y movement for tracking
00335     if(_event->button() == Qt::RightButton)
00336     {
00337         m_origMoveX = _event->x();
00338         m_origMoveY = _event->y();
00339         m_move =true;
00340     }
00341 }
00342 
00343 void Simulation::mouseReleaseEvent (QMouseEvent* _event)
00344 {
00345     //disable rotation
00346     if (_event->button() == Qt::LeftButton) m_rotate=false;
00347 
00348     //disable tracking
00349     if (_event->button() == Qt::RightButton) m_move=false;
00350 
00351 }
00352 
00353 void Simulation::createCamera()
00354 {
00355     //create camera from config parameters
00356     m_camera = Configuration::initialiseCamera(m_aspect);
00357 }
00358 
00359 void Simulation::createLight()
00360 {
00361     //set a material
00362     ngl::Material m(ngl::PEWTER); m.use();
00363     m.setSpecular(ngl::Colour(1,1,1));
00364     m.setSpecularExponent(0.2);;
00365 
00366     //create our light from config and enable it
00367     m_light = Configuration::initialiseLight(); m_light.enable();
00368 }
00369 
00370 void Simulation::updateCameraZoom(int depth)
00371 {
00372     //calculate the relative change in Z value
00373     float newZ = (float)(-depth);
00374     float changeInZ = newZ - m_lastCamZ;
00375 
00376     //calculate the small change in N that corresponds to the change in Z
00377     float dn = changeInZ / m_camera.getN().m_z;
00378 
00379     //slide the camera along the N vector by the change in N
00380     m_camera.slide(0, 0, dn);
00381 
00382     //save the new Z value
00383     m_lastCamZ = m_camera.getEye().m_z;
00384 
00385     //update view projection matrices of camera
00386     if ((m_shaderLibrary != NULL) && (m_shaderLibrary != 0)) m_shaderLibrary->updateViewProjection(&m_camera);
00387 
00388     //refresh display
00389     updateGL();
00390 }
00391 
00392 void Simulation::setup2DTopView()
00393 {
00394     //2d top view enabled -> rotate our view to give a top view
00395     m_spinXFace=-90;
00396     m_spinYFace=0;
00397 
00398     updateGL();
00399 }
00400 
00401 void Simulation::setup2DFrontView()
00402 {
00403     //2d front view enabled -> rotate our view to give a front view
00404     m_spinXFace=0;
00405     m_spinYFace=0;
00406 
00407     updateGL();
00408 }
00409 
00410 void Simulation::updateFPSTimer()
00411 {
00412     if (m_solver->getEnableAutoFluidUpdate())
00413     {
00414         //start timer
00415         if (m_fpsTimerEnabled)
00416         {
00417             m_frameCounter = 0;
00418             m_fpsTimer->start();
00419         }
00420     }
00421     else
00422     {
00423         //stop timer
00424         m_fpsTimer->stop();
00425     }
00426 }
00427 
00428 void Simulation::processFPS()
00429 {
00430     std::cout << "FPS : " << m_frameCounter << "\n";
00431     m_frameCounter = 0;
00432 }
00433 
00434 void Simulation::toggleCache()
00435 {
00436     m_simulationCacheTimer->stop();
00437 
00438     if (m_cacheEnabled)
00439     {
00440         //cache current initial frame
00441         m_simulationCacheTimer->singleShot(0, this, SLOT(processSimulationCache()));
00442 
00443         //start timers
00444         m_simulationCacheTimer->start(m_cacheSamplingInterval);
00445     }
00446 }
00447 
00448 void Simulation::toggleAutomaticCacheFlush()
00449 {
00450     //stop timers
00451     m_simulationCacheAutoFlushTimer->stop();
00452 
00453     if (m_cacheAutomaticFlushEnabled)
00454     {
00455         //start timers
00456         m_simulationCacheAutoFlushTimer->start(m_cacheAutomaticFlushInterval);
00457     }
00458 }
00459 
00460 void Simulation::processSimulationCache()
00461 {
00462     if (m_cacheEnabled)
00463     {
00464         ++m_simulationCacheFrameNumber;
00465 
00466         //create a fluid cache item and add it to the cache
00467         m_cache->addItem(CacheItem(m_solver->getPositionList(), m_solver->getNameList(), m_simulationCacheFrameNumber, FLUID_CACHE));
00468 
00469         //create a boundary cache item and add it to the cache if enabled
00470         if (m_cacheExportBoundaryEnabled) m_cache->addItem(CacheItem(m_environment->getBoundaryPositionList(), m_environment->getBoundaryNameList(), m_simulationCacheFrameNumber, BOUNDARY_CACHE));
00471 
00472         //create a rbd cache item and add it to the cache if enabled
00473         if (m_cacheExportRBDEnabled && m_environment->getObstacleEnabled())
00474         {
00475             m_cache->addItem(CacheItem(m_environment->getSpherePositionList(), m_environment->getSphereNameList(), m_simulationCacheFrameNumber, SPHERE_CACHE));
00476             m_cache->addItem(CacheItem(m_environment->getCapsulePositionList(), m_environment->getCapsuleNameList(), m_simulationCacheFrameNumber, CAPSULE_CACHE));
00477         }
00478 
00479         std::cout << "Cached frame number : " << m_simulationCacheFrameNumber << "\n";
00480     }
00481 }
00482 
00483 void Simulation::processSimulationCacheAutoFlush()
00484 {
00485     if (m_cacheAutomaticFlushEnabled) flushCache();
00486 }
00487 
00488 void Simulation::flushCache()
00489 {
00490     //flush cache to file
00491     m_cache->flushCache(m_cachePath);
00492 
00493     std::cout << "Cache Flushed\n";
00494 }
00495 
00496 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator