diff options
Diffstat (limited to 'src/mame/video/gaelco3d.c')
-rw-r--r-- | src/mame/video/gaelco3d.c | 535 |
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); +} |