complexanimation now beginning to be suitable for SVQC use
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 12 Nov 2011 11:17:38 +0000 (11:17 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 12 Nov 2011 11:17:38 +0000 (11:17 +0000)
- only set E5_COMPLEXANIMATION flag when required to save a LOT of bandwidth
- don't also send a legacy frame number when using complex animation to save a bit more
- no more support of "anims that start in the future", use a QC think function to handle them
- fix wraparound logic for complex animation (if a model animation is shorter than 30 sec, it won't jerk when playing the same anim for over a minute)

Bandwidth use per update message:
- simple anim: 1 byte
- 1 blends: 5 bytes
- 2 blends: 11 bytes
- 3 blends: 16 bytes
- 4 blends: 21 bytes
- skeletonobject: 4 + 12*bonecount bytes

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@11546 d7cf8633-e32d-0410-b094-e92efae38249

protocol.c
sv_main.c

index 4f0139b..d63d091 100644 (file)
@@ -2062,6 +2062,31 @@ static int EntityState5_Priority(entityframe5_database_t *d, int stateindex)
        return bound(1, priority, ENTITYFRAME5_PRIORITYLEVELS - 1);
 }
 
+static double anim_reducetime(double t, double frameduration, double maxtime)
+{
+       if(t < 0) // clamp to non-negative
+               return 0;
+       if(t <= maxtime) // time can be represented normally
+               return t;
+       if(frameduration == 0) // don't like dividing by zero
+               return t;
+       if(maxtime <= 2 * frameduration) // if two frames don't fit, we better not do this
+               return t;
+       t -= frameduration * ceil((t - maxtime) / frameduration);
+       // now maxtime - frameduration < t <= maxtime
+       return t;
+}
+
+// see VM_SV_frameduration
+static double anim_frameduration(dp_model_t *model, int framenum)
+{
+       if (!model || !model->animscenes || framenum < 0 || framenum >= model->numframes)
+               return 0;
+       if(model->animscenes[framenum].framerate)
+               return model->animscenes[framenum].framecount / model->animscenes[framenum].framerate;
+       return 0;
+}
+
 void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbits, sizebuf_t *msg)
 {
        prvm_prog_t *prog = SVVM_prog;
@@ -2231,50 +2256,54 @@ void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbi
                                        MSG_WriteShort(msg, pose6s[5]);
                                }
                        }
-                       else if (s->framegroupblend[3].lerp > 0)
-                       {
-                               MSG_WriteByte(msg, 3);
-                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[2].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[3].frame);
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[0].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[1].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[2].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[3].start) * 1000.0));
-                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[2].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[3].lerp * 255.0f);
-                       }
-                       else if (s->framegroupblend[2].lerp > 0)
-                       {
-                               MSG_WriteByte(msg, 2);
-                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[2].frame);
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[0].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[1].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[2].start) * 1000.0));
-                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[2].lerp * 255.0f);
-                       }
-                       else if (s->framegroupblend[1].lerp > 0)
-                       {
-                               MSG_WriteByte(msg, 1);
-                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[0].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[1].start) * 1000.0));
-                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
-                       }
                        else
                        {
-                               MSG_WriteByte(msg, 0);
-                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[0].start) * 1000.0));
+                               dp_model_t *model = SV_GetModelByIndex(s->modelindex);
+                               if (s->framegroupblend[3].lerp > 0)
+                               {
+                                       MSG_WriteByte(msg, 3);
+                                       MSG_WriteShort(msg, s->framegroupblend[0].frame);
+                                       MSG_WriteShort(msg, s->framegroupblend[1].frame);
+                                       MSG_WriteShort(msg, s->framegroupblend[2].frame);
+                                       MSG_WriteShort(msg, s->framegroupblend[3].frame);
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[0].start, anim_frameduration(model, s->framegroupblend[0].frame), 65.535) * 1000.0));
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[1].start, anim_frameduration(model, s->framegroupblend[1].frame), 65.535) * 1000.0));
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[2].start, anim_frameduration(model, s->framegroupblend[2].frame), 65.535) * 1000.0));
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[3].start, anim_frameduration(model, s->framegroupblend[3].frame), 65.535) * 1000.0));
+                                       MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
+                                       MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
+                                       MSG_WriteByte(msg, s->framegroupblend[2].lerp * 255.0f);
+                                       MSG_WriteByte(msg, s->framegroupblend[3].lerp * 255.0f);
+                               }
+                               else if (s->framegroupblend[2].lerp > 0)
+                               {
+                                       MSG_WriteByte(msg, 2);
+                                       MSG_WriteShort(msg, s->framegroupblend[0].frame);
+                                       MSG_WriteShort(msg, s->framegroupblend[1].frame);
+                                       MSG_WriteShort(msg, s->framegroupblend[2].frame);
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[0].start, anim_frameduration(model, s->framegroupblend[0].frame), 65.535) * 1000.0));
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[1].start, anim_frameduration(model, s->framegroupblend[1].frame), 65.535) * 1000.0));
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[2].start, anim_frameduration(model, s->framegroupblend[2].frame), 65.535) * 1000.0));
+                                       MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
+                                       MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
+                                       MSG_WriteByte(msg, s->framegroupblend[2].lerp * 255.0f);
+                               }
+                               else if (s->framegroupblend[1].lerp > 0)
+                               {
+                                       MSG_WriteByte(msg, 1);
+                                       MSG_WriteShort(msg, s->framegroupblend[0].frame);
+                                       MSG_WriteShort(msg, s->framegroupblend[1].frame);
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[0].start, anim_frameduration(model, s->framegroupblend[0].frame), 65.535) * 1000.0));
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[1].start, anim_frameduration(model, s->framegroupblend[1].frame), 65.535) * 1000.0));
+                                       MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
+                                       MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
+                               }
+                               else
+                               {
+                                       MSG_WriteByte(msg, 0);
+                                       MSG_WriteShort(msg, s->framegroupblend[0].frame);
+                                       MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[0].start, anim_frameduration(model, s->framegroupblend[0].frame), 65.535) * 1000.0));
+                               }
                        }
                }
                if (bits & E5_TRAILEFFECTNUM)
