182 lines
5.1 KiB
C++
182 lines
5.1 KiB
C++
/* Copyright (c) 2020-2023, Arm Limited and Contributors
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 the "License";
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "animation.h"
|
|
|
|
#include "scene_graph/node.h"
|
|
|
|
namespace vkb
|
|
{
|
|
namespace sg
|
|
{
|
|
Animation::Animation(const std::string &name) :
|
|
Script{name}
|
|
{
|
|
}
|
|
|
|
Animation::Animation(const Animation &other) :
|
|
channels{other.channels}
|
|
{
|
|
}
|
|
|
|
void Animation::add_channel(Node &node, const AnimationTarget &target, const AnimationSampler &sampler)
|
|
{
|
|
channels.push_back({node, target, sampler});
|
|
}
|
|
|
|
void Animation::update(float delta_time)
|
|
{
|
|
current_time += delta_time;
|
|
if (current_time > end_time)
|
|
{
|
|
current_time -= end_time;
|
|
}
|
|
|
|
for (auto &channel : channels)
|
|
{
|
|
for (size_t i = 0; i < channel.sampler.inputs.size() - 1; ++i)
|
|
{
|
|
if ((current_time >= channel.sampler.inputs[i]) && (current_time <= channel.sampler.inputs[i + 1]))
|
|
{
|
|
float time = (current_time - channel.sampler.inputs[i]) / (channel.sampler.inputs[i + 1] - channel.sampler.inputs[i]);
|
|
|
|
auto &transform = channel.node.get_transform();
|
|
|
|
if (channel.sampler.type == AnimationType::Linear)
|
|
{
|
|
switch (channel.target)
|
|
{
|
|
case Translation:
|
|
{
|
|
transform.set_translation(glm::vec3(glm::mix(channel.sampler.outputs[i],
|
|
channel.sampler.outputs[i + 1],
|
|
time)));
|
|
break;
|
|
}
|
|
case Rotation:
|
|
{
|
|
glm::quat q1;
|
|
q1.x = channel.sampler.outputs[i].x;
|
|
q1.y = channel.sampler.outputs[i].y;
|
|
q1.z = channel.sampler.outputs[i].z;
|
|
q1.w = channel.sampler.outputs[i].w;
|
|
|
|
glm::quat q2;
|
|
q2.x = channel.sampler.outputs[i + 1].x;
|
|
q2.y = channel.sampler.outputs[i + 1].y;
|
|
q2.z = channel.sampler.outputs[i + 1].z;
|
|
q2.w = channel.sampler.outputs[i + 1].w;
|
|
|
|
transform.set_rotation(glm::normalize(glm::slerp(q1, q2, time)));
|
|
break;
|
|
}
|
|
|
|
case Scale:
|
|
{
|
|
transform.set_scale(glm::vec3(glm::mix(channel.sampler.outputs[i],
|
|
channel.sampler.outputs[i + 1],
|
|
time)));
|
|
}
|
|
}
|
|
}
|
|
else if (channel.sampler.type == AnimationType::Step)
|
|
{
|
|
switch (channel.target)
|
|
{
|
|
case Translation:
|
|
{
|
|
transform.set_translation(glm::vec3(channel.sampler.outputs[i]));
|
|
break;
|
|
}
|
|
case Rotation:
|
|
{
|
|
glm::quat q1;
|
|
q1.x = channel.sampler.outputs[i].x;
|
|
q1.y = channel.sampler.outputs[i].y;
|
|
q1.z = channel.sampler.outputs[i].z;
|
|
q1.w = channel.sampler.outputs[i].w;
|
|
|
|
transform.set_rotation(glm::normalize(q1));
|
|
break;
|
|
}
|
|
|
|
case Scale:
|
|
{
|
|
transform.set_scale(glm::vec3(channel.sampler.outputs[i]));
|
|
}
|
|
}
|
|
}
|
|
else if (channel.sampler.type == AnimationType::CubicSpline)
|
|
{
|
|
float delta = channel.sampler.inputs[i + 1] - channel.sampler.inputs[i];
|
|
|
|
glm::vec4 p0 = channel.sampler.outputs[i * 3 + 1]; // Starting point
|
|
glm::vec4 p1 = channel.sampler.outputs[(i + 1) * 3 + 1]; // Ending point
|
|
|
|
glm::vec4 m0 = delta * channel.sampler.outputs[i * 3 + 2]; // Delta time * out tangent
|
|
glm::vec4 m1 = delta * channel.sampler.outputs[(i + 1) * 3 + 0]; // Delta time * in tangent of next point
|
|
|
|
// This equation is taken from the GLTF 2.0 specification Appendix C (https://github.com/KhronosGroup/glTF/tree/main/specification/2.0#appendix-c-spline-interpolation)
|
|
glm::vec4 result = (2.0f * glm::pow(time, 3.0f) - 3.0f * glm::pow(time, 2.0f) + 1.0f) * p0 + (glm::pow(time, 3.0f) - 2.0f * glm::pow(time, 2.0f) + time) * m0 + (-2.0f * glm::pow(time, 3.0f) + 3.0f * glm::pow(time, 2.0f)) * p1 + (glm::pow(time, 3.0f) - glm::pow(time, 2.0f)) * m1;
|
|
|
|
auto &transform = channel.node.get_transform();
|
|
|
|
switch (channel.target)
|
|
{
|
|
case Translation:
|
|
{
|
|
transform.set_translation(glm::vec3(result));
|
|
break;
|
|
}
|
|
case Rotation:
|
|
{
|
|
glm::quat q1;
|
|
q1.x = result.x;
|
|
q1.y = result.y;
|
|
q1.z = result.z;
|
|
q1.w = result.w;
|
|
|
|
transform.set_rotation(glm::normalize(q1));
|
|
break;
|
|
}
|
|
|
|
case Scale:
|
|
{
|
|
transform.set_scale(glm::vec3(result));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Animation::update_times(float new_start_time, float new_end_time)
|
|
{
|
|
if (new_start_time < start_time)
|
|
{
|
|
start_time = new_start_time;
|
|
}
|
|
if (new_end_time > end_time)
|
|
{
|
|
end_time = new_end_time;
|
|
}
|
|
}
|
|
|
|
} // namespace sg
|
|
} // namespace vkb
|