Open Asset Import Library (ASSIMP)
Assimp es una librería que nos servirá para cargar modelos o escenas 3D almacenados en gran variedad de formatos, como: Collada (*.dae; *.xml), Blender (*.blend), 3D Studio Max 3DS (*.3ds), Wavefront Object (*.obj ), y muchos más, Assimp puede cargar información de vértices, coordenadas de textura, normales, materiales, animación, y otros.
Descargarnos el correspondiente código fuente, para este ejemplo usaremos la versión Assimp 3.2, usaremos CMake para generar el proyecto que luego construiremos con Visual Studio 2015, opcionalmente podemos instalar las librerías DirectX SDK y la biblioteca Boost que serán usadas para construir Assimp con algunas mejoras de rendimiento.
Abrimos CMake GUI e indicamos el CMakeLists.txt de Assimp y la carpeta de salida, presionamos configurar, debemos recordar cambiar la ruta de instalación CMAKE_INSTALL_PREFIX, presionamos configurar nuevamente, si no tenemos errores, presionamos generar.

Una vez tenemos el proyecto generado para Visual Studio 2015 abrimos la solución correspondiente, cambiamos a la configuración adecuada, Debug o Release, según lo necesitemos, para generar e instalar la biblioteca Assimp, nos situamos sobre el proyecto llamado, INSTALL y presionamos generar.
Para finalizar debemos agregar la variable de entorno ASSIMP_DIR apuntando a la carpeta de instalación C:/Developer/opengl/ASSIMP/lib/cmake/assimp-3.2 o podemos indicarla manualmente a la hora de usar CMake para generar los ejemplos, requerimos también agregar la ruta C:/Developer/opengl/ASSIMP/bin a la variable PATH.
Normalmente un modelo o escena 3D está compuesto por varias partes, a cada una de las partes de componen un modelo le llamaremos Mesh, por ejemplo: a la hora de modelar un auto, para facilitarnos el trabajo podemos ir creándolo por partes, las llantas, las puertas, ventanas, etc., cada una de estas partes (Meshes) tendrá sus normales, texturas, vértices, etc., por lo que podemos dibujarlas individualmente, todas en conjunto forman el modelo de un auto.

Usando la aplicación Open 3D Model Viewer podemos observar cómo está compuesto un modelo, en la parte derecha se pueden ver cada uno de los meses que conforman el modelo, en la imagen vemos el mesh 148 resaltado, este es el cuerpo de deadpool, los demás corresponden a sus armas y el traje, veamos como dibujar un mesh con OpenGL Moderno.
Se código muestra el código necesario para dibujar un mesh en particular, el número 148, primero cargamos el archivo, con el método ReadFile, este nos devuelve un objeto aiScene este contiene todos los meshes que componen el modelo, obtenemos el deseado, un objeto aiMesh contiene la información de vértices, normales y texturas que necesitamos para dibujar, obtenemos cada uno y lo almacenamos en los contenedores STL vector.

