How do I get models to animate using Assimp?
Asked Answered
F

1

5

Currently I'm trying to make a game engine in C++ with OpenGL and want to get 3D animations to work. I have been advised to use Assimp and was able to find a tutorial to get static models to work, but I have no idea where to even start with animations. I have been trying to Google it, but haven't been able to find anything that works. How can I modify my code to get animations? What file format is recommended for it?

This is the code I have currently:

//Mesh.h    
#include <string>

#include "glut\include\GL\glew.h"
#include "glut\include\GL\glut.h"

#include <assimp/Importer.hpp>      // C++ importer interface
#include <assimp/scene.h>           // Output data structure
#include <assimp/postprocess.h>     // Post processing fla

//textures
#include <SOIL.h>

class Mesh
{
public:
    Mesh(void);
    Mesh(std::string filename, std::string textureFilename, float x, float y, float z, float width, float height, float depth, float rotX, float rotY, float rotZ);
    ~Mesh(void);

    void Init(std::string filename);
    void LoadTexture(std::string textureName);
    void Draw();

private:
    GLfloat *vertexArray;
    GLfloat *normalArray;
    GLfloat *uvArray;

    GLint numVerts;

    GLuint m_Texture[1];

    float m_CenterX, m_CenterY, m_CenterZ, m_Width, m_Height, m_Depth;
    float m_XRotation, m_YRotation, m_ZRotation;
};

//Mesh.cpp
#include "Mesh.h"

Mesh::Mesh(void)
{
}

Mesh::Mesh(std::string filename, std::string textureFilename, float x, float y, float z, float width, float height, float depth, float rotX, float rotY, float rotZ)
{
    //fills in variables
    Init(filename);
    LoadTexture(textureFilename);
}

Mesh::~Mesh(void)
{

}

void Mesh::Init(std::string filename)
{
    Assimp::Importer importer;
    const aiScene *scene = importer.ReadFile(filename,aiProcessPreset_TargetRealtime_Fast);//aiProcessPreset_TargetRealtime_Fast has the configs you'll need

    aiMesh *mesh = scene->mMeshes[0]; //assuming you only want the first mesh

    numVerts = mesh->mNumFaces*3;

    vertexArray = new float[mesh->mNumFaces*3*3];
    normalArray = new float[mesh->mNumFaces*3*3];
    uvArray = new float[mesh->mNumFaces*3*2];

    for(unsigned int i=0;i<mesh->mNumFaces;i++)
    {
        const aiFace& face = mesh->mFaces[i];

        for(int j=0;j<3;j++)
        {
            aiVector3D uv = mesh->mTextureCoords[0][face.mIndices[j]];
            memcpy(uvArray,&uv,sizeof(float)*2);
            uvArray+=2;

            aiVector3D normal = mesh->mNormals[face.mIndices[j]];
            memcpy(normalArray,&normal,sizeof(float)*3);
            normalArray+=3;

            aiVector3D pos = mesh->mVertices[face.mIndices[j]];
            memcpy(vertexArray,&pos,sizeof(float)*3);
            vertexArray+=3;
        }
    }

    uvArray-=mesh->mNumFaces*3*2;
    normalArray-=mesh->mNumFaces*3*3;
    vertexArray-=mesh->mNumFaces*3*3;
}

void Mesh::LoadTexture(std::string textureName)         
{
    glGenTextures(1, &m_Texture[0]);
    glBindTexture(GL_TEXTURE_2D, m_Texture[0]);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  // NOTE the GL_NEAREST Here! 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  // NOTE the GL_NEAREST Here! 

    m_Texture[0] = SOIL_load_OGL_texture // load an image file directly as a new OpenGL texture
    (
        textureName.c_str(),
        SOIL_LOAD_AUTO,
        SOIL_CREATE_NEW_ID,
        SOIL_FLAG_MIPMAPS | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT
    );
}

void Mesh::Draw()
{
    glPushMatrix();
        glTranslatef(m_CenterX, m_CenterY, m_CenterZ);

        glRotatef(m_XRotation, 1, 0, 0);
        glRotatef(m_YRotation, 0, 1, 0);
        glRotatef(m_ZRotation, 0, 0, 1);

        glScalef(m_Width, m_Height, m_Depth);

        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glEnableClientState(GL_VERTEX_ARRAY);

            glNormalPointer(GL_FLOAT,0,normalArray);
            glTexCoordPointer(2,GL_FLOAT,0,uvArray);
            glVertexPointer(3,GL_FLOAT,0,vertexArray);

            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, m_Texture[0]);
            glDrawArrays(GL_TRIANGLES,0,numVerts);

        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);
    glPopMatrix();
}
Faxan answered 18/2, 2015 at 23:16 Comment(0)
L
13

