| 279 | } |
| 280 | |
| 281 | void MjUtils::DrawDebugJoint(UWorld* World, const FVector& anchor, const FVector& Axis, |
| 282 | int JointType, bool bLimited, float RangeMin, float RangeMax, |
| 283 | float CurrentPos, float RefPos, float ArcRadius) |
| 284 | { |
| 285 | if (!World) |
| 286 | return; |
| 287 | |
| 288 | const FColor ArcColor(40, 200, 40); // Bright green arc outline |
| 289 | const FColor FillColor(40, 180, 40, 50); // Semi-transparent green fill |
| 290 | const FColor LimitColor(220, 60, 60); // Red limit marks |
| 291 | const FColor PosColor(255, 220, 30); // Yellow current position |
| 292 | const FColor RefColor(180, 180, 220); // Light blue-grey reference position |
| 293 | FVector AxisDir = Axis.GetSafeNormal(); |
| 294 | |
| 295 | // Negate all rotation angles: MuJoCo uses right-hand rule but the |
| 296 | // axis has been Y-negated for UE's left-hand coordinate system. |
| 297 | const float Sign = -1.0f; |
| 298 | |
| 299 | if (JointType == mjJNT_HINGE) |
| 300 | { |
| 301 | // Find a perpendicular radial direction |
| 302 | FVector RadialDir = FVector::CrossProduct(AxisDir, FVector::UpVector); |
| 303 | if (RadialDir.SizeSquared() < 1e-6f) |
| 304 | { |
| 305 | RadialDir = FVector::CrossProduct(AxisDir, FVector::RightVector); |
| 306 | } |
| 307 | RadialDir.Normalize(); |
| 308 | |
| 309 | if (bLimited && (RangeMin != 0.0f || RangeMax != 0.0f)) |
| 310 | { |
| 311 | // Compute draw angles (swap min/max since we negate) |
| 312 | float DrawMin = Sign * RangeMax; |
| 313 | float DrawMax = Sign * RangeMin; |
| 314 | const int NumSegments = 48; |
| 315 | float Span = DrawMax - DrawMin; |
| 316 | float Step = Span / NumSegments; |
| 317 | |
| 318 | // Build arc points for reuse |
| 319 | TArray<FVector> ArcPoints; |
| 320 | ArcPoints.Reserve(NumSegments + 1); |
| 321 | for (int i = 0; i <= NumSegments; ++i) |
| 322 | { |
| 323 | float Theta = DrawMin + Step * i; |
| 324 | ArcPoints.Add(anchor + FQuat(AxisDir, Theta).RotateVector(RadialDir) * ArcRadius); |
| 325 | } |
| 326 | |
| 327 | // Filled arc (triangle fan from anchor) |
| 328 | TArray<FVector> FillVerts; |
| 329 | TArray<int32> FillIndices; |
| 330 | FillVerts.Add(anchor); // index 0 = center |
| 331 | for (int i = 0; i <= NumSegments; ++i) |
| 332 | { |
| 333 | FillVerts.Add(ArcPoints[i]); |
| 334 | } |
| 335 | for (int i = 0; i < NumSegments; ++i) |
| 336 | { |
| 337 | FillIndices.Add(0); |
| 338 | FillIndices.Add(i + 1); |
nothing calls this directly
no outgoing calls
no test coverage detected