Compare commits
1 Commits
renderingb
...
plantbuild
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ff50cb43d |
2
makefile
2
makefile
@@ -34,7 +34,7 @@ endif
|
|||||||
CXXFLAGS = $(BASE_CXXFLAGS) $(SIMD_CXXFLAGS)
|
CXXFLAGS = $(BASE_CXXFLAGS) $(SIMD_CXXFLAGS)
|
||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
SRC := $(SRC_DIR)/materialtest.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
|
||||||
|
|||||||
@@ -10,9 +10,6 @@
|
|||||||
#include "../util/grid/grid3eigen.hpp"
|
#include "../util/grid/grid3eigen.hpp"
|
||||||
#include "../util/output/frame.hpp"
|
#include "../util/output/frame.hpp"
|
||||||
#include "../util/output/bmpwriter.hpp"
|
#include "../util/output/bmpwriter.hpp"
|
||||||
#include "../util/output/aviwriter.hpp"
|
|
||||||
#include "../util/timing_decorator.hpp"
|
|
||||||
#include "../util/timing_decorator.cpp"
|
|
||||||
|
|
||||||
// Helper function to create a solid volume of voxels with material properties
|
// 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,
|
void createBox(Octree<int>& octree, const Eigen::Vector3f& center, const Eigen::Vector3f& size,
|
||||||
@@ -126,16 +123,11 @@ int main() {
|
|||||||
octree.generateLODs();
|
octree.generateLODs();
|
||||||
octree.printStats();
|
octree.printStats();
|
||||||
|
|
||||||
// 3. Setup video rendering
|
// 3. Setup rendering loop
|
||||||
int width = 512;
|
int width = 512;
|
||||||
int height = 512;
|
int height = 512;
|
||||||
|
int samples = 400;
|
||||||
// --- Video Animation Parameters ---
|
int bounces = 5;
|
||||||
const float fps = 30.0f;
|
|
||||||
const float durationPerSegment = 10.0f; // Seconds to travel between each view
|
|
||||||
const int framesPerSegment = static_cast<int>(fps * durationPerSegment);
|
|
||||||
const int video_samples = 100; // Samples per pixel for each video frame
|
|
||||||
const int video_bounces = 5; // Ray bounces for each video frame
|
|
||||||
|
|
||||||
struct View {
|
struct View {
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -143,65 +135,61 @@ int main() {
|
|||||||
Eigen::Vector3f up;
|
Eigen::Vector3f up;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define the keyframe camera views for the animation
|
// 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 = {
|
std::vector<View> views = {
|
||||||
{"-Y", Eigen::Vector3f( 0.0f, -6.8f, 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)},
|
{"+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)},
|
|
||||||
{"-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)},
|
||||||
{"+Z", Eigen::Vector3f( 0.0f, 0.0f, 7.3f), Eigen::Vector3f(0.0f, 1.0f, 0.0f)} // Top-down view
|
{"+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); // The camera will always look at this point
|
Eigen::Vector3f target(0.0f, 0.0f, 0.5f);
|
||||||
|
|
||||||
// --- Main Animation and Rendering Loop ---
|
for (const auto& view : views) {
|
||||||
std::vector<frame> videoFrames;
|
std::cout << "\nRendering view from " << view.name << " direction (Fast Pass)..." << std::endl;
|
||||||
const int totalFrames = framesPerSegment * views.size();
|
|
||||||
videoFrames.reserve(totalFrames);
|
Camera cam;
|
||||||
int frameCounter = 0;
|
cam.origin = view.origin;
|
||||||
|
cam.direction = (target - view.origin).normalized();
|
||||||
std::cout << "\nStarting video render..." << std::endl;
|
cam.up = view.up;
|
||||||
std::cout << "Total frames to render: " << totalFrames << std::endl;
|
|
||||||
|
frame out = octree.fastRenderFrame(cam, height, width, frame::colormap::RGB);
|
||||||
for (size_t i = 0; i < views.size(); ++i) {
|
|
||||||
const View& startView = views[i];
|
std::string filename = "output/fast/render_" + view.name + ".bmp";
|
||||||
const View& endView = views[(i + 1) % views.size()]; // Loop back to the first view at the end
|
BMPWriter::saveBMP(filename, out);
|
||||||
|
|
||||||
std::cout << "\nAnimating segment: " << startView.name << " -> " << endView.name << std::endl;
|
|
||||||
|
|
||||||
for (int j = 0; j < framesPerSegment; ++j) {
|
|
||||||
frameCounter++;
|
|
||||||
float t = static_cast<float>(j) / static_cast<float>(framesPerSegment);
|
|
||||||
|
|
||||||
// Interpolate camera position (origin) linearly
|
|
||||||
Eigen::Vector3f currentOrigin = startView.origin * (1.0f - t) + endView.origin * t;
|
|
||||||
|
|
||||||
// Interpolate camera orientation (up vector) and normalize
|
|
||||||
Eigen::Vector3f currentUp = (startView.up * (1.0f - t) + endView.up * t).normalized();
|
|
||||||
|
|
||||||
Camera cam;
|
|
||||||
cam.origin = currentOrigin;
|
|
||||||
cam.up = currentUp;
|
|
||||||
cam.direction = (target - cam.origin).normalized();
|
|
||||||
|
|
||||||
std::cout << "Rendering video frame " << frameCounter << "/" << totalFrames << "..." << std::endl;
|
|
||||||
|
|
||||||
frame out = octree.renderFrame(cam, height, width, frame::colormap::RGB, video_samples, video_bounces, false, true);
|
|
||||||
|
|
||||||
videoFrames.push_back(std::move(out)); // Use std::move for efficiency
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Save the final video ---
|
|
||||||
std::cout << "\nAll frames rendered. Saving video file..." << std::endl;
|
|
||||||
std::string videoFilename = "output/material_test_video.avi";
|
|
||||||
|
|
||||||
if (AVIWriter::saveAVIFromCompressedFrames(videoFilename, std::move(videoFrames), width, height, fps)) {
|
|
||||||
std::cout << "Video saved successfully to " << videoFilename << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Error: Failed to save video!" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "\nRender complete!" << std::endl;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
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 "../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");
|
||||||
|
|||||||
@@ -71,39 +71,14 @@ public:
|
|||||||
bool active;
|
bool active;
|
||||||
bool visible;
|
bool visible;
|
||||||
|
|
||||||
// --- NEW ACCUMULATION FIELDS ---
|
|
||||||
Eigen::Vector3f accumColorSum; // Sum of all samples currently in the "bucket"
|
|
||||||
float accumWeight; // Current number of samples in the "bucket"
|
|
||||||
int lastUpdateFrame; // Frame number this node was last hit
|
|
||||||
mutable std::mutex lightMutex; // Thread safety
|
|
||||||
// -------------------------------
|
|
||||||
|
|
||||||
NodeData(const T& data, const PointType& pos, bool visible, IndexType colorIdx, float size = 0.01f,
|
NodeData(const T& data, const PointType& pos, bool visible, IndexType colorIdx, float size = 0.01f,
|
||||||
bool active = true, int objectId = -1, int subId = 0, IndexType materialIdx = 0)
|
bool active = true, int objectId = -1, int subId = 0, IndexType materialIdx = 0)
|
||||||
: data(data), position(pos), objectId(objectId), subId(subId), size(size),
|
: data(data), position(pos), objectId(objectId), subId(subId), size(size),
|
||||||
colorIdx(colorIdx), materialIdx(materialIdx), active(active), visible(visible),
|
colorIdx(colorIdx), materialIdx(materialIdx), active(active), visible(visible) {}
|
||||||
accumColorSum(Eigen::Vector3f::Zero()), accumWeight(0.0f), lastUpdateFrame(-1) {}
|
|
||||||
|
|
||||||
NodeData() : objectId(-1), subId(0), size(0.0f), colorIdx(0), materialIdx(0),
|
NodeData() : objectId(-1), subId(0), size(0.0f), colorIdx(0), materialIdx(0),
|
||||||
active(false), visible(false),
|
active(false), visible(false) {}
|
||||||
accumColorSum(Eigen::Vector3f::Zero()), accumWeight(0.0f), lastUpdateFrame(-1) {}
|
|
||||||
|
|
||||||
// Manual copy constructor needed because std::mutex is not copyable
|
|
||||||
NodeData(const NodeData& other) {
|
|
||||||
data = other.data;
|
|
||||||
position = other.position;
|
|
||||||
objectId = other.objectId;
|
|
||||||
subId = other.subId;
|
|
||||||
size = other.size;
|
|
||||||
colorIdx = other.colorIdx;
|
|
||||||
materialIdx = other.materialIdx;
|
|
||||||
active = other.active;
|
|
||||||
visible = other.visible;
|
|
||||||
accumColorSum = Eigen::Vector3f::Zero();
|
|
||||||
accumWeight = 0.0f;
|
|
||||||
lastUpdateFrame = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
PointType getHalfSize() const {
|
PointType getHalfSize() const {
|
||||||
return PointType(size * 0.5f, size * 0.5f, size * 0.5f);
|
return PointType(size * 0.5f, size * 0.5f, size * 0.5f);
|
||||||
}
|
}
|
||||||
@@ -152,11 +127,6 @@ private:
|
|||||||
|
|
||||||
Eigen::Vector3f skylight_ = {0.1f, 0.1f, 0.1f};
|
Eigen::Vector3f skylight_ = {0.1f, 0.1f, 0.1f};
|
||||||
Eigen::Vector3f backgroundColor_ = {0.53f, 0.81f, 0.92f};
|
Eigen::Vector3f backgroundColor_ = {0.53f, 0.81f, 0.92f};
|
||||||
|
|
||||||
// Parallel Lightmap (Sun) settings
|
|
||||||
Eigen::Vector3f sunDirection_ = {0.0f, 1.0f, 0.0f}; // Default straight up
|
|
||||||
Eigen::Vector3f sunColor_ = {1.0f, 1.0f, 0.9f};
|
|
||||||
float sunIntensity_ = 1.0f; // 0.0 = disabled
|
|
||||||
|
|
||||||
// Addressable Maps
|
// Addressable Maps
|
||||||
std::unique_ptr<std::mutex> mapMutex_;
|
std::unique_ptr<std::mutex> mapMutex_;
|
||||||
@@ -169,9 +139,6 @@ private:
|
|||||||
std::map<std::pair<int, int>, std::shared_ptr<Mesh>> meshCache_;
|
std::map<std::pair<int, int>, std::shared_ptr<Mesh>> meshCache_;
|
||||||
std::set<std::pair<int, int>> dirtyMeshes_;
|
std::set<std::pair<int, int>> dirtyMeshes_;
|
||||||
int nextSubIdGenerator_ = 1;
|
int nextSubIdGenerator_ = 1;
|
||||||
|
|
||||||
// Temporal Coherence
|
|
||||||
int frameCount_ = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline IndexType getColorIndex(const Eigen::Vector3f& color) {
|
inline IndexType getColorIndex(const Eigen::Vector3f& color) {
|
||||||
@@ -287,6 +254,11 @@ private:
|
|||||||
|
|
||||||
uint8_t getOctant(const PointType& point, const PointType& center) const {
|
uint8_t getOctant(const PointType& point, const PointType& center) const {
|
||||||
return (point[0] >= center[0]) | ((point[1] >= center[1]) << 1) | ((point[2] >= center[2]) << 2);
|
return (point[0] >= center[0]) | ((point[1] >= center[1]) << 1) | ((point[2] >= center[2]) << 2);
|
||||||
|
// uint8_t octant = 0;
|
||||||
|
// if (point[0] >= center[0]) octant |= 1;
|
||||||
|
// if (point[1] >= center[1]) octant |= 2;
|
||||||
|
// if (point[2] >= center[2]) octant |= 4;
|
||||||
|
// return octant;
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBox createChildBounds(const OctreeNode* node, uint8_t octant) const {
|
BoundingBox createChildBounds(const OctreeNode* node, uint8_t octant) const {
|
||||||
@@ -626,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;
|
||||||
@@ -705,52 +677,26 @@ private:
|
|||||||
int maxBounces, bool globalIllumination, bool useLod) {
|
int maxBounces, bool globalIllumination, bool useLod) {
|
||||||
if (bounces > maxBounces) return globalIllumination ? skylight_ : Eigen::Vector3f::Zero();
|
if (bounces > maxBounces) return globalIllumination ? skylight_ : Eigen::Vector3f::Zero();
|
||||||
|
|
||||||
PointType currOrig = rayOrig;
|
auto hit = voxelTraverse(rayOrig, rayDir, std::numeric_limits<float>::max(), useLod);
|
||||||
std::shared_ptr<NodeData> hit = nullptr;
|
|
||||||
PointType hitPoint;
|
|
||||||
PointType normal;
|
|
||||||
float t = 0.0f;
|
|
||||||
|
|
||||||
// Loop to skip internal boundaries of transmissive objects
|
|
||||||
int safety = 0;
|
|
||||||
while(safety++ < 5000) {
|
|
||||||
hit = voxelTraverse(currOrig, rayDir, std::numeric_limits<float>::max(), useLod);
|
|
||||||
if (!hit) break;
|
|
||||||
|
|
||||||
Ray ray(currOrig, rayDir);
|
|
||||||
rayCubeIntersect(ray, hit.get(), t, normal, hitPoint);
|
|
||||||
|
|
||||||
Material objMat = getMaterial(hit->materialIdx);
|
|
||||||
|
|
||||||
if (objMat.transmission > 0.0f && rayDir.dot(normal) > 0.0f) {
|
|
||||||
float coordMax = hitPoint.cwiseAbs().maxCoeff();
|
|
||||||
float rayOffset = std::max(1e-4f, 1e-5f * coordMax);
|
|
||||||
PointType checkPos = hitPoint + rayDir * (rayOffset * 2.0f);
|
|
||||||
|
|
||||||
auto nextNode = find(checkPos);
|
|
||||||
|
|
||||||
if (nextNode && nextNode->active && nextNode->materialIdx == hit->materialIdx) {
|
|
||||||
currOrig = checkPos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hit) {
|
if (!hit) {
|
||||||
if (bounces == 0) return backgroundColor_;
|
if (bounces == 0) return backgroundColor_;
|
||||||
return globalIllumination ? skylight_ : Eigen::Vector3f::Zero();
|
return globalIllumination ? skylight_ : Eigen::Vector3f::Zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto obj = hit;
|
auto obj = hit;
|
||||||
Eigen::Vector3f calculatedColor = Eigen::Vector3f::Zero();
|
|
||||||
|
PointType hitPoint;
|
||||||
|
PointType normal;
|
||||||
|
float t = 0.0f;
|
||||||
|
Ray ray(rayOrig, rayDir);
|
||||||
|
rayCubeIntersect(ray, obj.get(), t, normal, hitPoint);
|
||||||
|
|
||||||
Eigen::Vector3f objColor = getColor(obj->colorIdx);
|
Eigen::Vector3f objColor = getColor(obj->colorIdx);
|
||||||
Material objMat = getMaterial(obj->materialIdx);
|
Material objMat = getMaterial(obj->materialIdx);
|
||||||
|
|
||||||
Eigen::Vector3f lighting = globalIllumination ? skylight_ : Eigen::Vector3f::Zero();
|
Eigen::Vector3f finalColor = globalIllumination ? skylight_ : Eigen::Vector3f::Zero();
|
||||||
if (objMat.emittance > 0.0f) {
|
if (objMat.emittance > 0.0f) {
|
||||||
lighting += objColor * objMat.emittance;
|
finalColor += objColor * objMat.emittance;
|
||||||
}
|
}
|
||||||
|
|
||||||
float roughness = std::clamp(objMat.roughness, 0.01f, 1.0f);
|
float roughness = std::clamp(objMat.roughness, 0.01f, 1.0f);
|
||||||
@@ -768,36 +714,6 @@ private:
|
|||||||
|
|
||||||
Eigen::Vector3f F0 = Eigen::Vector3f::Constant(0.04f);
|
Eigen::Vector3f F0 = Eigen::Vector3f::Constant(0.04f);
|
||||||
F0 = F0 * (1.0f - metallic) + objColor * metallic;
|
F0 = F0 * (1.0f - metallic) + objColor * metallic;
|
||||||
|
|
||||||
if (sunIntensity_ > 0.0f) {
|
|
||||||
PointType L = sunDirection_.normalized();
|
|
||||||
PointType shadowOrig = hitPoint + n_eff * rayOffset;
|
|
||||||
|
|
||||||
auto shadowHit = voxelTraverse(shadowOrig, L, std::numeric_limits<float>::max(), useLod);
|
|
||||||
|
|
||||||
if (!shadowHit) {
|
|
||||||
float NdotL = std::max(0.001f, n_eff.dot(L));
|
|
||||||
PointType H_sun = (V + L).normalized();
|
|
||||||
float VdotH_sun = std::max(0.001f, V.dot(H_sun));
|
|
||||||
float NdotH_sun = std::max(0.001f, n_eff.dot(H_sun));
|
|
||||||
|
|
||||||
Eigen::Vector3f F_sun = F0 + (Eigen::Vector3f::Constant(1.0f) - F0) * std::pow(std::max(0.0f, 1.0f - VdotH_sun), 5.0f);
|
|
||||||
|
|
||||||
float alpha2 = roughness * roughness * roughness * roughness;
|
|
||||||
float denom = (NdotH_sun * NdotH_sun * (alpha2 - 1.0f) + 1.0f);
|
|
||||||
float D = alpha2 / (M_PI * denom * denom);
|
|
||||||
|
|
||||||
float k = (roughness + 1.0f) * (roughness + 1.0f) / 8.0f;
|
|
||||||
float G = (NdotL / (NdotL * (1.0f - k) + k)) * (cosThetaI / (cosThetaI * (1.0f - k) + k));
|
|
||||||
|
|
||||||
Eigen::Vector3f spec = (F_sun * D * G) / (4.0f * NdotL * cosThetaI + EPSILON);
|
|
||||||
|
|
||||||
Eigen::Vector3f kD = (Eigen::Vector3f::Constant(1.0f) - F_sun) * (1.0f - metallic);
|
|
||||||
Eigen::Vector3f diff = kD.cwiseProduct(objColor) / M_PI;
|
|
||||||
|
|
||||||
lighting += (diff + spec).cwiseProduct(sunColor_) * sunIntensity_ * NdotL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PointType H = sampleGGX(n_eff, roughness, rngState);
|
PointType H = sampleGGX(n_eff, roughness, rngState);
|
||||||
float VdotH = std::max(0.001f, V.dot(H));
|
float VdotH = std::max(0.001f, V.dot(H));
|
||||||
@@ -864,74 +780,22 @@ private:
|
|||||||
secondColor = W_second.cwiseProduct(traceRay(secondOrigin, secondDir, bounces + 1, rngState, maxBounces, globalIllumination, useLod));
|
secondColor = W_second.cwiseProduct(traceRay(secondOrigin, secondDir, bounces + 1, rngState, maxBounces, globalIllumination, useLod));
|
||||||
}
|
}
|
||||||
|
|
||||||
calculatedColor = lighting + specColor + secondColor;
|
return finalColor + specColor + secondColor;
|
||||||
} else {
|
} else {
|
||||||
float totalLum = lumSpec + lumSecond;
|
float totalLum = lumSpec + lumSecond;
|
||||||
if (totalLum < 0.0001f) calculatedColor = lighting;
|
if (totalLum < 0.0001f) return finalColor;
|
||||||
else {
|
|
||||||
float pSpec = lumSpec / totalLum;
|
float pSpec = lumSpec / totalLum;
|
||||||
float roll = float(rand_r(&rngState)) / float(RAND_MAX);
|
float roll = float(rand_r(&rngState)) / float(RAND_MAX);
|
||||||
|
|
||||||
if (roll < pSpec) {
|
if (roll < pSpec) {
|
||||||
Eigen::Vector3f sample = traceRay(hitPoint + n_eff * rayOffset, specDir, bounces + 1, rngState, maxBounces, globalIllumination, useLod);
|
Eigen::Vector3f sample = traceRay(hitPoint + n_eff * rayOffset, specDir, bounces + 1, rngState, maxBounces, globalIllumination, useLod);
|
||||||
calculatedColor = lighting + (W_spec / std::max(EPSILON, pSpec)).cwiseProduct(sample);
|
return finalColor + (W_spec / std::max(EPSILON, pSpec)).cwiseProduct(sample);
|
||||||
} else {
|
} else {
|
||||||
Eigen::Vector3f sample = traceRay(secondOrigin, secondDir, bounces + 1, rngState, maxBounces, globalIllumination, useLod);
|
Eigen::Vector3f sample = traceRay(secondOrigin, secondDir, bounces + 1, rngState, maxBounces, globalIllumination, useLod);
|
||||||
calculatedColor = lighting + (W_second / std::max(EPSILON, 1.0f - pSpec)).cwiseProduct(sample);
|
return finalColor + (W_second / std::max(EPSILON, 1.0f - pSpec)).cwiseProduct(sample);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::lock_guard<std::mutex> lock(obj->lightMutex);
|
|
||||||
|
|
||||||
// 1. Calculate Capacity based on Material Properties
|
|
||||||
// How many frames do we keep in the "bucket" before removing the oldest?
|
|
||||||
|
|
||||||
float capacity = 60.0f; // Base capacity (e.g. Diffuse surfaces)
|
|
||||||
|
|
||||||
// REFLECTIONS: Fade out VERY fast (low capacity) to prevent ghosting on moving objects
|
|
||||||
// If it's metallic and smooth, we might only keep the last 2-5 frames.
|
|
||||||
float reflectionIntensity = metallic * (1.0f - roughness);
|
|
||||||
if (reflectionIntensity > 0.0f) {
|
|
||||||
// Reduces capacity down to a minimum of 2.0 frames for perfect mirrors
|
|
||||||
capacity = std::max(2.0f, capacity * (1.0f - reflectionIntensity));
|
|
||||||
}
|
|
||||||
|
|
||||||
// REFRACTIONS: Keep longer (high capacity) to stabilize noise
|
|
||||||
if (transmission > 0.0f) {
|
|
||||||
capacity += transmission * 40.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Handle Time Gaps (Age Limit)
|
|
||||||
// If this node wasn't hit last frame, the object or camera moved.
|
|
||||||
// Decay aggressively.
|
|
||||||
if (obj->lastUpdateFrame != -1) {
|
|
||||||
int framesMissed = frameCount_ - obj->lastUpdateFrame;
|
|
||||||
if (framesMissed > 1) {
|
|
||||||
// Divide accumulated weight by 2 for every missed frame
|
|
||||||
float decay = std::pow(0.5f, (float)framesMissed);
|
|
||||||
obj->accumColorSum *= decay;
|
|
||||||
obj->accumWeight *= decay;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. The "Leaky Bucket" Eviction
|
|
||||||
// If the bucket is full (weight >= capacity), we "pour out" the average old value
|
|
||||||
// before adding the new one.
|
|
||||||
if (obj->accumWeight >= capacity) {
|
|
||||||
// Scale down the sum and weight so that adding 1.0 brings it back to capacity
|
|
||||||
float keepRatio = (capacity - 1.0f) / obj->accumWeight;
|
|
||||||
obj->accumColorSum *= keepRatio;
|
|
||||||
obj->accumWeight *= keepRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Accumulate
|
|
||||||
obj->accumColorSum += calculatedColor;
|
|
||||||
obj->accumWeight += 1.0f;
|
|
||||||
obj->lastUpdateFrame = frameCount_;
|
|
||||||
|
|
||||||
// 5. Return the Average
|
|
||||||
// This average represents the last 'capacity' frames moving average
|
|
||||||
return obj->accumColorSum / obj->accumWeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearNode(OctreeNode* node) {
|
void clearNode(OctreeNode* node) {
|
||||||
@@ -1059,7 +923,6 @@ private:
|
|||||||
writeVal(out, pt->size);
|
writeVal(out, pt->size);
|
||||||
writeVal(out, pt->colorIdx);
|
writeVal(out, pt->colorIdx);
|
||||||
writeVal(out, pt->materialIdx);
|
writeVal(out, pt->materialIdx);
|
||||||
// accumulatedLight is transient cache, do not serialize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node->isLeaf) {
|
if (!node->isLeaf) {
|
||||||
@@ -1101,8 +964,6 @@ private:
|
|||||||
readVal(in, pt->size);
|
readVal(in, pt->size);
|
||||||
readVal(in, pt->colorIdx);
|
readVal(in, pt->colorIdx);
|
||||||
readVal(in, pt->materialIdx);
|
readVal(in, pt->materialIdx);
|
||||||
pt->accumulatedLight = Eigen::Vector3f::Zero(); // Initialize clean
|
|
||||||
pt->lastUpdateFrame = -1;
|
|
||||||
node->points.push_back(pt);
|
node->points.push_back(pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1383,7 +1244,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalWeight > EPSILON) {
|
if (totalWeight > 1e-6) {
|
||||||
return {density, accumulatedColor / totalWeight};
|
return {density, accumulatedColor / totalWeight};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1450,12 +1311,6 @@ public:
|
|||||||
return backgroundColor_;
|
return backgroundColor_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSun(const Eigen::Vector3f& direction, const Eigen::Vector3f& color, float intensity) {
|
|
||||||
sunDirection_ = direction.normalized();
|
|
||||||
sunColor_ = color;
|
|
||||||
sunIntensity_ = intensity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLODFalloff(float rate) { lodFalloffRate_ = rate; }
|
void setLODFalloff(float rate) { lodFalloffRate_ = rate; }
|
||||||
void setLODMinDistance(float dist) { lodMinDistance_ = dist; }
|
void setLODMinDistance(float dist) { lodMinDistance_ = dist; }
|
||||||
void setMaxDistance(float dist) { maxDistance_ = dist; }
|
void setMaxDistance(float dist) { maxDistance_ = dist; }
|
||||||
@@ -1498,10 +1353,6 @@ public:
|
|||||||
writeVec3(out, skylight_);
|
writeVec3(out, skylight_);
|
||||||
writeVec3(out, backgroundColor_);
|
writeVec3(out, backgroundColor_);
|
||||||
|
|
||||||
writeVec3(out, sunDirection_);
|
|
||||||
writeVec3(out, sunColor_);
|
|
||||||
writeVal(out, sunIntensity_);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(*mapMutex_);
|
std::lock_guard<std::mutex> lock(*mapMutex_);
|
||||||
size_t cMapSize = colorMap_.size();
|
size_t cMapSize = colorMap_.size();
|
||||||
@@ -1549,10 +1400,6 @@ public:
|
|||||||
readVec3(in, skylight_);
|
readVec3(in, skylight_);
|
||||||
readVec3(in, backgroundColor_);
|
readVec3(in, backgroundColor_);
|
||||||
|
|
||||||
readVec3(in, sunDirection_);
|
|
||||||
readVec3(in, sunColor_);
|
|
||||||
readVal(in, sunIntensity_);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(*mapMutex_);
|
std::lock_guard<std::mutex> lock(*mapMutex_);
|
||||||
colorMap_.clear();
|
colorMap_.clear();
|
||||||
@@ -1812,7 +1659,6 @@ public:
|
|||||||
|
|
||||||
frame renderFrame(const Camera& cam, int height, int width, frame::colormap colorformat = frame::colormap::RGB, int samplesPerPixel = 2,
|
frame renderFrame(const Camera& cam, int height, int width, frame::colormap colorformat = frame::colormap::RGB, int samplesPerPixel = 2,
|
||||||
int maxBounces = 4, bool globalIllumination = false, bool useLod = true) {
|
int maxBounces = 4, bool globalIllumination = false, bool useLod = true) {
|
||||||
frameCount_++; // Advance time
|
|
||||||
generateLODs();
|
generateLODs();
|
||||||
PointType origin = cam.origin;
|
PointType origin = cam.origin;
|
||||||
PointType dir = cam.direction.normalized();
|
PointType dir = cam.direction.normalized();
|
||||||
@@ -1834,7 +1680,7 @@ public:
|
|||||||
for (int y = 0; y < height; ++y) {
|
for (int y = 0; y < height; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
int pidx = (y * width + x);
|
int pidx = (y * width + x);
|
||||||
uint32_t seed = pidx * 1973 + 9277 + frameCount_ * 12345;
|
uint32_t seed = pidx * 1973 + 9277;
|
||||||
int idx = pidx * channels;
|
int idx = pidx * channels;
|
||||||
|
|
||||||
float px = (2.0f * (x + 0.5f) / width - 1.0f) * tanfovx;
|
float px = (2.0f * (x + 0.5f) / width - 1.0f) * tanfovx;
|
||||||
@@ -1878,7 +1724,6 @@ public:
|
|||||||
|
|
||||||
frame fastRenderFrame(const Camera& cam, int height, int width, frame::colormap colorformat = frame::colormap::RGB) {
|
frame fastRenderFrame(const Camera& cam, int height, int width, frame::colormap colorformat = frame::colormap::RGB) {
|
||||||
//TIME_FUNCTION;
|
//TIME_FUNCTION;
|
||||||
frameCount_++;
|
|
||||||
generateLODs();
|
generateLODs();
|
||||||
PointType origin = cam.origin;
|
PointType origin = cam.origin;
|
||||||
PointType dir = cam.direction.normalized();
|
PointType dir = cam.direction.normalized();
|
||||||
@@ -1895,7 +1740,7 @@ public:
|
|||||||
const float tanfovy = tanHalfFov;
|
const float tanfovy = tanHalfFov;
|
||||||
const float tanfovx = tanHalfFov * aspect;
|
const float tanfovx = tanHalfFov * aspect;
|
||||||
|
|
||||||
const PointType globalLightDir = sunDirection_.normalized();
|
const PointType globalLightDir = (-cam.direction * 0.2f).normalized();
|
||||||
const float fogStart = 1000.0f;
|
const float fogStart = 1000.0f;
|
||||||
const float minVisibility = 0.2f;
|
const float minVisibility = 0.2f;
|
||||||
|
|
||||||
@@ -1927,12 +1772,9 @@ public:
|
|||||||
if (objMat.emittance > 0.0f) {
|
if (objMat.emittance > 0.0f) {
|
||||||
color = color * objMat.emittance;
|
color = color * objMat.emittance;
|
||||||
} else {
|
} else {
|
||||||
// Use Sun Direction for quick shading
|
|
||||||
float diffuse = std::max(0.0f, normal.dot(globalLightDir));
|
float diffuse = std::max(0.0f, normal.dot(globalLightDir));
|
||||||
float ambient = 0.35f;
|
float ambient = 0.35f;
|
||||||
float intensity = std::min(1.0f, ambient + diffuse * 0.65f);
|
float intensity = std::min(1.0f, ambient + diffuse * 0.65f);
|
||||||
if (sunIntensity_ > 0.0f) intensity = std::min(1.0f, ambient + diffuse * sunIntensity_);
|
|
||||||
|
|
||||||
color = color * intensity;
|
color = color * intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1956,7 +1798,6 @@ public:
|
|||||||
frame renderFrameTimed(const Camera& cam, int height, int width, frame::colormap colorformat = frame::colormap::RGB,
|
frame renderFrameTimed(const Camera& cam, int height, int width, frame::colormap colorformat = frame::colormap::RGB,
|
||||||
double maxTimeSeconds = 0.16, int maxBounces = 4, bool globalIllumination = false, bool useLod = true) {
|
double maxTimeSeconds = 0.16, int maxBounces = 4, bool globalIllumination = false, bool useLod = true) {
|
||||||
auto startTime = std::chrono::high_resolution_clock::now();
|
auto startTime = std::chrono::high_resolution_clock::now();
|
||||||
frameCount_++;
|
|
||||||
|
|
||||||
generateLODs();
|
generateLODs();
|
||||||
PointType origin = cam.origin;
|
PointType origin = cam.origin;
|
||||||
@@ -1975,7 +1816,7 @@ public:
|
|||||||
const float tanfovy = tanHalfFov;
|
const float tanfovy = tanHalfFov;
|
||||||
const float tanfovx = tanHalfFov * aspect;
|
const float tanfovx = tanHalfFov * aspect;
|
||||||
|
|
||||||
const PointType globalLightDir = sunDirection_.normalized();
|
const PointType globalLightDir = (-cam.direction * 0.2f).normalized();
|
||||||
const float fogStart = 1000.0f;
|
const float fogStart = 1000.0f;
|
||||||
const float minVisibility = 0.2f;
|
const float minVisibility = 0.2f;
|
||||||
|
|
||||||
@@ -2009,7 +1850,6 @@ public:
|
|||||||
float diffuse = std::max(0.0f, normal.dot(globalLightDir));
|
float diffuse = std::max(0.0f, normal.dot(globalLightDir));
|
||||||
float ambient = 0.35f;
|
float ambient = 0.35f;
|
||||||
float intensity = std::min(1.0f, ambient + diffuse * 0.65f);
|
float intensity = std::min(1.0f, ambient + diffuse * 0.65f);
|
||||||
if (sunIntensity_ > 0.0f) intensity = std::min(1.0f, ambient + diffuse * sunIntensity_);
|
|
||||||
color = color * intensity;
|
color = color * intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2070,7 +1910,7 @@ public:
|
|||||||
rayDir.normalize();
|
rayDir.normalize();
|
||||||
|
|
||||||
uint32_t pass = currentOffset / totalPixels;
|
uint32_t pass = currentOffset / totalPixels;
|
||||||
uint32_t seed = pidx * 1973 + pass * 12345 + localSeed + frameCount_ * 777;
|
uint32_t seed = pidx * 1973 + pass * 12345 + localSeed;
|
||||||
|
|
||||||
Eigen::Vector3f pbrColor = traceRay(origin, rayDir, 0, seed, maxBounces, globalIllumination, useLod);
|
Eigen::Vector3f pbrColor = traceRay(origin, rayDir, 0, seed, maxBounces, globalIllumination, useLod);
|
||||||
|
|
||||||
@@ -2439,8 +2279,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
size = 0;
|
size = 0;
|
||||||
frameCount_ = 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
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