Crispy pixels in Body Harvest

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?

Body Harvest with no modifications, all screenshots are double-sized from PJ64 with angrylion’s video plugin.

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.

Exporting cars

Thought I’d take a break from the tracks and look at the cars again, which are a lot less complex. I’m now at the point where I can export a car with correct textures to an .obj file. Doing the reverse and putting a modified car back into the game is now just a matter of implementing it (unless there’s something I don’t know I don’t know… which is quite possible).

Car 20 imported to BlenderI’ll make a first release once importing a car is possible.

The power of visualization

Sometimes opening a file in a hex editor and staring at it is enough to begin working out patterns and possible meaning, other times I can see structure but the meaning is totally beyond comprehension.

Overlaying this data onto already understood data in a visual form can sometimes make its meaning clear. For example, each track collision section has an array of “Triangle” structs which describe the collision mesh. Each Triangle is comprised of 3 indices into the vertex array, and then 4 Bytes of unknown data. The last of these four Bytes is always a low value (<6), often the same value for most of the Triangles in a section, but I have no idea what-so-ever as to its meaning.
So as an experiment, I render the collision mesh and use this value to select a colour for each Triangle from an array of pre-defined colours:

Black Forest with the ground type colour coded

Black Forest with the ground type colour coded

Aha! These different colours match exactly with the different parts of the track, this value must be the collision’s type, which could be road, dirt, grass etc. I still don’t know where these types are defined but this is a good start.

Moving on, each collision vertex is defined as 3 floats for position, 4 Bytes for lighting color (R8G8B8A8), and two shorts whose purpose is unknown. The first short is sometimes 0xFFFF, and otherwise has similar values to the second short which seems to start at 0 for the first vertex and gradually increments by 1 for later vertices.
If I colour the mesh differently depending on whether or not this vertex has a 0xFFFF value, this is the result:

Sydney with the unknown vertex properties == 0xFFFF highlighted

Sydney with the unknown vertex properties == 0xFFFF highlighted

The collision sections with a 0xFFFF value are the sections where the different variations of the track diverge from each other.
If I instead alter the meshes colour depending on the value of the second short I get this:

Kyoto with the collision polygons coloured by the vertex unknown2 value

Kyoto with the collision polygons coloured by the vertex unknown2 value

I’m still not sure what this data is, but now I can see the overall pattern. Perhaps the increasing values are how the game determines if you are going the wrong way around the track, or each cars placing. Doing this colouring using the first shorts values shows that excepting the 0xFFFF values, these are forming a similar – but less segmented – pattern in reverse.

Kyoto with the collision polygons coloured by the vertex unknown1 value

Kyoto with the collision polygons coloured by the vertex unknown1 value

Dreamin’

So I spent the past months going down a rabbit hole. When the game loads a track it doesn’t just inflate all the parts and dump them into RAM, as it does, it modifies some of them, and even calculates some things and inserts them between the parts.
“Well”, I thought, “if the game does it, then I must too!” And that’s how I spent the past few months decompiling the track loading functions by hand. Only to realise, as I got deeper and deeper, that to do the job properly I would more or less have to write an N64 cpu emulator to emulate the whole game.

At that point I gave up. That point was yesterday.
When I thought about it, the data I want to replace is what’s in the ROM, not the RAM, so really I don’t care what the game does with the data once it’s in the RAM, so long as the data I put into the ROM is compatible.

Moving on then, I’ve now parsed the basics of the track models, bringing me back to where I was last time I tried this years ago. Onwards!

One of the Hawaii track variations

One of the Hawaii track variations

Normals

The 3 LoD models for the 1999 Stallion SR A

The 3 LoD models for the 1999 Stallion SR A

Happily the data I thought was normals turned out to be normals, which you can see rendered here. They’re most obviously used to show reflections on the cars.

Each car has the hi-def model made of 25 individual parts, and three LoD models, in addition to 4 wheels and a few other entries with <3 vertices. Not sure if those are used for something or just dummy data.

I haven’t really looked at the wheel data yet, at the moment they’re displaying with the texture only covering half of the wheel, I guess the bottom half is meant to be a mirror of the top but I have to find out what causes this (or is it hard-coded? wheel textures have their own table).
Still no idea how a given model/texture is assigned a palette, and I feel like there might be a problem with the palettes I’m inserting, for one model I’ve noticed the palette doesn’t seem to match the in-game one anymore.

Zlib it is :)

LoD models for cars 0, 6 and 33

LoD models for cars 0, 6 and 33

So it turns out the game does use an unaltered version of Zlib. My bad data was a result of assumptions about how the texture data is inserted into the car’s data blob.
At first glance it appears the texture data is just dumped straight into the gap that exists for it, but this is not the case. What I thought was a second table of texture descriptor pointers is actually a list of all the individual textures the car uses. After inflating the car’s data this table is walked through and used to transfer the needed textures into their positions within the blob, so in some cases not all the inflated data ends up transferred into the blob. As simple as that!

I was able to work this out pretty quickly using MAME, not only does it actually run WDC, the graphics are correct, and it has an excellent debugger with every feature I could dream of a N64 emulator having. I can’t believe I didn’t find out about this sooner. The only down-side is it runs very slowly, but frankly I couldn’t care less!

The reason I’m only showing LoD models and not the most detailed ones, is that the detailed models are split up into separate pieces (grill, bonnet left, bonnet right, etc) and I haven’t added the ability to draw them all at once yet. Soon, soon.