Skip to content

Commit

Permalink
tr2/objects/pickup: add 3d mesh drawing
Browse files Browse the repository at this point in the history
Resolves #1634.
  • Loading branch information
rr- committed Nov 5, 2024
1 parent 4795f7e commit 9cf197c
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 84 deletions.
1 change: 1 addition & 0 deletions docs/tr2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- added the ability to skip FMVs with the action key (#1650)
- added the ability to hold forward/back to move through menus more quickly (#1644)
- added optional rendering of pickups in the UI as 3D meshes (#1633)
- added optional rendering of pickups on the ground as 3D meshes (#1634)
- changed the inputs backend from DirectX to SDL (#1695)
- improved controller support to match TR1X
- changed the number of custom layouts to 3
Expand Down
83 changes: 83 additions & 0 deletions src/tr2/game/objects/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

#include "game/items.h"
#include "game/lara/misc.h"
#include "game/matrix.h"
#include "global/funcs.h"
#include "global/vars.h"

#include <libtrx/utils.h>

OBJECT *Object_GetObject(GAME_OBJECT_ID object_id)
{
return &g_Objects[object_id];
Expand Down Expand Up @@ -45,3 +48,83 @@ void __cdecl Object_Collision_Trap(
Object_Collision(item_num, lara_item, coll);
}
}

BOUNDS_16 Object_GetBoundingBox(
const OBJECT *const obj, const FRAME_INFO *const frame)
{
int16_t **mesh_ptrs = &g_Meshes[obj->mesh_idx];
int32_t *bone = &g_AnimBones[obj->bone_idx];
const int16_t *mesh_rots = frame->mesh_rots;

Matrix_PushUnit();
Matrix_TranslateRel(frame->offset.x, frame->offset.y, frame->offset.z);
Matrix_RotYXZsuperpack(&mesh_rots, 0);

BOUNDS_16 new_bounds = {
.min_x = 0x7FFF,
.min_y = 0x7FFF,
.min_z = 0x7FFF,
.max_x = -0x7FFF,
.max_y = -0x7FFF,
.max_z = -0x7FFF,
};

for (int32_t mesh_idx = 0; mesh_idx < obj->mesh_count; mesh_idx++) {
if (mesh_idx != 0) {
int32_t bone_extra_flags = *bone;
if (bone_extra_flags & BF_MATRIX_POP) {
Matrix_Pop();
}

if (bone_extra_flags & BF_MATRIX_PUSH) {
Matrix_Push();
}

Matrix_TranslateRel(bone[1], bone[2], bone[3]);
Matrix_RotYXZsuperpack(&mesh_rots, 0);
bone += 4;
}

const int16_t *obj_ptr = mesh_ptrs[mesh_idx];
obj_ptr += 5;
const int32_t vtx_count = *obj_ptr++;
for (int32_t i = 0; i < vtx_count; i++) {
// clang-format off
const MATRIX *const mptr = g_MatrixPtr;
const double xv = (
mptr->_00 * obj_ptr[0] +
mptr->_01 * obj_ptr[1] +
mptr->_02 * obj_ptr[2] +
mptr->_03
);
const double yv = (
mptr->_10 * obj_ptr[0] +
mptr->_11 * obj_ptr[1] +
mptr->_12 * obj_ptr[2] +
mptr->_13
);
double zv = (
mptr->_20 * obj_ptr[0] +
mptr->_21 * obj_ptr[1] +
mptr->_22 * obj_ptr[2] +
mptr->_23
);
// clang-format on

const int32_t x = ((int32_t)xv) >> W2V_SHIFT;
const int32_t y = ((int32_t)yv) >> W2V_SHIFT;
const int32_t z = ((int32_t)zv) >> W2V_SHIFT;

new_bounds.min_x = MIN(new_bounds.min_x, x);
new_bounds.min_y = MIN(new_bounds.min_y, y);
new_bounds.min_z = MIN(new_bounds.min_z, z);
new_bounds.max_x = MAX(new_bounds.max_x, x);
new_bounds.max_y = MAX(new_bounds.max_y, y);
new_bounds.max_z = MAX(new_bounds.max_z, z);
obj_ptr += 3;
}
}

Matrix_Pop();
return new_bounds;
}
2 changes: 2 additions & 0 deletions src/tr2/game/objects/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ void __cdecl Object_Collision(

void __cdecl Object_Collision_Trap(
int16_t item_num, ITEM *lara_item, COLL_INFO *coll);

BOUNDS_16 Object_GetBoundingBox(const OBJECT *obj, const FRAME_INFO *frame);
104 changes: 103 additions & 1 deletion src/tr2/game/objects/general/pickup.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
#include "game/objects/general/pickup.h"

#include "config.h"
#include "game/gameflow.h"
#include "game/gun/gun.h"
#include "game/input.h"
#include "game/inventory/backpack.h"
#include "game/inventory/common.h"
#include "game/items.h"
#include "game/lara/control.h"
#include "game/lara/misc.h"
#include "game/matrix.h"
#include "game/objects/common.h"
#include "game/output.h"
#include "game/overlay.h"
#include "game/room.h"
#include "global/funcs.h"
#include "global/vars.h"

Expand Down Expand Up @@ -179,11 +185,107 @@ void Pickup_Setup(OBJECT *const obj)
// TODO: change this to Pickup_Collision after we decompile
// both comparisons in ExtractSaveGameInfo() and GetCarriedItems()
obj->collision = (void *)0x00437E70;
obj->draw_routine = Object_DrawSpriteItem;
obj->draw_routine = Pickup_Draw;
obj->save_position = 1;
obj->save_flags = 1;
}

void Pickup_Draw(const ITEM *const item)
{
if (!g_Config.visuals.enable_3d_pickups) {
Object_DrawSpriteItem(item);
return;
}

if (!g_Objects[item->object_id].loaded) {
Object_DrawSpriteItem(item);
return;
}

// Convert item to menu display item.
const GAME_OBJECT_ID inv_object_id = Inv_GetItemOption(item->object_id);
const OBJECT *const obj = Object_GetObject(inv_object_id);
if (!obj->loaded || obj->mesh_count < 0) {
Object_DrawSpriteItem(item);
return;
}

// Get the first frame of the first animation, and its bounding box.
const FRAME_INFO *frame = (const FRAME_INFO *)obj->frame_base;
const BOUNDS_16 bounds = Object_GetBoundingBox(obj, frame);

// First - Is there floor under the item?
// This is mostly true, but for example the 4 items in the Obelisk of
// Khamoon the 4 items are sitting on top of a static mesh which is not
// floor.
int16_t room_num = item->room_num;
const SECTOR *const sector =
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
const int16_t floor_height =
Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z);

int16_t offset;
if (item->pos.y == floor_height) {
// Is the floor "just below" the item? Take the position from the anim.
offset =
floor_height - frame->offset.y - (bounds.max_y - bounds.min_y) / 2;
} else {
// Otherwise leave it as-is.
offset = item->pos.y;
}

Matrix_Push();
Matrix_TranslateAbs(item->pos.x, offset, item->pos.z);
Matrix_RotYXZ(item->rot.y, item->rot.x, item->rot.z);

S_CalculateLight(item->pos.x, item->pos.y, item->pos.z, item->room_num);

const int32_t clip = S_GetObjectBounds(&frame->bounds);
if (clip) {
// From this point on the function is a slightly customised version
// of the code in DrawAnimatingItem starting with the line that
// matches the following line.
int32_t bit = 1;
int16_t **meshpp = &g_Meshes[obj->mesh_idx];
int32_t *bone = &g_AnimBones[obj->bone_idx];

Matrix_TranslateRel(frame->offset.x, frame->offset.y, frame->offset.z);

const int16_t *mesh_rots = frame->mesh_rots;
Matrix_RotYXZsuperpack(&mesh_rots, 0);

if (item->mesh_bits & bit) {
Output_InsertPolygons(*meshpp++, clip);
}

for (int i = 1; i < obj->mesh_count; i++) {
int32_t bone_extra_flags = *bone;
if (bone_extra_flags & BF_MATRIX_POP) {
Matrix_Pop();
}

if (bone_extra_flags & BF_MATRIX_PUSH) {
Matrix_Push();
}

Matrix_TranslateRel(bone[1], bone[2], bone[3]);
Matrix_RotYXZsuperpack(&mesh_rots, 0);

// Extra rotation is ignored in this case as it's not needed.

bit <<= 1;
if (item->mesh_bits & bit) {
Output_InsertPolygons(*meshpp, clip);
}

bone += 4;
meshpp++;
}
}

Matrix_Pop();
}

void __cdecl Pickup_Collision(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{
Expand Down
2 changes: 2 additions & 0 deletions src/tr2/game/objects/general/pickup.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

void Pickup_Setup(OBJECT *obj);

void Pickup_Draw(const ITEM *item);

void __cdecl Pickup_Collision(
int16_t item_num, ITEM *lara_item, COLL_INFO *coll);

Expand Down
85 changes: 2 additions & 83 deletions src/tr2/game/overlay.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "game/inventory/common.h"
#include "game/matrix.h"
#include "game/music.h"
#include "game/objects/common.h"
#include "game/output.h"
#include "game/text.h"
#include "game/viewport.h"
Expand Down Expand Up @@ -71,88 +72,6 @@ static float M_Ease(const int32_t cur_frame, const int32_t max_frames)
return result;
}

static BOUNDS_16 M_GetBounds(
const OBJECT *const obj, const FRAME_INFO *const frame)
{
int16_t **mesh_ptrs = &g_Meshes[obj->mesh_idx];
int32_t *bone = &g_AnimBones[obj->bone_idx];
const int16_t *mesh_rots = frame->mesh_rots;

Matrix_PushUnit();
Matrix_TranslateRel(frame->offset.x, frame->offset.y, frame->offset.z);
Matrix_RotYXZsuperpack(&mesh_rots, 0);

BOUNDS_16 new_bounds = {
.min_x = 0x7FFF,
.min_y = 0x7FFF,
.min_z = 0x7FFF,
.max_x = -0x7FFF,
.max_y = -0x7FFF,
.max_z = -0x7FFF,
};

for (int32_t mesh_idx = 0; mesh_idx < obj->mesh_count; mesh_idx++) {
if (mesh_idx != 0) {
int32_t bone_extra_flags = *bone;
if (bone_extra_flags & BF_MATRIX_POP) {
Matrix_Pop();
}

if (bone_extra_flags & BF_MATRIX_PUSH) {
Matrix_Push();
}

Matrix_TranslateRel(bone[1], bone[2], bone[3]);
Matrix_RotYXZsuperpack(&mesh_rots, 0);
bone += 4;
}

const int16_t *obj_ptr = mesh_ptrs[mesh_idx];
obj_ptr += 5;
const int32_t vtx_count = *obj_ptr++;
for (int32_t i = 0; i < vtx_count; i++) {
PHD_VBUF *const vbuf = &g_PhdVBuf[i];

// clang-format off
const MATRIX *const mptr = g_MatrixPtr;
const double xv = (
mptr->_00 * obj_ptr[0] +
mptr->_01 * obj_ptr[1] +
mptr->_02 * obj_ptr[2] +
mptr->_03
);
const double yv = (
mptr->_10 * obj_ptr[0] +
mptr->_11 * obj_ptr[1] +
mptr->_12 * obj_ptr[2] +
mptr->_13
);
double zv = (
mptr->_20 * obj_ptr[0] +
mptr->_21 * obj_ptr[1] +
mptr->_22 * obj_ptr[2] +
mptr->_23
);
// clang-format on

const int32_t x = ((int32_t)xv) >> W2V_SHIFT;
const int32_t y = ((int32_t)yv) >> W2V_SHIFT;
const int32_t z = ((int32_t)zv) >> W2V_SHIFT;

new_bounds.min_x = MIN(new_bounds.min_x, x);
new_bounds.min_y = MIN(new_bounds.min_y, y);
new_bounds.min_z = MIN(new_bounds.min_z, z);
new_bounds.max_x = MAX(new_bounds.max_x, x);
new_bounds.max_y = MAX(new_bounds.max_y, y);
new_bounds.max_z = MAX(new_bounds.max_z, z);
obj_ptr += 3;
}
}

Matrix_Pop();
return new_bounds;
}

bool __cdecl Overlay_FlashCounter(const int32_t ticks)
{
if (m_FlashCounter > 0) {
Expand Down Expand Up @@ -457,7 +376,7 @@ static void M_DrawPickup3D(const DISPLAY_PICKUP *const pickup)
if (frame->bounds.min_x == frame->bounds.max_x
&& frame->bounds.min_y == frame->bounds.max_y) {
// fix broken collision box for the prayer wheel
bounds = M_GetBounds(obj, frame);
bounds = Object_GetBoundingBox(obj, frame);
}

const int32_t scale = 1280;
Expand Down

0 comments on commit 9cf197c

Please sign in to comment.