PDA

View Full Version : Skeletal Animation problem



Asmodeus
12-29-2015, 08:29 AM
Hello, i am having a really annoying problem with my skeletal animation implementation. First here is the situation. I have implemented COMPLETELY the very same skeletal animation in a C++ engine of mine i created a few months ago. Now i decided to redo the engine in Java. But since some libraries are missing for java i decided to actually make a "loader" inside my C++ engine and export the data for several models as a flat bin file with data, containing vertices, normals, uv etc. So far everything is working fine , OBJ and Collada files can be read from the file and displayed. My problems come with the animation. I am able to read the vertex information from the file (for the animated file), it contains standard per vertex data and boneIndex and boneWeight (also per vertex). The second file is the animation itself - a bunch of pre-calculated matrices
The vertex information and the animation information are properly imported in Java (tripple checked). The model is rendering fine if i do not apply any animations to it.

Here is the shader i am testing with (working fine in my old engine). If i flag the isAnimatedModel as false , the model is rendered as expected, without any animation applied and static. Which leads me to the conclusion that every bit of data up until the BoneID and BoneWeight is working fine. The problem that is occuring is comming either from gBones , BoneID or BoneWeight. My assumption is that the BoneID(which is an index data) is corrupted , screenshot below ! BoneID is a int[4] currently so ivec4 covers it



layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
layout (location = 2) in vec2 TexCoord;
layout (location = 3) in vec3 Tangent;
layout (location = 4) in float Tid;
layout (location = 5) in float Mid;
layout (location = 6) in ivec4 BoneIDs;
layout (location = 7) in vec4 Weights;

#include "Shaders/UtilityShaders/Consts.txt"
#include "Shaders/UtilityShaders/Uniforms.txt"
#include "Shaders/UtilityShaders/Shadow/vShadow.txt"
#include "Shaders/UtilityShaders/Light/vLight.txt"
#include "Shaders/UtilityShaders/Fog/vFog.txt"

uniform float isAnimatedModel;
uniform mat4 gBones[MAX_BONES];

