Nintendo 64 console with EverDrive cartridge

Nintendo 64 Part 16: Z Buffering

, Nintendo 64, Programming

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.

Twice the cubes, twice the fun!