assimp nodes?
#1 Members - Reputation: 441
Posted 17 June 2012 - 07:58 PM
#2 Members - Reputation: 1408
Posted 18 June 2012 - 04:30 AM
- Using Assimp to load an animation definition from a file and pre-compute as much as possible. This is done once, in the initialization.
- The drawing function computes the interpolation matrices (bones).
- The shader transforms all skins, using the bones matrices.
There are some complications regarding the keys in the aiNodeAnim. You can see it as a frame in a movie. However, there isn't 24 frames per second. Instead, there may be fewer, or more, as needed, at irregular time intervals. When you need a frame in between, you have to interpolate.
Another problem here is that the number of frames (called keys in Assimp) are not the same for rotation, scaling and position. Ift he scaling never change, there may be 0 or one of them. It depends on how much detail is needed to make a good animation. This makes the data structure a little more complicated.
Another complication is how to use interpolation. It is possible to create a transformation matrix from the rotation, location and scaling, but interpolating rotation using transformation matrices are not good. If you interpolate an object moved in an arch defined by two keys 90 degrees rotated to each other, the result will be a straight line (at best). It actually looks quite fun, but is not good. Therefore, you usually should do the interpolation on the quaternions first, then create the transformation matrix.
I use the glm math library. It supports matrices, quaternions, interpolation and all possible matrix math.
#3 Members - Reputation: 345
Posted 18 June 2012 - 05:29 PM
If you still have questions after reading it, i can answer them here.
#4 Members - Reputation: 441
Posted 19 June 2012 - 08:15 PM
#6 Members - Reputation: 441
Posted 19 June 2012 - 09:07 PM
[source lang="cpp"]cbuffer c_buffer{ float4x4 World; float4x4 WorldViewProj; float4x4 FinalTransforms[32];}struct VS_OUT{ float4 position : SV_POSITION;};VS_OUT VShader(float4 position : POSITION, float4 weights : BLENDWEIGHT, int4 boneIndices : BLENDINDICES){ VS_OUT output; float4 p = float4(0.0f, 0.0f, 0.0f, 1.0f); float lastWeight = 0.0f; int n = 3; for(int i = n; i > 0; i--) { lastWeight += weights[i]; p += weights[i] * mul(FinalTransforms[boneIndices[i]], position); } lastWeight = 1.0f - lastWeight; p += lastWeight * mul(FinalTransforms[boneIndices[0]], position); p.w = 1.0f; output.position = mul(WorldViewProj, p); return output;}[/source]
I should also add that i am using opengl and am used to using GLSL when it comes to shader so i am also a little puzzled by the way your reading in uniform variables. I assume that this "float4 position : POSITION, float4 weights : BLENDWEIGHT, int4 boneIndices : BLENDINDICES" is HLSL's way of reading in those variables?
Edited by greenzone, 19 June 2012 - 09:13 PM.
#7 Members - Reputation: 1408
Posted 19 June 2012 - 11:48 PM
uniform mat4 projectionMatrix;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 bonesMatrix[64];
in vec4 vertex;
in vec3 weights;
in vec3 joints;
void main(void){
mat4 animationMatrix =
weights[0] * bonesMatrix[int(joints[0])] + weights[1] * bonesMatrix[int(joints[1])] + weights[2] * bonesMatrix[int(joints[2])];
gl_Position = projectionMatrix*viewMatrix*modelMatrix*animationMatrix*vertex;
}
Notice which ones are uniforms. These are updated every draw(), while the others are the same every time from a VBO.
bonesMatrix: Up to 64 joints can be used in a model. It is a uniform, as the same list of bones are used for all vertices.
vertex: This is a vertex from the mesh that is going to be animated by 0 to 3 bones.
joints: The index of three joints for the current vertex.
weights: The weights to be used for the three joints. There is one set of weights for each vertex.
Edited by larspensjo, 19 June 2012 - 11:52 PM.
#8 Members - Reputation: 345
Posted 20 June 2012 - 02:58 AM
Simply put, bone indices are just numbers that let you know where a certain bone's transformation is in the matrix array. That way you can retrieve the required matrix and multiply it by the weight.
As for my shader, you can ignore the loop. It just ensures that the total weight is unity if the vertex is affected by less than 4 bones. I have no idea why i didn't do that in the preprocessing. Just make sure the total bone weight for a vertex is equal to one when you're importing the data from assimp, and use larspensjo's shader
#9 Members - Reputation: 441
Posted 20 June 2012 - 06:21 PM
"weights[3] * bonesMatrix[int(joints[3])]"
also could you possibly provide the small vertex struct you use to store these values. for example
struct Vertex {
vec3 VertsPos;
vec3 Norms;
vec3 texCoords;
vec3 weights;
vec3 joints;
};
as well as the way you are setting up the VBO to receive joints and weights?
I have everything else ready to go i just want to cross check how one sect up a vbo to receive these other values. if not that is fine you have done more then enough to help. but please let me know about the joint bone equivilant if you can. thanks
Edited by greenzone, 20 June 2012 - 07:25 PM.
#10 Members - Reputation: 1408
Posted 21 June 2012 - 11:55 AM
[source lang="java"] const int AREA1 = vertexCount*sizeof (struct vertexData); const int AREA2 = vertexCount*sizeof (struct weight); const int AREA3 = vertexCount*3*sizeof (float); // 3 bones per vertex, max glGenBuffers(1, &fBufferId); glBindBuffer(GL_ARRAY_BUFFER, fBufferId); glBufferData(GL_ARRAY_BUFFER, AREA1+AREA2+AREA3, 0, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, AREA1, vertexData); glBufferSubData(GL_ARRAY_BUFFER, AREA1, AREA2, weights); glBufferSubData(GL_ARRAY_BUFFER, AREA1+AREA2, AREA3, joints); glGenBuffers(1, &fIndexBufferId); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, fIndexBufferId); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize*sizeof indexData[0], indexData, GL_STATIC_DRAW); glGenVertexArrays(1, &fVao); glBindVertexArray(fVao); glEnableVertexAttribArray(StageOneShader::Normal); // Using pre defined enums glEnableVertexAttribArray(StageOneShader::Vertex); glEnableVertexAttribArray(StageOneShader::Texture); glEnableVertexAttribArray(StageOneShader::SkinWeights); glEnableVertexAttribArray(StageOneShader::Joints); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, fIndexBufferId); // Will be remembered in the VAO state glVertexAttribPointer(StageOneShader::Normal, 3, GL_FLOAT, GL_FALSE, sizeof (struct vertexData), &p->fNormal[0]); glVertexAttribPointer(StageOneShader::Texture, 2, GL_FLOAT, GL_FALSE, sizeof (struct vertexData), &p->fTexture[0]); glVertexAttribPointer(StageOneShader::Vertex, 3, GL_FLOAT, GL_FALSE, sizeof (struct vertexData), &p->fVertex[0]); glVertexAttribPointer(StageOneShader::SkinWeights, 3, GL_FLOAT, GL_FALSE, sizeof (glm::vec3), (void *)skinOffset); glVertexAttribPointer(StageOneShader::Joints, 3, GL_FLOAT, GL_FALSE, 3*sizeof (float), (void *)jointOffset); glBindVertexArray(0);[/source]
Some comment about this:
- My vertex data doesn't include the weights and bones index. Your way of having all data in one struct is probably more efficient. I did it this way because of historical reasons (there was no weights or bones index in the first attempt).
- The generic vertex attribute index is a predefined constant, to enable me to use the same VAO on more than one shader.
- I am thinking of using GL_BYTE for the bones index, but I didn't get that to work.
- Change the shader so as to use the identity matrix instead of bones matrix. That should draw the mesh in bind pose.
- Do the same thing, but use bone indices to make a color. That way, you can verify that the right bones are selected by the indices.
- Instead, use weight information to make a color, that way you can test that the weights are correctly transferred.
Edited by larspensjo, 21 June 2012 - 12:06 PM.
#11 Members - Reputation: 345
Posted 24 June 2012 - 11:42 AM
... I just have three more questions. in your GLSL vertex shader I assume your "joints = bones"? ...
Yes. These terms are used interchangeably in animation.






