Inside3D!
     

QuakeC Animation "Tree"
Goto page 1, 2  Next
 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming
View previous topic :: View next topic  
Author Message
JasonX



Joined: 21 Apr 2009
Posts: 92

PostPosted: Fri May 07, 2010 7:00 pm    Post subject: QuakeC Animation "Tree" Reply with quote

After finishing my player model, as well it's animations, i'm going to start the nightmare: coding the player animations with QuakeC. I'll probably use IQM or PSA/PSK, depends on the results i get while testing both.

I would like to know, however, how can i work with more complex animations. For example, Quake does not handle strafing animations, aiming animations or anything like that. The player model just slides while playing the walk animation, or the shooting animation. So, how can i blend my animations together, just like in UE3 AnimTree? I remember LordHavoc telling me about DP's features, but i haven't found any practical examples of this. Seems confusing... and hard.

Thanks in advance guys!


ps.: I'm using latest DP, and my own QC, based on Clean QC.
Back to top
View user's profile Send private message
Downsider



Joined: 16 Sep 2008
Posts: 478

PostPosted: Fri May 07, 2010 8:27 pm    Post subject: Reply with quote

Needs CSQC.

Or movetype_follow.
Back to top
View user's profile Send private message
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Fri May 07, 2010 8:57 pm    Post subject: Reply with quote

you need to use csqc to blend animations.

you can use setattachment for a ssqc solution, but only if you want each part of your player to be a separate model (like q3). works fine for weapons though.
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
JasonX



Joined: 21 Apr 2009
Posts: 92

PostPosted: Sat May 08, 2010 1:10 am    Post subject: Reply with quote

I see, but... is there any tutorial for this specific implementation of CSQC? Or example, other than Nexuiz?
Back to top
View user's profile Send private message
Swift



Joined: 26 Jan 2010
Posts: 44

PostPosted: Sat May 08, 2010 3:31 am    Post subject: Reply with quote

Unimplemented outside of Nexuiz, it seems.

Darkplaces really needs an UDK style DP-dev kit which showcases all of these kinds of Nexuiz features in a usable package - but with the awfulness of the Nexuiz theme taken out.
Back to top
View user's profile Send private message
Biodude



Joined: 27 Aug 2008
Posts: 83

PostPosted: Sat May 08, 2010 3:33 am    Post subject: Reply with quote

doesn't dark places support md3, that has seperate legs, head,etc?

maybe use that...?
Back to top
View user's profile Send private message
Sajt



Joined: 16 Oct 2004
Posts: 1026

PostPosted: Sat May 08, 2010 4:28 am    Post subject: Reply with quote

Biodude wrote:
doesn't dark places support md3, that has seperate legs, head,etc?

maybe use that...?


Yes, but that looks ugly and is a pain to model for Razz
_________________
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Back to top
View user's profile Send private message
Swift



Joined: 26 Jan 2010
Posts: 44

PostPosted: Sat May 08, 2010 5:18 am    Post subject: Reply with quote

Sajt wrote:
Biodude wrote:
doesn't dark places support md3, that has seperate legs, head,etc?

maybe use that...?


Yes, but that looks ugly and is a pain to model for Razz


I wouldn't consider ugly, just gnarly to model for. The easiest to implement though.
Back to top
View user's profile Send private message
leileilol



Joined: 15 Oct 2004
Posts: 1321

PostPosted: Sat May 08, 2010 2:20 pm    Post subject: Reply with quote

That sucks because you'd need to handle more entities than usual.
_________________
Back to top
View user's profile Send private message
JasonX



Joined: 21 Apr 2009
Posts: 92

PostPosted: Sat May 08, 2010 3:26 pm    Post subject: Reply with quote

Biodude wrote:
doesn't dark places support md3, that has seperate legs, head,etc?

maybe use that...?


Not only it's a pain to model separated body parts, but the MD3 format does not support +10,000 polys models very well. It's an outdated and obsolete format. Why use it when you have IQM and PSA/PSK available?

Swift wrote:
Unimplemented outside of Nexuiz, it seems.

Darkplaces really needs an UDK style DP-dev kit which showcases all of these kinds of Nexuiz features in a usable package - but with the awfulness of the Nexuiz theme taken out.


I got the Nexuiz QuakeC source code, but it's, pardon the words, a mess. I can't understand where's everything and i can't understand their animation code.

