FTEQW - check active player view

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

FTEQW - check active player view

Post by toneddu2000 »

Hi guys! I would like to display full skeletal player if view is not in first person and displaying hands model(for now just display a null model) if view is the active one (I'm looking through player's view camera). But I cannot understand how to display player model, seen from an external view (for example when a player sees another player)

I only be able to hemi-accomplished this on predraw function but I think it's not the right place to put this, right?

Code: Select all

// in PredrawPlayer function
if (self.entnum == player_localentnum){
	playeViewOrigin = self.origin;	//without this, camera is not connected to player entity anymore!!
	setmodel(self,"");//no whole person model
}
Should it be placed in CSQC_Update_View(), right?
then I added, in StartupPlayer() function

Code: Select all

void Startup_Player(float newent)
{
	if (newent)
	{
		if (self.entnum == player_localentnum){
			playerActive = self;
			setmodel(self,"");//display no model
		}
		else{
			setmodel(self,PLAYER_MODEL);//display whole model
		}
		self.classname = "player";
		self.drawmask = MASK_NORMAL; // makes the entity visible
	}
}
In CSQC_Ent_Update I called Startup_Player

Code: Select all

void CSQC_Ent_Update (float isnew)
{
	local float i = readbyte();
	if (i == ENT_PLAYER){
		//read here all send values from client.c in ssqc
		//movements
		self.origin_x = readcoord();
		self.origin_y = readcoord();
		self.origin_z = readcoord();	
		self.angles_x = readcoord();
		self.angles_y = readcoord();
		self.angles_z = readcoord();
		self.velocity_x = readcoord();
		self.velocity_y = readcoord();
		self.velocity_z = readcoord();
		//additionals
		self.modelindex = readbyte();
		self.skeletonindex = readbyte();
		//functions
		self.drawmask = MASK_NORMAL;
		self.predraw = Predraw_Player;
		Startup_Player(isnew);
	}
}
My CSQC_Update_View

Code: Select all

void CSQC_UpdateView(float vwidth, float vheight, float notmenu)
{
	clearscene();
	if (playerActive){
		setviewprop(VF_ORIGIN, playeViewOrigin + PLAYER_HEIGHT);//player position frame by frame
		setviewprop(VF_ANGLES, view_angles); //player orientation frame by frame
		makevectors(view_angles);
		SetListener(playeViewOrigin, v_forward, v_right, v_up);
		setviewprop(VF_DRAWCROSSHAIR,1);
	}
	else{
		addentities(MASK_VIEWMODEL);
	}
	addentities(MASK_NORMAL|MASK_ENGINE); //withoutMASK_ENGINE, map entities models won't be displayed!
	renderscene();
}
But If I remove the code in PredrawPlayer

Code: Select all

if (self.entnum == player_localentnum){
	playeViewOrigin = self.origin;	//without this, when another client connects, player active view becomes crazy.
	setmodel(self,"");//no whole person model
}
Camerais not connected to player entity anymore (Maybe I should put this check in CSQC_UpdateView)and whole player model is displayed even in fps.
Where am I wrong?

Thanks a bunch in advance guys! :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: FTEQW - check active player view

Post by Spike »

the player entity should be added to the scene with renderflags&RF_EXTERNALMODEL;
this allows the entity to cast shadows and appear in mirrors without blocking vision.
thus you need this somewhere (inside an isnew check is fine):
if (self.entnum == local_playerentnum)
self.renderflags = RF_EXTERNALMODEL;
else
self.renderflags = 0;

if you use SendEntity to send the player entity to the client, you will be using a network protocol that the client is not able to directly interpret (the client sees only entity numbers - it is only the csqc that can correctly interpret the meaning of the actual data contained inside).
this means that the client is no longer aware of the actual origin of the player.
this means that you must use setviewprop(VF_ORIGIN, foo); in order to tell the renderer the correct position to use (remember to adjust by the view height stat).
you will likely also wish to implement either interpolation or prediction, as the engine cannot do this automatically. you may also wish to implement view bob.

using deltalisten on player entities does not obscure the player entity from the client, and can simplify all of this. however, you are more limited in the available set of fields.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: FTEQW - check active player view

Post by toneddu2000 »

Thanks Spike, it works, but ONLY if I add in PredrawPlayer function

Code: Select all

