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; }