making a proper widget.
This commit is contained in:
212
tests/planet.cpp
Normal file
212
tests/planet.cpp
Normal 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
138
tests/ptest.cpp
Normal 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;
|
||||
}
|
||||
@@ -957,9 +957,9 @@ public:
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
if (insertRecursive(root_.get(), pointData, 0)) {
|
||||
this->size++;
|
||||
@@ -1073,14 +1073,10 @@ public:
|
||||
|
||||
if (!remove(oldPos, tolerance)) return false;
|
||||
|
||||
bool res = set(newData, newPos,
|
||||
newVisible,
|
||||
bool res = set(newData, newPos, newVisible,
|
||||
newColor != Eigen::Vector3f(1.0f, 1.0f, 1.0f) ? newColor : pointData->color,
|
||||
newSize > 0 ? newSize : pointData->size,
|
||||
newActive,
|
||||
targetObjId,
|
||||
finalSubId,
|
||||
newLight,
|
||||
newActive, targetObjId, finalSubId, newLight,
|
||||
newEmittance > 0 ? newEmittance : pointData->emittance,
|
||||
newRefraction >= 0 ? newRefraction : pointData->refraction,
|
||||
newReflection >= 0 ? newReflection : pointData->reflection);
|
||||
|
||||
93
util/jsonhelper.hpp
Normal file
93
util/jsonhelper.hpp
Normal 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
|
||||
@@ -6,8 +6,11 @@
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "./pnoise2.hpp"
|
||||
#include "../jsonhelper.hpp"
|
||||
#include "../timing_decorator.hpp"
|
||||
#include "../../imgui/imgui.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
@@ -194,11 +197,94 @@ inline void updateNoiseTexture(NoisePreviewState& state) {
|
||||
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) {
|
||||
ImGui::Begin("2D Noise Lab");
|
||||
|
||||
// Master Controls
|
||||
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::DragFloat2("Pan Offset", noiseState.offset, 1.0f);
|
||||
|
||||
@@ -254,7 +340,7 @@ inline void drawNoiseLab(NoisePreviewState& noiseState) {
|
||||
}
|
||||
|
||||
if (open) {
|
||||
ImGui::Checkbox("##enabled", &layer.enabled);
|
||||
if (ImGui::Checkbox("##enabled", &layer.enabled)) changed = true;
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##name", layer.name, 32);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <limits>
|
||||
#include "../../eigen/Eigen/Core"
|
||||
#include "../timing_decorator.hpp"
|
||||
#include "../basicdefines.hpp"
|
||||
|
||||
class PNoise2 {
|
||||
private:
|
||||
|
||||
315
util/sim/planet.hpp
Normal file
315
util/sim/planet.hpp
Normal 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
|
||||
Reference in New Issue
Block a user