Custom 3D Mesh Node

First Part

In this part we will see how to create and render a custom node.

To create our custom node, we'll need to create a new class driver, because all fonctions to create an 3D object, to begin with nothing, are not implemented in UDriver as CDriverUser. UDriver and CDriverUser are user class of an IDriver instance.
Then them class don't implement we need, we going to create a new class name MDriver implementation of CDriverUser.

What do we need ? First MDriver must manage the Vertex Buffer and Index buffer. But we need to rendu them too.

If we look at IDriver class, we can see 3 interestings functions : activeVertexBuffer (CVertexBuffer &VB), activeIndexBuffer(CIndexBuffer &IB) and renderTriangles(CMaterial &mat, uint32 firstIndex, uint32 ntris). They return bool.

We have now :

file m_driver.h :

#ifndef M_DRIVER_H
#define M_DRIVER_H
#include <nel/3d/driver_user.h>
using namespace NL3D;
class MDriver : public NL3D::CDriverUser
{
    public :
    MDriver ();
    virtual    ~MDriver();
    static MDriver *createDriver();
    virtual bool  activeVertexBuffer (CVertexBuffer &VB);
    virtual bool activeIndexBuffer(CIndexBuffer &IB);
    virtual bool renderTriangles(CMaterial &mat, uint32 firstIndex, uint32 ntris);
};
#endif //  M_DRIVER_H

We only use IDriver instance.

 

file m_driver.cpp :

#include "../include/m_driver.h"
using namespace NLMISC;
using namespace NL3D;
MDriver::MDriver()
{
}
MDriver::~MDriver()
{
    purgeMemory();
}
MDriver    *MDriver::createDriver()
{
    return new MDriver();
}
bool     MDriver::activeVertexBuffer (CVertexBuffer &VB)
{
   return _Driver->activeVertexBuffer (VB);
}
bool MDriver::activeIndexBuffer(CIndexBuffer &IB)
{
    return _Driver->activeIndexBuffer(IB);
}
bool MDriver::renderTriangles(CMaterial &mat, uint32 firstIndex, uint32 ntris)
{
    return _Driver->renderTriangles(mat, firstIndex, ntris);
}

We can now create our custom 3D node.

 

For this tutorial we going to create a 3D cube. The displayCube function will do it.

In file main.cpp we going to put only minimum code.

