diff --git a/tests/g3test2.cpp b/tests/g3test2.cpp index e1b861c..0a52217 100644 --- a/tests/g3test2.cpp +++ b/tests/g3test2.cpp @@ -3,6 +3,7 @@ #include #include #include "../util/grid/grid3.hpp" +#include "../util/grid/g3_serialization.hpp" #include "../util/output/bmpwriter.hpp" #include "../util/output/frame.hpp" #include "../util/timing_decorator.cpp" @@ -196,6 +197,19 @@ int main() { float camvZ = 0.f; //float camYaw = 0.0f; //float camPitch = 0.0f; + bool autoRotate = false; // Toggle for auto-rotation + bool autoRotateView = false; // Toggle for auto-rotation of the view only + float rotationSpeedX = 0.1f; // Speed for X rotation + float rotationSpeedY = 0.07f; // Speed for Y rotation + float rotationSpeedZ = 0.05f; // Speed for Z rotation + float autoRotationTime = 0.0f; // Timer for auto-rotation + Vec3f initialViewDir = Vec3f(0, 0, 1); // Initial view direction + float rotationRadius = 50.0f; // Distance from center for rotation + float yawSpeed = 0.5f; // Horizontal rotation speed (degrees per second) + float pitchSpeed = 0.3f; // Vertical rotation speed + float rollSpeed = 0.2f; // Roll rotation speed (optional) + float autoRotationAngle = 0.0f; // Accumulated rotation angle + Vec3f initialUpDir = Vec3f(0, 1, 0); // Initial up direction // Variables for framerate limiting const double targetFrameTime = 1.0 / config.fps; // 30 FPS @@ -349,6 +363,90 @@ int main() { // Update preview if camera moved or enough time has passed if (gridInitialized && !updatePreview) { double timeSinceLastUpdate = currentTime - lastUpdateTime; + if (autoRotate) { + // Update rotation time + autoRotationTime += deltaTime; + + // Calculate new view direction using spherical coordinates + // This creates smooth 360-degree rotation with different speeds + float angleX = autoRotationTime * rotationSpeedX; + float angleY = autoRotationTime * rotationSpeedY; + float angleZ = autoRotationTime * rotationSpeedZ; + + // Create rotation matrix or calculate new direction + // Using simple parametric equation for a sphere + camvX = sinf(angleX) * cosf(angleY); + camvY = sinf(angleY) * sinf(angleZ); + camvZ = cosf(angleX) * cosf(angleZ); + + // Normalize the direction vector (optional but good practice) + float length = sqrtf(camvX * camvX + camvY * camvY + camvZ * camvZ); + if (length > 0.001f) { + camvX /= length; + camvY /= length; + camvZ /= length; + } + + // Update camera position to orbit around grid center + camX = config.gridWidth / 2.0f + rotationRadius * camvX; + camY = config.gridHeight / 2.0f + rotationRadius * camvY; + camZ = config.gridDepth / 2.0f + rotationRadius * camvZ; + + // Update camera + cam.posfor.origin = Vec3f(camX, camY, camZ); + cam.posfor.direction = Vec3f(camvX, camvY, camvZ); + + cameraMoved = true; + } + + if (autoRotateView) { + // Update rotation angle based on time + autoRotationAngle += deltaTime; + + // Calculate rotation angles (in radians) + float yaw = autoRotationAngle * yawSpeed * (3.14159f / 180.0f); // Convert to radians + float pitch = sinf(autoRotationAngle * 0.7f) * pitchSpeed * (3.14159f / 180.0f); + float roll = sinf(autoRotationAngle * 0.3f) * rollSpeed * (3.14159f / 180.0f); + + // Start with forward direction + Vec3f forward = initialViewDir; + + // Apply yaw rotation (around Y axis) + float cosYaw = cosf(yaw); + float sinYaw = sinf(yaw); + Vec3f tempForward; + tempForward.x = forward.x * cosYaw + forward.z * sinYaw; + tempForward.y = forward.y; + tempForward.z = -forward.x * sinYaw + forward.z * cosYaw; + forward = tempForward; + + // Apply pitch rotation (around X axis) + float cosPitch = cosf(pitch); + float sinPitch = sinf(pitch); + tempForward.x = forward.x; + tempForward.y = forward.y * cosPitch - forward.z * sinPitch; + tempForward.z = forward.y * sinPitch + forward.z * cosPitch; + forward = tempForward; + + // Normalize the direction + float length = sqrtf(forward.x * forward.x + forward.y * forward.y + forward.z * forward.z); + if (length > 0.001f) { + forward.x /= length; + forward.y /= length; + forward.z /= length; + } + + // Update view direction variables + camvX = forward.x; + camvY = forward.y; + camvZ = forward.z; + + // Update camera + cam.posfor.direction = Vec3f(camvX, camvY, camvZ); + + cameraMoved = true; + } + if (cameraMoved || timeSinceLastUpdate > 0.1) { // Update at least every 0.1 seconds livePreview(grid, config, cam); lastUpdateTime = currentTime; @@ -356,6 +454,123 @@ int main() { } } + + ImGui::Separator(); + ImGui::Text("Auto-Rotation:"); + + // Toggle button for auto-rotation + if (ImGui::Button(autoRotate ? "Stop Auto-Rotation" : "Start Auto-Rotation")) { + autoRotate = !autoRotate; + if (autoRotate) { + // Reset rotation time when starting + autoRotationTime = 0.0f; + // Save initial positions + initialViewDir = Vec3f(camvX, camvY, camvZ); + } + } + + if (ImGui::Button(autoRotateView ? "Stop Looking Around" : "Start Looking Around")) { + autoRotateView = !autoRotateView; + if (autoRotateView) { + // Reset rotation when starting + autoRotationAngle = 0.0f; + // Save current view direction as initial + initialViewDir = Vec3f(camvX, camvY, camvZ); + } + } + + if (autoRotate) { + ImGui::SameLine(); + ImGui::Text("(Running)"); + + // 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); + + // Reset button + if (ImGui::Button("Reset to Center")) { + camX = config.gridWidth / 2.0f; + camY = config.gridHeight / 2.0f; + camZ = config.gridDepth / 2.0f; + cam.posfor.origin = Vec3f(camX, camY, camZ); + cameraMoved = true; + } + } + + if (autoRotateView) { + ImGui::SameLine(); + ImGui::TextColored(ImVec4(0, 1, 0, 1), " ACTIVE"); + + // 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::SliderFloat("Roll Speed", &rollSpeed, 0.0f, 1.0f, "%.2f deg/sec"); + + // Add some presets + ImGui::Separator(); + ImGui::Text("Presets:"); + + if (ImGui::Button("Slow Pan")) { + yawSpeed = 0.3f; + pitchSpeed = 0.1f; + rollSpeed = 0.0f; + } + ImGui::SameLine(); + if (ImGui::Button("Full Exploration")) { + yawSpeed = 0.8f; + pitchSpeed = 0.5f; + rollSpeed = 0.2f; + } + ImGui::SameLine(); + if (ImGui::Button("Reset View")) { + // Reset to forward view + camvX = 0.0f; + camvY = 0.0f; + camvZ = 1.0f; + cam.posfor.direction = Vec3f(camvX, camvY, camvZ); + cameraMoved = true; + } + + // Progress indicator + float progress = fmodf(autoRotationAngle * yawSpeed / 360.0f, 1.0f); + ImGui::ProgressBar(progress, ImVec2(-1, 0)); + ImGui::Text("Full rotation progress: %.1f%%", progress * 100.0f); + } + + if (ImGui::Button("Save Screenshot During Rotation")) { + if (autoRotate) { + // Generate filename with timestamp + auto now = std::chrono::system_clock::now(); + auto timestamp = std::chrono::duration_cast( + now.time_since_epoch()).count(); + std::string filename = "output/rotation_frame_" + + std::to_string(timestamp) + ".bmp"; + + // Save current frame + frame output = grid.renderFrame(cam, Vec2i(config.outWidth, config.outHeight), + frame::colormap::RGB); + BMPWriter::saveBMP(filename.c_str(), output); + + ImGui::OpenPopup("Screenshot Saved"); + } + } + + // Popup notification + if (ImGui::BeginPopupModal("Screenshot Saved", NULL, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("Screenshot saved during rotation!"); + if (ImGui::Button("OK")) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } // Reset accumulator for next frame accumulator -= targetFrameTime; diff --git a/util/basicdefines.hpp b/util/basicdefines.hpp index 54b5230..f9baba7 100644 --- a/util/basicdefines.hpp +++ b/util/basicdefines.hpp @@ -6,5 +6,297 @@ #define EPSILON 0.0000000000000000000000001f #endif #ifndef INF -#define INF 2^31-1 -#endif \ No newline at end of file +#define INF 2 ^ 31 - 1 +#endif + +int edgeTable[256] = { + 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0}; + +int triTable[256][16] = + {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; \ No newline at end of file diff --git a/util/grid/g3_serialization.hpp b/util/grid/g3_serialization.hpp new file mode 100644 index 0000000..f5981fd --- /dev/null +++ b/util/grid/g3_serialization.hpp @@ -0,0 +1,98 @@ +#ifndef GRID3_Serialization +#define GRID3_Serialization + +#include +#include +#include "grid3.hpp" + +constexpr char magic[4] = {'Y', 'G', 'G', '3'}; + +inline bool VoxelGrid::serializeToFile(const std::string& filename) { + std::ofstream file(filename, std::ios::binary); + if (!file.is_open()) { + std::cerr << "failed to open file (serializeToFile): " << filename << std::endl; + return false; + } + + file.write(magic, 4); + int dims[3] = {gridSize.x, gridSize.y, gridSize.z}; + file.write(reinterpret_cast(dims), sizeof(dims)); + size_t voxelCount = voxels.size(); + for (const Voxel& voxel : voxels) { + auto write_member = [&file](const auto& member) { + file.write(reinterpret_cast(&member), sizeof(member)); + }; + + std::apply([&write_member](const auto&... members) { + (write_member(members), ...); + }, voxel.members()); + } + + file.close(); + return !file.fail(); +} + +std::unique_ptr VoxelGrid::deserializeFromFile(const std::string& filename) { + std::ifstream file(filename, std::ios::binary); + if (!file.is_open()) { + std::cerr << "failed to open file (deserializeFromFile): " << filename << std::endl; + return nullptr; + } + + // Read and verify magic number + char filemagic[4]; + file.read(filemagic, 4); + if (std::strncmp(filemagic, magic, 4) != 0) { + std::cerr << "Error: Invalid file format or corrupted file (expected " + << magic[0] << magic[1] << magic[2] << magic[3] + << ", got " << filemagic[0] << filemagic[1] << filemagic[2] << filemagic[3] + << ")" << std::endl; + return nullptr; + } + + // Create output grid + auto outgrid = std::make_unique(); + + // Read grid dimensions + int dims[3]; + file.read(reinterpret_cast(dims), sizeof(dims)); + + // Resize grid + outgrid->gridSize = Vec3i(dims[0], dims[1], dims[2]); + outgrid->voxels.resize(dims[0] * dims[1] * dims[2]); + + // Read voxel count + size_t voxelCount; + file.read(reinterpret_cast(&voxelCount), sizeof(voxelCount)); + + // Verify voxel count matches grid dimensions + size_t expectedCount = static_cast(dims[0]) * dims[1] * dims[2]; + if (voxelCount != expectedCount) { + std::cerr << "Error: Voxel count mismatch. Expected " << expectedCount + << ", found " << voxelCount << std::endl; + return nullptr; + } + + // Read all voxels + for (size_t i = 0; i < voxelCount; ++i) { + auto members = outgrid->voxels[i].members(); + + std::apply([&file](auto&... member) { + ((file.read(reinterpret_cast(&member), sizeof(member))), ...); + }, members); + } + + file.close(); + + if (file.fail() && !file.eof()) { + std::cerr << "Error: Failed to read from file: " << filename << std::endl; + return nullptr; + } + + std::cout << "Successfully loaded grid: " << dims[0] << " x " + << dims[1] << " x " << dims[2] << std::endl; + + return outgrid; +} + +#endif \ No newline at end of file diff --git a/util/grid/grid3.hpp b/util/grid/grid3.hpp index fab00aa..db7861b 100644 --- a/util/grid/grid3.hpp +++ b/util/grid/grid3.hpp @@ -11,18 +11,57 @@ #include "../output/frame.hpp" #include "../noise/pnoise2.hpp" #include "../vecmat/mat4.hpp" -//#include "serialization.hpp" #include #include #include "../basicdefines.hpp" -constexpr char magic[4] = {'Y', 'G', 'G', '3'}; +//constexpr char magic[4] = {'Y', 'G', 'G', '3'}; + +Mat4f lookAt(const Vec3f& eye, const Vec3f& center, const Vec3f& up) { + Vec3f const f = (center - eye).normalized(); + Vec3f const s = f.cross(up).normalized(); + Vec3f const u = s.cross(f); + + Mat4f Result = Mat4f::identity(); + Result(0, 0) = s.x; + Result(1, 0) = s.y; + Result(2, 0) = s.z; + Result(3, 0) = -s.dot(eye); + Result(0, 1) = u.x; + Result(1, 1) = u.y; + Result(2, 1) = u.z; + Result(3, 1) = -u.dot(eye); + Result(0, 2) = -f.x; + Result(1, 2) = -f.y; + Result(2, 2) = -f.z; + Result(3, 2) = f.dot(eye); + return Result; +} + +Mat4f perspective(float fovy, float aspect, float zNear, float zfar) { + float const tanhalfF = tan(fovy / 2); + Mat4f Result = 0; + Result(0,0) = 1 / (aspect * tanhalfF); + Result(1,1) = 1 / tanhalfF; + Result(2,2) = zfar / (zNear - zfar); + Result(2,3) = -1; + Result(3,2) = -(zfar * zNear) / (zfar - zNear); + return Result; +} struct Voxel { - //float active; + float weight; bool active; - //Vec3f position; + float alpha; Vec3ui8 color; + // TODO: add curving and similar for water and glass and so on. + auto members() const -> std::tuple { + return std::tie(weight, active, alpha, color); + } + + auto members() -> std::tuple { + return std::tie(weight, active, alpha, color); + } }; struct Camera { @@ -64,48 +103,114 @@ struct Camera { } }; +struct Vertex { + Vec3f position; + Vec3f normal; + Vec3ui8 color; + Vec2f texCoord; + + Vertex() = default; + Vertex(Vec3f pos, Vec3f norm, Vec3ui8 colr, Vec2f tex = Vec2f(0,0)) : position(pos), normal(norm), color(colr), texCoord(tex) {} +}; + +struct Tri { + size_t v0,v1,v2; + Tri() = default; + Tri(size_t a, size_t b, size_t c) : v0(a), v1(b), v2(c) {} +}; + +class Mesh { +private: + std::vector vertices; + std::vector tris; + Vec3f boundBoxMin; + Vec3f boundBoxMax; +public: + Mesh() = default; + + void clear() { + vertices.clear(); + tris.clear(); + boundBoxMax = Vec3f(0,0,0); + boundBoxMin = Vec3f(0,0,0); + } + + void addVertex(const Vertex& vertex) { + vertices.push_back(vertex); + boundBoxMin = boundBoxMin.min(vertex.position); + boundBoxMax = boundBoxMax.max(vertex.position); + } + + void addTriangle(const Tri& triangle) { + tris.push_back(triangle); + } + + void addTriangle(uint32_t v0, uint32_t v1, uint32_t v2) { + tris.emplace_back(v0, v1, v2); + } + + const std::vector& getVertices() const { return vertices; } + const std::vector& getTriangles() const { return tris; } + + size_t getVertexCount() const { return vertices.size(); } + size_t getTriangleCount() const { return tris.size(); } + + Vec3f getBoundingBoxMin() const { return boundBoxMin; } + Vec3f getBoundingBoxMax() const { return boundBoxMax; } + Vec3f getBoundingBoxSize() const { return boundBoxMax - boundBoxMin; } + Vec3f getBoundingBoxCenter() const { return (boundBoxMin + boundBoxMax) * 0.5f; } + + // Calculate normals if they're not already set + void calculateNormals() { + // Reset all normals to zero + for (auto& vertex : vertices) { + vertex.normal = Vec3f(0, 0, 0); + } + + // Accumulate face normals to vertices + for (const auto& tri : tris) { + const Vec3f& v0 = vertices[tri.v0].position; + const Vec3f& v1 = vertices[tri.v1].position; + const Vec3f& v2 = vertices[tri.v2].position; + + Vec3f edge1 = v1 - v0; + Vec3f edge2 = v2 - v0; + Vec3f normal = edge1.cross(edge2).normalized(); + + vertices[tri.v0].normal = vertices[tri.v0].normal + normal; + vertices[tri.v1].normal = vertices[tri.v1].normal + normal; + vertices[tri.v2].normal = vertices[tri.v2].normal + normal; + } + + // Normalize all vertex normals + for (auto& vertex : vertices) { + if (vertex.normal.lengthSquared() > 0) { + vertex.normal = vertex.normal.normalized(); + } else { + vertex.normal = Vec3f(0, 1, 0); // Default up normal + } + } + } + + void optimize() { + calculateNormals(); + //optimize may have optional params later for future expansion of features + } +}; + class VoxelGrid { private: double binSize = 1; Vec3i gridSize; //int width, height, depth; std::vector voxels; + std::unique_ptr cachedMesh; + bool meshDirty = true; + float radians(float rads) { return rads * (M_PI / 180); } - static Mat4f lookAt(const Vec3f& eye, const Vec3f& center, const Vec3f& up) { - Vec3f const f = (center - eye).normalized(); - Vec3f const s = f.cross(up).normalized(); - Vec3f const u = s.cross(f); - - Mat4f Result = Mat4f::identity(); - Result(0, 0) = s.x; - Result(1, 0) = s.y; - Result(2, 0) = s.z; - Result(3, 0) = -s.dot(eye); - Result(0, 1) = u.x; - Result(1, 1) = u.y; - Result(2, 1) = u.z; - Result(3, 1) = -u.dot(eye); - Result(0, 2) = -f.x; - Result(1, 2) = -f.y; - Result(2, 2) = -f.z; - Result(3, 2) = f.dot(eye); - return Result; - } - - static Mat4f perspective(float fovy, float aspect, float zNear, float zfar) { - float const tanhalfF = tan(fovy / 2); - Mat4f Result = 0; - Result(0,0) = 1 / (aspect * tanhalfF); - Result(1,1) = 1 / tanhalfF; - Result(2,2) = zfar / (zNear - zfar); - Result(2,3) = -1; - Result(3,2) = -(zfar * zNear) / (zfar - zNear); - return Result; - } - public: VoxelGrid() : gridSize(0,0,0) { std::cout << "creating empty grid." << std::endl; @@ -115,93 +220,9 @@ public: voxels.resize(w * h * d); } - //bool serializeToFile(const VoxelGrid grid, const std::string& filename); - bool serializeToFile(const std::string& filename) { - std::ofstream file(filename, std::ios::binary); - if (!file.is_open()) { - std::cerr << "failed to open file (serializeToFile): " << filename << std::endl; - return false; - } - - file.write(magic, 4); - - //file.write(reinterpret_cast(&binSize), sizeof(binSize)); - - // Write grid dimensions - int dims[3] = {gridSize.x, gridSize.y, gridSize.z}; - file.write(reinterpret_cast(dims), sizeof(dims)); - - // Write voxel data - size_t voxelCount = voxels.size(); - file.write(reinterpret_cast(&voxelCount), sizeof(voxelCount)); - - // Write each voxel - for (const Voxel& voxel : voxels) { - file.write(reinterpret_cast(&voxel.active), sizeof(voxel.active)); - file.write(reinterpret_cast(&voxel.color.x), sizeof(voxel.color.x)); - file.write(reinterpret_cast(&voxel.color.y), sizeof(voxel.color.y)); - file.write(reinterpret_cast(&voxel.color.z), sizeof(voxel.color.z)); - } - - file.close(); - return !file.fail(); - } + bool serializeToFile(const std::string& filename); - static std::unique_ptr deserializeFromFile(const std::string& filename) { - VoxelGrid outgrid; - std::ifstream file(filename, std::ios::binary); - if (!file.is_open()) { - std::cerr << "Error: Could not open file for reading: " << filename << std::endl; - return nullptr; - } - - // Read and verify magic number - char filemagic[4]; - file.read(filemagic, 4); - if (std::strncmp(filemagic, "YGG7", 4) != 0) { - std::cerr << "Error: Invalid file format or corrupted file" << std::endl; - return nullptr; - } - - // Read binSize - //file.read(reinterpret_cast(&binSize), sizeof(binSize)); - - // Read grid dimensions - int dims[3]; - file.read(reinterpret_cast(dims), sizeof(dims)); - outgrid.resize(Vec3i(dims[0], dims[1], dims[2])); - //gridSize = Vec3i(dims[0], dims[1], dims[2]); - - // Read voxel count - size_t voxelCount; - file.read(reinterpret_cast(&voxelCount), sizeof(voxelCount)); - - // Verify voxel count matches grid dimensions - size_t expectedCount = static_cast(dims[0]) * dims[1] * dims[2]; - if (voxelCount != expectedCount) { - std::cerr << "Error: Voxel count mismatch. Expected " << expectedCount - << ", found " << voxelCount << std::endl; - return nullptr; - } - - // Resize and read voxels - //voxels.resize(voxelCount); - grid->voxels.resize(voxelCount); - for (size_t i = 0; i < voxelCount; ++i) { - file.read(reinterpret_cast(&grid->voxels[i].active), sizeof(grid->voxels[i].active)); - file.read(reinterpret_cast(&grid->voxels[i].color.x), sizeof(grid->voxels[i].color.x)); - file.read(reinterpret_cast(&grid->voxels[i].color.y), sizeof(grid->voxels[i].color.y)); - file.read(reinterpret_cast(&grid->voxels[i].color.z), sizeof(grid->voxels[i].color.z)); - } - - file.close(); - if (file.fail()) { - std::cerr << "Error: Failed to read from file: " << filename << std::endl; - return nullptr; - } - - return grid; - } + static std::unique_ptr deserializeFromFile(const std::string& filename); Voxel& get(int x, int y, int z) { return voxels[z * gridSize.x * gridSize.y + y * gridSize.x + x]; @@ -215,6 +236,10 @@ public: return voxels[xyz.z * gridSize.x * gridSize.y + xyz.y * gridSize.x + xyz.x]; } + const Voxel& get(const Vec3i& xyz) const { + return voxels[xyz.z * gridSize.x * gridSize.y + xyz.y * gridSize.x + xyz.x]; + } + void resize(int newW, int newH, int newD) { std::vector newVoxels(newW * newH * newD); int copyW = std::min(static_cast(gridSize.x), newW); @@ -266,11 +291,11 @@ public: } template - bool inGrid(Vec3 voxl) { + bool inGrid(Vec3 voxl) const { return (voxl >= 0 && voxl.x < gridSize.x && voxl.y < gridSize.y && voxl.z < gridSize.z); } - void voxelTraverse(const Vec3d& origin, const Vec3d& end, std::vector& visitedVoxel) { + void voxelTraverse(const Vec3d& origin, const Vec3d& end, std::vector& visitedVoxel) const { Vec3i cv = (origin / binSize).floorToI(); Vec3i lv = (end / binSize).floorToI(); Vec3d ray = end - origin; @@ -338,7 +363,7 @@ public: return gridSize.z; } - frame renderFrame(const Camera& cam, Vec2i resolution, frame::colormap colorformat = frame::colormap::RGB) { + frame renderFrame(const Camera& cam, Vec2i resolution, frame::colormap colorformat = frame::colormap::RGB) const { TIME_FUNCTION; Vec3f forward = cam.forward(); Vec3f right = cam.right(); @@ -421,8 +446,221 @@ public: std::cout << "Inactive voxels: " << (totalVoxels - activeVoxels) << std::endl; std::cout << "Active percentage: " << activePercentage << "%" << std::endl; std::cout << "Memory usage (approx): " << (voxels.size() * sizeof(Voxel)) / 1024 << " KB" << std::endl; + std::cout << "Mesh cached: " << (cachedMesh ? "Yes" : "No") << std::endl; + if (cachedMesh) { + std::cout << "Mesh vertices: " << cachedMesh->getVertexCount() << std::endl; + std::cout << "Mesh triangles: " << cachedMesh->getTriangleCount() << std::endl; + } std::cout << "============================" << std::endl; } + +private: + // Helper function to check if a voxel is on the surface + bool isSurfaceVoxel(int x, int y, int z) const { + if (!inGrid(Vec3i(x, y, z))) return false; + if (!get(x, y, z).active) return false; + + // Check all 6 neighbors + static const std::array neighbors = {{ + Vec3i(1, 0, 0), Vec3i(-1, 0, 0), + Vec3i(0, 1, 0), Vec3i(0, -1, 0), + Vec3i(0, 0, 1), Vec3i(0, 0, -1) + }}; + + for (const auto& n : neighbors) { + Vec3i neighborPos(x + n.x, y + n.y, z + n.z); + if (!inGrid(neighborPos) || !get(neighborPos).active) { + return true; // At least one empty neighbor means this is a surface voxel + } + } + + return false; + } + + // Get normal for a surface voxel + Vec3f calculateVoxelNormal(int x, int y, int z) const { + Vec3f normal(0, 0, 0); + + // Simple gradient-based normal calculation + if (inGrid(Vec3i(x+1, y, z)) && !get(x+1, y, z).active) normal.x += 1; + if (inGrid(Vec3i(x-1, y, z)) && !get(x-1, y, z).active) normal.x -= 1; + if (inGrid(Vec3i(x, y+1, z)) && !get(x, y+1, z).active) normal.y += 1; + if (inGrid(Vec3i(x, y-1, z)) && !get(x, y-1, z).active) normal.y -= 1; + if (inGrid(Vec3i(x, y, z+1)) && !get(x, y, z+1).active) normal.z += 1; + if (inGrid(Vec3i(x, y, z-1)) && !get(x, y, z-1).active) normal.z -= 1; + + if (normal.lengthSquared() > 0) { + return normal.normalized(); + } + return Vec3f(0, 1, 0); // Default up normal + } + + Vertex getEdgeVertex(int edge, int x, int y, int z, float isoLevel = 0.5f) const { + // Edge vertices based on Marching Cubes algorithm + static const std::array, 12> edgeVertices = {{ + {0, 1}, {1, 2}, {2, 3}, {3, 0}, // Bottom edges + {4, 5}, {5, 6}, {6, 7}, {7, 4}, // Top edges + {0, 4}, {1, 5}, {2, 6}, {3, 7} // Vertical edges + }}; + + static const std::array cubeVertices = {{ + Vec3f(0, 0, 0), Vec3f(1, 0, 0), Vec3f(1, 1, 0), Vec3f(0, 1, 0), + Vec3f(0, 0, 1), Vec3f(1, 0, 1), Vec3f(1, 1, 1), Vec3f(0, 1, 1) + }}; + + const auto& [v1, v2] = edgeVertices[edge]; + const Vec3f& p1 = cubeVertices[v1]; + const Vec3f& p2 = cubeVertices[v2]; + + // For binary voxels, we can just use midpoint + Vec3f position = (p1 + p2) * 0.5f; + + // Convert to world coordinates + position = position + Vec3f(x, y, z); + position = position * binSize; + + // Get colors from neighboring voxels + Vec3ui8 color1 = get(x, y, z).color; + Vec3ui8 color2 = color1; + + // Determine which neighboring voxel to use for the second color + // This is simplified - in a full implementation, you'd interpolate based on values + if (v2 == 1) color2 = get(x+1, y, z).color; + else if (v2 == 3) color2 = get(x, y+1, z).color; + else if (v2 == 4) color2 = get(x, y, z+1).color; + + // Interpolate color + Vec3ui8 color( + static_cast((color1.x + color2.x) / 2), + static_cast((color1.y + color2.y) / 2), + static_cast((color1.z + color2.z) / 2) + ); + + // Calculate normal (simplified) + Vec3f normal = calculateVoxelNormal(x, y, z); + + return Vertex(position, normal, color); + } + + // Helper function to add a face to the mesh + void addFace(Mesh& mesh, const Vec3f& basePos, const Vec3f& normal, + const Vec3ui8& color, bool flipWinding = false) { + Vec3f right, up; + + // Determine right and up vectors based on normal + if (std::abs(normal.x) > std::abs(normal.y)) { + right = Vec3f(0, 0, 1); + } else { + right = Vec3f(1, 0, 0); + } + up = normal.cross(right).normalized(); + right = up.cross(normal).normalized(); + + // Create face vertices + float halfSize = binSize * 0.5f; + Vec3f center = basePos + normal * halfSize; + + Vec3f v0 = center - right * halfSize - up * halfSize; + Vec3f v1 = center + right * halfSize - up * halfSize; + Vec3f v2 = center + right * halfSize + up * halfSize; + Vec3f v3 = center - right * halfSize + up * halfSize; + + // Add vertices to mesh + uint32_t startIndex = static_cast(mesh.getVertexCount()); + + mesh.addVertex(Vertex(v0, normal, color)); + mesh.addVertex(Vertex(v1, normal, color)); + mesh.addVertex(Vertex(v2, normal, color)); + mesh.addVertex(Vertex(v3, normal, color)); + + // Add triangles (two triangles per quad) + if (flipWinding) { + mesh.addTriangle(startIndex, startIndex + 1, startIndex + 2); + mesh.addTriangle(startIndex, startIndex + 2, startIndex + 3); + } else { + mesh.addTriangle(startIndex, startIndex + 2, startIndex + 1); + mesh.addTriangle(startIndex, startIndex + 3, startIndex + 2); + } + } + +public: + // Mesh generation using Naive Surface Nets (simpler than Marching Cubes) + std::unique_ptr meshify() { + TIME_FUNCTION; + + // If mesh is cached and not dirty, return it + if (cachedMesh && !meshDirty) { + return std::make_unique(*cachedMesh); + } + + auto mesh = std::make_unique(); + mesh->clear(); + + // For each voxel that's on the surface, create a quad + for (int z = 0; z < gridSize.z; z++) { + for (int y = 0; y < gridSize.y; y++) { + for (int x = 0; x < gridSize.x; x++) { + if (!get(x, y, z).active) continue; + + const Voxel& voxel = get(x, y, z); + Vec3f basePos(x * binSize, y * binSize, z * binSize); + + // Check each face + // Right face (x+) + if (!inGrid(Vec3i(x+1, y, z)) || !get(x+1, y, z).active) { + addFace(*mesh, basePos, Vec3f(1, 0, 0), voxel.color, true); + } + // Left face (x-) + if (!inGrid(Vec3i(x-1, y, z)) || !get(x-1, y, z).active) { + addFace(*mesh, basePos, Vec3f(-1, 0, 0), voxel.color, false); + } + // Top face (y+) + if (!inGrid(Vec3i(x, y+1, z)) || !get(x, y+1, z).active) { + addFace(*mesh, basePos, Vec3f(0, 1, 0), voxel.color, true); + } + // Bottom face (y-) + if (!inGrid(Vec3i(x, y-1, z)) || !get(x, y-1, z).active) { + addFace(*mesh, basePos, Vec3f(0, -1, 0), voxel.color, false); + } + // Front face (z+) + if (!inGrid(Vec3i(x, y, z+1)) || !get(x, y, z+1).active) { + addFace(*mesh, basePos, Vec3f(0, 0, 1), voxel.color, true); + } + // Back face (z-) + if (!inGrid(Vec3i(x, y, z-1)) || !get(x, y, z-1).active) { + addFace(*mesh, basePos, Vec3f(0, 0, -1), voxel.color, false); + } + } + } + } + + // Optimize the mesh + mesh->optimize(); + + // Cache the mesh + cachedMesh = std::make_unique(*mesh); + meshDirty = false; + + return mesh; + } + + // Get cached mesh (regenerates if dirty) + std::unique_ptr getMesh() { + return meshify(); + } + + // Clear mesh cache + void clearMeshCache() { + cachedMesh.reset(); + meshDirty = true; + } + + // Check if mesh needs regeneration + bool isMeshDirty() const { + return meshDirty; + } + }; +//#include "g3_serialization.hpp" needed to be usable #endif diff --git a/util/grid/serialization.hpp b/util/grid/serialization.hpp deleted file mode 100644 index 6e9e260..0000000 --- a/util/grid/serialization.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef GRID3_Serialization -#define GRID3_Serialization - -#include -#include -#include "grid3.hpp" - -constexpr char magic[4] = {'Y', 'G', 'G', '3'}; - -inline bool serializeToFile(const VoxelGrid& grid, const std::string& filename) { - std::ofstream file(filename, std::ios::binary); - if (!file.is_open()) { - std::cerr << "failed to open file (serializeToFile): " << filename << std::endl; - return false; - } - - file.write(magic, 4); - //int dims[3] = {grid.gridSize.x, grid.gridSize.y, grid.gridSize.z}; - -} - -#endif \ No newline at end of file