init
This commit is contained in:
commit
444f800536
122 changed files with 17137 additions and 0 deletions
319
shared/UtilsAnim.cpp
Normal file
319
shared/UtilsAnim.cpp
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
#include "UtilsAnim.h"
|
||||
|
||||
#include "UtilsGLTF.h"
|
||||
#include "VulkanApp.h"
|
||||
#include <assimp/GltfMaterial.h>
|
||||
#include <assimp/cimport.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/types.h>
|
||||
#include <cmath>
|
||||
|
||||
using glm::quat;
|
||||
|
||||
AnimationChannel initChannel(const aiNodeAnim* anim)
|
||||
{
|
||||
AnimationChannel channel;
|
||||
channel.pos.resize(anim->mNumPositionKeys);
|
||||
|
||||
for (uint32_t i = 0; i < anim->mNumPositionKeys; ++i) {
|
||||
channel.pos[i] = { .pos = aiVector3DToVec3(anim->mPositionKeys[i].mValue), .time = (float)anim->mPositionKeys[i].mTime };
|
||||
}
|
||||
|
||||
channel.rot.resize(anim->mNumRotationKeys);
|
||||
for (uint32_t i = 0; i < anim->mNumRotationKeys; ++i) {
|
||||
channel.rot[i] = { .rot = aiQuaternionToQuat(anim->mRotationKeys[i].mValue), .time = (float)anim->mRotationKeys[i].mTime };
|
||||
}
|
||||
|
||||
channel.scale.resize(anim->mNumScalingKeys);
|
||||
for (uint32_t i = 0; i < anim->mNumScalingKeys; ++i) {
|
||||
channel.scale[i] = { .scale = aiVector3DToVec3(anim->mScalingKeys[i].mValue), .time = (float)anim->mScalingKeys[i].mTime };
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
template <typename T> uint32_t getTimeIndex(const std::vector<T>& t, float time)
|
||||
{
|
||||
return std::max(
|
||||
0,
|
||||
(int)std::distance(t.begin(), std::lower_bound(t.begin(), t.end(), time, [&](const T& lhs, float rhs) { return lhs.time < rhs; })) -
|
||||
1);
|
||||
}
|
||||
|
||||
float interpolationVal(float lastTimeStamp, float nextTimeStamp, float animationTime)
|
||||
{
|
||||
return (animationTime - lastTimeStamp) / (nextTimeStamp - lastTimeStamp);
|
||||
}
|
||||
|
||||
vec3 interpolatePosition(const AnimationChannel& channel, float time)
|
||||
{
|
||||
if (channel.pos.size() == 1)
|
||||
return channel.pos[0].pos;
|
||||
|
||||
uint32_t start = getTimeIndex<>(channel.pos, time);
|
||||
uint32_t end = start + 1;
|
||||
float mix = interpolationVal(channel.pos[start].time, channel.pos[end].time, time);
|
||||
return glm::mix(channel.pos[start].pos, channel.pos[end].pos, mix);
|
||||
}
|
||||
|
||||
glm::quat interpolateRotation(const AnimationChannel& channel, float time)
|
||||
{
|
||||
if (channel.rot.size() == 1)
|
||||
return channel.rot[0].rot;
|
||||
|
||||
uint32_t start = getTimeIndex<>(channel.rot, time);
|
||||
uint32_t end = start + 1;
|
||||
float mix = interpolationVal(channel.rot[start].time, channel.rot[end].time, time);
|
||||
return glm::slerp(channel.rot[start].rot, channel.rot[end].rot, mix);
|
||||
}
|
||||
|
||||
vec3 interpolateScaling(const AnimationChannel& channel, float time)
|
||||
{
|
||||
if (channel.scale.size() == 1)
|
||||
return channel.scale[0].scale;
|
||||
|
||||
uint32_t start = getTimeIndex<>(channel.scale, time);
|
||||
uint32_t end = start + 1;
|
||||
float coef = interpolationVal(channel.scale[start].time, channel.scale[end].time, time);
|
||||
return glm::mix(channel.scale[start].scale, channel.scale[end].scale, coef);
|
||||
}
|
||||
|
||||
mat4 animationTransform(const AnimationChannel& channel, float time)
|
||||
{
|
||||
mat4 translation = glm::translate(mat4(1.0f), interpolatePosition(channel, time));
|
||||
mat4 rotation = glm::toMat4(glm::normalize(interpolateRotation(channel, time)));
|
||||
mat4 scale = glm::scale(mat4(1.0f), interpolateScaling(channel, time));
|
||||
return translation * rotation * scale;
|
||||
}
|
||||
|
||||
mat4 animationTransformBlending(const AnimationChannel& channel1, float time1, const AnimationChannel& channel2, float time2, float weight)
|
||||
{
|
||||
mat4 trans1 = glm::translate(mat4(1.0f), interpolatePosition(channel1, time1));
|
||||
mat4 trans2 = glm::translate(mat4(1.0f), interpolatePosition(channel2, time2));
|
||||
mat4 translation = glm::mix(trans1, trans2, weight);
|
||||
|
||||
quat rot1 = interpolateRotation(channel1, time1);
|
||||
quat rot2 = interpolateRotation(channel2, time2);
|
||||
|
||||
mat4 rotation = glm::toMat4(glm::normalize(glm::slerp(rot1, rot2, weight)));
|
||||
|
||||
vec3 scl1 = interpolateScaling(channel1, time1);
|
||||
vec3 scl2 = interpolateScaling(channel2, time2);
|
||||
mat4 scale = glm::scale(mat4(1.0f), glm::mix(scl1, scl2, weight));
|
||||
|
||||
return translation * rotation * scale;
|
||||
}
|
||||
|
||||
MorphState morphTransform(const MorphTarget& target, const MorphingChannel& channel, float time)
|
||||
{
|
||||
MorphState ms;
|
||||
ms.meshId = target.meshId;
|
||||
|
||||
float mix = 0.0f;
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
|
||||
if (channel.key.size() > 0) {
|
||||
start = getTimeIndex(channel.key, time);
|
||||
end = start + 1;
|
||||
mix = interpolationVal(channel.key[start].time, channel.key[end].time, time);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < std::min((uint32_t)target.offset.size(), (uint32_t)MAX_MORPH_WEIGHTS); ++i) {
|
||||
ms.morphTarget[i] = target.offset[channel.key[start].mesh[i]];
|
||||
ms.weights[i] = glm::mix(channel.key[start].weight[i], channel.key[end].weight[i], mix);
|
||||
}
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
void initAnimations(GLTFContext& glTF, const aiScene* scene)
|
||||
{
|
||||
glTF.animations.resize(scene->mNumAnimations);
|
||||
|
||||
for (uint32_t i = 0; i < scene->mNumAnimations; ++i) {
|
||||
Animation& anim = glTF.animations[i];
|
||||
anim.name = scene->mAnimations[i]->mName.C_Str();
|
||||
anim.duration = scene->mAnimations[i]->mDuration;
|
||||
anim.ticksPerSecond = scene->mAnimations[i]->mTicksPerSecond;
|
||||
for (uint32_t c = 0; c < scene->mAnimations[i]->mNumChannels; c++) {
|
||||
const aiNodeAnim* channel = scene->mAnimations[i]->mChannels[c];
|
||||
const char* boneName = channel->mNodeName.data;
|
||||
uint32_t boneId = glTF.bonesByName[boneName].boneId;
|
||||
if (boneId == ~0u) {
|
||||
for (const GLTFNode& node : glTF.nodesStorage) {
|
||||
if (node.name != boneName)
|
||||
continue;
|
||||
boneId = node.modelMtxId;
|
||||
glTF.bonesByName[boneName] = {
|
||||
.boneId = boneId,
|
||||
.transform = glTF.hasBones ? glm::inverse(node.transform) : mat4(1),
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(boneId != ~0u);
|
||||
anim.channels[boneId] = initChannel(channel);
|
||||
}
|
||||
|
||||
const uint32_t numMorphTargetChannels = scene->mAnimations[i]->mNumMorphMeshChannels;
|
||||
anim.morphChannels.resize(numMorphTargetChannels);
|
||||
|
||||
for (uint32_t c = 0; c < numMorphTargetChannels; c++) {
|
||||
const aiMeshMorphAnim* channel = scene->mAnimations[i]->mMorphMeshChannels[c];
|
||||
|
||||
MorphingChannel& morphChannel = anim.morphChannels[c];
|
||||
|
||||
morphChannel.name = channel->mName.C_Str();
|
||||
morphChannel.key.resize(channel->mNumKeys);
|
||||
|
||||
for (uint32_t k = 0; k < channel->mNumKeys; ++k) {
|
||||
MorphingChannelKey& key = morphChannel.key[k];
|
||||
key.time = channel->mKeys[k].mTime;
|
||||
for (uint32_t v = 0; v < std::min((uint32_t)MAX_MORPH_WEIGHTS, channel->mKeys[k].mNumValuesAndWeights); ++v) {
|
||||
key.mesh[v] = channel->mKeys[k].mValues[v];
|
||||
key.weight[v] = channel->mKeys[k].mWeights[v];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateAnimation(GLTFContext& glTF, AnimationState& anim, float dt)
|
||||
{
|
||||
if (!anim.active || (anim.animId == ~0u)) {
|
||||
glTF.morphing = false;
|
||||
glTF.skinning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const Animation& activeAnim = glTF.animations[anim.animId];
|
||||
anim.currentTime += activeAnim.ticksPerSecond * dt;
|
||||
|
||||
if (anim.playOnce && anim.currentTime > activeAnim.duration) {
|
||||
anim.currentTime = activeAnim.duration;
|
||||
anim.active = false;
|
||||
} else
|
||||
anim.currentTime = fmodf(anim.currentTime, activeAnim.duration);
|
||||
|
||||
// Apply animations
|
||||
std::function<void(GLTFNodeRef gltfNode, const mat4& parentTransform)> traverseTree = [&](GLTFNodeRef gltfNode,
|
||||
const mat4& parentTransform) {
|
||||
const GLTFBone& bone = glTF.bonesByName[glTF.nodesStorage[gltfNode].name];
|
||||
const uint32_t boneId = bone.boneId;
|
||||
|
||||
if (boneId != ~0u) {
|
||||
assert(boneId == glTF.nodesStorage[gltfNode].modelMtxId);
|
||||
auto channel = activeAnim.channels.find(boneId);
|
||||
const bool hasActiveChannel = channel != activeAnim.channels.end();
|
||||
|
||||
glTF.matrices[glTF.nodesStorage[gltfNode].modelMtxId] =
|
||||
parentTransform *
|
||||
(hasActiveChannel ? animationTransform(channel->second, anim.currentTime) : glTF.nodesStorage[gltfNode].transform);
|
||||
|
||||
glTF.skinning = true;
|
||||
} else {
|
||||
glTF.matrices[glTF.nodesStorage[gltfNode].modelMtxId] = parentTransform * glTF.nodesStorage[gltfNode].transform;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < glTF.nodesStorage[gltfNode].children.size(); i++) {
|
||||
const GLTFNodeRef child = glTF.nodesStorage[gltfNode].children[i];
|
||||
|
||||
traverseTree(child, glTF.matrices[glTF.nodesStorage[gltfNode].modelMtxId]);
|
||||
}
|
||||
};
|
||||
|
||||
traverseTree(glTF.root, mat4(1.0f));
|
||||
|
||||
for (const std::pair<std::string, GLTFBone>& b : glTF.bonesByName) {
|
||||
if (b.second.boneId != ~0u) {
|
||||
glTF.matrices[b.second.boneId] = glTF.matrices[b.second.boneId] * b.second.transform;
|
||||
}
|
||||
}
|
||||
|
||||
glTF.morphStates.resize(glTF.meshesStorage.size());
|
||||
// update morphing
|
||||
if (glTF.enableMorphing) {
|
||||
if (!activeAnim.morphChannels.empty()) {
|
||||
for (size_t i = 0; i < activeAnim.morphChannels.size(); ++i) {
|
||||
const MorphingChannel& channel = activeAnim.morphChannels[i];
|
||||
const uint32_t meshId = glTF.meshesRemap[channel.name];
|
||||
const MorphTarget& morphTarget = glTF.morphTargets[meshId];
|
||||
|
||||
if (morphTarget.meshId != ~0u) {
|
||||
glTF.morphStates[morphTarget.meshId] = morphTransform(morphTarget, channel, anim.currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
glTF.morphing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateAnimationBlending(GLTFContext& glTF, AnimationState& anim1, AnimationState& anim2, float weight, float dt)
|
||||
{
|
||||
if (anim1.active && anim2.active) {
|
||||
const Animation& activeAnim1 = glTF.animations[anim1.animId];
|
||||
anim1.currentTime += activeAnim1.ticksPerSecond * dt;
|
||||
|
||||
if (anim1.playOnce && anim1.currentTime > activeAnim1.duration) {
|
||||
anim1.currentTime = activeAnim1.duration;
|
||||
anim1.active = false;
|
||||
} else {
|
||||
anim1.currentTime = fmodf(anim1.currentTime, activeAnim1.duration);
|
||||
}
|
||||
|
||||
const Animation& activeAnim2 = glTF.animations[anim2.animId];
|
||||
anim2.currentTime += activeAnim2.ticksPerSecond * dt;
|
||||
|
||||
if (anim2.playOnce && anim2.currentTime > activeAnim2.duration) {
|
||||
anim2.currentTime = activeAnim2.duration;
|
||||
anim2.active = false;
|
||||
} else {
|
||||
anim2.currentTime = fmodf(anim2.currentTime, activeAnim2.duration);
|
||||
}
|
||||
|
||||
// Update skinning
|
||||
std::function<void(GLTFNodeRef gltfNode, const mat4& parentTransform)> traverseTree = [&](GLTFNodeRef gltfNode,
|
||||
const mat4& parentTransform) {
|
||||
const GLTFBone& bone = glTF.bonesByName[glTF.nodesStorage[gltfNode].name];
|
||||
const uint32_t boneId = bone.boneId;
|
||||
if (boneId != ~0u) {
|
||||
auto channel1 = activeAnim1.channels.find(boneId);
|
||||
auto channel2 = activeAnim2.channels.find(boneId);
|
||||
|
||||
if (channel1 != activeAnim1.channels.end() && channel2 != activeAnim2.channels.end()) {
|
||||
glTF.matrices[glTF.nodesStorage[gltfNode].modelMtxId] =
|
||||
parentTransform *
|
||||
animationTransformBlending(channel1->second, anim1.currentTime, channel2->second, anim2.currentTime, weight);
|
||||
} else if (channel1 != activeAnim1.channels.end()) {
|
||||
glTF.matrices[glTF.nodesStorage[gltfNode].modelMtxId] = parentTransform * animationTransform(channel1->second, anim1.currentTime);
|
||||
} else if (channel2 != activeAnim2.channels.end()) {
|
||||
glTF.matrices[glTF.nodesStorage[gltfNode].modelMtxId] = parentTransform * animationTransform(channel2->second, anim2.currentTime);
|
||||
} else {
|
||||
glTF.matrices[glTF.nodesStorage[gltfNode].modelMtxId] = parentTransform * glTF.nodesStorage[gltfNode].transform;
|
||||
}
|
||||
glTF.skinning = true;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < glTF.nodesStorage[gltfNode].children.size(); i++) {
|
||||
const uint32_t child = glTF.nodesStorage[gltfNode].children[i];
|
||||
|
||||
traverseTree(child, glTF.matrices[glTF.nodesStorage[gltfNode].modelMtxId]);
|
||||
}
|
||||
};
|
||||
|
||||
traverseTree(glTF.root, mat4(1.0f));
|
||||
|
||||
for (const std::pair<std::string, GLTFBone>& b : glTF.bonesByName) {
|
||||
if (b.second.boneId != ~0u) {
|
||||
glTF.matrices[b.second.boneId] = glTF.matrices[b.second.boneId] * b.second.transform;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
glTF.morphing = false;
|
||||
glTF.skinning = false;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue