diff --git a/src/Camera.c b/src/Camera.c index 26897bc78..9bd233584 100644 --- a/src/Camera.c +++ b/src/Camera.c @@ -84,7 +84,7 @@ static void PerspectiveCamera_UpdateMouseRotation(double delta) { return; } - update.flags = LU_INCLUDES_YAW | LU_INCLUDES_PITCH; + update.flags = LU_HAS_YAW | LU_HAS_PITCH; update.yaw = e->next.yaw + rot.X; update.pitch = e->next.pitch + rot.Y; update.pitch = Math_ClampAngle(update.pitch); diff --git a/src/Chat.c b/src/Chat.c index f6f5238ff..4d5dcea30 100644 --- a/src/Chat.c +++ b/src/Chat.c @@ -631,7 +631,7 @@ static void TeleportCommand_Execute(const cc_string* args, int argsCount) { return; } - update.flags = LU_INCLUDES_POS; + update.flags = LU_HAS_POS; update.pos = v; e->VTABLE->SetLocation(e, &update); } diff --git a/src/Entity.c b/src/Entity.c index 62f5f37be..92a26f3b2 100644 --- a/src/Entity.c +++ b/src/Entity.c @@ -508,7 +508,7 @@ void Entity_LerpAngles(struct Entity* e, float t) { e->Pitch = Math_LerpAngle(prev->pitch, next->pitch, t); e->Yaw = Math_LerpAngle(prev->yaw, next->yaw, t); e->RotX = Math_LerpAngle(prev->rotX, next->rotX, t); - e->RotY = Math_LerpAngle(prev->rotY, next->rotY, t); + e->RotY = Math_LerpAngle(prev->rotY, next->rotY, t); e->RotZ = Math_LerpAngle(prev->rotZ, next->rotZ, t); } @@ -997,7 +997,7 @@ static void LocalPlayer_DoRespawn(void) { /* it's obvious to the player that they are being respawned */ spawn.Y += 2.0f/16.0f; - update.flags = LU_INCLUDES_POS | LU_INCLUDES_YAW | LU_INCLUDES_PITCH; + update.flags = LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH; update.pos = spawn; update.yaw = p->SpawnYaw; update.pitch = p->SpawnPitch; @@ -1107,7 +1107,7 @@ void LocalPlayer_MoveToSpawn(void) { struct LocalPlayer* p = &LocalPlayer_Instance; struct LocationUpdate update; - update.flags = LU_INCLUDES_POS | LU_INCLUDES_YAW | LU_INCLUDES_PITCH; + update.flags = LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH; update.pos = p->Spawn; update.yaw = p->SpawnYaw; update.pitch = p->SpawnPitch; diff --git a/src/Entity.h b/src/Entity.h index 151763ad0..485bbddcb 100644 --- a/src/Entity.h +++ b/src/Entity.h @@ -32,20 +32,26 @@ extern const char* const ShadowMode_Names[SHADOW_MODE_COUNT]; enum EntityType { ENTITY_TYPE_NONE, ENTITY_TYPE_PLAYER }; /* Which fields are included/valid in a LocationUpdate */ -#define LU_INCLUDES_POS 0x01 -#define LU_INCLUDES_PITCH 0x02 -#define LU_INCLUDES_YAW 0x04 -#define LU_INCLUDES_ROTX 0x08 -#define LU_INCLUDES_ROTZ 0x10 +#define LU_HAS_POS 0x01 +#define LU_HAS_PITCH 0x02 +#define LU_HAS_YAW 0x04 +#define LU_HAS_ROTX 0x08 +#define LU_HAS_ROTZ 0x10 -/* If set, then new position is calculated by adding current position to update->pos */ -/* If not set, then new position is just update->pos */ -#define LU_FLAG_RELATIVEPOS 0x20 -/* TODO fill this in when implemented */ -#define LU_FLAG_X 0x40 -/* If set, then linearly interpolates between current and new state */ -/* If not set, then current state is immediately updated to new state */ -#define LU_FLAG_INTERPOLATE 0x80 +/* How to move the entity when position field is included */ +#define LU_POS_MODEMASK 0x60 +/* Entity is instantly teleported to update->pos */ +#define LU_POS_ABSOLUTE_INSTANT 0x00 +/* Entity is smoothly moved to update->pos */ +#define LU_POS_ABSOLUTE_SMOOTH 0x20 +/* Entity is smoothly moved to current position + update->pos */ +#define LU_POS_RELATIVE_SMOOTH 0x40 +/* Entity is offset/shifted by update->pos */ +#define LU_POS_RELATIVE_SHIFT 0x60 + +/* If set, then linearly interpolates between current and new angles */ +/* If not set, then current angles are immediately updated to new angles */ +#define LU_ORI_INTERPOLATE 0x80 /* Represents a location update for an entity. Can be a relative position, full position, and/or an orientation update. */ struct LocationUpdate { diff --git a/src/EntityComponents.c b/src/EntityComponents.c index 76347c72e..a8d55ee87 100644 --- a/src/EntityComponents.c +++ b/src/EntityComponents.c @@ -313,61 +313,94 @@ static void InterpComp_AdvanceRotY(struct InterpComp* interp, struct Entity* e) /*########################################################################################################################* *----------------------------------------------NetworkInterpolationComponent----------------------------------------------* *#########################################################################################################################*/ -#define NetInterpState_Copy(dst, src) \ -(dst).pos = (src)->Pos;\ +#define NetInterpAngles_Copy(dst, src) \ (dst).pitch = (src)->Pitch;\ (dst).yaw = (src)->Yaw;\ (dst).rotX = (src)->RotX;\ (dst).rotZ = (src)->RotZ; -static void NetInterpComp_RemoveOldestState(struct NetInterpComp* interp) { +static void NetInterpComp_RemoveOldestPosition(struct NetInterpComp* interp) { int i; - for (i = 0; i < Array_Elems(interp->States); i++) { - interp->States[i] = interp->States[i + 1]; + interp->PositionsCount--; + + for (i = 0; i < interp->PositionsCount; i++) { + interp->Positions[i] = interp->Positions[i + 1]; } - interp->StatesCount--; } -static void NetInterpComp_AddState(struct NetInterpComp* interp, struct NetInterpState state) { - if (interp->StatesCount == Array_Elems(interp->States)) { - NetInterpComp_RemoveOldestState(interp); +static void NetInterpComp_AddPosition(struct NetInterpComp* interp, Vec3 pos) { + if (interp->PositionsCount == Array_Elems(interp->Positions)) { + NetInterpComp_RemoveOldestPosition(interp); } - interp->States[interp->StatesCount] = state; interp->StatesCount++; + interp->Positions[interp->PositionsCount++] = pos; +} + +static void NetInterpComp_SetPosition(struct NetInterpComp* interp, struct LocationUpdate* update, struct Entity* e, int mode) { + Vec3 lastPos = interp->CurPos; + Vec3* curPos = &interp->CurPos; + Vec3 midPos; + + if (mode == LU_POS_ABSOLUTE_INSTANT || mode == LU_POS_ABSOLUTE_SMOOTH) { + *curPos = update->pos; + } else { + Vec3_AddBy(curPos, &update->pos); + } + + if (mode == LU_POS_ABSOLUTE_INSTANT) { + e->prev.pos = *curPos; + e->next.pos = *curPos; + interp->PositionsCount = 0; + } else { + /* Smoother interpolation by also adding midpoint */ + Vec3_Lerp(&midPos, &lastPos, curPos, 0.5f); + NetInterpComp_AddPosition(interp, midPos); + NetInterpComp_AddPosition(interp, *curPos); + } +} + +static void NetInterpComp_RemoveOldestAngles(struct NetInterpComp* interp) { + int i; + interp->AnglesCount--; + + for (i = 0; i < interp->AnglesCount; i++) { + interp->Angles[i] = interp->Angles[i + 1]; + } +} + +static void NetInterpComp_AddAngles(struct NetInterpComp* interp, struct NetInterpAngles angles) { + if (interp->AnglesCount == Array_Elems(interp->Angles)) { + NetInterpComp_RemoveOldestAngles(interp); + } + interp->Angles[interp->AnglesCount++] = angles; } void NetInterpComp_SetLocation(struct NetInterpComp* interp, struct LocationUpdate* update, struct Entity* e) { - struct NetInterpState last = interp->Cur; - struct NetInterpState* cur = &interp->Cur; - struct NetInterpState mid; + struct NetInterpAngles last = interp->CurAngles; + struct NetInterpAngles* cur = &interp->CurAngles; + struct NetInterpAngles mid; cc_uint8 flags = update->flags; - cc_bool interpolate = flags & LU_FLAG_INTERPOLATE; + cc_bool interpolate = flags & LU_ORI_INTERPOLATE; - if (flags & LU_INCLUDES_POS) { - if (flags & LU_FLAG_RELATIVEPOS) { - Vec3_AddBy(&cur->Pos, &update->pos); - } else { - cur->Pos = update->pos; - } + if (flags & LU_HAS_POS) { + NetInterpComp_SetPosition(interp, update, e, flags & LU_POS_MODEMASK); } - - if (flags & LU_INCLUDES_ROTX) cur->RotX = Math_ClampAngle(update->rotX); - if (flags & LU_INCLUDES_ROTZ) cur->RotZ = Math_ClampAngle(update->rotZ); - if (flags & LU_INCLUDES_PITCH) cur->Pitch = Math_ClampAngle(update->pitch); - if (flags & LU_INCLUDES_YAW) cur->Yaw = Math_ClampAngle(update->yaw); + if (flags & LU_HAS_ROTX) cur->RotX = Math_ClampAngle(update->rotX); + if (flags & LU_HAS_ROTZ) cur->RotZ = Math_ClampAngle(update->rotZ); + if (flags & LU_HAS_PITCH) cur->Pitch = Math_ClampAngle(update->pitch); + if (flags & LU_HAS_YAW) cur->Yaw = Math_ClampAngle(update->yaw); if (!interpolate) { - NetInterpState_Copy(e->prev, cur); e->prev.rotY = cur->Yaw; - NetInterpState_Copy(e->next, cur); e->next.rotY = cur->Yaw; - interp->RotYCount = 0; interp->StatesCount = 0; + NetInterpAngles_Copy(e->prev, cur); e->prev.rotY = cur->Yaw; + NetInterpAngles_Copy(e->next, cur); e->next.rotY = cur->Yaw; + interp->RotYCount = 0; interp->AnglesCount = 0; } else { - /* Smoother interpolation by also adding midpoint. */ - Vec3_Lerp(&mid.Pos, &last.Pos, &cur->Pos, 0.5f); + /* Smoother interpolation by also adding midpoint */ mid.RotX = Math_LerpAngle(last.RotX, cur->RotX, 0.5f); mid.RotZ = Math_LerpAngle(last.RotZ, cur->RotZ, 0.5f); mid.Pitch = Math_LerpAngle(last.Pitch, cur->Pitch, 0.5f); mid.Yaw = Math_LerpAngle(last.Yaw, cur->Yaw, 0.5f); - NetInterpComp_AddState(interp, mid); - NetInterpComp_AddState(interp, *cur); + NetInterpComp_AddAngles(interp, mid); + NetInterpComp_AddAngles(interp, *cur); /* Body rotation lags behind head a tiny bit */ InterpComp_AddRotY((struct InterpComp*)interp, Math_LerpAngle(last.Yaw, cur->Yaw, 0.33333333f)); @@ -380,9 +413,13 @@ void NetInterpComp_AdvanceState(struct NetInterpComp* interp, struct Entity* e) e->prev = e->next; e->Position = e->prev.pos; - if (interp->StatesCount > 0) { - NetInterpState_Copy(e->next, &interp->States[0]); - NetInterpComp_RemoveOldestState(interp); + if (interp->PositionsCount) { + e->next.pos = interp->Positions[0]; + NetInterpComp_RemoveOldestPosition(interp); + } + if (interp->AnglesCount) { + NetInterpAngles_Copy(e->next, &interp->Angles[0]); + NetInterpComp_RemoveOldestAngles(interp); } InterpComp_AdvanceRotY((struct InterpComp*)interp, e); } @@ -391,6 +428,28 @@ void NetInterpComp_AdvanceState(struct NetInterpComp* interp, struct Entity* e) /*########################################################################################################################* *-----------------------------------------------LocalInterpolationComponent-----------------------------------------------* *#########################################################################################################################*/ +static void LocalInterpComp_SetPosition(struct LocationUpdate* update, int mode) { + struct Entity* e = &LocalPlayer_Instance.Base; + float yOffset; + + if (mode == LU_POS_ABSOLUTE_INSTANT || mode == LU_POS_ABSOLUTE_SMOOTH) { + e->next.pos = update->pos; + } else if (mode == LU_POS_RELATIVE_SMOOTH) { + Vec3_AddBy(&e->next.pos, &update->pos); + } else if (mode == LU_POS_RELATIVE_SHIFT) { + Vec3_AddBy(&e->prev.pos, &update->pos); + Vec3_AddBy(&e->next.pos, &update->pos); + } + + /* If server sets Y position exactly on ground, push up a tiny bit */ + yOffset = e->next.pos.Y - Math_Floor(e->next.pos.Y); + if (yOffset < ENTITY_ADJUSTMENT) e->next.pos.Y += ENTITY_ADJUSTMENT; + + if (mode == LU_POS_ABSOLUTE_INSTANT) { + e->prev.pos = e->next.pos; e->Position = e->next.pos; + } +} + static void LocalInterpComp_Angle(float* prev, float* next, float value, cc_bool interpolate) { value = Math_ClampAngle(value); *next = value; @@ -402,33 +461,22 @@ void LocalInterpComp_SetLocation(struct InterpComp* interp, struct LocationUpdat struct EntityLocation* prev = &e->prev; struct EntityLocation* next = &e->next; cc_uint8 flags = update->flags; - cc_bool interpolate = flags & LU_FLAG_INTERPOLATE; + cc_bool interpolate = flags & LU_ORI_INTERPOLATE; float yOffset; - if (flags & LU_INCLUDES_POS) { - if (flags & LU_FLAG_RELATIVEPOS) { - Vec3_AddBy(&next->pos, &update->pos); - } else { - next->pos = update->pos; - } - - /* If server sets Y position exactly on ground, push up a tiny bit */ - yOffset = next->pos.Y - Math_Floor(next->pos.Y); - - if (yOffset < ENTITY_ADJUSTMENT) next->pos.Y += ENTITY_ADJUSTMENT; - if (!interpolate) { prev->pos = next->pos; e->Position = next->pos; } + if (flags & LU_HAS_POS) { + LocalInterpComp_SetPosition(update, flags & LU_POS_MODEMASK); } - - if (flags & LU_INCLUDES_PITCH) { + if (flags & LU_HAS_PITCH) { LocalInterpComp_Angle(&prev->pitch, &next->pitch, update->pitch, interpolate); } - if (flags & LU_INCLUDES_ROTX) { + if (flags & LU_HAS_ROTX) { LocalInterpComp_Angle(&prev->rotX, &next->rotX, update->rotX, interpolate); } - if (flags & LU_INCLUDES_ROTZ) { + if (flags & LU_HAS_ROTZ) { LocalInterpComp_Angle(&prev->rotZ, &next->rotZ, update->rotZ, interpolate); } - if (flags & LU_INCLUDES_YAW) { + if (flags & LU_HAS_YAW) { LocalInterpComp_Angle(&prev->yaw, &next->yaw, update->yaw, interpolate); if (!interpolate) { diff --git a/src/EntityComponents.h b/src/EntityComponents.h index a2512a564..6f5532b2c 100644 --- a/src/EntityComponents.h +++ b/src/EntityComponents.h @@ -84,16 +84,17 @@ struct InterpComp { InterpComp_Layout }; void LocalInterpComp_SetLocation(struct InterpComp* interp, struct LocationUpdate* update); void LocalInterpComp_AdvanceState(struct InterpComp* interp, struct Entity* e); -/* Represents a network position and orientation state */ -struct NetInterpState { Vec3 Pos; float Pitch, Yaw, RotX, RotZ; }; +/* Represents a network orientation state */ +struct NetInterpAngles { float Pitch, Yaw, RotX, RotZ; }; /* Entity component that performs interpolation for network players */ struct NetInterpComp { InterpComp_Layout /* Last known position and orientation sent by the server */ - struct NetInterpState Cur; - int StatesCount; - struct NetInterpState States[10]; + Vec3 CurPos; struct NetInterpAngles CurAngles; + /* Interpolated position and orientation state */ + int PositionsCount, AnglesCount; + Vec3 Positions[10]; struct NetInterpAngles Angles[10]; }; void NetInterpComp_SetLocation(struct NetInterpComp* interp, struct LocationUpdate* update, struct Entity* e); diff --git a/src/Input.c b/src/Input.c index 01b727cde..6edbb5dbe 100644 --- a/src/Input.c +++ b/src/Input.c @@ -652,7 +652,7 @@ static cc_bool PushbackPlace(struct AABB* blockBB) { return false; } - update.flags = LU_INCLUDES_POS; + update.flags = LU_HAS_POS | LU_POS_ABSOLUTE_INSTANT; update.pos = pos; p->VTABLE->SetLocation(p, &update); return true; @@ -711,7 +711,7 @@ static cc_bool CheckIsFree(BlockID block) { /* Push player upwards when they are jumping and trying to place a block underneath them */ nextPos.Y = pos.Y + Blocks.MaxBB[block].Y + ENTITY_ADJUSTMENT; - update.flags = LU_INCLUDES_POS; + update.flags = LU_HAS_POS | LU_POS_ABSOLUTE_INSTANT; update.pos = nextPos; p->VTABLE->SetLocation(p, &update); return true; diff --git a/src/Protocol.c b/src/Protocol.c index d9247570c..054396006 100644 --- a/src/Protocol.c +++ b/src/Protocol.c @@ -151,7 +151,8 @@ static void AddEntity(cc_uint8* data, EntityID id, const cc_string* name, const Entity_SetName(e, name); if (!readPosition) return; - Classic_ReadAbsoluteLocation(data, id, 0); + Classic_ReadAbsoluteLocation(data, id, + LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH | LU_POS_ABSOLUTE_INSTANT); if (id != ENTITIES_SELF_ID) return; p->Spawn = p->Base.Position; @@ -600,14 +601,15 @@ static void Classic_AddEntity(cc_uint8* data) { static void Classic_EntityTeleport(cc_uint8* data) { EntityID id = *data++; - Classic_ReadAbsoluteLocation(data, id, LU_FLAG_INTERPOLATE); + Classic_ReadAbsoluteLocation(data, id, + LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH | LU_ORI_INTERPOLATE); } static void Classic_RelPosAndOrientationUpdate(cc_uint8* data) { struct LocationUpdate update; EntityID id = data[0]; - update.flags = LU_INCLUDES_POS | LU_INCLUDES_YAW | LU_INCLUDES_PITCH | LU_FLAG_RELATIVEPOS | LU_FLAG_INTERPOLATE; + update.flags = LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH | LU_POS_RELATIVE_SMOOTH | LU_ORI_INTERPOLATE; update.pos.X = (cc_int8)data[1] / 32.0f; update.pos.Y = (cc_int8)data[2] / 32.0f; update.pos.Z = (cc_int8)data[3] / 32.0f; @@ -620,7 +622,7 @@ static void Classic_RelPositionUpdate(cc_uint8* data) { struct LocationUpdate update; EntityID id = data[0]; - update.flags = LU_INCLUDES_POS | LU_FLAG_RELATIVEPOS | LU_FLAG_INTERPOLATE; + update.flags = LU_HAS_POS | LU_POS_RELATIVE_SMOOTH | LU_ORI_INTERPOLATE; update.pos.X = (cc_int8)data[1] / 32.0f; update.pos.Y = (cc_int8)data[2] / 32.0f; update.pos.Z = (cc_int8)data[3] / 32.0f; @@ -631,7 +633,7 @@ static void Classic_OrientationUpdate(cc_uint8* data) { struct LocationUpdate update; EntityID id = data[0]; - update.flags = LU_INCLUDES_YAW | LU_INCLUDES_PITCH| LU_FLAG_INTERPOLATE; + update.flags = LU_HAS_YAW | LU_HAS_PITCH | LU_ORI_INTERPOLATE; update.yaw = Math_Packed2Deg(data[1]); update.pitch = Math_Packed2Deg(data[2]); UpdateLocation(id, &update); @@ -701,7 +703,7 @@ static void Classic_ReadAbsoluteLocation(cc_uint8* data, EntityID id, cc_uint8 f /* so to simplify things, just always add 22 to Y*/ if (id == ENTITIES_SELF_ID) y += 22; - update.flags = LU_INCLUDES_POS | LU_INCLUDES_PITCH | LU_INCLUDES_YAW | flags; + update.flags = flags; update.pos.X = x/32.0f; update.pos.Y = y/32.0f; update.pos.Z = z/32.0f; @@ -1281,13 +1283,13 @@ static void CPE_SetEntityProperty(cc_uint8* data) { switch (type) { case 0: - update.flags = LU_INCLUDES_ROTX | LU_FLAG_INTERPOLATE; + update.flags = LU_HAS_ROTX | LU_ORI_INTERPOLATE; update.rotX = (float)value; break; case 1: - update.flags = LU_INCLUDES_YAW | LU_FLAG_INTERPOLATE; + update.flags = LU_HAS_YAW | LU_ORI_INTERPOLATE; update.yaw = (float)value; break; case 2: - update.flags = LU_INCLUDES_ROTZ | LU_FLAG_INTERPOLATE; + update.flags = LU_HAS_ROTZ | LU_ORI_INTERPOLATE; update.rotZ = (float)value; break; case 3: