Compare commits
1 Commits
needsfixes
...
plantbuild
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ff50cb43d |
264
tests/plant.cpp
Normal file
264
tests/plant.cpp
Normal 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
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "../util/noise/pnoise.cpp"
|
||||
#include "planet.cpp"
|
||||
#include "plant.cpp"
|
||||
#include "../util/basicdefines.hpp"
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
|
||||
@@ -80,6 +81,7 @@ int main() {
|
||||
|
||||
planetSimUI planetApp;
|
||||
NoisePreviewState noiseState;
|
||||
PlantSimUI plantApp;
|
||||
|
||||
if (noiseState.layers.empty()) {
|
||||
NoiseLayer defaultLayer;
|
||||
@@ -101,6 +103,7 @@ int main() {
|
||||
ImGui::GetMainViewport();
|
||||
drawNoiseLab(noiseState);
|
||||
planetApp.renderUI(window);
|
||||
plantApp.renderUI(window);
|
||||
|
||||
ImGui::Begin("Integration Control");
|
||||
ImGui::Text("Bridge: Noise Lab -> Planet Sim");
|
||||
|
||||
@@ -598,7 +598,7 @@ private:
|
||||
|
||||
float t;
|
||||
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) {
|
||||
maxDist = t;
|
||||
hit = pointData;
|
||||
|
||||
1023
util/sim/plant.hpp
Normal file
1023
util/sim/plant.hpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user