Well, I did manage to make it work after endless hours! Sort of... I made a model that is transformed in a loop x+5, y+5, x-5, y-5
...
What I ended up doing, well it was the only thing I could think of anyways. Is reading the data from the scene->mAnimations[] and this basicly consists of an array of the keyframes only. So I had to interpolate all the vertices myself, (which is always a funny task to approach!).
Effectivly:
- You get the time the keyframe should have interpolated fully.
- Then subtract where the object currently is to figure out how much you need to move.
- Now you need to figure out how much to move each step, so I took the easiest solution, divided it by how many frames the movement should be splitted over.
- Now it was just a matter of updating all my vertices before sending them to the VBO. (This step is probably a little varying depending on your data-setup)
After those steps, I got something that looks like this:
Header:
class AssimpMesh {
private:
struct ShaderProgram {
//Shader data
GLuint program;
string programName;
vector <GLuint> shaders, uniforms;
};
struct MeshData {
//Mesh data
GLuint meshArray;
vector <GLuint> buffers;
vector <string> bufferNames;
//Shader data
ShaderProgram *shader;
vector <aiVector3D> transedVertices;
int totalIndices;
};
struct Frame {
vector <MeshData*> meshes;
};
struct Animation {
string name;
vector <Frame*> frames;
};
//Global shader data
ShaderProgram *globalShader;
//Model data
Assimp::Importer importer;
const aiScene *scene;
//Mesh data
bool initialized, perMeshShading;
vector <Animation*> animations;
int currentFrame, currentAnimation;
Uint32 lastFrameTicks;
Transform *transform;
glm::mat4 projectionView;
aiVector3D lightPosition;
void loadScene(string filePath);
void loadAnimation(Animation *animation, int numFrames);
void initMesh(aiMesh *mesh, MeshData *data);
...
public:
AssimpMesh(string filePath);
~AssimpMesh();
void draw();
...
};
Source:
void AssimpMesh::loadScene(string filePath) {
//Load animation file
scene = importer.ReadFile(filePath.c_str(), aiProcessPreset_TargetRealtime_MaxQuality);
if (scene) {
if (scene->HasAnimations()) {
for (int i = 0; i < scene->mNumAnimations; i++) {
aiAnimation *anime = scene->mAnimations[i];
int framesInAnimation = ceil(anime->mDuration * ANIMATION_IMPORT_FPS);
Animation *animation = new Animation();
animation->name = anime->mName.C_Str();
loadAnimation(animation, framesInAnimation);
animations.push_back(animation);
}
}
else {
Animation *animation = new Animation();
animation->name = "Default";
loadAnimation(animation, 1);
animations.push_back(animation);
}
printf("Done loading '%s'\n", filePath.c_str());
}
else {
//Report error
printf("Assimp error: %s\n", importer.GetErrorString());
}
}
void AssimpMesh::loadAnimation(Animation *animation, int numFrames) {
int nextKeyframe = -1;
int nextKeyframeId = -1;
int transedFrames = 0;
aiVector3D transPos = aiVector3D();
aiVector3D transVec = aiVector3D();
for (int f = 0; f < numFrames; f++) {
Frame *frame = new Frame();
if (f > nextKeyframe && nextKeyframe < numFrames) {
//Get the new keyframe
aiAnimation *anime = scene->mAnimations[animations.size()];
aiNodeAnim *aniNode = anime->mChannels[0];
aiVectorKey key = aniNode->mPositionKeys[++nextKeyframeId];
nextKeyframe = ceil(key.mTime * ANIMATION_IMPORT_FPS);
if (!nextKeyframeId) {
transVec = key.mValue;
transPos = key.mValue;
}
else {
int transFrames = nextKeyframe - (f - 1);
aiVector3D transDir = key.mValue - transPos;
transPos = key.mValue;
transVec = transDir;
transVec /= transFrames;
transedFrames = 0;
}
}
if (scene->HasLights()) {
aiLight *light = scene->mLights[0];
//lightPosition = light->mPosition;
}
//Put data into vertex arrays
transedFrames++;
aiMesh *mesh;
MeshData *data;
for (int i = 0; i < scene->mNumMeshes; i++) {
mesh = scene->mMeshes[i];
data = new MeshData();
if (!i) {
for (int j = 0; j < mesh->mNumVertices; j++) {
if (!f) {
data->transedVertices.push_back(mesh->mVertices[j] + transVec);
}
else {
data->transedVertices.push_back(animation->frames[f-1]->meshes[i]->transedVertices[j] + transVec);
}
}
}
//Assign VBO
initMesh(mesh, data);
//Assign shader
if (perMeshShading) {
initShader(mesh, data);
setUniforms(mesh, data);
}
frame->meshes.push_back(data);
}
animation->frames.push_back(frame);
}
}
void AssimpMesh::initMesh(aiMesh *mesh, MeshData *data) {
//Buffer for temporary storage of new ids
GLuint id;
//Make vertex array
if (!initialized) {
glGenVertexArrays(1, &id);
}
data->meshArray = id;
//Tell OpenGL to use this array
glBindVertexArray(id);
//Assign vertices
if (mesh->HasPositions()) {
//Make buffer
if (!initialized) {
glGenBuffers(1, &id);
}
data->buffers.push_back(id);
data->bufferNames.push_back("Positions");
//Set buffer data
glBindBuffer(GL_ARRAY_BUFFER, id);
if (data->transedVertices.size()) {
glBufferData(GL_ARRAY_BUFFER, sizeof(aiVector3D) * data->transedVertices.size(), &data->transedVertices[0], GL_STATIC_DRAW);
}
else {
glBufferData(GL_ARRAY_BUFFER, sizeof(aiVector3D) * mesh->mNumVertices, &mesh->mVertices[0], GL_STATIC_DRAW);
}
//Set shader attribute data
glEnableVertexAttribArray(VBO_VERTEX);
glVertexAttribPointer(VBO_VERTEX, 3, GL_FLOAT, GL_FALSE, NULL, NULL);
}
unsigned int matId = mesh->mMaterialIndex;
aiMaterial *material = scene->mMaterials[matId];
vector <aiColor3D> colors;
aiColor3D diffuse(0, 0, 0);
material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);
for (int i = 0; i < mesh->mNumVertices; i++) {
colors.push_back(diffuse);
}
//Make buffer
if (!initialized) {
glGenBuffers(1, &id);
}
data->buffers.push_back(id);
data->bufferNames.push_back("Colors");
//Set buffer data
glBindBuffer(GL_ARRAY_BUFFER, id);
glBufferData(GL_ARRAY_BUFFER, sizeof(aiColor3D) * mesh->mNumVertices, &colors.front(), GL_STATIC_DRAW);
//Set shader attribute data
glEnableVertexAttribArray(VBO_COLOR);
glVertexAttribPointer(VBO_COLOR, 3, GL_FLOAT, GL_FALSE, NULL, NULL);
//Assign texture coords
if (mesh->HasTextureCoords(0)) {
//Make buffer
if (!initialized) {
glGenBuffers(1, &id);
}
data->buffers.push_back(id);
data->bufferNames.push_back("TextureCoords");
//Set buffer data
glBindBuffer(GL_ARRAY_BUFFER, id);
glBufferData(GL_ARRAY_BUFFER, sizeof(aiVector3D) * mesh->mNumVertices, &mesh->mTextureCoords[0], GL_STATIC_DRAW);
//Set shader attribute data
glEnableVertexAttribArray(VBO_TEXCORD);
glVertexAttribPointer(VBO_TEXCORD, 3, GL_FLOAT, GL_FALSE, NULL, NULL);
}
//Assign colors
if (mesh->HasNormals()) {
//Make buffer
if (!initialized) {
glGenBuffers(1, &id);
}
data->buffers.push_back(id);
data->bufferNames.push_back("Normals");
//Set buffer data
glBindBuffer(GL_ARRAY_BUFFER, id);
glBufferData(GL_ARRAY_BUFFER, sizeof(aiVector3D) * mesh->mNumVertices, &mesh->mNormals[0], GL_STATIC_DRAW);
//Set shader attribute data
glEnableVertexAttribArray(VBO_NORMAL);
glVertexAttribPointer(VBO_NORMAL, 3, GL_FLOAT, GL_FALSE, NULL, NULL);
}
if (mesh->HasFaces()) {
vector <unsigned int> indices;
aiFace face;
for (int i = 0; i < mesh->mNumFaces; i++) {
face = mesh->mFaces[i];
for (int j = 0; j < face.mNumIndices; j++) {
indices.push_back(face.mIndices[j]);
}
}
data->totalIndices = indices.size();
//Make buffer
if (!initialized) {
glGenBuffers(1, &id);
}
data->buffers.push_back(id);
data->bufferNames.push_back("Faces");
//Set buffer data
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indices.size(), &indices.front(), GL_STATIC_DRAW);
}
}
Of course, it doesn't work for everything yet. Actually only translation and the entire model. Apparently it doesn't read name values correctly so I can't which meshes the animation is meant for. But it got me going, maybe someone will find this helpful. =)