From b0906ecdc6a0a0fffd4d7dcfecd5e47d6840f0a6 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 27 Jan 2026 14:43:45 -0600 Subject: [PATCH] collada_scene: linear interpolation --- collada/header.py | 5 +- include/collada_scene.hpp | 4 +- include/scenes/curve_interpolation.hpp | 42 +++++---- src/collada_scene.cpp | 118 ++++++++++++++++++++----- src/main.cpp | 2 +- 5 files changed, 130 insertions(+), 41 deletions(-) diff --git a/collada/header.py b/collada/header.py index 9547f74..56a8281 100644 --- a/collada/header.py +++ b/collada/header.py @@ -525,7 +525,10 @@ def render_array(state, collada, accessor, array): it = iter(array.floats) for i in range(accessor.count): vector = ", ".join(f"{float(f)}f" for f in islice(it, accessor.stride)) - yield f"{{ {vector} }}," + if accessor.stride == 1: + yield f"{vector}," + else: + yield f"{{ {vector} }}," yield "};" else: assert False, type(array) diff --git a/include/collada_scene.hpp b/include/collada_scene.hpp index c02f6db..b3e46c2 100644 --- a/include/collada_scene.hpp +++ b/include/collada_scene.hpp @@ -24,6 +24,7 @@ namespace collada_scene { struct node_instance { transform * transforms = NULL; + XMMATRIX world; }; struct scene_state { @@ -41,7 +42,7 @@ namespace collada_scene { collada::descriptor const * m_descriptor; HRESULT load_scene(collada::descriptor const * const descriptor); - void render(); + void render(float t); private: HRESULT load_layouts(); @@ -51,6 +52,7 @@ namespace collada_scene { void render_geometries(collada::instance_geometry const * const instance_geometries, int const instance_geometries_count); + void node_world_transform(collada::node const& node, node_instance& node_instance); }; HRESULT LoadEffect(); diff --git a/include/scenes/curve_interpolation.hpp b/include/scenes/curve_interpolation.hpp index 6da641d..d4bae77 100644 --- a/include/scenes/curve_interpolation.hpp +++ b/include/scenes/curve_interpolation.hpp @@ -5,17 +5,17 @@ namespace curve_interpolation { using namespace collada; float const array_node_cube_translation_x_input_array[] = { - { 0.0f }, - { 1.666667f }, - { 3.333333f }, - { 5.0f }, + 0.0f, + 1.666667f, + 3.333333f, + 5.0f, }; float const array_node_cube_translation_x_output_array[] = { - { 10.0f }, - { -10.0f }, - { 10.0f }, - { -10.0f }, + 10.0f, + -10.0f, + 10.0f, + -10.0f, }; float2 const array_node_cube_translation_x_intangent_array[] = { @@ -68,17 +68,17 @@ sampler const sampler_node_cube_translation_x_sampler = { }; float const array_node_cube_translation_y_input_array[] = { - { -0.8333334f }, - { 0.8333334f }, - { 2.5f }, - { 4.166667f }, + -0.8333334f, + 0.8333334f, + 2.5f, + 4.166667f, }; float const array_node_cube_translation_y_output_array[] = { - { -10.05776f }, - { 10.05852f }, - { -9.941484f }, - { 10.05852f }, + -10.05776f, + 10.05852f, + -9.941484f, + 10.05852f, }; float2 const array_node_cube_translation_y_intangent_array[] = { @@ -519,6 +519,8 @@ instance_geometry const instance_geometries_node_environmentambientlight[] = { channel const * const node_channels_node_environmentambientlight[] = {}; node const node_node_environmentambientlight = { + .parent_index = -1, + .type = node_type::NODE, .transforms = transforms_node_environmentambientlight, @@ -574,11 +576,13 @@ instance_geometry const instance_geometries_node_cube[] = { }; channel const * const node_channels_node_cube[] = { - &node_channel_node_cube_translation_x, &node_channel_node_cube_translation_y, + &node_channel_node_cube_translation_x, }; node const node_node_cube = { + .parent_index = -1, + .type = node_type::NODE, .transforms = transforms_node_cube, @@ -613,6 +617,8 @@ channel const * const node_channels_node_cylinder001[] = { }; node const node_node_cylinder001 = { + .parent_index = -1, + .type = node_type::NODE, .transforms = transforms_node_cylinder001, @@ -655,6 +661,8 @@ channel const * const node_channels_node_plane001[] = { }; node const node_node_plane001 = { + .parent_index = -1, + .type = node_type::NODE, .transforms = transforms_node_plane001, diff --git a/src/collada_scene.cpp b/src/collada_scene.cpp index 9b1bb46..188cb81 100644 --- a/src/collada_scene.cpp +++ b/src/collada_scene.cpp @@ -287,26 +287,26 @@ namespace collada_scene { { switch (effect.type) { case effect_type::BLINN: - g_pEmissionVariable->SetFloatVector((float *)&effect.blinn.emission.color.x); - g_pAmbientVariable->SetFloatVector((float *)&effect.blinn.ambient.color.x); - g_pDiffuseVariable->SetFloatVector((float *)&effect.blinn.diffuse.color.x); - g_pSpecularVariable->SetFloatVector((float *)&effect.blinn.specular.color.x); + g_pEmissionVariable->SetFloatVector((float *)&effect.blinn.emission.color); + g_pAmbientVariable->SetFloatVector((float *)&effect.blinn.ambient.color); + g_pDiffuseVariable->SetFloatVector((float *)&effect.blinn.diffuse.color); + g_pSpecularVariable->SetFloatVector((float *)&effect.blinn.specular.color); g_pShininessVariable->SetFloat(effect.blinn.shininess); break; case effect_type::LAMBERT: - g_pEmissionVariable->SetFloatVector((float *)&effect.lambert.emission.color.x); - g_pAmbientVariable->SetFloatVector((float *)&effect.lambert.ambient.color.x); - g_pDiffuseVariable->SetFloatVector((float *)&effect.lambert.diffuse.color.x); + g_pEmissionVariable->SetFloatVector((float *)&effect.lambert.emission.color); + g_pAmbientVariable->SetFloatVector((float *)&effect.lambert.ambient.color); + g_pDiffuseVariable->SetFloatVector((float *)&effect.lambert.diffuse.color); break; case effect_type::PHONG: - g_pEmissionVariable->SetFloatVector((float *)&effect.phong.emission.color.x); - g_pAmbientVariable->SetFloatVector((float *)&effect.phong.ambient.color.x); - g_pDiffuseVariable->SetFloatVector((float *)&effect.phong.diffuse.color.x); - g_pSpecularVariable->SetFloatVector((float *)&effect.phong.specular.color.x); + g_pEmissionVariable->SetFloatVector((float *)&effect.phong.emission.color); + g_pAmbientVariable->SetFloatVector((float *)&effect.phong.ambient.color); + g_pDiffuseVariable->SetFloatVector((float *)&effect.phong.diffuse.color); + g_pSpecularVariable->SetFloatVector((float *)&effect.phong.specular.color); g_pShininessVariable->SetFloat(effect.phong.shininess); break; case effect_type::CONSTANT: - g_pEmissionVariable->SetFloatVector((float *)&effect.constant.color.x); + g_pEmissionVariable->SetFloatVector((float *)&effect.constant.color); break; default: break; @@ -340,26 +340,102 @@ namespace collada_scene { } } - void scene_state::render() + static inline float fract(float f) + { + return f - floorf(f); + } + + static inline float loop(float f, float n) + { + return fract(f / n) * n; + } + + static inline int find_frame_ix(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; + } + } + return -1; + } + + static inline float interpolation_value(source const& source, int ix, float t) + { + float prev = source.float_array[ix]; + float next = source.float_array[ix+1]; + return (t - prev) / (next - prev); + } + + static inline float linear_interpolate(source const& source, int ix, float iv) + { + float prev = source.float_array[ix]; + float next = source.float_array[ix+1]; + return prev + iv * (next - prev); + } + + static void animate_node(node const& node, node_instance& 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]; + + int frame_ix = find_frame_ix(channel.source_sampler->input, t); + if (frame_ix < 0) + continue; // animation is missing a key frame + + float iv = interpolation_value(channel.source_sampler->input, frame_ix, t); + float value = linear_interpolate(channel.source_sampler->output, frame_ix, iv); + switch (transform.type) { + case transform_type::TRANSLATE: + switch (channel.target_attribute) { + case target_attribute::X: transform.translate = XMVectorSetX(transform.translate, value); break; + case target_attribute::Y: transform.translate = XMVectorSetY(transform.translate, value); break; + case target_attribute::Z: transform.translate = XMVectorSetZ(transform.translate, value); break; + default: break; + } + break; + default: + break; + } + } + } + + void scene_state::node_world_transform(node const& node, node_instance& node_instance) + { + // build the node's world transform matrix + if (node.parent_index >= 0) + node_instance.world = m_nodeInstances[node.parent_index].world; + else + node_instance.world = XMMatrixIdentity(); + + for (int j = 0; j < node.transforms_count; j++) { + node_instance.world = TransformMatrix(node_instance.transforms[j]) * node_instance.world; + } + } + + void scene_state::render(float t) { g_pViewVariable->SetMatrix((float *)&g_View); g_pProjectionVariable->SetMatrix((float *)&g_Projection); g_pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + t = loop(t / 2.0f, 3.333333f); + for (int i = 0; i < m_descriptor->nodes_count; i++) { node const& node = *m_descriptor->nodes[i]; + node_instance& node_instance = m_nodeInstances[i]; + + animate_node(node, node_instance, t); + node_world_transform(node, node_instance); + + g_pWorldVariable->SetMatrix((float *)&node_instance.world); + + // joints aren't rendered if (node.type != node_type::NODE) continue; - node_instance const& node_instance = m_nodeInstances[i]; - XMMATRIX World = XMMatrixIdentity(); - // build the world transform - for (int j = 0; j < node.transforms_count; j++) { - World = TransformMatrix(node_instance.transforms[j]) * World; - } - g_pWorldVariable->SetMatrix((float *)&World); - render_geometries(node.instance_geometries, node.instance_geometries_count); } } diff --git a/src/main.cpp b/src/main.cpp index e93e744..cd8528d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1843,7 +1843,7 @@ void Render(float t, float dt) //collada::Render(t); - g_SceneState.render(); + g_SceneState.render(t); RenderFont(dt);