Created
October 21, 2020 20:13
-
-
Save mlfarrell/a53e7c8b17f19d6a4e47fe2dc01e48ce to your computer and use it in GitHub Desktop.
Skeletal animation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static const int currentAnimation = 0; | |
typedef unordered_map<aiString, const aiNodeAnim *> NodeAnimMap; | |
void EntityModel::boneAnimTransform(float timeInSeconds, vutil::GrowableArray *transforms, aiScene *aiSceneObj, const float16 *globalInverseTransform, bool loop) | |
{ | |
const aiScene *scene = aiSceneObj; | |
mat4 identity = mat4Identity; | |
if(!nodeAnimMapPtr) | |
{ | |
nodeAnimMapPtr = new NodeAnimMap(); | |
} | |
//NSLog(@"%d", scene->mNumAnimations); | |
float ticksPerSecond = (float)(scene->mAnimations[currentAnimation]->mTicksPerSecond != 0 ? scene->mAnimations[currentAnimation]->mTicksPerSecond : 25.0f); | |
float timeInTicks = timeInSeconds * ticksPerSecond; | |
float animationTime = timeInTicks; | |
if(loop) | |
animationTime = fmod(animationTime, (float)scene->mAnimations[currentAnimation]->mDuration); | |
else | |
animationTime = min<float>(animationTime, scene->mAnimations[currentAnimation]->mDuration-0.0001f); | |
mat4 *globalInverse = (mat4 *)globalInverseTransform; | |
readAnimNodeHeirarchy(animationTime, scene->mRootNode, identity, scene, *globalInverse); | |
if(transforms->getCount() == 0) | |
transforms->addElements((int)boneMapping().size()); | |
for(uint i = 0; i < boneMapping().size(); i++) | |
{ | |
memcpy(transforms->elementAtIndex(i), &((BoneTransformInfo *)editBoneTransforms->elementAtIndex(i))->finalTransform, sizeof(float16)); | |
} | |
} | |
static const aiNodeAnim *findNodeAnim(const aiAnimation *pAnimation, const aiString &NodeName, NodeAnimMap &nodeMap) | |
{ | |
auto iter = nodeMap.find(NodeName); | |
if(iter != nodeMap.end()) | |
{ | |
return iter->second; | |
} | |
for(uint i = 0; i < pAnimation->mNumChannels; i++) | |
{ | |
const aiNodeAnim *pNodeAnim = pAnimation->mChannels[i]; | |
if(pNodeAnim->mNodeName == NodeName) | |
{ | |
nodeMap[NodeName] = pNodeAnim; | |
return pNodeAnim; | |
} | |
} | |
nodeMap[NodeName] = NULL; | |
return NULL; | |
} | |
static uint findPosition(float AnimationTime, const aiNodeAnim* pNodeAnim) | |
{ | |
for (uint i = 0 ; i < pNodeAnim->mNumPositionKeys - 1 ; i++) { | |
if (AnimationTime < (float)pNodeAnim->mPositionKeys[i + 1].mTime) { | |
return i; | |
} | |
} | |
assert(0); | |
return 0; | |
} | |
static uint findRotation(float AnimationTime, const aiNodeAnim* pNodeAnim) | |
{ | |
assert(pNodeAnim->mNumRotationKeys > 0); | |
for (uint i = 0 ; i < pNodeAnim->mNumRotationKeys - 1 ; i++) { | |
if (AnimationTime < (float)pNodeAnim->mRotationKeys[i + 1].mTime) { | |
return i; | |
} | |
} | |
assert(0); | |
return 0; | |
} | |
static uint findScaling(float AnimationTime, const aiNodeAnim* pNodeAnim) | |
{ | |
assert(pNodeAnim->mNumScalingKeys > 0); | |
for (uint i = 0 ; i < pNodeAnim->mNumScalingKeys - 1 ; i++) { | |
if (AnimationTime < (float)pNodeAnim->mScalingKeys[i + 1].mTime) { | |
return i; | |
} | |
} | |
assert(0); | |
return 0; | |
} | |
static float clamp(float f) | |
{ | |
return (f < 0.0f) ? 0.0f : ((f > 1.0f) ? 1.0f : f); | |
} | |
static void calcInterpolatedPosition(aiVector3D &Out, float AnimationTime, const aiNodeAnim *pNodeAnim) | |
{ | |
if(pNodeAnim->mNumPositionKeys == 1) | |
{ | |
Out = pNodeAnim->mPositionKeys[0].mValue; | |
return; | |
} | |
uint PositionIndex = findPosition(AnimationTime, pNodeAnim); | |
uint NextPositionIndex = (PositionIndex + 1); | |
assert(NextPositionIndex < pNodeAnim->mNumPositionKeys); | |
float DeltaTime = (float)(pNodeAnim->mPositionKeys[NextPositionIndex].mTime - pNodeAnim->mPositionKeys[PositionIndex].mTime); | |
float Factor = clamp((AnimationTime - (float)pNodeAnim->mPositionKeys[PositionIndex].mTime) / DeltaTime); | |
const aiVector3D& Start = pNodeAnim->mPositionKeys[PositionIndex].mValue; | |
const aiVector3D& End = pNodeAnim->mPositionKeys[NextPositionIndex].mValue; | |
aiVector3D Delta = End - Start; | |
Out = Start + Factor * Delta; | |
} | |
static void calcInterpolatedRotation(aiQuaternion &Out, float AnimationTime, const aiNodeAnim *pNodeAnim) | |
{ | |
// we need at least two values to interpolate... | |
if (pNodeAnim->mNumRotationKeys == 1) { | |
Out = pNodeAnim->mRotationKeys[0].mValue; | |
return; | |
} | |
uint RotationIndex = findRotation(AnimationTime, pNodeAnim); | |
uint NextRotationIndex = (RotationIndex + 1); | |
assert(NextRotationIndex < pNodeAnim->mNumRotationKeys); | |
float DeltaTime = (float)(pNodeAnim->mRotationKeys[NextRotationIndex].mTime - pNodeAnim->mRotationKeys[RotationIndex].mTime); | |
float Factor = clamp((AnimationTime - (float)pNodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime); | |
const aiQuaternion& StartRotationQ = pNodeAnim->mRotationKeys[RotationIndex].mValue; | |
const aiQuaternion& EndRotationQ = pNodeAnim->mRotationKeys[NextRotationIndex].mValue; | |
aiQuaternion::Interpolate(Out, StartRotationQ, EndRotationQ, Factor); | |
Out = Out.Normalize(); | |
} | |
static void calcInterpolatedScaling(aiVector3D &Out, float AnimationTime, const aiNodeAnim* pNodeAnim) | |
{ | |
if (pNodeAnim->mNumScalingKeys == 1) { | |
Out = pNodeAnim->mScalingKeys[0].mValue; | |
return; | |
} | |
uint ScalingIndex = findScaling(AnimationTime, pNodeAnim); | |
uint NextScalingIndex = (ScalingIndex + 1); | |
assert(NextScalingIndex < pNodeAnim->mNumScalingKeys); | |
float DeltaTime = (float)(pNodeAnim->mScalingKeys[NextScalingIndex].mTime - pNodeAnim->mScalingKeys[ScalingIndex].mTime); | |
float Factor = clamp((AnimationTime - (float)pNodeAnim->mScalingKeys[ScalingIndex].mTime) / DeltaTime); | |
//assert(Factor >= 0.0f && Factor <= 1.0f); | |
const aiVector3D& Start = pNodeAnim->mScalingKeys[ScalingIndex].mValue; | |
const aiVector3D& End = pNodeAnim->mScalingKeys[NextScalingIndex].mValue; | |
aiVector3D Delta = End - Start; | |
Out = Start + Factor * Delta; | |
} | |
void EntityModel::readAnimNodeHeirarchy(float animationTime, const aiNode *pNode, const mat4 &parentTransform, const aiScene *scene, const mat4 &globalInverseTransform) | |
{ | |
const aiString &nodeName = pNode->mName; | |
const aiAnimation *pAnimation = scene->mAnimations[currentAnimation]; | |
mat4 nodeTransformation; | |
aiMatrix4x4 nodeTrans = pNode->mTransformation; | |
memcpy(&nodeTransformation, &(nodeTrans.Transpose()), sizeof(float16)); | |
const aiNodeAnim *pNodeAnim = findNodeAnim(pAnimation, nodeName, *((NodeAnimMap *)nodeAnimMapPtr)); | |
if(pNodeAnim) | |
{ | |
// Interpolate scaling and generate scaling transformation matrix | |
aiVector3D scaling; | |
calcInterpolatedScaling(scaling, animationTime, pNodeAnim); | |
mat4 scalingM = mat4::makeScale(scaling.x, scaling.y, scaling.z); | |
// Interpolate rotation and generate rotation transformation matrix | |
aiQuaternion rotationQ; | |
calcInterpolatedRotation(rotationQ, animationTime, pNodeAnim); | |
quat q = quat(rotationQ.x, rotationQ.y, rotationQ.z, rotationQ.w); | |
mat4 rotationM = mat4(q); | |
// Interpolate translation and generate translation transformation matrix | |
aiVector3D translation; | |
calcInterpolatedPosition(translation, animationTime, pNodeAnim); | |
mat4 translationM = mat4::makeTranslation(translation.x, translation.y, translation.z); | |
// Combine the above transformations | |
nodeTransformation = (translationM * rotationM) * scalingM; | |
} | |
mat4 globalTransformation = parentTransform * nodeTransformation; | |
auto iter = boneMapping().find(nodeName); | |
if(iter != boneMapping().end()) | |
{ | |
uint boneIndex = iter->second; | |
BoneTransformInfo *boneInfo = (BoneTransformInfo *)editBoneTransforms->elementAtIndex(boneIndex); | |
mat4 *boneOff = (mat4 *)&boneInfo->boneOffset; | |
mat4 trans = (globalInverseTransform * globalTransformation) * (*boneOff); | |
memcpy(&boneInfo->finalTransform, &trans, sizeof(float16)); | |
} | |
for(uint i = 0; i < pNode->mNumChildren; i++) | |
{ | |
readAnimNodeHeirarchy(animationTime, pNode->mChildren[i], globalTransformation, scene, globalInverseTransform); | |
} | |
} | |
BoneMap &EntityModel::boneMapping() | |
{ | |
if(!boneMappingPtr) | |
boneMappingPtr = new BoneMap(); | |
return *((BoneMap *)boneMappingPtr); | |
} | |
size_t EntityModel::boneTransformsSize() | |
{ | |
return boneMapping().size(); | |
} | |
void EntityModel::updateBoneAnimVBOs(vgl::BufferArray *fromBuffer, int fromBufferIndex) | |
{ | |
if(editBoneData && editBoneData->getCount()) | |
{ | |
if(!fromBuffer) | |
{ | |
boneBuffers = make_shared<vgl::BufferArray>(); | |
boneBuffers->provideData(0, sizeof(BoneData) * editBoneData->getCount(), editBoneData->getData()); | |
} | |
else | |
{ | |
boneBuffers = make_shared<vgl::BufferArray>(); | |
fromBuffer->copyDataTo(boneBuffers.get(), 0, 0, sizeof(BoneData) * editBoneData->getCount()); | |
} | |
setupVAO(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void SkeletalAnimationShaderEffect::setupBoneTransformUniformLocations(vutil::GrowableArray::Pointer bt) | |
{ | |
if(uniformBoneTransformLocations) | |
free(uniformBoneTransformLocations); | |
uniformBoneTransformLocations = (GLint *)malloc(sizeof(GLint)*MAX_BONES*2); | |
for(int i = 0; i < MAX_BONES; i++) | |
{ | |
ostringstream ostr; | |
ostr << "bones[" << i << "]"; | |
auto name = ostr.str(); | |
GLint loc = glGetUniformLocation(shaderProgram, name.c_str()); | |
uniformBoneTransformLocations[i] = loc; | |
loc = glGetUniformLocation(lightRenderShaderProgram, name.c_str()); | |
uniformBoneTransformLocations[MAX_BONES+i] = loc; | |
} | |
} | |
//... | |
void SkeletalAnimationShaderEffect::prepareToDrawFromState(vgl::StateMachine *machine) | |
{ | |
//... | |
glBindBufferBase(GL_UNIFORM_BUFFER, 0, boneTransformsUBO); | |
glUniformBlockBinding(shaderProgram, boneTransformsUniformBlockIndex, 0); | |
UserShaderEffect::prepareToDrawFromState(machine); | |
} | |
void SkeletalAnimationShaderEffect::setBoneTransforms(vutil::GrowableArray::Pointer bt) | |
{ | |
boneTransforms = bt; | |
glBindBuffer(GL_UNIFORM_BUFFER, boneTransformsUBO); | |
glBufferData(GL_UNIFORM_BUFFER, boneTransforms->getCount()*boneTransforms->getElementSize(), boneTransforms->getData(), GL_DYNAMIC_DRAW); | |
//setupBoneTransformUniformLocations(bt); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//... | |
//Load skeletal animation bones | |
if(mesh->mNumBones) | |
{ | |
if(!e.editBoneData) | |
e.editBoneData = make_shared<GrowableArray>(sizeof(BoneData)); | |
e.editBoneData->addElements(mesh->mNumVertices); | |
for(int j = 0; j < mesh->mNumVertices; j++) | |
{ | |
BoneData *bdata = (BoneData *)e.editBoneData->elementAtIndex(baseVertex+j); | |
memset(bdata, 0, sizeof(BoneData)); | |
} | |
} | |
//... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
in vec4 position; | |
in vec3 normal; | |
in vec2 texcoord0; | |
in ivec4 boneIDs; | |
in vec4 weights; | |
#ifdef SSAO | |
out mediump vec4 va_pos_proj; | |
#endif | |
out vec2 varTexcoord; | |
out vec3 varNormal; | |
out mediump vec4 ec_pos; | |
out float shadowBias; | |
out vec4 shadowCoord; | |
#ifdef SECOND_SHADOW | |
out vec4 secondShadowCoord; | |
#endif | |
uniform float shadowBiasFactor; | |
const int MAX_BONES = 145; | |
layout (std140) uniform InstancedBones | |
{ | |
mat4 bones[MAX_BONES]; | |
}; | |
uniform highp mat4 modelViewProjectionMatrix; | |
uniform highp mat4 modelViewMatrix; | |
uniform mat3 normalMatrix; | |
struct Light | |
{ | |
mediump vec4 position; | |
lowp vec4 ambient, diffuse, specular; | |
mediump vec3 spotDirection; | |
float spotExponent, spotCosCutoff; | |
float constantAttenuation, linearAttenuation, quadraticAttenuation; | |
mat4 biasShadowMatrix; | |
}; | |
#ifdef SECOND_SHADOW | |
uniform mat4 secondBiasShadowMatrix; | |
#endif | |
uniform Light lights[8]; | |
//debug | |
//out vec3 transformedPosition; | |
void main() | |
{ | |
mat4 boneTransform = bones[boneIDs[0]] * weights[0]; | |
boneTransform += bones[boneIDs[1]] * weights[1]; | |
boneTransform += bones[boneIDs[2]] * weights[2]; | |
boneTransform += bones[boneIDs[3]] * weights[3]; | |
vec4 norm = boneTransform * vec4(normal, 0.0); | |
varTexcoord = texcoord0; | |
varNormal = normalMatrix * norm.xyz; | |
vec4 pos = boneTransform * position; | |
//Compute vert position in eye coordinates | |
ec_pos = (modelViewMatrix * pos); | |
shadowBias = shadowBiasFactor; | |
shadowCoord = lights[0].biasShadowMatrix * pos; | |
#ifdef SECOND_SHADOW | |
secondShadowCoord = secondBiasShadowMatrix * pos; | |
#endif | |
#ifdef SSAO | |
va_pos_proj = modelViewProjectionMatrix * pos; | |
gl_Position = va_pos_proj; | |
#else | |
gl_Position = modelViewProjectionMatrix * pos; | |
#endif | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment