How makevectors works, really?

Discuss programming in the QuakeC language.
Post Reply
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

How makevectors works, really?

Post by toneddu2000 »

Hy guys, I'm reading a CSQC code that bend skeletal spine bone depending on mouse input:

Code: Select all

   local vector  ang, body_ang;
   body_ang = skel_get_bonerel (self.skeletonindex, 	1);
	ang = '180 0 0';
	ang_y = self.bodyangle_y * 0.5;
	makevectors(ang);
	skel_set_bone(self.skeletonindex, 1, body_ang);
	//spine bone
	ang = '0 0 0';
	ang_y = self.bodyangle_y * 0.25;
	ang_z = -input_angles_x * 0.5; // let's divide it.
	self.angles_y = input_angles_y+90;
	makevectors(ang);
Now, I'm just not understanding how it works.
From the manual, makevectors is described in this way:

Code: Select all

Calculate the vectors pointing forward, right and up, according to the provided angles.
Ok, cool.

Code: Select all

 Returns result in the global variables: 
        vector  v_forward;  // points forward
        vector  v_up;       // points up
        vector  v_right;    // points toward the right
...uh?! I mean, what global vars? Linked to which entity? The player, A player, an enemy, a bone (in this case)?
It would have been simpler to me to grasp if it was something like that:

Code: Select all

local vector  newvector;
newvector = makevectors(boneSpine.angles);
print("The angle of Spine is "+ vtos(newvector));
In this case, I trap return values of makevector function in a vector and I use that vector to do my stuff...

But I really don't understand how is that possible that calling makevectors(angle) it make the result values petrified in global vars...and HOW these global vars are "reset" after makevectors function has been called?!

EDIT: I put in the QC section because this wasn't meant to be a CSQC topic (the CSQC code above is just an example)
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: How makevectors works, really?

Post by Spike »

makevectors takes eular angles (pitch, yaw, roll) and sets v_forward, v_right, and v_up to that position.
the result is actually a 3*3 matrix (often refered to as an 'axis set', or axis for short despite the issues with plurality).

skel_set_bone sets a bone's relative position to the v_forward/v_right/v_up orientation, along with the offset passed to the command.
note that this is a RELATIVE position, thus you need to consider the parent position too, and this will also affect the bone's descendants too.
the angle passed to makevectors beforehand will be the angle of the bone relative to the angle of the bone's parent. any animation info for this bone will be _completely replaced_.

skel_mul_bone (and skel_mul_bones) will rotate the bone by the arguments without destroying any information.
makevectors('0 5 0') beforehand will rotate that bone (and its decendants) by 5 degrees around the vertical, from its prior position, regardless of what it was.
makevectors('0 0 0') will result in the identity matrix. rotations by the rotation matrix will of course do nothing.

skel_set_bone_world sets a bone's position to an actual world position for the bone. if you want to put a single bone in a specific place, you can use this builtin to put it there. note that this is not inverse kinematics, and that the skeleton stays relative. so if you're updating a whole bunch of bones with this builtin (because you wrote your own IK), start with the root and work through to the leafs. :s
you can use gettaginfo in order to read a bone's world position.



So, back to your problem...
if you have a spine with 4 bones, you can use skel_mul_bones with a yaw angle of (mouseangle_y-toonangle_y)/4 to affect all 4 bones in a single call with a vaugely smooth curve.
make this call after skel_build, so that you don't destroy any animation data/it doesn't get overwritten aka ignored.



Yes, I hate matrix multiplication too.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: How makevectors works, really?

Post by toneddu2000 »

Thanks Spike for the detailed explanation of skel_mul_bones now I understood (I think) that:
skel_set bone rotates a bone discarding animation data (useful for spine control imo)
skel_mul_bone rotates a bone CONSIDERING animation data, so it's like a blend of animation and code inject, right?

Unfortunately you quite didn't answer my original makevectors question.
Every time I issue the command makevectors, it sets v_forward, v_right, and v_up to that position. Ok.
But this position, what entity is linked to? Self entity?
And, if I issue the command twice, one after the other, in the same piece of code, just like the example I posted:

Code: Select all

