Forum

Fix - Large Brushmodel Flickering/Not Visible

Post tutorials on how to do certain tasks within game or engine code here.

Moderator: InsideQC Admins

Fix - Large Brushmodel Flickering/Not Visible

Postby mh » Wed Jul 14, 2010 10:31 pm

In case anybody missed it:

Original thread, discussion and sample code

There is a problem with many Quake engines where extra large brush models go into too many leafs during the process of the server determining which entities to send to the client. Quake sets a maximum of 16 leafs which an entity can be in, and if all 16 of those are filled by leafs outside of the PVS, then the entity won't be sent.

This is not necessarily restricted to brush models and could in theory also happen with MDLs, although in practice I have never seen one large enough to cause the problem. It could also happen if QBSP carves your map up such that there are more than 16 leafs butting together close enough in a tight area, but I'd expect that's quite unlikely.

The original thread linked above contains a discussion of the problem, various methods of fixing it, and sample code for one method of fixing it (derived from DirectQ; this will have to be adapted to your own engine, but it should be quite evident what needs to be changed).

The code given is by no means the only way or the best way, but it has been in production for over a year and has been proven to work with a specific incidence of the problem.
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 revelator » Sat Oct 23, 2010 7:14 am

also fixed some trouble i had with the world volume carving code in earlier mhquake code so :D (Realm is based on one of those).

something tells me the limit was a bit conservative as these problems
surfaced even in standard id1 maps.

the world volume carving system actually relies on having all leaf data availiable but for some reason without this fix it didnt.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Postby revelator » Sat Oct 23, 2010 8:12 am

btw it also causes a massive speedup in loading times + :D

Code: Select all
void SV_WriteEntitiesToClient (edict_t   *clent, sizebuf_t *msg)
{
    int      e, i;
    int      bits;
    int      packet;
    byte   *pvs;
    vec3_t   org;
    float   miss;
    edict_t   *ent;
    float   lastmsg;

    // terminate lastmessage properly.
    memset (&lastmsg, 0, sizeof(float));

    // find the client's PVS
    VectorAdd (clent->v.origin, clent->v.view_ofs, org);

    pvs = SV_FatPVS (org, sv.worldmodel);

    // send over all entities (except the client) that touch the pvs
    ent = NEXT_EDICT(sv.edicts);

    for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
    {
        // ignore if not touching a PV leaf
        if (ent != clent)   // clent is ALLWAYS sent
        {
            // ignore ents without visible models
            if (!ent->v.modelindex || !pr_strings[ent->v.model]) continue;

         // link to PVS leafs - deferred to here so that we can compare leafs that are touched to the PVS.
         // this is less optimal on one hand as it now needs to be done separately for each client, rather than once
         // only (covering all clients), but more optimal on the other as it only needs to hit one leaf and will
         // start dropping out of the recursion as soon as it does so.  on balance it should be more optimal overall.
         ent->touchleaf = false;

         SV_FindTouchedLeafs (ent, sv.worldmodel->nodes, pvs);

         // if the entity didn't touch any leafs in the pvs don't send it to the client
         if (!ent->touchleaf) continue;
        }

        // send an update
        bits = 0;

        for (i=0 ; i<3 ; i++)
        {
            miss = ent->v.origin[i] - ent->baseline.origin[i];

            if ( miss < -0.1 || miss > 0.1 )
            {
                bits |= U_ORIGIN1<<i;
            }
        }

        if ( ent->v.angles[0] != ent->baseline.angles[0] )
        {
            bits |= U_ANGLE1;
        }

        if ( ent->v.angles[1] != ent->baseline.angles[1] )
        {
            bits |= U_ANGLE2;
        }

        if ( ent->v.angles[2] != ent->baseline.angles[2] )
        {
            bits |= U_ANGLE3;
        }

        if (ent->v.movetype == MOVETYPE_STEP)
        {
            bits |= U_NOLERP;   // don't mess up the step animation
        }

        if (ent->baseline.colormap != ent->v.colormap)
        {
            bits |= U_COLORMAP;
        }

        if (ent->baseline.skin != ent->v.skin)
        {
            bits |= U_SKIN;
        }

        if (ent->baseline.frame != ent->v.frame)
        {
            bits |= U_FRAME;
        }

        if (ent->baseline.effects != ent->v.effects)
        {
            bits |= U_EFFECTS;
        }

        if (ent->baseline.modelindex != ent->v.modelindex)
        {
            bits |= U_MODEL;
        }

        if (e >= 256)
        {
            bits |= U_LONGENTITY;
        }

        if (bits >= 256)
        {
            bits |= U_MOREBITS;
        }

        // Original + missing for worst case
        packet = 16 + 2;

        if (sv.protocol != PROTOCOL_VERSION)
        {
            ++packet; // PROTOCOL_VERSION_EXT3
        }

        if (sv_max_datagram == MAX_DATAGRAM)
        {
            packet += 256; // Empirical margin to avoid Loop_SendMessage or other overflows
        }

        if (msg->maxsize - msg->cursize < packet)
        {
            if (COM_Timeout (&lastmsg, 2))
            {
                Con_Printf ("packet overflow\n");
            }
            return;
        }

        //
        // write the message
        //
        MSG_WriteByte (msg, bits | U_SIGNAL);

        if (bits & U_MOREBITS)
        {
            MSG_WriteByte (msg, bits>>8);
        }

        if (bits & U_LONGENTITY)
        {
            MSG_WriteShort (msg,e);
        }
        else
        {
            MSG_WriteByte (msg,e);
        }

        if (bits & U_MODEL)
        {
            MSG_WriteByteShort (msg, ent->v.modelindex);
        }

        if (bits & U_FRAME)
        {
            MSG_WriteByte (msg, ent->v.frame);
        }

        if (bits & U_COLORMAP)
        {
            MSG_WriteByte (msg, ent->v.colormap);
        }

        if (bits & U_SKIN)
        {
            MSG_WriteByte (msg, ent->v.skin);
        }

        if (bits & U_EFFECTS)
        {
            MSG_WriteByte (msg, ent->v.effects);
        }

        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]);
        }
    }
}