void main()
{
vec4 PosL = vec4(0.0);
vec4 NormalL = vec4(0.0);
mat4 gWorld = mTranslate;
mat4 gWVP = mProjection*mView;

if(isAnimatedModel > 0.5)
{
//ivec4 boneIDs = ivec4(int(BoneIDs[0] + 0.5),int(BoneIDs[1] + 0.5),int(BoneIDs[2] + 0.5),int(BoneIDs[3] + 0.5));
mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
BoneTransform += gBones[BoneIDs[1]] * Weights[1];
BoneTransform += gBones[BoneIDs[2]] * Weights[2];
BoneTransform += gBones[BoneIDs[3]] * Weights[3];
PosL = BoneTransform * vec4(Position, 1.0);
NormalL = BoneTransform * vec4(Normal, 0.0);
}
else {
PosL = vec4(Position, 1.0);
NormalL = vec4(Normal, 0.0);
}
vec4 worldPosition = gWorld * PosL;
gl_Position = gWVP * worldPosition;


The actual code from Java. The convert functions just take the contents of the List<DATA> and converts it to FloatBuffer or IntBuffer (for the BoneID).


//Index Buffer stuff here

//VertexArrayObject (VAO class)
vertexArray.bindBuffer();

//All vertex data compacted in the vertexBuffer (VBO class)
vertexBuffer.bindBuffer();

//Load the VertexAttrixPointer offsets and all
loadToVertexPointer();

//Load the data from the vertexData to a float buffer
vertexBuffer.setBufferData(VertexData.convert(vert exData));
vertexBuffer.unbindBuffer();

//All BoneIndex and BoneWeight compacted in the boneBuffer (VBO class)
// At first i used only ONE buffer but i decided to divide the information
//since the BoneID is INT and BoneWeight is FLOAT


boneId.bindBuffer();
VertexPointer.enable(Constants.ATTRIB_LOCATION_BON EID);

//Set vertex attrib pointer as !! VertexAttribIPointer !! (here is where i assume is the problem) .
//The odd thing is that in my C++ version it works standard (float) VertexAttribPointer
VertexPointer.seti(Constants.ATTRIB_LOCATION_BONEI D, 4, 16, 0);

int[] ids = BoneData.convertId(bonesData);
boneId.setBufferData(ids);
boneId.unbindBuffer();

boneWt.bindBuffer();
VertexPointer.enable(Constants.ATTRIB_LOCATION_WEI GHT);

//Standard (float) VertexAttribPointer
VertexPointer.setf(Constants.ATTRIB_LOCATION_WEIGH T, 4, 16, 0);

float[] wts = BoneData.convertWt(bonesData);
boneWt.setBufferData(wts);
boneWt.unbindBuffer();

vertexArray.unbindBuffer();


From my C++ version how this looks. Bone and Vertex states are just the VertexArrayPointer states



ibo->IBO_Bind();
ibo->IBO_BufferData(Indices.size(), Indices);
ibo->IBO_Unbind();

vao->VAO_Bind();
vertex_vbo->VBO_Bind();
vertex_vbo->VBO_BufferData(modeldata);
VertexState();
vertex_vbo->VBO_Unbind();

bone_vbo->VBO_Bind();
bone_vbo->VBO_BufferData(Bones);
BoneState();
bone_vbo->VBO_Unbind();
vao->VAO_Unbind();


Animating the model. I am using a 3d array of matrices to store the animation.
Matrix data -> [number_animations][animation_matrices_per_animation][bone_matrices]




private void shaderLoadAnimationBones() {

for (int i = 0; i < animationStack[index].length; i++) {
modelShader.loadUniformMat4f(animationStack[index][i], modelShader.getUniformId("gBones", i));
}
}

@Override
public void animate() {
// TODO Auto-generated method stub
if (!isAnimated) {
return;
}

if (currAnimation != prevAnimation) {
animationStack = ((ColladaData) modelData).getAnimationData()[currAnimation];
}

int size = animationStack.length;
runningTime += DisplayManager.getElapsedTime();
double timeInTicks = runningTime * Constants.TICKS_PER_SECOND;
double animationTime = timeInTicks % (size - 1);
index = (int) animationTime;
}



The convert functions which i made to actually divide the data so i can load the boneIDs as IntBuffer and the boneWeights as FloatBuffer




private static void arrayToArray(float[] array, int[] src, MInteger startIndex) {
for (int i = 0; i < src.length; i++) {
array[startIndex.value++] = src[i];
}
}

private static void arrayToArray(int[] array, int[] src, MInteger startIndex) {
for (int i = 0; i < src.length; i++) {
array[startIndex.value++] = src[i];
}
}
public static int[] convertId(List<BoneData> boneData) {
MInteger index = new MInteger();
int[] vertexDataResult = new int[Constants.NUM_BONES_PER_VEREX * boneData.size()];

for (BoneData vertex : boneData) {
arrayToArray(vertexDataResult, vertex.boneId, index);
}
return vertexDataResult;
}

public static float[] convertWt(List<BoneData> boneData) {
MInteger index = new MInteger();
float[] vertexDataResult = new float[Constants.NUM_BONES_PER_VEREX * boneData.size()];

for (BoneData vertex : boneData) {
arrayToArray(vertexDataResult, vertex.boneWeight, index);
}
return vertexDataResult;
}




http://i67.tinypic.com/332qduh.png

Asmodeus
12-29-2015, 09:57 AM
Short after this i just went and did this small change:
From this: modelShader.loadUniform4f(animationStack[index][i], modelShader.getUniformId("gBones", i));
To that: modelShader.loadUniform4fTransposed(animationStack[index][i], modelShader.getUniformId("gBones", i));

Turns out that for some reason Assimp4x4Matrix (which i was using) are reversed major , i have no idea why this would work in my old engine as normal (non-trasposed matrix) I am actually confused here ! The animation works fine now ....

http://gamedev.stackexchange.com/questions/47505/opengl-glsl-skinning-problem

Here is the man , who has only 1 vote and saved my day, thank you!