Este código nos servirá para dibujar modelos que estén formados por un solo mesh obteniendo el primer mesh (
Para dibujar una escena completa debemos obtener todos los nodos que componen la escena, una escena tiene un nodo principal, este contiene los meshes que forman el modelo y otros nodos hijos que a su vez contienen esta misma información, para obtener todos los meshes debemos recorrer recursivamente los nodos de la escena.
Creamos la clase Model, esta contiene un conjunto de objetos Mesh que conforman la escena a dibujar, el método processNode será el encargado de recorrer cada uno de los nodos de la escena y obtener cada uno de los objetos Mesh que la conforman.
La clase Mesh será la encargada de obtener la información necesaria para dibujar cada mesh, vértices, normales, uv, etc., funciona de la misma manera como lo vimos al principio, solo debemos tener en cuenta que, para cada mesh creamos un VAO, almacenamos los buffer correspondientes en este VAO.
Ejecutamos la aplicación y veremos el resultado, podemos observar que ahora si tenemos el modelo dibujado con todas sus componentes.

En el próximo tutorial veremos cómo añadir texturas y materiales a nuestra escena o modelo 3D y cómo podemos aplicar animaciones a un modelo.
Este modelo no esta incluido, lo puedes descargar de: Dead Pool By TF3DM
GitHub: Importar Modelos con ASSIMP
Construir Assimp
Descargarnos el correspondiente código fuente, para este ejemplo usaremos la versión Assimp 3.2, usaremos CMake para generar el proyecto que luego construiremos con Visual Studio 2015, opcionalmente podemos instalar las librerías DirectX SDK y la biblioteca Boost que serán usadas para construir Assimp con algunas mejoras de rendimiento.
Abrimos CMake GUI e indicamos el CMakeLists.txt de Assimp y la carpeta de salida, presionamos configurar, debemos recordar cambiar la ruta de instalación CMAKE_INSTALL_PREFIX, presionamos configurar nuevamente, si no tenemos errores, presionamos generar.
Una vez tenemos el proyecto generado para Visual Studio 2015 abrimos la solución correspondiente, cambiamos a la configuración adecuada, Debug o Release, según lo necesitemos, para generar e instalar la biblioteca Assimp, nos situamos sobre el proyecto llamado, INSTALL y presionamos generar.
Para finalizar debemos agregar la variable de entorno ASSIMP_DIR apuntando a la carpeta de instalación C:/Developer/opengl/ASSIMP/lib/cmake/assimp-3.2 o podemos indicarla manualmente a la hora de usar CMake para generar los ejemplos, requerimos también agregar la ruta C:/Developer/opengl/ASSIMP/bin a la variable PATH.
Model Mesh
Normalmente un modelo o escena 3D está compuesto por varias partes, a cada una de las partes de componen un modelo le llamaremos Mesh, por ejemplo: a la hora de modelar un auto, para facilitarnos el trabajo podemos ir creándolo por partes, las llantas, las puertas, ventanas, etc., cada una de estas partes (Meshes) tendrá sus normales, texturas, vértices, etc., por lo que podemos dibujarlas individualmente, todas en conjunto forman el modelo de un auto.
Usando la aplicación Open 3D Model Viewer podemos observar cómo está compuesto un modelo, en la parte derecha se pueden ver cada uno de los meses que conforman el modelo, en la imagen vemos el mesh 148 resaltado, este es el cuerpo de deadpool, los demás corresponden a sus armas y el traje, veamos como dibujar un mesh con OpenGL Moderno.
Assimp::Importer importer; // Cragar el archivo indicado, usar caras triangulares (aiProcess_Triangulate) const aiScene* scene = importer.ReadFile(archivo, aiProcess_Triangulate); // Si no se ha podido cargar, mustra el error. if (!scene) { std::cout << importer.GetErrorString() << std::endl; return; } // Obtener el mesh deseado const aiMesh* mesh = scene->mMeshes[148]; // Obtener la posicion de cada vertice vertex.reserve(mesh->mNumVertices); for (unsigned int i = 0; i < mesh->mNumVertices; i++) { aiVector3D pos = mesh->mVertices[i]; vertex.push_back(glm::vec3(pos.x, pos.y, pos.z)); } // Obetener las coordenadas de textura uv.reserve(mesh->mNumVertices); for (unsigned int i = 0; i < mesh->mNumVertices; i++) { aiVector3D UVW = mesh->mTextureCoords[0][i]; uv.push_back(glm::vec2(UVW.x, UVW.y)); } // Obtener los vectores normales normal.reserve(mesh->mNumVertices); for (unsigned int i = 0; i < mesh->mNumVertices; i++) { aiVector3D n = mesh->mNormals[i]; normal.push_back(glm::vec3(n.x, n.y, n.z)); } // Obtener los indices indices.reserve(3 * mesh->mNumFaces); for (unsigned int i = 0; i < mesh->mNumFaces; i++) { indices.push_back(mesh->mFaces[i].mIndices[0]); indices.push_back(mesh->mFaces[i].mIndices[1]); indices.push_back(mesh->mFaces[i].mIndices[2]); }
Se código muestra el código necesario para dibujar un mesh en particular, el número 148, primero cargamos el archivo, con el método ReadFile, este nos devuelve un objeto aiScene este contiene todos los meshes que componen el modelo, obtenemos el deseado, un objeto aiMesh contiene la información de vértices, normales y texturas que necesitamos para dibujar, obtenemos cada uno y lo almacenamos en los contenedores STL vector.
Este código nos servirá para dibujar modelos que estén formados por un solo mesh obteniendo el primer mesh (
const aiMesh* mesh = scene->mMeshes[0]
), para dibujar modelos complejos haremos lo siguiente: Para dibujar una escena completa debemos obtener todos los nodos que componen la escena, una escena tiene un nodo principal, este contiene los meshes que forman el modelo y otros nodos hijos que a su vez contienen esta misma información, para obtener todos los meshes debemos recorrer recursivamente los nodos de la escena.
Creamos la clase Model, esta contiene un conjunto de objetos Mesh que conforman la escena a dibujar, el método processNode será el encargado de recorrer cada uno de los nodos de la escena y obtener cada uno de los objetos Mesh que la conforman.
class Model { private: vector<Mesh> meshes; GLuint vao; // procesar recusivamente cada nodo de la escena void processNode(aiNode* node, const aiScene* scene) { // obtener los mesh de esta escena for (unsigned int i = 0; i < node->mNumMeshes; i++) { Mesh mesh(scene->mMeshes[node->mMeshes[i]]); meshes.push_back(mesh); } // procesar los hijos del nodo for (unsigned int i = 0; i < node->mNumChildren; i++) this->processNode(node->mChildren[i], scene); } public: // cargar el archivo deseado void init(const std::string& file_name) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(file_name, aiProcess_Triangulate); processNode(scene->mRootNode, scene); } // dibujar la escena completa void draw() { for (Mesh m : meshes) m.draw(); } };
La clase Mesh será la encargada de obtener la información necesaria para dibujar cada mesh, vértices, normales, uv, etc., funciona de la misma manera como lo vimos al principio, solo debemos tener en cuenta que, para cada mesh creamos un VAO, almacenamos los buffer correspondientes en este VAO.
class Mesh { public: // dibujar el mesh void draw() { glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, 0); glBindVertexArray(0); }; // inicializar el mesh void init(const aiMesh* mesh) { load(mesh); create(); }; private: vector<glm::vec3> vertex; vector<glm::vec3> normal; vector<glm::vec2> uv; vector<unsigned short> indices; GLuint buffer[4]; GLuint vao; // obtener los datos de cada mesh void load(const aiMesh* mesh) { vertex.reserve(mesh->mNumVertices); uv.reserve(mesh->mNumVertices); normal.reserve(mesh->mNumVertices); indices.reserve(3 * mesh->mNumFaces); for (unsigned int i = 0; i < mesh->mNumVertices; i++) { // Obtener la posicion de cada vertice aiVector3D pos = mesh->mVertices[i]; vertex.push_back(glm::vec3(pos.x, pos.y, pos.z)); // Obetener las coordenadas de textura aiVector3D UVW = mesh->mTextureCoords[0][i]; uv.push_back(glm::vec2(UVW.x, UVW.y)); // Obtener los vectores normales aiVector3D n = mesh->mNormals[i]; normal.push_back(glm::vec3(n.x, n.y, n.z)); } // Obtener los indices for (unsigned int i = 0; i < mesh->mNumFaces; i++) { indices.push_back(mesh->mFaces[i].mIndices[0]); indices.push_back(mesh->mFaces[i].mIndices[1]); indices.push_back(mesh->mFaces[i].mIndices[2]); } } void create() { // generar y activar el VAO glGenVertexArrays(1, &vao); glBindVertexArray(vao); // generar dos ids para los buffer glGenBuffers(4, buffer); // buffer de vertices glBindBuffer(GL_ARRAY_BUFFER, buffer[0]); glBufferData(GL_ARRAY_BUFFER, vertex.size() * sizeof(glm::vec3), &vertex[0], GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(0); // buffer de textura glBindBuffer(GL_ARRAY_BUFFER, buffer[1]); glBufferData(GL_ARRAY_BUFFER, uv.size() * sizeof(glm::vec2), &uv[0], GL_STATIC_DRAW); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(1); // buffer de normales glBindBuffer(GL_ARRAY_BUFFER, buffer[2]); glBufferData(GL_ARRAY_BUFFER, normal.size() * sizeof(glm::vec3), &normal[0], GL_STATIC_DRAW); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(2); // buffer de indices glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW); // desactivar el VAO glBindVertexArray(0); } };
Ejecutamos la aplicación y veremos el resultado, podemos observar que ahora si tenemos el modelo dibujado con todas sus componentes.
En el próximo tutorial veremos cómo añadir texturas y materiales a nuestra escena o modelo 3D y cómo podemos aplicar animaciones a un modelo.
Este modelo no esta incluido, lo puedes descargar de: Dead Pool By TF3DM
GitHub: Importar Modelos con ASSIMP
Comentarios
Publicar un comentario