Graphics3D

Software 3D rasterizer. Renders Gouraud-shaded, flat-coloured and textured triangles into an integer pixel buffer.

The pixel format is Alpha<<24 | Red<<16 | Green<<8 | Blue, which is what the PColorBGRA buffer TImage.FromData reads, so a finished buffer becomes an image without being walked again. Colours are palette indices rather than colours, looked up in ColorUtils.Palette - the one palette the whole library draws through, so there is nothing to point this at and nothing to keep in step.


Graphics3D.SetDrawRegion

procedure TGraphics3D.SetDrawRegion(bounds: TBox);

Restricts drawing to bounds, clipped to the buffer. Call TGraphics3D.SetRasterClipping afterwards to rebuild the row table for the new region.


Graphics3D.SetRasterBuffer

procedure TGraphics3D.SetRasterBuffer(constref buf: TIntegerArray; w, h: Integer);

Points the rasterizer at the pixel buffer it should draw into and resets the draw region to all of it.

The buffer is shared rather than copied, so whatever owns it sees the drawing as it happens.


Graphics3D.UpdateClipDims

procedure TGraphics3D.UpdateClipDims();

Recentres projection on the middle of the clip region and recomputes the bounds triangles are tested against.


Graphics3D.SetRasterClipping

procedure TGraphics3D.SetRasterClipping();

Prepares the rasterizer for the current draw region.

ClipY ends up holding the buffer offset of each row of that region, so a scanline can start from a single lookup instead of a multiply. It is grown to the next power of two and kept, since this runs per rendered sprite.


Graphics3D.SetOffset

procedure TGraphics3D.SetOffset(x, y: Integer);

Moves the point that projection treats as the centre to x, y within the buffer.

An item icon is drawn around the middle of its 36 by 32 canvas rather than around the middle of the draw region, which is what this is for.


Graphics3D.GouraudRaster

procedure TGraphics3D.GouraudRaster(baseIdx, leftX, rightX, startColor, colorStep: Integer);

Interpolates a colour value across [leftX, rightX] using a fixed-point step, looks up each pixel in the colour palette, and optionally applies alpha blending.

startColor is the colour extrapolated back to x = 0, not the colour at leftX - this re-adds leftX * colorStep itself, so that clipping leftX to 0 still lands on the right colour. When RasterGouraudLowRes is true, every 4 pixels share one colour for speed (as the original client does on low-res mode).


Graphics3D.GouraudTriangle

procedure TGraphics3D.GouraudTriangle(y0, y1, y2, x0, x1, x2, c0, c1, c2: Integer);

Fills a triangle whose three corners each have their own colour, blending between them across the face.

The three vertices are sorted by height and the triangle is walked one scanline at a time, with the two edges bounding that scanline and the colour along it all advanced by fixed point steps rather than recomputed. Colour is a palette index, so what is interpolated is the index and not the colour itself - which is why shading a face means walking its palette rather than mixing channels. Each scanline is handed to TGraphics3D.GouraudRaster.

A degenerate triangle - one with no area - is dropped, since it has no gradient to interpolate along.


Graphics3D.FlatRaster

procedure TGraphics3D.FlatRaster(baseIdx, leftX, rightX, color: Integer);

Fills one scanline of a flat triangle with a single colour, blending it into what is already there when the face is translucent.

An alpha of 254 doesn’t draw the colour at all. Instead every pixel of the scanline takes the colour of the pixel to its right, smearing what is already drawn one pixel to the left. That is a quirk of the client, whose copy reads the next pixel after it has already moved on to it, and models use such faces on purpose, so it is reproduced here.


Graphics3D.RasterFlat

procedure TGraphics3D.RasterFlat(x0, x1, x2, y0, y1, y2, color: Integer);

Fills a triangle with a single colour.

It walks the triangle exactly as TGraphics3D.GouraudTriangle does - sorted by height, one scanline at a time, edges advanced by fixed point steps - only without a colour to carry along, and hands each scanline to TGraphics3D.FlatRaster. Unlike a shaded triangle, one with no area is still walked.


Graphics3D.TexturedRaster

procedure TGraphics3D.TexturedRaster(baseIdx, leftX, rightX, shade, shadeStep, u, v, w, uStep, vStep, wStep: Integer; const texels: TIntegerArray; opaque: Boolean);

Fills one scanline of a textured triangle.

u, v and w are the texture coordinates and depth of the scanline, not yet divided by depth, and each Step is how much they change per pixel. Dividing by depth for every pixel is what keeps a texture from warping across a face, but it is only done every 8 pixels, with the texture position stepped linearly in between, the same trade the client makes.

The texel position is packed into one integer, the column in its top 7 bits and the row in bits 7 to 13, so that one addition moves along both at once. The column is clamped to the texture while the row wraps around it.

shade darkens each texel by the face’s lighting. opaque is the texture’s own flag: when it is not set, texels that are 0 are holes and leave the pixel underneath alone.


Graphics3D.TexturedTriangle

procedure TGraphics3D.TexturedTriangle(y0, y1, y2, x0, x1, x2, c0, c1, c2, localX0, localX1, localX2, localY0, localY1, localY2, localZ0, localZ1, localZ2: Integer; const texels: TIntegerArray; opaque: Boolean);

Fills a triangle with a texture, perspective correct, and shaded by a lighting value at each corner.

x and y are the corners on screen and c their lighting, as for TGraphics3D.GouraudTriangle, except that c is a light level from 2 to 126 rather than a palette index. The local coordinates are three points in camera space that set how the texture lies across the face: the first is where the texture’s origin sits and the other two are where its edges run to. They are usually the face’s own corners, but a model can stretch one texture across several faces by pointing them all at the same three points.

texels is a 128 by 128 texture from TTextureLoader.Texels and opaque is its TTextureDefinition flag.

The triangle is walked exactly like TGraphics3D.GouraudTriangle, carrying the texture coordinates of the start of each scanline along with it for TGraphics3D.TexturedRaster.