How makevectors works, really?
Moderator: InsideQC Admins
8 posts
• Page 1 of 1
How makevectors works, really?
Hy guys, I'm reading a CSQC code that bend skeletal spine bone depending on mouse input:
Now, I'm just not understanding how it works.
From the manual, makevectors is described in this way:
It would have been simpler to me to grasp if it was something like that:
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)
- 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.
- 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
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));
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
- toneddu2000
- Posts: 1352
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: How makevectors works, really?
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.
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.
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Re: How makevectors works, really?
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:
what values replaces the first ones? I can't understand (just for the records, the code above DOES WORK but I cannot understand HOW!
) 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:
Thanks again
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!
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
- toneddu2000
- Posts: 1352
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: How makevectors works, really?
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.
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.
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: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Re: How makevectors works, really?
and because you'll probably still complain that I didn't explain makevectors...
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.
- 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.
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Re: How makevectors works, really?
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.
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?
- 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.
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
- toneddu2000
- Posts: 1352
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
Re: How makevectors works, really?
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:
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.
- 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)
-

frag.machine - Posts: 2090
- Joined: Sat Nov 25, 2006 1:49 pm
Re: How makevectors works, really?
Thanks frag.machine, I'm start grasping the concept! 
Meadow Fun!! - my first commercial game, made with FTEQW game engine
- toneddu2000
- Posts: 1352
- Joined: Tue Feb 24, 2009 4:39 pm
- Location: Italy
8 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest