diff --git a/.gitmodules b/.gitmodules index 217e3b2..81b8a67 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,4 @@ [submodule "stb"] path = stb url = https://github.com/nothings/stb + \ No newline at end of file diff --git a/tests/g3test3.cpp b/tests/g3test3.cpp index 2e93ca0..d0ba86b 100644 --- a/tests/g3test3.cpp +++ b/tests/g3test3.cpp @@ -1,9 +1,41 @@ -// test_voxel_render.cpp #include "../util/grid/grid33.hpp" +//#include "../util/grid/treexy/treexy_serialization.hpp" #include "../util/output/bmpwriter.hpp" +#include "../util/noise/pnoise2.hpp" +#include "../util/timing_decorator.cpp" #include #include +struct configuration { + float threshold = 0.1; + int gridWidth = 128; + int gridHeight = 128; + int gridDepth = 128; + PNoise2 noise = PNoise2(42); +}; + +void setup(configuration& config, VoxelGrid& grid) { + TIME_FUNCTION; + uint8_t thresh = config.threshold * 255; + for (int z = 0; z < config.gridDepth; ++z) { + if (z % 64 == 0) { + std::cout << "Processing layer " << z << " of " << config.gridDepth << std::endl; + } + + for (int y = 0; y < config.gridHeight; ++y) { + for (int x = 0; x < config.gridWidth; ++x) { + uint8_t r = std::clamp(config.noise.permute(Vec3f(static_cast(x) / config.gridWidth / 64, static_cast(y) / config.gridHeight / 64, static_cast(z) / config.gridDepth / 64)), 0.f, 1.f) * 255; + uint8_t g = std::clamp(config.noise.permute(Vec3f(static_cast(x) / config.gridWidth / 32, static_cast(y) / config.gridHeight / 32, static_cast(z) / config.gridDepth / 32)), 0.f, 1.f) * 255; + uint8_t b = std::clamp(config.noise.permute(Vec3f(static_cast(x) / config.gridWidth / 16, static_cast(y) / config.gridHeight / 16, static_cast(z) / config.gridDepth / 16)), 0.f, 1.f) * 255; + uint8_t a = std::clamp(config.noise.permute(Vec3f(static_cast(x) / config.gridWidth / 8 , static_cast(y) / config.gridHeight / 8 , static_cast(z) / config.gridDepth / 8 )), 0.f, 1.f) * 255; + if (a > thresh) { + bool wasOn = grid.setVoxelColor(Vec3d(x,y,z), Vec3ui8(r,g,b)); + } + } + } + } +} + int main() { // Initialize random number generator std::random_device rd; @@ -13,33 +45,9 @@ int main() { // Create a voxel grid with 0.1 unit resolution VoxelGrid voxelGrid(0.1); - - std::cout << "Placing 10 random colored voxels..." << std::endl; - - // Place 10 random colored voxels - for (int i = 0; i < 10; ++i) { - // Generate random position - double x = pos_dist(gen); - double y = pos_dist(gen); - double z = pos_dist(gen); - // Generate random color - uint8_t r = static_cast(color_dist(gen)); - uint8_t g = static_cast(color_dist(gen)); - uint8_t b = static_cast(color_dist(gen)); - - Vec3d worldPos(x, y, z); - Vec3ui8 color(r, g, b); - - // Set voxel color - bool wasOn = voxelGrid.setVoxelColor(worldPos, color); - - std::cout << "Voxel " << i + 1 << ": " - << "Pos(" << x << ", " << y << ", " << z << ") " - << "Color(RGB:" << static_cast(r) << "," - << static_cast(g) << "," << static_cast(b) << ") " - << (wasOn ? "(overwritten)" : "(new)") << std::endl; - } + configuration config; + setup(config, voxelGrid); std::cout << "\nMemory usage: " << voxelGrid.getMemoryUsage() << " bytes" << std::endl; @@ -53,15 +61,16 @@ int main() { std::vector imageBuffer; // Render with orthographic projection (view along Z axis) - voxelGrid.renderProjectedToRGBBuffer(imageBuffer, width, height, - Vec3d(0, 0, 1), // View direction (looking along Z) - Vec3d(0, 1, 0)); // Up direction + Vec3d camPos = Vec3d(config.gridDepth, config.gridHeight, config.gridWidth * 2); + Vec3d lookAt = Vec3d(config.gridDepth / 2, config.gridHeight / 2, config.gridWidth / 2); + Vec3d viewDir = (lookAt-camPos).normalized(); + voxelGrid.renderToRGB(imageBuffer, width, height, camPos, viewDir, Vec3d(0, 1, 0), 80.f); std::cout << "Image buffer size: " << imageBuffer.size() << " bytes" << std::endl; std::cout << "Expected size: " << (width * height * 3) << " bytes" << std::endl; // Save to BMP using BMPWriter - std::string filename = "voxel_render.bmp"; + std::string filename = "output/voxel_render.bmp"; // Create a frame object from the buffer frame renderFrame(width, height, frame::colormap::RGB); @@ -71,33 +80,8 @@ int main() { if (BMPWriter::saveBMP(filename, renderFrame)) { std::cout << "Successfully saved to: " << filename << std::endl; - // Also save using the direct vector interface as backup - if (BMPWriter::saveBMP("voxel_render_direct.bmp", imageBuffer, width, height)) { - std::cout << "Also saved direct version: voxel_render_direct.bmp" << std::endl; - } } else { std::cout << "Failed to save BMP!" << std::endl; - - // Try alternative save method - std::cout << "Trying alternative save method..." << std::endl; - - // Convert to Vec3ui8 format - std::vector pixelsVec3; - pixelsVec3.reserve(width * height); - - for (size_t i = 0; i < imageBuffer.size(); i += 3) { - pixelsVec3.push_back(Vec3ui8( - imageBuffer[i], - imageBuffer[i + 1], - imageBuffer[i + 2] - )); - } - - if (BMPWriter::saveBMP("voxel_render_vec3.bmp", pixelsVec3, width, height)) { - std::cout << "Saved Vec3ui8 version: voxel_render_vec3.bmp" << std::endl; - } else { - std::cout << "All save methods failed!" << std::endl; - } } // Test accessor functionality @@ -132,6 +116,7 @@ int main() { }); std::cout << "\nTotal voxels in grid: " << voxelCount << std::endl; + FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED); return 0; } \ No newline at end of file diff --git a/util/grid/grid33.hpp b/util/grid/grid33.hpp index f4ff3b7..c5b21aa 100644 --- a/util/grid/grid33.hpp +++ b/util/grid/grid33.hpp @@ -7,6 +7,7 @@ #include #include "../vectorlogic/vec3.hpp" #include "../basicdefines.hpp" +#include "../timing_decorator.hpp" /// @brief Finds the index of the least significant bit set to 1 in a 64-bit integer. /// @details Uses compiler intrinsics (_BitScanForward64, __builtin_ctzll) where available, @@ -658,8 +659,8 @@ public: /// @param upDir The up vector of the camera. /// @param fov the field of view for the camera void renderToRGB(std::vector& buffer, int width, int height, const Vec3d& viewOrigin, - const Vec3d& viewDir, const Vec3d& upDir, float fov = 80) { - // Resize buffer to hold width * height * 3 bytes (RGB) + const Vec3d& viewDir, const Vec3d& upDir, float fov = 80) { + TIME_FUNCTION; buffer.resize(width * height * 3); std::fill(buffer.begin(), buffer.end(), 0); @@ -674,14 +675,11 @@ public: // Compute focal length based on FOV double aspectRatio = static_cast(width) / static_cast(height); double fovRad = fov * M_PI / 180.0; - double focalLength = 1.0 / tan(fovRad * 0.5); + double focalLength = 0.5 / tan(fovRad * 0.5); // Reduced for wider view - // Precompute scaling factors for screen coordinates - double pixelWidth = 2.0 / (width - 1); - double pixelHeight = 2.0 / (height - 1); - - // Compute half voxel size for accurate ray-voxel intersection - double halfVoxel = resolution * 0.5; + // Pixel to world scaling + double pixelWidth = 2.0 * focalLength / width; + double pixelHeight = 2.0 * focalLength / height; // Create an accessor for efficient voxel lookup Accessor accessor = createAccessor(); @@ -689,81 +687,55 @@ public: // For each pixel in the output image for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { - // Convert pixel coordinates to normalized device coordinates [-1, 1] - double ndcX = (2.0 * x / (width - 1)) - 1.0; - double ndcY = 1.0 - (2.0 * y / (height - 1)); // Flip Y + // Calculate pixel position in camera space + double u = (x - width * 0.5) * pixelWidth; + double v = (height * 0.5 - y) * pixelHeight; - // Scale by aspect ratio - ndcX *= aspectRatio; - - // Compute ray direction in camera space - Vec3d rayDirCam(ndcX, ndcY, focalLength); - - // Transform ray direction to world space - Vec3d rayDirWorld = (rightDir * rayDirCam.x) + - (realUpDir * rayDirCam.y) + - (viewDirN * rayDirCam.z); + // Compute ray direction in world space + Vec3d rayDirWorld = viewDirN * focalLength + + rightDir * u + + realUpDir * v; rayDirWorld = rayDirWorld.normalized(); // Set up ray marching Vec3d rayPos = viewOrigin; - double maxDistance = 100.0; // Maximum ray distance - double stepSize = resolution; // Step size for ray marching + double maxDistance = 1000.0; // Increased maximum ray distance + double stepSize = resolution * 0.5; // Smaller step size // Ray marching loop - for (double t = 0; t < maxDistance; t += stepSize) { + bool hit = false; + for (double t = 0; t < maxDistance && !hit; t += stepSize) { rayPos = viewOrigin + rayDirWorld * t; + // Check if we're inside the grid bounds + if (rayPos.x < 0 || rayPos.y < 0 || rayPos.z < 0 || + rayPos.x >= 128 || rayPos.y >= 128 || rayPos.z >= 128) { + continue; + } + // Convert world position to voxel coordinate Vec3i coord = posToCoord(rayPos); - // Look up voxel value using accessor (cached for efficiency) + // Look up voxel value using accessor DataT* voxelData = accessor.value(coord); if (voxelData) { // Voxel hit - extract color - // Assuming DataT is Vec3ui8 or compatible Vec3ui8* colorPtr = reinterpret_cast(voxelData); // Get buffer index for this pixel size_t pixelIdx = (y * width + x) * 3; - // Apply simple shading based on normal - // Estimate normal by checking neighbors - double shading = 1.0; + // Simple distance-based attenuation + double distance = t; + double attenuation = 1.0 / (1.0 + distance * 0.01); - // Check neighboring voxels to estimate surface normal - Vec3d voxelCenter = Vec3iToPos(coord); - Vec3d toRay = (rayPos - voxelCenter).normalized(); - - // Simple normal estimation by checking adjacent voxels - Vec3i neighbors[6] = { - Vec3i(coord.x + 1, coord.y, coord.z), - Vec3i(coord.x - 1, coord.y, coord.z), - Vec3i(coord.x, coord.y + 1, coord.z), - Vec3i(coord.x, coord.y - 1, coord.z), - Vec3i(coord.x, coord.y, coord.z + 1), - Vec3i(coord.x, coord.y, coord.z - 1) - }; - - // Count empty neighbors to estimate surface orientation - int emptyCount = 0; - for (int i = 0; i < 6; ++i) { - if (!accessor.value(neighbors[i])) { - emptyCount++; - } - } - - // Simple shading: more visible if fewer neighbors (edge/corner) - if (emptyCount > 0) { - shading = 0.7 + 0.3 * (emptyCount / 6.0); - } - - // Store color in buffer with shading - buffer[pixelIdx] = static_cast(colorPtr->x * shading); - buffer[pixelIdx + 1] = static_cast(colorPtr->y * shading); - buffer[pixelIdx + 2] = static_cast(colorPtr->z * shading); + // Store color in buffer with attenuation + buffer[pixelIdx] = static_cast(colorPtr->x * attenuation); + buffer[pixelIdx + 1] = static_cast(colorPtr->y * attenuation); + buffer[pixelIdx + 2] = static_cast(colorPtr->z * attenuation); + hit = true; break; // Stop ray marching after hitting first voxel } }