This is an old question but I'm sure it could be of future use to others so I'll try to outline some options you have for doing animation with the Assimp library.

First off, I'd just like to mention that you can do animation without the Assimp library. The library just provides you with a nice way to load your models, but as you've discovered it won't do the animation for you.

Conceptually animation is going to be fairly similar regardless of whether or not you use Assimp. For example; if you have written your own model loader you could easily use that instead of Assimp and still be able to do animation. However, as there is more than one way to do animation, you may be more limited in the way you can achieve it as doing skeletal animation without Assimp would involve writing a model loader that can get the bone transforms, weights and various data out of the model files, and that could take a while.

There are multiple ways to do the animation; both in terms of technique and whether you want to do it with hardware acceleration (on the GPU or on the CPU). I'm going to mention a few of the options you have here since most people use Assimp to do skeletal animation which can be pretty intimidating if you aren't strong in your math skills and just want something that is easy to put together.

Generally there are three accepted ways to do animation:

  1. Keyframe
  2. Keframe with Interpolation
  3. Skeletal Animation (hardware skinning)

Keyframe

Keyframe animation is when you create a separate model for each frame of the animation, similar to a 2D sprite sheet. You render the models in succession to produce the animation. This method is probably the simplest, but most naive implementation, since you need to load multiple models for each animation. The transitions between these frames may be noticeable depending on how many frames you produce and you may need to export several model files before it looks acceptable. Another downside to this method is that you would likely need to produce your own models.

Keyframe with Interpolation

This method is similar to the above however rather than producing each key frame as a separate model, just a few key frames are produced and the "missing" models are produced with the engine using interpolation. We can do this because if we know the starting point of a vertex and the ending point of a vertex we can interpolate to find out where the vertex should be at time = t.

This tutorial does a great job of explaining how to do key frame animation:

https://www.khronos.org/opengl/wiki/Keyframe_Animation

Again, it doesn't talk about Assimp but the concepts are the same, and you can still use Assimp to load your models. This form of animation is fairly simple to implement and is quite good for a beginner. However it does come with some drawbacks. If you choose to go this route you may be limited by memory as this method can consume a lot of memory with VBO's, this will depend on how detailed your models are. If you choose to create your own models you will also want to preserve the vertex order in the model files so that interpolating between vertex 2 of one model file (key frame 1) to vertex 2 of another model file (key frame 2) will be correct.

Skeletal Animation

This is probably the most difficult way to do animation but it deals with a lot of the issues in method 1 and 2. You'll also find that by doing skeletal animation you are able to load a lot of the newer file formats; those that specify bone transformation and rotations rather than having to load new files for each key frame.

This is one case where I think having Assimp will be of great benefit. Assimp is very well equipped to deal with getting the data you need out of the model file to do skeletal animation.

If you are interested in doing skeletal animation this tutorial is a fantastic way to go about it, and even uses Assimp as well.

http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html

I used this tutorial myself to achieve skeletal animation in my own engine. So I strongly encourage you to read this if you decide to go down that path.

The last thing I will mention that I've noticed confuses some people is that animation can be done using hardware acceleration, but it's not strictly necessary.

In the previous tutorial links I provided, both of these are doing animation using hardware acceleration. In this context, it means that the vertex computations are being done on the GPU in the body of the vertex shader.

However I know that a lot people may not be familiar with modern OpenGL in which case you can still do these same calculations on the CPU. The idea here being be to look at what is happening in the vertex shader and create a function that performs those calculations for you.

You also asked about file formats for animation; this is going to depend what route you take to do the animation. If you want to do animation for formats like .fbx and .md5 you will likely be doing skeletal animation. If you go for key frame animation I would probably stick with .obj, this is the format I find the easiest to work with as the format specification is quite easy to understand.

While you're debugging the animation in your engine make sure you have a file which you know works; another pitfall is that downloading free models on the internet can contain any old format, absolute texture paths and different coordinate systems (Y is up or Z is up) etc.

Lesslie answered 15/8, 2018 at 11:55 Comment(2)
Hi, can u show me a minimal example on loading a Mixamo FBX animation in ASSIMP and render in OpenGL? I can't even found such a simple example on the internetEelgrass
Hi Nicholas, Please checkout my video on Mixamo and FBX: youtu.be/7JMehLi2vWk. Thanks, Etay.Memorialize

© 2022 - 2024 — McMap. All rights reserved.