Thread: Need help with Vec2f:Rotate, or general math, I dunno...

1. Need help with Vec2f:Rotate, or general math, I dunno...

I have two "entities", and one entity "Weapon" is parented to another "Player". To handle parenting I'm updating the position of the child each frame to the parent's position + the local position of the child.

But I want it so if the parent is rotated, the position of the child is located accordingly, so I'm rotating the child's updated position around the center of the parent (as the pivot) and the parent's angle as the angle.

This works, kinda, but the child's position isn't always where it should be, let me show a few screens to help you understand:

The parent is the player object (the obvious player sprite).
The child is the weapon object (the red sprite).

This is where the parent is at a 0 degree angle:

This is where the parent is at a 90 degree angle:

This is where the parent is at a 180 degree angle:

And here is the code (in Lua, but it's binded to C++ using LuaBind, so the vector:Rotate function is ClanLib).

A function to set an entity's parent:
Code:
```function ENTITY:SetParent(entity)
if ( entity ~= nil and entity:IsValid() ) then
self.m_parent = entity;
self.m_localPos = self:GetPos() - entity:GetPos();
self.m_localAng = entity:GetAngle(ANGLE_DEGREES) - self:GetAngle(ANGLE_DEGREES);
else
self.m_parent = nil;
self.m_localPos = nil;
self.m_localAng = nil;
end;
end;```
Called every frame to update the child's position:
Code:
```	if ( self.m_parent and self.m_parent:IsValid() ) then
self:SetPos( self.m_parent:LocalToWorld(self.m_localPos) );
self:SetAngle( self.m_parent:GetAngle(ANGLE_DEGREES) + self.m_localAng );
else
self:SetParent(nil);
end;```
Local to world function:
Code:
```function ENTITY:LocalToWorld(localPos)
local targetPos = self:GetPos() + localPos;
targetPos:Rotate( self:GetCenter(), self:GetAngle() );

return targetPos;
end;```

2. I don't fully understand the lua script (because i'm tired at the moment)

However, this is how I would approach it:

The MAN is defined at MAN_Width, MAN_Height. The centre of rotation is MAN_Centre

The GUN is defined at GUN_Width, GUN_Height. The centre of rotation is GUN_Centre

The DEST position is (100,100)

At 0 degrees. If you place MAN_Center at DEST, and GUN_Center at DEST. The GUN will be at the center of the MAN.

So, an offset to GUN is required. We define this as GUN_Translation.

Now, At 0 degrees. If you place MAN_Center at DEST, and (GUN_Center + GUN_Translate) at DEST. The GUN will be at placed at the desired position on the MAN.

For other angles.

We define ANGLE as the desired angle

Now, at ANGLE degrees.
1) Place MAN : MAN_Center at DEST. Apply ANGLE rotation to the sprite
2) Calculate : GUN_Rotated_Translation = ANGLE * GUN_Translation
3) Place GUN at : (GUN_Center + GUN_Rotation Translation) at DEST. Apply ANGLE rotation to the sprite.

I believe step 2 is performed automatically when you use CL_Sprite::set_alignment() to GUN_Translation.

Be careful to pick the correct CL_Origin parameter if using CL_Sprite::set_alignment() and CL_Sprite::set_rotation_hotspot(). The x,y parameter is relative to the origin you pick. i.e. for origin_center, x=0, y=0 would be the center of the sprite.

3. You also have the option of using a 3x3 matrix to do your rotations. My lua skills are even worse than those of rombust, so I'll illustrate it with some C++ code:

Code:
```class GameObject
{
public:
float x, y, angle;
virtual void render(CL_GraphicContext &gc, CL_Mat3f local_to_world) = 0;
};```
OK, so the idea is that to render a base object we call all top-level objects like this:

Code:
```CL_Mat3f local_to_world = CL_Mat3f::identity();
for (size_t i = 0; i < objects.size(); i++)
{
objects[i]->render(gc, local_to_world);
}```
A simple object rendering itself using CL_Draw::texture might be implemented like this:

Code:
```void SimpleObject::render(CL_GraphicContext &gc, CL_Mat3f local_to_world)
{
local_to_world.multiply(translate_2d_matrix(x,y));
local_to_world.multiply(rotate_2d_matrix(angle));
CL_Vec3f p1 = local_to_world * CL_Vec3f(0.0f, 0.0f, 1.0f);
CL_Vec3f p2 = local_to_world * CL_Vec3f(texture_width, 0.0f, 1.0f);
CL_Vec3f p3 = local_to_world * CL_Vec3f(0.0f, texture_height, 1.0f);
CL_Vec3f p4 = local_to_world * CL_Vec3f(texture_width, texture_height, 1.0f);

CL_Draw::texture(gc, texture, CL_Quad(p1, p2, p3, p4));
}```
The object with relative child objects would look like this:

Code:
```void ParentObject::render(CL_GraphicContext &gc, CL_Mat3f local_to_world)
{
local_to_world.multiply(translate_2d_matrix(x,y));
local_to_world.multiply(rotate_2d_matrix(angle));
for (size_t i = 0; i < children.size(); i++)
{
children[i]->render(gc, local_to_world);
}
}```
Although Rombust' amazing vector and matrix classes got almost everything you'll ever need, it needs these two extra functions:

Code:
```CL_Mat3f translate_2d_matrix(float x, floaty)
{
CL_Mat3f m = CL_Mat3f::identity();
m[2] = x;
m[5] = y;
return m;
}

CL_Mat3f rotate_2d_matrix(CL_Angle angle)
{
CL_Mat3f m = CL_Mat3f::identity();
m[0] = cos(angle.value);
m[1] = sin(angle.value);
m[3] = -sin(angle.value);
m[4] = cos(angle.value);
return m;
}```
That should do it, I think! The advantage of using this method is that if you ever play around with 3D graphics its usually done exactly the same way, except using CL_Mat4 and CL_Vec4 instead.

Disclaimer: None of this code has been tested. It may burn down your PC and kill your cat. If you can't get it to work, look up the 2D 3x3 rotation and translation matrices and verify that my retarded attempt at math is even remotely correct.

4. Wow thanks for that, you're both lifesavers!

Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•