mirror of
https://github.com/antopilo/Nuake.git
synced 2026-09-09 20:08:57 +03:00
Animation system
This commit is contained in:
@@ -53,7 +53,7 @@ namespace Nuake
|
||||
{ Vector3(-0.5f, 0.5f, 0.5f), Vector2(1, 1), Vector3(-1, 0, 0) }
|
||||
};
|
||||
|
||||
std::vector<unsigned int> CubeIndices
|
||||
std::vector<uint32_t> CubeIndices
|
||||
{
|
||||
0, 1, 3, 3, 1, 2,
|
||||
1, 5, 2, 2, 5, 6,
|
||||
|
||||
@@ -84,20 +84,70 @@ namespace Nuake
|
||||
ProcessSkinnedNode(scene->mRootNode, scene);
|
||||
if (scene->HasAnimations())
|
||||
{
|
||||
SkeletalAnimation animation;
|
||||
std::vector<Ref<SkeletalAnimation>> animations = std::vector<Ref<SkeletalAnimation>>();
|
||||
|
||||
// Parse animations
|
||||
for (uint32_t i = 0; i < scene->mNumAnimations; i++)
|
||||
{
|
||||
aiAnimation* aiAnim = scene->mAnimations[i];
|
||||
const float duration = aiAnim->mDuration;
|
||||
const float ticksPerSecond = aiAnim->mTicksPerSecond;
|
||||
|
||||
// Read data
|
||||
SkeletonNode rootSkeletonNode;
|
||||
ProcessAnimationNode(rootSkeletonNode, scene->mRootNode);
|
||||
const std::string animationName = aiAnim->mName.data;
|
||||
const float animationDuration = aiAnim->mDuration;
|
||||
const float animationTicksPerSecond = aiAnim->mTicksPerSecond;
|
||||
auto animation = CreateRef<SkeletalAnimation>(animationName, animationDuration, animationTicksPerSecond);
|
||||
|
||||
model->SetSkeletonRootNode(rootSkeletonNode);
|
||||
// Here we iterate over every channel(each channel represents a bone) and fill the SkeletalAnimation
|
||||
// object with every keyframe. We essentially just convert every assimp vector, quat, string to our own types.
|
||||
for (uint32_t j = 0; j < aiAnim->mNumChannels; j++)
|
||||
{
|
||||
aiNodeAnim* animChannel = aiAnim->mChannels[j];
|
||||
const std::string channelName = animChannel->mNodeName.data; // this should be a bone name
|
||||
|
||||
// Create the track
|
||||
BoneTransformTrack& track = animation->GetTrack(channelName);
|
||||
|
||||
// Create every keyframe in the current channel
|
||||
// Position
|
||||
for (uint32_t p = 0; p < animChannel->mNumPositionKeys; p++)
|
||||
{
|
||||
aiVectorKey positionKey = animChannel->mPositionKeys[p];
|
||||
const float keyTime = positionKey.mTime;
|
||||
const aiVector3D assimpKeyValue = positionKey.mValue;
|
||||
const Vector3 keyValue = Vector3(assimpKeyValue.x, assimpKeyValue.y, assimpKeyValue.z);
|
||||
|
||||
track.PushPositionKeyframe(keyTime, keyValue);
|
||||
}
|
||||
|
||||
// Rotation
|
||||
for (uint32_t r = 0; r < animChannel->mNumRotationKeys; r++)
|
||||
{
|
||||
aiQuatKey rotationKey = animChannel->mRotationKeys[r];
|
||||
const float keyTime = rotationKey.mTime;
|
||||
const aiQuaterniont assimpKeyValue = rotationKey.mValue;
|
||||
const Quat keyValue = Quat(assimpKeyValue.w, assimpKeyValue.x, assimpKeyValue.y, assimpKeyValue.z);
|
||||
|
||||
track.PushRotationKeyframe(keyTime, keyValue);
|
||||
}
|
||||
|
||||
// Scaling
|
||||
for (uint32_t s = 0; s < animChannel->mNumScalingKeys; s++)
|
||||
{
|
||||
aiVectorKey scaleKey = animChannel->mScalingKeys[s];
|
||||
const float keyTime = scaleKey.mTime;
|
||||
const aiVector3D assimpKeyValue = scaleKey.mValue;
|
||||
const Vector3 keyValue = Vector3(assimpKeyValue.x, assimpKeyValue.y, assimpKeyValue.z);
|
||||
|
||||
track.PushScaleKeyframe(keyTime, keyValue);
|
||||
}
|
||||
}
|
||||
|
||||
animations.push_back(animation);
|
||||
}
|
||||
|
||||
SkeletonNode rootSkeletonNode;
|
||||
ProcessAnimationNode(rootSkeletonNode, scene->mRootNode);
|
||||
model->SetSkeletonRootNode(std::move(rootSkeletonNode));
|
||||
model->SetAnimations(std::move(animations));
|
||||
}
|
||||
|
||||
for (const auto& mesh : m_SkinnedMeshes)
|
||||
|
||||
104
Nuake/src/Resource/SkeletalAnimation.cpp
Normal file
104
Nuake/src/Resource/SkeletalAnimation.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "SkeletalAnimation.h"
|
||||
|
||||
namespace Nuake
|
||||
{
|
||||
BoneTransformTrack::BoneTransformTrack()
|
||||
{
|
||||
m_Positions = std::vector<Vector3>();
|
||||
m_Rotations = std::vector<Quat>();
|
||||
m_Scales = std::vector<Vector3>();
|
||||
|
||||
m_PositionTimestamps = std::vector<float>();
|
||||
m_RotationTimestamps = std::vector<float>();
|
||||
m_ScaleTimestamps = std::vector<float>();
|
||||
}
|
||||
|
||||
float BoneTransformTrack::GetScaleFactor(float lastTime, float nextTime, float animationTime)
|
||||
{
|
||||
float scaleFactor = 0.0f;
|
||||
float midWayLength = animationTime - lastTime;
|
||||
float framesDiff = nextTime - lastTime;
|
||||
scaleFactor = midWayLength / framesDiff;
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
Nuake::Matrix4 BoneTransformTrack::InterpolatePosition(float time)
|
||||
{
|
||||
if (m_Positions.size() == 0)
|
||||
{
|
||||
return Matrix4(1.0f);
|
||||
}
|
||||
|
||||
if (m_Positions.size() == 1)
|
||||
{
|
||||
return glm::translate(Matrix4(1.0f), m_Positions[0]);
|
||||
}
|
||||
|
||||
int p0Index = GetPositionIndex(time);
|
||||
int p1Index = p0Index + 1;
|
||||
float scaleFactor = GetScaleFactor(m_PositionTimestamps[p0Index], m_PositionTimestamps[p1Index], time);
|
||||
glm::vec3 finalPosition = glm::mix(m_Positions[p0Index], m_Positions[p1Index], scaleFactor);
|
||||
return glm::translate(Matrix4(1.0f), finalPosition);
|
||||
}
|
||||
|
||||
Nuake::Matrix4 BoneTransformTrack::InterpolateRotation(float time)
|
||||
{
|
||||
if (m_Rotations.size() == 0)
|
||||
{
|
||||
return Matrix4(1.0f);
|
||||
}
|
||||
|
||||
if (m_Rotations.size() == 1)
|
||||
{
|
||||
auto rotation = glm::normalize(m_Rotations[0]);
|
||||
return glm::toMat4(rotation);
|
||||
}
|
||||
|
||||
int p0Index = GetRotationIndex(time);
|
||||
int p1Index = p0Index + 1;
|
||||
float scaleFactor = GetScaleFactor(m_RotationTimestamps[p0Index],
|
||||
m_RotationTimestamps[p1Index], time);
|
||||
glm::quat finalRotation = glm::slerp(m_Rotations[p0Index],
|
||||
m_Rotations[p1Index], scaleFactor);
|
||||
finalRotation = glm::normalize(finalRotation);
|
||||
return glm::toMat4(finalRotation);
|
||||
}
|
||||
|
||||
Nuake::Matrix4 BoneTransformTrack::InterpolateScale(float time)
|
||||
{
|
||||
if (m_Scales.size() == 0)
|
||||
{
|
||||
return Matrix4(1.0f);
|
||||
}
|
||||
|
||||
if (1 == m_Scales.size())
|
||||
return glm::scale(glm::mat4(1.0f), m_Scales[0]);
|
||||
|
||||
int p0Index = GetScaleIndex(time);
|
||||
int p1Index = p0Index + 1;
|
||||
float scaleFactor = GetScaleFactor(m_ScaleTimestamps[p0Index],
|
||||
m_ScaleTimestamps[p1Index], time);
|
||||
glm::vec3 finalScale = glm::mix(m_Scales[p0Index], m_Scales[p1Index], scaleFactor);
|
||||
return glm::scale(glm::mat4(1.0f), finalScale);
|
||||
}
|
||||
|
||||
SkeletalAnimation::SkeletalAnimation(const std::string& name, float duration, float ticksPerSecond)
|
||||
{
|
||||
m_Name = name;
|
||||
m_Duration = duration;
|
||||
m_TicksPerSecond = ticksPerSecond;
|
||||
m_CurrentTime = 0.0f;
|
||||
m_Loop = true;
|
||||
}
|
||||
|
||||
BoneTransformTrack& SkeletalAnimation::GetTrack(const std::string& name)
|
||||
{
|
||||
if (m_Tracks.find(name) != m_Tracks.end())
|
||||
{
|
||||
return m_Tracks[name];
|
||||
}
|
||||
|
||||
m_Tracks[name] = BoneTransformTrack();
|
||||
return m_Tracks[name];
|
||||
}
|
||||
}
|
||||
@@ -12,32 +12,118 @@ namespace Nuake
|
||||
Vector3 Scalings;
|
||||
};
|
||||
|
||||
struct BoneTransformTrack
|
||||
class BoneTransformTrack
|
||||
{
|
||||
std::vector<float> positionTimestamps = {};
|
||||
std::vector<float> rotationTimestamps = {};
|
||||
std::vector<float> scaleTimestamps = {};
|
||||
private:
|
||||
std::vector<float> m_PositionTimestamps = {};
|
||||
std::vector<float> m_RotationTimestamps = {};
|
||||
std::vector<float> m_ScaleTimestamps = {};
|
||||
|
||||
std::vector<Vector3> positions = {};
|
||||
std::vector<Quat> rotations = {};
|
||||
std::vector<Vector3> scales = {};
|
||||
std::vector<Vector3> m_Positions = {};
|
||||
std::vector<Quat> m_Rotations = {};
|
||||
std::vector<Vector3> m_Scales = {};
|
||||
|
||||
public:
|
||||
BoneTransformTrack();
|
||||
~BoneTransformTrack() = default;
|
||||
|
||||
void PushPositionKeyframe(float timestamp, const Vector3& position)
|
||||
{
|
||||
m_PositionTimestamps.push_back(timestamp);
|
||||
m_Positions.push_back(position);
|
||||
}
|
||||
|
||||
void PushRotationKeyframe(float timestamp, const Quat& rotation)
|
||||
{
|
||||
m_RotationTimestamps.push_back(timestamp);
|
||||
m_Rotations.push_back(rotation);
|
||||
}
|
||||
|
||||
void PushScaleKeyframe(float timestamp, const Vector3& scale)
|
||||
{
|
||||
m_ScaleTimestamps.push_back(timestamp);
|
||||
m_Scales.push_back(scale);
|
||||
}
|
||||
|
||||
int GetPositionIndex(float animationTime)
|
||||
{
|
||||
if (m_Positions.size() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int index = 0; index < m_Positions.size() - 1; index++)
|
||||
{
|
||||
if (animationTime < m_PositionTimestamps[index + 1])
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Gets the current index on mKeyRotations to interpolate to based on the
|
||||
current animation time*/
|
||||
int GetRotationIndex(float animationTime)
|
||||
{
|
||||
for (int index = 0; index < m_Rotations.size() - 1; ++index)
|
||||
{
|
||||
if (animationTime < m_RotationTimestamps[index + 1])
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Gets the current index on mKeyScalings to interpolate to based on the
|
||||
current animation time */
|
||||
int GetScaleIndex(float animationTime)
|
||||
{
|
||||
for (int index = 0; index < m_Scales.size() - 1; ++index)
|
||||
{
|
||||
if (animationTime < m_ScaleTimestamps[index + 1])
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
float GetScaleFactor(float lastTime, float nextTime, float animationTime);
|
||||
Matrix4 InterpolatePosition(float time);
|
||||
Matrix4 InterpolateRotation(float time);
|
||||
Matrix4 InterpolateScale(float time);
|
||||
};
|
||||
|
||||
class SkeletalAnimation
|
||||
{
|
||||
private:
|
||||
float m_Duration;
|
||||
int m_TicksPerSecond;
|
||||
|
||||
std::unordered_map<std::string, BoneTransformTrack> m_Tracks;
|
||||
std::vector<Bone> m_Bones;
|
||||
float m_CurrentTime;
|
||||
float m_Duration;
|
||||
float m_TicksPerSecond;
|
||||
std::string m_Name;
|
||||
bool m_Loop;
|
||||
|
||||
public:
|
||||
SkeletalAnimation() = default;
|
||||
SkeletalAnimation(const std::string& name, float duration, float ticksPerSecond);
|
||||
|
||||
~SkeletalAnimation() = default;
|
||||
|
||||
Bone& FindBone(const std::string& boneName);
|
||||
void SetCurrentTime(float time)
|
||||
{
|
||||
m_CurrentTime = fmod(time, m_Duration);
|
||||
}
|
||||
|
||||
float GetCurrentTime() const { return m_CurrentTime; }
|
||||
void SetDuration(float duration) { m_Duration = duration; }
|
||||
float GetDuration() const { return m_Duration; }
|
||||
float GetTicksPerSecond() const { return m_TicksPerSecond; }
|
||||
void SetTicksPerSecond(float ticks) { m_TicksPerSecond = ticks; }
|
||||
|
||||
BoneTransformTrack& GetTrack(const std::string& name);
|
||||
std::unordered_map<std::string, BoneTransformTrack>& GetTracks() { return m_Tracks; }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,6 +27,43 @@ namespace Nuake
|
||||
return m_Meshes;
|
||||
}
|
||||
|
||||
void SkinnedModel::SetAnimations(const std::vector<Ref<SkeletalAnimation>> animations)
|
||||
{
|
||||
m_NumAnimation = static_cast<uint32_t>(animations.size());
|
||||
m_CurrentAnimation = 0;
|
||||
m_Animations = animations;
|
||||
}
|
||||
|
||||
void SkinnedModel::AddAnimation(Ref<SkeletalAnimation> animation)
|
||||
{
|
||||
m_NumAnimation++;
|
||||
m_Animations.push_back(std::move(animation));
|
||||
}
|
||||
|
||||
Ref<Nuake::SkeletalAnimation> SkinnedModel::GetCurrentAnimation()
|
||||
{
|
||||
if (m_CurrentAnimation < m_NumAnimation)
|
||||
{
|
||||
return m_Animations[m_CurrentAnimation];
|
||||
}
|
||||
|
||||
Logger::Log("Cannot get animation if no animation exists", "skinned model", WARNING);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SkinnedModel::PlayAnimation(uint32_t animationId)
|
||||
{
|
||||
if (animationId >= m_NumAnimation)
|
||||
{
|
||||
Logger::Log("Cannot play animation, index out of range", "skinned model", CRITICAL);
|
||||
return;
|
||||
}
|
||||
|
||||
GetCurrentAnimation()->SetCurrentTime(0.0f); // Reset previous animation
|
||||
|
||||
m_CurrentAnimation = animationId;
|
||||
}
|
||||
|
||||
json SkinnedModel::Serialize()
|
||||
{
|
||||
BEGIN_SERIALIZE();
|
||||
|
||||
@@ -15,6 +15,10 @@ namespace Nuake
|
||||
|
||||
SkeletonNode m_SkeletonRoot;
|
||||
|
||||
uint32_t m_CurrentAnimation = 0;
|
||||
uint32_t m_NumAnimation = 0;
|
||||
std::vector<Ref<SkeletalAnimation>> m_Animations;
|
||||
|
||||
public:
|
||||
SkinnedModel();
|
||||
SkinnedModel(const std::string path);
|
||||
@@ -29,6 +33,11 @@ namespace Nuake
|
||||
void AddMesh(Ref<SkinnedMesh> mesh);
|
||||
std::vector<Ref<SkinnedMesh>>& GetMeshes();
|
||||
|
||||
void SetAnimations(const std::vector<Ref<SkeletalAnimation>> animations);
|
||||
void AddAnimation(Ref<SkeletalAnimation> animation);
|
||||
Ref<SkeletalAnimation> GetCurrentAnimation();
|
||||
void PlayAnimation(uint32_t animationId);
|
||||
|
||||
json Serialize() override;
|
||||
bool Deserialize(const json& j) override;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "src/Scene/Systems/TransformSystem.h"
|
||||
#include "src/Scene/Systems/QuakeMapBuilder.h"
|
||||
#include "src/Scene/Systems/ParticleSystem.h"
|
||||
#include "src/Scene/Systems/AnimationSystem.h"
|
||||
|
||||
#include "src/Rendering/SceneRenderer.h"
|
||||
#include "Scene.h"
|
||||
@@ -49,6 +50,7 @@ namespace Nuake
|
||||
// Adding systems - Order is important
|
||||
m_Systems.push_back(CreateRef<PhysicsSystem>(this));
|
||||
m_Systems.push_back(CreateRef<ScriptingSystem>(this));
|
||||
m_Systems.push_back(CreateRef<AnimationSystem>(this));
|
||||
m_Systems.push_back(CreateRef<TransformSystem>(this));
|
||||
m_Systems.push_back(CreateRef<ParticleSystem>(this));
|
||||
|
||||
@@ -473,6 +475,17 @@ namespace Nuake
|
||||
skeletonRootEntity.AddComponent<BoneComponent>();
|
||||
entity.AddChild(skeletonRootEntity);
|
||||
|
||||
Vector3 bonePosition;
|
||||
Quat boneRotation;
|
||||
Vector3 boneScale;
|
||||
Decompose(skeletonRoot.Transform, bonePosition, boneRotation, boneScale);
|
||||
|
||||
auto& transformComponent = skeletonRootEntity.GetComponent<TransformComponent>();
|
||||
transformComponent.SetLocalPosition(bonePosition);
|
||||
transformComponent.SetLocalRotation(boneRotation);
|
||||
transformComponent.SetLocalScale(boneScale);
|
||||
transformComponent.SetLocalTransform(skeletonRoot.Transform);
|
||||
|
||||
CreateSkeletonTraverse(skeletonRootEntity, skeletonRoot);
|
||||
}
|
||||
|
||||
@@ -484,6 +497,18 @@ namespace Nuake
|
||||
boneEntity.AddComponent<BoneComponent>();
|
||||
entity.AddChild(boneEntity);
|
||||
|
||||
Vector3 bonePosition;
|
||||
Quat boneRotation;
|
||||
Vector3 boneScale;
|
||||
Decompose(c.Transform, bonePosition, boneRotation, boneScale);
|
||||
|
||||
auto& transformComponent = boneEntity.GetComponent<TransformComponent>();
|
||||
transformComponent.SetLocalPosition(bonePosition);
|
||||
transformComponent.SetLocalRotation(boneRotation);
|
||||
transformComponent.SetLocalScale(boneScale);
|
||||
transformComponent.SetLocalTransform(c.Transform);
|
||||
transformComponent.Dirty = false;
|
||||
|
||||
CreateSkeletonTraverse(boneEntity, c);
|
||||
}
|
||||
}
|
||||
|
||||
89
Nuake/src/Scene/Systems/AnimationSystem.cpp
Normal file
89
Nuake/src/Scene/Systems/AnimationSystem.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "AnimationSystem.h"
|
||||
|
||||
#include "src/Scene/Scene.h"
|
||||
#include "src/Scene/Entities/Entity.h"
|
||||
#include "src/Scene/Components/SkinnedModelComponent.h"
|
||||
|
||||
namespace Nuake
|
||||
{
|
||||
AnimationSystem::AnimationSystem(Scene* scene)
|
||||
{
|
||||
m_Scene = scene;
|
||||
}
|
||||
|
||||
bool AnimationSystem::Init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationSystem::Update(Timestep ts)
|
||||
{
|
||||
auto view = m_Scene->m_Registry.view<TransformComponent, SkinnedModelComponent>();
|
||||
for (auto e : view)
|
||||
{
|
||||
auto [transformComponent, skinnedComponent] = view.get<TransformComponent, SkinnedModelComponent>(e);
|
||||
|
||||
auto& model = skinnedComponent.ModelResource;
|
||||
if (!model)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Ref<SkeletalAnimation> animation = model->GetCurrentAnimation();
|
||||
float newAnimationTime = animation->GetCurrentTime() + (ts * animation->GetTicksPerSecond());
|
||||
animation->SetCurrentTime(newAnimationTime);
|
||||
|
||||
auto& rootBone = model->GetSkeletonRootNode();
|
||||
UpdateBonePositionTraversal(rootBone, animation, animation->GetCurrentTime());
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationSystem::UpdateBonePositionTraversal(SkeletonNode& bone, Ref<SkeletalAnimation> animation, float time)
|
||||
{
|
||||
const std::string& boneName = bone.Name;
|
||||
|
||||
|
||||
Entity& boneEntity = m_Scene->GetEntity(boneName);
|
||||
if (boneEntity.GetHandle() != -1)
|
||||
{
|
||||
auto& animationTrack = animation->GetTrack(boneName);
|
||||
auto& transformComponent = boneEntity.GetComponent<TransformComponent>();
|
||||
|
||||
// Get Update transform
|
||||
const Matrix4 newPosition = animationTrack.InterpolatePosition(time);
|
||||
const Matrix4 newRotation = animationTrack.InterpolateRotation(time);
|
||||
const Matrix4 newScale = animationTrack.InterpolateScale(time);
|
||||
const Matrix4 finalTransform = newPosition * newRotation * newScale;
|
||||
|
||||
Vector3 localPosition;
|
||||
Quat localRotation;
|
||||
Vector3 localScale;
|
||||
Decompose(finalTransform, localPosition, localRotation, localScale);
|
||||
|
||||
transformComponent.SetLocalPosition(localPosition);
|
||||
transformComponent.SetLocalRotation(localRotation);
|
||||
transformComponent.SetLocalScale(localScale);
|
||||
transformComponent.SetLocalTransform(finalTransform);
|
||||
}
|
||||
|
||||
for (auto& childBone : bone.Children)
|
||||
{
|
||||
UpdateBonePositionTraversal(childBone, animation, time);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationSystem::FixedUpdate(Timestep ts)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AnimationSystem::EditorUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AnimationSystem::Exit()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
24
Nuake/src/Scene/Systems/AnimationSystem.h
Normal file
24
Nuake/src/Scene/Systems/AnimationSystem.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <src/Scene/Systems/System.h>
|
||||
#include <src/Core/Maths.h>
|
||||
|
||||
#include "src/Resource/SkeletonNode.h"
|
||||
#include "src/Resource/SkeletalAnimation.h"
|
||||
|
||||
namespace Nuake
|
||||
{
|
||||
class AnimationSystem : public System
|
||||
{
|
||||
public:
|
||||
AnimationSystem(Scene* scene);
|
||||
bool Init() override;
|
||||
void Update(Timestep ts) override;
|
||||
void Draw() override {}
|
||||
void EditorUpdate() override;
|
||||
void FixedUpdate(Timestep ts) override;
|
||||
void Exit() override;
|
||||
|
||||
private:
|
||||
void UpdateBonePositionTraversal(SkeletonNode& bone, Ref<SkeletalAnimation> animation, float time);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user