bunch of planet sim changes
This commit is contained in:
349
tests/planet.cpp
349
tests/planet.cpp
@@ -34,6 +34,29 @@ private:
|
||||
float rotationRadius = 2500;
|
||||
float angle = 0.0f;
|
||||
const float ω = (std::pow(M_PI, 2) / 30) / 10;
|
||||
bool tectonicGenned = false;
|
||||
bool doFixPlates = true;
|
||||
bool platesUseCellular = false;
|
||||
|
||||
enum class DebugColorMode {
|
||||
BASE,
|
||||
PLATES,
|
||||
NOISE,
|
||||
RESERVED
|
||||
};
|
||||
DebugColorMode currentColorMode = DebugColorMode::BASE;
|
||||
|
||||
enum class DebugMapMode {
|
||||
NONE,
|
||||
BASE,
|
||||
NOISE,
|
||||
TECTONIC,
|
||||
TECTONICCOLOR,
|
||||
CURRENT
|
||||
};
|
||||
DebugMapMode currentMapMode = DebugMapMode::NONE;
|
||||
GLuint mapTexture = 0;
|
||||
frame mapFrame;
|
||||
|
||||
public:
|
||||
planetSimUI() {
|
||||
@@ -48,11 +71,28 @@ public:
|
||||
if (textu != 0) {
|
||||
glDeleteTextures(1, &textu);
|
||||
}
|
||||
if (mapTexture != 0) {
|
||||
glDeleteTextures(1, &mapTexture);
|
||||
}
|
||||
sim.grid.clear();
|
||||
}
|
||||
|
||||
void renderUI(GLFWwindow* window) {
|
||||
handleCameraControls(window);
|
||||
ImGui::Begin("Planet Simulation");
|
||||
if (ImGui::BeginTable("MainLayout", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter)) {
|
||||
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||
ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
ImGui::TableNextColumn();
|
||||
renderControlsPanel();
|
||||
ImGui::TableNextColumn();
|
||||
renderPreviewPanel();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void handleCameraControls(GLFWwindow* window) {
|
||||
if (orbitEquator) {
|
||||
angle += cam.rotationSpeed * deltaTime * ω;
|
||||
|
||||
@@ -75,6 +115,10 @@ public:
|
||||
if (keyStates[GLFW_KEY_X]) cam.moveDown(deltaTime);
|
||||
if (keyStates[GLFW_KEY_Q]) cam.rotateYaw(deltaTime);
|
||||
if (keyStates[GLFW_KEY_R]) cam.rotateYaw(-deltaTime);
|
||||
}
|
||||
|
||||
void renderControlsPanel() {
|
||||
ImGui::BeginChild("ControlsScroll", ImVec2(0, 0), true);
|
||||
|
||||
if (ImGui::CollapsingHeader("Base Configuration", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::DragFloat("Radius", &sim.config.radius, 1.0f, 10.0f, 10000.0f);
|
||||
@@ -86,9 +130,11 @@ public:
|
||||
|
||||
if (ImGui::Button("1. Generate Fib Sphere", ImVec2(-1, 40))) {
|
||||
sim.generateFibSphere();
|
||||
applyDebugColorMode();
|
||||
}
|
||||
ImGui::Text("Current Step: %d", sim.config.currentStep);
|
||||
ImGui::Text("Nodes: %zu", sim.config.surfaceNodes.size());
|
||||
ImGui::InputFloat("Noise strength", &sim.config.noiseStrength, 0.01, 1, "%.4f");
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Physics Parameters")) {
|
||||
@@ -100,19 +146,115 @@ public:
|
||||
|
||||
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);
|
||||
ImGui::DragFloat("Max Elevation Ratio", &sim.config.maxElevationRatio, 1.0f, 0.0f, 1.0f);
|
||||
ImGui::Checkbox("Fix Boundaries", &doFixPlates);
|
||||
ImGui::Checkbox("use Cellular", &platesUseCellular);
|
||||
|
||||
if (ImGui::Button("2. Simulate Tectonics", ImVec2(-1, 40))) {
|
||||
simulateTectonics();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Camera Controls")) {
|
||||
if (ImGui::CollapsingHeader("Celestial Bodies")) {
|
||||
///TODO: add controls for moon, star.
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Fillings")) {
|
||||
if (ImGui::Button("Interpolate surface", ImVec2(-1, 40))) {
|
||||
interpolateSurface();
|
||||
}
|
||||
if (ImGui::Button("Fill Planet", ImVec2(-1, 40))) {
|
||||
fillPlanet();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Debug Views")) {
|
||||
ImGui::Text("3D Planet Color Mode:");
|
||||
bool colorChanged = false;
|
||||
if (ImGui::RadioButton("Base Color", currentColorMode == DebugColorMode::BASE)) {
|
||||
currentColorMode = DebugColorMode::BASE;
|
||||
colorChanged = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Plates", currentColorMode == DebugColorMode::PLATES)) {
|
||||
currentColorMode = DebugColorMode::PLATES;
|
||||
colorChanged = true;
|
||||
}
|
||||
|
||||
if (ImGui::RadioButton("Noise", currentColorMode == DebugColorMode::NOISE)) {
|
||||
currentColorMode = DebugColorMode::NOISE;
|
||||
colorChanged = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Reserved", currentColorMode == DebugColorMode::RESERVED)) {
|
||||
currentColorMode = DebugColorMode::RESERVED;
|
||||
colorChanged = true;
|
||||
}
|
||||
|
||||
if (colorChanged) {
|
||||
applyDebugColorMode();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("2D Height Map Mode:");
|
||||
bool mapChanged = false;
|
||||
|
||||
if (ImGui::RadioButton("None", currentMapMode == DebugMapMode::NONE)) {
|
||||
currentMapMode = DebugMapMode::NONE;
|
||||
mapChanged = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Base Pos", currentMapMode == DebugMapMode::BASE)) {
|
||||
currentMapMode = DebugMapMode::BASE;
|
||||
mapChanged = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Noise Pos", currentMapMode == DebugMapMode::NOISE)) {
|
||||
currentMapMode = DebugMapMode::NOISE;
|
||||
mapChanged = true;
|
||||
}
|
||||
|
||||
if (!tectonicGenned) ImGui::BeginDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Tectonic Pos", currentMapMode == DebugMapMode::TECTONIC)) {
|
||||
currentMapMode = DebugMapMode::TECTONIC;
|
||||
mapChanged = true;
|
||||
}
|
||||
|
||||
if (ImGui::RadioButton("Tectonic Color", currentMapMode == DebugMapMode::TECTONICCOLOR)) {
|
||||
currentMapMode = DebugMapMode::TECTONICCOLOR;
|
||||
mapChanged = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (!tectonicGenned) ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::RadioButton("Current Pos", currentMapMode == DebugMapMode::CURRENT)) {
|
||||
currentMapMode = DebugMapMode::CURRENT;
|
||||
mapChanged = true;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Refresh Map", ImVec2(-1, 24))) {
|
||||
mapChanged = true;
|
||||
generateDebugMap(currentMapMode);
|
||||
}
|
||||
|
||||
if (mapChanged && currentMapMode != DebugMapMode::NONE) {
|
||||
generateDebugMap(currentMapMode);
|
||||
}
|
||||
|
||||
if (currentMapMode != DebugMapMode::NONE && mapTexture != 0) {
|
||||
float availWidth = ImGui::GetContentRegionAvail().x;
|
||||
ImGui::Image((void*)(intptr_t)mapTexture, ImVec2(availWidth, availWidth * 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Camera Controls", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
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);
|
||||
@@ -129,6 +271,11 @@ public:
|
||||
|
||||
if (ImGui::Button(orbitEquator ? "Stop Equator" : "Orbit Equator")) orbitEquator = !orbitEquator;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void renderPreviewPanel() {
|
||||
ImGui::BeginChild("PreviewChild", ImVec2(0, 0), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
|
||||
livePreview();
|
||||
if (textureInitialized) {
|
||||
@@ -137,7 +284,179 @@ public:
|
||||
ImGui::Image((void*)(intptr_t)textu, ImVec2(availWidth, availWidth / aspect));
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void applyDebugColorMode() {
|
||||
if (sim.config.surfaceNodes.empty()) return;
|
||||
|
||||
float minNoise = std::numeric_limits<float>::max();
|
||||
float maxNoise = std::numeric_limits<float>::lowest();
|
||||
int minSub = std::numeric_limits<int>::max();
|
||||
int maxSub = std::numeric_limits<int>::lowest();
|
||||
|
||||
for (const auto& p : sim.config.surfaceNodes) {
|
||||
if (p.noiseDisplacement < minNoise) minNoise = p.noiseDisplacement;
|
||||
if (p.noiseDisplacement > maxNoise) maxNoise = p.noiseDisplacement;
|
||||
}
|
||||
|
||||
for (auto& p : sim.config.surfaceNodes) {
|
||||
v3 color = p.originColor;
|
||||
|
||||
switch (currentColorMode) {
|
||||
case DebugColorMode::PLATES:
|
||||
if (p.plateID != -1 && p.plateID < sim.plates.size()) {
|
||||
color = sim.plates[p.plateID].debugColor;
|
||||
} else {
|
||||
color = v3(0.5f, 0.5f, 0.5f);
|
||||
}
|
||||
break;
|
||||
case DebugColorMode::NOISE: {
|
||||
float t = 0.5f;
|
||||
if (maxNoise > minNoise) t = (p.noiseDisplacement - minNoise) / (maxNoise - minNoise);
|
||||
color = v3(t, t, t);
|
||||
break;
|
||||
}
|
||||
case DebugColorMode::BASE:
|
||||
default:
|
||||
color = p.originColor;
|
||||
break;
|
||||
}
|
||||
|
||||
sim.grid.update(p.currentPos, p.currentPos, p, true, color, sim.config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
for (auto& p : sim.config.interpolatedNodes) {
|
||||
v3 color = p.originColor;
|
||||
|
||||
switch (currentColorMode) {
|
||||
case DebugColorMode::PLATES:
|
||||
if (p.plateID != -1 && p.plateID < sim.plates.size()) {
|
||||
color = sim.plates[p.plateID].debugColor;
|
||||
} else {
|
||||
color = v3(0.5f, 0.5f, 0.5f);
|
||||
}
|
||||
break;
|
||||
case DebugColorMode::NOISE: {
|
||||
float t = 0.5f;
|
||||
if (maxNoise > minNoise) t = (p.noiseDisplacement - minNoise) / (maxNoise - minNoise);
|
||||
color = v3(t, t, t);
|
||||
break;
|
||||
}
|
||||
case DebugColorMode::BASE:
|
||||
default:
|
||||
color = p.originColor;
|
||||
break;
|
||||
}
|
||||
|
||||
sim.grid.update(p.currentPos, p.currentPos, p, true, color, sim.config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void generateDebugMap(DebugMapMode mode) {
|
||||
if (mode == DebugMapMode::NONE || sim.config.surfaceNodes.empty()) return;
|
||||
|
||||
int w = 512;
|
||||
int h = 348;
|
||||
std::vector<float> depths(w * h, -1.0f);
|
||||
float minD = std::numeric_limits<float>::max();
|
||||
float maxD = std::numeric_limits<float>::lowest();
|
||||
|
||||
for (const auto& p : sim.config.surfaceNodes) {
|
||||
v3 pos;
|
||||
switch(mode) {
|
||||
case DebugMapMode::BASE:
|
||||
pos = p.originalPos;
|
||||
break;
|
||||
case DebugMapMode::NOISE:
|
||||
pos = p.noisePos;
|
||||
break;
|
||||
case DebugMapMode::TECTONIC:
|
||||
pos = p.tectonicPos;
|
||||
break;
|
||||
case DebugMapMode::CURRENT:
|
||||
default:
|
||||
pos = p.currentPos;
|
||||
break;
|
||||
}
|
||||
float d = pos.norm();
|
||||
if (d < minD) minD = d;
|
||||
if (d > maxD) maxD = d;
|
||||
}
|
||||
|
||||
for (const auto& p : sim.config.surfaceNodes) {
|
||||
v3 pos;
|
||||
switch(mode) {
|
||||
case DebugMapMode::BASE:
|
||||
pos = p.originalPos;
|
||||
break;
|
||||
case DebugMapMode::NOISE:
|
||||
pos = p.noisePos;
|
||||
break;
|
||||
case DebugMapMode::TECTONIC:
|
||||
pos = p.tectonicPos;
|
||||
break;
|
||||
case DebugMapMode::TECTONICCOLOR:
|
||||
pos = sim.plates[p.plateID].debugColor;
|
||||
break;
|
||||
case DebugMapMode::CURRENT:
|
||||
default:
|
||||
pos = p.currentPos;
|
||||
break;
|
||||
}
|
||||
|
||||
float d = pos.norm();
|
||||
v3 n = p.originalPos.normalized();
|
||||
|
||||
float u = 0.5f + std::atan2(n.z(), n.x()) / (2.0f * static_cast<float>(M_PI));
|
||||
float v = 0.5f - std::asin(n.y()) / static_cast<float>(M_PI);
|
||||
|
||||
int px = std::clamp(static_cast<int>(u * w), 0, w - 1);
|
||||
int py = std::clamp(static_cast<int>(v * h), 0, h - 1);
|
||||
|
||||
float normalizedD = (maxD > minD) ? (d - minD) / (maxD - minD) : 0.5f;
|
||||
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
int nx = px + dx;
|
||||
int ny = py + dy;
|
||||
|
||||
if (nx < 0) nx += w;
|
||||
if (nx >= w) nx -= w;
|
||||
|
||||
if (ny >= 0 && ny < h) {
|
||||
int idx = ny * w + nx;
|
||||
if (depths[idx] < 0.0f || normalizedD > depths[idx]) {
|
||||
depths[idx] = normalizedD;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < w * h; i++) {
|
||||
if (depths[i] < 0.0f) depths[i] = 0.0f;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pixels(w * h * 3);
|
||||
for (int i = 0; i < w * h; i++) {
|
||||
uint8_t val = static_cast<uint8_t>(depths[i] * 255.0f);
|
||||
pixels[i * 3 + 0] = val;
|
||||
pixels[i * 3 + 1] = val;
|
||||
pixels[i * 3 + 2] = val;
|
||||
}
|
||||
|
||||
mapFrame = frame(w, h, frame::colormap::RGB);
|
||||
mapFrame.setData(pixels);
|
||||
|
||||
if (mapTexture == 0) {
|
||||
glGenTextures(1, &mapTexture);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mapTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, mapFrame.getData().data());
|
||||
}
|
||||
|
||||
void applyNoise(const NoisePreviewState& noiseState) {
|
||||
@@ -159,6 +478,7 @@ public:
|
||||
return vYZ * blend.x() + vXZ * blend.y() + vXY * blend.z();
|
||||
};
|
||||
sim._applyNoise(triplanarNoise);
|
||||
applyDebugColorMode();
|
||||
}
|
||||
|
||||
void livePreview() {
|
||||
@@ -196,13 +516,30 @@ public:
|
||||
}
|
||||
|
||||
void simulateTectonics() {
|
||||
|
||||
currentColorMode = DebugColorMode::PLATES;
|
||||
|
||||
sim.assignSeeds();
|
||||
sim.buildAdjacencyList();
|
||||
// sim.growPlatesCellular();
|
||||
sim.growPlatesRandom();
|
||||
//sim.fixBoundaries();
|
||||
if (platesUseCellular) {
|
||||
sim.growPlatesCellular();
|
||||
} else sim.growPlatesRandom();
|
||||
if (doFixPlates) sim.fixBoundaries();
|
||||
sim.extraplateste();
|
||||
sim.boundaryStress();
|
||||
sim.finalizeApplyResults();
|
||||
|
||||
applyDebugColorMode();
|
||||
tectonicGenned = true;
|
||||
if(currentMapMode != DebugMapMode::NONE) generateDebugMap(currentMapMode);
|
||||
}
|
||||
|
||||
void interpolateSurface() {
|
||||
sim.interpolateSurface();
|
||||
}
|
||||
|
||||
void fillPlanet() {
|
||||
sim.fillPlanet();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -35,8 +35,11 @@ enum class PlateType {
|
||||
struct Particle {
|
||||
float noiseDisplacement = 0.0f;
|
||||
int plateID = -1;
|
||||
Eigen::Vector3f basePos;
|
||||
Eigen::Vector3f originalPos;
|
||||
Eigen::Vector3f noisePos;
|
||||
Eigen::Vector3f tectonicPos;
|
||||
Eigen::Vector3f currentPos;
|
||||
|
||||
float plateDisplacement = 0.0f;
|
||||
float temperature = -1;
|
||||
float water = -1;
|
||||
@@ -56,9 +59,17 @@ struct Particle {
|
||||
float mass;
|
||||
bool isStatic = false;
|
||||
float soundSpeed = 100.0f;
|
||||
float temperature = 0.0f;
|
||||
float water = 0.0f;
|
||||
float sandcontent = 0.0f;
|
||||
float siltcontent = 0.0f;
|
||||
float claycontent = 0.0f;
|
||||
float rockcontent = 0.0f;
|
||||
float metalcontent = 0.0f;
|
||||
|
||||
|
||||
std::unordered_map<int, float> neighbors;
|
||||
std::vector<int> nearNeighbors; // Switched to vector to prevent key collision & drops
|
||||
std::vector<int> nearNeighbors;
|
||||
};
|
||||
|
||||
struct planetConfig {
|
||||
@@ -73,16 +84,18 @@ struct planetConfig {
|
||||
|
||||
float displacementStrength = 200.0f;
|
||||
std::vector<Particle> surfaceNodes;
|
||||
std::vector<Particle> interpolatedNodes;
|
||||
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 gridSizeCube = 65536; //absolute max size for all nodes
|
||||
float gridSizeCubeMin = 16384; //max size, if something leaves this, then it probably needs to be purged before it leaves the grid and becomes lost
|
||||
float SMOOTHING_RADIUS = 1024.0f;
|
||||
float REST_DENSITY = 0.00005f;
|
||||
float TIMESTEP = 0.016f;
|
||||
@@ -173,7 +186,9 @@ public:
|
||||
v3 dir(x, y, z);
|
||||
v3 pos = config.center + dir * config.radius;
|
||||
Particle pt;
|
||||
pt.basePos = pos;
|
||||
pt.originalPos = pos;
|
||||
pt.noisePos = pos;
|
||||
pt.tectonicPos = pos;
|
||||
pt.currentPos = pos;
|
||||
pt.originColor = config.color;
|
||||
pt.noiseDisplacement = 0.0f;
|
||||
@@ -189,10 +204,11 @@ public:
|
||||
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);
|
||||
float displacementValue = noiseFunc(p.originalPos);
|
||||
p.noiseDisplacement = displacementValue;
|
||||
Eigen::Vector3f normal = p.basePos.normalized();
|
||||
p.currentPos = p.basePos + (normal * displacementValue * config.displacementStrength);
|
||||
Eigen::Vector3f normal = p.originalPos.normalized();
|
||||
p.noisePos = p.originalPos + (normal * displacementValue * config.noiseStrength);
|
||||
p.currentPos = p.noisePos;
|
||||
|
||||
grid.update(oldPos, p.currentPos, p, true, p.originColor, config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
@@ -220,7 +236,7 @@ public:
|
||||
const auto& existingSeed = config.surfaceNodes[selectedIndex];
|
||||
const auto& candidateSeed = config.surfaceNodes[seedIndex];
|
||||
|
||||
float dot = existingSeed.basePos.normalized().dot(candidateSeed.basePos.normalized());
|
||||
float dot = existingSeed.originalPos.normalized().dot(candidateSeed.originalPos.normalized());
|
||||
float angle = std::acos(std::clamp(dot, -1.0f, 1.0f));
|
||||
float distanceOnSphere = angle * config.radius;
|
||||
|
||||
@@ -282,7 +298,7 @@ public:
|
||||
std::vector<v3> normPos(numNodes);
|
||||
#pragma omp parallel for schedule(static)
|
||||
for (int i = 0; i < numNodes; i++) {
|
||||
normPos[i] = config.surfaceNodes[i].basePos.normalized();
|
||||
normPos[i] = config.surfaceNodes[i].originalPos.normalized();
|
||||
}
|
||||
|
||||
#pragma omp parallel for schedule(static)
|
||||
@@ -449,7 +465,7 @@ public:
|
||||
int closestPlate = 0;
|
||||
float minDist = std::numeric_limits<float>::max();
|
||||
for (int p = 0; p < config.numPlates; p++) {
|
||||
float d = (config.surfaceNodes[i].basePos - plates[p].plateEulerPole.basePos).norm();
|
||||
float d = (config.surfaceNodes[i].originalPos - plates[p].plateEulerPole.originalPos).norm();
|
||||
if (d < minDist) {
|
||||
minDist = d;
|
||||
closestPlate = p;
|
||||
@@ -519,7 +535,7 @@ public:
|
||||
|
||||
for (int nIdx : plates[i].assignedNodes) {
|
||||
sumElevation += config.surfaceNodes[nIdx].currentPos.norm();
|
||||
centroid += config.surfaceNodes[nIdx].basePos;
|
||||
centroid += config.surfaceNodes[nIdx].originalPos;
|
||||
}
|
||||
|
||||
if (!plates[i].assignedNodes.empty()) {
|
||||
@@ -528,18 +544,18 @@ public:
|
||||
|
||||
float maxSpread = 0.0f;
|
||||
for (int nIdx : plates[i].assignedNodes) {
|
||||
float d = (config.surfaceNodes[nIdx].basePos - centroid).norm();
|
||||
float d = (config.surfaceNodes[nIdx].originalPos - centroid).norm();
|
||||
if (d > maxSpread) maxSpread = d;
|
||||
}
|
||||
|
||||
float distToCentroid = (plates[i].plateEulerPole.basePos - centroid).norm();
|
||||
float distToCentroid = (plates[i].plateEulerPole.originalPos - centroid).norm();
|
||||
|
||||
if (distToCentroid > maxSpread * 0.6f) {
|
||||
int bestNodeIdx = plates[i].assignedNodes[0];
|
||||
float minDistToCentroid = std::numeric_limits<float>::max();
|
||||
|
||||
for (int nIdx : plates[i].assignedNodes) {
|
||||
float d = (config.surfaceNodes[nIdx].basePos - centroid).norm();
|
||||
float d = (config.surfaceNodes[nIdx].originalPos - centroid).norm();
|
||||
if (d < minDistToCentroid) {
|
||||
minDistToCentroid = d;
|
||||
bestNodeIdx = nIdx;
|
||||
@@ -555,7 +571,7 @@ public:
|
||||
Eigen::Vector3f randomDir(distFloat(rng) - 0.5f, distFloat(rng) - 0.5f, distFloat(rng) - 0.5f);
|
||||
randomDir.normalize();
|
||||
|
||||
Eigen::Vector3f poleDir = plates[i].plateEulerPole.basePos.normalized();
|
||||
Eigen::Vector3f poleDir = plates[i].plateEulerPole.originalPos.normalized();
|
||||
plates[i].direction = (randomDir - poleDir * randomDir.dot(poleDir)).normalized();
|
||||
|
||||
plates[i].angularVelocity = distFloat(rng) * 0.1f + 0.02f;
|
||||
@@ -596,7 +612,7 @@ public:
|
||||
|
||||
std::vector<Eigen::Vector3f> ω(config.numPlates);
|
||||
for (int i = 0; i < config.numPlates; i++) {
|
||||
ω[i] = plates[i].plateEulerPole.basePos.normalized().cross(plates[i].direction) * plates[i].angularVelocity;
|
||||
ω[i] = plates[i].plateEulerPole.originalPos.normalized().cross(plates[i].direction) * plates[i].angularVelocity;
|
||||
}
|
||||
|
||||
std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
|
||||
@@ -609,7 +625,7 @@ public:
|
||||
int myPlate = config.surfaceNodes[i].plateID;
|
||||
if (myPlate == -1) continue;
|
||||
|
||||
Eigen::Vector3f myPos = config.surfaceNodes[i].basePos.normalized();
|
||||
Eigen::Vector3f myPos = config.surfaceNodes[i].originalPos.normalized();
|
||||
Eigen::Vector3f myVel = ω[myPlate].cross(myPos);
|
||||
|
||||
float localStress = 0.0f;
|
||||
@@ -620,7 +636,7 @@ public:
|
||||
int nPlate = config.surfaceNodes[nIdx].plateID;
|
||||
if (nPlate != -1 && myPlate != nPlate) {
|
||||
boundaryCount++;
|
||||
Eigen::Vector3f nPos = config.surfaceNodes[nIdx].basePos.normalized();
|
||||
Eigen::Vector3f nPos = config.surfaceNodes[nIdx].originalPos.normalized();
|
||||
Eigen::Vector3f nVel = ω[nPlate].cross(nPos);
|
||||
|
||||
Eigen::Vector3f relVel = nVel - myVel;
|
||||
@@ -672,8 +688,8 @@ public:
|
||||
|
||||
float noiseVal = dist(rng) * nodeNoise[i];
|
||||
|
||||
Eigen::Vector3f normal = p.basePos.normalized();
|
||||
p.currentPos += normal * (p.plateDisplacement + noiseVal);
|
||||
Eigen::Vector3f normal = p.originalPos.normalized();
|
||||
p.tectonicPos = p.noisePos + (normal * (p.plateDisplacement + noiseVal));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,23 +699,12 @@ public:
|
||||
|
||||
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);
|
||||
p.currentPos = p.tectonicPos;
|
||||
grid.update(oldPos, p.currentPos, p, true, p.originColor, config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
std::cout << "Finalize apply results completed." << std::endl;
|
||||
}
|
||||
|
||||
void interpolateSurface() {
|
||||
TIME_FUNCTION;
|
||||
///TODO: go through all surface nodes and fill in gaps between each and their near neighbors until the surface has no holes
|
||||
//lets keep these separate so they can be removed when redoing any prior steps
|
||||
}
|
||||
|
||||
void fillPlanet() {
|
||||
TIME_FUNCTION;
|
||||
///TODO: completely fill the planet, interpolating the entire planet.
|
||||
//same as interpolatesurface, these should be kept separate. but since they will probably be bigger than a vector I dont know how.
|
||||
}
|
||||
|
||||
void addStar() {
|
||||
///TODO: add a star at roughly earth distance scaled based on planet radius.
|
||||
}
|
||||
@@ -708,6 +713,70 @@ public:
|
||||
///TODO: using planetConfig, add moon(s).
|
||||
}
|
||||
|
||||
void stretchPlanet() {
|
||||
///TODO: simulate millenia of gravitational stretching by nearby celestial bodies by squeezing the planet slightly at its poles
|
||||
}
|
||||
|
||||
void interpolateSurface() {
|
||||
TIME_FUNCTION;
|
||||
std::unordered_map<uint64_t, bool> processedEdges;
|
||||
size_t counter = 0;
|
||||
|
||||
for (int i = 0; i < config.surfaceNodes.size(); i++) {
|
||||
Particle& p1 = config.surfaceNodes[i];
|
||||
|
||||
for (int j : p1.nearNeighbors) {
|
||||
if (i >= j) continue;
|
||||
|
||||
uint64_t edgeKey = ((uint64_t)i << 32) | (uint32_t)j;
|
||||
if (processedEdges[edgeKey]) continue;
|
||||
processedEdges[edgeKey] = true;
|
||||
|
||||
Particle& p2 = config.surfaceNodes[j];
|
||||
float dist = (p1.currentPos - p2.currentPos).norm();
|
||||
|
||||
// If nodes are too far apart, fill the gap
|
||||
if (dist > config.voxelSize) {
|
||||
int steps = static_cast<int>(dist / config.voxelSize);
|
||||
for (int step = 1; step <= steps; step++) {
|
||||
float t = static_cast<float>(step) / (steps + 1);
|
||||
|
||||
Particle newPt;
|
||||
newPt.surface = true;
|
||||
newPt.plateID = (t < 0.5f) ? p1.plateID : p2.plateID;
|
||||
newPt.originColor = (t < 0.5f) ? p1.originColor : p2.originColor;
|
||||
|
||||
// Spherically interpolate base positions
|
||||
Eigen::Vector3f baseP1 = p1.originalPos.normalized();
|
||||
Eigen::Vector3f baseP2 = p2.originalPos.normalized();
|
||||
|
||||
// SLERP (Spherical Linear Interpolation) for perfect curves
|
||||
float dot = std::clamp(baseP1.dot(baseP2), -1.0f, 1.0f);
|
||||
float theta = std::acos(dot);
|
||||
Eigen::Vector3f interpBase = ((std::sin((1.0f - t) * theta) / std::sin(theta)) * baseP1 +
|
||||
(std::sin(t * theta) / std::sin(theta)) * baseP2).normalized();
|
||||
|
||||
newPt.originalPos = interpBase * config.radius;
|
||||
newPt.noisePos = p1.noisePos * (1.0f - t) + p2.noisePos * t;
|
||||
newPt.tectonicPos = p1.tectonicPos * (1.0f - t) + p2.tectonicPos * t;
|
||||
newPt.currentPos = p1.currentPos * (1.0f - t) + p2.currentPos * t; // Linear for height
|
||||
|
||||
grid.set(newPt, newPt.currentPos, true, newPt.originColor, config.voxelSize, true, 1, 2 /*subid 2 for interpolated*/, false, 0.0f, 0.0f, 0.0f);
|
||||
config.interpolatedNodes.emplace_back(newPt);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "Interpolated " << counter << " surface gaps." << std::endl;
|
||||
}
|
||||
|
||||
void fillPlanet() {
|
||||
TIME_FUNCTION;
|
||||
///TODO: completely fill the planet, interpolating the entire planet.
|
||||
//same as interpolatesurface, these should be kept separate. but since they will probably be bigger than a vector I dont know how.
|
||||
}
|
||||
|
||||
void simulateImpacts() {
|
||||
TIME_FUNCTION;
|
||||
///TODO: this needs to be run on a separate thread to allow visuals to continue.
|
||||
@@ -717,10 +786,6 @@ public:
|
||||
//the gravity portion should be turned off when this is done.
|
||||
}
|
||||
|
||||
void stretchPlanet() {
|
||||
///TODO: simulate millenia of gravitational stretching by nearby celestial bodies by squeezing the planet slightly at its poles
|
||||
}
|
||||
|
||||
void erosion() {
|
||||
///TODO: simulate erosion by spawning many nodes all over the surface one at a time and then pulling them towards the lowest neighboring points. reducing height from source as it flows downhill and increasing at bottom.
|
||||
// this needs to be run on a separate thread to allow visuals to continue.
|
||||
|
||||
Reference in New Issue
Block a user