it works
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
@@ -20,112 +21,547 @@
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
int main() {
|
||||
// Define octree boundaries (world space)
|
||||
using PointType = Eigen::Matrix<float, 3, 1>;
|
||||
PointType minBound(-2.0f, -2.0f, -2.0f);
|
||||
PointType maxBound(2.0f, 2.0f, 2.0f);
|
||||
|
||||
// Create octree
|
||||
Octree<int> octree(minBound, maxBound, 16, 8); // max 16 points per node, max depth 8
|
||||
|
||||
// Create green sphere (center at origin, radius 1.0)
|
||||
float radius = 1.0f;
|
||||
int pointCount = 0;
|
||||
Eigen::Vector3f greenColor(0.0f, 1.0f, 0.0f); // green
|
||||
|
||||
// Add points on sphere surface (simplified representation)
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
// Spherical coordinates
|
||||
float u = static_cast<float>(rand()) / RAND_MAX;
|
||||
float v = static_cast<float>(rand()) / RAND_MAX;
|
||||
|
||||
// Convert to spherical coordinates
|
||||
float theta = 2.0f * M_PI * u; // azimuth
|
||||
float phi = acos(2.0f * v - 1.0f); // polar
|
||||
|
||||
// Convert to Cartesian coordinates
|
||||
float x = radius * sin(phi) * cos(theta);
|
||||
float y = radius * sin(phi) * sin(theta);
|
||||
float z = radius * cos(phi);
|
||||
|
||||
PointType pos(x, y, z);
|
||||
|
||||
// Set point data with larger size for visibility
|
||||
// Note: The third parameter is size, which should be radius squared for intersection test
|
||||
octree.set(i, pos, true, greenColor, 1.0, true);
|
||||
pointCount++;
|
||||
struct defaults {
|
||||
int outWidth = 512;
|
||||
int outHeight = 512;
|
||||
int gridSizecube = 512;
|
||||
PNoise2 noise = PNoise2(42);
|
||||
};
|
||||
|
||||
struct spheredefaults {
|
||||
float centerX = 0.0f;
|
||||
float centerY = 0.0f;
|
||||
float centerZ = 0.0f;
|
||||
float radius = 128.0f;
|
||||
float color[3] = {0.0f, 1.0f, 0.0f};
|
||||
bool light = false;
|
||||
float emittance = 0.0f;
|
||||
float reflection = 0.0f;
|
||||
float refraction = 0.0f;
|
||||
bool fillInside = true;
|
||||
float voxelSize = 1.0f;
|
||||
};
|
||||
|
||||
struct ceilingdefaults {
|
||||
float minX = 0.0f;
|
||||
float maxX = 512.0f;
|
||||
float minZ = 0.0f;
|
||||
float maxZ = 512.0f;
|
||||
float yLevel = 450.0f; // Near the top
|
||||
float spacing = 10.0f; // Distance between light points
|
||||
float color[3] = {1.0f, 1.0f, 1.0f}; // White light
|
||||
float emittance = 5.0f; // Brightness
|
||||
float voxelSize = 2.0f;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
std::mutex PreviewMutex;
|
||||
GLuint textu = 0;
|
||||
bool textureInitialized = false;
|
||||
bool updatePreview = false;
|
||||
bool previewRequested = false;
|
||||
using PointType = Eigen::Matrix<float, 3, 1>;
|
||||
|
||||
void createSphere(const defaults& config, const spheredefaults& sconfig, Octree<int>& grid) {
|
||||
if (!grid.empty()) grid.clear();
|
||||
int minX = std::max(0, (int)(sconfig.centerX - sconfig.radius - 1));
|
||||
int maxX = std::min(config.gridSizecube, (int)(sconfig.centerX + sconfig.radius + 1));
|
||||
int minY = std::max(0, (int)(sconfig.centerY - sconfig.radius - 1));
|
||||
int maxY = std::min(config.gridSizecube, (int)(sconfig.centerY + sconfig.radius + 1));
|
||||
int minZ = std::max(0, (int)(sconfig.centerZ - sconfig.radius - 1));
|
||||
int maxZ = std::min(config.gridSizecube, (int)(sconfig.centerZ + sconfig.radius + 1));
|
||||
|
||||
float radSq = sconfig.radius * sconfig.radius;
|
||||
float innerRadSq = 0.0f;
|
||||
if (!sconfig.fillInside) {
|
||||
float innerR = std::max(0.0f, sconfig.radius - 2.0f);
|
||||
innerRadSq = innerR * innerR;
|
||||
}
|
||||
|
||||
std::cout << "Added " << pointCount << " points to the green sphere." << std::endl;
|
||||
|
||||
// Render parameters
|
||||
int width = 2048;
|
||||
int height = 2048;
|
||||
|
||||
// Set up random number generator for camera positions
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<float> angleDist(0.0f, 2.0f * M_PI);
|
||||
std::uniform_real_distribution<float> elevationDist(0.1f, M_PI - 0.1f); // Avoid poles
|
||||
std::uniform_real_distribution<float> radiusDist(2.0f, 4.0f); // Distance from sphere
|
||||
|
||||
// Generate and save 15 random views
|
||||
const int numViews = 15;
|
||||
|
||||
for (int viewIndex = 0; viewIndex < numViews; ++viewIndex) {
|
||||
std::cout << "\nRendering view " << (viewIndex + 1) << " of " << numViews << "..." << std::endl;
|
||||
|
||||
// Generate random spherical coordinates for camera position
|
||||
float azimuth = angleDist(gen);
|
||||
float elevation = elevationDist(gen);
|
||||
float camRadius = radiusDist(gen);
|
||||
|
||||
// Convert to Cartesian coordinates for camera position
|
||||
float camX = camRadius * sin(elevation) * cos(azimuth);
|
||||
float camY = camRadius * sin(elevation) * sin(azimuth);
|
||||
float camZ = camRadius * cos(elevation);
|
||||
|
||||
// Camera looks at the origin (center of sphere)
|
||||
Vector3f cameraPos(camX, camY, camZ);
|
||||
Vector3f lookAt(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Calculate camera direction (from position to lookAt)
|
||||
Vector3f cameraDir = (lookAt - cameraPos).normalized();
|
||||
|
||||
// Calculate up vector (avoid gimbal lock)
|
||||
Vector3f worldUp(0.0f, 1.0f, 0.0f);
|
||||
Vector3f right = cameraDir.cross(worldUp).normalized();
|
||||
Vector3f cameraUp = right.cross(cameraDir).normalized();
|
||||
|
||||
// Create camera
|
||||
Camera cam(cameraPos, cameraDir, cameraUp, 80);
|
||||
|
||||
// Render frame
|
||||
frame renderedFrame = octree.renderFrame(cam, height, width, frame::colormap::RGB);
|
||||
|
||||
std::cout << "Frame rendered. Dimensions: "
|
||||
<< renderedFrame.getWidth() << "x"
|
||||
<< renderedFrame.getHeight() << std::endl;
|
||||
|
||||
// Save as BMP
|
||||
std::string filename = "output/green_sphere_view_" + std::to_string(viewIndex + 1) + ".bmp";
|
||||
std::cout << "Saving to " << filename << "..." << std::endl;
|
||||
|
||||
if (BMPWriter::saveBMP(filename, renderedFrame)) {
|
||||
std::cout << "Successfully saved view to " << filename << std::endl;
|
||||
Eigen::Vector3f colorVec(sconfig.color[0], sconfig.color[1], sconfig.color[2]);
|
||||
for (int x = minX; x < maxX; ++x) {
|
||||
for (int y = minY; y < maxY; ++y) {
|
||||
for (int z = minZ; z < maxZ; ++z) {
|
||||
float dx = x - sconfig.centerX;
|
||||
float dy = y - sconfig.centerY;
|
||||
float dz = z - sconfig.centerZ;
|
||||
float distSq = dx*dx + dy*dy + dz*dz;
|
||||
|
||||
bool solid = distSq <= radSq;
|
||||
|
||||
if (solid) {
|
||||
if (!(sconfig.fillInside) && distSq < innerRadSq) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PointType pos((float)x, (float)y, (float)z);
|
||||
grid.set(1,pos, true, colorVec, sconfig.voxelSize, true, sconfig.light, sconfig.emittance, sconfig.refraction, sconfig.reflection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addCeilingLight(const defaults& config, const ceilingdefaults& ceilingconf, Octree<int>& grid) {
|
||||
if (!ceilingconf.enabled) return;
|
||||
|
||||
Eigen::Vector3f colorVec(ceilingconf.color[0], ceilingconf.color[1], ceilingconf.color[2]);
|
||||
|
||||
// Iterate over X and Z within bounds, stepping by 'spacing'
|
||||
for (float x = ceilingconf.minX; x <= ceilingconf.maxX; x += ceilingconf.spacing) {
|
||||
for (float z = ceilingconf.minZ; z <= ceilingconf.maxZ; z += ceilingconf.spacing) {
|
||||
|
||||
// Print camera position for reference
|
||||
std::cout << "Camera position: (" << camX << ", " << camY << ", " << camZ << ")" << std::endl;
|
||||
} else {
|
||||
std::cerr << "Failed to save BMP file: " << filename << std::endl;
|
||||
PointType pos(x, ceilingconf.yLevel, z);
|
||||
|
||||
grid.set(2, pos, true, colorVec, ceilingconf.voxelSize, true, true, ceilingconf.emittance, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
grid.printStats();
|
||||
}
|
||||
|
||||
void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
||||
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||
updatePreview = true;
|
||||
frame currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB);
|
||||
|
||||
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);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textu);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(),
|
||||
0, GL_RGB, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data());
|
||||
//BMPWriter::saveBMP("output/frameoutput.bmp", currentPreviewFrame);
|
||||
updatePreview = false;
|
||||
textureInitialized = true;
|
||||
}
|
||||
|
||||
void resetView(Camera& cam, float gridSize) {
|
||||
cam.origin = Vector3f(gridSize * 1.5f, gridSize * 1.5f, gridSize * 1.5f);
|
||||
Vector3f center(gridSize / 2.0f, gridSize / 2.0f, gridSize / 2.0f);
|
||||
cam.lookAt(center);
|
||||
}
|
||||
|
||||
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()) {
|
||||
std::cerr << "gui stuff is dumb in c++." << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
// COPIED VERBATIM FROM IMGUI.
|
||||
#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), "voxelgrid live renderer", 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);
|
||||
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
|
||||
defaults config;
|
||||
PointType minBound(-config.gridSizecube, -config.gridSizecube, -config.gridSizecube);
|
||||
PointType maxBound(config.gridSizecube, config.gridSizecube, config.gridSizecube);
|
||||
Octree<int> grid(minBound, maxBound, 16, 16);
|
||||
bool gridInitialized = false;
|
||||
float ghalf = config.gridSizecube / 2.f;
|
||||
Camera cam(PointType(ghalf, ghalf, ghalf), PointType(0,0,1), PointType(0,1,0), 80);
|
||||
spheredefaults sphereConf;
|
||||
ceilingdefaults ceilingConf;
|
||||
|
||||
bool autoRotate = false;
|
||||
bool autoRotateView = false;
|
||||
float rotationSpeedX = 0.1f;
|
||||
float rotationSpeedY = 0.07f;
|
||||
float rotationSpeedZ = 0.05f;
|
||||
float autoRotationTime = 0.0f;
|
||||
PointType initialViewDir(0, 0, 1);
|
||||
float rotationRadius = 255.0f;
|
||||
float yawSpeed = 0.5f;
|
||||
float pitchSpeed = 0.3f;
|
||||
float rollSpeed = 0.2f;
|
||||
float autoRotationAngle = 0.0f;
|
||||
PointType initialUpDir(0, 1, 0);
|
||||
float camX = 0.0f;
|
||||
float camY = 0.0f;
|
||||
float camZ = 0.0f;
|
||||
float camvX = 0.f;
|
||||
float camvY = 0.f;
|
||||
float camvZ = 0.f;
|
||||
float camspeed = 50;
|
||||
|
||||
// Keyboard state tracking
|
||||
std::map<int, bool> keyStates;
|
||||
bool mouseCaptured = false;
|
||||
double lastMouseX = 0, lastMouseY = 0;
|
||||
float deltaTime = 0.016f;
|
||||
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
double currentTime = glfwGetTime();
|
||||
static double lastFrameTime = currentTime;
|
||||
deltaTime = currentTime - lastFrameTime;
|
||||
lastFrameTime = currentTime;
|
||||
|
||||
glfwPollEvents();
|
||||
|
||||
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
|
||||
keyStates[i] = (glfwGetKey(window, i) == GLFW_PRESS);
|
||||
}
|
||||
|
||||
// Small delay to ensure unique random seeds
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
// Camera movement - WASD + QE + ZX
|
||||
float actualMoveSpeed = deltaTime;
|
||||
float actualRotateSpeed = deltaTime;
|
||||
|
||||
if (keyStates[GLFW_KEY_W]) {
|
||||
cam.moveForward(actualMoveSpeed);
|
||||
previewRequested = true;
|
||||
}
|
||||
if (keyStates[GLFW_KEY_S]) {
|
||||
cam.moveBackward(actualMoveSpeed);
|
||||
previewRequested = true;
|
||||
}
|
||||
if (keyStates[GLFW_KEY_A]) {
|
||||
cam.moveLeft(actualMoveSpeed);
|
||||
previewRequested = true;
|
||||
}
|
||||
if (keyStates[GLFW_KEY_D]) {
|
||||
cam.moveRight(actualMoveSpeed);
|
||||
previewRequested = true;
|
||||
}
|
||||
if (keyStates[GLFW_KEY_Z]) {
|
||||
cam.moveUp(actualMoveSpeed);
|
||||
previewRequested = true;
|
||||
}
|
||||
if (keyStates[GLFW_KEY_X]) {
|
||||
cam.moveDown(actualMoveSpeed);
|
||||
previewRequested = true;
|
||||
}
|
||||
if (keyStates[GLFW_KEY_Q]) {
|
||||
cam.rotateYaw(actualRotateSpeed);
|
||||
previewRequested = true;
|
||||
}
|
||||
if (keyStates[GLFW_KEY_R]) {
|
||||
cam.rotateYaw(-actualRotateSpeed);
|
||||
previewRequested = true;
|
||||
}
|
||||
|
||||
// Update camera position and view direction variables for UI
|
||||
camX = cam.origin[0];
|
||||
camY = cam.origin[1];
|
||||
camZ = cam.origin[2];
|
||||
|
||||
camvX = cam.direction[0];
|
||||
camvY = cam.direction[1];
|
||||
camvZ = cam.direction[2];
|
||||
camspeed = cam.movementSpeed;
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
{
|
||||
ImGui::Begin("Controls");
|
||||
|
||||
ImGui::Text("Sphere Parameters");
|
||||
float pos[3] = { sphereConf.centerX, sphereConf.centerY, sphereConf.centerZ };
|
||||
if (ImGui::DragFloat3("Center (X,Y,Z)", pos, 1.0f, 0.0f, (float)config.gridSizecube)) {
|
||||
sphereConf.centerX = pos[0];
|
||||
sphereConf.centerY = pos[1];
|
||||
sphereConf.centerZ = pos[2];
|
||||
}
|
||||
|
||||
ImGui::DragFloat("Radius", &sphereConf.radius, 0.5f, 1.0f, 250.0f);
|
||||
ImGui::ColorEdit3("Color", sphereConf.color);
|
||||
ImGui::DragFloat("Voxel Size", &sphereConf.voxelSize, 0.1f, 0.1f, 5.0f);
|
||||
|
||||
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("Ceiling Light Parameters", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Checkbox("Enable Ceiling Light", &ceilingConf.enabled);
|
||||
ImGui::DragFloat("Height (Y)", &ceilingConf.yLevel, 1.0f, 0.0f, (float)config.gridSizecube);
|
||||
ImGui::DragFloat("Spacing", &ceilingConf.spacing, 0.5f, 1.0f, 100.0f);
|
||||
ImGui::DragFloat("Light Emittance", &ceilingConf.emittance, 0.1f, 0.0f, 100.0f);
|
||||
ImGui::ColorEdit3("Light Color", ceilingConf.color);
|
||||
ImGui::DragFloat("Light Voxel Size", &ceilingConf.voxelSize, 0.1f, 0.1f, 10.0f);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("Create Sphere & Render")) {
|
||||
createSphere(config, sphereConf, grid);
|
||||
grid.printStats();
|
||||
addCeilingLight(config, ceilingConf, grid);
|
||||
gridInitialized = true;
|
||||
|
||||
resetView(cam, config.gridSizecube);
|
||||
|
||||
livePreview(grid, config, cam);
|
||||
ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight));
|
||||
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
{
|
||||
ImGui::Begin("Preview");
|
||||
|
||||
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::End();
|
||||
}
|
||||
|
||||
{
|
||||
ImGui::Begin("controls");
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Camera Controls:");
|
||||
|
||||
float maxSliderValueX = config.gridSizecube;
|
||||
float maxSliderValueY = config.gridSizecube;
|
||||
float maxSliderValueZ = config.gridSizecube;
|
||||
float maxSliderValueRotation = 360.0f;
|
||||
|
||||
ImGui::Text("Position (0 to grid size²):");
|
||||
if (ImGui::SliderFloat("Camera X", &camX, 0.0f, maxSliderValueX)) {
|
||||
cam.origin[0] = camX;
|
||||
}
|
||||
if (ImGui::SliderFloat("Camera Y", &camY, 0.0f, maxSliderValueY)) {
|
||||
cam.origin[1] = camY;
|
||||
}
|
||||
if (ImGui::SliderFloat("Camera Z", &camZ, 0.0f, maxSliderValueZ)) {
|
||||
cam.origin[2] = camZ;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("View Direction:");
|
||||
if (ImGui::SliderFloat("Camera View X", &camvX, -1.0f, 1.0f)) {
|
||||
cam.direction[0] = camvX;
|
||||
}
|
||||
if (ImGui::SliderFloat("Camera View Y", &camvY, -1.0f, 1.0f)) {
|
||||
cam.direction[1] = camvY;
|
||||
}
|
||||
if (ImGui::SliderFloat("Camera View Z", &camvZ, -1.0f, 1.0f)) {
|
||||
cam.direction[2] = camvZ;
|
||||
}
|
||||
|
||||
if (ImGui::SliderFloat("Camera Speed", &camspeed, 1, 100)) {
|
||||
cam.movementSpeed = camspeed;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Current Camera Position:");
|
||||
ImGui::Text("X: %.2f, Y: %.2f, Z: %.2f",
|
||||
cam.origin[0],
|
||||
cam.origin[1],
|
||||
cam.origin[2]);
|
||||
|
||||
ImGui::Text("Auto-Rotation:");
|
||||
|
||||
// Toggle button for auto-rotation
|
||||
if (ImGui::Button(autoRotate ? "Stop Auto-Rotation" : "Start Auto-Rotation")) {
|
||||
autoRotate = !autoRotate;
|
||||
if (autoRotate) {
|
||||
autoRotationTime = 0.0f;
|
||||
initialViewDir = PointType(camvX, camvY, camvZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Button(autoRotateView ? "Stop Looking Around" : "Start Looking Around")) {
|
||||
autoRotateView = !autoRotateView;
|
||||
if (autoRotateView) {
|
||||
autoRotationAngle = 0.0f;
|
||||
initialViewDir = PointType(camvX, camvY, camvZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (autoRotate) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("(Running)");
|
||||
|
||||
// Calculate new view direction using frame-based timing
|
||||
float angleX = autoRotationTime * rotationSpeedX;
|
||||
float angleY = autoRotationTime * rotationSpeedY;
|
||||
float angleZ = autoRotationTime * rotationSpeedZ;
|
||||
|
||||
camvX = sinf(angleX) * cosf(angleY);
|
||||
camvY = sinf(angleY) * sinf(angleZ);
|
||||
camvZ = cosf(angleX) * cosf(angleZ);
|
||||
|
||||
// Normalize
|
||||
float length = sqrtf(camvX * camvX + camvY * camvY + camvZ * camvZ);
|
||||
if (length > 0.001f) {
|
||||
camvX /= length;
|
||||
camvY /= length;
|
||||
camvZ /= length;
|
||||
}
|
||||
|
||||
// Update camera position
|
||||
camX = config.gridSizecube / 2.0f + rotationRadius * camvX;
|
||||
camY = config.gridSizecube / 2.0f + rotationRadius * camvY;
|
||||
camZ = config.gridSizecube / 2.0f + rotationRadius * camvZ;
|
||||
|
||||
// Update camera
|
||||
cam.origin = PointType(camX, camY, camZ);
|
||||
cam.direction = PointType(camvX, camvY, camvZ);
|
||||
|
||||
// Sliders to control rotation speeds
|
||||
ImGui::SliderFloat("X Speed", &rotationSpeedX, 0.01f, 1.0f);
|
||||
ImGui::SliderFloat("Y Speed", &rotationSpeedY, 0.01f, 1.0f);
|
||||
ImGui::SliderFloat("Z Speed", &rotationSpeedZ, 0.01f, 1.0f);
|
||||
|
||||
// Slider for orbit radius
|
||||
ImGui::SliderFloat("Orbit Radius", &rotationRadius, 10.0f, 200.0f);
|
||||
}
|
||||
|
||||
if (autoRotateView) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), " ACTIVE");
|
||||
|
||||
// Calculate rotation angles using frame-based timing
|
||||
float yaw = autoRotationAngle * yawSpeed * (3.14159f / 180.0f);
|
||||
float pitch = sinf(autoRotationAngle * 0.7f) * pitchSpeed * (3.14159f / 180.0f);
|
||||
|
||||
// Apply rotations
|
||||
PointType forward = initialViewDir;
|
||||
|
||||
// Yaw rotation (around Y axis)
|
||||
float cosYaw = cosf(yaw);
|
||||
float sinYaw = sinf(yaw);
|
||||
PointType tempForward;
|
||||
tempForward[0] = forward[0] * cosYaw + forward[2] * sinYaw;
|
||||
tempForward[1] = forward[1];
|
||||
tempForward[2] = -forward[0] * sinYaw + forward[2] * cosYaw;
|
||||
forward = tempForward;
|
||||
|
||||
// Pitch rotation (around X axis)
|
||||
float cosPitch = cosf(pitch);
|
||||
float sinPitch = sinf(pitch);
|
||||
tempForward[0] = forward[0];
|
||||
tempForward[1] = forward[1] * cosPitch - forward[2] * sinPitch;
|
||||
tempForward[2] = forward[1] * sinPitch + forward[2] * cosPitch;
|
||||
forward = tempForward;
|
||||
|
||||
// Normalize
|
||||
float length = sqrtf(forward[0] * forward[0] + forward[1] * forward[1] + forward[2] * forward[2]);
|
||||
if (length > 0.001f) {
|
||||
forward[0] /= length;
|
||||
forward[1] /= length;
|
||||
forward[2] /= length;
|
||||
}
|
||||
|
||||
// Update view direction
|
||||
camvX = forward[0];
|
||||
camvY = forward[1];
|
||||
camvZ = forward[2];
|
||||
|
||||
// Update camera
|
||||
cam.direction = PointType(camvX, camvY, camvZ);
|
||||
|
||||
// Show current view direction
|
||||
ImGui::Text("Current View: (%.3f, %.3f, %.3f)", camvX, camvY, camvZ);
|
||||
|
||||
// Sliders to control rotation speeds
|
||||
ImGui::SliderFloat("Yaw Speed", &yawSpeed, 0.1f, 5.0f, "%.2f deg/sec");
|
||||
ImGui::SliderFloat("Pitch Speed", &pitchSpeed, 0.0f, 2.0f, "%.2f deg/sec");
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (gridInitialized) livePreview(grid, config, cam);
|
||||
|
||||
ImGui::Render();
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
std::cout << "\nAll " << numViews << " views have been saved to the output directory." << std::endl;
|
||||
// Cleanup
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
glfwDestroyWindow(window);
|
||||
if (textu != 0) {
|
||||
glDeleteTextures(1, &textu);
|
||||
textu = 0;
|
||||
}
|
||||
glfwTerminate();
|
||||
|
||||
FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -13,24 +13,23 @@ struct Camera {
|
||||
Vector3f direction;
|
||||
Vector3f up;
|
||||
float fov;
|
||||
float movementSpeed;
|
||||
float rotationSpeed;
|
||||
|
||||
Camera(const Vector3f& pos, const Vector3f& viewdir, const Vector3f& up, float fov = 80)
|
||||
: origin(pos), direction(viewdir), up(up.normalized()), fov(fov) {}
|
||||
Camera(const Vector3f& pos, const Vector3f& viewdir, const Vector3f& up, float fov = 80,
|
||||
float moveSpeed = 1.0f, float rotSpeed = 0.5f)
|
||||
: origin(pos), direction(viewdir.normalized()), up(up.normalized()), fov(fov), movementSpeed(moveSpeed), rotationSpeed(rotSpeed) {}
|
||||
|
||||
void rotateYaw(float angle) {
|
||||
float cosA = cos(angle);
|
||||
float sinA = sin(angle);
|
||||
|
||||
Vector3f right = direction.cross(up).normalized();
|
||||
|
||||
// Rotate around up vector (yaw)
|
||||
angle *= rotationSpeed;
|
||||
Matrix3f rotation;
|
||||
rotation = Eigen::AngleAxisf(angle, up);
|
||||
direction = rotation * direction;
|
||||
direction.normalize();
|
||||
}
|
||||
|
||||
void rotatePitch(float angle) {
|
||||
// Clamp pitch to avoid gimbal lock
|
||||
angle *= rotationSpeed;
|
||||
Vector3f right = direction.cross(up).normalized();
|
||||
|
||||
// Rotate around right vector (pitch)
|
||||
@@ -43,6 +42,30 @@ struct Camera {
|
||||
up = right.cross(direction).normalized();
|
||||
}
|
||||
|
||||
void moveForward(float distance) {
|
||||
origin += forward() * distance * movementSpeed;
|
||||
}
|
||||
|
||||
void moveBackward(float distance) {
|
||||
origin -= forward() * distance * movementSpeed;
|
||||
}
|
||||
|
||||
void moveRight(float distance) {
|
||||
origin += right() * distance * movementSpeed;
|
||||
}
|
||||
|
||||
void moveLeft(float distance) {
|
||||
origin -= right() * distance * movementSpeed;
|
||||
}
|
||||
|
||||
void moveUp(float distance) {
|
||||
origin += up * distance * movementSpeed;
|
||||
}
|
||||
|
||||
void moveDown(float distance) {
|
||||
origin -= up * distance * movementSpeed;
|
||||
}
|
||||
|
||||
Vector3f forward() const {
|
||||
return direction.normalized();
|
||||
}
|
||||
@@ -55,17 +78,32 @@ struct Camera {
|
||||
return fov * (M_PI / 180.0f);
|
||||
}
|
||||
|
||||
// Additional useful methods
|
||||
void moveForward(float distance) {
|
||||
origin += forward() * distance;
|
||||
// Look at a specific point
|
||||
void lookAt(const Vector3f& target) {
|
||||
direction = (target - origin).normalized();
|
||||
|
||||
// Recalculate up vector
|
||||
Vector3f worldUp(0, 1, 0);
|
||||
if (direction.cross(worldUp).norm() < 0.001f) {
|
||||
worldUp = Vector3f(0, 0, 1);
|
||||
}
|
||||
|
||||
Vector3f right = direction.cross(worldUp).normalized();
|
||||
up = right.cross(direction).normalized();
|
||||
}
|
||||
|
||||
void moveRight(float distance) {
|
||||
origin += right() * distance;
|
||||
// Set position directly
|
||||
void setPosition(const Vector3f& pos) {
|
||||
origin = pos;
|
||||
}
|
||||
|
||||
void moveUp(float distance) {
|
||||
origin += up * distance;
|
||||
// Set view direction directly
|
||||
void setDirection(const Vector3f& dir) {
|
||||
direction = dir.normalized();
|
||||
// Recalculate up
|
||||
Vector3f worldUp(0, 1, 0);
|
||||
Vector3f right = direction.cross(worldUp).normalized();
|
||||
up = right.cross(direction).normalized();
|
||||
}
|
||||
|
||||
// Get view matrix (lookAt matrix)
|
||||
@@ -88,7 +126,7 @@ struct Camera {
|
||||
}
|
||||
|
||||
// Get projection matrix (perspective)
|
||||
Eigen::Matrix4f getProjectionMatrix(float aspectRatio, float nearPlane = 0.1f, float farPlane = 100.0f) const {
|
||||
Eigen::Matrix4f getProjectionMatrix(float aspectRatio, float nearPlane = 0.1f, float farPlane = 1000.0f) const {
|
||||
float fovrad = fovRad();
|
||||
float tanHalfFov = tan(fovrad / 2.0f);
|
||||
|
||||
@@ -102,6 +140,14 @@ struct Camera {
|
||||
|
||||
return projection;
|
||||
}
|
||||
|
||||
void mouseLook(float deltaX, float deltaY) {
|
||||
float yaw = -deltaX * 0.001f;
|
||||
float pitch = -deltaY * 0.001f;
|
||||
|
||||
rotateYaw(yaw);
|
||||
rotatePitch(pitch);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef SSE
|
||||
#include <immintrin.h>
|
||||
@@ -44,6 +45,10 @@ public:
|
||||
float reflection = 0.0f) : data(data), position(pos), active(active), visible(visible),
|
||||
color(color), size(size), light(light), emittance(emittance), refraction(refraction),
|
||||
reflection(reflection) {}
|
||||
|
||||
// Default constructor for serialization
|
||||
NodeData() : active(false), visible(false), size(0.0f), light(false),
|
||||
emittance(0.0f), refraction(0.0f), reflection(0.0f) {}
|
||||
};
|
||||
|
||||
struct OctreeNode {
|
||||
@@ -139,6 +144,116 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void writeVal(std::ofstream& out, const V& val) const {
|
||||
out.write(reinterpret_cast<const char*>(&val), sizeof(V));
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void readVal(std::ifstream& in, V& val) {
|
||||
in.read(reinterpret_cast<char*>(&val), sizeof(V));
|
||||
}
|
||||
|
||||
void writeVec3(std::ofstream& out, const Eigen::Vector3f& vec) const {
|
||||
writeVal(out, vec.x());
|
||||
writeVal(out, vec.y());
|
||||
writeVal(out, vec.z());
|
||||
}
|
||||
|
||||
void readVec3(std::ifstream& in, Eigen::Vector3f& vec) {
|
||||
float x, y, z;
|
||||
readVal(in, x); readVal(in, y); readVal(in, z);
|
||||
vec = Eigen::Vector3f(x, y, z);
|
||||
}
|
||||
|
||||
void serializeNode(std::ofstream& out, const OctreeNode* node) const {
|
||||
writeVal(out, node->isLeaf);
|
||||
|
||||
if (node->isLeaf) {
|
||||
size_t pointCount = node->points.size();
|
||||
writeVal(out, pointCount);
|
||||
for (const auto& pt : node->points) {
|
||||
// Write raw data T (Must be POD)
|
||||
writeVal(out, pt->data);
|
||||
// Write properties
|
||||
writeVec3(out, pt->position);
|
||||
writeVal(out, pt->active);
|
||||
writeVal(out, pt->visible);
|
||||
writeVal(out, pt->size);
|
||||
writeVec3(out, pt->color);
|
||||
writeVal(out, pt->light);
|
||||
writeVal(out, pt->emittance);
|
||||
writeVal(out, pt->refraction);
|
||||
writeVal(out, pt->reflection);
|
||||
}
|
||||
} else {
|
||||
// Write bitmask of active children
|
||||
uint8_t childMask = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (node->children[i] != nullptr) {
|
||||
childMask |= (1 << i);
|
||||
}
|
||||
}
|
||||
writeVal(out, childMask);
|
||||
|
||||
// Recursively write only existing children
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (node->children[i]) {
|
||||
serializeNode(out, node->children[i].get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deserializeNode(std::ifstream& in, OctreeNode* node) {
|
||||
bool isLeaf;
|
||||
readVal(in, isLeaf);
|
||||
node->isLeaf = isLeaf;
|
||||
|
||||
if (isLeaf) {
|
||||
size_t pointCount;
|
||||
readVal(in, pointCount);
|
||||
node->points.reserve(pointCount);
|
||||
|
||||
for (size_t i = 0; i < pointCount; ++i) {
|
||||
auto pt = std::make_shared<NodeData>();
|
||||
readVal(in, pt->data);
|
||||
readVec3(in, pt->position);
|
||||
readVal(in, pt->active);
|
||||
readVal(in, pt->visible);
|
||||
readVal(in, pt->size);
|
||||
readVec3(in, pt->color);
|
||||
readVal(in, pt->light);
|
||||
readVal(in, pt->emittance);
|
||||
readVal(in, pt->refraction);
|
||||
readVal(in, pt->reflection);
|
||||
node->points.push_back(pt);
|
||||
}
|
||||
} else {
|
||||
uint8_t childMask;
|
||||
readVal(in, childMask);
|
||||
|
||||
PointType center = node->center;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if ((childMask >> i) & 1) {
|
||||
// Reconstruct bounds for child
|
||||
PointType childMin, childMax;
|
||||
for (int d = 0; d < Dim; ++d) {
|
||||
bool high = (i >> d) & 1;
|
||||
childMin[d] = high ? center[d] : node->bounds.first[d];
|
||||
childMax[d] = high ? node->bounds.second[d] : center[d];
|
||||
}
|
||||
|
||||
node->children[i] = std::make_unique<OctreeNode>(childMin, childMax);
|
||||
deserializeNode(in, node->children[i].get());
|
||||
} else {
|
||||
node->children[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bitonic_sort_8(std::array<std::pair<int, float>, 8>& arr) const noexcept {
|
||||
#ifdef SSE
|
||||
alignas(32) float values[8];
|
||||
@@ -280,6 +395,8 @@ public:
|
||||
root_(std::make_unique<OctreeNode>(minBound, maxBound)), maxPointsPerNode(maxPointsPerNode),
|
||||
maxDepth(maxDepth), size(0) {}
|
||||
|
||||
Octree() : root_(nullptr), maxPointsPerNode(16), maxDepth(16), size(0) {}
|
||||
|
||||
bool set(const T& data, const PointType& pos, bool visible, Eigen::Vector3f color, float size, bool active,
|
||||
bool light = false, float emittance = 0.0f, float refraction = 0.0f, float reflection = 0.0f) {
|
||||
auto pointData = std::make_shared<NodeData>(data, pos, visible, color, size, active,
|
||||
@@ -291,6 +408,53 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool save(const std::string& filename) const {
|
||||
if (!root_) return false;
|
||||
std::ofstream out(filename, std::ios::binary);
|
||||
if (!out) return false;
|
||||
|
||||
uint32_t magic = 0x79676733;
|
||||
writeVal(out, magic);
|
||||
writeVal(out, maxDepth);
|
||||
writeVal(out, maxPointsPerNode);
|
||||
writeVal(out, size);
|
||||
|
||||
writeVec3(out, root_->bounds.first);
|
||||
writeVec3(out, root_->bounds.second);
|
||||
|
||||
serializeNode(out, root_.get());
|
||||
|
||||
out.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load Octree from binary file
|
||||
bool load(const std::string& filename) {
|
||||
std::ifstream in(filename, std::ios::binary);
|
||||
if (!in) return false;
|
||||
|
||||
uint32_t magic;
|
||||
readVal(in, magic);
|
||||
if (magic != 0x0C78E3) {
|
||||
std::cerr << "Invalid Octree file format" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
readVal(in, maxDepth);
|
||||
readVal(in, maxPointsPerNode);
|
||||
readVal(in, size);
|
||||
|
||||
PointType minBound, maxBound;
|
||||
readVec3(in, minBound);
|
||||
readVec3(in, maxBound);
|
||||
|
||||
root_ = std::make_unique<OctreeNode>(minBound, maxBound);
|
||||
deserializeNode(in, root_.get());
|
||||
|
||||
in.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<NodeData>> voxelTraverse(const PointType& origin, const PointType& direction,
|
||||
float maxDist, bool stopAtFirstHit) {
|
||||
std::vector<std::shared_ptr<NodeData>> hits;
|
||||
@@ -580,6 +744,34 @@ public:
|
||||
}
|
||||
|
||||
bool empty() const { return size == 0; }
|
||||
|
||||
void clear() {
|
||||
if (!root_) return;
|
||||
|
||||
std::function<void(OctreeNode*)> clearNode = [&](OctreeNode* node) {
|
||||
if (!node) return;
|
||||
|
||||
node->points.clear();
|
||||
node->points.shrink_to_fit();
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (node->children[i]) {
|
||||
clearNode(node->children[i].get());
|
||||
node->children[i].reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
node->isLeaf = true;
|
||||
};
|
||||
|
||||
clearNode(root_.get());
|
||||
|
||||
PointType minBound = root_->bounds.first;
|
||||
PointType maxBound = root_->bounds.second;
|
||||
root_ = std::make_unique<OctreeNode>(minBound, maxBound);
|
||||
|
||||
size = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user