diff options
Diffstat (limited to 'src/mame/sega/model3_v.cpp')
-rw-r--r-- | src/mame/sega/model3_v.cpp | 2488 |
1 files changed, 2488 insertions, 0 deletions
diff --git a/src/mame/sega/model3_v.cpp b/src/mame/sega/model3_v.cpp new file mode 100644 index 00000000000..df181d55a23 --- /dev/null +++ b/src/mame/sega/model3_v.cpp @@ -0,0 +1,2488 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont, Ville Linde +#include "emu.h" +#include "video/rgbutil.h" +#include "model3.h" + +/* + TODO: + - Tilemap flash effect + - Fog + - Mipmapping + - Mipmap uploads smaller than a tile + - Some of the 4-bit and 8-bit textures need their alpha values rechecked + - Spotlights + - Recheck normal vector transform + +*/ + +#define ENABLE_BILINEAR 1 + +#define TRI_PARAM_TEXTURE_PAGE 0x1 +#define TRI_PARAM_TEXTURE_MIRROR_U 0x2 +#define TRI_PARAM_TEXTURE_MIRROR_V 0x4 +#define TRI_PARAM_TEXTURE_ENABLE 0x8 +#define TRI_PARAM_ALPHA_TEST 0x10 +#define TRI_PARAM_COLOR_MOD 0x20 + +#define TRI_BUFFER_SIZE 50000 +#define TRI_ALPHA_BUFFER_SIZE 15000 + + +/*****************************************************************************/ + +/* matrix stack */ +#define MATRIX_STACK_SIZE 256 + + +#define BYTE_REVERSE32(x) (((x >> 24) & 0xff) | \ + ((x >> 8) & 0xff00) | \ + ((x << 8) & 0xff0000) | \ + ((x << 24) & 0xff000000)) + +#define BYTE_REVERSE16(x) (((x >> 8) & 0xff) | ((x << 8) & 0xff00)) + + +void model3_state::model3_exit() +{ +#if 0 + FILE* file; + int i; + file = fopen("m3_texture_ram.bin","wb"); + for (i=0; i < 0x200000; i++) + { + fputc((uint8_t)(m_texture_ram[0][i] >> 8), file); + fputc((uint8_t)(m_texture_ram[0][i] >> 0), file); + } + for (i=0; i < 0x200000; i++) + { + fputc((uint8_t)(m_texture_ram[1][i] >> 8), file); + fputc((uint8_t)(m_texture_ram[1][i] >> 0), file); + } + fclose(file); + + file = fopen("m3_displist.bin","wb"); + for (i=0; i < 0x40000; i++) + { + fputc((uint8_t)(m_display_list_ram[i] >> 24), file); + fputc((uint8_t)(m_display_list_ram[i] >> 16), file); + fputc((uint8_t)(m_display_list_ram[i] >> 8), file); + fputc((uint8_t)(m_display_list_ram[i] >> 0), file); + } + fclose(file); + + file = fopen("m3_culling_ram.bin","wb"); + for (i=0; i < 0x100000; i++) + { + fputc((uint8_t)(m_culling_ram[i] >> 24), file); + fputc((uint8_t)(m_culling_ram[i] >> 16), file); + fputc((uint8_t)(m_culling_ram[i] >> 8), file); + fputc((uint8_t)(m_culling_ram[i] >> 0), file); + } + fclose(file); + + file = fopen("m3_polygon_ram.bin","wb"); + for (i=0; i < 0x100000; i++) + { + fputc((uint8_t)(m_polygon_ram[i] >> 24), file); + fputc((uint8_t)(m_polygon_ram[i] >> 16), file); + fputc((uint8_t)(m_polygon_ram[i] >> 8), file); + fputc((uint8_t)(m_polygon_ram[i] >> 0), file); + } + fclose(file); + + file = fopen("m3_vrom.bin","wb"); + for (i=0; i < 0x1000000; i++) + { + fputc((uint8_t)(m_vrom[i] >> 24), file); + fputc((uint8_t)(m_vrom[i] >> 16), file); + fputc((uint8_t)(m_vrom[i] >> 8), file); + fputc((uint8_t)(m_vrom[i] >> 0), file); + } + fclose(file); +#endif + + invalidate_texture(0, 0, 0, 6, 5); + invalidate_texture(1, 0, 0, 6, 5); +} + +void model3_state::video_start() +{ + static const gfx_layout char4_layout = + { + 8, 8, + 31744, + 4, + { 0,1,2,3 }, + { 0*4, 1*4, 2*4, 3*4, 4*4, 5*4, 6*4, 7*4 }, + { 1*32, 0*32, 3*32, 2*32, 5*32, 4*32, 7*32, 6*32 }, + 4 * 8*8 + }; + + static const gfx_layout char8_layout = + { + 8, 8, + 15872, + 8, + { 0,1,2,3,4,5,6,7 }, + { 4*8, 5*8, 6*8, 7*8, 0*8, 1*8, 2*8, 3*8 }, + { 0*64, 1*64, 2*64, 3*64, 4*64, 5*64, 6*64, 7*64 }, + 8 * 8*8 + }; + + int width = m_screen->width(); + int height = m_screen->height(); + + m_renderer = std::make_unique<model3_renderer>(machine(), width, height); + + m_tri_buffer = std::make_unique<m3_triangle[]>(TRI_BUFFER_SIZE); + m_tri_alpha_buffer = std::make_unique<m3_triangle[]>(TRI_ALPHA_BUFFER_SIZE); + + machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&model3_state::model3_exit, this)); + + m_m3_char_ram = make_unique_clear<uint64_t[]>(0x100000/8); + m_m3_tile_ram = make_unique_clear<uint64_t[]>(0x8000/8); + + m_texture_fifo = make_unique_clear<uint32_t[]>(0x100000/4); + + /* 2x 4MB texture sheets */ + m_texture_ram[0] = std::make_unique<uint16_t[]>(0x400000/2); + m_texture_ram[1] = std::make_unique<uint16_t[]>(0x400000/2); + + /* 1MB Display List RAM */ + m_display_list_ram = make_unique_clear<uint32_t[]>(0x100000/4); + /* 4MB for nodes (< Step 2.0 have only 2MB) */ + m_culling_ram = make_unique_clear<uint32_t[]>(0x400000/4); + /* 4MB Polygon RAM */ + m_polygon_ram = make_unique_clear<uint32_t[]>(0x400000/4); + + m_vid_reg0 = 0; + + std::fill_n(&m_texcache[0][0][0], 2 * (1024 / 32) * (2048 / 32), nullptr); + + m_layer4[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(model3_state::tile_info_layer0_4bit)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); + m_layer8[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(model3_state::tile_info_layer0_8bit)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); + m_layer4[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(model3_state::tile_info_layer1_4bit)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); + m_layer8[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(model3_state::tile_info_layer1_8bit)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); + m_layer4[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(model3_state::tile_info_layer2_4bit)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); + m_layer8[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(model3_state::tile_info_layer2_8bit)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); + m_layer4[3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(model3_state::tile_info_layer3_4bit)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); + m_layer8[3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(model3_state::tile_info_layer3_8bit)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64); + + // 4-bit tiles + m_gfxdecode->set_gfx(0, std::make_unique<gfx_element>(m_palette, char4_layout, (uint8_t*)m_m3_char_ram.get(), 0, m_palette->entries() / 16, 0)); + + // 8-bit tiles + m_gfxdecode->set_gfx(1, std::make_unique<gfx_element>(m_palette, char8_layout, (uint8_t*)m_m3_char_ram.get(), 0, m_palette->entries() / 256, 0)); + + init_matrix_stack(); +} + +#define MODEL3_TILE_INFO4(address) \ +do { \ + uint16_t *tiles = (uint16_t*)&m_m3_tile_ram[address + (tile_index / 4)]; \ + uint16_t t = BYTE_REVERSE16(tiles[(tile_index & 3) ^ NATIVE_ENDIAN_VALUE_LE_BE(2,0)]); \ + int tile = ((t << 1) & 0x7ffe) | ((t >> 15) & 0x1); \ + int color = (t & 0x7ff0) >> 4; \ + tileinfo.set(0, tile, color, 0); \ +} while (0) + +#define MODEL3_TILE_INFO8(address) \ +do { \ + uint16_t *tiles = (uint16_t*)&m_m3_tile_ram[address + (tile_index / 4)]; \ + uint16_t t = BYTE_REVERSE16(tiles[(tile_index & 3) ^ NATIVE_ENDIAN_VALUE_LE_BE(2,0)]); \ + int tile = ((t << 1) & 0x7ffe) | ((t >> 15) & 0x1); \ + int color = (t & 0x7f00) >> 8; \ + tileinfo.set(1, tile >> 1, color, 0); \ +} while (0) + +TILE_GET_INFO_MEMBER(model3_state::tile_info_layer0_4bit) { MODEL3_TILE_INFO4(0x000); } +TILE_GET_INFO_MEMBER(model3_state::tile_info_layer0_8bit) { MODEL3_TILE_INFO8(0x000); } +TILE_GET_INFO_MEMBER(model3_state::tile_info_layer1_4bit) { MODEL3_TILE_INFO4(0x400); } +TILE_GET_INFO_MEMBER(model3_state::tile_info_layer1_8bit) { MODEL3_TILE_INFO8(0x400); } +TILE_GET_INFO_MEMBER(model3_state::tile_info_layer2_4bit) { MODEL3_TILE_INFO4(0x800); } +TILE_GET_INFO_MEMBER(model3_state::tile_info_layer2_8bit) { MODEL3_TILE_INFO8(0x800); } +TILE_GET_INFO_MEMBER(model3_state::tile_info_layer3_4bit) { MODEL3_TILE_INFO4(0xc00); } +TILE_GET_INFO_MEMBER(model3_state::tile_info_layer3_8bit) { MODEL3_TILE_INFO8(0xc00); } + +#ifdef UNUSED_FUNCTION +void model3_state::draw_texture_sheet(bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + for(int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + uint16_t *const d = &bitmap.pix(y); + int index = (y*2)*2048; + for(int x = cliprect.min_x; x <= cliprect.max_x; x++) { + uint16_t pix = m_texture_ram[0][index]; + index+=4; + if(pix != 0) { + d[x] = pix; + } + } + } +} +#endif + +void model3_state::draw_layer(bitmap_rgb32 &bitmap, const rectangle &cliprect, int layer, int sx, int sy, int prio) +{ + int bitdepth = (m_layer_priority & (0x10 << layer)) ? 1 : 0; +// int layer_prio = (m_layer_priority & (0x1 << layer)) ? 1 : 0; + + tilemap_t *tmap = bitdepth ? m_layer4[layer] : m_layer8[layer]; + bitmap_ind16 &pixmap = tmap->pixmap(); + const pen_t *pens = m_palette->pens(); + + uint32_t* palram = (uint32_t*)&m_paletteram64[0]; + uint16_t* rowscroll_ram = (uint16_t*)&m_m3_char_ram[0x1ec00]; + uint32_t* rowmask_ram = (uint32_t*)&m_m3_char_ram[0x1ee00]; + + int x1 = cliprect.min_x; + int y1 = cliprect.min_y; + int x2 = cliprect.max_x; + int y2 = cliprect.max_y; + + int ix = sx; + int iy = sy; + if (ix < 0) + { + ix = 0 - ix; + } + if (iy < 0) + { + iy = 0 - iy; + } + + for (int y = y1; y <= y2; y++) + { + uint32_t *const dst = &bitmap.pix(y); + uint16_t const *const src = &pixmap.pix(iy & 0x1ff); + + int rowscroll = BYTE_REVERSE16(rowscroll_ram[((layer * 0x200) + y) ^ NATIVE_ENDIAN_VALUE_LE_BE(3,0)]) & 0x7fff; + if (rowscroll & 0x100) + rowscroll |= ~0x1ff; + + uint16_t rowmask; + if (prio && (layer == 1 || layer == 2)) + rowmask = BYTE_REVERSE32(rowmask_ram[(y & 0x1ff) ^ NATIVE_ENDIAN_VALUE_LE_BE(1,0)]) & 0xffff; + else + rowmask = 0xffff; + + int iix = ix & 0x1ff; + + int rx1 = x1 - (rowscroll * 2); + int rx2 = x2 - (rowscroll * 2); + + if (rx1 < 0) + { + iix += (0 - rx1); + rx1 = 0; + } + if (rx2 > cliprect.max_x) + rx2 = cliprect.max_x; + + for (int x = rx1; x <= rx2; x++) + { + uint32_t mask = rowmask & (1 << ((iix & 0x1ff) >> 5)); + + if (mask) + { + uint16_t p0 = src[iix & 0x1ff]; + if ((palram[p0^NATIVE_ENDIAN_VALUE_LE_BE(1,0)] & NATIVE_ENDIAN_VALUE_LE_BE(0x00800000,0x00008000)) == 0) + { + dst[x] = pens[p0]; + } + } + iix++; + } + + iy++; + } +} + +uint32_t model3_state::screen_update_model3(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + int layer_scroll_x[4], layer_scroll_y[4]; + uint32_t layer_data[4]; + + layer_data[0] = BYTE_REVERSE32((uint32_t)(m_layer_scroll[0] >> 32)); + layer_data[1] = BYTE_REVERSE32((uint32_t)(m_layer_scroll[0] >> 0)); + layer_data[2] = BYTE_REVERSE32((uint32_t)(m_layer_scroll[1] >> 32)); + layer_data[3] = BYTE_REVERSE32((uint32_t)(m_layer_scroll[1] >> 0)); + layer_scroll_x[0] = layer_data[0] & 0x1ff; + layer_scroll_y[0] = (layer_data[0] >> 16) & 0x1ff; + layer_scroll_x[1] = layer_data[1] & 0x1ff; + layer_scroll_y[1] = (layer_data[1] >> 16) & 0x1ff; + layer_scroll_x[2] = layer_data[2] & 0x1ff; + layer_scroll_y[2] = (layer_data[2] >> 16) & 0x1ff; + layer_scroll_x[3] = layer_data[3] & 0x1ff; + layer_scroll_y[3] = (layer_data[3] >> 16) & 0x1ff; + + m_screen_clip = (rectangle*)&cliprect; + m_clip3d = cliprect; + + bitmap.fill(0, cliprect); + + // render enabled layers with priority 0 + if ((layer_data[3] & 0x80000000) && (m_layer_priority & 0x8) == 0) + draw_layer(bitmap, cliprect, 3, layer_scroll_x[3], layer_scroll_y[3], 0); + if ((layer_data[2] & 0x80000000) && (m_layer_priority & 0x4) == 0) + draw_layer(bitmap, cliprect, 2, layer_scroll_x[2], layer_scroll_y[2], 0); + if ((layer_data[1] & 0x80000000) && (m_layer_priority & 0x2) == 0) + draw_layer(bitmap, cliprect, 1, layer_scroll_x[1], layer_scroll_y[1], 0); + if ((layer_data[0] & 0x80000000) && (m_layer_priority & 0x1) == 0) + draw_layer(bitmap, cliprect, 0, layer_scroll_x[0], layer_scroll_y[0], 0); + + // render 3D + m_renderer->draw(bitmap, cliprect); + + // render enabled layers with priority 1 + if ((layer_data[3] & 0x80000000) && (m_layer_priority & 0x8) != 0) + draw_layer(bitmap, cliprect, 3, layer_scroll_x[3], layer_scroll_y[3], 1); + if ((layer_data[2] & 0x80000000) && (m_layer_priority & 0x4) != 0) + draw_layer(bitmap, cliprect, 2, layer_scroll_x[2], layer_scroll_y[2], 1); + if ((layer_data[0] & 0x80000000) && (m_layer_priority & 0x1) != 0) + draw_layer(bitmap, cliprect, 0, layer_scroll_x[0], layer_scroll_y[0], 1); + if ((layer_data[1] & 0x80000000) && (m_layer_priority & 0x2) != 0) + draw_layer(bitmap, cliprect, 1, layer_scroll_x[1], layer_scroll_y[1], 1); + + return 0; +} + + + +uint64_t model3_state::model3_char_r(offs_t offset) +{ + return m_m3_char_ram[offset]; +} + +void model3_state::model3_char_w(offs_t offset, uint64_t data, uint64_t mem_mask) +{ + COMBINE_DATA(&m_m3_char_ram[offset]); + m_gfxdecode->gfx(0)->mark_dirty(offset / 4); + m_gfxdecode->gfx(1)->mark_dirty(offset / 8); +} + +uint64_t model3_state::model3_tile_r(offs_t offset) +{ + return m_m3_tile_ram[offset]; +} + +void model3_state::model3_tile_w(offs_t offset, uint64_t data, uint64_t mem_mask) +{ + COMBINE_DATA(&m_m3_tile_ram[offset]); + + /* + m_layer4[0]->mark_all_dirty(); + m_layer8[0]->mark_all_dirty(); + m_layer4[1]->mark_all_dirty(); + m_layer8[1]->mark_all_dirty(); + m_layer4[2]->mark_all_dirty(); + m_layer8[2]->mark_all_dirty(); + m_layer4[3]->mark_all_dirty(); + m_layer8[3]->mark_all_dirty(); + */ + + int layer = (offset >> 10) & 0x3; + int tile = (offset & 0x3ff) * 4; + m_layer4[layer]->mark_tile_dirty(tile+0); + m_layer4[layer]->mark_tile_dirty(tile+1); + m_layer4[layer]->mark_tile_dirty(tile+2); + m_layer4[layer]->mark_tile_dirty(tile+3); + m_layer8[layer]->mark_tile_dirty(tile+0); + m_layer8[layer]->mark_tile_dirty(tile+1); + m_layer8[layer]->mark_tile_dirty(tile+2); + m_layer8[layer]->mark_tile_dirty(tile+3); +} + +/* + Video registers: + + 0xF1180000: ? + 0xF1180004: ? + 0xF1180008: ? lostwsga: writes 0x7f010000 + lemans24, magtruck, von2, lamachin: writes 0xee000000 + bass, vs2, harley, scud, skichamp, fvipers2, eca: writes 0xef000000 + srally2, swtrilgy: writes 0x70010000 + daytona2: writes 0x4f010000 + + 0xF1180010: VBL IRQ acknowledge + + 0xF1180020: xxxxxxxx -------- -------- -------- ? + -------- x------- -------- -------- Layer 3 bitdepth (0 = 8-bit, 1 = 4-bit) + -------- -x------ -------- -------- Layer 2 bitdepth (0 = 8-bit, 1 = 4-bit) + -------- --x----- -------- -------- Layer 1 bitdepth (0 = 8-bit, 1 = 4-bit) + -------- ---x---- -------- -------- Layer 0 bitdepth (0 = 8-bit, 1 = 4-bit) + -------- ----x--- -------- -------- Layer 3 priority (0 = below 3D, 1 = above 3D) + -------- -----x-- -------- -------- Layer 2 priority (0 = below 3D, 1 = above 3D) + -------- ------x- -------- -------- Layer 1 priority (0 = below 3D, 1 = above 3D) + -------- -------x -------- -------- Layer 0 priority (0 = below 3D, 1 = above 3D) + + 0xF1180040: Foreground layer color modulation? + -------- xxxxxxxx -------- -------- Red component + -------- -------- xxxxxxxx -------- Green component + -------- -------- -------- xxxxxxxx Blue component + + 0xF1180044: Background layer color modulation? + -------- xxxxxxxx -------- -------- Red component + -------- -------- xxxxxxxx -------- Green component + -------- -------- -------- xxxxxxxx Blue component + + 0xF1180060: x------- -------- -------- -------- Layer 0 enable + -------x xxxxxxxx -------- -------- Layer 0 Y scroll position + -------- -------- -------x xxxxxxxx Layer 0 X scroll position + + 0xF1180064: x------- -------- -------- -------- Layer 1 enable + -------x xxxxxxxx -------- -------- Layer 1 Y scroll position + -------- -------- -------x xxxxxxxx Layer 1 X scroll position + + 0xF1180068: x------- -------- -------- -------- Layer 2 enable + -------x xxxxxxxx -------- -------- Layer 2 Y scroll position + -------- -------- -------x xxxxxxxx Layer 2 X scroll position + + 0xF118006C: x------- -------- -------- -------- Layer 3 enable + -------x xxxxxxxx -------- -------- Layer 3 Y scroll position + -------- -------- -------x xxxxxxxx Layer 3 X scroll position +*/ + + +uint64_t model3_state::model3_vid_reg_r(offs_t offset) +{ + switch(offset) + { + case 0x00/8: return m_vid_reg0; + case 0x08/8: return 0xffffffffffffffffU; /* ??? */ + case 0x20/8: return (uint64_t)m_layer_priority << 48; + case 0x40/8: return ((uint64_t)m_layer_modulate1 << 32) | (uint64_t)m_layer_modulate2; + default: logerror("read reg %02X\n", offset);break; + } + return 0; +} + +void model3_state::model3_vid_reg_w(offs_t offset, uint64_t data, uint64_t mem_mask) +{ + switch(offset) + { + case 0x00/8: logerror("vid_reg0: %08X%08X\n", (uint32_t)(data>>32),(uint32_t)(data)); m_vid_reg0 = data; break; + case 0x08/8: break; /* ??? */ + case 0x10/8: set_irq_line((data >> 56) & 0x0f, CLEAR_LINE); break; /* VBL IRQ Ack */ + + case 0x20/8: m_layer_priority = (data >> 48); break; + + case 0x40/8: m_layer_modulate1 = (uint32_t)(data >> 32); + m_layer_modulate2 = (uint32_t)(data); + break; + case 0x60/8: COMBINE_DATA(&m_layer_scroll[0]); break; + case 0x68/8: COMBINE_DATA(&m_layer_scroll[1]); break; + default: logerror("model3_vid_reg_w: %02X, %08X%08X\n", offset, (uint32_t)(data >> 32), (uint32_t)(data)); break; + } +} + +void model3_state::model3_palette_w(offs_t offset, uint64_t data, uint64_t mem_mask) +{ + COMBINE_DATA(&m_paletteram64[offset]); + uint32_t data1 = BYTE_REVERSE32((uint32_t)(m_paletteram64[offset] >> 32)); + uint32_t data2 = BYTE_REVERSE32((uint32_t)(m_paletteram64[offset] >> 0)); + + m_palette->set_pen_color((offset*2)+0, pal5bit(data1 >> 0), pal5bit(data1 >> 5), pal5bit(data1 >> 10)); + m_palette->set_pen_color((offset*2)+1, pal5bit(data2 >> 0), pal5bit(data2 >> 5), pal5bit(data2 >> 10)); +} + +uint64_t model3_state::model3_palette_r(offs_t offset) +{ + return m_paletteram64[offset]; +} + + +/*****************************************************************************/ +/* texture caching */ + +/* + array of cached textures: + 4 potential textures for 4-bit grayscale + 2 pages + 1024 pixels / 32 pixel resolution vertically + 2048 pixels / 32 pixel resolution horizontally +*/ +void model3_state::invalidate_texture(int page, int texx, int texy, int texwidth, int texheight) +{ + int wtiles = 1 << texwidth; + int htiles = 1 << texheight; + + for (int y = 0; y < htiles; y++) + for (int x = 0; x < wtiles; x++) + while (m_texcache[page][texy + y][texx + x] != nullptr) + { + cached_texture *freeme = m_texcache[page][texy + y][texx + x]; + m_texcache[page][texy + y][texx + x] = freeme->next; + delete freeme; + } +} + +cached_texture *model3_state::get_texture(int page, int texx, int texy, int texwidth, int texheight, int format) +{ + cached_texture *tex = m_texcache[page][texy][texx]; + int pixheight = 32 << texheight; + int pixwidth = 32 << texwidth; + uint32_t alpha = ~0; + int x, y; + + /* if we have one already, validate it */ + for (tex = m_texcache[page][texy][texx]; tex != nullptr; tex = tex->next) + if (tex->width == texwidth && tex->height == texheight && tex->format == format) + return tex; + + /* create a new texture */ + tex = new cached_texture(texwidth, texheight, format); + + /* set the new texture */ + tex->next = m_texcache[page][texy][texx]; + m_texcache[page][texy][texx] = tex; + + /* decode it */ + for (y = 0; y < pixheight; y++) + { + const uint16_t *texsrc = &m_texture_ram[page][(texy * 32 + y) * 2048 + texx * 32]; + rgb_t *dest = &tex->data[2 * pixwidth * y]; + + switch (format) + { + case 0: /* 1-5-5-5 ARGB */ + for (x = 0; x < pixwidth; x++) + { + uint16_t pixdata = texsrc[x]; + alpha &= dest[x] = rgb_t(pal1bit(~pixdata >> 15), pal5bit(pixdata >> 10), pal5bit(pixdata >> 5), pal5bit(pixdata >> 0)); + } + break; + + case 1: /* A4L4 interleaved */ + for (x = 0; x < pixwidth; x++) + { + uint8_t grayvalue = pal4bit(texsrc[x] & 0xf); + uint8_t a = pal4bit((texsrc[x] >> 4) & 0xf); + alpha &= dest[x] = rgb_t(a, grayvalue, grayvalue, grayvalue); + } + break; + + case 2: /* A4L4? */ + for (x = 0; x < pixwidth; x++) + { + uint8_t grayvalue = pal4bit((texsrc[x] >> 0) & 0xf); + uint8_t a = pal4bit((texsrc[x] >> 4) & 0xf); + alpha &= dest[x] = rgb_t(a, grayvalue, grayvalue, grayvalue); + } + break; + + case 3: /* A4L4 interleaved */ + for (x = 0; x < pixwidth; x++) + { + uint8_t grayvalue = pal4bit((texsrc[x] >> 8) & 0xf); + uint8_t a = pal4bit((texsrc[x] >> 12) & 0xf); + alpha &= dest[x] = rgb_t(a, grayvalue, grayvalue, grayvalue); + } + break; + + case 4: /* 8-bit A4L4 */ + for (x = 0; x < pixwidth; x++) + { + uint8_t pixdata = texsrc[x] >> 8; + alpha &= dest[x] = rgb_t(pal4bit(pixdata), pal4bit(pixdata >> 4), pal4bit(pixdata >> 4), pal4bit(pixdata >> 4)); + } + break; + + case 5: /* L8 */ + for (x = 0; x < pixwidth; x++) + { + uint8_t grayvalue = texsrc[x]; + alpha &= dest[x] = rgb_t(0xff, grayvalue, grayvalue, grayvalue); + } + break; + + case 6: /* L8 */ + for (x = 0; x < pixwidth; x++) + { + uint8_t grayvalue = texsrc[x] >> 8; + alpha &= dest[x] = rgb_t(0xff, grayvalue, grayvalue, grayvalue); + } + break; + + case 7: /* 4-4-4-4 ARGB */ + for (x = 0; x < pixwidth; x++) + { + uint16_t pixdata = texsrc[x]; + alpha &= dest[x] = rgb_t(pal4bit(pixdata >> 0), pal4bit(pixdata >> 12), pal4bit(pixdata >> 8), pal4bit(pixdata >> 4)); + } + break; + } + + /* create the horizontal mirror of this line */ + for (x = 0; x < pixwidth; x++) + dest[pixwidth * 2 - 1 - x] = dest[x]; + } + + /* create the vertical mirror of the texture */ + for (y = 0; y < pixheight; y++) + memcpy(&tex->data[2 * pixwidth * (pixheight * 2 - 1 - y)], &tex->data[2 * pixwidth * y], sizeof(rgb_t) * pixwidth * 2); + + /* remember the overall alpha */ + tex->alpha = alpha >> 24; + + /* return a pointer to the texture */ + return tex; +} + +/*****************************************************************************/ +/* Real3D Graphics stuff */ + +/* + Real3D Pro-1000 capabilities: + + Coordinate sets + - 4096 matrices (matrix base pointer in viewport node) + + Polygons + - 32MB max polygon memory. VROM in Model 3, the low 4MB of VROM is overlaid by Polygon RAM for runtime generated content. + + Texture + - 2 texture sheets of 2048x1024 + - Mipmaps located in the bottom right corner + - Texture size 32x32 to 1024x1024 + - Microtextures (is this featured in Model 3?) + + LODs + - 127 blend types per viewport with 4 sets of min/max angle or range (where is this in the viewport node?) + + Lighting + - Self-luminous lighting (enable and luminosity parameter in polygon structure) + - Fixed polygon shading, fixed shading weight per vertex (not found in Model 3, yet) + - Flat sun shading (lighting parameters in viewport node) needs a separate enable? + - Smooth polygon shading (lighting parameters in viewport, use vertex normals) + + Gamma table + - 256 entry 8-bit table, possibly in Polygon RAM +*/ + +/* + Real3D Memory Structures: + + Culling Nodes: + - Located in Culling RAM (0x8E000000) + - Limit of 15 child nodes (nesting), not including polygon nodes + - Color table (is this featured in Model 3?) + + 0x00: xxxxxxxx xxxxxxxx xxxxxx-- -------- Viewport number + -------- -------- ------xx -------- Viewport select 0-3 + -------- -------- -------- x------- Display select + -------- -------- -------- -x------ Fix point centroid + -------- -------- -------- --x----- Viewport off + -------- -------- -------- ---xx--- Viewport priority + -------- -------- -------- -----x-- "clr_xlator_tbl_sel" + -------- -------- -------- ------xx Node type + + 0x01: -------x -------- -------- -------- Sibling null + -------- xxxxxxxx xxxxxxxx xxxxxxxx Sibling pointer + + 0x02: -----x-- -------- -------- -------- Child is sibling table + -------x -------- -------- -------- Leaf node + -------- xxxxxxxx xxxxxxxx xxxxxxxx Child pointer + + 0x03: "cota" (float) Affected by frustum angles and viewport size + 0x04: Sun light vector X-component (float) + 0x05: Sun light vector Y-component (float) + 0x06: Sun light vector Z-component (float) + 0x07: Sun light intensity (float) + 0x08: "Cv" ? (float) Affected by left and right angle + 0x09: "Cw" ? (float) Affected by top and bottom angle + 0x0a: "Io" ? (float) Affected by top and bottom angle + 0x0b: "Jo" ? (float) Affected by left and right angle + 0x0c: (float) Left clip plane U + 0x0d: (float) Left clip plane V + 0x0e: (float) Top clip plane U + 0x0f: (float) Top clip plane W + 0x10: (float) Right clip plane U + 0x11: (float) Right clip plane V + 0x12: (float) Bottom clip plane U + 0x13: (float) Bottom clip plane W + + 0x14: xxxxxxxx xxxxxxxx -------- -------- Viewport height (14.2 fixed-point) + -------- -------- xxxxxxxx xxxxxxxx Viewport width (14.2 fixed-point) + + 0x15: xxxxxxxx xxxxxxxx -------- -------- "env_to_chan_matrix_index" + + 0x16: -------- xxxxxxxx xxxxxxxx xxxxxxxx Matrix base pointer + + 0x17: -------- xxxxxxxx xxxxxxxx xxxxxxxx LOD pointer + + 0x18: -------- xxxxxxxx xxxxxxxx xxxxxxxx Culling offset + + 0x19: -------- xxxxxxxx xxxxxxxx xxxxxxxx Polygon offset + + 0x1a: xxxxxxxx xxxxxxxx -------- -------- Viewport Y coordinate (12.4 fixed-point) + -------- -------- xxxxxxxx xxxxxxxx Viewport X coordinate (12.4 fixed-point) + + 0x1b: Copy of word 0x00 + + 0x1c: xxxxxxxx xxxxxxxx -------- -------- "lj" + -------- -------- xxxxxxxx xxxxxxxx "li" + + 0x1d: -------- xxxxxxxx -------- -------- Spotlight Y size + -------- -------- xxxxxxxx xxxxxxxx Spotlight Y position (13.3 fixed-point?) + + 0x1e: -------- xxxxxxxx -------- -------- Spotlight X size + -------- -------- xxxxxxxx xxxxxxxx Spotlight X position (13.3 fixed-point?) + + 0x1f: Light extent (float) + + 0x20: xxxxxxxx -------- -------- -------- ? + -------- xxxxxxxx -------- -------- ? + -------- -------- -x------ -------- Parallel projection + -------- -------- --xxx--- -------- Light RGB (RGB111?) + -------- -------- -----xxx -------- Light RGB Fog (RGB111?) + -------- -------- -------- xxxxxxxx Scroll Fog (0.8 fixed-point?) What is this??? + + 0x21: "inv_light_start_range" (float) + 0x22: Fog Color (RGB888) + 0x23: Fog Density (float) + + 0x24: -------- xxxxxxxx -------- -------- Fog attenuation + -------- -------- xxxxxxxx -------- Sun light ambient (0.8 fixed-point) + -------- -------- -------- xxxxxxxx Scroll attenuation (0.8 fixed-point) What is this??? + + 0x25: -------- xxxxxxxx -------- -------- Ambient fog + -------- -------- xxxxxxxx xxxxxxxx Fog offset + + 0x26: xxxxxxxx xxxxxxxx xxxxxxxx -------- Spot light + -------- -------- -------- x------- Clear viewport + -------- -------- -------- -x------ New sun parameters + + 0x27: x------- -------- -------- -------- Valid spot light + -x------ -------- -------- -------- Pro 1000 dummy + -------- --xxxxxx xxxxxxxx xxxxxxxx "upgrade_wd_10" + + 0x28: Fog layer altitude (float) + + 0x29: Top fog density (float) + + 0x2a: Bottom fog density (float) + + 0x2b: Near clipping plane (float) + + 0x2c: Far clipping plane (float) + + 0x2d: Pfog offset top + + 0x2e: Pfog offset bottom + 0x2f: ? + + + Sub types: + LOD Culling Node. Up to 4 LODs. + + Articulated Part Culling Node (is this used by Model 3?) + - An Articulated Part culling node, or six degree?of?freedom node, is used to define + geometry that can move relative to the parent coordinate set to which it is attached. + Fifteen levels of coordinate set nesting (levels of articulation) are supported. + + Animation Culling Node + - Animation culling nodes are used to build a culling hierarchy for an object with different + representations, or animation frames. which can be turned on and off by the + application. Each child (culling node or polygon) added to an Animation culling node + specifies the frame for which the child is valid. + + Instance Culling Node + - Instance culling nodes define the top of a shared display list segment that can be + referenced from other parts of the scene display list. + + Instance Reference Culling Node + - An Instance Reference node is considered a leaf node; its + "child" is the shared geometry segment. An Instance Reference may be attached to + a parent node and may not have any other children, but may have siblings. + + Point Light + - A Point Light is used to create an instance of a point luminous feature. The size, + feature type, and number of sides of the point light model may be customized. + + Instance Set + - An Instance Set is a culling node which defines a set of point features. Each feature + is positioned individually. This type of culling node can be used to simulate particles. + + + + Instance Node? + + 0x00: x------- -------- -------- -------- Is UF ref + -x------ -------- -------- -------- Is 3D model + --x----- -------- -------- -------- Is point + ---x---- -------- -------- -------- Is point ref + ----x--- -------- -------- -------- Is animation + -----x-- -------- -------- -------- Is billboard + ------x- -------- -------- -------- Child is billboard + -------x -------- -------- -------- Extra child pointer needed + -------- -----xxx xxxxxx-- -------- Node ID + + -------- -------- -------- x------- Reset matrix + -------- -------- -------- -x------ Use child pointer + -------- -------- -------- --x----- Use sibling pointer + -------- -------- -------- ---x---- No matrix + -------- -------- -------- ----x--- Indirect child + -------- -------- -------- -----x-- Valid color table + -------- -------- -------- ------xx Node type (0 = viewport, 1 = root node, 2 = culling node) + + 0x01,0x02 only present on Step 2+ + + 0x01: xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx Model scale (float) + 0x02: -------- -------- x------- -------- Texture replace + -------- -------- -x------ -------- Switch bank + -------- -------- --xxxxxx x------- X offset + -------- -------- -------- -xxxxxxx Y offset + + 0x03: xxxxxxxx xxxxx--- -------- -------- Color table address 1 + -------- -----xxx xxxx---- -------- LOD table pointer + -------- -------- ----xxxx xxxxxxxx Node matrix + + 0x04: Translation X coordinate + 0x05: Translation Y coordinate + 0x06: Translation Z coordinate + + 0x07: xxxx---- -------- -------- -------- Color table address 2 + -----x-- -------- -------- -------- Sibling table + ------x- -------- -------- -------- Point + -------x -------- -------- -------- Leaf node + -------- xxxxxxxx xxxxxxxx xxxxxxxx Child pointer + + 0x08: xxxxxxx- -------- -------- -------- Color table address 3 + -------x -------- -------- -------- Null sibling + -------- xxxxxxxx xxxxxxxx xxxxxxxx Sibling pointer + + 0x09: xxxxxxxx xxxxxxxx -------- -------- Blend radius + -------- -------- xxxxxxxx xxxxxxxx Culling radius + + + Polygon Data + + 0x00: xxxxxx-- -------- -------- -------- Specular + ------x- -------- -------- -------- Clockwise data + -------x xxxxxxxx xxxxxx-- -------- Polygon ID + -------- -------- ------xx -------- Discard this polygon + -------- -------- -------- x------- Specular enable + -------- -------- -------- -x------ 0 = Triangle, 1 = Quad + -------- -------- -------- --x----- Polygon is points + -------- -------- -------- ---x---- Smoothing enable + -------- -------- -------- ----x--- Vertex 3 shared from previous polygon + -------- -------- -------- -----x-- Vertex 2 shared from previous polygon + -------- -------- -------- ------x- Vertex 1 shared from previous polygon + -------- -------- -------- -------x Vertex 0 shared from previous polygon + + + 0x01: xxxxxxxx xxxxxxxx xxxxxxxx -------- Polygon normal X coordinate (2.22 fixed point) + -------- -------- -------- x------- Edge on translucency + -------- -------- -------- -x------ UV format (0 = 13.3, 1 = 16.0) + -------- -------- -------- --x----- Enable fixed shading + -------- -------- -------- ---x---- Enable double-sided polygon + -------- -------- -------- ----x--- Enable smooth shading + -------- -------- -------- -----x-- If set, this is the last polygon + -------- -------- -------- ------x- Poly color, 1 = RGB, 0 = color table + -------- -------- -------- -------x No LOS return + + + 0x02: xxxxxxxx xxxxxxxx xxxxxxxx -------- Polygon normal Y coordinate (2.22 fixed point) + -------- -------- -------- xxx----- Microtexture select + -------- -------- -------- ---x---- Microtexture enable + -------- -------- -------- ----xx-- Microtexture min LOD + -------- -------- -------- ------x- Texture U mirror enable + -------- -------- -------- -------x Texture V mirror enable + + 0x03: xxxxxxxx xxxxxxxx xxxxxxxx -------- Polygon normal Z coordinate (2.22 fixed point) + -------- -------- -------- x------- X wrap smoothing + -------- -------- -------- -x------ Y wrap smoothing + -------- -------- -------- --xxx--- Texture width (in 8-pixel tiles) + -------- -------- -------- -----xxx Texture height (in 8-pixel tiles) + + 0x04: xxxxxxxx xxxxxxxx xxxxxxxx -------- Color (RGB888) + -------- -------- -------- x------- Translator map select + -------- -------- -------- -x------ Texture page + -------- -------- -------- --xxxxxx Upper 6 bits of texture U coordinate + + 0x05: xxxxxxxx xxxxxxxx xxxxxxxx -------- Texture NP scale + -------- -------- -------- x------- Low bit of texture U coordinate + -------- -------- -------- ---xxxxx Low 5 bits of texture V coordinate + -------- -------- -------- -xx----- Top 2 bits of texture V coordinate (unused by Model 3) + + 0x06: x------- -------- -------- -------- Texture contour enable + -xxxxxxx -------- -------- -------- Translator map offset + -------- xxxxxx-- -------- -------- Polygon translucency + -------- ------x- -------- -------- Translucency pattern select + -------- -------x -------- -------- 1 = disable lighting + -------- -------- xxxxx--- -------- Polygon light modifier (Amount that a luminous polygon will burn through fog. + Valid range is 0.0 to 1.0. 0.0 is completely fogged; + 1.0 has no fog.) + -------- -------- -----x-- -------- Texture enable + -------- -------- ------xx x------- Texture format + -------- -------- -------- -xx----- Shininess + -------- -------- -------- ---x---- High priority polygon + -------- -------- -------- ----x--- Layered polygon (stencil) + -------- -------- -------- -----xxx Translucency mode + + + Vertex entry + + 0x00: xxxxxxxx xxxxxxxx xxxxxxxx -------- Vertex X coordinate (17.7 fixed-point in Step 1.0, 13.11 otherwise) + -------- -------- -------- xxxxxxxx Vertex normal X + + 0x01: xxxxxxxx xxxxxxxx xxxxxxxx -------- Vertex Y coordinate + -------- -------- -------- xxxxxxxx Vertex normal Y + + 0x02: xxxxxxxx xxxxxxxx xxxxxxxx -------- Vertex Z coordinate + -------- -------- -------- xxxxxxxx Vertex normal Z + + 0x03: xxxxxxxx xxxxxxxx -------- -------- Vertex U coordinate + -------- -------- xxxxxxxx xxxxxxxx Vertex V coordinate + +*/ + + +void model3_state::real3d_display_list_w(offs_t offset, uint64_t data, uint64_t mem_mask) +{ + if (ACCESSING_BITS_32_63) + { + m_display_list_ram[offset*2] = BYTE_REVERSE32((uint32_t)(data >> 32)); + } + if (ACCESSING_BITS_0_31) + { + m_display_list_ram[(offset*2)+1] = BYTE_REVERSE32((uint32_t)(data)); + } +} + +void model3_state::real3d_polygon_ram_w(offs_t offset, uint64_t data, uint64_t mem_mask) +{ + if (ACCESSING_BITS_32_63) + { + m_polygon_ram[offset*2] = BYTE_REVERSE32((uint32_t)(data >> 32)); + } + if (ACCESSING_BITS_0_31) + { + m_polygon_ram[(offset*2)+1] = BYTE_REVERSE32((uint32_t)(data)); + } +} + +static const uint8_t texture_decode16[64] = +{ + 0, 1, 4, 5, 8, 9, 12, 13, + 2, 3, 6, 7, 10, 11, 14, 15, + 16, 17, 20, 21, 24, 25, 28, 29, + 18, 19, 22, 23, 26, 27, 30, 31, + 32, 33, 36, 37, 40, 41, 44, 45, + 34, 35, 38, 39, 42, 43, 46, 47, + 48, 49, 52, 53, 56, 57, 60, 61, + 50, 51, 54, 55, 58, 59, 62, 63 +}; + +static const uint8_t texture_decode8[32] = +{ + 1, 3, 5, 7, + 0, 2, 4, 6, + 9, 11, 13, 15, + 8, 10, 12, 14, + 17, 19, 21, 23, + 16, 18, 20, 22, + 25, 27, 29, 31, + 24, 26, 28, 30 +}; + +inline void model3_state::write_texture16(int xpos, int ypos, int width, int height, int page, uint16_t *data) +{ + int x,y,i,j; + + for(y=ypos; y < ypos+height; y+=8) + { + for(x=xpos; x < xpos+width; x+=8) + { + uint16_t *texture = &m_texture_ram[page][y*2048+x]; + int b = 0; + for(j=y; j < y+8; j++) { + for(i=x; i < x+8; i++) { + *texture++ = data[texture_decode16[b^1]]; + ++b; + } + texture += 2048-8; + } + data += 64; + } + } +} + +inline void model3_state::write_texture8(int xpos, int ypos, int width, int height, int page, int upper, int lower, uint16_t *data) +{ + int x,y,i,j; + + for(y=ypos; y < ypos+height; y+=8) + { + for(x=xpos; x < xpos+width; x+=8) + { + uint16_t *texture = &m_texture_ram[page][y*2048+x]; + int b = 0; + for(j=y; j < y+8; j++) + { + for(i=x; i < x+8; i+=2) + { + uint16_t d = data[texture_decode8[b]]; + + if (upper) + *texture = (*texture & 0xff) | (d & 0xff00); + if (lower) + *texture = (*texture & 0xff00) | ((d >> 8) & 0xff); + texture++; + + if (upper) + *texture = (*texture & 0xff) | ((d & 0xff) << 8); + if (lower) + *texture = (*texture & 0xff00) | (d & 0xff); + texture++; + + ++b; + } + texture += 2048-8; + } + data += 32; + } + } +} + +/* + Texture header: + + -------- -------- -------- --xxxxxx X-position + -------- -------- ----xxxx x------- Y-position + -------- -------x xx------ -------- Width + -------- ----xxx- -------- -------- Height + -------- ---x---- -------- -------- Texture page + -------- --x----- -------- -------- Write 8-bit data to the lower byte of texel + -------- -x------ -------- -------- Write 8-bit data to the upper byte of texel + -------- x------- -------- -------- Bitdepth, 0 = 8-bit, 1 = 16-bit + xxxxxxxx -------- -------- -------- Texture type + 0x00 = texture with mipmaps + 0x01 = texture without mipmaps + 0x02 = only mipmaps + 0x80 = possibly gamma table + +*/ + +static const int mipmap_coords[9][2] = +{ + { 1024, 512 }, + { 1536, 768 }, + { 1792, 896 }, + { 1920, 960 }, + { 1984, 992 }, + { 2016, 1008 }, + { 2032, 1016 }, + { 2040, 1020 }, + { 2044, 1022 }, +}; + +static const int mipmap_divider[9] = { 2, 4, 8, 16, 32, 64, 128, 256, 512 }; + +void model3_state::real3d_upload_texture(uint32_t header, uint32_t *data) +{ + int width = 32 << ((header >> 14) & 0x7); + int height = 32 << ((header >> 17) & 0x7); + int xpos = (header & 0x3f) * 32; + int ypos = ((header >> 7) & 0x1f) * 32; + int page = (header >> 20) & 0x1; + int bitdepth = (header >> 23) & 0x1; + int upper_byte = (header >> 22) & 0x1; + int lower_byte = (header >> 21) & 0x1; + + //printf("write tex: %08X, w %d, h %d, x %d, y %d, p %d, b %d\n", header, width, height, xpos, ypos, page, bitdepth); + + switch(header >> 24) + { + case 0x00: /* Texture with mipmaps */ + { + int x = xpos; + int y = ypos; + int w = width; + int h = height; + + int mipmap = 0; + + while (w >= 8 && h >= 8) + { + if (bitdepth) + { + write_texture16(x, y, w, h, page, (uint16_t*)data); + } + else + { + //printf("write tex8: %08X, w %d, h %d, x %d, y %d, p %d, b %d\n", header, width, height, xpos, ypos, page, bitdepth); + write_texture8(x, y, w, h, page, upper_byte, lower_byte, (uint16_t*)data); + } + + data += (w * h * (bitdepth ? 2 : 1)) / 4; + w /= 2; + h /= 2; + + x = mipmap_coords[mipmap][0] + (xpos / mipmap_divider[mipmap]); + y = mipmap_coords[mipmap][1] + (ypos / mipmap_divider[mipmap]); + mipmap++; + } + + invalidate_texture(page, header & 0x3f, (header >> 7) & 0x1f, (header >> 14) & 0x7, (header >> 17) & 0x7); + break; + } + case 0x01: /* Texture without mipmaps */ + { + if (bitdepth) + { + write_texture16(xpos, ypos, width, height, page, (uint16_t*)data); + } + else + { + //printf("write tex8: %08X, w %d, h %d, x %d, y %d, p %d, b %d\n", header, width, height, xpos, ypos, page, bitdepth); + write_texture8(xpos, ypos, width, height, page, upper_byte, lower_byte, (uint16_t*)data); + } + + invalidate_texture(page, header & 0x3f, (header >> 7) & 0x1f, (header >> 14) & 0x7, (header >> 17) & 0x7); + break; + } + case 0x02: /* Only mipmaps */ + { + int x = mipmap_coords[0][0] + (xpos / mipmap_divider[0]); + int y = mipmap_coords[0][1] + (ypos / mipmap_divider[0]); + int w = width / 2; + int h = height / 2; + + int mipmap = 1; + + while (w >= 8 && h >= 8) + { + if (bitdepth) + { + write_texture16(x, y, w, h, page, (uint16_t*)data); + } + else + { + //printf("write tex8: %08X, w %d, h %d, x %d, y %d, p %d, b %d\n", header, width, height, xpos, ypos, page, bitdepth); + write_texture8(x, y, w, h, page, upper_byte, lower_byte, (uint16_t*)data); + } + + data += (w * h * (bitdepth ? 2 : 1)) / 4; + w /= 2; + h /= 2; + + x = mipmap_coords[mipmap][0] + (xpos / mipmap_divider[mipmap]); + y = mipmap_coords[mipmap][1] + (ypos / mipmap_divider[mipmap]); + mipmap++; + } + + invalidate_texture(page, header & 0x3f, (header >> 7) & 0x1f, (header >> 14) & 0x7, (header >> 17) & 0x7); + break; + } + case 0x80: /* Gamma-table ? */ + break; + default: + fatalerror("Unknown texture type: %02X (%08X)\n", header >> 24, header); + break; + } +} + +void model3_state::real3d_display_list_end() +{ + /* upload textures if there are any in the FIFO */ + if (m_texture_fifo_pos > 0) + { + int i = 0; + while (i < m_texture_fifo_pos) + { + int length = (m_texture_fifo[i] / 2) + 2; + uint32_t header = m_texture_fifo[i+1]; + real3d_upload_texture(header, &m_texture_fifo[i+2]); + i += length; + }; + } + m_texture_fifo_pos = 0; + + m_renderer->clear_fb(); + + reset_triangle_buffers(); + real3d_traverse_display_list(); + + for (int i=0; i < 4; i++) + { + int ticount, tiacount; + int ti = m_viewport_tri_index[i]; + int tia = m_viewport_tri_alpha_index[i]; + if (i < 3) + { + ticount = m_viewport_tri_index[i+1] - ti; + tiacount = m_viewport_tri_alpha_index[i+1] - tia; + } + else + { + ticount = m_tri_buffer_ptr - ti; + tiacount = m_tri_alpha_buffer_ptr - tia; + } + + if (ticount > 0 || tiacount > 0) + { + m_renderer->clear_zb(); + m_renderer->draw_opaque_triangles(&m_tri_buffer[ti], ticount); + m_renderer->draw_alpha_triangles(&m_tri_alpha_buffer[tia], tiacount); + m_renderer->wait_for_polys(); + } + } +} + +void model3_state::real3d_display_list1_dma(uint32_t src, uint32_t dst, int length, int byteswap) +{ + address_space &space = m_maincpu->space(AS_PROGRAM); + int d = (dst & 0xffffff) / 4; + for (int i = 0; i < length; i += 4) + { + uint32_t w = space.read_dword(src); + + if (byteswap) + w = BYTE_REVERSE32(w); + + m_display_list_ram[d++] = w; + src += 4; + } +} + +void model3_state::real3d_display_list2_dma(uint32_t src, uint32_t dst, int length, int byteswap) +{ + address_space &space = m_maincpu->space(AS_PROGRAM); + int d = (dst & 0xffffff) / 4; + for (int i = 0; i < length; i += 4) + { + uint32_t w = space.read_dword(src); + + if (byteswap) + w = BYTE_REVERSE32(w); + + m_culling_ram[d++] = w; + src += 4; + } +} + +void model3_state::real3d_vrom_texture_dma(uint32_t src, uint32_t dst, int length, int byteswap) +{ + address_space &space = m_maincpu->space(AS_PROGRAM); + if ((dst & 0xff) == 0) + { + for (int i=0; i < length; i+=12) + { + uint32_t address = space.read_dword(src+i+0); + uint32_t header = space.read_dword(src+i+4); + + if (byteswap) + { + address = BYTE_REVERSE32(address); + header = BYTE_REVERSE32(header); + } + + real3d_upload_texture(header, (uint32_t*)&m_vrom[address]); + } + } +} + +void model3_state::real3d_texture_fifo_dma(uint32_t src, int length, int byteswap) +{ + address_space &space = m_maincpu->space(AS_PROGRAM); + for (int i = 0; i < length; i += 4) + { + uint32_t w = space.read_dword(src); + + if (byteswap) + w = BYTE_REVERSE32(w); + + m_texture_fifo[m_texture_fifo_pos] = w; + m_texture_fifo_pos++; + src += 4; + } +} + +void model3_state::real3d_polygon_ram_dma(uint32_t src, uint32_t dst, int length, int byteswap) +{ + address_space &space = m_maincpu->space(AS_PROGRAM); + int d = (dst & 0xffffff) / 4; + for (int i = 0; i < length; i += 4) + { + uint32_t w = space.read_dword(src); + + if (byteswap) + w = BYTE_REVERSE32(w); + + m_polygon_ram[d++] = w; + src += 4; + } +} + +void model3_state::real3d_cmd_w(uint64_t data) +{ + real3d_display_list_end(); +} + + +/*****************************************************************************/ +/* matrix and vector operations */ + +static inline float dot_product3(VECTOR3 a, VECTOR3 b) +{ + return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); +} + +/* multiplies a 4-element vector by a 4x4 matrix */ +static void matrix_multiply_vector(MATRIX matrix, const VECTOR v, VECTOR *p) +{ + (*p)[0] = (v[0] * matrix[0][0]) + (v[1] * matrix[1][0]) + (v[2] * matrix[2][0]) + (v[3] * matrix[3][0]); + (*p)[1] = (v[0] * matrix[0][1]) + (v[1] * matrix[1][1]) + (v[2] * matrix[2][1]) + (v[3] * matrix[3][1]); + (*p)[2] = (v[0] * matrix[0][2]) + (v[1] * matrix[1][2]) + (v[2] * matrix[2][2]) + (v[3] * matrix[3][2]); + (*p)[3] = (v[0] * matrix[0][3]) + (v[1] * matrix[1][3]) + (v[2] * matrix[2][3]) + (v[3] * matrix[3][3]); +} + +/* multiplies a 4x4 matrix with another 4x4 matrix */ +static void matrix_multiply(MATRIX a, MATRIX b, MATRIX *out) +{ + int i,j; + MATRIX tmp; + + for( i=0; i < 4; i++ ) { + for( j=0; j < 4; j++ ) { + tmp[i][j] = (a[i][0] * b[0][j]) + (a[i][1] * b[1][j]) + (a[i][2] * b[2][j]) + (a[i][3] * b[3][j]); + } + } + memcpy(out, &tmp, sizeof(MATRIX)); +} + +void model3_state::init_matrix_stack() +{ + m_matrix_stack = std::make_unique<MATRIX[]>(MATRIX_STACK_SIZE); + MATRIX *matrix_stack = &m_matrix_stack[0]; + + /* initialize the first matrix as identity */ + matrix_stack[0][0][0] = 1.0f; + matrix_stack[0][0][1] = 0.0f; + matrix_stack[0][0][2] = 0.0f; + matrix_stack[0][0][3] = 0.0f; + matrix_stack[0][1][0] = 0.0f; + matrix_stack[0][1][1] = 1.0f; + matrix_stack[0][1][2] = 0.0f; + matrix_stack[0][1][3] = 0.0f; + matrix_stack[0][2][0] = 0.0f; + matrix_stack[0][2][1] = 0.0f; + matrix_stack[0][2][2] = 1.0f; + matrix_stack[0][2][3] = 0.0f; + matrix_stack[0][3][0] = 0.0f; + matrix_stack[0][3][1] = 0.0f; + matrix_stack[0][3][2] = 0.0f; + matrix_stack[0][3][3] = 1.0f; + + m_matrix_stack_ptr = 0; +} + +void model3_state::get_top_matrix(MATRIX *out) +{ + memcpy(out, &m_matrix_stack[m_matrix_stack_ptr], sizeof(MATRIX)); +} + +void model3_state::push_matrix_stack() +{ + m_matrix_stack_ptr++; + if (m_matrix_stack_ptr >= MATRIX_STACK_SIZE) + fatalerror("push_matrix_stack: matrix stack overflow\n"); + + memcpy(&m_matrix_stack[m_matrix_stack_ptr], &m_matrix_stack[m_matrix_stack_ptr - 1], sizeof(MATRIX)); +} + +void model3_state::pop_matrix_stack() +{ + m_matrix_stack_ptr--; + if (m_matrix_stack_ptr < 0) + fatalerror("pop_matrix_stack: matrix stack underflow\n"); +} + +void model3_state::multiply_matrix_stack(MATRIX matrix) +{ + matrix_multiply(matrix, m_matrix_stack[m_matrix_stack_ptr], &m_matrix_stack[m_matrix_stack_ptr]); +} + +void model3_state::translate_matrix_stack(float x, float y, float z) +{ + MATRIX tm; + + tm[0][0] = 1.0f; tm[0][1] = 0.0f; tm[0][2] = 0.0f; tm[0][3] = 0.0f; + tm[1][0] = 0.0f; tm[1][1] = 1.0f; tm[1][2] = 0.0f; tm[1][3] = 0.0f; + tm[2][0] = 0.0f; tm[2][1] = 0.0f; tm[2][2] = 1.0f; tm[2][3] = 0.0f; + tm[3][0] = x; tm[3][1] = y; tm[3][2] = z; tm[3][3] = 1.0f; + + matrix_multiply(tm, m_matrix_stack[m_matrix_stack_ptr], &m_matrix_stack[m_matrix_stack_ptr]); +} + +void model3_state::set_projection(float left, float right, float top, float bottom, float near, float far) +{ + float l = near * tanf(left * 0.5f); + float r = near * tanf(right * 0.5f); + float t = near * tanf(top * 0.5f ); + float b = near * tanf(bottom * 0.5f); + + m_projection_matrix[0][0] = (2.0f * near) / (l - r); + m_projection_matrix[0][1] = 0.0f; + m_projection_matrix[0][2] = (r + l) / (r - l); + m_projection_matrix[0][3] = 0.0f; + m_projection_matrix[1][0] = 0.0f; + m_projection_matrix[1][1] = (2.0f * near) / (t - b); + m_projection_matrix[1][2] = (t + b) / (t - b); + m_projection_matrix[1][3] = 0.0f; + m_projection_matrix[2][0] = 0.0f; + m_projection_matrix[2][1] = 0.0f; + m_projection_matrix[2][2] = -(far + near) / (far - near); + m_projection_matrix[2][3] = -(2.0f * far * near) / (far - near); + m_projection_matrix[3][0] = 0.0f; + m_projection_matrix[3][1] = 0.0f; + m_projection_matrix[3][2] = -1.0f; + m_projection_matrix[3][3] = 0.0f; +} + +/*****************************************************************************/ +/* transformation and rasterizing */ + +void model3_state::reset_triangle_buffers() +{ + m_tri_buffer_ptr = 0; + m_tri_alpha_buffer_ptr = 0; +} + +m3_triangle *model3_state::push_triangle(bool alpha) +{ + if (!alpha) + { + int i = m_tri_buffer_ptr; + + if (m_tri_buffer_ptr >= TRI_BUFFER_SIZE) + { + return nullptr; + //fatalerror("push_triangle: tri buffer max exceeded"); + } + + m_tri_buffer_ptr++; + return &m_tri_buffer[i]; + } + else + { + int i = m_tri_alpha_buffer_ptr; + + if (m_tri_alpha_buffer_ptr >= TRI_ALPHA_BUFFER_SIZE) + { + return nullptr; + //fatalerror("push_triangle: tri alpha buffer max exceeded"); + } + + m_tri_alpha_buffer_ptr++; + return &m_tri_alpha_buffer[i]; + } +} + +void model3_state::draw_model(uint32_t addr) +{ + // Polygon RAM is mapped to the low 4MB of VROM + uint32_t *model = (addr >= 0x100000) ? &m_vrom[addr] : &m_polygon_ram[addr]; + + uint32_t header[7]; + int index = 0; + bool last_polygon = false, first_polygon = true, back_face = false; + int num_vertices; + int i, v, vi; + float fixed_point_fraction; + m3_vertex vertex[4]; + m3_vertex prev_vertex[4]; + m3_clip_vertex clip_vert[10]; + + MATRIX transform_matrix; + MATRIX vp_matrix; + MATRIX coord_matrix; + + memset(&coord_matrix, 0, sizeof(coord_matrix)); + coord_matrix[0][0] = m_coordinate_system[0][1]; + coord_matrix[1][1] = m_coordinate_system[1][2]; + coord_matrix[2][2] = -m_coordinate_system[2][0]; + coord_matrix[3][3] = 1.0f; + + if (m_step < 0x15) // position coordinates are 17.7 fixed-point in Step 1.0 + fixed_point_fraction = 1.0f / 128.0f; + else // 13.11 fixed-point in other Steps + fixed_point_fraction = 1.0f / 2048.0f; + + get_top_matrix(&transform_matrix); + + // make view-projection matrix + matrix_multiply(transform_matrix, coord_matrix, &transform_matrix); + matrix_multiply(transform_matrix, m_projection_matrix, &vp_matrix); + + memset(prev_vertex, 0, sizeof(prev_vertex)); + + while (!last_polygon) + { + float texture_coord_scale; + uint32_t color; + VECTOR3 normal; + VECTOR3 sn; + VECTOR p[4]; + int polygon_transparency; + + for (i = 0; i < 7; i++) + header[i] = model[index++]; + + if (first_polygon && (header[0] & 0x0f) != 0) + return; + first_polygon = false; + + if (header[6] == 0) + return; + + if (header[1] & 0x4) + last_polygon = true; + + if ((header[0] & 0x300) == 0x300) // TODO: broken polygons in srally2 have these bits set + return; + + num_vertices = (header[0] & 0x40) ? 4 : 3; + + /* texture coordinates are 16.0 or 13.3 fixed-point */ + texture_coord_scale = (header[1] & 0x40) ? 1.0f : (1.0f / 8.0f); + + /* polygon normal (sign + 1.22 fixed-point) */ + normal[0] = (float)((int32_t)header[1] >> 8) * (1.0f / 4194304.0f); + normal[1] = (float)((int32_t)header[2] >> 8) * (1.0f / 4194304.0f); + normal[2] = (float)((int32_t)header[3] >> 8) * (1.0f / 4194304.0f); + + /* load reused vertices */ + vi = 0; + for (v = 0; v < 4; v++) + if (header[0] & (1 << v)) + vertex[vi++] = prev_vertex[v]; + + /* load new vertices */ + for ( ; vi < num_vertices; vi++) + { + uint32_t xw = model[index++]; + uint32_t yw = model[index++]; + uint32_t zw = model[index++]; + + vertex[vi].x = (float)((int32_t)(xw) >> 8) * fixed_point_fraction; + vertex[vi].y = (float)((int32_t)(yw) >> 8) * fixed_point_fraction; + vertex[vi].z = (float)((int32_t)(zw) >> 8) * fixed_point_fraction; + vertex[vi].u = (uint16_t)(model[index] >> 16); + vertex[vi].v = (uint16_t)(model[index++]); +// vertex[vi].nx = normal[0] + ((float)((int8_t)(xw)) / 127.0f); +// vertex[vi].ny = normal[1] + ((float)((int8_t)(yw)) / 127.0f); +// vertex[vi].nz = normal[2] + ((float)((int8_t)(zw)) / 127.0f); + + vertex[vi].nx = ((float)((int8_t)(xw)) / 127.0f); + vertex[vi].ny = ((float)((int8_t)(yw)) / 127.0f); + vertex[vi].nz = ((float)((int8_t)(zw)) / 127.0f); + } + + /* Copy current vertices as previous vertices */ + memcpy(prev_vertex, vertex, sizeof(m3_vertex) * 4); + + if (header[1] & 0x2) + { + color = (header[4] >> 8) & 0xffffff; + } + else + { + int ci = (header[4] >> 8) & 0x7ff; + color = m_polygon_ram[0x400 + ci]; + } + + polygon_transparency = (header[6] & 0x800000) ? 32 : ((header[6] >> 18) & 0x1f); + + /* transform polygon normal to view-space */ + sn[0] = (normal[0] * transform_matrix[0][0]) + + (normal[1] * transform_matrix[0][1]) + + (normal[2] * transform_matrix[0][2]); + sn[1] = (normal[0] * transform_matrix[1][0]) + + (normal[1] * transform_matrix[1][1]) + + (normal[2] * transform_matrix[1][2]); + sn[2] = (normal[0] * transform_matrix[2][0]) + + (normal[1] * transform_matrix[2][1]) + + (normal[2] * transform_matrix[2][2]); + + // TODO: depth bias + // transform and light vertices + for (i = 0; i < num_vertices; i++) + { + VECTOR vect; + + vect[0] = vertex[i].x; + vect[1] = vertex[i].y; + vect[2] = vertex[i].z; + vect[3] = 1.0f; + + // transform to projection space + matrix_multiply_vector(vp_matrix, vect, &p[i]); + + clip_vert[i].x = p[i][0]; + clip_vert[i].y = p[i][1]; + clip_vert[i].z = p[i][2]; + clip_vert[i].w = p[i][3]; + + clip_vert[i].p[0] = vertex[i].u * texture_coord_scale * 256.0f; // 8 bits of subtexel accuracy for bilinear filtering + clip_vert[i].p[1] = vertex[i].v * texture_coord_scale * 256.0f; + + // transform vertex normal + VECTOR3 n; + n[0] = (vertex[i].nx * transform_matrix[0][0]) + + (vertex[i].ny * transform_matrix[0][1]) + + (vertex[i].nz * transform_matrix[0][2]); + n[1] = (vertex[i].nx * transform_matrix[1][0]) + + (vertex[i].ny * transform_matrix[1][1]) + + (vertex[i].nz * transform_matrix[1][2]); + n[2] = (vertex[i].nx * transform_matrix[2][0]) + + (vertex[i].ny * transform_matrix[2][1]) + + (vertex[i].nz * transform_matrix[2][2]); + + // lighting + float intensity; + if ((header[6] & 0x10000) == 0) + { + float dot = dot_product3(n, m_parallel_light); + + if (header[1] & 0x10) + dot = fabs(dot); + + intensity = ((dot * m_parallel_light_intensity) + m_ambient_light_intensity) * 255.0f; + if (intensity > 255.0f) + { + intensity = 255.0f; + } + if (intensity < 0.0f) + { + intensity = 0.0f; + } + } + else + { + intensity = 255.0f; + } + + clip_vert[i].p[2] = intensity; + } + + /* clip against all edges of the view frustum */ + num_vertices = frustum_clip_all<float, 4>(clip_vert, num_vertices, clip_vert); + + /* divide by W, transform to screen coords */ + for(i=0; i < num_vertices; i++) + { + float oow = 1.0f / clip_vert[i].w; + + clip_vert[i].x *= oow; + clip_vert[i].y *= oow; + clip_vert[i].z *= oow; + clip_vert[i].p[0] *= oow; + clip_vert[i].p[1] *= oow; + + clip_vert[i].x = (((clip_vert[i].x * 0.5f) + 0.5f) * m_viewport_width) + m_viewport_x; + clip_vert[i].y = (((clip_vert[i].y * 0.5f) + 0.5f) * m_viewport_height) + m_viewport_y; + clip_vert[i].z = (((clip_vert[i].z * 0.5f) + 0.5f) * (m_viewport_far - m_viewport_near)) + m_viewport_near; + } + + /* backface culling */ + if( (header[6] & 0x800000) && (!(header[1] & 0x0010)) ) + { + if (sn[0]*clip_vert[0].x + sn[1]*clip_vert[0].y + sn[2]*clip_vert[0].z > 0) + back_face = 1; + else + back_face = 0; + } + else + { + back_face = 0; //no culling for transparent or two-sided polygons + } + + back_face = 0; + + if (!back_face) + { + bool colormod = false; + cached_texture* texture; + + if (header[6] & 0x0000400) + { + int tex_x = ((header[4] & 0x1f) << 1) | ((header[5] >> 7) & 0x1); + int tex_y = (header[5] & 0x1f); + int tex_width = ((header[3] >> 3) & 0x7); + int tex_height = (header[3] & 0x7); + int tex_format = (header[6] >> 7) & 0x7; + + if (tex_format != 0 && tex_format != 7) // enable color modulation if this is not a color texture + colormod = true; + + if (tex_width >= 6 || tex_height >= 6) // srally2 poly ram has degenerate polys with 2k tex size (cpu bug or intended?) + return; + + texture = get_texture((header[4] & 0x40) ? 1 : 0, tex_x, tex_y, tex_width, tex_height, tex_format); + } + else + { + texture = nullptr; + } + + for (i=2; i < num_vertices; i++) + { + bool alpha = (header[6] & 0x1) || ((header[6] & 0x800000) == 0); // put to alpha buffer if there's any transparency involved + m3_triangle* tri = push_triangle(alpha); + + // bail out if tri buffer is maxed out (happens during harley boot) + if (!tri) + return; + + memcpy(&tri->v[0], &clip_vert[0], sizeof(m3_clip_vertex)); + memcpy(&tri->v[1], &clip_vert[i-1], sizeof(m3_clip_vertex)); + memcpy(&tri->v[2], &clip_vert[i], sizeof(m3_clip_vertex)); + + tri->texture = texture; + tri->transparency = polygon_transparency; + tri->color = color; + + tri->param = 0; + tri->param |= (header[4] & 0x40) ? TRI_PARAM_TEXTURE_PAGE : 0; + tri->param |= (header[6] & 0x00000400) ? TRI_PARAM_TEXTURE_ENABLE : 0; + tri->param |= (header[2] & 0x2) ? TRI_PARAM_TEXTURE_MIRROR_U : 0; + tri->param |= (header[2] & 0x1) ? TRI_PARAM_TEXTURE_MIRROR_V : 0; + tri->param |= (header[6] & 0x80000000) ? TRI_PARAM_ALPHA_TEST : 0; + tri->param |= (colormod) ? TRI_PARAM_COLOR_MOD : 0; + } + } + } +} + + +/*****************************************************************************/ +/* display list parser */ + +uint32_t *model3_state::get_memory_pointer(uint32_t address) +{ + if (address & 0x800000) + { + if (address >= 0x840000) { + fatalerror("get_memory_pointer: invalid display list memory address %08X\n", address); + } + return &m_display_list_ram[address & 0x7fffff]; + } + else + { + if (address >= 0x100000) { + fatalerror("get_memory_pointer: invalid node ram address %08X\n", address); + } + return &m_culling_ram[address]; + } +} + +void model3_state::load_matrix(int matrix_num, MATRIX *out) +{ + float *matrix = (float *)get_memory_pointer(m_matrix_base_address + matrix_num * 12); + + (*out)[0][0] = matrix[3]; (*out)[0][1] = matrix[6]; (*out)[0][2] = matrix[9]; (*out)[0][3] = 0.0f; + (*out)[1][0] = matrix[4]; (*out)[1][1] = matrix[7]; (*out)[1][2] = matrix[10]; (*out)[1][3] = 0.0f; + (*out)[2][0] = matrix[5]; (*out)[2][1] = matrix[8]; (*out)[2][2] = matrix[11]; (*out)[2][3] = 0.0f; + (*out)[3][0] = matrix[0]; (*out)[3][1] = matrix[1]; (*out)[3][2] = matrix[2]; (*out)[3][3] = 1.0f; +} + +void model3_state::traverse_list4(int lod_num, uint32_t address) +{ + /* does something with the LOD selection */ + uint32_t *list = get_memory_pointer(address); + uint32_t link = list[0]; + + draw_model(link & 0xffffff); +} + +void model3_state::traverse_list(uint32_t address) +{ + uint32_t *list = get_memory_pointer(address); + int list_ptr = 0; + + if (m_list_depth > 2) + return; + + m_list_depth++; + + /* find the end of the list */ + while (1) + { + address = list[list_ptr++]; + if (address & 0x02000000) + break; + if (address == 0 || (address >> 24) != 0) + { + list_ptr--; + break; + } + } + + /* walk it backwards */ + while (list_ptr > 0) + { + address = list[--list_ptr] & 0xffffff; + if (address != 0 && address != 0x800800) + //if (address != 0) + draw_block(address); + } + + m_list_depth--; +} + +inline void model3_state::process_link(uint32_t address, uint32_t link) +{ + if (link != 0 && link != 0x0fffffff && link != 0x00800800 && link != 0x01000000) + { + switch (link >> 24) + { + case 0x00: /* link to another node */ + draw_block(link & 0xffffff); + break; + + case 0x01: + case 0x03: /* both of these link to models, is there any difference ? */ + draw_model(link & 0xffffff); + break; + + case 0x04: /* list of links */ + traverse_list(link & 0xffffff); + break; + + default: + logerror("process_link %08X: link = %08X\n", address, link); + break; + } + } +} + +void model3_state::draw_block(uint32_t address) +{ + const uint32_t *node = get_memory_pointer(address); + uint32_t link; + int node_matrix; + float x, y, z; + MATRIX matrix; + int offset; + + offset = (m_step < 0x15) ? 2 : 0; + link = node[7 - offset]; + + /* apply matrix and translation */ + node_matrix = node[3 - offset] & 0xfff; + load_matrix(node_matrix, &matrix); + + push_matrix_stack(); + + if (node[0] & 0x10) + { + x = *(float *)&node[4 - offset]; + y = *(float *)&node[5 - offset]; + z = *(float *)&node[6 - offset]; + translate_matrix_stack(x, y, z); + } + else if (node_matrix != 0) + multiply_matrix_stack(matrix); + + /* bit 0x08 of word 0 indicates a pointer list */ + if (node[0] & 0x08) + traverse_list4((node[3 - offset] >> 12) & 0x7f, link & 0xffffff); + else + process_link(address, link); + + pop_matrix_stack(); + + /* handle the second link */ + if ((node[0] & 0x7) != 0x6) + { + link = node[8 - offset]; + process_link(address, link); + } +} + +void model3_state::draw_viewport(int pri, uint32_t address) +{ + const uint32_t *node = get_memory_pointer(address); + uint32_t link_address; + + link_address = node[1]; + + /* traverse to the link node before drawing this viewport */ + /* check this is correct as this affects the rendering order */ + if (link_address != 0x01000000 && link_address != 0) + draw_viewport(pri, link_address); + + /* skip if this isn't the right priority */ + if (pri != ((node[0] >> 3) & 3)) + return; + + /* set viewport parameters */ + m_viewport_x = (float)(node[26] & 0xffff) / 16.0f; /* 12.4 fixed point */ + m_viewport_y = (float)((node[26] >> 16) & 0xffff) / 16.0f; + m_viewport_width = (float)(node[20] & 0xffff) / 4.0f; /* 14.2 fixed point */ + m_viewport_height = (float)((node[20] >> 16) & 0xffff) / 4.0f; + + m_viewport_near = 1.0f; + m_viewport_far = 100000.0f; + + /* set up frustum */ + float frustum_left = atan2(*(float *)&node[12], *(float *)&node[13]); + float frustum_right = -atan2(*(float *)&node[16], -*(float *)&node[17]); + float frustum_top = atan2(*(float *)&node[14], *(float *)&node[15]); + float frustum_bottom = -atan2(*(float *)&node[18], -*(float *)&node[19]); +// float frustum_1 = atan2(*(float *)&node[9], *(float *)&node[8]); +// float frustum_2 = atan2(*(float *)&node[11], *(float *)&node[10]); + + /* + printf("%f\n", *(float *)&node[3]); + printf("0: %f, 1: %f, 2: %f, 3: %f\n", *(float *)&node[8], *(float *)&node[9], *(float *)&node[10], *(float *)&node[11]); + printf("4: %f, 5: %f, 6: %f, 7: %f\n", *(float *)&node[12], *(float *)&node[13], *(float *)&node[14], *(float *)&node[15]); + printf("8: %f, 9: %f, A: %f, B: %f\n", *(float *)&node[16], *(float *)&node[17], *(float *)&node[18], *(float *)&node[19]); + printf("fl = %f, fr = %f, ft = %f, fb = %f\n", RADIAN_TO_DEGREE(frustum_left), RADIAN_TO_DEGREE(frustum_right), RADIAN_TO_DEGREE(frustum_top), RADIAN_TO_DEGREE(frustum_bottom)); + printf("f1 = %f, f2 = %f\n", RADIAN_TO_DEGREE(frustum_1), RADIAN_TO_DEGREE(frustum_2)); + */ + + set_projection(frustum_left, frustum_right, frustum_top, frustum_bottom, m_viewport_near, m_viewport_far); + + + m_matrix_base_address = node[22]; + /* TODO: where does node[23] point to ? LOD table ? */ + + /* set lighting parameters */ + m_parallel_light[0] = *(float *)&node[5]; + m_parallel_light[1] = *(float *)&node[6]; + m_parallel_light[2] = *(float *)&node[4]; + m_parallel_light_intensity = *(float *)&node[7]; + m_ambient_light_intensity = (uint8_t)(node[36] >> 8) / 256.0f; + + /* set coordinate system matrix */ + load_matrix(0, &m_coordinate_system); + + /* process a link */ + process_link(link_address, node[2]); +} + + +void model3_state::real3d_traverse_display_list() +{ + init_matrix_stack(); + + m_list_depth = 0; + + for (int pri = 0; pri < 4; pri++) + { + m_viewport_tri_index[pri] = m_tri_buffer_ptr; + m_viewport_tri_alpha_index[pri] = m_tri_alpha_buffer_ptr; + draw_viewport(pri, 0x800000); + } +} + +void model3_renderer::draw(bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + for (int j = cliprect.min_y; j <= cliprect.max_y; ++j) + { + uint32_t *const dst = &bitmap.pix(j); + uint32_t const *const src = &m_fb->pix(j); + + for (int i = cliprect.min_x; i <= cliprect.max_x; ++i) + { + if (src[i] & 0xff000000) + { + dst[i] = src[i]; + } + } + } +} + +void model3_renderer::clear_fb() +{ + rectangle cliprect; + cliprect.min_x = 0; + cliprect.min_y = 0; + cliprect.max_x = 495; + cliprect.max_y = 383; + + m_fb->fill(0x00000000, cliprect); +} + +void model3_renderer::clear_zb() +{ + rectangle cliprect; + cliprect.min_x = 0; + cliprect.min_y = 0; + cliprect.max_x = 495; + cliprect.max_y = 383; + + float zvalue = 10000000000.0f; + m_zb->fill(*(int*)&zvalue, cliprect); +} + +void model3_renderer::wait_for_polys() +{ + wait(); +} + +void model3_renderer::draw_opaque_triangles(const m3_triangle* tris, int num_tris) +{ + rectangle cliprect; + cliprect.min_x = 0; + cliprect.min_y = 0; + cliprect.max_x = 495; + cliprect.max_y = 383; + +// printf("draw opaque: %d\n", num_tris); + + vertex_t v[3]; + + for (int t=0; t < num_tris; t++) + { + const m3_triangle* tri = &tris[t]; + + if (tri->param & TRI_PARAM_TEXTURE_ENABLE) + { + for (int i=0; i < 3; i++) + { + v[i].x = tri->v[i].x; + v[i].y = tri->v[i].y; + v[i].p[0] = tri->v[i].w; + v[i].p[1] = 1.0f / tri->v[i].w; + v[i].p[2] = tri->v[i].p[0]; + v[i].p[3] = tri->v[i].p[1]; + v[i].p[4] = tri->v[i].p[2]; + } + + model3_polydata &extra = object_data().next(); + extra.texture = tri->texture; + extra.transparency = tri->transparency; + extra.texture_param = tri->param; + extra.color = tri->color; + + if (tri->param & TRI_PARAM_ALPHA_TEST) + { + render_triangle<5>(cliprect, render_delegate(&model3_renderer::draw_scanline_tex_contour, this), v[0], v[1], v[2]); + } + else + { + if (tri->param & TRI_PARAM_COLOR_MOD) + render_triangle<5>(cliprect, render_delegate(&model3_renderer::draw_scanline_tex_colormod, this), v[0], v[1], v[2]); + else + render_triangle<5>(cliprect, render_delegate(&model3_renderer::draw_scanline_tex, this), v[0], v[1], v[2]); + } + } + else + { + for (int i=0; i < 3; i++) + { + v[i].x = tri->v[i].x; + v[i].y = tri->v[i].y; + v[i].p[0] = tri->v[i].w; + v[i].p[1] = tri->v[i].p[2]; + } + + model3_polydata &extra = object_data().next(); + extra.color = tri->color; + + render_triangle<2>(cliprect, render_delegate(&model3_renderer::draw_scanline_solid, this), v[0], v[1], v[2]); + } + } +} + +void model3_renderer::draw_alpha_triangles(const m3_triangle* tris, int num_tris) +{ + rectangle cliprect; + cliprect.min_x = 0; + cliprect.min_y = 0; + cliprect.max_x = 495; + cliprect.max_y = 383; + +// printf("draw alpha: %d\n", num_tris); + + vertex_t v[3]; + + for (int t=num_tris-1; t >= 0; t--) + { + const m3_triangle* tri = &tris[t]; + + if (tri->param & TRI_PARAM_TEXTURE_ENABLE) + { + for (int i=0; i < 3; i++) + { + v[i].x = tri->v[i].x; + v[i].y = tri->v[i].y; + v[i].p[0] = tri->v[i].w; + v[i].p[1] = 1.0f / tri->v[i].w; + v[i].p[2] = tri->v[i].p[0]; + v[i].p[3] = tri->v[i].p[1]; + v[i].p[4] = tri->v[i].p[2]; + } + + model3_polydata &extra = object_data().next(); + extra.texture = tri->texture; + extra.transparency = tri->transparency; + extra.texture_param = tri->param; + + render_triangle<5>(cliprect, render_delegate(&model3_renderer::draw_scanline_tex_alpha, this), v[0], v[1], v[2]); + } + else + { + for (int i=0; i < 3; i++) + { + v[i].x = tri->v[i].x; + v[i].y = tri->v[i].y; + v[i].p[0] = tri->v[i].w; + v[i].p[1] = tri->v[i].p[2]; + } + + model3_polydata &extra = object_data().next(); + extra.color = tri->color; + extra.transparency = tri->transparency; + + render_triangle<2>(cliprect, render_delegate(&model3_renderer::draw_scanline_solid_trans, this), v[0], v[1], v[2]); + } + } +} + +void model3_renderer::draw_scanline_solid(int32_t scanline, const extent_t &extent, const model3_polydata &polydata, int threadid) +{ + uint32_t *const fb = &m_fb->pix(scanline); + float *const zb = (float*)&m_zb->pix(scanline); + + float z = extent.param[0].start; + float dz = extent.param[0].dpdx; + + float in = extent.param[1].start; + float inz = extent.param[1].dpdx; + + rgbaint_t color(polydata.color); + + for (int x = extent.startx; x < extent.stopx; x++) + { + if (z <= zb[x]) + { + rgbaint_t c(color); + + c.scale_imm_and_clamp((int)in); + + fb[x] = 0xff000000 | c.to_rgba_clamp(); + zb[x] = z; + } + + in += inz; + z += dz; + } +} + +void model3_renderer::draw_scanline_solid_trans(int32_t scanline, const extent_t &extent, const model3_polydata &polydata, int threadid) +{ + uint32_t *const fb = &m_fb->pix(scanline); + float *const zb = (float*)&m_zb->pix(scanline); + + float z = extent.param[0].start; + float dz = extent.param[0].dpdx; + + float in = extent.param[1].start; + float inz = extent.param[1].dpdx; + + rgbaint_t color(polydata.color); + + int trans = (polydata.transparency << 3) | (polydata.transparency >> 2); + + for (int x = extent.startx; x < extent.stopx; x++) + { + if (z <= zb[x]) + { + rgbaint_t c(color); + + c.scale_imm_and_clamp((int)in); + + if (trans != 0xff) + { + c.blend(rgbaint_t(fb[x]), trans); + } + + fb[x] = 0xff000000 | c.to_rgba_clamp(); + } + + in += inz; + z += dz; + } +} + +#define TEX_FETCH_NOFILTER() \ +do { \ + float intz = 1.0f / ooz; \ + uint32_t u = uoz * intz; \ + uint32_t v = voz * intz; \ + uint32_t u1 = (u >> 8) & umask; \ + uint32_t v1 = (v >> 8) & vmask; \ + texel = texture->data[(v1 << width) + u1]; \ +} while(0); + +#define TEX_FETCH_BILINEAR() \ +do { \ + float intz = 1.0f / ooz; \ + uint32_t u = uoz * intz; \ + uint32_t v = voz * intz; \ + uint32_t u1 = (u >> 8) & umask; \ + uint32_t v1 = (v >> 8) & vmask; \ + uint32_t u2 = (u1 + 1) & umask; \ + uint32_t v2 = (v1 + 1) & vmask; \ + uint32_t pix00 = texture->data[(v1 << width) + u1]; \ + uint32_t pix01 = texture->data[(v1 << width) + u2]; \ + uint32_t pix10 = texture->data[(v2 << width) + u1]; \ + uint32_t pix11 = texture->data[(v2 << width) + u2]; \ + texel = rgbaint_t::bilinear_filter(pix00, pix01, pix10, pix11, u, v); \ +} while(0); + +#if ENABLE_BILINEAR +#define TEX_FETCH() TEX_FETCH_BILINEAR() +#else +#define TEX_FETCH() TEX_FETCH_NOFILTER() +#endif + +void model3_renderer::draw_scanline_tex(int32_t scanline, const extent_t &extent, const model3_polydata &polydata, int threadid) +{ + uint32_t *const fb = &m_fb->pix(scanline); + float *const zb = (float*)&m_zb->pix(scanline); + const cached_texture *texture = polydata.texture; + + float z = extent.param[0].start; + float dz = extent.param[0].dpdx; + float ooz = extent.param[1].start; + float dooz = extent.param[1].dpdx; + float uoz = extent.param[2].start; + float duoz = extent.param[2].dpdx; + float voz = extent.param[3].start; + float dvoz = extent.param[3].dpdx; + float in = extent.param[4].start; + float inz = extent.param[4].dpdx; + + uint32_t umask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1; + uint32_t vmask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1; + uint32_t width = 6 + texture->width; + + for (int x = extent.startx; x < extent.stopx; x++) + { + if (z <= zb[x]) + { + uint32_t texel; + TEX_FETCH(); // TODO fetch rgbaint_t instead + + rgbaint_t color(texel); + + color.scale_imm_and_clamp((int)in); + + fb[x] = 0xff000000 | color.to_rgba_clamp(); + zb[x] = z; + } + + ooz += dooz; + uoz += duoz; + voz += dvoz; + in += inz; + z += dz; + } +} + +void model3_renderer::draw_scanline_tex_colormod(int32_t scanline, const extent_t &extent, const model3_polydata &polydata, int threadid) +{ + uint32_t *const fb = &m_fb->pix(scanline); + float *const zb = (float*)&m_zb->pix(scanline); + const cached_texture *texture = polydata.texture; + + float z = extent.param[0].start; + float dz = extent.param[0].dpdx; + float ooz = extent.param[1].start; + float dooz = extent.param[1].dpdx; + float uoz = extent.param[2].start; + float duoz = extent.param[2].dpdx; + float voz = extent.param[3].start; + float dvoz = extent.param[3].dpdx; + float in = extent.param[4].start; + float inz = extent.param[4].dpdx; + + uint32_t umask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1; + uint32_t vmask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1; + uint32_t width = 6 + texture->width; + + rgbaint_t polycolor(polydata.color); + + for (int x = extent.startx; x < extent.stopx; x++) + { + if (z <= zb[x]) + { + uint32_t texel; + TEX_FETCH(); // TODO fetch rgbaint_t instead + + rgbaint_t color(texel); + + color.scale_and_clamp(polycolor); + color.scale_imm_and_clamp((int)in); + + fb[x] = 0xff000000 | color.to_rgba_clamp(); + zb[x] = z; + } + + ooz += dooz; + uoz += duoz; + voz += dvoz; + in += inz; + z += dz; + } +} + +void model3_renderer::draw_scanline_tex_contour(int32_t scanline, const extent_t &extent, const model3_polydata &polydata, int threadid) +{ + uint32_t *const fb = &m_fb->pix(scanline); + float *const zb = (float*)&m_zb->pix(scanline); + const cached_texture *texture = polydata.texture; + + float z = extent.param[0].start; + float dz = extent.param[0].dpdx; + float ooz = extent.param[1].start; + float dooz = extent.param[1].dpdx; + float uoz = extent.param[2].start; + float duoz = extent.param[2].dpdx; + float voz = extent.param[3].start; + float dvoz = extent.param[3].dpdx; + float in = extent.param[4].start; + float inz = extent.param[4].dpdx; + + uint32_t umask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1; + uint32_t vmask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1; + uint32_t width = 6 + texture->width; + + rgbaint_t polycolor(polydata.color); + + for (int x = extent.startx; x < extent.stopx; x++) + { + if (z <= zb[x]) + { + uint32_t texel; + TEX_FETCH(); + + uint32_t fa = texel >> 24; + if (fa >= 0xf8) + { + rgbaint_t color(texel); + + color.scale_and_clamp(polycolor); + color.scale_imm_and_clamp((int)in); + color.blend(rgbaint_t(fb[x]), fa); + + fb[x] = 0xff000000 | color.to_rgba_clamp(); + zb[x] = z; + } + } + + ooz += dooz; + uoz += duoz; + voz += dvoz; + in += inz; + z += dz; + } +} + +void model3_renderer::draw_scanline_tex_trans(int32_t scanline, const extent_t &extent, const model3_polydata &polydata, int threadid) +{ + uint32_t *const fb = &m_fb->pix(scanline); + float *const zb = (float*)&m_zb->pix(scanline); + const cached_texture *texture = polydata.texture; + + float z = extent.param[0].start; + float dz = extent.param[0].dpdx; + float ooz = extent.param[1].start; + float dooz = extent.param[1].dpdx; + float uoz = extent.param[2].start; + float duoz = extent.param[2].dpdx; + float voz = extent.param[3].start; + float dvoz = extent.param[3].dpdx; + float in = extent.param[4].start; + float inz = extent.param[4].dpdx; + + int trans = (polydata.transparency << 3) | (polydata.transparency >> 2); + + uint32_t umask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1; + uint32_t vmask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1; + uint32_t width = 6 + texture->width; + + rgbaint_t polycolor(polydata.color); + + for (int x = extent.startx; x < extent.stopx; x++) + { + if (z <= zb[x]) + { + uint32_t texel; + TEX_FETCH(); + + rgbaint_t color(texel); + + color.scale_and_clamp(polycolor); + color.scale_imm_and_clamp((int)in); + color.blend(rgbaint_t(fb[x]), trans); + + fb[x] = 0xff000000 | color.to_rgba_clamp(); + } + + ooz += dooz; + uoz += duoz; + voz += dvoz; + in += inz; + z += dz; + } +} + +void model3_renderer::draw_scanline_tex_alpha(int32_t scanline, const extent_t &extent, const model3_polydata &polydata, int threadid) +{ + uint32_t *const fb = &m_fb->pix(scanline); + float *const zb = (float*)&m_zb->pix(scanline); + const cached_texture *texture = polydata.texture; + + float z = extent.param[0].start; + float dz = extent.param[0].dpdx; + float ooz = extent.param[1].start; + float dooz = extent.param[1].dpdx; + float uoz = extent.param[2].start; + float duoz = extent.param[2].dpdx; + float voz = extent.param[3].start; + float dvoz = extent.param[3].dpdx; + float in = extent.param[4].start; + float inz = extent.param[4].dpdx; + +// int srctrans = polydata.transparency; +// int desttrans = 32 - polydata.transparency; + + uint32_t umask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_U) ? 64 : 32) << texture->width) - 1; + uint32_t vmask = (((polydata.texture_param & TRI_PARAM_TEXTURE_MIRROR_V) ? 64 : 32) << texture->height) - 1; + uint32_t width = 6 + texture->width; + + for (int x = extent.startx; x < extent.stopx; x++) + { + if (z <= zb[x]) + { + uint32_t texel; + TEX_FETCH(); + + uint32_t fa = texel >> 24; + if (fa != 0) + { + rgbaint_t color(texel); + + color.scale_imm_and_clamp((int)in); + color.blend(rgbaint_t(fb[x]), fa); + + fb[x] = 0xff000000 | color.to_rgba_clamp(); + } + } + + ooz += dooz; + uoz += duoz; + voz += dvoz; + in += inz; + z += dz; + } +} |