frame rate independence
[carveJwlIkooP6JGAAIwe30JlM.git] / main.c
1 /*
2 * =============================================================================
3 *
4 * Copyright . . . -----, ,----- ,---. .---.
5 * 2021-2022 |\ /| | / | | | | /|
6 * | \ / | +-- / +----- +---' | / |
7 * | \ / | | / | | \ | / |
8 * | \/ | | / | | \ | / |
9 * ' ' '--' [] '----- '----- ' ' '---' SOFTWARE
10 *
11 * =============================================================================
12 *
13 * register: shader register & init scheduling
14 * init: initialization
15 * update: logic
16 * render: graphics
17 * free: resource free
18 *
19 */
20
21 #define SR_NETWORKED
22 #define VG_3D
23 #include "common.h"
24 #include "steam.h"
25 #include "render.h"
26 #include "audio.h"
27 #include "world.h"
28 #include "player.h"
29
30 static int cl_ui = 1;
31
32 int main( int argc, char *argv[] )
33 {
34 vg_enter( argc, argv, "Voyager Game Engine" );
35 }
36
37 static void highscores_save_at_exit(void*_)
38 {
39 highscores_serialize_all();
40 highscores_free();
41 }
42
43 void vg_preload(void)
44 {
45 vg_convar_push( (struct vg_convar){
46 .name = "cl_ui",
47 .data = &cl_ui,
48 .data_type = k_convar_dtype_i32,
49 .opt_i32 = { .min=0, .max=1, .clamp=1 },
50 .persistent = 1
51 });
52
53 vg_info(" Copyright . . . -----, ,----- ,---. .---. \n" );
54 vg_info(" 2021-2022 |\\ /| | / | | | | /| \n" );
55 vg_info(" | \\ / | +-- / +----- +---' | / | \n" );
56 vg_info(" | \\ / | | / | | \\ | / | \n" );
57 vg_info(" | \\/ | | / | | \\ | / | \n" );
58 vg_info(" ' ' '--' [] '----- '----- ' ' '---' "
59 "SOFTWARE\n" );
60
61 highscores_init( 2000, 50 );
62 vg_loader_highwater( NULL, highscores_save_at_exit, NULL );
63
64 vg_sleep_ms(200);
65
66 steam_init();
67 vg_loader_highwater( NULL, steam_end, NULL );
68
69 vg_loader_highwater( network_init, network_end, NULL );
70 }
71
72 void vg_load(void)
73 {
74 vg_loader_highwater( render_init, render_free, NULL );
75 vg_loader_highwater( world_init, world_free, NULL );
76 vg_loader_highwater( player_init, NULL, NULL );
77
78 if( !vg_bake_shaders() )
79 vg_fatal_exit_loop( "Did not load all shaders" );
80
81 vg_loader_highwater( audio_init, audio_free, NULL );
82
83 /* FInal step */
84 world_load();
85 vg_console_load_autos();
86 }
87
88 static void vg_start(void)
89 {
90 player_load_model( "ch_new" );
91 reset_player( 1, (const char *[]){ "start" } );
92 }
93
94 static void draw_origin_axis(void)
95 {
96 vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 1.0f, 0.0f, 0.0f }, 0xffff0000 );
97 vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 0.0f, 1.0f, 0.0f }, 0xff00ff00 );
98 vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 0.0f, 0.0f, 1.0f }, 0xff0000ff );
99 }
100
101 void vg_update( int loaded )
102 {
103 steam_update();
104
105 if( loaded )
106 {
107 draw_origin_axis();
108 network_update();
109 player_update_pre();
110 world_update( player.phys.rb.co );
111 }
112 }
113
114 static void vg_update_fixed( int loaded )
115 {
116 if( loaded )
117 {
118 player_update_fixed();
119 }
120 }
121
122 static void vg_update_post( int loaded )
123 {
124 if( loaded )
125 {
126 player_update_post();
127 }
128 }
129
130 static void vg_framebuffer_resize( int w, int h )
131 {
132 render_fb_resize();
133 gate_fb_resize();
134 water_fb_resize();
135 }
136
137 static void render_main_game(void)
138 {
139 m4x4f world_4x4;
140 m4x3_expand( player.camera_inverse, world_4x4 );
141
142 gpipeline.fov = freecam? 60.0f: 125.0f; /* 120 */
143 m4x4_projection( vg.pv, gpipeline.fov,
144 (float)vg.window_x / (float)vg.window_y,
145 0.02f, 2100.0f );
146
147 m4x4_mul( vg.pv, world_4x4, vg.pv );
148 glEnable( GL_DEPTH_TEST );
149
150 /*
151 * Draw world
152 */
153
154 int draw_solid = player.is_dead | freecam;
155
156 render_world( vg.pv, player.camera );
157 if( draw_solid )
158 draw_player();
159 render_water_texture( player.camera );
160
161 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
162 render_water_surface( vg.pv, player.camera );
163
164 render_world_gates( vg.pv, player.phys.rb.co, player.camera );
165
166 /* Copy the RGB of what we have into the background buffer */
167 glBindFramebuffer( GL_READ_FRAMEBUFFER, 0 );
168 glBindFramebuffer( GL_DRAW_FRAMEBUFFER, gpipeline.fb_background );
169 glBlitFramebuffer( 0,0, vg.window_x, vg.window_y,
170 0,0, vg.window_x, vg.window_y,
171 GL_COLOR_BUFFER_BIT,
172 GL_LINEAR );
173
174 /* Clear out the colour buffer, but keep depth */
175 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
176 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
177
178 if( !player.is_dead )
179 glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
180 else
181 glClear( GL_COLOR_BUFFER_BIT );
182
183 if( !draw_solid )
184 {
185 m4x4_projection( vg.pv, gpipeline.fov,
186 (float)vg.window_x / (float)vg.window_y,
187 0.01f, 600.0f );
188 m4x4_mul( vg.pv, world_4x4, vg.pv );
189 draw_player();
190 }
191
192 /* Draw back in the background
193 *
194 * TODO: need to disable alpha write in the terrain shader so this works
195 * again.
196 */
197 glEnable(GL_BLEND);
198 glDisable(GL_DEPTH_TEST);
199 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
200 glBlendEquation(GL_FUNC_ADD);
201
202 shader_blit_use();
203 shader_blit_uTexMain( 0 );
204 glActiveTexture(GL_TEXTURE0);
205 glBindTexture( GL_TEXTURE_2D, gpipeline.rgb_background );
206
207 render_fsquad();
208 }
209
210 void vg_render(void)
211 {
212 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
213 glViewport( 0,0, vg.window_x, vg.window_y );
214 glDisable( GL_DEPTH_TEST );
215
216 glClearColor( 0.11f, 0.35f, 0.37f, 1.0f );
217 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
218
219 render_main_game();
220
221
222 /* Other shite */
223 glDisable(GL_BLEND);
224 glDisable( GL_DEPTH_TEST );
225 vg_lines_drawall( (float *)vg.pv );
226 glViewport( 0,0, vg.window_x, vg.window_y );
227 }
228
229 void vg_ui(void)
230 {
231 #if 0
232 if( lightedit )
233 {
234 ui_global_ctx.cursor[0] = 10;
235 ui_global_ctx.cursor[1] = 10;
236 ui_global_ctx.cursor[2] = 200;
237 ui_global_ctx.cursor[3] = 20;
238
239 struct ub_world_lighting *wl = &gpipeline.ub_world_lighting;
240 struct ui_slider_vector
241 s5 = { .min=0.0f, .max=2.0f, .len=3, .data=wl->g_ambient_colour };
242
243 struct ui_slider
244 s8 = { .min=0.0f, .max=2.0f, .data = &gpipeline.shadow_spread },
245 s9 = { .min=0.0f, .max=25.0f, .data = &gpipeline.shadow_length };
246
247 for( int i=0; i<3; i++ )
248 run_light_widget( &gpipeline.widgets[i] );
249
250 gui_text( ui_global_ctx.cursor, "Ambient", 1, 0 );
251 ui_global_ctx.cursor[1] += 16;
252 ui_slider_vector( &ui_global_ctx, &s5 );
253
254 gui_text( ui_global_ctx.cursor, "Shadows", 1, 0 );
255 ui_global_ctx.cursor[1] += 16;
256 ui_slider( &ui_global_ctx, &s8 );
257 ui_slider( &ui_global_ctx, &s9 );
258
259 gui_text( ui_global_ctx.cursor, "Misc", 1, 0 );
260 ui_global_ctx.cursor[1] += 16;
261 struct ui_checkbox c1 = {.data = &wl->g_light_preview};
262 ui_checkbox( &ui_global_ctx, &c1 );
263
264 render_update_lighting_ub();
265 }
266 #endif
267
268 //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
269 if( cl_ui )
270 {
271 render_world_routes_ui();
272 }
273 //glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
274
275 audio_debug_soundscapes();
276 }
277
278 #if 0
279 static void run_light_widget( struct light_widget *lw )
280 {
281 struct ui_checkbox c1 = { .data=&lw->enabled };
282
283 ui_checkbox( &ui_global_ctx, &c1 );
284
285 if( lw->enabled )
286 {
287 struct ui_slider_vector
288 colour = { .min=0.0f, .max=2.0f, .len=3, .data=lw->colour },
289 dir = { .min=-VG_PIf, .max=VG_PIf, .len=2, .data=lw->dir };
290
291 ui_slider_vector( &ui_global_ctx, &colour );
292 ui_global_ctx.cursor[1] += 4;
293 ui_slider_vector( &ui_global_ctx, &dir );
294 }
295 }
296 #endif
297
298 static void run_debug_info(void)
299 {
300 char buf[40];
301
302 snprintf( buf, 40, "%.2fm/s", v3_length( player.phys.rb.v ) );
303 gui_text( (ui_px [2]){ 0, 0 }, buf, 1, k_text_align_left );
304
305 snprintf( buf, 40, "%.2f %.2f %.2f m/s",
306 player.phys.a[0], player.phys.a[1], player.phys.a[2] );
307 gui_text( (ui_px [2]){ 0, 20 }, buf, 1, k_text_align_left );
308
309 snprintf( buf, 40, "pos %.2f %.2f %.2f",
310 player.phys.rb.co[0], player.phys.rb.co[1], player.phys.rb.co[2] );
311 gui_text( (ui_px [2]){ 0, 40 }, buf, 1, k_text_align_left );
312
313 if( vg.gamepad_ready )
314 {
315 for( int i=0; i<6; i++ )
316 {
317 snprintf( buf, 40, "%.2f", vg.gamepad.axes[i] );
318 gui_text( (ui_px [2]){ 0, (i+3)*20 }, buf, 1, k_text_align_left );
319 }
320 }
321 else
322 {
323 gui_text( (ui_px [2]){ 0, 60 },
324 "Gamepad not ready", 1, k_text_align_left );
325 }
326 }