progs.h

Code: Select all
typedef struct edict_s
{
    link_t         area;         // linked to a division node or leaf
   qboolean      touchleaf;      // true if the ent touches a leaf in the pvs

    entity_state_t   baseline;
    entvars_t       v;            // C exported fields from progs
// other fields from progs come immediately after
} edict_t;


world.c

Code: Select all
/*
===============
SV_FindTouchedLeafs
===============
*/
void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node, byte *pvs)
{
   mplane_t   *splitplane;
   int         sides;
   int         leafnum;

loc0:;
   // ent already touches a leaf
   if (ent->touchleaf) return;

   // hit solid
   if (node->contents == CONTENTS_SOLID) return;

   // add an efrag if the node is a leaf
   // this is used for sending ents to the client so it needs to stay
   if (node->contents < 0)
   {
loc1:;
      leafnum = ((mleaf_t *) node) - sv.worldmodel->leafs - 1;

      if ((pvs[leafnum >> 3] & (1 << (leafnum & 7))))
      {
         ent->touchleaf = true;
      }
      return;
   }

   // NODE_MIXED
   splitplane = node->plane;
   sides = BOX_ON_PLANE_SIDE (ent->v.absmin, ent->v.absmax, splitplane);

   // recurse down the contacted sides, start dropping out if we hit anything
   if ((sides & 1) && !ent->touchleaf && node->children[0]->contents != CONTENTS_SOLID)
   {
      if (!(sides & 2) && node->children[0]->contents < 0)
      {
         node = node->children[0];
         goto loc1;
      }
      else if (!(sides & 2))
      {
         node = node->children[0];
         goto loc0;
      }
      else
      {
            SV_FindTouchedLeafs (ent, node->children[0], pvs);
      }
   }

   if ((sides & 2) && !ent->touchleaf && node->children[1]->contents != CONTENTS_SOLID)
   {
      // test for a leaf and drop out if so, otherwise it's a node so go round again
      node = node->children[1];

      if (node->contents < 0)
      {
         goto loc1;
      }
      else
      {
            goto loc0;   // SV_FindTouchedLeafs (ent, node, pvs);
      }
   }
}
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger


Return to Programming Tutorials

Who is online

Users browsing this forum: No registered users and 1 guest