DP has the power to create amazing games, with amazing features, the lack of documentation out there is the thing that bothers me most. For example, this topic. It's possible? Yes. How? CSQC. And then no one bothers to explain exactly how, they just shout CSQC! CSQC! Look at Nexuiz!

Crying or Very sad
Back to top
View user's profile Send private message
GiffE



Joined: 08 Oct 2006
Posts: 141
Location: USA, CT

PostPosted: Sat May 08, 2010 8:40 pm    Post subject: Reply with quote

From dpextensions.qc:
Code:
//FTE_CSQC_SKELETONOBJECTS
//idea: Spike, LordHavoc
//darkplaces implementation: LordHavoc
//builtin definitions:
// all skeleton numbers are 1-based (0 being no skeleton)
// all bone numbers are 1-based (0 being invalid)
float(float modlindex) skel_create = #263; // create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex, as the skeleton uses the hierarchy from the model.
float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure
float(float skel) skel_get_numbones = #265; // returns how many bones exist in the created skeleton, 0 if skeleton does not exist
string(float skel, float bonenum) skel_get_bonename = #266; // returns name of bone (as a tempstring), "" if invalid bonenum (< 1 for example) or skeleton does not exist
float(float skel, float bonenum) skel_get_boneparent = #267; // returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
float(float skel, string tagname) skel_find_bone = #268; // get number of bone with specified name, 0 on failure, bonenum (1-based) on success, same as using gettagindex but takes modelindex instead of entity
vector(float skel, float bonenum) skel_get_bonerel = #269; // get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
vector(float skel, float bonenum) skel_get_boneabs = #270; // get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
void(float skel, float bonenum, vector org) skel_set_bone = #271; // set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
void(float skel, float bonenum, vector org) skel_mul_bone = #272; // transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones)
void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
void(float skel) skel_delete = #275; // deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
float(float modlindex, string framename) frameforname = #276; // finds number of a specified frame in the animation, returns -1 if no match found
float(float modlindex, float framenum) frameduration = #277; // returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0.
//fields:
.float skeletonindex; // active skeleton overriding standard animation on model
.float frame; // primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4)
.float frame2; // secondary framegroup animation (strength = lerpfrac)
.float frame3; // tertiary framegroup animation (strength = lerpfrac3)
.float frame4; // quaternary framegroup animation (strength = lerpfrac4)
.float lerpfrac; // strength of framegroup blend
.float lerpfrac3; // strength of framegroup blend
.float lerpfrac4; // strength of framegroup blend
.float frame1time; // start time of framegroup animation
.float frame2time; // start time of framegroup animation
.float frame3time; // start time of framegroup animation
.float frame4time; // start time of framegroup animation
//description:
//this extension provides a way to do complex skeletal animation on an entity.
//
//see also DP_SKELETONOBJECTS (this extension implemented on server as well as client)
//
//notes:
//each model contains its own skeleton, reusing a skeleton with incompatible models will yield garbage (or not render).
//each model contains its own animation data, you can use animations from other model files (for example saving out all character animations as separate model files).
//if an engine supports loading an animation-only file format such as .md5anim in FTEQW, it can be used to animate any model with a compatible skeleton.
//proper use of this extension may require understanding matrix transforms (v_forward, v_right, v_up, origin), and you must keep in mind that v_right is negative for this purpose.
//
//features include:
//multiple animations blended together.
//animating a model with animations from another model with a compatible skeleton.
//restricting animation blends to certain bones of a model - for example independent animation of legs, torso, head.
//custom bone controllers - for example making eyes track a target location.
//
//
//
//example code follows...
//
//this helper function lets you identify (by parentage) what group a bone
//belongs to - for example "torso", "leftarm", would return 1 ("torso") for
//all children of the bone named "torso", unless they are children of
//"leftarm" (which is a child of "torso") which would return 2 instead...
float(float skel, float bonenum, string g1, string g2, string g3, string g4, string g5, string g6) example_skel_findbonegroup =
{
   local string bonename;
   while (bonenum >= 0)
   {
      bonename = skel_get_bonename(skel, bonenum);
      if (bonename == g1) return 1;
      if (bonename == g2) return 2;
      if (bonename == g3) return 3;
      if (bonename == g4) return 4;
      if (bonename == g5) return 5;
      if (bonename == g6) return 6;
      bonenum = skel_get_boneparent(skel, bonenum);
   }
   return 0;
};
// create a skeletonindex for our player using current modelindex
void() example_skel_player_setup =
{
   self.skeletonindex = skel_create(self.modelindex);
};
// setup bones of skeleton based on an animation
// note: animmodelindex can be a different model than self.modelindex
void(float animmodelindex, float framegroup, float framegroupstarttime) example_skel_player_update_begin =
{
   // start with our standard animation
   self.frame = framegroup;
   self.frame2 = 0;
   self.frame3 = 0;
   self.frame4 = 0;
   self.frame1time = framegroupstarttime;
   self.frame2time = 0;
   self.frame3time = 0;
   self.frame4time = 0;
   self.lerpfrac = 0;
   self.lerpfrac3 = 0;
   self.lerpfrac4 = 0;
   skel_build(self.skeletonindex, self, animmodelindex, 0, 0, 100000);
};
// apply a different framegroup animation to bones with a specified parent
void(float animmodelindex, float framegroup, float framegroupstarttime, float blendalpha, string groupbonename, string excludegroupname1, string excludegroupname2) example_skel_player_update_applyoverride =
{
   local float bonenum;
   local float numbones;
   self.frame = framegroup;
   self.frame2 = 0;
   self.frame3 = 0;
   self.frame4 = 0;
   self.frame1time = framegroupstarttime;
   self.frame2time = 0;
   self.frame3time = 0;
   self.frame4time = 0;
   self.lerpfrac = 0;
   self.lerpfrac3 = 0;
   self.lerpfrac4 = 0;
   bonenum = 0;
   numbones = skel_get_numbones(self.skeletonindex);
   while (bonenum < numbones)
   {
      if (example_skel_findbonegroup(self.skeletonindex, bonenum, groupbonename, excludegroupname1, excludegroupname2, "", "", "") == 1)
         skel_build(self.skeletonindex, self, animmodelindex, 1 - blendalpha, bonenum, bonenum + 1);
      bonenum = bonenum + 1;
   }
};
// make eyes point at a target location, be sure v_forward, v_right, v_up are set correctly before calling
void(vector eyetarget, string bonename) example_skel_player_update_eyetarget =
{
   local float bonenum;
   local vector ang;
   local vector oldforward, oldright, oldup;
   local vector relforward, relright, relup, relorg;
   local vector boneforward, boneright, boneup, boneorg;
   local vector parentforward, parentright, parentup, parentorg;
   local vector u, v;
   local vector modeleyetarget;
   bonenum = skel_find_bone(self.skeletonindex, bonename) - 1;
   if (bonenum < 0)
      return;
   oldforward = v_forward;
   oldright = v_right;
   oldup = v_up;
   v = eyetarget - self.origin;
   modeleyetarget_x =   v * v_forward;
   modeleyetarget_y = 0-v * v_right;
   modeleyetarget_z =   v * v_up;
   // this is an eyeball, make it point at the target location
   // first get all the data we can...
   relorg = skel_get_bonerel(self.skeletonindex, bonenum);
   relforward = v_forward;
   relright = v_right;
   relup = v_up;
   boneorg = skel_get_boneabs(self.skeletonindex, bonenum);
   boneforward = v_forward;
   boneright = v_right;
   boneup = v_up;
   parentorg = skel_get_boneabs(self.skeletonindex, skel_get_boneparent(self.skeletonindex, bonenum));
   parentforward = v_forward;
   parentright = v_right;
   parentup = v_up;
   // get the vector from the eyeball to the target
   u = modeleyetarget - boneorg;
   // now transform it inversely by the parent matrix to produce new rel vectors
   v_x = u * parentforward;
   v_y = u * parentright;
   v_z = u * parentup;
   ang = vectoangles2(v, relup);
   ang_x = 0 - ang_x;
   makevectors(ang);
   // set the relative bone matrix
   skel_set_bone(self.skeletonindex, bonenum, relorg);
   // restore caller's v_ vectors
   v_forward = oldforward;
   v_right = oldright;
   v_up = oldup;
};
// delete skeleton when we're done with it
// note: skeleton remains valid until next frame when it is really deleted
void() example_skel_player_delete =
{
   skel_delete(self.skeletonindex);
   self.skeletonindex = 0;
};
//
// END OF EXAMPLES FOR FTE_CSQC_SKELETONOBJECTS
//


