WIP: uncapped framerate 5

This commit is contained in:
MysterD 2022-04-27 19:43:55 -07:00
parent 0eece3001f
commit 6e47f226c9
4 changed files with 149 additions and 62 deletions

View file

@ -38,7 +38,7 @@
* - Script node (Cannon overlay)
*
*/
#define MATRIX_STACK_SIZE 32
s16 gMatStackIndex;
@ -155,6 +155,11 @@ Vtx* gBackgroundSkyboxVerts[3][3] = { 0 };
Mtx* gBackgroundSkyboxMtx = NULL;
struct GraphNodeRoot* sBackgroundNodeRoot = NULL;
#define MAX_SHADOW_NODES 128
struct ShadowInterp sShadowInterp[MAX_SHADOW_NODES] = { 0 };
struct ShadowInterp* gShadowInterpCurrent = NULL;
static u8 sShadowInterpCount = 0;
struct {
Gfx *pos;
void *mtx;
@ -189,6 +194,8 @@ void patch_mtx_before(void) {
sBackgroundNode = NULL;
gBackgroundSkyboxGfx = NULL;
}
sShadowInterpCount = 0;
}
void patch_mtx_interpolated(f32 delta) {
@ -232,6 +239,20 @@ void patch_mtx_interpolated(f32 delta) {
gCurGraphNodeRoot = rootCopy;
}
struct GraphNodeObject* savedObj = gCurGraphNodeObject;
for (s32 i = 0; i < sShadowInterpCount; i++) {
struct ShadowInterp* interp = &sShadowInterp[i];
gShadowInterpCurrent = interp;
Vec3f posInterp;
delta_interpolate_vec3f(posInterp, interp->shadowPosPrev, interp->shadowPos, delta);
if (i == 0) {
printf("XXX: %f <--> %f == %f\n", interp->shadowPosPrev[1], interp->shadowPos[1], posInterp[1]);
}
gCurGraphNodeObject = interp->obj;
create_shadow_below_xyz(posInterp[0], posInterp[1], posInterp[2], interp->shadowScale, interp->node->shadowSolidity, interp->node->shadowType);
}
gCurGraphNodeObject = savedObj;
for (s32 i = 0; i < gMtxTblSize; i++) {
Gfx *pos = gMtxTbl[i].pos;
delta_interpolate_mtx(&gMtxTbl[i].interp, (Mtx*) gMtxTbl[i].mtxPrev, (Mtx*) gMtxTbl[i].mtx, delta);
@ -241,6 +262,7 @@ void patch_mtx_interpolated(f32 delta) {
gSPDisplayList(pos++, gMtxTbl[i].displayList);
}
}
}
/**
@ -250,7 +272,7 @@ static u8 increment_mat_stack() {
Mtx *mtx = alloc_display_list(sizeof(*mtx));
Mtx *mtxPrev = alloc_display_list(sizeof(*mtxPrev));
if (mtx == NULL || mtxPrev == NULL) { return FALSE; }
gMatStackIndex++;
mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]);
mtxf_to_mtx(mtxPrev, gMatStackPrev[gMatStackIndex]);
@ -446,10 +468,10 @@ static void geo_process_switch(struct GraphNodeSwitchCase *node) {
*/
static void geo_process_camera(struct GraphNodeCamera *node) {
Mat4 cameraTransform;
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx));
if (rollMtx == NULL) { return; }
@ -477,7 +499,7 @@ static void geo_process_camera(struct GraphNodeCamera *node) {
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
if (node->fnNode.node.children != 0) {
gCurGraphNodeCamera = node;
node->matrixPtr = &gMatStack[gMatStackIndex];
@ -497,7 +519,7 @@ static void geo_process_camera(struct GraphNodeCamera *node) {
static void geo_process_translation_rotation(struct GraphNodeTranslationRotation *node) {
Mat4 mtxf;
Vec3f translation;
// Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
@ -505,10 +527,10 @@ static void geo_process_translation_rotation(struct GraphNodeTranslationRotation
mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, node->node.flags >> 8);
}
@ -526,7 +548,7 @@ static void geo_process_translation_rotation(struct GraphNodeTranslationRotation
static void geo_process_translation(struct GraphNodeTranslation *node) {
Mat4 mtxf;
Vec3f translation;
// Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
@ -534,10 +556,10 @@ static void geo_process_translation(struct GraphNodeTranslation *node) {
mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, node->node.flags >> 8);
}
@ -554,7 +576,7 @@ static void geo_process_translation(struct GraphNodeTranslation *node) {
*/
static void geo_process_rotation(struct GraphNodeRotation *node) {
Mat4 mtxf;
// Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
@ -569,10 +591,10 @@ static void geo_process_rotation(struct GraphNodeRotation *node) {
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, gMatStackPrev[gMatStackIndex]);
vec3s_copy(node->prevRotation, node->rotation);
node->prevTimestamp = gGlobalTimer;
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, node->node.flags >> 8);
}
@ -590,7 +612,7 @@ static void geo_process_rotation(struct GraphNodeRotation *node) {
static void geo_process_scale(struct GraphNodeScale *node) {
Vec3f scaleVec;
Vec3f prevScaleVec;
// Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
@ -603,7 +625,7 @@ static void geo_process_scale(struct GraphNodeScale *node) {
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, node->node.flags >> 8);
}
@ -621,12 +643,12 @@ static void geo_process_scale(struct GraphNodeScale *node) {
*/
static void geo_process_billboard(struct GraphNodeBillboard *node) {
Vec3f translation;
// Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
s16 nextMatStackIndex = gMatStackIndex + 1;
vec3s_to_vec3f(translation, node->translation);
mtxf_billboard(gMatStack[nextMatStackIndex], gMatStack[gMatStackIndex], translation,
gCurGraphNodeCamera->roll);
@ -646,7 +668,7 @@ static void geo_process_billboard(struct GraphNodeBillboard *node) {
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
if (node->displayList != NULL) {
geo_append_display_list(node->displayList, node->node.flags >> 8);
}
@ -792,10 +814,10 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
Vec3f translation;
Vec3s rotationPrev;
Vec3f translationPrev;
// Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
u16 *animAttribute = gCurrAnimAttribute;
u8 animType = gCurAnimType;
@ -813,7 +835,7 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
mtxf_rotate_xyz_and_translate(matrix, translationPrev, rotationPrev);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], matrix, gMatStackPrev[gMatStackIndex]);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
@ -886,7 +908,7 @@ static void geo_process_shadow(struct GraphNodeShadow *node) {
Vec3f shadowPosPrev;
Vec3f animOffset;
f32 shadowScale;
// Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
@ -949,30 +971,44 @@ static void geo_process_shadow(struct GraphNodeShadow *node) {
gCurGraphNodeObject->prevShadowPosTimestamp = gGlobalTimer;
}
if (sShadowInterpCount < MAX_SHADOW_NODES) {
struct ShadowInterp* interp = &sShadowInterp[sShadowInterpCount++];
gShadowInterpCurrent = interp;
interp->gfx = NULL;
interp->node = node;
interp->shadowScale = shadowScale;
interp->obj = gCurGraphNodeObject;
vec3f_copy(interp->shadowPos, shadowPos);
vec3f_copy(interp->shadowPosPrev, shadowPosPrev);
} else {
gShadowInterpCurrent = NULL;
}
Gfx *shadowListPrev = create_shadow_below_xyz(shadowPosPrev[0], shadowPosPrev[1],
shadowPosPrev[2], shadowScale,
node->shadowSolidity, node->shadowType);
Gfx *shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale,
node->shadowSolidity, node->shadowType);
if (shadowListPrev != NULL && shadowList != NULL) {
if (gShadowInterpCurrent != NULL) {
gShadowInterpCurrent->gfx = shadowListPrev;
}
if (shadowListPrev != NULL) {
mtxf_translate(mtxf, shadowPos);
mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, *gCurGraphNodeCamera->matrixPtr);
mtxf_translate(mtxf, shadowPosPrev);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], mtxf, *gCurGraphNodeCamera->matrixPtrPrev);
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
if (gShadowAboveWaterOrLava == TRUE) {
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList),
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowListPrev),
(void *) VIRTUAL_TO_PHYSICAL(shadowListPrev), 4);
} else if (gMarioOnIceOrCarpet == TRUE) {
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList),
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowListPrev),
(void *) VIRTUAL_TO_PHYSICAL(shadowListPrev), 5);
} else {
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowList),
geo_append_display_list2((void *) VIRTUAL_TO_PHYSICAL(shadowListPrev),
(void *) VIRTUAL_TO_PHYSICAL(shadowListPrev), 6);
}
gMatStackIndex--;
@ -1034,7 +1070,7 @@ static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) {
// This multiplication should really be performed on 4:3 as well,
// but the issue will be more apparent on widescreen.
hScreenEdge *= GFX_DIMENSIONS_ASPECT_RATIO;
s16 cullingRadius = 300;
struct GraphNode *geo = node->sharedChild;
if (geo != NULL && geo->type == GRAPH_NODE_TYPE_CULLING_RADIUS) {
@ -1260,7 +1296,7 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
Mat4 mat;
Vec3f translation;
Vec3f scalePrev;
// Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB.
if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; }
@ -1304,10 +1340,10 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
if (node->fnNode.func != NULL) {
node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node, (struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]);
}
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
gGeoTempState.type = gCurAnimType;
gGeoTempState.enabled = gCurAnimEnabled;
gGeoTempState.frame = gCurrAnimFrame;
@ -1461,7 +1497,7 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor)
Mtx *initialMatrix = alloc_display_list(sizeof(*initialMatrix));
if (initialMatrix == NULL) { return; }
gMatStackIndex = 0;
gCurAnimType = 0;
vec3s_set(viewport->vp.vtrans, node->x * 4, node->y * 4, 511);
@ -1493,7 +1529,7 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor)
gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&sViewportPrev));
gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
gCurGraphNodeRoot = node;
if (node->node.children != NULL) {
geo_process_node_and_siblings(node->node.children);

View file

@ -33,4 +33,15 @@ void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor);
void interpolate_vectors(Vec3f res, Vec3f a, Vec3f b);
void interpolate_vectors_s16(Vec3s res, Vec3s a, Vec3s b);
struct ShadowInterp {
Gfx* gfx;
Vec3f shadowPos;
Vec3f shadowPosPrev;
Vtx *verts;
Gfx *displayList;
struct GraphNodeShadow *node;
f32 shadowScale;
struct GraphNodeObject *obj;
};
#endif // RENDERING_GRAPH_NODE_H

