making a proper widget.

This commit is contained in:
yggdrasil75
2026-02-22 21:07:20 -05:00
parent cb0b8b7643
commit 2c993995e8
8 changed files with 852 additions and 11 deletions

212
tests/planet.cpp Normal file
View File

@@ -0,0 +1,212 @@
#ifndef PLANET_CPP
#define PLANET_CPP
#include "../util/sim/planet.hpp"
#include "../util/grid/camera.hpp"
#include "../util/noise/pnoise2.hpp"
#include "../util/noise/pnoise.cpp"
class planetSimUI {
private:
planetsim sim;
Camera cam;
bool isRunning = false;
// Texture Management
GLuint textu = 0;
std::mutex PreviewMutex;
bool updatePreview = false;
bool textureInitialized = false;
frame currentPreviewFrame;
int outWidth = 512;
int outHeight = 512;
float fps = 60;
int rayCount = 3;
int reflectCount = 4;
bool slowRender = false;
float lodDist = 4096.0f;
float lodDropoff = 0.001f;
bool globalIllumination = false;
bool useLod = true;
std::map<int, bool> keyStates;
float deltaTime = 0.16f;
bool orbitEquator = false;
float rotationRadius = 2500;
float angle = 0.0f;
const float ω = (std::pow(M_PI, 2) / 30) / 10;
public:
planetSimUI() {
cam.origin = v3(4000, 4000, 4000);
cam.direction = (v3(0,0,0) - cam.origin).normalized();
cam.up = v3(0,1,0);
cam.fov = 60;
cam.rotationSpeed = M_1_PI;
}
~planetSimUI() {
if (textu != 0) {
glDeleteTextures(1, &textu);
}
sim.grid.clear();
}
void renderUI(GLFWwindow* window) {
ImGui::Begin("Planet Simulation");
if (orbitEquator) {
angle += cam.rotationSpeed * deltaTime * ω;
cam.origin[0] = sim.config.center[0] + rotationRadius * cosf(angle);
cam.origin[1] = sim.config.center[1];
cam.origin[2] = sim.config.center[2] + rotationRadius * sinf(angle);
v3 target(sim.config.center);
cam.direction = (target - cam.origin).normalized();
}
glfwPollEvents();
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
keyStates[i] = (glfwGetKey(window, i) == GLFW_PRESS);
}
if (keyStates[GLFW_KEY_W]) cam.moveForward(deltaTime);
if (keyStates[GLFW_KEY_S]) cam.moveBackward(deltaTime);
if (keyStates[GLFW_KEY_A]) cam.moveLeft(deltaTime);
if (keyStates[GLFW_KEY_D]) cam.moveRight(deltaTime);
if (keyStates[GLFW_KEY_Z]) cam.moveUp(deltaTime);
if (keyStates[GLFW_KEY_X]) cam.moveDown(deltaTime);
if (keyStates[GLFW_KEY_Q]) cam.rotateYaw(deltaTime);
if (keyStates[GLFW_KEY_R]) cam.rotateYaw(-deltaTime);
if (ImGui::CollapsingHeader("Base Configuration", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::DragFloat("Radius", &sim.config.radius, 1.0f, 10.0f, 10000.0f);
ImGui::InputInt("Surface Points", &sim.config.surfacePoints);
ImGui::DragFloat("Voxel Size", &sim.config.voxelSize, 0.1f, 0.1f, 100.0f);
ImGui::ColorEdit3("Base Color", sim.config.color.data());
ImGui::Separator();
if (ImGui::Button("1. Generate Fib Sphere", ImVec2(-1, 40))) {
sim.generateFibSphere();
}
ImGui::Text("Current Step: %d", sim.config.currentStep);
ImGui::Text("Nodes: %zu", sim.config.surfaceNodes.size());
}
if (ImGui::Button("Apply Tectonics")) {
simulateTectonics();
}
if (ImGui::CollapsingHeader("Physics Parameters")) {
ImGui::DragFloat("Gravity (G)", &sim.config.G_ATTRACTION, 0.1f);
ImGui::DragFloat("Time Step", &sim.config.TIMESTEP, 0.001f, 0.0001f, 0.1f);
ImGui::DragFloat("Viscosity", &sim.config.dampingFactor, 0.001f, 0.0f, 1.0f);
ImGui::DragFloat("Pressure Stiffness", &sim.config.pressureStiffness, 10.0f);
ImGui::DragInt("Num Plates", &sim.config.numPlates, 1, 1, 50);
}
if (ImGui::CollapsingHeader("Tectonic Simulation")) {
ImGui::DragInt("Num Plates", &sim.config.numPlates, 1, 1, 100);
ImGui::DragFloat("Plate Randomness", &sim.config.plateRandom, 0.01f, 0.0f, 2.0f);
ImGui::DragInt("Smoothing Passes", &sim.config.smoothingPasses, 1, 0, 10);
ImGui::DragFloat("Mountain Height", &sim.config.mountHeight, 1.0f, 0.0f, 1000.0f);
ImGui::DragFloat("Valley Depth", &sim.config.valleyDepth, 1.0f, -1000.0f, 0.0f);
ImGui::DragFloat("Transform Roughness", &sim.config.transformRough, 1.0f, 0.0f, 500.0f);
ImGui::DragInt("Stress Passes", &sim.config.stressPasses, 1, 0, 20);
if (ImGui::Button("2. Simulate Tectonics", ImVec2(-1, 40))) {
simulateTectonics();
}
}
if (ImGui::CollapsingHeader("Camera Controls")) {
ImGui::DragFloat3("Origin", cam.origin.data());
ImGui::DragFloat3("Direction", cam.direction.data(), 0.0001f, -1.0f, 1.0f);
ImGui::DragFloat("Movement Speed", &cam.movementSpeed, 0.1f, 1.0f, 500.0f);
ImGui::DragFloat("Rotation Speed", &cam.rotationSpeed, M_1_PI, M_1_PI, M_PI);
ImGui::InputFloat("Rotation Distance", &rotationRadius, 10, 100);
ImGui::Checkbox("Use Slower Render", &slowRender);
if (ImGui::Button("Focus on Planet")) {
v3 target(sim.config.center);
v3 newDir = (target - cam.origin).normalized();
cam.direction = newDir;
}
if (ImGui::Button(orbitEquator ? "Stop Equator" : "Orbit Equator")) orbitEquator = !orbitEquator;
}
livePreview();
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();
}
void applyNoise(const NoisePreviewState& noiseState) {
TIME_FUNCTION;
auto triplanarNoise = [&](const Eigen::Vector3f& pos) -> float {
PNoise2 gen(noiseState.masterSeed);
Eigen::Vector3f n = pos.normalized();
Eigen::Vector3f blend = n.cwiseAbs();
float sum = blend.x() + blend.y() + blend.z();
blend /= sum;
Eigen::Vector3f offsetPos = pos + Eigen::Vector3f(noiseState.offset[0], noiseState.offset[1], 0.0f);
float vXY = sim.evaluate2DStack(Eigen::Vector2f(offsetPos.x(), offsetPos.y()), noiseState, gen);
float vXZ = sim.evaluate2DStack(Eigen::Vector2f(offsetPos.x(), offsetPos.z()), noiseState, gen);
float vYZ = sim.evaluate2DStack(Eigen::Vector2f(offsetPos.y(), offsetPos.z()), noiseState, gen);
// Blend results
return vYZ * blend.x() + vXZ * blend.y() + vXY * blend.z();
};
sim._applyNoise(triplanarNoise);
}
void livePreview() {
std::lock_guard<std::mutex> lock(PreviewMutex);
updatePreview = true;
sim.grid.setLODMinDistance(lodDist);
sim.grid.setLODFalloff(lodDropoff);
if (slowRender) {
currentPreviewFrame = sim.grid.renderFrame(cam, outWidth, outHeight, frame::colormap::RGB, rayCount, reflectCount, globalIllumination, useLod);
} else {
currentPreviewFrame = sim.grid.fastRenderFrame(cam, outWidth, 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;
}
void resetView() {
cam.origin = Vector3f(sim.config.gridSizeCube, sim.config.gridSizeCube, sim.config.gridSizeCube);
Vector3f center(sim.config.gridSizeCube / 2.0f, sim.config.gridSizeCube / 2.0f, sim.config.gridSizeCube / 2.0f);
cam.lookAt(center);
}
void simulateTectonics() {
sim.assignSeeds();
// sim.growPlatesCellular();
sim.growPlatesRandom();
//sim.fixBoundaries();
sim.extraplateste();
sim.finalizeApplyResults();
}
};
#endif

138
tests/ptest.cpp Normal file
View File

@@ -0,0 +1,138 @@
#include <iostream>
#include <vector>
#include <string>
// Include GLAD/GLFW
// Ensure these are in your include path
#include <GLFW/glfw3.h>
// ImGui
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
// Include your implementation files
// (Unity build style as requested)
#include "../util/noise/pnoise.cpp"
#include "planet.cpp"
#include "../util/basicdefines.hpp"
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}
static void glfw_error_callback(int error, const char* description)
{
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
}
int main() {
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
return -1;
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100 (WebGL 1.0)
const char* glsl_version = "#version 100";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#elif defined(IMGUI_IMPL_OPENGL_ES3)
// GL ES 3.0 + GLSL 300 es (WebGL 2.0)
const char* glsl_version = "#version 300 es";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#elif defined(__APPLE__)
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
#endif
bool application_not_closed = true;
GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "StupidSim", nullptr, nullptr);
if (window == nullptr) {
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
ImGui_ImplGlfw_InitForOpenGL(window, true);
#ifdef __EMSCRIPTEN__
ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
#endif
ImGui_ImplOpenGL3_Init(glsl_version);
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
planetSimUI planetApp;
NoisePreviewState noiseState;
if (noiseState.layers.empty()) {
NoiseLayer defaultLayer;
strcpy(defaultLayer.name, "Base Terrain");
defaultLayer.type = NoiseType::Fractal;
noiseState.layers.push_back(defaultLayer);
}
updateNoiseTexture(noiseState);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::GetMainViewport();
drawNoiseLab(noiseState);
planetApp.renderUI(window);
ImGui::Begin("Integration Control");
ImGui::Text("Bridge: Noise Lab -> Planet Sim");
ImGui::Separator();
ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f), "Current Noise Layers: %zu", noiseState.layers.size());
if (ImGui::Button("APPLY CURRENT NOISE TO PLANET", ImVec2(-1, 50))) {
planetApp.applyNoise(noiseState);
}
ImGui::End();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
}
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
return 0;
}

View File

@@ -957,9 +957,9 @@ public:
} }
bool set(const T& data, const PointType& pos, bool visible, Eigen::Vector3f color, float size, bool active, bool set(const T& data, const PointType& pos, bool visible, Eigen::Vector3f color, float size, bool active,
int objectId = -1, bool light = false, float emittance = 0.0f, float refraction = 0.0f, int objectId = -1, int subId = 0, bool light = false, float emittance = 0.0f, float refraction = 0.0f,
float reflection = 0.0f) { float reflection = 0.0f) {
auto pointData = std::make_shared<NodeData>(data, pos, visible, color, size, active, objectId, auto pointData = std::make_shared<NodeData>(data, pos, visible, color, size, active, objectId, subId,
light, emittance, refraction, reflection); light, emittance, refraction, reflection);
if (insertRecursive(root_.get(), pointData, 0)) { if (insertRecursive(root_.get(), pointData, 0)) {
this->size++; this->size++;
@@ -1073,14 +1073,10 @@ public:
if (!remove(oldPos, tolerance)) return false; if (!remove(oldPos, tolerance)) return false;
bool res = set(newData, newPos, bool res = set(newData, newPos, newVisible,
newVisible,
newColor != Eigen::Vector3f(1.0f, 1.0f, 1.0f) ? newColor : pointData->color, newColor != Eigen::Vector3f(1.0f, 1.0f, 1.0f) ? newColor : pointData->color,
newSize > 0 ? newSize : pointData->size, newSize > 0 ? newSize : pointData->size,
newActive, newActive, targetObjId, finalSubId, newLight,
targetObjId,
finalSubId,
newLight,
newEmittance > 0 ? newEmittance : pointData->emittance, newEmittance > 0 ? newEmittance : pointData->emittance,
newRefraction >= 0 ? newRefraction : pointData->refraction, newRefraction >= 0 ? newRefraction : pointData->refraction,
newReflection >= 0 ? newReflection : pointData->reflection); newReflection >= 0 ? newReflection : pointData->reflection);

93
util/jsonhelper.hpp Normal file
View File

@@ -0,0 +1,93 @@
#ifndef JSONHELPER_HPP
#define JSONHELPER_HPP
#include <string>
#include <vector>
#include <cctype>
namespace JsonHelper {
// Helper to get string value between quotes
inline std::string parseString(const std::string& json, const std::string& key) {
size_t pos = json.find("\"" + key + "\"");
if (pos == std::string::npos) return "";
pos = json.find(":", pos);
if (pos == std::string::npos) return "";
size_t start = json.find("\"", pos);
if (start == std::string::npos) return "";
size_t end = json.find("\"", start + 1);
if (end == std::string::npos) return "";
return json.substr(start + 1, end - start - 1);
}
// Helper to get raw non-string value (int, float, bool)
inline std::string parseRaw(const std::string& json, const std::string& key) {
size_t pos = json.find("\"" + key + "\"");
if (pos == std::string::npos) return "";
pos = json.find(":", pos);
if (pos == std::string::npos) return "";
pos++; // skip ':'
while (pos < json.length() && std::isspace(json[pos])) pos++;
size_t end = pos;
while (end < json.length() && json[end] != ',' && json[end] != '}' && json[end] != ']' && !std::isspace(json[end])) end++;
return json.substr(pos, end - pos);
}
inline int parseInt(const std::string& json, const std::string& key, int defaultVal = 0) {
std::string raw = parseRaw(json, key);
if (raw.empty()) return defaultVal;
try { return std::stoi(raw); } catch(...) { return defaultVal; }
}
inline float parseFloat(const std::string& json, const std::string& key, float defaultVal = 0.0f) {
std::string raw = parseRaw(json, key);
if (raw.empty()) return defaultVal;
try { return std::stof(raw); } catch(...) { return defaultVal; }
}
inline bool parseBool(const std::string& json, const std::string& key, bool defaultVal = false) {
std::string raw = parseRaw(json, key);
if (raw.empty()) return defaultVal;
return raw == "true" || raw == "1";
}
// Helper to extract JSON objects out of a JSON array
inline std::vector<std::string> parseArray(const std::string& json, const std::string& key) {
std::vector<std::string> items;
size_t pos = json.find("\"" + key + "\"");
if (pos == std::string::npos) return items;
pos = json.find(":", pos);
if (pos == std::string::npos) return items;
pos = json.find("[", pos);
if (pos == std::string::npos) return items;
int depth = 0;
size_t start = 0;
bool inString = false;
for (size_t i = pos + 1; i < json.length(); ++i) {
if (json[i] == '"' && (i == 0 || json[i-1] != '\\')) {
inString = !inString;
}
if (!inString) {
if (json[i] == '{') {
if (depth == 0) start = i;
depth++;
} else if (json[i] == '}') {
depth--;
if (depth == 0) {
items.push_back(json.substr(start, i - start + 1));
}
} else if (json[i] == ']') {
if (depth == 0) break;
}
}
}
return items;
}
}
#endif

View File

@@ -6,8 +6,11 @@
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <fstream>
#include <sstream>
#include "./pnoise2.hpp" #include "./pnoise2.hpp"
#include "../jsonhelper.hpp"
#include "../timing_decorator.hpp" #include "../timing_decorator.hpp"
#include "../../imgui/imgui.h" #include "../../imgui/imgui.h"
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
@@ -194,11 +197,94 @@ inline void updateNoiseTexture(NoisePreviewState& state) {
state.needsUpdate = false; state.needsUpdate = false;
} }
inline void saveNoiseState(const NoisePreviewState& state, const std::string& filename) {
std::ofstream out(filename);
if (!out) return;
out << "{\n";
out << " \"masterSeed\": " << state.masterSeed << ",\n";
out << " \"offsetX\": " << state.offset[0] << ",\n";
out << " \"offsetY\": " << state.offset[1] << ",\n";
out << " \"layers\": [\n";
for (size_t i = 0; i < state.layers.size(); ++i) {
const auto& l = state.layers[i];
out << " {\n";
out << " \"enabled\": " << (l.enabled ? "true" : "false") << ",\n";
out << " \"name\": \"" << l.name << "\",\n";
out << " \"type\": " << (int)l.type << ",\n";
out << " \"blend\": " << (int)l.blend << ",\n";
out << " \"seedOffset\": " << l.seedOffset << ",\n";
out << " \"scale\": " << l.scale << ",\n";
out << " \"strength\": " << l.strength << ",\n";
out << " \"octaves\": " << l.octaves << ",\n";
out << " \"persistence\": " << l.persistence << ",\n";
out << " \"lacunarity\": " << l.lacunarity << ",\n";
out << " \"ridgeOffset\": " << l.ridgeOffset << "\n";
out << " }" << (i < state.layers.size() - 1 ? "," : "") << "\n";
}
out << " ]\n";
out << "}\n";
}
inline void loadNoiseState(NoisePreviewState& state, const std::string& filename) {
std::ifstream in(filename);
if (!in) return;
std::stringstream buffer;
buffer << in.rdbuf();
std::string json = buffer.str();
state.masterSeed = JsonHelper::parseInt(json, "masterSeed", 1337);
state.offset[0] = JsonHelper::parseFloat(json, "offsetX", 0.0f);
state.offset[1] = JsonHelper::parseFloat(json, "offsetY", 0.0f);
auto layerStrs = JsonHelper::parseArray(json, "layers");
state.layers.clear();
for (const auto& lStr : layerStrs) {
NoiseLayer l;
l.enabled = JsonHelper::parseBool(lStr, "enabled", true);
std::string name = JsonHelper::parseString(lStr, "name");
if (!name.empty()) {
std::strncpy(l.name, name.c_str(), 31);
l.name[31] = '\0';
}
l.type = (NoiseType)JsonHelper::parseInt(lStr, "type", 0);
l.blend = (BlendMode)JsonHelper::parseInt(lStr, "blend", 0);
l.seedOffset = JsonHelper::parseInt(lStr, "seedOffset", 0);
l.scale = JsonHelper::parseFloat(lStr, "scale", 0.02f);
l.strength = JsonHelper::parseFloat(lStr, "strength", 1.0f);
l.octaves = JsonHelper::parseInt(lStr, "octaves", 4);
l.persistence = JsonHelper::parseFloat(lStr, "persistence", 0.5f);
l.lacunarity = JsonHelper::parseFloat(lStr, "lacunarity", 2.0f);
l.ridgeOffset = JsonHelper::parseFloat(lStr, "ridgeOffset", 1.0f);
state.layers.push_back(l);
}
state.needsUpdate = true;
}
inline void drawNoiseLab(NoisePreviewState& noiseState) { inline void drawNoiseLab(NoisePreviewState& noiseState) {
ImGui::Begin("2D Noise Lab"); ImGui::Begin("2D Noise Lab");
// Master Controls
bool changed = false; bool changed = false;
static char filenameBuffer[128] = "output/noise_preset.json";
ImGui::InputText("File", filenameBuffer, sizeof(filenameBuffer));
ImGui::SameLine();
if (ImGui::Button("Save JSON")) {
saveNoiseState(noiseState, filenameBuffer);
}
ImGui::SameLine();
if (ImGui::Button("Load JSON")) {
loadNoiseState(noiseState, filenameBuffer);
changed = true;
}
ImGui::Separator();
changed |= ImGui::InputInt("Master Seed", &noiseState.masterSeed); changed |= ImGui::InputInt("Master Seed", &noiseState.masterSeed);
changed |= ImGui::DragFloat2("Pan Offset", noiseState.offset, 1.0f); changed |= ImGui::DragFloat2("Pan Offset", noiseState.offset, 1.0f);
@@ -254,7 +340,7 @@ inline void drawNoiseLab(NoisePreviewState& noiseState) {
} }
if (open) { if (open) {
ImGui::Checkbox("##enabled", &layer.enabled); if (ImGui::Checkbox("##enabled", &layer.enabled)) changed = true;
ImGui::SameLine(); ImGui::SameLine();
ImGui::InputText("##name", layer.name, 32); ImGui::InputText("##name", layer.name, 32);

View File

@@ -9,6 +9,7 @@
#include <limits> #include <limits>
#include "../../eigen/Eigen/Core" #include "../../eigen/Eigen/Core"
#include "../timing_decorator.hpp" #include "../timing_decorator.hpp"
#include "../basicdefines.hpp"
class PNoise2 { class PNoise2 {
private: private:

315
util/sim/planet.hpp Normal file
View File

@@ -0,0 +1,315 @@
#ifndef PLANET_HPP
#define PLANET_HPP
#include <map>
#include <iostream>
#include <vector>
#include <chrono>
#include <thread>
#include <atomic>
#include <mutex>
#include <cmath>
#include <random>
#include <algorithm>
#include <queue>
#include <unordered_map>
#include "../grid/grid3eigen.hpp"
#include "../timing_decorator.cpp"
#include "../imgui/imgui.h"
#include "../imgui/backends/imgui_impl_glfw.h"
#include "../imgui/backends/imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include "../stb/stb_image.h"
using v3 = Eigen::Vector3f;
const float Φ = M_PI * (3.0f - std::sqrt(5.0f));
enum class PlateType {
CONTINENTAL,
OCEANIC,
MIXED
};
struct Particle {
float noiseDisplacement = 0.0f;
int plateID = -1;
Eigen::Vector3f basePos;
Eigen::Vector3f currentPos;
float plateDisplacement = 0.0f;
float temperature = -1;
float water = -1;
Eigen::Vector3f originColor;
bool surface = false;
//gravity factors:
Eigen::Matrix<float, 3, 1> velocity;
Eigen::Matrix<float, 3, 1> acceleration;
Eigen::Matrix<float, 3, 1> forceAccumulator;
float density = 0.0f;
float pressure = 0.0f;
Eigen::Matrix<float, 3, 1> pressureForce;
float viscosity = 0.5f;
Eigen::Matrix<float, 3, 1> viscosityForce;
float restitution = 5.0f;
float mass;
bool isStatic = false;
float soundSpeed = 100.0f;
std::unordered_map<int, float> neighbors;
};
struct planetConfig {
Eigen::Vector3f center = Eigen::Vector3f(0,0,0);
float radius = 1024.0f;
Eigen::Vector3f color = Eigen::Vector3f(0, 1, 0);
float voxelSize = 10.0f;
int surfacePoints = 50000;
int currentStep = 0;
float displacementStrength = 200.0f;
std::vector<Particle> surfaceNodes;
float noiseStrength = 1.0f;
int numPlates = 15;
float plateRandom = 0.6f;
int smoothingPasses = 3;
float mountHeight = 250.0f;
float valleyDepth = -150.0f;
float transformRough = 80.0f;
int stressPasses = 5;
float maxElevationRatio = 0.25f;
float gridSizeCube = 16384;
float SMOOTHING_RADIUS = 1024.0f;
float REST_DENSITY = 0.00005f;
float TIMESTEP = 0.016f;
float G_ATTRACTION = 50.0f;
float gravitySoftening = 10.0f;
float pressureStiffness = 50000.0f;
float coreRepulsionRadius = 1000.0f;
float coreRepulsionStiffness = 100000.0f;
float dampingFactor = 0.98f;
};
struct PlateConfig {
int plateId;
Eigen::Vector3f plateEulerPole;
Eigen::Vector3f direction;
float angularVelocity;
float thickness;
float density;
float rigidity;
float temperature;
Eigen::Vector3f debugColor;
PlateType ptype;
};
class planetsim {
public:
planetConfig config;
Octree<Particle> grid;
std::vector<PlateConfig> plates;
std::mt19937 rng = std::mt19937(42);
planetsim() {
config = planetConfig();
grid = Octree<Particle>({-config.gridSizeCube,-config.gridSizeCube,-config.gridSizeCube,},{config.gridSizeCube,config.gridSizeCube,config.gridSizeCube});
}
float evaluate2DStack(const Eigen::Vector2f& point, const NoisePreviewState& state, PNoise2& gen) {
float finalValue = 0.0f;
Eigen::Vector2f p = point;
for (const auto& layer : state.layers) {
if (!layer.enabled) continue;
Eigen::Vector2f samplePoint = p * layer.scale;
samplePoint += Eigen::Vector2f((float)layer.seedOffset * 10.5f, (float)layer.seedOffset * -10.5f);
if (layer.blend == BlendMode::DomainWarp) {
if (layer.type == NoiseType::CurlNoise) {
Eigen::Vector2f flow = gen.curlNoise(samplePoint);
p += flow * layer.strength * 100.0f;
} else {
float warpX = sampleNoiseLayer(gen, layer.type, samplePoint, layer);
float warpY = sampleNoiseLayer(gen, layer.type, samplePoint + Eigen::Vector2f(5.2f, 1.3f), layer);
p += Eigen::Vector2f(warpX, warpY) * layer.strength * 100.0f;
}
continue;
}
float nVal = sampleNoiseLayer(gen, layer.type, samplePoint, layer);
switch (layer.blend) {
case BlendMode::Replace: finalValue = nVal * layer.strength; break;
case BlendMode::Add: finalValue += nVal * layer.strength; break;
case BlendMode::Subtract: finalValue -= nVal * layer.strength; break;
case BlendMode::Multiply: finalValue *= (nVal * layer.strength); break;
case BlendMode::Max: finalValue = std::max(finalValue, nVal * layer.strength); break;
case BlendMode::Min: finalValue = std::min(finalValue, nVal * layer.strength); break;
}
}
float norm = std::tanh(finalValue);
return norm;
}
void generateFibSphere() {
TIME_FUNCTION;
grid.clear();
config.surfaceNodes.clear();
for (int i = 0; i < config.surfacePoints; i++) {
float y = 1.0f - (i * 2.0f) / (config.surfacePoints - 1);
float radiusY = std::sqrt(1.0f- y * y);
float Θ = Φ * i;
float x = std::cos(Θ) * radiusY;
float z = std::sin(Θ) * radiusY;
v3 dir(x, y, z);
v3 pos = config.center + dir * config.radius;
Particle pt;
pt.basePos = pos;
pt.currentPos = pos;
pt.originColor = config.color;
pt.noiseDisplacement = 0.0f;
pt.surface = true;
config.surfaceNodes.emplace_back(pt);
grid.set(pt, pt.currentPos, true, pt.originColor, config.voxelSize, true, 1, 0, false, 0.0f, 0.0f, 0.0f);
}
config.currentStep = 1;
std::cout << "Step 1 done. base sphere generated" << std::endl;
buildAdjacencyList();
grid.save("output/fibSphere");
}
inline void _applyNoise(std::function<float(const Eigen::Vector3f&)> noiseFunc) {
for (auto& p : config.surfaceNodes) {
Eigen::Vector3f oldPos = p.currentPos;
float displacementValue = noiseFunc(p.basePos);
p.noiseDisplacement = displacementValue;
Eigen::Vector3f normal = p.basePos.normalized();
p.currentPos = p.basePos + (normal * displacementValue * config.displacementStrength);
grid.update(oldPos, p.currentPos, p, true, p.originColor, config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
}
}
void assignSeeds() {
plates.clear();
plates.resize(config.numPlates);
float sphereSurfaceArea = 4.0f * M_PI * config.radius * config.radius;
float averageAreaPerPlate = sphereSurfaceArea / config.numPlates;
float minDistance = std::sqrt(averageAreaPerPlate) * 0.4f;
std::vector<int> selectedSeedIndices;
std::uniform_int_distribution<int> distNode(0, config.surfaceNodes.size() - 1);
for (int i = 0; i < config.numPlates; ++i) {
int attempts = 1000;
bool foundValidSeed = false;
int seedid = distNode(rng);
plates[i].plateId = i;
while (!foundValidSeed && attempts > 0) {
int seedIndex = distNode(rng);
bool tooClose = false;
for (int selectedIndex : selectedSeedIndices) {
const auto& existingSeed = config.surfaceNodes[selectedIndex];
const auto& candidateSeed = config.surfaceNodes[seedIndex];
float dot = existingSeed.basePos.normalized().dot(candidateSeed.basePos.normalized());
float angle = std::acos(std::clamp(dot, -1.0f, 1.0f));
float distanceOnSphere = angle * config.radius;
if (distanceOnSphere < minDistance) {
tooClose = true;
break;
}
}
if (!tooClose || selectedSeedIndices.empty()) {
selectedSeedIndices.push_back(seedIndex);
plates[i].plateId = i;
config.surfaceNodes[seedIndex].plateID = i;
float colorVal = static_cast<float>(seedid) / config.surfaceNodes.size();
plates[i].debugColor = v3(colorVal,colorVal,colorVal);
foundValidSeed = true;
}
attempts--;
}
if (!foundValidSeed) {
int seedIndex = distNode(rng);
selectedSeedIndices.push_back(seedIndex);
plates[i].plateId = i;
float colorVal = static_cast<float>(seedIndex) / config.surfaceNodes.size();
plates[i].debugColor = v3(colorVal,colorVal,colorVal);
config.surfaceNodes[seedIndex].plateID = i;
}
}
}
void buildAdjacencyList() {
TIME_FUNCTION;
for (int i = 0; i < config.surfaceNodes.size(); i++) {
Particle in = config.surfaceNodes[i];
v3 inn = in.basePos.normalized();
for (int j = 1; j < config.surfaceNodes.size(); j++) {
if (i == j) {
continue;
}
auto ij = config.surfaceNodes[j];
if (ij.neighbors.contains(i)){
in.neighbors[j] = ij.neighbors[i];
}
v3 ijn = ij.basePos.normalized();
float cosangle = inn.dot(ijn);
float angle = std::acos(cosangle);
in.neighbors[j] = angle;
}
}
}
void growPlatesRandom() {
TIME_FUNCTION;
}
void growPlatesCellular() {
TIME_FUNCTION;
}
void fixBoundaries() {
TIME_FUNCTION;
}
void extraplateste() {
}
void boundaryStress() {
TIME_FUNCTION;
}
void finalizeApplyResults() {
TIME_FUNCTION;
float maxAllowedDisp = config.radius * config.maxElevationRatio;
for (auto& p : config.surfaceNodes) {
Eigen::Vector3f oldPos = p.currentPos;
grid.update(oldPos, p.currentPos, p, true, plates[p.plateID].debugColor, config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
}
std::cout << "Finalize apply results completed." << std::endl;
}
};
#endif