if (self.entnum == player_localentnum){
	playeViewOrigin = self.origin;
}
Without this, camera is not connected to player entity anymore. Should I use this code in predraw? Shouldn't I use predraw just to iteract player movements and skeleton frame by frame? Does camera position/angles belong ONLY to CSQC_UpdtateView?
Spike wrote: if you use SendEntity to send the player entity to the client, you will be using a network protocol that the client is not able to directly interpret (the client sees only entity numbers - it is only the csqc that can correctly interpret the meaning of the actual data contained inside).
this means that the client is no longer aware of the actual origin of the player.
This is what I wrote in ssqc that sends data to csqc

Code: Select all

float SS2CS_Send_Player (entity playerent, float changedflags)
{
	//entity
	WriteByte(MSG_ENTITY, ENT_PLAYER);
	//movements
	WriteCoord(MSG_ENTITY, self.origin_x);
	WriteCoord(MSG_ENTITY, self.origin_y);
	WriteCoord(MSG_ENTITY, self.origin_z);
	WriteCoord(MSG_ENTITY, self.angles_x);
	WriteCoord(MSG_ENTITY, self.angles_y);
	WriteCoord(MSG_ENTITY, self.angles_z);
	WriteCoord(MSG_ENTITY, self.velocity_x);
	WriteCoord(MSG_ENTITY, self.velocity_y);
	WriteCoord(MSG_ENTITY, self.velocity_z);
	//additional
	WriteByte(MSG_ENTITY, self.modelindex);//important!Otherwise skeleton in csqc is not set!
	WriteByte(MSG_ENTITY, self.skeletonindex);//important!Otherwise skeleton in csqc is not set!
	return TRUE;
}

void PutClientInServer()
{
	local				entity	playerspwn;
	
	playerspwn = Entity_FindStartPoint();
	self.origin = playerspwn.origin;
	self.health = PLAYER_MAXHEALTH;
	self.movetype = MOVETYPE_WALK;
	self.solid = SOLID_SLIDEBOX;
	self.takedamage = DAMAGE_AIM;
	precache_model(PLAYER_MODEL);
	setmodel(self, PLAYER_MODEL);
	setsize (self, PLAYER_BBOX_MIN, PLAYER_BBOX_MAX);//without a correct HULL box, monsters can't fire at you!!
	self.fixangle = TRUE;
	self.flags = FL_CLIENT;
	self.classname = "player";
	self.weapon = 0;
	self.view_ofs = '0 0 22';
	self.SendEntity = SS2CS_Send_Player;
}
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: FTEQW - check active player view

Post by Spike »

Should I use this code in predraw? Shouldn't I use predraw just to iteract player movements and skeleton frame by frame? Does camera position/angles belong ONLY to CSQC_UpdtateView?
I think you believe that predraw functions are somehow special. they're not.

the addentities builtin can be summed up as:

Code: Select all

void(float mask) addentities =
{
    entity oself = self;
    if (mask&1)
        addpacketentities();
    if (mask&2)
        addviewmodel();
    for (self = nextent(world); self; self = nextent(self))
    {
        if (!(self.drawmask & mask))
            continue;
        if (self.predraw)
            if (self.predraw())
                continue;
        addentity(self);
    }
    self = oself;
};
There's two private 'builtins' in there which are internal to the addentities builtin. The rest is trivially re-implemented in qc.
('addviewmodel' could be implemented in csqc, but 'addpacketentities' depends upon state not visible to csqc.)
As you can see, the predraw function is called as part of addentities, and is thus also called as a child of CSQC_UpdateView.

In many situations, you are correct in that updating the player's view origin inside a predraw function is bad. Yes, it imposes an order on things. Ideally you would track the entity that refers to the player entity separately and update the view origin *before* calling addentities and thus before the predraw functions so that they all see the final view position.

In the real world, the player is never removed, is guarenteed to be present in all enity updates, and thus a (non-deltalisten) player entity is guarenteed to be one of the lowest-numbered entities in the csqc. Assuming the predraw function of other players does not depend upon the view origin, and that you have not removed any of the entities that you (may have) spawned within CSQC_Init, then the view origin set in the local player's predraw function will be correct for any other non-player entity.
And even if its not correct, then it'll only lag by one frame (and consistantly so), which is often not a problem.

Either way, any network parsing has already happened, and thus you can use something like this:

Code: Select all

entity theplayer = findfloat(world, entnum, player_localentnum);
if (theplayer) //can be world while still loading
    playeViewOrigin = theplayer.origin;