#include <nel/misc/path.h>
#include <nel/misc/file.h>
#include <nel/misc/common.h>
#include <nel/3d/u_scene.h>
#include <nel/3d/texture.h>
#include <nel/3d/texture_mem.h>
#include <nel/3d/u_light.h>
#include <nel/3d/u_camera.h>
#include <nel/3d/u_driver.h>
#include <nel/3d/u_instance.h>
#include "../include/m_driver.h"
using namespace NLMISC;
using namespace NL3D;
//We create an MDriver instance here because fonctions displayCube and main will need it.
MDriver *Driver = MDriver::createDriver();
//Create and render the cube
void displayCube()
{
    //Material needed by the fonction of render triangles
    CMaterial mat;
    mat.initUnlit();
    mat.setSrcBlend(CMaterial::srcalpha);
    mat.setDstBlend(CMaterial::invsrcalpha);
    mat.setColor(CRGBA(50,255,255,150)); //Set a color to the material
    CVertexBuffer vb;
    
    //Must determinate the format for vertex
    //Later we'll see that we could add other informations as the color for exemple
    vb.setVertexFormat (CVertexBuffer::PositionFlag); 
    vb.setNumVertices (8);
    {
        CVertexBufferReadWrite vba;
        vb.lock(vba);
        vba.setVertexCoord (0, CVector (0.0, 0.0, 0.0));
        vba.setVertexCoord (1, CVector (0.2, 0.00, 0.0));
        vba.setVertexCoord (2, CVector (0.2, 0.00, 0.2));
        vba.setVertexCoord (3, CVector (0, 0.0, 0.2));
        vba.setVertexCoord (4, CVector (0.0, 0.2, 0));
        vba.setVertexCoord (5, CVector (0.20, 0.2, 0.0));
        vba.setVertexCoord (6, CVector (0.2, 0.2, 0.20));
        vba.setVertexCoord (7, CVector (0, 0.2, 0.20));
    }
    //Now we can set to active our vertex buffer
    Driver->activeVertexBuffer(vb);
    CIndexBuffer pbQuad1;
    pbQuad1.setNumIndexes (36);
    {
        CIndexBufferReadWrite iba1;
        pbQuad1.lock(iba1);
        //With vertex we can now make some triangles for cube
        iba1.setTri (0, 0, 1, 2); //front
        iba1.setTri (3, 2, 3, 0); //front
        iba1.setTri (6, 2, 1, 5); //right
        iba1.setTri (9, 5, 6, 2); //right
        iba1.setTri (12, 2, 7, 3); //top
        iba1.setTri (15, 2, 6, 7); //top
        iba1.setTri (18, 0, 4, 1); //bottom
        iba1.setTri (21, 1, 4, 5); //bottom
        iba1.setTri (24, 3, 4, 0); //left
        iba1.setTri (27, 3, 7, 4); //left
        iba1.setTri (30, 6, 5, 4); //behind
        iba1.setTri (33, 4, 7, 6); //behind
    }
     
    //Active index buffer
    Driver->activeIndexBuffer(pbQuad1);
    //Render of all triangles of the cube
    Driver->renderTriangles(mat, 0, pbQuad1.getNumIndexes()/3);
}
sint main(int argc, char **argv)
{
    try
    {
        if (!Driver) throw 2;
        // create a window in 800x600
        Driver->setDisplay(NL3D::UDriver::CMode(800, 600, 32, true),true, false);
        // set the title
        Driver->setWindowTitle(ucstring("NeL shape viewer"));
        // can use both dds and tga textures for shapes
        CPath::remapExtension ("dds", "tga", true);
        // Create a scene
        NL3D::UScene *Scene = Driver->createScene(true);
        if (!Scene) throw 4;
        // create the camera
        UCamera Camera = Scene->getCam();
        if (Camera.empty()) throw 5;
        Camera.setTransformMode (UTransformable::DirectMatrix);
        Camera.setPerspective ((float)Pi/2.f, 1.33f, 0.1f, 1000);
        // camera will look at entities
        Camera.lookAt (CVector(0, -10.f, 0.f), CVector(0.f, 0.f, 0.f));
        // main loop
        while (Driver->isActive())
        {
            Driver->EventServer.pump();
            // the background is black
            Driver->clearBuffers(CRGBA(0, 0, 0));
            //Display our cube
            displayCube();
            // show the scene
            Driver->swapBuffers();
            // escape will leave the program
            if (Driver->AsyncListener.isKeyPushed(KeyESCAPE))
            {
                break;
            }
            // F3 will change the render mode
            else if (Driver->AsyncListener.isKeyPushed(KeyF3))
            {
                NL3D::UDriver::TPolygonMode p = Driver->getPolygonMode();
                p = NL3D::UDriver::TPolygonMode(((int)p+1)%3);
                Driver->setPolygonMode(p);
            }
        }
        // we are leaving the program
        // delete the scene
        Driver->deleteScene(Scene);
        // release all textures and others elements
        Driver->release();
        // delete the driver
        delete Driver;
    }
    catch(int a)
    {
        return a;
    }
    catch(...)
    {
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

Second Part

 

In this part you learn how to move the object and change the color of vertex.

 

You have create a cube you only see a rectangle because the cube don't move and it have no color to distinc face.

So we going to set up this.

 

For moving an object you need to modify it, to do this you can use matrix. When you have a matrix you can move, rotate and scale an 3d object.

 

You can create a matrix alone but if you want see modification of four object the driver must manage matrix too. So we'll add news functions to MDriver :

setupViewMatrix (const CMatrix &mtx) and setupModelMatrix(const CMatrix &mtx).

 

Add this to m_driver.h :

    virtual void  setupViewMatrix (const CMatrix &mtx);
    virtual void  setupModelMatrix(const CMatrix &mtx);

And this to m_driver.cpp :

void  MDriver::setupViewMatrix (const CMatrix &mtx)
{
    _Driver->setupViewMatrix (mtx);
}
void  MDriver::setupModelMatrix(const CMatrix &mtx)
{
    _Driver->setupModelMatrix(mtx);
}

Now you can add to your fonction displayCube :

    CMatrix mtx;
    mtx.identity();
    Driver->setupViewMatrix (mtx);
    Driver->setupModelMatrix (mtx);

But if you compile and run, nothing apend because you have not set mofication for matrix.

Try to add this between Driver->setupViewMatrix (mtx); and Driver->setupModelMatrix (mtx); :

    mtx.translate(CVector(0.5,0.5,0.5));
    _RotX +=0.005;
    mtx.rotateX(_RotX );
    mtx.rotateY(_RotY );

and this after MDriver *Driver = MDriver::createDriver(); :

float _RotX = 0;
float _RotY = 0;

Compile and run to see the result

 

The result need some color...

 

so modifify that :

     vb.setVertexFormat (CVertexBuffer::PositionFlag); 
     vb.setNumVertices (8);
     {
         CVertexBufferReadWrite vba;
         vb.lock(vba);
 
         vba.setVertexCoord (0, CVector (0.0, 0.0, 0.0));
         vba.setVertexCoord (1, CVector (0.2, 0.00, 0.0));
         vba.setVertexCoord (2, CVector (0.2, 0.00, 0.2));
         vba.setVertexCoord (3, CVector (0, 0.0, 0.2));
         vba.setVertexCoord (4, CVector (0.0, 0.2, 0));
         vba.setVertexCoord (5, CVector (0.20, 0.2, 0.0));
         vba.setVertexCoord (6, CVector (0.2, 0.2, 0.20));
         vba.setVertexCoord (7, CVector (0, 0.2, 0.20));
     }

by that :

    vb.setVertexFormat (CVertexBuffer::PositionFlag |CVertexBuffer::PrimaryColorFlag);
    vb.setNumVertices (8);
    {
        CVertexBufferReadWrite vba;
        vb.lock(vba);
        vba.setVertexCoord (0, CVector (0.0, 0.0, 0.0));
        vba.setColor(0, NLMISC::CRGBA (255,0,0));
        vba.setVertexCoord (1, CVector (0.2, 0.00, 0.0));
        vba.setColor(1, NLMISC::CRGBA (255,0,0));
        vba.setVertexCoord (2, CVector (0.2, 0.00, 0.2));
        vba.setColor(2, NLMISC::CRGBA (255,0,0));
        vba.setVertexCoord (3, CVector (0, 0.0, 0.2));
        vba.setColor(3, NLMISC::CRGBA (255,0,0));
        vba.setVertexCoord (4, CVector (0.0, 0.2, 0));
        vba.setColor(4, NLMISC::CRGBA (0,255,0));
        vba.setVertexCoord (5, CVector (0.20, 0.2, 0.0));
        vba.setColor(5, NLMISC::CRGBA (0,255,0));
        vba.setVertexCoord (6, CVector (0.2, 0.2, 0.20));
        vba.setColor(6, NLMISC::CRGBA (0,255,0));
        vba.setVertexCoord (7, CVector (0, 0.2, 0.20));
        vba.setColor(7,NLMISC::CRGBA (0,255,0));
    }

It's ok now, but i think you would like to modify object with your keyboard now. So add this after the "else if" for KeyF3 :

    else if (Driver->AsyncListener.isKeyPushed(KeyUP))
    {
                _RotX+=0.05;
    }
    else if (Driver->AsyncListener.isKeyPushed(KeyDOWN))
    {
                _RotX-=0.05;
    }
    else if (Driver->AsyncListener.isKeyPushed(KeyLEFT))
    {
                _RotY-=0.05;
    }
    else if (Driver->AsyncListener.isKeyPushed(KeyRIGHT))
    {
                _RotY+=0.05;
    }

Add all key/transmormation that you want ;)

 

all code for main.cpp

#include <nel/misc/path.h>
#include <nel/misc/file.h>
#include <nel/misc/common.h>
#include <nel/3d/u_scene.h>
#include <nel/3d/texture.h>
#include <nel/3d/texture_mem.h>
#include <nel/3d/u_light.h>
#include <nel/3d/u_camera.h>
#include <nel/3d/u_driver.h>
#include <nel/3d/u_instance.h>
#include "../include/m_driver.h"
using namespace NLMISC;
using namespace NL3D;
MDriver *Driver = MDriver::createDriver();
float _RotZ = 0;
float _RotY = 0;
float _RotX = 0;
void displayCube()
{
    CMaterial mat;
    mat.initUnlit();
    mat.setSrcBlend(CMaterial::srcalpha);
    mat.setDstBlend(CMaterial::invsrcalpha);
    CVertexBuffer vb;
    vb.setVertexFormat (CVertexBuffer::PositionFlag |CVertexBuffer::PrimaryColorFlag);
    vb.setNumVertices (8);
    {
        CVertexBufferReadWrite vba;
        vb.lock(vba);
        // tri
        vba.setVertexCoord (0, CVector (0.0, 0.0, 0.0));
        vba.setColor(0, NLMISC::CRGBA (255,0,0));
        vba.setVertexCoord (1, CVector (0.2, 0.00, 0.0));
        vba.setColor(1, NLMISC::CRGBA (255,0,0));
        vba.setVertexCoord (2, CVector (0.2, 0.00, 0.2));
        vba.setColor(2, NLMISC::CRGBA (255,0,0));
        vba.setVertexCoord (3, CVector (0, 0.0, 0.2));
        vba.setColor(3, NLMISC::CRGBA (255,0,0));
        vba.setVertexCoord (4, CVector (0.0, 0.2, 0));
        vba.setColor(4, NLMISC::CRGBA (0,255,0));
        vba.setVertexCoord (5, CVector (0.20, 0.2, 0.0));
        vba.setColor(5, NLMISC::CRGBA (0,255,0));
        vba.setVertexCoord (6, CVector (0.2, 0.2, 0.20));
        vba.setColor(6, NLMISC::CRGBA (0,255,0));
        vba.setVertexCoord (7, CVector (0, 0.2, 0.20));
        vba.setColor(7,NLMISC::CRGBA (0,255,0));
    }
    Driver->activeVertexBuffer(vb);
    CIndexBuffer pbQuad1;
    pbQuad1.setNumIndexes (36);
    {
        CIndexBufferReadWrite iba1;
        pbQuad1.lock(iba1);
        iba1.setTri (0, 0, 1, 2); //front
        iba1.setTri (3, 2, 3, 0); //front
        iba1.setTri (6, 2, 1, 5); //right
        iba1.setTri (9, 5, 6, 2); //right
        iba1.setTri (12, 2, 7, 3); //top
        iba1.setTri (15, 2, 6, 7); //top
        iba1.setTri (18, 0, 4, 1); //bottom
        iba1.setTri (21, 1, 4, 5); //bottom
        iba1.setTri (24, 3, 4, 0); //left
        iba1.setTri (27, 3, 7, 4); //left
        iba1.setTri (30, 6, 5, 4); //behind
        iba1.setTri (33, 4, 7, 6); //behind
    }
    CMatrix mtx;
    mtx.identity();
    Driver->setupViewMatrix (mtx);
    mat.setColor(CRGBA(50,255,255,150));
    mtx.translate(CVector(0.5,0.5,0.5));
    mtx.rotateX(_RotX );
    mtx.rotateY(_RotY );
    Driver->setupModelMatrix (mtx);
    Driver->activeIndexBuffer(pbQuad1);
    Driver->renderTriangles(mat, 0, pbQuad1.getNumIndexes()/3);
}
sint main(int argc, char **argv)
{
    try
    {
        // create OpenGL driver
        //MDriver *Driver = MDriver::createDriver();
        if (!Driver) throw 2;
        // create a window in 800x600
        Driver->setDisplay(NL3D::UDriver::CMode(800, 600, 32, true),true, false);
        // set the title
        Driver->setWindowTitle(ucstring("NeL shape viewer"));
        // can use both dds and tga textures for shapes
        CPath::remapExtension ("dds", "tga", true);
        // Create a scene
        NL3D::UScene *Scene = Driver->createScene(true);
        if (!Scene) throw 4;
        // create the camera
        UCamera Camera = Scene->getCam();
        if (Camera.empty()) throw 5;
        Camera.setTransformMode (UTransformable::DirectMatrix);
        Camera.setPerspective ((float)Pi/2.f, 1.33f, 0.1f, 1000);
        // camera will look at entities
        Camera.lookAt (CVector(0, -10.f, 0.f), CVector(0.f, 0.f, 0.f));
        // main loop
        while (Driver->isActive())
        {
            Driver->EventServer.pump();
            // the background is black
            Driver->clearBuffers(CRGBA(0, 0, 0));
            displayCube();
            // show the scene
            Driver->swapBuffers();
            // escape will leave the program
            if (Driver->AsyncListener.isKeyPushed(KeyESCAPE))
            {
                break;
            }
            // F3 will change the render mode
            else if (Driver->AsyncListener.isKeyPushed(KeyF3))
            {
                NL3D::UDriver::TPolygonMode p = Driver->getPolygonMode();
                p = NL3D::UDriver::TPolygonMode(((int)p+1)%3);
                Driver->setPolygonMode(p);
            }
            else if (Driver->AsyncListener.isKeyPushed(KeyUP))
            {
                _RotX+=0.05;
            }
            else if (Driver->AsyncListener.isKeyPushed(KeyDOWN))
            {
                _RotX-=0.05;
            }
            else if (Driver->AsyncListener.isKeyPushed(KeyLEFT))
            {
                _RotY-=0.05;
            }
            else if (Driver->AsyncListener.isKeyPushed(KeyRIGHT))
            {
                _RotY+=0.05;
            }
        }
        // we are leaving the program
        // delete the scene
        Driver->deleteScene(Scene);
        // release all textures and others elements
        Driver->release();
        // delete the driver
        delete Driver;
    }
    catch(int a)
    {
        return a;
    }
    catch(...)
    {
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}