/* ResidualVM - A 3D game interpreter * * ResidualVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /* * This file is based on, or a modified version of code from TinyGL (C) 1997-1998 Fabrice Bellard, * which is licensed under the zlib-license (see LICENSE). * It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later). */ #include "graphics/tinygl/zbuffer.h" namespace TinyGL { template FORCEINLINE static void putPixel(FrameBuffer *buffer, int pixelOffset, const Graphics::PixelFormat &cmode, unsigned int *pz, unsigned int &z, int &color, unsigned int &r, unsigned int &g, unsigned int &b) { if (buffer->scissorPixel(pixelOffset)) { return; } if (kInterpZ) { if (buffer->compareDepth(z, *pz)) { if (kInterpRGB) { buffer->writePixel(pixelOffset, RGB_TO_PIXEL(r, g, b)); } else { buffer->writePixel(pixelOffset, color); } if (kDepthWrite) { *pz = z; } } } else { if (kInterpRGB) { buffer->writePixel(pixelOffset, RGB_TO_PIXEL(r, g, b)); } else { buffer->writePixel(pixelOffset, color); } } } template FORCEINLINE static void drawLine(FrameBuffer *buffer, ZBufferPoint *p1, ZBufferPoint *p2, int &pixelOffset, const Graphics::PixelFormat &cmode, unsigned int *pz, unsigned int &z, int &color, unsigned int &r, unsigned int &g, unsigned int &b, int dx, int dy, int inc_1, int inc_2) { int n = dx; int rinc, ginc, binc; int zinc; if (kInterpZ) { zinc = (p2->z - p1->z) / n; } if (kInterpRGB) { rinc = ((p2->r - p1->r) << 8) / n; ginc = ((p2->g - p1->g) << 8) / n; binc = ((p2->b - p1->b) << 8) / n; } int a = 2 * dy - dx; dy = 2 * dy; dx = 2 * dx - dy; int pp_inc_1 = (inc_1); int pp_inc_2 = (inc_2); do { putPixel(buffer, pixelOffset, cmode, pz, z, color, r, g, b); if (kInterpZ) { z += zinc; } if (kInterpRGB) { r += rinc; g += ginc; b += binc; } if (a > 0) { pixelOffset += pp_inc_1; if (kInterpZ) { pz += inc_1; } a -= dx; } else { pixelOffset += pp_inc_2; if (kInterpZ) { pz += inc_2; } a += dy; } } while (--n >= 0); } template void FrameBuffer::fillLineGeneric(ZBufferPoint *p1, ZBufferPoint *p2, int color) { int dx, dy, sx; unsigned int r, g, b; unsigned int *pz = NULL; unsigned int z; int pixelOffset; if (p1->y > p2->y || (p1->y == p2->y && p1->x > p2->x)) { ZBufferPoint *tmp; tmp = p1; p1 = p2; p2 = tmp; } sx = xsize; pixelOffset = xsize * p1->y + p1->x; if (kInterpZ) { pz = _zbuf + (p1->y * sx + p1->x); z = p1->z; } dx = p2->x - p1->x; dy = p2->y - p1->y; if (kInterpRGB) { r = p2->r << 8; g = p2->g << 8; b = p2->b << 8; } if (dx == 0 && dy == 0) { putPixel(this, pixelOffset, cmode, pz, z, color, r, g, b); } else if (dx > 0) { if (dx >= dy) { drawLine(this, p1, p2, pixelOffset, cmode, pz, z, color, r, g, b, dx, dy, sx + 1, 1); } else { drawLine(this, p1, p2, pixelOffset, cmode, pz, z, color, r, g, b, dx, dy, sx + 1, sx); } } else { dx = -dx; if (dx >= dy) { drawLine(this, p1, p2, pixelOffset, cmode, pz, z, color, r, g, b, dx, dy, sx - 1, -1); } else { drawLine(this, p1, p2, pixelOffset, cmode, pz, z, color, r, g, b, dx, dy, sx - 1, sx); } } } void FrameBuffer::plot(ZBufferPoint *p) { unsigned int *pz; unsigned int r, g, b; pz = _zbuf + (p->y * xsize + p->x); int col = RGB_TO_PIXEL(p->r, p->g, p->b); unsigned int z = p->z; if (_depthWrite && _depthTestEnabled) putPixel(this, linesize * p->y + p->x * PSZB, cmode, pz, z, col, r, g, b); else putPixel(this, linesize * p->y + p->x * PSZB, cmode, pz, z, col, r, g, b); } void FrameBuffer::fillLineFlatZ(ZBufferPoint *p1, ZBufferPoint *p2, int color) { if (_depthWrite && _depthTestEnabled) fillLineGeneric(p1, p2, color); else fillLineGeneric(p1, p2, color); } // line with color interpolation void FrameBuffer::fillLineInterpZ(ZBufferPoint *p1, ZBufferPoint *p2) { if (_depthWrite && _depthTestEnabled) fillLineGeneric(p1, p2, 0); else fillLineGeneric(p1, p2, 0); } // no Z interpolation void FrameBuffer::fillLineFlat(ZBufferPoint *p1, ZBufferPoint *p2, int color) { if (_depthWrite && _depthTestEnabled) fillLineGeneric(p1, p2, color); else fillLineGeneric(p1, p2, color); } void FrameBuffer::fillLineInterp(ZBufferPoint *p1, ZBufferPoint *p2) { if (_depthWrite && _depthTestEnabled) fillLineGeneric(p1, p2, 0); else fillLineGeneric(p1, p2, 0); } void FrameBuffer::fillLineZ(ZBufferPoint *p1, ZBufferPoint *p2) { int color1, color2; color1 = RGB_TO_PIXEL(p1->r, p1->g, p1->b); color2 = RGB_TO_PIXEL(p2->r, p2->g, p2->b); // choose if the line should have its color interpolated or not if (color1 == color2) { fillLineFlatZ(p1, p2, color1); } else { fillLineInterpZ(p1, p2); } } void FrameBuffer::fillLine(ZBufferPoint *p1, ZBufferPoint *p2) { int color1, color2; color1 = RGB_TO_PIXEL(p1->r, p1->g, p1->b); color2 = RGB_TO_PIXEL(p2->r, p2->g, p2->b); // choose if the line should have its color interpolated or not if (color1 == color2) { fillLineFlat(p1, p2, color1); } else { fillLineInterp(p1, p2); } } } // end of namespace TinyGL