nkeynes@222: /** nkeynes@561: * $Id$ nkeynes@222: * nkeynes@222: * PVR2 renderer routines for depth sorted polygons nkeynes@222: * nkeynes@222: * Copyright (c) 2005 Nathan Keynes. nkeynes@222: * nkeynes@222: * This program is free software; you can redistribute it and/or modify nkeynes@222: * it under the terms of the GNU General Public License as published by nkeynes@222: * the Free Software Foundation; either version 2 of the License, or nkeynes@222: * (at your option) any later version. nkeynes@222: * nkeynes@222: * This program is distributed in the hope that it will be useful, nkeynes@222: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@222: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@222: * GNU General Public License for more details. nkeynes@222: */ nkeynes@222: #include nkeynes@653: #include nkeynes@653: #include nkeynes@222: #include "pvr2/pvr2.h" nkeynes@653: #include "pvr2/scene.h" nkeynes@222: #include "asic.h" nkeynes@222: nkeynes@222: #define MIN3( a,b,c ) ((a) < (b) ? ( (a) < (c) ? (a) : (c) ) : ((b) < (c) ? (b) : (c)) ) nkeynes@222: #define MAX3( a,b,c ) ((a) > (b) ? ( (a) > (c) ? (a) : (c) ) : ((b) > (c) ? (b) : (c)) ) nkeynes@876: #define EPSILON 0.0001 nkeynes@222: nkeynes@653: struct sort_triangle { nkeynes@653: struct polygon_struct *poly; nkeynes@653: int triangle_num; // triangle number in the poly, from 0 nkeynes@876: /* plane equation */ nkeynes@876: float mx, my, mz, d; nkeynes@876: float bounds[6]; /* x1,x2,y1,y2,z1,z2 */ nkeynes@222: }; nkeynes@222: nkeynes@318: #define SENTINEL 0xDEADBEEF nkeynes@318: nkeynes@222: /** nkeynes@222: * Count the number of triangles in the list starting at the given nkeynes@222: * pvr memory address. nkeynes@222: */ nkeynes@876: static int sort_count_triangles( pvraddr_t tile_entry ) { nkeynes@222: uint32_t *tile_list = (uint32_t *)(video_base+tile_entry); nkeynes@222: int count = 0; nkeynes@222: while(1) { nkeynes@736: uint32_t entry = *tile_list++; nkeynes@736: if( entry >> 28 == 0x0F ) { nkeynes@736: break; nkeynes@736: } else if( entry >> 28 == 0x0E ) { nkeynes@736: tile_list = (uint32_t *)(video_base+(entry&0x007FFFFF)); nkeynes@736: } else if( entry >> 29 == 0x04 ) { /* Triangle array */ nkeynes@736: count += ((entry >> 25) & 0x0F)+1; nkeynes@736: } else if( entry >> 29 == 0x05 ) { /* Quad array */ nkeynes@736: count += ((((entry >> 25) & 0x0F)+1)<<1); nkeynes@736: } else { /* Polygon */ nkeynes@736: int i; nkeynes@736: for( i=0; i<6; i++ ) { nkeynes@736: if( entry & (0x40000000>>i) ) { nkeynes@736: count++; nkeynes@736: } nkeynes@736: } nkeynes@736: } nkeynes@222: } nkeynes@222: return count; nkeynes@222: } nkeynes@222: nkeynes@876: static void sort_add_triangle( struct sort_triangle *triangle, struct polygon_struct *poly, int index ) nkeynes@876: { nkeynes@876: struct vertex_struct *vertexes = &pvr2_scene.vertex_array[poly->vertex_index+index]; nkeynes@876: triangle->poly = poly; nkeynes@876: triangle->triangle_num = index; nkeynes@876: nkeynes@876: /* Compute triangle bounding-box */ nkeynes@876: triangle->bounds[0] = MIN3(vertexes[0].x,vertexes[1].x,vertexes[2].x); nkeynes@876: triangle->bounds[1] = MAX3(vertexes[0].x,vertexes[1].x,vertexes[2].x); nkeynes@876: triangle->bounds[2] = MIN3(vertexes[0].y,vertexes[1].y,vertexes[2].y); nkeynes@876: triangle->bounds[3] = MAX3(vertexes[0].y,vertexes[1].y,vertexes[2].y); nkeynes@876: triangle->bounds[4] = MIN3(vertexes[0].z,vertexes[1].z,vertexes[2].z); nkeynes@876: triangle->bounds[5] = MAX3(vertexes[0].z,vertexes[1].z,vertexes[2].z); nkeynes@876: nkeynes@876: /* Compute plane equation */ nkeynes@876: float sx = vertexes[1].x - vertexes[0].x; nkeynes@876: float sy = vertexes[1].y - vertexes[0].y; nkeynes@876: float sz = vertexes[1].z - vertexes[0].z; nkeynes@876: float tx = vertexes[2].x - vertexes[0].x; nkeynes@876: float ty = vertexes[2].y - vertexes[0].y; nkeynes@876: float tz = vertexes[2].z - vertexes[0].z; nkeynes@876: triangle->mx = sy*tz - sz*ty; nkeynes@876: triangle->my = sz*tx - sx*tz; nkeynes@876: triangle->mz = sx*ty - sy*tx; nkeynes@876: triangle->d = -vertexes[0].x*triangle->mx - nkeynes@876: vertexes[0].y*triangle->my - nkeynes@876: vertexes[0].z*triangle->mz; nkeynes@876: } nkeynes@876: nkeynes@653: /** nkeynes@653: * Extract a triangle list from the tile (basically indexes into the polygon list, plus nkeynes@653: * computing maxz while we go through it nkeynes@653: */ nkeynes@653: int sort_extract_triangles( pvraddr_t tile_entry, struct sort_triangle *triangles ) nkeynes@222: { nkeynes@222: uint32_t *tile_list = (uint32_t *)(video_base+tile_entry); nkeynes@862: int strip_count; nkeynes@862: struct polygon_struct *poly; nkeynes@862: int count = 0, i; nkeynes@862: nkeynes@222: while(1) { nkeynes@736: uint32_t entry = *tile_list++; nkeynes@862: switch( entry >> 28 ) { nkeynes@862: case 0x0F: nkeynes@862: return count; // End-of-list nkeynes@862: case 0x0E: nkeynes@862: tile_list = (uint32_t *)(video_base + (entry&0x007FFFFF)); nkeynes@736: break; nkeynes@862: case 0x08: case 0x09: case 0x0A: case 0x0B: nkeynes@862: strip_count = ((entry >> 25) & 0x0F)+1; nkeynes@862: poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF]; nkeynes@862: while( strip_count > 0 ) { nkeynes@862: assert( poly != NULL ); nkeynes@876: for( i=0; ivertex_count-2; i++ ) { nkeynes@876: sort_add_triangle( &triangles[count], poly, i ); nkeynes@736: count++; nkeynes@736: } nkeynes@862: poly = poly->next; nkeynes@862: strip_count--; nkeynes@862: } nkeynes@862: break; nkeynes@862: default: nkeynes@862: if( entry & 0x7E000000 ) { nkeynes@862: poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF]; nkeynes@736: for( i=0; i<6; i++ ) { nkeynes@736: if( entry & (0x40000000>>i) ) { nkeynes@876: sort_add_triangle( &triangles[count], poly, i ); nkeynes@736: count++; nkeynes@736: } nkeynes@736: } nkeynes@736: } nkeynes@736: } nkeynes@862: } nkeynes@862: nkeynes@222: } nkeynes@222: nkeynes@876: void sort_render_triangles( struct sort_triangle **triangles, int num_triangles ) nkeynes@222: { nkeynes@429: int i; nkeynes@222: for( i=0; ipoly; nkeynes@736: if( poly->tex_id != -1 ) { nkeynes@736: glBindTexture(GL_TEXTURE_2D, poly->tex_id); nkeynes@736: } nkeynes@865: render_set_context( poly->context, GL_GEQUAL ); nkeynes@736: glDepthMask(GL_FALSE); nkeynes@736: /* Fix cull direction */ nkeynes@876: if( triangles[i]->triangle_num & 1 ) { nkeynes@736: glCullFace(GL_FRONT); nkeynes@736: } else { nkeynes@736: glCullFace(GL_BACK); nkeynes@736: } nkeynes@736: nkeynes@876: glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index + triangles[i]->triangle_num, 3 ); nkeynes@222: } nkeynes@222: } nkeynes@222: nkeynes@876: static int sort_triangle_compare( const void *a, const void *b ) nkeynes@222: { nkeynes@653: const struct sort_triangle *tri1 = a; nkeynes@653: const struct sort_triangle *tri2 = b; nkeynes@876: if( tri1->bounds[5] <= tri2->bounds[4] ) nkeynes@876: return 1; /* tri1 is entirely under tri2 */ nkeynes@876: else if( tri2->bounds[5] <= tri1->bounds[4] ) nkeynes@876: return -1; /* tri2 is entirely under tri1 */ nkeynes@876: else if( tri1->bounds[1] <= tri2->bounds[0] || nkeynes@876: tri2->bounds[1] <= tri1->bounds[0] || nkeynes@876: tri1->bounds[3] <= tri2->bounds[2] || nkeynes@876: tri2->bounds[3] <= tri1->bounds[2] ) nkeynes@876: return 0; /* tri1 and tri2 don't actually overlap at all */ nkeynes@876: else { nkeynes@876: struct vertex_struct *tri1v = &pvr2_scene.vertex_array[tri1->poly->vertex_index + tri1->triangle_num]; nkeynes@876: struct vertex_struct *tri2v = &pvr2_scene.vertex_array[tri2->poly->vertex_index + tri2->triangle_num]; nkeynes@876: float v[3]; nkeynes@876: int i; nkeynes@876: for( i=0; i<3; i++ ) { nkeynes@876: v[i] = tri1->mx * tri2v[i].x + tri1->my * tri2v[i].y + tri1->mz * tri2v[i].z + tri1->d; nkeynes@876: if( v[i] > -EPSILON && v[i] < EPSILON ) v[i] = 0; nkeynes@876: } nkeynes@876: if( v[0] == 0 && v[1] == 0 && v[2] == 0 ) { nkeynes@876: return 0; /* coplanar */ nkeynes@876: } nkeynes@876: if( (v[0] >=0 && v[1] >= 0 && v[2] >= 0) || nkeynes@876: (v[0] <= 0 && v[1] <= 0 && v[2] <= 0) ) { nkeynes@876: /* Tri is on one side of the plane. Pick an arbitrary point to determine which side */ nkeynes@876: float t1z = -(tri1->mx * tri2v[0].x + tri1->my * tri2v[0].y + tri1->d) / tri1->mz; nkeynes@876: return tri2v[0].z - t1z; nkeynes@876: } nkeynes@876: nkeynes@876: /* If the above test failed, then tri2 intersects tri1's plane. This nkeynes@876: * doesn't necessarily mean the triangles intersect (although they may). nkeynes@876: * For now just return 0, and come back to this later as it's a fairly nkeynes@876: * uncommon case in practice. nkeynes@876: */ nkeynes@876: return 0; nkeynes@876: } nkeynes@222: } nkeynes@222: nkeynes@876: /** nkeynes@876: * This is pretty much a standard merge sort (Unfortunately can't depend on nkeynes@876: * the system to provide one. Note we can't use quicksort here - the sort nkeynes@876: * must be stable to preserve the order of coplanar triangles. nkeynes@876: */ nkeynes@876: static void sort_triangles( struct sort_triangle **triangles, int num_triangles, struct sort_triangle **out ) nkeynes@222: { nkeynes@876: if( num_triangles > 2 ) { nkeynes@876: int l = num_triangles>>1, r=num_triangles-l, i=0,j=0; nkeynes@876: struct sort_triangle *left[l]; nkeynes@876: struct sort_triangle *right[r]; nkeynes@876: sort_triangles( triangles, l, left ); nkeynes@876: sort_triangles( triangles+l, r, right ); nkeynes@876: nkeynes@876: /* Merge */ nkeynes@876: while(1) { nkeynes@876: if( sort_triangle_compare(left[i], right[j]) <= 0 ) { nkeynes@876: *out++ = left[i++]; nkeynes@876: if( i == l ) { nkeynes@876: memcpy( out, &right[j], (r-j)*sizeof(struct sort_triangle *) ); nkeynes@876: break; nkeynes@876: } nkeynes@876: } else { nkeynes@876: *out++ = right[j++]; nkeynes@876: if( j == r ) { nkeynes@876: memcpy( out, &left[i], (l-i)*sizeof(struct sort_triangle *) ); nkeynes@876: break; nkeynes@876: } nkeynes@876: } nkeynes@876: } nkeynes@876: } else if( num_triangles == 2 ) { nkeynes@876: if( sort_triangle_compare(triangles[0], triangles[1]) <= 0 ) { nkeynes@876: out[0] = triangles[0]; nkeynes@876: out[1] = triangles[1]; nkeynes@876: } else { nkeynes@876: struct sort_triangle *tmp = triangles[0]; nkeynes@876: out[0] = triangles[1]; nkeynes@876: out[1] = tmp; nkeynes@876: } nkeynes@876: } else { nkeynes@876: out[0] = triangles[0]; nkeynes@876: } nkeynes@222: } nkeynes@736: nkeynes@653: void render_autosort_tile( pvraddr_t tile_entry, int render_mode ) nkeynes@222: { nkeynes@653: int num_triangles = sort_count_triangles(tile_entry); nkeynes@222: if( num_triangles == 0 ) { nkeynes@736: return; /* nothing to do */ nkeynes@222: } else if( num_triangles == 1 ) { /* Triangle can hardly overlap with itself */ nkeynes@893: gl_render_tilelist(tile_entry, GL_GEQUAL); nkeynes@222: } else { /* Ooh boy here we go... */ nkeynes@876: int i; nkeynes@736: struct sort_triangle triangles[num_triangles+1]; nkeynes@876: struct sort_triangle *triangle_order[num_triangles+1]; nkeynes@736: triangles[num_triangles].poly = (void *)SENTINEL; nkeynes@876: for( i=0; i