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/collada/scene/vulkan.o \
|
||||
src/collada/scene.o \
|
||||
src/collada/node_state.o
|
||||
src/collada/node_state.o \
|
||||
src/collada/animate.o
|
||||
|
||||
SCENES = \
|
||||
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 draw();
|
||||
|
||||
void update(XMMATRIX const & projection,
|
||||
XMMATRIX const & view,
|
||||
float t);
|
||||
int find_node_index_by_name(const char * name);
|
||||
|
||||
void update(float t);
|
||||
|
||||
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 "collada/scene.h"
|
||||
#include "collada/animate.h"
|
||||
|
||||
namespace collada::scene {
|
||||
|
||||
void state::load_scene(types::descriptor const * const descriptor)
|
||||
@ -35,21 +38,25 @@ namespace collada::scene {
|
||||
}
|
||||
}
|
||||
|
||||
void state::update(XMMATRIX const & projection,
|
||||
XMMATRIX const & view,
|
||||
float t)
|
||||
int state::find_node_index_by_name(const char * name)
|
||||
{
|
||||
//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++) {
|
||||
//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]);
|
||||
}
|
||||
|
||||
vulkan.transfer_transforms(projection,
|
||||
view,
|
||||
descriptor->nodes_count,
|
||||
node_state.node_instances);
|
||||
}
|
||||
|
||||
void state::unload_scene()
|
||||
|
||||
30
src/main.cpp
30
src/main.cpp
@ -112,9 +112,10 @@ XMMATRIX currentProjection()
|
||||
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 up = XMVectorSet(0, 0, 1, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
SDL_CHECK(SDL_Init(SDL_INIT_VIDEO));
|
||||
@ -1191,6 +1200,11 @@ int main()
|
||||
uint32_t imageIndex{ 0 };
|
||||
bool quit{ false };
|
||||
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) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
@ -1364,10 +1378,16 @@ int main()
|
||||
|
||||
collada_state.vulkan.change_frame(commandBuffer, frameIndex);
|
||||
|
||||
XMMATRIX projection = currentProjection();
|
||||
XMMATRIX view = currentView();
|
||||
collada_state.update(projection, view, 0);
|
||||
double time = getTime(start_time);
|
||||
collada_state.update(time / 3.0f);
|
||||
|
||||
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();
|
||||
|
||||
vkCmdEndRendering(commandBuffer);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user