Lagrangian Liquid Simulation
Master Thesis project on simulation of liquids using Lagrangian approach and SPH
|
00001 00002 00003 00004 00005 #include <iostream> 00006 #include "QString" 00007 00008 #include "pugixml.hpp" 00009 00010 #include "boost/tokenizer.hpp" 00011 #include <boost/algorithm/string.hpp> 00012 #include <boost/lexical_cast.hpp> 00013 #include <boost/foreach.hpp> 00014 00015 #include "ngl/Colour.h" 00016 #include "ngl/VBOPrimitives.h" 00017 00018 #include "Configuration.h" 00019 00020 00021 //static variables initialisation 00022 std::string Configuration::s_settingsDoc = "config/Settings.xml"; 00023 bool Configuration::s_debug = true; 00024 ngl::Real Configuration::s_accelerationOfFreeFall = -9.28; 00025 int Configuration::s_nextParticleId = 0; 00026 int Configuration::s_nextObstacleId = -1; 00027 00028 ngl::Colour Configuration::s_normalColour(0, 0, 1); 00029 ngl::Colour Configuration::s_boundaryColour(1, 1, 1); 00030 00031 std::vector<ngl::Real> Configuration::getFloatVectorFromString 00032 ( 00033 const std::string _str, 00034 const int _d 00035 ) 00036 { 00037 //split string into array of strings by "," 00038 std::vector<std::string> strArray; 00039 boost::split(strArray, _str, boost::is_any_of(",")); 00040 00041 //loop through strings and convert to float array 00042 std::vector<ngl::Real> floatArray; 00043 for (int i = 0; i < _d; i++) floatArray.push_back(boost::lexical_cast<ngl::Real>(strArray[i])); 00044 00045 //return array of float 00046 return floatArray; 00047 } 00048 00049 void Configuration::initialiseWindow 00050 ( 00051 int& o_frameRate, 00052 int& o_mouseMoveSensitivity, 00053 ngl::Real& o_timestep, 00054 bool& o_fpsTimerEnabled, 00055 bool& o_cacheEnabled, 00056 int& o_cacheSamplingInterval, 00057 bool& o_cacheAutomaticFlushEnabled, 00058 int& o_cacheAutomaticFlushInterval, 00059 bool& o_cacheExportRBDEnabled, 00060 bool& o_cacheExportBoundaryEnabled, 00061 std::string& o_path 00062 ) 00063 { 00064 //load settings xml file 00065 pugi::xml_document doc; 00066 doc.load_file(s_settingsDoc.c_str()); 00067 00068 //load global settings here 00069 pugi::xpath_node_set node = doc.select_nodes("/Settings/Global"); 00070 00071 o_frameRate = node.first().node().attribute("fpsFrequency").as_int(); 00072 o_mouseMoveSensitivity = node.first().node().attribute("mouseSensitivity").as_int(); 00073 o_timestep = node.first().node().attribute("timestep").as_float(); 00074 o_fpsTimerEnabled = node.first().node().attribute("fpsTimerEnabled").as_bool(); 00075 00076 //read cache parameters 00077 node = doc.select_nodes("/Settings/Cache"); 00078 00079 o_cacheEnabled = node.first().node().attribute("enabled").as_bool(); 00080 o_cacheSamplingInterval = node.first().node().attribute("samplingInterval").as_int(); 00081 o_cacheAutomaticFlushEnabled = node.first().node().attribute("automaticFlush").as_bool(); 00082 o_cacheAutomaticFlushInterval = node.first().node().attribute("automaticFlushInterval").as_int(); 00083 o_cacheExportRBDEnabled = node.first().node().attribute("exportRBD").as_bool(); 00084 o_cacheExportBoundaryEnabled = node.first().node().attribute("exportBoundary").as_bool(); 00085 00086 o_path = node.first().node().attribute("path").value(); 00087 00088 } 00089 00090 ngl::Camera Configuration::initialiseCamera(const float _aspect) 00091 { 00092 //load settings xml file 00093 pugi::xml_document doc; 00094 doc.load_file(s_settingsDoc.c_str()); 00095 00096 //get camera node 00097 pugi::xpath_node_set node = doc.select_nodes("/Settings/Camera"); 00098 00099 //get near and far plane values 00100 float near = node.first().node().attribute("near").as_float(); 00101 float far = node.first().node().attribute("far").as_float(); 00102 00103 //get fov 00104 int fov = node.first().node().attribute("fov").as_int(); 00105 00106 //get from and to values and convert to float arrays 00107 std::vector<float> from = getFloatVectorFromString(node.first().node().attribute("from").value(), 3); 00108 std::vector<float> to = getFloatVectorFromString(node.first().node().attribute("to").value(), 3); 00109 00110 //build vectors 00111 ngl::Vector eye(from[0], from[1], from[2]); 00112 ngl::Vector look(to[0], to[1], to[2]); 00113 ngl::Vector up(0, 1, 0); 00114 00115 //create a camera with the read data 00116 ngl::Camera cam; 00117 cam.set(eye, look, up); 00118 cam.setShape(fov, _aspect, near, far, ngl::PERSPECTIVE); 00119 00120 return cam; 00121 } 00122 00123 ngl::Light Configuration::initialiseLight() 00124 { 00125 //load settings xml file 00126 pugi::xml_document doc; 00127 doc.load_file(s_settingsDoc.c_str()); 00128 00129 //get camera node 00130 pugi::xpath_node_set node = doc.select_nodes("/Settings/Light"); 00131 00132 //get to values and convert to float array 00133 std::vector<float> to = getFloatVectorFromString(node.first().node().attribute("to").value(), 3); 00134 00135 //get color values and convert to float array 00136 std::vector<float> color = getFloatVectorFromString(node.first().node().attribute("color").value(), 4); 00137 00138 //build vectors 00139 ngl::Vector direction(to[0], to[1], to[2]); 00140 ngl::Colour lightColor(color[0], color[1], color[2], color[3]); 00141 00142 //create a light with the read data 00143 ngl::Light light(direction, lightColor, ngl::LIGHTREMOTE); 00144 00145 return light; 00146 } 00147 00148 std::vector<ShaderObject*>* Configuration::initialiseShader() 00149 { 00150 //load settings xml file 00151 pugi::xml_document doc; 00152 doc.load_file(s_settingsDoc.c_str()); 00153 00154 std::vector<ShaderObject*>* result = new std::vector<ShaderObject*>; 00155 00156 //get list of normal shaders 00157 pugi::xpath_node_set node = doc.select_nodes("/Settings/Shaders/Shader"); 00158 for (pugi::xpath_node_set::const_iterator it = node.begin(); it != node.end(); ++it) 00159 { 00160 //get details for a shader 00161 pugi::xpath_node node = *it; 00162 00163 //save values in array 00164 std::string name = node.node().attribute("name").value(); 00165 std::string vs = node.node().attribute("vertex").value(); 00166 std::string fs = node.node().attribute("fragment").value(); 00167 bool isSpecial = node.node().attribute("isSpecial").as_bool(); 00168 00169 std::vector<std::string> attribNames; 00170 std::vector<int> attribValues; 00171 00172 if (isSpecial) 00173 { 00174 //load attributes 00175 pugi::xpath_node_set attributes = node.node().select_nodes("Attrib"); 00176 for (pugi::xpath_node_set::const_iterator it1 = attributes.begin(); it1 != attributes.end(); ++it1) 00177 { 00178 pugi::xpath_node innerNode = *it1; 00179 00180 std::string attribName = innerNode.node().attribute("name").value(); 00181 int value = innerNode.node().attribute("value").as_int(); 00182 00183 attribNames.push_back(attribName); 00184 attribValues.push_back(value); 00185 } 00186 } 00187 00188 //create a shader and add it to our list 00189 result->push_back(new ShaderObject(name, vs, fs, attribNames, attribValues, isSpecial)); 00190 } 00191 00192 return result; 00193 } 00194 00195 void Configuration::initialiseEnvironment 00196 ( 00197 bool& o_obstacleEnabled, 00198 ngl::Vector& o_boundaryPosition, 00199 ngl::Vector& o_boundaryDimension, 00200 ngl::Real& o_boundaryRestitutionCoefficientForFluid, 00201 ngl::Real& o_boundaryRestitutionCoefficientForRBD, 00202 bool& o_boundaryBoundTop, 00203 std::vector<Particle>& o_sphereObstacleList, 00204 std::vector<Capsule>& o_capsuleObstacleList, 00205 ngl::Real& o_obstacleRestitutionCoefficient, 00206 bool& o_periodicWallEnabled, 00207 ngl::Real& o_periodicWallMaxAmplitude, 00208 ngl::Real& o_periodicWallSpeed, 00209 ngl::Real& o_periodicWallAngleIncrement, 00210 int& o_capsuleResolution 00211 ) 00212 { 00213 //load settings xml file 00214 pugi::xml_document doc; 00215 doc.load_file(s_settingsDoc.c_str()); 00216 00217 //temporary variable 00218 std::vector<ngl::Real> tmp; 00219 00220 //load global settings here 00221 pugi::xpath_node_set node = doc.select_nodes("/Settings/Environment"); 00222 00223 //read info about the boundary 00224 tmp = getFloatVectorFromString(node.first().node().select_single_node("Boundary").node().attribute("position").value(), 3); 00225 o_boundaryPosition.set(tmp[0], tmp[1], tmp[2]); 00226 00227 tmp = getFloatVectorFromString(node.first().node().select_single_node("Boundary").node().attribute("dimension").value(), 3); 00228 o_boundaryDimension.set(tmp[0], tmp[1], tmp[2]); 00229 00230 o_boundaryRestitutionCoefficientForFluid = node.first().node().select_single_node("Boundary").node().attribute("restitutionForFluid").as_float(); 00231 o_boundaryRestitutionCoefficientForRBD = node.first().node().select_single_node("Boundary").node().attribute("restitutionForRBD").as_float(); 00232 00233 o_boundaryBoundTop = node.first().node().select_single_node("Boundary").node().attribute("boundTop").as_bool(); 00234 00235 //read info about periodic wall 00236 o_periodicWallEnabled = node.first().node().select_single_node("Boundary").node().attribute("periodicWallEnabled").as_bool(); 00237 00238 node = node.first().node().select_single_node("Boundary").node().select_nodes("PeriodicWall"); 00239 00240 o_periodicWallMaxAmplitude = node.first().node().attribute("maxAmplitude").as_float(); 00241 o_periodicWallSpeed = node.first().node().attribute("speed").as_float(); 00242 o_periodicWallAngleIncrement = node.first().node().attribute("angleIncrement").as_float(); 00243 00244 //read info about obstacles 00245 node = doc.select_nodes("/Settings/Environment/Obstacles"); 00246 o_obstacleEnabled = node.first().node().attribute("enabled").as_bool(); 00247 00248 o_obstacleRestitutionCoefficient = node.first().node().attribute("restitution").as_float(); 00249 00250 o_capsuleResolution = node.first().node().attribute("capsuleResolution").as_int(); 00251 00252 //get list of sphere obstacles 00253 node = node.first().node().select_nodes("Sphere"); 00254 for (pugi::xpath_node_set::const_iterator it = node.begin(); it != node.end(); ++it) 00255 { 00256 //get details for an obstacle 00257 pugi::xpath_node node1 = *it; 00258 00259 //get parameters active parameter 00260 tmp = getFloatVectorFromString(node1.node().attribute("position").value(), 3); 00261 ngl::Vector position(tmp[0], tmp[1], tmp[2]); 00262 00263 tmp = getFloatVectorFromString(node1.node().attribute("velocity").value(), 3); 00264 ngl::Vector velocity(tmp[0], tmp[1], tmp[2]); 00265 00266 ngl::Real radius = node1.node().attribute("radius").as_float(); 00267 00268 ngl::Real moveable = node1.node().attribute("moveable").as_bool(); 00269 00270 tmp = getFloatVectorFromString(node1.node().attribute("colour").value(), 3); 00271 ngl::Colour colour(tmp[0], tmp[1], tmp[2]); 00272 00273 //create and add obstacle to list 00274 o_sphereObstacleList.push_back 00275 ( 00276 Particle 00277 ( 00278 Configuration::s_nextObstacleId--, 00279 0, 00280 position, 00281 colour, 00282 radius, 00283 moveable, 00284 velocity 00285 ) 00286 ); 00287 } 00288 00289 //get list of capsule obstacles 00290 node = doc.select_nodes("/Settings/Environment/Obstacles/Capsule"); 00291 for (pugi::xpath_node_set::const_iterator it = node.begin(); it != node.end(); ++it) 00292 { 00293 //get details for an obstacle 00294 pugi::xpath_node node1 = *it; 00295 00296 //get parameters active parameter 00297 tmp = getFloatVectorFromString(node1.node().attribute("position1").value(), 3); 00298 ngl::Vector position1(tmp[0], tmp[1], tmp[2]); 00299 00300 tmp = getFloatVectorFromString(node1.node().attribute("position2").value(), 3); 00301 ngl::Vector position2(tmp[0], tmp[1], tmp[2]); 00302 00303 ngl::Real incrementAngle = node1.node().attribute("incrementAngle").as_float(); 00304 00305 ngl::Real initialAngle = node1.node().attribute("initialAngle").as_float(); 00306 00307 ngl::Real radius = node1.node().attribute("radius").as_float(); 00308 00309 ngl::Real moveable = node1.node().attribute("moveable").as_bool(); 00310 00311 tmp = getFloatVectorFromString(node1.node().attribute("colour").value(), 3); 00312 ngl::Colour colour(tmp[0], tmp[1], tmp[2]); 00313 00314 //calculate orientation vector 00315 ngl::Vector orientationVector = position2 - position1; 00316 00317 //calculate height half 00318 ngl::Real height = orientationVector.length() / 2.0; 00319 00320 //normalise orientation vector 00321 orientationVector.normalize(); 00322 00323 //calculate centre position 00324 ngl::Vector centrePosition = position1 + (orientationVector * height); 00325 00326 std::cout << "test Capsules : " << centrePosition << "\n"; 00327 00328 //create and add obstacle to list 00329 o_capsuleObstacleList.push_back 00330 ( 00331 Capsule 00332 ( 00333 Configuration::s_nextObstacleId--, 00334 0, 00335 centrePosition, 00336 colour, 00337 radius, 00338 height, 00339 orientationVector, 00340 incrementAngle, 00341 initialAngle, 00342 moveable 00343 ) 00344 ); 00345 } 00346 00347 } 00348 00349 void Configuration::initialiseFluidSolver 00350 ( 00351 ngl::Real& o_smoothingLength, 00352 std::vector<FluidParticle>& o_particleList, 00353 std::vector<FluidParticle>& o_hoseParticlePrototypeList, 00354 std::vector<FluidParticle>& o_hoseParticleList, 00355 ngl::Vector& o_centerOfHose, 00356 ngl::Vector& o_velocityOfHose, 00357 bool& o_drawHoseMarker, 00358 bool& o_hoseWaitUntilFirstHitBoundary, 00359 bool& o_hoseWaitUntilFirstHitRBD 00360 ) 00361 { 00362 //load settings xml file 00363 pugi::xml_document doc; 00364 doc.load_file(s_settingsDoc.c_str()); 00365 00366 //load global settings here 00367 pugi::xpath_node_set node = doc.select_nodes("/Settings/FluidSolver"); 00368 00369 //read smoothing length 00370 o_smoothingLength = node.first().node().attribute("smoothingLength").as_float(); 00371 00372 //initially reserve space for maximum growth of particle list 00373 int initialReservedParticleCount =node.first().node().attribute("initialReservedParticleCount").as_int(); 00374 o_particleList.reserve(initialReservedParticleCount); 00375 00376 //tmp variable 00377 std::vector<ngl::Real> tmp; 00378 00379 //get hose info 00380 node = doc.select_nodes("/Settings/FluidSolver/Hose"); 00381 00382 tmp = getFloatVectorFromString(node.first().node().attribute("center").value(), 3); 00383 o_centerOfHose = ngl::Vector(tmp[0], tmp[1], tmp[2]); 00384 00385 o_hoseWaitUntilFirstHitBoundary = node.first().node().attribute("waitUntilHitBoundary").as_bool(); 00386 o_hoseWaitUntilFirstHitRBD = node.first().node().attribute("waitUntilHitRBD").as_bool(); 00387 00388 o_drawHoseMarker = node.first().node().attribute("drawMarker").as_bool(); 00389 00390 std::string hoseMeshName = node.first().node().select_single_node("Obj").node().child_value(); 00391 ngl::Obj hoseMesh(hoseMeshName); 00392 00393 tmp = getFloatVectorFromString(node.first().node().select_single_node("Obj").node().attribute("center").value(), 3); 00394 ngl::Vector centerOfHoseMesh(tmp[0], tmp[1], tmp[2]); 00395 00396 tmp = getFloatVectorFromString(node.first().node().select_single_node("Velocity").node().attribute("value").value(), 3); 00397 o_velocityOfHose = ngl::Vector(tmp[0], tmp[1], tmp[2]); 00398 00399 for (int i = 0; i < hoseMesh.getNumVerts(); i++) 00400 { 00401 //create particle @ mesh vertices and add to hose particle list 00402 o_hoseParticleList.push_back 00403 ( 00404 FluidParticle 00405 ( 00406 0, 00407 0, 00408 0, 00409 centerOfHoseMesh + hoseMesh.getVertexAtIndex(i) 00410 ) 00411 ); 00412 } 00413 00414 //get a list of fluids 00415 node = doc.select_nodes("/Settings/FluidSolver/Fluid"); 00416 for (pugi::xpath_node_set::const_iterator it = node.begin(); it != node.end(); ++it) 00417 { 00418 //get details for a fluid 00419 pugi::xpath_node node = *it; 00420 00421 //get its active parameter 00422 bool isActive = node.node().attribute("active").as_bool(); 00423 00424 //skip this fluid if not active 00425 if (isActive) 00426 { 00427 //else, active fluid => go on creating and adding particles of fluid to particle list 00428 00429 //read parameters 00430 ngl::Real volume = boost::lexical_cast<ngl::Real>(node.node().select_single_node("Volume").node().child_value()); 00431 00432 ngl::Real materialDensity = boost::lexical_cast<ngl::Real>(node.node().select_single_node("MaterialDensity").node().child_value()); 00433 00434 tmp = getFloatVectorFromString(node.node().select_single_node("Colour").node().attribute("value").value(), 3); 00435 ngl::Colour color(tmp[0], tmp[1], tmp[2]); 00436 00437 std::string meshName = node.node().select_single_node("Obj").node().child_value(); 00438 ngl::Obj mesh(meshName); 00439 00440 tmp = getFloatVectorFromString(node.node().select_single_node("Obj").node().attribute("center").value(), 3); 00441 ngl::Vector centerOfMesh(tmp[0], tmp[1], tmp[2]); 00442 00443 tmp = getFloatVectorFromString(node.node().select_single_node("Velocity").node().attribute("value").value(), 3); 00444 ngl::Vector velocity(tmp[0], tmp[1], tmp[2]); 00445 00446 ngl::Real viscosityConstant = boost::lexical_cast<ngl::Real>(node.node().select_single_node("ViscosityConstant").node().child_value()); 00447 00448 ngl::Real gasConstant = boost::lexical_cast<ngl::Real>(node.node().select_single_node("GasConstant").node().child_value()); 00449 00450 ngl::Real surfaceTensionCoefficient = node.node().select_single_node("SurfaceTension").node().attribute("coefficient").as_float(); 00451 ngl::Real surfaceTensionThreshold = node.node().select_single_node("SurfaceTension").node().attribute("threshold").as_float(); 00452 ngl::Real surfaceColorCoefficient = node.node().select_single_node("SurfaceTension").node().attribute("color").as_float(); 00453 00454 ngl::Real interfaceTensionCoefficient = node.node().select_single_node("SurfaceInterface").node().attribute("coefficient").as_float(); 00455 ngl::Real interfaceTensionThreshold = node.node().select_single_node("SurfaceInterface").node().attribute("threshold").as_float(); 00456 ngl::Real interfaceColorCoefficient = node.node().select_single_node("SurfaceInterface").node().attribute("color").as_float(); 00457 00458 ngl::Real radius = boost::lexical_cast<ngl::Real>(node.node().select_single_node("Radius").node().child_value()); 00459 00460 std::string name = node.node().attribute("name").value(); 00461 00462 //calculate mass per particle 00463 int particleCount = mesh.getNumVerts(); 00464 ngl::Real mass = materialDensity * (volume / ((ngl::Real)particleCount)); 00465 00466 std::cout << "Mesh : " << meshName << "\tparticle count : " << particleCount << "\tunit mass : " << mass << "\n"; 00467 00468 //create fluid particles from obj 00469 for (int i = 0; i < particleCount; i++) 00470 { 00471 //create particle @ mesh vertices and add to particle list 00472 o_particleList.push_back 00473 ( 00474 FluidParticle 00475 ( 00476 Configuration::s_nextParticleId++, 00477 mass, 00478 materialDensity, 00479 centerOfMesh + mesh.getVertexAtIndex(i), 00480 velocity, 00481 viscosityConstant, 00482 gasConstant, 00483 surfaceTensionCoefficient, 00484 surfaceTensionThreshold, 00485 surfaceColorCoefficient, 00486 interfaceTensionCoefficient, 00487 interfaceTensionThreshold, 00488 interfaceColorCoefficient, 00489 color, 00490 radius, 00491 name 00492 ) 00493 ); 00494 } 00495 std::cout << "Fluid : << " << name << "\tListSize : " << o_particleList.size() << "\n"; 00496 00497 //add a sample of this fluid's particle to hose prototype list 00498 o_hoseParticlePrototypeList.push_back 00499 ( 00500 FluidParticle 00501 ( 00502 -1, 00503 mass, 00504 materialDensity, 00505 0, 00506 0, 00507 viscosityConstant, 00508 gasConstant, 00509 surfaceTensionCoefficient, 00510 surfaceTensionThreshold, 00511 surfaceColorCoefficient, 00512 interfaceTensionCoefficient, 00513 interfaceTensionThreshold, 00514 interfaceColorCoefficient, 00515 color, 00516 radius, 00517 name, 00518 o_hoseWaitUntilFirstHitBoundary, 00519 o_hoseWaitUntilFirstHitRBD 00520 ) 00521 ); 00522 } 00523 } 00524 }