summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/gaelco3d.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/video/gaelco3d.c')
-rw-r--r--src/mame/video/gaelco3d.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/src/mame/video/gaelco3d.c b/src/mame/video/gaelco3d.c
new file mode 100644
index 00000000000..d346236242c
--- /dev/null
+++ b/src/mame/video/gaelco3d.c
@@ -0,0 +1,535 @@
+/*************************************************************************
+
+ Driver for Gaelco 3D games
+
+ driver by Aaron Giles
+
+**************************************************************************/
+
+#include "driver.h"
+#include "eminline.h"
+#include "gaelco3d.h"
+#include "video/rgbutil.h"
+#include "video/poly.h"
+
+UINT8 *gaelco3d_texture;
+UINT8 *gaelco3d_texmask;
+UINT32 gaelco3d_texture_size;
+UINT32 gaelco3d_texmask_size;
+
+
+#define MAX_POLYGONS 4096
+#define MAX_POLYDATA (MAX_POLYGONS * 21)
+#define MAX_VERTICES 32
+
+#define DISPLAY_TEXTURE 0
+#define LOG_POLYGONS 0
+#define DISPLAY_STATS 0
+
+#define IS_POLYEND(x) (((x) ^ ((x) >> 1)) & 0x4000)
+
+
+static mame_bitmap *screenbits;
+static mame_bitmap *zbuffer;
+static rgb_t *palette;
+static UINT32 *polydata_buffer;
+static UINT32 polydata_count;
+
+static int polygons;
+static int lastscan;
+static int video_changed;
+
+static poly_manager *poly;
+
+
+typedef struct _poly_extra_data poly_extra_data;
+struct _poly_extra_data
+{
+ UINT32 tex, color;
+ float ooz_dx, ooz_dy, ooz_base;
+ float uoz_dx, uoz_dy, uoz_base;
+ float voz_dx, voz_dy, voz_base;
+ float z0;
+};
+
+
+static void render_noz_noperspective(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
+static void render_normal(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
+static void render_alphablend(void *dest, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid);
+
+
+
+/*************************************
+ *
+ * Video init
+ *
+ *************************************/
+
+static void gaelco3d_exit(running_machine *machine)
+{
+ poly_free(poly);
+}
+
+
+VIDEO_START( gaelco3d )
+{
+ poly = poly_alloc(2000, sizeof(poly_extra_data), 0);
+ add_exit_callback(machine, gaelco3d_exit);
+
+ screenbits = auto_bitmap_alloc(machine->screen[0].width, machine->screen[0].height, machine->screen[0].format);
+
+ zbuffer = auto_bitmap_alloc(machine->screen[0].width, machine->screen[0].height, BITMAP_FORMAT_INDEXED16);
+
+ palette = auto_malloc(32768 * sizeof(palette[0]));
+ polydata_buffer = auto_malloc(MAX_POLYDATA * sizeof(polydata_buffer[0]));
+
+ /* save states */
+
+ state_save_register_global_pointer(palette, 32768);
+ state_save_register_global_pointer(polydata_buffer, MAX_POLYDATA);
+ state_save_register_global(polydata_count);
+
+ state_save_register_global(polygons);
+ state_save_register_global(lastscan);
+
+ state_save_register_bitmap("video", 0, "screenbits", screenbits);
+ state_save_register_bitmap("video", 0, "zbuffer", zbuffer);
+}
+
+
+
+/*************************************
+ *
+ * TMS32031 floating point conversion
+ *
+ *************************************/
+
+typedef union int_double
+{
+ double d;
+ float f[2];
+ UINT32 i[2];
+} int_double;
+
+
+static float dsp_to_float(UINT32 val)
+{
+ INT32 _mantissa = val << 8;
+ INT8 _exponent = (INT32)val >> 24;
+ int_double id;
+
+ if (_mantissa == 0 && _exponent == -128)
+ return 0;
+ else if (_mantissa >= 0)
+ {
+ int exponent = (_exponent + 127) << 23;
+ id.i[0] = exponent + (_mantissa >> 8);
+ }
+ else
+ {
+ int exponent = (_exponent + 127) << 23;
+ INT32 man = -_mantissa;
+ id.i[0] = 0x80000000 + exponent + ((man >> 8) & 0x00ffffff);
+ }
+ return id.f[0];
+}
+
+
+/*************************************
+ *
+ * Polygon rendering
+ *
+ *************************************/
+
+/*
+ Polygon data stream format:
+
+ data[0] (float) = scale factor to map Z to fixed Z buffer value
+ data[1] (float) = dvoz/dy (change in v/z per Y)
+ data[2] (float) = dvoz/dx (change in v/z per X)
+ data[3] (float) = dooz/dy (change in 1/z per Y)
+ data[4] (float) = dooz/dx (change in 1/z per X)
+ data[5] (float) = duoz/dy (change in u/z per Y)
+ data[6] (float) = duoz/dx (change in u/z per X)
+ data[7] (float) = voz origin (value of v/z at coordinate (0,0))
+ data[8] (float) = ooz origin (value of 1/z at coordinate (0,0))
+ data[9] (float) = uoz origin (value of u/z at coordinate (0,0))
+ data[10] (int) = palette base (bits 14-8)
+ data[11] (int) = texture address
+ data[12] (int) = start point (bits 15-8 = X coordinate, bits 12-0 = Y coordinate)
+ data[13] (int) = next point (bits 15-8 = X coordinate, bits 12-0 = Y coordinate)
+ data[14] (int) = 16.16 dx/dy from previous point to this point
+ (repeat these two for each additional point in the fan)
+*/
+
+static void render_poly(UINT32 *polydata)
+{
+ float midx = Machine->screen[0].width / 2;
+ float midy = Machine->screen[0].height / 2;
+ float z0 = dsp_to_float(polydata[0]);
+ float voz_dy = dsp_to_float(polydata[1]) * 256.0f;
+ float voz_dx = dsp_to_float(polydata[2]) * 256.0f;
+ float ooz_dy = dsp_to_float(polydata[3]);
+ float ooz_dx = dsp_to_float(polydata[4]);
+ float uoz_dy = dsp_to_float(polydata[5]) * 256.0f;
+ float uoz_dx = dsp_to_float(polydata[6]) * 256.0f;
+ float voz_base = dsp_to_float(polydata[7]) * 256.0f - midx * voz_dx - midy * voz_dy;
+ float ooz_base = dsp_to_float(polydata[8]) - midx * ooz_dx - midy * ooz_dy;
+ float uoz_base = dsp_to_float(polydata[9]) * 256.0f - midx * uoz_dx - midy * uoz_dy;
+ poly_extra_data *extra = poly_get_extra_data(poly);
+ int color = (polydata[10] & 0x7f) << 8;
+ poly_vertex vert[MAX_VERTICES];
+ UINT32 data;
+ int vertnum;
+
+ if (LOG_POLYGONS)
+ {
+ int t;
+ logerror("poly: %12.2f %12.2f %12.2f %12.2f %12.2f %12.2f %12.2f %12.2f %12.2f %12.2f %08X %08X (%4d,%4d) %08X",
+ (double)dsp_to_float(polydata[0]),
+ (double)dsp_to_float(polydata[1]),
+ (double)dsp_to_float(polydata[2]),
+ (double)dsp_to_float(polydata[3]),
+ (double)dsp_to_float(polydata[4]),
+ (double)dsp_to_float(polydata[5]),
+ (double)dsp_to_float(polydata[6]),
+ (double)dsp_to_float(polydata[7]),
+ (double)dsp_to_float(polydata[8]),
+ (double)dsp_to_float(polydata[9]),
+ polydata[10],
+ polydata[11],
+ (INT16)(polydata[12] >> 16), (INT16)(polydata[12] << 2) >> 2, polydata[12]);
+
+ logerror(" (%4d,%4d) %08X %08X", (INT16)(polydata[13] >> 16), (INT16)(polydata[13] << 2) >> 2, polydata[13], polydata[14]);
+ for (t = 15; !IS_POLYEND(polydata[t - 2]); t += 2)
+ logerror(" (%4d,%4d) %08X %08X", (INT16)(polydata[t] >> 16), (INT16)(polydata[t] << 2) >> 2, polydata[t], polydata[t+1]);
+ logerror("\n");
+ }
+
+ /* fill in extra data */
+ extra->tex = polydata[11];
+ extra->color = color;
+ extra->ooz_dx = ooz_dx;
+ extra->ooz_dy = ooz_dy;
+ extra->ooz_base = ooz_base;
+ extra->uoz_dx = uoz_dx;
+ extra->uoz_dy = uoz_dy;
+ extra->uoz_base = uoz_base;
+ extra->voz_dx = voz_dx;
+ extra->voz_dy = voz_dy;
+ extra->voz_base = voz_base;
+ extra->z0 = z0;
+
+ /* extract vertices */
+ data = 0;
+ for (vertnum = 0; vertnum < ARRAY_LENGTH(vert) && !IS_POLYEND(data); vertnum++)
+ {
+ /* extract vertex data */
+ data = polydata[13 + vertnum * 2];
+ vert[vertnum].x = midx + (float)((INT32)data >> 16) + 0.5f;
+ vert[vertnum].y = midy + (float)((INT32)(data << 18) >> 18) + 0.5f;
+ }
+
+ /* if we have a valid number of verts, render them */
+ if (vertnum >= 3)
+ {
+ /* special case: no Z buffering and no perspective correction */
+ if (color != 0x7f00 && z0 < 0 && ooz_dx == 0 && ooz_dy == 0)
+ poly_render_triangle_fan(poly, screenbits, &Machine->screen[0].visarea, render_noz_noperspective, 0, vertnum, &vert[0]);
+
+ /* general case: non-alpha blended */
+ else if (color != 0x7f00)
+ poly_render_triangle_fan(poly, screenbits, &Machine->screen[0].visarea, render_normal, 0, vertnum, &vert[0]);
+
+ /* color 0x7f seems to be hard-coded as a 50% alpha blend */
+ else
+ poly_render_triangle_fan(poly, screenbits, &Machine->screen[0].visarea, render_alphablend, 0, vertnum, &vert[0]);
+
+ polygons += vertnum - 2;
+ }
+}
+
+
+
+static void render_noz_noperspective(void *destbase, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
+{
+ const poly_extra_data *extra = extradata;
+ mame_bitmap *bitmap = destbase;
+ float zbase = recip_approx(extra->ooz_base);
+ float uoz_step = extra->uoz_dx * zbase;
+ float voz_step = extra->voz_dx * zbase;
+ int zbufval = (int)(-extra->z0 * zbase);
+ offs_t endmask = gaelco3d_texture_size - 1;
+ const rgb_t *palsource = palette + extra->color;
+ UINT32 tex = extra->tex;
+ UINT16 *dest = BITMAP_ADDR16(bitmap, scanline, 0);
+ UINT16 *zbuf = BITMAP_ADDR16(zbuffer, scanline, 0);
+ int startx = extent->startx;
+ float uoz = (extra->uoz_base + scanline * extra->uoz_dy + startx * extra->uoz_dx) * zbase;
+ float voz = (extra->voz_base + scanline * extra->voz_dy + startx * extra->voz_dx) * zbase;
+ int x;
+
+ for (x = startx; x < extent->stopx; x++)
+ {
+ int u = (int)uoz;
+ int v = (int)voz;
+ int pixeloffs = (tex + (v >> 8) * 4096 + (u >> 8)) & endmask;
+ if (pixeloffs >= gaelco3d_texmask_size || !gaelco3d_texmask[pixeloffs])
+ {
+ rgb_t rgb00 = palsource[gaelco3d_texture[pixeloffs]];
+ rgb_t rgb01 = palsource[gaelco3d_texture[(pixeloffs + 1) & endmask]];
+ rgb_t rgb10 = palsource[gaelco3d_texture[(pixeloffs + 4096) & endmask]];
+ rgb_t rgb11 = palsource[gaelco3d_texture[(pixeloffs + 4097) & endmask]];
+ rgb_t filtered = rgb_bilinear_filter(rgb00, rgb01, rgb10, rgb11, u, v);
+ dest[x] = (filtered & 0x1f) | ((filtered & 0x1ff800) >> 6);
+ zbuf[x] = zbufval;
+ }
+
+ /* advance texture params to the next pixel */
+ uoz += uoz_step;
+ voz += voz_step;
+ }
+}
+
+
+static void render_normal(void *destbase, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
+{
+ const poly_extra_data *extra = extradata;
+ mame_bitmap *bitmap = destbase;
+ float ooz_dx = extra->ooz_dx;
+ float uoz_dx = extra->uoz_dx;
+ float voz_dx = extra->voz_dx;
+ offs_t endmask = gaelco3d_texture_size - 1;
+ const rgb_t *palsource = palette + extra->color;
+ UINT32 tex = extra->tex;
+ float z0 = extra->z0;
+ UINT16 *dest = BITMAP_ADDR16(bitmap, scanline, 0);
+ UINT16 *zbuf = BITMAP_ADDR16(zbuffer, scanline, 0);
+ int startx = extent->startx;
+ float ooz = extra->ooz_base + scanline * extra->ooz_dy + startx * ooz_dx;
+ float uoz = extra->uoz_base + scanline * extra->uoz_dy + startx * uoz_dx;
+ float voz = extra->voz_base + scanline * extra->voz_dy + startx * voz_dx;
+ int x;
+
+ for (x = startx; x < extent->stopx; x++)
+ {
+ if (ooz > 0)
+ {
+ /* compute Z and check the Z buffer value first */
+ float z = recip_approx(ooz);
+ int zbufval = (int)(z0 * z);
+ if (zbufval < zbuf[x])
+ {
+ int u = (int)(uoz * z);
+ int v = (int)(voz * z);
+ int pixeloffs = (tex + (v >> 8) * 4096 + (u >> 8)) & endmask;
+ if (pixeloffs >= gaelco3d_texmask_size || !gaelco3d_texmask[pixeloffs])
+ {
+ rgb_t rgb00 = palsource[gaelco3d_texture[pixeloffs]];
+ rgb_t rgb01 = palsource[gaelco3d_texture[(pixeloffs + 1) & endmask]];
+ rgb_t rgb10 = palsource[gaelco3d_texture[(pixeloffs + 4096) & endmask]];
+ rgb_t rgb11 = palsource[gaelco3d_texture[(pixeloffs + 4097) & endmask]];
+ rgb_t filtered = rgb_bilinear_filter(rgb00, rgb01, rgb10, rgb11, u, v);
+ dest[x] = (filtered & 0x1f) | ((filtered & 0x1ff800) >> 6);
+ zbuf[x] = (zbufval < 0) ? -zbufval : zbufval;
+ }
+ }
+ }
+
+ /* advance texture params to the next pixel */
+ ooz += ooz_dx;
+ uoz += uoz_dx;
+ voz += voz_dx;
+ }
+}
+
+
+static void render_alphablend(void *destbase, INT32 scanline, const poly_extent *extent, const void *extradata, int threadid)
+{
+ const poly_extra_data *extra = extradata;
+ mame_bitmap *bitmap = destbase;
+ float ooz_dx = extra->ooz_dx;
+ float uoz_dx = extra->uoz_dx;
+ float voz_dx = extra->voz_dx;
+ offs_t endmask = gaelco3d_texture_size - 1;
+ const rgb_t *palsource = palette + extra->color;
+ UINT32 tex = extra->tex;
+ float z0 = extra->z0;
+ UINT16 *dest = BITMAP_ADDR16(bitmap, scanline, 0);
+ UINT16 *zbuf = BITMAP_ADDR16(zbuffer, scanline, 0);
+ int startx = extent->startx;
+ float ooz = extra->ooz_base + extra->ooz_dy * scanline + startx * ooz_dx;
+ float uoz = extra->uoz_base + extra->uoz_dy * scanline + startx * uoz_dx;
+ float voz = extra->voz_base + extra->voz_dy * scanline + startx * voz_dx;
+ int x;
+
+ for (x = startx; x < extent->stopx; x++)
+ {
+ if (ooz > 0)
+ {
+ /* compute Z and check the Z buffer value first */
+ float z = recip_approx(ooz);
+ int zbufval = (int)(z0 * z);
+ if (zbufval < zbuf[x])
+ {
+ int u = (int)(uoz * z);
+ int v = (int)(voz * z);
+ int pixeloffs = (tex + (v >> 8) * 4096 + (u >> 8)) & endmask;
+ if (pixeloffs >= gaelco3d_texmask_size || !gaelco3d_texmask[pixeloffs])
+ {
+ rgb_t rgb00 = palsource[gaelco3d_texture[pixeloffs]];
+ rgb_t rgb01 = palsource[gaelco3d_texture[(pixeloffs + 1) & endmask]];
+ rgb_t rgb10 = palsource[gaelco3d_texture[(pixeloffs + 4096) & endmask]];
+ rgb_t rgb11 = palsource[gaelco3d_texture[(pixeloffs + 4097) & endmask]];
+ rgb_t filtered = rgb_bilinear_filter(rgb00, rgb01, rgb10, rgb11, u, v) >> 1;
+ dest[x] = ((filtered & 0x0f) | ((filtered & 0x0f7800) >> 6)) + ((dest[x] >> 1) & 0x3def);
+ zbuf[x] = (zbufval < 0) ? -zbufval : zbufval;
+ }
+ }
+ }
+
+ /* advance texture params to the next pixel */
+ ooz += ooz_dx;
+ uoz += uoz_dx;
+ voz += voz_dx;
+ }
+}
+
+
+/*************************************
+ *
+ * Scene rendering
+ *
+ *************************************/
+
+void gaelco3d_render(void)
+{
+ /* wait for any queued stuff to complete */
+ poly_wait(poly, "Time to render");
+
+#if DISPLAY_STATS
+{
+ int scan = video_screen_get_vpos(0);
+ popmessage("Polys = %4d Timeleft = %3d", polygons, (lastscan < scan) ? (scan - lastscan) : (scan + (lastscan - Machine->screen[0].visarea.max_y)));
+}
+#endif
+
+ polydata_count = 0;
+ polygons = 0;
+ lastscan = -1;
+}
+
+
+
+/*************************************
+ *
+ * Renderer access
+ *
+ *************************************/
+
+WRITE32_HANDLER( gaelco3d_render_w )
+{
+ /* append the data to our buffer */
+ polydata_buffer[polydata_count++] = data;
+ if (polydata_count >= MAX_POLYDATA)
+ fatalerror("Out of polygon buffer space!");
+
+ /* if we've accumulated a completed poly set of data, queue it */
+ if (!video_skip_this_frame())
+ {
+ if (polydata_count >= 18 && (polydata_count % 2) == 1 && IS_POLYEND(polydata_buffer[polydata_count - 2]))
+ {
+ render_poly(&polydata_buffer[0]);
+ polydata_count = 0;
+ }
+ video_changed = TRUE;
+ }
+
+#if DISPLAY_STATS
+ lastscan = video_screen_get_vpos(0);
+#endif
+}
+
+
+
+/*************************************
+ *
+ * Palette access
+ *
+ *************************************/
+
+WRITE16_HANDLER( gaelco3d_paletteram_w )
+{
+ poly_wait(poly, "Palette change");
+ COMBINE_DATA(&paletteram16[offset]);
+ palette[offset] = ((paletteram16[offset] & 0x7fe0) << 6) | (paletteram16[offset] & 0x1f);
+}
+
+
+WRITE32_HANDLER( gaelco3d_paletteram_020_w )
+{
+ poly_wait(poly, "Palette change");
+ COMBINE_DATA(&paletteram32[offset]);
+ palette[offset*2+0] = ((paletteram32[offset] & 0x7fe00000) >> 10) | ((paletteram32[offset] & 0x1f0000) >> 16);
+ palette[offset*2+1] = ((paletteram32[offset] & 0x7fe0) << 6) | (paletteram32[offset] & 0x1f);
+}
+
+
+
+/*************************************
+ *
+ * Video update
+ *
+ *************************************/
+
+VIDEO_UPDATE( gaelco3d )
+{
+ int x, y, ret;
+
+ if (DISPLAY_TEXTURE && (input_code_pressed(KEYCODE_Z) || input_code_pressed(KEYCODE_X)))
+ {
+ static int xv = 0, yv = 0x1000;
+ UINT8 *base = gaelco3d_texture;
+ int length = gaelco3d_texture_size;
+
+ if (input_code_pressed(KEYCODE_X))
+ {
+ base = gaelco3d_texmask;
+ length = gaelco3d_texmask_size;
+ }
+
+ if (input_code_pressed(KEYCODE_LEFT) && xv >= 4)
+ xv -= 4;
+ if (input_code_pressed(KEYCODE_RIGHT) && xv < 4096 - 4)
+ xv += 4;
+
+ if (input_code_pressed(KEYCODE_UP) && yv >= 4)
+ yv -= 4;
+ if (input_code_pressed(KEYCODE_DOWN) && yv < 0x40000)
+ yv += 4;
+
+ for (y = cliprect->min_y; y <= cliprect->max_y; y++)
+ {
+ UINT16 *dest = BITMAP_ADDR16(bitmap, y, 0);
+ for (x = cliprect->min_x; x <= cliprect->max_x; x++)
+ {
+ int offs = (yv + y - cliprect->min_y) * 4096 + xv + x - cliprect->min_x;
+ if (offs < length)
+ dest[x] = base[offs];
+ else
+ dest[x] = 0;
+ }
+ }
+ popmessage("(%04X,%04X)", xv, yv);
+ }
+ else
+ {
+ if (video_changed)
+ copybitmap(bitmap, screenbits, 0,1, 0,0, cliprect, TRANSPARENCY_NONE, 0);
+ ret = video_changed;
+ video_changed = FALSE;
+ }
+
+ logerror("---update---\n");
+ return (!ret);
+}