("vertex_start",c_uint32),
("vertex_count",c_uint32),
("bbx",(c_float*3)*2),
- ("material_id",c_uint32)] # index into the material array
+ ("material_id",c_uint16), # index into the material array
+ ("flags",c_uint16)]
#}
class mdl_material(Structure):
("glyph_utf32_base",c_uint32)]
#}
+class ent_traffic(Structure):
+#{
+ _fields_ = [("transform",mdl_transform),
+ ("submesh_start",c_uint32),
+ ("submesh_count",c_uint32),
+ ("start_node",c_uint32),
+ ("node_count",c_uint32),
+ ("speed",c_float),
+ ("t",c_float),
+ ("index",c_uint32)]
+#}
+
def obj_ent_type( obj ):
#{
if obj.type == 'ARMATURE': return 'mdl_armature'
yield from _recurse_bone( b )
#}
-def sr_compile_mesh( obj ):
+# Returns submesh_start,count and armature_id
+def sr_compile_mesh_internal( obj ):
#{
- node=mdl_mesh()
- compile_obj_transform(obj, node.transform)
- node.pstr_name = sr_compile_string(obj.name)
- ent_type = obj_ent_type( obj )
-
- node.entity_id = 0
-
- if ent_type != 'none':#{
- ent_id_lwr = sr_compile.entity_ids[obj.name]
- ent_id_upr = get_entity_enum_id( obj_ent_type(obj) )
- node.entity_id = (ent_id_upr << 16) | ent_id_lwr
- #}
- print( node.entity_id )
-
can_use_cache = True
armature = None
+ submesh_start = 0
+ submesh_count = 0
+ armature_id = 0
+
for mod in obj.modifiers:#{
if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
armature = mod.object
rig_weight_groups = \
['0 [ROOT]']+[_.name for _ in sr_armature_bones(mod.object)]
- node.armature_id = sr_compile.entity_ids[armature.name]
+ armature_id = sr_compile.entity_ids[armature.name]
POSE_OR_REST_CACHE = armature.data.pose_position
armature.data.pose_position = 'REST'
#
if can_use_cache and (obj.data.name in sr_compile.mesh_cache):#{
ref = sr_compile.mesh_cache[obj.data.name]
- node.submesh_start = ref[0]
- node.submesh_count = ref[1]
- sr_compile.mesh_data.extend(bytearray(node))
- return
+ submesh_start = ref[0]
+ submesh_count = ref[1]
+ return (submesh_start,submesh_count,armature_id)
#}
# Compile a whole new mesh
#
- node.submesh_start = len(sr_compile.submesh_data)//sizeof(mdl_submesh)
- node.submesh_count = 0
+ submesh_start = len(sr_compile.submesh_data)//sizeof(mdl_submesh)
+ submesh_count = 0
dgraph = bpy.context.evaluated_depsgraph_get()
data = obj.evaluated_get(dgraph).data
# Add submesh to encoder
#
sr_compile.submesh_data.extend( bytearray(sm) )
- node.submesh_count += 1
+ submesh_count += 1
#}
if armature:#{
armature.data.pose_position = POSE_OR_REST_CACHE
#}
- # Save a reference to this node since we want to reuse the submesh indices
+ # Save a reference to this mesh since we want to reuse the submesh indices
# later.
- sr_compile.mesh_cache[obj.data.name]=(node.submesh_start,node.submesh_count)
+ sr_compile.mesh_cache[obj.data.name]=(submesh_start,submesh_count)
+ return (submesh_start,submesh_count,armature_id)
+#}
+
+def sr_compile_mesh( obj ):
+#{
+ node=mdl_mesh()
+ compile_obj_transform(obj, node.transform)
+ node.pstr_name = sr_compile_string(obj.name)
+ ent_type = obj_ent_type( obj )
+
+ node.entity_id = 0
+
+ if ent_type != 'none':#{
+ ent_id_lwr = sr_compile.entity_ids[obj.name]
+ ent_id_upr = get_entity_enum_id( obj_ent_type(obj) )
+ node.entity_id = (ent_id_upr << 16) | ent_id_lwr
+ #}
+
+ node.submesh_start, node.submesh_count, node.armature_id = \
+ sr_compile_mesh_internal( obj )
+
sr_compile.mesh_data.extend(bytearray(node))
#}
mesh_count = 0
for obj in collection.all_objects: #{
- if obj.type == 'MESH': mesh_count += 1
+ if obj.type == 'MESH':#{
+ mesh_count += 1
+ #}
ent_type = obj_ent_type( obj )
if ent_type == 'none': continue
for obj in collection.all_objects:#{
if obj.type == 'MESH':#{
i+=1
+
+ ent_type = obj_ent_type( obj )
+
+ # entity ignore mesh list
+ #
+ if ent_type == 'ent_traffic': continue
+ #--------------------------
+
print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}', end='\r' )
sr_compile_mesh( obj )
#}
route_gates = []
route_curves = []
routes = []
+ traffics = []
for obj in col.objects:#{
if obj.type == 'ARMATURE': pass
#}
elif ent_type == 'ent_route':
routes += [obj]
+ elif ent_type == 'ent_traffic':
+ traffics += [obj]
#}
#}
sr_ent_push( route )
#}
+ for obj in traffics:#{
+ traffic = ent_traffic()
+ compile_obj_transform( obj, traffic.transform )
+ traffic.submesh_start, traffic.submesh_count, _ = \
+ sr_compile_mesh_internal( obj )
+
+ # find best subsection
+
+ graph_keys = list(dij.graph)
+ min_dist = 100.0
+ best_point = 0
+
+ for j in range(len(dij.points)):#{
+ point = dij.points[j]
+ dist = (point-obj.location).magnitude
+
+ if dist < min_dist:#{
+ min_dist = dist
+ best_point = j
+ #}
+ #}
+
+ # scan to each edge
+ best_begin = best_point
+ best_end = best_point
+
+ while True:#{
+ map0 = dij.subsections[best_begin]
+ if map0[1] == -1: break
+ best_begin = map0[1]
+ #}
+ while True:#{
+ map1 = dij.subsections[best_end]
+ if map1[2] == -1: break
+ best_end = map1[2]
+ #}
+
+ traffic.start_node = routenode_count + best_begin
+ traffic.node_count = best_end - best_begin
+ traffic.index = best_point - best_begin
+ traffic.speed = obj.SR_data.ent_traffic[0].speed
+ traffic.t = 0.0
+
+ sr_ent_push(traffic)
+ #}
+
for point in dij.points:#{
rn = ent_route_node()
rn.co[0] = point[0]
routenode_count += len(dij.points)
#}
-
print( F"[SR] Writing file" )
file_array_instructions = {}
fp = open( path, "wb" )
header = mdl_header()
- header.version = 40
+ header.version = 100
sr_array_title( header.arrays, \
'index', len(file_array_instructions), \
sizeof(mdl_array), header_size )
class SR_OBJECT_ENT_TRAFFIC(bpy.types.PropertyGroup):
#{
- track: bpy.props.PointerProperty(\
- type=bpy.types.Object, name='track', \
- poll=lambda self,obj: sr_filter_ent_type(obj,['ent_route_node']))
+ speed: bpy.props.FloatProperty(default=1.0)
#}
class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
typedef struct ent_audio ent_audio;
typedef struct ent_index ent_index;
typedef struct ent_marker ent_marker;
+typedef struct ent_traffic ent_traffic;
typedef struct ent_font ent_font;
typedef struct ent_font_variant ent_font_variant;
typedef struct ent_glyph ent_glyph;
u32 pstr_alias;
};
+struct ent_traffic{
+ mdl_transform transform;
+ u32 submesh_start,
+ submesh_count,
+ start_node,
+ node_count;
+ float speed,
+ t;
+ u32 index; /* into the path */
+};
+
VG_STATIC ent_marker *ent_find_marker( mdl_context *mdl,
mdl_array_ptr *arr, const char *alias )
{
#include "common.h"
+#define MDL_VERSION_NR 100
+
enum mdl_shader
{
k_shader_standard = 0,
enum material_flag
{
- k_material_flag_skate_target = 0x1,
- k_material_flag_collision = 0x2,
- k_material_flag_grow_grass = 0x4,
- k_material_flag_grindable = 0x8,
- k_material_flag_invisible = 0x10,
- k_material_flag_boundary = 0x20
+ k_material_flag_skate_target = 0x00000001,
+ k_material_flag_collision = 0x00000002,
+ k_material_flag_grow_grass = 0x00000004,
+ k_material_flag_grindable = 0x00000008,
+ k_material_flag_invisible = 0x00000010,
+ k_material_flag_boundary = 0x00000020
};
#pragma pack(push,1)
v4f q;
};
-struct mdl_submesh
-{
- u32 indice_start,
- indice_count,
- vertex_start,
- vertex_count;
-
- boxf bbx;
- u32 material_id;
-};
-
struct mdl_material
{
u32 pstr_name,
enum bone_flag
{
- k_bone_flag_deform = 0x1,
- k_bone_flag_ik = 0x2,
- k_bone_flag_cone_constraint = 0x4
+ k_bone_flag_deform = 0x00000001,
+ k_bone_flag_ik = 0x00000002,
+ k_bone_flag_cone_constraint = 0x00000004
};
enum bone_collider
u32 offset;
};
+struct mdl_submesh
+{
+ u32 indice_start,
+ indice_count,
+ vertex_start,
+ vertex_count;
+
+ boxf bbx;
+ u16 material_id, flags;
+};
+
+enum esubmesh_flags
+{
+ k_submesh_flag_none = 0x0000,
+ k_submesh_flag_consumed = 0x0001
+};
+
struct mdl_mesh
{
mdl_transform transform;
if( l != 1 )
mdl_load_fatal_corrupt( mdl );
+ if( mdl->info.version != MDL_VERSION_NR ){
+ vg_warn( "For model: %s\n", path );
+ vg_warn( " version: %u (current: %u)\n", mdl->info.version,
+ MDL_VERSION_NR );
+
+ vg_fatal_exit_loop( "Legacy model version incompatable" );
+ }
+
mdl_load_array_file( mdl, &mdl->index, &mdl->info.index, lin_alloc );
mdl_array *pack = mdl_find_array( mdl, "pack" );
ent_audio_clip,
ent_audio,
- ent_volume;
+ ent_volume,
+ ent_traffic;
ent_gate *rendering_gate;
world_routes_update_timer_texts( world );
world_routes_update( world );
//world_routes_debug( world );
+
+ /* ---- traffic -------- */
+
+ for( u32 i=0; i<mdl_arrcount( &world->ent_traffic ); i++ ){
+ ent_traffic *traffic = mdl_arritm( &world->ent_traffic, i );
+
+ u32 i1 = traffic->index,
+ i0,
+ i2 = i1+1;
+
+ if( i1 == 0 ) i0 = traffic->node_count-1;
+ else i0 = i1-1;
+
+ if( i2 >= traffic->node_count ) i2 = 0;
+
+ i0 += traffic->start_node;
+ i1 += traffic->start_node;
+ i2 += traffic->start_node;
+
+ v3f h[3];
+
+ ent_route_node *rn0 = mdl_arritm( &world->ent_route_node, i0 ),
+ *rn1 = mdl_arritm( &world->ent_route_node, i1 ),
+ *rn2 = mdl_arritm( &world->ent_route_node, i2 );
+
+ v3_copy( rn1->co, h[1] );
+ v3_lerp( rn0->co, rn1->co, 0.5f, h[0] );
+ v3_lerp( rn1->co, rn2->co, 0.5f, h[2] );
+
+ float const k_sample_dist = 0.0025f;
+ v3f pc, pd;
+ eval_bezier3( h[0], h[1], h[2], traffic->t, pc );
+ eval_bezier3( h[0], h[1], h[2], traffic->t+k_sample_dist, pd );
+
+ v3f v0;
+ v3_sub( pd, pc, v0 );
+ float length = vg_maxf( 0.0001f, v3_length( v0 ) );
+ v3_muls( v0, 1.0f/length, v0 );
+
+ float mod = k_sample_dist / length;
+
+ traffic->t += traffic->speed * vg.time_delta * mod;
+
+ if( traffic->t > 1.0f ){
+ traffic->t -= 1.0f;
+
+ if( traffic->t > 1.0f ) traffic->t = 0.0f;
+
+ traffic->index ++;
+
+ if( traffic->index >= traffic->node_count )
+ traffic->index = 0;
+ }
+
+ v3_copy( pc, traffic->transform.co );
+
+ float a = atan2f( -v0[0], v0[2] );
+ q_axis_angle( traffic->transform.q, (v3f){0.0f,1.0f,0.0f}, -a );
+
+ vg_line_pt3( traffic->transform.co, 0.3f, VG__BLUE );
+ }
/* ---- SFD ------------ */
scene_copy_slice( world->scene_no_collide, &mat->sm_no_collide );
}
+ /* this FIXME TODO IMPORTANT is going here because need to write down.
+ *
+ * acuire_thread_sync; replace this with a buffer that you fill up with
+ * opengl loader commands in a seperate memory area. the operation blocks
+ * if the buffer is full, then those instructions get ran on the sync line.
+ * (start of the frame)
+ *
+ * also blocks if the other thread is executing the instructions, obviously.
+ *
+ * this prevents rapid context swaps between threads.
+ *
+ * guessing a 50mb loader buffer approx.
+ *
+ * TODO also: fadeout loading screen!
+ */
+
+ for( u32 i=0; i<mdl_arrcount( &world->ent_traffic ); i++ ){
+ ent_traffic *vehc = mdl_arritm( &world->ent_traffic, i );
+
+ for( u32 j=0; j<vehc->submesh_count; j++ ){
+ mdl_submesh *sm = mdl_arritm( &world->meta.submeshs,
+ vehc->submesh_start+j );
+
+ if( sm->flags & k_submesh_flag_consumed ){
+ continue;
+ }
+
+ m4x3f identity;
+ m4x3_identity( identity );
+ scene_add_mdl_submesh( world->scene_no_collide, &world->meta,
+ sm, identity );
+
+ scene_copy_slice( world->scene_no_collide, sm );
+ sm->flags |= k_submesh_flag_consumed;
+ }
+ }
+
/* upload and free that */
vg_acquire_thread_sync();
{
world->textures = vg_linear_alloc( world->heap,
vg_align8(sizeof(GLuint)*world->texture_count) );
+ /* TODO FIXME IMPORTANT
+ *
+ * this is another area that would benefit from our load thread buffer idea.
+ * could get a stall if lots of textures, since its freading, we're locking
+ * the frame up from drawing based on that disk read!!! terrible!
+ */
+
vg_acquire_thread_sync();
{
/* error texture */
mdl_load_array( meta, &world->ent_audio_clip,"ent_audio_clip", heap );
mdl_load_array( meta, &world->ent_audio, "ent_audio", heap );
mdl_load_array( meta, &world->ent_volume, "ent_volume", heap );
+ mdl_load_array( meta, &world->ent_traffic, "ent_traffic", heap );
/* process resources from pack */
world_process_resources( world );
vg_tex2d_bind( &tex_terrain_noise, 0 );
}
-typedef void (*func_bind_point)( world_instance *world,
- struct world_surface *mat );
-
-VG_STATIC void world_render_if( world_instance *world,
- enum mdl_shader shader,
- enum geo_type geo_type,
- func_bind_point bind_point )
+struct world_pass{
+ camera *cam;
+ enum mdl_shader shader;
+ enum geo_type geo_type;
+
+ void (*fn_bind_textures)( world_instance *world,
+ struct world_surface *mat );
+ void (*fn_set_mdl)( m4x3f mdl );
+ void (*fn_set_uPvmPrev)( m4x4f pvm );
+};
+
+VG_STATIC void world_render_if( world_instance *world, struct world_pass *pass )
{
-
- for( int i=0; i<world->surface_count; i++ )
- {
+ for( int i=0; i<world->surface_count; i++ ){
struct world_surface *mat = &world->surfaces[i];
- if( mat->info.shader == shader )
- {
+ if( mat->info.shader == pass->shader ){
mdl_submesh *sm;
- if( geo_type == k_geo_type_solid )
+ if( pass->geo_type == k_geo_type_solid )
sm = &mat->sm_geo;
else
sm = &mat->sm_no_collide;
if( !sm->indice_count )
continue;
- bind_point( world, mat );
+ m4x3f mmdl;
+ m4x3_identity( mmdl );
+ pass->fn_set_mdl( mmdl );
+ pass->fn_set_uPvmPrev( pass->cam->mtx_prev.pv );
+
+ pass->fn_bind_textures( world, mat );
mdl_draw_submesh( sm );
+
+ for( u32 j=0; j<mdl_arrcount( &world->ent_traffic ); j++ ){
+ ent_traffic *traffic = mdl_arritm( &world->ent_traffic, j );
+
+ for( u32 k=0; k<traffic->submesh_count; k++ ){
+ sm = mdl_arritm( &world->meta.submeshs,
+ traffic->submesh_start+k );
+
+ q_m3x3( traffic->transform.q, mmdl );
+ v3_copy( traffic->transform.co, mmdl[3] );
+
+ m4x4f m4mdl;
+ m4x3_expand( mmdl, m4mdl );
+ m4x4_mul( pass->cam->mtx_prev.pv, m4mdl, m4mdl );
+
+ pass->fn_set_mdl( mmdl );
+ pass->fn_set_uPvmPrev( m4mdl );
+
+ mdl_draw_submesh( sm );
+ }
+ }
}
}
}
VG_STATIC
-void world_render_both_stages( world_instance *world,
- enum mdl_shader shader,
- func_bind_point bind_point )
+void world_render_both_stages( world_instance *world, struct world_pass *pass )
{
mesh_bind( &world->mesh_geo );
- world_render_if( world, shader, k_geo_type_solid, bind_point );
+ pass->geo_type = k_geo_type_solid;
+ world_render_if( world, pass );
glDisable( GL_CULL_FACE );
mesh_bind( &world->mesh_no_collide );
- world_render_if( world, shader, k_geo_type_nonsolid, bind_point );
+ pass->geo_type = k_geo_type_nonsolid;
+ world_render_if( world, pass );
glEnable( GL_CULL_FACE );
}
VG_STATIC void bindpoint_diffuse_texture1( world_instance *world,
struct world_surface *mat )
+
{
glActiveTexture( GL_TEXTURE1 );
glBindTexture( GL_TEXTURE_2D, world->textures[ mat->info.tex_diffuse ] );
VG_STATIC void render_world_vb( world_instance *world, camera *cam )
{
- m4x3f identity_matrix;
- m4x3_identity( identity_matrix );
-
shader_scene_vertex_blend_use();
shader_scene_vertex_blend_uTexGarbage(0);
shader_scene_vertex_blend_uTexGradients(1);
vg_tex2d_bind( &tex_terrain_noise, 0 );
shader_scene_vertex_blend_uPv( cam->mtx.pv );
- shader_scene_vertex_blend_uPvmPrev( cam->mtx_prev.pv );
- shader_scene_vertex_blend_uMdl( identity_matrix );
shader_scene_vertex_blend_uCamera( cam->transform[3] );
- world_render_both_stages( world, k_shader_standard_vertex_blend,
- bindpoint_diffuse_texture1 );
+ struct world_pass pass = {
+ .shader = k_shader_standard_vertex_blend,
+ .cam = cam,
+ .fn_bind_textures = bindpoint_diffuse_texture1,
+ .fn_set_mdl = shader_scene_vertex_blend_uMdl,
+ .fn_set_uPvmPrev = shader_scene_vertex_blend_uPvmPrev,
+ };
+
+ world_render_both_stages( world, &pass );
}
VG_STATIC void render_world_standard( world_instance *world, camera *cam )
{
- m4x3f identity_matrix;
- m4x3_identity( identity_matrix );
-
shader_scene_standard_use();
shader_scene_standard_uTexGarbage(0);
shader_scene_standard_uTexMain(1);
shader_scene_standard_uPv( cam->mtx.pv );
- shader_scene_standard_uPvmPrev( cam->mtx_prev.pv );
world_link_lighting_ub( world, _shader_scene_standard.id );
world_bind_position_texture( world, _shader_scene_standard.id,
_uniform_scene_standard_uLightsIndex, 4 );
bind_terrain_noise();
-
- shader_scene_standard_uMdl( identity_matrix );
shader_scene_standard_uCamera( cam->transform[3] );
- world_render_both_stages( world, k_shader_standard,
- bindpoint_diffuse_texture1 );
+ struct world_pass pass = {
+ .shader = k_shader_standard,
+ .cam = cam,
+ .fn_bind_textures = bindpoint_diffuse_texture1,
+ .fn_set_mdl = shader_scene_standard_uMdl,
+ .fn_set_uPvmPrev = shader_scene_standard_uPvmPrev,
+ };
+
+ world_render_both_stages( world, &pass );
}
VG_STATIC void render_world_alphatest( world_instance *world, camera *cam )
{
- m4x3f identity_matrix;
- m4x3_identity( identity_matrix );
-
shader_scene_standard_alphatest_use();
shader_scene_standard_alphatest_uTexGarbage(0);
shader_scene_standard_alphatest_uTexMain(1);
shader_scene_standard_alphatest_uPv( cam->mtx.pv );
- shader_scene_standard_alphatest_uPvmPrev( cam->mtx_prev.pv );
world_link_lighting_ub( world, _shader_scene_standard_alphatest.id );
world_bind_position_texture( world, _shader_scene_standard_alphatest.id,
bind_terrain_noise();
- shader_scene_standard_alphatest_uMdl( identity_matrix );
+
shader_scene_standard_alphatest_uCamera( cam->transform[3] );
glDisable(GL_CULL_FACE);
-
- world_render_both_stages( world, k_shader_standard_cutout,
- bindpoint_diffuse_texture1 );
+
+ struct world_pass pass = {
+ .shader = k_shader_standard_cutout,
+ .cam = cam,
+ .fn_bind_textures = bindpoint_diffuse_texture1,
+ .fn_set_mdl = shader_scene_standard_alphatest_uMdl,
+ .fn_set_uPvmPrev = shader_scene_standard_alphatest_uPvmPrev,
+ };
+
+ world_render_both_stages( world, &pass );
glEnable(GL_CULL_FACE);
}
VG_STATIC void render_terrain( world_instance *world, camera *cam )
{
- m4x3f identity_matrix;
- m4x3_identity( identity_matrix );
-
shader_scene_terrain_use();
shader_scene_terrain_uTexGarbage(0);
shader_scene_terrain_uTexGradients(1);
vg_tex2d_bind( &tex_terrain_noise, 0 );
shader_scene_terrain_uPv( cam->mtx.pv );
- shader_scene_terrain_uPvmPrev( cam->mtx_prev.pv );
-
- shader_scene_terrain_uMdl( identity_matrix );
shader_scene_terrain_uCamera( cam->transform[3] );
- world_render_both_stages( world, k_shader_terrain_blend, bindpoint_terrain );
+ struct world_pass pass = {
+ .shader = k_shader_terrain_blend,
+ .cam = cam,
+ .fn_bind_textures = bindpoint_terrain,
+ .fn_set_mdl = shader_scene_terrain_uMdl,
+ .fn_set_uPvmPrev = shader_scene_terrain_uPvmPrev,
+ };
+
+ world_render_both_stages( world, &pass );
}
VG_STATIC void render_sky( world_instance *world, camera *cam )
state->g_debug_indices = k_debug_light_indices;
state->g_light_preview = k_light_preview;
state->g_debug_complexity = k_debug_light_complexity;
-
- state->g_time_of_day = vg_fractf( g_time );
+state->g_time_of_day = vg_fractf( g_time );
state->g_day_phase = cosf( state->g_time_of_day * VG_PIf * 2.0f );
state->g_sunset_phase= cosf( state->g_time_of_day * VG_PIf * 4.0f + VG_PIf );