collada scene animation
This commit is contained in:
parent
8a9198c3e8
commit
779b837caa
3
Makefile
3
Makefile
@ -45,7 +45,8 @@ OBJS = \
|
|||||||
src/vulkan_helper.o \
|
src/vulkan_helper.o \
|
||||||
src/collada/scene/vulkan.o \
|
src/collada/scene/vulkan.o \
|
||||||
src/collada/scene.o \
|
src/collada/scene.o \
|
||||||
src/collada/node_state.o
|
src/collada/node_state.o \
|
||||||
|
src/collada/animate.o
|
||||||
|
|
||||||
SCENES = \
|
SCENES = \
|
||||||
data/scenes/shadow_test/shadow_test.o
|
data/scenes/shadow_test/shadow_test.o
|
||||||
|
|||||||
15
include/collada/animate.h
Normal file
15
include/collada/animate.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "collada/instance_types.h"
|
||||||
|
|
||||||
|
namespace collada::animate {
|
||||||
|
static inline float fract(float f)
|
||||||
|
{
|
||||||
|
return f - floorf(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float loop(float f, float n)
|
||||||
|
{
|
||||||
|
return fract(f / n) * n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void animate_node(instance_types::node& node_instance, float t);
|
||||||
|
}
|
||||||
@ -16,9 +16,9 @@ namespace collada::scene {
|
|||||||
void load_scene(types::descriptor const * const descriptor);
|
void load_scene(types::descriptor const * const descriptor);
|
||||||
void draw();
|
void draw();
|
||||||
|
|
||||||
void update(XMMATRIX const & projection,
|
int find_node_index_by_name(const char * name);
|
||||||
XMMATRIX const & view,
|
|
||||||
float t);
|
void update(float t);
|
||||||
|
|
||||||
void unload_scene();
|
void unload_scene();
|
||||||
};
|
};
|
||||||
|
|||||||
253
src/collada/animate.cpp
Normal file
253
src/collada/animate.cpp
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "directxmath/directxmath.h"
|
||||||
|
|
||||||
|
#include "collada/types.h"
|
||||||
|
#include "collada/instance_types.h"
|
||||||
|
|
||||||
|
namespace collada::animate {
|
||||||
|
|
||||||
|
struct frame_ix {
|
||||||
|
int f0;
|
||||||
|
int f1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline frame_ix find_frame_ix(types::source const& source, float t)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < source.count - 1; i++) {
|
||||||
|
if (source.float_array[i] <= t && source.float_array[i+1] > t) {
|
||||||
|
return {i, i + 1};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {source.count - 1, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float linear_interpolate_iv(types::source const& source, frame_ix frame_ix, float t)
|
||||||
|
{
|
||||||
|
float prev = source.float_array[(frame_ix.f0) * source.stride];
|
||||||
|
float next = source.float_array[(frame_ix.f1) * source.stride];
|
||||||
|
return (t - prev) / (next - prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float linear_interpolate_value(types::source const& source, frame_ix frame_ix, int parameter_ix, float iv)
|
||||||
|
{
|
||||||
|
float prev = source.float_array[(frame_ix.f0) * source.stride + parameter_ix];
|
||||||
|
float next = source.float_array[(frame_ix.f1) * source.stride + parameter_ix];
|
||||||
|
return prev + iv * (next - prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline XMMATRIX linear_interpolate_matrix(types::source const& source, frame_ix frame_ix, float iv)
|
||||||
|
{
|
||||||
|
XMFLOAT4X4 const * prev = (XMFLOAT4X4 const *)&source.float_array[(frame_ix.f0) * source.stride];
|
||||||
|
XMFLOAT4X4 const * next = (XMFLOAT4X4 const *)&source.float_array[(frame_ix.f1) * source.stride];
|
||||||
|
|
||||||
|
XMVECTOR prev_scale;
|
||||||
|
XMVECTOR prev_rotate;
|
||||||
|
XMVECTOR prev_translate;
|
||||||
|
|
||||||
|
bool prev_srt = XMMatrixDecompose(&prev_scale, &prev_rotate, &prev_translate, XMMatrixTranspose(XMLoadFloat4x4(prev)));
|
||||||
|
assert(prev_srt == true);
|
||||||
|
|
||||||
|
XMVECTOR next_scale;
|
||||||
|
XMVECTOR next_rotate;
|
||||||
|
XMVECTOR next_translate;
|
||||||
|
|
||||||
|
bool next_srt = XMMatrixDecompose(&next_scale, &next_rotate, &next_translate, XMMatrixTranspose(XMLoadFloat4x4(next)));
|
||||||
|
assert(next_srt == true);
|
||||||
|
|
||||||
|
XMVECTOR scale = XMVectorLerp(prev_scale, next_scale, iv);
|
||||||
|
XMVECTOR rotate = XMQuaternionSlerp(prev_rotate, next_rotate, iv);
|
||||||
|
XMVECTOR translate = XMVectorLerp(prev_translate, next_translate, iv);
|
||||||
|
|
||||||
|
return XMMatrixAffineTransformation(scale,
|
||||||
|
XMVectorZero(),
|
||||||
|
rotate,
|
||||||
|
translate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float pow3(float f)
|
||||||
|
{
|
||||||
|
return f * f * f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float pow2(float f)
|
||||||
|
{
|
||||||
|
return f * f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline XMVECTOR bezier(XMVECTOR p0, XMVECTOR c0, XMVECTOR c1, XMVECTOR p1, float s)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
p0 * pow3(1 - s)
|
||||||
|
+ 3 * c0 * s * pow2(1 - s)
|
||||||
|
+ 3 * c1 * pow2(s) * (1 - s)
|
||||||
|
+ p1 * pow3(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float bezier_binary_search(XMVECTOR p0, XMVECTOR c0, XMVECTOR c1, XMVECTOR p1, float want)
|
||||||
|
{
|
||||||
|
float low = 0.0f;
|
||||||
|
float high = 1.0f;
|
||||||
|
|
||||||
|
int iterations = 0;
|
||||||
|
while (iterations < 20) {
|
||||||
|
iterations += 1;
|
||||||
|
|
||||||
|
float s = (high + low) * 0.5f;
|
||||||
|
XMVECTOR bs = bezier(p0, c0, c1, p1, s);
|
||||||
|
float t = XMVectorGetX(bs);
|
||||||
|
|
||||||
|
const float epsilon = 0.001f;
|
||||||
|
if (fabsf(t - want) < epsilon) {
|
||||||
|
return XMVectorGetY(bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t > want) {
|
||||||
|
high = s;
|
||||||
|
} else {
|
||||||
|
low = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "%f %f\n", XMVectorGetX(p0), XMVectorGetY(p0));
|
||||||
|
fprintf(stderr, "%f %f\n", XMVectorGetX(c0), XMVectorGetY(c0));
|
||||||
|
fprintf(stderr, "%f %f\n", XMVectorGetX(c1), XMVectorGetY(c1));
|
||||||
|
fprintf(stderr, "%f %f\n", XMVectorGetX(p1), XMVectorGetY(p1));
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline XMFLOAT2 const * tangent_index(types::source const& source, int frame_ix, int parameter_ix)
|
||||||
|
{
|
||||||
|
int ix = frame_ix * source.stride + parameter_ix * 2;
|
||||||
|
return (XMFLOAT2 const *)&source.float_array[ix];
|
||||||
|
}
|
||||||
|
|
||||||
|
static float bezier_sampler(types::sampler const * const sampler, frame_ix frame_ix, int parameter_ix, float t)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
P0 is (INPUT[i] , OUTPUT[i])
|
||||||
|
C0 (or T0) is (OUT_TANGENT[i][0] , OUT_TANGENT[i][1])
|
||||||
|
C1 (or T1) is (IN_TANGENT[i+1][0], IN_TANGENT[i+1][1])
|
||||||
|
P1 is (INPUT[i+1], OUTPUT[i+1])
|
||||||
|
*/
|
||||||
|
|
||||||
|
float frame0_input = sampler->input.float_array[frame_ix.f0];
|
||||||
|
float frame1_input = sampler->input.float_array[frame_ix.f1];
|
||||||
|
|
||||||
|
float frame0_output = sampler->output.float_array[(frame_ix.f0) * sampler->output.stride + parameter_ix];
|
||||||
|
float frame1_output = sampler->output.float_array[(frame_ix.f1) * sampler->output.stride + parameter_ix];
|
||||||
|
|
||||||
|
XMVECTOR p0 = XMVectorSet(frame0_input, frame0_output, 0, 0);
|
||||||
|
XMVECTOR c0 = XMLoadFloat2(tangent_index(sampler->out_tangent, frame_ix.f0, parameter_ix));
|
||||||
|
XMVECTOR c1 = XMLoadFloat2(tangent_index(sampler->in_tangent, frame_ix.f1, parameter_ix));
|
||||||
|
XMVECTOR p1 = XMVectorSet(frame1_input, frame1_output, 0, 0);
|
||||||
|
|
||||||
|
return bezier_binary_search(p0, c0, c1, p1, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apply_transform_target(instance_types::transform& transform,
|
||||||
|
enum types::target_attribute channel_target_attribute,
|
||||||
|
float value)
|
||||||
|
{
|
||||||
|
switch (transform.type) {
|
||||||
|
case types::transform_type::TRANSLATE: [[fallthrough]];
|
||||||
|
case types::transform_type::SCALE:
|
||||||
|
switch (channel_target_attribute) {
|
||||||
|
case types::target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return;
|
||||||
|
case types::target_attribute::Y: transform.vector = XMVectorSetY(transform.vector, value); return;
|
||||||
|
case types::target_attribute::Z: transform.vector = XMVectorSetZ(transform.vector, value); return;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
case types::transform_type::ROTATE:
|
||||||
|
switch (channel_target_attribute) {
|
||||||
|
case types::target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return;
|
||||||
|
case types::target_attribute::Y: transform.vector = XMVectorSetY(transform.vector, value); return;
|
||||||
|
case types::target_attribute::Z: transform.vector = XMVectorSetZ(transform.vector, value); return;
|
||||||
|
case types::target_attribute::ANGLE: transform.vector = XMVectorSetW(transform.vector, value); return;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum types::target_attribute const rotate_target_attributes[] = {
|
||||||
|
types::target_attribute::X,
|
||||||
|
types::target_attribute::Y,
|
||||||
|
types::target_attribute::Z,
|
||||||
|
types::target_attribute::ANGLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum types::target_attribute const translate_scale_target_attributes[] = {
|
||||||
|
types::target_attribute::X,
|
||||||
|
types::target_attribute::Y,
|
||||||
|
types::target_attribute::Z,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void animate_channel_segment(types::channel const& channel,
|
||||||
|
instance_types::transform& transform,
|
||||||
|
frame_ix frame_ix, float t)
|
||||||
|
{
|
||||||
|
enum types::target_attribute const * target_attributes = &channel.target_attribute;
|
||||||
|
int target_attributes_count = 1;
|
||||||
|
if (channel.target_attribute == types::target_attribute::ALL) {
|
||||||
|
switch (transform.type) {
|
||||||
|
case types::transform_type::TRANSLATE: [[fallthrough]];
|
||||||
|
case types::transform_type::SCALE:
|
||||||
|
target_attributes = translate_scale_target_attributes;
|
||||||
|
target_attributes_count = 3;
|
||||||
|
break;
|
||||||
|
case types::transform_type::ROTATE:
|
||||||
|
target_attributes = rotate_target_attributes;
|
||||||
|
target_attributes_count = 4;
|
||||||
|
break;
|
||||||
|
case types::transform_type::MATRIX:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(channel.source_sampler->interpolation.stride == 1);
|
||||||
|
|
||||||
|
if (transform.type == types::transform_type::MATRIX) {
|
||||||
|
enum types::interpolation interpolation = channel.source_sampler->interpolation.interpolation_array[frame_ix.f0];
|
||||||
|
assert(interpolation == types::interpolation::LINEAR);
|
||||||
|
|
||||||
|
float iv = linear_interpolate_iv(channel.source_sampler->input, frame_ix, t);
|
||||||
|
XMMATRIX matrix = linear_interpolate_matrix(channel.source_sampler->output, frame_ix, iv);
|
||||||
|
transform.matrix = matrix;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (int parameter_ix = 0; parameter_ix < target_attributes_count; parameter_ix++) {
|
||||||
|
|
||||||
|
enum types::interpolation interpolation = channel.source_sampler->interpolation.interpolation_array[frame_ix.f0];
|
||||||
|
|
||||||
|
float value;
|
||||||
|
if (interpolation == types::interpolation::BEZIER) {
|
||||||
|
value = bezier_sampler(channel.source_sampler, frame_ix, parameter_ix, t);
|
||||||
|
} else {
|
||||||
|
float iv = linear_interpolate_iv(channel.source_sampler->input, frame_ix, t);
|
||||||
|
value = linear_interpolate_value(channel.source_sampler->output, frame_ix, parameter_ix, iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_transform_target(transform, target_attributes[parameter_ix], value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void animate_node(instance_types::node& node_instance, float t)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < node_instance.node->channels_count; i++) {
|
||||||
|
types::channel const& channel = *node_instance.node->channels[i];
|
||||||
|
instance_types::transform& transform = node_instance.transforms[channel.target_transform_index];
|
||||||
|
|
||||||
|
frame_ix frame_ix = find_frame_ix(channel.source_sampler->input, t);
|
||||||
|
//assert(frame_ix >= 0); // animation is missing a key frame
|
||||||
|
|
||||||
|
animate_channel_segment(channel, transform, frame_ix, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,10 @@
|
|||||||
#include "collada/scene.h"
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "collada/scene.h"
|
||||||
|
#include "collada/animate.h"
|
||||||
|
|
||||||
namespace collada::scene {
|
namespace collada::scene {
|
||||||
|
|
||||||
void state::load_scene(types::descriptor const * const descriptor)
|
void state::load_scene(types::descriptor const * const descriptor)
|
||||||
@ -35,21 +38,25 @@ namespace collada::scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void state::update(XMMATRIX const & projection,
|
int state::find_node_index_by_name(const char * name)
|
||||||
XMMATRIX const & view,
|
|
||||||
float t)
|
|
||||||
{
|
{
|
||||||
//t = animate::loop(t / 1.0f, 1.0f);
|
for (int i = 0; i < descriptor->nodes_count; i++) {
|
||||||
|
if (strcmp(descriptor->nodes[i]->name, name) == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "node `%s` not found in scene\n", name);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void state::update(float t)
|
||||||
|
{
|
||||||
|
t = animate::loop(t, 3.3f);
|
||||||
|
|
||||||
for (int i = 0; i < descriptor->nodes_count; i++) {
|
for (int i = 0; i < descriptor->nodes_count; i++) {
|
||||||
//animate::animate_node(node_state.node_instances[i], t);
|
animate::animate_node(node_state.node_instances[i], t);
|
||||||
node_state.update_node_world_transform(node_state.node_instances[i]);
|
node_state.update_node_world_transform(node_state.node_instances[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
vulkan.transfer_transforms(projection,
|
|
||||||
view,
|
|
||||||
descriptor->nodes_count,
|
|
||||||
node_state.node_instances);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void state::unload_scene()
|
void state::unload_scene()
|
||||||
|
|||||||
30
src/main.cpp
30
src/main.cpp
@ -112,9 +112,10 @@ XMMATRIX currentProjection()
|
|||||||
return projection;
|
return projection;
|
||||||
}
|
}
|
||||||
|
|
||||||
XMMATRIX currentView()
|
XMMATRIX currentView(collada::instance_types::node const & camera_node)
|
||||||
{
|
{
|
||||||
XMVECTOR eye = XMVectorSet(-57, 159, 269, 0);
|
|
||||||
|
XMVECTOR eye = XMVector3Transform(XMVectorZero(), camera_node.world);
|
||||||
XMVECTOR at = XMVectorSet(0, 0, 0, 0);
|
XMVECTOR at = XMVectorSet(0, 0, 0, 0);
|
||||||
XMVECTOR up = XMVectorSet(0, 0, 1, 0);
|
XMVECTOR up = XMVectorSet(0, 0, 1, 0);
|
||||||
XMMATRIX view = XMMatrixLookAtLH(eye, at, up);
|
XMMATRIX view = XMMatrixLookAtLH(eye, at, up);
|
||||||
@ -276,6 +277,14 @@ inline static int positive_modulo(int i, unsigned int n) {
|
|||||||
return (i % n + n) % n;
|
return (i % n + n) % n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static double getTime(int64_t start_time)
|
||||||
|
{
|
||||||
|
int64_t current_time;
|
||||||
|
SDL_GetCurrentTime(¤t_time);
|
||||||
|
int64_t time = current_time - start_time;
|
||||||
|
return (double)(time / 1000) * 0.000001;
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
SDL_CHECK(SDL_Init(SDL_INIT_VIDEO));
|
SDL_CHECK(SDL_Init(SDL_INIT_VIDEO));
|
||||||
@ -1191,6 +1200,11 @@ int main()
|
|||||||
uint32_t imageIndex{ 0 };
|
uint32_t imageIndex{ 0 };
|
||||||
bool quit{ false };
|
bool quit{ false };
|
||||||
int32_t samplerIndex{ 0 };
|
int32_t samplerIndex{ 0 };
|
||||||
|
int64_t start_time;
|
||||||
|
SDL_GetCurrentTime(&start_time);
|
||||||
|
|
||||||
|
int cameraIndex = collada_state.find_node_index_by_name("Camera001");
|
||||||
|
|
||||||
while (quit == false) {
|
while (quit == false) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
@ -1364,10 +1378,16 @@ int main()
|
|||||||
|
|
||||||
collada_state.vulkan.change_frame(commandBuffer, frameIndex);
|
collada_state.vulkan.change_frame(commandBuffer, frameIndex);
|
||||||
|
|
||||||
XMMATRIX projection = currentProjection();
|
double time = getTime(start_time);
|
||||||
XMMATRIX view = currentView();
|
collada_state.update(time / 3.0f);
|
||||||
collada_state.update(projection, view, 0);
|
|
||||||
|
|
||||||
|
XMMATRIX projection = currentProjection();
|
||||||
|
XMMATRIX view = currentView(collada_state.node_state.node_instances[cameraIndex]);
|
||||||
|
|
||||||
|
collada_state.vulkan.transfer_transforms(projection,
|
||||||
|
view,
|
||||||
|
collada_state.descriptor->nodes_count,
|
||||||
|
collada_state.node_state.node_instances);
|
||||||
collada_state.draw();
|
collada_state.draw();
|
||||||
|
|
||||||
vkCmdEndRendering(commandBuffer);
|
vkCmdEndRendering(commandBuffer);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user