Loading meshes using Assimp in OpenGL
I have been catching up on OpenGL 4.4 lately and realized that there are few working examples on the newer versions of OpenGL. The following code uses Assimp, GLFW and GLEW to load and render all supported Assimp formats and requires OpenGL 3.0 or above.
You will be required to set up an OpenGL context, I will presume that you have the knowledge to do that (if not, I can recommend this tutorial from opengl-tutorial.org).
To use the Mesh class, this is all that is required:
Mesh *mesh = new Mesh("filename.dae");
while (!glfwWindowShouldClose(window)) {
mesh->render();
}
delete m;
The combined source and example shaders can be downloaded at the bottom of this page.
Source code
Mesh.h
#pragma once
#include "include/GL/glew.h"
#include "include/GL/glfw3.h"
#include "include\assimp\scene.h"
#include "include\assimp\mesh.h"
#include
class Mesh
{
public :
struct MeshEntry {
static enum BUFFERS {
VERTEX_BUFFER, TEXCOORD_BUFFER, NORMAL_BUFFER, INDEX_BUFFER
};
GLuint vao;
GLuint vbo[4];
unsigned int elementCount;
MeshEntry(aiMesh *mesh);
~MeshEntry();
void load(aiMesh *mesh);
void render();
};
std::vector<MeshEntry*>; meshEntries;
public:
Mesh(const char *filename);
~Mesh(void);
void render();
};
Mesh.cpp
#include "Mesh.h"
#include "include\assimp\Importer.hpp"
#include "include\assimp\postprocess.h"
/**
* Constructor, loading the specified aiMesh
**/
Mesh::MeshEntry::MeshEntry(aiMesh *mesh) {
vbo[VERTEX_BUFFER] = NULL;
vbo[TEXCOORD_BUFFER] = NULL;
vbo[NORMAL_BUFFER] = NULL;
vbo[INDEX_BUFFER] = NULL;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
elementCount = mesh->mNumFaces * 3;
if(mesh->HasPositions()) {
float *vertices = new float[mesh->mNumVertices * 3];
for(int i = 0; i < mesh->mNumVertices; ++i) {
vertices[i * 3] = mesh->mVertices[i].x;
vertices[i * 3 + 1] = mesh->mVertices[i].y;
vertices[i * 3 + 2] = mesh->mVertices[i].z;
}
glGenBuffers(1, &vbo[VERTEX_BUFFER]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[VERTEX_BUFFER]);
glBufferData(GL_ARRAY_BUFFER, 3 * mesh->mNumVertices * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray (0);
delete[] vertices;
}
if(mesh->HasTextureCoords(0)) {
float *texCoords = new float[mesh->mNumVertices * 2];
for(int i = 0; i < mesh->mNumVertices; ++i) {
texCoords[i * 2] = mesh->mTextureCoords[0][i].x;
texCoords[i * 2 + 1] = mesh->mTextureCoords[0][i].y;
}
glGenBuffers(1, &vbo[TEXCOORD_BUFFER]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[TEXCOORD_BUFFER]);
glBufferData(GL_ARRAY_BUFFER, 2 * mesh->mNumVertices * sizeof(GLfloat), texCoords, GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray (1);
delete[] texCoords;
}
if(mesh->HasNormals()) {
float *normals = new float[mesh->mNumVertices * 3];
for(int i = 0; i < mesh->mNumVertices; ++i) {
normals[i * 3] = mesh->mNormals[i].x;
normals[i * 3 + 1] = mesh->mNormals[i].y;
normals[i * 3 + 2] = mesh->mNormals[i].z;
}
glGenBuffers(1, &vbo[NORMAL_BUFFER]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[NORMAL_BUFFER]);
glBufferData(GL_ARRAY_BUFFER, 3 * mesh->mNumVertices * sizeof(GLfloat), normals, GL_STATIC_DRAW);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray (2);
delete[] normals;
}
if(mesh->HasFaces()) {
unsigned int *indices = new unsigned int[mesh->mNumFaces * 3];
for(int i = 0; i < mesh->mNumFaces; ++i) {
indices[i * 3] = mesh->mFaces[i].mIndices[0];
indices[i * 3 + 1] = mesh->mFaces[i].mIndices[1];
indices[i * 3 + 2] = mesh->mFaces[i].mIndices[2];
}
glGenBuffers(1, &vbo[INDEX_BUFFER]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[INDEX_BUFFER]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * mesh->mNumFaces * sizeof(GLuint), indices, GL_STATIC_DRAW);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray (3);
delete[] indices;
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
/**
* Deletes the allocated OpenGL buffers
**/
Mesh::MeshEntry::~MeshEntry() {
if(vbo[VERTEX_BUFFER]) {
glDeleteBuffers(1, &vbo[VERTEX_BUFFER]);
}
if(vbo[TEXCOORD_BUFFER]) {
glDeleteBuffers(1, &vbo[TEXCOORD_BUFFER]);
}
if(vbo[NORMAL_BUFFER]) {
glDeleteBuffers(1, &vbo[NORMAL_BUFFER]);
}
if(vbo[INDEX_BUFFER]) {
glDeleteBuffers(1, &vbo[INDEX_BUFFER]);
}
glDeleteVertexArrays(1, &vao);
}
/**
* Renders this MeshEntry
**/
void Mesh::MeshEntry::render() {
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, NULL);
glBindVertexArray(0);
}
/**
* Mesh constructor, loads the specified filename if supported by Assimp
**/
Mesh::Mesh(const char *filename)
{
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(filename, NULL);
if(!scene) {
printf("Unable to laod mesh: %s\n", importer.GetErrorString());
}
for(int i = 0; i < scene->mNumMeshes; ++i) {
meshEntries.push_back(new Mesh::MeshEntry(scene->mMeshes[i]));
}
}
/**
* Clears all loaded MeshEntries
**/
Mesh::~Mesh(void)
{
for(int i = 0; i < meshEntries.size(); ++i) {
delete meshEntries.at(i);
}
meshEntries.clear();
}
/**
* Renders all loaded MeshEntries
**/
void Mesh::render() {
for(int i = 0; i < meshEntries.size(); ++i) { meshEntries.at(i)->render();
}
}
Comments
Truncator
on Jul 24, 2014Reply
Very helpful article, thank you!
Skirrah
on Sep 27, 2014Reply
Very helpful code but perhaps someone could help me resolve an error when using with assimp-3.1.-win-binaries. Code compiles but gives runtime error; “The program can’t start because assimp.exe is missing from your computer.”
The assimp.dll file is in the same folder as the program executable.
Nex
on Sep 30, 2014Reply
Greetings Skirrah. What version of Assimp are you using? As I have understood, there is a problem with the 3.1(.1) version of the Windows binaries. Until the issue is fixed, I would recommend that you downgrade your version of Assimp. The bug has been reported to the Github repo, you can keep up with the status of it here: https://github.com/assimp/assimp/issues/302
Tomas Mikulica
on Oct 26, 2014Reply
Hi, first of all I would like to thank you for great article. I have one question. May I use your code in my app (Diploma thesis) when I will put link to this article in the header file?
Nex
on Oct 29, 2014Reply
Greetings Tomas. First of all, thank you! And of course you may! The sole intention for me writing about this stuff is to share the knowledge. Any backlinks are of course much appreciated ;)
Tomas Mikulica
on Nov 29, 2014Reply
Thank you. By the the way I think that the line (in texcoord loading):
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
should look more like that:
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL);
Because you are passing to shader only 2 floats not 3. It took me awhile (and lot of weird stuff on my screen :) ) to figure that out ;)
pacific00
on Apr 07, 2015Reply
Hi Tom ,
I’m trying to load a model using assimp , in mac using core 4.1 but i’m stuck on “vertex object not bound” error.. tried to bind a vao before vbo still not happening..
It’d help if you could take a look at the below code(your code :) ) and give some suggestion..
vbo[VERTEX_BUFFER] = NULL; vbo[TEXCOORD_BUFFER] = NULL; vbo[NORMAL_BUFFER] = NULL; vbo[INDEX_BUFFER] = NULL;
// this was the only vao that was bound initially with legacy opengl //glGenVertexArraysAPPLE(1, &vao); //glBindVertexArrayAPPLE(vao);
elementCount = mesh->mNumFaces * 3; if(mesh->HasPositions()) { float *vertices = new float[mesh->mNumVertices * 3]; for(int i = 0; i mNumVertices; ++i) { vertices[i * 3] = mesh->mVertices[i].x; vertices[i * 3 + 1] = mesh->mVertices[i].y; vertices[i * 3 + 2] = mesh->mVertices[i].z; } // here trying to bind vao and so on before every vbo glGenVertexArraysAPPLE(1, &vaoTemp[VERTEX_BUFFER]); glBindVertexArrayAPPLE(vaoTemp[VERTEX_BUFFER]);
glGenBuffers(1, &vbo[VERTEX_BUFFER]); glBindBuffer(GL_ARRAY_BUFFER, vbo[VERTEX_BUFFER]); glBufferData(GL_ARRAY_BUFFER, 3 * mesh->mNumVertices * sizeof(GLfloat), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray (0); delete vertices; }
Praveen
on May 14, 2015Reply
Hi, When I am using this code, GlDrawelements doing nothing, I am working in OpenGL 2.1. Thanks
George Broughton
on Oct 06, 2015Reply
MeshEntry constructor, all of your deletes should be delete[] as they are an array, or they will leak memory
Nex
on Oct 09, 2015Reply
Yes they should, updated now :) Thank you!
Mike Hou
on Nov 26, 2015Reply
Hi, I learned assimp importing from this tutorial, thank you. I think the code glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray (3); should be removed since they are not used, am I correct?
Leave a comment