Skip to content

Commit

Permalink
PointCloud refactor
Browse files Browse the repository at this point in the history
The point here is to hide the actual structure of the point cloud data,
specifically PointData and the std::vector<PointData>.
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.
  • Loading branch information
hyperlogic committed Jun 26, 2024
1 parent a325912 commit 13cb609
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 53 deletions.
98 changes: 64 additions & 34 deletions src/pointcloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
;
Expand Down Expand Up @@ -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<double>(data));
pointDataVec[i].position[1] = SRGBToLinear((float)props.y.Get<double>(data));
pointDataVec[i].position[2] = SRGBToLinear((float)props.z.Get<double>(data));
pointDataPtr[i].position[0] = SRGBToLinear((float)props.x.Get<double>(data));
pointDataPtr[i].position[1] = SRGBToLinear((float)props.y.Get<double>(data));
pointDataPtr[i].position[2] = SRGBToLinear((float)props.z.Get<double>(data));
}
else
{
pointDataVec[i].position[0] = (float)props.x.Get<double>(data);
pointDataVec[i].position[1] = (float)props.y.Get<double>(data);
pointDataVec[i].position[2] = (float)props.z.Get<double>(data);
pointDataPtr[i].position[0] = (float)props.x.Get<double>(data);
pointDataPtr[i].position[1] = (float)props.y.Get<double>(data);
pointDataPtr[i].position[2] = (float)props.z.Get<double>(data);
}
pointDataVec[i].position[3] = 1.0f;
pointDataVec[i].color[0] = (float)props.red.Get<uint8_t>(data) / 255.0f;
pointDataVec[i].color[1] = (float)props.green.Get<uint8_t>(data) / 255.0f;
pointDataVec[i].color[2] = (float)props.blue.Get<uint8_t>(data) / 255.0f;
pointDataVec[i].color[3] = 1.0f;
pointDataPtr[i].position[3] = 1.0f;
pointDataPtr[i].color[0] = (float)props.red.Get<uint8_t>(data) / 255.0f;
pointDataPtr[i].color[1] = (float)props.green.Get<uint8_t>(data) / 255.0f;
pointDataPtr[i].color[2] = (float)props.blue.Get<uint8_t>(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<float>(data));
pointDataVec[i].position[1] = SRGBToLinear(props.y.Get<float>(data));
pointDataVec[i].position[2] = SRGBToLinear(props.z.Get<float>(data));
pointDataPtr[i].position[0] = SRGBToLinear(props.x.Get<float>(data));
pointDataPtr[i].position[1] = SRGBToLinear(props.y.Get<float>(data));
pointDataPtr[i].position[2] = SRGBToLinear(props.z.Get<float>(data));
}
else
{
pointDataVec[i].position[0] = props.x.Get<float>(data);
pointDataVec[i].position[1] = props.y.Get<float>(data);
pointDataVec[i].position[2] = props.z.Get<float>(data);
pointDataPtr[i].position[0] = props.x.Get<float>(data);
pointDataPtr[i].position[1] = props.y.Get<float>(data);
pointDataPtr[i].position[2] = props.z.Get<float>(data);
}
pointDataVec[i].position[3] = 1.0f;
pointDataVec[i].color[0] = (float)props.red.Get<uint8_t>(data) / 255.0f;
pointDataVec[i].color[1] = (float)props.green.Get<uint8_t>(data) / 255.0f;
pointDataVec[i].color[2] = (float)props.blue.Get<uint8_t>(data) / 255.0f;
pointDataVec[i].color[2] = 1.0f;
pointDataPtr[i].position[3] = 1.0f;
pointDataPtr[i].color[0] = (float)props.red.Get<uint8_t>(data) / 255.0f;
pointDataPtr[i].color[1] = (float)props.green.Get<uint8_t>(data) / 255.0f;
pointDataPtr[i].color[2] = (float)props.blue.Get<uint8_t>(data) / 255.0f;
pointDataPtr[i].color[3] = 1.0f;
i++;
});
}
Expand All @@ -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";
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
}
33 changes: 23 additions & 10 deletions src/pointcloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

#pragma once

#include <string>
#include <vector>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>

class PointCloud
{
Expand All @@ -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<PointData>& GetPointDataVec() const { return pointDataVec; }
std::vector<PointData>& 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(const void*)>;
void ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const;

protected:
std::vector<PointData> pointDataVec;
std::shared_ptr<void> data;
AttribData positionAttrib;
AttribData colorAttrib;

size_t numPoints;
size_t pointSize;
bool useLinearColors;
};
23 changes: 14 additions & 9 deletions src/pointrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,14 @@ bool PointRenderer::Init(std::shared_ptr<PointCloud> 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);

Expand Down Expand Up @@ -194,9 +196,8 @@ void PointRenderer::BuildVertexArrayObject(std::shared_ptr<PointCloud> 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<BufferObject>(GL_ARRAY_BUFFER, pointDataPtr, pointDataSize, 0);
pointDataBuffer = std::make_shared<BufferObject>(GL_ARRAY_BUFFER, pointCloud->GetRawDataPtr(),
pointCloud->GetTotalSize(), 0);

// build element array
indexVec.reserve(numPoints);
Expand All @@ -211,12 +212,16 @@ void PointRenderer::BuildVertexArrayObject(std::shared_ptr<PointCloud> 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);
}

0 comments on commit 13cb609

Please sign in to comment.