But you'll probably want to interpolate that origin using the same logic as inside your player's predraw. Alternatively, predict the player's origin before calling addentities and have your predraw skip interpolation for the local player.
You'll likely also want to update the player entity's angles to match the current view angles too, at least if you're going to use mirrors or shadows or whatever.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: FTEQW - check active player view

Post by toneddu2000 »

Spike wrote:I think you believe that predraw functions are somehow special. they're not.
...
As you can see, the predraw function is called as part of addentities, and is thus also called as a child of CSQC_UpdateView.
I noticed that self.predraw = MyPredrawFunction is the only method to use complex skeletal animation. Running skeletal animations from CSQC_UpdateView doesn't work or, worse, it makes the client crashes. This makes .predraw functions a little special to me! :D
Spike wrote:Ideally you would track the entity that refers to the player entity separately and update the view origin *before* calling addentities and thus before the predraw functions so that they all see the final view position.
I tried to do it but without success
Spike wrote:Either way, any network parsing has already happened, and thus you can use something like this:
entity theplayer = findfloat(world, entnum, player_localentnum);
if (theplayer) //can be world while still loading
playeViewOrigin = theplayer.origin;
Thanks for the code, Spike! But is that similar to something like this

Code: Select all

playeViewOrigin = getentity(player_localentnum, GE_ORIGIN);//needed to catch player origin frame by frame
	setviewprop(VF_ORIGIN, playeViewOrigin + PLAYER_HEIGHT);//player position frame by frame
in CSQC_UpdateView?
Spike wrote:using deltalisten on player entities does not obscure the player entity from the client, and can simplify all of this. however, you are more limited in the available set of fields.
I didn't quite grasp the concept of deltalisten. The only code I found where deltalisten is mentioned is the great Mobster Massacre from Shpuld/(very well code to learn CSQC).

I then made a completely overhaul to my code to use deltalisten. This is what I did.
SSQC
1) I removed in ssqc every code related to csqc send data.
2) ssqc PutClientInServer is now

Code: Select all

void PutClientInServer()
{
	local				entity	playerspwn;
	
	playerspwn = 		Entity_FindStartPoint();
	self.origin = playerspwn.origin;
	self.health = PLAYER_MAXHEALTH;
	self.movetype = MOVETYPE_WALK;
	self.solid = SOLID_SLIDEBOX;
	self.takedamage = DAMAGE_AIM;
	precache_model(PLAYER_MODEL);
	setmodel(self, PLAYER_MODEL);
	setsize (self, PLAYER_BBOX_MIN, PLAYER_BBOX_MAX);//without a correct HULL box, monsters can't fire at you!!
	self.fixangle = TRUE;
	self.flags = FL_CLIENT;
	self.classname = "player";
	self.weapon = 0;
	self.view_ofs = '0 0 22';
}
2)No more SendFlags in ssqc.
CSQC
1) CSQC_EntUpdate() is useless for now so I removed
2) created a deltalisten function named playerlisten

Code: Select all

float(float isnew) playerlisten =
{
	float runvelocity = vlen(self.velocity);
	if(isnew){
		self.frame2 = 0;
		if (self.entnum == player_localentnum){
			self.renderflags = RF_EXTERNALMODEL;
		}
		else{
			self.renderflags = 0;
		}
	}
	self.frame1time += frametime;
	if(runvelocity > 0){
		self.frame = 1;
		self.lerpfrac = 1 - (runvelocity/250);
		if(self.lerpfrac > 1){
			self.lerpfrac = 1;
		}
		else if(self.lerpfrac < 0){
			self.lerpfrac = 0;
		}
	}
	else{
		self.lerpfrac = 1;
		self.frame = 0;
	}
	
	return 1;
}
3) CSQC_UpdateView is

Code: Select all

void CSQC_UpdateView(float vwidth, float vheight, float notmenu)
{
	clearscene();
	//deltalisten
	deltalisten(PLAYER_MODEL, playerlisten, 0);
	//view properties
	playeViewOrigin = getentity(player_localentnum, GE_ORIGIN);//needed to catch player origin frame by frame
	setviewprop(VF_ORIGIN, playeViewOrigin + PLAYER_HEIGHT);//player position frame by frame
	setviewprop(VF_ANGLES, view_angles); //player orientation frame by frame
	makevectors(view_angles);
	setviewprop(VF_DRAWCROSSHAIR,1);
	addentities(MASK_NORMAL|MASK_ENGINE); //withoutMASK_ENGINE, map entities models won't be displayed!
	renderscene();
}
The camera works well now, even without that check I put earlier in predraw. But if I add skeleton creation via predraw in deltalisten function, like this

