Compare commits
24 Commits
tdgame
...
needsfixes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c2fbd43ac | ||
|
|
6336d45075 | ||
|
|
3d96e569c8 | ||
|
|
31fb9ffedb | ||
|
|
4478bc1845 | ||
|
|
83989e955e | ||
|
|
494ee931df | ||
|
|
d36d00bc13 | ||
|
|
a0afac9c18 | ||
|
|
565646e13e | ||
|
|
14158fae83 | ||
|
|
4f0dba5eb4 | ||
|
|
926ffe18cd | ||
|
|
b4a7f536bc | ||
|
|
06d5b383e5 | ||
|
|
18aa8f06b7 | ||
|
|
fdd5553d20 | ||
|
|
e99bbc08af | ||
|
|
c7eb5739f4 | ||
|
|
2c993995e8 | ||
|
|
cb0b8b7643 | ||
|
|
dc36b93e4f | ||
|
|
2768b6849e | ||
|
|
4f227df1d7 |
4
makefile
4
makefile
@@ -7,7 +7,7 @@ STB_DIR := ./stb
|
|||||||
|
|
||||||
# Compiler and flags
|
# Compiler and flags
|
||||||
CXX := g++
|
CXX := g++
|
||||||
BASE_CXXFLAGS = -std=c++23 -O3 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -I$(STB_DIR)
|
BASE_CXXFLAGS = -std=c++23 -O3 -fopenmp -march=native -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -I$(STB_DIR) -g
|
||||||
BASE_CXXFLAGS += `pkg-config --cflags glfw3`
|
BASE_CXXFLAGS += `pkg-config --cflags glfw3`
|
||||||
CFLAGS = $(BASE_CXXFLAGS)
|
CFLAGS = $(BASE_CXXFLAGS)
|
||||||
LDFLAGS := -L./imgui -limgui -lGL
|
LDFLAGS := -L./imgui -limgui -lGL
|
||||||
@@ -34,7 +34,7 @@ endif
|
|||||||
CXXFLAGS = $(BASE_CXXFLAGS) $(SIMD_CXXFLAGS)
|
CXXFLAGS = $(BASE_CXXFLAGS) $(SIMD_CXXFLAGS)
|
||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
SRC := $(SRC_DIR)/g3etest.cpp
|
SRC := $(SRC_DIR)/ptest.cpp
|
||||||
#SRC := $(SRC_DIR)/g2chromatic2.cpp
|
#SRC := $(SRC_DIR)/g2chromatic2.cpp
|
||||||
SRC += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
SRC += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
||||||
SRC += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
|
SRC += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ struct defaults {
|
|||||||
bool slowRender = false;
|
bool slowRender = false;
|
||||||
bool globalIllumination = true;
|
bool globalIllumination = true;
|
||||||
bool useLod = true;
|
bool useLod = true;
|
||||||
int rayCount = 3;
|
int rayCount = 5;
|
||||||
int reflectCount = 3;
|
int reflectCount = 3;
|
||||||
int lodDist = 500;
|
int lodDist = 50000;
|
||||||
float lodDropoff = 0.1;
|
float lodDropoff = 0.1;
|
||||||
PNoise2 noise = PNoise2(42);
|
PNoise2 noise = PNoise2(42);
|
||||||
|
|
||||||
@@ -82,6 +82,12 @@ std::vector<double> renderFrameTimes;
|
|||||||
int frameHistoryIndex = 0;
|
int frameHistoryIndex = 0;
|
||||||
bool firstFrameMeasured = false;
|
bool firstFrameMeasured = false;
|
||||||
|
|
||||||
|
// Stats update timer
|
||||||
|
std::chrono::steady_clock::time_point lastStatsUpdate;
|
||||||
|
const std::chrono::seconds STATS_UPDATE_INTERVAL(10);
|
||||||
|
std::string cachedStats;
|
||||||
|
bool statsNeedUpdate = true;
|
||||||
|
|
||||||
Scene scene;
|
Scene scene;
|
||||||
bool meshNeedsUpdate = false;
|
bool meshNeedsUpdate = false;
|
||||||
|
|
||||||
@@ -182,38 +188,45 @@ void addStar(const defaults& config, const stardefaults& starconf, Octree<int>&
|
|||||||
meshNeedsUpdate = true;
|
meshNeedsUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateStatsCache(Octree<int>& grid) {
|
||||||
|
std::stringstream gridstats;
|
||||||
|
grid.printStats(gridstats);
|
||||||
|
cachedStats = gridstats.str();
|
||||||
|
lastStatsUpdate = std::chrono::steady_clock::now();
|
||||||
|
statsNeedUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
||||||
std::lock_guard<std::mutex> lock(PreviewMutex);
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
updatePreview = true;
|
updatePreview = true;
|
||||||
|
|
||||||
|
|
||||||
if (meshNeedsUpdate) {
|
// if (meshNeedsUpdate) {
|
||||||
scene.clear();
|
// scene.clear();
|
||||||
std::shared_ptr<Mesh> planetMesh = grid.generateMesh(1, config.meshIsoLevel, pow(config.meshResolution, 2));
|
// std::shared_ptr<Mesh> planetMesh = grid.generateMesh(1, config.meshIsoLevel, pow(config.meshResolution, 2));
|
||||||
std::shared_ptr<Mesh> starMesh = grid.generateMesh(2, config.meshIsoLevel, config.meshResolution);
|
// std::shared_ptr<Mesh> starMesh = grid.generateMesh(2, config.meshIsoLevel, config.meshResolution);
|
||||||
|
|
||||||
scene.addMesh(planetMesh);
|
// scene.addMesh(planetMesh);
|
||||||
scene.addMesh(starMesh);
|
// scene.addMesh(starMesh);
|
||||||
|
|
||||||
// planetMesh.setResolution(config.meshResolution);
|
// // planetMesh.setResolution(config.meshResolution);
|
||||||
// planetMesh.setIsoLevel(config.meshIsoLevel);
|
// // planetMesh.setIsoLevel(config.meshIsoLevel);
|
||||||
// planetMesh.update(grid);
|
// // planetMesh.update(grid);
|
||||||
meshNeedsUpdate = false;
|
// meshNeedsUpdate = false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
auto renderStart = std::chrono::high_resolution_clock::now();
|
auto renderStart = std::chrono::high_resolution_clock::now();
|
||||||
frame currentPreviewFrame;
|
frame currentPreviewFrame;
|
||||||
|
|
||||||
currentPreviewFrame = scene.render(cam, config.outWidth, config.outHeight, 0.1f, 10000.0f, frame::colormap::RGB);
|
// currentPreviewFrame = scene.render(cam, config.outWidth, config.outHeight, 0.1f, 10000.0f, frame::colormap::RGB);
|
||||||
|
|
||||||
// grid.setLODMinDistance(config.lodDist);
|
grid.setLODMinDistance(config.lodDist);
|
||||||
// grid.setLODFalloff(config.lodDropoff);
|
grid.setLODFalloff(config.lodDropoff);
|
||||||
// if (config.slowRender) {
|
if (config.slowRender) {
|
||||||
// currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB, config.rayCount, config.reflectCount, config.globalIllumination, config.useLod);
|
currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB, config.rayCount, config.reflectCount, config.globalIllumination, config.useLod);
|
||||||
// } else {
|
} else {
|
||||||
// currentPreviewFrame = grid.fastRenderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB);
|
currentPreviewFrame = grid.fastRenderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB);
|
||||||
// }
|
}
|
||||||
|
|
||||||
auto renderEnd = std::chrono::high_resolution_clock::now();
|
auto renderEnd = std::chrono::high_resolution_clock::now();
|
||||||
renderFrameTime = std::chrono::duration<double>(renderEnd - renderStart).count();
|
renderFrameTime = std::chrono::duration<double>(renderEnd - renderStart).count();
|
||||||
@@ -330,7 +343,7 @@ int main() {
|
|||||||
defaults config;
|
defaults config;
|
||||||
PointType minBound(-config.gridSizecube, -config.gridSizecube, -config.gridSizecube);
|
PointType minBound(-config.gridSizecube, -config.gridSizecube, -config.gridSizecube);
|
||||||
PointType maxBound(config.gridSizecube, config.gridSizecube, config.gridSizecube);
|
PointType maxBound(config.gridSizecube, config.gridSizecube, config.gridSizecube);
|
||||||
Octree<int> grid(minBound, maxBound, 16, 16);
|
Octree<int> grid(minBound, maxBound, 8, 32);
|
||||||
bool gridInitialized = false;
|
bool gridInitialized = false;
|
||||||
float ghalf = config.gridSizecube / 2.f;
|
float ghalf = config.gridSizecube / 2.f;
|
||||||
|
|
||||||
@@ -505,22 +518,22 @@ int main() {
|
|||||||
ImGui::ColorEdit3("Color", sphereConf.color);
|
ImGui::ColorEdit3("Color", sphereConf.color);
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Marching Cubes Config");
|
// ImGui::Text("Marching Cubes Config");
|
||||||
if (ImGui::SliderInt("Mesh Resolution", &config.meshResolution, 1, 64)) {
|
// if (ImGui::SliderInt("Mesh Resolution", &config.meshResolution, 1, 64)) {
|
||||||
meshNeedsUpdate = true;
|
// meshNeedsUpdate = true;
|
||||||
}
|
|
||||||
if (ImGui::SliderFloat("Iso Level", &config.meshIsoLevel, 0.01f, 1.0f)) {
|
|
||||||
meshNeedsUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Checkbox("Is Light", &sphereConf.light);
|
|
||||||
// if(sphereConf.light) {
|
|
||||||
// ImGui::DragFloat("Emittance", &sphereConf.emittance, 0.1f, 0.0f, 100.0f);
|
|
||||||
// }
|
// }
|
||||||
// ImGui::SliderFloat("Reflection", &sphereConf.reflection, 0.0f, 1.0f);
|
// if (ImGui::SliderFloat("Iso Level", &config.meshIsoLevel, 0.01f, 1.0f)) {
|
||||||
// ImGui::SliderFloat("Refraction", &sphereConf.refraction, 0.0f, 1.0f);
|
// meshNeedsUpdate = true;
|
||||||
// ImGui::Checkbox("Fill Inside", &sphereConf.fillInside);
|
// }
|
||||||
|
|
||||||
|
// ImGui::Separator();
|
||||||
|
// ImGui::Checkbox("Is Light", &sphereConf.light);
|
||||||
|
if(sphereConf.light) {
|
||||||
|
ImGui::DragFloat("Emittance", &sphereConf.emittance, 0.1f, 0.0f, 100.0f);
|
||||||
|
}
|
||||||
|
ImGui::SliderFloat("Reflection", &sphereConf.reflection, 0.0f, 1.0f);
|
||||||
|
ImGui::SliderFloat("Refraction", &sphereConf.refraction, 0.0f, 1.0f);
|
||||||
|
ImGui::Checkbox("Fill Inside", &sphereConf.fillInside);
|
||||||
|
|
||||||
if (ImGui::CollapsingHeader("Star/Sun Parameters", ImGuiTreeNodeFlags_DefaultOpen)) {
|
if (ImGui::CollapsingHeader("Star/Sun Parameters", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
ImGui::Checkbox("Enable Star", &starConf.enabled);
|
ImGui::Checkbox("Enable Star", &starConf.enabled);
|
||||||
@@ -564,8 +577,56 @@ int main() {
|
|||||||
fluidUI.renderUI();
|
fluidUI.renderUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
scene.drawSceneWindow("Planet Preview", cam, 0.01, 1000);
|
// scene.drawSceneWindow("Planet Preview", cam, 0.01, 1000);
|
||||||
scene.drawGridStats();
|
// scene.drawGridStats();
|
||||||
|
|
||||||
|
{
|
||||||
|
ImGui::Begin("Planet Preview");
|
||||||
|
if (worldPreview) {
|
||||||
|
if (gridInitialized) {
|
||||||
|
livePreview(grid, config, cam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gridInitialized && textureInitialized) {
|
||||||
|
ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight));
|
||||||
|
} else if (gridInitialized) {
|
||||||
|
ImGui::Text("Preview not generated yet");
|
||||||
|
} else {
|
||||||
|
ImGui::Text("No grid generated");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Render Performance:");
|
||||||
|
if (renderFPS > 0) {
|
||||||
|
// Color code based on FPS
|
||||||
|
ImVec4 fpsColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f);
|
||||||
|
// if (renderFPS >= 30.0) {
|
||||||
|
// fpsColor = ImVec4(0.0f, 1.0f, 0.0f, 1.0f); // Green for good FPS
|
||||||
|
// } else if (renderFPS >= 15.0) {
|
||||||
|
// fpsColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); // Yellow for okay FPS
|
||||||
|
// } else {
|
||||||
|
// fpsColor = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); // Red for poor FPS
|
||||||
|
// }
|
||||||
|
|
||||||
|
ImGui::TextColored(fpsColor, "FPS: %.1f", renderFPS);
|
||||||
|
ImGui::Text("Frame time: %.1f ms", avgRenderFrameTime * 1000.0);
|
||||||
|
|
||||||
|
// Simple progress bar for frame time
|
||||||
|
ImGui::Text("%.1f/100 ms", avgRenderFrameTime * 1000.0);
|
||||||
|
|
||||||
|
// Show latest frame time
|
||||||
|
ImGui::Text("Latest: %.1f ms", renderFrameTime * 1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (gridInitialized) {
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
if ((now - lastStatsUpdate) > STATS_UPDATE_INTERVAL) updateStatsCache(grid);
|
||||||
|
ImGui::TextUnformatted(cachedStats.c_str());
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ImGui::Begin("controls");
|
ImGui::Begin("controls");
|
||||||
@@ -759,19 +820,18 @@ int main() {
|
|||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::Checkbox("Continuous Preview", &worldPreview);
|
// ImGui::Checkbox("Continuous Preview", &worldPreview);
|
||||||
|
|
||||||
// Removed Raytracing specific controls (Global Illum, LOD) as they don't apply to raster mesh
|
ImGui::Checkbox("update Preview", &worldPreview);
|
||||||
// ImGui::Checkbox("update Preview", &worldPreview);
|
ImGui::Checkbox("Use Slower renderer", &config.slowRender);
|
||||||
// ImGui::Checkbox("Use Slower renderer", &config.slowRender);
|
if (config.slowRender) {
|
||||||
// if (config.slowRender) {
|
ImGui::InputInt("Rays per pixel", &config.rayCount);
|
||||||
// ImGui::InputInt("Rays per pixel", &config.rayCount);
|
ImGui::InputInt("Max reflections", &config.reflectCount);
|
||||||
// ImGui::InputInt("Max reflections", &config.reflectCount);
|
}
|
||||||
// }
|
ImGui::InputFloat("Lod dropoff", &config.lodDropoff);
|
||||||
// ImGui::InputFloat("Lod dropoff", &config.lodDropoff);
|
ImGui::InputInt("lod minimum Distance", &config.lodDist);
|
||||||
// ImGui::InputInt("lod minimum Distance", &config.lodDist);
|
ImGui::Checkbox("use Global illumination", &config.globalIllumination);
|
||||||
// ImGui::Checkbox("use Global illumination", &config.globalIllumination);
|
ImGui::Checkbox("use Lod", &config.useLod);
|
||||||
// ImGui::Checkbox("use Lod", &config.useLod);
|
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|||||||
195
tests/materialtest.cpp
Normal file
195
tests/materialtest.cpp
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
// Include Eigen and project headers
|
||||||
|
#include "../eigen/Eigen/Dense"
|
||||||
|
#include "../util/grid/camera.hpp"
|
||||||
|
#include "../util/grid/grid3eigen.hpp"
|
||||||
|
#include "../util/output/frame.hpp"
|
||||||
|
#include "../util/output/bmpwriter.hpp"
|
||||||
|
|
||||||
|
// Helper function to create a solid volume of voxels with material properties
|
||||||
|
void createBox(Octree<int>& octree, const Eigen::Vector3f& center, const Eigen::Vector3f& size,
|
||||||
|
const Eigen::Vector3f& albedo, float emission = 0.0f,
|
||||||
|
float roughness = 0.8f, float metallic = 0.0f, float transmission = 0.0f, float ior = 1.45f) {
|
||||||
|
float step = 0.1f; // Voxel spacing
|
||||||
|
Eigen::Vector3f halfSize = size / 2.0f;
|
||||||
|
Eigen::Vector3f minB = center - halfSize;
|
||||||
|
Eigen::Vector3f maxB = center + halfSize;
|
||||||
|
|
||||||
|
for (float x = minB.x(); x <= maxB.x(); x += step) {
|
||||||
|
for (float y = minB.y(); y <= maxB.y(); y += step) {
|
||||||
|
for (float z = minB.z(); z <= maxB.z(); z += step) {
|
||||||
|
Eigen::Vector3f pos(x, y, z);
|
||||||
|
|
||||||
|
// .set(data, pos, visible, albedo, size, active, objectId, subId, emission, roughness, metallic, transmission, ior)
|
||||||
|
octree.set(1, pos, true, albedo, step, true, -1, 0, emission, roughness, metallic, transmission, ior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create a checkerboard pattern volume
|
||||||
|
void createCheckerBox(Octree<int>& octree, const Eigen::Vector3f& center, const Eigen::Vector3f& size,
|
||||||
|
const Eigen::Vector3f& color1, const Eigen::Vector3f& color2, float checkerSize) {
|
||||||
|
float step = 0.1f;
|
||||||
|
Eigen::Vector3f halfSize = size / 2.0f;
|
||||||
|
Eigen::Vector3f minB = center - halfSize;
|
||||||
|
Eigen::Vector3f maxB = center + halfSize;
|
||||||
|
|
||||||
|
for (float x = minB.x(); x <= maxB.x(); x += step) {
|
||||||
|
for (float y = minB.y(); y <= maxB.y(); y += step) {
|
||||||
|
for (float z = minB.z(); z <= maxB.z(); z += step) {
|
||||||
|
Eigen::Vector3f pos(x, y, z);
|
||||||
|
|
||||||
|
// Use floor to correctly handle negative coordinates for the repeating pattern
|
||||||
|
int cx = static_cast<int>(std::floor(x / checkerSize));
|
||||||
|
int cy = static_cast<int>(std::floor(y / checkerSize));
|
||||||
|
int cz = static_cast<int>(std::floor(z / checkerSize));
|
||||||
|
|
||||||
|
// 3D Checkerboard logic
|
||||||
|
bool isEven = ((cx + cy + cz) % 2 == 0);
|
||||||
|
Eigen::Vector3f albedo = isEven ? color1 : color2;
|
||||||
|
|
||||||
|
octree.set(1, pos, true, albedo, step, true, -1, 0, 0.0f, 0.8f, 0.1f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Initializing Octree..." << std::endl;
|
||||||
|
|
||||||
|
// 1. Initialize Octree bounds
|
||||||
|
Eigen::Vector3f minBound(-10.0f, -10.0f, -10.0f);
|
||||||
|
Eigen::Vector3f maxBound(10.0f, 10.0f, 10.0f);
|
||||||
|
Octree<int> octree(minBound, maxBound, 8, 16);
|
||||||
|
|
||||||
|
// Set a dark background to emphasize the PBR light emission
|
||||||
|
octree.setBackgroundColor(Eigen::Vector3f(0.02f, 0.02f, 0.02f));
|
||||||
|
octree.setSkylight(Eigen::Vector3f(0.01f, 0.01f, 0.01f));
|
||||||
|
|
||||||
|
std::cout << "Building scene..." << std::endl;
|
||||||
|
|
||||||
|
// 2a. Build Room (Floor and 4 Walls)
|
||||||
|
Eigen::Vector3f cLightGray(0.8f, 0.8f, 0.8f);
|
||||||
|
Eigen::Vector3f cDarkGray(0.2f, 0.2f, 0.2f);
|
||||||
|
float chkSize = 1.0f;
|
||||||
|
|
||||||
|
// Floor (Bounds: Z from -0.7 to -0.5)
|
||||||
|
// The boxes sit exactly on Z = -0.5
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f(0.0f, 0.0f, -0.6f), Eigen::Vector3f(14.4f, 14.4f, 0.2f), cLightGray, cDarkGray, chkSize);
|
||||||
|
|
||||||
|
// Walls (Bounds: X/Y inner boundaries at +/- 7.0, rising from Z=-0.5 up to Z=7.5)
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f( 7.1f, 0.0f, 3.5f), Eigen::Vector3f(0.2f, 14.4f, 8.0f), cLightGray, cDarkGray, chkSize); // +X
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f(-7.1f, 0.0f, 3.5f), Eigen::Vector3f(0.2f, 14.4f, 8.0f), cLightGray, cDarkGray, chkSize); // -X
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f( 0.0f, 7.1f, 3.5f), Eigen::Vector3f(14.0f, 0.2f, 8.0f), cLightGray, cDarkGray, chkSize); // +Y
|
||||||
|
createCheckerBox(octree, Eigen::Vector3f( 0.0f, -7.1f, 3.5f), Eigen::Vector3f(14.0f, 0.2f, 8.0f), cLightGray, cDarkGray, chkSize); // -Y
|
||||||
|
|
||||||
|
// 2b. Create the 3x3 material sampler grid inside the room
|
||||||
|
Eigen::Vector3f cRed(1.0f, 0.1f, 0.1f);
|
||||||
|
Eigen::Vector3f cBlue(0.1f, 0.1f, 1.0f);
|
||||||
|
Eigen::Vector3f cPurple(0.6f, 0.1f, 0.8f);
|
||||||
|
Eigen::Vector3f size(1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
float sp = 2.0f; // spacing between cubes
|
||||||
|
|
||||||
|
// --- LAYER 1: Metals ---
|
||||||
|
// (metallic = 1.0, slight roughness for blurry reflections, transmission = 0.0)
|
||||||
|
createBox(octree, Eigen::Vector3f(-sp, -sp, 0.0f), size, cRed, 0.0f, 0.15f, 1.0f, 0.0f, 1.45f);
|
||||||
|
createBox(octree, Eigen::Vector3f( 0, -sp, 0.0f), size, cBlue, 0.0f, 0.15f, 1.0f, 0.0f, 1.45f);
|
||||||
|
createBox(octree, Eigen::Vector3f( sp, -sp, 0.0f), size, cPurple, 0.0f, 0.15f, 1.0f, 0.0f, 1.45f);
|
||||||
|
|
||||||
|
// --- LAYER 2: Opaque & Highly Refractive ---
|
||||||
|
// (metallic = 0.0, very low roughness. transmission = 0.0 for opacity, ior = 2.4 for extreme diamond-like reflection)
|
||||||
|
createBox(octree, Eigen::Vector3f(-sp, 0, 0.0f), size, cRed, 0.0f, 0.05f, 0.0f, 0.0f, 2.4f);
|
||||||
|
createBox(octree, Eigen::Vector3f( 0, 0, 0.0f), size, cBlue, 0.0f, 0.05f, 0.0f, 0.0f, 2.4f);
|
||||||
|
createBox(octree, Eigen::Vector3f( sp, 0, 0.0f), size, cPurple, 0.0f, 0.05f, 0.0f, 0.0f, 2.4f);
|
||||||
|
|
||||||
|
// --- LAYER 3: Clear Glass ---
|
||||||
|
// (metallic = 0.0, near-zero roughness, transmission = 1.0 for full transparency, ior = 1.5 for glass)
|
||||||
|
createBox(octree, Eigen::Vector3f(-sp, sp, 0.0f), size, cRed, 0.0f, 0.01f, 0.0f, 1.0f, 1.5f);
|
||||||
|
createBox(octree, Eigen::Vector3f( 0, sp, 0.0f), size, cBlue, 0.0f, 0.01f, 0.0f, 1.0f, 1.5f);
|
||||||
|
createBox(octree, Eigen::Vector3f( sp, sp, 0.0f), size, cPurple, 0.0f, 0.01f, 0.0f, 1.0f, 1.5f);
|
||||||
|
|
||||||
|
// White Light Box (Above)
|
||||||
|
// Placed near the ceiling (Z=7.4), made large (8x8) to cast soft shadows evenly over the whole 3x3 grid
|
||||||
|
createBox(octree, Eigen::Vector3f(0.0f, 0.0f, 7.4f), Eigen::Vector3f(8.0f, 8.0f, 0.2f), Eigen::Vector3f(1.0f, 1.0f, 1.0f), 15.0f);
|
||||||
|
|
||||||
|
std::cout << "Optimizing and Generating LODs..." << std::endl;
|
||||||
|
octree.generateLODs();
|
||||||
|
octree.printStats();
|
||||||
|
|
||||||
|
// 3. Setup rendering loop
|
||||||
|
int width = 512;
|
||||||
|
int height = 512;
|
||||||
|
int samples = 400;
|
||||||
|
int bounces = 5;
|
||||||
|
|
||||||
|
struct View {
|
||||||
|
std::string name;
|
||||||
|
Eigen::Vector3f origin;
|
||||||
|
Eigen::Vector3f up;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The walls are set perfectly at +/- 7.0 inner edges.
|
||||||
|
// Placing camera at +/- 6.8 will put it "just barely inside".
|
||||||
|
// Floor is at Z = -0.5, Wall top is at Z = 7.5
|
||||||
|
std::vector<View> views = {
|
||||||
|
{"+X", Eigen::Vector3f( 6.8f, 0.0f, 1.0f), Eigen::Vector3f(0.0f, 0.0f, 1.0f)},
|
||||||
|
{"-X", Eigen::Vector3f(-6.8f, 0.0f, 1.0f), Eigen::Vector3f(0.0f, 0.0f, 1.0f)},
|
||||||
|
{"+Y", Eigen::Vector3f( 0.0f, 6.8f, 1.0f), Eigen::Vector3f(0.0f, 0.0f, 1.0f)},
|
||||||
|
{"-Y", Eigen::Vector3f( 0.0f, -6.8f, 1.0f), Eigen::Vector3f(0.0f, 0.0f, 1.0f)},
|
||||||
|
{"+Z", Eigen::Vector3f( 0.0f, 0.0f, 7.3f), Eigen::Vector3f(0.0f, 1.0f, 0.0f)} // Looking down from just beneath wall top
|
||||||
|
};
|
||||||
|
|
||||||
|
Eigen::Vector3f target(0.0f, 0.0f, 0.5f);
|
||||||
|
|
||||||
|
for (const auto& view : views) {
|
||||||
|
std::cout << "\nRendering view from " << view.name << " direction (Fast Pass)..." << std::endl;
|
||||||
|
|
||||||
|
Camera cam;
|
||||||
|
cam.origin = view.origin;
|
||||||
|
cam.direction = (target - view.origin).normalized();
|
||||||
|
cam.up = view.up;
|
||||||
|
|
||||||
|
frame out = octree.fastRenderFrame(cam, height, width, frame::colormap::RGB);
|
||||||
|
|
||||||
|
std::string filename = "output/fast/render_" + view.name + ".bmp";
|
||||||
|
BMPWriter::saveBMP(filename, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& view : views) {
|
||||||
|
std::cout << "\nRendering view from " << view.name << " direction (Medium 60s Pass)..." << std::endl;
|
||||||
|
|
||||||
|
Camera cam;
|
||||||
|
cam.origin = view.origin;
|
||||||
|
cam.direction = (target - view.origin).normalized();
|
||||||
|
cam.up = view.up;
|
||||||
|
|
||||||
|
frame out = octree.renderFrameTimed(cam, height, width, frame::colormap::RGB, 60, bounces, false, true);
|
||||||
|
|
||||||
|
std::string filename = "output/medium/render_" + view.name + ".bmp";
|
||||||
|
BMPWriter::saveBMP(filename, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& view : views) {
|
||||||
|
std::cout << "\nRendering view from " << view.name << " direction (Slow 400 Samples Pass)..." << std::endl;
|
||||||
|
|
||||||
|
Camera cam;
|
||||||
|
cam.origin = view.origin;
|
||||||
|
cam.direction = (target - view.origin).normalized();
|
||||||
|
cam.up = view.up;
|
||||||
|
|
||||||
|
frame out = octree.renderFrame(cam, height, width, frame::colormap::RGB, samples, bounces, false, true);
|
||||||
|
|
||||||
|
std::string filename = "output/slow/render_" + view.name + ".bmp";
|
||||||
|
BMPWriter::saveBMP(filename, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\nAll renders complete!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
584
tests/planet.cpp
Normal file
584
tests/planet.cpp
Normal file
@@ -0,0 +1,584 @@
|
|||||||
|
#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 = 1024;
|
||||||
|
int outHeight = 1024;
|
||||||
|
float fps = 60;
|
||||||
|
int rayCount = 3;
|
||||||
|
int reflectCount = 4;
|
||||||
|
bool slowRender = false;
|
||||||
|
float lodDist = 1024.0f;
|
||||||
|
float lodDropoff = 0.001f;
|
||||||
|
float maxViewDistance = 4096;
|
||||||
|
bool globalIllumination = false;
|
||||||
|
bool useLod = true;
|
||||||
|
std::map<int, bool> keyStates;
|
||||||
|
float deltaTime = 0.16f;
|
||||||
|
bool orbitEquator = false;
|
||||||
|
bool collapsed;
|
||||||
|
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;
|
||||||
|
std::chrono::steady_clock::time_point lastStatsUpdate;
|
||||||
|
std::string cachedStats;
|
||||||
|
bool statsNeedUpdate = true;
|
||||||
|
float framerate = 60.0;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (mapTexture != 0) {
|
||||||
|
glDeleteTextures(1, &mapTexture);
|
||||||
|
}
|
||||||
|
sim.grid.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderUI(GLFWwindow* window) {
|
||||||
|
handleCameraControls(window);
|
||||||
|
collapsed = 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 * ω;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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();
|
||||||
|
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")) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Tectonic Simulation")) {
|
||||||
|
ImGui::DragInt("Num Plates", &sim.config.numPlates, 1, 1, 100);
|
||||||
|
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("Celestial Bodies")) {
|
||||||
|
///TODO: add controls for moon, star.
|
||||||
|
if (ImGui::Button("Add Star", ImVec2(-1, 40))) {
|
||||||
|
sim.addStar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
ImGui::DragFloat("Rotation Speed", &cam.rotationSpeed, M_1_PI, M_1_PI, M_PI);
|
||||||
|
ImGui::InputFloat("Rotation Distance", &rotationRadius, 10, 100);
|
||||||
|
ImGui::InputFloat("Max Framerate", &framerate, 1, 10);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatsCache();
|
||||||
|
ImGui::TextUnformatted(cachedStats.c_str());
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderPreviewPanel() {
|
||||||
|
ImGui::BeginChild("PreviewChild", ImVec2(0, 0), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
|
if (!collapsed) {
|
||||||
|
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::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;
|
||||||
|
}
|
||||||
|
int snf = 0;
|
||||||
|
int inf = 0;
|
||||||
|
|
||||||
|
for (auto& p : sim.config.surfaceNodes) {
|
||||||
|
v3 color = p.originColor.cast<float>();
|
||||||
|
|
||||||
|
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.cast<float>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sim.grid.setColor(p.currentPos, color)) {
|
||||||
|
snf++;
|
||||||
|
}
|
||||||
|
// 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.cast<float>();
|
||||||
|
|
||||||
|
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.cast<float>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sim.grid.setColor(p.currentPos, color)) {
|
||||||
|
inf++;
|
||||||
|
}
|
||||||
|
// sim.grid.update(p.currentPos, p.currentPos, p, true, color, sim.config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
if (snf > 0 || inf > 0) {
|
||||||
|
std::cout << snf << " original nodes failed to set" << std::endl;
|
||||||
|
std::cout << inf << " interpolated nodes failed to set" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.altPos->originalPos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::NOISE:
|
||||||
|
pos = p.altPos->noisePos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::TECTONIC:
|
||||||
|
pos = p.altPos->tectonicPos.cast<float>();
|
||||||
|
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.altPos->originalPos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::NOISE:
|
||||||
|
pos = p.altPos->noisePos.cast<float>();
|
||||||
|
break;
|
||||||
|
case DebugMapMode::TECTONIC:
|
||||||
|
pos = p.altPos->tectonicPos.cast<float>();
|
||||||
|
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.altPos->originalPos.cast<float>().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) {
|
||||||
|
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);
|
||||||
|
applyDebugColorMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void livePreview() {
|
||||||
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
|
updatePreview = true;
|
||||||
|
|
||||||
|
sim.grid.setLODMinDistance(lodDist);
|
||||||
|
sim.grid.setLODFalloff(lodDropoff);
|
||||||
|
sim.grid.setMaxDistance(maxViewDistance);
|
||||||
|
float invFrameRate = 1 / framerate;
|
||||||
|
|
||||||
|
if (slowRender) {
|
||||||
|
currentPreviewFrame = sim.grid.renderFrameTimed(cam, outHeight, outWidth, frame::colormap::RGB, invFrameRate, reflectCount, globalIllumination, useLod);
|
||||||
|
} else {
|
||||||
|
currentPreviewFrame = sim.grid.fastRenderFrame(cam, outHeight, outWidth, 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() {
|
||||||
|
|
||||||
|
currentColorMode = DebugColorMode::PLATES;
|
||||||
|
|
||||||
|
sim.assignSeeds();
|
||||||
|
sim.buildAdjacencyList();
|
||||||
|
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();
|
||||||
|
applyDebugColorMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillPlanet() {
|
||||||
|
sim.fillPlanet();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStatsCache() {
|
||||||
|
std::stringstream gridstats;
|
||||||
|
sim.grid.printStats(gridstats);
|
||||||
|
cachedStats = gridstats.str();
|
||||||
|
lastStatsUpdate = std::chrono::steady_clock::now();
|
||||||
|
statsNeedUpdate = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
136
tests/ptest.cpp
Normal file
136
tests/ptest.cpp
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_glfw.h"
|
||||||
|
#include "imgui_impl_opengl3.h"
|
||||||
|
|
||||||
|
#include "../util/noise/pnoise.cpp"
|
||||||
|
#include "planet.cpp"
|
||||||
|
#include "worldbox.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;
|
||||||
|
worldboxSimUI worldBox;
|
||||||
|
|
||||||
|
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);
|
||||||
|
worldBox.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;
|
||||||
|
}
|
||||||
347
tests/worldbox.cpp
Normal file
347
tests/worldbox.cpp
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
#ifndef WORLDBOX_CPP
|
||||||
|
#define WORLDBOX_CPP
|
||||||
|
|
||||||
|
#include "../util/sim/worldbox.hpp"
|
||||||
|
#include "../util/grid/camera.hpp"
|
||||||
|
|
||||||
|
#include "../imgui/imgui.h"
|
||||||
|
#include "../imgui/backends/imgui_impl_glfw.h"
|
||||||
|
#include "../imgui/backends/imgui_impl_opengl3.h"
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
class worldboxSimUI {
|
||||||
|
private:
|
||||||
|
worldboxsim sim;
|
||||||
|
Camera cam;
|
||||||
|
|
||||||
|
// UI and Render State
|
||||||
|
GLuint textu = 0;
|
||||||
|
std::mutex PreviewMutex;
|
||||||
|
bool updatePreview = false;
|
||||||
|
bool textureInitialized = false;
|
||||||
|
frame currentPreviewFrame;
|
||||||
|
|
||||||
|
// Rendering Settings
|
||||||
|
int outWidth = 1024;
|
||||||
|
int outHeight = 1024;
|
||||||
|
int reflectCount = 2;
|
||||||
|
bool slowRender = false;
|
||||||
|
float lodDist = 1024.0f;
|
||||||
|
float lodDropoff = 0.05f;
|
||||||
|
float maxViewDistance = 4096.0f;
|
||||||
|
bool globalIllumination = false;
|
||||||
|
bool useLod = true;
|
||||||
|
float framerate = 60.0f;
|
||||||
|
|
||||||
|
// Input/Time
|
||||||
|
std::map<int, bool> keyStates;
|
||||||
|
std::chrono::steady_clock::time_point lastFrameTime;
|
||||||
|
float deltaTime = 0.016f;
|
||||||
|
|
||||||
|
// Stats tracking
|
||||||
|
std::chrono::steady_clock::time_point lastStatsUpdate;
|
||||||
|
std::string cachedStats;
|
||||||
|
bool statsNeedUpdate = true;
|
||||||
|
|
||||||
|
enum class DebugColorMode {
|
||||||
|
BASE,
|
||||||
|
NUTRIENTS,
|
||||||
|
MOISTURE
|
||||||
|
};
|
||||||
|
DebugColorMode currentColorMode = DebugColorMode::BASE;
|
||||||
|
|
||||||
|
public:
|
||||||
|
worldboxSimUI() {
|
||||||
|
// Position camera to look at the center of the world slightly from above
|
||||||
|
cam.origin = v3(0, 50, 80);
|
||||||
|
v3 target = v3(0, 0, 0);
|
||||||
|
cam.direction = (target - cam.origin).normalized();
|
||||||
|
cam.up = v3(0, 1, 0);
|
||||||
|
cam.fov = 60;
|
||||||
|
cam.rotationSpeed = M_1_PI;
|
||||||
|
cam.movementSpeed = 50.0f;
|
||||||
|
lastFrameTime = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
~worldboxSimUI() {
|
||||||
|
if (textu != 0) {
|
||||||
|
glDeleteTextures(1, &textu);
|
||||||
|
}
|
||||||
|
sim.grid.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderUI(GLFWwindow* window) {
|
||||||
|
// Compute delta time for consistent movement and sim stepping
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
deltaTime = std::chrono::duration<float>(now - lastFrameTime).count();
|
||||||
|
lastFrameTime = now;
|
||||||
|
|
||||||
|
handleCameraControls(window);
|
||||||
|
|
||||||
|
// Update simulation objects like the Star
|
||||||
|
sim.updateStar(deltaTime);
|
||||||
|
sim.updateWeatherAndPhysics(deltaTime);
|
||||||
|
|
||||||
|
ImGui::Begin("WorldBox 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) {
|
||||||
|
glfwPollEvents();
|
||||||
|
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
|
||||||
|
keyStates[i] = (glfwGetKey(window, i) == GLFW_PRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentSpeed = cam.movementSpeed * deltaTime;
|
||||||
|
if (keyStates[GLFW_KEY_LEFT_SHIFT]) currentSpeed *= 3.0f; // Sprint
|
||||||
|
|
||||||
|
if (keyStates[GLFW_KEY_W]) cam.moveForward(currentSpeed);
|
||||||
|
if (keyStates[GLFW_KEY_S]) cam.moveBackward(currentSpeed);
|
||||||
|
if (keyStates[GLFW_KEY_A]) cam.moveLeft(currentSpeed);
|
||||||
|
if (keyStates[GLFW_KEY_D]) cam.moveRight(currentSpeed);
|
||||||
|
if (keyStates[GLFW_KEY_E]) cam.moveUp(currentSpeed);
|
||||||
|
if (keyStates[GLFW_KEY_Q]) cam.moveDown(currentSpeed);
|
||||||
|
|
||||||
|
if (keyStates[GLFW_KEY_LEFT]) cam.rotateYaw(cam.rotationSpeed * deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_RIGHT]) cam.rotateYaw(-cam.rotationSpeed * deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_UP]) cam.rotatePitch(cam.rotationSpeed * deltaTime);
|
||||||
|
if (keyStates[GLFW_KEY_DOWN]) cam.rotatePitch(-cam.rotationSpeed * deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderControlsPanel() {
|
||||||
|
ImGui::BeginChild("ControlsScroll", ImVec2(0, 0), true);
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("World Generation", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
ImGui::DragFloat("Width (X)", &sim.config.worldSizeX, 1.0f, 10.0f, 2000.0f);
|
||||||
|
ImGui::DragFloat("Length (Z)", &sim.config.worldSizeZ, 1.0f, 10.0f, 2000.0f);
|
||||||
|
ImGui::DragFloat("Depth (Y)", &sim.config.worldDepth, 1.0f, 1.0f, 500.0f);
|
||||||
|
ImGui::DragFloat("Voxel Size", &sim.config.voxelSize, 0.1f, 0.1f, 10.0f);
|
||||||
|
|
||||||
|
ImGui::ColorEdit3("Dirt Base Color", sim.config.baseDirtColor.data());
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button("Generate Flat World", ImVec2(-1, 40))) {
|
||||||
|
sim.generateFlatWorld();
|
||||||
|
// applyDebugColorMode();
|
||||||
|
statsNeedUpdate = true;
|
||||||
|
}
|
||||||
|
if (ImGui::Button("Clear World", ImVec2(-1, 30))) {
|
||||||
|
sim.clearWorld();
|
||||||
|
statsNeedUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Weather & Physics", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
ImGui::Checkbox("Enable Gravity (Terrain)", &sim.config.enableGravity);
|
||||||
|
ImGui::DragFloat3("Gravity", sim.config.gravity.data());
|
||||||
|
ImGui::DragFloat3("Wind", sim.config.wind.data());
|
||||||
|
ImGui::DragFloat("Physics Step (sec)", &sim.config.physicsStep, 0.01f, 0.01f, 1.0f);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Clouds & Rain");
|
||||||
|
ImGui::DragInt("Cloud Count", &sim.config.cloudCount, 1, 0, 100);
|
||||||
|
ImGui::DragFloat("Cloud Height", &sim.config.cloudHeight, 5.0f, 10.0f, 1000.0f);
|
||||||
|
ImGui::DragFloat("Rain Spawn Rate", &sim.config.rainSpawnRate, 0.1f, 0.0f, 50.0f);
|
||||||
|
ImGui::ColorEdit3("Cloud Color", sim.config.cloudColor.data());
|
||||||
|
ImGui::ColorEdit3("Rain Color", sim.config.rainColor.data());
|
||||||
|
|
||||||
|
if (ImGui::Button("Generate Clouds", ImVec2(-1, 40))) {
|
||||||
|
sim.generateClouds();
|
||||||
|
applyDebugColorMode();
|
||||||
|
statsNeedUpdate = true;
|
||||||
|
}
|
||||||
|
if (ImGui::Button("Clear Weather", ImVec2(-1, 30))) {
|
||||||
|
for (auto& c : sim.clouds) sim.grid.remove(c.pos);
|
||||||
|
for (auto& r : sim.rainDrops) sim.grid.remove(r.pos);
|
||||||
|
sim.clouds.clear();
|
||||||
|
sim.rainDrops.clear();
|
||||||
|
statsNeedUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Environment & Celestial", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
ImGui::Text("Star Settings");
|
||||||
|
ImGui::Checkbox("Enable Star Rotation", &sim.config.enableStarRotation);
|
||||||
|
ImGui::DragFloat("Orbit Radius", &sim.config.starOrbitRadius, 10.0f, 100.0f, 5000.0f);
|
||||||
|
ImGui::DragFloat("Star Speed", &sim.config.starSpeed, 0.01f, 0.0f, 5.0f);
|
||||||
|
ImGui::DragFloat("Panel Size", &sim.config.starPanelSize, 5.0f, 10.0f, 1000.0f);
|
||||||
|
ImGui::ColorEdit3("Star Color", sim.config.starColor.data());
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Grass Settings");
|
||||||
|
ImGui::SliderFloat("Grass Density", &sim.config.grassDensity, 0.0f, 1.0f);
|
||||||
|
ImGui::ColorEdit3("Grass Color Base", sim.config.grassColorBase.data());
|
||||||
|
|
||||||
|
if (ImGui::Button("Generate Grass", ImVec2(-1, 40))) {
|
||||||
|
sim.generateGrass();
|
||||||
|
applyDebugColorMode();
|
||||||
|
statsNeedUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Debug Views")) {
|
||||||
|
ImGui::Text("Render Data Mode:");
|
||||||
|
bool colorChanged = false;
|
||||||
|
if (ImGui::RadioButton("Base Color", currentColorMode == DebugColorMode::BASE)) {
|
||||||
|
currentColorMode = DebugColorMode::BASE;
|
||||||
|
colorChanged = true;
|
||||||
|
}
|
||||||
|
if (ImGui::RadioButton("Nutrients", currentColorMode == DebugColorMode::NUTRIENTS)) {
|
||||||
|
currentColorMode = DebugColorMode::NUTRIENTS;
|
||||||
|
colorChanged = true;
|
||||||
|
}
|
||||||
|
if (ImGui::RadioButton("Moisture", currentColorMode == DebugColorMode::MOISTURE)) {
|
||||||
|
currentColorMode = DebugColorMode::MOISTURE;
|
||||||
|
colorChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorChanged) {
|
||||||
|
applyDebugColorMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Camera & Render Settings", 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);
|
||||||
|
ImGui::InputFloat("Max Framerate", &framerate, 1, 10);
|
||||||
|
|
||||||
|
ImGui::Checkbox("Use PBR/Raytracing (Slow)", &slowRender);
|
||||||
|
if(slowRender) {
|
||||||
|
ImGui::DragInt("Bounces", &reflectCount, 1, 0, 10);
|
||||||
|
ImGui::Checkbox("Global Illumination", &globalIllumination);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Checkbox("Use LODs", &useLod);
|
||||||
|
|
||||||
|
if (ImGui::Button("Reset Camera")) {
|
||||||
|
cam.origin = v3(0, sim.config.worldDepth * 2.0f, std::max(sim.config.worldSizeX, sim.config.worldSizeZ));
|
||||||
|
cam.direction = (v3(0, 0, 0) - cam.origin).normalized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatsCache();
|
||||||
|
ImGui::TextUnformatted(cachedStats.c_str());
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderPreviewPanel() {
|
||||||
|
ImGui::BeginChild("PreviewChild", ImVec2(0, 0), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
|
|
||||||
|
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::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyDebugColorMode() {
|
||||||
|
if (sim.grid.empty()) return;
|
||||||
|
|
||||||
|
v3 boundsHalfExtent = v3(sim.config.worldSizeX, sim.config.worldDepth, sim.config.worldSizeZ);
|
||||||
|
float searchRadius = boundsHalfExtent.norm() * 2.0f;
|
||||||
|
|
||||||
|
auto allNodes = sim.grid.findInRadius(v3(0,0,0), searchRadius);
|
||||||
|
|
||||||
|
for (auto& p : allNodes) {
|
||||||
|
if (!p || !p->active) continue;
|
||||||
|
|
||||||
|
v3 color = sim.config.baseDirtColor;
|
||||||
|
|
||||||
|
switch (currentColorMode) {
|
||||||
|
case DebugColorMode::NUTRIENTS: {
|
||||||
|
float t = std::clamp(p->data.nutrients, 0.0f, 1.0f);
|
||||||
|
color = v3(1.0f - t, t, 0.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DebugColorMode::MOISTURE: {
|
||||||
|
float t = std::clamp(p->data.moisture, 0.0f, 1.0f);
|
||||||
|
color = v3(1.0f - t, 1.0f - t, 1.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DebugColorMode::BASE:
|
||||||
|
default:
|
||||||
|
if (p->data.type == 0) {
|
||||||
|
v3 darkDirt = sim.config.baseDirtColor * 0.4f;
|
||||||
|
color = sim.config.baseDirtColor * (1.0f - p->data.moisture) + darkDirt * p->data.moisture;
|
||||||
|
} else if (p->data.type == 1) {
|
||||||
|
color = sim.config.baseRockColor;
|
||||||
|
} else if (p->data.type == 2) {
|
||||||
|
v3 lushGrass = sim.config.grassColorBase * 1.5f;
|
||||||
|
color = sim.config.grassColorBase * (1.0f - p->data.moisture) + lushGrass * p->data.moisture;
|
||||||
|
} else if (p->data.type == 3) {
|
||||||
|
color = sim.config.starColor;
|
||||||
|
} else if (p->data.type == 4) {
|
||||||
|
color = sim.config.cloudColor;
|
||||||
|
} else if (p->data.type == 5) {
|
||||||
|
color = sim.config.rainColor;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sim.grid.setColor(p->position, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void livePreview() {
|
||||||
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
|
updatePreview = true;
|
||||||
|
|
||||||
|
float invFrameRate = 1.0f / framerate;
|
||||||
|
if (!useLod) {
|
||||||
|
sim.grid.setLODFalloff(0.01);
|
||||||
|
sim.grid.setLODMinDistance(1000.0f);
|
||||||
|
} else {
|
||||||
|
sim.grid.setLODMinDistance(lodDist);
|
||||||
|
sim.grid.setLODFalloff(lodDropoff);
|
||||||
|
}
|
||||||
|
sim.grid.setMaxDistance(maxViewDistance);
|
||||||
|
|
||||||
|
if (slowRender) {
|
||||||
|
currentPreviewFrame = sim.grid.renderFrameTimed(cam, outHeight, outWidth, frame::colormap::RGB, invFrameRate, reflectCount, globalIllumination, useLod);
|
||||||
|
} else {
|
||||||
|
currentPreviewFrame = sim.grid.fastRenderFrame(cam, outHeight, outWidth, 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 updateStatsCache() {
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
if (statsNeedUpdate || std::chrono::duration_cast<std::chrono::seconds>(now - lastStatsUpdate).count() >= 2) {
|
||||||
|
std::stringstream gridstats;
|
||||||
|
sim.grid.printStats(gridstats);
|
||||||
|
cachedStats = gridstats.str();
|
||||||
|
lastStatsUpdate = now;
|
||||||
|
statsNeedUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -27,42 +27,24 @@ constexpr int Dim2 = 2;
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
class Grid2 {
|
class Grid2 {
|
||||||
public:
|
public:
|
||||||
using PointType = Eigen::Matrix<float, Dim2, 1>; // Eigen::Vector2f
|
using PointType = Eigen::Matrix<float, Dim2, 1>;
|
||||||
using BoundingBox = std::pair<PointType, PointType>;
|
using BoundingBox = std::pair<PointType, PointType>;
|
||||||
|
|
||||||
// Shape for 2D is usually a Circle or a Square (AABB)
|
|
||||||
enum class Shape {
|
|
||||||
CIRCLE,
|
|
||||||
SQUARE
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NodeData {
|
struct NodeData {
|
||||||
T data;
|
T data;
|
||||||
PointType position;
|
PointType position;
|
||||||
int objectId;
|
int objectId;
|
||||||
bool active;
|
bool active;
|
||||||
bool visible;
|
bool visible;
|
||||||
float size; // Radius or half-width
|
float size;
|
||||||
Eigen::Vector4f color; // RGBA
|
Eigen::Vector4f color;
|
||||||
|
|
||||||
// Physics properties
|
|
||||||
float temperature;
|
|
||||||
float conductivity;
|
|
||||||
float specific_heat;
|
|
||||||
float density;
|
|
||||||
float next_temperature; // For double-buffering simulation
|
|
||||||
|
|
||||||
Shape shape;
|
|
||||||
|
|
||||||
NodeData(const T& data, const PointType& pos, bool visible, Eigen::Vector4f color, float size = 1.0f,
|
NodeData(const T& data, const PointType& pos, bool visible, Eigen::Vector4f color, float size = 1.0f,
|
||||||
bool active = true, int objectId = -1, Shape shape = Shape::SQUARE)
|
bool active = true, int objectId = -1, Shape shape = Shape::SQUARE)
|
||||||
: data(data), position(pos), objectId(objectId), active(active), visible(visible),
|
: data(data), position(pos), objectId(objectId), active(active), visible(visible),
|
||||||
color(color), size(size), shape(shape),
|
color(color), size(size) {}
|
||||||
temperature(0.0f), conductivity(1.0f), specific_heat(1.0f), density(1.0f), next_temperature(0.0f) {}
|
|
||||||
|
|
||||||
NodeData() : objectId(-1), active(false), visible(false), size(0.0f),
|
NodeData() : objectId(-1), active(false), visible(false), size(0.0f), color(0,0,0,0) {}
|
||||||
color(0,0,0,0), shape(Shape::SQUARE),
|
|
||||||
temperature(0.0f), conductivity(1.0f), specific_heat(1.0f), density(1.0f), next_temperature(0.0f) {}
|
|
||||||
|
|
||||||
// Helper for Square bounds
|
// Helper for Square bounds
|
||||||
BoundingBox getSquareBounds() const {
|
BoundingBox getSquareBounds() const {
|
||||||
@@ -74,7 +56,7 @@ public:
|
|||||||
struct QuadNode {
|
struct QuadNode {
|
||||||
BoundingBox bounds;
|
BoundingBox bounds;
|
||||||
std::vector<std::shared_ptr<NodeData>> points;
|
std::vector<std::shared_ptr<NodeData>> points;
|
||||||
std::array<std::unique_ptr<QuadNode>, 4> children; // 4 quadrants
|
std::array<std::unique_ptr<QuadNode>, 4> children;
|
||||||
PointType center;
|
PointType center;
|
||||||
bool isLeaf;
|
bool isLeaf;
|
||||||
|
|
||||||
@@ -106,35 +88,19 @@ private:
|
|||||||
Eigen::Vector4f backgroundColor_ = {0.0f, 0.0f, 0.0f, 0.0f};
|
Eigen::Vector4f backgroundColor_ = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
PNoise2 noisegen;
|
PNoise2 noisegen;
|
||||||
|
|
||||||
// Determine quadrant: 0:SW, 1:SE, 2:NW, 3:NE
|
|
||||||
uint8_t getQuadrant(const PointType& point, const PointType& center) const {
|
uint8_t getQuadrant(const PointType& point, const PointType& center) const {
|
||||||
uint8_t quad = 0;
|
return (point.x() >= center.x()) | ((point.y() >= center.y()) << 1);
|
||||||
if (point.x() >= center.x()) quad |= 1; // Right
|
|
||||||
if (point.y() >= center.y()) quad |= 2; // Top
|
|
||||||
return quad;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBox createChildBounds(const QuadNode* node, uint8_t quad) const {
|
BoundingBox createChildBounds(const QuadNode* node, uint8_t quad) const {
|
||||||
PointType childMin, childMax;
|
PointType childMin, childMax;
|
||||||
PointType center = node->center;
|
PointType center = node->center;
|
||||||
|
|
||||||
// X axis
|
childMin[0] = (quad & 1) ? center[0] : node->bounds.first[0];
|
||||||
if (quad & 1) { // Right
|
childMax[0] = (quad & 1) ? node->bounds.second[0] : center[0];
|
||||||
childMin.x() = center.x();
|
|
||||||
childMax.x() = node->bounds.second.x();
|
|
||||||
} else { // Left
|
|
||||||
childMin.x() = node->bounds.first.x();
|
|
||||||
childMax.x() = center.x();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Y axis
|
childMin[1] = (quad & 2) ? center[1] : node->bounds.first[1];
|
||||||
if (quad & 2) { // Top
|
childMax[1] = (quad & 2) ? node->bounds.second[1] : center[1];
|
||||||
childMin.y() = center.y();
|
|
||||||
childMax.y() = node->bounds.second.y();
|
|
||||||
} else { // Bottom
|
|
||||||
childMin.y() = node->bounds.first.y();
|
|
||||||
childMax.y() = center.y();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {childMin, childMax};
|
return {childMin, childMax};
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
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 <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>
|
||||||
@@ -186,19 +189,101 @@ inline void updateNoiseTexture(NoisePreviewState& state) {
|
|||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, state.textureId);
|
glBindTexture(GL_TEXTURE_2D, state.textureId);
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, state.width, state.height,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, state.width, state.height, 0, GL_RGB, GL_UNSIGNED_BYTE, state.pixelBuffer.data());
|
||||||
0, GL_RGB, GL_UNSIGNED_BYTE, state.pixelBuffer.data());
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
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 +339,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);
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
383
util/sim/elementcontent.hpp
Normal file
383
util/sim/elementcontent.hpp
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
#ifndef ELEMENTS
|
||||||
|
#define ELEMENTS
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
struct BaseElementProps {
|
||||||
|
float density; // kg/m^3
|
||||||
|
float meltingPoint; // Kelvin
|
||||||
|
float boilingPoint; // Kelvin
|
||||||
|
float specificHeat; // J/(kg*K)
|
||||||
|
float electronegativity; // Pauling scale
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::array<BaseElementProps, 118> ELEMENT_DB = {{
|
||||||
|
// 1: Hydrogen
|
||||||
|
{0.08988f, 14.01f, 20.28f, 14304.0f, 2.20f},
|
||||||
|
// 2: Helium
|
||||||
|
{0.1785f, 0.95f, 4.22f, 5193.0f, 0.0f}, // No electronegativity, using 0
|
||||||
|
// 3: Lithium
|
||||||
|
{534.0f, 453.69f, 1560.0f, 3582.0f, 0.98f},
|
||||||
|
// 4: Beryllium
|
||||||
|
{1850.0f, 1560.0f, 2742.0f, 1825.0f, 1.57f},
|
||||||
|
// 5: Boron
|
||||||
|
{2340.0f, 2349.0f, 4200.0f, 1026.0f, 2.04f},
|
||||||
|
// 6: Carbon
|
||||||
|
{2267.0f, 4000.0f, 4300.0f, 709.0f, 2.55f},
|
||||||
|
// 7: Nitrogen
|
||||||
|
{1.2506f, 63.15f, 77.36f, 1040.0f, 3.04f},
|
||||||
|
// 8: Oxygen
|
||||||
|
{1.429f, 54.36f, 90.2f, 918.0f, 3.44f},
|
||||||
|
// 9: Fluorine
|
||||||
|
{1.696f, 53.53f, 85.03f, 824.0f, 3.98f},
|
||||||
|
// 10: Neon
|
||||||
|
{0.9002f, 24.56f, 27.07f, 1030.0f, 0.0f}, // No electronegativity
|
||||||
|
// 11: Sodium
|
||||||
|
{968.0f, 370.87f, 1156.0f, 1228.0f, 0.93f},
|
||||||
|
// 12: Magnesium
|
||||||
|
{1738.0f, 923.0f, 1363.0f, 1023.0f, 1.31f},
|
||||||
|
// 13: Aluminium
|
||||||
|
{2700.0f, 933.47f, 2792.0f, 897.0f, 1.61f},
|
||||||
|
// 14: Silicon
|
||||||
|
{2329.0f, 1687.0f, 3538.0f, 705.0f, 1.9f},
|
||||||
|
// 15: Phosphorus
|
||||||
|
{1823.0f, 317.3f, 550.0f, 769.0f, 2.19f},
|
||||||
|
// 16: Sulfur
|
||||||
|
{2070.0f, 388.36f, 717.87f, 710.0f, 2.58f},
|
||||||
|
// 17: Chlorine
|
||||||
|
{3.2f, 171.6f, 239.11f, 479.0f, 3.16f},
|
||||||
|
// 18: Argon
|
||||||
|
{1.784f, 83.8f, 87.3f, 520.0f, 0.0f}, // No electronegativity
|
||||||
|
// 19: Potassium
|
||||||
|
{890.0f, 336.53f, 1032.0f, 757.0f, 0.82f},
|
||||||
|
// 20: Calcium
|
||||||
|
{1550.0f, 1115.0f, 1757.0f, 647.0f, 1.0f},
|
||||||
|
// 21: Scandium
|
||||||
|
{2985.0f, 1814.0f, 3109.0f, 568.0f, 1.36f},
|
||||||
|
// 22: Titanium
|
||||||
|
{4506.0f, 1941.0f, 3560.0f, 523.0f, 1.54f},
|
||||||
|
// 23: Vanadium
|
||||||
|
{6110.0f, 2183.0f, 3680.0f, 489.0f, 1.63f},
|
||||||
|
// 24: Chromium
|
||||||
|
{7150.0f, 2180.0f, 2944.0f, 449.0f, 1.66f},
|
||||||
|
// 25: Manganese
|
||||||
|
{7210.0f, 1519.0f, 2334.0f, 479.0f, 1.55f},
|
||||||
|
// 26: Iron
|
||||||
|
{7874.0f, 1811.0f, 3134.0f, 449.0f, 1.83f},
|
||||||
|
// 27: Cobalt
|
||||||
|
{8900.0f, 1768.0f, 3200.0f, 421.0f, 1.88f},
|
||||||
|
// 28: Nickel
|
||||||
|
{8908.0f, 1728.0f, 3186.0f, 444.0f, 1.91f},
|
||||||
|
// 29: Copper
|
||||||
|
{8960.0f, 1357.77f, 2835.0f, 385.0f, 1.9f},
|
||||||
|
// 30: Zinc
|
||||||
|
{7140.0f, 692.88f, 1180.0f, 388.0f, 1.65f},
|
||||||
|
// 31: Gallium
|
||||||
|
{5910.0f, 302.9146f, 2673.0f, 371.0f, 1.81f},
|
||||||
|
// 32: Germanium
|
||||||
|
{5323.0f, 1211.4f, 3106.0f, 320.0f, 2.01f},
|
||||||
|
// 33: Arsenic
|
||||||
|
{5727.0f, 1090.0f, 887.0f, 329.0f, 2.18f}, // Sublimes at 887K
|
||||||
|
// 34: Selenium
|
||||||
|
{4810.0f, 453.0f, 958.0f, 321.0f, 2.55f},
|
||||||
|
// 35: Bromine
|
||||||
|
{3102.8f, 265.8f, 332.0f, 474.0f, 2.96f},
|
||||||
|
// 36: Krypton
|
||||||
|
{3.749f, 115.79f, 119.93f, 248.0f, 3.0f},
|
||||||
|
// 37: Rubidium
|
||||||
|
{1532.0f, 312.46f, 961.0f, 363.0f, 0.82f},
|
||||||
|
// 38: Strontium
|
||||||
|
{2640.0f, 1050.0f, 1655.0f, 301.0f, 0.95f},
|
||||||
|
// 39: Yttrium
|
||||||
|
{4472.0f, 1799.0f, 3609.0f, 298.0f, 1.22f},
|
||||||
|
// 40: Zirconium
|
||||||
|
{6520.0f, 2128.0f, 4682.0f, 278.0f, 1.33f},
|
||||||
|
// 41: Niobium
|
||||||
|
{8570.0f, 2750.0f, 5017.0f, 265.0f, 1.6f},
|
||||||
|
// 42: Molybdenum
|
||||||
|
{10280.0f, 2896.0f, 4912.0f, 251.0f, 2.16f},
|
||||||
|
// 43: Technetium
|
||||||
|
{11000.0f, 2430.0f, 4538.0f, 0.0f, 1.9f}, // No specific heat data
|
||||||
|
// 44: Ruthenium
|
||||||
|
{12450.0f, 2607.0f, 4423.0f, 238.0f, 2.2f},
|
||||||
|
// 45: Rhodium
|
||||||
|
{12410.0f, 2237.0f, 3968.0f, 243.0f, 2.28f},
|
||||||
|
// 46: Palladium
|
||||||
|
{12023.0f, 1828.05f, 3236.0f, 244.0f, 2.2f},
|
||||||
|
// 47: Silver
|
||||||
|
{10490.0f, 1234.93f, 2435.0f, 235.0f, 1.93f},
|
||||||
|
// 48: Cadmium
|
||||||
|
{8650.0f, 594.22f, 1040.0f, 232.0f, 1.69f},
|
||||||
|
// 49: Indium
|
||||||
|
{7310.0f, 429.75f, 2345.0f, 233.0f, 1.78f},
|
||||||
|
// 50: Tin
|
||||||
|
{7265.0f, 505.08f, 2875.0f, 228.0f, 1.96f},
|
||||||
|
// 51: Antimony
|
||||||
|
{6697.0f, 903.78f, 1860.0f, 207.0f, 2.05f},
|
||||||
|
// 52: Tellurium
|
||||||
|
{6240.0f, 722.66f, 1261.0f, 202.0f, 2.1f},
|
||||||
|
// 53: Iodine
|
||||||
|
{4933.0f, 386.85f, 457.4f, 214.0f, 2.66f},
|
||||||
|
// 54: Xenon
|
||||||
|
{5.894f, 161.4f, 165.03f, 158.0f, 2.6f},
|
||||||
|
// 55: Caesium
|
||||||
|
{1930.0f, 301.59f, 944.0f, 242.0f, 0.79f},
|
||||||
|
// 56: Barium
|
||||||
|
{3510.0f, 1000.0f, 2170.0f, 204.0f, 0.89f},
|
||||||
|
// 57: Lanthanum
|
||||||
|
{6162.0f, 1193.0f, 3737.0f, 195.0f, 1.1f},
|
||||||
|
// 58: Cerium
|
||||||
|
{6770.0f, 1068.0f, 3716.0f, 192.0f, 1.12f},
|
||||||
|
// 59: Praseodymium
|
||||||
|
{6770.0f, 1208.0f, 3793.0f, 193.0f, 1.13f},
|
||||||
|
// 60: Neodymium
|
||||||
|
{7010.0f, 1297.0f, 3347.0f, 190.0f, 1.14f},
|
||||||
|
// 61: Promethium
|
||||||
|
{7260.0f, 1315.0f, 3273.0f, 0.0f, 1.13f}, // No specific heat data
|
||||||
|
// 62: Samarium
|
||||||
|
{7520.0f, 1345.0f, 2067.0f, 197.0f, 1.17f},
|
||||||
|
// 63: Europium
|
||||||
|
{5244.0f, 1099.0f, 1802.0f, 182.0f, 1.2f},
|
||||||
|
// 64: Gadolinium
|
||||||
|
{7900.0f, 1585.0f, 3546.0f, 236.0f, 1.2f},
|
||||||
|
// 65: Terbium
|
||||||
|
{8230.0f, 1629.0f, 3503.0f, 182.0f, 1.2f},
|
||||||
|
// 66: Dysprosium
|
||||||
|
{8540.0f, 1680.0f, 2840.0f, 170.0f, 1.22f},
|
||||||
|
// 67: Holmium
|
||||||
|
{8790.0f, 1734.0f, 2993.0f, 165.0f, 1.23f},
|
||||||
|
// 68: Erbium
|
||||||
|
{9066.0f, 1802.0f, 3141.0f, 168.0f, 1.24f},
|
||||||
|
// 69: Thulium
|
||||||
|
{9320.0f, 1818.0f, 2223.0f, 160.0f, 1.25f},
|
||||||
|
// 70: Ytterbium
|
||||||
|
{6900.0f, 1097.0f, 1469.0f, 155.0f, 1.1f},
|
||||||
|
// 71: Lutetium
|
||||||
|
{9841.0f, 1925.0f, 3675.0f, 154.0f, 1.27f},
|
||||||
|
// 72: Hafnium
|
||||||
|
{13310.0f, 2506.0f, 4876.0f, 144.0f, 1.3f},
|
||||||
|
// 73: Tantalum
|
||||||
|
{16690.0f, 3290.0f, 5731.0f, 140.0f, 1.5f},
|
||||||
|
// 74: Tungsten
|
||||||
|
{19250.0f, 3695.0f, 6203.0f, 132.0f, 2.36f},
|
||||||
|
// 75: Rhenium
|
||||||
|
{21020.0f, 3459.0f, 5869.0f, 137.0f, 1.9f},
|
||||||
|
// 76: Osmium
|
||||||
|
{22590.0f, 3306.0f, 5285.0f, 130.0f, 2.2f},
|
||||||
|
// 77: Iridium
|
||||||
|
{22560.0f, 2719.0f, 4701.0f, 131.0f, 2.2f},
|
||||||
|
// 78: Platinum
|
||||||
|
{21450.0f, 2041.4f, 4098.0f, 133.0f, 2.28f},
|
||||||
|
// 79: Gold
|
||||||
|
{19300.0f, 1337.33f, 3129.0f, 129.0f, 2.54f},
|
||||||
|
// 80: Mercury
|
||||||
|
{13534.0f, 234.43f, 629.88f, 140.0f, 2.0f},
|
||||||
|
// 81: Thallium
|
||||||
|
{11850.0f, 577.0f, 1746.0f, 129.0f, 1.62f},
|
||||||
|
// 82: Lead
|
||||||
|
{11340.0f, 600.61f, 2022.0f, 129.0f, 2.33f}, // Using 4+ value
|
||||||
|
// 83: Bismuth
|
||||||
|
{9780.0f, 544.7f, 1837.0f, 122.0f, 2.02f},
|
||||||
|
// 84: Polonium
|
||||||
|
{9196.0f, 527.0f, 1235.0f, 0.0f, 2.0f}, // No specific heat data
|
||||||
|
// 85: Astatine
|
||||||
|
{8930.0f, 575.0f, 610.0f, 0.0f, 2.2f}, // Approx density, no specific heat
|
||||||
|
// 86: Radon
|
||||||
|
{9.73f, 202.0f, 211.3f, 94.0f, 2.2f},
|
||||||
|
// 87: Francium
|
||||||
|
{2480.0f, 281.0f, 890.0f, 0.0f, 0.79f}, // Approx values
|
||||||
|
// 88: Radium
|
||||||
|
{5500.0f, 973.0f, 2010.0f, 94.0f, 0.9f},
|
||||||
|
// 89: Actinium
|
||||||
|
{10000.0f, 1323.0f, 3471.0f, 120.0f, 1.1f},
|
||||||
|
// 90: Thorium
|
||||||
|
{11700.0f, 2115.0f, 5061.0f, 113.0f, 1.3f},
|
||||||
|
// 91: Protactinium
|
||||||
|
{15370.0f, 1841.0f, 4300.0f, 0.0f, 1.5f}, // No specific heat data
|
||||||
|
// 92: Uranium
|
||||||
|
{19100.0f, 1405.3f, 4404.0f, 116.0f, 1.38f},
|
||||||
|
// 93: Neptunium
|
||||||
|
{20450.0f, 917.0f, 4273.0f, 0.0f, 1.36f}, // No specific heat data
|
||||||
|
// 94: Plutonium
|
||||||
|
{19850.0f, 912.5f, 3501.0f, 0.0f, 1.28f}, // No specific heat data
|
||||||
|
// 95: Americium
|
||||||
|
{12000.0f, 1449.0f, 2880.0f, 0.0f, 1.13f}, // No specific heat data
|
||||||
|
// 96: Curium
|
||||||
|
{13510.0f, 1613.0f, 3383.0f, 0.0f, 1.28f}, // No specific heat data
|
||||||
|
// 97: Berkelium
|
||||||
|
{14780.0f, 1259.0f, 2900.0f, 0.0f, 1.3f}, // No specific heat data
|
||||||
|
// 98: Californium
|
||||||
|
{15100.0f, 1173.0f, 1743.0f, 0.0f, 1.3f}, // No specific heat data
|
||||||
|
// 99: Einsteinium
|
||||||
|
{8840.0f, 1133.0f, 1269.0f, 0.0f, 1.3f}, // No specific heat data
|
||||||
|
// 100: Fermium
|
||||||
|
{9700.0f, 1125.0f, 1800.0f, 0.0f, 1.3f}, // Estimated values
|
||||||
|
// 101: Mendelevium
|
||||||
|
{10300.0f, 1100.0f, 0.0f, 0.0f, 1.3f}, // Estimated
|
||||||
|
// 102: Nobelium
|
||||||
|
{9900.0f, 1100.0f, 0.0f, 0.0f, 1.3f}, // Estimated
|
||||||
|
// 103: Lawrencium
|
||||||
|
{14400.0f, 1900.0f, 0.0f, 0.0f, 1.3f}, // Estimated
|
||||||
|
// 104: Rutherfordium
|
||||||
|
{17000.0f, 2400.0f, 5800.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 105: Dubnium
|
||||||
|
{21600.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 106: Seaborgium
|
||||||
|
{23500.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 107: Bohrium
|
||||||
|
{26500.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 108: Hassium
|
||||||
|
{28000.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 109: Meitnerium
|
||||||
|
{27500.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 110: Darmstadtium
|
||||||
|
{26500.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 111: Roentgenium
|
||||||
|
{23000.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 112: Copernicium
|
||||||
|
{14000.0f, 283.0f, 340.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 113: Nihonium
|
||||||
|
{16000.0f, 700.0f, 1400.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 114: Flerovium
|
||||||
|
{11400.0f, 284.0f, 0.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 115: Moscovium
|
||||||
|
{13500.0f, 700.0f, 1400.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 116: Livermorium
|
||||||
|
{12900.0f, 700.0f, 1100.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 117: Tennessine
|
||||||
|
{7200.0f, 700.0f, 883.0f, 0.0f, 0.0f}, // Estimated
|
||||||
|
// 118: Oganesson
|
||||||
|
{7000.0f, 325.0f, 450.0f, 0.0f, 0.0f} // Estimated
|
||||||
|
}};
|
||||||
|
|
||||||
|
struct PointProperties {
|
||||||
|
float weight = 0.0f; // Total mass
|
||||||
|
float density = 0.0f; // Mass / Volume
|
||||||
|
float meltingPoint = 0.0f;
|
||||||
|
float boilingPoint = 0.0f;
|
||||||
|
float specificHeat = 0.0f;
|
||||||
|
float electronegativity = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct elementContent {
|
||||||
|
float hydrogen = 0.0f;
|
||||||
|
float helium = 0.0f;
|
||||||
|
float lithium = 0.0f;
|
||||||
|
float beryllium = 0.0f;
|
||||||
|
float boron = 0.0f;
|
||||||
|
float carbon = 0.0f;
|
||||||
|
float nitrogen = 0.0f;
|
||||||
|
float oxygen = 0.0f;
|
||||||
|
float fluorine = 0.0f;
|
||||||
|
float neon = 0.0f;
|
||||||
|
float sodium = 0.0f;
|
||||||
|
float magnesium = 0.0f;
|
||||||
|
float aluminum = 0.0f;
|
||||||
|
float silicon = 0.0f;
|
||||||
|
float phosporus = 0.0f;
|
||||||
|
float sulfur = 0.0f;
|
||||||
|
float chlorine = 0.0f;
|
||||||
|
float argon = 0.0f;
|
||||||
|
float potassium = 0.0f;
|
||||||
|
float calcium = 0.0f;
|
||||||
|
float scandium = 0.0f;
|
||||||
|
float titanium = 0.0f;
|
||||||
|
float vanadium = 0.0f;
|
||||||
|
float chromium = 0.0f;
|
||||||
|
float manganese = 0.0f;
|
||||||
|
float iron = 0.0f;
|
||||||
|
float cobalt = 0.0f;
|
||||||
|
float nickel = 0.0f;
|
||||||
|
float copper = 0.0f;
|
||||||
|
float zinc = 0.0f;
|
||||||
|
float gallium = 0.0f;
|
||||||
|
float germanium = 0.0f;
|
||||||
|
float arsenic = 0.0f;
|
||||||
|
float selenium = 0.0f;
|
||||||
|
float bromine = 0.0f;
|
||||||
|
float krypton = 0.0f;
|
||||||
|
float rubidium = 0.0f;
|
||||||
|
float strontium = 0.0f;
|
||||||
|
float yttrium = 0.0f;
|
||||||
|
float zirconium = 0.0f;
|
||||||
|
float niobium = 0.0f;
|
||||||
|
float molybdenum = 0.0f;
|
||||||
|
float technetium = 0.0f;
|
||||||
|
float ruthenium = 0.0f;
|
||||||
|
float rhodium = 0.0f;
|
||||||
|
float palladium = 0.0f;
|
||||||
|
float silver = 0.0f;
|
||||||
|
float cadmium = 0.0f;
|
||||||
|
float indium = 0.0f;
|
||||||
|
float tin = 0.0f;
|
||||||
|
float antimony = 0.0f;
|
||||||
|
float tellurium = 0.0f;
|
||||||
|
float iodine = 0.0f;
|
||||||
|
float xenon = 0.0f;
|
||||||
|
float caesium = 0.0f;
|
||||||
|
float barium = 0.0f;
|
||||||
|
float lanthanum = 0.0f;
|
||||||
|
float cerium = 0.0f;
|
||||||
|
float praseodymium = 0.0f;
|
||||||
|
float neodymium = 0.0f;
|
||||||
|
float promethium = 0.0f;
|
||||||
|
float samarium = 0.0f;
|
||||||
|
float europium = 0.0f;
|
||||||
|
float gadolinium = 0.0f;
|
||||||
|
float terbium = 0.0f;
|
||||||
|
float dysprosium = 0.0f;
|
||||||
|
float holmium = 0.0f;
|
||||||
|
float erbium = 0.0f;
|
||||||
|
float thulium = 0.0f;
|
||||||
|
float ytterbium = 0.0f;
|
||||||
|
float lutetium = 0.0f;
|
||||||
|
float hafnium = 0.0f;
|
||||||
|
float tantalum = 0.0f;
|
||||||
|
float tungsten = 0.0f;
|
||||||
|
float rhenium = 0.0f;
|
||||||
|
float osmium = 0.0f;
|
||||||
|
float iridium = 0.0f;
|
||||||
|
float platinum = 0.0f;
|
||||||
|
float gold = 0.0f;
|
||||||
|
float mercury = 0.0f;
|
||||||
|
float thallium = 0.0f;
|
||||||
|
float lead = 0.0f;
|
||||||
|
float bismuth = 0.0f;
|
||||||
|
float polonium = 0.0f;
|
||||||
|
float astatine = 0.0f;
|
||||||
|
float radon = 0.0f;
|
||||||
|
float francium = 0.0f;
|
||||||
|
float radium = 0.0f;
|
||||||
|
float actinium = 0.0f;
|
||||||
|
float thorium = 0.0f;
|
||||||
|
float protactinium = 0.0f;
|
||||||
|
float uranium = 0.0f;
|
||||||
|
float neptunium = 0.0f;
|
||||||
|
float plutonium = 0.0f;
|
||||||
|
float americium = 0.0f;
|
||||||
|
float curium = 0.0f;
|
||||||
|
float berkelium = 0.0f;
|
||||||
|
float californium = 0.0f;
|
||||||
|
float einsteinium = 0.0f;
|
||||||
|
float fermium = 0.0f;
|
||||||
|
float mendelevium = 0.0f;
|
||||||
|
float nobelium = 0.0f;
|
||||||
|
float lawrencium = 0.0f;
|
||||||
|
float rutherfordium = 0.0f;
|
||||||
|
float dubnium = 0.0f;
|
||||||
|
float seaborgium = 0.0f;
|
||||||
|
float bohrium = 0.0f;
|
||||||
|
float hassium = 0.0f;
|
||||||
|
float meitnerium = 0.0f;
|
||||||
|
float darmstadtium = 0.0f;
|
||||||
|
float roentgenium = 0.0f;
|
||||||
|
float cpernicium = 0.0f;
|
||||||
|
float nihnium = 0.0f;
|
||||||
|
float flerovium = 0.0f;
|
||||||
|
float moscovium = 0.0f;
|
||||||
|
float livermorium = 0.0f;
|
||||||
|
float tennessine = 0.0f;
|
||||||
|
float oganesson = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
1077
util/sim/planet.hpp
Normal file
1077
util/sim/planet.hpp
Normal file
File diff suppressed because it is too large
Load Diff
421
util/sim/worldbox.hpp
Normal file
421
util/sim/worldbox.hpp
Normal file
@@ -0,0 +1,421 @@
|
|||||||
|
#ifndef WORLDBOX_HPP
|
||||||
|
#define WORLDBOX_HPP
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <cmath>
|
||||||
|
#include <random>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "../grid/grid3eigen.hpp"
|
||||||
|
#include "../timing_decorator.cpp"
|
||||||
|
|
||||||
|
using v3 = Eigen::Vector3f;
|
||||||
|
|
||||||
|
struct WorldVoxel {
|
||||||
|
float nutrients = 1.0f;
|
||||||
|
float moisture = 0.5f;
|
||||||
|
int type = 0; // 0=Dirt, 1=Rock, 2=Grass, 3=Star, 4=Cloud, 5=Rain
|
||||||
|
|
||||||
|
WorldVoxel() = default;
|
||||||
|
|
||||||
|
WorldVoxel(float nut, float mois, int t) : nutrients(nut), moisture(mois), type(t) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WorldBoxConfig {
|
||||||
|
v3 center = v3(0, 0, 0);
|
||||||
|
float worldSizeX = 1000.0f;
|
||||||
|
float worldSizeZ = 1000.0f;
|
||||||
|
float worldDepth = 20.0f;
|
||||||
|
|
||||||
|
float voxelSize = 2.0f;
|
||||||
|
v3 baseDirtColor = v3(0.36f, 0.25f, 0.14f);
|
||||||
|
v3 baseRockColor = v3(0.45f, 0.45f, 0.45f);
|
||||||
|
|
||||||
|
float gridSizeCubeMin = 1024.0f;
|
||||||
|
|
||||||
|
// Grass Config
|
||||||
|
float grassDensity = 0.05f;
|
||||||
|
v3 grassColorBase = v3(0.2f, 0.6f, 0.15f);
|
||||||
|
|
||||||
|
// Star Config
|
||||||
|
bool enableStarRotation = false; // Off by default
|
||||||
|
float starOrbitRadius = 800.0f;
|
||||||
|
float starPanelSize = 100.0f;
|
||||||
|
float starVoxelSize = 10.0f;
|
||||||
|
v3 starColor = v3(1.0f, 0.95f, 0.8f);
|
||||||
|
float starSpeed = 0.2f; // Radians per second
|
||||||
|
float starAngle = 0.0f;
|
||||||
|
|
||||||
|
// Weather Config
|
||||||
|
int cloudCount = 15;
|
||||||
|
float cloudHeight = 150.0f;
|
||||||
|
v3 cloudColor = v3(0.9f, 0.9f, 0.95f);
|
||||||
|
float cloudBaseSize = 6.0f;
|
||||||
|
|
||||||
|
v3 rainColor = v3(0.2f, 0.4f, 0.9f);
|
||||||
|
float rainDropSize = 0.5f;
|
||||||
|
float rainSpawnRate = 1.0f;
|
||||||
|
|
||||||
|
// Physics Config
|
||||||
|
bool enableGravity = true;
|
||||||
|
v3 gravity = v3(0.0f, -60.0f, 0.0f);
|
||||||
|
v3 wind = v3(20.0f, 0.0f, 10.0f);
|
||||||
|
float physicsStep = 0.1f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CloudVoxel {
|
||||||
|
v3 pos;
|
||||||
|
float size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RainDrop {
|
||||||
|
v3 pos;
|
||||||
|
v3 vel;
|
||||||
|
};
|
||||||
|
|
||||||
|
class worldboxsim {
|
||||||
|
public:
|
||||||
|
WorldBoxConfig config;
|
||||||
|
Octree<WorldVoxel> grid;
|
||||||
|
std::mt19937 rng;
|
||||||
|
std::vector<v3> starVoxelPositions;
|
||||||
|
|
||||||
|
std::vector<CloudVoxel> clouds;
|
||||||
|
std::vector<RainDrop> rainDrops;
|
||||||
|
float physicsTimer = 0.0f;
|
||||||
|
|
||||||
|
worldboxsim() : rng(42) {
|
||||||
|
config = WorldBoxConfig();
|
||||||
|
grid = Octree<WorldVoxel>(v3(-config.gridSizeCubeMin, -config.gridSizeCubeMin, -config.gridSizeCubeMin),v3(config.gridSizeCubeMin, config.gridSizeCubeMin, config.gridSizeCubeMin), 16, 32);
|
||||||
|
grid.setBackgroundColor(v3(0.53f, 0.81f, 0.92f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStar(float dt) {
|
||||||
|
if (!config.enableStarRotation) {
|
||||||
|
if (!starVoxelPositions.empty()) {
|
||||||
|
for(const auto& pos : starVoxelPositions) {
|
||||||
|
grid.remove(pos);
|
||||||
|
}
|
||||||
|
starVoxelPositions.clear();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate rotation
|
||||||
|
config.starAngle += dt * config.starSpeed;
|
||||||
|
if (config.starAngle > 2 * M_PI) config.starAngle -= 2 * M_PI;
|
||||||
|
|
||||||
|
// Calculate new center of star (orbiting on the X/Y plane)
|
||||||
|
v3 starCenter(cos(config.starAngle) * config.starOrbitRadius, sin(config.starAngle) * config.starOrbitRadius, 0.0f);
|
||||||
|
|
||||||
|
// Create a flat panel facing the origin
|
||||||
|
v3 n = -starCenter.normalized();
|
||||||
|
v3 worldUp(0, 1, 0);
|
||||||
|
if (std::abs(n.dot(worldUp)) > 0.99f) worldUp = v3(0, 0, 1);
|
||||||
|
v3 right = worldUp.cross(n).normalized();
|
||||||
|
v3 up = n.cross(right).normalized();
|
||||||
|
|
||||||
|
int halfGrid = std::max(1, static_cast<int>((config.starPanelSize / config.starVoxelSize) / 2.0f));
|
||||||
|
WorldVoxel starVoxel(0.0f, 0.0f, 3); // Type 3 = Star
|
||||||
|
|
||||||
|
// Calculate the new ideal positions for this frame
|
||||||
|
std::vector<v3> newPositions;
|
||||||
|
newPositions.reserve((2 * halfGrid + 1) * (2 * halfGrid + 1));
|
||||||
|
|
||||||
|
for (int i = -halfGrid; i <= halfGrid; ++i) {
|
||||||
|
for (int j = -halfGrid; j <= halfGrid; ++j) {
|
||||||
|
newPositions.push_back(starCenter + (right * (i * config.starVoxelSize)) + (up * (j * config.starVoxelSize)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply grid changes
|
||||||
|
if (starVoxelPositions.empty()) {
|
||||||
|
// Creation: Spawn voxels into the grid for the first time
|
||||||
|
for (const auto& pos : newPositions) {
|
||||||
|
// Injecting a high emittance factor (15.0f) to make it a bright emissive light source
|
||||||
|
grid.set(starVoxel, pos, true, config.starColor, config.starVoxelSize, true, 1, 1, 15.0f);
|
||||||
|
}
|
||||||
|
starVoxelPositions = newPositions;
|
||||||
|
} else if (starVoxelPositions.size() == newPositions.size()) {
|
||||||
|
// Moving: Using grid.move() to smoothly transfer nodes in the Octree
|
||||||
|
for (size_t i = 0; i < starVoxelPositions.size(); ++i) {
|
||||||
|
grid.move(starVoxelPositions[i], newPositions[i]);
|
||||||
|
}
|
||||||
|
starVoxelPositions = newPositions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateClouds() {
|
||||||
|
std::uniform_real_distribution<float> randX(-config.worldSizeX/2, config.worldSizeX/2);
|
||||||
|
std::uniform_real_distribution<float> randZ(-config.worldSizeZ/2, config.worldSizeZ/2);
|
||||||
|
std::uniform_real_distribution<float> randY(config.cloudHeight - 10.0f, config.cloudHeight + 10.0f);
|
||||||
|
|
||||||
|
for (int i=0; i<config.cloudCount; ++i) {
|
||||||
|
v3 center(randX(rng), randY(rng), randZ(rng));
|
||||||
|
int numVoxels = 10 + (rng() % 20);
|
||||||
|
|
||||||
|
for (int j=0; j<numVoxels; ++j) {
|
||||||
|
v3 offset(
|
||||||
|
(rng() % 40) - 20,
|
||||||
|
(rng() % 10) - 5,
|
||||||
|
(rng() % 40) - 20
|
||||||
|
);
|
||||||
|
v3 pos = center + offset;
|
||||||
|
float size = config.cloudBaseSize + (rng() % 6);
|
||||||
|
|
||||||
|
WorldVoxel vox(0.0f, 1.0f, 4); // Type 4 = Cloud
|
||||||
|
// Adding to grid with transmission=0.4f (makes it partially transparent for RTX)
|
||||||
|
grid.set(vox, pos, true, config.cloudColor, size, true, 4, 0, 0.0f, 1.0f, 0.0f, 0.4f);
|
||||||
|
clouds.push_back({pos, size});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateWeatherAndPhysics(float dt) {
|
||||||
|
float halfX = config.worldSizeX / 2.0f;
|
||||||
|
float halfZ = config.worldSizeZ / 2.0f;
|
||||||
|
|
||||||
|
// 1. Clouds Update
|
||||||
|
std::vector<CloudVoxel> nextClouds;
|
||||||
|
for (auto& c : clouds) {
|
||||||
|
v3 nextPos = c.pos + config.wind * dt;
|
||||||
|
|
||||||
|
// Screen wrap logic for wind drift
|
||||||
|
if (nextPos.x() > halfX) nextPos.x() -= config.worldSizeX;
|
||||||
|
if (nextPos.x() < -halfX) nextPos.x() += config.worldSizeX;
|
||||||
|
if (nextPos.z() > halfZ) nextPos.z() -= config.worldSizeZ;
|
||||||
|
if (nextPos.z() < -halfZ) nextPos.z() += config.worldSizeZ;
|
||||||
|
|
||||||
|
if (grid.move(c.pos, nextPos)) {
|
||||||
|
c.pos = nextPos;
|
||||||
|
} else {
|
||||||
|
WorldVoxel vox(0.0f, 1.0f, 4);
|
||||||
|
grid.set(vox, nextPos, true, config.cloudColor, c.size, true, 4, 0, 0.0f, 1.0f, 0.0f, 0.4f);
|
||||||
|
c.pos = nextPos;
|
||||||
|
}
|
||||||
|
nextClouds.push_back(c);
|
||||||
|
|
||||||
|
// Spawn Rain
|
||||||
|
std::uniform_real_distribution<float> dist(0, 1);
|
||||||
|
if (dist(rng) < (config.rainSpawnRate * dt * 0.1f)) {
|
||||||
|
RainDrop r = {c.pos - v3(0, c.size, 0), config.wind};
|
||||||
|
rainDrops.push_back(r);
|
||||||
|
WorldVoxel rv(0.0f, 1.0f, 5); // Type 5 = Rain
|
||||||
|
grid.set(rv, r.pos, true, config.rainColor, config.rainDropSize, true, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clouds = nextClouds;
|
||||||
|
|
||||||
|
// 2. Rain Update
|
||||||
|
std::vector<RainDrop> nextRain;
|
||||||
|
for (auto& r : rainDrops) {
|
||||||
|
r.vel += config.gravity * dt;
|
||||||
|
v3 nextPos = r.pos + r.vel * dt;
|
||||||
|
|
||||||
|
v3 dir = (nextPos - r.pos);
|
||||||
|
float distMag = dir.norm();
|
||||||
|
|
||||||
|
if (distMag > 0) {
|
||||||
|
dir.normalize();
|
||||||
|
auto hit = grid.voxelTraverse(r.pos, dir, distMag, false);
|
||||||
|
|
||||||
|
// If it hits solid terrain
|
||||||
|
if (hit && hit->data.type != 4 && hit->data.type != 5) {
|
||||||
|
if (hit->data.type == 0) { // Hit Dirt
|
||||||
|
hit->data.moisture = std::min(1.0f, hit->data.moisture + 0.15f);
|
||||||
|
v3 darkDirt = config.baseDirtColor * 0.4f;
|
||||||
|
v3 wetColor = config.baseDirtColor * (1.0f - hit->data.moisture) + darkDirt * hit->data.moisture;
|
||||||
|
grid.setColor(hit->position, wetColor);
|
||||||
|
} else if (hit->data.type == 2) { // Hit Grass
|
||||||
|
hit->data.moisture = std::min(1.0f, hit->data.moisture + 0.15f);
|
||||||
|
v3 lushGrass = config.grassColorBase * 1.5f;
|
||||||
|
v3 wetColor = config.grassColorBase * (1.0f - hit->data.moisture) + lushGrass * hit->data.moisture;
|
||||||
|
grid.setColor(hit->position, wetColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
grid.remove(r.pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete if falls out of bounds
|
||||||
|
if (nextPos.y() < -config.worldDepth - 20.0f) {
|
||||||
|
grid.remove(r.pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid.move(r.pos, nextPos)) {
|
||||||
|
r.pos = nextPos;
|
||||||
|
nextRain.push_back(r);
|
||||||
|
} else {
|
||||||
|
WorldVoxel rv(0.0f, 1.0f, 5);
|
||||||
|
grid.set(rv, nextPos, true, config.rainColor, config.rainDropSize, true, 5);
|
||||||
|
r.pos = nextPos;
|
||||||
|
nextRain.push_back(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rainDrops = nextRain;
|
||||||
|
|
||||||
|
// 3. Apply Block Gravity
|
||||||
|
if (config.enableGravity) {
|
||||||
|
physicsTimer += dt;
|
||||||
|
if (physicsTimer >= config.physicsStep) {
|
||||||
|
applyTerrainGravity();
|
||||||
|
physicsTimer = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyTerrainGravity() {
|
||||||
|
std::vector<std::shared_ptr<Octree<WorldVoxel>::NodeData>> nodes;
|
||||||
|
grid.collectNodesByObjectId( -1, nodes);
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Octree<WorldVoxel>::NodeData>> terrain;
|
||||||
|
terrain.reserve(nodes.size());
|
||||||
|
for (auto& n : nodes) {
|
||||||
|
// Include Dirt, Rock, and Grass in gravity sweep
|
||||||
|
if (n->data.type == 0 || n->data.type == 1 || n->data.type == 2) {
|
||||||
|
terrain.push_back(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process Bottom-Up
|
||||||
|
std::sort(terrain.begin(), terrain.end(), [](const auto& a, const auto& b) {
|
||||||
|
return a->position.y() < b->position.y();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto& n : terrain) {
|
||||||
|
v3 belowPos = n->position + v3(0, -config.voxelSize, 0);
|
||||||
|
|
||||||
|
// Bounds check so voxels don't fall infinitely
|
||||||
|
if (belowPos.y() < -config.worldDepth) continue;
|
||||||
|
|
||||||
|
auto hit = grid.find(belowPos, config.voxelSize * 0.1f);
|
||||||
|
if (!hit) {
|
||||||
|
grid.move(n->position, belowPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateGrass() {
|
||||||
|
TIME_FUNCTION;
|
||||||
|
float halfX = config.worldSizeX / 2.0f;
|
||||||
|
float halfZ = config.worldSizeZ / 2.0f;
|
||||||
|
float surfaceY = 0.0f;
|
||||||
|
|
||||||
|
int stepsX = static_cast<int>(std::round(config.worldSizeX / config.voxelSize)) + 1;
|
||||||
|
int stepsZ = static_cast<int>(std::round(config.worldSizeZ / config.voxelSize)) + 1;
|
||||||
|
|
||||||
|
int grassCount = 0;
|
||||||
|
|
||||||
|
#pragma omp parallel
|
||||||
|
{
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 local_rng(rd() ^ std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||||
|
std::uniform_real_distribution<float> probDist(0.0f, 1.0f);
|
||||||
|
std::uniform_int_distribution<int> grassHeightDist(1, 8);
|
||||||
|
|
||||||
|
#pragma omp for schedule(static) collapse(2)
|
||||||
|
for (int i = 0; i < stepsX; ++i) {
|
||||||
|
for (int j = 0; j < stepsZ; ++j) {
|
||||||
|
|
||||||
|
float x = -halfX + i * config.voxelSize;
|
||||||
|
float z = -halfZ + j * config.voxelSize;
|
||||||
|
|
||||||
|
if (x > halfX || z > halfZ) continue;
|
||||||
|
|
||||||
|
if (probDist(local_rng) < config.grassDensity) {
|
||||||
|
int gHeight = grassHeightDist(local_rng);
|
||||||
|
float gSize = config.voxelSize / 25.0f;
|
||||||
|
|
||||||
|
std::uniform_real_distribution<float> offDist(-config.voxelSize/2.0f + gSize/2.0f, config.voxelSize/2.0f - gSize/2.0f);
|
||||||
|
float offsetX = offDist(local_rng);
|
||||||
|
float offsetZ = offDist(local_rng);
|
||||||
|
|
||||||
|
WorldVoxel gVox(1.0f, 0.8f, 2); // Type 2 = Grass
|
||||||
|
float baseY = surfaceY + (config.voxelSize / 2.0f) + (gSize / 2.0f);
|
||||||
|
|
||||||
|
#pragma omp critical
|
||||||
|
{
|
||||||
|
for (int g = 0; g < gHeight; ++g) {
|
||||||
|
v3 gPos(x + offsetX, baseY + g * gSize, z + offsetZ);
|
||||||
|
grid.set(gVox, gPos, true, config.grassColorBase, gSize, true, 1, 0);
|
||||||
|
grassCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "Grass generation complete. Placed " << grassCount << " grass voxels." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateFlatWorld() {
|
||||||
|
TIME_FUNCTION;
|
||||||
|
grid.clear();
|
||||||
|
|
||||||
|
float halfX = config.worldSizeX / 2.0f;
|
||||||
|
float halfZ = config.worldSizeZ / 2.0f;
|
||||||
|
float surfaceY = 0.0f;
|
||||||
|
|
||||||
|
// 1. Calculate integer bounds to satisfy OpenMP
|
||||||
|
int stepsX = static_cast<int>(std::round(config.worldSizeX / config.voxelSize)) + 1;
|
||||||
|
int stepsZ = static_cast<int>(std::round(config.worldSizeZ / config.voxelSize)) + 1;
|
||||||
|
int stepsY = static_cast<int>(std::round(config.worldDepth / config.voxelSize)) + 1;
|
||||||
|
size_t maxSteps = stepsX * stepsZ * stepsY;
|
||||||
|
|
||||||
|
int nodeCount = 0;
|
||||||
|
|
||||||
|
#pragma omp parallel for schedule(static) collapse(3)
|
||||||
|
for (int i = 0; i < stepsX; ++i) {
|
||||||
|
for (int j = 0; j < stepsZ; ++j) {
|
||||||
|
for (int k = 0; k < stepsY; ++k) {
|
||||||
|
|
||||||
|
float x = -halfX + i * config.voxelSize;
|
||||||
|
float z = -halfZ + j * config.voxelSize;
|
||||||
|
float y = surfaceY - k * config.voxelSize;
|
||||||
|
|
||||||
|
if (x > halfX || z > halfZ || y < surfaceY - config.worldDepth) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldVoxel voxel;
|
||||||
|
v3 color;
|
||||||
|
|
||||||
|
float depthRatio = std::abs(y - surfaceY) / config.worldDepth;
|
||||||
|
|
||||||
|
if (depthRatio > 0.8f) {
|
||||||
|
voxel.type = 1;
|
||||||
|
voxel.nutrients = 0.1f;
|
||||||
|
color = config.baseRockColor;
|
||||||
|
} else {
|
||||||
|
voxel.type = 0;
|
||||||
|
voxel.nutrients = 1.0f - depthRatio;
|
||||||
|
color = config.baseDirtColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
v3 pos(x, y, z);
|
||||||
|
|
||||||
|
#pragma omp critical
|
||||||
|
grid.set(voxel, pos, true, color, config.voxelSize, true, 1, 0);
|
||||||
|
// nodeCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "World generation complete. Placed " << nodeCount << " voxels." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearWorld() {
|
||||||
|
grid.clear();
|
||||||
|
clouds.clear();
|
||||||
|
rainDrops.clear();
|
||||||
|
starVoxelPositions.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user