well yeah i guess
[carveJwlIkooP6JGAAIwe30JlM.git] / render.h
1 /*
2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #include "common.h"
6 #include "model.h"
7
8 #include "shaders/blit.h"
9 #include "shaders/standard.h"
10 #include "shaders/vblend.h"
11
12 VG_STATIC void render_water_texture( m4x3f camera );
13 VG_STATIC void render_water_surface( m4x4f pv, m4x3f camera );
14 VG_STATIC void render_world( m4x4f projection, m4x3f camera );
15 VG_STATIC void shader_link_standard_ub( GLuint shader, int texture_id );
16 VG_STATIC void render_world_depth( m4x4f projection, m4x3f camera );
17
18 #ifndef RENDER_H
19 #define RENDER_H
20
21 struct framebuffer
22 {
23 GLuint fb, colour, rb;
24 int div;
25 GLuint format;
26
27 int allocated;
28 };
29
30 VG_STATIC struct pipeline
31 {
32 float fov;
33 glmesh fsquad;
34
35 GLuint fb_background,
36 rgb_background;
37
38 /* STD140 */
39 struct ub_world_lighting
40 {
41 /* v3f (padded) */
42 v4f g_light_colours[3],
43 g_light_directions[3],
44 g_ambient_colour;
45
46 v4f g_water_plane,
47 g_depth_bounds;
48
49 float g_water_fog;
50 int g_light_count;
51 int g_light_preview;
52 }
53 ub_world_lighting;
54
55 struct light_widget
56 {
57 int enabled;
58 v2f dir;
59 v3f colour;
60 }
61 widgets[3];
62
63 float shadow_spread, shadow_length;
64
65 GLuint fb_depthmap, rgb_depthmap;
66 GLuint ubo_world_lighting,
67 ubo_world;
68
69 int ready;
70 }
71 gpipeline =
72 {
73 .widgets =
74 {
75 {
76 .enabled = 1,
77 .colour = { 1.36f, 1.35f, 1.01f },
78 .dir = { 0.63f, -0.08f }
79 },
80 {
81 .enabled = 1,
82 .colour = { 0.33f, 0.56f, 0.64f },
83 .dir = { -2.60f, -0.13f }
84 },
85 {
86 .enabled = 1,
87 .colour = { 0.05f, 0.05f, 0.23f },
88 .dir = { 2.60f, -0.84f }
89 }
90 },
91 .shadow_spread = 0.65f,
92 .shadow_length = 9.50f,
93
94 .ub_world_lighting =
95 {
96 .g_ambient_colour = { 0.09f, 0.03f, 0.07f }
97 }
98 };
99
100 /*
101 * Matrix Projections
102 */
103 /*
104 * http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
105 */
106 VG_STATIC void plane_clip_projection( m4x4f mat, v4f plane )
107 {
108 v4f c =
109 {
110 (vg_signf(plane[0]) + mat[2][0]) / mat[0][0],
111 (vg_signf(plane[1]) + mat[2][1]) / mat[1][1],
112 -1.0f,
113 (1.0f + mat[2][2]) / mat[3][2]
114 };
115
116 v4_muls( plane, 2.0f / v4_dot(plane,c), c );
117
118 mat[0][2] = c[0];
119 mat[1][2] = c[1];
120 mat[2][2] = c[2] + 1.0f;
121 mat[3][2] = c[3];
122 }
123
124 VG_STATIC void pipeline_projection( m4x4f mat, float nearz, float farz )
125 {
126 m4x4_projection( mat,
127 gpipeline.fov,
128 (float)vg.window_x / (float)vg.window_y,
129 nearz, farz );
130 }
131
132 /*
133 * Shaders
134 */
135 VG_STATIC void shader_link_standard_ub( GLuint shader, int texture_id )
136 {
137 GLuint idx = glGetUniformBlockIndex( shader, "ub_world_lighting" );
138 glUniformBlockBinding( shader, idx, 0 );
139
140 glActiveTexture( GL_TEXTURE0 + texture_id );
141 glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_depthmap );
142 glUniform1i( glGetUniformLocation( shader, "g_world_depth" ), texture_id );
143 }
144
145 VG_STATIC void render_update_lighting_ub(void)
146 {
147 struct ub_world_lighting *winf = &gpipeline.ub_world_lighting;
148 int c = 0;
149
150 for( int i=0; i<3; i++ )
151 {
152 struct light_widget *lw = &gpipeline.widgets[i];
153
154 if( lw->enabled )
155 {
156 float pitch = lw->dir[0],
157 yaw = lw->dir[1],
158 xz = cosf( pitch );
159
160 v3_copy( (v3f){ xz*cosf(yaw), sinf(pitch), xz*sinf(yaw) },
161 winf->g_light_directions[c] );
162 v3_copy( lw->colour, winf->g_light_colours[c] );
163
164 c ++;
165 }
166 }
167
168 winf->g_light_count = c;
169 winf->g_light_directions[0][3] = gpipeline.shadow_length;
170 winf->g_light_colours[0][3] = gpipeline.shadow_spread;
171
172 glBindBuffer( GL_UNIFORM_BUFFER, gpipeline.ubo_world_lighting );
173 glBufferSubData( GL_UNIFORM_BUFFER, 0, sizeof(struct ub_world_lighting),
174 &gpipeline.ub_world_lighting );
175 }
176
177 /*
178 * Framebuffers
179 */
180
181 VG_STATIC void fb_use( struct framebuffer *fb )
182 {
183 if( !fb )
184 {
185 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
186 glViewport( 0, 0, vg.window_x, vg.window_y );
187 }
188 else
189 {
190 glBindFramebuffer( GL_FRAMEBUFFER, fb->fb );
191 glViewport( 0, 0, vg.window_x / fb->div, vg.window_y / fb->div );
192 }
193 }
194
195 VG_STATIC void fb_init( struct framebuffer *fb )
196 {
197 i32 ix = vg.window_x / fb->div,
198 iy = vg.window_y / fb->div;
199
200 glGenFramebuffers( 1, &fb->fb );
201 glBindFramebuffer( GL_FRAMEBUFFER, fb->fb );
202
203 glGenTextures( 1, &fb->colour );
204 glBindTexture( GL_TEXTURE_2D, fb->colour );
205 glTexImage2D( GL_TEXTURE_2D, 0, fb->format, ix, iy,
206 0, fb->format, GL_UNSIGNED_BYTE, NULL);
207
208 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
209 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
210 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
211 GL_TEXTURE_2D, fb->colour, 0);
212
213 glGenRenderbuffers( 1, &fb->rb );
214 glBindRenderbuffer( GL_RENDERBUFFER, fb->rb );
215 glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, ix, iy );
216
217 glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
218 GL_RENDERBUFFER, fb->rb );
219 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
220
221 VG_CHECK_GL_ERR();
222 fb->allocated = 1;
223 }
224
225 VG_STATIC void fb_free( struct framebuffer *fb )
226 {
227 glDeleteTextures( 1, &fb->colour );
228 glDeleteFramebuffers( 1, &fb->fb );
229 }
230
231 VG_STATIC void fb_bindtex( struct framebuffer *fb, int texture )
232 {
233 glActiveTexture( GL_TEXTURE0 + texture );
234 glBindTexture( GL_TEXTURE_2D, fb->colour );
235 }
236
237 VG_STATIC void fb_resize( struct framebuffer *fb )
238 {
239 if( !fb->allocated )
240 return;
241
242 i32 ix = vg.window_x / fb->div,
243 iy = vg.window_y / fb->div;
244
245 glBindTexture( GL_TEXTURE_2D, fb->colour );
246 glTexImage2D( GL_TEXTURE_2D, 0, fb->format, ix, iy, 0,
247 fb->format, GL_UNSIGNED_BYTE, NULL );
248
249 glBindRenderbuffer( GL_RENDERBUFFER, fb->rb );
250 glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, ix, iy );
251 }
252
253 VG_STATIC void render_fb_resize(void)
254 {
255 if( gpipeline.ready )
256 {
257 glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background );
258 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg.window_x, vg.window_y, 0,
259 GL_RGB, GL_UNSIGNED_BYTE, NULL );
260 }
261 }
262
263 /* used for drawing player onto */
264 VG_STATIC void render_init_temp_buffer(void)
265 {
266 vg_info( "[render] Allocate temporary framebuffer\n" );
267
268 glGenFramebuffers( 1, &gpipeline.fb_background );
269 glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_background );
270
271 glGenTextures( 1, &gpipeline.rgb_background );
272 glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background );
273 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, vg.window_x, vg.window_y,
274 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
275
276 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
277 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
278 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
279 GL_TEXTURE_2D,
280 gpipeline.rgb_background, 0);
281
282 VG_CHECK_GL_ERR();
283 }
284
285 /* used for drawing world depth from the top view, used in our water and
286 * lighting calculations */
287 VG_STATIC void render_init_depthmap_buffer(void)
288 {
289 vg_info( "[render] Allocate depth map buffer\n" );
290
291 glGenFramebuffers( 1, &gpipeline.fb_depthmap );
292 glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_depthmap );
293
294 glGenTextures( 1, &gpipeline.rgb_depthmap );
295 glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_depthmap );
296 glTexImage2D( GL_TEXTURE_2D, 0, GL_R32F, 1024, 1024, 0,
297 GL_RED, GL_FLOAT, NULL );
298 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
299 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
300 vg_tex2d_clamp();
301
302 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
303 GL_TEXTURE_2D,
304 gpipeline.rgb_depthmap, 0);
305
306 VG_CHECK_GL_ERR();
307 }
308
309 VG_STATIC void render_init_fs_quad(void)
310 {
311 vg_info( "[render] Allocate quad\n" );
312
313 float quad[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
314 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
315
316 glGenVertexArrays( 1, &gpipeline.fsquad.vao );
317 glGenBuffers( 1, &gpipeline.fsquad.vbo );
318 glBindVertexArray( gpipeline.fsquad.vao );
319 glBindBuffer( GL_ARRAY_BUFFER, gpipeline.fsquad.vbo );
320 glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
321 glBindVertexArray( gpipeline.fsquad.vao );
322 glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE,
323 sizeof(float)*2, (void*)0 );
324 glEnableVertexAttribArray( 0 );
325
326 VG_CHECK_GL_ERR();
327 }
328
329 VG_STATIC void render_init_uniform_buffers(void)
330 {
331 vg_info( "[render] Allocate uniform buffer\n" );
332
333 glGenBuffers( 1, &gpipeline.ubo_world_lighting );
334 glBindBuffer( GL_UNIFORM_BUFFER, gpipeline.ubo_world_lighting );
335 glBufferData( GL_UNIFORM_BUFFER, sizeof(struct ub_world_lighting),
336 NULL, GL_DYNAMIC_DRAW );
337
338 render_update_lighting_ub();
339 glBindBufferBase( GL_UNIFORM_BUFFER, 0, gpipeline.ubo_world_lighting );
340
341 VG_CHECK_GL_ERR();
342 }
343
344 VG_STATIC void render_init(void)
345 {
346 shader_blit_register();
347 shader_standard_register();
348 shader_vblend_register();
349
350 vg_acquire_thread_sync();
351 {
352 render_init_temp_buffer();
353 render_init_depthmap_buffer();
354 render_init_fs_quad();
355 render_init_uniform_buffers();
356
357 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
358 gpipeline.ready = 1;
359 }
360
361 vg_release_thread_sync();
362 }
363
364 /*
365 * Utility
366 */
367 VG_STATIC void render_fsquad(void)
368 {
369 glBindVertexArray( gpipeline.fsquad.vao );
370 glDrawArrays( GL_TRIANGLES, 0, 6 );
371 }
372
373 #endif /* RENDER_H */