collada: node animation

This commit is contained in:
Zack Buhman 2026-03-17 14:46:00 -05:00
parent 0489f33243
commit 71744c4344
16 changed files with 2508 additions and 74 deletions

View File

@ -40,8 +40,10 @@ OBJS = \
src/collada/scene.o \
src/collada/effect.o \
src/collada/node_state.o \
src/collada/animate.o \
data/scenes/ship20/ship20.o \
data/scenes/noodle/noodle.o
data/scenes/noodle/noodle.o \
data/scenes/shadow_test/shadow_test.o
all: test.so

View File

@ -9,3 +9,15 @@ PYTHONPATH=~/d3d10 python -m collada.main \
PYTHONPATH=~/d3d10 python -m collada.main \
include/data/scenes/noodle.h
# shadow_test
PYTHONPATH=~/d3d10 python -m collada.main \
~/love-demo/scene/shadow_test/shadow_test.DAE \
data/scenes/shadow_test/shadow_test.cpp \
data/scenes/shadow_test/shadow_test.vtx \
data/scenes/shadow_test/shadow_test.vjw \
data/scenes/shadow_test/shadow_test.idx
PYTHONPATH=~/d3d10 python -m collada.main \
include/data/scenes/shadow_test.h

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

Binary file not shown.

15
include/collada/animate.h Normal file
View 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);
}

View File

