/*
- * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
+ * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
+ *
+ * World generation/population. Different to regular loading, since it needs to
+ * create geometry, apply procedural stuff and save that image to files etc.
*/
#ifndef WORLD_GEN_H
#define WORLD_GEN_H
-/*
- * FUTURE:
- * If we have multiple levels, write an unloader
- */
-
#include "world.h"
-static void world_add_all_if_material( m4x3f transform, scene *pscene,
- mdl_header *mdl, u32 id )
-{
- for( int i=0; i<mdl->node_count; i++ )
- {
- mdl_node *pnode = mdl_node_from_id( mdl, i );
-
- for( int j=0; j<pnode->submesh_count; j++ )
- {
- mdl_submesh *sm = mdl_node_submesh( mdl, pnode, j );
-
- if( sm->material_id == id )
- {
- m4x3f transform2;
- mdl_node_transform( pnode, transform2 );
- m4x3_mul( transform, transform2, transform2 );
-
- scene_add_submesh( pscene, mdl, sm, transform2 );
- }
- }
-
- if( pnode->classtype == k_classtype_instance )
- {
- if( pnode->sub_uid )
- {
- u32 instance_id = pnode->sub_uid -1;
- struct instance_cache *cache = &world.instance_cache[instance_id];
- mdl_header *mdl2 = cache->mdl;
-
- m4x3f transform2;
- mdl_node_transform( pnode, transform2 );
- m4x3_mul( transform, transform2, transform2 );
-
- world_add_all_if_material( transform2, pscene, mdl2, id );
- }
- }
- }
-}
-
-static void world_apply_procedural_foliage(void)
-{
- mdl_header *mfoliage = mdl_load("models/rs_foliage.mdl");
-
- v3f volume;
- v3_sub( world.geo.bbx[1], world.geo.bbx[0], volume );
- volume[1] = 1.0f;
-
- m4x3f transform;
- mdl_node *mblob = mdl_node_from_name( mfoliage, "blob" );
- mdl_submesh *sm_blob = mdl_node_submesh( mfoliage, mblob, 0 );
-
- for( int i=0;i<100000;i++ )
- {
- v3f pos;
- v3_mul( volume, (v3f){ vg_randf(), 1000.0f, vg_randf() }, pos );
- pos[1] = 1000.0f;
- v3_add( pos, world.geo.bbx[0], pos );
-
- ray_hit hit;
- hit.dist = INFINITY;
-
- if( ray_world( pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
- {
- if( (hit.normal[1] > 0.8f) && ray_hit_is_terrain(&hit) &&
- (hit.pos[1] > 0.0f+10.0f) )
- {
- v4f qsurface, qrandom;
- v3f axis;
-
- v3_cross( (v3f){0.0f,1.0f,0.0f}, hit.normal, axis );
-
- float angle = v3_dot(hit.normal,(v3f){0.0f,1.0f,0.0f});
- q_axis_angle( qsurface, axis, angle );
- q_axis_angle( qrandom, (v3f){0.0f,1.0f,0.0f}, vg_randf()*VG_TAUf );
- q_mul( qsurface, qrandom, qsurface );
- q_m3x3( qsurface, transform );
-
- v3_copy( hit.pos, transform[3] );
- scene_add_submesh( &world.foliage, mfoliage, sm_blob, transform);
- }
- }
- }
-
- vg_free( mfoliage );
-}
-
-static void world_load(void)
-{
- mdl_header *mworld = mdl_load( "models/mp_dev.mdl" );
- vg_info( "Loading world: models/mp_dev.mdl\n" );
-
- world.spawn_count = 0;
- world.traffic_count = 0;
- world.instance_cache = NULL;
-
- /*
- * Process entities
- */
- for( int i=0; i<mworld->node_count; i++ )
- {
- mdl_node *pnode = mdl_node_from_id( mworld, i );
-
- if( pnode->classtype == k_classtype_none )
- {}
- else if( pnode->classtype == k_classtype_spawn )
- {
- struct respawn_point *rp = &world.spawns[ world.spawn_count ++ ];
-
- v3_copy( pnode->co, rp->co );
- v4_copy( pnode->q, rp->q );
- strcpy( rp->name, mdl_pstr( mworld, pnode->pstr_name ) );
- }
- else if( pnode->classtype == k_classtype_water )
- {
- if( wrender.enabled )
- {
- vg_warn( "Multiple water surfaces in level! ('%s')\n",
- mdl_pstr( mworld, pnode->pstr_name ));
- continue;
- }
-
- mdl_submesh *sm = mdl_node_submesh( mworld, pnode, 0 );
-
- if( sm )
- {
- vg_acquire_thread_sync();
- {
- glmesh surf;
- mdl_unpack_submesh( mworld, &surf, sm );
- water_set_surface( &surf, pnode->co[1] );
- }
- vg_release_thread_sync();
- }
- }
- else if( pnode->classtype == k_classtype_car_path )
- {
- struct classtype_car_path *p = mdl_get_entdata( mworld, pnode );
- traffic_node *tn = &world.traffic[ world.traffic_count ];
- tn->mn_next = NULL;
- tn->mn_next1 = NULL;
-
- if( p->target ) tn->mn_next = mdl_node_from_id( mworld, p->target );
- if( p->target1 ) tn->mn_next1 = mdl_node_from_id( mworld, p->target1 );
-
- m4x3f transform;
- mdl_node_transform( pnode, transform );
- m3x3_mulv( transform, (v3f){1.0f,0.0f,0.0f}, tn->h );
- v3_copy( transform[3], tn->co );
-
- pnode->sub_uid = world.traffic_count ++;
- }
- else if( pnode->classtype == k_classtype_instance )
- {
- struct classtype_instance *inst = mdl_get_entdata( mworld, pnode );
- pnode->sub_uid = 0;
-
- int cached = 0;
- for( int i=0; i<world.instance_cache_count; i++ )
- {
- struct instance_cache *cache = &world.instance_cache[i];
- if( inst->pstr_file == cache->pstr_file )
- {
- cached = 1;
- pnode->sub_uid = i+1;
- break;
- }
- }
-
- if( !cached )
- {
- world.instance_cache = buffer_reserve(
- world.instance_cache, world.instance_cache_count,
- &world.instance_cache_cap, 1,
- sizeof(struct instance_cache) );
-
- struct instance_cache *cache =
- &world.instance_cache[world.instance_cache_count];
-
- const char *filename = mdl_pstr(mworld, inst->pstr_file);
-
- cache->pstr_file = inst->pstr_file;
- cache->mdl = mdl_load( filename );
-
- if( cache->mdl )
- {
- world.instance_cache_count ++;
- pnode->sub_uid = world.instance_cache_count;
- mdl_link_materials( mworld, cache->mdl );
- vg_success( "Cached %s\n", filename );
- }
- else
- {
- vg_warn( "Failed to cache %s\n", filename );
- }
- }
- }
- else if( pnode->classtype == k_classtype_achievement_box )
- {
- world.achievement_zones =
- buffer_reserve( world.achievement_zones,
- world.achievement_zones_count,
- &world.achievement_zones_cap, 1,
- sizeof(struct achievement_zone) );
-
- struct achievement_zone *zone = &world.achievement_zones[
- world.achievement_zones_count ++ ];
-
-
- struct classtype_achievement_box *box = mdl_get_entdata(mworld,pnode);
-
- mdl_node_transform( pnode, zone->transform );
- m4x3_invert_full( zone->transform, zone->inv_transform );
- vg_strncpy( mdl_pstr(mworld, box->pstr_name), zone->name, 31 );
- zone->name[31] = 0x00;
- zone->triggered = 0;
- }
- }
-
- world.instance_cache = buffer_fix( world.instance_cache,
- world.instance_cache_count,
- &world.instance_cache_cap,
- sizeof( struct instance_cache ) );
-
-#if 0
- traffic_finalize( world.traffic, world.traffic_count );
- for( int i=0; i<vg_list_size(world.van_man); i++ )
- world.van_man[i].current =&world.traffic[vg_randint(world.traffic_count)];
-#endif
-
- /*
- * Compile meshes into the world scenes
- */
- scene_init( &world.geo );
-
- u32 mat_surf = 0,
- mat_surf_oob = 0,
- mat_vertex_blend = 0,
- mat_alphatest = 0,
- mat_graffiti = 0,
- mat_subworld = 0,
- mat_terrain = 0;
-
- for( int i=1; i<mworld->material_count; i++ )
- {
- mdl_material *mat = mdl_material_from_id( mworld, i );
- const char *mat_name = mdl_pstr( mworld, mat->pstr_name );
-
- if( !strcmp( "surf", mat_name ))
- mat_surf = i;
- else if( !strcmp( "surf_oob", mat_name ))
- mat_surf_oob = i;
- else if( !strcmp( "vertex_blend", mat_name ))
- mat_vertex_blend = i;
- else if( !strcmp( "alphatest", mat_name ))
- mat_alphatest = i;
- else if( !strcmp( "graffitibox", mat_name ))
- mat_graffiti = i;
- else if( !strcmp( "terrain", mat_name ) )
- mat_terrain = i;
- }
-
- m4x3f midentity;
- m4x3_identity( midentity );
-
- if( mat_terrain )
- world_add_all_if_material( midentity, &world.geo, mworld, mat_terrain );
- scene_copy_slice( &world.geo, &world.sm_terrain );
-
- if( mat_surf_oob )
- world_add_all_if_material( midentity, &world.geo, mworld, mat_surf_oob );
- else
- vg_warn( "No OOB surface\n" );
- scene_copy_slice( &world.geo, &world.sm_geo_std_oob );
-
- if( mat_surf )
- world_add_all_if_material( midentity, &world.geo, mworld, mat_surf );
- scene_copy_slice( &world.geo, &world.sm_geo_std );
-
- if( mat_vertex_blend )
- world_add_all_if_material( midentity, &world.geo,mworld,mat_vertex_blend);
- scene_copy_slice( &world.geo, &world.sm_geo_vb );
-
- vg_acquire_thread_sync();
- scene_upload( &world.geo );
- vg_release_thread_sync();
-
- scene_bh_create( &world.geo );
-
-
- /* Foliage /nocollide layer.
- * TODO: Probably should have material traits for this
- */
- scene_init( &world.foliage );
-
- world_apply_procedural_foliage();
- scene_copy_slice( &world.foliage, &world.sm_foliage_main );
-
- world_add_all_if_material( midentity, &world.foliage, mworld, mat_alphatest);
- scene_copy_slice( &world.foliage, &world.sm_foliage_alphatest );
-
- world_add_all_if_material( midentity, &world.foliage, mworld, mat_graffiti );
- scene_copy_slice( &world.foliage, &world.sm_graffiti );
-
-
- vg_acquire_thread_sync();
- {
- scene_upload( &world.foliage );
-
- /*
- * Rendering the depth map
- */
- m4x4f ortho;
- m4x3f camera;
-
- v3f extent;
- v3_sub( world.geo.bbx[1], world.geo.bbx[0], extent );
-
- float fl = world.geo.bbx[0][0],
- fr = world.geo.bbx[1][0],
- fb = world.geo.bbx[0][2],
- ft = world.geo.bbx[1][2],
- rl = 1.0f / (fr-fl),
- tb = 1.0f / (ft-fb);
-
- m4x4_zero( ortho );
- ortho[0][0] = 2.0f * rl;
- ortho[2][1] = 2.0f * tb;
- ortho[3][0] = (fr + fl) * -rl;
- ortho[3][1] = (ft + fb) * -tb;
- ortho[3][3] = 1.0f;
- m4x3_identity( camera );
-
- glViewport( 0, 0, 1024, 1024 );
- glDisable(GL_DEPTH_TEST);
- glBindFramebuffer( GL_FRAMEBUFFER, gpipeline.fb_depthmap );
- shader_fscolour_use();
- shader_fscolour_uColour( (v4f){-9999.0f,-9999.0f,-9999.0f,-9999.0f} );
- render_fsquad();
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- glBlendEquation(GL_MAX);
- render_world_depth( ortho, camera );
- glDisable(GL_BLEND);
- glEnable(GL_DEPTH_TEST);
- glBindFramebuffer( GL_FRAMEBUFFER, 0 );
-
-
- /*
- * TODO: World settings entity
- */
- struct ub_world_lighting *winfo = &gpipeline.ub_world_lighting;
- v4_copy( wrender.plane, winfo->g_water_plane );
-
- v4f bounds;
- bounds[0] = world.geo.bbx[0][0];
- bounds[1] = world.geo.bbx[0][2];
- bounds[2] = 1.0f/ (world.geo.bbx[1][0]-world.geo.bbx[0][0]);
- bounds[3] = 1.0f/ (world.geo.bbx[1][2]-world.geo.bbx[0][2]);
- v4_copy( bounds, winfo->g_depth_bounds );
-
- winfo->g_water_fog = 0.04f;
- render_update_lighting_ub();
- }
-
- vg_release_thread_sync();
-
- world_routes_loadfrom( mworld );
-
- for( int i=0; i<world.instance_cache_count; i++ )
- vg_free( world.instance_cache[i].mdl );
-
- vg_free( world.instance_cache );
- vg_free( mworld );
- scene_free_offline_buffers( &world.foliage );
-
- /*
- * Setup scene collider
- */
- v3_zero( world.rb_geo.co );
- q_identity( world.rb_geo.q );
-
- world.rb_geo.type = k_rb_shape_scene;
- world.rb_geo.inf.scene.pscene = &world.geo;
- world.rb_geo.is_world = 1;
- rb_init( &world.rb_geo );
-}
+static void world_init_blank( world_instance *world );
+static void world_gen_load_surfaces( world_instance *world );
+static void world_gen_generate_meshes( world_instance *world );
+static void world_gen_compute_light_indices( world_instance *world );
+static void async_world_postprocess( void *payload, u32 _size );
#endif /* WORLD_GEN_H */