View file

@ -16,6 +16,9 @@
// Avoid Z-fighting
#define find_floor_height_and_data 0.4 + find_floor_height_and_data
extern u8 gRenderingInterpolated;
extern struct ShadowInterp* gShadowInterpCurrent;
/**
* @file shadow.c
* This file implements a self-contained subsystem used to draw shadows.
@ -114,6 +117,39 @@ s8 gMarioOnIceOrCarpet;
s8 sMarioOnFlyingCarpet;
s16 sSurfaceTypeBelowShadow;
static Vtx* shadow_get_or_alloc_verts(u8 vertCount) {
if (gRenderingInterpolated) {
if (gShadowInterpCurrent == NULL) {
return NULL;
}
return gShadowInterpCurrent->verts;
} else {
Vtx* verts = alloc_display_list(vertCount * sizeof(Vtx));
if (gShadowInterpCurrent) {
gShadowInterpCurrent->verts = verts;
}
return verts;
}
return NULL;
}
static Gfx* shadow_get_or_alloc_display_list(u8 dlCount) {
if (gRenderingInterpolated) {
if (gShadowInterpCurrent == NULL) {
return NULL;
}
return gShadowInterpCurrent->displayList;
} else {
Gfx *displayList = alloc_display_list(dlCount * sizeof(Gfx));
if (gShadowInterpCurrent) {
gShadowInterpCurrent->displayList = displayList;
}
return displayList;
}
return NULL;
}
/**
* Let (oldZ, oldX) be the relative coordinates of a point on a rectangle,
* assumed to be centered at the origin on the standard SM64 X-Z plane. This
@ -622,8 +658,8 @@ Gfx *create_shadow_player(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 soli
return NULL;
}
verts = alloc_display_list(9 * sizeof(Vtx));
displayList = alloc_display_list(5 * sizeof(Gfx));
verts = shadow_get_or_alloc_verts(9);
displayList = shadow_get_or_alloc_display_list(5);
if (verts == NULL || displayList == NULL) {
return NULL;
}
@ -650,8 +686,8 @@ Gfx *create_shadow_circle_9_verts(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale,
return NULL;
}
verts = alloc_display_list(9 * sizeof(Vtx));
displayList = alloc_display_list(5 * sizeof(Gfx));
verts = shadow_get_or_alloc_verts(9);
displayList = shadow_get_or_alloc_display_list(5);
if (verts == NULL || displayList == NULL) {
return 0;
@ -676,8 +712,8 @@ Gfx *create_shadow_circle_4_verts(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale,
return NULL;
}
verts = alloc_display_list(4 * sizeof(Vtx));
displayList = alloc_display_list(5 * sizeof(Gfx));
verts = shadow_get_or_alloc_verts(4);
displayList = shadow_get_or_alloc_display_list(5);
if (verts == NULL || displayList == NULL) {
return 0;
@ -709,8 +745,8 @@ Gfx *create_shadow_circle_assuming_flat_ground(f32 xPos, f32 yPos, f32 zPos, s16
distBelowFloor = floorHeight - yPos;
}
verts = alloc_display_list(4 * sizeof(Vtx));
displayList = alloc_display_list(5 * sizeof(Gfx));
verts = shadow_get_or_alloc_verts(4);
displayList = shadow_get_or_alloc_display_list(5);
if (verts == NULL || displayList == NULL) {
return 0;
@ -738,8 +774,8 @@ Gfx *create_shadow_spike_ext(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 s
return NULL;
}
verts = alloc_display_list(4 * sizeof(Vtx));
displayList = alloc_display_list(5 * sizeof(Gfx));
verts = shadow_get_or_alloc_verts(4);
displayList = shadow_get_or_alloc_display_list(5);
if (verts == NULL || displayList == NULL) {
return 0;
@ -757,8 +793,9 @@ Gfx *create_shadow_spike_ext(f32 xPos, f32 yPos, f32 zPos, s16 shadowScale, u8 s
* underneath the shadow is totally flat.
*/
Gfx *create_shadow_rectangle(f32 halfWidth, f32 halfLength, f32 relY, u8 solidity) {
Vtx *verts = alloc_display_list(4 * sizeof(Vtx));
Gfx *displayList = alloc_display_list(5 * sizeof(Gfx));
Vtx *verts = shadow_get_or_alloc_verts(4);
Gfx *displayList = shadow_get_or_alloc_display_list(5);
f32 frontLeftX, frontLeftZ, frontRightX, frontRightZ, backLeftX, backLeftZ, backRightX, backRightZ;
if (verts == NULL || displayList == NULL) {

View file

@ -45,10 +45,10 @@
* first, in a display list with the Z buffer disabled
*/
struct Skybox {
/// The camera's yaw, from 0 to 65536, which maps to 0 to 360 degrees
u16 yaw;
/// The camera's pitch, which is bounded by +-16384, which maps to -90 to 90 degrees
s16 pitch;
/// The camera's yaw, from 0 to (M_PI*2), which maps to 0 to 360 degrees
f32 yaw;
/// The camera's pitch, which is bounded by +-(M_PI/2), which maps to -90 to 90 degrees
f32 pitch;
/// The skybox's X position in world space
f32 scaledX;
/// The skybox's Y position in world space
@ -138,11 +138,11 @@ u8 sSkyboxColors[][3] = {
* (how far is the camera rotated from 0, scaled 0 to 1) *
* (the screen width)
*/
s32 calculate_skybox_scaled_x(s8 player, f32 fov) {
f32 calculate_skybox_scaled_x(s8 player, f32 fov) {
f32 yaw = sSkyBoxInfo[player].yaw;
//! double literals are used instead of floats
f32 scaledX = SCREEN_WIDTH * 360.0 * yaw / (fov * 65536.0);
f32 scaledX = SCREEN_WIDTH * 180.0 * yaw / (fov * M_PI);
if (scaledX > SKYBOX_WIDTH) {
scaledX -= (s32) scaledX / SKYBOX_WIDTH * SKYBOX_WIDTH;
@ -156,9 +156,9 @@ s32 calculate_skybox_scaled_x(s8 player, f32 fov) {
* fov may have been used in an earlier version, but the developers changed the function to always use
* 90 degrees.
*/
s32 calculate_skybox_scaled_y(s8 player, UNUSED f32 fov) {
f32 calculate_skybox_scaled_y(s8 player, UNUSED f32 fov) {
// Convert pitch to degrees. Pitch is bounded between -90 (looking down) and 90 (looking up).
f32 pitchInDegrees = (f32) sSkyBoxInfo[player].pitch * 360.0 / 65535.0;
f32 pitchInDegrees = sSkyBoxInfo[player].pitch * 180.0 / M_PI;
// Scale by 360 / fov
f32 degreesToScale = 360.0f * pitchInDegrees / 90.0;
@ -193,7 +193,7 @@ static s32 get_top_left_tile_idx(s8 player) {
* into an x and y by modulus and division by SKYBOX_COLS. x and y are then scaled by
* SKYBOX_TILE_WIDTH to get a point in world space.
*/
Vtx *make_skybox_rect(s32 tileIndex, s8 colorIndex, s32 row, s32 col, s8 player) {
Vtx *make_skybox_rect(s32 tileIndex, s8 colorIndex, s32 row, s32 col) {
extern Vtx* gBackgroundSkyboxVerts[3][3];
Vtx *verts;
@ -242,7 +242,7 @@ void draw_skybox_tile_grid(Gfx **dlist, s8 background, s8 player, s8 colorIndex)
texture = (Texture*)(*(SkyboxTexture *) segmented_to_virtual(sSkyboxTextures[background]))[tileIndex];
}
Vtx *vertices = make_skybox_rect(tileIndex, colorIndex, row, col, player);
Vtx *vertices = make_skybox_rect(tileIndex, colorIndex, row, col);
gLoadBlockTexture((*dlist)++, 32, 32, G_IM_FMT_RGBA, texture);
gSPVertex((*dlist)++, VIRTUAL_TO_PHYSICAL(vertices), 4, 0);
@ -342,8 +342,11 @@ Gfx *create_skybox_facing_camera(s8 player, s8 background, f32 fov,
//! fov is always set to 90.0f. If this line is removed, then the game crashes because fov is 0 on
//! the first frame, which causes a floating point divide by 0
fov = 90.0f;
sSkyBoxInfo[player].yaw = atan2s(cameraFaceZ, cameraFaceX);
sSkyBoxInfo[player].pitch = atan2s(sqrtf(cameraFaceX * cameraFaceX + cameraFaceZ * cameraFaceZ), cameraFaceY);
sSkyBoxInfo[player].yaw = (M_PI / 2.0) - atan2(cameraFaceZ, cameraFaceX);
if (sSkyBoxInfo[player].yaw < 0) { sSkyBoxInfo[player].yaw += M_PI * 2.0; }
sSkyBoxInfo[player].pitch = (M_PI / 2.0) - atan2(sqrtf(cameraFaceX * cameraFaceX + cameraFaceZ * cameraFaceZ), cameraFaceY);
sSkyBoxInfo[player].scaledX = calculate_skybox_scaled_x(player, fov);
sSkyBoxInfo[player].scaledY = calculate_skybox_scaled_y(player, fov);
sSkyBoxInfo[player].upperLeftTile = get_top_left_tile_idx(player);