@ -21,8 +21,12 @@ namespace collada::instance_types {
types::transform_type type;
};
struct node_instance {
transform * transforms = NULL;
struct node {
// immutable state
types::node const * node;
// mutable state
transform * transforms;
XMMATRIX world;
};
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "collada/types.h"
#include "collada/instance_types.h"
namespace collada::node_state {
struct state {
instance_types::node * node_instances;
void allocate_node_instances(types::node const * const * const nodes, int nodes_count);
void update_node_world_transform(instance_types::node & node_instance);
};
};

View File

@ -1,6 +1,8 @@
#pragma once
#include "collada/types.h"
#include "collada/instance_types.h"
#include "collada/node_state.h"
namespace collada::scene {
struct static_skinned {
@ -10,6 +12,7 @@ namespace collada::scene {
struct state {
types::descriptor const * descriptor;
node_state::state node_state;
unsigned int vertex_buffer_pnt;
unsigned int vertex_buffer_jw;
@ -20,6 +23,7 @@ namespace collada::scene {
unsigned int * textures;
// drawing
void load_layouts();
void load_images();
void load_scene(types::descriptor const * const descriptor);
@ -40,7 +44,13 @@ namespace collada::scene {
void draw_instance_controllers(types::instance_controller const * const instance_controllers,
int const instance_controllers_count);
void draw_node(types::node const & node);
void draw_node(types::node const & node, instance_types::node const & node_instance);
void draw();
// state updates
void update(float t);
// query
instance_types::node * find_node_by_name(char const * name);
};
}

View File

@ -362,6 +362,8 @@ namespace collada::types {
//////////////////////////////////////////////////////////////////////
struct node {
char const * name;
int const parent_index;
node_type const type;

View File

@ -0,0 +1,3 @@
namespace shadow_test {
extern collada::types::descriptor const descriptor;
}

View File

@ -1,14 +1,13 @@
static inline float fract(float f)
{
return f - floorf(f);
}
#include <stdio.h>
static inline float loop(float f, float n)
{
return fract(f / n) * n;
}
#include "directxmath/directxmath.h"
static inline int find_frame_ix(source const& source, float t)
#include "collada/types.h"
#include "collada/instance_types.h"
namespace collada::animate {
static inline int 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) {
@ -18,14 +17,14 @@
return -1;
}
static inline float linear_interpolate_iv(source const& source, int frame_ix, float t)
static inline float linear_interpolate_iv(types::source const& source, int frame_ix, float t)
{
float prev = source.float_array[(frame_ix+0) * source.stride];
float next = source.float_array[(frame_ix+1) * source.stride];
return (t - prev) / (next - prev);
}
static inline float linear_interpolate_value(source const& source, int frame_ix, int parameter_ix, float iv)
static inline float linear_interpolate_value(types::source const& source, int frame_ix, int parameter_ix, float iv)
{
float prev = source.float_array[(frame_ix+0) * source.stride + parameter_ix];
float next = source.float_array[(frame_ix+1) * source.stride + parameter_ix];
@ -76,20 +75,20 @@
}
}
print("%f %f\n", XMVectorGetX(p0), XMVectorGetY(p0));
print("%f %f\n", XMVectorGetX(c0), XMVectorGetY(c0));
print("%f %f\n", XMVectorGetX(c1), XMVectorGetY(c1));
print("%f %f\n", XMVectorGetX(p1), XMVectorGetY(p1));
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(source const& source, int frame_ix, int parameter_ix)
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(sampler const * const sampler, int frame_ix, int parameter_ix, float t)
static float bezier_sampler(types::sampler const * const sampler, int frame_ix, int parameter_ix, float t)
{
/*
P0 is (INPUT[i] , OUTPUT[i])
@ -112,25 +111,25 @@
return bezier_binary_search(p0, c0, c1, p1, t);
}
static void apply_transform_target(transform& transform,
enum target_attribute channel_target_attribute,
static void apply_transform_target(instance_types::transform& transform,
enum types::target_attribute channel_target_attribute,
float value)
{
switch (transform.type) {
case transform_type::TRANSLATE: __attribute__((fallthrough));
case transform_type::SCALE:
case types::transform_type::TRANSLATE: __attribute__((fallthrough));
case types::transform_type::SCALE:
switch (channel_target_attribute) {
case target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return;
case target_attribute::Y: transform.vector = XMVectorSetY(transform.vector, value); return;
case target_attribute::Z: transform.vector = XMVectorSetZ(transform.vector, value); return;
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 transform_type::ROTATE:
case types::transform_type::ROTATE:
switch (channel_target_attribute) {
case target_attribute::X: transform.vector = XMVectorSetX(transform.vector, value); return;
case target_attribute::Y: transform.vector = XMVectorSetY(transform.vector, value); return;
case target_attribute::Z: transform.vector = XMVectorSetZ(transform.vector, value); return;
case target_attribute::ANGLE: transform.vector = XMVectorSetW(transform.vector, value); return;
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:
@ -139,33 +138,33 @@
}
}
static enum target_attribute const rotate_target_attributes[] = {
target_attribute::X,
target_attribute::Y,
target_attribute::Z,
target_attribute::ANGLE,
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 target_attribute const translate_scale_target_attributes[] = {
target_attribute::X,
target_attribute::Y,
target_attribute::Z,
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(channel const& channel,
transform& transform,
static void animate_channel_segment(types::channel const& channel,
instance_types::transform& transform,
int frame_ix, float t)
{
enum target_attribute const * target_attributes = &channel.target_attribute;
enum types::target_attribute const * target_attributes = &channel.target_attribute;
int target_attributes_count = 1;
if (channel.target_attribute == target_attribute::ALL) {
if (channel.target_attribute == types::target_attribute::ALL) {
switch (transform.type) {
case transform_type::TRANSLATE: __attribute__((fallthrough));
case transform_type::SCALE:
case types::transform_type::TRANSLATE: __attribute__((fallthrough));
case types::transform_type::SCALE:
target_attributes = translate_scale_target_attributes;
target_attributes_count = 3;
break;
case transform_type::ROTATE:
case types::transform_type::ROTATE:
target_attributes = rotate_target_attributes;
target_attributes_count = 4;
break;
@ -177,10 +176,10 @@
for (int parameter_ix = 0; parameter_ix < target_attributes_count; parameter_ix++) {
enum collada::interpolation interpolation = channel.source_sampler->interpolation.interpolation_array[frame_ix];
enum types::interpolation interpolation = channel.source_sampler->interpolation.interpolation_array[frame_ix];
float value;
if (interpolation == interpolation::BEZIER) {
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);
@ -191,11 +190,11 @@
}
}
static void animate_node(node const& node, node_instance& node_instance, float t)
void animate_node(instance_types::node& node_instance, float t)
{
for (int i = 0; i < node.channels_count; i++) {
channel const& channel = *node.channels[i];
transform& transform = node_instance.transforms[channel.target_transform_index];
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];
int frame_ix = find_frame_ix(channel.source_sampler->input, t);
assert(frame_ix >= 0); // animation is missing a key frame
@ -203,3 +202,4 @@
animate_channel_segment(channel, transform, frame_ix, t);
}
}
}

View File

@ -1,11 +1,19 @@
#include <stdio.h>
#include "directxmath/directxmath.h"
#include "collada/types.h"
#include "collada/instance_types.h"
#include "new.h"
#include "collada/node_state.h"
namespace collada::node_state {
inline static void load_transform(instance_types::transform * instance_transform,
types::transform const & transform)
//////////////////////////////////////////////////////////////////////
// transforms
//////////////////////////////////////////////////////////////////////
inline static void load_transform(types::transform const & transform,
instance_types::transform * instance_transform)
{
switch (transform.type) {
case types::transform_type::LOOKAT:
@ -28,17 +36,37 @@ namespace collada::node_state {
default:
assert(false);
}
instance_transform->type = transform.type;
}
void initialize_node_transforms(types::node const * const node,
instance_types::node_instance * const node_instance)
inline static void initialize_node_transforms(instance_types::node & node_instance)
{
for (int i = 0; i < node->transforms_count; i++) {
load_transform(&node_instance->transforms[i],
node->transforms[i]);
for (int i = 0; i < node_instance.node->transforms_count; i++) {
load_transform(node_instance.node->transforms[i], &node_instance.transforms[i]);
}
}
inline static void allocate_node_instance(instance_types::node & node_instance,
types::node const * const node)
{
node_instance.node = node;
node_instance.transforms = New<instance_types::transform>(node->transforms_count);
initialize_node_transforms(node_instance);
}
void state::allocate_node_instances(types::node const * const * const nodes, int nodes_count)
{
node_instances = New<instance_types::node>(nodes_count);
for (int i = 0; i < nodes_count; i++) {
allocate_node_instance(node_instances[i], nodes[i]);
}
}
//////////////////////////////////////////////////////////////////////
// world matrix
//////////////////////////////////////////////////////////////////////
inline static bool vector_equal(XMVECTOR V1, XMVECTOR V2)
{
uint32_t CR;
@ -60,8 +88,25 @@ namespace collada::node_state {
case types::transform_type::MATRIX:
return transform.matrix;
default:
fprintf(stderr, "unknown transform type %d\n", (int)transform.type);
assert(false);
break;
}
}
void state::update_node_world_transform(instance_types::node & node_instance)
{
XMMATRIX world;
if (node_instance.node->parent_index >= 0)
world = node_instances[node_instance.node->parent_index].world;
else
world = XMMatrixIdentity();
for (int i = 0; i < node_instance.node->transforms_count; i++) {
world = transform_matrix(node_instance.transforms[i]) * world;
}
node_instance.world = world;
}
}

View File

@ -14,6 +14,7 @@
#include "collada/instance_types.h"
#include "collada/scene.h"
#include "collada/effect.h"
#include "collada/animate.h"
namespace collada::scene {
@ -275,6 +276,8 @@ namespace collada::scene {
index_buffer = load_index_buffer(descriptor->index_buffer);
load_images();
node_state.allocate_node_instances(descriptor->nodes, descriptor->nodes_count);
}
void state::set_color_or_texture(types::color_or_texture const& color_or_texture,
@ -337,8 +340,6 @@ namespace collada::scene {
types::instance_material const * const instance_materials,
int const instance_materials_count)
{
glUseProgram(collada::effect::program_static);
types::mesh const& mesh = geometry.mesh;
for (int j = 0; j < instance_materials_count; j++) {
@ -437,18 +438,30 @@ namespace collada::scene {
}
}
void state::draw_node(types::node const & node)
void state::draw_node(types::node const & node, instance_types::node const & node_instance)
{
XMMATRIX transform = node_instance.world * view::state.transform;
XMFLOAT4X4 float_transform;
XMStoreFloat4x4(&float_transform, transform);
if (node.instance_geometries_count) {
glUseProgram(collada::effect::program_static);
glUniformMatrix4fv(layout.uniform.transform, 1, false, (float *)&float_transform);
draw_instance_geometries(node.instance_geometries, node.instance_geometries_count);
}
if (node.instance_controllers_count) {
glUseProgram(collada::effect::program_static);
glUniformMatrix4fv(layout.uniform.transform, 1, false, (float *)&float_transform);
draw_instance_controllers(node.instance_controllers, node.instance_controllers_count);
}
}
void state::draw()
{
unsigned int effects[] = {collada::effect::program_static, collada::effect::program_skinned};
for (int i = 0; i < 2; i++) {
glUseProgram(effects[i]);
glUniformMatrix4fv(layout.uniform.transform, 1, false, (float *)&view::state.float_transform);
glUniform1i(layout.uniform.emission_sampler, 0);
glUniform1i(layout.uniform.ambient_sampler, 1);
glUniform1i(layout.uniform.diffuse_sampler, 2);
@ -468,7 +481,27 @@ namespace collada::scene {
if (node.type != types::node_type::NODE)
continue;
draw_node(node);
draw_node(node, node_state.node_instances[i]);
}
}
void state::update(float t)
{
t = animate::loop(t / 4.0f, 3.333333f);
for (int i = 0; i < descriptor->nodes_count; i++) {
animate::animate_node(node_state.node_instances[i], t);
node_state.update_node_world_transform(node_state.node_instances[i]);
}
}
instance_types::node * state::find_node_by_name(char const * name)
{
for (int i = 0; i < descriptor->nodes_count; i++) {
if (strcmp(node_state.node_instances[i].node->name, name) == 0) {
return &node_state.node_instances[i];
}
}
return nullptr;
}
}

View File

@ -24,9 +24,11 @@
#include "collada/effect.h"
#include "collada/scene.h"
#include "collada/types.h"
#include "collada/instance_types.h"
#include "data/scenes/ship20.h"
#include "data/scenes/noodle.h"
#include "data/scenes/shadow_test.h"
struct line_location {
struct {
@ -73,6 +75,9 @@ static target_type const geometry_buffer_pnc_types[3] = {
[target_name::COLOR] = { GL_RGBA8, GL_COLOR_ATTACHMENT2 },
};
collada::instance_types::node * node_eye;
collada::instance_types::node * node_at;
void load_quad_index_buffer()
{
uint8_t const data[] = {
@ -304,7 +309,11 @@ void load(const char * source_path)
//////////////////////////////////////////////////////////////////////
collada::effect::load_effects();
scene_state.load_scene(&noodle::descriptor);
scene_state.load_scene(&shadow_test::descriptor);
node_eye = scene_state.find_node_by_name("Camera001");
assert(node_eye != nullptr);
node_at = scene_state.find_node_by_name("Camera001.Target");
assert(node_at != nullptr);
}
void update_keyboard(int up, int down, int left, int right,
@ -416,8 +425,11 @@ void update_joystick(int joystick_index,
}
*/
view::state.at = view::state.at + direction;
view::state.eye = view::state.at - view::state.direction * view::at_distance;
view::state.eye = view::state.eye + direction;
//view::state.at = view::state.at - view::state.direction * view::at_distance;
//view::state.at = view::state.at + direction;
//view::state.eye = view::state.at - view::state.direction * view::at_distance;
/*
lighting.quadratic += 0.01 * a + -0.01 * b;
@ -440,6 +452,10 @@ void update(float time)
{
current_time = time;
scene_state.update(time);
view::state.eye = XMVector3Transform(XMVectorZero(), node_eye->world);
view::state.at = XMVector3Transform(XMVectorZero(), node_at->world);
view::update_transforms();
}