Body Harvest for the Nintendo 64 is generally regarded as being fun to play, but a bit of a pig to look at, is there anything that can be done to improve that?
A couple of years back there was a big hullabaloo around disabling Anti-aliasing on N64 games – removing some of that characteristic blur, and BH certainly has blur. But before changing video interface settings, there’s actually a bigger problem we can solve. In its low-resolution mode the N64 outputs a video image of 320×240 pixels, but a lot of games don’t even manage to render this. During gameplay Body Harvest renders at 304×230 and then scales that image up to 320×240 for display, which results in a much blurrier output, what if it were to render at the full output resolution?
At 0x7984 in the NTSC ROM is a function that is called before gameplay starts to set the render resolution:
addiu sp, sp, $ffe8 # 00006d84:27bdffe8
sw ra, $0014(sp) # 00006d88:afbf0014
jal $0000f648 # 00006d8c:0c003d92 v FNC_0000f648
// setVideoInterfaceXSize = 304
addiu a0, zero, $0130 # 00006d90:24040130 a0=$00000130
jal $0000f67c # 00006d94:0c003d9f v FNC_0000f67c
// setVideoInterfaceYSize = 230
addiu a0, zero, $00e6 # 00006d98:240400e6 a0=$000000e6
lw ra, $0014(sp) # 00006d9c:8fbf0014
addiu sp, sp, $0018 # 00006da0:27bd0018
jr ra # 00006da4:03e00008
nop # 00006da8:00000000
If we change the arguments to the two function calls here to 0x140 and 0xF0 respectively, then the game renders the full output resolution and does no scaling, resulting in this:
That’s already a noticeable improvement! Presumably the lower resolution was chosen as a performance enhancement, but it’s a shame it was, the game looks a lot better without any scaling going on.
Can we do more?
At 0x10218 in the ROM is this function which sets two Video Interface Special Features, Gamma to off, and Dither to on.
addiu sp, sp, $ffe8 # 0000f618:27bdffe8
sw ra, $0014(sp) # 0000f61c:afbf0014
jal $0000a160 # 0000f620:0c002858 ^ FNC_0000a160
nop # 0000f624:00000000
jal $0001f310 # 0000f628:0c007cc4 v FNC_0001f310
// osViSetSpecialFeatures sets OS_VI_GAMMA_OFF
addiu a0, zero, $0002 # 0000f62c:24040002 a0=$00000002
jal $0001f310 # 0000f630:0c007cc4 v FNC_0001f310
// osViSetSpecialFeatures sets OS_VI_DITHER_FILTER_ON
addiu a0, zero, $0040 # 0000f634:24040040 a0=$00000040
lw ra, $0014(sp) # 0000f638:8fbf0014
addiu sp, sp, $0018 # 0000f63c:27bd0018
jr ra # 0000f640:03e00008
nop # 0000f644:00000000
If we switch the Dither on flag (0x40) for the off flag (0x80):
That makes a big difference, the textures are a lot clearer. Seeing the game run with dither off is a mixed bag though, everything looks quite grainy, like a film, I’m not sure I prefer it.
Now the Video Mode the game is currently running in is LAN1, where the A stands for Anti-aliasing, in theory we should be able to switch to LPN1, where the P stands for Point-sampling, to achieve even further visual clarity. However changing the call to osViSetMode to LPN1 doesn’t seem to make any visual difference at all, even on real hardware. It might be that some other value needs changing.
Additionally, as BH doesn’t make use of the Expansion Pak it should be possible to re-point the framebuffers there and switch to a higher resolution using one of the modes such as HPF1 or HPN1. However when I switch to one of those modes, set the resolutionn to 640×480 and point the (four times larger) framebuffers at some empty RAM, the results are quite odd.
Every second frame seems to have what looks something like the Z-buffer drawn half over it, and each vertical half of the frame seems to be drawn over the top of each other as a single frame. I’m not sure exactly what is going on here.