2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
10 * If we have multiple levels, write an unloader
15 static void world_add_all_if_material( m4x3f transform
, scene
*pscene
,
16 mdl_header
*mdl
, u32 id
)
18 for( int i
=0; i
<mdl
->node_count
; i
++ )
20 mdl_node
*pnode
= mdl_node_from_id( mdl
, i
);
22 for( int j
=0; j
<pnode
->submesh_count
; j
++ )
24 mdl_submesh
*sm
= mdl_node_submesh( mdl
, pnode
, j
);
26 if( sm
->material_id
== id
)
29 mdl_node_transform( pnode
, transform2
);
30 m4x3_mul( transform
, transform2
, transform2
);
32 scene_add_submesh( pscene
, mdl
, sm
, transform2
);
36 if( pnode
->classtype
== k_classtype_instance
)
40 u32 instance_id
= pnode
->sub_uid
-1;
41 struct instance_cache
*cache
= &world
.instance_cache
[instance_id
];
42 mdl_header
*mdl2
= cache
->mdl
;
45 mdl_node_transform( pnode
, transform2
);
46 m4x3_mul( transform
, transform2
, transform2
);
48 world_add_all_if_material( transform2
, pscene
, mdl2
, id
);
54 static void world_apply_procedural_foliage(void)
56 mdl_header
*mfoliage
= mdl_load("models/rs_foliage.mdl");
59 v3_sub( world
.geo
.bbx
[1], world
.geo
.bbx
[0], volume
);
63 mdl_node
*mblob
= mdl_node_from_name( mfoliage
, "blob" );
64 mdl_submesh
*sm_blob
= mdl_node_submesh( mfoliage
, mblob
, 0 );
66 for( int i
=0;i
<100000;i
++ )
69 v3_mul( volume
, (v3f
){ vg_randf(), 1000.0f
, vg_randf() }, pos
);
71 v3_add( pos
, world
.geo
.bbx
[0], pos
);
76 if( ray_world( pos
, (v3f
){0.0f
,-1.0f
,0.0f
}, &hit
))
78 if( (hit
.normal
[1] > 0.8f
) && ray_hit_is_terrain(&hit
) &&
79 (hit
.pos
[1] > 0.0f
+10.0f
) )
81 v4f qsurface
, qrandom
;
84 v3_cross( (v3f
){0.0f
,1.0f
,0.0f
}, hit
.normal
, axis
);
86 float angle
= v3_dot(hit
.normal
,(v3f
){0.0f
,1.0f
,0.0f
});
87 q_axis_angle( qsurface
, axis
, angle
);
88 q_axis_angle( qrandom
, (v3f
){0.0f
,1.0f
,0.0f
}, vg_randf()*VG_TAUf
);
89 q_mul( qsurface
, qrandom
, qsurface
);
90 q_m3x3( qsurface
, transform
);
92 v3_copy( hit
.pos
, transform
[3] );
93 scene_add_submesh( &world
.foliage
, mfoliage
, sm_blob
, transform
);
101 static void world_load(void)
103 mdl_header
*mworld
= mdl_load( "models/mp_dev.mdl" );
104 vg_info( "Loading world: models/mp_dev.mdl\n" );
106 world
.spawn_count
= 0;
107 world
.traffic_count
= 0;
108 world
.instance_cache
= NULL
;
113 for( int i
=0; i
<mworld
->node_count
; i
++ )
115 mdl_node
*pnode
= mdl_node_from_id( mworld
, i
);
117 if( pnode
->classtype
== k_classtype_none
)
119 else if( pnode
->classtype
== k_classtype_spawn
)
121 struct respawn_point
*rp
= &world
.spawns
[ world
.spawn_count
++ ];
123 v3_copy( pnode
->co
, rp
->co
);
124 v4_copy( pnode
->q
, rp
->q
);
125 strcpy( rp
->name
, mdl_pstr( mworld
, pnode
->pstr_name
) );
127 else if( pnode
->classtype
== k_classtype_water
)
129 if( wrender
.enabled
)
131 vg_warn( "Multiple water surfaces in level! ('%s')\n",
132 mdl_pstr( mworld
, pnode
->pstr_name
));
136 mdl_submesh
*sm
= mdl_node_submesh( mworld
, pnode
, 0 );
140 vg_acquire_thread_sync();
143 mdl_unpack_submesh( mworld
, &surf
, sm
);
144 water_set_surface( &surf
, pnode
->co
[1] );
146 vg_release_thread_sync();
149 else if( pnode
->classtype
== k_classtype_car_path
)
151 struct classtype_car_path
*p
= mdl_get_entdata( mworld
, pnode
);
152 traffic_node
*tn
= &world
.traffic
[ world
.traffic_count
];
156 if( p
->target
) tn
->mn_next
= mdl_node_from_id( mworld
, p
->target
);
157 if( p
->target1
) tn
->mn_next1
= mdl_node_from_id( mworld
, p
->target1
);
160 mdl_node_transform( pnode
, transform
);
161 m3x3_mulv( transform
, (v3f
){1.0f
,0.0f
,0.0f
}, tn
->h
);
162 v3_copy( transform
[3], tn
->co
);
164 pnode
->sub_uid
= world
.traffic_count
++;
166 else if( pnode
->classtype
== k_classtype_instance
)
168 struct classtype_instance
*inst
= mdl_get_entdata( mworld
, pnode
);
172 for( int i
=0; i
<world
.instance_cache_count
; i
++ )
174 struct instance_cache
*cache
= &world
.instance_cache
[i
];
175 if( inst
->pstr_file
== cache
->pstr_file
)
178 pnode
->sub_uid
= i
+1;
185 world
.instance_cache
= buffer_reserve(
186 world
.instance_cache
, world
.instance_cache_count
,
187 &world
.instance_cache_cap
, 1,
188 sizeof(struct instance_cache
) );
190 struct instance_cache
*cache
=
191 &world
.instance_cache
[world
.instance_cache_count
];
193 const char *filename
= mdl_pstr(mworld
, inst
->pstr_file
);
195 cache
->pstr_file
= inst
->pstr_file
;
196 cache
->mdl
= mdl_load( filename
);
200 world
.instance_cache_count
++;
201 pnode
->sub_uid
= world
.instance_cache_count
;
202 mdl_link_materials( mworld
, cache
->mdl
);
203 vg_success( "Cached %s\n", filename
);
207 vg_warn( "Failed to cache %s\n", filename
);
211 else if( pnode
->classtype
== k_classtype_achievement_box
)
213 world
.achievement_zones
=
214 buffer_reserve( world
.achievement_zones
,
215 world
.achievement_zones_count
,
216 &world
.achievement_zones_cap
, 1,
217 sizeof(struct achievement_zone
) );
219 struct achievement_zone
*zone
= &world
.achievement_zones
[
220 world
.achievement_zones_count
++ ];
223 struct classtype_achievement_box
*box
= mdl_get_entdata(mworld
,pnode
);
225 mdl_node_transform( pnode
, zone
->transform
);
226 m4x3_invert_full( zone
->transform
, zone
->inv_transform
);
227 vg_strncpy( mdl_pstr(mworld
, box
->pstr_name
), zone
->name
, 31 );
228 zone
->name
[31] = 0x00;
232 zone
->ptarget_delegated
= mdl_node_from_id( mworld
, box
->trigger
);
234 zone
->ptarget_delegated
= NULL
;
236 else if( pnode
->classtype
== k_classtype_audio
)
239 buffer_reserve( world
.audio_things
,
240 world
.audio_things_count
,
241 &world
.audio_things_cap
, 1,
242 sizeof(struct world_audio_thing
) );
244 struct world_audio_thing
*thing
= &world
.audio_things
[
245 world
.audio_things_count
];
246 memset( thing
, 0, sizeof(struct world_audio_thing
) );
247 struct classtype_audio
*aud
= mdl_get_entdata(mworld
,pnode
);
249 v3_copy( pnode
->co
, thing
->pos
);
251 if( aud
->flags
& AUDIO_FLAG_SPACIAL_3D
)
252 thing
->volume
= aud
->volume
* pnode
->s
[0];
254 thing
->volume
= aud
->volume
;
256 thing
->flags
= aud
->flags
;
257 const char *fp
= mdl_pstr( mworld
, aud
->pstr_file
);
258 char *path2
= vg_alloc( strlen(fp
)+1 );
260 thing
->temp_embedded_clip
.path
= path2
;
261 thing
->temp_embedded_clip
.source_mode
= k_audio_source_compressed
;
262 audio_clip_load( &thing
->temp_embedded_clip
);
263 thing
->player
.name
= "[temp]";
264 thing
->player
.enqued
= 0;
266 pnode
->sub_uid
= world
.audio_things_count
;
267 world
.audio_things_count
++;
272 for( int i
=0; i
<world
.achievement_zones_count
; i
++ )
274 struct achievement_zone
*ach
= &world
.achievement_zones
[ i
];
275 if( ach
->ptarget_delegated
)
277 u32 id
= ach
->ptarget_delegated
->sub_uid
;
278 ach
->ptarget
= &world
.audio_things
[ id
];
284 /* initialize audio if need be */
286 for( int i
=0; i
<world
.audio_things_count
; i
++ )
288 struct world_audio_thing
*thingy
= &world
.audio_things
[ i
];
290 audio_player_init( &thingy
->player
);
291 audio_player_set_flags( &thingy
->player
, thingy
->flags
);
292 audio_player_set_vol( &thingy
->player
, thingy
->volume
);
293 audio_player_set_pan( &thingy
->player
, 0.0f
);
295 if( thingy
->flags
& AUDIO_FLAG_SPACIAL_3D
)
296 audio_player_set_position( &thingy
->player
, thingy
->pos
);
298 if( thingy
->flags
& AUDIO_FLAG_AUTO_START
)
299 audio_player_playclip( &thingy
->player
, &thingy
->temp_embedded_clip
);
303 world
.instance_cache
= buffer_fix( world
.instance_cache
,
304 world
.instance_cache_count
,
305 &world
.instance_cache_cap
,
306 sizeof( struct instance_cache
) );
309 traffic_finalize( world
.traffic
, world
.traffic_count
);
310 for( int i
=0; i
<vg_list_size(world
.van_man
); i
++ )
311 world
.van_man
[i
].current
=&world
.traffic
[vg_randint(world
.traffic_count
)];
315 * Compile meshes into the world scenes
317 scene_init( &world
.geo
);
321 mat_vertex_blend
= 0,
327 for( int i
=1; i
<mworld
->material_count
; i
++ )
329 mdl_material
*mat
= mdl_material_from_id( mworld
, i
);
330 const char *mat_name
= mdl_pstr( mworld
, mat
->pstr_name
);
332 if( !strcmp( "surf", mat_name
))
334 else if( !strcmp( "surf_oob", mat_name
))
336 else if( !strcmp( "vertex_blend", mat_name
))
337 mat_vertex_blend
= i
;
338 else if( !strcmp( "alphatest", mat_name
))
340 else if( !strcmp( "graffitibox", mat_name
))
342 else if( !strcmp( "terrain", mat_name
) )
347 m4x3_identity( midentity
);
350 world_add_all_if_material( midentity
, &world
.geo
, mworld
, mat_terrain
);
351 scene_copy_slice( &world
.geo
, &world
.sm_terrain
);
354 world_add_all_if_material( midentity
, &world
.geo
, mworld
, mat_surf_oob
);
356 vg_warn( "No OOB surface\n" );
357 scene_copy_slice( &world
.geo
, &world
.sm_geo_std_oob
);
360 world_add_all_if_material( midentity
, &world
.geo
, mworld
, mat_surf
);
361 scene_copy_slice( &world
.geo
, &world
.sm_geo_std
);
363 if( mat_vertex_blend
)
364 world_add_all_if_material( midentity
, &world
.geo
,mworld
,mat_vertex_blend
);
365 scene_copy_slice( &world
.geo
, &world
.sm_geo_vb
);
367 vg_acquire_thread_sync();
368 scene_upload( &world
.geo
);
369 vg_release_thread_sync();
371 scene_bh_create( &world
.geo
);
374 /* Foliage /nocollide layer.
375 * TODO: Probably should have material traits for this
377 scene_init( &world
.foliage
);
379 world_apply_procedural_foliage();
380 scene_copy_slice( &world
.foliage
, &world
.sm_foliage_main
);
382 world_add_all_if_material( midentity
, &world
.foliage
, mworld
, mat_alphatest
);
383 scene_copy_slice( &world
.foliage
, &world
.sm_foliage_alphatest
);
385 world_add_all_if_material( midentity
, &world
.foliage
, mworld
, mat_graffiti
);
386 scene_copy_slice( &world
.foliage
, &world
.sm_graffiti
);
389 vg_acquire_thread_sync();
391 scene_upload( &world
.foliage
);
394 * Rendering the depth map
400 v3_sub( world
.geo
.bbx
[1], world
.geo
.bbx
[0], extent
);
402 float fl
= world
.geo
.bbx
[0][0],
403 fr
= world
.geo
.bbx
[1][0],
404 fb
= world
.geo
.bbx
[0][2],
405 ft
= world
.geo
.bbx
[1][2],
410 ortho
[0][0] = 2.0f
* rl
;
411 ortho
[2][1] = 2.0f
* tb
;
412 ortho
[3][0] = (fr
+ fl
) * -rl
;
413 ortho
[3][1] = (ft
+ fb
) * -tb
;
415 m4x3_identity( camera
);
417 glViewport( 0, 0, 1024, 1024 );
418 glDisable(GL_DEPTH_TEST
);
419 glBindFramebuffer( GL_FRAMEBUFFER
, gpipeline
.fb_depthmap
);
420 shader_fscolour_use();
421 shader_fscolour_uColour( (v4f
){-9999.0f
,-9999.0f
,-9999.0f
,-9999.0f
} );
425 glBlendFunc(GL_ONE
, GL_ONE
);
426 glBlendEquation(GL_MAX
);
427 render_world_depth( ortho
, camera
);
429 glEnable(GL_DEPTH_TEST
);
430 glBindFramebuffer( GL_FRAMEBUFFER
, 0 );
434 * TODO: World settings entity
436 struct ub_world_lighting
*winfo
= &gpipeline
.ub_world_lighting
;
437 v4_copy( wrender
.plane
, winfo
->g_water_plane
);
440 bounds
[0] = world
.geo
.bbx
[0][0];
441 bounds
[1] = world
.geo
.bbx
[0][2];
442 bounds
[2] = 1.0f
/ (world
.geo
.bbx
[1][0]-world
.geo
.bbx
[0][0]);
443 bounds
[3] = 1.0f
/ (world
.geo
.bbx
[1][2]-world
.geo
.bbx
[0][2]);
444 v4_copy( bounds
, winfo
->g_depth_bounds
);
446 winfo
->g_water_fog
= 0.04f
;
447 render_update_lighting_ub();
450 vg_release_thread_sync();
452 world_routes_loadfrom( mworld
);
454 for( int i
=0; i
<world
.instance_cache_count
; i
++ )
455 vg_free( world
.instance_cache
[i
].mdl
);
457 vg_free( world
.instance_cache
);
459 scene_free_offline_buffers( &world
.foliage
);
462 * Setup scene collider
464 v3_zero( world
.rb_geo
.co
);
465 q_identity( world
.rb_geo
.q
);
467 world
.rb_geo
.type
= k_rb_shape_scene
;
468 world
.rb_geo
.inf
.scene
.pscene
= &world
.geo
;
469 world
.rb_geo
.is_world
= 1;
470 rb_init( &world
.rb_geo
);
473 #endif /* WORLD_GEN_H */