diff --git a/port/fast3d/gfx_pc.cpp b/port/fast3d/gfx_pc.cpp index 571e03f05..0acaff3fc 100644 --- a/port/fast3d/gfx_pc.cpp +++ b/port/fast3d/gfx_pc.cpp @@ -192,6 +192,9 @@ static struct RDP { bool viewport_or_scissor_changed; void* z_buf_address; void* color_image_address; + + int16_t subpixel_ofs_x; + int16_t subpixel_ofs_y; } rdp; static struct RenderingState { @@ -1573,7 +1576,7 @@ static inline void gfx_sp_tri4(Gfx *cmd) { uint8_t x = C1(0, 4); uint8_t y = C1(4, 4); uint8_t z = C0(0, 4); - + if(x || y || z) { gfx_sp_tri1(x, y, z, false); } @@ -1969,6 +1972,11 @@ static void gfx_dp_set_fill_color(uint32_t packed_color) { rdp.fill_color.a = a * 255; } +static void gfx_dp_set_subpixel_offset(int16_t x, int16_t y) { + rdp.subpixel_ofs_x = x; + rdp.subpixel_ofs_y = y; +} + static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) { uint32_t saved_other_mode_h = rdp.other_mode_h; uint32_t cycle_type = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)); @@ -1977,6 +1985,11 @@ static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lr rdp.other_mode_h = (rdp.other_mode_h & ~(3U << G_MDSFT_TEXTFILT)) | G_TF_POINT; } + ulx += rdp.subpixel_ofs_x; + lrx += rdp.subpixel_ofs_x; + uly += rdp.subpixel_ofs_y; + lry += rdp.subpixel_ofs_y; + // U10.2 coordinates float ulxf = ulx; float ulyf = uly; @@ -2361,6 +2374,10 @@ static void gfx_run_dl(Gfx* cmd) { break; // G_SETPRIMCOLOR, G_CCMUX_PRIMITIVE, G_ACMUX_PRIMITIVE, is used by Goddard // G_CCMUX_TEXEL1, LOD_FRACTION is used in Bowser room 1 + case G_SETSUBPIXELOFFSET_EXT: { + gfx_dp_set_subpixel_offset(C0(0, 16), C1(0, 16)); + break; + } case G_TEXRECT: case G_TEXRECTFLIP: { int32_t lrx, lry, tile, ulx, uly; diff --git a/src/game/bondview.c b/src/game/bondview.c index 13bf75af2..ed527006c 100644 --- a/src/game/bondview.c +++ b/src/game/bondview.c @@ -1109,6 +1109,10 @@ Gfx *bviewDrawEyespyMetrics(Gfx *gdl) s32 x = viewleft + (viewwidth >> 1); s32 y = viewtop + (viewheight >> 1); +#ifndef PLATFORM_N64 + gDPSetSubpixelOffsetEXT(gdl++, -2, -2); +#endif + gDPFillRectangle(gdl++, x + 2, y + 0, x + 7, y + 1); gDPFillRectangle(gdl++, x + 2, y + 0, x + 5, y + 1); gDPFillRectangle(gdl++, x - 6, y + 0, x - 1, y + 1); @@ -1117,6 +1121,10 @@ Gfx *bviewDrawEyespyMetrics(Gfx *gdl) gDPFillRectangle(gdl++, x + 0, y + 2, x + 1, y + 5); gDPFillRectangle(gdl++, x + 0, y - 6, x + 1, y - 1); gDPFillRectangle(gdl++, x + 0, y - 4, x + 1, y - 1); + +#ifndef PLATFORM_N64 + gDPSetSubpixelOffsetEXT(gdl++, 0, 0); +#endif } if (g_Vars.currentplayer->eyespy->mode == EYESPYMODE_CAMSPY) { diff --git a/src/game/radar.c b/src/game/radar.c index 2a449e034..d289a9a04 100644 --- a/src/game/radar.c +++ b/src/game/radar.c @@ -80,7 +80,7 @@ Gfx *radarRenderBackground(Gfx *gdl, struct textureconfig *tconfig, s32 arg2, s3 texSelect(&gdl, tconfig, 2, 0, 0, 1, NULL); func0f0b278c(&gdl, spb0, spa8, tconfig->width, tconfig->height, - 0, 0, 1, 0, 0xff, 0, 40, tconfig->level > 0, 0); + 0, 0, 0, 0, 0xff, 0, 40, tconfig->level > 0, 0); gDPPipeSync(gdl++); gDPSetColorDither(gdl++, G_CD_BAYER); @@ -329,6 +329,7 @@ Gfx *radarRender(Gfx *gdl) } #ifndef PLATFORM_N64 gSPExtraGeometryModeEXT(gdl++, G_ASPECT_MODE_EXT, g_HudAlignModeR); + gDPSetSubpixelOffsetEXT(gdl++, -2, 2); #endif } @@ -337,6 +338,7 @@ Gfx *radarRender(Gfx *gdl) // Draw dots for human players #ifndef PLATFORM_N64 + gDPSetSubpixelOffsetEXT(gdl++, 0, 0); if (!(g_MpSetup.options & MPOPTION_NOPLAYERONRADAR)) { #endif for (i = 0; i < playercount; i++) { diff --git a/src/game/sight.c b/src/game/sight.c index 78359c4ef..0764caf5d 100644 --- a/src/game/sight.c +++ b/src/game/sight.c @@ -515,6 +515,7 @@ Gfx *sightDrawAimer(Gfx *gdl, s32 x, s32 y, s32 radius, s32 cornergap, u32 colou #ifndef PLATFORM_N64 x = sightGetAdjustedX(x); gSPSetExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT); + gDPSetSubpixelOffsetEXT(gdl++, -2, -2); #endif // Draw the lines that span most of the viewport @@ -551,6 +552,7 @@ Gfx *sightDrawAimer(Gfx *gdl, s32 x, s32 y, s32 radius, s32 cornergap, u32 colou #ifndef PLATFORM_N64 gSPClearExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT); + gDPSetSubpixelOffsetEXT(gdl++, 0, 0); #endif gdl = text0f153838(gdl); @@ -908,7 +910,7 @@ Gfx *sightDrawClassic(Gfx *gdl, bool sighton) f32 spc4[2]; f32 spbc[2]; s32 x = g_Vars.currentplayer->crosspos[0]; - s32 y = g_Vars.currentplayer->crosspos[1]; + s32 y = g_Vars.currentplayer->crosspos[1] + 1; // Plus one, to align with the laser sight. s32 x1; s32 x2; s32 y1; @@ -1034,6 +1036,14 @@ Gfx *sightDrawSkedarTriangle(Gfx *gdl, s32 x, s32 y, s32 dir, u32 colour) vertices[2].y = points[5] * 10; vertices[2].z = -10; +#ifndef PLATFORM_N64 + // Center-align Skedar tris + for (int i = 0; i < 3; ++i) { + vertices[i].x -= 2; + vertices[i].y += 2; + } +#endif + // @bug: This also needs to check for COLOUR_LIGHTRED because the caller can // use two shades of red. The second colour is used when zeroing the sight // in on a new target. Because of this bug, targeting an ally with the @@ -1331,6 +1341,16 @@ Gfx *sightDrawZoom(Gfx *gdl, bool sighton) marginbottom = viewhalfheight - availablebelow * frac; margintop = viewhalfheight - availableabove * frac; +#ifndef PLATFORM_N64 + // Center-align the zoom range + if (frac != 1.0f) { + viewleft += 1; + viewright += 1; + viewbottom += 1; + viewtop += 1; + } +#endif + #define BOXLEFT (viewleft + marginleft) #define BOXRIGHT (viewright - marginright) #define BOXBOTTOM (viewbottom - marginbottom) @@ -1346,6 +1366,7 @@ Gfx *sightDrawZoom(Gfx *gdl, bool sighton) #ifndef PLATFORM_N64 gSPSetExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT); + gDPSetSubpixelOffsetEXT(gdl++, -2, -2); #endif // Top left @@ -1387,6 +1408,7 @@ Gfx *sightDrawZoom(Gfx *gdl, bool sighton) #ifndef PLATFORM_N64 gSPClearExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT); + gDPSetSubpixelOffsetEXT(gdl++, 0, 0); #endif gdl = text0f153838(gdl); @@ -1425,6 +1447,7 @@ Gfx *sightDrawMaian(Gfx *gdl, bool sighton) #ifndef PLATFORM_N64 x = sightGetAdjustedX(x); gSPSetExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT); + gDPSetSubpixelOffsetEXT(gdl++, -2, -2); #endif vertices = gfxAllocateVertices(8); @@ -1469,6 +1492,14 @@ Gfx *sightDrawMaian(Gfx *gdl, bool sighton) vertices[7].y = inner[2] * 10; vertices[7].z = -10; +#ifndef PLATFORM_N64 + // Center-align Maian tris + for (int i = 0; i < 8; ++i) { + vertices[i].x -= 2; + vertices[i].y += 2; + } +#endif + colours[0].word = PD_BE32(0x00ff000f); colours[1].word = PD_BE32(hasprop ? colour : 0x00ff0044); @@ -1499,6 +1530,7 @@ Gfx *sightDrawMaian(Gfx *gdl, bool sighton) #ifndef PLATFORM_N64 gSPClearExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT); + gDPSetSubpixelOffsetEXT(gdl++, 0, 0); #endif return gdl; @@ -1519,6 +1551,7 @@ Gfx *sightDrawTarget(Gfx *gdl) #ifndef PLATFORM_N64 gSPSetExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT); + gDPSetSubpixelOffsetEXT(gdl++, -2, -2); if (SIGHT_SCALE == 0) { // Draw single rectangle to preserve intended opacity gDPHudRectangle(gdl++, x, y, x, y); @@ -1537,6 +1570,7 @@ Gfx *sightDrawTarget(Gfx *gdl) #ifndef PLATFORM_N64 gSPClearExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT); + gDPSetSubpixelOffsetEXT(gdl++, 0, 0); #endif gdl = text0f153838(gdl); @@ -1568,6 +1602,16 @@ Gfx *sightDraw(Gfx *gdl, bool sighton, s32 sight) return gdl; } +#ifndef PLATFORM_N64 + // Rounding the crosshair positions allow them to more accurately follow the + // gun's vector. Without this, the mantissa isn't factored in at all (cast + // to integer), which leads to some awkward behavior, such as the crosshair + // taking a long time to return to the center of the screen when coming from + // an up and/or left direction. + g_Vars.currentplayer->crosspos[0] = roundf(g_Vars.currentplayer->crosspos[0]); + g_Vars.currentplayer->crosspos[1] = roundf(g_Vars.currentplayer->crosspos[1]); +#endif + #if PAL g_ScaleX = 1; #else diff --git a/src/include/gbiex.h b/src/include/gbiex.h index 8cc1d50c6..687d08d90 100644 --- a/src/include/gbiex.h +++ b/src/include/gbiex.h @@ -184,18 +184,19 @@ /* Extended commands */ -#define G_SETFB_EXT 0x21 -#define G_SETTIMG_FB_EXT 0x23 -#define G_INVALTEXCACHE_EXT 0x34 -#define G_TEXRECT_WIDE_EXT 0x37 -#define G_FILLRECT_WIDE_EXT 0x38 -#define G_SETGRAYSCALE_EXT 0x39 -#define G_EXTRAGEOMETRYMODE_EXT 0x3a -#define G_SETINTENSITY_EXT 0x40 -#define G_COPYFB_EXT 0x41 -#define G_IMAGERECT_EXT 0x42 -#define G_RDPFLUSH_EXT 0x43 -#define G_CLEAR_DEPTH_EXT 0x44 +#define G_SETFB_EXT 0x21 +#define G_SETTIMG_FB_EXT 0x23 +#define G_INVALTEXCACHE_EXT 0x34 +#define G_TEXRECT_WIDE_EXT 0x37 +#define G_FILLRECT_WIDE_EXT 0x38 +#define G_SETGRAYSCALE_EXT 0x39 +#define G_EXTRAGEOMETRYMODE_EXT 0x3a +#define G_SETINTENSITY_EXT 0x40 +#define G_COPYFB_EXT 0x41 +#define G_IMAGERECT_EXT 0x42 +#define G_RDPFLUSH_EXT 0x43 +#define G_CLEAR_DEPTH_EXT 0x44 +#define G_SETSUBPIXELOFFSET_EXT 0x45 /* G_EXTRAGEOMETRYMODE flags */ @@ -292,6 +293,14 @@ _g->words.w1 = (u32)(s); \ } +#define gDPSetSubpixelOffsetEXT(pkt, x, y) \ +{ \ + Gfx *_g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETSUBPIXELOFFSET_EXT, 24, 8) | _SHIFTL((s16)(x), 0, 16); \ + _g->words.w1 = _SHIFTL((s16)(y), 0, 16); \ +} + #define gSPSetExtraGeometryModeEXT(pkt, word) gSPExtraGeometryModeEXT((pkt), 0, word) #define gSPClearExtraGeometryModeEXT(pkt, word) gSPExtraGeometryModeEXT((pkt), word, 0)