From ca226d8c02bbf9e1338c93b705c898a0ac44c7e2 Mon Sep 17 00:00:00 2001 From: yggdrasil75 Date: Sat, 7 Feb 2026 08:58:54 -0500 Subject: [PATCH] added fluidsim to main --- makefile | 2 +- tests/fluidsim.cpp | 216 +++++++++++++++++++++++++------------- tests/g3etest.cpp | 16 +++ util/sim/fluidsim.hpp | 33 +++--- util/timing_decorator.cpp | 7 +- 5 files changed, 186 insertions(+), 88 deletions(-) diff --git a/makefile b/makefile index cf3ef30..d3ef09d 100644 --- a/makefile +++ b/makefile @@ -34,7 +34,7 @@ endif CXXFLAGS = $(BASE_CXXFLAGS) $(SIMD_CXXFLAGS) # Source files -SRC := $(SRC_DIR)/fluidsim.cpp +SRC := $(SRC_DIR)/g3etest.cpp #SRC := $(SRC_DIR)/g2chromatic2.cpp SRC += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp SRC += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp diff --git a/tests/fluidsim.cpp b/tests/fluidsim.cpp index 5a4a538..d381dec 100644 --- a/tests/fluidsim.cpp +++ b/tests/fluidsim.cpp @@ -13,88 +13,158 @@ #include "../stb/stb_image.h" -int main() { +struct fluidSimPreviewConfig { + float lodDist = 4096.0f; + float lodDropoff = 0.001f; + bool slowRender = false; + int outWidth = 1024; + int outHeight = 1024; + int rayCount = 1; + int reflectCount = 1; + bool globalIllumination = false; + bool useLod = true; +}; + +class FluidSimUI { +private: fluidSim sim; - - // Simulation settings - const int TOTAL_FRAMES = 10000; - const int WIDTH = 1024; - const int HEIGHT = 1024; - - // Setup Camera - Camera cam; - cam.origin = Eigen::Vector3f(0.0f, 4000.0f, -5800.0f); - cam.direction = (Eigen::Vector3f(0.0f, 0.0f, 0.0f) - cam.origin).normalized(); - cam.up = Eigen::Vector3f(0.0f, 1.0f, 0.0f); - cam.fov = 60.0f; - - // Base particle template fluidParticle baseParticle; - baseParticle.velocity = Eigen::Vector3f::Zero(); - baseParticle.acceleration = Eigen::Vector3f::Zero(); - baseParticle.forceAccumulator = Eigen::Vector3f::Zero(); - baseParticle.pressure = 0.0f; - baseParticle.viscosity = 8.0f; - baseParticle.restitution = 200.0f; + fluidSimPreviewConfig viewConfig; + Camera cam; + + // UI State + bool isRunning = false; + int particlesToSpawnPerFrame = 2; + int totalParticleCap = 5000; + + // Texture Management + GLuint textu = 0; + std::mutex PreviewMutex; + bool updatePreview = false; + bool textureInitialized = false; + frame currentPreviewFrame; - std::cout << "Starting Fluid Simulation..." << std::endl; - sim.grid.setLODFalloff(0.001); - sim.grid.setLODMinDistance(4096); +public: + FluidSimUI() { + cam.origin = Eigen::Vector3f(0.0f, 4000.0f, -5800.0f); + cam.direction = (Eigen::Vector3f(0.0f, 0.0f, 0.0f) - cam.origin).normalized(); + cam.up = Eigen::Vector3f(0.0f, 1.0f, 0.0f); + cam.fov = 60.0f; - for (int frameIdx = 0; frameIdx < TOTAL_FRAMES; frameIdx++) { + baseParticle.velocity = Eigen::Vector3f::Zero(); + baseParticle.acceleration = Eigen::Vector3f::Zero(); + baseParticle.forceAccumulator = Eigen::Vector3f::Zero(); + baseParticle.pressure = 0.0f; + baseParticle.viscosity = 8.0f; + baseParticle.restitution = 200.0f; - if (frameIdx < (TOTAL_FRAMES * 0.1f)) { - if (frameIdx % 1 == 0) { - //float t = static_cast(frameIdx) / (TOTAL_FRAMES * 0.9f); - int spawnCount = 2; // + static_cast(t * 4); - sim.spawnParticles(baseParticle, spawnCount); - } - } + sim.grid.setLODFalloff(viewConfig.lodDropoff); + sim.grid.setLODMinDistance(viewConfig.lodDist); + } - sim.applyPhysics(); - - if (frameIdx % 100 == 0) { - std::cout << "Rendering Frame " << frameIdx << " / " << TOTAL_FRAMES << std::endl; - - frame renderedFrame = sim.grid.renderFrame(cam, HEIGHT, WIDTH, frame::colormap::RGB, 4, 5, true, false); - - std::stringstream ss; - ss << "output/frame_" << std::setw(4) << std::setfill('0') << frameIdx << ".bmp"; - BMPWriter::saveBMP(ss.str(), renderedFrame); - } else if (frameIdx % 25 == 0) { - std::cout << "Rendering quick Frame " << frameIdx << " / " << TOTAL_FRAMES << std::endl; - - frame renderedFrame = sim.grid.renderFrame(cam, HEIGHT, WIDTH, frame::colormap::RGB, 1, 1, true, false); - - std::stringstream ss; - ss << "output/frame_" << std::setw(4) << std::setfill('0') << frameIdx << ".bmp"; - BMPWriter::saveBMP(ss.str(), renderedFrame); - } else if (frameIdx % 5 == 0) { - std::cout << "Rendering ultrafast Frame " << frameIdx << " / " << TOTAL_FRAMES << std::endl; - - frame renderedFrame = sim.grid.fastRenderFrame(cam, HEIGHT / 2, WIDTH / 2, frame::colormap::RGB); - renderedFrame.resize(HEIGHT, WIDTH, frame::interpolation::NEAREST); - - std::stringstream ss; - ss << "output/frame_" << std::setw(4) << std::setfill('0') << frameIdx << ".bmp"; - BMPWriter::saveBMP(ss.str(), renderedFrame); - } - - if (frameIdx % 500 == 0) { - sim.grid.printStats(); + ~FluidSimUI() { + if (textu != 0) { + glDeleteTextures(1, &textu); } } - sim.grid.printStats(); - frame renderedFrame = sim.grid.renderFrame(cam, HEIGHT, WIDTH, frame::colormap::RGB, 5, 7, true, false); - - std::stringstream ss; - ss << "output/frame_" << std::setw(4) << std::setfill('0') << TOTAL_FRAMES << ".bmp"; - BMPWriter::saveBMP(ss.str(), renderedFrame); + void update() { + if (isRunning) { + if (sim.getParticleCount() < (size_t)totalParticleCap) { + sim.spawnParticles(baseParticle, particlesToSpawnPerFrame); + } + sim.applyPhysics(); + } + } - std::cout << "Simulation Complete." << std::endl; - FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED); - return 0; -} + void renderUI() { + ImGui::Begin("Fluid Simulation Control"); + + if (ImGui::Button(isRunning ? "Pause" : "Start")) { + isRunning = !isRunning; + } + ImGui::SameLine(); + if (ImGui::Button("Step")) { + sim.applyPhysics(); + } + ImGui::SameLine(); + if (ImGui::Button("Reset")) { + std::lock_guard lock(PreviewMutex); + sim.reset(); + } + + ImGui::Separator(); + ImGui::Text("Particle Management"); + ImGui::DragInt("Spawn Rate (per frame)", &particlesToSpawnPerFrame, 1, 0, 100); + ImGui::DragInt("Max Particles", &totalParticleCap, 10, 0, 50000); + ImGui::Text("Current Particles: %zu", sim.getParticleCount()); + + if (ImGui::CollapsingHeader("Physics Parameters")) { + ImGui::DragFloat("Smoothing Radius", &sim.config.SMOOTHING_RADIUS, 1.0f, 100.0f, 5000.0f); + ImGui::DragFloat("Rest Density", &sim.config.REST_DENSITY, 0.00001f, 0.0f, 0.01f, "%.6f"); + ImGui::DragFloat("Timestep", &sim.config.TIMESTEP, 0.001f, 0.001f, 0.1f); + ImGui::DragFloat("Gravity (Attraction)", &sim.config.G_ATTRACTION, 0.1f, 0.0f, 500.0f); + + ImGui::Text("Base Particle Properties"); + ImGui::DragFloat("Viscosity", &baseParticle.viscosity, 0.1f, 0.0f, 50.0f); + ImGui::DragFloat("Restitution", &baseParticle.restitution, 1.0f, 0.0f, 1000.0f); + } + + if (ImGui::CollapsingHeader("View Settings")) { + ImGui::DragFloat("Camera FOV", &cam.fov, 1.0f, 10.0f, 170.0f); + + float camPos[3] = {cam.origin.x(), cam.origin.y(), cam.origin.z()}; + if(ImGui::DragFloat3("Camera Pos", camPos, 10.0f)) { + cam.origin = Eigen::Vector3f(camPos[0], camPos[1], camPos[2]); + cam.direction = (Eigen::Vector3f(0.0f, 0.0f, 0.0f) - cam.origin).normalized(); + } + + ImGui::Checkbox("High Quality Render", &viewConfig.slowRender); + if (viewConfig.slowRender) { + ImGui::DragInt("Ray Count", &viewConfig.rayCount, 1, 1, 16); + ImGui::Checkbox("Global Illumination", &viewConfig.globalIllumination); + } + } + + updatePreviewTexture(); + + if (textureInitialized) { + float aspect = (float)currentPreviewFrame.getWidth() / (float)currentPreviewFrame.getHeight(); + float availWidth = ImGui::GetContentRegionAvail().x; + ImGui::Image((void*)(intptr_t)textu, ImVec2(availWidth, availWidth / aspect)); + } + + ImGui::End(); + } + +private: + void updatePreviewTexture() { + std::lock_guard lock(PreviewMutex); + updatePreview = true; + + sim.grid.setLODMinDistance(viewConfig.lodDist); + sim.grid.setLODFalloff(viewConfig.lodDropoff); + + if (viewConfig.slowRender) { + currentPreviewFrame = sim.grid.renderFrame(cam, viewConfig.outWidth, viewConfig.outHeight, frame::colormap::RGB, viewConfig.rayCount, viewConfig.reflectCount, viewConfig.globalIllumination, viewConfig.useLod); + } else { + currentPreviewFrame = sim.grid.fastRenderFrame(cam, viewConfig.outWidth, viewConfig.outHeight, frame::colormap::RGB); + } + + if (textu == 0) { + glGenTextures(1, &textu); + } + glBindTexture(GL_TEXTURE_2D, textu); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(), + 0, GL_RGB, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data()); + + updatePreview = false; + textureInitialized = true; + } +}; #endif \ No newline at end of file diff --git a/tests/g3etest.cpp b/tests/g3etest.cpp index d0642ac..1152004 100644 --- a/tests/g3etest.cpp +++ b/tests/g3etest.cpp @@ -16,6 +16,7 @@ #include "../util/noise/pnoise2.hpp" #include "../util/noise/pnoise.cpp" #include "../util/output/aviwriter.hpp" +#include "fluidsim.cpp" #include "../imgui/imgui.h" #include "../imgui/backends/imgui_impl_glfw.h" @@ -337,6 +338,9 @@ int main() { updateNoiseTexture(noiseState); + FluidSimUI fluidUI; + bool showFluidSim = false; + sphereConf.centerX = ghalf; sphereConf.centerY = ghalf; sphereConf.centerZ = ghalf; @@ -387,6 +391,10 @@ int main() { static double lastFrameTime = currentTime; deltaTime = currentTime - lastFrameTime; lastFrameTime = currentTime; + + if (showFluidSim) { + fluidUI.update(); + } if (orbitEquator || orbitPoles || autoRotate) autoRotationTime += deltaTime; if (autoRotateView) autoRotationAngle += deltaTime; @@ -527,10 +535,18 @@ int main() { ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight)); } + + ImGui::Separator(); + ImGui::Text("Modules"); + ImGui::Checkbox("Fluid Simulation", &showFluidSim); ImGui::End(); } + if (showFluidSim) { + fluidUI.renderUI(); + } + { ImGui::Begin("Planet Preview"); if (worldPreview) { diff --git a/util/sim/fluidsim.hpp b/util/sim/fluidsim.hpp index 8d4a2c1..e0c11f5 100644 --- a/util/sim/fluidsim.hpp +++ b/util/sim/fluidsim.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "../util/grid/grid3eigen.hpp" #include "../util/output/frame.hpp" @@ -30,10 +31,10 @@ struct fluidParticle { struct gridConfig { float gridSizeCube = 8192; - const float SMOOTHING_RADIUS = 1024.0f; - const float REST_DENSITY = 0.00005f; - const float TIMESTEP = 0.016f; - const float G_ATTRACTION = 50.0f; + float SMOOTHING_RADIUS = 1024.0f; + float REST_DENSITY = 0.00005f; + float TIMESTEP = 0.016f; + float G_ATTRACTION = 50.0f; }; Eigen::Matrix posGen() { @@ -170,20 +171,27 @@ private: std::map gradientmap; public: gridConfig config; - float closeThresh = 0.01f * config.SMOOTHING_RADIUS; + float closeThresh; Octree grid; fluidSim() : grid({-config.gridSizeCube, -config.gridSizeCube, -config.gridSizeCube}, {config.gridSizeCube, config.gridSizeCube, config.gridSizeCube}) { + closeThresh = 0.01f * config.SMOOTHING_RADIUS; grid.setBackgroundColor({0.1f, 0.1f, 0.2f}); gradientmap.emplace(0.0, Eigen::Vector3f(1, 0, 0)); gradientmap.emplace(0.5, Eigen::Vector3f(0, 1, 0)); gradientmap.emplace(1.0, Eigen::Vector3f(0, 0, 1)); } + void reset() { + grid.clear(); + idposMap.clear(); + nextObjectId = 0; + newMass = 1000; + } + void spawnParticles(fluidParticle toSpawn, int count, bool resize = true) { TIME_FUNCTION; toSpawn.mass = newMass; - //float size = toSpawn.mass / 10; Eigen::Vector3f color = buildGradient(toSpawn.mass / 1000, gradientmap); for (int i = 0; i < count; i++) { Eigen::Matrix pos = posGen(); @@ -219,7 +227,6 @@ public: } void applyPressure() { - size_t pointcounter = 0; for (auto& point : idposMap) { auto node = grid.find(point.second); if (!node) { @@ -311,10 +318,11 @@ public: void replaceLost() { std::vector idsToRemove; + int gridHalfSize = config.gridSizeCube / 2; for (auto& point : idposMap) { - if (config.gridSizeCube / 2 > point.second[0] > config.gridSizeCube / 2 || - config.gridSizeCube / 2 > point.second[1] > config.gridSizeCube / 2 || - config.gridSizeCube / 2 > point.second[2] > config.gridSizeCube / 2) { + if (std::abs(point.second[0]) > gridHalfSize || + std::abs(point.second[1]) > gridHalfSize || + std::abs(point.second[2]) > gridHalfSize) { idsToRemove.push_back(point.first); } } @@ -331,7 +339,6 @@ public: } void applyPhysics() { - TIME_FUNCTION; computeDensities(); applyPressure(); applyViscosity(); @@ -345,13 +352,13 @@ public: Eigen::Matrix newPos = point.second + (node->data.velocity); Eigen::Vector3f oldPos = point.second; if (grid.move(oldPos, newPos)) { - auto newpoint = grid.find(newPos); idposMap[point.first] = newPos; } } replaceLost(); } + + size_t getParticleCount() const { return idposMap.size(); } }; - #endif \ No newline at end of file diff --git a/util/timing_decorator.cpp b/util/timing_decorator.cpp index b8ef2af..7659efa 100644 --- a/util/timing_decorator.cpp +++ b/util/timing_decorator.cpp @@ -1,3 +1,6 @@ +#ifndef TIMING_CPP +#define TIMING_CPP + #include "timing_decorator.hpp" #include @@ -112,4 +115,6 @@ void FunctionTimer::printStats(Mode mode) { void FunctionTimer::clearStats() { stats_.clear(); -} \ No newline at end of file +} + +#endif \ No newline at end of file