CSQC skeletal animations played by anim name

Discuss CSQC related programming.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

CSQC skeletal animations played by anim name

Post by toneddu2000 »

Hi guys, I started tonight looking at the great work Christian Ice started and Nahuel finished about client side player skeletal animation on QuakeOne forums.
I've looked at the animation player part and I noticed this:

Code: Select all

self.frame = 505 + self.totframes_dw;
or

Code: Select all

self.frame = 471 + self.totframes_dw;
and so on.
Is it possible to play skeletal animations just using animation name?
I'm using IQM and in Blender I would make in Action Editor for example two animations: runforward and runbackward.
Is it possible to "play_animation(runforward, animation rate, loop yes|no)" in Darkplaces or FTE (at the moment the only 2 engines that use skeletal animation if I understood correctly), getting rid of the tremendous "frame" syntax?
Thanks in advance for lighting this dark path! :)
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: CSQC skeletal animations played by anim name

Post by Spike »

void(float modidx, string framename) frameforname = #276;
self.frame = frameforname(self.modelindex, "walk1") + index;

dedicated servers don't load models, frameforname is thus only valid in csqc. you should probably use some sort of enumeration for the actions in ssqc, and translate those to actual per-model frame numbers inside the csqc.

string(float modidx, float framenum) frametoname = #284; also exists for completeness. really its only useful for debugging.

once you have your entity in csqc, you can do something like:
enum
{
ACTION_WALK=0,
ACTION_STAND=1,
ACTION_TAUNT=2,
};
struct {
string firstframe;
float numframes;
float nextaction;
} actioninfo[] =
{
{"walk1", 6, ACTION_WALK},
{"stand1", 6, ACTION_STAND},
{"taunt1", 6, ACTION_STAND}
};
void() mypredraw =
{
self.lerpfrac -= frametime*framerate;
while (self.lerpfrac < 0)
{
self.frame2 = self.frame;
self.frame += 1;
float firstframe = frameforname(actioninfo[self.action].firstframe);
if (self.frame == frameforname(actioninfo[self.action].firstframe) + actioninfo[self.action].numframes)
{
self.action = actioninfo[self.action].nextaction;
self.frame = frameforname(actioninfo[self.action].firstframe);
}
}
};
then you can set self.action to ACTION_STAND to get it to loop a stand animation, or ACTION_WALK to get it to loop the walk animation... or ACTION_TAUNT to get it to taunt and then loop the stand animation instead.
obviously getting this stuff to work properly will require ensuring that the new action is only applied if the action has actually changed. which makes taunting fun.
you probably want to use an attack animation like this example uses taunt or something. I dunno, you'll probably want nailguns to loop instead.

Alternatively you can use framegroups (with frame1time+frame2time in combination with the frameduration builtin), and blend between animations separately from actual frames.

The skeletal objects extension allows you to animate separate parts of the model independantly (by copying the animation data into some separate skeletal state object). By blending multiple leg animations together with it, and playing them at different rates based upon their speed (self.frame1time += (distancemovedvector*v_forward)/animatedspeed) you should be able to achieve some sort of foot/ground syncronisation.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: CSQC skeletal animations played by anim name

Post by toneddu2000 »

Thanks Spike, I tried you first example (I know that you wrote that code in minutes and it shouldn't be considered as working code) but I couldn't make it work.
I didn't find in dp extensions action variable. Did you make it up? I defined it as .float action;
I have an error at this block of code:

Code: Select all

if (self.frame == frameforname(actioninfo[self.action].firstframe) + actioninfo[self.action].numframes)
      {
         self.action = actioninfo[self.action].nextaction;
         self.frame = frameforname(actioninfo[self.action].firstframe);
      }

Code: Select all

error: Opcode "<CFI>|C_FTOI" not valid for target
Alternatively you can use framegroups (with frame1time+frame2time in combination with the frameduration builtin), and blend between animations separately from actual frames.
I've done like you said and in fact it seems to work. What's the difference with the first method? Can I still use framgroups AND control different parts of a skeleton?
The skeletal objects extension allows you to animate separate parts of the model independantly (by copying the animation data into some separate skeletal state object).
?!? Really no idea of what do you mean with "some separate skeletal state object" :) Do you mean using skel_build and skel_create (by the way I use both but I can't figure out what's the difference between them)?

