This commit is contained in:
yggdrasil75
2026-03-09 05:38:27 -04:00
parent 3d96e569c8
commit 3ff50cb43d
4 changed files with 1291 additions and 1 deletions

264
tests/plant.cpp Normal file
View File

@@ -0,0 +1,264 @@
#ifndef PLANT_CPP
#define PLANT_CPP
#include "../util/sim/plant.hpp"
#include "../util/grid/camera.hpp"
// Assuming ImGui headers are available via ptest.cpp or similar include paths
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
class PlantSimUI {
private:
PlantSim sim;
Camera cam;
// Rendering / Texture vars
GLuint textu = 0;
std::mutex PreviewMutex;
bool textureInitialized = false;
frame currentPreviewFrame;
// Render Settings
int outWidth = 512;
int outHeight = 512;
bool slowRender = false;
bool globalIllumination = true; // Default to true to see sun emission
int reflectCount = 2;
float maxDist = 200.0f;
float framerate = 10.0f;
// Input state
std::map<int, bool> keyStates;
float deltaTime = 0.016f; // approx 30fps
const char* getSeasonName(float season, float latitude) {
bool north = latitude >= 0;
if (season < 0.25f) return north ? "Spring" : "Autumn";
if (season < 0.50f) return north ? "Summer" : "Winter";
if (season < 0.75f) return north ? "Autumn" : "Spring";
return north ? "Winter" : "Summer";
}
const char* getWeatherName(PlantSim::WeatherState state) {
switch(state) {
case PlantSim::WeatherState::RAIN: return "Raining";
case PlantSim::WeatherState::SNOW: return "Snowing";
default: return "Clear";
}
}
public:
PlantSimUI() {
// Position camera to look at the dirt
cam.origin = v3(0, 5, 30);
cam.lookAt(v3(0, 2, 0));
cam.fov = 45;
// Init the simulation
sim.initWorld();
v3 bg = v3(0.511f, 0.625f, 0.868f);
sim.grid.setBackgroundColor(bg);
sim.grid.setSkylight(bg);
}
~PlantSimUI() {
if (textu != 0) glDeleteTextures(1, &textu);
}
void renderUI(GLFWwindow* window) {
handleCameraControls(window);
ImGui::Begin("Plant Growth Lab");
if (ImGui::BeginTable("PlantLayout", 2, ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f);
ImGui::TableSetupColumn("Viewport", ImGuiTableColumnFlags_WidthStretch, 0.7f);
ImGui::TableNextColumn();
renderControls();
ImGui::TableNextColumn();
renderPreview();
ImGui::EndTable();
}
sim.update(deltaTime);
ImGui::End();
}
private:
void handleCameraControls(GLFWwindow* window) {
glfwPollEvents();
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
keyStates[i] = (glfwGetKey(window, i) == GLFW_PRESS);
}
float speed = 10.0f * deltaTime;
if (keyStates[GLFW_KEY_W]) cam.moveForward(deltaTime * 10.0f);
if (keyStates[GLFW_KEY_S]) cam.moveBackward(deltaTime * 10.0f);
if (keyStates[GLFW_KEY_A]) cam.moveLeft(deltaTime * 10.0f);
if (keyStates[GLFW_KEY_D]) cam.moveRight(deltaTime * 10.0f);
if (keyStates[GLFW_KEY_Q]) cam.rotateYaw(deltaTime);
if (keyStates[GLFW_KEY_E]) cam.rotateYaw(-deltaTime);
}
void renderControls() {
if (ImGui::CollapsingHeader("World State", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("Day: %d / %d", sim.config.currentDay + 1, sim.config.daysPerYear);
ImGui::Text("Season: %s", getSeasonName(sim.config.season, sim.config.latitude));
ImGui::Text("Global Temperature: %.1f °C", sim.currentTemperature);
ImVec4 weatherColor = ImVec4(1, 1, 1, 1);
if (sim.currentWeather == PlantSim::WeatherState::RAIN) weatherColor = ImVec4(0.3f, 0.5f, 1.0f, 1.0f);
else if (sim.currentWeather == PlantSim::WeatherState::SNOW) weatherColor = ImVec4(0.8f, 0.9f, 1.0f, 1.0f);
ImGui::TextColored(weatherColor, "Current Weather: %s", getWeatherName(sim.currentWeather));
ImGui::TextColored(weatherColor, "(Time Remaining: %.1fs)", sim.weatherTimer);
ImGui::Separator();
ImGui::Text("Atmospheric Moisture: %.1f", sim.atmosphericMoisture);
float localCo2 = 0.0f;
float localAirTemp = sim.currentTemperature;
auto airNodes = sim.grid.findInRadius(v3(0, sim.config.voxelSize / 2.0f, 0), sim.config.voxelSize, 5);
if (!airNodes.empty()) {
auto ap = std::static_pointer_cast<AirParticle>(airNodes[0]->data);
localCo2 = ap->co2;
localAirTemp = ap->temperature;
}
ImGui::Text("Local Air Temp: %.1f °C", localAirTemp);
ImGui::Text("Ambient CO2: %.1f ppm", localCo2);
if (sim.extendedHeatTimer > 0.0f) {
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.0f, 1.0f), "Heat Wave Timer: %.1f days", sim.extendedHeatTimer / std::max(1.0f, sim.config.dayDuration));
} else {
ImGui::Text("Heat Wave Timer: 0.0 days");
}
}
if (ImGui::CollapsingHeader("Plant Health & Structure", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("Active Plant Cells:");
ImGui::BulletText("Leaves: %d", sim.leafCount);
ImGui::BulletText("Roots: %d", sim.rootCount);
ImGui::Text("Total Organism Resources:");
ImGui::ProgressBar(std::min(sim.totalPlantEnergy / (std::max(1, sim.leafCount + sim.rootCount) * 20.0f), 1.0f), ImVec2(-1, 0),
("Energy: " + std::to_string((int)sim.totalPlantEnergy)).c_str());
ImGui::ProgressBar(std::min(sim.totalPlantWater / (std::max(1, sim.leafCount + sim.rootCount) * 20.0f), 1.0f), ImVec2(-1, 0),
("Water: " + std::to_string((int)sim.totalPlantWater)).c_str());
}
ImGui::Separator();
if (ImGui::CollapsingHeader("Soil Environment")) {
float currentHydration = 0.0f;
float currentTemp = 0.0f;
float n = 0;
float p = 0;
float k = 0;
float c = 0;
float mg = 0;
auto dirtNodes = sim.grid.findInRadius(v3(0, -sim.config.voxelSize / 2.0f, 0), sim.config.voxelSize, 0);
if (!dirtNodes.empty()) {
auto dp = std::static_pointer_cast<DirtParticle>(dirtNodes[0]->data);
currentHydration = dp->hydration;
currentTemp = dp->temperature;
n = dp->nitrogen;
p = dp->phosphorus;
k = dp->potassium;
c = dp->carbon;
mg = dp->magnesium;
}
ImGui::Text("Soil Temp: %.1f °C", currentTemp);
ImGui::ProgressBar(std::min(currentHydration / 500.0f, 1.0f), ImVec2(-1, 0),
("Water: " + std::to_string((int)currentHydration)).c_str());
ImGui::Text("Nitrogen (N): %.1f", n);
ImGui::Text("Phosphorus (P): %.1f", p);
ImGui::Text("Potassium (K): %.1f", k);
ImGui::Text("Carbon (C): %.1f", c);
ImGui::Text("Magnesium (Mg): %.1f", mg);
}
if (ImGui::CollapsingHeader("Sun & Seasons", ImGuiTreeNodeFlags_DefaultOpen)) {
bool rebuildSun = false;
ImGui::Text("Time & Season");
ImGui::SliderFloat("Time of Day", &sim.config.timeOfDay, 0.0f, 1.0f);
int prevDay = sim.config.currentDay;
if (ImGui::SliderInt("Current Day", &sim.config.currentDay, 0, sim.config.daysPerYear - 1)) {
sim.config.season = (static_cast<float>(sim.config.currentDay) + sim.config.timeOfDay) / sim.config.daysPerYear;
}
ImGui::SliderFloat("Day Duration (s)", &sim.config.dayDuration, 1.0f, 600.0f);
if (ImGui::SliderInt("Days per Year", &sim.config.daysPerYear, 4, 365)) {
if (sim.config.currentDay >= sim.config.daysPerYear) sim.config.currentDay = 0;
}
ImGui::Separator();
ImGui::Text("Geography");
ImGui::SliderFloat("Latitude", &sim.config.latitude, -90.0f, 90.0f);
ImGui::SliderFloat("Axial Tilt", &sim.config.axialTilt, 0.0f, 90.0f);
ImGui::Separator();
ImGui::Text("Sun Appearance");
rebuildSun |= ImGui::SliderFloat("Distance", &sim.config.sunDistance, 10.0f, 100.0f);
rebuildSun |= ImGui::ColorEdit3("Color", sim.config.sunColor.data());
rebuildSun |= ImGui::DragFloat("Intensity", &sim.config.sunIntensity, 0.1f, 0.0f, 100.0f);
ImGui::Text("Weather Constraints");
ImGui::SliderFloat("Precipitation Rate", &sim.config.precipRate, 10.0f, 500.0f);
}
if (ImGui::CollapsingHeader("Simulation")) {
if (ImGui::Button("Reset World", ImVec2(-1, 0))) {
sim.initWorld();
}
}
if (ImGui::CollapsingHeader("Render Settings")) {
ImGui::Checkbox("High Quality (Slow)", &slowRender);
ImGui::Checkbox("Global Illumination", &globalIllumination);
ImGui::DragInt("Bounces", &reflectCount, 1, 0, 8);
}
}
void renderPreview() {
livePreview();
if (textureInitialized) {
float availW = ImGui::GetContentRegionAvail().x;
float aspect = (float)outWidth / (float)outHeight;
ImGui::Image((void*)(intptr_t)textu, ImVec2(availW, availW / aspect));
}
}
void livePreview() {
std::lock_guard<std::mutex> lock(PreviewMutex);
// Update Grid settings based on UI
sim.grid.setMaxDistance(maxDist);
// Render
if (slowRender) {
currentPreviewFrame = sim.grid.renderFrame(cam, outHeight, outWidth, frame::colormap::RGB, 10, reflectCount, globalIllumination, true);
} else {
currentPreviewFrame = sim.grid.fastRenderFrame(cam, outHeight, outWidth, frame::colormap::RGB);
}
// Upload to GPU
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());
textureInitialized = true;
}
};
#endif

