Page 1 of 1

Point Cloud Scene Node

Posted: Mon Oct 03, 2016 7:33 pm
by Wol101
Here is a quick implementation of a custom scene node to render point clouds where each point has a colour and a normal. I use it for data generated using 3D photogrammetry (e.g. with VisualSFM). I feed the data in as a structure array (x,y,z),(a,r,g,b),(nx,ny,nz) but it would be easy to adapt. I have also precalculated the bounding box but that is trivial to add if you want it done in the initialiser. All the work is done via drawVertexPrimitiveList. You use the code exactly like Tutorial 03 except that you have to call initializeVertices after creation.

PointCloudSceneNode.h

Code: Select all

#ifndef POINTCLOUDSCENENODE_H
#define POINTCLOUDSCENENODE_H
 
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuninitialized"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <irrlicht.h>
#pragma GCC diagnostic pop
 
class PointCloud;
 
class PointCloudSceneNode : public irr::scene::ISceneNode
{
public:
    PointCloudSceneNode(irr::scene::ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id);
    ~PointCloudSceneNode();
 
    virtual void OnRegisterSceneNode();
 
    virtual void render();
 
    virtual const irr::core::aabbox3d<irr::f32>& getBoundingBox() const;
 
    virtual irr::u32 getMaterialCount() const;
 
    virtual irr::video::SMaterial& getMaterial(irr::u32 i );
 
    // custom initialiser for this scene node
    void initializeVertices(PointCloud *pointCloud, irr::s32 maxPointsToRender);
 
protected:
    irr::core::aabbox3d<irr::f32> Box;
    irr::video::SMaterial Material;
    irr::video::S3DVertex *Vertices;
    irr::u32 *IndexList;
    irr::u32 NumVertices;
};
 
#endif // POINTCLOUDSCENENODE_H
 
PointCloudSceneNode.cpp

Code: Select all

#include "PointCloudSceneNode.h"
#include "PointCloud.h"
 
PointCloudSceneNode::PointCloudSceneNode(irr::scene::ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id)
    : irr::scene::ISceneNode(parent, mgr, id)
{
    Material.Wireframe = false;
    Material.Lighting = false;
    Material.PointCloud = true;
 
    Vertices = 0;
    IndexList = 0;
    NumVertices = 0;
}
 
PointCloudSceneNode::~PointCloudSceneNode()
{
    if (Vertices) delete [] Vertices;
    if (IndexList) delete [] IndexList;
}
 
void PointCloudSceneNode::OnRegisterSceneNode()
{
    if (IsVisible)
        SceneManager->registerNodeForRendering(this);
 
    ISceneNode::OnRegisterSceneNode();
}
 
void PointCloudSceneNode::render()
{
    irr::video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
    driver->setMaterial(Material);
    driver->setTransform(irr::video::ETS_WORLD, AbsoluteTransformation);
    driver->drawVertexPrimitiveList(Vertices, NumVertices, IndexList, NumVertices, irr::video::EVT_STANDARD, irr::scene::EPT_POINTS, irr::video::EIT_32BIT);
 
    // for debug purposes only:
    if (DebugDataVisible)
    {
        irr::video::SMaterial debug_mat;
        debug_mat.Lighting = false;
        debug_mat.AntiAliasing=0;
        driver->setMaterial(debug_mat);
        // show normals
        if (DebugDataVisible & irr::scene::EDS_NORMALS)
        {
            const irr::f32 debugNormalLength = SceneManager->getParameters()->getAttributeAsFloat(irr::scene::DEBUG_NORMAL_LENGTH);
            const irr::video::SColor debugNormalColor = SceneManager->getParameters()->getAttributeAsColor(irr::scene::DEBUG_NORMAL_COLOR);
            // draw normals
            for (irr::u32 i=0; i < NumVertices; ++i)
            {
                irr::core::vector3df normalizedNormal = Vertices[i].Normal;
                normalizedNormal.normalize();
                const irr::core::vector3df& pos = Vertices[i].Pos;
                driver->draw3DLine(pos, pos + (normalizedNormal * debugNormalLength), debugNormalColor);
            }
        }
 
        debug_mat.ZBuffer = irr::video::ECFN_DISABLED;
        debug_mat.Lighting = false;
        driver->setMaterial(debug_mat);
 
        if (DebugDataVisible & irr::scene::EDS_BBOX || DebugDataVisible & irr::scene::EDS_BBOX_BUFFERS)
            driver->draw3DBox(Box, irr::video::SColor(255,255,255,255));
    }
}
 