I haven't figure it out yet... But I'd definitely like to see this up as a tutorial explaining it more clear...
_________________
http://www.giffe-bin.net/
Back to top
View user's profile Send private message Visit poster's website
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Sun May 09, 2010 2:50 pm    Post subject: Reply with quote

skeletal models are a heirachy of bones, a tree.
generally all the bones of one limb are grouped into the linear order of the bones.
thus you end up with a list of bones containing groups. legs, arms, weapon, head.
each animation is a set of rotations based upon the parent bone's rotation. to set a specific animation, you just grab the values for each bone and multiply it out in order to find out where the bones are, then each vertex is expressed using some sort of maths.
to blend two animations, you can just mix the two bones, then when its multiplied out, the animation results in the average.
well, because its a tree, with each bone expressed relative to its parent, you can blend two animations on subsections of the skeleton.
or override a single bone to pivot or pitch a model at the waist.

FTE_CSQC_SKELETONOBJECTS allows you attach an object to an entity that overrides all frame fields and interpolation.
you can change the bone values in the skeletal object, blending in animations as you wish, or overriding specific bones programatically.

because you can 'build' (ie: copy from the animation data) using subsets of bones, you can basically do:

if (!self.skeletonindex)
self.skeletonindex = skel_create(self.modelindex);
self.frame = $runanimationsequence;
self.frame1time = durationofrun;
skel_build(self.skeletonindex, self, self.modelindex, 0, 0, skel_find_bone(self.skeletonindex, "waist"));

