mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-09-23 18:43:15 +08:00
Implement some animation.
This commit is contained in:
parent
1b85cb8c59
commit
5c47eda8be
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
Example use CPU implementation of skinning for the explanation of how to process skin property in glTF format.
|
Example use CPU implementation of skinning for the explanation of how to process skin property in glTF format.
|
||||||
|
|
||||||
OpenGL is used to display transformed vertex.
|
Animation and skinning code is based on SacchaWillems' Vulkan-glTF-PBR: https://github.com/SaschaWillems/Vulkan-glTF-PBR
|
||||||
|
|
||||||
|
OpenGL is still used to display renderings.
|
||||||
|
|
||||||
## Build on Linux and macOS
|
## Build on Linux and macOS
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
#define GLFW_INCLUDE_GLU
|
#define GLFW_INCLUDE_GLU
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#include "../common/trackball.h"
|
|
||||||
#include "../common/matrix.h"
|
#include "../common/matrix.h"
|
||||||
|
#include "../common/trackball.h"
|
||||||
|
|
||||||
#include "skinning.h"
|
#include "skinning.h"
|
||||||
|
|
||||||
@ -49,7 +49,9 @@ float eye[3], lookat[3], up[3];
|
|||||||
|
|
||||||
GLFWwindow *window;
|
GLFWwindow *window;
|
||||||
|
|
||||||
typedef struct { GLuint vb; } GLBufferState;
|
typedef struct {
|
||||||
|
GLuint vb;
|
||||||
|
} GLBufferState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
std::vector<GLuint> diffuseTex; // for each primitive in mesh
|
std::vector<GLuint> diffuseTex; // for each primitive in mesh
|
||||||
@ -65,16 +67,36 @@ typedef struct {
|
|||||||
size_t count; // byte count
|
size_t count; // byte count
|
||||||
} GLCurvesState;
|
} GLCurvesState;
|
||||||
|
|
||||||
|
|
||||||
// Stores vertex position transformed by skinning
|
// Stores vertex position transformed by skinning
|
||||||
typedef struct {
|
typedef struct {
|
||||||
std::vector<float> positions; // float4
|
std::vector<float> positions; // float4
|
||||||
} SkinnedMesh;
|
} SkinnedMesh;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
std::vector<example::mat4> inverseBindMatrices; // mat44
|
std::vector<example::mat4> inverseBindMatrices; // mat44
|
||||||
} SkinningMatrices;
|
} SkinningMatrices;
|
||||||
|
|
||||||
|
struct AnimationChannel {
|
||||||
|
enum PathType { TRANSLATION, ROTATION, SCALE };
|
||||||
|
PathType path;
|
||||||
|
// Node *node;
|
||||||
|
uint32_t samplerIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnimationSampler {
|
||||||
|
enum InterpolationType { LINEAR, STEP, CUBICSPLINE };
|
||||||
|
InterpolationType interpolation;
|
||||||
|
std::vector<float> inputs;
|
||||||
|
std::vector<example::vec4> outputsVec4;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Animation {
|
||||||
|
std::string name;
|
||||||
|
std::vector<AnimationSampler> samplers;
|
||||||
|
std::vector<AnimationChannel> channels;
|
||||||
|
float start = std::numeric_limits<float>::max();
|
||||||
|
float end = std::numeric_limits<float>::min();
|
||||||
|
};
|
||||||
|
|
||||||
std::map<int, GLBufferState> gBufferState;
|
std::map<int, GLBufferState> gBufferState;
|
||||||
std::map<std::string, GLMeshState> gMeshState;
|
std::map<std::string, GLMeshState> gMeshState;
|
||||||
@ -545,7 +567,8 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) {
|
|||||||
for (; it != itEnd; it++) {
|
for (; it != itEnd; it++) {
|
||||||
assert(it->second >= 0);
|
assert(it->second >= 0);
|
||||||
const tinygltf::Accessor &accessor = model.accessors[it->second];
|
const tinygltf::Accessor &accessor = model.accessors[it->second];
|
||||||
const tinygltf::BufferView &bufferView = model.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &bufferView =
|
||||||
|
model.bufferViews[accessor.bufferView];
|
||||||
|
|
||||||
if (bufferView.target == 0) {
|
if (bufferView.target == 0) {
|
||||||
continue;
|
continue;
|
||||||
@ -571,12 +594,13 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) {
|
|||||||
(it->first.compare("TEXCOORD_0") == 0)) {
|
(it->first.compare("TEXCOORD_0") == 0)) {
|
||||||
if (gGLProgramState.attribs[it->first] >= 0) {
|
if (gGLProgramState.attribs[it->first] >= 0) {
|
||||||
// Compute byteStride from Accessor + BufferView combination.
|
// Compute byteStride from Accessor + BufferView combination.
|
||||||
int byteStride = accessor.ByteStride(model.bufferViews[accessor.bufferView]);
|
int byteStride =
|
||||||
|
accessor.ByteStride(model.bufferViews[accessor.bufferView]);
|
||||||
assert(byteStride != -1);
|
assert(byteStride != -1);
|
||||||
glVertexAttribPointer(gGLProgramState.attribs[it->first], size,
|
glVertexAttribPointer(gGLProgramState.attribs[it->first], size,
|
||||||
accessor.componentType, accessor.normalized ? GL_TRUE : GL_FALSE,
|
accessor.componentType,
|
||||||
byteStride,
|
accessor.normalized ? GL_TRUE : GL_FALSE,
|
||||||
BUFFER_OFFSET(accessor.byteOffset));
|
byteStride, BUFFER_OFFSET(accessor.byteOffset));
|
||||||
CheckErrors("vertex attrib pointer");
|
CheckErrors("vertex attrib pointer");
|
||||||
glEnableVertexAttribArray(gGLProgramState.attribs[it->first]);
|
glEnableVertexAttribArray(gGLProgramState.attribs[it->first]);
|
||||||
CheckErrors("enable vertex attrib array");
|
CheckErrors("enable vertex attrib array");
|
||||||
@ -742,8 +766,9 @@ static void PrintNodes(const tinygltf::Scene &scene) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ConstructNodeMatrix(const tinygltf::Node &node, example::mat4 *xform) {
|
#if 0
|
||||||
|
static void ConstructNodeMatrix(const tinygltf::Node &node,
|
||||||
|
example::mat4 *xform) {
|
||||||
if (node.matrix.size() == 16) {
|
if (node.matrix.size() == 16) {
|
||||||
for (size_t j = 0; j < 4; j++) {
|
for (size_t j = 0; j < 4; j++) {
|
||||||
for (size_t i = 0; i < 4; i++) {
|
for (size_t i = 0; i < 4; i++) {
|
||||||
@ -779,45 +804,54 @@ static void ConstructNodeMatrix(const tinygltf::Node &node, example::mat4 *xform
|
|||||||
}
|
}
|
||||||
|
|
||||||
example::BuildTransofrmMatrix(translation, rotation, scale, xform);
|
example::BuildTransofrmMatrix(translation, rotation, scale, xform);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Hierarchically evalute skining matrix
|
// Hierarchically evalute skining
|
||||||
//
|
//
|
||||||
static void ApplySkinningToMesh(const tinygltf::model &model, const tinygltf::Node &node, example::mat4 &parent_xform, std::vector<SkinnedMesh> *skinned_meshes) {
|
static void ApplySkinningToMesh(const tinygltf::Model &model,
|
||||||
example::mat4 xform = parent_xform;
|
const tinygltf::Node &node,
|
||||||
|
example::mat4 &parent_xform,
|
||||||
|
std::vector<SkinnedMesh> *skinned_meshes) {
|
||||||
|
example::mat4 node_xform;
|
||||||
|
ConstructNodeMatrix(node, &node_xform);
|
||||||
|
|
||||||
if (node.mesh) {
|
if (node.mesh) {
|
||||||
if (node.skin) {
|
if (node.skin) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
example::mat4 xform;
|
||||||
|
Matrix::Mult(xform.m, parent_xform.m, node_xform.m);
|
||||||
|
|
||||||
for (auto &child : node.children) {
|
for (auto &child : node.children) {
|
||||||
ApplySkinningToMesh(model.nodes[child], xform, skinned_meshes);
|
ApplySkinningToMesh(model, model.nodes[child], xform, skinned_meshes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Read inverseBindMatricies for each skin
|
// Read inverseBindMatricies for each skin
|
||||||
//
|
//
|
||||||
static void SetupSkinningMatrices(const tinygltf::Model &model, std::map<int, SkinningMatrices> &skinning_matrices)
|
static void SetupSkinningMatrices(
|
||||||
{
|
const tinygltf::Model &model,
|
||||||
|
std::map<int, SkinningMatrices> &skinning_matrices) {
|
||||||
for (size_t s = 0; s < model.skins.size(); s++) {
|
for (size_t s = 0; s < model.skins.size(); s++) {
|
||||||
const tinygltf::Skin &skin = model.skins[s];
|
const tinygltf::Skin &skin = model.skins[s];
|
||||||
|
|
||||||
if (skin.inverseBindMatrices > -1) {
|
if (skin.inverseBindMatrices > -1) {
|
||||||
|
|
||||||
if (skin.joints.size() > 0) {
|
if (skin.joints.size() > 0) {
|
||||||
|
const tinygltf::Accessor &accessor =
|
||||||
const tinygltf::Accessor &accessor = model.accessors[skin.inverseBindMatrices];
|
model.accessors[skin.inverseBindMatrices];
|
||||||
assert(accessor.type == TINYGLTF_TYPE_MAT4);
|
assert(accessor.type == TINYGLTF_TYPE_MAT4);
|
||||||
|
|
||||||
const tinygltf::BufferView &bufferView = model.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &bufferView =
|
||||||
|
model.bufferViews[accessor.bufferView];
|
||||||
|
|
||||||
const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer];
|
const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer];
|
||||||
|
|
||||||
const float *ptr = reinterpret_cast<const float *>(buffer.data.data() + accessor.byteOffset + bufferView.byteOffset);
|
const float *ptr = reinterpret_cast<const float *>(
|
||||||
|
buffer.data.data() + accessor.byteOffset + bufferView.byteOffset);
|
||||||
std::cout << "count = " << accessor.count << std::endl;
|
std::cout << "count = " << accessor.count << std::endl;
|
||||||
|
|
||||||
std::vector<example::mat4> inverse_bind_matrices(accessor.count);
|
std::vector<example::mat4> inverse_bind_matrices(accessor.count);
|
||||||
@ -830,14 +864,11 @@ static void SetupSkinningMatrices(const tinygltf::Model &model, std::map<int, Sk
|
|||||||
|
|
||||||
std::cout << "j[" << j << "] = " << std::endl;
|
std::cout << "j[" << j << "] = " << std::endl;
|
||||||
Matrix::Print(inverse_bind_matrices[j].m);
|
Matrix::Print(inverse_bind_matrices[j].m);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
skinning_matrices[s].inverseBindMatrices = inverse_bind_matrices;
|
skinning_matrices[s].inverseBindMatrices = inverse_bind_matrices;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,7 +893,8 @@ int main(int argc, char **argv) {
|
|||||||
bool ret = false;
|
bool ret = false;
|
||||||
if (ext.compare("glb") == 0) {
|
if (ext.compare("glb") == 0) {
|
||||||
// assume binary glTF.
|
// assume binary glTF.
|
||||||
ret = loader.LoadBinaryFromFile(&model, &err, &warn, input_filename.c_str());
|
ret =
|
||||||
|
loader.LoadBinaryFromFile(&model, &err, &warn, input_filename.c_str());
|
||||||
} else {
|
} else {
|
||||||
// assume ascii glTF.
|
// assume ascii glTF.
|
||||||
ret = loader.LoadASCIIFromFile(&model, &err, &warn, input_filename.c_str());
|
ret = loader.LoadASCIIFromFile(&model, &err, &warn, input_filename.c_str());
|
||||||
@ -889,7 +921,8 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
std::cout << "defaultScene = " << model.defaultScene << std::endl;
|
std::cout << "defaultScene = " << model.defaultScene << std::endl;
|
||||||
if (model.defaultScene >= int(model.scenes.size())) {
|
if (model.defaultScene >= int(model.scenes.size())) {
|
||||||
std::cerr << "Invalid defualtScene value : " << model.defaultScene << std::endl;
|
std::cerr << "Invalid defualtScene value : " << model.defaultScene
|
||||||
|
<< std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,49 @@
|
|||||||
#include "skinning.h"
|
#include "skinning.h"
|
||||||
|
|
||||||
#include "../common/matrix.h"
|
#include "../common/matrix.h"
|
||||||
#include "../common/trackball.h" // for quaternion
|
#include "../common/trackball.h" // for quaternion
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Weverything"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HANDMADE_MATH_IMPLEMENTATION
|
||||||
|
#include "HandmadeMath.h"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace example {
|
namespace example {
|
||||||
|
|
||||||
void BuildTransofrmMatrix(
|
struct Node {
|
||||||
const float translate[3],
|
|
||||||
const float rotation[4], // as quaternion in glTF
|
|
||||||
const float scale[3],
|
|
||||||
mat4 *transform_matrix) {
|
|
||||||
|
|
||||||
|
float translation[3] = {0.0f, 0.0f, 0.0f};
|
||||||
|
float scale[4] = {1.0f, 1.0f, 1.0f};
|
||||||
|
float rotation[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline vec4 mix(vec4 x, vec4 y, float a) {
|
||||||
|
vec4 v;
|
||||||
|
v.f[0] = (1.0f - a) * x.f[0] + a * y.f[0];
|
||||||
|
v.f[1] = (1.0f - a) * x.f[1] + a * y.f[1];
|
||||||
|
v.f[2] = (1.0f - a) * x.f[2] + a * y.f[2];
|
||||||
|
v.f[3] = (1.0f - a) * x.f[3] + a * y.f[3];
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildTransofrmMatrix(const float translate[3],
|
||||||
|
const float rotation[4], // as quaternion in glTF
|
||||||
|
const float scale[3], mat4 *transform_matrix) {
|
||||||
float T[4][4];
|
float T[4][4];
|
||||||
T[0][0] = 1.0f;
|
T[0][0] = 1.0f;
|
||||||
T[0][1] = 0.0f;
|
T[0][1] = 0.0f;
|
||||||
@ -68,11 +98,10 @@ void BuildTransofrmMatrix(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ComputeJointMatrices(
|
void ComputeJointMatrices(
|
||||||
const std::vector<mat4> global_transform_of_nodes,
|
const std::vector<mat4> global_transform_of_nodes,
|
||||||
const std::vector<mat4> global_transform_of_joint_nodes,
|
const std::vector<mat4> global_transform_of_joint_nodes,
|
||||||
const std::vector<mat4> inverse_bind_matrix_for_joints,
|
const std::vector<mat4> inverse_bind_matrix_for_joints,
|
||||||
std::vector<mat4> *output_joint_matrices) {
|
std::vector<mat4> *output_joint_matrices) {
|
||||||
|
|
||||||
const size_t n = global_transform_of_nodes.size();
|
const size_t n = global_transform_of_nodes.size();
|
||||||
|
|
||||||
output_joint_matrices->resize(n);
|
output_joint_matrices->resize(n);
|
||||||
@ -84,21 +113,18 @@ void ComputeJointMatrices(
|
|||||||
mat4 g_joint = global_transform_of_joint_nodes[i];
|
mat4 g_joint = global_transform_of_joint_nodes[i];
|
||||||
mat4 inverse_bind_matrix = inverse_bind_matrix_for_joints[i];
|
mat4 inverse_bind_matrix = inverse_bind_matrix_for_joints[i];
|
||||||
|
|
||||||
float a[4][4]; // temp matrix
|
float a[4][4]; // temp matrix
|
||||||
Matrix::Mult(a, g_joint.m, inverse_bind_matrix.m);
|
Matrix::Mult(a, g_joint.m, inverse_bind_matrix.m);
|
||||||
|
|
||||||
Matrix::Mult((*output_joint_matrices)[i].m, g_inv.m, a);
|
Matrix::Mult((*output_joint_matrices)[i].m, g_inv.m, a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Skining(const std::vector<float> vertices,
|
void Skining(const std::vector<float> vertices,
|
||||||
const std::vector<float> weights, const std::vector<size_t> joints,
|
const std::vector<float> weights, const std::vector<size_t> joints,
|
||||||
const size_t num_skinning_weights,
|
const size_t num_skinning_weights,
|
||||||
const std::vector<mat4> joint_matrices,
|
const std::vector<mat4> joint_matrices, const float t,
|
||||||
const float t,
|
|
||||||
std::vector<float> *skinned_vertices) {
|
std::vector<float> *skinned_vertices) {
|
||||||
|
|
||||||
assert((vertices.size() % 4) == 0);
|
assert((vertices.size() % 4) == 0);
|
||||||
const size_t num_vertices = vertices.size() / 4;
|
const size_t num_vertices = vertices.size() / 4;
|
||||||
|
|
||||||
@ -136,7 +162,6 @@ void Skining(const std::vector<float> vertices,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float vtx[4];
|
float vtx[4];
|
||||||
vtx[0] = vertices[4 * v + 0];
|
vtx[0] = vertices[4 * v + 0];
|
||||||
vtx[1] = vertices[4 * v + 1];
|
vtx[1] = vertices[4 * v + 1];
|
||||||
@ -148,5 +173,80 @@ void Skining(const std::vector<float> vertices,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdateAnimation(std::vector<Animation> &animations, uint32_t index,
|
||||||
|
float time, std::vector<Node*> &nodes) {
|
||||||
|
if (index > uint32_t(animations.size() - 1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace example
|
const Animation &animation = animations[index];
|
||||||
|
|
||||||
|
bool updated = false;
|
||||||
|
for (auto &channel : animation.channels) {
|
||||||
|
const AnimationSampler &sampler = animation.samplers[channel.samplerIndex];
|
||||||
|
if (sampler.inputs.size() > sampler.outputsVec4.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(LTE): support interpolation other than LINEAR
|
||||||
|
for (size_t i = 0; i < sampler.inputs.size() - 1; i++) {
|
||||||
|
if ((time >= sampler.inputs[i]) && (time <= sampler.inputs[i + 1])) {
|
||||||
|
float u = std::max(0.0f, time - sampler.inputs[i]) /
|
||||||
|
(sampler.inputs[i + 1] - sampler.inputs[i]);
|
||||||
|
if (u <= 1.0f) {
|
||||||
|
switch (channel.path) {
|
||||||
|
case AnimationChannel::PathType::TRANSLATION: {
|
||||||
|
example::vec4 trans =
|
||||||
|
mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
|
||||||
|
channel.node->translation[0] = trans.f[0];
|
||||||
|
channel.node->translation[1] = trans.f[1];
|
||||||
|
channel.node->translation[2] = trans.f[2];
|
||||||
|
// drop w
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AnimationChannel::PathType::SCALE: {
|
||||||
|
example::vec4 scale =
|
||||||
|
mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
|
||||||
|
channel.node->scale[0] = scale.f[0];
|
||||||
|
channel.node->scale[1] = scale.f[1];
|
||||||
|
channel.node->scale[2] = scale.f[2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AnimationChannel::PathType::ROTATION: {
|
||||||
|
|
||||||
|
hmm_quaternion q1 = HMM_Quaternion(
|
||||||
|
sampler.outputsVec4[i].f[0],
|
||||||
|
sampler.outputsVec4[i].f[1],
|
||||||
|
sampler.outputsVec4[i].f[2],
|
||||||
|
sampler.outputsVec4[i].f[3]);
|
||||||
|
|
||||||
|
hmm_quaternion q2 = HMM_Quaternion(
|
||||||
|
sampler.outputsVec4[i + 1].f[0],
|
||||||
|
sampler.outputsVec4[i + 1].f[1],
|
||||||
|
sampler.outputsVec4[i + 1].f[2],
|
||||||
|
sampler.outputsVec4[i + 1].f[3]);
|
||||||
|
|
||||||
|
hmm_quaternion q = HMM_NormalizeQuaternion(HMM_Slerp(q1, u, q2));
|
||||||
|
|
||||||
|
channel.node->rotation[0] = q.Elements[0];
|
||||||
|
channel.node->rotation[1] = q.Elements[1];
|
||||||
|
channel.node->rotation[2] = q.Elements[2];
|
||||||
|
channel.node->rotation[3] = q.Elements[3];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
for (auto &node : nodes) {
|
||||||
|
node->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace example
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace example {
|
namespace example {
|
||||||
|
|
||||||
@ -10,6 +13,36 @@ struct mat4 {
|
|||||||
float m[4][4];
|
float m[4][4];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct vec4 {
|
||||||
|
float f[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
// glTF node
|
||||||
|
struct Node;
|
||||||
|
|
||||||
|
|
||||||
|
struct AnimationChannel {
|
||||||
|
enum PathType { TRANSLATION, ROTATION, SCALE };
|
||||||
|
PathType path;
|
||||||
|
Node *node;
|
||||||
|
uint32_t samplerIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnimationSampler {
|
||||||
|
enum InterpolationType { LINEAR, STEP, CUBICSPLINE };
|
||||||
|
InterpolationType interpolation;
|
||||||
|
std::vector<float> inputs;
|
||||||
|
std::vector<example::vec4> outputsVec4;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Animation {
|
||||||
|
std::string name;
|
||||||
|
std::vector<AnimationSampler> samplers;
|
||||||
|
std::vector<AnimationChannel> channels;
|
||||||
|
float start = std::numeric_limits<float>::max();
|
||||||
|
float end = std::numeric_limits<float>::min();
|
||||||
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Utility function to build transformation matrix from translate/rotation/scale
|
/// Utility function to build transformation matrix from translate/rotation/scale
|
||||||
///
|
///
|
||||||
|
Loading…
x
Reference in New Issue
Block a user