// license:BSD-3-Clause // copyright-holders:Olivier Galibert #include #include "emu.h" #include "cpu/mb86233/mb86233.h" #include "includes/model1.h" #define LOG_TGP_VIDEO 0 #define LOG_TGP(x) do { if (LOG_TGP_VIDEO) logerror x; } while (0) #define LOG_TGP_DEV(d,x) do { if (LOG_TGP_VIDEO) d->logerror x; } while (0) // Model 1 geometrizer TGP and rasterizer simulation enum { FRAC_SHIFT = 16 }; enum { MOIRE = 0x01000000 }; uint32_t model1_state::readi(int adr) const { return m_display_list_current[(adr + 0)&0x7fff] | (m_display_list_current[(adr + 1)&0x7fff] << 16); } int16_t model1_state::readi16(int adr) const { return m_display_list_current[(adr + 0)&0x7fff]; } float model1_state::readf(int adr) const { return u2f(readi(adr)); } void model1_state::view_t::transform_point(point_t *p) const { point_t q = *p; float xx = translation[0] * q.x + translation[3] * q.y + translation[6] * q.z + translation[ 9] + vxx; p->y = translation[1] * q.x + translation[4] * q.y + translation[7] * q.z + translation[10] + vyy; float zz = translation[2] * q.x + translation[5] * q.y + translation[8] * q.z + translation[11] + vzz; p->x = ayyc * xx - ayys * zz; p->z = ayys * xx + ayyc * zz; } void model1_state::view_t::transform_vector(glm::vec3& p) const { glm::vec3 q(p); glm::vec3 row1(translation[0], translation[3], translation[6]); glm::vec3 row2(translation[1], translation[4], translation[7]); glm::vec3 row3(translation[2], translation[5], translation[8]); p = glm::vec3(glm::dot(q, row1), glm::dot(q, row2), glm::dot(q, row3)); //p->set_x(translation[0] * q.x() + translation[3] * q.y() + translation[6] * q.z()); //p->set_y(translation[1] * q.x() + translation[4] * q.y() + translation[7] * q.z()); //p->set_z(translation[2] * q.x() + translation[5] * q.y() + translation[8] * q.z()); } void model1_state::cross_product(point_t* o, const point_t* p, const point_t* q) const { o->x = p->y * q->z - q->y * p->z; o->y = p->z * q->x - q->z * p->x; o->z = p->x * q->y - q->x * p->y; } float model1_state::view_determinant(const point_t *p1, const point_t *p2, const point_t *p3) const { float x1 = p2->x - p1->x; float y1 = p2->y - p1->y; float z1 = p2->z - p1->z; float x2 = p3->x - p1->x; float y2 = p3->y - p1->y; float z2 = p3->z - p1->z; return p1->x * (y1 * z2 - y2 * z1) + p1->y * (z1 * x2 - z2 * x1) + p1->z * (x1 * y2 - x2 * y1); } void model1_state::view_t::project_point(point_t *p) const { p->xx = p->x / p->z; p->yy = p->y / p->z; p->s.x = xc + (p->xx * zoomx + viewx); p->s.y = yc - (p->yy * zoomy + viewy); } void model1_state::view_t::project_point_direct(point_t *p) const { p->xx = p->x /*/ p->z*/; p->yy = p->y /*/ p->z*/; p->s.x = xc + p->xx; p->s.y = yc - p->yy; } void model1_state::draw_hline(bitmap_rgb32 &bitmap, int x1, int x2, int y, int color) { uint32_t *base = &bitmap.pix32(y); while(x1 <= x2) { base[x1] = color; x1++; } } void model1_state::draw_hline_moired(bitmap_rgb32 &bitmap, int x1, int x2, int y, int color) { uint32_t *base = &bitmap.pix32(y); while(x1 <= x2) { if((x1^y) & 1) { base[x1] = color; } x1++; } } void model1_state::fill_slope(bitmap_rgb32 &bitmap, view_t *view, int color, int32_t x1, int32_t x2, int32_t sl1, int32_t sl2, int32_t y1, int32_t y2, int32_t *nx1, int32_t *nx2) { if(y1 > view->y2) { return; } if (y2 <= view->y1) { int delta = y2 - y1; *nx1 = x1 + delta * sl1; *nx2 = x2 + delta * sl2; return; } if (y2 > view->y2) { y2 = view->y2 + 1; } if (y1 < view->y1) { int delta = view->y1 - y1; x1 += delta * sl1; x2 += delta * sl2; y1 = view->y1; } if (x1 > x2 || (x1 == x2 && sl1 > sl2)) { int32_t t = x1; x1 = x2; x2 = t; t = sl1; sl1 = sl2; sl2 = t; int32_t *tp = nx1; nx1 = nx2; nx2 = tp; } while(y1 < y2) { if(y1 >= view->y1) { int xx1 = x1>>FRAC_SHIFT; int xx2 = x2>>FRAC_SHIFT; if(xx1 <= view->x2 || xx2 >= view->x1) { if(xx1 < view->x1) { xx1 = view->x1; } if(xx2 > view->x2) { xx2 = view->x2; } if(color & MOIRE) { draw_hline_moired(bitmap, xx1, xx2, y1, color); } else { draw_hline(bitmap, xx1, xx2, y1, color); } } } x1 += sl1; x2 += sl2; y1++; } *nx1 = x1; *nx2 = x2; } void model1_state::fill_line(bitmap_rgb32 &bitmap, view_t *view, int color, int32_t y, int32_t x1, int32_t x2) { int xx1 = x1>>FRAC_SHIFT; int xx2 = x2>>FRAC_SHIFT; if(y > view->y2 || y < view->y1) return; if(xx1 <= view->x2 || xx2 >= view->x1) { if(xx1 < view->x1) xx1 = view->x1; if(xx2 > view->x2) xx2 = view->x2; if(color & MOIRE) draw_hline_moired(bitmap, xx1, xx2, y, color); else draw_hline(bitmap, xx1, xx2, y, color); } } void model1_state::fill_quad(bitmap_rgb32 &bitmap, view_t *view, const quad_t& q) const { spoint_t p[8]; int color = q.col; if (color < 0) { color = -1 - color; LOG_TGP_DEV(this,("VIDEOD: Q (%d, %d)-(%d, %d)-(%d, %d)-(%d, %d)\n", q.p[0]->s.x, q.p[0]->s.y, q.p[1]->s.x, q.p[1]->s.y, q.p[2]->s.x, q.p[2]->s.y, q.p[3]->s.x, q.p[3]->s.y)); } for (int i = 0; i < 4; i++) { p[i].x = p[i+4].x = q.p[i]->s.x << FRAC_SHIFT; p[i].y = p[i+4].y = q.p[i]->s.y; } int pmin = 0; int pmax = 0; for (int i = 1; i < 4; i++) { if(p[i].y < p[pmin].y) { pmin = i; } if(p[i].y > p[pmax].y) { pmax = i; } } int32_t cury = p[pmin].y; int32_t limy = p[pmax].y; int32_t x1, x2; if (cury == limy) { x1 = p[0].x; x2 = p[0].x; for (int i = 1; i < 4; i++) { if (p[i].x < x1) { x1 = p[i].x; } if (p[i].x > x2) { x2 = p[i].x; } } fill_line(bitmap, view, color, cury, x1, x2); return; } if(cury > view->y2) { return; } if(limy <= view->y1) { return; } if(limy > view->y2) { limy = view->y2; } int ps1 = pmin+4; int ps2 = pmin; goto startup; int32_t sl1, sl2; for(;;) { if (p[ps1 - 1].y == p[ps2 + 1].y) { fill_slope(bitmap, view, color, x1, x2, sl1, sl2, cury, p[ps1 - 1].y, &x1, &x2); cury = p[ps1 - 1].y; if(cury >= limy) { break; } ps1--; ps2++; startup: while(p[ps1-1].y == cury) { ps1--; } while(p[ps2+1].y == cury) { ps2++; } x1 = p[ps1].x; x2 = p[ps2].x; sl1 = (x1 - p[ps1 - 1].x) / (cury - p[ps1 - 1].y); sl2 = (x2 - p[ps2 + 1].x) / (cury - p[ps2 + 1].y); } else if (p[ps1 - 1].y < p[ps2 + 1].y) { fill_slope(bitmap, view, color, x1, x2, sl1, sl2, cury, p[ps1 - 1].y, &x1, &x2); cury = p[ps1 - 1].y; if(cury >= limy) { break; } ps1--; while(p[ps1 - 1].y == cury) { ps1--; } x1 = p[ps1].x; sl1 = (x1 - p[ps1 - 1].x) / (cury - p[ps1 - 1].y); } else { fill_slope(bitmap, view, color, x1, x2, sl1, sl2, cury, p[ps2 + 1].y, &x1, &x2); cury = p[ps2 + 1].y; if(cury >= limy) { break; } ps2++; while(p[ps2 + 1].y == cury) { ps2++; } x2 = p[ps2].x; sl2 = (x2 - p[ps2 + 1].x) / (cury - p[ps2 + 1].y); } } if(cury == limy) { fill_line(bitmap, view, color, cury, x1, x2); } } #if 0 void model1_state::draw_line(bitmap_rgb32 &bitmap, model1_state::view_t *view, int color, int x1, int y1, int x2, int y2) const { if ((x1 < view->x1 && x2 < view->x1) || (x1 > view->x2 && x2 > view->x2) || (y1 < view->y1 && y2 < view->y1) || (y1 > view->y2 && y2 > view->y2)) return; int x = x1; int y = y1; int s1x = 1; int s1y = 0; int s2x = 0; int s2y = 1; int d1 = x2-x1; int d2 = y2-y1; if (d1 < 0) { s1x = -s1x; d1 = -d1; } if (d2 < 0) { s2y = -s2y; d2 = -d2; } if (d1 < d2) { int t = s1x; s1x = s2x; s2x = t; t = s1y; s1y = s2y; s2y = t; t = d1; d1 = d2; d2 = t; } if(d1 > 600) { return; } int cur = 0; while (x != x2 || y != y2) { if (x >= view->x1 && x <= view->x2 && y >= view->y1 && y <= view->y2) { bitmap.pix32(y, x) = color; } x += s1x; y += s1y; cur += d2; if (cur >= d1) { cur -= d1; x += s2x; y += s2y; } } if (x >= view->x1 && x <= view->x2 && y >= view->y1 && y <= view->y2) { bitmap.pix16(y, x) = color; } } #endif static int comp_quads(const void *q1, const void *q2) { model1_state::quad_t* const* c1 = static_cast(q1); model1_state::quad_t* const* c2 = static_cast(q2); return (*c1)->compare(*c2); } int model1_state::quad_t::compare(const model1_state::quad_t* other) const { float z2 = other->z; if (z < z2) return +1; if (z > z2) return -1; if (this - other < 0) return -1; return +1; } void model1_state::sort_quads() const { const int count = m_quadpt - m_quaddb; for (int i = 0; i < count; i++) { m_quadind[i] = m_quaddb + i; } qsort(m_quadind, count, sizeof(model1_state::quad_t*), comp_quads); } void model1_state::unsort_quads() const { const int count = m_quadpt - m_quaddb; for (int i = 0; i < count; i++) { m_quadind[i] = m_quaddb + i; } } void model1_state::draw_quads(bitmap_rgb32 &bitmap, const rectangle &cliprect) { view_t *view = m_view.get(); int count = m_quadpt - m_quaddb; /* clip to the cliprect */ int save_x1 = view->x1; int save_x2 = view->x2; int save_y1 = view->y1; int save_y2 = view->y2; view->x1 = std::max(view->x1, cliprect.min_x); view->x2 = std::min(view->x2, cliprect.max_x); view->y1 = std::max(view->y1, cliprect.min_y); view->y2 = std::min(view->y2, cliprect.max_y); for (int i = 0; i < count; i++) { fill_quad(bitmap, view, *m_quadind[i]); #if 0 quad_t *q = m_quadind[i]; draw_line(bitmap, m_palette->black_pen(), q.p[0]->s.x, q.p[0]->s.y, q.p[1]->s.x, q.p[1]->s.y); draw_line(bitmap, m_palette->black_pen(), q.p[1]->s.x, q.p[1]->s.y, q.p[2]->s.x, q.p[2]->s.y); draw_line(bitmap, m_palette->black_pen(), q.p[2]->s.x, q.p[2]->s.y, q.p[3]->s.x, q.p[3]->s.y); draw_line(bitmap, m_palette->black_pen(), q.p[3]->s.x, q.p[3]->s.y, q.p[0]->s.x, q.p[0]->s.y); #endif } view->x1 = save_x1; view->x2 = save_x2; view->y1 = save_y1; view->y2 = save_y2; } #if 0 uint16_t model1_state::scale_color(uint16_t color, float level) const { int r = ((color >> 10) & 31) * level; int g = ((color >> 5) & 31) * level; int b = (color & 31) * level; return (r << 10) | (g << 5) | b; } #endif // xe = xc + (x/z * zm + tx) // xe < x1 => xc + (x/z * zm + tx) < x1 // => x/z < (x1-xc-tx)/zm // => x < z*(x1-xc-tx)/zm // ye = yc - (y/z * zm + ty) // ye < y1 => yc - (y/z * zm + ty) < y1 // => -y/z < (y1-yc+ty)/zm // => y > -z*(y1-yc+ty)/zm // xf = zf*a // xx = x1*t+x2(1-t); zz = z1*t+z2(1-t) // => x1*t+x2(1-t) = z1*t*a+z2*(1-t)*a // => t*(x1-x2+a*(z2-z1) = -x2+a*z2 // => t = (z2*a-x2)/((z2-z1)*a-(x2-x1)) void model1_state::view_t::recompute_frustum() { a_left = ( x1 - xc - viewx) / zoomx; a_right = ( x2 - xc - viewx) / zoomx; a_bottom = (-y1 + yc - viewy) / zoomy; a_top = (-y2 + yc - viewy) / zoomy; } bool model1_state::fclip_isc_bottom(view_t *view, point_t *p) { return p->y > (p->z * view->a_bottom); } void model1_state::fclip_clip_bottom(view_t *view, point_t *pt, point_t *p1, point_t *p2) { float t = (p2->z * view->a_bottom-p2->y) / ((p2->z - p1->z) * view->a_bottom - (p2->y - p1->y)); pt->x = p1->x * t + p2->x * (1 - t); pt->y = p1->y * t + p2->y * (1 - t); pt->z = p1->z * t + p2->z * (1 - t); view->project_point(pt); } bool model1_state::fclip_isc_top(view_t *view, point_t *p) { return p->y < (p->z * view->a_top); } void model1_state::fclip_clip_top(view_t *view, point_t *pt, point_t *p1, point_t *p2) { float t = (p2->z * view->a_top - p2->y) / ((p2->z - p1->z) * view->a_top - (p2->y - p1->y)); pt->x = p1->x * t + p2->x * (1 - t); pt->y = p1->y * t + p2->y * (1 - t); pt->z = p1->z * t + p2->z * (1 - t); view->project_point(pt); } bool model1_state::fclip_isc_left(view_t *view, point_t *p) { return p->x < (p->z * view->a_left); } void model1_state::fclip_clip_left(view_t *view, point_t *pt, point_t *p1, point_t *p2) { float t = (p2->z * view->a_left - p2->x) / ((p2->z - p1->z) * view->a_left - (p2->x - p1->x)); pt->x = p1->x * t + p2->x * (1 - t); pt->y = p1->y * t + p2->y * (1 - t); pt->z = p1->z * t + p2->z * (1 - t); view->project_point(pt); } bool model1_state::fclip_isc_right(view_t *view, point_t *p) { return p->x > (p->z * view->a_right); } void model1_state::fclip_clip_right(view_t *view, point_t *pt, point_t *p1, point_t *p2) { float t = (p2->z * view->a_right - p2->x) / ((p2->z - p1->z) * view->a_right - (p2->x - p1->x)); pt->x = p1->x * t + p2->x * (1 - t); pt->y = p1->y * t + p2->y * (1 - t); pt->z = p1->z * t + p2->z * (1 - t); view->project_point(pt); } void model1_state::fclip_push_quad_next(int level, quad_t& q, point_t *p1, point_t *p2, point_t *p3, point_t *p4) { quad_t cquad(q.col, q.z, p1, p2, p3, p4); fclip_push_quad(level+1, cquad); } void model1_state::fclip_push_quad(int level, quad_t& q) { view_t *view = m_view.get(); if (level == 4) { LOG_TGP(("VIDEOCQ %d", level)); for (int i = 0; i < 4; i++) LOG_TGP((" (%f, %f, %f)", q.p[i]->x, q.p[i]->y, q.p[i]->z)); LOG_TGP(("\n")); *m_quadpt = q; m_quadpt++; return; } bool is_out[4]; for (int i = 0; i < 4; i++) { is_out[i] = m_clipfn[level].m_isclipped(view, q.p[i]); } LOG_TGP(("VIDEOCQ %d", level)); for (int i = 0; i < 4; i++) LOG_TGP((" (%f, %f, %f, %d)", q.p[i]->x, q.p[i]->y, q.p[i]->z, is_out[i])); LOG_TGP(("\n")); // No clipping if(!is_out[0] && !is_out[1] && !is_out[2] && !is_out[3]) { fclip_push_quad(level+1, q); return; } // Full clipping if(is_out[0] && is_out[1] && is_out[2] && is_out[3]) return; // Find n so that point n is clipped and n-1 isn't int i; for (i = 0; i < 4; i++) if(is_out[i] && !is_out[(i-1)&3]) break; point_t* pt[4]; bool is_out2[4]; for (int j = 0; j < 4; j++) { pt[j] = q.p[(i + j) & 3]; is_out2[j] = is_out[(i + j) & 3]; } // At this point, pt[0] is clipped out and pt[3] isn't. Test for the 4 possible cases if (is_out2[1]) { if (is_out2[2]) { // pt 0,1,2 clipped out, one triangle left m_clipfn[level].m_clip(view, m_pointpt, pt[2], pt[3]); point_t* pi1 = m_pointpt++; m_clipfn[level].m_clip(view, m_pointpt, pt[3], pt[0]); point_t* pi2 = m_pointpt++; fclip_push_quad_next(level, q, pi1, pt[3], pi2, pi2); } else { // pt 0,1 clipped out, one quad left m_clipfn[level].m_clip(view, m_pointpt, pt[1], pt[2]); point_t* pi1 = m_pointpt++; m_clipfn[level].m_clip(view, m_pointpt, pt[3], pt[0]); point_t* pi2 = m_pointpt++; fclip_push_quad_next(level, q, pi1, pt[2], pt[3], pi2); } } else { if (is_out2[2]) { // pt 0,2 clipped out, shouldn't happen, two triangles m_clipfn[level].m_clip(view, m_pointpt, pt[0], pt[1]); point_t* pi1 = m_pointpt++; m_clipfn[level].m_clip(view, m_pointpt, pt[1], pt[2]); point_t* pi2 = m_pointpt++; fclip_push_quad_next(level, q, pi1, pt[1], pi2, pi2); m_clipfn[level].m_clip(view, m_pointpt, pt[2], pt[3]); pi1 = m_pointpt++; m_clipfn[level].m_clip(view, m_pointpt, pt[3], pt[0]); pi2 = m_pointpt++; fclip_push_quad_next(level, q, pi1, pt[3], pi2, pi2); } else { // pt 0 clipped out, one decagon left, split in quad+tri m_clipfn[level].m_clip(view, m_pointpt, pt[0], pt[1]); point_t* pi1 = m_pointpt++; m_clipfn[level].m_clip(view, m_pointpt, pt[3], pt[0]); point_t* pi2 = m_pointpt++; fclip_push_quad_next(level, q, pi1, pt[1], pt[2], pt[3]); fclip_push_quad_next(level, q, pt[3], pi2, pi1, pi1); } } } float model1_state::min4f(float a, float b, float c, float d) { float m = a; if(b m) m = b; if (c > m) m = c; if (d > m) m = d; return m; } #ifdef UNUSED_DEFINITION static const uint8_t num_of_times[]={1,1,1,1,2,2,2,3}; #endif float model1_state::compute_specular(glm::vec3& normal, glm::vec3& light, float diffuse, int lmode) { #if 0 int p = m_view->lightparams[lmode].p & 7; float sv = m_view->lightparams[lmode].s; //This is how it should be according to model2 geo program, but doesn't work fine float s = 2 * (diffuse * normal.z - light.z); for (int i = 0; i < num_of_times[p]; i++) { s *= s; } s *= sv; if (s < 0.0f) { return 0.0f; } if (s > 1.0f) { return 1.0f; } return s; // ??? //return fabs(diffuse)*sv; #endif return 0; } void model1_state::push_object(uint32_t tex_adr, uint32_t poly_adr, uint32_t size) { #if 0 int dump; #endif float *poly_data; if (poly_adr & 0x800000) poly_data = (float *)m_poly_ram.get(); else poly_data = (float *)m_poly_rom.target(); poly_adr &= 0x7fffff; #if 0 dump = poly_adr == 0x944ea; dump = 0; #endif #if 0 if (poly_adr < 0x10000 || poly_adr >= 0x80000) return; if (poly_adr < 0x40000 || poly_adr >= 0x50000) return; if (poly_adr == 0x4c7db || poly_adr == 0x4cdaa || poly_adr == 0x4d3e7) return; if (poly_adr != 0x483e5) return; #endif if (true) { LOG_TGP(("XVIDEO: draw object (%x, %x, %x)\n", tex_adr, poly_adr, size)); } if (!size) size = 0xffffffff; point_t *old_p0 = m_pointpt++; point_t *old_p1 = m_pointpt++; old_p0->x = poly_data[poly_adr + 0]; old_p0->y = poly_data[poly_adr + 1]; old_p0->z = poly_data[poly_adr + 2]; old_p1->x = poly_data[poly_adr + 3]; old_p1->y = poly_data[poly_adr + 4]; old_p1->z = poly_data[poly_adr + 5]; m_view->transform_point(old_p0); m_view->transform_point(old_p1); if (old_p0->z > 0) { m_view->project_point(old_p0); } else { old_p0->s.x = old_p0->s.y = 0; } if (old_p1->z > 0) { m_view->project_point(old_p1); } else { old_p1->s.x = old_p1->s.y = 0; } float old_z = 0; poly_adr += 6; for (int i = 0; i < size; i++) { #if 0 LOG_TGP(("VIDEO: %08x (%f, %f, %f) (%f, %f, %f) (%f, %f, %f)\n", *(uint32_t *)(poly_data + poly_adr) & ~(0x01800303), poly_data[poly_adr + 1], poly_data[poly_adr + 2], poly_data[poly_adr + 3], poly_data[poly_adr + 4], poly_data[poly_adr + 5], poly_data[poly_adr + 6], poly_data[poly_adr + 7], poly_data[poly_adr + 8], poly_data[poly_adr + 9])); #endif uint32_t flags = *reinterpret_cast(poly_data + poly_adr); int type = flags & 3; if (!type) break; if (flags & 0x00001000) tex_adr++; int lightmode = (flags >> 17) & 15; point_t *p0 = m_pointpt++; point_t *p1 = m_pointpt++; glm::vec3 vn(poly_data[poly_adr + 1], poly_data[poly_adr + 2], poly_data[poly_adr + 3]); p0->x = poly_data[poly_adr + 4]; p0->y = poly_data[poly_adr + 5]; p0->z = poly_data[poly_adr + 6]; p1->x = poly_data[poly_adr + 7]; p1->y = poly_data[poly_adr + 8]; p1->z = poly_data[poly_adr + 9]; int link = (flags >> 8) & 3; m_view->transform_vector(vn); m_view->transform_point(p0); m_view->transform_point(p1); if (p0->z > 0) { m_view->project_point(p0); } else { p0->s.x = p0->s.y = 0; } if (p1->z > 0) { m_view->project_point(p1); } else { p1->s.x = p1->s.y = 0; } #if 0 if (dump) LOG_TGP(("VIDEO: %08x (%f, %f, %f) (%f, %f, %f)\n", *(uint32_t *)(poly_data + poly_adr), p0->x, p0->y, p0->z, p1->x, p1->y, p1->z)); #endif #if 0 if (true || dump) { LOG_TGP(("VIDEO: %08x (%d, %d) (%d, %d) (%d, %d) (%d, %d)\n", *(uint32_t *)(poly_data + poly_adr), old_p0->s.x, old_p0->s.y, old_p1->s.x, old_p1->s.y, p0->s.x, p0->s.y, p1->s.x, p1->s.y)); } #endif quad_t cquad; if (!link) goto next; if (!(flags & 0x00004000) && view_determinant(old_p1, old_p0, p0) > 0) goto next; vn = glm::normalize(vn); cquad.p[0] = old_p1; cquad.p[1] = old_p0; cquad.p[2] = p0; cquad.p[3] = p1; switch ((flags >> 10) & 3) { case 0: cquad.z = old_z; break; case 1: cquad.z = old_z = min4f(old_p1->z, old_p0->z, p0->z, p1->z); break; case 2: cquad.z = old_z = max4f(old_p1->z, old_p0->z, p0->z, p1->z); break; case 3: default: cquad.z = 0.0; break; } { #if 0 float dif = mult_vector(&vn, &view->light); float ln = view->lightparams[lightmode].a + view->lightparams[lightmode].d*MAX(0.0, dif); cquad.col = scale_color(machine().pens[0x1000 | (m_tgp_ram[tex_adr - 0x40000] & 0x3ff)], MIN(1.0, ln)); cquad.col = scale_color(machine().pens[0x1000 | (m_tgp_ram[tex_adr - 0x40000] & 0x3ff)], MIN(1.0, ln)); #endif float dif = glm::dot(vn, m_view->light); float spec = compute_specular(vn, m_view->light, dif, lightmode); float ln = m_view->lightparams[lightmode].a + m_view->lightparams[lightmode].d * std::max(0.0f, dif) + spec; int lumval = 255.0f * std::min(1.0f, ln); int color = m_paletteram16[0x1000 | (m_tgp_ram[tex_adr - 0x40000] & 0x3ff)]; int r = (color >> 0x0) & 0x1f; int g = (color >> 0x5) & 0x1f; int b = (color >> 0xA) & 0x1f; lumval >>= 2; //there must be a luma translation table somewhere if (lumval > 0x3f) lumval = 0x3f; else if (lumval < 0) lumval = 0; r = (m_color_xlat[(r << 8) | lumval | 0x0] >> 3) & 0x1f; g = (m_color_xlat[(g << 8) | lumval | 0x2000] >> 3) & 0x1f; b = (m_color_xlat[(b << 8) | lumval | 0x4000] >> 3) & 0x1f; cquad.col = (pal5bit(r) << 16) | (pal5bit(g) << 8) | (pal5bit(b) << 0); } if (flags & 0x00002000) cquad.col |= MOIRE; fclip_push_quad(0, cquad); next: poly_adr += 10; switch (link) { case 0: case 2: old_p0 = p0; old_p1 = p1; break; case 1: old_p1 = p0; break; case 3: old_p0 = p1; break; } } } int model1_state::push_direct(int list_offset) { uint32_t tex_adr = readi(list_offset + 2); // v1 = readi(list_offset+2+2); // v2 = readi(list_offset+2+10); point_t *old_p0 = m_pointpt++; point_t *old_p1 = m_pointpt++; old_p0->x = readf(list_offset + 2 + 4); old_p0->y = readf(list_offset + 2 + 6); old_p0->z = readf(list_offset + 2 + 8); old_p1->x = readf(list_offset + 2 + 12); old_p1->y = readf(list_offset + 2 + 14); old_p1->z = readf(list_offset + 2 + 16); LOG_TGP(("VIDEOD start direct\n")); LOG_TGP(("VIDEOD (%f, %f, %f) (%f, %f, %f)\n", old_p0->x, old_p0->y, old_p0->z, old_p1->x, old_p1->y, old_p1->z)); //m_view-transform_point(old_p0); //m_view->transform_point(old_p1); if (old_p0->z > 0) { m_view->project_point_direct(old_p0); } else { old_p0->s.x = old_p0->s.y = 0; } if (old_p1->z > 0) { m_view->project_point_direct(old_p1); } else { old_p1->s.x = old_p1->s.y = 0; } list_offset += 18; for (;;) { uint32_t flags = readi(list_offset + 2); int type = flags & 3; if (!type) break; if (flags & 0x00001000) tex_adr++; // list+2+2 is luminosity // list+2+4 is 0? // list+2+12 is z? point_t *p0 = m_pointpt++; point_t *p1 = m_pointpt++; uint32_t lum = readi(list_offset + 2 + 2); // v1 = readi(list_offset+2+4); float z = 0; if (type == 2) { p0->x = readf(list_offset + 2 + 6); p0->y = readf(list_offset + 2 + 8); p0->z = readf(list_offset + 2 + 10); z = p0->z; LOG_TGP(("VIDEOD %08x %08x (%f, %f, %f)\n", flags, lum, p0->x, p0->y, p0->z)); *p1 = *p0; list_offset += 12; } else { p0->x = readf(list_offset + 2 + 6); p0->y = readf(list_offset + 2 + 8); p0->z = readf(list_offset + 2 + 10); p1->x = readf(list_offset + 2 + 14); p1->y = readf(list_offset + 2 + 16); p1->z = readf(list_offset + 2 + 18); z = readf(list_offset + 2 + 12); LOG_TGP(("VIDEOD %08x %08x (%f, %f, %f) (%f, %f, %f)\n", flags, lum, p0->x, p0->y, p0->z, p1->x, p1->y, p1->z)); list_offset += 20; } int link = (flags >> 8) & 3; //m_view->transform_point(p0); //m_view->transform_point(p1); if (p0->z > 0) { m_view->project_point_direct(p0); } if (p1->z > 0) { m_view->project_point_direct(p1); } #if 1 if (old_p0 && old_p1) LOG_TGP(("VIDEOD: %08x (%d, %d) (%d, %d) (%d, %d) (%d, %d)\n", flags, old_p0->s.x, old_p0->s.y, old_p1->s.x, old_p1->s.y, p0->s.x, p0->s.y, p1->s.x, p1->s.y)); else LOG_TGP(("VIDEOD: %08x (%d, %d) (%d, %d)\n", flags, p0->s.x, p0->s.y, p1->s.x, p1->s.y)); #endif quad_t cquad; if (!link) goto next; cquad.p[0] = old_p1; cquad.p[1] = old_p0; cquad.p[2] = p0; cquad.p[3] = p1; cquad.z = z; { int lumval = ((float)(lum >> 24)) * 2.0f; int color = m_paletteram16[0x1000 | (m_tgp_ram[tex_adr - 0x40000] & 0x3ff)]; int r = (color >> 0x0) & 0x1f; int g = (color >> 0x5) & 0x1f; int b = (color >> 0xA) & 0x1f; lumval >>= 2; //there must be a luma translation table somewhere if (lumval > 0x3f) lumval = 0x3f; else if (lumval < 0) lumval = 0; r = (m_color_xlat[(r << 8) | lumval | 0x0] >> 3) & 0x1f; g = (m_color_xlat[(g << 8) | lumval | 0x2000] >> 3) & 0x1f; b = (m_color_xlat[(b << 8) | lumval | 0x4000] >> 3) & 0x1f; cquad.col = (pal5bit(r) << 16) | (pal5bit(g) << 8) | (pal5bit(b) << 0); } //cquad.col = scale_color(machine().pens[0x1000|(m_tgp_ram[tex_adr-0x40000] & 0x3ff)],((float) (lum>>24)) / 128.0); if (flags & 0x00002000) cquad.col |= MOIRE; fclip_push_quad(0, cquad); next: switch (link) { case 0: case 2: old_p0 = p0; old_p1 = p1; break; case 1: old_p1 = p0; break; case 3: old_p0 = p1; break; } } list_offset += 4; return list_offset; } int model1_state::skip_direct(int list_offset) const { list_offset += 18; while (true) { uint32_t flags = readi(list_offset + 2); int type = flags & 3; if (!type) break; if (type == 2) list_offset += 12; else list_offset += 20; } list_offset += 4; return list_offset; } void model1_state::draw_objects(bitmap_rgb32 &bitmap, const rectangle &cliprect) { if (m_quadpt != m_quaddb) { LOG_TGP(("VIDEO: sort&draw\n")); sort_quads(); draw_quads(bitmap, cliprect); } m_quadpt = m_quaddb; m_pointpt = m_pointdb; } int model1_state::draw_direct(bitmap_rgb32 &bitmap, const rectangle &cliprect, int list_offset) { LOG_TGP(("VIDEO: draw direct %x\n", readi(list_offset + 2))); draw_objects(bitmap, cliprect); list_offset = push_direct(list_offset); unsort_quads(); draw_quads(bitmap, cliprect); m_quadpt = m_quaddb; m_pointpt = m_pointdb; return list_offset; } void model1_state::set_current_render_list() { if(!(m_listctl[0] & 4)) m_listctl[0] = (m_listctl[0] & ~0x40) | (m_listctl[0] & 8 ? 0x40 : 0); m_display_list_current = m_listctl[0] & 0x40 ? m_display_list1 : m_display_list0; } int model1_state::get_list_number() { if(!(m_listctl[0] & 4)) m_listctl[0] = (m_listctl[0] & ~0x40) | (m_listctl[0] & 8 ? 0x40 : 0); return m_listctl[0] & 0x40 ? 0 : 1; } void model1_state::end_frame() { if((m_listctl[0] & 4) && (m_screen->frame_number() & 1)) m_listctl[0] ^= 0x40; } READ16_MEMBER(model1_state::model1_listctl_r) { if(!offset) return m_listctl[0] | 0x30; else return m_listctl[1]; } WRITE16_MEMBER(model1_state::model1_listctl_w) { COMBINE_DATA(m_listctl + offset); LOG_TGP(("VIDEO: control=%08x\n", (m_listctl[1] << 16) | m_listctl[0])); } void model1_state::view_t::init_translation_matrix() { memset(translation, 0, sizeof(translation)); translation[0] = 1.0; translation[4] = 1.0; translation[8] = 1.0; } void model1_state::view_t::set_viewport(float xcenter, float ycenter, float xl, float xr, float yb, float yt) { xc = xcenter; yc = ycenter; x1 = xl; x2 = xr; y1 = yb; y2 = yt; recompute_frustum(); } void model1_state::view_t::set_lightparam(int index, float diffuse, float ambient, float specular, int power) { lightparams[index].d = diffuse; lightparams[index].a = ambient; lightparams[index].s = specular; lightparams[index].p = power; } void model1_state::view_t::set_zoom(float x, float y) { zoomx = x; zoomy = y; recompute_frustum(); } void model1_state::view_t::set_light_direction(float x, float y, float z) { light = glm::normalize(glm::vec3(x, y, z)); } void model1_state::view_t::set_translation_matrix(float* mat) { for (int i = 0; i < 12; i++) { translation[i] = mat[i]; } } void model1_state::view_t::set_view_translation(float x, float y) { viewx = x; viewy = y; recompute_frustum(); } void model1_state::tgp_render(bitmap_rgb32 &bitmap, const rectangle &cliprect) { m_render_done = 1; if ((m_listctl[1] & 0x1f) == 0x1f) { set_current_render_list(); int zz = 0; LOG_TGP(("VIDEO: render list %d\n", get_list_number())); m_view->init_translation_matrix(); int list_offset = 0; for (;;) { int type = readi(list_offset + 0); switch (type) { case 0: list_offset += 2; break; case 1: case 0x41: // 1 = plane 1 // 2 = ?? draw object (413d3, 17c4c, e) // 3 = plane 2 // 4 = ?? draw object (408a8, a479, 9) // 5 = decor // 6 = ?? draw object (57bd4, 387460, 2ad) if (true || zz >= 666) push_object(readi(list_offset + 2), readi(list_offset + 4), readi(list_offset + 6)); list_offset += 8; break; case 2: { list_offset = draw_direct(bitmap, cliprect, list_offset); break; } case 3: { LOG_TGP(("VIDEO: viewport (%d, %d, %d, %d, %d, %d, %d)\n", readi(list_offset + 2), readi16(list_offset + 4), readi16(list_offset + 6), readi16(list_offset + 8), readi16(list_offset + 10), readi16(list_offset + 12), readi16(list_offset + 14))); draw_objects(bitmap, cliprect); float xc = readi16(list_offset + 4); float yc = 383 - (readi16(list_offset + 6) - 39); float x1 = readi16(list_offset + 8); float y2 = 383 - (readi16(list_offset + 10) - 39); float x2 = readi16(list_offset + 12); float y1 = 383 - (readi16(list_offset + 14) - 39); m_view->set_viewport(xc, yc, x1, x2, y1, y2); list_offset += 16; break; } case 4: { int adr = readi(list_offset + 2); int len = readi(list_offset + 4) + 1; LOG_TGP(("ZVIDEO: color write, adr=%x, len=%x\n", adr, len)); for (int i = 0; i < len; i++) { m_tgp_ram[adr - 0x40000 + i] = readi16(list_offset + 6 + 2 * i); } list_offset += 6 + len * 2; break; } case 5: { int adr = readi(list_offset + 2); int len = readi(list_offset + 4); for (int i = 0; i < len; i++) { m_poly_ram[adr - 0x800000 + i] = readi(list_offset + 2 * i + 6); } list_offset += 6 + len * 2; break; } case 6: { int adr = readi(list_offset + 2); int len = readi(list_offset + 4); LOG_TGP(("VIDEO: upload data, adr=%x, len=%x\n", adr, len)); for (int i = 0; i < len; i++) { int v = readi(list_offset + 6 + i * 2); float diffuse = (float(v & 0xff)) / 255.0f; float ambient = (float((v >> 8) & 0xff)) / 255.0f; float specular = (float((v >> 16) & 0xff)) / 255.0f; int power = (v >> 24) & 0xff; m_view->set_lightparam(i + adr, diffuse, ambient, specular, power); } list_offset += 6 + len * 2; break; } case 7: LOG_TGP(("VIDEO: code 7 (%d)\n", readi(list_offset + 2))); zz++; list_offset += 4; break; case 8: LOG_TGP(("VIDEO: select mode %08x\n", readi(list_offset + 2))); list_offset += 4; break; case 9: LOG_TGP(("VIDEO: zoom (%f, %f)\n", readf(list_offset + 2), readf(list_offset + 4))); m_view->set_zoom(readf(list_offset + 2) * 4, readf(list_offset + 4) * 4); list_offset += 6; break; case 0xa: LOG_TGP(("VIDEO: light vector (%f, %f, %f)\n", readf(list_offset + 2), readf(list_offset + 4), readf(list_offset + 6))); m_view->set_light_direction(readf(list_offset + 2), readf(list_offset + 4), readf(list_offset + 6)); list_offset += 8; break; case 0xb: { LOG_TGP(("VIDEO: matrix (%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)\n", readf(list_offset + 2), readf(list_offset + 4), readf(list_offset + 6), readf(list_offset + 8), readf(list_offset + 10), readf(list_offset + 12), readf(list_offset + 14), readf(list_offset + 16), readf(list_offset + 18), readf(list_offset + 20), readf(list_offset + 22), readf(list_offset + 24))); float mat[12]; for (int i = 0; i < 12; i++) { mat[i] = readf(list_offset + 2 + 2 * i); } m_view->set_translation_matrix(mat); list_offset += 26; break; } case 0xc: LOG_TGP(("VIDEO: trans (%f, %f)\n", readf(list_offset + 2), readf(list_offset + 4))); m_view->set_view_translation(readf(list_offset + 2), readf(list_offset + 4)); list_offset += 6; break; case 0xf: //case -1: goto end; default: LOG_TGP(("VIDEO: unknown type %d\n", type)); goto end; } } end: draw_objects(bitmap, cliprect); } } void model1_state::tgp_scan() { #if 0 if (machine().input().code_pressed_once(KEYCODE_F)) { FILE *fp; fp = fopen("tgp-ram.bin", "w+b"); if (fp) { fwrite(m_tgp_ram, (0x100000 - 0x40000) * 2, 1, fp); fclose(fp); } exit(0); } #endif if (!m_render_done && (m_listctl[1] & 0x1f) == 0x1f) { set_current_render_list(); // Skip everything but the data uploads LOG_TGP(("VIDEO: scan list %d\n", get_list_number())); int list_offset = 0; for (;;) { int type = readi(list_offset + 0); switch (type) { case 0: list_offset += 2; break; case 1: case 0x41: list_offset += 8; break; case 2: list_offset = skip_direct(list_offset); break; case 3: list_offset += 16; break; case 4: { int adr = readi(list_offset + 2); int len = readi(list_offset + 4) + 1; LOG_TGP(("ZVIDEO: scan color write, adr=%x, len=%x\n", adr, len)); for (int i = 0; ilightparams[i + adr].d = (float(v & 0xff)) / 255.0f; m_view->lightparams[i + adr].a = (float((v >> 8) & 0xff)) / 255.0f; m_view->lightparams[i + adr].s = (float((v >> 16) & 0xff)) / 255.0f; m_view->lightparams[i + adr].p = (v >> 24) & 0xff; //LOG_TGP((" %02X\n",v)); } list_offset += 6 + len * 2; break; } case 7: list_offset += 4; break; case 8: list_offset += 4; break; case 9: list_offset += 6; break; case 0xa: list_offset += 8; break; case 0xb: list_offset += 26; break; case 0xc: list_offset += 6; break; case 0xf: case -1: goto end; default: LOG_TGP(("VIDEO: unknown type %d\n", type)); goto end; } } end: ; } m_render_done = 0; } VIDEO_START_MEMBER(model1_state, model1) { m_view = std::make_unique(); m_poly_ram = make_unique_clear(0x400000); m_tgp_ram = make_unique_clear(0x100000-0x40000); m_pointdb = auto_alloc_array_clear(machine(), model1_state::point_t, 1000000*2); m_quaddb = auto_alloc_array_clear(machine(), model1_state::quad_t, 1000000); m_quadind = auto_alloc_array_clear(machine(), model1_state::quad_t *, 1000000); m_pointpt = m_pointdb; m_quadpt = m_quaddb; m_listctl[0] = m_listctl[1] = 0; m_clipfn[0].m_isclipped = &model1_state::fclip_isc_bottom; m_clipfn[0].m_clip = &model1_state::fclip_clip_bottom; m_clipfn[1].m_isclipped = &model1_state::fclip_isc_top; m_clipfn[1].m_clip = &model1_state::fclip_clip_top; m_clipfn[2].m_isclipped = &model1_state::fclip_isc_left; m_clipfn[2].m_clip = &model1_state::fclip_clip_left; m_clipfn[3].m_isclipped = &model1_state::fclip_isc_right; m_clipfn[3].m_clip = &model1_state::fclip_clip_right; save_pointer(NAME(m_tgp_ram.get()), 0x100000-0x40000); save_pointer(NAME(m_poly_ram.get()), 0x40000); save_item(NAME(m_listctl)); } uint32_t model1_state::screen_update_model1(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { model1_state::view_t *view = m_view.get(); #if 0 { bool mod = false; double delta = 1.0; if(machine().input().code_pressed(KEYCODE_F)) { mod = true; view->vxx -= delta; } if(machine().input().code_pressed(KEYCODE_G)) { mod = true; view->vxx += delta; } if(machine().input().code_pressed(KEYCODE_H)) { mod = true; view->vyy -= delta; } if(machine().input().code_pressed(KEYCODE_J)) { mod = true; view->vyy += delta; } if(machine().input().code_pressed(KEYCODE_K)) { mod = true; view->vzz -= delta; } if(machine().input().code_pressed(KEYCODE_L)) { mod = true; view->vzz += delta; } if(machine().input().code_pressed(KEYCODE_U)) { mod = true; view->ayy -= 0.05; } if(machine().input().code_pressed(KEYCODE_I)) { mod = true; view->ayy += 0.05; } if(mod) { popmessage("%g,%g,%g:%g", view->vxx, view->vyy, view->vzz, view->ayy); } } #endif view->ayyc = cos(view->ayy); view->ayys = sin(view->ayy); screen.priority().fill(0); bitmap.fill(m_palette->pen(0), cliprect); m_tiles->draw(screen, bitmap, cliprect, 6, 0, 0); m_tiles->draw(screen, bitmap, cliprect, 4, 0, 0); m_tiles->draw(screen, bitmap, cliprect, 2, 0, 0); m_tiles->draw(screen, bitmap, cliprect, 0, 0, 0); tgp_render(bitmap, cliprect); m_tiles->draw(screen, bitmap, cliprect, 7, 0, 0); m_tiles->draw(screen, bitmap, cliprect, 5, 0, 0); m_tiles->draw(screen, bitmap, cliprect, 3, 0, 0); m_tiles->draw(screen, bitmap, cliprect, 1, 0, 0); return 0; } WRITE_LINE_MEMBER(model1_state::screen_vblank_model1) { // on rising edge if (state) { tgp_scan(); end_frame(); LOG_TGP(("TGP: vsync\n")); } }