Nintendo 64 Part 16: Z Buffering
The Nintendo 64 has Z buffering, unlike its contemporary, the Sony PlayStation. Here is how I enabled it.
Defining the Z Buffer
Unlike the color buffer, the Z is not displayed on screen so it can be reused immediately. So there is only one Z buffer. I add it to a new section so I can set its location in the linker script.
static u16 zbuffer[SCREEN_WIDTH * SCREEN_HEIGHT]
__attribute__((section("uninit.zb"), aligned(16)));
Here is the linker script, which puts the framebuffer and the zbuffer in different banks, as recommended by the Nintendo 64 programming guide.
_heap1_start = ALIGN(16);
cfb 0x80200000 (NOLOAD) : {
_heap1_end = .;
*(uninit.cfb)
. = ALIGN(16);
_heap2_start = .;
}
zb 0x80300000 (NOLOAD) : {
_heap2_end = .;
*(uninit.zb)
. = ALIGN(16);
_heap3_start = .;
}
Clearing the Z Buffer
You clear the Z buffer the same way you clear the color buffer. That means that you make the Z buffer the current color buffer, and draw in a “color” which is the maximum Z value.
gDPSetCycleType(dl++, G_CYC_FILL);
gDPSetColorImage(dl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH,
zbuffer);
gDPSetFillColor(dl++, (GPACK_ZDZ(G_MAXFBZ, 0) << 16) |
GPACK_ZDZ(G_MAXFBZ, 0));
gDPFillRectangle(dl++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
Using the Z Buffer
All I need to do to use it is set the Z buffer, make the RSP generate Z values in the primitives it sends to the RDP, and enable a rendering mode which uses the Z buffer.
gDPPipeSync(dl++);
gDPSetDepthImage(dl++, gr->zbuffer);
gSPSetGeometryMode(dl++, G_ZBUFFER);
gDPSetRenderMode(dl++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2);
It Works!
The cubes are correctly rendered over each other.