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

View File

@@ -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
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 <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);

View File

@@ -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
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