diff --git a/tests/g3etest.cpp b/tests/g3etest.cpp index 1c05286..83873e6 100644 --- a/tests/g3etest.cpp +++ b/tests/g3etest.cpp @@ -28,6 +28,7 @@ struct defaults { int gridSizecube = 10000; bool slowRender = false; bool globalIllumination = true; + bool useLod = true; int rayCount = 3; int reflectCount = 3; int lodDist = 500; @@ -408,7 +409,7 @@ void livePreview(Octree& grid, defaults& config, const Camera& cam) { grid.setLODMinDistance(config.lodDist); grid.setLODFalloff(config.lodDropoff); if (config.slowRender) { - currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB, config.rayCount, config.reflectCount, config.globalIllumination); + currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB, config.rayCount, config.reflectCount, config.globalIllumination, config.useLod); } else { currentPreviewFrame = grid.fastRenderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB); } @@ -998,6 +999,7 @@ int main() { ImGui::InputFloat("Lod dropoff", &config.lodDropoff); ImGui::InputInt("lod minimum Distance", &config.lodDist); ImGui::Checkbox("use Global illumination", &config.globalIllumination); + ImGui::Checkbox("use Lod", &config.useLod); ImGui::End(); } diff --git a/util/grid/grid3eigen.hpp b/util/grid/grid3eigen.hpp index f9c86d2..57f29ed 100644 --- a/util/grid/grid3eigen.hpp +++ b/util/grid/grid3eigen.hpp @@ -502,9 +502,6 @@ private: return randomDir; } - float rgbToGrayscale(const Eigen::Vector3f& color) const { - return 0.2126f * color[0] + 0.7152f * color[1] + 0.0722f * color[2]; - } void collectNodesByObjectId(OctreeNode* node, int id, std::vector>& results) const { if (!node) return; @@ -900,14 +897,14 @@ public: if (!pointData->active) continue; float t; - // if (pointData->shape == Shape::SPHERE) { - // if (raySphereIntersect(origin, dir, pointData->position, pointData->size, t)) { - // if (t >= 0 && t <= maxDist) { - // hits.emplace_back(pointData); - // if (stopAtFirstHit) return; - // } - // } - // } else { + if (pointData->shape == Shape::SPHERE) { + if (raySphereIntersect(origin, dir, pointData->position, pointData->size, t)) { + if (t >= 0 && t <= maxDist) { + hits.emplace_back(pointData); + if (stopAtFirstHit) return; + } + } + } else { PointType normal, hitPoint; if (rayCubeIntersect(origin, dir, pointData.get(), t, normal, hitPoint)) { if (t >= 0 && t <= maxDist) { @@ -915,7 +912,7 @@ public: if (stopAtFirstHit) return; } } - // } + } } } else { for (int i = 0; i < 8; ++i) { @@ -950,15 +947,8 @@ public: PointType right = cam.right(); frame outFrame(width, height, colorformat); - std::vector colorBuffer; - int channels; - if (colorformat == frame::colormap::B) { - channels = 1; - } else if (colorformat == frame::colormap::RGB || colorformat == frame::colormap::BGR) { - channels = 3; - } else { //BGRA and RGBA - channels = 4; - } + std::vector colorBuffer; + int channels = 3; colorBuffer.resize(width * height * channels); float aspect = static_cast(width) / height; @@ -1005,7 +995,6 @@ public: hitPoint = rayOrig + rayDir * t; normal = (hitPoint - center).normalized(); } else { - // Cube intersection PointType cubeNormal; if (!rayCubeIntersect(rayOrig, rayDir, obj.get(), t, normal, hitPoint)) { return globalIllumination ? skylight_ : Eigen::Vector3f::Zero(); @@ -1030,11 +1019,6 @@ public: finalColor += obj->color.cwiseProduct(incomingLight) * diffuseProb; } - if (refl > 0.001f) { - PointType rDir = (rayDir - 2.0f * rayDir.dot(normal) * normal).normalized(); - finalColor += traceRay(hitPoint + normal * 0.002f, rDir, bounces + 1, rngState) * refl; - } - if (refr > 0.001f) { float ior = 1.45f; float η = 1.0f / ior; @@ -1083,38 +1067,14 @@ public: Eigen::Vector3f color = accumulatedColor / static_cast(samplesPerPixel); color = color.cwiseMax(0.0f).cwiseMin(1.0f); - - switch(colorformat) { - case frame::colormap::B: - colorBuffer[idx ] = static_cast(rgbToGrayscale(color) * 255.0f); - break; - case frame::colormap::RGB: - colorBuffer[idx ] = static_cast(color[0] * 255.0f); - colorBuffer[idx + 1] = static_cast(color[1] * 255.0f); - colorBuffer[idx + 2] = static_cast(color[2] * 255.0f); - break; - case frame::colormap::BGR: - colorBuffer[idx ] = static_cast(color[2] * 255.0f); - colorBuffer[idx + 1] = static_cast(color[1] * 255.0f); - colorBuffer[idx + 2] = static_cast(color[0] * 255.0f); - break; - case frame::colormap::RGBA: - colorBuffer[idx ] = static_cast(color[0] * 255.0f); - colorBuffer[idx + 1] = static_cast(color[1] * 255.0f); - colorBuffer[idx + 2] = static_cast(color[2] * 255.0f); - colorBuffer[idx + 3] = 255; - break; - case frame::colormap::BGRA: - colorBuffer[idx ] = static_cast(color[2] * 255.0f); - colorBuffer[idx + 1] = static_cast(color[1] * 255.0f); - colorBuffer[idx + 2] = static_cast(color[0] * 255.0f); - colorBuffer[idx + 3] = 255; - break; - } + + colorBuffer[idx ] = color[0]; + colorBuffer[idx + 1] = color[1]; + colorBuffer[idx + 2] = color[2]; } } - outFrame.setData(colorBuffer); + outFrame.setData(colorBuffer, frame::colormap::RGB); return outFrame; } @@ -1214,15 +1174,9 @@ public: PointType right = cam.right(); frame outFrame(width, height, colorformat); - std::vector colorBuffer; + std::vector colorBuffer; int channels; - if (colorformat == frame::colormap::B) { - channels = 1; - } else if (colorformat == frame::colormap::RGB || colorformat == frame::colormap::BGR) { - channels = 3; - } else { //BGRA and RGBA - channels = 4; - } + channels = 3; colorBuffer.resize(width * height * channels); float aspect = static_cast(width) / height; @@ -1262,37 +1216,13 @@ public: color = color * lightDot; } - switch(colorformat) { - case frame::colormap::B: - colorBuffer[idx ] = static_cast(rgbToGrayscale(color) * 255.0f); - break; - case frame::colormap::RGB: - colorBuffer[idx ] = static_cast(color[0] * 255.0f); - colorBuffer[idx + 1] = static_cast(color[1] * 255.0f); - colorBuffer[idx + 2] = static_cast(color[2] * 255.0f); - break; - case frame::colormap::BGR: - colorBuffer[idx ] = static_cast(color[2] * 255.0f); - colorBuffer[idx + 1] = static_cast(color[1] * 255.0f); - colorBuffer[idx + 2] = static_cast(color[0] * 255.0f); - break; - case frame::colormap::RGBA: - colorBuffer[idx ] = static_cast(color[0] * 255.0f); - colorBuffer[idx + 1] = static_cast(color[1] * 255.0f); - colorBuffer[idx + 2] = static_cast(color[2] * 255.0f); - colorBuffer[idx + 3] = 255; - break; - case frame::colormap::BGRA: - colorBuffer[idx ] = static_cast(color[2] * 255.0f); - colorBuffer[idx + 1] = static_cast(color[1] * 255.0f); - colorBuffer[idx + 2] = static_cast(color[0] * 255.0f); - colorBuffer[idx + 3] = 255; - break; - } + colorBuffer[idx ] = color[0]; + colorBuffer[idx + 1] = color[1]; + colorBuffer[idx + 2] = color[2]; } } - outFrame.setData(colorBuffer); + outFrame.setData(colorBuffer, frame::colormap::RGB); return outFrame; } diff --git a/util/output/frame.hpp b/util/output/frame.hpp index 28fa3ad..4930dd8 100644 --- a/util/output/frame.hpp +++ b/util/output/frame.hpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "../timing_decorator.hpp" class frame { @@ -26,7 +28,6 @@ private: size_t sourceSize = 0; size_t width = 0; size_t height = 0; - public: enum class colormap { RGB, @@ -44,7 +45,29 @@ public: HUFFMAN, RAW }; +private: + size_t getChannels(colormap fmt) const { + switch (fmt) { + case colormap::RGBA: return 4; + case colormap::BGR: return 3; + case colormap::BGRA: return 4; + case colormap::B: return 1; + case colormap::RGB: default: return 3; + } + } + void resetState(size_t newSize) { + cformat = compresstype::RAW; + _compressedData.clear(); + _compressedData.shrink_to_fit(); + overheadmap.clear(); + sourceSize = newSize; + } + + float rgbToGrayscale(float r, float g, float b) const { + return 0.2126f * r + 0.7152f * g + 0.0722f * b; + } +public: colormap colorFormat = colormap::RGB; compresstype cformat = compresstype::RAW; @@ -58,29 +81,244 @@ public: frame() {}; frame(size_t w, size_t h, colormap format = colormap::RGB) : width(w), height(h), colorFormat(format), cformat(compresstype::RAW) { - size_t channels = 3; - switch (format) { - case colormap::RGBA: channels = 4; break; - case colormap::BGR: channels = 3; break; - case colormap::BGRA: channels = 4; break; - case colormap::B: channels = 1; break; - default: channels = 3; break; - } - _data.resize(width * height * channels); + _data.resize(width * height * getChannels(format)); } void setData(const std::vector& data) { _data = data; - cformat = compresstype::RAW; - _compressedData.clear(); - _compressedData.shrink_to_fit(); - overheadmap.clear(); - sourceSize = data.size(); + resetState(data.size()); + } + + void setData(const std::vector& inputData, colormap inputFormat) { + if (inputFormat == colorFormat) { + setData(inputData); + return; + } + + size_t srcChannels = getChannels(inputFormat); + size_t dstChannels = getChannels(colorFormat); + size_t numPixels = width * height; + + if (inputData.size() != numPixels * srcChannels) { + throw std::runtime_error("Input data size does not match frame dimensions for the specified format."); + } + + std::vector newData; + newData.reserve(numPixels * dstChannels); + + for (size_t i = 0; i < numPixels; ++i) { + size_t px = i * srcChannels; + uint8_t r = 0, g = 0, b = 0, a = 255; + + switch (inputFormat) { + case colormap::RGB: { + r = inputData[px]; + g = inputData[px+1]; + b = inputData[px+2]; + break; + } + case colormap::RGBA: + r = inputData[px]; + g = inputData[px+1]; + b = inputData[px+2]; + a = inputData[px+3]; + break; + case colormap::BGR: + b = inputData[px]; + g = inputData[px+1]; + r = inputData[px+2]; + break; + case colormap::BGRA: + b = inputData[px]; + g = inputData[px+1]; + r = inputData[px+2]; + a = inputData[px+3]; + break; + case colormap::B: + r = g = b = inputData[px]; + break; + } + + switch (colorFormat) { + case colormap::RGB: + newData.push_back(r); + newData.push_back(g); + newData.push_back(b); + break; + case colormap::RGBA: + newData.push_back(r); + newData.push_back(g); + newData.push_back(b); + newData.push_back(a); + break; + case colormap::BGR: + newData.push_back(b); + newData.push_back(g); + newData.push_back(r); + break; + case colormap::BGRA: + newData.push_back(b); + newData.push_back(g); + newData.push_back(r); + newData.push_back(a); + break; + case colormap::B: + newData.push_back(rgbToGrayscale(r, g, b)); + break; + } + } + + _data = std::move(newData); + resetState(_data.size()); + } + + void setData(const std::vector& inputData) { + size_t channels = getChannels(colorFormat); + + if (inputData.size() != width * height * channels) { + throw std::runtime_error("Input float data size does not match frame dimensions."); + } + + std::vector newData; + newData.reserve(inputData.size()); + + for (float val : inputData) { + // Clamp between 0.0 and 1.0, scale to 255 + float v = std::max(0.0f, std::min(1.0f, val)); + newData.push_back(static_cast(v * 255.0f)); + } + + _data = std::move(newData); + resetState(_data.size()); + } + + void setData(const std::vector& inputData, colormap inputFormat) { + size_t srcChannels = getChannels(inputFormat); + size_t dstChannels = getChannels(colorFormat); + size_t numPixels = width * height; + + if (inputData.size() != numPixels * srcChannels) { + throw std::runtime_error("Input float data size does not match frame dimensions."); + } + + std::vector newData; + newData.reserve(numPixels * dstChannels); + + auto floatToByte = [](float f) -> uint8_t { + return static_cast(std::max(0.0f, std::min(1.0f, f)) * 255.0f); + }; + + for (size_t i = 0; i < numPixels; ++i) { + size_t px = i * srcChannels; + uint8_t r = 0, g = 0, b = 0, a = 255; + + // Extract and convert floats to bytes + switch (inputFormat) { + case colormap::RGB: + r = floatToByte(inputData[px]); + g = floatToByte(inputData[px+1]); + b = floatToByte(inputData[px+2]); + break; + case colormap::RGBA: + r = floatToByte(inputData[px]); + g = floatToByte(inputData[px+1]); + b = floatToByte(inputData[px+2]); + a = floatToByte(inputData[px+3]); + break; + case colormap::BGR: + b = floatToByte(inputData[px]); + g = floatToByte(inputData[px+1]); + r = floatToByte(inputData[px+2]); + break; + case colormap::BGRA: + b = floatToByte(inputData[px]); + g = floatToByte(inputData[px+1]); + r = floatToByte(inputData[px+2]); + a = floatToByte(inputData[px+3]); + break; + case colormap::B: + r = g = b = floatToByte(inputData[px]); + break; + } + + switch (colorFormat) { + case colormap::RGB: + newData.push_back(r); + newData.push_back(g); + newData.push_back(b); + break; + case colormap::RGBA: + newData.push_back(r); + newData.push_back(g); + newData.push_back(b); + newData.push_back(a); + break; + case colormap::BGR: + newData.push_back(b); + newData.push_back(g); + newData.push_back(r); + break; + case colormap::BGRA: + newData.push_back(b); + newData.push_back(g); + newData.push_back(r); + newData.push_back(a); + break; + case colormap::B: + newData.push_back(rgbToGrayscale(r, g, b)); + break; + } + } + + _data = std::move(newData); + resetState(_data.size()); } const std::vector& getData() const { return _data; } + + std::vector getPixel(size_t x, size_t y) const { + if (cformat != compresstype::RAW) { + throw std::runtime_error("Cannot get pixel data from a compressed frame. Decompress first."); + } + if (x >= width || y >= height) { + throw std::out_of_range("Pixel coordinates out of bounds."); + } + + size_t channels = getChannels(colorFormat); + size_t index = (y * width + x) * channels; + + std::vector pixel; + pixel.reserve(channels); + + for (size_t i = 0; i < channels; ++i) { + pixel.push_back(_data[index + i]); + } + return pixel; + } + + void setPixel(size_t x, size_t y, const std::vector& values) { + if (cformat != compresstype::RAW) { + throw std::runtime_error("Cannot set pixel data on a compressed frame. Decompress first."); + } + if (x >= width || y >= height) { + throw std::out_of_range("Pixel coordinates out of bounds."); + } + + size_t channels = getChannels(colorFormat); + if (values.size() != channels) { + throw std::invalid_argument("Input value count does not match frame channel count."); + } + + size_t index = (y * width + x) * channels; + for (size_t i = 0; i < channels; ++i) { + _data[index + i] = values[i]; + } + + // Since data changed, previous compression stats are invalid + resetState(_data.size()); + } // Run-Length Encoding (RLE) compression frame& compressFrameRLE() { @@ -243,13 +481,27 @@ public: void printCompressionInfo() const { std::cout << "Compression Type: "; switch (cformat) { - case compresstype::RLE: std::cout << "RLE"; break; - case compresstype::DIFF: std::cout << "DIFF"; break; - case compresstype::DIFFRLE: std::cout << "DIFF + RLE"; break; - case compresstype::LZ78: std::cout << "LZ78 (kinda)"; break; - case compresstype::HUFFMAN: std::cout << "HUFFMAN"; break; - case compresstype::RAW: std::cout << "RAW (uncompressed)"; break; - default: std::cout << "UNKNOWN"; break; + case compresstype::RLE: + std::cout << "RLE"; + break; + case compresstype::DIFF: + std::cout << "DIFF"; + break; + case compresstype::DIFFRLE: + std::cout << "DIFF + RLE"; + break; + case compresstype::LZ78: + std::cout << "LZ78 (kinda)"; + break; + case compresstype::HUFFMAN: + std::cout << "HUFFMAN"; + break; + case compresstype::RAW: + std::cout << "RAW (uncompressed)"; + break; + default: + std::cout << "UNKNOWN"; + break; } std::cout << std::endl; @@ -503,19 +755,30 @@ private: }; - -std::ostream& operator<<(std::ostream& os, frame& f) { +inline std::ostream& operator<<(std::ostream& os, frame& f) { os << "Frame[" << f.getWidth() << "x" << f.getHeight() << "] "; // Color format os << "Format: "; switch (f.colorFormat) { - case frame::colormap::RGB: os << "RGB"; break; - case frame::colormap::RGBA: os << "RGBA"; break; - case frame::colormap::BGR: os << "BGR"; break; - case frame::colormap::BGRA: os << "BGRA"; break; - case frame::colormap::B: os << "Grayscale"; break; - default: os << "Unknown"; break; + case frame::colormap::RGB: + os << "RGB"; + break; + case frame::colormap::RGBA: + os << "RGBA"; + break; + case frame::colormap::BGR: + os << "BGR"; + break; + case frame::colormap::BGRA: + os << "BGRA"; + break; + case frame::colormap::B: + os << "Grayscale"; + break; + default: + os << "Unknown"; + break; } // Compression info