diff --git a/src/code/z_play.c b/src/code/z_play.c index 590680da25..679c5b7772 100644 --- a/src/code/z_play.c +++ b/src/code/z_play.c @@ -224,6 +224,10 @@ void func_800BC88C(PlayState* this) { this->transitionCtx.transitionType = -1; } +/** + * Set the environment fog, from parameters controlled by the environment system. + * If a custom fog state is used at any point in drawing, the environment fog is expected to be restored afterwards. + */ Gfx* Play_SetFog(PlayState* this, Gfx* gfx) { return Gfx_SetFog2(gfx, this->lightCtx.fogColor[0], this->lightCtx.fogColor[1], this->lightCtx.fogColor[2], 0, this->lightCtx.fogNear, 1000); diff --git a/src/code/z_rcp.c b/src/code/z_rcp.c index 135ffa8876..657bd9e560 100644 --- a/src/code/z_rcp.c +++ b/src/code/z_rcp.c @@ -854,28 +854,50 @@ Gfx gEmptyDL[] = { gsSPEndDisplayList(), }; +/** + * Set fog color and range. + * + * At or prior to fog near, geometry is unaffected by fog. At or beyond fog far, geometry is fully fogged. + * Between near and far, rendered geometry will be blended between the unfogged color and the supplied fog color. + * + * Fog far should be in the range 0 to 1000 and greater than or equal to fog near. If fog near is negative everything + * will be fully fogged. If fog near is 1000 or greater there is no fog. + */ Gfx* Gfx_SetFog(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far) { + // Avoid 0 divisor in gSPFogPosition below if (far == near) { far++; } - ASSERT(near != far, "n != f", "../z_rcp.c", 1155); + // Set the fog color, far away geometry will be rendered as this solid color. gDPSetFogColor(gfx++, r, g, b, a); if (near >= 1000) { + // Set a constant shade alpha of 0 for no fog gSPFogFactor(gfx++, 0, 0); - } else if (near >= 997) { - gSPFogFactor(gfx++, 0x7FFF, 0x8100); + } else if (near > 996) { + // Avoid an overflow when near and far are close (see bug below), by effectively clamping near to ~996. + // This is almost SPFogPosition(996.0937f, 1000) + gSPFogFactor(gfx++, 0x7FFF, -0x7F00); } else if (near < 0) { + // Set a constant shade alpha of 255 for fully fogged gSPFogFactor(gfx++, 0, 255); } else { + // Normal range. Shade alpha is 0 at z <= near and 255 at z >= far, linearly interpolated in between. + //! @bug If far - near < 4, the computed `fm` fog factor coefficient will overflow. + //! For example: 128000 / (983 - 980) > 32767 + //! This is handled above in the case of near > 996, but the general case is not accounted for. gSPFogPosition(gfx++, near, far); } - return gfx; } +/** + * Like Gfx_SetFog but issues a pipesync before changing fog color. + * + * @see Gfx_SetFog + */ Gfx* Gfx_SetFogWithSync(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far) { if (far == near) { far++; @@ -898,6 +920,11 @@ Gfx* Gfx_SetFogWithSync(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far) return gfx; } +/** + * Wrapper for Gfx_SetFog + * + * @see Gfx_SetFog + */ Gfx* Gfx_SetFog2(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far) { return Gfx_SetFog(gfx, r, g, b, a, near, far); }