naev 0.12.6
render.c
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
4#include "render.h"
5
6#include "array.h"
7#include "conf.h"
8#include "gui.h"
9#include "hook.h"
10#include "map_overlay.h"
11#include "menu.h"
12#include "naev.h"
13#include "nlua_canvas.h"
14#include "ntracing.h"
15#include "opengl.h"
16#include "pause.h"
17#include "player.h"
18#include "space.h"
19#include "spfx.h"
20#include "toolkit.h"
21#include "weapon.h"
22
23#include "nlua_shader.h"
24
30typedef struct PPShader_s {
31 unsigned int id; /*< Global id (greater than 0). */
33 unsigned int flags;
34 double dt;
35 GLuint program;
36 /* Shared uniforms. */
37 GLint ClipSpaceFromLocal;
38 GLint u_time;
39 /* Fragment Shader. */
40 GLint MainTex;
41 GLint love_ScreenSize;
42 /* Vertex shader. */
43 GLint VertexPosition;
44 GLint VertexTexCoord;
45 /* Textures. */
46 LuaTexture_t *tex;
47} PPShader;
48
49static unsigned int pp_shaders_id = 0;
50static PPShader *pp_shaders_list[PP_LAYER_MAX];
52
53static LuaShader_t gamma_correction_shader;
54static int pp_gamma_correction = 0;
55
59static void render_fbo( double dt, GLuint fbo, GLuint tex, PPShader *shader )
60{
61 glBindFramebuffer( GL_FRAMEBUFFER, fbo );
62
63 glUseProgram( shader->program );
64
65 /* Screen size. */
66 if ( shader->love_ScreenSize >= 0 )
67 /* TODO don't have to upload this every frame, only when resized... */
68 glUniform4f( shader->love_ScreenSize, SCREEN_W, SCREEN_H, 1., 0. );
69
70 /* Time stuff. */
71 if ( shader->u_time >= 0 ) {
72 shader->dt += dt;
73 glUniform1f( shader->u_time, shader->dt );
74 }
75
76 /* Set up stuff .*/
77 glEnableVertexAttribArray( shader->VertexPosition );
78 gl_vboActivateAttribOffset( gl_squareVBO, shader->VertexPosition, 0, 2,
79 GL_FLOAT, 0 );
80 if ( shader->VertexTexCoord >= 0 ) {
81 glEnableVertexAttribArray( shader->VertexTexCoord );
82 gl_vboActivateAttribOffset( gl_squareVBO, shader->VertexTexCoord, 0, 2,
83 GL_FLOAT, 0 );
84 }
85
86 /* Set the texture(s). */
87 glBindTexture( GL_TEXTURE_2D, tex );
88 glUniform1i( shader->MainTex, 0 );
89 for ( int i = 0; i < array_size( shader->tex ); i++ ) {
90 LuaTexture_t *t = &shader->tex[i];
91 glActiveTexture( t->active );
92 glBindTexture( GL_TEXTURE_2D, t->texid );
93 glUniform1i( t->uniform, t->value );
94 }
95 glActiveTexture( GL_TEXTURE0 );
96
97 /* Set shader uniforms. */
98 const mat4 ortho = mat4_ortho( 0., 1., 1., 0., 1., -1. );
99 gl_uniformMat4( shader->ClipSpaceFromLocal, &ortho );
100
101 /* Draw. */
102 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
103
104 /* Clear state. */
105 glDisableVertexAttribArray( shader->VertexPosition );
106 if ( shader->VertexTexCoord >= 0 )
107 glDisableVertexAttribArray( shader->VertexTexCoord );
108 glUseProgram( 0 );
109}
110
114static void render_fbo_list( double dt, PPShader *list, int *current, int done )
115{
116 PPShader *pplast;
117 int i, cur, next;
118 cur = *current;
119
120 /* Render all except the last post-process shader. */
121 for ( i = 0; i < array_size( list ) - 1; i++ ) {
122 PPShader *pp = &list[i];
123 next = 1 - cur;
124 /* Render cur to next. */
125 render_fbo( dt, gl_screen.fbo[next], gl_screen.fbo_tex[cur], pp );
126 cur = next;
127 }
128
129 /* Final render is to the screen. */
130 pplast = &list[i];
131 if ( done ) {
132 gl_screen.current_fbo = 0;
133 /* Do the render. */
134 render_fbo( dt, gl_screen.current_fbo, gl_screen.fbo_tex[cur], pplast );
135 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.current_fbo );
136 return;
137 }
138
139 /* Draw the last shader. */
140 next = 1 - cur;
141 render_fbo( dt, gl_screen.fbo[next], gl_screen.fbo_tex[cur], pplast );
142 cur = next;
143
144 /* Set the framebuffer again. */
145 gl_screen.current_fbo = gl_screen.fbo[cur];
146 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.current_fbo );
147
148 /* Set the new current framebuffer. */
149 *current = cur;
150}
151
170void render_all( double game_dt, double real_dt )
171{
172 NTracingZone( _ctx, 1 );
173
174 double dt;
175 int pp_core, pp_final, pp_gui, pp_game;
176 int cur = 0;
177
178 /* See what post-processing is up. */
179 pp_game = ( array_size( pp_shaders_list[PP_LAYER_GAME] ) > 0 );
180 pp_gui = ( array_size( pp_shaders_list[PP_LAYER_GUI] ) > 0 );
181 pp_final = ( array_size( pp_shaders_list[PP_LAYER_FINAL] ) > 0 );
182 pp_core = ( array_size( pp_shaders_list[PP_LAYER_CORE] ) > 0 );
183
184 /* Use pitch black for main screens. */
185 glClearColor( 0., 0., 0., 1. );
186
187 /* Case we have a post-processing shader we use the framebuffers. */
188 if ( pp_game || pp_gui || pp_final || pp_core ) {
189 /* Clear main screen. */
190 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
191 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
192
193 /* Clear back buffer. */
194 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.fbo[1] );
195 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
196
197 /* Set to front buffer. */
198 gl_screen.current_fbo = gl_screen.fbo[cur];
199 } else
200 gl_screen.current_fbo = 0;
201
202 /* Bind and clear new drawing area. */
203 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.current_fbo );
204 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
205 glClearColor( 0., 0., 0., 0. );
206
207 dt = ( paused ) ? 0. : game_dt;
208
209 /* Set up the default viewport. */
211
212 /* Background stuff */
213 space_render( real_dt ); /* Nebula looks really weird otherwise. This also
214 sets up the lighting from the background. */
215 render_reset(); /* space_render can use a lua background. */
216 NTracingZoneName( _ctx_renderbg, "hooks[renderbg]", 1 );
217 hooks_run( "renderbg" );
218 NTracingZoneEnd( _ctx_renderbg );
219 render_reset();
220 spobs_render();
221 spfx_render( SPFX_LAYER_BACK, dt );
222 weapons_render( WEAPON_LAYER_BG, dt );
223 /* Middle stuff */
226 spfx_render( SPFX_LAYER_MIDDLE, dt );
227 weapons_render( WEAPON_LAYER_FG, dt );
228 /* Foreground stuff */
229 player_render( dt );
230 spfx_render( SPFX_LAYER_FRONT, dt );
232 render_reset(); /* space_render can use a lua background. */
233 gui_renderReticles( dt );
235 NTracingZoneName( _ctx_renderfg, "hooks[renderfg]", 1 );
236 hooks_run( "renderfg" );
237 NTracingZoneEnd( _ctx_renderfg );
238 render_reset();
239
240 /* Process game stuff only. */
241 if ( pp_game ) {
242 NTracingZoneName( _ctx_pp_game, "postprocess_shader[game]", 1 );
243 render_fbo_list( dt, pp_shaders_list[PP_LAYER_GAME], &cur,
244 !( pp_core || pp_final || pp_gui ) );
245 NTracingZoneEnd( _ctx_pp_game );
246 }
247
248 /* GUi stuff. */
249 gui_render( dt );
250 render_reset();
251
252 if ( pp_gui ) {
253 NTracingZoneName( _ctx_pp_gui, "postprocess_shader[gui]", 1 );
254 render_fbo_list( dt, pp_shaders_list[PP_LAYER_GUI], &cur,
255 !( pp_core || pp_final ) );
256 NTracingZoneEnd( _ctx_pp_gui );
257 }
258
259 /* We set the to fullscreen, ignoring the GUI modifications. */
260 gl_viewport( 0, 0, gl_screen.nw, gl_screen.nh );
261
262 /* Top stuff. */
263 ovr_render( real_dt ); /* Using real_dt is sort of a hack for now. */
264 NTracingZoneName( _ctx_rendertop, "hooks[rendertop]", 1 );
265 hooks_run( "rendertop" );
266 NTracingZoneEnd( _ctx_rendertop );
267 render_reset();
268 fps_display( real_dt ); /* Exception using real_dt. */
269 if ( !menu_open )
271
272 /* Final post-processing. */
273 if ( pp_final ) {
274 NTracingZoneName( _ctx_pp_final, "postprocess_shader[final]", 1 );
275 render_fbo_list( dt, pp_shaders_list[PP_LAYER_FINAL], &cur,
276 !( pp_core ) );
277 NTracingZoneEnd( _ctx_pp_final );
278 }
279
280 if ( menu_open )
282
283 /* Final post-processing. */
284 if ( pp_core ) {
285 NTracingZoneName( _ctx_pp_core, "postprocess_shader[core]", 1 );
286 render_fbo_list( dt, pp_shaders_list[PP_LAYER_CORE], &cur, 1 );
287 NTracingZoneEnd( _ctx_pp_core );
288 }
289
290 /* check error every loop */
291 gl_checkErr();
292
293 NTracingZoneEnd( _ctx );
294}
295
299static int ppshader_compare( const void *a, const void *b )
300{
301 const PPShader *ppa = a;
302 const PPShader *ppb = b;
303 if ( ppa->priority > ppb->priority )
304 return +1;
305 if ( ppa->priority < ppb->priority )
306 return -1;
307 return 0;
308}
309
319unsigned int render_postprocessAdd( LuaShader_t *shader, int layer,
320 int priority, unsigned int flags )
321{
322 PPShader *pp, **pp_shaders;
323 unsigned int id;
324
325 /* Select the layer. */
326 if ( layer < 0 || layer >= PP_LAYER_MAX ) {
327 WARN( _( "Unknown post-processing shader layer '%d'!" ), layer );
328 return 0;
329 }
330 pp_shaders = &pp_shaders_list[layer];
331
332 if ( *pp_shaders == NULL )
333 *pp_shaders = array_create( PPShader );
334 pp = &array_grow( pp_shaders );
335 id = ++pp_shaders_id;
336 pp->id = id;
337 pp->priority = priority;
338 pp->flags = flags;
339 pp->program = shader->program;
340 pp->ClipSpaceFromLocal = shader->ClipSpaceFromLocal;
341 pp->MainTex = shader->MainTex;
342 pp->VertexPosition = shader->VertexPosition;
343 pp->VertexTexCoord = shader->VertexTexCoord;
344 if ( shader->tex != NULL )
345 pp->tex = array_copy( LuaTexture_t, shader->tex );
346 else
347 pp->tex = NULL;
348 /* Special uniforms. */
349 pp->u_time = glGetUniformLocation( pp->program, "u_time" );
350 pp->love_ScreenSize = glGetUniformLocation( pp->program, "love_ScreenSize" );
351 pp->dt = 0.;
352
353 /* Resort n case stuff is weird. */
354 qsort( *pp_shaders, array_size( *pp_shaders ), sizeof( PPShader ),
355 ppshader_compare );
356
357 gl_checkErr();
358
359 return id;
360}
361
368int render_postprocessRm( unsigned int id )
369{
370 int j;
371 int found = -1;
372 for ( j = 0; j < PP_LAYER_MAX; j++ ) {
373 PPShader *pp_shaders = pp_shaders_list[j];
374 for ( int i = 0; i < array_size( pp_shaders ); i++ ) {
375 const PPShader *pp = &pp_shaders[i];
376 if ( pp->id != id )
377 continue;
378 found = i;
379 break;
380 }
381 if ( found >= 0 )
382 break;
383 }
384 if ( found == -1 ) {
385 /* Don't warn since they can get cleaned up twice: once from
386 * postprocessCleanup, once from Lua gc. */
387 // WARN(_("Trying to remove non-existant post-processing shader with id
388 // '%d'!"), id);
389 return -1;
390 }
391
392 /* No need to resort. */
393 array_erase( &pp_shaders_list[j], &pp_shaders_list[j][found],
394 &pp_shaders_list[j][found + 1] );
395 return 0;
396}
397
401void render_postprocessCleanup( void )
402{
403 for ( int j = 0; j < PP_LAYER_MAX; j++ ) {
404 PPShader *pp_shaders = pp_shaders_list[j];
405 for ( int i = array_size( pp_shaders ) - 1; i >= 0; i-- ) {
406 const PPShader *pp = &pp_shaders[i];
407 if ( pp->flags & PP_SHADER_PERMANENT )
408 continue;
409 array_erase( &pp_shaders_list[j], &pp_shaders_list[j][i],
410 &pp_shaders_list[j][i + 1] );
411 }
412 }
413 /* No need to resort. */
414}
415
419void render_init( void )
420{
421 LuaShader_t *s = &gamma_correction_shader;
422 memset( s, 0, sizeof( LuaShader_t ) );
423 s->program = shaders.gamma_correction.program;
424 s->VertexPosition = shaders.gamma_correction.VertexPosition;
425 s->ClipSpaceFromLocal = shaders.gamma_correction.ClipSpaceFromLocal;
426 s->MainTex = shaders.gamma_correction.MainTex;
427
428 /* Initialize the gamma. */
429 render_setGamma( conf.gamma_correction );
430}
431
435void render_exit( void )
436{
437 for ( int i = 0; i < PP_LAYER_MAX; i++ ) {
438 array_free( pp_shaders_list[i] );
439 pp_shaders_list[i] = NULL;
440 }
441}
442
446void render_setGamma( double gamma )
447{
448 if ( pp_gamma_correction > 0 ) {
449 render_postprocessRm( pp_gamma_correction );
450 pp_gamma_correction = 0;
451 }
452
453 /* Ignore small gamma. */
454 if ( fabs( gamma - 1. ) < 1e-3 )
455 return;
456
457 /* Set gamma and upload. */
458 glUseProgram( shaders.gamma_correction.program );
459 glUniform1f( shaders.gamma_correction.gamma, gamma );
460 glUseProgram( 0 );
461 pp_gamma_correction = render_postprocessAdd(
462 &gamma_correction_shader, PP_LAYER_CORE, 98, PP_SHADER_PERMANENT );
463}
464
465static int needsReset = 0;
469void render_reset( void )
470{
471 if ( !needsReset )
472 return;
473 needsReset = 0;
474
475 glBlendEquation( GL_FUNC_ADD );
476 glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
477 GL_ONE_MINUS_SRC_ALPHA );
479 canvas_reset();
480}
481
486void render_needsReset( void )
487{
488 needsReset = 1;
489}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_copy(basic_type, ptr_array)
Returns a shallow copy of the input array.
Definition array.h:230
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
void gui_renderReticles(double dt)
Renders the gui targeting reticles.
Definition gui.c:732
void gui_render(double dt)
Renders the player's GUI.
Definition gui.c:755
int hooks_run(const char *stack)
Runs all the hooks of stack.
Definition hook.c:1049
mat4 mat4_ortho(double left, double right, double bottom, double top, double nearVal, double farVal)
Creates an orthographic projection matrix.
Definition mat4.c:347
Handles the important game menus.
void fps_display(double dt)
Displays FPS on the screen.
Definition naev.c:955
static double game_dt
Definition naev.c:110
static double real_dt
Definition naev.c:111
Header file with generic functions and naev-specifics.
void gl_defViewport(void)
Resets viewport to default.
Definition opengl.c:754
void gl_viewport(int x, int y, int w, int h)
Sets the opengl viewport.
Definition opengl.c:715
glInfo gl_screen
Definition opengl.c:47
void gl_unclipRect(void)
Clears the 2d clipping planes.
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
Definition opengl_vbo.c:224
int paused
Definition pause.c:18
void pilots_renderOverlay(void)
Renders all the pilots overlays.
Definition pilot.c:4314
void pilots_render(void)
Renders all the pilots.
Definition pilot.c:4293
void player_render(double dt)
Renders the player.
Definition player.c:1055
void player_renderUnderlay(double dt)
Renders the player underlay.
Definition player.c:1099
void space_render(const double dt)
Renders the system.
Definition space.c:3666
void space_renderOverlay(const double dt)
Renders the system overlay.
Definition space.c:3686
void spobs_render(void)
Renders the current systems' spobs.
Definition space.c:3709
void spfx_render(int layer, double dt)
Renders the entire spfx layer.
Definition spfx.c:1178
Post-Processing Shader.
Definition render.c:30
unsigned int flags
Definition render.c:33
GLuint program
Definition render.c:35
double dt
Definition render.c:34
GLint u_time
Definition render.c:38
int priority
Definition render.c:32
Definition mat4.h:12
void toolkit_render(double dt)
Renders the windows.
Definition toolkit.c:1625
void weapons_render(const WeaponLayer layer, double dt)
Renders all the weapons in a layer.
Definition weapon.c:762