Modified version of lib3ds reader of .3ds format






4.78/5 (15 votes)
In this article I present version of lib3ds reader for C++ language
- Download lib3ds.zip - 47.5 KB
- Download 3dsViewer.zip Source of viewer and lib3ds for Qt5.3.2 - 115 KB
Introduction
Formats of keeping 3D models and stages in 3DStudio are very different. However, most of them are bound to the engine of 3DStudio. And only a few of them present data in form of simple rendering in OpenGL. For my project I chose 3DS format. I found a very powerfull code of reader of 3ds format - lib3ds on site http://www.lib3ds.org/. Nonetheless, the code on this site is presented on C language. As a result, in the original code of lib3ds there are much static function which have difficult links and there are not possibilities to use abilities of object languages. For my project on C++ I rewrote the original lib3ds 2.0.0-rc1 from C into C++ structure with using of classes and interfaces. As a result, I wrote the code, which separates of reading 3ds data from using of data for rendering. This decision can be used in COM-like technologies and give simple managment with data.
For testing of code I make special program 3dsViewer whic is presented here Download 3dsViewer_exe.zip
Background
For using of structure of interfaces I made 10 files which describ of structure of data which using in the interface. The interface is contented in seperated file ILib3ds_stage.h such as base class and contents all data and virtual functions. Other classes which inherit interface contents only functions. As a result, size of top inherit class is equal size of interface and enough to call delete for interface to free all memory, which selected for all data. Moreover, the date of different size such as vertexes and faces has very simple instrument of deleting. In the original code there is a difficult algorithm of managment data and deleting data. I changed them on simple classes: QString, QVector, QList, QMap - are analogy of Qt framework of std String, Vector, List, Map. As a result, data with non-fixed size is wrapped into stack object and is deleted automaticaly.
Using the code
All code is contented in 19 classes which have the inherited hierarchy. However, for using of code enough to call static function of makeILib3ds_stage of class Lib3ds_loader
with path to 3ds file. This function returns interface ILib3ds_stage
on data of 3D model.
class Lib3ds_loader // Class of loading
{
public:
Lib3ds_loader();
static ILib3ds_stage *makeILib3ds_stage(QString filename); // Static
// function of making interface of 3ds data
};
This code is example of using interface ILib3ds_stage
in form of pointer Istage
. When there is not camera in data 3ds after examination Istage->cameras.isEmpty()
in list Istage->cameras
add structure of camera. The same doing with list of source of light.
bool OpenGLWidget::LoadModel(QString filename)
{
if(Istage) delete Istage; // Deleting data
Istage = Lib3ds_loader::makeILib3ds_stage(filename); // Getting interface on data
if(Istage != NULL)
/*Checking of existence of inteface of 3ds data*/
{
Istage->lib3ds_stage_eval(1.0f); // Setting first frame animation
Istage->lib3ds_stage_bounding_box_of_nodes(1, 0, 0, bmin, bmax, NULL); //
// finding of borders in node structure
// Computing of size of box which surround of scene
sx = bmax[0] - bmin[0];
sy = bmax[1] - bmin[1];
sz = bmax[2] - bmin[2];
boxsize = MAX(sx, sy); boxsize = MAX(boxsize, sz);
cx = (bmin[0] + bmax[0])/2; // Computing of centres of box
cy = (bmin[1] + bmax[1])/2;
cz = (bmin[2] + bmax[2])/2;
if(Istage->cameras.isEmpty()) // Checking of list of cameras in 3ds data
{
// If thre is not any camera in 3ds data than created camera
Lib3dsCamera camera;
camera.name = "Camera_Z"; // Naming new camera
memset(&camera.setting, 0, sizeof(Lib3dsCamera_setting));
// setting of angle of view
camera.setting.fov = 45;
// Setting point of target of camera in center of scene
camera.setting.target[0] = cx;
camera.setting.target[1] = cy;
camera.setting.target[2] = cz;
// Setting position camera out of scene
camera.setting.position[0] = cx;
camera.setting.position[1] = cy;
camera.setting.position[2] = bmax[2] + 2 * MAX(sx,sy);
// Setting near and far borders of camera
camera.setting.near_range = ( camera.setting.position[2] - bmax[2] ) * .5;
camera.setting.far_range = ( camera.setting.position[2] - bmin[2] ) * 2;
// adding camera into list of cameras of 3ds data
Istage->cameras[camera.name] = camera;
}
if (Istage->lights.isEmpty())// Checking of list of lights in 3ds data
// If thre is not any light source in 3ds data than created light source
{
Lib3dsLight light;
memset(&light.setting, 0, sizeof(Lib3dsLight_setting));
light.name = "light0"; // Named light source
light.setting.spot_light = 0; // Setting of 3DStudio
light.setting.see_cone = 0;// Setting of 3DStudio
// Setting of color of light
light.setting.color[0] = light.setting.color[1] = light.setting.color[2] = .6;
// Setting position of light source out of scene
light.setting.position[0] = cx + boxsize * .75;
light.setting.position[1] = cy - boxsize * 1.;
light.setting.position[2] = cz + boxsize * 1.5;
light.setting.position[3] = 0.;
light.setting.outer_range = 100;// Setting of 3DStudio
light.setting.inner_range = 10;// Setting of 3DStudio
light.setting.multiplier = 1;// Setting of 3DStudio
Istage->lights[light.name] = light; // adding light
}
Istage->lib3ds_stage_eval(0.0f); // setting in start position
}
// If there is 3ds data than return true;
return (Istage != NULL);
}
The next code renders 3ds data by parsing of nodes of 3ds. Parsing is doing for root nodes and their children. Function of void OpenGLWidget::updateAnimation()
executes animation of the positions of vertexes.
QList<lib3dsnode>::iterator p = Istage->nodes.begin();
while(p!=Istage->nodes.end())
// Enumiration of root nodes in 3ds data which do not have parents
{
render_node(&(*p)); // Function of rendering of node
++p;
}
void OpenGLWidget::render_node(Lib3dsNode *node)
{
{
QList<lib3dsnode>::iterator p = node->children.begin();
while(p!=node->children.end())
// Enumiration of children nodes
{
render_node(&(*p)); // Rendering of children nodes
++p;
}
}
if (node->setting.type == LIB3DS_NODE_MESH_INSTANCE)
/*Checking type of node. if it equals flag LIB3DS_NODE_MESH_INSTANCE then
execute rendering of mesh's vertexes
{
Lib3dsMesh *mesh = NULL; // Pointer on mesh
if(node->name == "$$$DUMMY")
/*Checking name on valid*/
{
return;
}
if(Istage->meshes.contains(node->name))
/*Checking of name node in list of meses in 3ds model
{
/*Getting addres on mesh*/
mesh = &Istage->meshes[node->name];
}
if(mesh != NULL)
{
/*Checking the binding of mesh with list rendering of OpenGL*/
if (!mesh->setting.user_id)
{
// Creating of binding with mesh
mesh->setting.user_id=glGenLists(1);
glNewList(mesh->setting.user_id, GL_COMPILE);
// Iterator of trangles primitives in mesh
QVector<lib3dsface>::iterator p = mesh->faces.begin();
// Setting movement mesh in initial position
float M[4][4];
Istage->lib3ds_matrix_copy(M, mesh->setting.matrix);
Istage->lib3ds_matrix_inv(M);
glMultMatrixf(&M[0][0]);
// Allocated memory for normals for smooth mesh
Lib3dsVector *normalL = new Lib3dsVector[3*mesh->faces.size()];
// Calculation of smooth normals for vertexes
Istage->lib3ds_mesh_calculate_vertex_normals(mesh, normalL);
unsigned int f = 0;
// Enumiration of trangles primitives in mesh
while (p != mesh->faces.end())
{
Lib3dsMaterial *mat = NULL; // Material of triangle primitive
Lib3dsMaterial *oldmat = NULL;//Material of old triangle primitive
// Finding material in list of materials of 3ds data
if(Istage->materials.contains((*p).material))
{
mat = &Istage->materials[(*p).material];
}
if( mat)
{
// If material is founded than reading of data
if (mat != oldmat)
{
if( mat->setting.two_sided )
glDisable(GL_CULL_FACE);
else
glEnable(GL_CULL_FACE);
// Setting colors for lightning
glMaterialfv(GL_FRONT, GL_AMBIENT, mat->setting.ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat->setting.diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat->setting.specular);
glMaterialf(GL_FRONT, GL_SHININESS, pow(2, 10.0*
mat->setting.shininess));
}
oldmat = mat;
}
else
{
// Setting default colors in cases of there is not material
static const GLfloat a[]={0.7, 0.7, 0.7, 1.0};
static const GLfloat d[]={0.7, 0.7, 0.7, 1.0};
static const GLfloat s[]={1.0, 1.0, 1.0, 1.0};
glMaterialfv(GL_FRONT, GL_AMBIENT, a);
glMaterialfv(GL_FRONT, GL_DIFFUSE, d);
glMaterialfv(GL_FRONT, GL_SPECULAR, s);
glMaterialf(GL_FRONT, GL_SHININESS, pow(2, 10.0*0.5));
}
glBegin(GL_TRIANGLES);// Start drawing primitive
glNormal3fv((*p).normal);
for (int i=0; i<3; ++i)
{
// Setting normal for vertex
glNormal3fv(normalL[3*f+i]);
// Drawing of vertex
glVertex3f(mesh->vertices[(*p).index[i]].x(),
mesh->vertices[(*p).index[i]].y(),
mesh->vertices[(*p).index[i]].z());
}
glEnd();
++p;
f++;
}
delete []normalL;
glEndList();
}
//Rendering of list of calls of OpenGL
if (mesh->setting.user_id)
{
glPushMatrix();
//Multiplication of mesh position with current matrix
glMultMatrixf(&node->setting.matrix[0][0]);
//Movement mesh in point rotation
glTranslatef(-node->MeshInstanceNode.pivot[0],
-node->MeshInstanceNode.pivot[1],
-node->MeshInstanceNode.pivot[2]);
// Calling of list compiled commands
glCallList(mesh->setting.user_id);
glPopMatrix();
}
}
}
else
{
// There can be executed animation of other objects such as camera or light source
}
}
void OpenGLWidget::updateAnimation()
{
if(Istage)
{
current_frame+=1.0;
// Current frame cannot be more maximum frames of 3ds model
if (current_frame>Istage->frames)
{
current_frame=0.0;
}
// Function of execution computing matrexes nodes in current frame
Istage->lib3ds_stage_eval(current_frame);
this->update();
}
}
This code is written on Qt framework, but all specific classes such as QString
, QList
, or QVector3D
can be rewritten in specific classes of almost all other platforms and languages.
Points of Interest
I spent much time on reading code of original lib3ds. For more clear understanding I commented almost all functions in classes. Alse I commented almost all application of flags for reading data. I hope my decision can help you to understand the structure of 3ds format.
Update for Qt5.3.2
I updated source code for lib3ds and Qt viewer for Qt5.3.2 - Download 3dsViewer.zip Source of viewer and lib3ds for Qt5.3.2 - 115 KB. 3ds model for testing - Download UEXTREM.zip.