Lagrangian Liquid Simulation
Master Thesis project on simulation of liquids using Lagrangian approach and SPH
|
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