View File

@@ -10,6 +10,7 @@
#include "../util/noise/pnoise.cpp" #include "../util/noise/pnoise.cpp"
#include "planet.cpp" #include "planet.cpp"
#include "plant.cpp"
#include "../util/basicdefines.hpp" #include "../util/basicdefines.hpp"
void framebuffer_size_callback(GLFWwindow* window, int width, int height) { void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
@@ -80,6 +81,7 @@ int main() {
planetSimUI planetApp; planetSimUI planetApp;
NoisePreviewState noiseState; NoisePreviewState noiseState;
PlantSimUI plantApp;
if (noiseState.layers.empty()) { if (noiseState.layers.empty()) {
NoiseLayer defaultLayer; NoiseLayer defaultLayer;
@@ -101,6 +103,7 @@ int main() {
ImGui::GetMainViewport(); ImGui::GetMainViewport();
drawNoiseLab(noiseState); drawNoiseLab(noiseState);
planetApp.renderUI(window); planetApp.renderUI(window);
plantApp.renderUI(window);
ImGui::Begin("Integration Control"); ImGui::Begin("Integration Control");
ImGui::Text("Bridge: Noise Lab -> Planet Sim"); ImGui::Text("Bridge: Noise Lab -> Planet Sim");

View File

@@ -598,7 +598,7 @@ private:
float t; float t;
PointType normal, hitPoint; PointType normal, hitPoint;
if (rayCubeIntersect(ray, pointData.get(), t, normal, hitPoint)) { if (rayCubeIntersect(ray, pointData.get(), t, normal, hitPoint) && pointData->visible) {
if (t >= 0 && t <= maxDist && t <= tMax + 0.001f) { if (t >= 0 && t <= maxDist && t <= tMax + 0.001f) {
maxDist = t; maxDist = t;
hit = pointData; hit = pointData;

1023
util/sim/plant.hpp Normal file

File diff suppressed because it is too large Load Diff