Forum

Don't use Q_rint in MSG_WriteAngles...

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Don't use Q_rint in MSG_WriteAngles...

Postby mh » Sun Apr 10, 2011 5:00 pm

...or other +/- 0.5f tricks.

There is a bug with some maps (outlined here: http://forums.inside3d.com/viewtopic.php?t=2376&start=70) where entities do have angles set, but the standard precision loss in stock ID code doesn't cause them to have any effect. Switching to Q_rint-like behaviour causes things to go mildly berserk.

This also affects any protocol change where angles have more precision that 1 byte.

Fully reproducable in hrim_sp1: the part where you push a button and a platform rises out of lava.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby goldenboy » Sun Apr 10, 2011 5:14 pm

In other words it was always broken but it was never noticed owing to loss of precision when transmitting angles in stock protocol 15.


For my part, I need the smoothness and I'll say "to hell with the old stuff".

Throwing away real improvements in order to support old stuff is wrong.

That's my opinion.
User avatar
goldenboy
 
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel

Postby mh » Sun Apr 10, 2011 5:17 pm

Compromise/hacky/quick-n-dirty solution involves popping this at the top of MSG_WriteAngles:
Code: Select all
   // compensate for Q_rint and higher precision angles causing incorrect angles at this range
   // by setting anything from -1..1 to 0 in order to match the loss with stock ID Quake
   if (f > 1)
      ;
   else if (f < -1)
      ;
   else f = 0;
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby Spike » Sun Apr 10, 2011 7:38 pm

how does that fix anything?
if the server is generating the wrong bbox then its collisions will be broken anyway.
if the model really is rotated with an origin, its clientside representation will differ from the server's 1-rotated bbox whether its sent as 0 or 1.0/255.
Spike
 
Posts: 2911
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby mh » Sun Apr 10, 2011 7:56 pm

It's not bbox-related.

OK, specific case where this occurs. hrim_sp1 has a brush model that has angles of (0, -1, 0).

Protocol 15's default handling of angles (((int)f*256/360) & 255) makes this send to the client as (0, 0, 0).

Using Q_rint makes it send to the client as (0, ~(-1), 0).

In the former case the bmodel is drawn in the correct location.

In the latter case it's not. It's not just out of position by a small amount, it's wildly out of position.

Compare:

Image

The bbox on the server stays correct, the bmodel is drawn in the incorrect position on the client, you jump on the platform but only a small part of it will actually catch you - the rest of the time you're going into lava.

So what the hacky/quick-n-dirty thing does is kinda reproduce the behaviour of standard protocol 15 for really small angles by forcing anything in the range -1..1 to 0. Standard protocol 15 already does this anyway so it's nothing new.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby mh » Sun Apr 10, 2011 10:56 pm

This lot seems to fix things up in a more robust and general manner; some brief testing done and haven't noticed anything untoward yet. Gonna throw it at the RMQ people and see if they can find anywhere it breaks.
Code: Select all
void MSG_WriteByteAngle (sizebuf_t *sb, float f)
{
   byte bang = 0;

   // -1 and -2 are legal values with special meaning so handle them
   // the same way as ID Quake did; other values can use rounding
   if (f == -1 || f == -2)
      bang = (int) (f * 256.0 / 360.0) & 255;
   else bang = Q_rint (f * 256.0 / 360.0) & 255;

   MSG_WriteByte (sb, bang);
}


void MSG_WriteShortAngle (sizebuf_t *sb, float f)
{
   int sang = 0;

   // -1 and -2 are legal values with special meaning so handle them
   // the same way as ID Quake did; other values can use rounding
   if (f == -1 || f == -2)
   {
      // encode to byte, decode back to float to get the same behaviour as ID Quake
      sang = (int) (f * 256.0 / 360.0) & 255;
      f = (float) sang * (360.0 / 256);
   }

   sang = Q_rint (f * 65536.0 / 360.0) & 65535;

   MSG_WriteShort (sb, sang);
}


void MSG_WriteFloatAngle (sizebuf_t *sb, float f)
{
   // -1 and -2 are legal values with special meaning so handle them
   // the same way as ID Quake did; other values can use rounding
   if (f == -1 || f == -2)
   {
      // encode to byte, decode back to float to get the same behaviour as ID Quake
      int fang = (int) (f * 256.0 / 360.0) & 255;
      f = (float) fang * (360.0 / 256);
   }

   MSG_WriteFloat (sb, f);
}

...and just call the appropriate writing function from your main MSG_WriteAngle.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby r00k » Mon Apr 11, 2011 8:56 pm

Shouldn't there be corresponding READ functions?
Last edited by r00k on Mon Apr 11, 2011 9:15 pm, edited 2 times in total.
r00k
 
Posts: 1110
Joined: Sat Nov 13, 2004 10:39 pm

Postby mh » Mon Apr 11, 2011 9:02 pm

No need because once an angle is encoded into a byte, that byte is only ever going to give the same result back when decoded. It's the byte that it gets encoded into that matters.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby r00k » Mon Apr 11, 2011 9:15 pm

_small tangent_

I noticed ProQuake (for lack of another comparison) uses shorts for angles (client --> server) BUT doesnt send PRECISEangles to the client from the server in SV_WriteEntitiesToClient.

So im a bit confused, (easily done), the client moves the mouse, aims at angle 34.54 (truncate/rounds/sends) to the server 34degrees the server runs through its frame sends back to ALL client's 34*(360/256) = 47.8125 ???
Last edited by r00k on Mon Apr 11, 2011 9:55 pm, edited 2 times in total.
r00k
 
Posts: 1110
Joined: Sat Nov 13, 2004 10:39 pm

Postby Tomaz » Mon Apr 11, 2011 9:53 pm

r00k wrote:_small tangent_

I noticed ProQuake (for lack of another comparison) uses shorts for angles (client --> server) BUT doesnt send PRECISEangles to the client from the server in SV_WriteEntitiesToClient.

So im a bit confused, (easily done), the client moves the mouse, aims at angle 34.54 (truncate/rounds/sends) to the server 34degrees the server runs through its frame sends back to ALL client's 34*(360/256) = 47.8125 ???


Correct me if I'm wrong

Server sends:
34.54 * ( 256 / 360 ) which is roughly 24.56 which it rounds to 25
Client reads:
25 and does 25 * ( 360 * 256 ) which gives 35.15

So from 34.54 we got 35.15, close enough when dealing with just byte angles.

This is due to the fact that when using byte angles you get 360 / 255 precision which is roughly 1.4 degrees, compared to shorts which gives 360 / 65535 precision which is roughly 0.005 degrees.

Or did you mean that ProQuake uses shorts in one direction and bytes in the other? I wasn't aware that the client ever sends any angles to the server, at all. Isn't it just sending input events?
Tomaz
 
Posts: 67
Joined: Fri Nov 05, 2004 8:21 pm

Postby r00k » Mon Apr 11, 2011 10:01 pm

Or did you mean that ProQuake uses shorts in one direction and bytes in the other? I wasn't aware that the client ever sends any angles to the server, at all. Isn't it just sending input events?


I was thinking the PQclient sends hires angles
cl_input.c
Code: Select all
//
// send the movement message
//
    MSG_WriteByte (buf, clc_move);

   MSG_WriteFloat (buf, cl.mtime[0]);
   
   if (!cls.demoplayback && (cls.netcon->mod == MOD_PROQUAKE)) // JPG - precise aim for ProQuake!
   {
      for (i=0 ; i<3 ; i++)
         MSG_WriteAngle16(buf, cl.viewangles[i]);         
   }
   else
   {
      for (i=0 ; i<3 ; i++)
         MSG_WriteAngle (buf, cl.viewangles[i]);
   }


server Reads hires angle from the client,
sv_user.c
Code: Select all
void SV_ReadClientMove (usercmd_t *move)
{
   int      i, bits;
   vec3_t   angle;

// read ping time
   host_client->ping_times[host_client->num_pings%NUM_PING_TIMES] = sv.time - MSG_ReadFloat ();
   host_client->num_pings++;

// read current angles
   if (host_client->netconnection->mod == MOD_PROQUAKE)
   {
      for (i=0 ; i<3 ; i++)
         angle[i] = MSG_ReadAngle16 ();
   }
   else
   {
      for (i=0 ; i<3 ; i++)
         angle[i] = MSG_ReadAngle ();
   }


but when angles are sent to all entities about all entities, the hires angles (for model orientation), was being overwritten.

sv_main.c
Code: Select all
      if (bits & U_ORIGIN1)
         MSG_WriteCoord (msg, ent->v.origin[0]);      
      if (bits & U_ANGLE1)
         MSG_WriteAngle(msg, ent->v.angles[0]);
      if (bits & U_ORIGIN2)
         MSG_WriteCoord (msg, ent->v.origin[1]);
      if (bits & U_ANGLE2)
         MSG_WriteAngle(msg, ent->v.angles[1]);
      if (bits & U_ORIGIN3)
         MSG_WriteCoord (msg, ent->v.origin[2]);
      if (bits & U_ANGLE3)
         MSG_WriteAngle(msg, ent->v.angles[2]);



but cl.viewangles and ent->v.angles must be loosly related...?

not to mention svc_setangle

cl_parse
Code: Select all
case svc_setangle:
            for (i=0 ; i<3 ; i++)
            {
               cl.viewangles[i] = MSG_ReadAngle ();
            }


sv_main:
Code: Select all
   if (ent->v.fixangle)
   {
      MSG_WriteByte (msg, svc_setangle);
      
      for (i=0 ; i<3 ; i++)
         MSG_WriteAngle (msg, ent->v.angles[i]);

      ent->v.fixangle = 0;
   }


again not using hires angles :|
r00k
 
Posts: 1110
Joined: Sat Nov 13, 2004 10:39 pm

Postby mh » Mon Apr 11, 2011 10:21 pm

All clients sent angles to the server as part of their input.

But.

Yes, the send back from the server does overwrite, but the client does nothing with this data. At least for the local client; it does use the data for other clients to orient their player.mdl correctly; for the local client it does absolutely nothing. The angles go into the entity_t struct for the local client for sure, but the view position on the local client is actually controlled by cl.viewent which is updated separately in your view.c

Then the next frame the local client just sends it's angles again, which are accumulated from input into a usercmd_t; doesn't even go near an entity_t (at least for this part of it; r_refdef.viewangles also comes in but let's not complicate things; Quake has too many variables scattered around the place all containing the same data and it makes my head hurt sometimes).

So really you've got two entity_t structs on each client representing the local client; cl_entities[cl.viewentity] is one of the regular entity_t structs that get overwritten by the server, and all other clients live there too, cl.viewent is a separate "special" entity_t that manages the state of you as the local client.

Clear as mud?
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby r00k » Mon Apr 11, 2011 10:28 pm

Ok that's why sometimes players look like they are aiming at one point but the rockets comes out from the corner of their "bounding box"...

as long as my POV isnt getting reAdjusted. That would make for jittery aim.
r00k
 
Posts: 1110
Joined: Sat Nov 13, 2004 10:39 pm

Postby mh » Mon Apr 11, 2011 10:29 pm

...or it might be just MDL animations being crap... :lol:
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest