Point Cloud Scene Node

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Wol101
Posts: 9
Joined: Sat Apr 23, 2016 6:39 am

Point Cloud Scene Node

Post 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);
 
    }
}
 
 
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Point Cloud Scene Node

Post 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.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Wol101
Posts: 9
Joined: Sat Apr 23, 2016 6:39 am

Re: Point Cloud Scene Node

Post 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...
chronologicaldot
Competition winner
Posts: 684
Joined: Mon Sep 10, 2012 8:51 am

Re: Point Cloud Scene Node

Post 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.
AReichl
Posts: 268
Joined: Wed Jul 13, 2011 2:34 pm

Re: Point Cloud Scene Node

Post by AReichl »

where is PointCloud.h ?
Wol101
Posts: 9
Joined: Sat Apr 23, 2016 6:39 am

Re: Point Cloud Scene Node

Post 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
 
Post Reply