summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/model2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/video/model2.cpp')
-rw-r--r--src/mame/video/model2.cpp2662
1 files changed, 0 insertions, 2662 deletions
diff --git a/src/mame/video/model2.cpp b/src/mame/video/model2.cpp
deleted file mode 100644
index 2c2b6387274..00000000000
--- a/src/mame/video/model2.cpp
+++ /dev/null
@@ -1,2662 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:R. Belmont, Olivier Galibert, ElSemi, Angelo Salese
-/*********************************************************************************************************************************
-
- Sega Model 2 Geometry Engine and 3D Rasterizer Emulation
-
- General Notes:
-
- - The 3D rendering system has 3 main parts:
- 1) The Geometry Engine: Model 2B and 2C hardware upload the geometry code to a DSP. The original and 2A hardware
- have the geometry code in an internal ROM inside the DSP itself. The simulation written here for the geometry engine
- is based off the disassembly of the code uploaded to a 2B game.
- 2) The Z-Sort and Clip Hardware: Not much information about this hardware. There are a couple of variables that
- can be passed to this stage, like a master Z-Clip enable register, and a Z-Sort mode variable.
- 3) The Hardware Renderer: Not much information about the inner workings of this hardware. Based on the available
- tidbits from the 2B manual, it is a double-buffered device (can be toggled between page flipping and single
- buffer mode), and can also be switched to generate output at 60Hz or 30Hz.
-
- - Most of the information used to write this code come from 3 main sources:
- 1) The Geometry Engine code disassembly from a 2B game.
- 2) The Model 2B-CRX Manual.
- 3) ElSemi's Direct3D implementation of the geometrizer engine.
-
- - The emulation strategy used here is to decouple the geometrizer code from the Z-Sort/Clip/Renderer code, so that for
- original and 2A games, the HLE version of the geometrizer can be used, while for 2B and 2C games, the original geometrizer
- DSP can be emulated, and data can be pushed to the Z-Sort/Clip/Renderer code.
-
-
-
- Geometry Engine Notes and Known Bugs:
-
- - Top Skater seem to use a slightly different geometry code that has a secondary transformation matrix. Once we implement
- the real DSP code upload and emulation for 2B and 2C games, this should not be an issue.
-
-
-
- Z-Sort Notes and Known Bugs:
-
- - The Z-Sort algorithm is not entirely understood. The manual states that the Z-Sort works internally in 1.4.8 bit format,
- but then it states that the Z-Sort Mode register value is added to the floating point z value, and converted to a 16 bit
- format (most likely 4.12).
- - The system allows for Z-Sort override, by means of specifying whether a polygon will use the same Z value ordinal as the
- previous polygon, or the calculated minimum or maximum from it's points. This allows for a full object to be in front of
- another, even though the first object might have some z coordinates that are bigger than the second object's z coordinates.
- - The current implementation takes the effective computed z value for the polygon and converts it into a 4.12 fixed point
- representation, used as an index into an array of linked polygons. Every polygon with the same z value is linked with any
- previous polygon that had that same z value:
-
- z-value index linked list of polygons
- 0000 triangle3->triangle2->triangle1
- 0001 triangle22->triangle8->triangle7
- ....
- FFFF triangle33->triangle11->triangle9
-
- As we add polygons to the array, we also keep track of the minimum and maximum z-value indexes seen this frame.
- When it's time to render, we start and the max z-value seen, and iterate through the array going down to the minimum
- z-value seen, clipping and rendering the linked polygons as we go.
- Unfortunately, it seems there's something not quite right, as there are some visible Z-Sort problems in various games.
- Perhaps the linked list of polygons need to be presorted by a unknown factor before rendering.
-
-
-
- Clip Notes and Known Bugs:
-
- - The Z-Clip happens at z=0. Since dividing by 0 during projection would give bogus values, we scale by a distance of
- 1, thus our projection equation turns into vd.x = (d*vs.x) / (d+vs.z) where d = 1, since we know we won't have polygons
- with a z < 0. This, however, poses the problem that for objects originally between 0.0 and 1.0 are not going to be
- scaled properly. We still need to find a solution for this.
- - A small offset need to be added horizontally and vertically to the viewport and center variables for certain games (like
- the original Model 2 games). The coordinate system has been worked out from the 2B specifications, but the older games
- need a slight adjustment.
-
-
-
- Hardware Renderer Notes and Known Bugs:
-
- - Texturing code could use a real good speed optimization.
- - The U and V coordinates provided by the game are in 13.3 fixed point format.
- - The luma/texel combination algorithm is not known. There are currently some small color glitches here and
- there, and this might be the culprit.
- - The log tables and distance coefficients are used to calculate the number of texels per world unit that need to
- be used to render a texture. Textures can also be provided with smaller levels of details and a LOD bit selector
- in the texture header tells the rasterizer which texture map to use. The rasterizer then can average two texture
- maps to do mip mapping. More information can be found on the 2B manual, on the 'Texturing' and 'Data Format' chapters.
- This is currently unemulated. We always use the texture data from the bigger texture map.
- - The rasterizer supports up to 128x128 'microtex' textures, which are supposed to be higher resolution textures used
- to display more detail when a texture is real close to the viewer. This is currently unemulated.
-
-*********************************************************************************************************************************/
-
-#include "emu.h"
-#include "includes/model2.h"
-
-#include <cmath>
-
-#define pz p[0]
-#define pu p[1]
-#define pv p[2]
-
-
-
-/*******************************************
- *
- * Generic 3D Math Functions
- *
- *******************************************/
-
-static inline void transform_point( poly_vertex *point, float *matrix )
-{
- float tx = (point->x * matrix[0]) + (point->y * matrix[3]) + (point->pz * matrix[6]) + (matrix[9]);
- float ty = (point->x * matrix[1]) + (point->y * matrix[4]) + (point->pz * matrix[7]) + (matrix[10]);
- float tz = (point->x * matrix[2]) + (point->y * matrix[5]) + (point->pz * matrix[8]) + (matrix[11]);
-
- point->x = tx;
- point->y = ty;
- point->pz = tz;
-}
-
-static inline void transform_vector( poly_vertex *vector, float *matrix )
-{
- float tx = (vector->x * matrix[0]) + (vector->y * matrix[3]) + (vector->pz * matrix[6]);
- float ty = (vector->x * matrix[1]) + (vector->y * matrix[4]) + (vector->pz * matrix[7]);
- float tz = (vector->x * matrix[2]) + (vector->y * matrix[5]) + (vector->pz * matrix[8]);
-
- vector->x = tx;
- vector->y = ty;
- vector->pz = tz;
-}
-
-static inline void normalize_vector( poly_vertex *vector )
-{
- float n = sqrt( (vector->x * vector->x) + (vector->y * vector->y) + (vector->pz * vector->pz) );
-
- if ( n )
- {
- float oon = 1.0f / n;
- vector->x *= oon;
- vector->y *= oon;
- vector->pz *= oon;
- }
-}
-
-static inline float dot_product( poly_vertex *v1, poly_vertex *v2 )
-{
- return (v1->x * v2->x) + (v1->y * v2->y) + (v1->pz * v2->pz);
-}
-
-static inline void vector_cross3( poly_vertex *dst, poly_vertex *v0, poly_vertex *v1, poly_vertex *v2 )
-{
- poly_vertex p1, p2;
-
- p1.x = v1->x - v0->x; p1.y = v1->y - v0->y; p1.pz = v1->pz - v0->pz;
- p2.x = v2->x - v0->x; p2.y = v2->y - v0->y; p2.pz = v2->pz - v0->pz;
-
- dst->x = (p1.y * p2.pz) - (p1.pz * p2.y);
- dst->y = (p1.pz * p2.x) - (p1.x * p2.pz);
- dst->pz = (p1.x * p2.y) - (p1.y * p2.x);
-}
-
-static inline void apply_focus( model2_state::geo_state *geo, poly_vertex *p0)
-{
- p0->x *= geo->focus.x;
- p0->y *= geo->focus.y;
-}
-
-/* 1.8.23 float to 4.12 float converter, courtesy of Aaron Giles */
-inline u16 model2_state::float_to_zval( float floatval )
-{
- int32_t fpint = f2u(floatval);
- int32_t exponent = ((fpint >> 23) & 0xff) - 127;
- u32 mantissa = fpint & 0x7fffff;
-
- /* round the low bits and reduce to 12 */
- mantissa += 0x400;
- if (mantissa > 0x7fffff) { exponent++; mantissa = (mantissa & 0x7fffff) >> 1; }
- mantissa >>= 11;
-
- /* if negative, clamp to 0 */
- if (fpint < 0)
- return 0x0000;
-
- /* the rest depends on the exponent */
- /* less than -12 is too small, return 0 */
- if ( exponent < -12 )
- return 0x0000;
-
- /* between -12 and 0 create a denormal with exponent of 0 */
- if ( exponent < 0 )
- return (mantissa | 0x1000) >> -exponent;
-
- /* between 0 and 14 create a FP value with exponent + 1 */
- if ( exponent < 15 )
- return (( exponent + 1 ) << 12) | mantissa;
-
- /* above 14 is too large */
- return 0xffff;
-}
-
-static int32_t clip_polygon(poly_vertex *v, int32_t num_vertices, poly_vertex *vout)
-{
- poly_vertex *cur, *out;
- float curdot, nextdot, scale;
- int32_t i, curin, nextin, nextvert, outcount;
- model2_state::plane clip_plane;
-
- clip_plane.normal.x = 0.0f;
- clip_plane.normal.y = 0.0f;
- clip_plane.normal.pz = 1.0f;
- clip_plane.distance = 0.0f;
-
- outcount = 0;
-
- cur = v;
- out = vout;
-
- curdot = dot_product( cur, &clip_plane.normal );
- curin = (curdot >= clip_plane.distance) ? 1 : 0;
-
- for( i = 0; i < num_vertices; i++ )
- {
- nextvert = (i + 1) % num_vertices;
-
- /* if the current point is inside the plane, add it */
- if ( curin ) memcpy( &out[outcount++], cur, sizeof( poly_vertex ) );
-
- nextdot = dot_product( &v[nextvert], &clip_plane.normal );
- nextin = (nextdot >= clip_plane.distance) ? 1 : 0;
-
- /* Add a clipped vertex if one end of the current edge is inside the plane and the other is outside */
- // TODO: displaying Honey in Fighting Vipers and Bean in Sonic the Fighters somehow causes a NaN dot product here,
- // causing MAME to hardlock in the renderer routine. They are also causing lots of invalid polygon renders
- // which might be related.
- if ( curin != nextin && std::isnan(curdot) == false && std::isnan(nextdot) == false )
- {
- scale = (clip_plane.distance - curdot) / (nextdot - curdot);
-
- out[outcount].x = cur->x + ((v[nextvert].x - cur->x) * scale);
- out[outcount].y = cur->y + ((v[nextvert].y - cur->y) * scale);
- out[outcount].pz = cur->pz + ((v[nextvert].pz - cur->pz) * scale);
- out[outcount].pu = (u16)((float)cur->pu + (((float)v[nextvert].pu - (float)cur->pu) * scale));
- out[outcount].pv = (u16)((float)cur->pv + (((float)v[nextvert].pv - (float)cur->pv) * scale));
- outcount++;
- }
-
- curdot = nextdot;
- curin = nextin;
- cur++;
- }
-
- return outcount;
-}
-
-inline bool model2_state::check_culling( raster_state *raster, u32 attr, float min_z, float max_z )
-{
- /* if doubleside is disabled */
- if ( ((attr >> 17) & 1) == 0 )
- {
- /* if it's the backface, cull it */
- if ( raster->command_buffer[9] & 0x00800000 )
- return true;
- }
-
- /* if the linktype is 0, then we can also cull it */
- if ( ((attr >> 8) & 3) == 0 )
- return true;
-
- /* if the minimum z value is bigger than the master z clip value, don't render */
- if ( (int32_t)(1.0/min_z) > raster->master_z_clip )
- return true;
-
- /* if the maximum z value is < 0 then we can safely clip the entire polygon */
- if ( max_z < 0 )
- return true;
-
- return false;
-}
-
-/***********************************************************************************************/
-
-/*******************************************
- *
- * Hardware 3D Rasterizer Initialization
- *
- *******************************************/
-
-void model2_state::raster_init( memory_region *texture_rom )
-{
- m_raster = std::make_unique<raster_state>();
-
- m_raster->texture_rom = (u16 *)texture_rom->base();
- m_raster->texture_rom_mask = (texture_rom->bytes() / 2) - 1;
-
- save_item(NAME(m_raster->min_z));
- save_item(NAME(m_raster->max_z));
-// save_item(NAME(m_raster->tri_list));
- save_item(NAME(m_raster->tri_list_index));
- save_item(NAME(m_raster->command_buffer));
- save_item(NAME(m_raster->command_index));
- save_item(NAME(m_raster->cur_command));
- save_item(NAME(m_raster->master_z_clip));
- save_item(NAME(m_raster->triangle_z));
- save_item(NAME(m_raster->z_adjust));
- save_item(NAME(m_raster->reverse));
- save_item(NAME(m_raster->viewport));
- save_item(NAME(m_raster->center));
- save_item(NAME(m_raster->center_sel));
- save_item(NAME(m_raster->texture_ram));
- save_item(NAME(m_raster->log_ram));
-}
-
-/*******************************************
- *
- * Hardware 3D Rasterizer Z-Clip selection
- *
- *******************************************/
-
-void model2_state::model2_3d_zclip_w(u32 data)
-{
- m_raster->master_z_clip = data;
-}
-
-// TODO: only Sky Target seems to use this for unknown purpose
-u32 model2_state::polygon_count_r()
-{
-// printf("%08x\n",m_raster->tri_list_index);
-
- return m_raster->tri_list_index;
-}
-
-/*******************************************
- *
- * Hardware 3D Rasterizer Processing
- *
- *******************************************/
-
-void model2_state::model2_3d_process_quad( raster_state *raster, u32 attr )
-{
- quad_m2 object;
- u16 *th, *tp;
- int32_t tho;
- u32 i;
- bool cull;
- float zvalue;
- float min_z, max_z;
-
- /* extract P0(n-1) */
- object.v[1].x = u2f( raster->command_buffer[2] << 8 );
- object.v[1].y = u2f( raster->command_buffer[3] << 8 );
- object.v[1].pz = u2f( raster->command_buffer[4] << 8 );
-
- /* extract P1(n-1) */
- object.v[0].x = u2f( raster->command_buffer[5] << 8 );
- object.v[0].y = u2f( raster->command_buffer[6] << 8 );
- object.v[0].pz = u2f( raster->command_buffer[7] << 8 );
-
- /* extract P0(n) */
- object.v[2].x = u2f( raster->command_buffer[11] << 8 );
- object.v[2].y = u2f( raster->command_buffer[12] << 8 );
- object.v[2].pz = u2f( raster->command_buffer[13] << 8 );
-
- /* extract P1(n) */
- object.v[3].x = u2f( raster->command_buffer[14] << 8 );
- object.v[3].y = u2f( raster->command_buffer[15] << 8 );
- object.v[3].pz = u2f( raster->command_buffer[16] << 8 );
-
- /* always calculate the min z and max z value */
- min_z = object.v[0].pz;
- if ( object.v[1].pz < min_z ) min_z = object.v[1].pz;
- if ( object.v[2].pz < min_z ) min_z = object.v[2].pz;
- if ( object.v[3].pz < min_z ) min_z = object.v[3].pz;
-
- max_z = object.v[0].pz;
- if ( object.v[1].pz > max_z ) max_z = object.v[1].pz;
- if ( object.v[2].pz > max_z ) max_z = object.v[2].pz;
- if ( object.v[3].pz > max_z ) max_z = object.v[3].pz;
-
- /* read in the texture information */
-
- /* texture point data */
- if ( raster->command_buffer[0] & 0x800000 )
- tp = &raster->texture_ram[raster->command_buffer[0] & 0xFFFF];
- else
- tp = &raster->texture_rom[raster->command_buffer[0] & raster->texture_rom_mask];
-
- object.v[0].pv = *tp++;
- object.v[0].pu = *tp++;
- object.v[1].pv = *tp++;
- object.v[1].pu = *tp++;
- object.v[2].pv = *tp++;
- object.v[2].pu = *tp++;
- object.v[3].pv = *tp++;
- object.v[3].pu = *tp++;
-
- /* update the address */
- raster->command_buffer[0] += 8;
-
- /* texture header data */
- if ( raster->command_buffer[1] & 0x800000 )
- th = &raster->texture_ram[raster->command_buffer[1] & 0xFFFF];
- else
- th = &raster->texture_rom[raster->command_buffer[1] & raster->texture_rom_mask];
-
- object.texheader[0] = *th++;
- object.texheader[1] = *th++;
- object.texheader[2] = *th++;
- object.texheader[3] = *th++;
-
- /* extract the texture header offset */
- tho = (attr >> 12) & 0x1F;
-
- /* adjust for sign */
- if ( tho & 0x10 )
- tho |= -16;
-
- /* update the address */
- raster->command_buffer[1] += tho * 4;
-
- /* set the luma value of this quad */
- object.luma = (raster->command_buffer[9] >> 15) & 0xFF;
-
- /* determine whether we can cull this quad */
- cull = check_culling(raster,attr,min_z,max_z);
-
- /* set the object's z value */
- switch((attr >> 10) & 3)
- {
- case 0: // old value
- zvalue = raster->triangle_z;
- break;
- case 1: // min z
- zvalue = min_z;
- break;
- case 2: // max z
- zvalue = max_z;
- break;
- case 3: // error
- default:
- zvalue = 1e10;
- break;
- }
-
- raster->triangle_z = zvalue;
-
- if ( cull == false )
- {
- int32_t clipped_verts;
- poly_vertex verts[10];
-
- /* do near z clipping */
- clipped_verts = clip_polygon( object.v, 4, verts);
-
- if ( clipped_verts > 2 )
- {
- triangle *ztri;
-
- /* adjust and set the object z-sort value */
- object.z = float_to_zval( zvalue + raster->z_adjust );
-
- /* get our list read to add the triangles */
- ztri = raster->tri_sorted_list[object.z];
-
- if ( ztri != nullptr )
- {
- while( ztri->next != nullptr )
- ztri = (triangle *)ztri->next;
- }
-
- /* go through the clipped vertex list, adding triangles */
- for( i = 2; i < clipped_verts; i++ )
- {
- triangle *tri;
-
- tri = &raster->tri_list[raster->tri_list_index++];
-
- if ( raster->tri_list_index >= MAX_TRIANGLES )
- {
- fatalerror( "SEGA 3D: Max triangle limit exceeded\n" );
- }
-
- /* copy the object information */
- tri->z = object.z;
- tri->texheader[0] = object.texheader[0];
- tri->texheader[1] = object.texheader[1];
- tri->texheader[2] = object.texheader[2];
- tri->texheader[3] = object.texheader[3];
- tri->luma = object.luma;
-
- /* set the viewport */
- tri->viewport[0] = raster->viewport[0];
- tri->viewport[1] = raster->viewport[1];
- tri->viewport[2] = raster->viewport[2];
- tri->viewport[3] = raster->viewport[3];
-
- /* set the center */
- tri->center[0] = raster->center[raster->center_sel][0];
- tri->center[1] = raster->center[raster->center_sel][1];
-
- memcpy( &tri->v[0], &verts[0], sizeof( poly_vertex ) );
- memcpy( &tri->v[1], &verts[i-1], sizeof( poly_vertex ) );
- memcpy( &tri->v[2], &verts[i], sizeof( poly_vertex ) );
-
- /* add to our sorted list */
- tri->next = nullptr;
-
- if ( ztri == nullptr )
- {
- raster->tri_sorted_list[object.z] = tri;
- }
- else
- {
- ztri->next = tri;
- }
-
- ztri = tri;
- }
-
- /* keep around the min and max z values for this frame */
- if ( object.z < raster->min_z ) raster->min_z = object.z;
- if ( object.z > raster->max_z ) raster->max_z = object.z;
- }
- }
-
- /* update linking */
- switch( ((attr >> 8) & 3) )
- {
- case 0:
- case 2:
- {
- /* reuse P0(n) and P1(n) */
- for( i = 0; i < 6; i++ ) /* P0(n) -> P0(n-1) */
- raster->command_buffer[2+i] = raster->command_buffer[11+i]; /* P1(n) -> P1(n-1) */
- }
- break;
-
- case 1:
- {
- /* reuse P0(n-1) and P0(n) */
- for( i = 0; i < 3; i++ )
- raster->command_buffer[5+i] = raster->command_buffer[11+i]; /* P0(n) -> P1(n-1) */
- }
- break;
-
- case 3:
- {
- /* reuse P1(n-1) and P1(n) */
- for( i = 0; i < 3; i++ )
- raster->command_buffer[2+i] = raster->command_buffer[14+i]; /* P1(n) -> P1(n-1) */
- }
- break;
- }
-}
-
-void model2_state::model2_3d_process_triangle( raster_state *raster, u32 attr )
-{
- triangle object;
- u16 *th, *tp;
- int32_t tho;
- u32 i;
- bool cull;
- float zvalue;
- float min_z, max_z;
-
- /* extract P0(n-1) */
- object.v[1].x = u2f( raster->command_buffer[2] << 8 );
- object.v[1].y = u2f( raster->command_buffer[3] << 8 );
- object.v[1].pz = u2f( raster->command_buffer[4] << 8 );
-
- /* extract P1(n-1) */
- object.v[0].x = u2f( raster->command_buffer[5] << 8 );
- object.v[0].y = u2f( raster->command_buffer[6] << 8 );
- object.v[0].pz = u2f( raster->command_buffer[7] << 8 );
-
- /* extract P0(n) */
- object.v[2].x = u2f( raster->command_buffer[11] << 8 );
- object.v[2].y = u2f( raster->command_buffer[12] << 8 );
- object.v[2].pz = u2f( raster->command_buffer[13] << 8 );
-
- /* for triangles, the rope of P1(n) is achieved by P0(n-1) (linktype 3) */
- raster->command_buffer[14] = raster->command_buffer[11];
- raster->command_buffer[15] = raster->command_buffer[12];
- raster->command_buffer[16] = raster->command_buffer[13];
-
- /* always calculate the min z and max z values */
- min_z = object.v[0].pz;
- if ( object.v[1].pz < min_z ) min_z = object.v[1].pz;
- if ( object.v[2].pz < min_z ) min_z = object.v[2].pz;
-
- max_z = object.v[0].pz;
- if ( object.v[1].pz > max_z ) max_z = object.v[1].pz;
- if ( object.v[2].pz > max_z ) max_z = object.v[2].pz;
-
- /* read in the texture information */
-
- /* texture point data */
- if ( raster->command_buffer[0] & 0x800000 )
- tp = &raster->texture_ram[raster->command_buffer[0] & 0xFFFF];
- else
- tp = &raster->texture_rom[raster->command_buffer[0] & raster->texture_rom_mask];
-
- object.v[0].pv = *tp++;
- object.v[0].pu = *tp++;
- object.v[1].pv = *tp++;
- object.v[1].pu = *tp++;
- object.v[2].pv = *tp++;
- object.v[2].pu = *tp++;
-
- /* update the address */
- raster->command_buffer[0] += 6;
-
- /* texture header data */
- if ( raster->command_buffer[1] & 0x800000 )
- th = &raster->texture_ram[raster->command_buffer[1] & 0xFFFF];
- else
- th = &raster->texture_rom[raster->command_buffer[1] & raster->texture_rom_mask];
-
- object.texheader[0] = *th++;
- object.texheader[1] = *th++;
- object.texheader[2] = *th++;
- object.texheader[3] = *th++;
-
- /* extract the texture header offset */
- tho = (attr >> 12) & 0x1F;
-
- /* adjust for sign */
- if ( tho & 0x10 )
- tho |= -16;
-
- /* update the address */
- raster->command_buffer[1] += tho * 4;
-
- /* set the luma value of this quad */
- object.luma = (raster->command_buffer[9] >> 15) & 0xFF;
-
- /* determine whether we can cull this quad */
- cull = check_culling(raster,attr,min_z,max_z);
-
- /* set the object's z value */
- switch((attr >> 10) & 3)
- {
- case 0: // old value
- zvalue = raster->triangle_z;
- break;
- case 1: // min z
- zvalue = min_z;
- break;
- case 2: // max z
- zvalue = max_z;
- break;
- case 3: // error
- default:
- zvalue = 1e10;
- break;
- }
-
- raster->triangle_z = zvalue;
-
- /* if we're not culling, do z-clip and add to out triangle list */
- if ( cull == false )
- {
- int32_t clipped_verts;
- poly_vertex verts[10];
-
- /* do near z clipping */
- clipped_verts = clip_polygon( object.v, 3, verts);
-
- if ( clipped_verts > 2 )
- {
- triangle *ztri;
-
- /* adjust and set the object z-sort value */
- object.z = float_to_zval( zvalue + raster->z_adjust );
-
- /* get our list read to add the triangles */
- ztri = raster->tri_sorted_list[object.z];
-
- if ( ztri != nullptr )
- {
- while( ztri->next != nullptr )
- ztri = (triangle *)ztri->next;
- }
-
- /* go through the clipped vertex list, adding triangles */
- for( i = 2; i < clipped_verts; i++ )
- {
- triangle *tri;
-
- tri = &raster->tri_list[raster->tri_list_index++];
-
- if ( raster->tri_list_index >= MAX_TRIANGLES )
- {
- fatalerror( "SEGA 3D: Max triangle limit exceeded\n" );
- }
-
- /* copy the object information */
- tri->z = object.z;
- tri->texheader[0] = object.texheader[0];
- tri->texheader[1] = object.texheader[1];
- tri->texheader[2] = object.texheader[2];
- tri->texheader[3] = object.texheader[3];
- tri->luma = object.luma;
-
- /* set the viewport */
- tri->viewport[0] = raster->viewport[0];
- tri->viewport[1] = raster->viewport[1];
- tri->viewport[2] = raster->viewport[2];
- tri->viewport[3] = raster->viewport[3];
-
- /* set the center */
- tri->center[0] = raster->center[raster->center_sel][0];
- tri->center[1] = raster->center[raster->center_sel][1];
-
- memcpy( &tri->v[0], &verts[0], sizeof( poly_vertex ) );
- memcpy( &tri->v[1], &verts[i-1], sizeof( poly_vertex ) );
- memcpy( &tri->v[2], &verts[i], sizeof( poly_vertex ) );
-
- /* add to our sorted list */
- tri->next = nullptr;
-
- if ( ztri == nullptr )
- {
- raster->tri_sorted_list[object.z] = tri;
- }
- else
- {
- ztri->next = tri;
- }
-
- ztri = tri;
- }
-
- /* keep around the min and max z values for this frame */
- if ( object.z < raster->min_z ) raster->min_z = object.z;
- if ( object.z > raster->max_z ) raster->max_z = object.z;
- }
- }
-
- /* update linking */
- switch( ((attr >> 8) & 3) )
- {
- case 0:
- case 2:
- {
- /* reuse P0(n) and P1(n) */
- for( i = 0; i < 6; i++ ) /* P0(n) -> P0(n-1) */
- raster->command_buffer[2+i] = raster->command_buffer[11+i]; /* P1(n) -> P1(n-1) */
- }
- break;
-
- case 1:
- {
- /* reuse P0(n-1) and P0(n) */
- for( i = 0; i < 3; i++ )
- raster->command_buffer[5+i] = raster->command_buffer[11+i]; /* P0(n) -> P1(n-1) */
- }
- break;
-
- case 3:
- {
- /* reuse P1(n-1) and P1(n) */
- for( i = 0; i < 3; i++ )
- raster->command_buffer[2+i] = raster->command_buffer[14+i]; /* P1(n) -> P1(n-1) */
- }
- break;
- }
-}
-
-/***********************************************************************************************/
-
-/***********************************************************************************************/
-
-void model2_renderer::model2_3d_render(triangle *tri, const rectangle &cliprect)
-{
- model2_renderer *poly = m_state.m_poly.get();
- m2_poly_extra_data& extra = poly->object_data().next();
- u8 renderer;
-
- /* select renderer based on attributes (bit15 = checker, bit14 = textured, bit13 = transparent */
- renderer = (tri->texheader[0] >> 13) & 7;
-
- /* calculate and clip to viewport */
- rectangle vp(tri->viewport[0] + m_xoffs, tri->viewport[2] + m_xoffs, (384-tri->viewport[3]) + m_yoffs, (384-tri->viewport[1]) + m_yoffs);
- vp &= cliprect;
-
- extra.state = &m_state;
- extra.lumabase = ((tri->texheader[1] & 0xFF) << 7) + ((tri->luma >> 5) ^ 0x7);
- extra.colorbase = (tri->texheader[3] >> 6) & 0x3FF;
-
- if (renderer & 2)
- {
- extra.texwidth = 32 << ((tri->texheader[0] >> 0) & 0x7);
- extra.texheight = 32 << ((tri->texheader[0] >> 3) & 0x7);
- extra.texx = 32 * ((tri->texheader[2] >> 0) & 0x1f);
- extra.texy = 32 * (((tri->texheader[2] >> 6) & 0x1f) + ( tri->texheader[2] & 0x20 ));
- /* TODO: Virtua Striker contradicts with this. */
- extra.texmirrorx = 0;//(tri->texheader[0] >> 9) & 1;
- extra.texmirrory = 0;//(tri->texheader[0] >> 8) & 1;
- extra.texsheet = (tri->texheader[2] & 0x1000) ? m_state.m_textureram1 : m_state.m_textureram0;
-
- tri->v[0].pz = 1.0f / (1.0f + tri->v[0].pz);
- tri->v[0].pu = tri->v[0].pu * tri->v[0].pz * (1.0f / 8.0f);
- tri->v[0].pv = tri->v[0].pv * tri->v[0].pz * (1.0f / 8.0f);
- tri->v[1].pz = 1.0f / (1.0f + tri->v[1].pz);
- tri->v[1].pu = tri->v[1].pu * tri->v[1].pz * (1.0f / 8.0f);
- tri->v[1].pv = tri->v[1].pv * tri->v[1].pz * (1.0f / 8.0f);
- tri->v[2].pz = 1.0f / (1.0f + tri->v[2].pz);
- tri->v[2].pu = tri->v[2].pu * tri->v[2].pz * (1.0f / 8.0f);
- tri->v[2].pv = tri->v[2].pv * tri->v[2].pz * (1.0f / 8.0f);
-
- // Note : The class model2_renderer has an array of function pointers in it named m_renderfuncs, in theory this simply
- // needs to be passed into the render_triangle function as such model2_renderer::m_renderfuncs[renderer], but
- // I was unable to make it work when converting to the new polygon rasterizer interface.
- switch (renderer)
- {
- case 0: render_triangle<3>(vp, render_delegate(&model2_renderer::model2_3d_render_0, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 1: render_triangle<3>(vp, render_delegate(&model2_renderer::model2_3d_render_1, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 2: render_triangle<3>(vp, render_delegate(&model2_renderer::model2_3d_render_2, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 3: render_triangle<3>(vp, render_delegate(&model2_renderer::model2_3d_render_3, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 4: render_triangle<3>(vp, render_delegate(&model2_renderer::model2_3d_render_4, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 5: render_triangle<3>(vp, render_delegate(&model2_renderer::model2_3d_render_5, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 6: render_triangle<3>(vp, render_delegate(&model2_renderer::model2_3d_render_6, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 7: render_triangle<3>(vp, render_delegate(&model2_renderer::model2_3d_render_7, this), tri->v[0], tri->v[1], tri->v[2]); break;
- }
- }
- else
- {
- switch (renderer)
- {
- case 0: render_triangle<0>(vp, render_delegate(&model2_renderer::model2_3d_render_0, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 1: render_triangle<0>(vp, render_delegate(&model2_renderer::model2_3d_render_1, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 2: render_triangle<0>(vp, render_delegate(&model2_renderer::model2_3d_render_2, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 3: render_triangle<0>(vp, render_delegate(&model2_renderer::model2_3d_render_3, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 4: render_triangle<0>(vp, render_delegate(&model2_renderer::model2_3d_render_4, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 5: render_triangle<0>(vp, render_delegate(&model2_renderer::model2_3d_render_5, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 6: render_triangle<0>(vp, render_delegate(&model2_renderer::model2_3d_render_6, this), tri->v[0], tri->v[1], tri->v[2]); break;
- case 7: render_triangle<0>(vp, render_delegate(&model2_renderer::model2_3d_render_7, this), tri->v[0], tri->v[1], tri->v[2]); break;
- }
- }
-}
-
-/*
- Projection:
-
- According to the 2B Manual the screen coordinates are:
-
- (8,474) (504,474)
- +--------------------------------+
- | |
- | |
- | |
- | |
- | |
- | |
- | |
- | |
- +--------------------------------+
- (8,90) (504,90)
-*/
-
-void model2_state::horizontal_sync_w(u16 data)
-{
- m_crtc_xoffset = 84 + (int16_t)data;
-// printf("H %04x %d %d\n",data,(int16_t)data,m_crtc_xoffset);
- m_poly->set_xoffset(m_crtc_xoffset);
-}
-
-void model2_state::vertical_sync_w(u16 data)
-{
- m_crtc_yoffset = 130 + (int16_t)data;
-// printf("V %04x %d %d\n",data,(int16_t)data,m_crtc_yoffset);
- m_poly->set_yoffset(m_crtc_yoffset);
-}
-
-/* 3D Rasterizer projection: projects a triangle into screen coordinates */
-inline void model2_state::model2_3d_project( triangle *tri )
-{
- u16 i;
-
- for( i = 0; i < 3; i++ )
- {
- /* project the vertices */
- tri->v[i].x = m_crtc_xoffset + tri->center[0] + (tri->v[i].x / (1.0f+tri->v[i].pz));
- tri->v[i].y = ((384 - tri->center[1])+m_crtc_yoffset) - (tri->v[i].y / (1.0f+tri->v[i].pz));
- }
-}
-
-/* 3D Rasterizer frame start: Resets frame variables */
-void model2_state::model2_3d_frame_start( void )
-{
- raster_state *raster = m_raster.get();
-
- /* reset the triangle list index */
- raster->tri_list_index = 0;
-
- /* reset the sorted z list */
- memset( raster->tri_sorted_list, 0, 0x10000 * sizeof( triangle * ) );
-
- /* reset the min-max sortable Z values */
- raster->min_z = 0xFFFF;
- raster->max_z = 0;
-}
-
-void model2_state::model2_3d_frame_end( bitmap_rgb32 &bitmap, const rectangle &cliprect )
-{
- raster_state *raster = m_raster.get();
- int32_t z;
-
- /* if we have nothing to render, bail */
- if ( raster->tri_list_index == 0 )
- return;
-
- m_poly->destmap().fill(0x00000000, cliprect);
-
- /* go through the Z levels, and render each bucket */
- for( z = raster->max_z; z >= raster->min_z; z-- )
- {
- /* see if we have items at this z level */
- if ( raster->tri_sorted_list[z] != nullptr )
- {
- /* get a pointer to the first triangle */
- triangle *tri = raster->tri_sorted_list[z];
-
- /* and loop clipping and rendering each triangle */
- while( tri != nullptr )
- {
- /* project and render */
- model2_3d_project( tri );
- m_poly->model2_3d_render(tri, cliprect);
-
- tri = (triangle *)tri->next;
- }
- }
- }
- m_poly->wait("End of frame");
-
- copybitmap_trans(bitmap, m_poly->destmap(), 0, 0, 0, 0, cliprect, 0x00000000);
-}
-
-// direct framebuffer drawing (enabled with render test mode, Last Bronx title screen)
-// pretty sure test mode cuts off DSP framebuffer drawing/clear, according to the manual description too
-void model2_state::draw_framebuffer( bitmap_rgb32 &bitmap, const rectangle &cliprect )
-{
- u16 *fbvram = &(m_screen->frame_number() & 1 ? m_fbvramB[0] : m_fbvramA[0]);
- // TODO: halved crtc values?
- int xoffs = (-m_crtc_xoffset)/2;
- int yoffs = m_crtc_yoffset/2;
-
- for (int y = cliprect.min_y; y <= cliprect.max_y; ++y)
- {
- for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
- {
- int offset = (x + xoffs) + (y + yoffs)*512;
- int b = (fbvram[offset] >> 0) & 0x1f;
- int r = (fbvram[offset] >> 5) & 0x1f;
- int g = (fbvram[offset] >> 10) & 0x1f;
- r = pal5bit(r);
- g = pal5bit(g);
- b = pal5bit(b);
- bitmap.pix(y, x) = r << 16 | g << 8 | b;
- }
- }
-}
-
-/* 3D Rasterizer main data input port */
-void model2_state::model2_3d_push( raster_state *raster, u32 input )
-{
- /* see if we have a command in progress */
- if ( raster->cur_command != 0 )
- {
- raster->command_buffer[raster->command_index++] = input;
-
- switch( raster->cur_command )
- {
- case 0x00: /* NOP */
- break;
-
- case 0x01: /* Polygon Data */
- {
- u32 attr;
-
- /* start by looking if we have the basic input data */
- if ( raster->command_index < 9 )
- return;
-
- /* get the attributes */
- attr = raster->command_buffer[8];
-
- /* see if we're done */
- if ( (attr & 3) == 0 )
- {
- raster->cur_command = 0;
- return;
- }
-
- /* see if it's a quad or a triangle */
- if ( attr & 1 )
- {
- /* it's a quad, wait for the rest of the points */
- if ( raster->command_index < 17 )
- return;
-
- /* we have a full quad info, fill up our quad structure */
- model2_3d_process_quad( raster, attr );
-
- /* back up and wait for more data */
- raster->command_index = 8;
- }
- else
- {
- /* it's a triangle, wait for the rest of the point */
- if ( raster->command_index < 14 )
- return;
-
- /* we have a full quad info, fill up our quad structure */
- model2_3d_process_triangle( raster, attr );
-
- /* back up and wait for more data */
- raster->command_index = 8;
- }
- }
- break;
-
- case 0x03: /* Window Data */
- {
- u32 i;
-
- /* make sure we have all the data */
- if ( raster->command_index < 6 )
- return;
-
- /* coordinates are 12 bit signed */
-
- /* extract the viewport start x */
- raster->viewport[0] = (raster->command_buffer[0] >> 12) & 0xFFF;
-
- if ( raster->viewport[0] & 0x800 )
- raster->viewport[0] = -( 0x800 - (raster->viewport[0] & 0x7FF) );
-
- /* extract the viewport start y */
- raster->viewport[1] = raster->command_buffer[0] & 0xFFF;
-
- if ( raster->viewport[1] & 0x800 )
- raster->viewport[1] = -( 0x800 - (raster->viewport[1] & 0x7FF) );
-
- /* extract the viewport end x */
- raster->viewport[2] = (raster->command_buffer[1] >> 12) & 0xFFF;
-
- if ( raster->viewport[2] & 0x800 )
- raster->viewport[2] = -( 0x800 - (raster->viewport[2] & 0x7FF) );
-
- /* extract the viewport end y */
- raster->viewport[3] = raster->command_buffer[1] & 0xFFF;
-
- if ( raster->viewport[3] & 0x800 )
- raster->viewport[3] = -( 0x800 - (raster->viewport[3] & 0x7FF) );
-
- /* extract the centers */
- for( i = 0; i < 4; i++ )
- {
- /* center x */
- raster->center[i][0] = (raster->command_buffer[2+i] >> 12) & 0xFFF;
-
- if ( raster->center[i][0] & 0x800 )
- raster->center[i][0] = -( 0x800 - (raster->center[i][0] & 0x7FF) );
-
- /* center y */
- raster->center[i][1] = raster->command_buffer[2+i] & 0xFFF;
-
- if ( raster->center[i][1] & 0x800 )
- raster->center[i][1] = -( 0x800 - (raster->center[i][1] & 0x7FF) );
- }
-
- /* done with this command */
- raster->cur_command = 0;
- }
- break;
-
- case 0x04: /* Texture/Log Data write */
- {
- /* make sure we have enough data */
- if ( raster->command_index < 2 )
- return;
-
- /* see if the count is non-zero */
- if ( raster->command_buffer[1] > 0 )
- {
- /* see if we have data available */
- if ( raster->command_index >= 3 )
- {
- /* get the address */
- u32 address = raster->command_buffer[0];
-
- /* do the write */
- if ( address & 0x800000 )
- raster->texture_ram[address&0xFFFF] = raster->command_buffer[2];
- else
- raster->log_ram[address&0xFFFF] = raster->command_buffer[2];
-
- /* increment the address and decrease the count */
- raster->command_buffer[0]++;
- raster->command_buffer[1]--;
-
- /* decrease the index, so we keep placing data in the same slot */
- raster->command_index--;
- }
- }
-
- /* see if we're done with this command */
- if ( raster->command_buffer[1] == 0 )
- raster->cur_command = 0;
- }
- break;
-
- case 0x08: /* ZSort mode */
- {
- /* save the zsort mode value */
- raster->z_adjust = u2f( raster->command_buffer[0] << 8 );
-
- /* done with this command */
- raster->cur_command = 0;
- }
- break;
-
- default:
- {
- fatalerror( "SEGA 3D: Unknown rasterizer command %08x\n", raster->cur_command );
- }
- }
- }
- else
- {
- /* new command */
- raster->cur_command = input & 0x0F;
- raster->command_index = 0;
-
- /* see if it's object data */
- if ( raster->cur_command == 1 )
- {
- /* extract reverse bit */
- raster->reverse = (input >> 4) & 1;
-
- /* extract center select */
- raster->center_sel = ( input >> 6 ) & 3;
-
- /* reset the triangle z value */
- // Zero Gunner sets backgrounds with "previous z value" mode at the start of the display list,
- // needs this to be this big in order to work properly
- raster->triangle_z = 1e10;
- }
- }
-}
-
-/***********************************************************************************************/
-
-/*******************************************
- *
- * Geometry Engine Initialization
- *
- *******************************************/
-
-void model2_state::geo_init(memory_region *polygon_rom)
-{
- m_geo = std::make_unique<geo_state>();
- m_geo->state = this;
-
- m_geo->raster = m_raster.get();
- m_geo->polygon_rom = (u32 *)polygon_rom->base();
- m_geo->polygon_rom_mask = (polygon_rom->bytes() / 4) - 1;
-
- save_item(NAME(m_geo->mode));
- save_item(NAME(m_geo->matrix));
- save_item(NAME(m_geo->lod));
- save_item(NAME(m_geo->coef_table));
- save_item(NAME(m_geo->polygon_ram0));
- save_item(NAME(m_geo->polygon_ram1));
-}
-
-/*******************************************
- *
- * Geometry Engine Polygon Parsers
- *
- *******************************************/
-
-/* Parse Polygons: Normals Present, No Specular case */
-void model2_state::geo_parse_np_ns( geo_state *geo, u32 *input, u32 count )
-{
- raster_state *raster = geo->raster;
- poly_vertex point, normal;
- u32 attr, i;
-
- /* read the 1st point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push it to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* read the 2nd point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push it to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* loop through the following links */
- for( i = 0; i < count; i++ )
- {
- /* read in the attributes */
- attr = *input++;
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, attr & 0x0003FFFF );
-
- /* read in the normal */
- normal.x = u2f(*input++);
- normal.y = u2f(*input++);
- normal.pz = u2f(*input++);
-
- /* transform with the current matrix */
- transform_vector( &normal, geo->matrix );
-
- if ( (attr & 3) != 0 ) /* quad or triangle */
- {
- float dotl, dotp, luminance, distance;
- float coef, face;
- int32_t luma;
- texture_parameter * texparam;
-
- /* read in the next point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* calculate the dot product of the normal and the light vector */
- dotl = dot_product( &normal, &geo->light );
-
- /* calculate the dot product of the normal and the point */
- dotp = dot_product( &normal, &point );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* determine whether this is the front or the back of the polygon */
- face = 0x100; /* rear */
- if ( dotp >= 0 ) face = 0; /* front */
-
- /* get the texture parameters */
- texparam = &geo->texture_parameters[(attr>>18) & 0x1f];
-
- /* calculate luminance */
- if ( (dotl * dotp) < 0 ) luminance = 0;
- else luminance = fabs( dotl );
-
- luminance = (luminance * texparam->diffuse) + texparam->ambient;
- luma = (int32_t)luminance;
-
- if ( luma > 255 ) luma = 255;
- if ( luma < 0 ) luma = 0;
-
- /* add the face bit to the luma */
- luma += face;
-
- /* extract distance coefficient */
- coef = geo->coef_table[attr>>27];
-
- /* calculate texture level of detail */
- distance = coef * fabs( dotp ) * geo->lod;
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, luma << 15 );
- model2_3d_push( raster, f2u(distance) >> 8 );
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* if it's a quad, push one more point */
- if ( attr & 1 )
- {
- /* read in the next point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
- }
- else /* triangle */
- {
- /* skip the next 3 points */
- input += 3;
- }
- }
- else /* we're done */
- {
- break;
- }
- }
-
- /* notify the 3d rasterizer we're done */
- model2_3d_push( raster, 0 );
-}
-
-/* Parse Polygons: Normals Present, Specular case */
-void model2_state::geo_parse_np_s( geo_state *geo, u32 *input, u32 count )
-{
- raster_state *raster = geo->raster;
- poly_vertex point, normal;
- u32 attr, i;
-
- /* read the 1st point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push it to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* read the 2nd point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push it to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* loop through the following links */
- for( i = 0; i < count; i++ )
- {
- /* read in the attributes */
- attr = *input++;
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, attr & 0x0003FFFF );
-
- /* read in the normal */
- normal.x = u2f(*input++);
- normal.y = u2f(*input++);
- normal.pz = u2f(*input++);
-
- /* transform with the current matrix */
- transform_vector( &normal, geo->matrix );
-
- if ( (attr & 3) != 0 ) /* quad or triangle */
- {
- float dotl, dotp, luminance, distance, specular;
- float coef, face;
- int32_t luma;
- texture_parameter * texparam;
-
- /* read in the next point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* calculate the dot product of the normal and the light vector */
- dotl = dot_product( &normal, &geo->light );
-
- /* calculate the dot product of the normal and the point */
- dotp = dot_product( &normal, &point );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* determine whether this is the front or the back of the polygon */
- face = 0x100; /* rear */
- if ( dotp >= 0 ) face = 0; /* front */
-
- /* get the texture parameters */
- texparam = &geo->texture_parameters[(attr>>18) & 0x1f];
-
- /* calculate luminance and specular */
- if ( (dotl * dotp) < 0 ) luminance = 0;
- else luminance = fabs( dotl );
-
- specular = ((2*dotl) * normal.pz) - geo->light.pz;
- if ( specular < 0 ) specular = 0;
- if ( texparam->specular_control == 0 ) specular = 0;
- if ( (texparam->specular_control >> 1) != 0 ) specular *= specular;
- if ( (texparam->specular_control >> 2) != 0 ) specular *= specular;
- if ( ((texparam->specular_control+1) >> 3) != 0 ) specular *= specular;
-
- specular *= texparam->specular_scale;
-
- luminance = (luminance * texparam->diffuse) + texparam->ambient + specular;
- luma = (int32_t)luminance;
-
- if ( luma > 255 ) luma = 255;
- if ( luma < 0 ) luma = 0;
-
- /* add the face bit to the luma */
- luma += face;
-
- /* extract distance coefficient */
- coef = geo->coef_table[attr>>27];
-
- /* calculate texture level of detail */
- distance = coef * fabs( dotp ) * geo->lod;
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, luma << 15 );
- model2_3d_push( raster, f2u(distance) >> 8 );
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* if it's a quad, push one more point */
- if ( attr & 1 )
- {
- /* read in the next point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
- }
- else /* triangle */
- {
- /* skip the next 3 points */
- input += 3;
- }
- }
- else /* we're done */
- {
- break;
- }
- }
-
- /* notify the 3d rasterizer we're done */
- model2_3d_push( raster, 0 );
-}
-
-/* Parse Polygons: No Normals, No Specular case */
-void model2_state::geo_parse_nn_ns( geo_state *geo, u32 *input, u32 count )
-{
- raster_state *raster = geo->raster;
- poly_vertex point, normal, p0, p1, p2, p3;
- u32 attr, i;
-
- /* read the 1st point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* save for normal calculation */
- p0.x = point.x; p0.y = point.y; p0.pz = point.pz;
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push it to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* read the 2nd point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* save for normal calculation */
- p1.x = point.x; p1.y = point.y; p1.pz = point.pz;
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push it to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* loop through the following links */
- for( i = 0; i < count; i++ )
- {
- /* read in the attributes */
- attr = *input++;
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, attr & 0x0003FFFF );
-
- if ( (attr & 3) != 0 ) /* quad or triangle */
- {
- float dotl, dotp, luminance, distance;
- float coef, face;
- int32_t luma;
- texture_parameter * texparam;
-
- /* Skip normal */
- input += 3;
-
- /* read in the next point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* save for normal calculation */
- p2.x = point.x; p2.y = point.y; p2.pz = point.pz;
-
- /* compute the normal */
- vector_cross3( &normal, &p0, &p1, &p2 );
-
- /* normalize it */
- normalize_vector( &normal );
-
- /* calculate the dot product of the normal and the light vector */
- dotl = dot_product( &normal, &geo->light );
-
- /* calculate the dot product of the normal and the point */
- dotp = dot_product( &normal, &point );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* determine whether this is the front or the back of the polygon */
- face = 0x100; /* rear */
- if ( dotp >= 0 ) face = 0; /* front */
-
- /* get the texture parameters */
- texparam = &geo->texture_parameters[(attr>>18) & 0x1f];
-
- /* calculate luminance */
- if ( (dotl * dotp) < 0 ) luminance = 0;
- else luminance = fabs( dotl );
-
- luminance = (luminance * texparam->diffuse) + texparam->ambient;
- luma = (int32_t)luminance;
-
- if ( luma > 255 ) luma = 255;
- if ( luma < 0 ) luma = 0;
-
- /* add the face bit to the luma */
- luma += face;
-
- /* extract distance coefficient */
- coef = geo->coef_table[attr>>27];
-
- /* calculate texture level of detail */
- distance = coef * fabs( dotp ) * geo->lod;
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, luma << 15 );
- model2_3d_push( raster, f2u(distance) >> 8 );
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* if it's a quad, push one more point */
- if ( attr & 1 )
- {
- /* read in the next point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* save for normal calculation */
- p3.x = point.x; p3.y = point.y; p3.pz = point.pz;
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
- }
- else
- {
- /* skip the next 3 points */
- input += 3;
-
- /* for triangles, the rope of P1(n) is achieved by P0(n-1) (linktype 3) */
- p3.x = p2.x; p3.y = p2.y; p3.pz = p2.pz;
- }
- }
- else /* we're done */
- {
- break;
- }
-
- /* link type */
- switch( (attr>>8) & 3 )
- {
- case 0:
- case 2:
- {
- /* reuse P0(n) and P1(n) */
- p0.x = p2.x; p0.y = p2.y; p0.pz = p2.pz;
- p1.x = p3.x; p1.y = p3.y; p1.pz = p3.pz;
- }
- break;
-
- case 1:
- {
- /* reuse P0(n-1) and P0(n) */
- p1.x = p2.x; p1.y = p2.y; p1.pz = p2.pz;
- }
- break;
-
- case 3:
- {
- /* reuse P1(n-1) and P1(n) */
- p0.x = p3.x; p0.y = p3.y; p0.pz = p3.pz;
- }
- break;
- }
- }
-
- /* notify the 3d rasterizer we're done */
- model2_3d_push( raster, 0 );
-}
-
-/* Parse Polygons: No Normals, Specular case */
-void model2_state::geo_parse_nn_s( geo_state *geo, u32 *input, u32 count )
-{
- raster_state *raster = geo->raster;
- poly_vertex point, normal, p0, p1, p2, p3;
- u32 attr, i;
-
- /* read the 1st point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* save for normal calculation */
- p0.x = point.x; p0.y = point.y; p0.pz = point.pz;
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push it to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* read the 2nd point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* save for normal calculation */
- p1.x = point.x; p1.y = point.y; p1.pz = point.pz;
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push it to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* loop through the following links */
- for( i = 0; i < count; i++ )
- {
- /* read in the attributes */
- attr = *input++;
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, attr & 0x0003FFFF );
-
- if ( (attr & 3) != 0 ) /* quad or triangle */
- {
- float dotl, dotp, luminance, distance, specular;
- float coef, face;
- int32_t luma;
- texture_parameter * texparam;
-
- /* Skip normal */
- input += 3;
-
- /* read in the next point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* save for normal calculation */
- p2.x = point.x; p2.y = point.y; p2.pz = point.pz;
-
- /* compute the normal */
- vector_cross3( &normal, &p0, &p1, &p2 );
-
- /* normalize it */
- normalize_vector( &normal );
-
- /* calculate the dot product of the normal and the light vector */
- dotl = dot_product( &normal, &geo->light );
-
- /* calculate the dot product of the normal and the point */
- dotp = dot_product( &normal, &point );
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* determine whether this is the front or the back of the polygon */
- face = 0x100; /* rear */
- if ( dotp >= 0 ) face = 0; /* front */
-
- /* get the texture parameters */
- texparam = &geo->texture_parameters[(attr>>18) & 0x1f];
-
- /* calculate luminance and specular */
- if ( (dotl * dotp) < 0 ) luminance = 0;
- else luminance = fabs( dotl );
-
- specular = ((2*dotl) * normal.pz) - geo->light.pz;
- if ( specular < 0 ) specular = 0;
- if ( texparam->specular_control == 0 ) specular = 0;
- if ( (texparam->specular_control >> 1) != 0 ) specular *= specular;
- if ( (texparam->specular_control >> 2) != 0 ) specular *= specular;
- if ( ((texparam->specular_control+1) >> 3) != 0 ) specular *= specular;
-
- specular *= texparam->specular_scale;
-
- luminance = (luminance * texparam->diffuse) + texparam->ambient + specular;
- luma = (int32_t)luminance;
-
- if ( luma > 255 ) luma = 255;
- if ( luma < 0 ) luma = 0;
-
- /* add the face bit to the luma */
- luma += face;
-
- /* extract distance coefficient */
- coef = geo->coef_table[attr>>27];
-
- /* calculate texture level of detail */
- distance = coef * fabs( dotp ) * geo->lod;
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, luma << 15 );
- model2_3d_push( raster, f2u(distance) >> 8 );
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
-
- /* if it's a quad, push one more point */
- if ( attr & 1 )
- {
- /* read in the next point */
- point.x = u2f( *input++ );
- point.y = u2f( *input++ );
- point.pz = u2f( *input++ );
-
- /* transform with the current matrix */
- transform_point( &point, geo->matrix );
-
- /* save for normal calculation */
- p3.x = point.x; p3.y = point.y; p3.pz = point.pz;
-
- /* apply focus */
- apply_focus( geo, &point );
-
- /* push to the 3d rasterizer */
- model2_3d_push( raster, f2u(point.x) >> 8 );
- model2_3d_push( raster, f2u(point.y) >> 8 );
- model2_3d_push( raster, f2u(point.pz) >> 8 );
- }
- else
- {
- /* skip the next 3 points */
- input += 3;
-
- /* for triangles, the rope of P1(n) is achieved by P0(n-1) (linktype 3) */
- p3.x = p2.x; p3.y = p2.y; p3.pz = p2.pz;
- }
- }
- else /* we're done */
- {
- break;
- }
-
- /* link type */
- switch( (attr>>8) & 3 )
- {
- case 0:
- case 2:
- {
- /* reuse P0(n) and P1(n) */
- p0.x = p2.x; p0.y = p2.y; p0.pz = p2.pz;
- p1.x = p3.x; p1.y = p3.y; p1.pz = p3.pz;
- }
- break;
-
- case 1:
- {
- /* reuse P0(n-1) and P0(n) */
- p1.x = p2.x; p1.y = p2.y; p1.pz = p2.pz;
- }
- break;
-
- case 3:
- {
- /* reuse P1(n-1) and P1(n) */
- p0.x = p3.x; p0.y = p3.y; p0.pz = p3.pz;
- }
- break;
- }
- }
-
- /* notify the 3d rasterizer we're done */
- model2_3d_push( raster, 0 );
-}
-
-/*******************************************
- *
- * Geometry Engine Commands
- *
- *******************************************/
-
-/* Command 00: NOP */
-u32 *model2_state::geo_nop( geo_state *geo, u32 opcode, u32 *input )
-{
- raster_state *raster = geo->raster;
-
- /* push the opcode to the 3d rasterizer */
- model2_3d_push( raster, opcode >> 23 );
-
- return input;
-}
-
-/* Command 01: Object Data */
-u32 *model2_state::geo_object_data( geo_state *geo, u32 opcode, u32 *input )
-{
- raster_state *raster = geo->raster;
- u32 tpa = *input++; /* Texture Point Address */
- u32 tha = *input++; /* Texture Header Address */
- u32 oba = *input++; /* Object Address */
- u32 obc = *input++; /* Object Count */
-
- u32 *obp; /* Object Pointer */
-
- /* push the initial set of data to the 3d rasterizer */
- model2_3d_push( raster, opcode >> 23 );
- model2_3d_push( raster, tpa );
- model2_3d_push( raster, tha );
-
- /* select where we're reading polygon information from */
- if ( oba & 0x01000000 )
- {
- /* Fast polygon RAM */
- obp = &geo->polygon_ram1[oba & 0x7FFF];
- }
- else if ( oba & 0x00800000 )
- {
- /* Polygon ROM */
- obp = &geo->polygon_rom[oba & geo->polygon_rom_mask];
- }
- else
- {
- /* Slow Polygon RAM */
- obp = &geo->polygon_ram0[oba & 0x7FFF];
- }
-
- // if count == 0 then rolls over to max size
- // Virtual On & Gunblade NY
- if(obc == 0)
- obc = 0xfffff;
-
- switch( geo->mode & 3 )
- {
- /* Normals present, No Specular */
- case 0: geo_parse_np_ns( geo, obp, obc ); break;
-
- /* Normals present, Specular */
- case 1: geo_parse_np_s( geo, obp, obc ); break;
-
- /* No Normals present, No Specular */
- case 2: geo_parse_nn_ns( geo, obp, obc ); break;
-
- /* No Normals present, Specular */
- case 3: geo_parse_nn_s( geo, obp, obc ); break;
- }
-
- /* move by 4 parameters */
- return input;
-}
-
-/* Command 02: Direct Data */
-u32 *model2_state::geo_direct_data( geo_state *geo, u32 opcode, u32 *input )
-{
- raster_state *raster = geo->raster;
- u32 tpa = *input++; /* Texture Point Address */
- u32 tha = *input++; /* Texture Header Address */
- u32 attr;
-
- /* push the initial set of data to the 3d rasterizer */
- model2_3d_push( raster, (opcode >> 23) - 1 );
- model2_3d_push( raster, tpa );
- model2_3d_push( raster, tha );
-
- /* push the initial points */
- model2_3d_push( raster, (*input++) >> 8 ); /* x */
- model2_3d_push( raster, (*input++) >> 8 ); /* y */
- model2_3d_push( raster, (*input++) >> 8 ); /* z */
-
- model2_3d_push( raster, (*input++) >> 8 ); /* x */
- model2_3d_push( raster, (*input++) >> 8 ); /* y */
- model2_3d_push( raster, (*input++) >> 8 ); /* z */
-
- do
- {
- /* read in the attributes */
- attr = *input++;
-
- if ( (attr & 3) == 0 )
- break;
-
- /* push attributes */
- model2_3d_push( raster, attr & 0x00FFFFFF );
-
- /* push luma */
- model2_3d_push( raster, (*input++) >> 8 );
-
- /* push distance */
- model2_3d_push( raster, (*input++) >> 8 );
-
- /* push the next point */
- model2_3d_push( raster, (*input++) >> 8 ); /* x */
- model2_3d_push( raster, (*input++) >> 8 ); /* y */
- model2_3d_push( raster, (*input++) >> 8 ); /* z */
-
- /* if it's a quad, output another point */
- if ( attr & 1 )
- {
- model2_3d_push( raster, (*input++) >> 8 ); /* x */
- model2_3d_push( raster, (*input++) >> 8 ); /* y */
- model2_3d_push( raster, (*input++) >> 8 ); /* z */
- }
- } while( 1 );
-
- /* we're done */
- model2_3d_push( raster, 0 );
-
- return input;
-}
-
-/* Command 03: Window Data */
-u32 *model2_state::geo_window_data( geo_state *geo, u32 opcode, u32 *input )
-{
- raster_state *raster = geo->raster;
- u32 x, y, i;
-
- /* start by pushing the opcode */
- model2_3d_push( raster, opcode >> 23 );
-
- /*
- we're going to move 6 coordinates to the 3d rasterizer:
- - starting coordinate
- - completion coordinate
- - vanishing point 0 (eye mode 0)
- - vanishing point 1 (eye mode 1)
- - vanishing point 2 (eye mode 2)
- - vanishing point 3 (eye mode 3)
- */
-
- for( i = 0; i < 6; i++ )
- {
- /* read in the coordinate */
- y = *input++;
-
- /* convert to the 3d rasterizer format (00XXXYYY) */
- x = ( y & 0x0FFF0000 ) >> 4 ;
- y &= 0xFFF;
-
- /* push it */
- model2_3d_push( raster, x | y );
- }
-
- return input;
-}
-
-/* Command 04: Texture Data Write */
-u32 *model2_state::geo_texture_data( geo_state *geo, u32 opcode, u32 *input )
-{
- raster_state *raster = geo->raster;
- u32 i, count;
-
- /* start by pushing the opcode */
- model2_3d_push( raster, opcode >> 23 );
-
- /* push the starting address/dsp id */
- model2_3d_push( raster, *input++ );
-
- /* get the count */
- count = *input++;
-
- /* push the count */
- model2_3d_push( raster, count );
-
- /* loop and send the data */
- for( i = 0; i < count; i++ )
- model2_3d_push( raster, *input++ );
-
- return input;
-}
-
-/* Command 05: Polygon Data */
-u32 *model2_state::geo_polygon_data( geo_state *geo, u32 opcode, u32 *input )
-{
- u32 address, count, i;
- u32 *p;
-
- (void)opcode;
-
- /* read in the address */
- address = *input++;
-
- /* prepare the pointer */
- if ( address & 0x01000000 )
- {
- /* Fast polygon RAM */
- p = &geo->polygon_ram1[address & 0x7FFF];
- }
- else
- {
- /* Slow Polygon RAM */
- p = &geo->polygon_ram0[address & 0x7FFF];
- }
-
- /* read the count */
- count = *input++;
-
- /* move the data */
- for( i = 0; i < count; i++ )
- *p++ = *input++;
-
- return input;
-}
-
-/* Command 06: Texture Parameters */
-u32 *model2_state::geo_texture_parameters( geo_state *geo, u32 opcode, u32 *input )
-{
- u32 index, count, i, param;
-
- (void)opcode;
-
- /* read in the index */
- index = *input++;
-
- /* read in the conut */
- count = *input++;
-
- for( i = 0; i < count; i++ )
- {
- /* read in the texture parameters */
- param = *input++;
-
- geo->texture_parameters[index].diffuse = (float)( param & 0xFF );
- geo->texture_parameters[index].ambient = (float)( (param >> 8) & 0xFF );
- geo->texture_parameters[index].specular_control = (param >> 24) & 0xFF;
- geo->texture_parameters[index].specular_scale = (float)( (param >> 16) & 0xFF );
-
- /* read in the distance coefficient */
- geo->coef_table[index] = u2f(*input++);
-
- index = (index + 1) & 0x1F;
- }
-
- return input;
-}
-
-/* Command 07: Geo Mode */
-u32 *model2_state::geo_mode( geo_state *geo, u32 opcode, u32 *input )
-{
- (void)opcode;
-
- /* read in the mode */
- geo->mode = *input++;
-
- return input;
-}
-
-/* Command 08: ZSort Mode */
-u32 *model2_state::geo_zsort_mode( geo_state *geo, u32 opcode, u32 *input )
-{
- raster_state *raster = geo->raster;
-
- /* push the opcode */
- model2_3d_push( raster, opcode >> 23 );
-
- /* push the mode */
- model2_3d_push( raster, (*input++) >> 8 );
-
- return input;
-}
-
-/* Command 09: Focal Distance */
-u32 *model2_state::geo_focal_distance( geo_state *geo, u32 opcode, u32 *input )
-{
- (void)opcode;
-
- /* read the x focus value */
- geo->focus.x = u2f( *input++ );
-
- /* read the y focus value */
- geo->focus.y = u2f( *input++ );
-
- return input;
-}
-
-/* Command 0A: Light Source Vector Write */
-u32 *model2_state::geo_light_source( geo_state *geo, u32 opcode, u32 *input )
-{
- (void)opcode;
-
- /* read the x light value */
- geo->light.x = u2f( *input++ );
-
- /* read the y light value */
- geo->light.y = u2f( *input++ );
-
- /* read the z light value */
- geo->light.pz = u2f( *input++ );
-
- return input;
-}
-
-/* Command 0B: Transformation Matrix Write */
-u32 *model2_state::geo_matrix_write( geo_state *geo, u32 opcode, u32 *input )
-{
- u32 i;
-
- (void)opcode;
-
- /* read in the transformation matrix */
- for( i = 0; i < 12; i++ )
- geo->matrix[i] = u2f( *input++ );
-
- return input;
-}
-
-/* Command 0C: Parallel Transfer Vector Write */
-u32 *model2_state::geo_translate_write( geo_state *geo, u32 opcode, u32 *input )
-{
- u32 i;
-
- (void)opcode;
-
- /* read in the translation vector */
- for( i = 0; i < 3; i++ )
- geo->matrix[i+9] = u2f( *input++ );
-
- return input;
-}
-
-/* Command 0D: Geo Data Memory Push (undocumented, unsupported) */
-u32 *model2_state::geo_data_mem_push( geo_state *geo, u32 opcode, u32 *input )
-{
- u32 address, count, i;
-
- /*
- This command pushes data stored in the Geometry DSP's RAM
- to the hardware 3D rasterizer. Since we don't emulate the
- DSP, we don't know what the RAM contents are.
-
- Eventually, we could check for the address, and if it
- happens to point to a polygon ROM, we could potentially
- emulate it partially.
-
- No games are known to use this command yet.
- */
-
-
- (void)opcode;
-
- /* read in the address */
- address = *input++;
-
- /* read in the count */
- count = *input++;
-
- logerror( "SEGA GEO: Executing unsupported geo_data_mem_push (address = %08x, count = %08x)\n", address, count );
-
- (void)i;
-/*
- for( i = 0; i < count; i++ )
- model2_3d_push( 0 );
-*/
-
- return input;
-}
-
-/* Command 0E: Geo Test */
-u32 *model2_state::geo_test( geo_state *geo, u32 opcode, u32 *input )
-{
- u32 data, blocks, address, count, checksum, i;
-
- (void)opcode;
-
- /* fifo test */
- data = 1;
-
- for( i = 0; i < 32; i++ )
- {
- if ( *input++ != data )
- {
- /* TODO: Set Red LED on */
- logerror( "SEGA GEO: FIFO Test failed\n" );
- }
-
- data <<= 1;
- }
-
- /* get the number of checksums we have to run */
- blocks = *input++;
-
- for( i = 0; i < blocks; i++ )
- {
- u32 sum_even, sum_odd, j;
-
- /* read in the address */
- address = (*input++) & 0x7FFFFF;
-
- /* read in the count */
- count = *input++;
-
- /* read in the checksum */
- checksum = *input++;
-
- /* reset the checksum counters */
- sum_even = 0;
- sum_odd = 0;
-
- for( j = 0; j < count; j++ )
- {
- data = geo->polygon_rom[address++];
-
- address &= geo->polygon_rom_mask;
-
- sum_even += data >> 16;
- sum_even &= 0xFFFF;
-
- sum_odd += data & 0xFFFF;
- sum_odd &= 0xFFFF;
- }
-
- sum_even += checksum >> 16;
- sum_even &= 0xFFFF;
-
- sum_odd += checksum & 0xFFFF;
- sum_odd &= 0xFFFF;
-
- if ( sum_even != 0 || sum_odd != 0 )
- {
- /* TODO: Set Green LED on */
- logerror( "SEGA GEO: Polygon ROM Test failed\n" );
- }
- }
-
- return input;
-}
-
-/* Command 0F: End */
-u32 *model2_state::geo_end( geo_state *geo, u32 opcode, u32 *input )
-{
- raster_state *raster = geo->raster;
-
- (void)opcode;
-
- /* signal the end of this data block the rasterizer */
- model2_3d_push( raster, 0xFF000000 );
-
- /* signal end by returning nullptr */
- return nullptr;
-}
-
-/* Command 10: Dummy */
-u32 *model2_state::geo_dummy( geo_state *geo, u32 opcode, u32 *input )
-{
-// u32 data;
- (void)opcode;
-
- /* do the dummy read cycle */
-// data = *input++;
- input++;
-
- return input;
-}
-
-/* Command 14: Log Data Write */
-u32 *model2_state::geo_log_data( geo_state *geo, u32 opcode, u32 *input )
-{
- raster_state *raster = geo->raster;
- u32 i, count;
-
- /* start by pushing the opcode */
- model2_3d_push( raster, opcode >> 23 );
-
- /* push the starting address/dsp id */
- model2_3d_push( raster, *input++ );
-
- /* get the count */
- count = *input++;
-
- /* push the count */
- model2_3d_push( raster, count << 2 );
-
- /* loop and send the data */
- for( i = 0; i < count; i++ )
- {
- u32 data = *input++;
-
- model2_3d_push( raster, data & 0xff );
- model2_3d_push( raster, (data >> 8) & 0xff );
- model2_3d_push( raster, (data >> 16) & 0xff );
- model2_3d_push( raster, (data >> 24) & 0xff );
- }
-
- return input;
-}
-
-/* Command 16: LOD */
-u32 *model2_state::geo_lod( geo_state *geo, u32 opcode, u32 *input )
-{
- (void)opcode;
-
- /* read in the LOD */
- geo->lod = u2f(*input++);
-
- return input;
-}
-
-/* Command 1D: Code Upload (undocumented, unsupported) */
-u32 *model2_state::geo_code_upload( geo_state *geo, u32 opcode, u32 *input )
-{
- u32 count, i;
-
- /*
- This command uploads code to program memory and
- optionally runs it. Probably used for debugging.
-
- No games are known to use this command yet.
- */
-
- logerror( "SEGA GEO: Uploading debug code (unimplemented)\n" );
-
- (void)opcode;
-
- /* read in the flags */
-// flags = *input++;
- input++;
-
- /* read in the count */
- count = *input++;
-
- for( i = 0; i < count; i++ )
- {
- u64 code;
-
- /* read the top part of the opcode */
- code = *input++;
-
- code <<= 32;
-
- /* the bottom part comes in two pieces */
- code |= *input++;
- code |= (*input++) << 16;
- }
-
- /*
- Bit 10 of flags indicate whether to run iummediately after upload
- */
-
-/*
- if ( flags & 0x400 )
- code_jump();
-*/
-
- return input;
-}
-
-/* Command 1E: Code Jump (undocumented, unsupported) */
-u32 *model2_state::geo_code_jump( geo_state *geo, u32 opcode, u32 *input )
-{
-// u32 address;
-
- /*
- This command jumps to a specified address in program
- memory. Code can be uploaded with function 1D.
- Probably used for debugging.
-
- No games are known to use this command yet.
- */
-
- logerror( "SEGA GEO: Jumping to debug code (unimplemented)\n" );
-
- (void)opcode;
-
-// address = *input++ & 0x3FF;
- input++;
-
-/*
- code_jump( address )
-*/
- return input;
-}
-
-u32 *model2_state::geo_process_command( geo_state *geo, u32 opcode, u32 *input, bool *end_code )
-{
- switch( (opcode >> 23) & 0x1f )
- {
- case 0x00: input = geo_nop( geo, opcode, input ); break;
- case 0x01: input = geo_object_data( geo, opcode, input ); break;
- case 0x02: input = geo_direct_data( geo, opcode, input ); break;
- case 0x03: input = geo_window_data( geo, opcode, input ); break;
- case 0x04: input = geo_texture_data( geo, opcode, input ); break;
- case 0x05: input = geo_polygon_data( geo, opcode, input ); break;
- case 0x06: input = geo_texture_parameters( geo, opcode, input ); break;
- case 0x07: input = geo_mode( geo, opcode, input ); break;
- case 0x08: input = geo_zsort_mode( geo, opcode, input ); break;
- case 0x09: input = geo_focal_distance( geo, opcode, input ); break;
- case 0x0A: input = geo_light_source( geo, opcode, input ); break;
- case 0x0B: input = geo_matrix_write( geo, opcode, input ); break;
- case 0x0C: input = geo_translate_write( geo, opcode, input ); break;
- case 0x0D: input = geo_data_mem_push( geo, opcode, input ); break;
- case 0x0E: input = geo_test( geo, opcode, input ); break;
- case 0x0F: input = geo_end( geo, opcode, input ); *end_code = true; break;
- case 0x10: input = geo_dummy( geo, opcode, input ); break;
- case 0x11: input = geo_object_data( geo, opcode, input ); break;
- case 0x12: input = geo_direct_data( geo, opcode, input ); break;
- case 0x13: input = geo_window_data( geo, opcode, input ); break;
- case 0x14: input = geo_log_data( geo, opcode, input ); break;
- case 0x15: input = geo_polygon_data( geo, opcode, input ); break;
- case 0x16: input = geo_lod( geo, opcode, input ); break;
- case 0x17: input = geo_mode( geo, opcode, input ); break;
- case 0x18: input = geo_zsort_mode( geo, opcode, input ); break;
- case 0x19: input = geo_focal_distance( geo, opcode, input ); break;
- case 0x1A: input = geo_light_source( geo, opcode, input ); break;
- case 0x1B: input = geo_matrix_write( geo, opcode, input ); break;
- case 0x1C: input = geo_translate_write( geo, opcode, input ); break;
- case 0x1D: input = geo_code_upload( geo, opcode, input ); break;
- case 0x1E: input = geo_code_jump( geo, opcode, input ); break;
- case 0x1F: input = geo_end( geo, opcode, input ); *end_code = true; break;
- }
-
- return input;
-}
-
-void model2_state::geo_parse( void )
-{
- u32 address = (m_geo_read_start_address & 0x1ffff)/4;
- u32 *input = &m_bufferram[address];
- u32 opcode;
- bool end_code = false;
-
- while( end_code == false && (input - m_bufferram) < 0x20000/4 )
- {
- /* read in the opcode */
- opcode = *input++;
-
- /* if it's a jump opcode, do the jump */
- if ( opcode & 0x80000000 )
- {
- // TODO: daytona with master network enabled hardlocks by trying a jump with 0xffff0080 as opcode
- // bad timings for geo_parse?
- if(opcode & 0x078000000 )
- return;
-
- /* get the address */
- address = (opcode & 0x1FFFF) / 4;
-
- /* update our pointer */
- input = &m_bufferram[address];
-
- /* go again */
- continue;
- }
-
- /* process it */
- input = geo_process_command( m_geo.get(), opcode, input, &end_code );
- }
-}
-
-/***********************************************************************************************/
-
-
-void model2_state::video_start()
-{
- const rectangle &visarea = m_screen->visible_area();
- int width = visarea.width();
- int height = visarea.height();
-
- m_sys24_bitmap.allocate(width, height+4);
-
- m_poly = std::make_unique<model2_renderer>(*this);
-
- /* initialize the hardware rasterizer */
- raster_init( memregion("textures") );
-
- /* initialize the geometry engine */
- geo_init( memregion("polygons") );
-
- /* init various video-related pointers */
- m_palram = make_unique_clear<u16[]>(0x4000/2);
- m_colorxlat = make_unique_clear<u16[]>(0xc000/2);
- m_lumaram = make_unique_clear<u16[]>(0x10000/2);
- m_fbvramA = make_unique_clear<u16[]>(0x80000/2);
- m_fbvramB = make_unique_clear<u16[]>(0x80000/2);
-
- // convert (supposedly) 3d sRGB color space into linear
- // TODO: might be slightly different algorithm (Daytona USA road/cars, VF2 character skins)
- for(int i=0;i<256;i++)
- {
- double raw_value;
- raw_value = 255.0 * pow((double)(i) / 255.0,2.2);
- m_gamma_table[i] = (u8)raw_value;
-// printf("%02x: %02x %lf\n",i,m_gamma_table[i],raw_value);
- }
-
- save_item(NAME(m_render_test_mode));
- save_item(NAME(m_render_unk));
- save_item(NAME(m_render_mode));
- save_pointer(NAME(m_palram), 0x4000/2);
- save_pointer(NAME(m_colorxlat), 0xc000/2);
- save_pointer(NAME(m_lumaram), 0x10000/2);
- save_pointer(NAME(m_gamma_table), 256);
-}
-
-u32 model2_state::screen_update_model2(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
-{
- //logerror("--- frame ---\n");
- bitmap.fill(m_palette->pen(0), cliprect);
- m_sys24_bitmap.fill(0, cliprect);
-
- for(int layer = 3; layer >= 0; layer--)
- m_tiles->draw(screen, m_sys24_bitmap, cliprect, layer<<1, 0, 0);
-
- copybitmap_trans(bitmap, m_sys24_bitmap, 0, 0, 0, 0, cliprect, 0);
-
- /* tell the rasterizer we're starting a frame */
- if(m_render_test_mode == true)
- draw_framebuffer( bitmap, cliprect );
- else
- {
- model2_3d_frame_start();
-
- /* let the geometry engine do it's thing */
- // TODO: move it from here
- geo_parse();
-
- /* have the rasterizer output the frame */
- model2_3d_frame_end( bitmap, cliprect );
- }
-
- m_sys24_bitmap.fill(0, cliprect);
-
- for (int layer = 3; layer >= 0; layer--)
- m_tiles->draw(screen, m_sys24_bitmap, cliprect, (layer<<1) | 1, 0, 0);
-
- copybitmap_trans(bitmap, m_sys24_bitmap, 0, 0, 0, 0, cliprect, 0);
-
- return 0;
-}
-
-// called from machine/model2.cpp trilist command
-// TODO: fix forward declaration mess and move this function there instead
-void model2_state::tri_list_dump(FILE *dst)
-{
- u32 i;
-
- for( i = 0; i < m_raster->tri_list_index; i++ )
- {
- fprintf( dst, "index: %d\n", i );
- fprintf( dst, "v0.x = %f, v0.y = %f, v0.z = %f\n", m_raster->tri_list[i].v[0].x, m_raster->tri_list[i].v[0].y, m_raster->tri_list[i].v[0].pz );
- fprintf( dst, "v1.x = %f, v1.y = %f, v1.z = %f\n", m_raster->tri_list[i].v[1].x, m_raster->tri_list[i].v[1].y, m_raster->tri_list[i].v[1].pz );
- fprintf( dst, "v2.x = %f, v2.y = %f, v2.z = %f\n", m_raster->tri_list[i].v[2].x, m_raster->tri_list[i].v[2].y, m_raster->tri_list[i].v[2].pz );
-
- fprintf( dst, "tri z: %04x\n", m_raster->tri_list[i].z );
- fprintf( dst, "texheader - 0: %04x\n", m_raster->tri_list[i].texheader[0] );
- fprintf( dst, "texheader - 1: %04x\n", m_raster->tri_list[i].texheader[1] );
- fprintf( dst, "texheader - 2: %04x\n", m_raster->tri_list[i].texheader[2] );
- fprintf( dst, "texheader - 3: %04x\n", m_raster->tri_list[i].texheader[3] );
- fprintf( dst, "luma: %02x\n", m_raster->tri_list[i].luma );
- fprintf( dst, "vp.sx: %04x\n", m_raster->tri_list[i].viewport[0] );
- fprintf( dst, "vp.sy: %04x\n", m_raster->tri_list[i].viewport[1] );
- fprintf( dst, "vp.ex: %04x\n", m_raster->tri_list[i].viewport[2] );
- fprintf( dst, "vp.ey: %04x\n", m_raster->tri_list[i].viewport[3] );
- fprintf( dst, "vp.swx: %04x\n", m_raster->tri_list[i].center[0] );
- fprintf( dst, "vp.swy: %04x\n", m_raster->tri_list[i].center[1] );
- fprintf( dst, "\n---\n\n" );
- }
-
- fprintf( dst, "min_z = %04x, max_z = %04x\n", m_raster->min_z, m_raster->max_z );
-
- fclose( dst );
-
-}