body_ang = skel_get_bonerel (self.skeletonindex, 	1);
	ang = '180 0 0';
	ang_y = self.bodyangle_y * 0.5;
	makevectors(ang); // FIRST INVOCATION
	skel_set_bone(self.skeletonindex, 1, body_ang);
	ang = '0 0 0';
	ang_z = self.wish_ang_x * 0.5; // let's divide it.
	makevectors(ang); //SECOND INVOCATION
	skel_set_bone(self.skeletonindex, 2, '0.0 0.4 -2.0');
  //DO OTHER STUFF...
what values replaces the first ones? I can't understand (just for the records, the code above DOES WORK but I cannot understand HOW! :D ) how (and WHOSE)orientation values (of a root bone first and of a neck bone later) are stored in v_forward, v_right, and v_up. Is there a standard method to store the orientation of a bone in a vector and then move the bone according to that vector + the input movement?
I tried:

Code: Select all

local vector v;
body_ang = skel_get_bonerel (self.skeletonindex,skel_find_bone(self.skeletonindex,"pelvis"));
ang = '0 0 0';
v = vectoangles(body_ang); //I thougth that this could have been a valid alternative to makevectors
v_y = -input_angles_x * 0.5;
skel_set_bone(self.skeletonindex,skel_find_bone(self.skeletonindex,"belly"), v); //moves the torso
Thanks again
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: How makevectors works, really?

Post by Spike »

huh?
makevectors itself doesn't affect any entity.
when given the angle '0 0 0' (identity) it returns the identity matrix, that is, v_forward='1 0 0', v_right = '0 1 0', v_up = ' 0 0 1'. pass in 90 degrees in some axis and everything will rotate around by 90 degrees.
sure, if you pass self.v_angles then the result is correct for self's orientation... as expected...

your two set_bone calls apply to different bones, neither replaces the other because they write out different bone matricies. remember that the child bone's matrix is expressed as relative to its parent, thus changing the parent affects both (without destroying any information in the child, but will destroy/replace existing animation data in the parent bone's pose), while changing the child affects only the child (and will destroy any existing information in the child's pose).

your second example will set the belly orientation to that of the pelvis, and set the belly's position to some weird fecked up angle value, so it moves forwards/back based upon angle.
which really doesn't make any sense at all.

in order to set a bone to an absolute world orientation+position, you must multiply the desired child position by the inverse of the parent's orientation+position. this cancels out the inherited position of the parent giving you a rotation to the child, which can be stored in the bone's pose. QC kinda sucks for matrix maths (and matrix inversion is nasty), so you'll want to avoid that and either use skel_set_bone_world (so the engine does the inversion stuff for you) or use skel_mul_bone(s) so you can just specify a rotation from its previous position.
if you merely want to move a bone, you should query that bone's current (rel) orientation, then discard its translation and call set_bone with v_forward/right/up still set to the queried bone orientation.

most of the time, you'll want to avoid ever using skel_set_bone (the one exception here is for the root bone), and to exclusively use skel_mul_bone. called after skel_build combos and combined with makevectors you can express the rotation of a bone relative to its parent bone in a fairly convienient way, without needing to care about its current orientation.

Code: Select all

float spine = skel_find_bone(self.skeletonindex, "spine");
gettaginfo(self, spine); //set v_forward, v_right, v_up. discard world coord
vector spineang = vectoangles(v_forward, v_up); //convert the orientation to eular angles. lets hope its correctly normalised (in which case v_right is surplus to requirements).
spineang_x *= -1; //stupid vectoangle/mdl bug
//spineang is now the bone's current angle in world space. which is handy, because input_angles is in world space too
makevectors([input_angles_x - spineang_x, input_angles_y - spineang_y, 0]); //determine the new orientation relative to the old, in eular angles. note that eular angles suck, but we're discarding the roll angle anyway. writes v_forward and friends.
skel_mul_bone(self.skeletonindex, spine, '0 0 0'); //rotate the spine bone by the v_forward and friends values.
bones above your spine will now be facing your input_angles. bones below the spine will still be facing the entity angles, as they were before adding this code.
or something, hell, I don't know. I hate matricies. you figure it out.
of course if you just use the entity angle in the first place instead of gettaginfo+vectoangles, you can save a lot of work for the engine as well as allowing pelvis angle wobbles to affect the torso.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: How makevectors works, really?

Post by Spike »

and because you'll probably still complain that I didn't explain makevectors...

Code: Select all

//ripped from the quake engine. gpl, yada yada.
void AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
	float		angle;
	float		sr, sp, sy, cr, cp, cy;
	
	angle = angles[YAW] * (M_PI*2 / 360);
	sy = sin(angle);
	cy = cos(angle);
	angle = angles[PITCH] * (M_PI*2 / 360);
	sp = sin(angle);
	cp = cos(angle);
	angle = angles[ROLL] * (M_PI*2 / 360);
	sr = sin(angle);
	cr = cos(angle);

	if (forward)
	{
		forward[0] = cp*cy;
		forward[1] = cp*sy;
		forward[2] = -sp;
	}
	if (right)
	{
		right[0] = (-1*sr*sp*cy+-1*cr*-sy);
		right[1] = (-1*sr*sp*sy+-1*cr*cy);
		right[2] = -1*sr*cp;
	}
	if (up)
	{
		up[0] = (cr*sp*cy+-sr*-sy);
		up[1] = (cr*sp*sy+-sr*cy);
		up[2] = cr*cp;
	}
}

