filename | src/pvr2/glrender.c |
changeset | 863:a5e5310061e2 |
prev | 856:02ac5f37bfc9 |
next | 864:a90f3d5e57e1 |
author | nkeynes |
date | Sun Sep 28 01:09:51 2008 +0000 (15 years ago) |
permissions | -rw-r--r-- |
last change | Initial shadow volume implementation for opaque polygons (stencil isn't quite right, but we get some kind of shadows now) |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * Standard OpenGL rendering engine.
5 *
6 * Copyright (c) 2005 Nathan Keynes.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
19 #include <assert.h>
20 #include <sys/time.h>
21 #include "display.h"
22 #include "pvr2/pvr2.h"
23 #include "pvr2/pvr2mmio.h"
24 #include "pvr2/scene.h"
25 #include "pvr2/glutil.h"
27 #define IS_EMPTY_TILE_LIST(p) ((*((uint32_t *)(video_base+(p))) >> 28) == 0x0F)
29 int pvr2_poly_depthmode[8] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
30 GL_GREATER, GL_NOTEQUAL, GL_GEQUAL,
31 GL_ALWAYS };
32 int pvr2_poly_srcblend[8] = {
33 GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,
34 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
35 GL_ONE_MINUS_DST_ALPHA };
36 int pvr2_poly_dstblend[8] = {
37 GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR,
38 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA,
39 GL_ONE_MINUS_DST_ALPHA };
40 int pvr2_poly_texblend[4] = {
41 GL_REPLACE,
42 GL_MODULATE,
43 GL_DECAL,
44 GL_MODULATE
45 };
47 /**
48 * Clip the tile bounds to the clipping plane.
49 * @return TRUE if the tile was not clipped completely.
50 */
51 static gboolean clip_tile_bounds( uint32_t *tile, float *clip )
52 {
53 if( tile[0] < clip[0] ) tile[0] = clip[0];
54 if( tile[1] > clip[1] ) tile[1] = clip[1];
55 if( tile[2] < clip[2] ) tile[2] = clip[2];
56 if( tile[3] > clip[3] ) tile[3] = clip[3];
57 return tile[0] < tile[1] && tile[2] < tile[3];
58 }
60 void pvr2_scene_load_textures()
61 {
62 int i;
63 for( i=0; i < pvr2_scene.poly_count; i++ ) {
64 struct polygon_struct *poly = &pvr2_scene.poly_array[i];
65 if( POLY1_TEXTURED(poly->context[0]) ) {
66 poly->tex_id = texcache_get_texture( poly->context[2],
67 POLY2_TEX_WIDTH(poly->context[1]),
68 POLY2_TEX_HEIGHT(poly->context[1]) );
69 if( poly->mod_vertex_index != -1 ) {
70 if( pvr2_scene.shadow_mode == SHADOW_FULL ) {
71 poly->mod_tex_id = texcache_get_texture( poly->context[4],
72 POLY2_TEX_WIDTH(poly->context[3]),
73 POLY2_TEX_HEIGHT(poly->context[3]) );
74 } else {
75 poly->mod_tex_id = poly->tex_id;
76 }
77 }
78 } else {
79 poly->tex_id = -1;
80 poly->mod_tex_id = -1;
81 }
82 }
83 }
87 /**
88 * Once-off call to setup the OpenGL context.
89 */
90 void pvr2_setup_gl_context()
91 {
93 if( glsl_is_supported() ) {
94 if( !glsl_load_shaders( glsl_vertex_shader_src, NULL ) ) {
95 WARN( "Unable to load GL shaders" );
96 }
97 }
99 texcache_gl_init(); // Allocate texture IDs
100 glCullFace( GL_BACK );
101 glEnable( GL_BLEND );
102 glEnable( GL_DEPTH_TEST );
103 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
104 glMatrixMode(GL_MODELVIEW);
105 glLoadIdentity();
107 #ifdef HAVE_OPENGL_CLAMP_COLOR
108 if( isGLExtensionSupported("GL_ARB_color_buffer_float") ) {
109 glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FALSE );
110 glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE );
111 }
112 #endif
114 glEnableClientState( GL_COLOR_ARRAY );
115 glEnableClientState( GL_VERTEX_ARRAY );
116 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
117 glEnableClientState( GL_SECONDARY_COLOR_ARRAY );
118 glEnableClientState( GL_FOG_COORDINATE_ARRAY_EXT );
120 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
121 glClearDepth(0);
122 glClearStencil(0);
123 }
125 /**
126 * Setup the basic context that's shared between normal and modified modes -
127 * depth, culling, shade model, and color sum
128 */
129 static void render_set_base_context( uint32_t poly1 )
130 {
131 glDepthFunc( POLY1_DEPTH_MODE(poly1) );
132 glDepthMask( POLY1_DEPTH_WRITE(poly1) ? GL_TRUE : GL_FALSE );
134 switch( POLY1_CULL_MODE(poly1) ) {
135 case CULL_NONE:
136 case CULL_SMALL:
137 glDisable( GL_CULL_FACE );
138 break;
139 case CULL_CCW:
140 glEnable( GL_CULL_FACE );
141 glFrontFace( GL_CW );
142 break;
143 case CULL_CW:
144 glEnable( GL_CULL_FACE );
145 glFrontFace( GL_CCW );
146 break;
147 }
149 glShadeModel( POLY1_SHADE_MODEL(poly1) );
151 if( POLY1_SPECULAR(poly1) ) {
152 glEnable(GL_COLOR_SUM);
153 } else {
154 glDisable(GL_COLOR_SUM);
155 }
156 }
158 /**
159 * Setup the texture/shading settings (TSP) which vary between mod/unmod modes.
160 */
161 static void render_set_tsp_context( uint32_t poly1, uint32_t poly2, uint32_t texture )
162 {
163 if( POLY1_TEXTURED(poly1) ) {
164 glEnable(GL_TEXTURE_2D);
165 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, pvr2_poly_texblend[POLY2_TEX_BLEND(poly2)] );
166 if( POLY2_TEX_CLAMP_U(poly2) ) {
167 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
168 } else if( POLY2_TEX_MIRROR_U(poly2) ) {
169 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT_ARB );
170 } else {
171 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
172 }
173 if( POLY2_TEX_CLAMP_V(poly2) ) {
174 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
175 } else if( POLY2_TEX_MIRROR_V(poly2) ) {
176 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT_ARB );
177 } else {
178 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
179 }
180 } else {
181 glDisable( GL_TEXTURE_2D );
182 }
184 switch( POLY2_FOG_MODE(poly2) ) {
185 case PVR2_POLY_FOG_LOOKUP:
186 glFogfv( GL_FOG_COLOR, pvr2_scene.fog_lut_colour );
187 glEnable( GL_FOG );
188 break;
189 case PVR2_POLY_FOG_VERTEX:
190 if( POLY1_SPECULAR(poly1) ) {
191 glFogfv( GL_FOG_COLOR, pvr2_scene.fog_vert_colour );
192 glEnable( GL_FOG );
193 break;
194 } /* else fallthrough */
195 default:
196 glDisable( GL_FOG );
197 }
199 int srcblend = POLY2_SRC_BLEND(poly2);
200 int destblend = POLY2_DEST_BLEND(poly2);
201 glBlendFunc( srcblend, destblend );
203 if( POLY2_SRC_BLEND_TARGET(poly2) || POLY2_DEST_BLEND_TARGET(poly2) ) {
204 WARN( "Accumulation buffer not supported" );
205 }
206 }
208 /**
209 * Setup the GL context for the supplied polygon context.
210 * @param context pointer to 3 or 5 words of polygon context
211 * @param modified boolean flag indicating that the modified
212 * version should be used, rather than the normal version.
213 */
214 void render_set_context( uint32_t *context, int render_mode )
215 {
216 uint32_t poly1 = context[0], poly2, texture;
217 if( render_mode == RENDER_FULLMOD ) {
218 poly2 = context[3];
219 texture = context[4];
220 } else {
221 poly2 = context[1];
222 texture = context[2];
223 }
225 render_set_base_context(poly1);
226 render_set_tsp_context(poly1,poly2,texture);
227 }
230 static void gl_render_poly( struct polygon_struct *poly )
231 {
232 if( poly->tex_id != -1 ) {
233 glBindTexture(GL_TEXTURE_2D, poly->tex_id);
234 }
235 if( poly->mod_vertex_index == -1 ) {
236 render_set_context( poly->context, RENDER_NORMAL );
237 glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
238 } else {
239 render_set_base_context( poly->context[0] );
240 render_set_tsp_context( poly->context[0], poly->context[1], poly->context[2] );
241 glStencilFunc(GL_EQUAL, 0, 1);
242 glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
244 if( pvr2_scene.shadow_mode == SHADOW_FULL ) {
245 if( poly->mod_tex_id != -1 ) {
246 glBindTexture(GL_TEXTURE_2D, poly->mod_tex_id);
247 }
248 render_set_tsp_context( poly->context[0], poly->context[3], poly->context[4] );
249 }
250 glStencilFunc(GL_EQUAL, 1, 1);
251 glDrawArrays(GL_TRIANGLE_STRIP, poly->mod_vertex_index, poly->vertex_count );
252 }
253 }
255 static void gl_render_bkgnd( struct polygon_struct *poly )
256 {
257 if( poly->tex_id != -1 ) {
258 glBindTexture(GL_TEXTURE_2D, poly->tex_id);
259 }
260 render_set_context( poly->context, RENDER_NORMAL );
261 glDisable( GL_DEPTH_TEST );
262 glDisable( GL_CULL_FACE );
263 glBlendFunc( GL_ONE, GL_ZERO );
264 glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
265 glEnable( GL_CULL_FACE );
266 glEnable( GL_DEPTH_TEST );
267 }
269 void gl_render_tilelist( pvraddr_t tile_entry )
270 {
271 uint32_t *tile_list = (uint32_t *)(video_base+tile_entry);
272 int strip_count;
273 struct polygon_struct *poly;
275 if( !IS_TILE_PTR(tile_entry) )
276 return;
278 while(1) {
279 uint32_t entry = *tile_list++;
280 switch( entry >> 28 ) {
281 case 0x0F:
282 return; // End-of-list
283 case 0x0E:
284 tile_list = (uint32_t *)(video_base + (entry&0x007FFFFF));
285 break;
286 case 0x08: case 0x09: case 0x0A: case 0x0B:
287 strip_count = ((entry >> 25) & 0x0F)+1;
288 poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF];
289 while( strip_count > 0 ) {
290 assert( poly != NULL );
291 gl_render_poly( poly );
292 poly = poly->next;
293 strip_count--;
294 }
295 break;
296 default:
297 if( entry & 0x7E000000 ) {
298 poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF];
299 gl_render_poly( poly );
300 }
301 }
302 }
303 }
305 /**
306 * Render the tilelist with depthbuffer updates only.
307 */
308 void gl_render_tilelist_depthonly( pvraddr_t tile_entry )
309 {
310 uint32_t *tile_list = (uint32_t *)(video_base+tile_entry);
311 int strip_count;
312 struct polygon_struct *poly;
314 if( !IS_TILE_PTR(tile_entry) )
315 return;
317 glDisable( GL_TEXTURE_2D );
318 glDisable( GL_FOG );
319 glDisable( GL_COLOR_SUM );
321 while(1) {
322 uint32_t entry = *tile_list++;
323 switch( entry >> 28 ) {
324 case 0x0F:
325 return; // End-of-list
326 case 0x0E:
327 tile_list = (uint32_t *)(video_base + (entry&0x007FFFFF));
328 break;
329 case 0x08: case 0x09: case 0x0A: case 0x0B:
330 strip_count = ((entry >> 25) & 0x0F)+1;
331 poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF];
332 while( strip_count > 0 ) {
333 render_set_base_context(poly->context[0]);
334 glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
335 poly = poly->next;
336 strip_count--;
337 }
338 break;
339 default:
340 if( entry & 0x7E000000 ) {
341 poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF];
342 render_set_base_context(poly->context[0]);
343 glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
344 }
345 }
346 }
347 }
349 void gl_render_modifier_tilelist( pvraddr_t tile_entry )
350 {
351 uint32_t *tile_list = (uint32_t *)(video_base+tile_entry);
352 int strip_count;
353 struct polygon_struct *poly;
355 if( !IS_TILE_PTR(tile_entry) )
356 return;
358 glDisable( GL_TEXTURE_2D );
359 glDisable( GL_FOG );
360 glDisable( GL_COLOR_SUM );
361 glDisable( GL_CULL_FACE );
362 glEnable( GL_STENCIL_TEST );
363 glEnable( GL_DEPTH_TEST );
364 glDepthFunc( GL_LEQUAL );
366 glStencilFunc( GL_ALWAYS, 0, 1 );
367 glStencilOp( GL_KEEP,GL_INVERT, GL_KEEP );
369 glDepthMask( GL_FALSE );
371 while(1) {
372 uint32_t entry = *tile_list++;
373 switch( entry >> 28 ) {
374 case 0x0F:
375 glDepthMask( GL_TRUE );
376 glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
377 return; // End-of-list
378 case 0x0E:
379 tile_list = (uint32_t *)(video_base + (entry&0x007FFFFF));
380 break;
381 case 0x08: case 0x09: case 0x0A: case 0x0B:
382 strip_count = ((entry >> 25) & 0x0F)+1;
383 poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF];
384 while( strip_count > 0 ) {
385 glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
386 poly = poly->next;
387 strip_count--;
388 }
389 break;
390 default:
391 if( entry & 0x7E000000 ) {
392 poly = pvr2_scene.buf_to_poly_map[entry&0x000FFFFF];
393 glDrawArrays(GL_TRIANGLE_STRIP, poly->vertex_index, poly->vertex_count );
394 }
395 }
396 }
398 }
401 /**
402 * Render the currently defined scene in pvr2_scene
403 */
404 void pvr2_scene_render( render_buffer_t buffer )
405 {
406 /* Scene setup */
407 struct timeval start_tv, tex_tv, end_tv;
409 gettimeofday(&start_tv, NULL);
410 display_driver->set_render_target(buffer);
411 pvr2_check_palette_changed();
412 pvr2_scene_load_textures();
414 gettimeofday( &tex_tv, NULL );
415 uint32_t ms = (tex_tv.tv_sec - start_tv.tv_sec) * 1000 +
416 (tex_tv.tv_usec - start_tv.tv_usec)/1000;
417 DEBUG( "Scene setup in %dms", ms );
419 /* Setup view projection matrix */
420 glMatrixMode(GL_PROJECTION);
421 glLoadIdentity();
422 float nearz = pvr2_scene.bounds[4];
423 float farz = pvr2_scene.bounds[5];
424 if( nearz == farz ) {
425 farz*= 4.0;
426 }
427 glOrtho( 0, pvr2_scene.buffer_width, pvr2_scene.buffer_height, 0,
428 -farz, -nearz );
429 float alphaRef = ((float)(MMIO_READ(PVR2, RENDER_ALPHA_REF)&0xFF)+1)/256.0;
430 glAlphaFunc( GL_GEQUAL, alphaRef );
432 /* Clear the buffer (FIXME: May not want always want to do this) */
433 glDisable( GL_SCISSOR_TEST );
434 glDepthMask( GL_TRUE );
435 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
437 /* Setup vertex array pointers */
438 glVertexPointer(3, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].x);
439 glColorPointer(4, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].rgba[0]);
440 glTexCoordPointer(2, GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].u);
441 glSecondaryColorPointerEXT(3, GL_FLOAT, sizeof(struct vertex_struct), pvr2_scene.vertex_array[0].offset_rgba );
442 glFogCoordPointerEXT(GL_FLOAT, sizeof(struct vertex_struct), &pvr2_scene.vertex_array[0].offset_rgba[3] );
443 glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT);
444 glFogi(GL_FOG_MODE, GL_LINEAR);
445 glFogf(GL_FOG_START, 0.0);
446 glFogf(GL_FOG_END, 1.0);
447 /* Turn on the shaders (if available) */
448 glsl_enable_shaders(TRUE);
450 /* Render the background */
451 gl_render_bkgnd( pvr2_scene.bkgnd_poly );
453 glEnable( GL_SCISSOR_TEST );
455 /* Process the segment list */
456 struct tile_segment *segment = pvr2_scene.segment_list;
457 do {
458 int tilex = SEGMENT_X(segment->control);
459 int tiley = SEGMENT_Y(segment->control);
461 uint32_t tile_bounds[4] = { tilex << 5, (tilex+1)<<5, tiley<<5, (tiley+1)<<5 };
462 if( !clip_tile_bounds(tile_bounds, pvr2_scene.bounds) ) {
463 continue; // fully clipped, skip tile
464 }
466 /* Clip to the visible part of the tile */
467 glScissor( tile_bounds[0], pvr2_scene.buffer_height-tile_bounds[3],
468 tile_bounds[1]-tile_bounds[0], tile_bounds[3] - tile_bounds[2] );
469 if( display_driver->capabilities.stencil_bits != 0 &&
470 IS_TILE_PTR(segment->opaquemod_ptr) &&
471 !IS_EMPTY_TILE_LIST(segment->opaquemod_ptr) ) {
472 /* Don't do this unless there's actually some shadow polygons */
474 /* Use colormask instead of drawbuffer for simplicity */
475 glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
476 gl_render_tilelist_depthonly(segment->opaque_ptr);
477 gl_render_modifier_tilelist(segment->opaquemod_ptr);
478 glClear( GL_DEPTH_BUFFER_BIT );
479 glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
480 }
481 gl_render_tilelist(segment->opaque_ptr);
482 if( IS_TILE_PTR(segment->punchout_ptr) ) {
483 glEnable(GL_ALPHA_TEST );
484 render_autosort_tile(segment->punchout_ptr, RENDER_NORMAL );
485 glDisable(GL_ALPHA_TEST );
486 }
487 glDisable( GL_STENCIL_TEST );
489 if( IS_TILE_PTR(segment->trans_ptr) ) {
490 if( pvr2_scene.sort_mode == SORT_NEVER ||
491 (pvr2_scene.sort_mode == SORT_TILEFLAG && (segment->control&SEGMENT_SORT_TRANS))) {
492 gl_render_tilelist(segment->trans_ptr);
493 } else {
494 render_autosort_tile(segment->trans_ptr, RENDER_NORMAL );
495 }
496 }
497 } while( !IS_LAST_SEGMENT(segment++) );
498 glDisable( GL_SCISSOR_TEST );
500 glsl_enable_shaders(FALSE);
502 gettimeofday( &end_tv, NULL );
503 ms = (end_tv.tv_sec - tex_tv.tv_sec) * 1000 +
504 (end_tv.tv_usec - tex_tv.tv_usec)/1000;
505 DEBUG( "Scene render in %dms", ms );
506 }
.