nkeynes@635: /** nkeynes@636: * $Id$ nkeynes@635: * nkeynes@827: * Manage the internal vertex/polygon buffers and scene data structure. nkeynes@635: * nkeynes@635: * Copyright (c) 2005 Nathan Keynes. nkeynes@635: * nkeynes@635: * This program is free software; you can redistribute it and/or modify nkeynes@635: * it under the terms of the GNU General Public License as published by nkeynes@635: * the Free Software Foundation; either version 2 of the License, or nkeynes@635: * (at your option) any later version. nkeynes@635: * nkeynes@635: * This program is distributed in the hope that it will be useful, nkeynes@635: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@635: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@635: * GNU General Public License for more details. nkeynes@635: */ nkeynes@635: nkeynes@635: #include nkeynes@635: #include nkeynes@645: #include nkeynes@635: #include "lxdream.h" nkeynes@635: #include "display.h" nkeynes@635: #include "pvr2/pvr2.h" nkeynes@677: #include "pvr2/pvr2mmio.h" nkeynes@635: #include "pvr2/glutil.h" nkeynes@635: #include "pvr2/scene.h" nkeynes@635: nkeynes@863: #define U8TOFLOAT(n) (((float)((n)+1))/256.0) nkeynes@1155: #define POLY_IDX(addr) ( ((uint32_t *)addr) - ((uint32_t *)pvr2_scene.pvr2_pbuf)) nkeynes@863: nkeynes@687: static void unpack_bgra(uint32_t bgra, float *rgba) nkeynes@687: { nkeynes@687: rgba[0] = ((float)(((bgra&0x00FF0000)>>16) + 1)) / 256.0; nkeynes@687: rgba[1] = ((float)(((bgra&0x0000FF00)>>8) + 1)) / 256.0; nkeynes@687: rgba[2] = ((float)((bgra&0x000000FF) + 1)) / 256.0; nkeynes@687: rgba[3] = ((float)(((bgra&0xFF000000)>>24) + 1)) / 256.0; nkeynes@687: } nkeynes@635: nkeynes@639: static inline uint32_t bgra_to_rgba(uint32_t bgra) nkeynes@639: { nkeynes@639: return (bgra&0xFF00FF00) | ((bgra&0x00FF0000)>>16) | ((bgra&0x000000FF)<<16); nkeynes@639: } nkeynes@639: nkeynes@645: /** nkeynes@645: * Convert a half-float (16-bit) FP number to a regular 32-bit float. nkeynes@645: * Source is 1-bit sign, 5-bit exponent, 10-bit mantissa. nkeynes@645: * TODO: Check the correctness of this. nkeynes@645: */ nkeynes@645: static float halftofloat( uint16_t half ) nkeynes@645: { nkeynes@645: union { nkeynes@645: float f; nkeynes@645: uint32_t i; nkeynes@645: } temp; nkeynes@645: temp.i = ((uint32_t)half)<<16; nkeynes@645: return temp.f; nkeynes@645: } nkeynes@645: nkeynes@847: static float parse_fog_density( uint32_t value ) nkeynes@847: { nkeynes@847: union { nkeynes@847: uint32_t i; nkeynes@847: float f; nkeynes@847: } u; nkeynes@847: u.i = (((value+127)&0xFF)<<23)|((value & 0xFF00)<<7); nkeynes@847: return u.f; nkeynes@847: } nkeynes@645: nkeynes@635: struct pvr2_scene_struct pvr2_scene; nkeynes@1159: static float scene_shadow_intensity = 0.0; nkeynes@1159: static vertex_buffer_t vbuf = NULL; nkeynes@635: nkeynes@1159: static void vertex_buffer_map() nkeynes@1159: { nkeynes@1159: // Allow 8 vertexes for the background (4+4) nkeynes@1159: uint32_t size = (pvr2_scene.vertex_count + 8) * sizeof(struct vertex_struct); nkeynes@1159: pvr2_scene.vertex_array = vbuf->map(vbuf, size); nkeynes@1159: } nkeynes@669: nkeynes@1159: static void vertex_buffer_unmap() nkeynes@1159: { nkeynes@1159: pvr2_scene.vertex_array = vbuf->unmap(vbuf); nkeynes@1159: } nkeynes@635: nkeynes@635: /** nkeynes@1159: * Allocate vertex buffer + temporary structures. GL context must have been initialized before this nkeynes@635: * point. nkeynes@635: */ nkeynes@635: void pvr2_scene_init() nkeynes@635: { nkeynes@1159: if( vbuf == NULL ) { nkeynes@1159: vbuf = display_driver->create_vertex_buffer(); nkeynes@736: pvr2_scene.vertex_array = NULL; nkeynes@736: pvr2_scene.vertex_array_size = 0; nkeynes@736: pvr2_scene.poly_array = g_malloc( MAX_POLY_BUFFER_SIZE ); nkeynes@736: pvr2_scene.buf_to_poly_map = g_malloc0( BUF_POLY_MAP_SIZE ); nkeynes@635: } nkeynes@635: } nkeynes@635: nkeynes@639: /** nkeynes@639: * Clear the scene data structures in preparation for fresh data nkeynes@639: */ nkeynes@639: void pvr2_scene_reset() nkeynes@639: { nkeynes@1155: /* Faster to just clear the active entries */ nkeynes@1155: for( int i=0; idestroy(vbuf); nkeynes@1159: vbuf = NULL; nkeynes@635: g_free( pvr2_scene.poly_array ); nkeynes@645: pvr2_scene.poly_array = NULL; nkeynes@635: g_free( pvr2_scene.buf_to_poly_map ); nkeynes@645: pvr2_scene.buf_to_poly_map = NULL; nkeynes@635: } nkeynes@635: nkeynes@639: static struct polygon_struct *scene_add_polygon( pvraddr_t poly_idx, int vertex_count, nkeynes@863: shadow_mode_t is_modified ) nkeynes@635: { nkeynes@863: int vert_mul = is_modified != SHADOW_NONE ? 2 : 1; nkeynes@635: nkeynes@635: if( pvr2_scene.buf_to_poly_map[poly_idx] != NULL ) { nkeynes@687: if( vertex_count > pvr2_scene.buf_to_poly_map[poly_idx]->vertex_count ) { nkeynes@687: pvr2_scene.vertex_count += (vertex_count - pvr2_scene.buf_to_poly_map[poly_idx]->vertex_count) * vert_mul; nkeynes@687: pvr2_scene.buf_to_poly_map[poly_idx]->vertex_count = vertex_count; nkeynes@687: } nkeynes@687: return pvr2_scene.buf_to_poly_map[poly_idx]; nkeynes@635: } else { nkeynes@687: struct polygon_struct *poly = &pvr2_scene.poly_array[pvr2_scene.poly_count++]; nkeynes@687: poly->context = &pvr2_scene.pvr2_pbuf[poly_idx]; nkeynes@687: poly->vertex_count = vertex_count; nkeynes@687: poly->vertex_index = -1; nkeynes@687: poly->mod_vertex_index = -1; nkeynes@687: poly->next = NULL; nkeynes@1133: poly->sub_next = NULL; nkeynes@687: pvr2_scene.buf_to_poly_map[poly_idx] = poly; nkeynes@687: pvr2_scene.vertex_count += (vertex_count * vert_mul); nkeynes@687: return poly; nkeynes@635: } nkeynes@635: } nkeynes@635: nkeynes@635: /** nkeynes@1133: * Given a starting polygon, break it at the specified triangle so that the nkeynes@1133: * preceding triangles are retained, and the remainder are contained in a nkeynes@1133: * new sub-polygon. Does not preserve winding. nkeynes@1133: */ nkeynes@1133: static struct polygon_struct *scene_split_subpolygon( struct polygon_struct *parent, int split_offset ) nkeynes@1133: { nkeynes@1133: assert( split_offset > 0 && split_offset < (parent->vertex_count-2) ); nkeynes@1133: assert( pvr2_scene.poly_count < MAX_POLYGONS ); nkeynes@1133: struct polygon_struct *poly = &pvr2_scene.poly_array[pvr2_scene.poly_count++]; nkeynes@1133: poly->vertex_count = parent->vertex_count - split_offset; nkeynes@1133: poly->vertex_index = parent->vertex_index + split_offset; nkeynes@1133: if( parent->mod_vertex_index == -1 ) { nkeynes@1133: poly->mod_vertex_index = -1; nkeynes@1133: } else { nkeynes@1133: poly->mod_vertex_index = parent->mod_vertex_index + split_offset; nkeynes@1133: } nkeynes@1133: poly->context = parent->context; nkeynes@1133: poly->next = NULL; nkeynes@1133: poly->sub_next = parent->sub_next; nkeynes@1133: nkeynes@1133: parent->sub_next = poly; nkeynes@1133: parent->vertex_count = split_offset + 2; nkeynes@1133: nkeynes@1133: return poly; nkeynes@1133: } nkeynes@1133: nkeynes@1140: static float scene_get_palette_offset( uint32_t tex ) nkeynes@1140: { nkeynes@1140: uint32_t fmt = (tex & PVR2_TEX_FORMAT_MASK); nkeynes@1140: if( fmt == PVR2_TEX_FORMAT_IDX4 ) { nkeynes@1140: return ((float)((tex & 0x07E00000) >> 17))/1024.0 + 0.0002; nkeynes@1140: } else if( fmt == PVR2_TEX_FORMAT_IDX8 ) { nkeynes@1140: return ((float)((tex & 0x06000000) >> 17))/1024.0 + 0.0002; nkeynes@1140: } else { nkeynes@1140: return -1.0; nkeynes@1140: } nkeynes@1140: } nkeynes@1140: nkeynes@1133: /** nkeynes@635: * Decode a single PVR2 renderable vertex (opaque/trans/punch-out, but not shadow nkeynes@635: * volume) nkeynes@635: * @param vert Pointer to output vertex structure nkeynes@635: * @param poly1 First word of polygon context (needed to understand vertex) nkeynes@639: * @param poly2 Second word of polygon context nkeynes@635: * @param pvr2_data Pointer to raw pvr2 vertex data (in VRAM) nkeynes@635: * @param modify_offset Offset in 32-bit words to the tex/color data. 0 for nkeynes@635: * the normal vertex, half the vertex length for the modified vertex. nkeynes@635: */ nkeynes@1159: static void scene_decode_vertex( struct vertex_struct *vert, uint32_t poly1, nkeynes@1140: uint32_t poly2, uint32_t tex, uint32_t *pvr2_data, nkeynes@736: int modify_offset ) nkeynes@635: { nkeynes@639: gboolean force_alpha = !POLY2_ALPHA_ENABLE(poly2); nkeynes@635: union pvr2_data_type { nkeynes@687: uint32_t *ival; nkeynes@687: float *fval; nkeynes@635: } data; nkeynes@635: nkeynes@635: data.ival = pvr2_data; nkeynes@687: nkeynes@635: vert->x = *data.fval++; nkeynes@635: vert->y = *data.fval++; nkeynes@635: nkeynes@635: float z = *data.fval++; nkeynes@645: if( !isfinite(z) ) { nkeynes@687: z = 0; nkeynes@648: } else if( z != 0 ) { nkeynes@687: z = 1/z; nkeynes@645: } nkeynes@635: if( z > pvr2_scene.bounds[5] ) { nkeynes@687: pvr2_scene.bounds[5] = z; nkeynes@635: } else if( z < pvr2_scene.bounds[4] && z != 0 ) { nkeynes@687: pvr2_scene.bounds[4] = z; nkeynes@635: } nkeynes@635: vert->z = z; nkeynes@635: data.ival += modify_offset; nkeynes@635: nkeynes@687: nkeynes@635: if( POLY1_TEXTURED(poly1) ) { nkeynes@687: if( POLY1_UV16(poly1) ) { nkeynes@687: vert->u = halftofloat( *data.ival>>16 ); nkeynes@687: vert->v = halftofloat( *data.ival ); nkeynes@687: data.ival++; nkeynes@687: } else { nkeynes@687: vert->u = *data.fval++; nkeynes@687: vert->v = *data.fval++; nkeynes@687: } nkeynes@1139: nkeynes@1139: switch( POLY2_TEX_BLEND(poly2) ) { nkeynes@1139: case 0:/* Convert replace => modulate by setting colour values to 1.0 */ nkeynes@1139: vert->rgba[0] = vert->rgba[1] = vert->rgba[2] = vert->rgba[3] = 1.0; nkeynes@1140: vert->tex_mode = 0.0; nkeynes@1139: data.ival++; /* Skip the colour word */ nkeynes@1139: break; nkeynes@1140: case 2: /* Decal */ nkeynes@1140: vert->tex_mode = 1.0; nkeynes@1140: unpack_bgra(*data.ival++, vert->rgba); nkeynes@1140: break; nkeynes@1139: case 1: nkeynes@687: force_alpha = TRUE; nkeynes@1139: /* fall-through */ nkeynes@1140: default: nkeynes@1140: vert->tex_mode = 0.0; nkeynes@1139: unpack_bgra(*data.ival++, vert->rgba); nkeynes@1139: break; nkeynes@687: } nkeynes@1140: vert->r = scene_get_palette_offset(tex); nkeynes@1139: } else { nkeynes@1140: vert->tex_mode = 2.0; nkeynes@1140: vert->r = -1.0; nkeynes@1139: unpack_bgra(*data.ival++, vert->rgba); nkeynes@635: } nkeynes@1139: nkeynes@687: if( POLY1_SPECULAR(poly1) ) { nkeynes@687: unpack_bgra(*data.ival++, vert->offset_rgba); nkeynes@687: } else { nkeynes@687: vert->offset_rgba[0] = 0.0; nkeynes@687: vert->offset_rgba[1] = 0.0; nkeynes@687: vert->offset_rgba[2] = 0.0; nkeynes@827: vert->offset_rgba[3] = 0.0; nkeynes@687: } nkeynes@687: nkeynes@639: if( force_alpha ) { nkeynes@687: vert->rgba[3] = 1.0; nkeynes@635: } nkeynes@635: } nkeynes@635: nkeynes@635: /** nkeynes@687: * Compute texture, colour, and z values for 1 or more result points by interpolating from nkeynes@687: * a set of 3 input points. The result point(s) must define their x,y. nkeynes@635: */ nkeynes@827: static void scene_compute_vertexes( struct vertex_struct *result, nkeynes@687: int result_count, nkeynes@736: struct vertex_struct *input, nkeynes@736: gboolean is_solid_shaded ) nkeynes@635: { nkeynes@687: int i,j; nkeynes@635: float sx = input[2].x - input[1].x; nkeynes@635: float sy = input[2].y - input[1].y; nkeynes@635: float tx = input[0].x - input[1].x; nkeynes@635: float ty = input[0].y - input[1].y; nkeynes@635: nkeynes@635: float detxy = ((sy) * (tx)) - ((ty) * (sx)); nkeynes@635: if( detxy == 0 ) { nkeynes@827: // If the input points fall on a line, they don't define a usable nkeynes@687: // polygon - the PVR2 takes the last input point as the result in nkeynes@687: // this case. nkeynes@687: for( i=0; i pvr2_scene.bounds[5] ) { nkeynes@687: pvr2_scene.bounds[5] = rz; nkeynes@687: } else if( rz < pvr2_scene.bounds[4] ) { nkeynes@827: pvr2_scene.bounds[4] = rz; nkeynes@687: } nkeynes@687: result[i].z = rz; nkeynes@687: result[i].u = input[1].u + (t*tu) + (s*su); nkeynes@687: result[i].v = input[1].v + (t*tv) + (s*sv); nkeynes@1140: result[i].r = input[1].r; /* Last two components are flat */ nkeynes@1140: result[i].tex_mode = input[1].tex_mode; nkeynes@687: nkeynes@687: if( is_solid_shaded ) { nkeynes@687: memcpy( result->rgba, input[2].rgba, sizeof(result->rgba) ); nkeynes@687: memcpy( result->offset_rgba, input[2].offset_rgba, sizeof(result->offset_rgba) ); nkeynes@687: } else { nkeynes@687: float *rgba0 = input[0].rgba; nkeynes@687: float *rgba1 = input[1].rgba; nkeynes@687: float *rgba2 = input[2].rgba; nkeynes@687: float *rgba3 = result[i].rgba; nkeynes@687: for( j=0; j<8; j++ ) { nkeynes@687: float tc = *rgba0++ - *rgba1; nkeynes@687: float sc = *rgba2++ - *rgba1; nkeynes@687: float rc = *rgba1++ + (t*tc) + (s*sc); nkeynes@687: *rgba3++ = rc; nkeynes@687: } nkeynes@687: } nkeynes@635: } nkeynes@653: } nkeynes@635: nkeynes@847: static float scene_compute_lut_fog_vertex( float z, float fog_density, float fog_table[][2] ) nkeynes@847: { nkeynes@847: union { nkeynes@847: uint32_t i; nkeynes@847: float f; nkeynes@847: } v; nkeynes@847: v.f = z * fog_density; nkeynes@847: if( v.f < 1.0 ) v.f = 1.0; nkeynes@847: else if( v.f > 255.9999 ) v.f = 255.9999; nkeynes@847: nkeynes@847: uint32_t index = ((v.i >> 18) & 0x0F)|((v.i>>19)&0x70); nkeynes@847: return fog_table[index][0]; nkeynes@847: } nkeynes@847: nkeynes@847: /** nkeynes@847: * Compute the fog coefficients for all polygons using lookup-table fog. It's nkeynes@847: * a little more convenient to do this as a separate pass, since we don't have nkeynes@847: * to worry about computed vertexes. nkeynes@847: */ nkeynes@847: static void scene_compute_lut_fog( ) nkeynes@847: { nkeynes@847: int i,j; nkeynes@847: nkeynes@847: float fog_density = parse_fog_density(MMIO_READ( PVR2, RENDER_FOGCOEFF )); nkeynes@847: float fog_table[128][2]; nkeynes@847: nkeynes@847: /* Parse fog table out into floating-point format */ nkeynes@847: for( i=0; i<128; i++ ) { nkeynes@847: uint32_t ent = MMIO_READ( PVR2, RENDER_FOGTABLE + (i<<2) ); nkeynes@847: fog_table[i][0] = ((float)(((ent&0x0000FF00)>>8) + 1)) / 256.0; nkeynes@847: fog_table[i][1] = ((float)((ent&0x000000FF) + 1)) / 256.0; nkeynes@847: } nkeynes@847: nkeynes@847: nkeynes@847: for( i=0; ivertex_index; nkeynes@1133: unsigned tri_count = poly->vertex_count-2; nkeynes@1133: struct vertex_struct *vert = &pvr2_scene.vertex_array[vert_idx]; nkeynes@1133: unsigned i; nkeynes@1133: gboolean ccw = (POLY1_CULL_MODE(poly1) == CULL_CCW); nkeynes@1133: int first_visible = -1, last_visible = -1; nkeynes@1133: for( i=0; i 0 : nz < 0 ) { nkeynes@1133: /* Surface is visible */ nkeynes@1133: if( first_visible == -1 ) { nkeynes@1133: first_visible = i; nkeynes@1133: /* Elide the initial hidden triangles (note we don't nkeynes@1133: * need to care about winding anymore here) */ nkeynes@1133: poly->vertex_index += i; nkeynes@1133: poly->vertex_count -= i; nkeynes@1133: if( poly->mod_vertex_index != -1 ) nkeynes@1133: poly->mod_vertex_index += i; nkeynes@1133: } else if( last_visible != i-1 ) { nkeynes@1133: /* And... here we have to split the polygon. Allocate a new nkeynes@1133: * sub-polygon to hold the vertex references */ nkeynes@1133: struct polygon_struct *sub = scene_split_subpolygon(poly, (i-first_visible)); nkeynes@1133: poly->vertex_count -= (i-first_visible-1) - last_visible; nkeynes@1133: first_visible = i; nkeynes@1133: poly = sub; nkeynes@1133: } nkeynes@1133: last_visible = i; nkeynes@1133: } /* Else culled */ nkeynes@1133: /* Invert ccw flag for triangle strip processing */ nkeynes@1133: ccw = !ccw; nkeynes@1133: } nkeynes@1133: if( last_visible == -1 ) { nkeynes@1133: /* No visible surfaces, so we can mark the whole polygon as being vertex-less */ nkeynes@1133: poly->vertex_count = 0; nkeynes@1133: } else if( last_visible != tri_count-1 ) { nkeynes@1133: /* Remove final hidden tris */ nkeynes@1133: poly->vertex_count -= (tri_count - 1 - last_visible); nkeynes@1133: } nkeynes@1133: } nkeynes@1133: } nkeynes@1133: } nkeynes@1133: nkeynes@863: static void scene_add_cheap_shadow_vertexes( struct vertex_struct *src, struct vertex_struct *dest, int count ) nkeynes@863: { nkeynes@863: unsigned int i, j; nkeynes@863: nkeynes@863: for( i=0; ix = src->x; nkeynes@863: dest->y = src->y; nkeynes@863: dest->z = src->z; nkeynes@863: dest->u = src->u; nkeynes@863: dest->v = src->v; nkeynes@1140: dest->r = src->r; nkeynes@1140: dest->tex_mode = src->tex_mode; nkeynes@863: dest->rgba[0] = src->rgba[0] * scene_shadow_intensity; nkeynes@863: dest->rgba[1] = src->rgba[1] * scene_shadow_intensity; nkeynes@863: dest->rgba[2] = src->rgba[2] * scene_shadow_intensity; nkeynes@863: dest->rgba[3] = src->rgba[3] * scene_shadow_intensity; nkeynes@863: dest->offset_rgba[0] = src->offset_rgba[0] * scene_shadow_intensity; nkeynes@863: dest->offset_rgba[1] = src->offset_rgba[1] * scene_shadow_intensity; nkeynes@863: dest->offset_rgba[2] = src->offset_rgba[2] * scene_shadow_intensity; nkeynes@863: dest->offset_rgba[3] = src->offset_rgba[3]; nkeynes@863: dest++; nkeynes@863: src++; nkeynes@863: } nkeynes@635: } nkeynes@635: nkeynes@639: static void scene_add_vertexes( pvraddr_t poly_idx, int vertex_length, nkeynes@863: shadow_mode_t is_modified ) nkeynes@635: { nkeynes@635: struct polygon_struct *poly = pvr2_scene.buf_to_poly_map[poly_idx]; nkeynes@635: uint32_t *ptr = &pvr2_scene.pvr2_pbuf[poly_idx]; nkeynes@635: uint32_t *context = ptr; nkeynes@635: unsigned int i; nkeynes@635: nkeynes@635: if( poly->vertex_index == -1 ) { nkeynes@863: ptr += (is_modified == SHADOW_FULL ? 5 : 3 ); nkeynes@687: poly->vertex_index = pvr2_scene.vertex_index; nkeynes@687: nkeynes@687: assert( poly != NULL ); nkeynes@687: assert( pvr2_scene.vertex_index + poly->vertex_count <= pvr2_scene.vertex_count ); nkeynes@687: for( i=0; ivertex_count; i++ ) { nkeynes@1159: scene_decode_vertex( &pvr2_scene.vertex_array[pvr2_scene.vertex_index++], context[0], context[1], context[2], ptr, 0 ); nkeynes@687: ptr += vertex_length; nkeynes@687: } nkeynes@687: if( is_modified ) { nkeynes@687: assert( pvr2_scene.vertex_index + poly->vertex_count <= pvr2_scene.vertex_count ); nkeynes@687: poly->mod_vertex_index = pvr2_scene.vertex_index; nkeynes@863: if( is_modified == SHADOW_FULL ) { nkeynes@863: int mod_offset = (vertex_length - 3)>>1; nkeynes@863: ptr = &pvr2_scene.pvr2_pbuf[poly_idx] + 5; nkeynes@863: for( i=0; ivertex_count; i++ ) { nkeynes@1159: scene_decode_vertex( &pvr2_scene.vertex_array[pvr2_scene.vertex_index++], context[0], context[3], context[4], ptr, mod_offset ); nkeynes@863: ptr += vertex_length; nkeynes@863: } nkeynes@863: } else { nkeynes@863: scene_add_cheap_shadow_vertexes( &pvr2_scene.vertex_array[poly->vertex_index], nkeynes@863: &pvr2_scene.vertex_array[poly->mod_vertex_index], poly->vertex_count ); nkeynes@863: pvr2_scene.vertex_index += poly->vertex_count; nkeynes@687: } nkeynes@687: } nkeynes@635: } nkeynes@635: } nkeynes@635: nkeynes@827: static void scene_add_quad_vertexes( pvraddr_t poly_idx, int vertex_length, nkeynes@863: shadow_mode_t is_modified ) nkeynes@635: { nkeynes@635: struct polygon_struct *poly = pvr2_scene.buf_to_poly_map[poly_idx]; nkeynes@635: uint32_t *ptr = &pvr2_scene.pvr2_pbuf[poly_idx]; nkeynes@635: uint32_t *context = ptr; nkeynes@635: unsigned int i; nkeynes@635: nkeynes@635: if( poly->vertex_index == -1 ) { nkeynes@827: // Construct it locally and copy to the vertex buffer, as the VBO is nkeynes@736: // allowed to be horribly slow for reads (ie it could be direct-mapped nkeynes@736: // vram). nkeynes@736: struct vertex_struct quad[4]; nkeynes@736: nkeynes@736: assert( poly != NULL ); nkeynes@736: assert( pvr2_scene.vertex_index + poly->vertex_count <= pvr2_scene.vertex_count ); nkeynes@863: ptr += (is_modified == SHADOW_FULL ? 5 : 3 ); nkeynes@736: poly->vertex_index = pvr2_scene.vertex_index; nkeynes@736: for( i=0; i<4; i++ ) { nkeynes@1159: scene_decode_vertex( &quad[i], context[0], context[1], context[2], ptr, 0 ); nkeynes@736: ptr += vertex_length; nkeynes@736: } nkeynes@687: scene_compute_vertexes( &quad[3], 1, &quad[0], !POLY1_GOURAUD_SHADED(context[0]) ); nkeynes@736: // Swap last two vertexes (quad arrangement => tri strip arrangement) nkeynes@736: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index], quad, sizeof(struct vertex_struct)*2 ); nkeynes@736: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index+2], &quad[3], sizeof(struct vertex_struct) ); nkeynes@736: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index+3], &quad[2], sizeof(struct vertex_struct) ); nkeynes@1240: if( !POLY1_GOURAUD_SHADED(context[0]) ) { nkeynes@1240: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index].rgba, &pvr2_scene.vertex_array[pvr2_scene.vertex_index+3].rgba, sizeof(float)*8 ); nkeynes@1240: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index+1].rgba, &pvr2_scene.vertex_array[pvr2_scene.vertex_index+3].rgba, sizeof(float)*8 ); nkeynes@1240: } nkeynes@1240: nkeynes@736: pvr2_scene.vertex_index += 4; nkeynes@736: nkeynes@736: if( is_modified ) { nkeynes@736: assert( pvr2_scene.vertex_index + poly->vertex_count <= pvr2_scene.vertex_count ); nkeynes@736: poly->mod_vertex_index = pvr2_scene.vertex_index; nkeynes@863: if( is_modified == SHADOW_FULL ) { nkeynes@863: int mod_offset = (vertex_length - 3)>>1; nkeynes@863: ptr = &pvr2_scene.pvr2_pbuf[poly_idx] + 5; nkeynes@863: for( i=0; i<4; i++ ) { nkeynes@1159: scene_decode_vertex( &quad[4], context[0], context[3], context[4], ptr, mod_offset ); nkeynes@863: ptr += vertex_length; nkeynes@863: } nkeynes@863: scene_compute_vertexes( &quad[3], 1, &quad[0], !POLY1_GOURAUD_SHADED(context[0]) ); nkeynes@863: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index], quad, sizeof(struct vertex_struct)*2 ); nkeynes@863: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index+2], &quad[3], sizeof(struct vertex_struct) ); nkeynes@863: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index+3], &quad[2], sizeof(struct vertex_struct) ); nkeynes@1240: if( !POLY1_GOURAUD_SHADED(context[0]) ) { nkeynes@1240: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index].rgba, &pvr2_scene.vertex_array[pvr2_scene.vertex_index+3].rgba, sizeof(float)*8 ); nkeynes@1240: memcpy( &pvr2_scene.vertex_array[pvr2_scene.vertex_index+1].rgba, &pvr2_scene.vertex_array[pvr2_scene.vertex_index+3].rgba, sizeof(float)*8 ); nkeynes@1240: } nkeynes@863: } else { nkeynes@863: scene_add_cheap_shadow_vertexes( &pvr2_scene.vertex_array[poly->vertex_index], nkeynes@863: &pvr2_scene.vertex_array[poly->mod_vertex_index], poly->vertex_count ); nkeynes@863: pvr2_scene.vertex_index += poly->vertex_count; nkeynes@736: } nkeynes@736: pvr2_scene.vertex_index += 4; nkeynes@736: } nkeynes@635: } nkeynes@635: } nkeynes@635: nkeynes@639: static void scene_extract_polygons( pvraddr_t tile_entry ) nkeynes@635: { nkeynes@934: uint32_t *tile_list = (uint32_t *)(pvr2_main_ram+tile_entry); nkeynes@635: do { nkeynes@687: uint32_t entry = *tile_list++; nkeynes@687: if( entry >> 28 == 0x0F ) { nkeynes@687: break; nkeynes@687: } else if( entry >> 28 == 0x0E ) { nkeynes@934: tile_list = (uint32_t *)(pvr2_main_ram + (entry&0x007FFFFF)); nkeynes@687: } else { nkeynes@687: pvraddr_t polyaddr = entry&0x000FFFFF; nkeynes@863: shadow_mode_t is_modified = (entry & 0x01000000) ? pvr2_scene.shadow_mode : SHADOW_NONE; nkeynes@687: int vertex_length = (entry >> 21) & 0x07; nkeynes@687: int context_length = 3; nkeynes@863: if( is_modified == SHADOW_FULL ) { nkeynes@687: context_length = 5; nkeynes@687: vertex_length <<= 1 ; nkeynes@687: } nkeynes@687: vertex_length += 3; nkeynes@687: nkeynes@687: if( (entry & 0xE0000000) == 0x80000000 ) { nkeynes@687: /* Triangle(s) */ nkeynes@687: int strip_count = ((entry >> 25) & 0x0F)+1; nkeynes@687: int polygon_length = 3 * vertex_length + context_length; nkeynes@687: int i; nkeynes@687: struct polygon_struct *last_poly = NULL; nkeynes@687: for( i=0; inext == NULL ) { nkeynes@687: last_poly->next = poly; nkeynes@687: } nkeynes@687: last_poly = poly; nkeynes@687: } nkeynes@687: } else if( (entry & 0xE0000000) == 0xA0000000 ) { nkeynes@687: /* Sprite(s) */ nkeynes@687: int strip_count = ((entry >> 25) & 0x0F)+1; nkeynes@687: int polygon_length = 4 * vertex_length + context_length; nkeynes@687: int i; nkeynes@687: struct polygon_struct *last_poly = NULL; nkeynes@687: for( i=0; inext == NULL ) { nkeynes@687: last_poly->next = poly; nkeynes@687: } nkeynes@687: last_poly = poly; nkeynes@687: } nkeynes@687: } else { nkeynes@687: /* Polygon */ nkeynes@687: int i, last = -1; nkeynes@687: for( i=5; i>=0; i-- ) { nkeynes@687: if( entry & (0x40000000>>i) ) { nkeynes@687: last = i; nkeynes@687: break; nkeynes@687: } nkeynes@687: } nkeynes@687: if( last != -1 ) { nkeynes@687: scene_add_polygon( polyaddr, last+3, is_modified ); nkeynes@687: } nkeynes@687: } nkeynes@687: } nkeynes@635: } while( 1 ); nkeynes@635: } nkeynes@635: nkeynes@639: static void scene_extract_vertexes( pvraddr_t tile_entry ) nkeynes@635: { nkeynes@934: uint32_t *tile_list = (uint32_t *)(pvr2_main_ram+tile_entry); nkeynes@635: do { nkeynes@736: uint32_t entry = *tile_list++; nkeynes@736: if( entry >> 28 == 0x0F ) { nkeynes@736: break; nkeynes@736: } else if( entry >> 28 == 0x0E ) { nkeynes@934: tile_list = (uint32_t *)(pvr2_main_ram + (entry&0x007FFFFF)); nkeynes@736: } else { nkeynes@736: pvraddr_t polyaddr = entry&0x000FFFFF; nkeynes@863: shadow_mode_t is_modified = (entry & 0x01000000) ? pvr2_scene.shadow_mode : SHADOW_NONE; nkeynes@736: int vertex_length = (entry >> 21) & 0x07; nkeynes@736: int context_length = 3; nkeynes@863: if( is_modified == SHADOW_FULL ) { nkeynes@736: context_length = 5; nkeynes@736: vertex_length <<=1 ; nkeynes@736: } nkeynes@736: vertex_length += 3; nkeynes@736: nkeynes@736: if( (entry & 0xE0000000) == 0x80000000 ) { nkeynes@736: /* Triangle(s) */ nkeynes@736: int strip_count = ((entry >> 25) & 0x0F)+1; nkeynes@736: int polygon_length = 3 * vertex_length + context_length; nkeynes@736: int i; nkeynes@736: for( i=0; i> 25) & 0x0F)+1; nkeynes@736: int polygon_length = 4 * vertex_length + context_length; nkeynes@736: int i; nkeynes@736: for( i=0; i=0; i-- ) { nkeynes@736: if( entry & (0x40000000>>i) ) { nkeynes@736: last = i; nkeynes@736: break; nkeynes@736: } nkeynes@736: } nkeynes@736: if( last != -1 ) { nkeynes@736: scene_add_vertexes( polyaddr, vertex_length, is_modified ); nkeynes@736: } nkeynes@736: } nkeynes@736: } nkeynes@827: } while( 1 ); nkeynes@635: } nkeynes@635: nkeynes@687: static void scene_extract_background( void ) nkeynes@687: { nkeynes@687: uint32_t bgplane = MMIO_READ(PVR2, RENDER_BGPLANE); nkeynes@687: int vertex_length = (bgplane >> 24) & 0x07; nkeynes@687: int context_length = 3, i; nkeynes@863: shadow_mode_t is_modified = (bgplane & 0x08000000) ? pvr2_scene.shadow_mode : SHADOW_NONE; nkeynes@687: nkeynes@687: struct polygon_struct *poly = &pvr2_scene.poly_array[pvr2_scene.poly_count++]; nkeynes@687: uint32_t *context = &pvr2_scene.pvr2_pbuf[(bgplane & 0x00FFFFFF)>>3]; nkeynes@687: poly->context = context; nkeynes@687: poly->vertex_count = 4; nkeynes@687: poly->vertex_index = pvr2_scene.vertex_count; nkeynes@863: if( is_modified == SHADOW_FULL ) { nkeynes@687: context_length = 5; nkeynes@687: vertex_length <<= 1; nkeynes@863: } nkeynes@863: if( is_modified != SHADOW_NONE ) { nkeynes@687: poly->mod_vertex_index = pvr2_scene.vertex_count + 4; nkeynes@827: pvr2_scene.vertex_count += 8; nkeynes@687: } else { nkeynes@687: poly->mod_vertex_index = -1; nkeynes@687: pvr2_scene.vertex_count += 4; nkeynes@687: } nkeynes@687: vertex_length += 3; nkeynes@687: context_length += (bgplane & 0x07) * vertex_length; nkeynes@687: nkeynes@687: poly->next = NULL; nkeynes@1133: poly->sub_next = NULL; nkeynes@687: pvr2_scene.bkgnd_poly = poly; nkeynes@736: nkeynes@687: struct vertex_struct base_vertexes[3]; nkeynes@827: uint32_t *ptr = context + context_length; nkeynes@687: for( i=0; i<3; i++ ) { nkeynes@1159: scene_decode_vertex( &base_vertexes[i], context[0], context[1], context[2], nkeynes@736: ptr, 0 ); nkeynes@687: ptr += vertex_length; nkeynes@687: } nkeynes@687: struct vertex_struct *result_vertexes = &pvr2_scene.vertex_array[poly->vertex_index]; nkeynes@687: result_vertexes[0].x = result_vertexes[0].y = 0; nkeynes@687: result_vertexes[1].x = result_vertexes[3].x = pvr2_scene.buffer_width; nkeynes@687: result_vertexes[1].y = result_vertexes[2].x = 0; nkeynes@687: result_vertexes[2].y = result_vertexes[3].y = pvr2_scene.buffer_height; nkeynes@687: scene_compute_vertexes( result_vertexes, 4, base_vertexes, !POLY1_GOURAUD_SHADED(context[0]) ); nkeynes@687: nkeynes@863: if( is_modified == SHADOW_FULL ) { nkeynes@687: int mod_offset = (vertex_length - 3)>>1; nkeynes@687: ptr = context + context_length; nkeynes@687: for( i=0; i<3; i++ ) { nkeynes@1159: scene_decode_vertex( &base_vertexes[i], context[0], context[3], context[4], nkeynes@736: ptr, mod_offset ); nkeynes@687: ptr += vertex_length; nkeynes@687: } nkeynes@687: result_vertexes = &pvr2_scene.vertex_array[poly->mod_vertex_index]; nkeynes@687: result_vertexes[0].x = result_vertexes[0].y = 0; nkeynes@687: result_vertexes[1].x = result_vertexes[3].x = pvr2_scene.buffer_width; nkeynes@687: result_vertexes[1].y = result_vertexes[2].x = 0; nkeynes@687: result_vertexes[2].y = result_vertexes[3].y = pvr2_scene.buffer_height; nkeynes@687: scene_compute_vertexes( result_vertexes, 4, base_vertexes, !POLY1_GOURAUD_SHADED(context[0]) ); nkeynes@863: } else if( is_modified == SHADOW_CHEAP ) { nkeynes@863: scene_add_cheap_shadow_vertexes( &pvr2_scene.vertex_array[poly->vertex_index], nkeynes@863: &pvr2_scene.vertex_array[poly->mod_vertex_index], poly->vertex_count ); nkeynes@863: pvr2_scene.vertex_index += poly->vertex_count; nkeynes@687: } nkeynes@736: nkeynes@687: } nkeynes@687: nkeynes@687: nkeynes@639: uint32_t pvr2_scene_buffer_width() nkeynes@639: { nkeynes@639: return pvr2_scene.buffer_width; nkeynes@639: } nkeynes@639: nkeynes@639: uint32_t pvr2_scene_buffer_height() nkeynes@639: { nkeynes@639: return pvr2_scene.buffer_height; nkeynes@639: } nkeynes@639: nkeynes@635: /** nkeynes@635: * Extract the current scene into the rendering structures. We run two passes nkeynes@827: * - first pass extracts the polygons into pvr2_scene.poly_array (finding vertex counts), nkeynes@635: * second pass extracts the vertex data into the VBO/vertex array. nkeynes@635: * nkeynes@827: * Difficult to do in single pass as we don't generally know the size of a nkeynes@635: * polygon for certain until we've seen all tiles containing it. It also means we nkeynes@635: * can count the vertexes and allocate the appropriate size VBO. nkeynes@635: * nkeynes@635: * FIXME: accesses into VRAM need to be bounds-checked properly nkeynes@635: */ nkeynes@635: void pvr2_scene_read( void ) nkeynes@635: { nkeynes@635: pvr2_scene_init(); nkeynes@639: pvr2_scene_reset(); nkeynes@635: nkeynes@635: pvr2_scene.bounds[0] = MMIO_READ( PVR2, RENDER_HCLIP ) & 0x03FF; nkeynes@635: pvr2_scene.bounds[1] = ((MMIO_READ( PVR2, RENDER_HCLIP ) >> 16) & 0x03FF) + 1; nkeynes@635: pvr2_scene.bounds[2] = MMIO_READ( PVR2, RENDER_VCLIP ) & 0x03FF; nkeynes@635: pvr2_scene.bounds[3] = ((MMIO_READ( PVR2, RENDER_VCLIP ) >> 16) & 0x03FF) + 1; nkeynes@635: pvr2_scene.bounds[4] = pvr2_scene.bounds[5] = MMIO_READF( PVR2, RENDER_FARCLIP ); nkeynes@635: nkeynes@827: uint32_t scaler = MMIO_READ( PVR2, RENDER_SCALER ); nkeynes@827: if( scaler & SCALER_HSCALE ) { nkeynes@827: /* If the horizontal scaler is in use, we're (in principle) supposed to nkeynes@827: * divide everything by 2. However in the interests of display quality, nkeynes@827: * instead we want to render to the unscaled resolution and downsample nkeynes@827: * only if/when required. nkeynes@827: */ nkeynes@827: pvr2_scene.bounds[1] *= 2; nkeynes@827: } nkeynes@847: nkeynes@847: uint32_t fog_col = MMIO_READ( PVR2, RENDER_FOGTBLCOL ); nkeynes@847: unpack_bgra( fog_col, pvr2_scene.fog_lut_colour ); nkeynes@847: fog_col = MMIO_READ( PVR2, RENDER_FOGVRTCOL ); nkeynes@847: unpack_bgra( fog_col, pvr2_scene.fog_vert_colour ); nkeynes@847: nkeynes@934: uint32_t *tilebuffer = (uint32_t *)(pvr2_main_ram + MMIO_READ( PVR2, RENDER_TILEBASE )); nkeynes@635: uint32_t *segment = tilebuffer; nkeynes@863: uint32_t shadow = MMIO_READ(PVR2,RENDER_SHADOW); nkeynes@635: pvr2_scene.segment_list = (struct tile_segment *)tilebuffer; nkeynes@934: pvr2_scene.pvr2_pbuf = (uint32_t *)(pvr2_main_ram + MMIO_READ(PVR2,RENDER_POLYBASE)); nkeynes@863: pvr2_scene.shadow_mode = shadow & 0x100 ? SHADOW_CHEAP : SHADOW_FULL; nkeynes@863: scene_shadow_intensity = U8TOFLOAT(shadow&0xFF); nkeynes@687: nkeynes@635: int max_tile_x = 0; nkeynes@635: int max_tile_y = 0; nkeynes@635: int obj_config = MMIO_READ( PVR2, RENDER_OBJCFG ); nkeynes@635: int isp_config = MMIO_READ( PVR2, RENDER_ISPCFG ); nkeynes@635: nkeynes@635: if( (obj_config & 0x00200000) == 0 ) { nkeynes@687: if( isp_config & 1 ) { nkeynes@687: pvr2_scene.sort_mode = SORT_NEVER; nkeynes@687: } else { nkeynes@687: pvr2_scene.sort_mode = SORT_ALWAYS; nkeynes@687: } nkeynes@635: } else { nkeynes@687: pvr2_scene.sort_mode = SORT_TILEFLAG; nkeynes@635: } nkeynes@635: nkeynes@827: // Pass 1: Extract polygon list nkeynes@635: uint32_t control; nkeynes@635: int i; nkeynes@635: do { nkeynes@687: control = *segment++; nkeynes@687: int tile_x = SEGMENT_X(control); nkeynes@687: int tile_y = SEGMENT_Y(control); nkeynes@687: if( tile_x > max_tile_x ) { nkeynes@687: max_tile_x = tile_x; nkeynes@827: } nkeynes@687: if( tile_y > max_tile_y ) { nkeynes@687: max_tile_y = tile_y; nkeynes@687: } nkeynes@687: for( i=0; i<5; i++ ) { nkeynes@687: if( (*segment & NO_POINTER) == 0 ) { nkeynes@687: scene_extract_polygons( *segment ); nkeynes@687: } nkeynes@687: segment++; nkeynes@687: } nkeynes@635: } while( (control & SEGMENT_END) == 0 ); nkeynes@635: nkeynes@635: pvr2_scene.buffer_width = (max_tile_x+1)<<5; nkeynes@635: pvr2_scene.buffer_height = (max_tile_y+1)<<5; nkeynes@635: nkeynes@687: // Pass 2: Extract vertex data nkeynes@687: vertex_buffer_map(); nkeynes@687: pvr2_scene.vertex_index = 0; nkeynes@687: segment = tilebuffer; nkeynes@687: do { nkeynes@687: control = *segment++; nkeynes@687: for( i=0; i<5; i++ ) { nkeynes@687: if( (*segment & NO_POINTER) == 0 ) { nkeynes@687: scene_extract_vertexes( *segment ); nkeynes@687: } nkeynes@687: segment++; nkeynes@687: } nkeynes@687: } while( (control & SEGMENT_END) == 0 ); nkeynes@736: nkeynes@687: scene_extract_background(); nkeynes@847: scene_compute_lut_fog(); nkeynes@1133: scene_backface_cull(); nkeynes@736: nkeynes@687: vertex_buffer_unmap(); nkeynes@635: } nkeynes@645: nkeynes@1159: void pvr2_scene_finished( ) nkeynes@1159: { nkeynes@1159: vbuf->finished(vbuf); nkeynes@1159: } nkeynes@1159: nkeynes@645: /** nkeynes@645: * Dump the current scene to file in a (mostly) human readable form nkeynes@645: */ nkeynes@1142: void pvr2_scene_print( FILE *f ) nkeynes@645: { nkeynes@645: int i,j; nkeynes@645: nkeynes@645: fprintf( f, "Polygons: %d\n", pvr2_scene.poly_count ); nkeynes@645: for( i=0; icontext) - pvr2_main_ram) ); nkeynes@736: switch( poly->vertex_count ) { nkeynes@736: case 3: fprintf( f, "Tri " ); break; nkeynes@736: case 4: fprintf( f, "Quad " ); break; nkeynes@736: default: fprintf( f,"%d-Strip ", poly->vertex_count-2 ); break; nkeynes@736: } nkeynes@736: fprintf( f, "%08X %08X %08X ", poly->context[0], poly->context[1], poly->context[2] ); nkeynes@736: if( poly->mod_vertex_index != -1 ) { nkeynes@736: fprintf( f, "%08X %08X\n", poly->context[3], poly->context[5] ); nkeynes@736: } else { nkeynes@736: fprintf( f, "\n" ); nkeynes@736: } nkeynes@736: nkeynes@736: for( j=0; jvertex_count; j++ ) { nkeynes@736: struct vertex_struct *v = &pvr2_scene.vertex_array[poly->vertex_index+j]; nkeynes@784: fprintf( f, " %.5f %.5f %.5f, (%.5f,%.5f) %.5f,%.5f,%.5f,%.5f %.5f %.5f %.5f %.5f\n", v->x, v->y, v->z, v->u, v->v, nkeynes@827: v->rgba[0], v->rgba[1], v->rgba[2], v->rgba[3], nkeynes@784: v->offset_rgba[0], v->offset_rgba[1], v->offset_rgba[2], v->offset_rgba[3] ); nkeynes@736: } nkeynes@736: if( poly->mod_vertex_index != -1 ) { nkeynes@736: fprintf( f, " ---\n" ); nkeynes@736: for( j=0; jvertex_count; j++ ) { nkeynes@736: struct vertex_struct *v = &pvr2_scene.vertex_array[poly->mod_vertex_index+j]; nkeynes@784: fprintf( f, " %.5f %.5f %.5f, (%.5f,%.5f) %.5f,%.5f,%.5f,%.5f %.5f %.5f %.5f %.5f\n", v->x, v->y, v->z, v->u, v->v, nkeynes@827: v->rgba[0], v->rgba[1], v->rgba[2], v->rgba[3], nkeynes@784: v->offset_rgba[0], v->offset_rgba[1], v->offset_rgba[2], v->offset_rgba[3] ); nkeynes@736: } nkeynes@736: } nkeynes@645: } nkeynes@645: nkeynes@645: } nkeynes@1142: nkeynes@1142: void pvr2_scene_dump() nkeynes@1142: { nkeynes@1142: pvr2_scene_print(stdout); nkeynes@1142: }