From a3ff24873b746b6b98ae5cae435a1dcc2fb98070 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Fri, 3 Nov 2023 12:52:37 +0100 Subject: [PATCH] camera: fix Camera_Update Resolves #4. The bug was introduced by replacing a bit-shift operation with an integer division. I thought these operations are interchangeable, but turns out they're not: (-1) / 2 = 0 (-1) >> 1 = -1 --- docs/progress.txt | 26 +++++++++++------------ src/game/camera.c | 54 ++++++++++++++++++++++++----------------------- 2 files changed, 41 insertions(+), 39 deletions(-) diff --git a/docs/progress.txt b/docs/progress.txt index d128fbf6..0ef627ca 100644 --- a/docs/progress.txt +++ b/docs/progress.txt @@ -1282,7 +1282,7 @@ typedef enum LARA_MESH { 00410650 00000372 + void __cdecl Camera_Move(const struct GAME_VECTOR *target, int32_t speed); 004109D0 000000D7 + void __cdecl Camera_Clip(int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y, int32_t target_h, int32_t left, int32_t top, int32_t right, int32_t bottom); 00410AB0 00000154 + void __cdecl Camera_Shift(int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y, int32_t target_h, int32_t left, int32_t top, int32_t right, int32_t bottom); -00410C10 00000050 + const struct FLOOR_INFO __cdecl *Camera_GoodPosition(int32_t x, int32_t y, int32_t z, int16_t room_num); +00410C10 00000050 + const struct FLOOR_INFO *__cdecl Camera_GoodPosition(int32_t x, int32_t y, int32_t z, int16_t room_num); 00410C60 00000781 + void __cdecl Camera_SmartShift(struct GAME_VECTOR *target, void (*__cdecl shift)(int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y, int32_t target_h, int32_t left, int32_t top, int32_t right, int32_t bottom)); 004113F0 000000ED + void __cdecl Camera_Chase(const struct ITEM_INFO *item); 004114E0 0000019E + int32_t __cdecl Camera_ShiftClamp(struct GAME_VECTOR *pos, int32_t clamp); @@ -1331,7 +1331,7 @@ typedef enum LARA_MESH { 004146F0 00000338 - void __cdecl Item_Animate(struct ITEM_INFO *item); 00414A60 000000AB - int32_t __cdecl Item_GetAnimChange(struct ITEM_INFO *item, struct ANIM_STRUCT *anim); 00414B10 0000005F - void __cdecl Item_Translate(struct ITEM_INFO *item, int32_t x, int32_t y, int32_t z); -00414B70 00000198 * struct FLOOR_INFO __cdecl *Room_GetFloor(int32_t x, int32_t y, int32_t z, int16_t *room_num); +00414B70 00000198 * struct FLOOR_INFO *__cdecl Room_GetFloor(int32_t x, int32_t y, int32_t z, int16_t *room_num); 00414D10 00000168 - int32_t __cdecl Room_GetWaterHeight(int32_t x, int32_t y, int32_t z, int16_t room_num); 00414E80 00000265 * int32_t __cdecl Room_GetHeight(const struct FLOOR_INFO *floor, int32_t x, int32_t y, int32_t z); 00415100 000000E7 - void __cdecl Camera_RefreshFromTrigger(int16_t type, int16_t *data); @@ -2452,11 +2452,11 @@ typedef enum LARA_MESH { # Offset Flags Declaration 00464060 - int32_t g_PerspectiveDistance; -00464068 - void (__cdecl *g_PolyDrawRoutines[9])(const int16_t *); +00464068 - void (*__cdecl g_PolyDrawRoutines[9])(const int16_t *); 0046408C - float g_RhwFactor; 004640BC - int16_t g_CD_TrackID; 004640C4 - int32_t g_FlipEffect; -004641F8 - void (__cdecl *g_EffectRoutines[32])(struct ITEM_INFO *item); +004641F8 - void (*__cdecl g_EffectRoutines[32])(struct ITEM_INFO *item); 00465A60 - int16_t g_OptionMusicVolume; 00465AD4 - int32_t g_JumpPermitted = 1; 00465AD8 - int16_t g_LaraOldSlideAngle = 1; @@ -2473,12 +2473,12 @@ typedef enum LARA_MESH { 0047031C - float g_FltWinBottom; 00470320 - float g_FltResZBuf; 00470324 - float g_FltResZ; -00470328 - void (__cdecl *g_Output_InsertTransQuad)(int32_t x, int32_t y, int32_t width, int32_t height, int32_t z); +00470328 - void (*__cdecl g_Output_InsertTransQuad)(int32_t x, int32_t y, int32_t width, int32_t height, int32_t z); 0047032C - int32_t g_PhdWinHeight; 00470330 - int32_t g_PhdWinCenterX; 00470334 - int32_t g_PhdWinCenterY; 00470338 - int16_t g_LsYaw; -0047033C - void (__cdecl *g_Output_InsertTrans8)(const struct PHD_VBUF *vbuf, int16_t shade); +0047033C - void (*__cdecl g_Output_InsertTrans8)(const struct PHD_VBUF *vbuf, int16_t shade); 00470340 - float g_FltWinTop; 00470348 - struct SORT_ITEM g_SortBuffer[4000]; 00478048 - float g_FltWinLeft; @@ -2488,7 +2488,7 @@ typedef enum LARA_MESH { 00478060 - int32_t g_PhdWinBottom; 00478064 - int32_t g_PhdPersp; 00478068 - int32_t g_PhdWinLeft; -0047806C - void (__cdecl *g_Output_InsertFlatRect)(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t z, uint8_t color_idx); +0047806C - void (*__cdecl g_Output_InsertFlatRect)(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t z, uint8_t color_idx); 00478070 - int16_t g_Info3DBuffer[120000]; 004B29F0 - int32_t g_PhdWinMaxX; 004B29F4 - int32_t g_PhdNearZ; @@ -2502,23 +2502,23 @@ typedef enum LARA_MESH { 004B2A14 - float g_FltPerspONearZ; 004B2A18 - float g_FltRhwONearZ; 004B2A1C - int32_t g_PhdWinMaxY; -004B2A20 - void (__cdecl *g_Output_InsertSprite)(int32_t z, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t sprite_idx, int16_t shade); +004B2A20 - void (*__cdecl g_Output_InsertSprite)(int32_t z, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t sprite_idx, int16_t shade); 004B2A24 - float g_FltNearZ; 004B2A28 - struct MATRIX *g_MatrixPtr; -004B2A2C - const int16_t *(__cdecl *g_Output_DrawObjectGT3)(const int16_t*, int32_t, enum SORT_TYPE); -004B2A30 - const int16_t *(__cdecl *g_Output_DrawObjectGT4)(const int16_t*, int32_t, enum SORT_TYPE); +004B2A2C - const int16_t *(*__cdecl g_Output_DrawObjectGT3)(const int16_t*, int32_t, enum SORT_TYPE); +004B2A30 - const int16_t *(*__cdecl g_Output_DrawObjectGT4)(const int16_t*, int32_t, enum SORT_TYPE); 004B2A38 - int32_t g_RandomTable[32]; 004B2AB8 - float g_FltPersp; 004B2AC0 - struct MATRIX g_W2VMatrix; 004B2AF0 - int16_t *g_Info3DPtr; 004B2AF4 - int32_t g_PhdWinWidth; -004B2AF8 - void (__cdecl *g_Output_DrawLine)(int32_t, int32_t, int32_t, int32_t, int32_t, uint8_t); +004B2AF8 - void (*__cdecl g_Output_DrawLine)(int32_t, int32_t, int32_t, int32_t, int32_t, uint8_t); 004B2B00 - struct PHD_TEXTURE g_PhdTextureInfo[0x800]; 004BCB00 - int32_t g_PhdViewDistance; 004BCB04 - int16_t g_LsPitch; -004BCB08 - const int16_t *(__cdecl *g_Output_DrawObjectG4)(const int16_t*,int32_t, enum SORT_TYPE); +004BCB08 - const int16_t *(*__cdecl g_Output_DrawObjectG4)(const int16_t*,int32_t, enum SORT_TYPE); 004BCB10 - int16_t g_ShadesTable[32]; -004BCB50 - const int16_t *(__cdecl *g_Output_DrawObjectG3)(const int16_t*,int32_t, enum SORT_TYPE); +004BCB50 - const int16_t *(*__cdecl g_Output_DrawObjectG3)(const int16_t*,int32_t, enum SORT_TYPE); 004BCB58 - struct MATRIX g_MatrixStack[]; 004BD2D8 - struct DEPTHQ_ENTRY g_DepthQTable[32]; 004BF3D8 - int32_t g_PhdScreenWidth; diff --git a/src/game/camera.c b/src/game/camera.c index 70396940..2b224394 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -499,7 +499,8 @@ int32_t __cdecl Camera_ShiftClamp(struct GAME_VECTOR *pos, int32_t clamp) int32_t height = Room_GetHeight(floor, x, y, z) - clamp; int32_t ceiling = Room_GetCeiling(floor, x, y, z) + clamp; if (height < ceiling) { - ceiling = height = (height + ceiling) / 2; + ceiling = (height + ceiling) >> 1; + height = ceiling; } if (y > height) { @@ -669,9 +670,10 @@ void __cdecl Camera_Update(void) g_IsChunkyCamera = 1; } - int32_t fixed_camera = g_Camera.item + const int32_t fixed_camera = g_Camera.item != NULL && (g_Camera.type == CAM_FIXED || g_Camera.type == CAM_HEAVY); - const struct ITEM_INFO *item = fixed_camera ? g_Camera.item : g_LaraItem; + const struct ITEM_INFO *const item = + fixed_camera ? g_Camera.item : g_LaraItem; const int16_t *bounds = Item_GetBoundsAccurate(item); @@ -680,45 +682,45 @@ void __cdecl Camera_Update(void) y += (bounds[FBBOX_MIN_Y] + bounds[FBBOX_MAX_Y]) / 2; } else { y += bounds[FBBOX_MAX_Y] - + ((bounds[FBBOX_MIN_Y] - bounds[FBBOX_MAX_Y]) * 3 / 4); + + (((int32_t)(bounds[FBBOX_MIN_Y] - bounds[FBBOX_MAX_Y])) * 3 >> 2); } if (g_Camera.item && !fixed_camera) { bounds = Item_GetBoundsAccurate(g_Camera.item); - int32_t dz = g_Camera.item->pos.z - item->pos.z; - int32_t dx = g_Camera.item->pos.x - item->pos.x; - int32_t shift = Math_Sqrt(SQUARE(dx) + SQUARE(dz)); + const int32_t dx = g_Camera.item->pos.x - item->pos.x; + const int32_t dz = g_Camera.item->pos.z - item->pos.z; + const int32_t shift = Math_Sqrt(SQUARE(dx) + SQUARE(dz)); int16_t angle = Math_Atan(dz, dx) - item->pos.y_rot; + int16_t tilt = Math_Atan( shift, y - (bounds[FBBOX_MIN_Y] + bounds[FBBOX_MAX_Y]) / 2 - g_Camera.item->pos.y); - tilt /= 2; - angle /= 2; + angle >>= 1; + tilt >>= 1; if (angle > MIN_HEAD_ROTATION && angle < MAX_HEAD_ROTATION && tilt > MIN_HEAD_TILT_CAM && tilt < MAX_HEAD_TILT_CAM) { int16_t change = angle - g_Lara.head_y_rot; if (change > HEAD_TURN) { g_Lara.head_y_rot += HEAD_TURN; - } else if (change >= -HEAD_TURN) { - g_Lara.head_y_rot = angle; - } else { + } else if (change < -HEAD_TURN) { g_Lara.head_y_rot -= HEAD_TURN; + } else { + g_Lara.head_y_rot = angle; } change = tilt - g_Lara.head_x_rot; if (change > HEAD_TURN) { - g_Lara.head_x_rot = g_Lara.head_x_rot + HEAD_TURN; - } else if (change >= -HEAD_TURN) { - g_Lara.head_x_rot = change + g_Lara.head_x_rot; + g_Lara.head_x_rot += HEAD_TURN; + } else if (change < -HEAD_TURN) { + g_Lara.head_x_rot -= HEAD_TURN; } else { - g_Lara.head_x_rot = g_Lara.head_x_rot - HEAD_TURN; + g_Lara.head_x_rot += change; } - - g_Lara.torso_y_rot = g_Lara.head_y_rot; g_Lara.torso_x_rot = g_Lara.head_x_rot; + g_Lara.torso_y_rot = g_Lara.head_y_rot; g_Camera.type = CAM_LOOK; g_Camera.item->looked_at = 1; } @@ -726,17 +728,15 @@ void __cdecl Camera_Update(void) if (g_Camera.type == CAM_LOOK || g_Camera.type == CAM_COMBAT) { y -= STEP_L; - g_Camera.target.room_num = item->room_num; if (g_Camera.fixed_camera) { g_Camera.target.y = y; g_Camera.speed = 1; } else { - g_Camera.target.y += (y - g_Camera.target.y) / 4; + g_Camera.target.y += (y - g_Camera.target.y) >> 2; g_Camera.speed = g_Camera.type == CAM_LOOK ? LOOK_SPEED : COMBAT_SPEED; } - g_Camera.fixed_camera = 0; if (g_Camera.type == CAM_LOOK) { Camera_Look(item); @@ -746,27 +746,29 @@ void __cdecl Camera_Update(void) } else { g_Camera.target.x = item->pos.x; g_Camera.target.z = item->pos.z; + if (g_Camera.flags == CF_FOLLOW_CENTRE) { - int32_t shift = (bounds[FBBOX_MIN_Z] + bounds[FBBOX_MAX_Z]) / 2; + const int32_t shift = + (bounds[FBBOX_MIN_Z] + bounds[FBBOX_MAX_Z]) / 2; g_Camera.target.z += (shift * Math_Cos(item->pos.y_rot)) >> W2V_SHIFT; g_Camera.target.x += (shift * Math_Sin(item->pos.y_rot)) >> W2V_SHIFT; } - g_Camera.target.room_num = item->room_num; + g_Camera.target.room_num = item->room_num; if (g_Camera.fixed_camera != fixed_camera) { g_Camera.target.y = y; g_Camera.fixed_camera = 1; g_Camera.speed = 1; } else { - g_Camera.target.y += (y - g_Camera.target.y) / 4; g_Camera.fixed_camera = 0; + g_Camera.target.y += (y - g_Camera.target.y) / 4; } - const struct FLOOR_INFO *floor = Room_GetFloor( + const struct FLOOR_INFO *const floor = Room_GetFloor( g_Camera.target.x, y, g_Camera.target.z, &g_Camera.target.room_num); - int32_t height = Room_GetHeight( + const int32_t height = Room_GetHeight( floor, g_Camera.target.x, g_Camera.target.y, g_Camera.target.z); if (g_Camera.target.y > height) { g_IsChunkyCamera = 0;