Code: Select all

float(float isnew) playerlisten =
{
	float runvelocity = vlen(self.velocity);
	if(isnew){
		self.frame2 = 0;
		self.predraw = Predraw_Player2;
Player uses skeleton but animations are not not used anymore.

But, what is the correct way to handle player camera in ssqc+csqc? The SendData + CSQC_EntUpdate or this latter method (only deltalisten in CSQC_UpdateView)? Are both correct?

Thanks A LOT Spike for all these explanations. They're better than any other documentation! :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: FTEQW - check active player view

Post by Spike »

ssqc's skeleton indexes working in csqc is a ssqc-debugging 'feature' (ie: intentionally not checking ownership), and will not work when networked. you shouldn't network those numbers, unless in strictly single-player debugging scenarios, as I make no guarentee that they'll always have a shared namespace for skeletal objects.
predraws happen between clearscene and renderscene. they are not special, just a convienience.
remember that if your entity has a valid .skeletonindex value, then the addentity builtin will pass a reference to that skeletal object to the renderer, and this will COMPLETELY REPLACE any skeletal state generated from the .frame etc fields.
if you wish to use skeletal objects with model animations, you will need to use skel_build to read the current pose data implied by .frame etc into the skeletal object beforehand. Note that skel_build allows you to scale the data in order to interpolate multiple frames together, it allows allows you to specify bone ranges for separate torso+legs animations.

the getentity builtin peeks at the client's most-recent delta-entity state for the specified entity. this information is exposed only via getentity or deltalisten (feather vs hammer) - one returns a single field (or matrix), the other makes an entire csqc copy of the networked ssqc entity with the correct post-interpolation origins.
remember that entities are abstract things. the csqc and ssqc have completely independant entities that are tied only via the csqc's .entnum field, and some sort of ssqc->csqc table inside the engine which is only used when parsing entities (so that the csqc has the same 'self' value for each individual ssqc entnum).

deltalisten is good because it just queries+interpolates the entity, and prevents the client from adding it automatically as part of addentities - unless it has a drawmask anyway. In doing so, it allows you to change that data freely. however, its quite a heavy operation (hence the 'hammer' remark earlier)
getentity is good for its simplicity, but bad because you cannot do anything to modify the data other than using some sort of invisible modelindex (which makes using modelindex to distinguish between entity types really quite messy). if you know the entity index in advance (stats?) then it can be useful, but if you don't, you have to resort to polling. its lack of storage necessitates using some sort of array or loop(including find/findfloat) to associate any state to the entity (like previous position in order to animate things correctly). while it copies only the data that it needs, calling it 5000 times is not a light (knocking down a wall with a feather is not a good idea).

alternatively you have pure-qc entities that are networked using the MSG_ENTITY stuff. You can send exactly the stuff you want this way without the engine making any interpretation of the data you sent (like using float origins instead of limiting the game to +/-4096 qu). as such, they have less special cases, and are thus easier to explain, but also have less automatic behaviour being handled behind the scenes, which can result in much more work for you, as the csqc may be expected to reimplement half of the engine running within the (cs)qcvm.

there is no relationship between the player's entity and the camera. in fact, you do not even need a camera, or you may have multiple cameras. renderscene can be called multiple times for multiple cameras - note that you will likely wish to use VF_VIEWENTITY to hack the renderflags so that any RF_EXTERNALMODEL flags get updated without having to go through all the different predraw functions over and over again.
You can set VF_ORIGIN to some fixed position for an intermission camera. you can make it follow behind the player entity if you want 3rd person. you can make it follow a spline if you want something weird, or attach it to the nearest camera spot if you want something spooky like alone in the dark. If you're ssqc-centric, you can use stats to hold the new view position.

Using deltalisten allows you to reuse more of the legacy engine code than SendEntity does, and is thus typically less qc code, but it will have more expectations about engine behaviours.
ultimately, there is no single correct way.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: FTEQW - check active player view

Post by toneddu2000 »

ssqc's skeleton indexes working in csqc is a ssqc-debugging 'feature' (ie: intentionally not checking ownership), and will not work when networked. you shouldn't network those numbers, unless in strictly single-player debugging scenarios, as I make no guarentee that they'll always have a shared namespace for skeletal objects.
The problem is that, if I write down .skeletonindex value for my entity
for example

Code: Select all

void Startup_Player(float newent)
{
	if (newent)
	{
		self.skeletonindex = 12;
Skeletal animation will work but skeleton functions like skel_build,skel_set_bone and such will not! That's why the only method I found is to send from ssqc the .skeletonindex value!
remember that if your entity has a valid .skeletonindex value, then the addentity builtin will pass a reference to that skeletal object to the renderer, and this will COMPLETELY REPLACE any skeletal state generated from the .frame etc fields.
if you wish to use skeletal objects with model animations, you will need to use skel_build to read the current pose data implied by .frame etc into the skeletal object beforehand. Note that skel_build allows you to scale the data in order to interpolate multiple frames together, it allows allows you to specify bone ranges for separate torso+legs animations.
This approach works only if I sent .skeletonindex from ssqc. At least for all the tests I tried this is the only method it worked
the getentity builtin peeks at the client's most-recent delta-entity state for the specified entity. this information is exposed only via getentity or deltalisten (feather vs hammer) - one returns a single field (or matrix), the other makes an entire csqc copy of the networked ssqc entity with the correct post-interpolation origins.
remember that entities are abstract things. the csqc and ssqc have completely independant entities that are tied only via the csqc's .entnum field, and some sort of ssqc->csqc table inside the engine which is only used when parsing entities (so that the csqc has the same 'self' value for each individual ssqc entnum).
That's pretty clear, thanks a lot for this detailed description
there is no relationship between the player's entity and the camera. in fact, you do not even need a camera, or you may have multiple cameras. renderscene can be called multiple times for multiple cameras - note that you will likely wish to use VF_VIEWENTITY to hack the renderflags so that any RF_EXTERNALMODEL flags get updated without having to go through all the different predraw functions over and over again.
wow, I didn't know it was possible
You can set VF_ORIGIN to some fixed position for an intermission camera. you can make it follow behind the player entity if you want 3rd person. you can make it follow a spline if you want something weird, or attach it to the nearest camera spot if you want something spooky like alone in the dark. If you're ssqc-centric, you can use stats to hold the new view position.
And attach it to a bone through gettaginfo?
Using deltalisten allows you to reuse more of the legacy engine code than SendEntity does, and is thus typically less qc code, but it will have more expectations about engine behaviours.
Ok, got it, thanks a lot. I won't use deltalisten, most because I couldn't figure out how to use .predraw inside listen functions. Animations went well but skeletons blew everything up


Just a sidenote Spike: do you think you'll have the time in the future to make a new updated version of csqctest from scratch, without Quake elements, where to show in detail new features like skeletal animations , network parsing and hud? Because that could be very helpful for those (like me) who can't completely understand all the csqc technical aspects
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: FTEQW - check active player view

Post by Spike »

you can't just assign some random value to .skeletonindex.
it needs to be a result of skel_create.
and you need to delete it again afterwards.
note that you can do this:

Code: Select all

setmodelindex(self, self.modelindexthatwasreadbyreadshort); //sets .model properly, which can be handy in csqc where you might only be networking the modelindex.
self.skeletonindex = skel_create(self.modelindex); //allocate a new skeleton that is compatible with the specified model.
skel_build(...); //look up the args yourself. get creative and blend multiple animations based on axial distance traveled etc.
addentity(self);  //let the renderer know
skel_delete(self.skeletonindex); //don't leak
self.skeletonindex = 0;  //clean up
despite being deleted, the skeletonindex reference will persist for any renderscene calls that frame, but not beyond that. so no worries there.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: FTEQW - check active player view

Post by toneddu2000 »

Ok thanks a lot Spike, it worked!
1) removed send field .skeletonindex from ssqc to csqc
2) in CSQC_Ent_Update added

Code: Select all

local float mdli = readbyte();
setmodelindex(self,mdli);
instead of reading self.modeling=readbyte();

Thanks! Now I've last problem and it seems that it's related to the original issue: understand how the active player works. When I move the mouse, the player, in third person view, bends his spine.
I'm creating a spine bending animation based on input_angles.
I used 2 methods and both gave same result: when I launch a dedicated server and I connect to it more than 1 client, if I (in first person view) bend the visual, all the other clients present in the map (but in the same client window) bend their spines(all of them)!
But if I look at the other client windows (when I test my game I place all the client windows one near the other) all the players are not bending their spines! So, only the clients (not the active one) inside the active window bend their spines when active player moves mouse on Y axis! What a mess!! :D

FIRST METHOD (PREDRAW)
I tried creating the skeletal code in the predraw function following your indications on specs/skeletal.txt

Code: Select all

float Predraw_Player()
{	
	self.angles_x = 0;//avoid the rotation of the whole model on x axis while mouse moves(we'll use skel_mul_bone for rotate only spine and head)
	if (!self.skeletonindex){//|| isnotskeletal(self)
		self.skeletonindex = skel_create(self.modelindex);
		skel_build(self.skeletonindex, self, self.modelindex, 0, 0, 0, 1);
		addentity(self);
	}
	float spine = skel_find_bone(self.skeletonindex, "spine");
	float spine2 = skel_find_bone(self.skeletonindex, "spine2");
	float foot_l = skel_find_bone(self.skeletonindex,"foot_l");
	float neck = skel_find_bone(self.skeletonindex,"neck");
	float head = skel_find_bone(self.skeletonindex,"head");
	float hand_l = skel_find_bone(self.skeletonindex,"hand_l");
	float lastbone = 38;
	float bones = skel_get_numbones(self.skeletonindex);
	
	skel_build(self.skeletonindex, self, self.modelindex, 0, spine2,hand_l, 1);
	gettaginfo(self, spine2); //set v_forward, v_right, v_up. discard world coord
	local vector spineang = vectoangles(v_forward, v_up);
	makevectors([0, 0, -input_angles_x - spineang_z]);
	skel_mul_bone(self.skeletonindex, spine2, '0 0 0');
	
	skel_build(self.skeletonindex, self, self.modelindex, 0, spine, foot_l, 1);
	PlayerModelMovements();
	return PREDRAW_AUTOADD;
}
SECOND METHOD (CSQC_UpdateView)
I also tried same thing, but instead of using predraw (because I thought it was a wrong place to place skeletal animation) I created a function with an entity parameters called in CSQC_UpdateView

Code: Select all

void PlayerModelMovements2(entity ent)
{
	float vell = vlen(ent.velocity);
	ent.frame1time += frametime;
	if(vell <= 0){
		ent.frame = 0;
	}
	else if(vell > 0 && vell < 10){
		ent.frame = 1;
	}
	else if(vell > 10){
		ent.frame = 2;
	}
}

void AnimatePlayer(entity ent)
{
	float spine = skel_find_bone(ent.skeletonindex, "spine");
	float spine2 = skel_find_bone(ent.skeletonindex, "spine2");
	float foot_l = skel_find_bone(ent.skeletonindex,"foot_l");
	float hand_l = skel_find_bone(ent.skeletonindex,"hand_l");
	float lastbone = 38;
	
	skel_build(ent.skeletonindex, ent, ent.modelindex, 0, spine2,hand_l, 1);
	gettaginfo(ent, spine2); //set v_forward, v_right, v_up. discard world coord
	local 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).
	makevectors([0, 0, -input_angles_x - spineang_z]);
	skel_mul_bone(ent.skeletonindex, spine2, '0 0 0');
	skel_build(ent.skeletonindex, ent, ent.modelindex, 0, spine, foot_l, 1);
	PlayerModelMovements2(ent);
}
Then, I changed my Startup_Player function (called by CSQC_EntUpdate) to add here the skel_create, instead of placing it in predraw

Code: Select all

void Startup_Player(float newent)
{
	if (newent)
	{
		if (self.entnum == player_localentnum){
			playerActive = self;
			self.renderflags = RF_EXTERNALMODEL;
		}
		else{
			self.renderflags = 0;
		}
		if (!self.skeletonindex){
			self.skeletonindex = skel_create(self.modelindex);
			skel_build(self.skeletonindex, self, self.modelindex, 0, 0, 0, 1);
			addentity(self);
		}
		self.classname = "player";
		self.drawmask = MASK_NORMAL; // makes the entity visible
	}
}
I created finally a UpdatePlayer function where to identify the subject of skeletal animation (the player)

Code: Select all

void UpdatePlayer()
{
	local entity ent;

	ent = nextent(world);
	while (ent)
	{
		if (ent.classname == "player")
		{
			AnimatePlayer (ent);
		}
		ent = nextent(ent);
	}
}
And I put UpdatePlayer in CSQC_UpdateView, BEFORE clearscene

None of this 2 methods solve the problem. If I remove the spine skeletal stuff and I retest with multiple windows, players behave correctly: only the active player bends(the whole model,obviously) in other client windows, while the active camera rotates. So it's of course an error of mine when I created the bending code but I can't figure out where's my mistake

Thanks a lot Spike, you're helping me so much!
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: FTEQW - check active player view

Post by Spike »

input_angles is part of the local input state (you can query specific entries in the input log via getinputstate using values between clientcommandframe(equal to this obtains the current state that can change each frame, and which the server has not been told about yet) and servercommandframe(equal to this is the first frame that has not yet been applied to the latest raw data received from the server).

each client->server packet contains a copy of an input_angles value. this will be present within the SV_RunClientCommand function (assuming its present), and would normally be propogated into the player's v_angle field by the runstandardplayerphysics builtin which is assumed to be there (obviously, you can replace this if you wish, in which case you'll likely want to update v_angle or rewrite much of your ssqc to cope).
at the tail end of runstandardplayerphysics (or your equivelent), there will be some logic to set the player's angles field. typically using this formula: self.angles = [self.v_angle_x*-0.333,self.v_angle_y,self.v_angle_z]; this works around the input/renderer pitch difference, as well as prevents the player model from lying on his back when viewing vertically upward. with skeletal models, you'll probably want to discard the pitch entirely.
your SendEntity will then propogate the player's angle value somehow back to the csqc.
your csqc will receive that value and will then draw the entity using that angle.

basically, what I'm getting at, is that you need to separate the hips angle from the view angle. your player's hips will need to track the view/head angle over time (when moving, you can use the direction of travel to define the hips angle, and otherwise bound it so that its updated to match the view angle if the two angles diverge by more than 20 degrees or so).
but ultimately, you need to stop using input_angles to describe the angles of other players, because doing that really doesn't make sense.
if it IS the local player's entity, then you can override the view angle received from the server with the input_angles value, but if it is any other player, you cannot do that, and HAVE to use a value received from the server.
note that the hips angle can either be determined locally(in csqc), or in ssqc. if its in ssqc then all clients will have the same value. if its in csqc, you can more easily work with input_angles to avoid weirdness when lagged or paused.

also, addentity+PREDRAW_AUTOADD is redundant. you're generating two copies of the same entity.

side note:
distancetraveledtotheright = (neworigin - oldorigin) * v_right;
(the * is a dotproduct).
you can use the same maths to calculate the distance traveled forwards.
you can then rescale this by the distance the animation appears to move by per second, and adjust frame1time or so
self.frame1time += ((neworigin - oldorigin) * v_right) / 320;
note that this will also play the animation backwards if you travel to the left, which might be weird.
assuming the player only travels right, and the current .frame specifies a framegroup/sequence/animation that also travels right at a speed of exactly 320qu per second, this will make your player's feet exactly sync with the ground.
by determining the various strengths of the animations (ie: if travelling left instead, add in more of the move-left animation and decrease the move-right animation), you should be able to blend out the weird animations and thereby keep the player's feet synced with the ground regardless of the direction of travel.
also note that you can use v_forward instead of v_right to determine how far forwards you've gone.
deciding how far through an animation you are is easy. however, deciding which animations to play and the weight of those animations is an art form.
the total weight of all of your animations must equal 1 - any other sum total will result in scaling. you can use the addfrac and retainfrac arguments of skel_build in order to merge the varous animations together into the pose displayed that frame. just update the .frame+.frame1time fields between each call to skel_build to blend in the different animations.
it is unfortunate that .frame has this dual purpose. instead of adding lots of code to preserve its value, my recommendation would be to create a new .action field instead, and (within predraw) decide the .frame values based upon that. you can use the frameforname builtin to look up an animation by name from a model. this means you can provide models with animations used purely by name (allowing you to omit entire animations in certain models, potentially allowing you to use the same code to animate most monster types or whatever).


oh, and regarding your sidenote/question about csqctest, I'm not entirely sure how relevant csqctest still is. it was very much bolted on the side of vanilla quake with lots of random pointless things that are useful to test parts of the engine, but don't really do anything useful in themselves. it also lacks polish (in part because it was never written to be an actual worthwhile mod to play). parts of it are useful, but its not a mod I would recommend anyone use as a base.
I'd release the code for 'spark', which was some standalone mod I was hacking together ages back now, which was meant to include stuff like custom physics, skeletal animation blending, etc etc (ie: all the modern stuff and none of the quake stuff), but I don't own the content or half the qc code, and as such its not really usable as a demonstration of much at all. it would take a lot of work for it to be practical (as well as content so you can actually see it in action), and I have too many other distractions/projects right now.
maybe one day I'll get an itch to publicise+polish it.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: FTEQW - check active player view

Post by toneddu2000 »

each client->server packet contains a copy of an input_angles value. this will be present within the SV_RunClientCommand function (assuming its present), and would normally be propogated into the player's v_angle field by the runstandardplayerphysics builtin which is assumed to be there (obviously, you can replace this if you wish, in which case you'll likely want to update v_angle or rewrite much of your ssqc to cope).
at the tail end of runstandardplayerphysics (or your equivelent), there will be some logic to set the player's angles field. typically using this formula: self.angles = [self.v_angle_x*-0.333,self.v_angle_y,self.v_angle_z]; this works around the input/renderer pitch difference, as well as prevents the player model from lying on his back when viewing vertically upward. with skeletal models, you'll probably want to discard the pitch entirely.
your SendEntity will then propogate the player's angle value somehow back to the csqc.
your csqc will receive that value and will then draw the entity using that angle.
THANKS Spike! It worked!
from ssqc:
send self.v_angle to csqc as coord

Code: Select all

WriteCoord(MSG_ENTITY, self.v_angle_x);
	WriteCoord(MSG_ENTITY, self.v_angle_y);
	WriteCoord(MSG_ENTITY, self.v_angle_z);
Then in csdefs add

Code: Select all

.vector v_angle
read in CSQC_EntUpdate the three v_angle coords

Code: Select all

self.v_angle_x = readcoord();
self.v_angle_y = readcoord();
self.v_angle_z = readcoord();
finally in predraw replace input_angles_x with self.v_angle_x

Code: Select all

makevectors([0, 0, -self.v_angle_x - spineang_z]);
Now only active player bends his spine! Thanks!! :D
each client->server packet contains a copy of an input_angles value. this will be present within the SV_RunClientCommand function (assuming its present), and would normally be propogated into the player's v_angle field by the runstandardplayerphysics builtin which is assumed to be there (obviously, you can replace this if you wish, in which case you'll likely want to update v_angle or rewrite much of your ssqc to cope).
One day we need to talk about SV_RunClientCommand and runstandardplayerphysics because I don't really know what is it0 and I can't find knowledge anywhere. But, of course, in another thread because it's not camera-related(well, neither my skeletal animation question was related but I cheated! :D )
also, addentity+PREDRAW_AUTOADD is redundant. you're generating two copies of the same entity.
But I need the returning value in predraw or fteqcc will complain about it. Should I remove addentity?
side note:
distancetraveledtotheright = (neworigin - oldorigin) * v_right;
(the * is a dotproduct).
you can use the same maths to calculate the distance traveled forwards.
you can then rescale this by the distance the animation appears to move by per second, and adjust frame1time or so
self.frame1time += ((neworigin - oldorigin) * v_right) / 320;
note that this will also play the animation backwards if you travel to the left, which might be weird.
assuming the player only travels right, and the current .frame specifies a framegroup/sequence/animation that also travels right at a speed of exactly 320qu per second, this will make your player's feet exactly sync with the ground.
Oh, I'm gonna try that, I'm really gonna try that! :D Thanks a lot, I'll let you know (in another thread, maybe this one)!
oh, and regarding your sidenote/question about csqctest, I'm not entirely sure how relevant csqctest still is. it was very much bolted on the side of vanilla quake with lots of random pointless things that are useful to test parts of the engine, but don't really do anything useful in themselves. it also lacks polish (in part because it was never written to be an actual worthwhile mod to play). parts of it are useful, but its not a mod I would recommend anyone use as a base.
I'd release the code for 'spark', which was some standalone mod I was hacking together ages back now, which was meant to include stuff like custom physics, skeletal animation blending, etc etc (ie: all the modern stuff and none of the quake stuff), but I don't own the content or half the qc code, and as such its not really usable as a demonstration of much at all. it would take a lot of work for it to be practical (as well as content so you can actually see it in action), and I have too many other distractions/projects right now.
Yeah, infact I said csqctest but I meant a new scratch csqc framework released under a nowadays license (cc0 or mit) but of course only if you're in the mood/interested. I'm developing something similar but I've so few skills to do it on my own
maybe one day I'll get an itch to publicise+polish it.
fingers crossed! :D

Thanks again for your precious help!
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Post Reply