draw: add 3d pickups

This commit is contained in:
oziphantom 2021-11-01 10:04:33 +11:00 committed by GitHub
parent 3b2664bfc4
commit 8631b0902f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 217 additions and 5 deletions

View file

@ -163,6 +163,9 @@
// Overrides ingame brightness.
"brightness": 1.0,
// Enable round shadow instead of the default octagon one.
// Enables round shadow instead of the default octagon one.
"enable_round_shadow": true,
// Enables 3D models to be rendered in place of the sprites for pickup items
"enable_3d_pickups" : true,
}

View file

@ -155,6 +155,7 @@ int8_t T1MReadConfigFromJson(const char *cfg_data)
READ_BOOL(enable_xbox_one_controller, 0);
READ_FLOAT(brightness, 1.0);
READ_BOOL(enable_round_shadow, 1);
READ_BOOL(enable_3d_pickups, 1);
READ_BAR_SHOWING_MODE(healthbar_showing_mode, T1M_BSM_FLASHING_OR_DEFAULT);
READ_BAR_SHOWING_MODE(airbar_showing_mode, T1M_BSM_DEFAULT);

View file

@ -70,6 +70,7 @@ typedef struct {
int8_t enable_xbox_one_controller;
float brightness;
int8_t enable_round_shadow;
int8_t enable_3d_pickups;
} T1MConfigStruct;
extern T1MConfigStruct T1MConfig;

View file