@@ -2414,7 +2443,7 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        s->framegroupblend[1].frame = 0;
                        s->framegroupblend[2].frame = 0;
                        s->framegroupblend[3].frame = 0;
-                       s->framegroupblend[0].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[0].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
                        s->framegroupblend[1].start = 0;
                        s->framegroupblend[2].start = 0;
                        s->framegroupblend[3].start = 0;
@@ -2428,8 +2457,8 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        s->framegroupblend[1].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[2].frame = 0;
                        s->framegroupblend[3].frame = 0;
-                       s->framegroupblend[0].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[1].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[0].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[1].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
                        s->framegroupblend[2].start = 0;
                        s->framegroupblend[3].start = 0;
                        s->framegroupblend[0].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
@@ -2442,9 +2471,9 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        s->framegroupblend[1].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[2].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[3].frame = 0;
-                       s->framegroupblend[0].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[1].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[2].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[0].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[1].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[2].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
                        s->framegroupblend[3].start = 0;
                        s->framegroupblend[0].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
                        s->framegroupblend[1].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
@@ -2456,10 +2485,10 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        s->framegroupblend[1].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[2].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[3].frame = MSG_ReadShort(&cl_message);
-                       s->framegroupblend[0].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[1].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[2].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[3].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[0].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[1].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[2].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[3].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
                        s->framegroupblend[0].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
                        s->framegroupblend[1].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
                        s->framegroupblend[2].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
@@ -2599,7 +2628,25 @@ static int EntityState5_DeltaBits(const entity_state_t *o, const entity_state_t
                if (o->glowmod[0] != n->glowmod[0] || o->glowmod[1] != n->glowmod[1] || o->glowmod[2] != n->glowmod[2])
                        bits |= E5_GLOWMOD;
                if (n->flags & RENDER_COMPLEXANIMATION)
-                       bits |= E5_COMPLEXANIMATION;
+               {
+                       if ((o->skeletonobject.model && o->skeletonobject.relativetransforms) != (n->skeletonobject.model && n->skeletonobject.relativetransforms))
+                       {
+                               bits |= E5_COMPLEXANIMATION;
+                       }
+                       else if (o->skeletonobject.model && o->skeletonobject.relativetransforms)
+                       {
+                               if(o->modelindex != n->modelindex)
+                                       bits |= E5_COMPLEXANIMATION;
+                               else if(o->skeletonobject.model->num_bones != n->skeletonobject.model->num_bones)
+                                       bits |= E5_COMPLEXANIMATION;
+                               else if(memcmp(o->skeletonobject.relativetransforms, n->skeletonobject.relativetransforms, o->skeletonobject.model->num_bones * sizeof(*o->skeletonobject.relativetransforms)))
+                                       bits |= E5_COMPLEXANIMATION;
+                       }
+                       else if (memcmp(o->framegroupblend, n->framegroupblend, sizeof(o->framegroupblend)))
+                       {
+                               bits |= E5_COMPLEXANIMATION;
+                       }
+               }
                if (o->traileffectnum != n->traileffectnum)
                        bits |= E5_TRAILEFFECTNUM;
        }
index 21e3436..27befbc 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -1362,6 +1362,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
                cs->framegroupblend[2].lerp = PRVM_serveredictfloat(ent, lerpfrac3);
                cs->framegroupblend[3].lerp = PRVM_serveredictfloat(ent, lerpfrac4);
                cs->framegroupblend[0].lerp = 1.0f - cs->framegroupblend[1].lerp - cs->framegroupblend[2].lerp - cs->framegroupblend[3].lerp;
+               cs->frame = 0; // don't need the legacy frame
        }
 
        cs->light[0] = light[0];