@@ -47,19 +47,17 @@ void SkeletonDebugRenderSystem::Render(entt::registry& registry, IDebugDrawConte
4747 buffers.models .begin (), buffers.models .end ());
4848
4949 // Render this skeleton instance
50- RenderSkeleton (ctx, pose_models, metadata.joint_parents ,
51- inst_transform.world_transform , debug_state,
52- metadata.metadata );
50+ RenderSkeleton (ctx, pose_models, metadata,
51+ inst_transform.world_transform , debug_state);
5352 }
5453}
5554
5655void SkeletonDebugRenderSystem::RenderSkeleton (
5756 IDebugDrawContext& ctx,
5857 const std::vector<ozz::math::Float4x4>& pose_models,
59- const std::vector< int >& skeleton_parents ,
58+ const SkeletonMetadata& skeleton_metadata ,
6059 const ozz::math::Float4x4& instance_transform,
61- const SkeletonDebugState& debug_state,
62- const XRay::Animation::ExtendedBoneMetadataCollection& bone_metadata)
60+ const SkeletonDebugState& debug_state)
6361{
6462 if (pose_models.empty ())
6563 return ;
@@ -70,6 +68,11 @@ void SkeletonDebugRenderSystem::RenderSkeleton(
7068 const ozz::math::Float4 joint_color{0 .92f , 0 .35f , 0 .35f , 0 .65f }; // Red-orange
7169 const ozz::math::Float4 link_color{0 .65f , 0 .65f , 0 .95f , 0 .55f }; // Purple-blue
7270
71+ // Get references for easier access
72+ const auto & skeleton_parents = skeleton_metadata.joint_parents ;
73+ const auto & joint_children = skeleton_metadata.joint_children ;
74+ const auto & bone_metadata = skeleton_metadata.metadata ;
75+
7376 // Lambda to compute rest length as fallback
7477 auto compute_rest_length = [&](int bone_index) -> float
7578 {
@@ -90,10 +93,10 @@ void SkeletonDebugRenderSystem::RenderSkeleton(
9093 if (result > kSkeletonDebugEpsilon )
9194 return result;
9295
93- // Try child distance
94- for ( size_t child = 0 ; child < skeleton_parents .size (); ++child )
96+ // Try child distance using pre-computed children (O(1) instead of O(N))
97+ if (bone_index < static_cast < int >(joint_children .size ()) )
9598 {
96- if (skeleton_parents[ child] == bone_index)
99+ for ( int child : joint_children[ bone_index] )
97100 {
98101 result = std::max (result, DistanceBetween (
99102 pose_models[bone_index], pose_models[child]));
@@ -103,7 +106,17 @@ void SkeletonDebugRenderSystem::RenderSkeleton(
103106 return result;
104107 };
105108
106- // Render each bone
109+ // Collect all bone instances for batched rendering
110+ std::vector<IDebugDrawContext::BoneInstance> bone_instances;
111+ bone_instances.reserve (pose_models.size ());
112+
113+ std::vector<IDebugDrawContext::SphereInstance> sphere_instances;
114+ if (debug_state.show_joint_positions )
115+ {
116+ sphere_instances.reserve (pose_models.size () * 2 ); // head + tail per bone
117+ }
118+
119+ // First pass: collect all instances and draw lines
107120 for (size_t bone = 0 ; bone < pose_models.size (); ++bone)
108121 {
109122 const ozz::math::Float4x4& pose_transform = pose_models[bone];
@@ -147,17 +160,16 @@ void SkeletonDebugRenderSystem::RenderSkeleton(
147160 ozz::math::Float3 tail_position = bone_position;
148161 bool has_child = false ;
149162
150- // Find first child
151- for ( size_t child = 0 ; child < skeleton_parents .size (); ++child )
163+ // Find first child using pre-computed children map (O(1) instead of O(N))
164+ if (bone < joint_children .size ())
152165 {
153- if (skeleton_parents[child] == static_cast < int >( bone) &&
154- child < pose_models.size ())
166+ const auto & children = joint_children[ bone];
167+ if (!children. empty () && static_cast < size_t >(children[ 0 ]) < pose_models.size ())
155168 {
156169 const ozz::math::Float4x4 child_world =
157- instance_transform * pose_models[child ];
170+ instance_transform * pose_models[children[ 0 ] ];
158171 tail_position = ExtractTranslation (child_world);
159172 has_child = true ;
160- break ;
161173 }
162174 }
163175
@@ -208,15 +220,26 @@ void SkeletonDebugRenderSystem::RenderSkeleton(
208220 kSkeletonDefaultRadius * 0 .4f ,
209221 bone_length * 0 .45f );
210222
211- // Draw bone shape
212- const ozz::math::Float4 draw_color = (bone == 0 ) ? root_color : bone_color;
213- ctx.DrawBoneShape (bone_position, tail_position, joint_radius, draw_color);
223+ // Collect bone instance for batched rendering
224+ IDebugDrawContext::BoneInstance bone_inst;
225+ bone_inst.head = bone_position;
226+ bone_inst.tail = tail_position;
227+ bone_inst.radius = joint_radius;
228+ bone_inst.color = (bone == 0 ) ? root_color : bone_color;
229+ bone_instances.push_back (bone_inst);
214230
215- // Draw joint spheres
231+ // Collect joint sphere instances if enabled
216232 if (debug_state.show_joint_positions )
217233 {
218- ctx.DrawSphere (bone_position, joint_radius, joint_color, 24 );
219- ctx.DrawSphere (tail_position, joint_radius * 0 .6f , joint_color, 24 );
234+ IDebugDrawContext::SphereInstance sphere_inst;
235+ sphere_inst.center = bone_position;
236+ sphere_inst.radius = joint_radius;
237+ sphere_inst.color = joint_color;
238+ sphere_instances.push_back (sphere_inst);
239+
240+ sphere_inst.center = tail_position;
241+ sphere_inst.radius = joint_radius * 0 .6f ;
242+ sphere_instances.push_back (sphere_inst);
220243 }
221244
222245 // Draw axes at root bone
@@ -231,6 +254,17 @@ void SkeletonDebugRenderSystem::RenderSkeleton(
231254 ozz::math::Float4{0 .3f , 0 .6f , 1 .0f , 1 .0f }); // Blue Z
232255 }
233256 }
257+
258+ // Second pass: render all collected instances in batches
259+ if (!bone_instances.empty ())
260+ {
261+ ctx.DrawBoneShapesInstanced (bone_instances.data (), bone_instances.size ());
262+ }
263+
264+ if (!sphere_instances.empty ())
265+ {
266+ ctx.DrawSpheresInstanced (sphere_instances.data (), sphere_instances.size ());
267+ }
234268}
235269
236270float SkeletonDebugRenderSystem::ComputeRestLength (
0 commit comments