void PF_makevectors (void)
{
	AngleVectors (G_VECTOR(OFS_PARM0), GLOBAL_VEC(v_forward), GLOBAL_VEC(v_right), GLOBAL_VEC(v_up));
}
absolutely sod all to do with bones or entitys. its a general utility function. rewrite it in QC if you really want (although it would be less efficient and thus pointless).
the most interesting part is that it should really use left instead of right, but that's an argument for another day.
as is the need to randomly negate pitch.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: How makevectors works, really?

Post by toneddu2000 »

Thanks a lot for the alternative code you posted about rotating a bone, Spike. Unfortunately it doesn't work for me (it doesn't rotate at all) but I'll not post any more skeletal stuff here because I know it's not the right section.

Code: Select all

most of the time, you'll want to avoid ever using skel_set_bone (the one exception here is for the root bone), and to exclusively use skel_mul_bone. called after skel_build combos and combined with makevectors you can express the rotation of a bone relative to its parent bone in a fairly convienient way, without needing to care about its current orientation.
Well, anytime I tried skel_mul_bone or it didn't work or it has multiplied spine rotation in a loop

And thanks a lot for the C Code of AngleVectors. Just a question: so, can I store for my X function in the X .qc file a value in GLOBAL_VEC(v_forward), GLOBAL_VEC(v_right), GLOBAL_VEC(v_up) and then store another value in another qc Y function in another Y qc file immeditely after? There would be no problem of which GLOBAL_VEC corresponds to which?

But...
void PF_makevectors (void)
?!? Should'nt it have 1 parameter(angle) instead of void?
Meadow Fun!! - my first commercial game, made with FTEQW game engine
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Re: How makevectors works, really?

Post by frag.machine »

All QuakeC builtins get their parameters (and usually return computation values, although this particular example is an exception) through the use of C macros that handle the QuakeC VM stack directly. Thus, there is no such thing as:

Code: Select all

 vec_t *PF_makevectors (vec_t *angle)
Instead, the builtin uses a macro (G_VECTOR) that retrieves the first parameter (OFS_PARM0) from the QuakeC VM stack and calls another C function (AngleVectors) passing as parameters pointers to v_forward, v_right and v_up C vars using the GLOBAL_VEC macro.

Using global C vars to return information from the C code directly to the QuakeC code is not a very common technique, maybe restricted to some handful cases because performance concerns. There are other examples, like traceline, that sets all the trace_* C/QuakeC vars. But most of the time builtins will return their result in the QuakeC VM stack so the value can be either returned to user-defined QuakeC vars or used as input to nested calls to other builtins/user defined functions.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: How makevectors works, really?

Post by toneddu2000 »

Thanks frag.machine, I'm start grasping the concept! :D
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Post Reply