Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IKConstraint produces NaN (formerly [ue4] Meshes disappear) #903

Closed
badlogic opened this issue May 16, 2017 · 11 comments
Closed

IKConstraint produces NaN (formerly [ue4] Meshes disappear) #903

badlogic opened this issue May 16, 2017 · 11 comments
Assignees
Milestone

Comments

@badlogic
Copy link
Collaborator

See this forum thread plus email from Frank Staff for repro files http://esotericsoftware.com/forum/Problem-exporting-in-Unreal-8555?p=38913&hilit=unreal#p38913

@badlogic badlogic added this to the v3.6 milestone May 16, 2017
@badlogic badlogic self-assigned this May 16, 2017
@badlogic
Copy link
Collaborator Author

Not a spine-c problem, works in Cocos2d-X runtime.

@badlogic
Copy link
Collaborator Author

Can reproduce in test scene playing Death01. Seems the SkeletonRendererComponent is at fault drawing that attachment.

@badlogic
Copy link
Collaborator Author

Getting NaN's in vertex positions for back_downskirt in last frame of death animation!

@badlogic
Copy link
Collaborator Author

badlogic commented May 23, 2017

Bone data for bone36 contains NaN's.

@badlogic
Copy link
Collaborator Author

It's a bug in path constraint, constraint path_down_skirt02 appears to be the culprit.

@badlogic
Copy link
Collaborator Author

Seems to be a bug in IKConstraint, produces NaNs for rotation.

@badlogic
Copy link
Collaborator Author

angle = ACOS(-a * l1 / (aa - bb)); in IKConstraint.c:181 produces a NaN for constraint IK_down_skirt02

@badlogic badlogic changed the title [ue4] Meshes disappear IKCosntraint produces NaN (formerly [ue4] Meshes disappear) May 23, 2017
@badlogic badlogic changed the title IKCosntraint produces NaN (formerly [ue4] Meshes disappear) IKConstraint produces NaN (formerly [ue4] Meshes disappear) May 23, 2017
@badlogic
Copy link
Collaborator Author

Turns out IKConstraint produces a NaN. In the reference implementation, the NaN gets eliminated by accident and not distributed throughout the whole skeleton update. In spine-c using MSVC++, the code takes a different path, resulting in NaN's everywhere. This needs to be fixed in all runtimes.

@NathanSweet please take a look. Here is the repro:

  1. Load repro skeleton in SkeletonViewer (see Slack for model file)
  2. Select Death02 animation, non-looping and wait for it to hit the last frame of the animation
  3. Set a breakpoint in IKConstraint.java:87, with the conditional data.name.equals("IK_down_skirt02"). Wait for it to be hit.
  4. Once the first breakpoint is hit, step through the call to apply(). Specifically, the calculation in line 250 produces a NaN because the absolute value of the argument to acos is bigger than 1 (~42.18). That NaN is then assigned to x, y and d in the subsequent statements. Then d is compared in a few different ways.

The C implementation enters the body in line 255 (line number in ref impl), so the NaN is assigned to minAngle, minDist, minX and minY and subsequently ends up in a1 and a2 which mess with the bone updates of parent and child, resulting in NaNs everywhere.

In the reference implementation, the first two if statements after line 250 are not evaluated, so minAngle, minDist, minX and minY retain their initial values of 0. Because of that, a1 and a2 never become NaN, and all is well.

As a quickfix i detect if angle in line 250 becomes NaN, and if so, the first two if statements after line 250 are not evaluted. This fixes the C implementation. But I have no idea what the implications are, and why the argument to acos is invalid in the first place.

Mhhhhh, NaN.

@badlogic
Copy link
Collaborator Author

@NathanSweet
Copy link
Member

It should not be possible for a NaN returned from ACOS to escape IkConstraint:

angle = ACOS(-a * l1 / (aa - bb)); // NaN
x = a * COS(angle) + l1; // NaN
y = b * SIN(angle); // NaN
dist = x * x + y * y; // NaN
if (dist < minDist) { /*impossible*/ }
if (dist > maxDist) { /*impossible*/ }
if (dd <= (minDist + maxDist) / 2) {
	/*impossible*/
} else {
	// Doesn't use NaN variables:
	a1 = ta - ATAN2(maxY * bendDir, maxX);
	a2 = maxAngle * bendDir;
}

It is a bit odd for the IK algorithm to rely on NaN, but the above should work across all languages. Before adding a NaN check, I'd like to understand how a1 or a2 could possibly be set to NaN.

@NathanSweet
Copy link
Member

Can't rely on IEEE 754 NaN behavior in C. Sad. Might as well avoid trig/NaN in all runtimes.

@badlogic badlogic removed the bug label May 24, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants