From cf554cd072f19b4f95fde6feecff719d3fec95e4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Jun 2024 14:17:35 -0700 Subject: [PATCH 01/15] optimization to ply loading. Added an empty default constructor to the struct used in a large std::vector. This will skip the construction of each individual element in the vector. --- src/gaussiancloud.h | 1 + src/pointcloud.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gaussiancloud.h b/src/gaussiancloud.h index 7ab03fc..5621744 100644 --- a/src/gaussiancloud.h +++ b/src/gaussiancloud.h @@ -25,6 +25,7 @@ class GaussianCloud struct Gaussian { + Gaussian() noexcept {} float position[3]; // in world space float normal[3]; // unused float f_dc[3]; // first order spherical harmonics coeff (sRGB color space) diff --git a/src/pointcloud.h b/src/pointcloud.h index 947987e..c2d8d47 100644 --- a/src/pointcloud.h +++ b/src/pointcloud.h @@ -21,6 +21,7 @@ class PointCloud struct Point { + Point() noexcept {} float position[3]; uint8_t color[3]; }; From 3b7efc7bbbf0208890074cca30babfd6bcee0b1e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 26 Jun 2024 09:27:55 -0700 Subject: [PATCH 02/15] Add tracy zones and rename Property to PropertyInfo --- src/app.cpp | 9 ++- src/gaussiancloud.cpp | 149 +++++++++++++++++++++++++----------------- src/ply.cpp | 56 +++++++++++----- src/ply.h | 14 ++-- src/pointcloud.cpp | 12 ++-- src/splatrenderer.cpp | 1 + 6 files changed, 152 insertions(+), 89 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index da56e0a..1188876 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -17,6 +17,13 @@ #include #include +#ifdef TRACY_ENABLE +#include +#else +#define ZoneScoped +#define ZoneScopedNC(NAME, COLOR) +#endif + #include "core/framebuffer.h" #include "core/log.h" #include "core/debugrenderer.h" @@ -209,7 +216,6 @@ static void RenderDesktop(glm::ivec2 windowSize, std::shared_ptr deskto } } -// AJT: TODO this wrapper func is not needed anymore static std::shared_ptr LoadPointCloud(const std::string& plyFilename) { auto pointCloud = std::make_shared(); @@ -222,7 +228,6 @@ static std::shared_ptr LoadPointCloud(const std::string& plyFilename return pointCloud; } -// AJT: TODO this wrapper func is not needed anymore static std::shared_ptr LoadGaussianCloud(const std::string& plyFilename) { auto gaussianCloud = std::make_shared(); diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index 654d7f7..1402855 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -13,6 +13,13 @@ #include #include +#ifdef TRACY_ENABLE +#include +#else +#define ZoneScoped +#define ZoneScopedNC(NAME, COLOR) +#endif + #include "core/log.h" #include "core/util.h" @@ -24,6 +31,8 @@ GaussianCloud::GaussianCloud() bool GaussianCloud::ImportPly(const std::string& plyFilename) { + ZoneScopedNC("GC::ImportPly", tracy::Color::Red4); + std::ifstream plyFile(plyFilename, std::ios::binary); if (!plyFile.is_open()) { @@ -32,93 +41,111 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) } Ply ply; - if (!ply.Parse(plyFile)) + { - Log::E("Error parsing ply file \"%s\"\n", plyFilename.c_str()); - return false; + ZoneScopedNC("ply.Parse", tracy::Color::Blue); + if (!ply.Parse(plyFile)) + { + Log::E("Error parsing ply file \"%s\"\n", plyFilename.c_str()); + return false; + } } struct { - Ply::Property x, y, z; - Ply::Property f_dc[3]; - Ply::Property f_rest[45]; - Ply::Property opacity; - Ply::Property scale[3]; - Ply::Property rot[4]; + Ply::PropertyInfo x, y, z; + Ply::PropertyInfo f_dc[3]; + Ply::PropertyInfo f_rest[45]; + Ply::PropertyInfo opacity; + Ply::PropertyInfo scale[3]; + Ply::PropertyInfo rot[4]; } props; - if (!ply.GetProperty("x", props.x) || !ply.GetProperty("y", props.y) || !ply.GetProperty("z", props.z)) - { - Log::E("Error parsing ply file \"%s\", missing position property\n", plyFilename.c_str()); - } - for (int i = 0; i < 3; i++) { - if (!ply.GetProperty("f_dc_" + std::to_string(i), props.f_dc[i])) - { - Log::E("Error parsing ply file \"%s\", missing f_dc property\n", plyFilename.c_str()); - } - } + ZoneScopedNC("ply.GetProps", tracy::Color::Green); - for (int i = 0; i < 45; i++) - { - if (!ply.GetProperty("f_rest_" + std::to_string(i), props.f_rest[i])) + if (!ply.GetPropertyInfo("x", props.x) || + !ply.GetPropertyInfo("y", props.y) || + !ply.GetPropertyInfo("z", props.z)) { - // f_rest properties are optional - Log::W("PLY file \"%s\", missing f_rest property\n", plyFilename.c_str()); - break; + Log::E("Error parsing ply file \"%s\", missing position property\n", plyFilename.c_str()); } - } - if (!ply.GetProperty("opacity", props.opacity)) - { - Log::E("Error parsing ply file \"%s\", missing opacity property\n", plyFilename.c_str()); - } - - for (int i = 0; i < 3; i++) - { - if (!ply.GetProperty("scale_" + std::to_string(i), props.scale[i])) + for (int i = 0; i < 3; i++) { - Log::E("Error parsing ply file \"%s\", missing scale property\n", plyFilename.c_str()); + if (!ply.GetPropertyInfo("f_dc_" + std::to_string(i), props.f_dc[i])) + { + Log::E("Error parsing ply file \"%s\", missing f_dc property\n", plyFilename.c_str()); + } } - } - for (int i = 0; i < 4; i++) - { - if (!ply.GetProperty("rot_" + std::to_string(i), props.rot[i])) + for (int i = 0; i < 45; i++) { - Log::E("Error parsing ply file \"%s\", missing rot property\n", plyFilename.c_str()); + if (!ply.GetPropertyInfo("f_rest_" + std::to_string(i), props.f_rest[i])) + { + // f_rest properties are optional + Log::W("PLY file \"%s\", missing f_rest property\n", plyFilename.c_str()); + break; + } } - } - gaussianVec.resize(ply.GetVertexCount()); - - int i = 0; - ply.ForEachVertex([this, &i, &props](const uint8_t* data, size_t size) - { - gaussianVec[i].position[0] = props.x.Get(data); - gaussianVec[i].position[1] = props.y.Get(data); - gaussianVec[i].position[2] = props.z.Get(data); - for (int j = 0; j < 3; j++) + if (!ply.GetPropertyInfo("opacity", props.opacity)) { - gaussianVec[i].f_dc[j] = props.f_dc[j].Get(data); + Log::E("Error parsing ply file \"%s\", missing opacity property\n", plyFilename.c_str()); } - for (int j = 0; j < 45; j++) + + for (int i = 0; i < 3; i++) { - gaussianVec[i].f_rest[j] = props.f_rest[j].Get(data); + if (!ply.GetPropertyInfo("scale_" + std::to_string(i), props.scale[i])) + { + Log::E("Error parsing ply file \"%s\", missing scale property\n", plyFilename.c_str()); + } } - gaussianVec[i].opacity = props.opacity.Get(data); - for (int j = 0; j < 3; j++) + + for (int i = 0; i < 4; i++) { - gaussianVec[i].scale[j] = props.scale[j].Get(data); + if (!ply.GetPropertyInfo("rot_" + std::to_string(i), props.rot[i])) + { + Log::E("Error parsing ply file \"%s\", missing rot property\n", plyFilename.c_str()); + } } - for (int j = 0; j < 4; j++) + + } + + { + ZoneScopedNC("ply.resize", tracy::Color::Red4); + gaussianVec.resize(ply.GetVertexCount()); + } + + { + ZoneScopedNC("ply.ForEachVertex", tracy::Color::Blue); + int i = 0; + ply.ForEachVertex([this, &i, &props](const uint8_t* data, size_t size) { - gaussianVec[i].rot[j] = props.rot[j].Get(data); - } - i++; - }); + gaussianVec[i].position[0] = props.x.Get(data); + gaussianVec[i].position[1] = props.y.Get(data); + gaussianVec[i].position[2] = props.z.Get(data); + for (int j = 0; j < 3; j++) + { + gaussianVec[i].f_dc[j] = props.f_dc[j].Get(data); + } + for (int j = 0; j < 45; j++) + { + gaussianVec[i].f_rest[j] = props.f_rest[j].Get(data); + } + gaussianVec[i].opacity = props.opacity.Get(data); + for (int j = 0; j < 3; j++) + { + gaussianVec[i].scale[j] = props.scale[j].Get(data); + } + for (int j = 0; j < 4; j++) + { + gaussianVec[i].rot[j] = props.rot[j].Get(data); + } + i++; + }); + } return true; } diff --git a/src/ply.cpp b/src/ply.cpp index 52f0bdf..2ff1774 100644 --- a/src/ply.cpp +++ b/src/ply.cpp @@ -9,6 +9,13 @@ #include #include +#ifdef TRACY_ENABLE +#include +#else +#define ZoneScoped +#define ZoneScopedNC(NAME, COLOR) +#endif + #include "core/log.h" static bool CheckLine(std::ifstream& plyFile, const std::string& validLine) @@ -30,8 +37,10 @@ static bool GetNextPlyLine(std::ifstream& plyFile, std::string& lineOut) return false; } -bool Ply::Parse(std::ifstream& plyFile) +bool Ply::ParseHeader(std::ifstream& plyFile) { + ZoneScopedNC("Ply::ParseHeader", tracy::Color::Green); + // validate start of header std::string token1, token2, token3; @@ -95,6 +104,8 @@ bool Ply::Parse(std::ifstream& plyFile) break; } + using PropInfoPair = std::pair; + iss.str(line); iss.clear(); iss >> token1 >> token2 >> token3; @@ -105,42 +116,42 @@ bool Ply::Parse(std::ifstream& plyFile) } if (token2 == "char" || token2 == "int8") { - propertyMap.emplace(std::pair(token3, {offset, 1, Ply::Type::Char})); + propertyInfoMap.emplace(PropInfoPair(token3, {offset, 1, Ply::Type::Char})); offset += 1; } else if (token2 == "uchar" || token2 == "uint8") { - propertyMap.emplace(std::pair(token3, {offset, 1, Ply::Type::UChar})); + propertyInfoMap.emplace(PropInfoPair(token3, {offset, 1, Ply::Type::UChar})); offset += 1; } else if (token2 == "short" || token2 == "int16") { - propertyMap.emplace(std::pair(token3, {offset, 2, Ply::Type::Short})); + propertyInfoMap.emplace(PropInfoPair(token3, {offset, 2, Ply::Type::Short})); offset += 2; } else if (token2 == "ushort" || token2 == "uint16") { - propertyMap.emplace(std::pair(token3, {offset, 2, Ply::Type::UShort})); + propertyInfoMap.emplace(PropInfoPair(token3, {offset, 2, Ply::Type::UShort})); offset += 2; } else if (token2 == "int" || token2 == "int32") { - propertyMap.emplace(std::pair(token3, {offset, 4, Ply::Type::Int})); + propertyInfoMap.emplace(PropInfoPair(token3, {offset, 4, Ply::Type::Int})); offset += 4; } else if (token2 == "uint" || token2 == "uint32") { - propertyMap.emplace(std::pair(token3, {offset, 4, Ply::Type::UInt})); + propertyInfoMap.emplace(PropInfoPair(token3, {offset, 4, Ply::Type::UInt})); offset += 4; } else if (token2 == "float" || token2 == "float32") { - propertyMap.emplace(std::pair(token3, {offset, 4, Ply::Type::Float})); + propertyInfoMap.emplace(PropInfoPair(token3, {offset, 4, Ply::Type::Float})); offset += 4; } else if (token2 == "double" || token2 == "float64") { - propertyMap.emplace(std::pair(token3, {offset, 8, Ply::Type::Double})); + propertyInfoMap.emplace(PropInfoPair(token3, {offset, 8, Ply::Type::Double})); offset += 8; } else @@ -152,25 +163,38 @@ bool Ply::Parse(std::ifstream& plyFile) vertexSize = offset; + return true; +} + +bool Ply::Parse(std::ifstream& plyFile) +{ + if (!ParseHeader(plyFile)) + { + return false; + } + // read rest of file into dataVec - dataVec.resize(vertexSize * vertexCount); - plyFile.read((char*)dataVec.data(), vertexSize * vertexCount); + { + ZoneScopedNC("Ply::Parse() read data", tracy::Color::Yellow); + dataVec.resize(vertexSize * vertexCount); + plyFile.read((char*)dataVec.data(), vertexSize * vertexCount); + } return true; } -bool Ply::GetProperty(const std::string& key, Ply::Property& propertyOut) const +bool Ply::GetPropertyInfo(const std::string& key, Ply::PropertyInfo& propertyInfoOut) const { - auto iter = propertyMap.find(key); - if (iter != propertyMap.end()) + auto iter = propertyInfoMap.find(key); + if (iter != propertyInfoMap.end()) { - propertyOut = iter->second; + propertyInfoOut = iter->second; return true; } return false; } -void Ply::ForEachVertex(const VertexCallback& cb) +void Ply::ForEachVertex(const VertexCallback& cb) const { const uint8_t* vertexPtr = dataVec.data(); for (size_t i = 0; i < vertexCount; i++) diff --git a/src/ply.h b/src/ply.h index f3bc18f..dc033f2 100644 --- a/src/ply.h +++ b/src/ply.h @@ -30,10 +30,10 @@ class Ply Double }; - struct Property + struct PropertyInfo { - Property() : type(Type::Unknown) {} - Property(size_t offsetIn, size_t sizeIn, Type typeIn) : offset(offsetIn), size(sizeIn), type(typeIn) {} + PropertyInfo() : type(Type::Unknown) {} + PropertyInfo(size_t offsetIn, size_t sizeIn, Type typeIn) : offset(offsetIn), size(sizeIn), type(typeIn) {} size_t offset; size_t size; @@ -54,15 +54,17 @@ class Ply } }; - bool GetProperty(const std::string& key, Property& propertyOut) const; + bool GetPropertyInfo(const std::string& key, PropertyInfo& propertyInfoOut) const; using VertexCallback = std::function; - void ForEachVertex(const VertexCallback& cb); + void ForEachVertex(const VertexCallback& cb) const; size_t GetVertexCount() const { return vertexCount; } protected: - std::unordered_map propertyMap; + bool ParseHeader(std::ifstream& plyFile); + + std::unordered_map propertyInfoMap; std::vector dataVec; size_t vertexCount; size_t vertexSize; diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index 31760ab..116463c 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -36,18 +36,22 @@ bool PointCloud::ImportPly(const std::string& plyFilename) struct { - Ply::Property x, y, z; - Ply::Property red, green, blue; + Ply::PropertyInfo x, y, z; + Ply::PropertyInfo red, green, blue; } props; - if (!ply.GetProperty("x", props.x) || !ply.GetProperty("y", props.y) || !ply.GetProperty("z", props.z)) + if (!ply.GetPropertyInfo("x", props.x) || + !ply.GetPropertyInfo("y", props.y) || + !ply.GetPropertyInfo("z", props.z)) { Log::E("Error parsing ply file \"%s\", missing position property\n", plyFilename.c_str()); } bool useDoubles = (props.x.type == Ply::Type::Double && props.y.type == Ply::Type::Double && props.z.type == Ply::Type::Double); - if (!ply.GetProperty("red", props.red) || !ply.GetProperty("green", props.green) || !ply.GetProperty("blue", props.blue)) + if (!ply.GetPropertyInfo("red", props.red) || + !ply.GetPropertyInfo("green", props.green) || + !ply.GetPropertyInfo("blue", props.blue)) { Log::E("Error parsing ply file \"%s\", missing color property\n", plyFilename.c_str()); } diff --git a/src/splatrenderer.cpp b/src/splatrenderer.cpp index 4effc8b..118aa1e 100644 --- a/src/splatrenderer.cpp +++ b/src/splatrenderer.cpp @@ -43,6 +43,7 @@ SplatRenderer::~SplatRenderer() bool SplatRenderer::Init(std::shared_ptr gaussianCloud, bool isFramebufferSRGBEnabledIn, bool useFullSHIn, bool useRgcSortOverrideIn) { + ZoneScopedNC("SplatRenderer::Init()", tracy::Color::Blue); GL_ERROR_CHECK("SplatRenderer::Init() begin"); isFramebufferSRGBEnabled = isFramebufferSRGBEnabledIn; From a325912d4b3a2c0aa46a5a929fc46e1d7c4d9cde Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 26 Jun 2024 11:50:52 -0700 Subject: [PATCH 03/15] Convert PointRenderer to interleaved vertex arrays --- src/app.cpp | 6 +-- src/core/vertexbuffer.cpp | 11 +++++ src/core/vertexbuffer.h | 1 + src/pointcloud.cpp | 92 +++++++++++++++++++++++++++------------ src/pointcloud.h | 21 +++++---- src/pointrenderer.cpp | 53 +++++++++++----------- src/pointrenderer.h | 2 + 7 files changed, 117 insertions(+), 69 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 1188876..da95232 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -216,9 +216,9 @@ static void RenderDesktop(glm::ivec2 windowSize, std::shared_ptr deskto } } -static std::shared_ptr LoadPointCloud(const std::string& plyFilename) +static std::shared_ptr LoadPointCloud(const std::string& plyFilename, bool useLinearColors) { - auto pointCloud = std::make_shared(); + auto pointCloud = std::make_shared(useLinearColors); if (!pointCloud->ImportPly(plyFilename)) { @@ -509,7 +509,7 @@ bool App::Init() std::string pointCloudFilename = FindConfigFile(plyFilename, "input.ply"); if (!pointCloudFilename.empty()) { - pointCloud = LoadPointCloud(pointCloudFilename); + pointCloud = LoadPointCloud(pointCloudFilename, isFramebufferSRGBEnabled); if (!pointCloud) { Log::E("Error loading PointCloud\n"); diff --git a/src/core/vertexbuffer.cpp b/src/core/vertexbuffer.cpp index 1cb199a..d0c4a5d 100644 --- a/src/core/vertexbuffer.cpp +++ b/src/core/vertexbuffer.cpp @@ -52,6 +52,17 @@ static void glBufferStorage(GLenum target, GLsizeiptr size, const void* data, GL } #endif +BufferObject::BufferObject(int targetIn, void* data, size_t size, unsigned int flags) +{ + target = targetIn; + glGenBuffers(1, &obj); + Bind(); + glBufferStorage(target, size, data, flags); + Unbind(); + elementSize = 0; + numElements = 0; +} + BufferObject::BufferObject(int targetIn, const std::vector& data, unsigned int flags) { target = targetIn; diff --git a/src/core/vertexbuffer.h b/src/core/vertexbuffer.h index 49940ed..b060894 100644 --- a/src/core/vertexbuffer.h +++ b/src/core/vertexbuffer.h @@ -31,6 +31,7 @@ class BufferObject // flags can one of the following bitfields. // GL_DYNAMIC_STORAGE_BIT, GL_MAP_READ_BIT, GL_MAP_WRITE_BIT, GL_MAP_PERSISTENT_BIT // GL_MAP_COHERENT_BIT, GL_CLIENT_STORAGE_BIT + BufferObject(int targetIn, void* data, size_t size, unsigned int flags = 0); BufferObject(int targetIn, const std::vector& data, unsigned int flags = 0); BufferObject(int targetIn, const std::vector& data, unsigned int flags = 0); BufferObject(int targetIn, const std::vector& data, unsigned int flags = 0); diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index 116463c..28e49c1 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -12,10 +12,13 @@ #include #include "core/log.h" +#include "core/util.h" #include "ply.h" -PointCloud::PointCloud() +PointCloud::PointCloud(bool useLinearColorsIn) : + useLinearColors(useLinearColorsIn) { + ; } bool PointCloud::ImportPly(const std::string& plyFilename) @@ -56,19 +59,30 @@ bool PointCloud::ImportPly(const std::string& plyFilename) Log::E("Error parsing ply file \"%s\", missing color property\n", plyFilename.c_str()); } - pointVec.resize(ply.GetVertexCount()); + pointDataVec.resize(ply.GetVertexCount()); if (useDoubles) { int i = 0; ply.ForEachVertex([this, &i, &props](const uint8_t* data, size_t size) { - pointVec[i].position[0] = (float)props.x.Get(data); - pointVec[i].position[1] = (float)props.y.Get(data); - pointVec[i].position[2] = (float)props.z.Get(data); - pointVec[i].color[0] = props.red.Get(data); - pointVec[i].color[1] = props.green.Get(data); - pointVec[i].color[2] = props.blue.Get(data); + if (useLinearColors) + { + pointDataVec[i].position[0] = SRGBToLinear((float)props.x.Get(data)); + pointDataVec[i].position[1] = SRGBToLinear((float)props.y.Get(data)); + pointDataVec[i].position[2] = SRGBToLinear((float)props.z.Get(data)); + } + else + { + pointDataVec[i].position[0] = (float)props.x.Get(data); + pointDataVec[i].position[1] = (float)props.y.Get(data); + pointDataVec[i].position[2] = (float)props.z.Get(data); + } + pointDataVec[i].position[3] = 1.0f; + pointDataVec[i].color[0] = (float)props.red.Get(data) / 255.0f; + pointDataVec[i].color[1] = (float)props.green.Get(data) / 255.0f; + pointDataVec[i].color[2] = (float)props.blue.Get(data) / 255.0f; + pointDataVec[i].color[3] = 1.0f; i++; }); } @@ -77,12 +91,23 @@ bool PointCloud::ImportPly(const std::string& plyFilename) int i = 0; ply.ForEachVertex([this, &i, &props](const uint8_t* data, size_t size) { - pointVec[i].position[0] = props.x.Get(data); - pointVec[i].position[1] = props.y.Get(data); - pointVec[i].position[2] = props.z.Get(data); - pointVec[i].color[0] = props.red.Get(data); - pointVec[i].color[1] = props.green.Get(data); - pointVec[i].color[2] = props.blue.Get(data); + if (useLinearColors) + { + pointDataVec[i].position[0] = SRGBToLinear(props.x.Get(data)); + pointDataVec[i].position[1] = SRGBToLinear(props.y.Get(data)); + pointDataVec[i].position[2] = SRGBToLinear(props.z.Get(data)); + } + else + { + pointDataVec[i].position[0] = props.x.Get(data); + pointDataVec[i].position[1] = props.y.Get(data); + pointDataVec[i].position[2] = props.z.Get(data); + } + pointDataVec[i].position[3] = 1.0f; + pointDataVec[i].color[0] = (float)props.red.Get(data) / 255.0f; + pointDataVec[i].color[1] = (float)props.green.Get(data) / 255.0f; + pointDataVec[i].color[2] = (float)props.blue.Get(data) / 255.0f; + pointDataVec[i].color[2] = 1.0f; i++; }); } @@ -92,6 +117,9 @@ bool PointCloud::ImportPly(const std::string& plyFilename) bool PointCloud::ExportPly(const std::string& plyFilename) const { + // AJT: TODO FIXME BROKEN + return false; + std::ofstream plyFile(plyFilename, std::ios::binary); if (!plyFile.is_open()) { @@ -102,7 +130,7 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const // ply files have unix line endings. plyFile << "ply\n"; plyFile << "format binary_little_endian 1.0\n"; - plyFile << "element vertex " << pointVec.size() << "\n"; + plyFile << "element vertex " << pointDataVec.size() << "\n"; plyFile << "property float x\n"; plyFile << "property float y\n"; plyFile << "property float z\n"; @@ -114,18 +142,20 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const plyFile << "property uchar blue\n"; plyFile << "end_header\n"; + /* const size_t POINT_SIZE = 27; - for (auto&& p : pointVec) + for (auto&& p : pointDataVec) { plyFile.write((char*)&p, POINT_SIZE); } + */ return true; } void PointCloud::InitDebugCloud() { - pointVec.clear(); + pointDataVec.clear(); // // make an debug pointVec, that contains three lines one for each axis. @@ -133,38 +163,44 @@ void PointCloud::InitDebugCloud() const float AXIS_LENGTH = 1.0f; const int NUM_POINTS = 5; const float DELTA = (AXIS_LENGTH / (float)NUM_POINTS); - pointVec.resize(NUM_POINTS * 3); + pointDataVec.resize(NUM_POINTS * 3); // x axis for (int i = 0; i < NUM_POINTS; i++) { - PointCloud::Point& p = pointVec[i]; + PointCloud::PointData& p = pointDataVec[i]; p.position[0] = i * DELTA; p.position[1] = 0.0f; p.position[2] = 0.0f; - p.color[0] = 255; - p.color[1] = 0; - p.color[2] = 0; + p.position[3] = 1.0f; + p.color[0] = 1.0f; + p.color[1] = 0.0f; + p.color[2] = 0.0f; + p.color[3] = 1.0f; } // y axis for (int i = 0; i < NUM_POINTS; i++) { - PointCloud::Point& p = pointVec[i + NUM_POINTS]; + PointCloud::PointData& p = pointDataVec[i + NUM_POINTS]; p.position[0] = 0.0f; p.position[1] = i * DELTA; p.position[2] = 0.0f; - p.color[0] = 0; - p.color[1] = 255; - p.color[2] = 0; + p.position[3] = 1.0f; + p.color[0] = 0.0f; + p.color[1] = 1.0f; + p.color[2] = 0.0f; + p.color[3] = 1.0f; } // z axis for (int i = 0; i < NUM_POINTS; i++) { - PointCloud::Point& p = pointVec[i + 2 * NUM_POINTS]; + PointCloud::PointData& p = pointDataVec[i + 2 * NUM_POINTS]; p.position[0] = 0.0f; p.position[1] = 0.0f; p.position[2] = i * DELTA; + p.position[3] = 1.0f; p.color[0] = 0; p.color[1] = 0; - p.color[2] = 255; + p.color[2] = 1.0f; + p.color[3] = 0.0f; } } diff --git a/src/pointcloud.h b/src/pointcloud.h index c2d8d47..2b0f306 100644 --- a/src/pointcloud.h +++ b/src/pointcloud.h @@ -12,26 +12,25 @@ class PointCloud { public: - PointCloud(); + PointCloud(bool useLinearColorsIn); bool ImportPly(const std::string& plyFilename); bool ExportPly(const std::string& plyFilename) const; void InitDebugCloud(); - struct Point + struct PointData { - Point() noexcept {} - float position[3]; - uint8_t color[3]; + PointData() noexcept {} + float position[4]; + float color[4]; }; - size_t size() const { return pointVec.size(); } - const std::vector& GetPointVec() const { return pointVec; } - - // AJT: HACK FOR NOW give full access to pointVec - std::vector& GetPointVec() { return pointVec; } + size_t GetNumPoints() const { return pointDataVec.size(); } + const std::vector& GetPointDataVec() const { return pointDataVec; } + std::vector& GetPointDataVec() { return pointDataVec; } protected: - std::vector pointVec; + std::vector pointDataVec; + bool useLinearColors; }; diff --git a/src/pointrenderer.cpp b/src/pointrenderer.cpp index 009ee00..6b1d58d 100644 --- a/src/pointrenderer.cpp +++ b/src/pointrenderer.cpp @@ -69,13 +69,21 @@ bool PointRenderer::Init(std::shared_ptr pointCloud, bool isFramebuf return false; } + const size_t numPoints = pointCloud->GetNumPoints(); + // build posVec + posVec.reserve(numPoints); + for (auto&& p : pointCloud->GetPointDataVec()) + { + posVec.emplace_back(glm::vec4(p.position[0], p.position[1], p.position[2], p.position[3])); + } + BuildVertexArrayObject(pointCloud); - depthVec.resize(pointCloud->size()); + depthVec.resize(numPoints); keyBuffer = std::make_shared(GL_SHADER_STORAGE_BUFFER, depthVec, GL_DYNAMIC_STORAGE_BIT); valBuffer = std::make_shared(GL_SHADER_STORAGE_BUFFER, indexVec, GL_DYNAMIC_STORAGE_BIT); posBuffer = std::make_shared(GL_SHADER_STORAGE_BUFFER, posVec); - sorter = std::make_shared(pointCloud->size()); + sorter = std::make_shared(numPoints); atomicCounterVec.resize(1, 0); atomicCounterBuffer = std::make_shared(GL_ATOMIC_COUNTER_BUFFER, atomicCounterVec, GL_DYNAMIC_STORAGE_BIT | GL_MAP_READ_BIT); @@ -183,29 +191,12 @@ void PointRenderer::BuildVertexArrayObject(std::shared_ptr pointClou { pointVao = std::make_shared(); - // convert pointCloud positions and colors into buffers - size_t numPoints = pointCloud->size(); - posVec.reserve(numPoints); - - std::vector colorVec; - colorVec.reserve(numPoints); - for (auto&& p : pointCloud->GetPointVec()) - { - posVec.emplace_back(glm::vec4(p.position[0], p.position[1], p.position[2], 1.0f)); - colorVec.emplace_back(glm::vec4(p.color[0] / 255.0f, p.color[1] / 255.0f, p.color[2] / 255.0f, 1.0f)); - } - - if (isFramebufferSRGBEnabled) - { - // convert all colors to linear space - for (auto&& c : colorVec) - { - c = SRGBToLinear(c); - } - } + const size_t numPoints = pointCloud->GetNumPoints(); - auto positionBuffer = std::make_shared(GL_ARRAY_BUFFER, posVec); - auto colorBuffer = std::make_shared(GL_ARRAY_BUFFER, colorVec); + // allocate large buffer to hold interleaved vertex data + void* pointDataPtr = (void*)pointCloud->GetPointDataVec().data(); + size_t pointDataSize = (size_t)(numPoints * sizeof(PointCloud::PointData)); + pointDataBuffer = std::make_shared(GL_ARRAY_BUFFER, pointDataPtr, pointDataSize, 0); // build element array indexVec.reserve(numPoints); @@ -216,8 +207,16 @@ void PointRenderer::BuildVertexArrayObject(std::shared_ptr pointClou } auto indexBuffer = std::make_shared(GL_ELEMENT_ARRAY_BUFFER, indexVec, GL_DYNAMIC_STORAGE_BIT); - // setup vertex array object with buffers - pointVao->SetAttribBuffer(pointProg->GetAttribLoc("position"), positionBuffer); - pointVao->SetAttribBuffer(pointProg->GetAttribLoc("color"), colorBuffer); + pointVao->Bind(); + pointDataBuffer->Bind(); + + int positionLoc = pointProg->GetAttribLoc("position"); + glVertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, sizeof(PointCloud::PointData), (void*)0); + glEnableVertexAttribArray(positionLoc); + + int colorLoc = pointProg->GetAttribLoc("color"); + glVertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, sizeof(PointCloud::PointData), (void*)(sizeof(float) * 4)); + glEnableVertexAttribArray(positionLoc); + pointVao->SetElementBuffer(indexBuffer); } diff --git a/src/pointrenderer.h b/src/pointrenderer.h index 5b968dd..97343d1 100644 --- a/src/pointrenderer.h +++ b/src/pointrenderer.h @@ -40,6 +40,8 @@ class PointRenderer std::shared_ptr preSortProg; std::shared_ptr pointVao; + std::shared_ptr pointDataBuffer; + std::vector indexVec; std::vector depthVec; std::vector posVec; From 13cb609ff228e82fb0d2fb7d5151a46e63cd337e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 26 Jun 2024 13:56:59 -0700 Subject: [PATCH 04/15] PointCloud refactor The point here is to hide the actual structure of the point cloud data, specifically PointData and the std::vector. The data can be accessed with an AttribData API and a raw void* ptr. This allows us to load the raw data directly into an OpenGL buffer, and contains the data we need to create interleaved vertex attributes into that buffer, while still allowing us to iterate over each element, if necessary, using ForEachAttrib(). In the case of PointCloud this is probably overkill, but I want to use this same API for GaussianCloud in the future. Because GaussianCloud can optionally have some or all spherical harmonic coefficients, we don't know the data layout at compile time. This AttribData API gives us the ability to only include what we need at run-time. --- src/pointcloud.cpp | 98 ++++++++++++++++++++++++++++--------------- src/pointcloud.h | 33 ++++++++++----- src/pointrenderer.cpp | 23 ++++++---- 3 files changed, 101 insertions(+), 53 deletions(-) diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index 28e49c1..f582e38 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -15,7 +15,16 @@ #include "core/util.h" #include "ply.h" +struct PointData +{ + PointData() noexcept {} + float position[4]; + float color[4]; +}; + PointCloud::PointCloud(bool useLinearColorsIn) : + numPoints(0), + pointSize(0), useLinearColors(useLinearColorsIn) { ; @@ -59,55 +68,62 @@ bool PointCloud::ImportPly(const std::string& plyFilename) Log::E("Error parsing ply file \"%s\", missing color property\n", plyFilename.c_str()); } - pointDataVec.resize(ply.GetVertexCount()); + numPoints = ply.GetVertexCount(); + pointSize = sizeof(PointData); + PointData* pointDataPtr = new PointData[numPoints]; + data.reset(pointDataPtr); + + // GL_FLOAT = 0x1406 + positionAttrib = {4, 0x1406, (int)pointSize, offsetof(PointData, position)}; + colorAttrib = {4, 0x1406, (int)pointSize, offsetof(PointData, color)}; if (useDoubles) { int i = 0; - ply.ForEachVertex([this, &i, &props](const uint8_t* data, size_t size) + ply.ForEachVertex([this, pointDataPtr, &i, &props](const uint8_t* data, size_t size) { if (useLinearColors) { - pointDataVec[i].position[0] = SRGBToLinear((float)props.x.Get(data)); - pointDataVec[i].position[1] = SRGBToLinear((float)props.y.Get(data)); - pointDataVec[i].position[2] = SRGBToLinear((float)props.z.Get(data)); + pointDataPtr[i].position[0] = SRGBToLinear((float)props.x.Get(data)); + pointDataPtr[i].position[1] = SRGBToLinear((float)props.y.Get(data)); + pointDataPtr[i].position[2] = SRGBToLinear((float)props.z.Get(data)); } else { - pointDataVec[i].position[0] = (float)props.x.Get(data); - pointDataVec[i].position[1] = (float)props.y.Get(data); - pointDataVec[i].position[2] = (float)props.z.Get(data); + pointDataPtr[i].position[0] = (float)props.x.Get(data); + pointDataPtr[i].position[1] = (float)props.y.Get(data); + pointDataPtr[i].position[2] = (float)props.z.Get(data); } - pointDataVec[i].position[3] = 1.0f; - pointDataVec[i].color[0] = (float)props.red.Get(data) / 255.0f; - pointDataVec[i].color[1] = (float)props.green.Get(data) / 255.0f; - pointDataVec[i].color[2] = (float)props.blue.Get(data) / 255.0f; - pointDataVec[i].color[3] = 1.0f; + pointDataPtr[i].position[3] = 1.0f; + pointDataPtr[i].color[0] = (float)props.red.Get(data) / 255.0f; + pointDataPtr[i].color[1] = (float)props.green.Get(data) / 255.0f; + pointDataPtr[i].color[2] = (float)props.blue.Get(data) / 255.0f; + pointDataPtr[i].color[3] = 1.0f; i++; }); } else { int i = 0; - ply.ForEachVertex([this, &i, &props](const uint8_t* data, size_t size) + ply.ForEachVertex([this, pointDataPtr, &i, &props](const uint8_t* data, size_t size) { if (useLinearColors) { - pointDataVec[i].position[0] = SRGBToLinear(props.x.Get(data)); - pointDataVec[i].position[1] = SRGBToLinear(props.y.Get(data)); - pointDataVec[i].position[2] = SRGBToLinear(props.z.Get(data)); + pointDataPtr[i].position[0] = SRGBToLinear(props.x.Get(data)); + pointDataPtr[i].position[1] = SRGBToLinear(props.y.Get(data)); + pointDataPtr[i].position[2] = SRGBToLinear(props.z.Get(data)); } else { - pointDataVec[i].position[0] = props.x.Get(data); - pointDataVec[i].position[1] = props.y.Get(data); - pointDataVec[i].position[2] = props.z.Get(data); + pointDataPtr[i].position[0] = props.x.Get(data); + pointDataPtr[i].position[1] = props.y.Get(data); + pointDataPtr[i].position[2] = props.z.Get(data); } - pointDataVec[i].position[3] = 1.0f; - pointDataVec[i].color[0] = (float)props.red.Get(data) / 255.0f; - pointDataVec[i].color[1] = (float)props.green.Get(data) / 255.0f; - pointDataVec[i].color[2] = (float)props.blue.Get(data) / 255.0f; - pointDataVec[i].color[2] = 1.0f; + pointDataPtr[i].position[3] = 1.0f; + pointDataPtr[i].color[0] = (float)props.red.Get(data) / 255.0f; + pointDataPtr[i].color[1] = (float)props.green.Get(data) / 255.0f; + pointDataPtr[i].color[2] = (float)props.blue.Get(data) / 255.0f; + pointDataPtr[i].color[3] = 1.0f; i++; }); } @@ -130,7 +146,7 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const // ply files have unix line endings. plyFile << "ply\n"; plyFile << "format binary_little_endian 1.0\n"; - plyFile << "element vertex " << pointDataVec.size() << "\n"; + plyFile << "element vertex " << numPoints << "\n"; plyFile << "property float x\n"; plyFile << "property float y\n"; plyFile << "property float z\n"; @@ -155,19 +171,22 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const void PointCloud::InitDebugCloud() { - pointDataVec.clear(); + const int NUM_POINTS = 5; + + numPoints = NUM_POINTS * 3; + pointSize = sizeof(PointData); + PointData* pointDataPtr = new PointData[numPoints]; + data.reset(pointDataPtr); // // make an debug pointVec, that contains three lines one for each axis. // const float AXIS_LENGTH = 1.0f; - const int NUM_POINTS = 5; const float DELTA = (AXIS_LENGTH / (float)NUM_POINTS); - pointDataVec.resize(NUM_POINTS * 3); // x axis for (int i = 0; i < NUM_POINTS; i++) { - PointCloud::PointData& p = pointDataVec[i]; + PointData& p = pointDataPtr[i]; p.position[0] = i * DELTA; p.position[1] = 0.0f; p.position[2] = 0.0f; @@ -180,7 +199,7 @@ void PointCloud::InitDebugCloud() // y axis for (int i = 0; i < NUM_POINTS; i++) { - PointCloud::PointData& p = pointDataVec[i + NUM_POINTS]; + PointData& p = pointDataPtr[i + NUM_POINTS]; p.position[0] = 0.0f; p.position[1] = i * DELTA; p.position[2] = 0.0f; @@ -193,14 +212,25 @@ void PointCloud::InitDebugCloud() // z axis for (int i = 0; i < NUM_POINTS; i++) { - PointCloud::PointData& p = pointDataVec[i + 2 * NUM_POINTS]; + PointData& p = pointDataPtr[i + 2 * NUM_POINTS]; p.position[0] = 0.0f; p.position[1] = 0.0f; p.position[2] = i * DELTA; p.position[3] = 1.0f; - p.color[0] = 0; - p.color[1] = 0; + p.color[0] = 0.0f; + p.color[1] = 0.0f; p.color[2] = 1.0f; p.color[3] = 0.0f; } } + +void PointCloud::ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const +{ + const uint8_t* bytePtr = (uint8_t*)data.get(); + bytePtr += attribData.offset; + for (size_t i = 0; i < GetNumPoints(); i++) + { + cb((const void*)bytePtr); + bytePtr += attribData.stride; + } +} diff --git a/src/pointcloud.h b/src/pointcloud.h index 2b0f306..0704106 100644 --- a/src/pointcloud.h +++ b/src/pointcloud.h @@ -5,9 +5,10 @@ #pragma once -#include -#include #include +#include +#include +#include class PointCloud { @@ -19,18 +20,30 @@ class PointCloud void InitDebugCloud(); - struct PointData + struct AttribData { - PointData() noexcept {} - float position[4]; - float color[4]; + int32_t size; + int32_t type; + int32_t stride; + size_t offset; }; - size_t GetNumPoints() const { return pointDataVec.size(); } - const std::vector& GetPointDataVec() const { return pointDataVec; } - std::vector& GetPointDataVec() { return pointDataVec; } + size_t GetNumPoints() const { return numPoints; } + size_t GetTotalSize() const { return GetNumPoints() * pointSize; } + void* GetRawDataPtr() { return data.get(); } + + const AttribData& GetPositionAttrib() const { return positionAttrib; } + const AttribData& GetColorAttrib() const { return colorAttrib; } + + using AttribCallback = std::function; + void ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const; protected: - std::vector pointDataVec; + std::shared_ptr data; + AttribData positionAttrib; + AttribData colorAttrib; + + size_t numPoints; + size_t pointSize; bool useLinearColors; }; diff --git a/src/pointrenderer.cpp b/src/pointrenderer.cpp index 6b1d58d..3bfbf69 100644 --- a/src/pointrenderer.cpp +++ b/src/pointrenderer.cpp @@ -70,12 +70,14 @@ bool PointRenderer::Init(std::shared_ptr pointCloud, bool isFramebuf } const size_t numPoints = pointCloud->GetNumPoints(); + // build posVec posVec.reserve(numPoints); - for (auto&& p : pointCloud->GetPointDataVec()) + pointCloud->ForEachAttrib(pointCloud->GetPositionAttrib(), [this](const void* ptr) { - posVec.emplace_back(glm::vec4(p.position[0], p.position[1], p.position[2], p.position[3])); - } + const float* p = (const float*)ptr; + posVec.emplace_back(glm::vec4(p[0], p[1], p[2], p[3])); + }); BuildVertexArrayObject(pointCloud); @@ -194,9 +196,8 @@ void PointRenderer::BuildVertexArrayObject(std::shared_ptr pointClou const size_t numPoints = pointCloud->GetNumPoints(); // allocate large buffer to hold interleaved vertex data - void* pointDataPtr = (void*)pointCloud->GetPointDataVec().data(); - size_t pointDataSize = (size_t)(numPoints * sizeof(PointCloud::PointData)); - pointDataBuffer = std::make_shared(GL_ARRAY_BUFFER, pointDataPtr, pointDataSize, 0); + pointDataBuffer = std::make_shared(GL_ARRAY_BUFFER, pointCloud->GetRawDataPtr(), + pointCloud->GetTotalSize(), 0); // build element array indexVec.reserve(numPoints); @@ -211,12 +212,16 @@ void PointRenderer::BuildVertexArrayObject(std::shared_ptr pointClou pointDataBuffer->Bind(); int positionLoc = pointProg->GetAttribLoc("position"); - glVertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, sizeof(PointCloud::PointData), (void*)0); + PointCloud::AttribData positionAttrib = pointCloud->GetPositionAttrib(); + glVertexAttribPointer(positionLoc, positionAttrib.size, positionAttrib.type, GL_FALSE, + positionAttrib.stride, (void*)positionAttrib.offset); glEnableVertexAttribArray(positionLoc); int colorLoc = pointProg->GetAttribLoc("color"); - glVertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, sizeof(PointCloud::PointData), (void*)(sizeof(float) * 4)); - glEnableVertexAttribArray(positionLoc); + PointCloud::AttribData colorAttrib = pointCloud->GetColorAttrib(); + glVertexAttribPointer(colorLoc, colorAttrib.size, colorAttrib.type, GL_FALSE, + colorAttrib.stride, (void*)colorAttrib.offset); + glEnableVertexAttribArray(colorLoc); pointVao->SetElementBuffer(indexBuffer); } From a464ca475d710452edb26ae0830615a6e8e91a6c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jun 2024 11:37:43 -0700 Subject: [PATCH 05/15] Move to interleaved vertex arrays for SplatRenderer * GaussianCloud moved to AttribData style API. * Moved all cooking of data for rendering into GaussianCloud, So setting up interleaved vertex arrays in SplatRenderer is straight forward. * Overall 2x loading time improvement * ~20% fps improvement on large scenes * Still some cleanup necessary before merging into main. See AJT: comments --- src/app.cpp | 6 +- src/gaussiancloud.cpp | 200 +++++++++++++++++++++++++++++++++++++----- src/gaussiancloud.h | 88 +++++++++++++------ src/pointcloud.cpp | 62 ++++++------- src/pointcloud.h | 12 ++- src/pointrenderer.cpp | 20 ++--- src/splatrenderer.cpp | 149 ++++++++++--------------------- src/splatrenderer.h | 1 + 8 files changed, 332 insertions(+), 206 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index da95232..2998251 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -228,9 +228,9 @@ static std::shared_ptr LoadPointCloud(const std::string& plyFilename return pointCloud; } -static std::shared_ptr LoadGaussianCloud(const std::string& plyFilename) +static std::shared_ptr LoadGaussianCloud(const std::string& plyFilename, bool useLinearColors) { - auto gaussianCloud = std::make_shared(); + auto gaussianCloud = std::make_shared(useLinearColors); if (!gaussianCloud->ImportPly(plyFilename)) { @@ -528,7 +528,7 @@ bool App::Init() Log::D("Could not find input.ply\n"); } - gaussianCloud = LoadGaussianCloud(plyFilename); + gaussianCloud = LoadGaussianCloud(plyFilename, isFramebufferSRGBEnabled); if (!gaussianCloud) { Log::E("Error loading GaussianCloud\n"); diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index 1402855..715e14a 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -13,6 +13,8 @@ #include #include +#include + #ifdef TRACY_ENABLE #include #else @@ -25,8 +27,48 @@ #include "ply.h" -GaussianCloud::GaussianCloud() +struct GaussianData +{ + GaussianData() noexcept {} + float posWithAlpha[4]; // center of the gaussian in object coordinates, with alpha in w + float r_sh0[4]; // sh coeff for red channel (up to third-order) + float r_sh1[4]; + float r_sh2[4]; + float r_sh3[4]; + float g_sh0[4]; // sh coeff for green channel + float g_sh1[4]; + float g_sh2[4]; + float g_sh3[4]; + float b_sh0[4]; // sh coeff for blue channel + float b_sh1[4]; + float b_sh2[4]; + float b_sh3[4]; + float cov3_col0[3]; // 3x3 covariance matrix of the splat in object coordinates. + float cov3_col1[3]; + float cov3_col2[3]; +}; + +glm::mat3 ComputeCovMat(float rot[4], float scale[3]) { + glm::quat q(rot[0], rot[1], rot[2], rot[3]); + glm::mat3 R(glm::normalize(q)); + glm::mat3 S(glm::vec3(expf(scale[0]), 0.0f, 0.0f), + glm::vec3(0.0f, expf(scale[1]), 0.0f), + glm::vec3(0.0f, 0.0f, expf(scale[2]))); + return R * S * glm::transpose(S) * glm::transpose(R); +} + +float ComputeAlpha(float opacity) +{ + return 1.0f / (1.0f + expf(-opacity)); +} + +GaussianCloud::GaussianCloud(bool useLinearColorsIn) : + numGaussians(0), + gaussianSize(0), + useLinearColors(useLinearColorsIn) +{ + ; } bool GaussianCloud::ImportPly(const std::string& plyFilename) @@ -61,7 +103,6 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) Ply::PropertyInfo rot[4]; } props; - { ZoneScopedNC("ply.GetProps", tracy::Color::Green); @@ -113,36 +154,124 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) } + GaussianData* gd; { - ZoneScopedNC("ply.resize", tracy::Color::Red4); - gaussianVec.resize(ply.GetVertexCount()); + ZoneScopedNC("alloc data", tracy::Color::Red4); + + // AJT: TODO support with and without sh. + + numGaussians = ply.GetVertexCount(); + gaussianSize = sizeof(GaussianData); + gd = new GaussianData[numGaussians]; + data.reset(gd); + + // GL_FLOAT = 0x1406 + posWithAlphaAttrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, posWithAlpha)}; + r_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh0)}; + r_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh1)}; + r_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh2)}; + r_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh3)}; + g_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh0)}; + g_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh1)}; + g_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh2)}; + g_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh3)}; + b_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh0)}; + b_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh1)}; + b_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh2)}; + b_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh3)}; + cov3_col0Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col0)}; + cov3_col1Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col1)}; + cov3_col2Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col2)}; } { ZoneScopedNC("ply.ForEachVertex", tracy::Color::Blue); int i = 0; - ply.ForEachVertex([this, &i, &props](const uint8_t* data, size_t size) + ply.ForEachVertex([this, gd, &i, &props](const uint8_t* data, size_t size) { - gaussianVec[i].position[0] = props.x.Get(data); - gaussianVec[i].position[1] = props.y.Get(data); - gaussianVec[i].position[2] = props.z.Get(data); - for (int j = 0; j < 3; j++) - { - gaussianVec[i].f_dc[j] = props.f_dc[j].Get(data); - } - for (int j = 0; j < 45; j++) + gd[i].posWithAlpha[0] = props.x.Get(data); + gd[i].posWithAlpha[1] = props.y.Get(data); + gd[i].posWithAlpha[2] = props.z.Get(data); + gd[i].posWithAlpha[3] = ComputeAlpha(props.opacity.Get(data)); + + // AJT: TODO check for useLinearColors + + gd[i].r_sh0[0] = props.f_dc[0].Get(data); + gd[i].r_sh0[1] = props.f_rest[0].Get(data); + gd[i].r_sh0[2] = props.f_rest[1].Get(data); + gd[i].r_sh0[3] = props.f_rest[2].Get(data); + gd[i].r_sh1[0] = props.f_rest[3].Get(data); + gd[i].r_sh1[1] = props.f_rest[4].Get(data); + gd[i].r_sh1[2] = props.f_rest[5].Get(data); + gd[i].r_sh1[3] = props.f_rest[6].Get(data); + gd[i].r_sh2[0] = props.f_rest[7].Get(data); + gd[i].r_sh2[1] = props.f_rest[8].Get(data); + gd[i].r_sh2[2] = props.f_rest[9].Get(data); + gd[i].r_sh2[3] = props.f_rest[10].Get(data); + gd[i].r_sh3[0] = props.f_rest[11].Get(data); + gd[i].r_sh3[1] = props.f_rest[12].Get(data); + gd[i].r_sh3[2] = props.f_rest[13].Get(data); + gd[i].r_sh3[3] = props.f_rest[14].Get(data); + + gd[i].g_sh0[0] = props.f_dc[1].Get(data); + gd[i].g_sh0[1] = props.f_rest[15].Get(data); + gd[i].g_sh0[2] = props.f_rest[16].Get(data); + gd[i].g_sh0[3] = props.f_rest[17].Get(data); + gd[i].g_sh1[0] = props.f_rest[18].Get(data); + gd[i].g_sh1[1] = props.f_rest[19].Get(data); + gd[i].g_sh1[2] = props.f_rest[20].Get(data); + gd[i].g_sh1[3] = props.f_rest[21].Get(data); + gd[i].g_sh2[0] = props.f_rest[22].Get(data); + gd[i].g_sh2[1] = props.f_rest[23].Get(data); + gd[i].g_sh2[2] = props.f_rest[24].Get(data); + gd[i].g_sh2[3] = props.f_rest[25].Get(data); + gd[i].g_sh3[0] = props.f_rest[26].Get(data); + gd[i].g_sh3[1] = props.f_rest[27].Get(data); + gd[i].g_sh3[2] = props.f_rest[28].Get(data); + gd[i].g_sh3[3] = props.f_rest[29].Get(data); + + gd[i].b_sh0[0] = props.f_dc[2].Get(data); + gd[i].b_sh0[1] = props.f_rest[30].Get(data); + gd[i].b_sh0[2] = props.f_rest[31].Get(data); + gd[i].b_sh0[3] = props.f_rest[32].Get(data); + gd[i].b_sh1[0] = props.f_rest[33].Get(data); + gd[i].b_sh1[1] = props.f_rest[34].Get(data); + gd[i].b_sh1[2] = props.f_rest[35].Get(data); + gd[i].b_sh1[3] = props.f_rest[36].Get(data); + gd[i].b_sh2[0] = props.f_rest[37].Get(data); + gd[i].b_sh2[1] = props.f_rest[38].Get(data); + gd[i].b_sh2[2] = props.f_rest[39].Get(data); + gd[i].b_sh2[3] = props.f_rest[40].Get(data); + gd[i].b_sh3[0] = props.f_rest[41].Get(data); + gd[i].b_sh3[1] = props.f_rest[42].Get(data); + gd[i].b_sh3[2] = props.f_rest[43].Get(data); + gd[i].b_sh3[3] = props.f_rest[44].Get(data); + + float rot[4] = { - gaussianVec[i].f_rest[j] = props.f_rest[j].Get(data); - } - gaussianVec[i].opacity = props.opacity.Get(data); - for (int j = 0; j < 3; j++) + props.rot[0].Get(data), + props.rot[1].Get(data), + props.rot[2].Get(data), + props.rot[3].Get(data) + }; + float scale[4] = { - gaussianVec[i].scale[j] = props.scale[j].Get(data); - } - for (int j = 0; j < 4; j++) - { - gaussianVec[i].rot[j] = props.rot[j].Get(data); - } + props.scale[0].Get(data), + props.scale[1].Get(data), + props.scale[2].Get(data) + }; + + glm::mat3 V = ComputeCovMat(rot, scale); + gd[i].cov3_col0[0] = V[0][0]; + gd[i].cov3_col0[1] = V[0][1]; + gd[i].cov3_col0[2] = V[0][2]; + gd[i].cov3_col1[0] = V[1][0]; + gd[i].cov3_col1[1] = V[1][1]; + gd[i].cov3_col1[2] = V[1][2]; + gd[i].cov3_col2[0] = V[2][0]; + gd[i].cov3_col2[1] = V[2][1]; + gd[i].cov3_col2[2] = V[2][2]; + i++; }); } @@ -152,6 +281,9 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) bool GaussianCloud::ExportPly(const std::string& plyFilename) const { + // AJT: TODO FIXME: + return false; + std::ofstream plyFile(plyFilename, std::ios::binary); if (!plyFile.is_open()) { @@ -162,7 +294,7 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename) const // ply files have unix line endings. plyFile << "ply\n"; plyFile << "format binary_little_endian 1.0\n"; - plyFile << "element vertex " << gaussianVec.size() << "\n"; + plyFile << "element vertex " << numGaussians << "\n"; plyFile << "property float x\n"; plyFile << "property float y\n"; plyFile << "property float z\n"; @@ -227,6 +359,7 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename) const plyFile << "property float rot_3\n"; plyFile << "end_header\n"; + /* AJT: TODO FIXME const size_t GAUSSIAN_SIZE = 62 * sizeof(float); static_assert(sizeof(Gaussian) >= GAUSSIAN_SIZE); @@ -234,12 +367,16 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename) const { plyFile.write((char*)&g, GAUSSIAN_SIZE); } + */ return true; } void GaussianCloud::InitDebugCloud() { + // AJT: TODO fix me + +#if 0 gaussianVec.clear(); // @@ -310,11 +447,14 @@ void GaussianCloud::InitDebugCloud() g.scale[0] = S; g.scale[1] = S; g.scale[2] = S; g.rot[0] = 1.0f; g.rot[1] = 0.0f; g.rot[2] = 0.0f; g.rot[3] = 0.0f; gaussianVec.push_back(g); +#endif } // only keep the nearest splats void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats) { + // AJT: TODO fixme +#if 0 if (static_cast(numSplats) >= gaussianVec.size()) { return; @@ -342,4 +482,16 @@ void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats) } gaussianVec.swap(newGaussianVec); +#endif +} + +void GaussianCloud::ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const +{ + const uint8_t* bytePtr = (uint8_t*)data.get(); + bytePtr += attribData.offset; + for (size_t i = 0; i < GetNumGaussians(); i++) + { + cb((const void*)bytePtr); + bytePtr += attribData.stride; + } } diff --git a/src/gaussiancloud.h b/src/gaussiancloud.h index 5621744..1eb0f6f 100644 --- a/src/gaussiancloud.h +++ b/src/gaussiancloud.h @@ -5,15 +5,17 @@ #pragma once -#include -#include +#include +#include #include #include +#include + class GaussianCloud { public: - GaussianCloud(); + GaussianCloud(bool useLinearColorsIn); bool ImportPly(const std::string& plyFilename); bool ExportPly(const std::string& plyFilename) const; @@ -21,37 +23,65 @@ class GaussianCloud void InitDebugCloud(); // only keep the nearest splats - void PruneSplats(const glm::vec3& origin, uint32_t numSplats); + void PruneSplats(const glm::vec3& origin, uint32_t numGaussians); + + size_t GetNumGaussians() const { return numGaussians; } + size_t GetTotalSize() const { return GetNumGaussians() * gaussianSize; } + void* GetRawDataPtr() { return data.get(); } - struct Gaussian + struct AttribData { - Gaussian() noexcept {} - float position[3]; // in world space - float normal[3]; // unused - float f_dc[3]; // first order spherical harmonics coeff (sRGB color space) - float f_rest[45]; // more spherical harminics coeff - float opacity; // alpha = 1 / (1 + exp(-opacity)); - float scale[3]; - float rot[4]; // local rotation of guassian (real, i, j, k) - - // convert from (scale, rot) into the gaussian covariance matrix in world space - // See 3d Gaussian Splat paper for more info - glm::mat3 ComputeCovMat() const - { - glm::quat q(rot[0], rot[1], rot[2], rot[3]); - glm::mat3 R(glm::normalize(q)); - glm::mat3 S(glm::vec3(expf(scale[0]), 0.0f, 0.0f), - glm::vec3(0.0f, expf(scale[1]), 0.0f), - glm::vec3(0.0f, 0.0f, expf(scale[2]))); - return R * S * glm::transpose(S) * glm::transpose(R); - } + AttribData() : size(0), type(0), stride(0), offset(0) {} + AttribData(int32_t sizeIn, int32_t typeIn, int32_t strideIn, size_t offsetIn) : size(sizeIn), type(typeIn), stride(strideIn), offset(offsetIn) {} + bool IsValid() const { return size != 0; } + int32_t size; + int32_t type; + int32_t stride; + size_t offset; }; - const std::vector& GetGaussianVec() const { return gaussianVec; } - std::vector& GetGaussianVec() { return gaussianVec; } - size_t size() const { return gaussianVec.size(); } + const AttribData& GetPosWithAlphaAttrib() const { return posWithAlphaAttrib; } + const AttribData& GetR_SH0Attrib() const { return r_sh0Attrib; } + const AttribData& GetR_SH1Attrib() const { return r_sh1Attrib; } + const AttribData& GetR_SH2Attrib() const { return r_sh2Attrib; } + const AttribData& GetR_SH3Attrib() const { return r_sh3Attrib; } + const AttribData& GetG_SH0Attrib() const { return g_sh0Attrib; } + const AttribData& GetG_SH1Attrib() const { return g_sh1Attrib; } + const AttribData& GetG_SH2Attrib() const { return g_sh2Attrib; } + const AttribData& GetG_SH3Attrib() const { return g_sh3Attrib; } + const AttribData& GetB_SH0Attrib() const { return b_sh0Attrib; } + const AttribData& GetB_SH1Attrib() const { return b_sh1Attrib; } + const AttribData& GetB_SH2Attrib() const { return b_sh2Attrib; } + const AttribData& GetB_SH3Attrib() const { return b_sh3Attrib; } + const AttribData& GetCov3_Col0Attrib() const { return cov3_col0Attrib; } + const AttribData& GetCov3_Col1Attrib() const { return cov3_col1Attrib; } + const AttribData& GetCov3_Col2Attrib() const { return cov3_col2Attrib; } + + using AttribCallback = std::function; + void ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const; protected: - std::vector gaussianVec; + std::shared_ptr data; + + AttribData posWithAlphaAttrib; + AttribData r_sh0Attrib; + AttribData r_sh1Attrib; + AttribData r_sh2Attrib; + AttribData r_sh3Attrib; + AttribData g_sh0Attrib; + AttribData g_sh1Attrib; + AttribData g_sh2Attrib; + AttribData g_sh3Attrib; + AttribData b_sh0Attrib; + AttribData b_sh1Attrib; + AttribData b_sh2Attrib; + AttribData b_sh3Attrib; + AttribData cov3_col0Attrib; + AttribData cov3_col1Attrib; + AttribData cov3_col2Attrib; + + size_t numGaussians; + size_t gaussianSize; + bool useLinearColors; }; diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index f582e38..e168287 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -70,8 +70,8 @@ bool PointCloud::ImportPly(const std::string& plyFilename) numPoints = ply.GetVertexCount(); pointSize = sizeof(PointData); - PointData* pointDataPtr = new PointData[numPoints]; - data.reset(pointDataPtr); + PointData* pd = new PointData[numPoints]; + data.reset(pd); // GL_FLOAT = 0x1406 positionAttrib = {4, 0x1406, (int)pointSize, offsetof(PointData, position)}; @@ -80,50 +80,50 @@ bool PointCloud::ImportPly(const std::string& plyFilename) if (useDoubles) { int i = 0; - ply.ForEachVertex([this, pointDataPtr, &i, &props](const uint8_t* data, size_t size) + ply.ForEachVertex([this, pd, &i, &props](const uint8_t* data, size_t size) { if (useLinearColors) { - pointDataPtr[i].position[0] = SRGBToLinear((float)props.x.Get(data)); - pointDataPtr[i].position[1] = SRGBToLinear((float)props.y.Get(data)); - pointDataPtr[i].position[2] = SRGBToLinear((float)props.z.Get(data)); + pd[i].position[0] = SRGBToLinear((float)props.x.Get(data)); + pd[i].position[1] = SRGBToLinear((float)props.y.Get(data)); + pd[i].position[2] = SRGBToLinear((float)props.z.Get(data)); } else { - pointDataPtr[i].position[0] = (float)props.x.Get(data); - pointDataPtr[i].position[1] = (float)props.y.Get(data); - pointDataPtr[i].position[2] = (float)props.z.Get(data); + pd[i].position[0] = (float)props.x.Get(data); + pd[i].position[1] = (float)props.y.Get(data); + pd[i].position[2] = (float)props.z.Get(data); } - pointDataPtr[i].position[3] = 1.0f; - pointDataPtr[i].color[0] = (float)props.red.Get(data) / 255.0f; - pointDataPtr[i].color[1] = (float)props.green.Get(data) / 255.0f; - pointDataPtr[i].color[2] = (float)props.blue.Get(data) / 255.0f; - pointDataPtr[i].color[3] = 1.0f; + pd[i].position[3] = 1.0f; + pd[i].color[0] = (float)props.red.Get(data) / 255.0f; + pd[i].color[1] = (float)props.green.Get(data) / 255.0f; + pd[i].color[2] = (float)props.blue.Get(data) / 255.0f; + pd[i].color[3] = 1.0f; i++; }); } else { int i = 0; - ply.ForEachVertex([this, pointDataPtr, &i, &props](const uint8_t* data, size_t size) + ply.ForEachVertex([this, pd, &i, &props](const uint8_t* data, size_t size) { if (useLinearColors) { - pointDataPtr[i].position[0] = SRGBToLinear(props.x.Get(data)); - pointDataPtr[i].position[1] = SRGBToLinear(props.y.Get(data)); - pointDataPtr[i].position[2] = SRGBToLinear(props.z.Get(data)); + pd[i].position[0] = SRGBToLinear(props.x.Get(data)); + pd[i].position[1] = SRGBToLinear(props.y.Get(data)); + pd[i].position[2] = SRGBToLinear(props.z.Get(data)); } else { - pointDataPtr[i].position[0] = props.x.Get(data); - pointDataPtr[i].position[1] = props.y.Get(data); - pointDataPtr[i].position[2] = props.z.Get(data); + pd[i].position[0] = props.x.Get(data); + pd[i].position[1] = props.y.Get(data); + pd[i].position[2] = props.z.Get(data); } - pointDataPtr[i].position[3] = 1.0f; - pointDataPtr[i].color[0] = (float)props.red.Get(data) / 255.0f; - pointDataPtr[i].color[1] = (float)props.green.Get(data) / 255.0f; - pointDataPtr[i].color[2] = (float)props.blue.Get(data) / 255.0f; - pointDataPtr[i].color[3] = 1.0f; + pd[i].position[3] = 1.0f; + pd[i].color[0] = (float)props.red.Get(data) / 255.0f; + pd[i].color[1] = (float)props.green.Get(data) / 255.0f; + pd[i].color[2] = (float)props.blue.Get(data) / 255.0f; + pd[i].color[3] = 1.0f; i++; }); } @@ -175,8 +175,8 @@ void PointCloud::InitDebugCloud() numPoints = NUM_POINTS * 3; pointSize = sizeof(PointData); - PointData* pointDataPtr = new PointData[numPoints]; - data.reset(pointDataPtr); + PointData* pd = new PointData[numPoints]; + data.reset(pd); // // make an debug pointVec, that contains three lines one for each axis. @@ -186,7 +186,7 @@ void PointCloud::InitDebugCloud() // x axis for (int i = 0; i < NUM_POINTS; i++) { - PointData& p = pointDataPtr[i]; + PointData& p = pd[i]; p.position[0] = i * DELTA; p.position[1] = 0.0f; p.position[2] = 0.0f; @@ -199,7 +199,7 @@ void PointCloud::InitDebugCloud() // y axis for (int i = 0; i < NUM_POINTS; i++) { - PointData& p = pointDataPtr[i + NUM_POINTS]; + PointData& p = pd[i + NUM_POINTS]; p.position[0] = 0.0f; p.position[1] = i * DELTA; p.position[2] = 0.0f; @@ -212,7 +212,7 @@ void PointCloud::InitDebugCloud() // z axis for (int i = 0; i < NUM_POINTS; i++) { - PointData& p = pointDataPtr[i + 2 * NUM_POINTS]; + PointData& p = pd[i + 2 * NUM_POINTS]; p.position[0] = 0.0f; p.position[1] = 0.0f; p.position[2] = i * DELTA; diff --git a/src/pointcloud.h b/src/pointcloud.h index 0704106..dd4bf83 100644 --- a/src/pointcloud.h +++ b/src/pointcloud.h @@ -20,18 +20,21 @@ class PointCloud void InitDebugCloud(); + size_t GetNumPoints() const { return numPoints; } + size_t GetTotalSize() const { return GetNumPoints() * pointSize; } + void* GetRawDataPtr() { return data.get(); } + struct AttribData { + AttribData() : size(0), type(0), stride(0), offset(0) {} + AttribData(int32_t sizeIn, int32_t typeIn, int32_t strideIn, size_t offsetIn) : size(sizeIn), type(typeIn), stride(strideIn), offset(offsetIn) {} + bool IsValid() const { return size != 0; } int32_t size; int32_t type; int32_t stride; size_t offset; }; - size_t GetNumPoints() const { return numPoints; } - size_t GetTotalSize() const { return GetNumPoints() * pointSize; } - void* GetRawDataPtr() { return data.get(); } - const AttribData& GetPositionAttrib() const { return positionAttrib; } const AttribData& GetColorAttrib() const { return colorAttrib; } @@ -40,6 +43,7 @@ class PointCloud protected: std::shared_ptr data; + AttribData positionAttrib; AttribData colorAttrib; diff --git a/src/pointrenderer.cpp b/src/pointrenderer.cpp index 3bfbf69..d6b8743 100644 --- a/src/pointrenderer.cpp +++ b/src/pointrenderer.cpp @@ -30,6 +30,12 @@ #include "radix_sort.hpp" +static void SetupAttrib(int loc, const PointCloud::AttribData& attrib) +{ + glVertexAttribPointer(loc, attrib.size, attrib.type, GL_FALSE, attrib.stride, (void*)attrib.offset); + glEnableVertexAttribArray(loc); +} + PointRenderer::PointRenderer() { } @@ -211,17 +217,9 @@ void PointRenderer::BuildVertexArrayObject(std::shared_ptr pointClou pointVao->Bind(); pointDataBuffer->Bind(); - int positionLoc = pointProg->GetAttribLoc("position"); - PointCloud::AttribData positionAttrib = pointCloud->GetPositionAttrib(); - glVertexAttribPointer(positionLoc, positionAttrib.size, positionAttrib.type, GL_FALSE, - positionAttrib.stride, (void*)positionAttrib.offset); - glEnableVertexAttribArray(positionLoc); - - int colorLoc = pointProg->GetAttribLoc("color"); - PointCloud::AttribData colorAttrib = pointCloud->GetColorAttrib(); - glVertexAttribPointer(colorLoc, colorAttrib.size, colorAttrib.type, GL_FALSE, - colorAttrib.stride, (void*)colorAttrib.offset); - glEnableVertexAttribArray(colorLoc); + SetupAttrib(pointProg->GetAttribLoc("position"), pointCloud->GetPositionAttrib()); + SetupAttrib(pointProg->GetAttribLoc("color"), pointCloud->GetColorAttrib()); pointVao->SetElementBuffer(indexBuffer); + pointDataBuffer->Unbind(); } diff --git a/src/splatrenderer.cpp b/src/splatrenderer.cpp index 118aa1e..8091a55 100644 --- a/src/splatrenderer.cpp +++ b/src/splatrenderer.cpp @@ -32,6 +32,12 @@ static const uint32_t NUM_BLOCKS_PER_WORKGROUP = 1024; +static void SetupAttrib(int loc, const GaussianCloud::AttribData& attrib) +{ + glVertexAttribPointer(loc, attrib.size, attrib.type, GL_FALSE, attrib.stride, (void*)attrib.offset); + glEnableVertexAttribArray(loc); +} + SplatRenderer::SplatRenderer() { } @@ -96,9 +102,18 @@ bool SplatRenderer::Init(std::shared_ptr gaussianCloud, bool isFr } } + // build posVec + size_t numGaussians = gaussianCloud->GetNumGaussians(); + posVec.reserve(numGaussians); + gaussianCloud->ForEachAttrib(gaussianCloud->GetPosWithAlphaAttrib(), [this](const void* ptr) + { + const float* p = (const float*)ptr; + posVec.emplace_back(glm::vec4(p[0], p[1], p[2], 1.0f)); + }); + BuildVertexArrayObject(gaussianCloud); - depthVec.resize(gaussianCloud->size()); + depthVec.resize(numGaussians); if (useMultiRadixSort) { @@ -107,7 +122,7 @@ bool SplatRenderer::Init(std::shared_ptr gaussianCloud, bool isFr keyBuffer = std::make_shared(GL_SHADER_STORAGE_BUFFER, depthVec, GL_DYNAMIC_STORAGE_BIT); keyBuffer2 = std::make_shared(GL_SHADER_STORAGE_BUFFER, depthVec, GL_DYNAMIC_STORAGE_BIT); - const uint32_t NUM_ELEMENTS = static_cast(gaussianCloud->size()); + const uint32_t NUM_ELEMENTS = static_cast(numGaussians); const uint32_t NUM_WORKGROUPS = (NUM_ELEMENTS + numBlocksPerWorkgroup - 1) / numBlocksPerWorkgroup; const uint32_t RADIX_SORT_BINS = 256; @@ -125,7 +140,7 @@ bool SplatRenderer::Init(std::shared_ptr gaussianCloud, bool isFr valBuffer = std::make_shared(GL_SHADER_STORAGE_BUFFER, indexVec, GL_DYNAMIC_STORAGE_BIT); posBuffer = std::make_shared(GL_SHADER_STORAGE_BUFFER, posVec); - sorter = std::make_shared(gaussianCloud->size()); + sorter = std::make_shared(numGaussians); } atomicCounterVec.resize(1, 0); @@ -332,119 +347,45 @@ void SplatRenderer::BuildVertexArrayObject(std::shared_ptr gaussi { splatVao = std::make_shared(); - // convert gaussianCloud data into buffers - size_t numPoints = gaussianCloud->size(); - posVec.reserve(numPoints); - - // sh coeff - std::vector r_sh0Vec, g_sh0Vec, b_sh0Vec; - std::vector r_sh1Vec, r_sh2Vec, r_sh3Vec, r_sh4Vec; - std::vector g_sh1Vec, g_sh2Vec, g_sh3Vec, g_sh4Vec; - std::vector b_sh1Vec, b_sh2Vec, b_sh3Vec, b_sh4Vec; - - // 3x3 cov matrix - std::vector cov3_col0Vec, cov3_col1Vec, cov3_col2Vec; - - r_sh0Vec.reserve(numPoints); - g_sh0Vec.reserve(numPoints); - b_sh0Vec.reserve(numPoints); - - if (useFullSH) - { - r_sh1Vec.reserve(numPoints); - r_sh2Vec.reserve(numPoints); - r_sh3Vec.reserve(numPoints); - g_sh1Vec.reserve(numPoints); - g_sh2Vec.reserve(numPoints); - g_sh3Vec.reserve(numPoints); - b_sh1Vec.reserve(numPoints); - b_sh2Vec.reserve(numPoints); - b_sh3Vec.reserve(numPoints); - } - - cov3_col0Vec.reserve(numPoints); - cov3_col1Vec.reserve(numPoints); - cov3_col2Vec.reserve(numPoints); - - for (auto&& g : gaussianCloud->GetGaussianVec()) - { - // stick alpha into position.w - float alpha = 1.0f / (1.0f + expf(-g.opacity)); - posVec.emplace_back(glm::vec4(g.position[0], g.position[1], g.position[2], alpha)); + // allocate large buffer to hold interleaved vertex data + gaussianDataBuffer = std::make_shared(GL_ARRAY_BUFFER, + gaussianCloud->GetRawDataPtr(), + gaussianCloud->GetTotalSize(), 0); - r_sh0Vec.emplace_back(glm::vec4(g.f_dc[0], g.f_rest[0], g.f_rest[1], g.f_rest[2])); - g_sh0Vec.emplace_back(glm::vec4(g.f_dc[1], g.f_rest[15], g.f_rest[16], g.f_rest[17])); - b_sh0Vec.emplace_back(glm::vec4(g.f_dc[2], g.f_rest[30], g.f_rest[31], g.f_rest[32])); - - if (useFullSH) - { - r_sh1Vec.emplace_back(glm::vec4(g.f_rest[3], g.f_rest[4], g.f_rest[5], g.f_rest[6])); - r_sh2Vec.emplace_back(glm::vec4(g.f_rest[7], g.f_rest[8], g.f_rest[9], g.f_rest[10])); - r_sh3Vec.emplace_back(glm::vec4(g.f_rest[11], g.f_rest[12], g.f_rest[13], g.f_rest[14])); - g_sh1Vec.emplace_back(glm::vec4(g.f_rest[18], g.f_rest[19], g.f_rest[20], g.f_rest[21])); - g_sh2Vec.emplace_back(glm::vec4(g.f_rest[22], g.f_rest[23], g.f_rest[24], g.f_rest[25])); - g_sh3Vec.emplace_back(glm::vec4(g.f_rest[26], g.f_rest[27], g.f_rest[28], g.f_rest[29])); - b_sh1Vec.emplace_back(glm::vec4(g.f_rest[33], g.f_rest[34], g.f_rest[35], g.f_rest[36])); - b_sh2Vec.emplace_back(glm::vec4(g.f_rest[37], g.f_rest[38], g.f_rest[39], g.f_rest[40])); - b_sh3Vec.emplace_back(glm::vec4(g.f_rest[41], g.f_rest[42], g.f_rest[43], g.f_rest[44])); - } - - glm::mat3 V = g.ComputeCovMat(); - cov3_col0Vec.push_back(V[0]); - cov3_col1Vec.push_back(V[1]); - cov3_col2Vec.push_back(V[2]); - } - auto positionBuffer = std::make_shared(GL_ARRAY_BUFFER, posVec); - - auto r_sh0Buffer = std::make_shared(GL_ARRAY_BUFFER, r_sh0Vec); - auto g_sh0Buffer = std::make_shared(GL_ARRAY_BUFFER, g_sh0Vec); - auto b_sh0Buffer = std::make_shared(GL_ARRAY_BUFFER, b_sh0Vec); - - auto r_sh1Buffer = std::make_shared(GL_ARRAY_BUFFER, r_sh1Vec); - auto r_sh2Buffer = std::make_shared(GL_ARRAY_BUFFER, r_sh2Vec); - auto r_sh3Buffer = std::make_shared(GL_ARRAY_BUFFER, r_sh3Vec); - auto g_sh1Buffer = std::make_shared(GL_ARRAY_BUFFER, g_sh1Vec); - auto g_sh2Buffer = std::make_shared(GL_ARRAY_BUFFER, g_sh2Vec); - auto g_sh3Buffer = std::make_shared(GL_ARRAY_BUFFER, g_sh3Vec); - auto b_sh1Buffer = std::make_shared(GL_ARRAY_BUFFER, b_sh1Vec); - auto b_sh2Buffer = std::make_shared(GL_ARRAY_BUFFER, b_sh2Vec); - auto b_sh3Buffer = std::make_shared(GL_ARRAY_BUFFER, b_sh3Vec); - - auto cov3_col0Buffer = std::make_shared(GL_ARRAY_BUFFER, cov3_col0Vec); - auto cov3_col1Buffer = std::make_shared(GL_ARRAY_BUFFER, cov3_col1Vec); - auto cov3_col2Buffer = std::make_shared(GL_ARRAY_BUFFER, cov3_col2Vec); + const size_t numGaussians = gaussianCloud->GetNumGaussians(); // build element array - indexVec.reserve(numPoints); - assert(numPoints <= std::numeric_limits::max()); - for (uint32_t i = 0; i < (uint32_t)numPoints; i++) + indexVec.reserve(numGaussians); + assert(numGaussians <= std::numeric_limits::max()); + for (uint32_t i = 0; i < (uint32_t)numGaussians; i++) { indexVec.push_back(i); } auto indexBuffer = std::make_shared(GL_ELEMENT_ARRAY_BUFFER, indexVec, GL_DYNAMIC_STORAGE_BIT); - // setup vertex array object with buffers - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("position"), positionBuffer); - - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("r_sh0"), r_sh0Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("g_sh0"), g_sh0Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("b_sh0"), b_sh0Buffer); + splatVao->Bind(); + gaussianDataBuffer->Bind(); + SetupAttrib(splatProg->GetAttribLoc("position"), gaussianCloud->GetPosWithAlphaAttrib()); + SetupAttrib(splatProg->GetAttribLoc("r_sh0"), gaussianCloud->GetR_SH0Attrib()); + SetupAttrib(splatProg->GetAttribLoc("g_sh0"), gaussianCloud->GetG_SH0Attrib()); + SetupAttrib(splatProg->GetAttribLoc("b_sh0"), gaussianCloud->GetB_SH0Attrib()); if (useFullSH) { - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("r_sh1"), r_sh1Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("r_sh2"), r_sh2Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("r_sh3"), r_sh3Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("g_sh1"), g_sh1Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("g_sh2"), g_sh2Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("g_sh3"), g_sh3Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("b_sh1"), b_sh1Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("b_sh2"), b_sh2Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("b_sh3"), b_sh3Buffer); + SetupAttrib(splatProg->GetAttribLoc("r_sh1"), gaussianCloud->GetR_SH1Attrib()); + SetupAttrib(splatProg->GetAttribLoc("r_sh2"), gaussianCloud->GetR_SH2Attrib()); + SetupAttrib(splatProg->GetAttribLoc("r_sh3"), gaussianCloud->GetR_SH3Attrib()); + SetupAttrib(splatProg->GetAttribLoc("g_sh1"), gaussianCloud->GetG_SH1Attrib()); + SetupAttrib(splatProg->GetAttribLoc("g_sh2"), gaussianCloud->GetG_SH2Attrib()); + SetupAttrib(splatProg->GetAttribLoc("g_sh3"), gaussianCloud->GetG_SH3Attrib()); + SetupAttrib(splatProg->GetAttribLoc("b_sh1"), gaussianCloud->GetB_SH1Attrib()); + SetupAttrib(splatProg->GetAttribLoc("b_sh2"), gaussianCloud->GetB_SH2Attrib()); + SetupAttrib(splatProg->GetAttribLoc("b_sh3"), gaussianCloud->GetB_SH3Attrib()); } + SetupAttrib(splatProg->GetAttribLoc("cov3_col0"), gaussianCloud->GetCov3_Col0Attrib()); + SetupAttrib(splatProg->GetAttribLoc("cov3_col1"), gaussianCloud->GetCov3_Col1Attrib()); + SetupAttrib(splatProg->GetAttribLoc("cov3_col2"), gaussianCloud->GetCov3_Col2Attrib()); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("cov3_col0"), cov3_col0Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("cov3_col1"), cov3_col1Buffer); - splatVao->SetAttribBuffer(splatProg->GetAttribLoc("cov3_col2"), cov3_col2Buffer); splatVao->SetElementBuffer(indexBuffer); + gaussianDataBuffer->Unbind(); } diff --git a/src/splatrenderer.h b/src/splatrenderer.h index 28c43ea..71cf3bc 100644 --- a/src/splatrenderer.h +++ b/src/splatrenderer.h @@ -52,6 +52,7 @@ class SplatRenderer std::vector posVec; std::vector atomicCounterVec; + std::shared_ptr gaussianDataBuffer; std::shared_ptr keyBuffer; std::shared_ptr keyBuffer2; std::shared_ptr histogramBuffer; From 6c049c2914349ed90f460aee95888f4f3d546972 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jun 2024 14:27:09 -0700 Subject: [PATCH 06/15] fix GaussianCloud::PruneSplats --- src/gaussiancloud.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index 715e14a..9f29e7a 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -453,19 +453,18 @@ void GaussianCloud::InitDebugCloud() // only keep the nearest splats void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats) { - // AJT: TODO fixme -#if 0 - if (static_cast(numSplats) >= gaussianVec.size()) + if (!data || static_cast(numSplats) >= numGaussians) { return; } + GaussianData* gd = (GaussianData*)data.get(); using IndexDistPair = std::pair; std::vector indexDistVec; - indexDistVec.reserve(gaussianVec.size()); - for (uint32_t i = 0; i < gaussianVec.size(); i++) + indexDistVec.reserve(numGaussians); + for (uint32_t i = 0; i < numGaussians; i++) { - glm::vec3 pos(gaussianVec[i].position[0], gaussianVec[i].position[1], gaussianVec[i].position[2]); + glm::vec3 pos(gd[i].posWithAlpha[0], gd[i].posWithAlpha[1], gd[i].posWithAlpha[2]); indexDistVec.push_back(IndexDistPair(i, glm::distance(origin, pos))); } @@ -474,15 +473,14 @@ void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats) return a.second < b.second; }); - std::vector newGaussianVec; - newGaussianVec.reserve(numSplats); + GaussianData* gd2 = new GaussianData[numSplats]; for (uint32_t i = 0; i < numSplats; i++) { - newGaussianVec.push_back(gaussianVec[indexDistVec[i].first]); + gd2[i] = gd[indexDistVec[i].first]; } - - gaussianVec.swap(newGaussianVec); -#endif + numGaussians = numSplats; + data.reset(gd2); + gd = nullptr; } void GaussianCloud::ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const From 6d61c2a7d96d0a088dd52b97b4283250768807a2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jun 2024 15:00:22 -0700 Subject: [PATCH 07/15] fix InitDebugCloud for PointCloud and GaussianCloud --- src/gaussiancloud.cpp | 129 +++++++++++++++++++++--------------------- src/gaussiancloud.h | 1 + src/pointcloud.cpp | 17 ++++-- src/pointcloud.h | 2 + 4 files changed, 79 insertions(+), 70 deletions(-) diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index 9f29e7a..26c18c6 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -162,26 +162,9 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) numGaussians = ply.GetVertexCount(); gaussianSize = sizeof(GaussianData); + InitAttribs(gaussianSize); gd = new GaussianData[numGaussians]; data.reset(gd); - - // GL_FLOAT = 0x1406 - posWithAlphaAttrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, posWithAlpha)}; - r_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh0)}; - r_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh1)}; - r_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh2)}; - r_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh3)}; - g_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh0)}; - g_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh1)}; - g_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh2)}; - g_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh3)}; - b_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh0)}; - b_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh1)}; - b_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh2)}; - b_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh3)}; - cov3_col0Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col0)}; - cov3_col1Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col1)}; - cov3_col2Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col2)}; } { @@ -374,18 +357,20 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename) const void GaussianCloud::InitDebugCloud() { - // AJT: TODO fix me + const int NUM_SPLATS = 5; -#if 0 - gaussianVec.clear(); + numGaussians = NUM_SPLATS * 3 + 1; + gaussianSize = sizeof(GaussianData); + InitAttribs(gaussianSize); + GaussianData* gd = new GaussianData[numGaussians]; + data.reset(gd); // // make an debug GaussianClound, that contain red, green and blue axes. // const float AXIS_LENGTH = 1.0f; - const int NUM_SPLATS = 5; const float DELTA = (AXIS_LENGTH / (float)NUM_SPLATS); - const float S = logf(0.05f); + const float COV_DIAG = 0.005f; const float SH_C0 = 0.28209479177387814f; const float SH_ONE = 1.0f / (2.0f * SH_C0); const float SH_ZERO = -1.0f / (2.0f * SH_C0); @@ -393,61 +378,56 @@ void GaussianCloud::InitDebugCloud() // x axis for (int i = 0; i < NUM_SPLATS; i++) { - GaussianCloud::Gaussian g; - memset(&g, 0, sizeof(GaussianCloud::Gaussian)); - g.position[0] = i * DELTA + DELTA; - g.position[1] = 0.0f; - g.position[2] = 0.0f; + GaussianData g; + memset(&g, 0, sizeof(GaussianData)); + g.posWithAlpha[0] = i * DELTA + DELTA; + g.posWithAlpha[1] = 0.0f; + g.posWithAlpha[2] = 0.0f; + g.posWithAlpha[3] = 1.0f; // red - g.f_dc[0] = SH_ONE; g.f_dc[1] = SH_ZERO; g.f_dc[2] = SH_ZERO; - g.opacity = 100.0f; - g.scale[0] = S; g.scale[1] = S; g.scale[2] = S; - g.rot[0] = 1.0f; g.rot[1] = 0.0f; g.rot[2] = 0.0f; g.rot[3] = 0.0f; - gaussianVec.push_back(g); + g.r_sh0[0] = SH_ONE; g.g_sh0[0] = SH_ZERO; g.b_sh0[0] = SH_ZERO; + g.cov3_col0[0] = COV_DIAG; g.cov3_col1[1] = COV_DIAG; g.cov3_col2[2] = COV_DIAG; + gd[i] = g; } // y axis for (int i = 0; i < NUM_SPLATS; i++) { - GaussianCloud::Gaussian g; - memset(&g, 0, sizeof(GaussianCloud::Gaussian)); - g.position[0] = 0.0f; - g.position[1] = i * DELTA + DELTA; - g.position[2] = 0.0f; + GaussianData g; + memset(&g, 0, sizeof(GaussianData)); + g.posWithAlpha[0] = 0.0f; + g.posWithAlpha[1] = i * DELTA + DELTA; + g.posWithAlpha[2] = 0.0f; + g.posWithAlpha[3] = 1.0f; // green - g.f_dc[0] = SH_ZERO; g.f_dc[1] = SH_ONE; g.f_dc[2] = SH_ZERO; - g.opacity = 100.0f; - g.scale[0] = S; g.scale[1] = S; g.scale[2] = S; - g.rot[0] = 1.0f; g.rot[1] = 0.0f; g.rot[2] = 0.0f; g.rot[3] = 0.0f; - gaussianVec.push_back(g); + g.r_sh0[0] = SH_ZERO; g.g_sh0[0] = SH_ONE; g.b_sh0[0] = SH_ZERO; + g.cov3_col0[0] = COV_DIAG; g.cov3_col1[1] = COV_DIAG; g.cov3_col2[2] = COV_DIAG; + gd[NUM_SPLATS + i] = g; } // z axis for (int i = 0; i < NUM_SPLATS; i++) { - GaussianCloud::Gaussian g; - memset(&g, 0, sizeof(GaussianCloud::Gaussian)); - g.position[0] = 0.0f; - g.position[1] = 0.0f; - g.position[2] = i * DELTA + DELTA + 0.0001f; // AJT: HACK prevent div by zero for debug-shaders + GaussianData g; + memset(&g, 0, sizeof(GaussianData)); + g.posWithAlpha[0] = 0.0f; + g.posWithAlpha[1] = 0.0f; + g.posWithAlpha[2] = i * DELTA + DELTA + 0.0001f; // AJT: HACK prevent div by zero for debug-shaders + g.posWithAlpha[3] = 1.0f; // blue - g.f_dc[0] = SH_ZERO; g.f_dc[1] = SH_ZERO; g.f_dc[2] = SH_ONE; - g.opacity = 100.0f; - g.scale[0] = S; g.scale[1] = S; g.scale[2] = S; - g.rot[0] = 1.0f; g.rot[1] = 0.0f; g.rot[2] = 0.0f; g.rot[3] = 0.0f; - gaussianVec.push_back(g); + g.r_sh0[0] = SH_ZERO; g.g_sh0[0] = SH_ZERO; g.b_sh0[0] = SH_ONE; + g.cov3_col0[0] = COV_DIAG; g.cov3_col1[1] = COV_DIAG; g.cov3_col2[2] = COV_DIAG; + gd[(NUM_SPLATS * 2) + i] = g; } - GaussianCloud::Gaussian g; - memset(&g, 0, sizeof(GaussianCloud::Gaussian)); - g.position[0] = 0.0f; - g.position[1] = 0.0f; - g.position[2] = 0.0f; + GaussianData g; + memset(&g, 0, sizeof(GaussianData)); + g.posWithAlpha[0] = 0.0f; + g.posWithAlpha[1] = 0.0f; + g.posWithAlpha[2] = 0.0f; + g.posWithAlpha[3] = 1.0f; // white - g.f_dc[0] = SH_ONE; g.f_dc[1] = SH_ONE; g.f_dc[2] = SH_ONE; - g.opacity = 100.0f; - g.scale[0] = S; g.scale[1] = S; g.scale[2] = S; - g.rot[0] = 1.0f; g.rot[1] = 0.0f; g.rot[2] = 0.0f; g.rot[3] = 0.0f; - gaussianVec.push_back(g); -#endif + g.r_sh0[0] = SH_ONE; g.g_sh0[0] = SH_ONE; g.b_sh0[0] = SH_ONE; + g.cov3_col0[0] = COV_DIAG; g.cov3_col1[1] = COV_DIAG; g.cov3_col2[2] = COV_DIAG; + gd[(NUM_SPLATS * 3)] = g; } // only keep the nearest splats @@ -493,3 +473,24 @@ void GaussianCloud::ForEachAttrib(const AttribData& attribData, const AttribCall bytePtr += attribData.stride; } } + +void GaussianCloud::InitAttribs(size_t size) +{ + // GL_FLOAT = 0x1406 + posWithAlphaAttrib = {4, 0x1406, (int)size, offsetof(GaussianData, posWithAlpha)}; + r_sh0Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, r_sh0)}; + r_sh1Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, r_sh1)}; + r_sh2Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, r_sh2)}; + r_sh3Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, r_sh3)}; + g_sh0Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, g_sh0)}; + g_sh1Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, g_sh1)}; + g_sh2Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, g_sh2)}; + g_sh3Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, g_sh3)}; + b_sh0Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, b_sh0)}; + b_sh1Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, b_sh1)}; + b_sh2Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, b_sh2)}; + b_sh3Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, b_sh3)}; + cov3_col0Attrib = {3, 0x1406, (int)size, offsetof(GaussianData, cov3_col0)}; + cov3_col1Attrib = {3, 0x1406, (int)size, offsetof(GaussianData, cov3_col1)}; + cov3_col2Attrib = {3, 0x1406, (int)size, offsetof(GaussianData, cov3_col2)}; +} diff --git a/src/gaussiancloud.h b/src/gaussiancloud.h index 1eb0f6f..fac253c 100644 --- a/src/gaussiancloud.h +++ b/src/gaussiancloud.h @@ -61,6 +61,7 @@ class GaussianCloud void ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const; protected: + void InitAttribs(size_t size); std::shared_ptr data; diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index e168287..f27b7fc 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -70,13 +70,10 @@ bool PointCloud::ImportPly(const std::string& plyFilename) numPoints = ply.GetVertexCount(); pointSize = sizeof(PointData); + InitAttribs(pointSize); PointData* pd = new PointData[numPoints]; data.reset(pd); - // GL_FLOAT = 0x1406 - positionAttrib = {4, 0x1406, (int)pointSize, offsetof(PointData, position)}; - colorAttrib = {4, 0x1406, (int)pointSize, offsetof(PointData, color)}; - if (useDoubles) { int i = 0; @@ -175,6 +172,7 @@ void PointCloud::InitDebugCloud() numPoints = NUM_POINTS * 3; pointSize = sizeof(PointData); + InitAttribs(pointSize); PointData* pd = new PointData[numPoints]; data.reset(pd); @@ -212,7 +210,7 @@ void PointCloud::InitDebugCloud() // z axis for (int i = 0; i < NUM_POINTS; i++) { - PointData& p = pd[i + 2 * NUM_POINTS]; + PointData& p = pd[(2 * NUM_POINTS) + i]; p.position[0] = 0.0f; p.position[1] = 0.0f; p.position[2] = i * DELTA; @@ -220,7 +218,7 @@ void PointCloud::InitDebugCloud() p.color[0] = 0.0f; p.color[1] = 0.0f; p.color[2] = 1.0f; - p.color[3] = 0.0f; + p.color[3] = 1.0f; } } @@ -234,3 +232,10 @@ void PointCloud::ForEachAttrib(const AttribData& attribData, const AttribCallbac bytePtr += attribData.stride; } } + +void PointCloud::InitAttribs(size_t size) +{ + // GL_FLOAT = 0x1406 + positionAttrib = {4, 0x1406, (int)size, offsetof(PointData, position)}; + colorAttrib = {4, 0x1406, (int)size, offsetof(PointData, color)}; +} diff --git a/src/pointcloud.h b/src/pointcloud.h index dd4bf83..b95d55e 100644 --- a/src/pointcloud.h +++ b/src/pointcloud.h @@ -42,6 +42,8 @@ class PointCloud void ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const; protected: + void InitAttribs(size_t size); + std::shared_ptr data; AttribData positionAttrib; From 60e642ee5e86340935c196ce7ee22ac76b5876ab Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 28 Jun 2024 12:24:21 -0700 Subject: [PATCH 08/15] Small refactor of Ply in prep for saving --- src/ply.cpp | 75 +++++++++++++++++++++++++++++----------------- src/ply.h | 23 +++++++++----- src/pointcloud.cpp | 28 +++++++++++++++-- 3 files changed, 88 insertions(+), 38 deletions(-) diff --git a/src/ply.cpp b/src/ply.cpp index 2ff1774..6691776 100644 --- a/src/ply.cpp +++ b/src/ply.cpp @@ -18,6 +18,18 @@ #include "core/log.h" +static size_t propertyTypeSizeArr[Ply::PropertyType::NumTypes] = { + 0, // Unknown + 1, // Char + 1, // UChar + 2, // Short + 2, // UShort + 4, // Int + 4, // UInt + 4, // Float + 8 // Double +}; + static bool CheckLine(std::ifstream& plyFile, const std::string& validLine) { std::string line; @@ -37,6 +49,11 @@ static bool GetNextPlyLine(std::ifstream& plyFile, std::string& lineOut) return false; } +Ply::Ply() : vertexCount(0), vertexSize(0) +{ + ; +} + bool Ply::ParseHeader(std::ifstream& plyFile) { ZoneScopedNC("Ply::ParseHeader", tracy::Color::Green); @@ -90,7 +107,6 @@ bool Ply::ParseHeader(std::ifstream& plyFile) // TODO: support other "element" types faces, edges etc? // at the moment I only care about ply files with vertex elements. - size_t offset = 0; while (true) { if (!GetNextPlyLine(plyFile, line)) @@ -104,8 +120,6 @@ bool Ply::ParseHeader(std::ifstream& plyFile) break; } - using PropInfoPair = std::pair; - iss.str(line); iss.clear(); iss >> token1 >> token2 >> token3; @@ -116,43 +130,35 @@ bool Ply::ParseHeader(std::ifstream& plyFile) } if (token2 == "char" || token2 == "int8") { - propertyInfoMap.emplace(PropInfoPair(token3, {offset, 1, Ply::Type::Char})); - offset += 1; + AddPropertyInfo(token3, PropertyType::Char); } else if (token2 == "uchar" || token2 == "uint8") { - propertyInfoMap.emplace(PropInfoPair(token3, {offset, 1, Ply::Type::UChar})); - offset += 1; + AddPropertyInfo(token3, PropertyType::UChar); } else if (token2 == "short" || token2 == "int16") { - propertyInfoMap.emplace(PropInfoPair(token3, {offset, 2, Ply::Type::Short})); - offset += 2; + AddPropertyInfo(token3, PropertyType::Short); } else if (token2 == "ushort" || token2 == "uint16") { - propertyInfoMap.emplace(PropInfoPair(token3, {offset, 2, Ply::Type::UShort})); - offset += 2; + AddPropertyInfo(token3, PropertyType::UShort); } else if (token2 == "int" || token2 == "int32") { - propertyInfoMap.emplace(PropInfoPair(token3, {offset, 4, Ply::Type::Int})); - offset += 4; + AddPropertyInfo(token3, PropertyType::Int); } else if (token2 == "uint" || token2 == "uint32") { - propertyInfoMap.emplace(PropInfoPair(token3, {offset, 4, Ply::Type::UInt})); - offset += 4; + AddPropertyInfo(token3, PropertyType::UInt); } else if (token2 == "float" || token2 == "float32") { - propertyInfoMap.emplace(PropInfoPair(token3, {offset, 4, Ply::Type::Float})); - offset += 4; + AddPropertyInfo(token3, PropertyType::Float); } else if (token2 == "double" || token2 == "float64") { - propertyInfoMap.emplace(PropInfoPair(token3, {offset, 8, Ply::Type::Double})); - offset += 8; + AddPropertyInfo(token3, PropertyType::Double); } else { @@ -161,8 +167,6 @@ bool Ply::ParseHeader(std::ifstream& plyFile) } } - vertexSize = offset; - return true; } @@ -173,17 +177,17 @@ bool Ply::Parse(std::ifstream& plyFile) return false; } - // read rest of file into dataVec + // read rest of file into data ptr { ZoneScopedNC("Ply::Parse() read data", tracy::Color::Yellow); - dataVec.resize(vertexSize * vertexCount); - plyFile.read((char*)dataVec.data(), vertexSize * vertexCount); + AllocData(vertexCount); + plyFile.read((char*)data.get(), vertexSize * vertexCount); } return true; } -bool Ply::GetPropertyInfo(const std::string& key, Ply::PropertyInfo& propertyInfoOut) const +bool Ply::GetPropertyInfo(const std::string& key, PropertyInfo& propertyInfoOut) const { auto iter = propertyInfoMap.find(key); if (iter != propertyInfoMap.end()) @@ -194,12 +198,27 @@ bool Ply::GetPropertyInfo(const std::string& key, Ply::PropertyInfo& propertyInf return false; } +void Ply::AddPropertyInfo(const std::string& key, PropertyType type) +{ + using PropInfoPair = std::pair; + + size_t propSize = propertyTypeSizeArr[(int)type]; + propertyInfoMap.emplace(PropInfoPair(key, {vertexSize, propSize, PropertyType::Char, (uint16_t)propertyInfoMap.size()})); + vertexSize += propSize; +} + +void Ply::AllocData(size_t numVertices) +{ + data.reset(new uint8_t[vertexSize * numVertices]); +} + void Ply::ForEachVertex(const VertexCallback& cb) const { - const uint8_t* vertexPtr = dataVec.data(); + const uint8_t* ptr = data.get(); for (size_t i = 0; i < vertexCount; i++) { - cb(vertexPtr, vertexSize); - vertexPtr += vertexSize; + cb(ptr, vertexSize); + ptr += vertexSize; } } + diff --git a/src/ply.h b/src/ply.h index dc033f2..3e648ca 100644 --- a/src/ply.h +++ b/src/ply.h @@ -5,19 +5,22 @@ #pragma once + #include +#include #include +#include #include -#include #include #include class Ply { public: + Ply(); bool Parse(std::ifstream& plyFile); - enum class Type + enum class PropertyType : uint16_t { Unknown, Char, @@ -27,22 +30,24 @@ class Ply Int, UInt, Float, - Double + Double, + NumTypes }; struct PropertyInfo { - PropertyInfo() : type(Type::Unknown) {} - PropertyInfo(size_t offsetIn, size_t sizeIn, Type typeIn) : offset(offsetIn), size(sizeIn), type(typeIn) {} + PropertyInfo() : type(PropertyType::Unknown) {} + PropertyInfo(size_t offsetIn, size_t sizeIn, PropertyType typeIn, uint16_t indexIn) : offset(offsetIn), size(sizeIn), type(typeIn), index(indexIn) {} size_t offset; size_t size; - Type type; + PropertyType type; + uint16_t index; template const T Get(const uint8_t* data) const { - if (type == Type::Unknown) + if (type == PropertyType::Unknown) { return 0; } @@ -55,6 +60,8 @@ class Ply }; bool GetPropertyInfo(const std::string& key, PropertyInfo& propertyInfoOut) const; + void AddPropertyInfo(const std::string& key, PropertyType type); + void AllocData(size_t numVertices); using VertexCallback = std::function; void ForEachVertex(const VertexCallback& cb) const; @@ -65,7 +72,7 @@ class Ply bool ParseHeader(std::ifstream& plyFile); std::unordered_map propertyInfoMap; - std::vector dataVec; + std::unique_ptr data; size_t vertexCount; size_t vertexSize; }; diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index f27b7fc..7bda019 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -59,7 +59,9 @@ bool PointCloud::ImportPly(const std::string& plyFilename) Log::E("Error parsing ply file \"%s\", missing position property\n", plyFilename.c_str()); } - bool useDoubles = (props.x.type == Ply::Type::Double && props.y.type == Ply::Type::Double && props.z.type == Ply::Type::Double); + bool useDoubles = (props.x.type == Ply::PropertyType::Double && + props.y.type == Ply::PropertyType::Double && + props.z.type == Ply::PropertyType::Double); if (!ply.GetPropertyInfo("red", props.red) || !ply.GetPropertyInfo("green", props.green) || @@ -130,7 +132,7 @@ bool PointCloud::ImportPly(const std::string& plyFilename) bool PointCloud::ExportPly(const std::string& plyFilename) const { - // AJT: TODO FIXME BROKEN + // AJT: TODO return false; std::ofstream plyFile(plyFilename, std::ios::binary); @@ -140,6 +142,27 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const return false; } + Ply ply; + ply.AddPropertyInfo("x", Ply::PropertyType::Float); + ply.AddPropertyInfo("y", Ply::PropertyType::Float); + ply.AddPropertyInfo("z", Ply::PropertyType::Float); + ply.AddPropertyInfo("nx", Ply::PropertyType::Float); + ply.AddPropertyInfo("ny", Ply::PropertyType::Float); + ply.AddPropertyInfo("nz", Ply::PropertyType::Float); + ply.AddPropertyInfo("red", Ply::PropertyType::UChar); + ply.AddPropertyInfo("green", Ply::PropertyType::UChar); + ply.AddPropertyInfo("blue", Ply::PropertyType::UChar); + + ply.AllocData(numPoints); + + /* + ply.ForEachVertex([this](uint8_t* data, size_t size)) + { + positionAttrib.Set + } + */ + + /* // ply files have unix line endings. plyFile << "ply\n"; plyFile << "format binary_little_endian 1.0\n"; @@ -154,6 +177,7 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const plyFile << "property uchar green\n"; plyFile << "property uchar blue\n"; plyFile << "end_header\n"; + */ /* const size_t POINT_SIZE = 27; From e558090d6d90389a4aa3f317f59356bd5e08feea Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 29 Jun 2024 11:25:07 -0700 Subject: [PATCH 09/15] replace Ply::PropertyInfo and Cloud::AttribData with BinaryAttribute --- src/core/binaryattribute.h | 110 ++++++++++++++++++++ src/gaussiancloud.cpp | 199 ++++++++++++++++++------------------- src/gaussiancloud.h | 85 ++++++++-------- src/ply.cpp | 34 +++---- src/ply.h | 47 +-------- src/pointcloud.cpp | 97 +++++++++--------- src/pointcloud.h | 31 +++--- src/pointrenderer.cpp | 14 +-- src/splatrenderer.cpp | 43 ++++---- 9 files changed, 353 insertions(+), 307 deletions(-) create mode 100644 src/core/binaryattribute.h diff --git a/src/core/binaryattribute.h b/src/core/binaryattribute.h new file mode 100644 index 0000000..d448cb4 --- /dev/null +++ b/src/core/binaryattribute.h @@ -0,0 +1,110 @@ +/* + Copyright (c) 2024 Anthony J. Thibault + This software is licensed under the MIT License. See LICENSE for more details. +*/ + +#pragma once + +#include +#include + +class BinaryAttribute +{ +public: + enum class Type + { + Unknown, + Char, + UChar, + Short, + UShort, + Int, + UInt, + Float, + Double, + NumTypes + }; + + BinaryAttribute() : type(Type::Unknown), size(0), offset(0) {} + BinaryAttribute(Type typeIn, size_t sizeIn, size_t offsetIn) : type(typeIn), size(sizeIn), offset(offsetIn) {} + + template + const T* Get(const void* data) const + { + if (type == Type::Unknown) + { + return nullptr; + } + else + { + assert(size == sizeof(T)); + return reinterpret_cast(static_cast(data) + offset); + } + } + + template + T* Get(const void* data) + { + if (type == Type::Unknown) + { + return nullptr; + } + else + { + assert(size == sizeof(T)); + return reinterpret_cast(static_cast(data) + offset); + } + } + + template + const T Read(const void* data) const + { + const T* ptr = Get(data); + return ptr ? *ptr : 0; + } + + template + bool Write(void* data, const T& val) + { + T* ptr = Get(data); + if (ptr) + { + *ptr = val; + return true; + } + else + { + return false; + } + } + + template + void ForEach(void* data, size_t stride, size_t count, const std::function& cb) + { + assert(type != Type::Unknown); + assert(data); + uint8_t* ptr = (uint8_t*)data; + for (size_t i = 0; i < count; i++) + { + cb((T*)(ptr + offset)); + ptr += stride; + } + } + + template + void ForEachConst(const void* data, size_t stride, size_t count, const std::function& cb) const + { + assert(type != Type::Unknown); + assert(data); + const uint8_t* ptr = (const uint8_t*)data; + for (size_t i = 0; i < count; i++) + { + cb((const T*)(ptr + offset)); + ptr += stride; + } + } + + Type type; + size_t size; + size_t offset; +}; diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index 26c18c6..eb7c323 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -95,27 +95,27 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) struct { - Ply::PropertyInfo x, y, z; - Ply::PropertyInfo f_dc[3]; - Ply::PropertyInfo f_rest[45]; - Ply::PropertyInfo opacity; - Ply::PropertyInfo scale[3]; - Ply::PropertyInfo rot[4]; + BinaryAttribute x, y, z; + BinaryAttribute f_dc[3]; + BinaryAttribute f_rest[45]; + BinaryAttribute opacity; + BinaryAttribute scale[3]; + BinaryAttribute rot[4]; } props; { ZoneScopedNC("ply.GetProps", tracy::Color::Green); - if (!ply.GetPropertyInfo("x", props.x) || - !ply.GetPropertyInfo("y", props.y) || - !ply.GetPropertyInfo("z", props.z)) + if (!ply.GetProperty("x", props.x) || + !ply.GetProperty("y", props.y) || + !ply.GetProperty("z", props.z)) { Log::E("Error parsing ply file \"%s\", missing position property\n", plyFilename.c_str()); } for (int i = 0; i < 3; i++) { - if (!ply.GetPropertyInfo("f_dc_" + std::to_string(i), props.f_dc[i])) + if (!ply.GetProperty("f_dc_" + std::to_string(i), props.f_dc[i])) { Log::E("Error parsing ply file \"%s\", missing f_dc property\n", plyFilename.c_str()); } @@ -123,7 +123,7 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) for (int i = 0; i < 45; i++) { - if (!ply.GetPropertyInfo("f_rest_" + std::to_string(i), props.f_rest[i])) + if (!ply.GetProperty("f_rest_" + std::to_string(i), props.f_rest[i])) { // f_rest properties are optional Log::W("PLY file \"%s\", missing f_rest property\n", plyFilename.c_str()); @@ -131,14 +131,14 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) } } - if (!ply.GetPropertyInfo("opacity", props.opacity)) + if (!ply.GetProperty("opacity", props.opacity)) { Log::E("Error parsing ply file \"%s\", missing opacity property\n", plyFilename.c_str()); } for (int i = 0; i < 3; i++) { - if (!ply.GetPropertyInfo("scale_" + std::to_string(i), props.scale[i])) + if (!ply.GetProperty("scale_" + std::to_string(i), props.scale[i])) { Log::E("Error parsing ply file \"%s\", missing scale property\n", plyFilename.c_str()); } @@ -146,7 +146,7 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) for (int i = 0; i < 4; i++) { - if (!ply.GetPropertyInfo("rot_" + std::to_string(i), props.rot[i])) + if (!ply.GetProperty("rot_" + std::to_string(i), props.rot[i])) { Log::E("Error parsing ply file \"%s\", missing rot property\n", plyFilename.c_str()); } @@ -162,7 +162,7 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) numGaussians = ply.GetVertexCount(); gaussianSize = sizeof(GaussianData); - InitAttribs(gaussianSize); + InitAttribs(); gd = new GaussianData[numGaussians]; data.reset(gd); } @@ -172,76 +172,76 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) int i = 0; ply.ForEachVertex([this, gd, &i, &props](const uint8_t* data, size_t size) { - gd[i].posWithAlpha[0] = props.x.Get(data); - gd[i].posWithAlpha[1] = props.y.Get(data); - gd[i].posWithAlpha[2] = props.z.Get(data); - gd[i].posWithAlpha[3] = ComputeAlpha(props.opacity.Get(data)); + gd[i].posWithAlpha[0] = props.x.Read(data); + gd[i].posWithAlpha[1] = props.y.Read(data); + gd[i].posWithAlpha[2] = props.z.Read(data); + gd[i].posWithAlpha[3] = ComputeAlpha(props.opacity.Read(data)); // AJT: TODO check for useLinearColors - gd[i].r_sh0[0] = props.f_dc[0].Get(data); - gd[i].r_sh0[1] = props.f_rest[0].Get(data); - gd[i].r_sh0[2] = props.f_rest[1].Get(data); - gd[i].r_sh0[3] = props.f_rest[2].Get(data); - gd[i].r_sh1[0] = props.f_rest[3].Get(data); - gd[i].r_sh1[1] = props.f_rest[4].Get(data); - gd[i].r_sh1[2] = props.f_rest[5].Get(data); - gd[i].r_sh1[3] = props.f_rest[6].Get(data); - gd[i].r_sh2[0] = props.f_rest[7].Get(data); - gd[i].r_sh2[1] = props.f_rest[8].Get(data); - gd[i].r_sh2[2] = props.f_rest[9].Get(data); - gd[i].r_sh2[3] = props.f_rest[10].Get(data); - gd[i].r_sh3[0] = props.f_rest[11].Get(data); - gd[i].r_sh3[1] = props.f_rest[12].Get(data); - gd[i].r_sh3[2] = props.f_rest[13].Get(data); - gd[i].r_sh3[3] = props.f_rest[14].Get(data); - - gd[i].g_sh0[0] = props.f_dc[1].Get(data); - gd[i].g_sh0[1] = props.f_rest[15].Get(data); - gd[i].g_sh0[2] = props.f_rest[16].Get(data); - gd[i].g_sh0[3] = props.f_rest[17].Get(data); - gd[i].g_sh1[0] = props.f_rest[18].Get(data); - gd[i].g_sh1[1] = props.f_rest[19].Get(data); - gd[i].g_sh1[2] = props.f_rest[20].Get(data); - gd[i].g_sh1[3] = props.f_rest[21].Get(data); - gd[i].g_sh2[0] = props.f_rest[22].Get(data); - gd[i].g_sh2[1] = props.f_rest[23].Get(data); - gd[i].g_sh2[2] = props.f_rest[24].Get(data); - gd[i].g_sh2[3] = props.f_rest[25].Get(data); - gd[i].g_sh3[0] = props.f_rest[26].Get(data); - gd[i].g_sh3[1] = props.f_rest[27].Get(data); - gd[i].g_sh3[2] = props.f_rest[28].Get(data); - gd[i].g_sh3[3] = props.f_rest[29].Get(data); - - gd[i].b_sh0[0] = props.f_dc[2].Get(data); - gd[i].b_sh0[1] = props.f_rest[30].Get(data); - gd[i].b_sh0[2] = props.f_rest[31].Get(data); - gd[i].b_sh0[3] = props.f_rest[32].Get(data); - gd[i].b_sh1[0] = props.f_rest[33].Get(data); - gd[i].b_sh1[1] = props.f_rest[34].Get(data); - gd[i].b_sh1[2] = props.f_rest[35].Get(data); - gd[i].b_sh1[3] = props.f_rest[36].Get(data); - gd[i].b_sh2[0] = props.f_rest[37].Get(data); - gd[i].b_sh2[1] = props.f_rest[38].Get(data); - gd[i].b_sh2[2] = props.f_rest[39].Get(data); - gd[i].b_sh2[3] = props.f_rest[40].Get(data); - gd[i].b_sh3[0] = props.f_rest[41].Get(data); - gd[i].b_sh3[1] = props.f_rest[42].Get(data); - gd[i].b_sh3[2] = props.f_rest[43].Get(data); - gd[i].b_sh3[3] = props.f_rest[44].Get(data); + gd[i].r_sh0[0] = props.f_dc[0].Read(data); + gd[i].r_sh0[1] = props.f_rest[0].Read(data); + gd[i].r_sh0[2] = props.f_rest[1].Read(data); + gd[i].r_sh0[3] = props.f_rest[2].Read(data); + gd[i].r_sh1[0] = props.f_rest[3].Read(data); + gd[i].r_sh1[1] = props.f_rest[4].Read(data); + gd[i].r_sh1[2] = props.f_rest[5].Read(data); + gd[i].r_sh1[3] = props.f_rest[6].Read(data); + gd[i].r_sh2[0] = props.f_rest[7].Read(data); + gd[i].r_sh2[1] = props.f_rest[8].Read(data); + gd[i].r_sh2[2] = props.f_rest[9].Read(data); + gd[i].r_sh2[3] = props.f_rest[10].Read(data); + gd[i].r_sh3[0] = props.f_rest[11].Read(data); + gd[i].r_sh3[1] = props.f_rest[12].Read(data); + gd[i].r_sh3[2] = props.f_rest[13].Read(data); + gd[i].r_sh3[3] = props.f_rest[14].Read(data); + + gd[i].g_sh0[0] = props.f_dc[1].Read(data); + gd[i].g_sh0[1] = props.f_rest[15].Read(data); + gd[i].g_sh0[2] = props.f_rest[16].Read(data); + gd[i].g_sh0[3] = props.f_rest[17].Read(data); + gd[i].g_sh1[0] = props.f_rest[18].Read(data); + gd[i].g_sh1[1] = props.f_rest[19].Read(data); + gd[i].g_sh1[2] = props.f_rest[20].Read(data); + gd[i].g_sh1[3] = props.f_rest[21].Read(data); + gd[i].g_sh2[0] = props.f_rest[22].Read(data); + gd[i].g_sh2[1] = props.f_rest[23].Read(data); + gd[i].g_sh2[2] = props.f_rest[24].Read(data); + gd[i].g_sh2[3] = props.f_rest[25].Read(data); + gd[i].g_sh3[0] = props.f_rest[26].Read(data); + gd[i].g_sh3[1] = props.f_rest[27].Read(data); + gd[i].g_sh3[2] = props.f_rest[28].Read(data); + gd[i].g_sh3[3] = props.f_rest[29].Read(data); + + gd[i].b_sh0[0] = props.f_dc[2].Read(data); + gd[i].b_sh0[1] = props.f_rest[30].Read(data); + gd[i].b_sh0[2] = props.f_rest[31].Read(data); + gd[i].b_sh0[3] = props.f_rest[32].Read(data); + gd[i].b_sh1[0] = props.f_rest[33].Read(data); + gd[i].b_sh1[1] = props.f_rest[34].Read(data); + gd[i].b_sh1[2] = props.f_rest[35].Read(data); + gd[i].b_sh1[3] = props.f_rest[36].Read(data); + gd[i].b_sh2[0] = props.f_rest[37].Read(data); + gd[i].b_sh2[1] = props.f_rest[38].Read(data); + gd[i].b_sh2[2] = props.f_rest[39].Read(data); + gd[i].b_sh2[3] = props.f_rest[40].Read(data); + gd[i].b_sh3[0] = props.f_rest[41].Read(data); + gd[i].b_sh3[1] = props.f_rest[42].Read(data); + gd[i].b_sh3[2] = props.f_rest[43].Read(data); + gd[i].b_sh3[3] = props.f_rest[44].Read(data); float rot[4] = { - props.rot[0].Get(data), - props.rot[1].Get(data), - props.rot[2].Get(data), - props.rot[3].Get(data) + props.rot[0].Read(data), + props.rot[1].Read(data), + props.rot[2].Read(data), + props.rot[3].Read(data) }; float scale[4] = { - props.scale[0].Get(data), - props.scale[1].Get(data), - props.scale[2].Get(data) + props.scale[0].Read(data), + props.scale[1].Read(data), + props.scale[2].Read(data) }; glm::mat3 V = ComputeCovMat(rot, scale); @@ -361,7 +361,7 @@ void GaussianCloud::InitDebugCloud() numGaussians = NUM_SPLATS * 3 + 1; gaussianSize = sizeof(GaussianData); - InitAttribs(gaussianSize); + InitAttribs(); GaussianData* gd = new GaussianData[numGaussians]; data.reset(gd); @@ -463,34 +463,27 @@ void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats) gd = nullptr; } -void GaussianCloud::ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const +void GaussianCloud::ForEachPosWithAlpha(const ForEachPosWithAlphaCallback& cb) const { - const uint8_t* bytePtr = (uint8_t*)data.get(); - bytePtr += attribData.offset; - for (size_t i = 0; i < GetNumGaussians(); i++) - { - cb((const void*)bytePtr); - bytePtr += attribData.stride; - } + posWithAlphaAttrib.ForEachConst(GetRawDataPtr(), GetStride(), GetNumGaussians(), cb); } -void GaussianCloud::InitAttribs(size_t size) +void GaussianCloud::InitAttribs() { - // GL_FLOAT = 0x1406 - posWithAlphaAttrib = {4, 0x1406, (int)size, offsetof(GaussianData, posWithAlpha)}; - r_sh0Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, r_sh0)}; - r_sh1Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, r_sh1)}; - r_sh2Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, r_sh2)}; - r_sh3Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, r_sh3)}; - g_sh0Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, g_sh0)}; - g_sh1Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, g_sh1)}; - g_sh2Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, g_sh2)}; - g_sh3Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, g_sh3)}; - b_sh0Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, b_sh0)}; - b_sh1Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, b_sh1)}; - b_sh2Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, b_sh2)}; - b_sh3Attrib = {4, 0x1406, (int)size, offsetof(GaussianData, b_sh3)}; - cov3_col0Attrib = {3, 0x1406, (int)size, offsetof(GaussianData, cov3_col0)}; - cov3_col1Attrib = {3, 0x1406, (int)size, offsetof(GaussianData, cov3_col1)}; - cov3_col2Attrib = {3, 0x1406, (int)size, offsetof(GaussianData, cov3_col2)}; + posWithAlphaAttrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, posWithAlpha)}; + r_sh0Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, r_sh0)}; + r_sh1Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, r_sh1)}; + r_sh2Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, r_sh2)}; + r_sh3Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, r_sh3)}; + g_sh0Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, g_sh0)}; + g_sh1Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, g_sh1)}; + g_sh2Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, g_sh2)}; + g_sh3Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, g_sh3)}; + b_sh0Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, b_sh0)}; + b_sh1Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, b_sh1)}; + b_sh2Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, b_sh2)}; + b_sh3Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, b_sh3)}; + cov3_col0Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, cov3_col0)}; + cov3_col1Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, cov3_col1)}; + cov3_col2Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, cov3_col2)}; } diff --git a/src/gaussiancloud.h b/src/gaussiancloud.h index fac253c..a295a24 100644 --- a/src/gaussiancloud.h +++ b/src/gaussiancloud.h @@ -12,6 +12,8 @@ #include +#include "core/binaryattribute.h" + class GaussianCloud { public: @@ -26,61 +28,52 @@ class GaussianCloud void PruneSplats(const glm::vec3& origin, uint32_t numGaussians); size_t GetNumGaussians() const { return numGaussians; } + size_t GetStride() const { return gaussianSize; } size_t GetTotalSize() const { return GetNumGaussians() * gaussianSize; } void* GetRawDataPtr() { return data.get(); } + const void* GetRawDataPtr() const { return data.get(); } - struct AttribData - { - AttribData() : size(0), type(0), stride(0), offset(0) {} - AttribData(int32_t sizeIn, int32_t typeIn, int32_t strideIn, size_t offsetIn) : size(sizeIn), type(typeIn), stride(strideIn), offset(offsetIn) {} - bool IsValid() const { return size != 0; } - int32_t size; - int32_t type; - int32_t stride; - size_t offset; - }; - - const AttribData& GetPosWithAlphaAttrib() const { return posWithAlphaAttrib; } - const AttribData& GetR_SH0Attrib() const { return r_sh0Attrib; } - const AttribData& GetR_SH1Attrib() const { return r_sh1Attrib; } - const AttribData& GetR_SH2Attrib() const { return r_sh2Attrib; } - const AttribData& GetR_SH3Attrib() const { return r_sh3Attrib; } - const AttribData& GetG_SH0Attrib() const { return g_sh0Attrib; } - const AttribData& GetG_SH1Attrib() const { return g_sh1Attrib; } - const AttribData& GetG_SH2Attrib() const { return g_sh2Attrib; } - const AttribData& GetG_SH3Attrib() const { return g_sh3Attrib; } - const AttribData& GetB_SH0Attrib() const { return b_sh0Attrib; } - const AttribData& GetB_SH1Attrib() const { return b_sh1Attrib; } - const AttribData& GetB_SH2Attrib() const { return b_sh2Attrib; } - const AttribData& GetB_SH3Attrib() const { return b_sh3Attrib; } - const AttribData& GetCov3_Col0Attrib() const { return cov3_col0Attrib; } - const AttribData& GetCov3_Col1Attrib() const { return cov3_col1Attrib; } - const AttribData& GetCov3_Col2Attrib() const { return cov3_col2Attrib; } + const BinaryAttribute& GetPosWithAlphaAttrib() const { return posWithAlphaAttrib; } + const BinaryAttribute& GetR_SH0Attrib() const { return r_sh0Attrib; } + const BinaryAttribute& GetR_SH1Attrib() const { return r_sh1Attrib; } + const BinaryAttribute& GetR_SH2Attrib() const { return r_sh2Attrib; } + const BinaryAttribute& GetR_SH3Attrib() const { return r_sh3Attrib; } + const BinaryAttribute& GetG_SH0Attrib() const { return g_sh0Attrib; } + const BinaryAttribute& GetG_SH1Attrib() const { return g_sh1Attrib; } + const BinaryAttribute& GetG_SH2Attrib() const { return g_sh2Attrib; } + const BinaryAttribute& GetG_SH3Attrib() const { return g_sh3Attrib; } + const BinaryAttribute& GetB_SH0Attrib() const { return b_sh0Attrib; } + const BinaryAttribute& GetB_SH1Attrib() const { return b_sh1Attrib; } + const BinaryAttribute& GetB_SH2Attrib() const { return b_sh2Attrib; } + const BinaryAttribute& GetB_SH3Attrib() const { return b_sh3Attrib; } + const BinaryAttribute& GetCov3_Col0Attrib() const { return cov3_col0Attrib; } + const BinaryAttribute& GetCov3_Col1Attrib() const { return cov3_col1Attrib; } + const BinaryAttribute& GetCov3_Col2Attrib() const { return cov3_col2Attrib; } - using AttribCallback = std::function; - void ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const; + using ForEachPosWithAlphaCallback = std::function; + void ForEachPosWithAlpha(const ForEachPosWithAlphaCallback& cb) const; protected: - void InitAttribs(size_t size); + void InitAttribs(); std::shared_ptr data; - AttribData posWithAlphaAttrib; - AttribData r_sh0Attrib; - AttribData r_sh1Attrib; - AttribData r_sh2Attrib; - AttribData r_sh3Attrib; - AttribData g_sh0Attrib; - AttribData g_sh1Attrib; - AttribData g_sh2Attrib; - AttribData g_sh3Attrib; - AttribData b_sh0Attrib; - AttribData b_sh1Attrib; - AttribData b_sh2Attrib; - AttribData b_sh3Attrib; - AttribData cov3_col0Attrib; - AttribData cov3_col1Attrib; - AttribData cov3_col2Attrib; + BinaryAttribute posWithAlphaAttrib; + BinaryAttribute r_sh0Attrib; + BinaryAttribute r_sh1Attrib; + BinaryAttribute r_sh2Attrib; + BinaryAttribute r_sh3Attrib; + BinaryAttribute g_sh0Attrib; + BinaryAttribute g_sh1Attrib; + BinaryAttribute g_sh2Attrib; + BinaryAttribute g_sh3Attrib; + BinaryAttribute b_sh0Attrib; + BinaryAttribute b_sh1Attrib; + BinaryAttribute b_sh2Attrib; + BinaryAttribute b_sh3Attrib; + BinaryAttribute cov3_col0Attrib; + BinaryAttribute cov3_col1Attrib; + BinaryAttribute cov3_col2Attrib; size_t numGaussians; size_t gaussianSize; diff --git a/src/ply.cpp b/src/ply.cpp index 6691776..d908e66 100644 --- a/src/ply.cpp +++ b/src/ply.cpp @@ -18,7 +18,7 @@ #include "core/log.h" -static size_t propertyTypeSizeArr[Ply::PropertyType::NumTypes] = { +static uint32_t propertyTypeSizeArr[(size_t)BinaryAttribute::Type::NumTypes] = { 0, // Unknown 1, // Char 1, // UChar @@ -130,35 +130,35 @@ bool Ply::ParseHeader(std::ifstream& plyFile) } if (token2 == "char" || token2 == "int8") { - AddPropertyInfo(token3, PropertyType::Char); + AddProperty(token3, BinaryAttribute::Type::Char); } else if (token2 == "uchar" || token2 == "uint8") { - AddPropertyInfo(token3, PropertyType::UChar); + AddProperty(token3, BinaryAttribute::Type::UChar); } else if (token2 == "short" || token2 == "int16") { - AddPropertyInfo(token3, PropertyType::Short); + AddProperty(token3, BinaryAttribute::Type::Short); } else if (token2 == "ushort" || token2 == "uint16") { - AddPropertyInfo(token3, PropertyType::UShort); + AddProperty(token3, BinaryAttribute::Type::UShort); } else if (token2 == "int" || token2 == "int32") { - AddPropertyInfo(token3, PropertyType::Int); + AddProperty(token3, BinaryAttribute::Type::Int); } else if (token2 == "uint" || token2 == "uint32") { - AddPropertyInfo(token3, PropertyType::UInt); + AddProperty(token3, BinaryAttribute::Type::UInt); } else if (token2 == "float" || token2 == "float32") { - AddPropertyInfo(token3, PropertyType::Float); + AddProperty(token3, BinaryAttribute::Type::Float); } else if (token2 == "double" || token2 == "float64") { - AddPropertyInfo(token3, PropertyType::Double); + AddProperty(token3, BinaryAttribute::Type::Double); } else { @@ -187,23 +187,23 @@ bool Ply::Parse(std::ifstream& plyFile) return true; } -bool Ply::GetPropertyInfo(const std::string& key, PropertyInfo& propertyInfoOut) const +bool Ply::GetProperty(const std::string& key, BinaryAttribute& binaryAttributeOut) const { - auto iter = propertyInfoMap.find(key); - if (iter != propertyInfoMap.end()) + auto iter = propertyMap.find(key); + if (iter != propertyMap.end()) { - propertyInfoOut = iter->second; + binaryAttributeOut = iter->second; return true; } return false; } -void Ply::AddPropertyInfo(const std::string& key, PropertyType type) +void Ply::AddProperty(const std::string& key, BinaryAttribute::Type type) { - using PropInfoPair = std::pair; + using PropInfoPair = std::pair; - size_t propSize = propertyTypeSizeArr[(int)type]; - propertyInfoMap.emplace(PropInfoPair(key, {vertexSize, propSize, PropertyType::Char, (uint16_t)propertyInfoMap.size()})); + uint32_t propSize = propertyTypeSizeArr[(int)type]; + propertyMap.emplace(PropInfoPair(key, BinaryAttribute{type, propSize, (uint32_t)vertexSize})); vertexSize += propSize; } diff --git a/src/ply.h b/src/ply.h index 3e648ca..735296f 100644 --- a/src/ply.h +++ b/src/ply.h @@ -14,53 +14,16 @@ #include #include +#include "core/binaryattribute.h" + class Ply { public: Ply(); bool Parse(std::ifstream& plyFile); - enum class PropertyType : uint16_t - { - Unknown, - Char, - UChar, - Short, - UShort, - Int, - UInt, - Float, - Double, - NumTypes - }; - - struct PropertyInfo - { - PropertyInfo() : type(PropertyType::Unknown) {} - PropertyInfo(size_t offsetIn, size_t sizeIn, PropertyType typeIn, uint16_t indexIn) : offset(offsetIn), size(sizeIn), type(typeIn), index(indexIn) {} - - size_t offset; - size_t size; - PropertyType type; - uint16_t index; - - template - const T Get(const uint8_t* data) const - { - if (type == PropertyType::Unknown) - { - return 0; - } - else - { - assert(size == sizeof(T)); - return *reinterpret_cast(data + offset); - } - } - }; - - bool GetPropertyInfo(const std::string& key, PropertyInfo& propertyInfoOut) const; - void AddPropertyInfo(const std::string& key, PropertyType type); + bool GetProperty(const std::string& key, BinaryAttribute& attributeOut) const; + void AddProperty(const std::string& key, BinaryAttribute::Type type); void AllocData(size_t numVertices); using VertexCallback = std::function; @@ -71,7 +34,7 @@ class Ply protected: bool ParseHeader(std::ifstream& plyFile); - std::unordered_map propertyInfoMap; + std::unordered_map propertyMap; std::unique_ptr data; size_t vertexCount; size_t vertexSize; diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index 7bda019..106f692 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -48,31 +48,31 @@ bool PointCloud::ImportPly(const std::string& plyFilename) struct { - Ply::PropertyInfo x, y, z; - Ply::PropertyInfo red, green, blue; + BinaryAttribute x, y, z; + BinaryAttribute red, green, blue; } props; - if (!ply.GetPropertyInfo("x", props.x) || - !ply.GetPropertyInfo("y", props.y) || - !ply.GetPropertyInfo("z", props.z)) + if (!ply.GetProperty("x", props.x) || + !ply.GetProperty("y", props.y) || + !ply.GetProperty("z", props.z)) { Log::E("Error parsing ply file \"%s\", missing position property\n", plyFilename.c_str()); } - bool useDoubles = (props.x.type == Ply::PropertyType::Double && - props.y.type == Ply::PropertyType::Double && - props.z.type == Ply::PropertyType::Double); + bool useDoubles = (props.x.type == BinaryAttribute::Type::Double && + props.y.type == BinaryAttribute::Type::Double && + props.z.type == BinaryAttribute::Type::Double); - if (!ply.GetPropertyInfo("red", props.red) || - !ply.GetPropertyInfo("green", props.green) || - !ply.GetPropertyInfo("blue", props.blue)) + if (!ply.GetProperty("red", props.red) || + !ply.GetProperty("green", props.green) || + !ply.GetProperty("blue", props.blue)) { Log::E("Error parsing ply file \"%s\", missing color property\n", plyFilename.c_str()); } numPoints = ply.GetVertexCount(); pointSize = sizeof(PointData); - InitAttribs(pointSize); + InitAttribs(); PointData* pd = new PointData[numPoints]; data.reset(pd); @@ -83,20 +83,20 @@ bool PointCloud::ImportPly(const std::string& plyFilename) { if (useLinearColors) { - pd[i].position[0] = SRGBToLinear((float)props.x.Get(data)); - pd[i].position[1] = SRGBToLinear((float)props.y.Get(data)); - pd[i].position[2] = SRGBToLinear((float)props.z.Get(data)); + pd[i].position[0] = SRGBToLinear((float)props.x.Read(data)); + pd[i].position[1] = SRGBToLinear((float)props.y.Read(data)); + pd[i].position[2] = SRGBToLinear((float)props.z.Read(data)); } else { - pd[i].position[0] = (float)props.x.Get(data); - pd[i].position[1] = (float)props.y.Get(data); - pd[i].position[2] = (float)props.z.Get(data); + pd[i].position[0] = (float)props.x.Read(data); + pd[i].position[1] = (float)props.y.Read(data); + pd[i].position[2] = (float)props.z.Read(data); } pd[i].position[3] = 1.0f; - pd[i].color[0] = (float)props.red.Get(data) / 255.0f; - pd[i].color[1] = (float)props.green.Get(data) / 255.0f; - pd[i].color[2] = (float)props.blue.Get(data) / 255.0f; + pd[i].color[0] = (float)props.red.Read(data) / 255.0f; + pd[i].color[1] = (float)props.green.Read(data) / 255.0f; + pd[i].color[2] = (float)props.blue.Read(data) / 255.0f; pd[i].color[3] = 1.0f; i++; }); @@ -108,20 +108,20 @@ bool PointCloud::ImportPly(const std::string& plyFilename) { if (useLinearColors) { - pd[i].position[0] = SRGBToLinear(props.x.Get(data)); - pd[i].position[1] = SRGBToLinear(props.y.Get(data)); - pd[i].position[2] = SRGBToLinear(props.z.Get(data)); + pd[i].position[0] = SRGBToLinear(props.x.Read(data)); + pd[i].position[1] = SRGBToLinear(props.y.Read(data)); + pd[i].position[2] = SRGBToLinear(props.z.Read(data)); } else { - pd[i].position[0] = props.x.Get(data); - pd[i].position[1] = props.y.Get(data); - pd[i].position[2] = props.z.Get(data); + pd[i].position[0] = props.x.Read(data); + pd[i].position[1] = props.y.Read(data); + pd[i].position[2] = props.z.Read(data); } pd[i].position[3] = 1.0f; - pd[i].color[0] = (float)props.red.Get(data) / 255.0f; - pd[i].color[1] = (float)props.green.Get(data) / 255.0f; - pd[i].color[2] = (float)props.blue.Get(data) / 255.0f; + pd[i].color[0] = (float)props.red.Read(data) / 255.0f; + pd[i].color[1] = (float)props.green.Read(data) / 255.0f; + pd[i].color[2] = (float)props.blue.Read(data) / 255.0f; pd[i].color[3] = 1.0f; i++; }); @@ -143,15 +143,15 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const } Ply ply; - ply.AddPropertyInfo("x", Ply::PropertyType::Float); - ply.AddPropertyInfo("y", Ply::PropertyType::Float); - ply.AddPropertyInfo("z", Ply::PropertyType::Float); - ply.AddPropertyInfo("nx", Ply::PropertyType::Float); - ply.AddPropertyInfo("ny", Ply::PropertyType::Float); - ply.AddPropertyInfo("nz", Ply::PropertyType::Float); - ply.AddPropertyInfo("red", Ply::PropertyType::UChar); - ply.AddPropertyInfo("green", Ply::PropertyType::UChar); - ply.AddPropertyInfo("blue", Ply::PropertyType::UChar); + ply.AddProperty("x", BinaryAttribute::Type::Float); + ply.AddProperty("y", BinaryAttribute::Type::Float); + ply.AddProperty("z", BinaryAttribute::Type::Float); + ply.AddProperty("nx", BinaryAttribute::Type::Float); + ply.AddProperty("ny", BinaryAttribute::Type::Float); + ply.AddProperty("nz", BinaryAttribute::Type::Float); + ply.AddProperty("red", BinaryAttribute::Type::UChar); + ply.AddProperty("green", BinaryAttribute::Type::UChar); + ply.AddProperty("blue", BinaryAttribute::Type::UChar); ply.AllocData(numPoints); @@ -196,7 +196,7 @@ void PointCloud::InitDebugCloud() numPoints = NUM_POINTS * 3; pointSize = sizeof(PointData); - InitAttribs(pointSize); + InitAttribs(); PointData* pd = new PointData[numPoints]; data.reset(pd); @@ -246,20 +246,13 @@ void PointCloud::InitDebugCloud() } } -void PointCloud::ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const +void PointCloud::ForEachPosition(const ForEachPositionCallback& cb) const { - const uint8_t* bytePtr = (uint8_t*)data.get(); - bytePtr += attribData.offset; - for (size_t i = 0; i < GetNumPoints(); i++) - { - cb((const void*)bytePtr); - bytePtr += attribData.stride; - } + positionAttrib.ForEachConst(GetRawDataPtr(), GetStride(), GetNumPoints(), cb); } -void PointCloud::InitAttribs(size_t size) +void PointCloud::InitAttribs() { - // GL_FLOAT = 0x1406 - positionAttrib = {4, 0x1406, (int)size, offsetof(PointData, position)}; - colorAttrib = {4, 0x1406, (int)size, offsetof(PointData, color)}; + positionAttrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(PointData, position)}; + colorAttrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(PointData, color)}; } diff --git a/src/pointcloud.h b/src/pointcloud.h index b95d55e..3f36cdd 100644 --- a/src/pointcloud.h +++ b/src/pointcloud.h @@ -10,6 +10,8 @@ #include #include +#include "core/binaryattribute.h" + class PointCloud { public: @@ -21,33 +23,24 @@ class PointCloud void InitDebugCloud(); size_t GetNumPoints() const { return numPoints; } - size_t GetTotalSize() const { return GetNumPoints() * pointSize; } + size_t GetStride() const { return pointSize; } + size_t GetTotalSize() const { return GetNumPoints() * GetStride(); } void* GetRawDataPtr() { return data.get(); } + const void* GetRawDataPtr() const { return data.get(); } - struct AttribData - { - AttribData() : size(0), type(0), stride(0), offset(0) {} - AttribData(int32_t sizeIn, int32_t typeIn, int32_t strideIn, size_t offsetIn) : size(sizeIn), type(typeIn), stride(strideIn), offset(offsetIn) {} - bool IsValid() const { return size != 0; } - int32_t size; - int32_t type; - int32_t stride; - size_t offset; - }; - - const AttribData& GetPositionAttrib() const { return positionAttrib; } - const AttribData& GetColorAttrib() const { return colorAttrib; } + const BinaryAttribute& GetPositionAttrib() const { return positionAttrib; } + const BinaryAttribute& GetColorAttrib() const { return colorAttrib; } - using AttribCallback = std::function; - void ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const; + using ForEachPositionCallback = std::function; + void ForEachPosition(const ForEachPositionCallback& cb) const; protected: - void InitAttribs(size_t size); + void InitAttribs(); std::shared_ptr data; - AttribData positionAttrib; - AttribData colorAttrib; + BinaryAttribute positionAttrib; + BinaryAttribute colorAttrib; size_t numPoints; size_t pointSize; diff --git a/src/pointrenderer.cpp b/src/pointrenderer.cpp index d6b8743..bacf206 100644 --- a/src/pointrenderer.cpp +++ b/src/pointrenderer.cpp @@ -30,9 +30,10 @@ #include "radix_sort.hpp" -static void SetupAttrib(int loc, const PointCloud::AttribData& attrib) +static void SetupAttrib(int loc, const BinaryAttribute& attrib, int32_t numElems, size_t stride) { - glVertexAttribPointer(loc, attrib.size, attrib.type, GL_FALSE, attrib.stride, (void*)attrib.offset); + assert(attrib.type == BinaryAttribute::Type::Float); + glVertexAttribPointer(loc, numElems, GL_FLOAT, GL_FALSE, (uint32_t)stride, (void*)attrib.offset); glEnableVertexAttribArray(loc); } @@ -79,10 +80,9 @@ bool PointRenderer::Init(std::shared_ptr pointCloud, bool isFramebuf // build posVec posVec.reserve(numPoints); - pointCloud->ForEachAttrib(pointCloud->GetPositionAttrib(), [this](const void* ptr) + pointCloud->ForEachPosition([this](const float* pos) { - const float* p = (const float*)ptr; - posVec.emplace_back(glm::vec4(p[0], p[1], p[2], p[3])); + posVec.emplace_back(glm::vec4(pos[0], pos[1], pos[2], pos[3])); }); BuildVertexArrayObject(pointCloud); @@ -217,8 +217,8 @@ void PointRenderer::BuildVertexArrayObject(std::shared_ptr pointClou pointVao->Bind(); pointDataBuffer->Bind(); - SetupAttrib(pointProg->GetAttribLoc("position"), pointCloud->GetPositionAttrib()); - SetupAttrib(pointProg->GetAttribLoc("color"), pointCloud->GetColorAttrib()); + SetupAttrib(pointProg->GetAttribLoc("position"), pointCloud->GetPositionAttrib(), 4, pointCloud->GetStride()); + SetupAttrib(pointProg->GetAttribLoc("color"), pointCloud->GetColorAttrib(), 4, pointCloud->GetStride()); pointVao->SetElementBuffer(indexBuffer); pointDataBuffer->Unbind(); diff --git a/src/splatrenderer.cpp b/src/splatrenderer.cpp index 8091a55..600a95c 100644 --- a/src/splatrenderer.cpp +++ b/src/splatrenderer.cpp @@ -32,9 +32,10 @@ static const uint32_t NUM_BLOCKS_PER_WORKGROUP = 1024; -static void SetupAttrib(int loc, const GaussianCloud::AttribData& attrib) +static void SetupAttrib(int loc, const BinaryAttribute& attrib, int32_t count, size_t stride) { - glVertexAttribPointer(loc, attrib.size, attrib.type, GL_FALSE, attrib.stride, (void*)attrib.offset); + assert(attrib.type == BinaryAttribute::Type::Float); + glVertexAttribPointer(loc, count, GL_FLOAT, GL_FALSE, (uint32_t)stride, (void*)attrib.offset); glEnableVertexAttribArray(loc); } @@ -105,10 +106,9 @@ bool SplatRenderer::Init(std::shared_ptr gaussianCloud, bool isFr // build posVec size_t numGaussians = gaussianCloud->GetNumGaussians(); posVec.reserve(numGaussians); - gaussianCloud->ForEachAttrib(gaussianCloud->GetPosWithAlphaAttrib(), [this](const void* ptr) + gaussianCloud->ForEachPosWithAlpha([this](const float* pos) { - const float* p = (const float*)ptr; - posVec.emplace_back(glm::vec4(p[0], p[1], p[2], 1.0f)); + posVec.emplace_back(glm::vec4(pos[0], pos[1], pos[2], 1.0f)); }); BuildVertexArrayObject(gaussianCloud); @@ -366,25 +366,26 @@ void SplatRenderer::BuildVertexArrayObject(std::shared_ptr gaussi splatVao->Bind(); gaussianDataBuffer->Bind(); - SetupAttrib(splatProg->GetAttribLoc("position"), gaussianCloud->GetPosWithAlphaAttrib()); - SetupAttrib(splatProg->GetAttribLoc("r_sh0"), gaussianCloud->GetR_SH0Attrib()); - SetupAttrib(splatProg->GetAttribLoc("g_sh0"), gaussianCloud->GetG_SH0Attrib()); - SetupAttrib(splatProg->GetAttribLoc("b_sh0"), gaussianCloud->GetB_SH0Attrib()); + const size_t stride = gaussianCloud->GetStride(); + SetupAttrib(splatProg->GetAttribLoc("position"), gaussianCloud->GetPosWithAlphaAttrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("r_sh0"), gaussianCloud->GetR_SH0Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("g_sh0"), gaussianCloud->GetG_SH0Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("b_sh0"), gaussianCloud->GetB_SH0Attrib(), 4, stride); if (useFullSH) { - SetupAttrib(splatProg->GetAttribLoc("r_sh1"), gaussianCloud->GetR_SH1Attrib()); - SetupAttrib(splatProg->GetAttribLoc("r_sh2"), gaussianCloud->GetR_SH2Attrib()); - SetupAttrib(splatProg->GetAttribLoc("r_sh3"), gaussianCloud->GetR_SH3Attrib()); - SetupAttrib(splatProg->GetAttribLoc("g_sh1"), gaussianCloud->GetG_SH1Attrib()); - SetupAttrib(splatProg->GetAttribLoc("g_sh2"), gaussianCloud->GetG_SH2Attrib()); - SetupAttrib(splatProg->GetAttribLoc("g_sh3"), gaussianCloud->GetG_SH3Attrib()); - SetupAttrib(splatProg->GetAttribLoc("b_sh1"), gaussianCloud->GetB_SH1Attrib()); - SetupAttrib(splatProg->GetAttribLoc("b_sh2"), gaussianCloud->GetB_SH2Attrib()); - SetupAttrib(splatProg->GetAttribLoc("b_sh3"), gaussianCloud->GetB_SH3Attrib()); + SetupAttrib(splatProg->GetAttribLoc("r_sh1"), gaussianCloud->GetR_SH1Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("r_sh2"), gaussianCloud->GetR_SH2Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("r_sh3"), gaussianCloud->GetR_SH3Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("g_sh1"), gaussianCloud->GetG_SH1Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("g_sh2"), gaussianCloud->GetG_SH2Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("g_sh3"), gaussianCloud->GetG_SH3Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("b_sh1"), gaussianCloud->GetB_SH1Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("b_sh2"), gaussianCloud->GetB_SH2Attrib(), 4, stride); + SetupAttrib(splatProg->GetAttribLoc("b_sh3"), gaussianCloud->GetB_SH3Attrib(), 4, stride); } - SetupAttrib(splatProg->GetAttribLoc("cov3_col0"), gaussianCloud->GetCov3_Col0Attrib()); - SetupAttrib(splatProg->GetAttribLoc("cov3_col1"), gaussianCloud->GetCov3_Col1Attrib()); - SetupAttrib(splatProg->GetAttribLoc("cov3_col2"), gaussianCloud->GetCov3_Col2Attrib()); + SetupAttrib(splatProg->GetAttribLoc("cov3_col0"), gaussianCloud->GetCov3_Col0Attrib(), 3, stride); + SetupAttrib(splatProg->GetAttribLoc("cov3_col1"), gaussianCloud->GetCov3_Col1Attrib(), 3, stride); + SetupAttrib(splatProg->GetAttribLoc("cov3_col2"), gaussianCloud->GetCov3_Col2Attrib(), 3, stride); splatVao->SetElementBuffer(indexBuffer); gaussianDataBuffer->Unbind(); From 48767c412ca01845a61c84357e167b379a401ee6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 29 Jun 2024 11:48:36 -0700 Subject: [PATCH 10/15] Simplified BinaryAttribute ctor Also some prep work for Ply exporting, build fix for linux --- CMakeLists.txt | 1 + src/core/binaryattribute.cpp | 26 ++++++++++++++++++++++++++ src/core/binaryattribute.h | 9 +++++---- src/gaussiancloud.cpp | 36 ++++++++++++++++++------------------ src/ply.cpp | 29 ++++++++++++++--------------- src/ply.h | 5 ++++- src/pointcloud.cpp | 20 +++++++++----------- 7 files changed, 77 insertions(+), 49 deletions(-) create mode 100644 src/core/binaryattribute.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 10a3617..2eae190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ find_package(OpenXR CONFIG REQUIRED) # src include_directories(src) add_executable(${PROJECT_NAME} + src/core/binaryattribute.cpp src/core/debugrenderer.cpp src/core/framebuffer.cpp src/core/image.cpp diff --git a/src/core/binaryattribute.cpp b/src/core/binaryattribute.cpp new file mode 100644 index 0000000..2f77759 --- /dev/null +++ b/src/core/binaryattribute.cpp @@ -0,0 +1,26 @@ +/* + Copyright (c) 2024 Anthony J. Thibault + This software is licensed under the MIT License. See LICENSE for more details. +*/ + +#include "binaryattribute.h" + +static uint32_t propertyTypeSizeArr[(size_t)BinaryAttribute::Type::NumTypes] = { + 0, // Unknown + sizeof(int8_t), // Char + sizeof(uint8_t), // UChar + sizeof(int16_t), // Short + sizeof(uint16_t), // UShort + sizeof(int32_t), // Int + sizeof(uint32_t), // UInt + sizeof(float), // Float + sizeof(double) // Double +}; + +BinaryAttribute::BinaryAttribute(Type typeIn, size_t offsetIn) : + type(typeIn), + size(propertyTypeSizeArr[(uint32_t)typeIn]), + offset(offsetIn) +{ + ; +} diff --git a/src/core/binaryattribute.h b/src/core/binaryattribute.h index d448cb4..681b525 100644 --- a/src/core/binaryattribute.h +++ b/src/core/binaryattribute.h @@ -7,6 +7,7 @@ #include #include +#include class BinaryAttribute { @@ -26,7 +27,7 @@ class BinaryAttribute }; BinaryAttribute() : type(Type::Unknown), size(0), offset(0) {} - BinaryAttribute(Type typeIn, size_t sizeIn, size_t offsetIn) : type(typeIn), size(sizeIn), offset(offsetIn) {} + BinaryAttribute(Type typeIn, size_t offsetIn); template const T* Get(const void* data) const @@ -43,7 +44,7 @@ class BinaryAttribute } template - T* Get(const void* data) + T* Get(void* data) { if (type == Type::Unknown) { @@ -79,7 +80,7 @@ class BinaryAttribute } template - void ForEach(void* data, size_t stride, size_t count, const std::function& cb) + void ForEachMut(void* data, size_t stride, size_t count, const std::function& cb) { assert(type != Type::Unknown); assert(data); @@ -92,7 +93,7 @@ class BinaryAttribute } template - void ForEachConst(const void* data, size_t stride, size_t count, const std::function& cb) const + void ForEach(const void* data, size_t stride, size_t count, const std::function& cb) const { assert(type != Type::Unknown); assert(data); diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index eb7c323..8c7407a 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -170,7 +170,7 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) { ZoneScopedNC("ply.ForEachVertex", tracy::Color::Blue); int i = 0; - ply.ForEachVertex([this, gd, &i, &props](const uint8_t* data, size_t size) + ply.ForEachVertex([this, gd, &i, &props](const void* data, size_t size) { gd[i].posWithAlpha[0] = props.x.Read(data); gd[i].posWithAlpha[1] = props.y.Read(data); @@ -465,25 +465,25 @@ void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats) void GaussianCloud::ForEachPosWithAlpha(const ForEachPosWithAlphaCallback& cb) const { - posWithAlphaAttrib.ForEachConst(GetRawDataPtr(), GetStride(), GetNumGaussians(), cb); + posWithAlphaAttrib.ForEach(GetRawDataPtr(), GetStride(), GetNumGaussians(), cb); } void GaussianCloud::InitAttribs() { - posWithAlphaAttrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, posWithAlpha)}; - r_sh0Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, r_sh0)}; - r_sh1Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, r_sh1)}; - r_sh2Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, r_sh2)}; - r_sh3Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, r_sh3)}; - g_sh0Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, g_sh0)}; - g_sh1Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, g_sh1)}; - g_sh2Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, g_sh2)}; - g_sh3Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, g_sh3)}; - b_sh0Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, b_sh0)}; - b_sh1Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, b_sh1)}; - b_sh2Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, b_sh2)}; - b_sh3Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, b_sh3)}; - cov3_col0Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, cov3_col0)}; - cov3_col1Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, cov3_col1)}; - cov3_col2Attrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(GaussianData, cov3_col2)}; + posWithAlphaAttrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, posWithAlpha)}; + r_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, r_sh0)}; + r_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, r_sh1)}; + r_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, r_sh2)}; + r_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, r_sh3)}; + g_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, g_sh0)}; + g_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, g_sh1)}; + g_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, g_sh2)}; + g_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, g_sh3)}; + b_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, b_sh0)}; + b_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, b_sh1)}; + b_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, b_sh2)}; + b_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, b_sh3)}; + cov3_col0Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, cov3_col0)}; + cov3_col1Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, cov3_col1)}; + cov3_col2Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, cov3_col2)}; } diff --git a/src/ply.cpp b/src/ply.cpp index d908e66..5a38685 100644 --- a/src/ply.cpp +++ b/src/ply.cpp @@ -18,17 +18,6 @@ #include "core/log.h" -static uint32_t propertyTypeSizeArr[(size_t)BinaryAttribute::Type::NumTypes] = { - 0, // Unknown - 1, // Char - 1, // UChar - 2, // Short - 2, // UShort - 4, // Int - 4, // UInt - 4, // Float - 8 // Double -}; static bool CheckLine(std::ifstream& plyFile, const std::string& validLine) { @@ -201,10 +190,9 @@ bool Ply::GetProperty(const std::string& key, BinaryAttribute& binaryAttributeOu void Ply::AddProperty(const std::string& key, BinaryAttribute::Type type) { using PropInfoPair = std::pair; - - uint32_t propSize = propertyTypeSizeArr[(int)type]; - propertyMap.emplace(PropInfoPair(key, BinaryAttribute{type, propSize, (uint32_t)vertexSize})); - vertexSize += propSize; + BinaryAttribute attrib(type, vertexSize); + propertyMap.emplace(PropInfoPair(key, attrib)); + vertexSize += attrib.size; } void Ply::AllocData(size_t numVertices) @@ -222,3 +210,14 @@ void Ply::ForEachVertex(const VertexCallback& cb) const } } +void Ply::ForEachVertexMut(const VertexCallbackMut& cb) +{ + uint8_t* ptr = data.get(); + for (size_t i = 0; i < vertexCount; i++) + { + cb(ptr, vertexSize); + ptr += vertexSize; + } +} + + diff --git a/src/ply.h b/src/ply.h index 735296f..b34583e 100644 --- a/src/ply.h +++ b/src/ply.h @@ -26,9 +26,12 @@ class Ply void AddProperty(const std::string& key, BinaryAttribute::Type type); void AllocData(size_t numVertices); - using VertexCallback = std::function; + using VertexCallback = std::function; void ForEachVertex(const VertexCallback& cb) const; + using VertexCallbackMut = std::function; + void ForEachVertexMut(const VertexCallbackMut& cb); + size_t GetVertexCount() const { return vertexCount; } protected: diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index 106f692..ca4aa29 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -79,7 +79,7 @@ bool PointCloud::ImportPly(const std::string& plyFilename) if (useDoubles) { int i = 0; - ply.ForEachVertex([this, pd, &i, &props](const uint8_t* data, size_t size) + ply.ForEachVertex([this, pd, &i, &props](const void* data, size_t size) { if (useLinearColors) { @@ -104,7 +104,7 @@ bool PointCloud::ImportPly(const std::string& plyFilename) else { int i = 0; - ply.ForEachVertex([this, pd, &i, &props](const uint8_t* data, size_t size) + ply.ForEachVertex([this, pd, &i, &props](const void* data, size_t size) { if (useLinearColors) { @@ -155,13 +155,11 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const ply.AllocData(numPoints); - /* - ply.ForEachVertex([this](uint8_t* data, size_t size)) + ply.ForEachVertexMut([this](void* data, size_t size) { - positionAttrib.Set - } - */ - + // + }); + /* // ply files have unix line endings. plyFile << "ply\n"; @@ -248,11 +246,11 @@ void PointCloud::InitDebugCloud() void PointCloud::ForEachPosition(const ForEachPositionCallback& cb) const { - positionAttrib.ForEachConst(GetRawDataPtr(), GetStride(), GetNumPoints(), cb); + positionAttrib.ForEach(GetRawDataPtr(), GetStride(), GetNumPoints(), cb); } void PointCloud::InitAttribs() { - positionAttrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(PointData, position)}; - colorAttrib = {BinaryAttribute::Type::Float, sizeof(float), offsetof(PointData, color)}; + positionAttrib = {BinaryAttribute::Type::Float, offsetof(PointData, position)}; + colorAttrib = {BinaryAttribute::Type::Float, offsetof(PointData, color)}; } From 7d44266516ac9e1f86e901655ded6b5ea084d129 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 29 Jun 2024 13:47:24 -0700 Subject: [PATCH 11/15] fixed PointCloud::ExportPly --- src/ply.cpp | 165 ++++++++++++++++++++++++++++++--------------- src/ply.h | 2 + src/pointcloud.cpp | 64 ++++++++++-------- 3 files changed, 148 insertions(+), 83 deletions(-) diff --git a/src/ply.cpp b/src/ply.cpp index 5a38685..09a2c9b 100644 --- a/src/ply.cpp +++ b/src/ply.cpp @@ -18,7 +18,6 @@ #include "core/log.h" - static bool CheckLine(std::ifstream& plyFile, const std::string& validLine) { std::string line; @@ -38,11 +37,105 @@ static bool GetNextPlyLine(std::ifstream& plyFile, std::string& lineOut) return false; } +static const char* BinaryAttributeTypeToString(BinaryAttribute::Type type) +{ + switch (type) + { + case BinaryAttribute::Type::Char: + return "char"; + case BinaryAttribute::Type::UChar: + return "uchar"; + case BinaryAttribute::Type::Short: + return "short"; + case BinaryAttribute::Type::UShort: + return "ushort"; + case BinaryAttribute::Type::Int: + return "int"; + case BinaryAttribute::Type::UInt: + return "uint"; + case BinaryAttribute::Type::Float: + return "float"; + case BinaryAttribute::Type::Double: + return "double"; + default: + assert(false); // bad attribute type + return "unknown"; + }; +} + Ply::Ply() : vertexCount(0), vertexSize(0) { ; } +bool Ply::Parse(std::ifstream& plyFile) +{ + if (!ParseHeader(plyFile)) + { + return false; + } + + // read rest of file into data ptr + { + ZoneScopedNC("Ply::Parse() read data", tracy::Color::Yellow); + AllocData(vertexCount); + plyFile.read((char*)data.get(), vertexSize * vertexCount); + } + + return true; +} + +void Ply::Dump(std::ofstream& plyFile) const +{ + DumpHeader(plyFile); + plyFile.write((char*)data.get(), vertexSize * vertexCount); +} + +bool Ply::GetProperty(const std::string& key, BinaryAttribute& binaryAttributeOut) const +{ + auto iter = propertyMap.find(key); + if (iter != propertyMap.end()) + { + binaryAttributeOut = iter->second; + return true; + } + return false; +} + +void Ply::AddProperty(const std::string& key, BinaryAttribute::Type type) +{ + using PropInfoPair = std::pair; + BinaryAttribute attrib(type, vertexSize); + propertyMap.emplace(PropInfoPair(key, attrib)); + vertexSize += attrib.size; +} + +void Ply::AllocData(size_t numVertices) +{ + vertexCount = numVertices; + data.reset(new uint8_t[vertexSize * numVertices]); +} + +void Ply::ForEachVertex(const VertexCallback& cb) const +{ + const uint8_t* ptr = data.get(); + for (size_t i = 0; i < vertexCount; i++) + { + cb(ptr, vertexSize); + ptr += vertexSize; + } +} + +void Ply::ForEachVertexMut(const VertexCallbackMut& cb) +{ + uint8_t* ptr = data.get(); + for (size_t i = 0; i < vertexCount; i++) + { + cb(ptr, vertexSize); + ptr += vertexSize; + } +} + bool Ply::ParseHeader(std::ifstream& plyFile) { ZoneScopedNC("Ply::ParseHeader", tracy::Color::Green); @@ -159,65 +252,29 @@ bool Ply::ParseHeader(std::ifstream& plyFile) return true; } -bool Ply::Parse(std::ifstream& plyFile) +void Ply::DumpHeader(std::ofstream& plyFile) const { - if (!ParseHeader(plyFile)) - { - return false; - } + // ply files have unix line endings. + plyFile << "ply\n"; + plyFile << "format binary_little_endian 1.0\n"; + plyFile << "element vertex " << vertexCount << "\n"; - // read rest of file into data ptr - { - ZoneScopedNC("Ply::Parse() read data", tracy::Color::Yellow); - AllocData(vertexCount); - plyFile.read((char*)data.get(), vertexSize * vertexCount); - } - - return true; -} - -bool Ply::GetProperty(const std::string& key, BinaryAttribute& binaryAttributeOut) const -{ - auto iter = propertyMap.find(key); - if (iter != propertyMap.end()) - { - binaryAttributeOut = iter->second; - return true; - } - return false; -} - -void Ply::AddProperty(const std::string& key, BinaryAttribute::Type type) -{ + // sort properties by offset using PropInfoPair = std::pair; - BinaryAttribute attrib(type, vertexSize); - propertyMap.emplace(PropInfoPair(key, attrib)); - vertexSize += attrib.size; -} - -void Ply::AllocData(size_t numVertices) -{ - data.reset(new uint8_t[vertexSize * numVertices]); -} - -void Ply::ForEachVertex(const VertexCallback& cb) const -{ - const uint8_t* ptr = data.get(); - for (size_t i = 0; i < vertexCount; i++) + std::vector propVec; + propVec.reserve(propertyMap.size()); + for (auto& pair : propertyMap) { - cb(ptr, vertexSize); - ptr += vertexSize; + propVec.push_back(pair); } -} + std::sort(propVec.begin(), propVec.end(), [](const PropInfoPair& a, const PropInfoPair& b) + { + return a.second.offset < b.second.offset; + }); -void Ply::ForEachVertexMut(const VertexCallbackMut& cb) -{ - uint8_t* ptr = data.get(); - for (size_t i = 0; i < vertexCount; i++) + for (auto& pair : propVec) { - cb(ptr, vertexSize); - ptr += vertexSize; + plyFile << "property " << BinaryAttributeTypeToString(pair.second.type) << " " << pair.first << "\n"; } + plyFile << "end_header\n"; } - - diff --git a/src/ply.h b/src/ply.h index b34583e..d94ee5c 100644 --- a/src/ply.h +++ b/src/ply.h @@ -21,6 +21,7 @@ class Ply public: Ply(); bool Parse(std::ifstream& plyFile); + void Dump(std::ofstream& plyFile) const; bool GetProperty(const std::string& key, BinaryAttribute& attributeOut) const; void AddProperty(const std::string& key, BinaryAttribute::Type type); @@ -36,6 +37,7 @@ class Ply protected: bool ParseHeader(std::ifstream& plyFile); + void DumpHeader(std::ofstream& plyFile) const; std::unordered_map propertyMap; std::unique_ptr data; diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index ca4aa29..1df4c1a 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -132,9 +132,6 @@ bool PointCloud::ImportPly(const std::string& plyFilename) bool PointCloud::ExportPly(const std::string& plyFilename) const { - // AJT: TODO - return false; - std::ofstream plyFile(plyFilename, std::ios::binary); if (!plyFile.is_open()) { @@ -153,37 +150,46 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const ply.AddProperty("green", BinaryAttribute::Type::UChar); ply.AddProperty("blue", BinaryAttribute::Type::UChar); + struct + { + BinaryAttribute x, y, z; + BinaryAttribute nx, ny, nz; + BinaryAttribute red, green, blue; + } props; + + ply.GetProperty("x", props.x); + ply.GetProperty("y", props.y); + ply.GetProperty("z", props.z); + ply.GetProperty("nx", props.nx); + ply.GetProperty("ny", props.ny); + ply.GetProperty("nz", props.nz); + ply.GetProperty("red", props.red); + ply.GetProperty("green", props.green); + ply.GetProperty("blue", props.blue); + ply.AllocData(numPoints); - ply.ForEachVertexMut([this](void* data, size_t size) + uint8_t* cloudData = (uint8_t*)data.get(); + size_t runningSize = 0; + ply.ForEachVertexMut([this, &props, &cloudData, &runningSize](void* plyData, size_t size) { - // + const float* position = positionAttrib.Get(cloudData); + props.x.Write(plyData, position[0]); + props.y.Write(plyData, position[1]); + props.z.Write(plyData, position[2]); + props.nx.Write(plyData, 0.0f); + props.ny.Write(plyData, 0.0f); + props.nz.Write(plyData, 0.0f); + const float* color = colorAttrib.Get(cloudData); + props.red.Write(plyData, (uint8_t)(color[0] * 255.0f)); + props.green.Write(plyData, (uint8_t)(color[1] * 255.0f)); + props.blue.Write(plyData, (uint8_t)(color[2] * 255.0f)); + cloudData += pointSize; + runningSize += pointSize; + assert(runningSize <= GetTotalSize()); // bad, we went outside of data ptr contents. }); - /* - // ply files have unix line endings. - plyFile << "ply\n"; - plyFile << "format binary_little_endian 1.0\n"; - plyFile << "element vertex " << numPoints << "\n"; - plyFile << "property float x\n"; - plyFile << "property float y\n"; - plyFile << "property float z\n"; - plyFile << "property float nx\n"; - plyFile << "property float ny\n"; - plyFile << "property float nz\n"; - plyFile << "property uchar red\n"; - plyFile << "property uchar green\n"; - plyFile << "property uchar blue\n"; - plyFile << "end_header\n"; - */ - - /* - const size_t POINT_SIZE = 27; - for (auto&& p : pointDataVec) - { - plyFile.write((char*)&p, POINT_SIZE); - } - */ + ply.Dump(plyFile); return true; } From 9bfd22ebe05f6bbe1ff99146ec2e80249c422135 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 30 Jun 2024 14:52:05 -0700 Subject: [PATCH 12/15] fix GaussianCloud::ExportPly() (wip) * still need to fix ComputeRotScaleFromCovMat()! --- src/gaussiancloud.cpp | 235 ++++++++++++++++++++++++++---------------- src/gaussiancloud.h | 2 +- src/ply.cpp | 1 + src/pointcloud.cpp | 4 +- 4 files changed, 151 insertions(+), 91 deletions(-) diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index 8c7407a..5317f4b 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -48,7 +48,7 @@ struct GaussianData float cov3_col2[3]; }; -glm::mat3 ComputeCovMat(float rot[4], float scale[3]) +glm::mat3 ComputeCovMatFromRotScale(float rot[4], float scale[3]) { glm::quat q(rot[0], rot[1], rot[2], rot[3]); glm::mat3 R(glm::normalize(q)); @@ -58,11 +58,23 @@ glm::mat3 ComputeCovMat(float rot[4], float scale[3]) return R * S * glm::transpose(S) * glm::transpose(R); } -float ComputeAlpha(float opacity) +void ComputeRotScaleFromCovMat(const glm::mat3& V, glm::quat& rotOut, glm::vec3& scaleOut) +{ + // AJT: TODO: use eigendecomposition to compute this from V + rotOut = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); + scaleOut = glm::vec3(-2.99573231f, -2.99573231f, -2.99573231f); +} + +float ComputeAlphaFromOpacity(float opacity) { return 1.0f / (1.0f + expf(-opacity)); } +float ComputeOpacityFromAlpha(float alpha) +{ + return -logf((1.0f / alpha) - 1.0f); +} + GaussianCloud::GaussianCloud(bool useLinearColorsIn) : numGaussians(0), gaussianSize(0), @@ -175,7 +187,7 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) gd[i].posWithAlpha[0] = props.x.Read(data); gd[i].posWithAlpha[1] = props.y.Read(data); gd[i].posWithAlpha[2] = props.z.Read(data); - gd[i].posWithAlpha[3] = ComputeAlpha(props.opacity.Read(data)); + gd[i].posWithAlpha[3] = ComputeAlphaFromOpacity(props.opacity.Read(data)); // AJT: TODO check for useLinearColors @@ -230,6 +242,12 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) gd[i].b_sh3[2] = props.f_rest[43].Read(data); gd[i].b_sh3[3] = props.f_rest[44].Read(data); + float scale[3] = + { + props.scale[0].Read(data), + props.scale[1].Read(data), + props.scale[2].Read(data) + }; float rot[4] = { props.rot[0].Read(data), @@ -237,14 +255,8 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) props.rot[2].Read(data), props.rot[3].Read(data) }; - float scale[4] = - { - props.scale[0].Read(data), - props.scale[1].Read(data), - props.scale[2].Read(data) - }; - glm::mat3 V = ComputeCovMat(rot, scale); + glm::mat3 V = ComputeCovMatFromRotScale(rot, scale); gd[i].cov3_col0[0] = V[0][0]; gd[i].cov3_col0[1] = V[0][1]; gd[i].cov3_col0[2] = V[0][2]; @@ -262,11 +274,8 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) return true; } -bool GaussianCloud::ExportPly(const std::string& plyFilename) const +bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) const { - // AJT: TODO FIXME: - return false; - std::ofstream plyFile(plyFilename, std::ios::binary); if (!plyFile.is_open()) { @@ -274,83 +283,131 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename) const return false; } - // ply files have unix line endings. - plyFile << "ply\n"; - plyFile << "format binary_little_endian 1.0\n"; - plyFile << "element vertex " << numGaussians << "\n"; - plyFile << "property float x\n"; - plyFile << "property float y\n"; - plyFile << "property float z\n"; - plyFile << "property float nx\n"; - plyFile << "property float ny\n"; - plyFile << "property float nz\n"; - plyFile << "property float f_dc_0\n"; - plyFile << "property float f_dc_1\n"; - plyFile << "property float f_dc_2\n"; - plyFile << "property float f_rest_0\n"; - plyFile << "property float f_rest_1\n"; - plyFile << "property float f_rest_2\n"; - plyFile << "property float f_rest_3\n"; - plyFile << "property float f_rest_4\n"; - plyFile << "property float f_rest_5\n"; - plyFile << "property float f_rest_6\n"; - plyFile << "property float f_rest_7\n"; - plyFile << "property float f_rest_8\n"; - plyFile << "property float f_rest_9\n"; - plyFile << "property float f_rest_10\n"; - plyFile << "property float f_rest_11\n"; - plyFile << "property float f_rest_12\n"; - plyFile << "property float f_rest_13\n"; - plyFile << "property float f_rest_14\n"; - plyFile << "property float f_rest_15\n"; - plyFile << "property float f_rest_16\n"; - plyFile << "property float f_rest_17\n"; - plyFile << "property float f_rest_18\n"; - plyFile << "property float f_rest_19\n"; - plyFile << "property float f_rest_20\n"; - plyFile << "property float f_rest_21\n"; - plyFile << "property float f_rest_22\n"; - plyFile << "property float f_rest_23\n"; - plyFile << "property float f_rest_24\n"; - plyFile << "property float f_rest_25\n"; - plyFile << "property float f_rest_26\n"; - plyFile << "property float f_rest_27\n"; - plyFile << "property float f_rest_28\n"; - plyFile << "property float f_rest_29\n"; - plyFile << "property float f_rest_30\n"; - plyFile << "property float f_rest_31\n"; - plyFile << "property float f_rest_32\n"; - plyFile << "property float f_rest_33\n"; - plyFile << "property float f_rest_34\n"; - plyFile << "property float f_rest_35\n"; - plyFile << "property float f_rest_36\n"; - plyFile << "property float f_rest_37\n"; - plyFile << "property float f_rest_38\n"; - plyFile << "property float f_rest_39\n"; - plyFile << "property float f_rest_40\n"; - plyFile << "property float f_rest_41\n"; - plyFile << "property float f_rest_42\n"; - plyFile << "property float f_rest_43\n"; - plyFile << "property float f_rest_44\n"; - plyFile << "property float opacity\n"; - plyFile << "property float scale_0\n"; - plyFile << "property float scale_1\n"; - plyFile << "property float scale_2\n"; - plyFile << "property float rot_0\n"; - plyFile << "property float rot_1\n"; - plyFile << "property float rot_2\n"; - plyFile << "property float rot_3\n"; - plyFile << "end_header\n"; - - /* AJT: TODO FIXME - const size_t GAUSSIAN_SIZE = 62 * sizeof(float); - static_assert(sizeof(Gaussian) >= GAUSSIAN_SIZE); - - for (auto&& g : gaussianVec) + Ply ply; + ply.AddProperty("x", BinaryAttribute::Type::Float); + ply.AddProperty("y", BinaryAttribute::Type::Float); + ply.AddProperty("z", BinaryAttribute::Type::Float); + ply.AddProperty("nx", BinaryAttribute::Type::Float); + ply.AddProperty("ny", BinaryAttribute::Type::Float); + ply.AddProperty("nz", BinaryAttribute::Type::Float); + ply.AddProperty("f_dc_0", BinaryAttribute::Type::Float); + ply.AddProperty("f_dc_1", BinaryAttribute::Type::Float); + ply.AddProperty("f_dc_2", BinaryAttribute::Type::Float); + if (exportFullSh) { - plyFile.write((char*)&g, GAUSSIAN_SIZE); + for (int i = 0; i < 45; i++) + { + ply.AddProperty("f_rest_" + std::to_string(i), BinaryAttribute::Type::Float); + } } - */ + ply.AddProperty("opacity", BinaryAttribute::Type::Float); + ply.AddProperty("scale_0", BinaryAttribute::Type::Float); + ply.AddProperty("scale_1", BinaryAttribute::Type::Float); + ply.AddProperty("scale_2", BinaryAttribute::Type::Float); + ply.AddProperty("rot_0", BinaryAttribute::Type::Float); + ply.AddProperty("rot_1", BinaryAttribute::Type::Float); + ply.AddProperty("rot_2", BinaryAttribute::Type::Float); + ply.AddProperty("rot_3", BinaryAttribute::Type::Float); + + struct + { + BinaryAttribute x, y, z; + BinaryAttribute nx, ny, nz; + BinaryAttribute f_dc[3]; + BinaryAttribute f_rest[45]; + BinaryAttribute opacity; + BinaryAttribute scale[3]; + BinaryAttribute rot[4]; + } props; + + ply.GetProperty("x", props.x); + ply.GetProperty("y", props.y); + ply.GetProperty("z", props.z); + ply.GetProperty("nx", props.nx); + ply.GetProperty("ny", props.ny); + ply.GetProperty("nz", props.nz); + ply.GetProperty("f_dc_0", props.f_dc[0]); + ply.GetProperty("f_dc_1", props.f_dc[1]); + ply.GetProperty("f_dc_2", props.f_dc[2]); + if (exportFullSh) + { + for (int i = 0; i < 45; i++) + { + ply.GetProperty("f_rest_" + std::to_string(i), props.f_rest[i]); + } + } + ply.GetProperty("opacity", props.opacity); + ply.GetProperty("scale_0", props.scale[0]); + ply.GetProperty("scale_1", props.scale[1]); + ply.GetProperty("scale_2", props.scale[2]); + ply.GetProperty("rot_0", props.rot[0]); + ply.GetProperty("rot_1", props.rot[1]); + ply.GetProperty("rot_2", props.rot[2]); + ply.GetProperty("rot_3", props.rot[3]); + + ply.AllocData(numGaussians); + + uint8_t* gData = (uint8_t*)data.get(); + size_t runningSize = 0; + ply.ForEachVertexMut([this, &props, &gData, &runningSize, &exportFullSh](void* plyData, size_t size) + { + const float* posWithAlpha = posWithAlphaAttrib.Get(gData); + const float* r_sh0 = r_sh0Attrib.Get(gData); + const float* g_sh0 = g_sh0Attrib.Get(gData); + const float* b_sh0 = b_sh0Attrib.Get(gData); + const float* cov3_col0 = cov3_col0Attrib.Get(gData); + + props.x.Write(plyData, posWithAlpha[0]); + props.y.Write(plyData, posWithAlpha[1]); + props.z.Write(plyData, posWithAlpha[2]); + props.nx.Write(plyData, 0.0f); + props.ny.Write(plyData, 0.0f); + props.nz.Write(plyData, 0.0f); + props.f_dc[0].Write(plyData, r_sh0[0]); + props.f_dc[1].Write(plyData, g_sh0[0]); + props.f_dc[2].Write(plyData, b_sh0[0]); + + if (exportFullSh) + { + // TODO: maybe just a raw memcopy would be faster + for (int i = 0; i < 15; i++) + { + props.f_rest[i].Write(plyData, r_sh0[i + 1]); + } + for (int i = 0; i < 15; i++) + { + props.f_rest[i + 15].Write(plyData, g_sh0[i + 1]); + } + for (int i = 0; i < 15; i++) + { + props.f_rest[i + 30].Write(plyData, b_sh0[i + 1]); + } + } + + props.opacity.Write(plyData, ComputeOpacityFromAlpha(posWithAlpha[3])); + + glm::mat3 V(cov3_col0[0], cov3_col0[1], cov3_col0[2], + cov3_col0[2], cov3_col0[4], cov3_col0[5], + cov3_col0[6], cov3_col0[7], cov3_col0[8]); + + glm::quat rot; + glm::vec3 scale; + ComputeRotScaleFromCovMat(V, rot, scale); + + props.scale[0].Write(plyData, scale.x); + props.scale[1].Write(plyData, scale.y); + props.scale[2].Write(plyData, scale.z); + props.rot[0].Write(plyData, rot.w); + props.rot[1].Write(plyData, rot.x); + props.rot[2].Write(plyData, rot.y); + props.rot[3].Write(plyData, rot.z); + + gData += gaussianSize; + runningSize += gaussianSize; + assert(runningSize <= GetTotalSize()); // bad, we went off the end of the data ptr + }); + + ply.Dump(plyFile); return true; } diff --git a/src/gaussiancloud.h b/src/gaussiancloud.h index a295a24..c37980b 100644 --- a/src/gaussiancloud.h +++ b/src/gaussiancloud.h @@ -20,7 +20,7 @@ class GaussianCloud GaussianCloud(bool useLinearColorsIn); bool ImportPly(const std::string& plyFilename); - bool ExportPly(const std::string& plyFilename) const; + bool ExportPly(const std::string& plyFilename, bool exportFullSh) const; void InitDebugCloud(); diff --git a/src/ply.cpp b/src/ply.cpp index 09a2c9b..343bcf4 100644 --- a/src/ply.cpp +++ b/src/ply.cpp @@ -5,6 +5,7 @@ #include "ply.h" +#include #include #include #include diff --git a/src/pointcloud.cpp b/src/pointcloud.cpp index 1df4c1a..e1a1044 100644 --- a/src/pointcloud.cpp +++ b/src/pointcloud.cpp @@ -174,16 +174,18 @@ bool PointCloud::ExportPly(const std::string& plyFilename) const ply.ForEachVertexMut([this, &props, &cloudData, &runningSize](void* plyData, size_t size) { const float* position = positionAttrib.Get(cloudData); + const float* color = colorAttrib.Get(cloudData); + props.x.Write(plyData, position[0]); props.y.Write(plyData, position[1]); props.z.Write(plyData, position[2]); props.nx.Write(plyData, 0.0f); props.ny.Write(plyData, 0.0f); props.nz.Write(plyData, 0.0f); - const float* color = colorAttrib.Get(cloudData); props.red.Write(plyData, (uint8_t)(color[0] * 255.0f)); props.green.Write(plyData, (uint8_t)(color[1] * 255.0f)); props.blue.Write(plyData, (uint8_t)(color[2] * 255.0f)); + cloudData += pointSize; runningSize += pointSize; assert(runningSize <= GetTotalSize()); // bad, we went outside of data ptr contents. From f8a112b450bc8809d588e64bdf4d407dca10f61e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 1 Jul 2024 10:57:35 -0700 Subject: [PATCH 13/15] Added eigen3 and work on ComputeRotScaleFromCovMat() Doesn't work on some covariant matrices tho... wip --- CMakeLists.txt | 6 +++ src/gaussiancloud.cpp | 120 +++++++++++++++++++++++++++++++++++++++--- vcpkg.json | 1 + 3 files changed, 119 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2eae190..24d875a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,9 @@ if (WIN32) find_package(nlohmann_json CONFIG REQUIRED) endif() +# eigen +find_package(Eigen3 CONFIG REQUIRED) + # tracy if(WIN32 AND NOT SHIPPING) find_package(Tracy CONFIG REQUIRED) @@ -100,6 +103,7 @@ if(WIN32) glm::glm PNG::PNG nlohmann_json::nlohmann_json + Eigen3::Eigen OpenXR::headers OpenXR::openxr_loader ) @@ -112,6 +116,7 @@ if(WIN32) glm::glm PNG::PNG nlohmann_json::nlohmann_json + Eigen3::Eigen Tracy::TracyClient OpenXR::headers OpenXR::openxr_loader @@ -126,6 +131,7 @@ else() glm::glm PNG::PNG # nlohmann_json::nlohmann_json + Eigen3::Eigen OpenXR::headers OpenXR::openxr_loader ${X11_LIBRARIES} diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index 5317f4b..1c2bae0 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -15,6 +15,8 @@ #include +#include + #ifdef TRACY_ENABLE #include #else @@ -48,29 +50,110 @@ struct GaussianData float cov3_col2[3]; }; -glm::mat3 ComputeCovMatFromRotScale(float rot[4], float scale[3]) +// Function to convert glm::mat3 to Eigen::Matrix3f +static Eigen::Matrix3f glmToEigen(const glm::mat3& glmMat) +{ + Eigen::Matrix3f eigenMat; + for (int r = 0; r < 3; ++r) + { + for (int c = 0; c < 3; ++c) + { + eigenMat(r, c) = glmMat[c][r]; + } + } + return eigenMat; +} + +// Function to convert Eigen::Matrix3f to glm::mat3 +static glm::mat3 eigenToGlm(const Eigen::Matrix3f& eigenMat) +{ + glm::mat3 glmMat; + for (int r = 0; r < 3; ++r) + { + for (int c = 0; c < 3; ++c) + { + glmMat[c][r] = eigenMat(r, c); + } + } + return glmMat; +} + +static glm::mat3 ComputeCovMatFromRotScale(float rot[4], float scale[3]) { glm::quat q(rot[0], rot[1], rot[2], rot[3]); glm::mat3 R(glm::normalize(q)); + // NOTE: scale is stored in log scale in ply file glm::mat3 S(glm::vec3(expf(scale[0]), 0.0f, 0.0f), glm::vec3(0.0f, expf(scale[1]), 0.0f), glm::vec3(0.0f, 0.0f, expf(scale[2]))); return R * S * glm::transpose(S) * glm::transpose(R); } -void ComputeRotScaleFromCovMat(const glm::mat3& V, glm::quat& rotOut, glm::vec3& scaleOut) +static void ComputeRotScaleFromCovMat(const glm::mat3& V, glm::quat& rotOut, glm::vec3& scaleOut) { - // AJT: TODO: use eigendecomposition to compute this from V - rotOut = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); - scaleOut = glm::vec3(-2.99573231f, -2.99573231f, -2.99573231f); + Eigen::Matrix3f eigenV = glmToEigen(V); + + // Perform Eigen decomposition + Eigen::SelfAdjointEigenSolver solver(eigenV); + + // Get eigenvectors and eigenvalues + Eigen::Matrix3f eigenR = solver.eigenvectors(); + Eigen::Array3f eigenS = solver.eigenvalues(); + + glm::mat3 R = eigenToGlm(eigenR); + rotOut = glm::normalize(glm::quat(R)); + // NOTE: scale is stored in log scale in ply file + scaleOut = glm::vec3(logf(std::sqrt(solver.eigenvalues()(0))), + logf(std::sqrt(solver.eigenvalues()(1))), + logf(std::sqrt(solver.eigenvalues()(2)))); + +#if 0 + // AJT: HACK REMOVE + // now check the result. + static int AJT_Count = 0; + float r[] = {rotOut.w, rotOut.x, rotOut.y, rotOut.z}; + float s[] = {scaleOut.x, scaleOut.y, scaleOut.z}; + glm::mat3 V2 = ComputeCovMatFromRotScale(r, s); + + const float EPSILON = 0.0001f; + if (fabsf(V[0][0] - V2[0][0]) > EPSILON || + fabsf(V[0][1] - V2[0][1]) > EPSILON || + fabsf(V[0][2] - V2[0][2]) > EPSILON || + fabsf(V[1][0] - V2[1][0]) > EPSILON || + fabsf(V[1][1] - V2[1][1]) > EPSILON || + fabsf(V[1][2] - V2[1][2]) > EPSILON || + fabsf(V[2][0] - V2[2][0]) > EPSILON || + fabsf(V[2][1] - V2[2][1]) > EPSILON || + fabsf(V[2][2] - V2[2][2]) > EPSILON) + { + + Log::E("AJT: ComputeRotScaleFromCovMat i = %d\n", AJT_Count); + Log::E("AJT: V = | %10.5f, %10.5f, %10.5f |\n", V[0][0], V[1][0], V[2][0]); + Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][1], V[1][1], V[2][1]); + Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][2], V[1][2], V[2][2]); + Log::E("AJT:\n"); + Log::E("AJT: eigenR = | %10.5f %10.5f %10.5f |\n", eigenR(0,0), eigenR(0,1), eigenR(0,2)); + Log::E("AJT: | %10.5f %10.5f %10.5f |\n", eigenR(1,0), eigenR(1,1), eigenR(1,2)); + Log::E("AJT: | %10.5f %10.5f %10.5f |\n", eigenR(2,0), eigenR(2,1), eigenR(2,2)); + Log::E("AJT: eigenS = [%0.5f, %0.5f, %0.5f]\n", eigenS(0), eigenS(1), eigenS(2)); + Log::E("AJT:\n"); + Log::E("AJT: scaleOut = [%.5f, %.5f, %.5f]\n", scaleOut[0], scaleOut[1], scaleOut[2]); + Log::E("AJT: rotOut = [%.5f, %.5f, %.5f, %.5f]\n", rotOut.w, rotOut.x, rotOut.y, rotOut.z); + Log::E("AJT:\n"); + Log::E("AJT: V2 = | %10.5f, %10.5f, %10.5f |\n", V2[0][0], V2[1][0], V2[2][0]); + Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V2[0][1], V2[1][1], V2[2][1]); + Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V2[0][2], V2[1][2], V2[2][2]); + } + AJT_Count++; +#endif } -float ComputeAlphaFromOpacity(float opacity) +static float ComputeAlphaFromOpacity(float opacity) { return 1.0f / (1.0f + expf(-opacity)); } -float ComputeOpacityFromAlpha(float alpha) +static float ComputeOpacityFromAlpha(float alpha) { return -logf((1.0f / alpha) - 1.0f); } @@ -267,6 +350,17 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) gd[i].cov3_col2[1] = V[2][1]; gd[i].cov3_col2[2] = V[2][2]; + // AJT: TODO REMOVE DEBUG + if (i == 0) + { + Log::E("AJT: ImportPly\n"); + Log::E("AJT: rot = [%.5f, %.5f, %.5f, %.5f]\n", rot[0], rot[1], rot[2], rot[3]); + Log::E("AJT: scale = [%.5f, %.5f, %.5f]\n", scale[0], scale[1], scale[2]); + Log::E("AJT: V = | %10.5f, %10.5f, %10.5f |\n", V[0][0], V[1][0], V[2][0]); + Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][1], V[1][1], V[2][1]); + Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][2], V[1][2], V[2][2]); + } + i++; }); } @@ -387,7 +481,7 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) props.opacity.Write(plyData, ComputeOpacityFromAlpha(posWithAlpha[3])); glm::mat3 V(cov3_col0[0], cov3_col0[1], cov3_col0[2], - cov3_col0[2], cov3_col0[4], cov3_col0[5], + cov3_col0[3], cov3_col0[4], cov3_col0[5], cov3_col0[6], cov3_col0[7], cov3_col0[8]); glm::quat rot; @@ -402,6 +496,16 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) props.rot[2].Write(plyData, rot.y); props.rot[3].Write(plyData, rot.z); + if (runningSize == 0) + { + Log::E("AJT: ExportPly\n"); + Log::E("AJT: V = | %10.5f, %10.5f, %10.5f |\n", V[0][0], V[1][0], V[2][0]); + Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][1], V[1][1], V[2][1]); + Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][2], V[1][2], V[2][2]); + Log::E("AJT: rot = [%.5f, %.5f, %.5f, %.5f]\n", rot.w, rot.x, rot.y, rot.z); + Log::E("AJT: scale = [%.5f, %.5f, %.5f]\n", scale[0], scale[1], scale[2]); + } + gData += gaussianSize; runningSize += gaussianSize; assert(runningSize <= GetTotalSize()); // bad, we went off the end of the data ptr diff --git a/vcpkg.json b/vcpkg.json index 7b89f59..14b4f6d 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -7,6 +7,7 @@ "glm", "libpng", "nlohmann-json", + "eigen3", "tracy", "openxr-loader" ] From 2b7b9c20438cfa688544a23cca40f30caff83143 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 1 Jul 2024 18:32:22 -0700 Subject: [PATCH 14/15] bug fix ConvertRotScaleFromCovMat. Need to check determinant of matrix before converting it to a quaternion, and flip the sign of the matrix by multiplying all axes by -1, for quat convertion to be correct. GaussianClound::ExportPly() seems to work now. --- src/gaussiancloud.cpp | 84 ++++++++----------------------------------- 1 file changed, 15 insertions(+), 69 deletions(-) diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index 1c2bae0..d46ab24 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -97,55 +97,23 @@ static void ComputeRotScaleFromCovMat(const glm::mat3& V, glm::quat& rotOut, glm Eigen::SelfAdjointEigenSolver solver(eigenV); // Get eigenvectors and eigenvalues - Eigen::Matrix3f eigenR = solver.eigenvectors(); - Eigen::Array3f eigenS = solver.eigenvalues(); - - glm::mat3 R = eigenToGlm(eigenR); - rotOut = glm::normalize(glm::quat(R)); - // NOTE: scale is stored in log scale in ply file - scaleOut = glm::vec3(logf(std::sqrt(solver.eigenvalues()(0))), - logf(std::sqrt(solver.eigenvalues()(1))), - logf(std::sqrt(solver.eigenvalues()(2)))); - -#if 0 - // AJT: HACK REMOVE - // now check the result. - static int AJT_Count = 0; - float r[] = {rotOut.w, rotOut.x, rotOut.y, rotOut.z}; - float s[] = {scaleOut.x, scaleOut.y, scaleOut.z}; - glm::mat3 V2 = ComputeCovMatFromRotScale(r, s); - - const float EPSILON = 0.0001f; - if (fabsf(V[0][0] - V2[0][0]) > EPSILON || - fabsf(V[0][1] - V2[0][1]) > EPSILON || - fabsf(V[0][2] - V2[0][2]) > EPSILON || - fabsf(V[1][0] - V2[1][0]) > EPSILON || - fabsf(V[1][1] - V2[1][1]) > EPSILON || - fabsf(V[1][2] - V2[1][2]) > EPSILON || - fabsf(V[2][0] - V2[2][0]) > EPSILON || - fabsf(V[2][1] - V2[2][1]) > EPSILON || - fabsf(V[2][2] - V2[2][2]) > EPSILON) + Eigen::Matrix3f eigenVec = solver.eigenvectors(); + Eigen::Array3f eigenVal = solver.eigenvalues(); + glm::mat3 R = eigenToGlm(eigenVec); + // glm mat3 to quat only works when det is 1. + if (glm::determinant(R) < 0) { - - Log::E("AJT: ComputeRotScaleFromCovMat i = %d\n", AJT_Count); - Log::E("AJT: V = | %10.5f, %10.5f, %10.5f |\n", V[0][0], V[1][0], V[2][0]); - Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][1], V[1][1], V[2][1]); - Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][2], V[1][2], V[2][2]); - Log::E("AJT:\n"); - Log::E("AJT: eigenR = | %10.5f %10.5f %10.5f |\n", eigenR(0,0), eigenR(0,1), eigenR(0,2)); - Log::E("AJT: | %10.5f %10.5f %10.5f |\n", eigenR(1,0), eigenR(1,1), eigenR(1,2)); - Log::E("AJT: | %10.5f %10.5f %10.5f |\n", eigenR(2,0), eigenR(2,1), eigenR(2,2)); - Log::E("AJT: eigenS = [%0.5f, %0.5f, %0.5f]\n", eigenS(0), eigenS(1), eigenS(2)); - Log::E("AJT:\n"); - Log::E("AJT: scaleOut = [%.5f, %.5f, %.5f]\n", scaleOut[0], scaleOut[1], scaleOut[2]); - Log::E("AJT: rotOut = [%.5f, %.5f, %.5f, %.5f]\n", rotOut.w, rotOut.x, rotOut.y, rotOut.z); - Log::E("AJT:\n"); - Log::E("AJT: V2 = | %10.5f, %10.5f, %10.5f |\n", V2[0][0], V2[1][0], V2[2][0]); - Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V2[0][1], V2[1][1], V2[2][1]); - Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V2[0][2], V2[1][2], V2[2][2]); + R[0] *= -1.0f; + R[1] *= -1.0f; + R[2] *= -1.0f; } - AJT_Count++; -#endif + rotOut = glm::normalize(glm::quat(R)); + // NOTE: scale is stored in log scale in ply file + // Also, the eigenVal is for (S*S^T) we want it for S, so + // take the sqrt of the eigenVal. (or equivalently 1/2 of the log of the eigenVal.) + scaleOut = glm::vec3(logf(eigenVal(0)) / 2.0f, + logf(eigenVal(1)) / 2.0f, + logf(eigenVal(2)) / 2.0f); } static float ComputeAlphaFromOpacity(float opacity) @@ -349,18 +317,6 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) gd[i].cov3_col2[0] = V[2][0]; gd[i].cov3_col2[1] = V[2][1]; gd[i].cov3_col2[2] = V[2][2]; - - // AJT: TODO REMOVE DEBUG - if (i == 0) - { - Log::E("AJT: ImportPly\n"); - Log::E("AJT: rot = [%.5f, %.5f, %.5f, %.5f]\n", rot[0], rot[1], rot[2], rot[3]); - Log::E("AJT: scale = [%.5f, %.5f, %.5f]\n", scale[0], scale[1], scale[2]); - Log::E("AJT: V = | %10.5f, %10.5f, %10.5f |\n", V[0][0], V[1][0], V[2][0]); - Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][1], V[1][1], V[2][1]); - Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][2], V[1][2], V[2][2]); - } - i++; }); } @@ -496,16 +452,6 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) props.rot[2].Write(plyData, rot.y); props.rot[3].Write(plyData, rot.z); - if (runningSize == 0) - { - Log::E("AJT: ExportPly\n"); - Log::E("AJT: V = | %10.5f, %10.5f, %10.5f |\n", V[0][0], V[1][0], V[2][0]); - Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][1], V[1][1], V[2][1]); - Log::E("AJT: | %10.5f, %10.5f, %10.5f |\n", V[0][2], V[1][2], V[2][2]); - Log::E("AJT: rot = [%.5f, %.5f, %.5f, %.5f]\n", rot.w, rot.x, rot.y, rot.z); - Log::E("AJT: scale = [%.5f, %.5f, %.5f]\n", scale[0], scale[1], scale[2]); - } - gData += gaussianSize; runningSize += gaussianSize; assert(runningSize <= GetTotalSize()); // bad, we went off the end of the data ptr From d4e7dba6ba67d8c5efc0b65f9c33772fb2a1ceff Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Jul 2024 14:53:10 -0700 Subject: [PATCH 15/15] Added --nosh option, to skip loading/rendering of spherical harmonics data This reduces the memory requirements for large scenes, and can give a performance boost as well. GaussianCloud will use BaseGaussianData or FullGaussianData depending on this option. This will intern reduce the gpu memory required for each vertex. The shaders also use the FULL_SH pre-processor symbol define to handle this option. --- src/app.cpp | 32 ++-- src/app.h | 3 +- src/gaussiancloud.cpp | 351 +++++++++++++++++++++++++----------------- src/gaussiancloud.h | 16 +- src/splatrenderer.cpp | 11 +- src/splatrenderer.h | 7 +- 6 files changed, 246 insertions(+), 174 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 2998251..c8178a8 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -52,7 +52,8 @@ enum optionIndex DEBUG, HELP, FP16, - FP32 + FP32, + NOSH, }; const option::Descriptor usage[] = @@ -64,6 +65,7 @@ const option::Descriptor usage[] = { DEBUG, 0, "d", "debug", option::Arg::None, " -d, --debug Enable verbose debug logging." }, { FP16, 0, "", "fp16", option::Arg::None, " --fp16 Use 16-bit half-precision floating frame buffer, to reduce color banding artifacts" }, { FP32, 0, "", "fp32", option::Arg::None, " --fp32 Use 32-bit floating point frame buffer, to reduce color banding even more" }, + { NOSH, 0, "", "nosh", option::Arg::None, " --nosh Don't load/render full sh, this will reduce memory usage and higher performance" }, { UNKNOWN, 0, "", "", option::Arg::None, "\nExamples:\n splataplut data/test.ply\n splatapult -v data/test.ply" }, { 0, 0, 0, 0, 0, 0} }; @@ -159,13 +161,6 @@ static void Clear(glm::ivec2 windowSize, bool setViewport = true) // NOTE: if depth buffer has less then 24 bits, it can mess up splat rendering. glEnable(GL_DEPTH_TEST); - -#ifndef __ANDROID__ - // AJT: ANDROID: TODO: implement this in fragment shader, for OpenGLES I guess. - // enable alpha test - //glEnable(GL_ALPHA_TEST); - //glAlphaFunc(GL_GREATER, 1.0f / 256.0f); -#endif } // Draw a textured quad over the entire screen. @@ -228,10 +223,17 @@ static std::shared_ptr LoadPointCloud(const std::string& plyFilename return pointCloud; } -static std::shared_ptr LoadGaussianCloud(const std::string& plyFilename, bool useLinearColors) +static std::shared_ptr LoadGaussianCloud(const std::string& plyFilename, const App::Options& opt) { - auto gaussianCloud = std::make_shared(useLinearColors); - + GaussianCloud::Options options = {0}; +#ifdef __ANDROID__ + options.importFullSH = false; + options.exportFullSH = false; +#else + options.importFullSH = opt.importFullSH; + options.exportFullSH = true; +#endif + auto gaussianCloud = std::make_shared(options); if (!gaussianCloud->ImportPly(plyFilename)) { Log::E("Error loading GaussianCloud!\n"); @@ -330,6 +332,8 @@ App::ParseResult App::ParseArguments(int argc, const char* argv[]) opt.frameBuffer = Options::FrameBuffer::HalfFloat; } + opt.importFullSH = options[NOSH] ? false : true; + bool unknownOptionFound = false; for (option::Option* opt = options[UNKNOWN]; opt; opt = opt->next()) { @@ -528,7 +532,7 @@ bool App::Init() Log::D("Could not find input.ply\n"); } - gaussianCloud = LoadGaussianCloud(plyFilename, isFramebufferSRGBEnabled); + gaussianCloud = LoadGaussianCloud(plyFilename, opt); if (!gaussianCloud) { Log::E("Error loading GaussianCloud\n"); @@ -544,13 +548,11 @@ bool App::Init() splatRenderer = std::make_shared(); #if __ANDROID__ - bool useFullSH = false; bool useRgcSortOverride = true; #else - bool useFullSH = true; bool useRgcSortOverride = false; #endif - if (!splatRenderer->Init(gaussianCloud, isFramebufferSRGBEnabled, useFullSH, useRgcSortOverride)) + if (!splatRenderer->Init(gaussianCloud, isFramebufferSRGBEnabled, useRgcSortOverride)) { Log::E("Error initializing splat renderer!\n"); return false; diff --git a/src/app.h b/src/app.h index 707bb43..dddd796 100644 --- a/src/app.h +++ b/src/app.h @@ -57,7 +57,6 @@ class App using ResizeCallback = std::function; void OnResize(const ResizeCallback& cb); -protected: struct Options { enum class FrameBuffer @@ -76,8 +75,10 @@ class App bool drawFps = true; bool drawCameraFrustums = false; bool drawCameraPath = false; + bool importFullSH = true; }; +protected: MainContext& mainContext; Options opt; std::string plyFilename; diff --git a/src/gaussiancloud.cpp b/src/gaussiancloud.cpp index d46ab24..58eac0b 100644 --- a/src/gaussiancloud.cpp +++ b/src/gaussiancloud.cpp @@ -29,25 +29,30 @@ #include "ply.h" -struct GaussianData +struct BaseGaussianData { - GaussianData() noexcept {} + BaseGaussianData() noexcept {} float posWithAlpha[4]; // center of the gaussian in object coordinates, with alpha in w float r_sh0[4]; // sh coeff for red channel (up to third-order) + float g_sh0[4]; // sh coeff for green channel + float b_sh0[4]; // sh coeff for blue channel + float cov3_col0[3]; // 3x3 covariance matrix of the splat in object coordinates. + float cov3_col1[3]; + float cov3_col2[3]; +}; + +struct FullGaussianData : public BaseGaussianData +{ + FullGaussianData() noexcept {} float r_sh1[4]; float r_sh2[4]; float r_sh3[4]; - float g_sh0[4]; // sh coeff for green channel float g_sh1[4]; float g_sh2[4]; float g_sh3[4]; - float b_sh0[4]; // sh coeff for blue channel float b_sh1[4]; float b_sh2[4]; float b_sh3[4]; - float cov3_col0[3]; // 3x3 covariance matrix of the splat in object coordinates. - float cov3_col1[3]; - float cov3_col2[3]; }; // Function to convert glm::mat3 to Eigen::Matrix3f @@ -82,10 +87,9 @@ static glm::mat3 ComputeCovMatFromRotScale(float rot[4], float scale[3]) { glm::quat q(rot[0], rot[1], rot[2], rot[3]); glm::mat3 R(glm::normalize(q)); - // NOTE: scale is stored in log scale in ply file - glm::mat3 S(glm::vec3(expf(scale[0]), 0.0f, 0.0f), - glm::vec3(0.0f, expf(scale[1]), 0.0f), - glm::vec3(0.0f, 0.0f, expf(scale[2]))); + glm::mat3 S(glm::vec3(scale[0], 0.0f, 0.0f), + glm::vec3(0.0f, scale[1], 0.0f), + glm::vec3(0.0f, 0.0f, scale[2])); return R * S * glm::transpose(S) * glm::transpose(R); } @@ -108,12 +112,8 @@ static void ComputeRotScaleFromCovMat(const glm::mat3& V, glm::quat& rotOut, glm R[2] *= -1.0f; } rotOut = glm::normalize(glm::quat(R)); - // NOTE: scale is stored in log scale in ply file - // Also, the eigenVal is for (S*S^T) we want it for S, so - // take the sqrt of the eigenVal. (or equivalently 1/2 of the log of the eigenVal.) - scaleOut = glm::vec3(logf(eigenVal(0)) / 2.0f, - logf(eigenVal(1)) / 2.0f, - logf(eigenVal(2)) / 2.0f); + // The eigenVal gives us the diagonal of (S*S^T), so take the sqrt to give is S. + scaleOut = glm::vec3(sqrtf(eigenVal(0)), sqrtf(eigenVal(1)), sqrtf(eigenVal(2))); } static float ComputeAlphaFromOpacity(float opacity) @@ -126,10 +126,11 @@ static float ComputeOpacityFromAlpha(float alpha) return -logf((1.0f / alpha) - 1.0f); } -GaussianCloud::GaussianCloud(bool useLinearColorsIn) : +GaussianCloud::GaussianCloud(const Options& options) : numGaussians(0), gaussianSize(0), - useLinearColors(useLinearColorsIn) + opt(options), + hasFullSH(false) { ; } @@ -184,15 +185,24 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) } } - for (int i = 0; i < 45; i++) + if (opt.importFullSH) { - if (!ply.GetProperty("f_rest_" + std::to_string(i), props.f_rest[i])) + hasFullSH = true; + for (int i = 0; i < 45; i++) { - // f_rest properties are optional - Log::W("PLY file \"%s\", missing f_rest property\n", plyFilename.c_str()); - break; + if (!ply.GetProperty("f_rest_" + std::to_string(i), props.f_rest[i])) + { + // f_rest properties are optional + Log::W("PLY file \"%s\", missing f_rest property\n", plyFilename.c_str()); + hasFullSH = false; + break; + } } } + else + { + hasFullSH = false; + } if (!ply.GetProperty("opacity", props.opacity)) { @@ -217,114 +227,144 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename) } - GaussianData* gd; + InitAttribs(); + { ZoneScopedNC("alloc data", tracy::Color::Red4); - // AJT: TODO support with and without sh. - numGaussians = ply.GetVertexCount(); - gaussianSize = sizeof(GaussianData); - InitAttribs(); - gd = new GaussianData[numGaussians]; - data.reset(gd); + if (hasFullSH) + { + gaussianSize = sizeof(FullGaussianData); + FullGaussianData* fullPtr = new FullGaussianData[numGaussians]; + data.reset(fullPtr); + } + else + { + gaussianSize = sizeof(BaseGaussianData); + BaseGaussianData* basePtr = new BaseGaussianData[numGaussians]; + data.reset(basePtr); + } } { ZoneScopedNC("ply.ForEachVertex", tracy::Color::Blue); int i = 0; - ply.ForEachVertex([this, gd, &i, &props](const void* data, size_t size) + uint8_t* rawPtr = (uint8_t*)data.get(); + ply.ForEachVertex([this, &rawPtr, &i, &props](const void* plyData, size_t size) { - gd[i].posWithAlpha[0] = props.x.Read(data); - gd[i].posWithAlpha[1] = props.y.Read(data); - gd[i].posWithAlpha[2] = props.z.Read(data); - gd[i].posWithAlpha[3] = ComputeAlphaFromOpacity(props.opacity.Read(data)); - - // AJT: TODO check for useLinearColors - - gd[i].r_sh0[0] = props.f_dc[0].Read(data); - gd[i].r_sh0[1] = props.f_rest[0].Read(data); - gd[i].r_sh0[2] = props.f_rest[1].Read(data); - gd[i].r_sh0[3] = props.f_rest[2].Read(data); - gd[i].r_sh1[0] = props.f_rest[3].Read(data); - gd[i].r_sh1[1] = props.f_rest[4].Read(data); - gd[i].r_sh1[2] = props.f_rest[5].Read(data); - gd[i].r_sh1[3] = props.f_rest[6].Read(data); - gd[i].r_sh2[0] = props.f_rest[7].Read(data); - gd[i].r_sh2[1] = props.f_rest[8].Read(data); - gd[i].r_sh2[2] = props.f_rest[9].Read(data); - gd[i].r_sh2[3] = props.f_rest[10].Read(data); - gd[i].r_sh3[0] = props.f_rest[11].Read(data); - gd[i].r_sh3[1] = props.f_rest[12].Read(data); - gd[i].r_sh3[2] = props.f_rest[13].Read(data); - gd[i].r_sh3[3] = props.f_rest[14].Read(data); - - gd[i].g_sh0[0] = props.f_dc[1].Read(data); - gd[i].g_sh0[1] = props.f_rest[15].Read(data); - gd[i].g_sh0[2] = props.f_rest[16].Read(data); - gd[i].g_sh0[3] = props.f_rest[17].Read(data); - gd[i].g_sh1[0] = props.f_rest[18].Read(data); - gd[i].g_sh1[1] = props.f_rest[19].Read(data); - gd[i].g_sh1[2] = props.f_rest[20].Read(data); - gd[i].g_sh1[3] = props.f_rest[21].Read(data); - gd[i].g_sh2[0] = props.f_rest[22].Read(data); - gd[i].g_sh2[1] = props.f_rest[23].Read(data); - gd[i].g_sh2[2] = props.f_rest[24].Read(data); - gd[i].g_sh2[3] = props.f_rest[25].Read(data); - gd[i].g_sh3[0] = props.f_rest[26].Read(data); - gd[i].g_sh3[1] = props.f_rest[27].Read(data); - gd[i].g_sh3[2] = props.f_rest[28].Read(data); - gd[i].g_sh3[3] = props.f_rest[29].Read(data); - - gd[i].b_sh0[0] = props.f_dc[2].Read(data); - gd[i].b_sh0[1] = props.f_rest[30].Read(data); - gd[i].b_sh0[2] = props.f_rest[31].Read(data); - gd[i].b_sh0[3] = props.f_rest[32].Read(data); - gd[i].b_sh1[0] = props.f_rest[33].Read(data); - gd[i].b_sh1[1] = props.f_rest[34].Read(data); - gd[i].b_sh1[2] = props.f_rest[35].Read(data); - gd[i].b_sh1[3] = props.f_rest[36].Read(data); - gd[i].b_sh2[0] = props.f_rest[37].Read(data); - gd[i].b_sh2[1] = props.f_rest[38].Read(data); - gd[i].b_sh2[2] = props.f_rest[39].Read(data); - gd[i].b_sh2[3] = props.f_rest[40].Read(data); - gd[i].b_sh3[0] = props.f_rest[41].Read(data); - gd[i].b_sh3[1] = props.f_rest[42].Read(data); - gd[i].b_sh3[2] = props.f_rest[43].Read(data); - gd[i].b_sh3[3] = props.f_rest[44].Read(data); + BaseGaussianData* basePtr = reinterpret_cast(rawPtr); + basePtr->posWithAlpha[0] = props.x.Read(plyData); + basePtr->posWithAlpha[1] = props.y.Read(plyData); + basePtr->posWithAlpha[2] = props.z.Read(plyData); + basePtr->posWithAlpha[3] = ComputeAlphaFromOpacity(props.opacity.Read(plyData)); + if (hasFullSH) + { + FullGaussianData* fullPtr = reinterpret_cast(rawPtr); + fullPtr->r_sh0[0] = props.f_dc[0].Read(plyData); + fullPtr->r_sh0[1] = props.f_rest[0].Read(plyData); + fullPtr->r_sh0[2] = props.f_rest[1].Read(plyData); + fullPtr->r_sh0[3] = props.f_rest[2].Read(plyData); + fullPtr->r_sh1[0] = props.f_rest[3].Read(plyData); + fullPtr->r_sh1[1] = props.f_rest[4].Read(plyData); + fullPtr->r_sh1[2] = props.f_rest[5].Read(plyData); + fullPtr->r_sh1[3] = props.f_rest[6].Read(plyData); + fullPtr->r_sh2[0] = props.f_rest[7].Read(plyData); + fullPtr->r_sh2[1] = props.f_rest[8].Read(plyData); + fullPtr->r_sh2[2] = props.f_rest[9].Read(plyData); + fullPtr->r_sh2[3] = props.f_rest[10].Read(plyData); + fullPtr->r_sh3[0] = props.f_rest[11].Read(plyData); + fullPtr->r_sh3[1] = props.f_rest[12].Read(plyData); + fullPtr->r_sh3[2] = props.f_rest[13].Read(plyData); + fullPtr->r_sh3[3] = props.f_rest[14].Read(plyData); + + fullPtr->g_sh0[0] = props.f_dc[1].Read(plyData); + fullPtr->g_sh0[1] = props.f_rest[15].Read(plyData); + fullPtr->g_sh0[2] = props.f_rest[16].Read(plyData); + fullPtr->g_sh0[3] = props.f_rest[17].Read(plyData); + fullPtr->g_sh1[0] = props.f_rest[18].Read(plyData); + fullPtr->g_sh1[1] = props.f_rest[19].Read(plyData); + fullPtr->g_sh1[2] = props.f_rest[20].Read(plyData); + fullPtr->g_sh1[3] = props.f_rest[21].Read(plyData); + fullPtr->g_sh2[0] = props.f_rest[22].Read(plyData); + fullPtr->g_sh2[1] = props.f_rest[23].Read(plyData); + fullPtr->g_sh2[2] = props.f_rest[24].Read(plyData); + fullPtr->g_sh2[3] = props.f_rest[25].Read(plyData); + fullPtr->g_sh3[0] = props.f_rest[26].Read(plyData); + fullPtr->g_sh3[1] = props.f_rest[27].Read(plyData); + fullPtr->g_sh3[2] = props.f_rest[28].Read(plyData); + fullPtr->g_sh3[3] = props.f_rest[29].Read(plyData); + + fullPtr->b_sh0[0] = props.f_dc[2].Read(plyData); + fullPtr->b_sh0[1] = props.f_rest[30].Read(plyData); + fullPtr->b_sh0[2] = props.f_rest[31].Read(plyData); + fullPtr->b_sh0[3] = props.f_rest[32].Read(plyData); + fullPtr->b_sh1[0] = props.f_rest[33].Read(plyData); + fullPtr->b_sh1[1] = props.f_rest[34].Read(plyData); + fullPtr->b_sh1[2] = props.f_rest[35].Read(plyData); + fullPtr->b_sh1[3] = props.f_rest[36].Read(plyData); + fullPtr->b_sh2[0] = props.f_rest[37].Read(plyData); + fullPtr->b_sh2[1] = props.f_rest[38].Read(plyData); + fullPtr->b_sh2[2] = props.f_rest[39].Read(plyData); + fullPtr->b_sh2[3] = props.f_rest[40].Read(plyData); + fullPtr->b_sh3[0] = props.f_rest[41].Read(plyData); + fullPtr->b_sh3[1] = props.f_rest[42].Read(plyData); + fullPtr->b_sh3[2] = props.f_rest[43].Read(plyData); + fullPtr->b_sh3[3] = props.f_rest[44].Read(plyData); + } + else + { + basePtr->r_sh0[0] = props.f_dc[0].Read(plyData); + basePtr->r_sh0[1] = 0.0f; + basePtr->r_sh0[2] = 0.0f; + basePtr->r_sh0[3] = 0.0f; + + basePtr->g_sh0[0] = props.f_dc[1].Read(plyData); + basePtr->g_sh0[1] = 0.0f; + basePtr->g_sh0[2] = 0.0f; + basePtr->g_sh0[3] = 0.0f; + + basePtr->b_sh0[0] = props.f_dc[2].Read(plyData); + basePtr->b_sh0[1] = 0.0f; + basePtr->b_sh0[2] = 0.0f; + basePtr->b_sh0[3] = 0.0f; + } + + // NOTE: scale is stored in logarithmic scale in plyFile float scale[3] = { - props.scale[0].Read(data), - props.scale[1].Read(data), - props.scale[2].Read(data) + expf(props.scale[0].Read(plyData)), + expf(props.scale[1].Read(plyData)), + expf(props.scale[2].Read(plyData)) }; float rot[4] = { - props.rot[0].Read(data), - props.rot[1].Read(data), - props.rot[2].Read(data), - props.rot[3].Read(data) + props.rot[0].Read(plyData), + props.rot[1].Read(plyData), + props.rot[2].Read(plyData), + props.rot[3].Read(plyData) }; glm::mat3 V = ComputeCovMatFromRotScale(rot, scale); - gd[i].cov3_col0[0] = V[0][0]; - gd[i].cov3_col0[1] = V[0][1]; - gd[i].cov3_col0[2] = V[0][2]; - gd[i].cov3_col1[0] = V[1][0]; - gd[i].cov3_col1[1] = V[1][1]; - gd[i].cov3_col1[2] = V[1][2]; - gd[i].cov3_col2[0] = V[2][0]; - gd[i].cov3_col2[1] = V[2][1]; - gd[i].cov3_col2[2] = V[2][2]; + basePtr->cov3_col0[0] = V[0][0]; + basePtr->cov3_col0[1] = V[0][1]; + basePtr->cov3_col0[2] = V[0][2]; + basePtr->cov3_col1[0] = V[1][0]; + basePtr->cov3_col1[1] = V[1][1]; + basePtr->cov3_col1[2] = V[1][2]; + basePtr->cov3_col2[0] = V[2][0]; + basePtr->cov3_col2[1] = V[2][1]; + basePtr->cov3_col2[2] = V[2][2]; i++; + rawPtr += gaussianSize; }); } return true; } -bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) const +bool GaussianCloud::ExportPly(const std::string& plyFilename) const { std::ofstream plyFile(plyFilename, std::ios::binary); if (!plyFile.is_open()) @@ -343,7 +383,7 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) ply.AddProperty("f_dc_0", BinaryAttribute::Type::Float); ply.AddProperty("f_dc_1", BinaryAttribute::Type::Float); ply.AddProperty("f_dc_2", BinaryAttribute::Type::Float); - if (exportFullSh) + if (opt.exportFullSH) { for (int i = 0; i < 45; i++) { @@ -379,7 +419,7 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) ply.GetProperty("f_dc_0", props.f_dc[0]); ply.GetProperty("f_dc_1", props.f_dc[1]); ply.GetProperty("f_dc_2", props.f_dc[2]); - if (exportFullSh) + if (opt.exportFullSH) { for (int i = 0; i < 45; i++) { @@ -399,7 +439,7 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) uint8_t* gData = (uint8_t*)data.get(); size_t runningSize = 0; - ply.ForEachVertexMut([this, &props, &gData, &runningSize, &exportFullSh](void* plyData, size_t size) + ply.ForEachVertexMut([this, &props, &gData, &runningSize](void* plyData, size_t size) { const float* posWithAlpha = posWithAlphaAttrib.Get(gData); const float* r_sh0 = r_sh0Attrib.Get(gData); @@ -417,7 +457,7 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) props.f_dc[1].Write(plyData, g_sh0[0]); props.f_dc[2].Write(plyData, b_sh0[0]); - if (exportFullSh) + if (opt.exportFullSH) { // TODO: maybe just a raw memcopy would be faster for (int i = 0; i < 15; i++) @@ -444,9 +484,9 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename, bool exportFullSh) glm::vec3 scale; ComputeRotScaleFromCovMat(V, rot, scale); - props.scale[0].Write(plyData, scale.x); - props.scale[1].Write(plyData, scale.y); - props.scale[2].Write(plyData, scale.z); + props.scale[0].Write(plyData, logf(scale.x)); + props.scale[1].Write(plyData, logf(scale.y)); + props.scale[2].Write(plyData, logf(scale.z)); props.rot[0].Write(plyData, rot.w); props.rot[1].Write(plyData, rot.x); props.rot[2].Write(plyData, rot.y); @@ -467,9 +507,9 @@ void GaussianCloud::InitDebugCloud() const int NUM_SPLATS = 5; numGaussians = NUM_SPLATS * 3 + 1; - gaussianSize = sizeof(GaussianData); + gaussianSize = sizeof(FullGaussianData); InitAttribs(); - GaussianData* gd = new GaussianData[numGaussians]; + FullGaussianData* gd = new FullGaussianData[numGaussians]; data.reset(gd); // @@ -485,8 +525,8 @@ void GaussianCloud::InitDebugCloud() // x axis for (int i = 0; i < NUM_SPLATS; i++) { - GaussianData g; - memset(&g, 0, sizeof(GaussianData)); + FullGaussianData g; + memset(&g, 0, sizeof(FullGaussianData)); g.posWithAlpha[0] = i * DELTA + DELTA; g.posWithAlpha[1] = 0.0f; g.posWithAlpha[2] = 0.0f; @@ -499,8 +539,8 @@ void GaussianCloud::InitDebugCloud() // y axis for (int i = 0; i < NUM_SPLATS; i++) { - GaussianData g; - memset(&g, 0, sizeof(GaussianData)); + FullGaussianData g; + memset(&g, 0, sizeof(FullGaussianData)); g.posWithAlpha[0] = 0.0f; g.posWithAlpha[1] = i * DELTA + DELTA; g.posWithAlpha[2] = 0.0f; @@ -513,8 +553,8 @@ void GaussianCloud::InitDebugCloud() // z axis for (int i = 0; i < NUM_SPLATS; i++) { - GaussianData g; - memset(&g, 0, sizeof(GaussianData)); + FullGaussianData g; + memset(&g, 0, sizeof(FullGaussianData)); g.posWithAlpha[0] = 0.0f; g.posWithAlpha[1] = 0.0f; g.posWithAlpha[2] = i * DELTA + DELTA + 0.0001f; // AJT: HACK prevent div by zero for debug-shaders @@ -525,8 +565,8 @@ void GaussianCloud::InitDebugCloud() gd[(NUM_SPLATS * 2) + i] = g; } - GaussianData g; - memset(&g, 0, sizeof(GaussianData)); + FullGaussianData g; + memset(&g, 0, sizeof(FullGaussianData)); g.posWithAlpha[0] = 0.0f; g.posWithAlpha[1] = 0.0f; g.posWithAlpha[2] = 0.0f; @@ -545,14 +585,16 @@ void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats) return; } - GaussianData* gd = (GaussianData*)data.get(); using IndexDistPair = std::pair; std::vector indexDistVec; indexDistVec.reserve(numGaussians); + uint8_t* rawPtr = (uint8_t*)data.get(); for (uint32_t i = 0; i < numGaussians; i++) { - glm::vec3 pos(gd[i].posWithAlpha[0], gd[i].posWithAlpha[1], gd[i].posWithAlpha[2]); + BaseGaussianData* basePtr = reinterpret_cast(rawPtr); + glm::vec3 pos(basePtr->posWithAlpha[0], basePtr->posWithAlpha[1], basePtr->posWithAlpha[2]); indexDistVec.push_back(IndexDistPair(i, glm::distance(origin, pos))); + rawPtr += gaussianSize; } std::sort(indexDistVec.begin(), indexDistVec.end(), [](const IndexDistPair& a, const IndexDistPair& b) @@ -560,14 +602,27 @@ void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats) return a.second < b.second; }); - GaussianData* gd2 = new GaussianData[numSplats]; + uint8_t* newData; + if (hasFullSH) + { + FullGaussianData* fullPtr = new FullGaussianData[numSplats]; + newData = (uint8_t*)fullPtr; + } + else + { + BaseGaussianData* basePtr = new BaseGaussianData[numSplats]; + newData = (uint8_t*)basePtr; + } + rawPtr = (uint8_t*)data.get(); + uint8_t* rawPtr2 = newData; + for (uint32_t i = 0; i < numSplats; i++) { - gd2[i] = gd[indexDistVec[i].first]; + memcpy(rawPtr2, rawPtr + indexDistVec[i].first * gaussianSize, gaussianSize); + rawPtr2 += gaussianSize; } numGaussians = numSplats; - data.reset(gd2); - gd = nullptr; + data.reset(newData); } void GaussianCloud::ForEachPosWithAlpha(const ForEachPosWithAlphaCallback& cb) const @@ -577,20 +632,26 @@ void GaussianCloud::ForEachPosWithAlpha(const ForEachPosWithAlphaCallback& cb) c void GaussianCloud::InitAttribs() { - posWithAlphaAttrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, posWithAlpha)}; - r_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, r_sh0)}; - r_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, r_sh1)}; - r_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, r_sh2)}; - r_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, r_sh3)}; - g_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, g_sh0)}; - g_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, g_sh1)}; - g_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, g_sh2)}; - g_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, g_sh3)}; - b_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, b_sh0)}; - b_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, b_sh1)}; - b_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, b_sh2)}; - b_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, b_sh3)}; - cov3_col0Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, cov3_col0)}; - cov3_col1Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, cov3_col1)}; - cov3_col2Attrib = {BinaryAttribute::Type::Float, offsetof(GaussianData, cov3_col2)}; + // BaseGaussianData attribs + posWithAlphaAttrib = {BinaryAttribute::Type::Float, offsetof(BaseGaussianData, posWithAlpha)}; + r_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(BaseGaussianData, r_sh0)}; + g_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(BaseGaussianData, g_sh0)}; + b_sh0Attrib = {BinaryAttribute::Type::Float, offsetof(BaseGaussianData, b_sh0)}; + cov3_col0Attrib = {BinaryAttribute::Type::Float, offsetof(BaseGaussianData, cov3_col0)}; + cov3_col1Attrib = {BinaryAttribute::Type::Float, offsetof(BaseGaussianData, cov3_col1)}; + cov3_col2Attrib = {BinaryAttribute::Type::Float, offsetof(BaseGaussianData, cov3_col2)}; + + // FullGaussianData attribs + if (hasFullSH) + { + r_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, r_sh1)}; + r_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, r_sh2)}; + r_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, r_sh3)}; + g_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, g_sh1)}; + g_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, g_sh2)}; + g_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, g_sh3)}; + b_sh1Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, b_sh1)}; + b_sh2Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, b_sh2)}; + b_sh3Attrib = {BinaryAttribute::Type::Float, offsetof(FullGaussianData, b_sh3)}; + } } diff --git a/src/gaussiancloud.h b/src/gaussiancloud.h index c37980b..54643af 100644 --- a/src/gaussiancloud.h +++ b/src/gaussiancloud.h @@ -17,10 +17,16 @@ class GaussianCloud { public: - GaussianCloud(bool useLinearColorsIn); + struct Options + { + bool importFullSH; + bool exportFullSH; + }; + + GaussianCloud(const Options& options); bool ImportPly(const std::string& plyFilename); - bool ExportPly(const std::string& plyFilename, bool exportFullSh) const; + bool ExportPly(const std::string& plyFilename) const; void InitDebugCloud(); @@ -53,6 +59,8 @@ class GaussianCloud using ForEachPosWithAlphaCallback = std::function; void ForEachPosWithAlpha(const ForEachPosWithAlphaCallback& cb) const; + bool HasFullSH() const { return hasFullSH; } + protected: void InitAttribs(); @@ -77,5 +85,7 @@ class GaussianCloud size_t numGaussians; size_t gaussianSize; - bool useLinearColors; + + Options opt; + bool hasFullSH; }; diff --git a/src/splatrenderer.cpp b/src/splatrenderer.cpp index 600a95c..028b107 100644 --- a/src/splatrenderer.cpp +++ b/src/splatrenderer.cpp @@ -47,25 +47,24 @@ SplatRenderer::~SplatRenderer() { } -bool SplatRenderer::Init(std::shared_ptr gaussianCloud, bool isFramebufferSRGBEnabledIn, - bool useFullSHIn, bool useRgcSortOverrideIn) +bool SplatRenderer::Init(std::shared_ptr gaussianCloud, + bool isFramebufferSRGBEnabledIn, bool useRgcSortOverrideIn) { ZoneScopedNC("SplatRenderer::Init()", tracy::Color::Blue); GL_ERROR_CHECK("SplatRenderer::Init() begin"); isFramebufferSRGBEnabled = isFramebufferSRGBEnabledIn; - useFullSH = useFullSHIn; useRgcSortOverride = useRgcSortOverrideIn; splatProg = std::make_shared(); - if (isFramebufferSRGBEnabled || useFullSH) + if (isFramebufferSRGBEnabled || gaussianCloud->HasFullSH()) { std::string defines = ""; if (isFramebufferSRGBEnabled) { defines += "#define FRAMEBUFFER_SRGB\n"; } - if (useFullSH) + if (gaussianCloud->HasFullSH()) { defines += "#define FULL_SH\n"; } @@ -371,7 +370,7 @@ void SplatRenderer::BuildVertexArrayObject(std::shared_ptr gaussi SetupAttrib(splatProg->GetAttribLoc("r_sh0"), gaussianCloud->GetR_SH0Attrib(), 4, stride); SetupAttrib(splatProg->GetAttribLoc("g_sh0"), gaussianCloud->GetG_SH0Attrib(), 4, stride); SetupAttrib(splatProg->GetAttribLoc("b_sh0"), gaussianCloud->GetB_SH0Attrib(), 4, stride); - if (useFullSH) + if (gaussianCloud->HasFullSH()) { SetupAttrib(splatProg->GetAttribLoc("r_sh1"), gaussianCloud->GetR_SH1Attrib(), 4, stride); SetupAttrib(splatProg->GetAttribLoc("r_sh2"), gaussianCloud->GetR_SH2Attrib(), 4, stride); diff --git a/src/splatrenderer.h b/src/splatrenderer.h index 71cf3bc..da501c6 100644 --- a/src/splatrenderer.h +++ b/src/splatrenderer.h @@ -26,11 +26,11 @@ class SplatRenderer SplatRenderer(); ~SplatRenderer(); - bool Init(std::shared_ptr gaussianCloud, bool isFramebufferSRGBEnabledIn, - bool useFullSHIn, bool useRgcSortOverrideIn); + bool Init(std::shared_ptr gaussianCloud, + bool isFramebufferSRGBEnabledIn, bool useRgcSortOverrideIn); void Sort(const glm::mat4& cameraMat, const glm::mat4& projMat, - const glm::vec4& viewport, const glm::vec2& nearFar); + const glm::vec4& viewport, const glm::vec2& nearFar); // viewport = (x, y, width, height) void Render(const glm::mat4& cameraMat, const glm::mat4& projMat, @@ -63,6 +63,5 @@ class SplatRenderer uint32_t sortCount; bool isFramebufferSRGBEnabled; - bool useFullSH; bool useRgcSortOverride; };