Trouble with LibGDX resetting origin on an already rotated actor - rotation

If I do:
actor.setOrigin(0, 0);
actor.setRotation(45);
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
It appears that on the last setOrigin call, the actor gets repositioned to the location it would've been if actor.setRotation(45) would have been called after its latest origin was set.
What do I do to make it so that the latest origin of the actor is only used for future "scale" and "rotation" actions?

Okay so i looked in the source code of libgdx, and i'll tell you the short answer.
Basically when you set the origin or the rotation, you just change a variable named "originx", "originy" and "rotation". So every call to setOrigin will overwrite the values set in previous calls.
And every time you draw the actor, it recalculates the bounds using the current variable.
To be clear, setOrigin looks like this :
public void setOrigin (float originX, float originY) {
this.originX = originX;
this.originY = originY;
}
So the precedent setOrigin is lost.

The reposition of the actor itself in your case does not change, but the position of the displayed sprite or texture will change.
It is calculated in this order:
Position -> Origin -> Scale -> Rotation
See: Sprite.java (method: "getVertices ()")
When you change the Origin point of an already rotated element, the point in the plane around which the rotation occurs changes and the sprite will be drawn in a different place (the actor's position in this case does not change).

Related

Make a rigged character's head rotate in sync with a quaternion in Unity

I have a face detection app, and I want a character's head to rotate according to the detected face's pose.
I've managed to get the rotation of the detected face in the form of a quaternion, but I'm unsure about how I'm supposed to translate the data from the quaternion into 3D points for the reference points of the rigged character which I believe will decide the rotation.
Let's say I have this character: http://i.imgur.com/3pcRoYx.png
One solution could be to just cut off the head and make it an own object and then set the rotation of that object according to the quaternion, but I don't want that. I want an intact character.
Is it possible to move the reference points in the head with the data from a quaternion? Or have I gotten it wrong how rigged characters turn their heads? I haven't animated before.
You can apply rotation to a single bone. Get that bone in your script. Keep a var in your class to store the last quaternion in and every update, compare it to that and rotate by the different. I don't have the actual editor here but try this psuedocode.
class NeckRotator {
public GameObject Neck;
private Quaternion LastFace;
void Start(){
LastFace = Neck.transform.Rotation;
}
void Update(){
var DetectedFace = ... // Whatever you do to get this
var Change = Quaternion.Inverse(DetectedFace) * LastFace; // Found this online real quick
Neck.Rotate(Change);
LastFace = Neck.transform.Rotation;
}
}
I've done something like that before to rotate a neck of an NPC to look at a player. It should work for your deal as well.

How to give control back to animation in Unity3D once altering objects location

I have a character made of up child objects that are animated using Unity3D's animation system.
While the player is walking, I can programmatically move the hand object up to catch a ball using the following code.
hand.position.y = ball.transform.position.y;
I need the hand object to go back to following the walk animation after it touches the ball, but instead it just stays at the exact position since it was set.
You want to use inverse kinematics and let Unity do the work of figuring out positioning for you. Here's a quick-and-dirty (untested) example for catching a ball (it's in C#, but it should be pretty similar for UnityScript):
// in a script on the same GameObject as your animation controller
bool isCatching;
Transform ball;
void OnAnimatorIK (int layer) {
if (isCatching) {
// set position and rotation weights for your catching hand
animator.SetIKPosition(AvatarIKGoal.RightHand, ball.position);
animator.SetIKRotation(AvatarIKGoal.RightHand, ball.rotation);
} else {
// return your position and rotation weights back to their defaults (probably 0f?)
}
}
You'll need to do some work (possibly raycasting or just checking distance and direction) to determine when to set the isCatching flag to true, and you'll want to play with the weights for position and rotation to make it look natural. The IK manual entry has more detailed information.

How to get bone transform for a particular animation frame

I have an animation with about 20 frames. I need to be able to access local transforms for each bone for a given animation frame. I know how to access the bone and its local transform (a sample of the code)
Transform root, spine1;
getChildFromName(gameObj, "Jnt_Root", out root);
getChildFromName(root, "Jnt_Spine1", out spine1);
spine1.localRotation = someValue;
All of this works fine, but I don't know the values I'm getting are from which animation frame? I assume its from frame 1 (can verify using debugger but that's not the point)
The questions is how to get and set these values for a specific frame? Thanks!
Something like this should work for getting the current transforms:
AnimationState state = animation["your_animation"];
state.enabled = true;
state.normalizedTime = (1.0f/totalAnimationTime) * specificFrame;
animation.Sample();
// get all transforms of this animation, extract your root and spine from here.
Transform[] transforms = animation.gameObject.GetComponentsInChildren<Transform>();
Or if you're trying to sample while the animation is running you could do something like:
if(animation["your_animation"].normalizedTime > 0.3 && animation["your_animation"].normalizedTime < 0.5) {
//... do something at this point in time. You'll have to figure out the frame
//from the time
}
Last I checked you can't explicitly extract a particular frame. But if you know the total length of your animation (time-wise), you can move the animation to that point with something like: (1.0f/totalAnimationTime) * specificFrame; (this assumes the keyframes are uniformly spaced.)
Once stored you should be able to modify the transforms directly but I've never tried.

How to use Transformation Matrix for SpriteBatch correctly?

I'm new to XNA and would like to develop a light-weight 2D engine over it, with the entities organized into parent-child hierarchy. I think of matrix when drawing children, because their position, rotation and scale are depend on their parent.
If I use SpriteBatch.Begin(), my rectangles can be drawn on the screen, but when I change them into:
this.DrawingMatrix = Matrix.Identity;
this.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullClockwise, null, this.DrawingMatrix);
nothing is drawn anymore. I even tried new Matrix() or Matrix.CreateTranslation(0, 0, 0) for DrawingMatrix.
My first question is: why doesn't it work? I'm not working with any camera or viewport.
Secondly, before drawing an entity, I call the PreDraw to transform the matrix (I will then reset to original state at PostDraw):
protected virtual void PreDraw(Engine pEngine)
{
pEngine.DrawingMatrix *=
Matrix.CreateTranslation(this.X, this.Y, 0) *
Matrix.CreateScale(this.ScaleX, this.ScaleY, 1) *
Matrix.CreateRotationZ(this.Rotation);
}
Please clarify the correction of above code. And I need to scale not at the origin, but at ScaleCenterX and ScaleCenterY, how can I achieve this?
ADDED: Here is an example of my engine's draw process:
Call these code:
this.DrawingMatrix = Matrix.CreateTranslation(0, 0, 0);
this.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullClockwise, null, this.DrawingMatrix);
Call PreDraw(), with is:
protected virtual void PreDraw(Engine pEngine)
{
pEngine.DrawingMatrix *=
Matrix.CreateTranslation(this.X, this.Y, 0) *
Matrix.CreateScale(this.ScaleX, this.ScaleY, 1) *
Matrix.CreateRotationZ(this.Rotation);
}
Call Draw(), for example, in my Rect class:
protected override void Draw(Engine pEngine)
{
pEngine.SpriteBatch.Draw(pEngine.RectangleTexture, new Rectangle(0, 0, (int)this.Width, (int)this.Height), new Rectangle(0, 0, 1, 1), this.Color);
}
If I replace above Begin code with this.SpriteBatch.Begin(), the rectangle is drawn correctly, so I guess it is because of the matrix.
First issue is a simple bug: The default for SpriteBatch is CullCounterClockwise, but you have specified CullClockwise causing all your sprites to get back-face-culled. You can pass null if you just want to use the default render states - you don't need to specify them explicitly.
(You would need to change the cull mode if you used a negative scale.)
To answer your second question: You need to translate "back" to place the scaling origin (your ScaleCenterX and ScaleCenterY) at the world origin (0,0). Transformations always happen around (0,0). So normally the order is: translate sprite origin back to the world origin, scale, rotate, translate to place sprite origin at desired world position.
Also, I hope that your PostDraw is not applying the reverse transformations (you made it sound like it does). That is very likely to cause precision problems. You should save and restore the matrix instead.