const irr::core::aabbox3d<irr::f32>& PointCloudSceneNode::getBoundingBox() const
{
    return Box;
}
 
irr::u32 PointCloudSceneNode::getMaterialCount() const
{
    return 1;
}
 
irr::video::SMaterial& PointCloudSceneNode::getMaterial(irr::u32 /* i */)
{
    return Material;
}
 
// this is the custom initiation function
 
void PointCloudSceneNode::initializeVertices(PointCloud *pointCloud, irr::s32 maxPointsToRender)
{
    if (pointCloud) // load up the buffer objects
    {
        irr::u32 stride;
        if (maxPointsToRender >= pointCloud->GetNPoints())
        {
            stride = 1;
            NumVertices = pointCloud->GetNPoints();
        }
        else
        {
            stride = pointCloud->GetNPoints() / maxPointsToRender;
            if (pointCloud->GetNPoints() % maxPointsToRender) stride++;
            NumVertices = pointCloud->GetNPoints() / stride;
        }
 
        if (Vertices) delete [] Vertices;
        Vertices = new irr::video::S3DVertex[NumVertices];
        if (IndexList) delete [] IndexList;
        IndexList = new irr::u32[NumVertices];
 
        irr::video::S3DVertex v;
        irr::u32 i, j;
        const Point *pointList = pointCloud->GetPointList();
        for (j = 0; j < NumVertices; j++)
        {
            i = j * stride;
            v.Pos.set(pointList[i].x, pointList[i].y, pointList[i].z);
            v.Color.set(pointList[i].a, pointList[i].r, pointList[i].g, pointList[i].b);
            v.Normal.set(pointList[i].nx, pointList[i].ny, pointList[i].nz);
            // v.TCoords.set(tu, tv);
            Vertices[j] = v;
            IndexList[j] = j;
        }
 
        Vector3f minBound = pointCloud->GetMinBound();
        Vector3f maxBound = pointCloud->GetMaxBound();
        Box = irr::core::aabbox3df(minBound.x, minBound.y, minBound.z, maxBound.x, maxBound.y, maxBound.z);
 
    }
}
 
 

Re: Point Cloud Scene Node

Posted: Tue Oct 04, 2016 9:43 am
by CuteAlien
Nice. You should maybe add setter/getter to set the color and length directly. Using DEBUG_NORMAL_COLOR and DEBUG_NORMAL_LENGTH is probably because it's based on Irrrlicht internal code. We use that in debug - but it's rather slow (using a string comparison). Also be aware that DebugDataVisible is something that has to be explicitely set. If you use an explicit PointCloudSceneNode you probably want it rather to show when it's "Visible" instead (already checked in OnRegisterSceneNode), so maybe you can remove the DebugDataVisible code in render.

Re: Point Cloud Scene Node

Posted: Tue Oct 04, 2016 10:14 am
by Wol101
Well spotted - the debug code is more or less straight from CAnimatedMeshSceneNode. But actually that's what I want. The bounding box and normals are only there for debugging so performance isn't really an issue. Multiple calls of draw3DLine must be very slow! I guess if I wanted it faster I could do something with drawVertexPrimitiveList and EPT_LINES. If I was smarter I'd unify my storage format with the Irrlicht vertex format and avoid unnecessary copying but it only happens relatively infrequently and so isn't a huge bottleneck. And my point clouds aren't all that big. 500,000 points or so...

Re: Point Cloud Scene Node

Posted: Tue Oct 04, 2016 6:38 pm
by chronologicaldot
*sigh* This reminds me of the fact that I never got around to implementing the multiple rendering types for Burnings. It still can't render points, which is silly because that's rather simple, but it'll have to do with a vertex cache overall because of the triangle fan more than anything else.

Re: Point Cloud Scene Node

Posted: Wed Oct 05, 2016 7:49 am
by AReichl
where is PointCloud.h ?