There's somewhere FINISHED examples about skeletal animations in csqc(except that one made by ChristianIce and Nahuel which used numeric frames animation and dpm models which is very outdated)?
Because if you can help me, as I did with ragdolls in the previous thread, I'll post every update I make!
Thanks again for your help
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: CSQC skeletal animations played by anim name

Post by Spike »

split the struct/array into 3 regular non-struct arrays I guess, if you're not going fte-specific.

.action is just some random custom field to hold which action the csqc entity should be doing at the time (to avoid randomly overwriting .frame, which would break .frame2 and generally result in issues).

the frameforname I mentioned returns either framegroups or ungrouped frames (as opposed to poses within a framegroup/sequence). the code example assumes that there are no framegroups in the model, which is a valid assumption for most of vanilla quake, torches/flames basically being the only exception.
framegroups are conceptually simpler, as the framerate etc can be fully determined by the model itself (assuming the animations are looping anyway, use frameduration to query a framegroup's duration for non-looping animations). However, framegroups generally have a problem with the end of their animation. typically your framegroup's last frame will need to blend into some other animation somehow. This typically results in either wasted frames at the end (which the qc will have to be aware of somehow) or weird freezes as it blends from one framegroup to another. there's no problem when they're looped animations, its really just a problem when they don't blend into the idle or whatever animation properly.

regarding skeletal objects, you can think of the engine as doing this as a wrapper around addentity(which is used internally by addentities):
if (self.skeletonobject) //|| isnotskeletal(self)
addentity(self);//use a custom skeleton
else
{//build a temporary skeleton
self.skeletonobject = skel_create(self.modelindex);
skel_build(self.skeletonobject, self, self.modelindex, 0, 0, 0, 1);
addentity(self);
skel_delete(self.skeletonobject);
}
the key thing to note is that skel_build copies the animation data from the specified modelindex according to the frame+frame2+lerpfrac etc fields in the given entity into the named skeletal object. the renderer will use the entity's .skeletonobject field to determine the animation data to use instead of doing an implicit skel_build.
skel_delete calls are defered until the end of the frame, allowing you to create+add+delete within a single frame without having to take so much care.
by explicitly calling skel_build yourself, you can have _direct_ control over the state of the bones. note the extra arguments (which allow you to constrict the set of bones to a range (0 means all), and with custom blend weights as an alternative to lerpfrac). you can also call the other various skel_ functions to tweak or query the skeleton. note that gettaginfo builtin will query the entity's attached skeletal object, if you want world positions.
you can rotate an individual bone (and its children) by calling skel_build(...);makevectors('0 1 0'*time);skel_mul_bone(self.skeletonobject, skel_find_bone(self.skeletonobject, "head"), '0 0 0', v_forward, v_right, v_up); This will give you a really disturbing 'The Exorcist' effect with the head spinning around. The skel_build first ensures that you get new data for this frame, technically you can skip it however if you do so you should use frametime instead of time as the effects are then cumulative. Matricies can be fun.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: CSQC skeletal animations played by anim name

Post by toneddu2000 »

split the struct/array into 3 regular non-struct arrays I guess, if you're not going fte-specific.
yeah, I'm gonna merge to fte tonight, i'm sick of dp incompatibilities(imo you should talk to LordHavoc and choose a common approach for extensions,because the csqc part is per se already difficult to grasp, but with those differences in sintax, in function arguments and in variables it becomes impossible! :P )
.action is just some random custom field to hold which action the csqc entity should be doing at the time (to avoid randomly overwriting .frame, which would break .frame2 and generally result in issues).
mmm.. understood. but when you wrote: self.action = ACTION_BLABLA, how can the engine know how to really play animation ACTION_BLABLA? That's freaking me out
the frameforname I mentioned returns either framegroups or ungrouped frames (as opposed to poses within a framegroup/sequence). the code example assumes that there are no framegroups in the model, which is a valid assumption for most of vanilla quake, torches/flames basically being the only exception.
Ok, got it.
framegroups are conceptually simpler, as the framerate etc can be fully determined by the model itself (assuming the animations are looping anyway, use frameduration to query a framegroup's duration for non-looping animations). However, framegroups generally have a problem with the end of their animation. typically your framegroup's last frame will need to blend into some other animation somehow. This typically results in either wasted frames at the end (which the qc will have to be aware of somehow) or weird freezes as it blends from one framegroup to another. there's no problem when they're looped animations, its really just a problem when they don't blend into the idle or whatever animation properly.
Ok, so framegroups are good for loop sequences, good to know it
the key thing to note is that skel_build copies the animation data from the specified modelindex according to the frame+frame2+lerpfrac etc fields in the given entity into the named skeletal object. the renderer will use the entity's .skeletonobject field to determine the animation data to use instead of doing an implicit skel_build.
skel_delete calls are defered until the end of the frame, allowing you to create+add+delete within a single frame without having to take so much care.
This last piece of code enlightned me. I didn't know that the engine creates every frame and deletes a temporary skeleton.

Thanks a lot for now Spike! I'll try tonight to create an animation cycle created only by code... let's see what happens! :D
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: CSQC skeletal animations played by anim name

Post by Spike »

toneddu2000 wrote:i'm sick of dp incompatibilities
this is an fteqcc issue with it not supporting legacy code to the same level that it supports fteqw-specific code.
its a bug in fteqcc's vanilla-opcode target support, rather than a dp issue (that said, it would be nice if dp supported extended opcodes too... so long as they're the same ones!).
toneddu2000 wrote:mmm.. understood. but when you wrote: self.action = ACTION_BLABLA, how can the engine know how to really play animation ACTION_BLABLA? That's freaking me out
it doesn't, that's what the lerpfrac etc code fragment was meant to do. put it in your predraw function and it's meant to update the frame+frame2+lerpfrac fields accordingly to match the current action when the current frame is scheduled to end. there can thus be a little latency, but it'll at least be smooth.
I think I forgot to add 1 to lerpfrac when the new .frame is set. actually, that entire code fragment was pretty much poo, it might still get the gist across though. you should also ensure that the new .frame is still within the current action and reset it to the start of that action if not. so yeah, needs fixing before it'll be useful.
note that the code goes from lerpfrac 1 to 0, blending from the old frame (in .frame2) to the new frame (in .frame).
when the lerpfrac drops below 0, it picks a new frame. typically this is just the next in the sequence, but if it reaches the end of the animation, then it switches to the next animation and resets or something. that was the idea anyway.
toneddu2000 wrote:This last piece of code enlightned me. I didn't know that the engine creates every frame and deletes a temporary skeleton.
actually, the engine uses stack or something for it, the delete part is thus more efficient. qc should probably just simply reuse the same skeleton, call skel_create once, and delete/release it at the same time it calls remove on the owning entity (this is especially true if you're using the ragdoll extension, as the ragdoll physics bodies are owned by the skeletal object rather than the entity). the code I gave was equivelent code, rather than exactly what the engine does, to give an understanding of exactly what the extension does, and how to use it. The final result is the same though.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: CSQC skeletal animations played by anim name

Post by toneddu2000 »

this is an fteqcc issue with it not supporting legacy code to the same level that it supports fteqw-specific code.
its a bug in fteqcc's vanilla-opcode target support, rather than a dp issue (that said, it would be nice if dp supported extended opcodes too... so long as they're the same ones!).
No, I was talking about creating a csqc game from scratch with fte (and fteextensions) and dp (and dpextensions).
DP

Code: Select all

void(float w, float h) CSQC_UpdateView;
FTE

Code: Select all

void (float vwidth, float vheight, float notmenu) CSQC_UpdateView;
Why are they take a different number and name of parameters? And, most important, where can I find a solid reference of fteextensions? Should I use pr_dumpplatform command to have a complete and updated extensions file?
I ask you this because I checked out 2 minutes ago the fteqw from source, I compiled the gl version and it works fine but console inputs are not taken in consideration! I type something but nothing appears. Same problem with the virtual keyboard. I use Ubuntu 14.04 LTS. Sorry for bothering you even about this but I always used Dp and I kinda new with FTE (I tried only the ragdolls code months ago and console was working at that time)
it doesn't, that's what the lerpfrac etc code fragment was meant to do. put it in your predraw function and it's meant to update the frame+frame2+lerpfrac fields accordingly to match the current action when the current frame is scheduled to end. there can thus be a little latency, but it'll at least be smooth
Ok, I'll try it
actually, the engine uses stack or something for it, the delete part is thus more efficient. qc should probably just simply reuse the same skeleton, call skel_create once, and delete/release it at the same time it calls remove on the owning entity (this is especially true if you're using the ragdoll extension, as the ragdoll physics bodies are owned by the skeletal object rather than the entity). the code I gave was equivelent code, rather than exactly what the engine does, to give an understanding of exactly what the extension does, and how to use it. The final result is the same though.
That's the part I like it more:"ragdoll physics bodies are owned by the skeletal object rather than the entity". I always made confusion about this: I always tought that the skeleton was a property of the entity, not an entity on its own. Thanks Spike, now I'm starting getting it!
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: CSQC skeletal animations played by anim name

Post by Spike »

regarding conflicting extensions, its like targetting the lowest common denominator, but first you have to figure out what exactly that is.
renamed builtins are not a problem with fte or dp. the engine really doesn't care what the builtin is called. rename it if you want. only the # and the number of arguments is important.
renamed arguments is not something the engine is aware of. its safe. prototypes can have different argument names from your actual function body too!
additional arguments in entry points (qc functions) may have undefined values in the engine which does not define them. the qcc may care, but the engine sure as heck does not.
additional arguments in builtins may generate some annoying error in DP. FTE will ignore additional arguments, while missing arguments will be undefined - as in vanilla... so pay attention to warnings about missing arguments!

pr_dumpplatform will give you a dump that is meant to exactly match the binary you use the command from.
http://triptohell.info/moodles/fteqcc/fteextensions.qc is a link to one that is generated from the latest nightly build.

regarding your console inputs issue, I tried to add support for XIM for proper unicode text entry, and it worked in debian, however, I later noticed that it didn't work in cygwin which should now also be fixed.
If you still have issues, you can use -noxim to use the old code.

actually, the skeletal object is not an entity, but rather both are 'objects' in the programming sense, so you're mostly correct. model indexes refer to a model object too, which contain their own framegroup objects, which in turn contain pose objects, if that helps... a skeletal object is just some generic pose object that can be used independantly of the model that it came from, and can be dynamically changed.
by 'independantly', you can actually build a skeletal object from one model and use it with a different one instead. this is useful when you have all-animation models and mesh-only models like with doom3's md5mesh/md5anim formats, or whatever.
but yeah, the skeletal objects are referenced by index, much like entities are (except with floats instead of an entity/hidden-int type).
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: CSQC skeletal animations played by anim name

Post by toneddu2000 »

I always ignored missing arguments warnings, from now on I'll be more careful!
the -noxim argument did the trick, thanks
And thanks for the clarification about skeletons, let's see what I can do!
Meadow Fun!! - my first commercial game, made with FTEQW game engine
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: CSQC skeletal animations played by anim name

Post by toneddu2000 »

I don't know if I'm only sleep deprived but I think it's a hard task! :) It's a little progress, but really little
I made this:
enemy.c

Code: Select all

void myenemyPredraw()
{
    if (!self.skeletonindex){
         self.skeletonindex = skel_create(self.modelindex);   
    }
    skel_build(self.skeletonindex, self, self.modelindex, 0, 0, 0, 0);
    self.frame =  frameforname(self.modelindex,"walk");
    self.frame2 = 0;
    self.frame3 = 0;
    self.frame4 = 0;
    self.frame1time = 0;
    self.frame2time = 0;
    self.frame3time = 0;
    self.frame4time = 0;
    self.lerpfrac = 0;
    self.lerpfrac3 = 0;
    self.lerpfrac4 = 0;
    skel_delete(self.skeletonindex); //important! Otherwise the model appears all messed-up!
}
void myenemySpawn()
{
    local entity e;
    e = spawn();
    setsize(e, '-128 128 -128', '128 128 128');
    setorigin(e, '0 0 -275');
    e.drawmask = MASK_NORMAL;
    precache_model ("models/pippo/pippo.iqm");
    setmodel(e, "models/pippo/pippo.iqm");
    e.predraw = myenemyPredraw; 
}
CS_Main.c

Code: Select all

void CSQC_Init()
{
    myenemySpawn();
}
Ok, now it's obvious that it worked because the model is now in the pose of the walk animation but it's fixed in that pose! It is not animating. I then tought that the predaw function wasn't updating the animation but I tried this:

Code: Select all

local float framewalk;
framewalk= time * 0.1;
self.lerpfrac = framewalk;
and it's visible some sort of "animation". Actually is just (if I understood correctly what lerpfrac is) the linear interpolation of frame 1 (walk) with frame 2 (idle), only upgrading time to time.
I was neither capable of using skel_mul_bone to do the 'Exorxist' spinning head. It simply stands fixed.
So, my question is: how can I update the frame sequence? Did I put the right code in the right place?
Thanks again for your precious help!
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: CSQC skeletal animations played by anim name

Post by Spike »

lerpfrac is how much of .frame2 to use. the more of frame2 that is used, the less of .frame that is used.

essentually:
decrement lerpfrac by frametime*animframerate (where animframerate is 10 or so, and frametime is the global that says how much time has passed).
if lerpfrac is now less than one, set .frame2 to .frame, increase lerpfrac to 1, and set .frame to the next frame that should be displayed (ie: increment it by 1, and reset to the first frame when it reaches the last frame or so).

your skeletonindex stuff isn't doing anything. it doesn't help that you set the 'addition' field as 0 instead of 1, but also your skel_delete leaves the entity trying to use an invalid skeleton next frame (due to the check that avoids creating a new one).

if you're using a framegroup, you need to set frame1time/frame2time to the time into their respective animation. self.frame1time+=frametime; for instance. for a running animation, you should increment it based upon the distance traveled instead of the time interval. which gets ugly when you slow down to low speeds. woo.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: CSQC skeletal animations played by anim name

Post by toneddu2000 »

Thanks Spike but I'm still fighting with simple animations now! :) I'm almost giving up...
This is where I've been so far... practically nowhere!

myenemyCSQC.c

Code: Select all

void myenemyPredraw()
{
	local float framewalk,framedur,skeltemp,numbones,bonenumber;
	framewalk= time * 0.1;
	if (self.skeletonindex){
		addentity(self);//use a custom skeleton	
	}
	else{
		self.modelindex = myEnemy.modelindex;
		self.skeletonindex = skel_create(self.modelindex);
		self.frame =  frameforname(self.modelindex,"duck");
		self.frame2 =  frameforname(self.modelindex,"taunt");
		self.frame3 =  frameforname(self.modelindex,"strafeleft");
		self.frame4 =  frameforname(self.modelindex,"run");
		print ("Model num:",ftos(self.modelindex),"\n");
		skeltemp = skel_build(self.skeletonindex, self, self.modelindex, 0, 0, 0, 0);
                numbones = skel_get_numbones(self.skeletonindex);
                print("-num bones: ",ftos(numbones)," -skeleton index: ",ftos(self.skeletonindex), " -skelbuild ok?: ",ftos(skeltemp),"\n"); 
                print("ModelIndex Num?: ",ftos(myEnemy.modelindex),"\n"); //IT RETURNS -1 : NO MODEL INDEX FOUND
		self.frame1time = time;
		self.frame2time = time;
		self.frame3time = time;
		self.frame4time = time;
		self.lerpfrac = 0;
		self.lerpfrac3 = 0;
		self.lerpfrac4 = 0;
		addentity(self);
		skel_delete(self.skeletonindex);
	}
}

void myenemy_spawn()
{
   	local entity e;
   	local vector myorg = pippo_placeholder2();
   	local float numbones,skeltemp,bonenamefind;
   	myEnemy = spawn();
   	setorigin(myEnemy, '0 0 -275');
   	myEnemy.drawmask = MASK_NORMAL;
   	precache_model ("models/erebus/erebus.iqm");
   	setmodel(myEnemy, "models/erebus/erebus.iqm");
   	myEnemy.predraw = myenemyPredraw;
}
CS_main

Code: Select all

void CSQC_Init()
{
	myenemy_spawn();
}
The model starts a little with the idle anim and then stops. But I noticed that it does the same thing on SSQC so maybe it's nothing related to the code I made.
I've also read many many times this article written by Spike (I presume) and I tried the examples in it but nothing.
The only strange thing is that modelindex returns always -1 so it's invalid. I thought that it could be that. Infact I'm using the code for CSQC Animations modified by Nahuel here and the first thing I've done it was to query this command:

Code: Select all

void UpdateCSPlayer(float newent)
{
	if (newent)
	{
		print("Player is updating\n");
		self.classname = "player";
		self.legsframe = 212;
		self.drawmask = MASK_NORMAL; // makes the entity visible
		setmodel (self, "models/max/max.dpm");//the model is rendered here, not in SSQC
		if (!self.skeletonindex)
		{
			self.skeletonindex = skel_create(self.modelindex);
			skel_player_update_begin (self.modelindex, 201, 0);
			print("PlayerModelIndex Num?: ",ftos(self.modelindex),"\n");//IT RETURNS 2. MODELINDEX VALID! 
		}

	}
}
And the modelindex is 2. So I thought that the model needed to be created in CSQC and then, via AddStat, it needed to port animations, origin and stuff to CSQC.
So I created this in SSQC (I admit, from now on I've no idea what I've written, it was my first time trying to make communicating SSQC + CSQC so please be patient! :) )
enemySSQC.c

Code: Select all

//there's an entity in the map named myenemy_spawn
void myenemy_spawn()
{
	myenemy = self;
	myenemy.classname = "myenemy";
	//setmodel(myenemy, "models/erebus/erebus.iqm"); //disabled. I want to see the model in CSQC
	setsize (myenemy, VEC_HULL2_MIN, VEC_HULL2_MAX);
	myenemy.solid = SOLID_SLIDEBOX;
	myenemy.movetype = MOVETYPE_STEP;
	//myenemy.SendEntity = SendEnemyToCSQC;//tried to send it from here with no luck
}
worldSSQC.c

Code: Select all

void Adding_New_stats_To_CSQC()
{
	AddStat (STAT_MDLPLAYER, 8, PersID);
	AddStat (STAT_MYENEMY, 8, myEnemyID);
}
void worldspawn()
{
     ... other stuff
	Adding_New_stats_To_CSQC();
    ... other stuff
}
clientSSQC.c

Code: Select all

float SendEnemyToCSQC()
{
//	msg_entity = self;
	WriteByte(MSG_ENTITY, ENT_MYENEMY);
	WriteByte(MSG_ENTITY, myenemy.frame);	//FIXME NEED COMPRESSION
	WriteByte(MSG_ENTITY, myenemy.myEnemyID);
	WriteByte(MSG_ENTITY, myenemy.v_angle_x*(256/360));
	WriteByte(MSG_ENTITY, myenemy.v_angle_y*(256/360));
	WriteCoord(MSG_ENTITY, myenemy.origin_x);
	WriteCoord(MSG_ENTITY, myenemy.origin_y);
	WriteCoord(MSG_ENTITY, myenemy.origin_z);
	WriteShort(MSG_ENTITY, myenemy.velocity_x);
	WriteShort(MSG_ENTITY, myenemy.velocity_y);
	WriteShort(MSG_ENTITY, myenemy.velocity_z);
	return TRUE;
}
void() PutClientInServer =
{
        ...other stuff
        local	entity spot;
	spot = find (world, classname, "info_player_start");
	if (!spot)
		error ("PutClientInServer: no info_player_start on level");
	self.classname = "player";
	self.SendEntity = SendPlayerToCSQC;//VITAL! Otherwise the camera will stand at a funny angle and player skel won't move!
	myenemy.SendEntity = SendEnemyToCSQC; //I don't think this does anything
	self.Version = self.Version + 1;
};
defsSSQC.c

Code: Select all

const float STAT_MYENEMY= 33;//toneddu2000
entity myenemy;//toneddu2000
.float myEnemyID;//toneddu2000
defsCSQC.c

Code: Select all

float ENT_MYENEMY = 2; //toneddu2000
.float myEnemyID;//toneddu2000
But of course, nothing. I completely aware that the code I wrote is junk. I mean, how can it be possible that CSQC knows that myenemy_spawn() function in enemySSQC.c holds the stats to be ported?
Sorry guys for the total mess post but this is exactly what my mind is right now! :lol:
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: CSQC skeletal animations played by anim name

Post by Spike »

modelindexes are positive if they were precached on the server, and negative if they were precached only on the client. it is actually possible for both a positive and negative value to map to the same actual model, any lookups in that case will result the positive/ssqc index, but this typically requires late precaches in ssqc to achieve.
a modelindex of -1 is thus perfectly valid.
its modelindex 0 which is the only invalid one. generally the engine will disconnect with an error message instead of it turning up (when its the engine's fault, anyway).
this mechanism means that csqc can use models without requiring the server to have already precached them, while modelindexes sent from the ssqc can still be used as-is in csqc.

I somewhat recently added a 'modelviewer' command, which should show the various frames in a model as the engine sees them. framegroups will autoanimate, but single frames will not. pgup/pgdn to change the framegroup/frame shown.
it also displays the bones and their positions at that point in the animation. which is probably not useful, but hey.

in your myenemyPredraw function, this line:
skel_delete(self.skeletonindex);
releases the skeleton (at the end of the frame). it does NOT set self.skeletonindex=0;
the next frame happens, the engine calls your predraw function, skeletonindex is not 0, but the skeleton it refers to has been released so the engine ignores it because its no longer valid. you have also not updated the .frame/frame1time etc fields. thus nothing animates. aka: you have a dangling pointer.
addentity makes a copy of the entity including its skeletonindex field, this combined with released skeletons remaining valid until the end of the frame mean you can just addentity(self);skel_delete(self.skeletonindex);self.skeletonindex=0; safely.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: CSQC skeletal animations played by anim name

Post by toneddu2000 »

modelindexes are positive if they were precached on the server, and negative if they were precached only on the client. it is actually possible for both a positive and negative value to map to the same actual model, any lookups in that case will result the positive/ssqc index, but this typically requires late precaches in ssqc to achieve.
a modelindex of -1 is thus perfectly valid.
its modelindex 0 which is the only invalid one. generally the engine will disconnect with an error message instead of it turning up (when its the engine's fault, anyway).this mechanism means that csqc can use models without requiring the server to have already precached them, while modelindexes sent from the ssqc can still be used as-is in csqc.
That's another thing I didn't know! Thanks a lot Spike!
I somewhat recently added a 'modelviewer' command, which should show the various frames in a model as the engine sees them. framegroups will autoanimate, but single frames will not. pgup/pgdn to change the framegroup/frame shown.
it also displays the bones and their positions at that point in the animation. which is probably not useful, but hey.
No, it's a lot useful, I use that feature in AnimSet Editor in UDK a lot!

Code: Select all

skel_delete(self.skeletonindex);
releases the skeleton (at the end of the frame). it does NOT set self.skeletonindex=0;
Ok,done. Thanks. Here the result:

CS_myenemy.c

Code: Select all

entity	myEnemy;

void myenemyPredraw2()
{
   float frame1duration,frame2duration,frame3duration;
   string framename1 = "idle",framename2 = "run",framename3 = "jump";
   
   //initial check
   if (!self.skeletonindex) {
      self.skeletonindex = skel_create(self.modelindex);
   }
   //durations of frames
   frame1duration = frameduration (self.modelindex,frameforname(self.modelindex,framename1));
   frame2duration = frameduration (self.modelindex,frameforname(self.modelindex,framename2));
   frame3duration = frameduration (self.modelindex,frameforname(self.modelindex,framename3));
   //animation stuff
   self.frame = frameforname(self.modelindex,framename2);
   //self.frame2 = self.frame;
   self.frame1time = frame1duration;
   //self.frame2time = frame2duration;
   //skeleton stuff
   skel_build(self.skeletonindex, self, self.modelindex, 0, skel_find_bone(self.skeletonindex, "master"), skel_find_bone(self.skeletonindex, "head"));
   //debug stuff
   print("Frame1 ",framename1," Duration: ",ftos(frame1duration),"\n");
   print("Frame2 ",framename2," Duration: ",ftos(frame2duration),"\n");
   print("Frame3 ",framename3," Duration: ",ftos(frame3duration),"\n");
   //closing stuff
   skel_delete(self.skeletonindex);
   self.skeletonindex=0;
}

void myenemy_spawn()
{
   	myEnemy = spawn();
   	setorigin(myEnemy, '0 0 -275');
   	myEnemy.drawmask = MASK_NORMAL;
   	precache_model ("models/erebus/erebus.iqm");
   	setmodel(myEnemy, "models/erebus/erebus.iqm");
   	myEnemy.predraw = myenemyPredraw2;
}
CS_main.c

Code: Select all

void CSQC_Init()
{
	myenemy_spawn();
}
Ok, I created a quite polished code that can be commented. Animations now play but don't loop. I erased .framegroup file in the model folder because it messed up only the anim code.
self.frame1time = frame1duration; is the key value. First I put value like "10.0" or "15.0" but when I printed the actual duration of a frame I discovered that:

Code: Select all

Frame1 idle Duration: 1.366667
Frame2 run Duration: 0.700000
Frame3 jump Duration: 0.700000
infact in Blender Idle animation is 41 frames (starting from frame 1) and run/jump are 21 frames based (I imagine that jump and run = 21frames/0.7 = 30fps but Idle?!)
The wierd thing is that if I use idle animation with idle duration the animation goes well and then stop. If I use run animation with idle animation same thing, but (and that's weird), if I use run animation with RUN duration it stands still!!
But it's its duration! I think I'm so close but I can't understand where I'm wrong! :)
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: CSQC skeletal animations played by anim name

Post by Spike »

.frame1time is one of those engine difference things (sigh...). each engine's behaviour is good for something at least. if you're using the dp-specific csqc defs, fte will enable its dp-compat mode (along with warning about it).
fte behaviour: .frame1time is the time into the animation (need to update it each frame, giving easy control over the speed that it animates).
dp behaviour: .frame1time is the timestamp (=time) of the start of the animation (set it when the ent is first spawned. if you want to change the speed that it animates at you'll need to do extra maths).
fte feature: .renderflags |= 64; will switch the behaviour from one to the other.

in all cases, the frame1time is specified in seconds and not frames (some model formats potentially animate framegroups at a non-linear rate). the framerate of the animation is thus generally not important to the gamecode.

setting the current time to the duration of the animation is completely pointless.
you can use this maths to animate within a framegroup to ignore the loop flag completely.
if (dp) self.frame1time = time-self.frame1time; else self.frame1time += frametime;
float duration = frameduration(self.modelindex, self.frame);
float t = self.frame1time / duration; self.frame1time = (t-floor(t))*duration;
if (dp) self.frame1time = time-self.frame1time;

and this to animate it while honouring the loop flame properly:
if (!dp) self.frame1time += frametime;

both of the above require this when you spawn the entity/change animation:
if (dp) self.frame1time = time; else self.frame1time = 0;
Post Reply