stuff
[carveJwlIkooP6JGAAIwe30JlM.git] / world.h
1 #include "common.h"
2
3 static int ray_world( v3f pos, v3f dir, ray_hit *hit );
4
5 #ifndef WORLD_H
6 #define WORLD_H
7
8 #include "scene.h"
9 #include "terrain.h"
10 #include "render.h"
11 #include "water.h"
12 #include "rigidbody.h"
13 #include "gate.h"
14 #include "bvh.h"
15 #include "lighting.h"
16 #include "model.h"
17
18 #include "traffic.h" /*TODO: -> world_traffic.h */
19 #include "world_routes.h"
20 #include "world_sfd.h"
21 #include "world_audio.h"
22
23 #include "shaders/terrain.h"
24 #include "shaders/sky.h"
25 #include "shaders/planeinf.h"
26 #include "shaders/standard.h"
27 #include "shaders/vblend.h"
28 #include "shaders/gpos.h"
29 #include "shaders/fscolour.h"
30 #include "shaders/alphatest.h"
31
32 static struct gworld
33 {
34 /* gameplay */
35 struct respawn_point
36 {
37 v3f co;
38 v4f q;
39 char name[32];
40 }
41 spawns[32];
42 u32 spawn_count;
43
44 struct subworld_routes routes;
45 struct subworld_sfd sfd;
46
47 /* ...
48 struct subworld_spawns system_spawns;
49 struct subworld_physics system_physics;
50 */
51
52 /* Paths */
53 traffic_node traffic[128];
54 u32 traffic_count;
55
56 #if 0
57 traffic_driver van_man[6];
58 #endif
59
60 /* Physics */
61
62 /* Rendering & geometry */
63 scene geo, foliage;
64 rigidbody rb_geo;
65
66 /* TODO Maybe make this less hardcoded */
67 mdl_submesh sm_geo_std_oob, sm_geo_std, sm_geo_vb,
68 sm_foliage_main, sm_foliage_alphatest,
69 sm_graffiti, sm_subworld, sm_terrain;
70
71 glmesh skybox, skydome;
72 mdl_submesh dome_upper, dome_lower;
73
74 glmesh cars;
75 mdl_submesh car_holden;
76
77 rigidbody mr_ball;
78
79 /* Load time */
80
81 struct instance_cache
82 {
83 mdl_header *mdl;
84 u32 pstr_file;
85 }
86 * instance_cache;
87 u32 instance_cache_count,
88 instance_cache_cap;
89
90 v3f render_gate_pos;
91 int active_route_board;
92 }
93 world;
94
95 static struct subworld_routes *subworld_routes(void) { return &world.routes; }
96 static struct subworld_sfd *subworld_sfd(void) { return &world.sfd; }
97
98
99 vg_tex2d tex_terrain_colours = { .path = "textures/gradients.qoi",
100 .flags = VG_TEXTURE_CLAMP|VG_TEXTURE_NEAREST };
101
102 vg_tex2d tex_terrain_noise = { .path = "textures/garbage.qoi",
103 .flags = VG_TEXTURE_NEAREST };
104
105 vg_tex2d tex_alphatest = { .path = "textures/alphatest.qoi",
106 .flags = VG_TEXTURE_NEAREST };
107
108 vg_tex2d tex_graffiti = { .path = "textures/graffitibox.qoi",
109 .flags = VG_TEXTURE_NEAREST };
110
111 static void ray_world_get_tri( ray_hit *hit, v3f tri[3] )
112 {
113 for( int i=0; i<3; i++ )
114 v3_copy( world.geo.verts[ hit->tri[i] ].co, tri[i] );
115 }
116
117 static int ray_world( v3f pos, v3f dir, ray_hit *hit )
118 {
119 return scene_raycast( &world.geo, pos, dir, hit );
120 }
121
122 static int ray_hit_is_terrain( ray_hit *hit )
123 {
124 u32 valid_start = 0,
125 valid_end = world.sm_terrain.vertex_count;
126
127 return (hit->tri[0] >= valid_start) &&
128 (hit->tri[0] < valid_end);
129 }
130
131 static int ray_hit_is_ramp( ray_hit *hit )
132 {
133 u32 valid_start = world.sm_geo_std.vertex_start,
134 valid_end = world.sm_geo_vb.vertex_start;
135
136 return (hit->tri[0] >= valid_start) &&
137 (hit->tri[0] < valid_end);
138 }
139
140 static void world_register(void)
141 {
142 shader_terrain_register();
143 shader_sky_register();
144 shader_planeinf_register();
145 shader_gpos_register();
146 shader_fscolour_register();
147 shader_alphatest_register();
148
149 world_routes_register();
150 world_sfd_register();
151 }
152
153 static void world_free(void)
154 {
155 /* TODO.. */
156
157 world_sfd_free();
158 }
159
160 static void render_world_depth( m4x4f projection, m4x3f camera );
161
162 static void add_all_if_material( m4x3f transform, scene *pscene,
163 mdl_header *mdl, u32 id )
164 {
165 for( int i=0; i<mdl->node_count; i++ )
166 {
167 mdl_node *pnode = mdl_node_from_id( mdl, i );
168
169 for( int j=0; j<pnode->submesh_count; j++ )
170 {
171 mdl_submesh *sm = mdl_node_submesh( mdl, pnode, j );
172
173 if( sm->material_id == id )
174 {
175 m4x3f transform2;
176 mdl_node_transform( pnode, transform2 );
177 m4x3_mul( transform, transform2, transform2 );
178
179 scene_add_submesh( pscene, mdl, sm, transform2 );
180 }
181 }
182
183 if( pnode->classtype == k_classtype_instance )
184 {
185 if( pnode->sub_uid )
186 {
187 u32 instance_id = pnode->sub_uid -1;
188 struct instance_cache *cache = &world.instance_cache[instance_id];
189 mdl_header *mdl2 = cache->mdl;
190
191 m4x3f transform2;
192 mdl_node_transform( pnode, transform2 );
193 m4x3_mul( transform, transform2, transform2 );
194
195 add_all_if_material( transform2, pscene, mdl2, id );
196 }
197 }
198 }
199 }
200
201 static void world_apply_procedural_foliage(void)
202 {
203 mdl_header *mfoliage = mdl_load("models/rs_foliage.mdl");
204
205 v3f volume;
206 v3_sub( world.geo.bbx[1], world.geo.bbx[0], volume );
207 volume[1] = 1.0f;
208
209 m4x3f transform;
210 mdl_node *mblob = mdl_node_from_name( mfoliage, "blob" );
211 mdl_submesh *sm_blob = mdl_node_submesh( mfoliage, mblob, 0 );
212
213 for( int i=0;i<100000;i++ )
214 {
215 v3f pos;
216 v3_mul( volume, (v3f){ vg_randf(), 1000.0f, vg_randf() }, pos );
217 pos[1] = 1000.0f;
218 v3_add( pos, world.geo.bbx[0], pos );
219
220 ray_hit hit;
221 hit.dist = INFINITY;
222
223 if( ray_world( pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
224 {
225 if( (hit.normal[1] > 0.8f) && ray_hit_is_terrain(&hit) &&
226 (hit.pos[1] > water_height()+10.0f) )
227 {
228 v4f qsurface, qrandom;
229 v3f axis;
230
231 v3_cross( (v3f){0.0f,1.0f,0.0f}, hit.normal, axis );
232
233 float angle = v3_dot(hit.normal,(v3f){0.0f,1.0f,0.0f});
234 q_axis_angle( qsurface, axis, angle );
235 q_axis_angle( qrandom, (v3f){0.0f,1.0f,0.0f}, vg_randf()*VG_TAUf );
236 q_mul( qsurface, qrandom, qsurface );
237 q_m3x3( qsurface, transform );
238
239 v3_copy( hit.pos, transform[3] );
240 scene_add_submesh( &world.foliage, mfoliage, sm_blob, transform);
241 }
242 }
243 }
244 free( mfoliage );
245 }
246
247 static void world_load(void)
248 {
249 mdl_header *mworld = mdl_load( "models/mp_dev.mdl" );
250
251 world.spawn_count = 0;
252 world.traffic_count = 0;
253 world.instance_cache = NULL;
254
255 /*
256 * Process entities
257 */
258 for( int i=0; i<mworld->node_count; i++ )
259 {
260 mdl_node *pnode = mdl_node_from_id( mworld, i );
261
262 if( pnode->classtype == k_classtype_none )
263 {}
264 else if( pnode->classtype == k_classtype_spawn )
265 {
266 struct respawn_point *rp = &world.spawns[ world.spawn_count ++ ];
267
268 v3_copy( pnode->co, rp->co );
269 v4_copy( pnode->q, rp->q );
270 strcpy( rp->name, mdl_pstr( mworld, pnode->pstr_name ) );
271 }
272 else if( pnode->classtype == k_classtype_water )
273 {
274 if( wrender.enabled )
275 {
276 vg_warn( "Multiple water surfaces in level! ('%s')\n",
277 mdl_pstr( mworld, pnode->pstr_name ));
278 continue;
279 }
280
281 mdl_submesh *sm = mdl_node_submesh( mworld, pnode, 0 );
282
283 if( sm )
284 {
285 glmesh surf;
286 mdl_unpack_submesh( mworld, &surf, sm );
287 water_init();
288 water_set_surface( &surf, pnode->co[1] );
289 }
290 }
291 else if( pnode->classtype == k_classtype_car_path )
292 {
293 struct classtype_car_path *p = mdl_get_entdata( mworld, pnode );
294 traffic_node *tn = &world.traffic[ world.traffic_count ];
295 tn->mn_next = NULL;
296 tn->mn_next1 = NULL;
297
298 if( p->target ) tn->mn_next = mdl_node_from_id( mworld, p->target );
299 if( p->target1 ) tn->mn_next1 = mdl_node_from_id( mworld, p->target1 );
300
301 m4x3f transform;
302 mdl_node_transform( pnode, transform );
303 m3x3_mulv( transform, (v3f){1.0f,0.0f,0.0f}, tn->h );
304 v3_copy( transform[3], tn->co );
305
306 pnode->sub_uid = world.traffic_count ++;
307 }
308 else if( pnode->classtype == k_classtype_instance )
309 {
310 struct classtype_instance *inst = mdl_get_entdata( mworld, pnode );
311 pnode->sub_uid = 0;
312
313 int cached = 0;
314 for( int i=0; i<world.instance_cache_count; i++ )
315 {
316 struct instance_cache *cache = &world.instance_cache[i];
317 if( inst->pstr_file == cache->pstr_file )
318 {
319 cached = 1;
320 pnode->sub_uid = i+1;
321 break;
322 }
323 }
324
325 if( !cached )
326 {
327 world.instance_cache = buffer_reserve(
328 world.instance_cache, world.instance_cache_count,
329 &world.instance_cache_cap, 1,
330 sizeof(struct instance_cache) );
331
332 struct instance_cache *cache =
333 &world.instance_cache[world.instance_cache_count];
334
335 const char *filename = mdl_pstr(mworld, inst->pstr_file);
336
337 cache->pstr_file = inst->pstr_file;
338 cache->mdl = mdl_load( filename );
339
340 if( cache->mdl )
341 {
342 world.instance_cache_count ++;
343 pnode->sub_uid = world.instance_cache_count;
344 mdl_link_materials( mworld, cache->mdl );
345 vg_success( "Cached %s\n", filename );
346 }
347 else
348 {
349 vg_warn( "Failed to cache %s\n", filename );
350 }
351 }
352 }
353 }
354
355 world.instance_cache = buffer_fix( world.instance_cache,
356 world.instance_cache_count,
357 &world.instance_cache_cap,
358 sizeof( struct instance_cache ) );
359
360 #if 0
361 traffic_finalize( world.traffic, world.traffic_count );
362 for( int i=0; i<vg_list_size(world.van_man); i++ )
363 world.van_man[i].current =&world.traffic[vg_randint(world.traffic_count)];
364 #endif
365
366 /*
367 * Compile meshes into the world scenes
368 */
369 scene_init( &world.geo );
370
371 u32 mat_surf = 0,
372 mat_surf_oob = 0,
373 mat_vertex_blend = 0,
374 mat_alphatest = 0,
375 mat_graffiti = 0,
376 mat_subworld = 0,
377 mat_terrain = 0;
378
379 for( int i=1; i<mworld->material_count; i++ )
380 {
381 mdl_material *mat = mdl_material_from_id( mworld, i );
382 const char *mat_name = mdl_pstr( mworld, mat->pstr_name );
383
384 if( !strcmp( "surf", mat_name ))
385 mat_surf = i;
386 else if( !strcmp( "surf_oob", mat_name ))
387 mat_surf_oob = i;
388 else if( !strcmp( "vertex_blend", mat_name ))
389 mat_vertex_blend = i;
390 else if( !strcmp( "alphatest", mat_name ))
391 mat_alphatest = i;
392 else if( !strcmp( "graffitibox", mat_name ))
393 mat_graffiti = i;
394 else if( !strcmp( "terrain", mat_name ) )
395 mat_terrain = i;
396 }
397
398 m4x3f midentity;
399 m4x3_identity( midentity );
400
401 if( mat_terrain )
402 add_all_if_material( midentity, &world.geo, mworld, mat_terrain );
403 scene_copy_slice( &world.geo, &world.sm_terrain );
404
405 if( mat_surf_oob )
406 add_all_if_material( midentity, &world.geo, mworld, mat_surf_oob );
407 else
408 vg_warn( "No OOB surface\n" );
409 scene_copy_slice( &world.geo, &world.sm_geo_std_oob );
410
411 if( mat_surf )
412 add_all_if_material( midentity, &world.geo, mworld, mat_surf );
413 scene_copy_slice( &world.geo, &world.sm_geo_std );
414
415 if( mat_vertex_blend )
416 add_all_if_material( midentity, &world.geo, mworld, mat_vertex_blend );
417 scene_copy_slice( &world.geo, &world.sm_geo_vb );
418
419 scene_upload( &world.geo );
420 scene_bh_create( &world.geo );
421
422
423 /* Foliage /nocollide layer.
424 * TODO: Probably should have material traits for this
425 */
426 scene_init( &world.foliage );
427
428 world_apply_procedural_foliage();
429 scene_copy_slice( &world.foliage, &world.sm_foliage_main );
430
431 add_all_if_material( midentity, &world.foliage, mworld, mat_alphatest );
432 scene_copy_slice( &world.foliage, &world.sm_foliage_alphatest );
433
434 add_all_if_material( midentity, &world.foliage, mworld, mat_graffiti );
435 scene_copy_slice( &world.foliage, &world.sm_graffiti );
436
437 scene_upload( &world.foliage );
438 world_routes_loadfrom( mworld );
439
440 for( int i=0; i<world.instance_cache_count; i++ )
441 free( world.instance_cache[i].mdl );
442
443 free( world.instance_cache );
444 free( mworld );
445
446 /*
447 * Rendering the depth map
448 */
449 m4x4f ortho;
450 m4x3f camera;
451
452 v3f extent;
453 v3_sub( world.geo.bbx[1], world.geo.bbx[0], extent );
454
455 float fl = world.geo.bbx[0][0],
456 fr = world.geo.bbx[1][0],
457 fb = world.geo.bbx[0][2],
458 ft = world.geo.bbx[1][2],
459 rl = 1.0f / (fr-fl),
460 tb = 1.0f / (ft-fb);
461
462 m4x4_zero( ortho );
463 ortho[0][0] = 2.0f * rl;
464 ortho[2][1] = 2.0f * tb;
465 ortho[3][0] = (fr + fl) * -rl;
466 ortho[3][1] = (ft + fb) * -tb;
467 ortho[3][3] = 1.0f;
468 m4x3_identity( camera );
469
470 glViewport( 0, 0, 1024, 1024 );
471 glDisable(GL_DEPTH_TEST);
472 glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_depthmap );
473 shader_fscolour_use();
474 shader_fscolour_uColour( (v4f){-9999.0f,-9999.0f,-9999.0f,-9999.0f} );
475 render_fsquad();
476
477 glEnable(GL_BLEND);
478 glBlendFunc(GL_ONE, GL_ONE);
479 glBlendEquation(GL_MAX);
480 render_world_depth( ortho, camera );
481 glDisable(GL_BLEND);
482 glEnable(GL_DEPTH_TEST);
483
484 /*
485 * TODO: World settings entity
486 */
487 struct ub_world_lighting *winfo = &gpipeline.ub_world_lighting;
488 v4_copy( wrender.plane, winfo->g_water_plane );
489
490 v4f bounds;
491 bounds[0] = world.geo.bbx[0][0];
492 bounds[1] = world.geo.bbx[0][2];
493 bounds[2] = 1.0f/ (world.geo.bbx[1][0]-world.geo.bbx[0][0]);
494 bounds[3] = 1.0f/ (world.geo.bbx[1][2]-world.geo.bbx[0][2]);
495 v4_copy( bounds, winfo->g_depth_bounds );
496
497 winfo->g_water_fog = 0.04f;
498 render_update_lighting_ub();
499
500
501 world.mr_ball.type = k_rb_shape_sphere;
502 world.mr_ball.inf.sphere.radius = 2.0f;
503 v3_copy( (v3f){ 0.0f, 110.0f, 0.0f }, world.mr_ball.co );
504
505 q_identity(world.mr_ball.q);
506 rb_init( &world.mr_ball );
507
508 /*
509 * Setup scene collider
510 */
511 v3_zero( world.rb_geo.co );
512 q_identity( world.rb_geo.q );
513
514 world.rb_geo.type = k_rb_shape_scene;
515 world.rb_geo.inf.scene.pscene = &world.geo;
516 world.rb_geo.is_world = 1;
517 rb_init( &world.rb_geo );
518 }
519
520 static void world_init(void)
521 {
522 vg_tex2d_init( (vg_tex2d *[]){ &tex_terrain_colours,
523 &tex_terrain_noise,
524 &tex_alphatest,
525 &tex_graffiti }, 4 );
526
527 mdl_header *mcars = mdl_load( "models/rs_cars.mdl" );
528 mdl_unpack_glmesh( mcars, &world.cars );
529 mdl_node *nholden = mdl_node_from_name( mcars, "holden" );
530 world.car_holden = *mdl_node_submesh( mcars, nholden, 0 );
531 free(mcars);
532
533
534 mdl_header *msky = mdl_load("models/rs_skydome.mdl");
535 mdl_unpack_glmesh( msky, &world.skydome );
536
537 mdl_node *nlower = mdl_node_from_name( msky, "dome_lower" ),
538 *nupper = mdl_node_from_name( msky, "dome_upper" );
539
540 world.dome_lower = *mdl_node_submesh( msky, nlower, 0 );
541 world.dome_upper = *mdl_node_submesh( msky, nupper, 0 );
542 free(msky);
543
544
545 /* Other systems */
546 world_sfd_init();
547 world_audio_init();
548 }
549
550 static void world_update(void)
551 {
552 world_routes_update();
553 world_routes_debug();
554
555
556 sfd_update( &world.sfd.tester );
557
558 #if 0
559 rb_solver_reset();
560 rb_build_manifold_terrain_sphere( &world.mr_ball );
561
562 for( int i=0; i<5; i++ )
563 rb_solve_contacts( rb_contact_buffer, rb_contact_count );
564
565 rb_iter( &world.mr_ball );
566 rb_update_transform( &world.mr_ball );
567 rb_debug( &world.mr_ball, 0 );
568
569 for( int i=0; i<vg_list_size(world.van_man); i++ )
570 {
571 traffic_drive( &world.van_man[i] );
572 traffic_visualize_car( &world.van_man[i] );
573 }
574 #endif
575 }
576
577 /*
578 * Rendering
579 */
580
581 static void bind_terrain_textures(void)
582 {
583 vg_tex2d_bind( &tex_terrain_noise, 0 );
584 vg_tex2d_bind( &tex_terrain_colours, 1 );
585 }
586
587 static void render_world_vb( m4x4f projection, v3f camera )
588 {
589 m4x3f identity_matrix;
590 m4x3_identity( identity_matrix );
591
592 shader_vblend_use();
593 shader_vblend_uTexGarbage(0);
594 shader_vblend_uTexGradients(1);
595 shader_link_standard_ub( _shader_vblend.id, 2 );
596 bind_terrain_textures();
597
598 shader_vblend_uPv( projection );
599 shader_vblend_uMdl( identity_matrix );
600 shader_vblend_uCamera( camera );
601
602 scene_bind( &world.geo );
603 mdl_draw_submesh( &world.sm_geo_vb );
604
605 mesh_bind( &world.cars );
606
607 #if 0
608 for( int i=0; i<vg_list_size(world.van_man); i++ )
609 {
610 shader_vblend_uMdl( world.van_man[i].transform );
611 mdl_draw_submesh( &world.car_holden );
612 }
613 #endif
614 }
615
616 static void render_world_alphatest( m4x4f projection, v3f camera )
617 {
618 m4x3f identity_matrix;
619 m4x3_identity( identity_matrix );
620
621 shader_alphatest_use();
622 shader_alphatest_uTexGarbage(0);
623 shader_alphatest_uTexMain(1);
624 shader_link_standard_ub( _shader_alphatest.id, 2 );
625
626 vg_tex2d_bind( &tex_terrain_noise, 0 );
627 vg_tex2d_bind( &tex_alphatest, 1 );
628
629 shader_alphatest_uPv( projection );
630 shader_alphatest_uMdl( identity_matrix );
631 shader_alphatest_uCamera( camera );
632
633 glDisable(GL_CULL_FACE);
634 scene_bind( &world.foliage );
635 mdl_draw_submesh( &world.sm_foliage_alphatest );
636
637 vg_tex2d_bind( &tex_graffiti, 1 );
638 mdl_draw_submesh( &world.sm_graffiti );
639
640 glEnable(GL_CULL_FACE);
641 }
642
643 static void render_terrain( m4x4f projection, v3f camera )
644 {
645 m4x3f identity_matrix;
646 m4x3_identity( identity_matrix );
647
648 shader_terrain_use();
649 shader_terrain_uTexGarbage(0);
650 shader_terrain_uTexGradients(1);
651 shader_link_standard_ub( _shader_terrain.id, 2 );
652 bind_terrain_textures();
653
654 shader_terrain_uPv( projection );
655 shader_terrain_uMdl( identity_matrix );
656 shader_terrain_uCamera( camera );
657
658 scene_bind( &world.geo );
659 mdl_draw_submesh( &world.sm_terrain );
660 mdl_draw_submesh( &world.sm_geo_std_oob );
661 mdl_draw_submesh( &world.sm_geo_std );
662 mdl_draw_submesh( &world.sm_subworld );
663
664 /* TODO: Dont draw in reflection */
665 glDisable(GL_CULL_FACE);
666 scene_bind( &world.foliage );
667 mdl_draw_submesh( &world.sm_foliage_main );
668 glEnable(GL_CULL_FACE);
669 }
670
671 static void render_lowerdome( m4x3f camera )
672 {
673 m4x4f projection, full;
674 pipeline_projection( projection, 0.4f, 1000.0f );
675
676 m4x3f inverse;
677 m3x3_transpose( camera, inverse );
678 v3_copy((v3f){0.0f,0.0f,0.0f}, inverse[3]);
679 m4x3_expand( inverse, full );
680 m4x4_mul( projection, full, full );
681
682 m4x3f identity_matrix;
683 m4x3_identity( identity_matrix );
684
685 shader_planeinf_use();
686 shader_planeinf_uMdl(identity_matrix);
687 shader_planeinf_uPv(full);
688 shader_planeinf_uCamera(camera[3]);
689 shader_planeinf_uPlane( (v4f){0.0f,1.0f,0.0f, water_height()} );
690
691 mdl_draw_submesh( &world.dome_lower );
692 }
693
694 static void render_sky(m4x3f camera)
695 {
696 m4x4f projection, full;
697 pipeline_projection( projection, 0.4f, 1000.0f );
698
699 m4x3f inverse;
700 m3x3_transpose( camera, inverse );
701 v3_copy((v3f){0.0f,0.0f,0.0f}, inverse[3]);
702 m4x3_expand( inverse, full );
703 m4x4_mul( projection, full, full );
704
705 m4x3f identity_matrix;
706 m4x3_identity( identity_matrix );
707
708 shader_sky_use();
709 shader_sky_uMdl(identity_matrix);
710 shader_sky_uPv(full);
711 shader_sky_uTexGarbage(0);
712 shader_sky_uTime( vg_time );
713
714 vg_tex2d_bind( &tex_terrain_noise, 0 );
715
716 glDepthMask( GL_FALSE );
717 glDisable( GL_DEPTH_TEST );
718
719 mesh_bind( &world.skydome );
720 mdl_draw_submesh( &world.dome_upper );
721
722 glEnable( GL_DEPTH_TEST );
723 glDepthMask( GL_TRUE );
724 }
725
726 static void render_world_gates( m4x4f projection, m4x3f camera )
727 {
728 float closest = INFINITY;
729 int id = 0;
730
731 for( int i=0; i<world.routes.gate_count; i++ )
732 {
733 struct route_gate *rg = &world.routes.gates[i];
734 float dist = v3_dist2( rg->gate.co[0], camera[3] );
735
736 if( dist < closest )
737 {
738 closest = dist;
739 id = i;
740 }
741 }
742
743 render_gate( &world.routes.gates[id].gate, camera );
744 v3_lerp( world.render_gate_pos,
745 world.routes.gates[id].gate.co[0],
746 1.0f,
747 world.render_gate_pos );
748 }
749
750 static void render_world( m4x4f projection, m4x3f camera )
751 {
752 render_sky( camera );
753 render_world_routes( projection, camera[3] );
754 render_world_vb( projection, camera[3] );
755 render_world_alphatest( projection, camera[3] );
756 render_terrain( projection, camera[3] );
757
758 int closest = 0;
759 float min_dist = INFINITY;
760
761 for( int i=0; i<world.routes.route_count; i++ )
762 {
763 float dist = v3_dist2( world.routes.routes[i].scoreboard_transform[3],
764 camera[3] );
765
766 if( dist < min_dist )
767 {
768 min_dist = dist;
769 closest = i;
770 }
771 }
772
773 sfd_render( &world.sfd.tester, projection, camera[3],
774 world.routes.routes[closest].scoreboard_transform );
775 }
776
777 static void render_world_depth( m4x4f projection, m4x3f camera )
778 {
779 m4x3f identity_matrix;
780 m4x3_identity( identity_matrix );
781
782 shader_gpos_use();
783 shader_gpos_uCamera( camera[3] );
784 shader_gpos_uPv( projection );
785 shader_gpos_uMdl( identity_matrix );
786
787 scene_bind( &world.geo );
788 scene_draw( &world.geo );
789
790 #if 0
791 glDisable(GL_CULL_FACE);
792 scene_bind( &world.foliage );
793 scene_draw( &world.foliage );
794 glEnable(GL_CULL_FACE);
795 #endif
796 }
797
798 #endif /* WORLD_H */