self.frame = $shootingsequence;
self.frame1time = time - shottime;
skel_build(self.skeletonindex, self, self.modelindex, 0, skel_find_bone(self.skeletonindex, "waist"), skel_get_numbones(self.modelindex));

And that'll show a running animation on the bones before "waist", and a shooting animation on bones after "waist".

You can also explicitly change the waist bone's pitch by:
makevectors(angletofacerelativetoentity);
skel_mul_bone(self.skeletonindex, skel_find_bone(self.skeletonindex, "waist"), '0 0 0');

(don't forget to call skel_delete when your skeleton is no longer needed)
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
ceriux



Joined: 06 Sep 2008
Posts: 969
Location: Florida, USA

PostPosted: Wed May 12, 2010 9:07 pm    Post subject: Reply with quote

i have a question since we're tal.king about animations here. wouldnt it be better to code the attachment support based on model groups instead of the bone number like half-life uses? it would make modeling a lot easier.
_________________
QuakeDB - Quake ModDB Group
Back to top
View user's profile Send private message Yahoo Messenger
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Wed May 12, 2010 9:46 pm    Post subject: Reply with quote

huh?
model groups? you mean like multiple models, instead of a single one, ala q3? how's that easier?
or do you mean multiple surfaces within a model? doesn't work. such multi-surface models have a single skeleton still. just different textures on it.

note that the extension mentioned allows you to share animations from one model to another, assuming the bones are compatible :)
this is mostly for models that contain just hats or varying heads, or whatever, that you want added as part of the same 'model'.

if your bone numbers are ordered based on the 'submodel' ordering inside the model, then you can specify ranges of start-end.
you can also still use attachments, but make sure your ents are added in the correct order.
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
mh



Joined: 12 Jan 2008
Posts: 910

PostPosted: Wed May 12, 2010 10:51 pm    Post subject: Reply with quote

JasonX wrote:
the MD3 format does not support +10,000 polys models very well

In what sense? Have you tried changing the value of r_batchmode in DP? By default I believe it uses triangle strips (possibly with degenerate triangles) which is an utter waste of time on any reasonably modern (within the last 10 or so years) hardware; indexed traingle lists are now the optimal render path and have been for quite a long time. Change r_batchmode to 2 and see how things go.

DP however has a fatal flaw in that it uses 32-bit indexes (this FF also exists in Q3A); these are not supported on all hardware so your OpenGL implementation might be going through software emulation just so that it can pass conformance testing. LH really should switch to 16-bit indexes here...

But anyway, I get the feeling that this problem may be more a function of the renderer than it is of the format. And who has models with > 10,000 tris anyway?
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group