Page 1 of 1

Rotation around axis?

Posted: Mon Jan 29, 2007 9:46 pm
by Rookie One.pl
I haven't been able to find a solution on the net and I don't really get the function Quake has:

Code: Select all

/*
===============
RotatePointAroundVector

This is not implemented very well...
===============
*/
void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point,
							 float degrees ) {
	float	m[3][3];
	float	im[3][3];
	float	zrot[3][3];
	float	tmpmat[3][3];
	float	rot[3][3];
	int	i;
	vec3_t vr, vup, vf;
	float	rad;

	vf[0] = dir[0];
	vf[1] = dir[1];
	vf[2] = dir[2];

	PerpendicularVector( vr, dir );
	CrossProduct( vr, vf, vup );

	m[0][0] = vr[0];
	m[1][0] = vr[1];
	m[2][0] = vr[2];

	m[0][1] = vup[0];
	m[1][1] = vup[1];
	m[2][1] = vup[2];

	m[0][2] = vf[0];
	m[1][2] = vf[1];
	m[2][2] = vf[2];

	memcpy( im, m, sizeof( im ) );

	im[0][1] = m[1][0];
	im[0][2] = m[2][0];
	im[1][0] = m[0][1];
	im[1][2] = m[2][1];
	im[2][0] = m[0][2];
	im[2][1] = m[1][2];

	memset( zrot, 0, sizeof( zrot ) );
	zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;

	rad = DEG2RAD( degrees );
	zrot[0][0] = cos( rad );
	zrot[0][1] = sin( rad );
	zrot[1][0] = -sin( rad );
	zrot[1][1] = cos( rad );

	MatrixMultiply( m, zrot, tmpmat );
	MatrixMultiply( tmpmat, im, rot );

	for ( i = 0; i < 3; i++ ) {
		dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];
	}
}
Could any of our .map matrix-enabled brainiacs (*cough* jv *cough*) explain to me how to rotate a point around another point and an axis in 3D space? :) Specifically, I'd like to have a point with absolute coords rotated by a (pitch, yaw, roll) vector with the rotation centre in another point. C or pseudo code will be much appreciated. :)

I'm doing this project of real physics implementation in Quake 3 for the Culture & Science week at school. :P

Posted: Tue Jan 30, 2007 8:27 am
by lizardkid
This is going to be lengthy :oops:

The way i implemented it was like this. (in Java, but it should port well enough)

i made a Transform class, which held rotation, translation, and scaling data for a Polygon class, which held a list of Vectors that made up the poly. A poly that would move around a lot (such as a door or other entity) was given it's own Transform instance and the hierchy went on from there. (Polyhedrons would hold Poly lists as well as a master Transform, etc)

So then, i precomputed rotation values for that sort of thing;

In a nutshell, when i created a Transform it's constructor was passed the x y and z variables that it's Vector had. Then it went about creating cosine and sine values for the angles passed.

Code snippets use 'this' to emphasize it's a member variable...

Code: Select all

public Transform(float x, float y, float z)
{
      this.cosX = Math.cos(x);
      this.cosY = Math.cos(y); // Math.cos gets the cosine of the var.
      this.cosZ = Math.cos(z);

      //... same for Sine values
}
Simple enough.

So now, every update, the Vector class takes it's Transform instance and does this. (Sorry for the messiness, but it's a direct copy-paste)

Code: Select all


    
    // where it all starts
    public void addRotation(Transform t)
    {
        rotateX(t.getCosX(), t.getSinX());
        rotateZ(t.getCosZ(), t.getSinZ());
        rotateY(t.getCosY(), t.getSinY());
    }

    // GET ANGLES
    public float getAngleX()
    {return ((float)Math.atan2(sinX, cosX));}
    
    public float getAngleY()
    {return ((float)Math.atan2(sinY, cosY));}
    
    public float getAngleZ()
    {return ((float)Math.atan2(sinZ, cosZ));}
    

    // SET ANGLES
    public void setAngleX(float angleX)
    {
        cosX = (float)Math.cos(angleX);
        sinX = (float)Math.sin(angleX);
    }
    
    public void setAngleY(float angleY)
    {
        cosY = (float)Math.cos(angleY);
        sinY = (float)Math.sin(angleY);
    }
    
    public void setAngleZ(float angleZ)
    {
        cosZ = (float)Math.cos(angleZ);
        sinZ = (float)Math.sin(angleZ);
    }

    
    // ROTATE AXIS    
    public void rotateX(float cos, float sin)
    {// rotate this vector around the X AXIS
           
        y = y*cos - z*sin;
        z = y*sin + z*cos;
    }
    
    public void rotateY(float cos, float sin)
    {
        x = z*sin + x*cos;
        z = z*cos - x*sin;
    }
    
    public void rotateZ(float cos, float sin)
    {
        x = x*cos - y*sin;
        y = x*sin + y*cos;
    }
    
    

    public void rotateAngle(float angleX, float angleY, float angleZ)
    {
        rotateX(angleX);
        rotateY(angleY);
        rotateZ(angleZ);
    }

