View previous topic :: View next topic |
Author |
Message |
JasonX
Joined: 21 Apr 2009 Posts: 89
|
Posted: Fri May 07, 2010 7:00 pm Post subject: QuakeC Animation "Tree" |
|
|
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 |
|
 |
Downsider

Joined: 16 Sep 2008 Posts: 478
|
Posted: Fri May 07, 2010 8:27 pm Post subject: |
|
|
Needs CSQC.
Or movetype_follow. |
|
Back to top |
|
 |
Spike
Joined: 05 Nov 2004 Posts: 944 Location: UK
|
Posted: Fri May 07, 2010 8:57 pm Post subject: |
|
|
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 |
|
 |
JasonX
Joined: 21 Apr 2009 Posts: 89
|
Posted: Sat May 08, 2010 1:10 am Post subject: |
|
|
I see, but... is there any tutorial for this specific implementation of CSQC? Or example, other than Nexuiz? |
|
Back to top |
|
 |
Swift
Joined: 26 Jan 2010 Posts: 44
|
Posted: Sat May 08, 2010 3:31 am Post subject: |
|
|
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 |
|
 |
Biodude

Joined: 27 Aug 2008 Posts: 83
|
Posted: Sat May 08, 2010 3:33 am Post subject: |
|
|
doesn't dark places support md3, that has seperate legs, head,etc?
maybe use that...? |
|
Back to top |
|
 |
Sajt
Joined: 16 Oct 2004 Posts: 1026
|
Posted: Sat May 08, 2010 4:28 am Post subject: |
|
|
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  _________________ 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 |
|
 |
Swift
Joined: 26 Jan 2010 Posts: 44
|
Posted: Sat May 08, 2010 5:18 am Post subject: |
|
|
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  |
I wouldn't consider ugly, just gnarly to model for. The easiest to implement though. |
|
Back to top |
|
 |
leileilol

Joined: 15 Oct 2004 Posts: 1321
|
Posted: Sat May 08, 2010 2:20 pm Post subject: |
|
|
That sucks because you'd need to handle more entities than usual. _________________
 |
|
Back to top |
|
 |
JasonX
Joined: 21 Apr 2009 Posts: 89
|
Posted: Sat May 08, 2010 3:26 pm Post subject: |
|
|
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!
 |
|
Back to top |
|
 |
GiffE
Joined: 08 Oct 2006 Posts: 141 Location: USA, CT
|
Posted: Sat May 08, 2010 8:40 pm Post subject: |
|
|
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 |
|
 |
Spike
Joined: 05 Nov 2004 Posts: 944 Location: UK
|
Posted: Sun May 09, 2010 2:50 pm Post subject: |
|
|
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 |
|
 |
ceriux

Joined: 06 Sep 2008 Posts: 968 Location: Florida, USA
|
Posted: Wed May 12, 2010 9:07 pm Post subject: |
|
|
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 |
|
 |
Spike
Joined: 05 Nov 2004 Posts: 944 Location: UK
|
Posted: Wed May 12, 2010 9:46 pm Post subject: |
|
|
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 |
|
 |
mh

Joined: 12 Jan 2008 Posts: 909
|
Posted: Wed May 12, 2010 10:51 pm Post subject: |
|
|
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 |
|
 |
|
|
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
|