Convert a given point from the window’s base coordinate system to the screen coordinate system

I am trying to figure out the way to convert a given point from the window’s base coordinate system to the screen coordinate system. I mean something like - (NSPoint)convertBaseToScreen:(NSPoint)point.
But I want it from quartz/carbon.
I have CGContextRef and its Bounds with me. But the bounds are with respect to Window to which CGContextRef belongs. For Example, if window is at location (100, 100, 50, 50) with respect to screen the contextRef for window would be (0,0, 50, 50). i.e. I am at location (0,0) but actually on screen I am at (100,100). I
Any suggestion are appreciated.
Thank you.
The window maintains its own position in global screen space and the compositor knows how to put that window's image at the correct location in screen space. The context itself, however doesn't have a location.
Quartz Compositor knows where the window is positioned on the screen, but Quartz 2D doesn't know anything more than how big the area it is supposed to draw in is. It has no idea where Quartz Compositor is going to put the drawing once it is done.
Similarly, when putting together the contents of a window, the frameworks provide the view system. The view system allows the OS to create contexts for drawing individual parts of a window and manages the placement of the results of drawing in those views, usually by manipulating the context's transform, or by creating temporary offscreen contexts. The context itself, however, doesn't know where the final graphic is going to be rendered.
I'm not sure if you can use directly CGContextRef, you need window or view reference or something like do the conversion.
The code I use does the opposite convert mouse coordinates from global (screen) to view local and it goes something like this:
Point mouseLoc; // point you want to convert to global coordinates
HIPoint where; // final coordinates
PixMapHandle portPixMap;
// portpixmap is needed to get correct offset otherwise y coord off at least by menu bar height
portPixMap = portPixMap = GetPortPixMap( GetWindowPort( GetControlOwner( view ) ) );
QDGlobalToLocalPoint(GetWindowPort( GetControlOwner( view ), &mouseLoc);
where.x = mouseLoc.h - (**portPixMap).bounds.left;
where.y = mouseLoc.v - (**portPixMap).bounds.top;
HIViewConvertPoint( &where, NULL, view );
so I guess the opposite is needed for you (haven't tested if it actually works):
void convert_point_to_screen(HIView view, HIPoint *point)
{
Point point; // used for QD calls
PixMapHandle portPixMap = GetPortPixMap( GetWindowPort( GetControlOwner( view ) ) );
HIViewConvertPoint( &where, view, NULL ); // view local to window local coordtinates
point.h = where->x + (**portPixMap).bounds.left;
point.w = where->y + (**portPixMap).bounds.top;
QDLocalToGlobalPoint(GetWindowPort( GetControlOwner( view ), &point);
// convert Point to HIPoint
where->x = point.h;
where->y = point.v;
}

Resources