So pretty much, this rotates a Vector around a point. I had to collaborate with my math prof for most of this, so it uses the right-hand rule. Not totally sure what it'd look like with the normal MOH-style ruling.

If you wanted to rotate around an "alien" point, you'd just pass the Transform different values for x, y, and z so it would precompute sine and cosine differently.

sorry i couldn't give you C++ code, not used to it myself. You know how it goes when you get in a roll with one language, don't really want to reinvent the wheel...

Posted: Tue Jan 30, 2007 10:27 am
by jv_map
Hmm that's interesting liz... rotateX takes 1 argument yet you're sending 2 :P

Anyway, Rookie, I'm not totally sure what you want... the code snippet from Q3 looks like an application of Rodrigues' formula (Note: <u,v> on this page is simply the dot product) which rotates a vector (i.e. a point) around a unit vector (i.e. an axis) for a given angle (float degrees). Yet you say you want to rotate by a (pitch, yaw, roll) vector (like angles in moh) so I'm a bit confused.

The first 50% of solving a problem is finding out what you want :)

Posted: Tue Jan 30, 2007 3:21 pm
by Rookie One.pl
Thanks, Lizard. :) I have just skimmed your solution as I'm in a bit of a hurry right now, so I'll read it carefully later on, OK? ;) Don't be sorry for the language, this code snippet could be ported almost directly to C++. ;)

Yeah, JV, I know that function is different, that's why I asked for another way. ;) I just wasn't sure of what exactly does this Quake function do. However, after reading the Wikipedia article you posted the link to I think I figured it out right. ;) The thing is, the point around which it will be rotated is ( 0 0 0 ), right? If so, would this code be valid for what I want to do (rotate point by angles around centre)?

Code: Select all

void RotatePointAroundVectorByAngles(vec3_t dst, vec3_t point, vec3_t angles, vec3_t centre) {
	vec3_t axis;
	// absolute -> relative
	if (centre)
		VectorSubtract(point, centre, dst);
	// pitch
	axis[0] = 1.f;
	axis[1] = axis[2] = 0.f;
	RotatePointAroundVector(dst, axis, dst, angles[0]);
	// yaw
	axis[0] = axis[1] = 0.f;
	axis[2] = 1.f;
	RotatePointAroundVector(dst, axis, dst, angles[1]);
	// roll
	axis[0] = axis[2] = 0.f;
	axis[1] = 1.f;
	RotatePointAroundVector(dst, axis, dst, angles[2]);
	// relative -> absolute
	if (centre)
		VectorAdd(dst, centre, dst);
}
VectorSubtract(a, b, c) is c = a - b, VectorAdd(a, b, c) is c = a + b. The if (centre) lines are there in case I'll ever want to rotate around ( 0 0 0 ) I can just send NULL. ;)

Posted: Tue Jan 30, 2007 3:43 pm
by jv_map
Yah that looks great :) .. getting the hang of it I can see 8-) .. the 'centering' is implemented correctly. :D

The only thing is, if you want them to be true pitch, yaw and roll angles, you have to rotate yaw first and then pitch (this is what moh does too). What you have now will work too, but it gives a different result... e.g. in your case if you pitch down say 45 degrees first and then yaw 45 degrees you'll end up in a 'rolled' position (the y-axis is not horizontal). If you do yaw first and then pitch the y-axis will be horizontal. Always tricky stuff with angles :wink:

Posted: Tue Jan 30, 2007 3:50 pm
by Rookie One.pl
Ah, OK, good to know. :) Thanks! :)

Posted: Tue Jan 30, 2007 7:50 pm
by lizardkid
looks like i posted the wrong overloaded set... it's what i get for only taking half of a class' code i suppose. Fixed now.

Posted: Tue Jan 30, 2007 8:53 pm
by Rookie One.pl
Your implementation seems to be much more efficient than my half-assed solution, I'll use it when I get to the point of code optimization. ;)