@ -2,14 +2,19 @@
#include "3dsystem/3d_gen.h"
#include "3dsystem/scalespr.h"
#include "config.h"
#include "game/control.h"
#include "game/game.h"
#include "game/hair.h"
#include "game/health.h"
#include "game/inv.h"
#include "global/const.h"
#include "global/vars.h"
#include "specific/output.h"
#include "util.h"
#include <stdlib.h>
static int16_t InterpolatedBounds[6];
int32_t DrawPhaseCinematic()
@ -343,6 +348,207 @@ void DrawDummyItem(ITEM_INFO *item)
{
}
void DrawPickupItem(ITEM_INFO *item)
{
if (!T1MConfig.enable_3d_pickups) {
DrawSpriteItem(item);
return;
}
// Convert item to menu display item.
int16_t item_num_option = Inv_GetItemOption(item->object_number);
// Save the frame number.
int16_t old_frame_number = item->frame_number;
// Modify item to be the anim for inv item and animation 0.
item->anim_number = Objects[item_num_option].anim_index;
item->frame_number = Anims[item->anim_number].frame_base;
OBJECT_INFO *object = &Objects[item_num_option];
int16_t *frmptr[2];
int32_t rate;
int32_t frac = GetFrames(item, frmptr, &rate);
// Restore the old frame number in case we need to get the sprite again.
item->frame_number = old_frame_number;
// Fall back to normal sprite rendering if not found.
if (object->nmeshes < 0) {
DrawSpriteItem(item);
return;
}
// Good news is there is a mesh, we just need to work out where to put it
// First - Is there floor under the item?
// This is mostly true, but for example the 4 items in the Obelisk of
// Kharmoon the 4 items are sitting ontop of a static mesh which is not
// floor.
FLOOR_INFO *floor =
GetFloor(item->pos.x, item->pos.y, item->pos.z, &item->room_number);
int16_t floor_height =
GetHeight(floor, item->pos.x, item->pos.y, item->pos.z);
// Assume this is our offset.
int16_t offset = floor_height;
// Is the floor "just below" the item?
int16_t floor_mapped_delta = abs(floor_height - item->pos.y);
if (floor_mapped_delta > WALL_L / 4 || floor_mapped_delta == 0) {
// No, now we need to move it a bit.
// First get the sprite that was to be used,
int16_t spr_num =
Objects[item->object_number].mesh_index - item->frame_number;
PHD_SPRITE *sprite = &PhdSpriteInfo[spr_num];
// and get the animation bounding box, which is not the mesh one.
int16_t min_y = frmptr[0][FRAME_BOUND_MIN_Y];
int16_t max_y = frmptr[0][FRAME_BOUND_MAX_Y];
int16_t anim_y = frmptr[0][FRAME_POS_Y];
// Sifferent objects need different heuristics.
switch (item_num_option) {
case O_GUN_OPTION:
case O_SHOTGUN_OPTION:
case O_MAGNUM_OPTION:
case O_UZI_OPTION:
case O_MAG_AMMO_OPTION:
case O_UZI_AMMO_OPTION:
case O_EXPLOSIVE_OPTION:
case O_LEADBAR_OPTION:
case O_PICKUP_OPTION1:
case O_PICKUP_OPTION2:
case O_SCION_OPTION:
// Ignore the sprite and just position based upon the anim.
offset = item->pos.y + (min_y - anim_y) / 2;
break;
case O_MEDI_OPTION:
case O_BIGMEDI_OPTION:
case O_SG_AMMO_OPTION:
case O_PUZZLE_OPTION1:
case O_PUZZLE_OPTION2:
case O_PUZZLE_OPTION3:
case O_PUZZLE_OPTION4:
case O_KEY_OPTION1:
case O_KEY_OPTION2:
case O_KEY_OPTION3:
case O_KEY_OPTION4: {
// Take the difference from the bottom of the sprite and the bottom
// of the animation and divide it by 8.
// 8 was chosen because in testing it positioned objects correctly.
// Specifically the 4 items in the Obelisk of Kharmoon and keys.
// Some objects have a centred mesh and some have one that is from
// the bottom, for the centred ones; move up from the
// bottom is necessary.
int centred = abs(min_y + max_y) < 8;
if (floor_mapped_delta) {
offset = item->pos.y - abs(min_y - sprite->y1) / 8;
} else if (centred) {
offset = item->pos.y + min_y;
}
break;
}
}
}
phd_PushMatrix();
phd_TranslateAbs(item->pos.x, offset, item->pos.z);
int16_t *frame = &object->frame_base[(object->nmeshes * 2 + 10)];
CalculateObjectLighting(item, frame);
int32_t x = (PhdMatrixPtr->_03 >> W2V_SHIFT) + item->pos.x;
int32_t y = (PhdMatrixPtr->_13 >> W2V_SHIFT) + item->pos.y;
int32_t z = (PhdMatrixPtr->_23 >> W2V_SHIFT) + item->pos.z;
S_CalculateLight(x, y, z, item->room_number);
int32_t clip = S_GetObjectBounds(frame);
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 = &Meshes[object->mesh_index];
int32_t *bone = &AnimBones[object->bone_index];
if (!frac) {
phd_TranslateRel(
frmptr[0][FRAME_POS_X], frmptr[0][FRAME_POS_Y],
frmptr[0][FRAME_POS_Z]);
int32_t *packed_rotation = (int32_t *)(frmptr[0] + FRAME_ROT);
phd_RotYXZpack(*packed_rotation++);
if (item->mesh_bits & bit) {
phd_PutPolygons(*meshpp++, clip);
}
for (int i = 1; i < object->nmeshes; i++) {
int32_t bone_extra_flags = *bone;
if (bone_extra_flags & BEB_POP) {
phd_PopMatrix();
}
if (bone_extra_flags & BEB_PUSH) {
phd_PushMatrix();
}
phd_TranslateRel(bone[1], bone[2], bone[3]);
phd_RotYXZpack(*packed_rotation++);
// Extra rotation is ignored in this case as it's not needed.
bit <<= 1;
if (item->mesh_bits & bit) {
phd_PutPolygons(*meshpp, clip);
}
bone += 4;
meshpp++;
}
} else {
// This should never happen but is here "just in case".
InitInterpolate(frac, rate);
phd_TranslateRel_ID(
frmptr[0][FRAME_POS_X], frmptr[0][FRAME_POS_Y],
frmptr[0][FRAME_POS_Z], frmptr[1][FRAME_POS_X],
frmptr[1][FRAME_POS_Y], frmptr[1][FRAME_POS_Z]);
int32_t *packed_rotation1 = (int32_t *)(frmptr[0] + FRAME_ROT);
int32_t *packed_rotation2 = (int32_t *)(frmptr[1] + FRAME_ROT);
phd_RotYXZpack_I(*packed_rotation1++, *packed_rotation2++);
if (item->mesh_bits & bit) {
phd_PutPolygons_I(*meshpp++, clip);
}
for (int i = 1; i < object->nmeshes; i++) {
int32_t bone_extra_flags = *bone;
if (bone_extra_flags & BEB_POP) {
phd_PopMatrix_I();
}
if (bone_extra_flags & BEB_PUSH) {
phd_PushMatrix_I();
}
phd_TranslateRel_I(bone[1], bone[2], bone[3]);
phd_RotYXZpack_I(*packed_rotation1++, *packed_rotation2++);
// Extra rotation is ignored in this case as it's not needed.
bit <<= 1;
if (item->mesh_bits & bit) {
phd_PutPolygons_I(*meshpp, clip);
}
bone += 4;
meshpp++;
}
}
}
phd_PopMatrix();
}
void DrawAnimatingItem(ITEM_INFO *item)
{
static int16_t null_rotation[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

View file

@ -14,6 +14,7 @@ void PrintRooms(int16_t room_number);
void DrawEffect(int16_t fxnum);
void DrawSpriteItem(ITEM_INFO *item);
void DrawDummyItem(ITEM_INFO *item);
void DrawPickupItem(ITEM_INFO *item);
void DrawAnimatingItem(ITEM_INFO *item);
void DrawUnclippedItem(ITEM_INFO *item);
void DrawLara(ITEM_INFO *item);

View file

@ -21,7 +21,7 @@ void SetupMovingBar(OBJECT_INFO *obj)
void SetupLeadBar(OBJECT_INFO *obj)
{
obj->draw_routine = DrawSpriteItem;
obj->draw_routine = DrawPickupItem;
obj->collision = PickUpCollision;
obj->save_flags = 1;
}

View file

@ -34,7 +34,7 @@ int16_t PickUpBoundsUW[12] = {
void SetupPickupObject(OBJECT_INFO *obj)
{
obj->draw_routine = DrawSpriteItem;
obj->draw_routine = DrawPickupItem;
obj->collision = PickUpCollision;
obj->save_flags = 1;
}

View file

@ -46,13 +46,13 @@ int16_t PickUpScion4Bounds[12] = {
void SetupScion1(OBJECT_INFO *obj)
{
Objects[O_SCION_ITEM].draw_routine = DrawSpriteItem;
Objects[O_SCION_ITEM].draw_routine = DrawPickupItem;
Objects[O_SCION_ITEM].collision = PickUpScionCollision;
}
void SetupScion2(OBJECT_INFO *obj)
{
Objects[O_SCION_ITEM2].draw_routine = DrawSpriteItem;
Objects[O_SCION_ITEM2].draw_routine = DrawPickupItem;
Objects[O_SCION_ITEM2].collision = PickUpCollision;
Objects[O_SCION_ITEM2].save_flags = 1;
}