Re: Point Cloud Scene Node

Posted: Wed Oct 05, 2016 8:35 am
by Wol101
It's here but it isn't useful as an Irrlicht snippet. It has a lot of dependencies. However you can see how the points are stored and the bounds are accessed which might be useful. The code is part of a program for measuring locations on point cloud movies and I'll release it open source when it's finished (which should be this year).

PointCloud.h

Code: Select all

 
#ifndef POINTCLOUD_H
#define POINTCLOUD_H
 
#include "octree/octree.h"
#include "pipeline/math_3d.h"
#include "geometric_tools/Wm5ApprLineFit3.h"
#include "geometric_tools/Wm5ApprPlaneFit3.h"
 
struct Point
{
    float x;
    float y;
    float z;
    float nx;
    float ny;
    float nz;
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;
};
 
class PointCloud
{
public:
    PointCloud();
    ~PointCloud();
 
    enum AlphaCodes {selected = 0, unselected = 255};
 
    int ImportPLY(const char *filename);
    int ExportPLY(const char *filename);
    int SaveMatrix(const char * filename);
    int LoadMatrix(const char * filename);
    void SetAlpha(AlphaCodes alphaCode);
    void SetAlphaBounded(const Vector3f &bound1, const Vector3f &bound2, const Matrix4f &worldTrans, AlphaCodes alphaCode);
    void SetAlphaLine(const Vector3f &startLine, const Vector3f &endLine, float screenRadius, const Matrix4f &worldTrans, AlphaCodes alphaCode, bool useScreenPixelRadius, bool ignoreZ);
    void SetAlphaSphere(const Vector3f &screenSelect, float screenRadius, const Matrix4f &worldTrans, AlphaCodes alphaCode, bool useScreenPixelRadius);
    int FitLineToAlpha(AlphaCodes oldAlphaCode, AlphaCodes newAlphaCode);
    int FitPlaneToAlpha(AlphaCodes oldAlphaCode, AlphaCodes newAlphaCode);
    int FitPointToAlpha(AlphaCodes oldAlphaCode, AlphaCodes newAlphaCode);
    int FitPointToSphere(const Vector3f &screenSelect, float screenRadius, const Matrix4f &worldTrans);
    void RotateToVector(float x, float y, float z);
    void RotateToVector(float x, float y, float z, float ax, float ay, float az);
    void RotateByQuaternion(float x, float y, float z, float w);
    void RotateByAxisAngle(float x, float y, float z, float degrees);
    void Translate(float x, float y, float z);
    void Scale(float x, float y, float z);
    void ApplyTransformation(Matrix4f &matrix);
    void FindClosestPoint(const Vector3f &screenSelect, const Matrix4f &worldTrans, Vector3f *closestPoint);
 
    const Matrix4f &GetTransformation() { return m_Transformation; }
    const Vector3f &GetLastPoint() { return m_LastPoint; }
    const Vector3f &GetLastVector() { return m_LastVector; }
    const Point &GetPoint(int index) { return m_PointList[index]; }
    const Point *GetPointList() { return m_PointList; }
    int GetNPoints() { return m_NPoints; }
    int GetNSelected() { return m_NSelected; }
    const Vector3f &GetMinBound() { return m_MinBound; }
    const Vector3f &GetMaxBound() { return m_MaxBound; }
    bool GetLineFitted() { return m_lineFitted; }
    bool GetPointFitted() { return m_pointFitted; }
 
protected:
    static int TypeSize(const char *type);
 
    Point *m_PointList;
    int m_NPoints;
    int m_NSelected;
    Octree<int> *m_PointOctree;
    int m_OctreeSize;
    double m_OctreeIndexEpsilon;
    Vector3f m_MinBound;
    Vector3f m_MaxBound;
    double m_XDel;
    double m_YDel;
    double m_ZDel;
    bool m_UseOctree;
    Wm5::Line3<float> m_bestFitLine;
    Wm5::Plane3<float> m_BestFitPlane;
    Vector3f m_LastPoint;
    Vector3f m_LastVector;
    Matrix4f m_Transformation;
 
    bool m_lineFitted;
    bool m_pointFitted;
};
 
#endif // POINTCLOUD_H