From f4e95aee098a87e84b6e0cfbdbf122a0108b4364 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 17 Mar 2026 16:11:55 -0500 Subject: [PATCH] collada: draw animated joints --- build_collada.sh | 2 +- data/scenes/noodle/noodle.cpp | 64 ++++++++++++++++++++++++----------- include/collada/scene.h | 1 + shader/collada/skinned.vert | 18 +++++++++- src/collada/node_state.cpp | 4 ++- src/collada/scene.cpp | 45 ++++++++++++++++++++---- src/test.cpp | 2 +- 7 files changed, 107 insertions(+), 29 deletions(-) diff --git a/build_collada.sh b/build_collada.sh index 5be176a..a57e8e5 100644 --- a/build_collada.sh +++ b/build_collada.sh @@ -1,7 +1,7 @@ set -eux PYTHONPATH=~/d3d10 python -m collada.main \ - path/to/noodle.DAE \ + ~/love-demo/scene/noodle/noodle.DAE \ data/scenes/noodle/noodle.cpp \ data/scenes/noodle/noodle.vtx \ data/scenes/noodle/noodle.vjw \ diff --git a/data/scenes/noodle/noodle.cpp b/data/scenes/noodle/noodle.cpp index 0669d86..0debc2c 100644 --- a/data/scenes/noodle/noodle.cpp +++ b/data/scenes/noodle/noodle.cpp @@ -1913,6 +1913,8 @@ channel const * const node_channels_node_environmentambientlight[] = { }; node const node_node_environmentambientlight = { + .name = "EnvironmentAmbientLight", + .parent_index = -1, .type = node_type::NODE, @@ -1953,6 +1955,8 @@ channel const * const node_channels_node_cameratargethelper[] = { }; node const node_node_cameratargethelper = { + .name = "CameraTargetHelper", + .parent_index = -1, .type = node_type::NODE, @@ -1993,6 +1997,8 @@ channel const * const node_channels_node_camera_target[] = { }; node const node_node_camera_target = { + .name = "Camera.Target", + .parent_index = 1, .type = node_type::NODE, @@ -2040,6 +2046,8 @@ channel const * const node_channels_node_omni001[] = { }; node const node_node_omni001 = { + .name = "Omni001", + .parent_index = -1, .type = node_type::NODE, @@ -2103,6 +2111,8 @@ channel const * const node_channels_node_box001[] = { }; node const node_node_box001 = { + .name = "Box001", + .parent_index = -1, .type = node_type::NODE, @@ -2143,6 +2153,8 @@ channel const * const node_channels_node_bonehelper[] = { }; node const node_node_bonehelper = { + .name = "BoneHelper", + .parent_index = -1, .type = node_type::NODE, @@ -2204,18 +2216,20 @@ instance_light const instance_lights_node_bone001[] = { }; channel const * const node_channels_node_bone001[] = { - &node_channel_node_bone001_translation_x, - &node_channel_node_bone001_translation_z, - &node_channel_node_bone001_scale, - &node_channel_node_bone001_translation_y, - &node_channel_node_bone001_rotationz_angle, - &node_channel_node_bone001_scaleaxisrotation, - &node_channel_node_bone001_rotationx_angle, &node_channel_node_bone001_rotationy_angle, + &node_channel_node_bone001_rotationz_angle, + &node_channel_node_bone001_translation_y, + &node_channel_node_bone001_translation_z, + &node_channel_node_bone001_rotationx_angle, &node_channel_node_bone001_inversescaleaxisrotation, + &node_channel_node_bone001_scale, + &node_channel_node_bone001_scaleaxisrotation, + &node_channel_node_bone001_translation_x, }; node const node_node_bone001 = { + .name = "Bone001", + .parent_index = 5, .type = node_type::JOINT, @@ -2277,18 +2291,20 @@ instance_light const instance_lights_node_bone002[] = { }; channel const * const node_channels_node_bone002[] = { - &node_channel_node_bone002_translation_x, - &node_channel_node_bone002_inversescaleaxisrotation, &node_channel_node_bone002_translation_y, &node_channel_node_bone002_scale, - &node_channel_node_bone002_rotationx_angle, - &node_channel_node_bone002_rotationy_angle, + &node_channel_node_bone002_inversescaleaxisrotation, &node_channel_node_bone002_scaleaxisrotation, + &node_channel_node_bone002_translation_x, + &node_channel_node_bone002_rotationy_angle, &node_channel_node_bone002_rotationz_angle, + &node_channel_node_bone002_rotationx_angle, &node_channel_node_bone002_translation_z, }; node const node_node_bone002 = { + .name = "Bone002", + .parent_index = 6, .type = node_type::JOINT, @@ -2350,18 +2366,20 @@ instance_light const instance_lights_node_bone003[] = { }; channel const * const node_channels_node_bone003[] = { - &node_channel_node_bone003_translation_y, - &node_channel_node_bone003_rotationy_angle, - &node_channel_node_bone003_translation_x, - &node_channel_node_bone003_rotationz_angle, - &node_channel_node_bone003_inversescaleaxisrotation, - &node_channel_node_bone003_rotationx_angle, - &node_channel_node_bone003_translation_z, - &node_channel_node_bone003_scaleaxisrotation, &node_channel_node_bone003_scale, + &node_channel_node_bone003_rotationz_angle, + &node_channel_node_bone003_rotationy_angle, + &node_channel_node_bone003_rotationx_angle, + &node_channel_node_bone003_scaleaxisrotation, + &node_channel_node_bone003_translation_y, + &node_channel_node_bone003_inversescaleaxisrotation, + &node_channel_node_bone003_translation_z, + &node_channel_node_bone003_translation_x, }; node const node_node_bone003 = { + .name = "Bone003", + .parent_index = 7, .type = node_type::JOINT, @@ -2402,6 +2420,8 @@ channel const * const node_channels_node_camerahelper_1[] = { }; node const node_node_camerahelper_1 = { + .name = "CameraHelper", + .parent_index = -1, .type = node_type::NODE, @@ -2442,6 +2462,8 @@ channel const * const node_channels_node_camera001[] = { }; node const node_node_camera001 = { + .name = "Camera001", + .parent_index = 9, .type = node_type::NODE, @@ -2482,6 +2504,8 @@ channel const * const node_channels_node_cameratargethelper_1[] = { }; node const node_node_cameratargethelper_1 = { + .name = "CameraTargetHelper", + .parent_index = -1, .type = node_type::NODE, @@ -2522,6 +2546,8 @@ channel const * const node_channels_node_camera001_target[] = { }; node const node_node_camera001_target = { + .name = "Camera001.Target", + .parent_index = 11, .type = node_type::NODE, diff --git a/include/collada/scene.h b/include/collada/scene.h index a53673f..feb0831 100644 --- a/include/collada/scene.h +++ b/include/collada/scene.h @@ -17,6 +17,7 @@ namespace collada::scene { unsigned int vertex_buffer_pnt; unsigned int vertex_buffer_jw; unsigned int index_buffer; + unsigned int joint_uniform_buffer; static_skinned * vertex_arrays; int * vertex_buffer_strides_pnt; diff --git a/shader/collada/skinned.vert b/shader/collada/skinned.vert index 971eba0..2a45cdd 100644 --- a/shader/collada/skinned.vert +++ b/shader/collada/skinned.vert @@ -3,9 +3,16 @@ layout (location = 0) in vec3 Position; layout (location = 1) in vec3 Normal; layout (location = 2) in vec2 Texture; +layout (location = 3) in ivec4 BlendIndices; +layout (location = 4) in vec4 BlendWeight; layout (location = 0) uniform mat4 Transform; +layout (std140, binding = 0) uniform JointsLayout +{ + mat4 Joints[64]; +}; + out vec3 PixelNormal; out vec2 PixelTexture; @@ -14,5 +21,14 @@ void main() PixelNormal = Normal; PixelTexture = vec2(Texture.x, 1.0 - Texture.y); - gl_Position = Transform * vec4(Position, 1); + mat4 skin + = BlendWeight.x * Joints[BlendIndices.x] + + BlendWeight.y * Joints[BlendIndices.y] + + BlendWeight.z * Joints[BlendIndices.z] + + BlendWeight.w * Joints[BlendIndices.w] + ; + + vec4 position = skin * vec4(Position, 1); + + gl_Position = Transform * position; } diff --git a/src/collada/node_state.cpp b/src/collada/node_state.cpp index 89f5317..e1e7ded 100644 --- a/src/collada/node_state.cpp +++ b/src/collada/node_state.cpp @@ -80,7 +80,9 @@ namespace collada::node_state { case types::transform_type::TRANSLATE: return XMMatrixTranslationFromVector(transform.vector); case types::transform_type::ROTATE: - assert(!vector_equal(XMVectorSetW(transform.vector, 0), XMVectorZero())); + //assert(!vector_equal(XMVectorSetW(transform.vector, 0), XMVectorZero())); + if (vector_equal(XMVectorSetW(transform.vector, 0), XMVectorZero())) + return XMMatrixIdentity(); return XMMatrixRotationNormal(transform.vector, XMConvertToRadians(XMVectorGetW(transform.vector))); case types::transform_type::SCALE: diff --git a/src/collada/scene.cpp b/src/collada/scene.cpp index e2163a9..5bc1532 100644 --- a/src/collada/scene.cpp +++ b/src/collada/scene.cpp @@ -50,6 +50,9 @@ namespace collada::scene { unsigned int diffuse; unsigned int specular; } texture_unit; + struct { + unsigned int joint; + } binding; }; const layout layout = { @@ -84,6 +87,9 @@ namespace collada::scene { .diffuse = GL_TEXTURE2, .specular = GL_TEXTURE3, }, + .binding { + .joint = 0, + }, }; unsigned int attribute_location(char const * const semantic, @@ -151,7 +157,11 @@ namespace collada::scene { glEnableVertexAttribArray(location); unsigned int gl_size = input_format_gl_size(inputs.elements[i].format); unsigned int gl_type = input_format_gl_type(inputs.elements[i].format); - glVertexAttribFormat(location, gl_size, gl_type, GL_FALSE, offset); + if (gl_type == GL_INT) { + glVertexAttribIFormat(location, gl_size, gl_type, offset); + } else { + glVertexAttribFormat(location, gl_size, gl_type, GL_FALSE, offset); + } glVertexAttribBinding(location, binding); offset += gl_size * 4; } @@ -179,8 +189,8 @@ namespace collada::scene { }; const int vertex_buffer_stride_jw - = input_format_gl_size(skin_inputs.elements[0].format) - + input_format_gl_size(skin_inputs.elements[1].format); + = 4 * input_format_gl_size(skin_inputs.elements[0].format) + + 4 * input_format_gl_size(skin_inputs.elements[1].format); void state::load_layouts() { @@ -209,7 +219,7 @@ namespace collada::scene { } } - unsigned int load_vertex_buffer(const char * filename) + static unsigned int load_vertex_buffer(const char * filename) { int size; void * data = read_file(filename, &size); @@ -226,7 +236,7 @@ namespace collada::scene { return vertex_buffer; } - unsigned int load_index_buffer(const char * filename) + static unsigned int load_index_buffer(const char * filename) { int size; void * data = read_file(filename, &size); @@ -265,6 +275,13 @@ namespace collada::scene { glBindTexture(GL_TEXTURE_2D, 0); } + static unsigned int load_uniform_buffer() + { + unsigned int buffer; + glGenBuffers(1, &buffer); + return buffer; + } + void state::load_scene(types::descriptor const * const descriptor) { this->descriptor = descriptor; @@ -274,6 +291,7 @@ namespace collada::scene { vertex_buffer_pnt = load_vertex_buffer(descriptor->position_normal_texture_buffer); vertex_buffer_jw = load_vertex_buffer(descriptor->joint_weight_buffer); index_buffer = load_index_buffer(descriptor->index_buffer); + joint_uniform_buffer = load_uniform_buffer(); load_images(); @@ -432,6 +450,21 @@ namespace collada::scene { types::instance_controller const &instance_controller = instance_controllers[i]; types::skin const &skin = instance_controller.controller->skin; + XMFLOAT4X4 joints[instance_controller.joint_count]; + int joints_size = (sizeof (joints)); + for (int joint_index = 0; joint_index < instance_controller.joint_count; joint_index++) { + XMMATRIX ibm = XMLoadFloat4x4((XMFLOAT4X4*)&skin.inverse_bind_matrices[joint_index]); + int node_index = instance_controller.joint_node_indices[joint_index]; + instance_types::node& node_instance = node_state.node_instances[node_index]; + + XMStoreFloat4x4(&joints[joint_index], ibm * node_instance.world); + } + glBindBuffer(GL_UNIFORM_BUFFER, joint_uniform_buffer); + glBufferData(GL_UNIFORM_BUFFER, joints_size, (void *)&joints[0], GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + glBindBufferRange(GL_UNIFORM_BUFFER, layout.binding.joint, joint_uniform_buffer, 0, joints_size); + draw_skin(skin, instance_controller.instance_materials, instance_controller.instance_materials_count); @@ -451,7 +484,7 @@ namespace collada::scene { } if (node.instance_controllers_count) { - glUseProgram(collada::effect::program_static); + glUseProgram(collada::effect::program_skinned); glUniformMatrix4fv(layout.uniform.transform, 1, false, (float *)&float_transform); draw_instance_controllers(node.instance_controllers, node.instance_controllers_count); } diff --git a/src/test.cpp b/src/test.cpp index b3fc9a0..0e1eab8 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -309,7 +309,7 @@ void load(const char * source_path) ////////////////////////////////////////////////////////////////////// collada::effect::load_effects(); - scene_state.load_scene(&shadow_test::descriptor); + scene_state.load_scene(&noodle::descriptor); node_eye = scene_state.find_node_by_name("Camera001"); assert(node_eye != nullptr); node_at = scene_state.find_node_by_name("Camera001.Target");