i hope your hapy
authorhgn <hgodden00@gmail.com>
Sun, 26 Mar 2023 22:09:00 +0000 (23:09 +0100)
committerhgn <hgodden00@gmail.com>
Sun, 26 Mar 2023 22:09:00 +0000 (23:09 +0100)
57 files changed:
audio.h
blender_export.py
entity.h [new file with mode: 0644]
maps_src/mp_arizona.mdl
maps_src/mp_gridmap.mdl
maps_src/mp_home.mdl
maps_src/mp_mtzero.mdl
model.h
models_src/ch_empty.mdl [deleted file]
models_src/ch_jordan.mdl
models_src/ch_mike.mdl [deleted file]
models_src/ch_new.mdl
models_src/ch_outlaw.mdl
models_src/mp_test.mdl [deleted file]
models_src/rs_cars.mdl [deleted file]
models_src/rs_chicken.mdl [deleted file]
models_src/rs_foliage.mdl [deleted file]
models_src/rs_gate.mdl
models_src/rs_menu.mdl [deleted file]
models_src/rs_scoretext.mdl [deleted file]
models_src/rs_skydome.mdl
models_src/rs_vig.mdl [deleted file]
player.c
player.h
player_api.h
player_model.h
player_ragdoll.h
player_skate.c
player_skate.h
player_walk.c
rigidbody.h
scene.h
shaders/common_world.glsl
shaders/model_character_view.h
shaders/model_gate.h
shaders/model_gate_lq.fs
shaders/model_sky.h
shaders/scene_depth.h
shaders/scene_position.h
shaders/scene_route.fs
shaders/scene_route.h
shaders/scene_standard.h
shaders/scene_standard_alphatest.h
shaders/scene_terrain.h
shaders/scene_vertex_blend.h
shaders/scene_water.h
shaders/scene_water_fast.h
skaterift.c
skeleton.h
world.h
world_gate.h
world_gen.h
world_render.h
world_routes.h
world_sfd.h
world_volumes.h
world_water.h

diff --git a/audio.h b/audio.h
index eb58b267412cd67e9bc0b6cf5614f97df51310fe..4f735099fb87fddbb8d6f40524c55bc885b5b664 100644 (file)
--- a/audio.h
+++ b/audio.h
@@ -384,17 +384,14 @@ VG_STATIC enum audio_sprite_type audio_sample_sprite_random( v3f origin,
 
    world_instance *world = get_active_world();
    
-   if( ray_world( world, pos, (v3f){0.0f,-1.0f,0.0f}, &contact ) )
-   {
-      struct world_material *mat = ray_hit_material( world, &contact );
+   if( ray_world( world, pos, (v3f){0.0f,-1.0f,0.0f}, &contact ) ){
+      struct world_surface *mat = ray_hit_surface( world, &contact );
 
-      if( mat->info.surface_prop == k_surface_prop_grass) 
-      {
+      if( mat->info.surface_prop == k_surface_prop_grass){
          v3_copy( contact.pos, output );
          return k_audio_sprite_type_grass;
       }
-      else
-      {
+      else{
 #if 0
          vg_line( pos, contact.pos, 0xff0000ff );
          vg_line_pt3( contact.pos, 0.3f, 0xff0000ff );
index 091718d15d5b660c240d4b8017197832dbbfba1c..dee0d8f1ee0102774ce2f48da898b035f46f9bc6 100644 (file)
@@ -1,22 +1,3 @@
-# 
-# =============================================================================
-# 
-# Copyright  .        . .       -----, ,----- ,---.   .---.
-# 2021-2023  |\      /| |           /  |      |    | |    /|
-#            | \    / | +--        /   +----- +---'  |   / |
-#            |  \  /  | |         /    |      |   \  |  /  |
-#            |   \/   | |        /     |      |    \ | /   |
-#            '        ' '--' [] '----- '----- '     ' '---'  SOFTWARE
-# 
-# =============================================================================
-# 
-#       Python exporter for Blender, compiles .mdl format for Skate Rift.
-#
-# Its really slow, sorry, I don't know how to speed it up.
-# Also not sure why you need to put # before {} in code blocks, there is errors 
-# otherwise
-#
-
 import bpy, math, gpu, os
 import cProfile
 from ctypes import *
@@ -24,12 +5,12 @@ from mathutils import *
 from gpu_extras.batch import batch_for_shader
 
 bl_info = {
-   "name":"Skate Rift model compiler",
+   "name":"Skaterift .mdl exporter",
    "author": "Harry Godden (hgn)",
    "version": (0,2),
    "blender":(3,1,0),
    "location":"Export",
-   "descriptin":"",
+   "description":"",
    "warning":"",
    "wiki_url":"",
    "category":"Import/Export",
@@ -46,9 +27,15 @@ class mdl_vert(Structure):              # 48 bytes. Quite large. Could compress
                ("groups",c_uint8*4)]
 #}
 
+class mdl_transform(Structure):
+#{
+   _fields_ = [("co",c_float*3),
+               ( "s",c_float*3),
+               ( "q",c_float*4)]
+#}
+
 class mdl_submesh(Structure):
 #{
-   _pack_ = 1
    _fields_ = [("indice_start",c_uint32),
                ("indice_count",c_uint32),
                ("vertex_start",c_uint32),
@@ -57,17 +44,8 @@ class mdl_submesh(Structure):
                ("material_id",c_uint32)]        # index into the material array
 #}
 
-class mdl_texture(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("pstr_name",c_uint32),
-               ("pack_offset",c_uint32),
-               ("pack_length",c_uint32)]
-#}
-
 class mdl_material(Structure):
 #{
-   _pack_ = 1
    _fields_ = [("pstr_name",c_uint32),
                ("shader",c_uint32),
                ("flags",c_uint32),
@@ -75,1984 +53,1745 @@ class mdl_material(Structure):
                ("colour",c_float*4),
                ("colour1",c_float*4),
                ("tex_diffuse",c_uint32),
-               ("tex_decal",c_uint32),
-               ("tex_normal",c_uint32)]
+               ("tex_none0",c_uint32),
+               ("tex_none1",c_uint32)]
 #}
 
-class mdl_node(Structure):
+class mdl_bone(Structure):
 #{
-   _pack_ = 1
-   _fields_ = [("co",c_float*3),
-               ( "q",c_float*4),
-               ( "s",c_float*3),
-               ("sub_uid",c_uint32),        # dont use
-               ("submesh_start",c_uint32),
-               ("submesh_count",c_uint32),
-               ("classtype",c_uint32),
-               ("offset",c_uint32),
+   _fields_ = [("co",c_float*3),("end",c_float*3),
                ("parent",c_uint32),
-               ("pstr_name",c_uint32)]
+               ("collider",c_uint32),
+               ("ik_target",c_uint32),
+               ("ik_pole",c_uint32),
+               ("flags",c_uint32),
+               ("pstr_name",c_uint32),
+               ("hitbox",(c_float*3)*2),
+               ("conevx",c_float*3),("conevy",c_float*3),("coneva",c_float*3),
+               ("conet",c_float)]
 #}
 
-class mdl_header(Structure):
+class mdl_armature(Structure):
 #{
-   _pack_ = 1
-   _fields_ = [("identifier",c_uint32),
-               ("version",c_uint32),
-               ("file_length",c_uint32),
-               ("pad0",c_uint32),
+   _fields_ = [("transform",mdl_transform),
+               ("bone_start",c_uint32),
+               ("bone_count",c_uint32),
+               ("anim_start",c_uint32),
+               ("anim_count",c_uint32)]
+#}
 
-               ("node_count",c_uint32),
-               ("node_offset",c_uint32),
+class mdl_animation(Structure):
+#{
+   _fields_ = [("pstr_name",c_uint32),
+               ("length",c_uint32),
+               ("rate",c_float),
+               ("keyframe_start",c_uint32)]
+#}
 
+class mdl_mesh(Structure):
+#{
+   _fields_ = [("transform",mdl_transform),
+               ("submesh_start",c_uint32),
                ("submesh_count",c_uint32),
-               ("submesh_offset",c_uint32),
-
-               ("material_count",c_uint32),
-               ("material_offset",c_uint32),
+               ("pstr_name",c_uint32),
+               ("flags",c_uint32),
+               ("armature_id",c_uint32)]
+#}
 
-               ("texture_count",c_uint32),
-               ("texture_offset",c_uint32),
+class mdl_file(Structure):
+#{
+   _fields_ = [("path",c_uint32),
+               ("pack_offset",c_uint32),
+               ("pack_size",c_uint32)]
+#}
 
-               ("anim_count",c_uint32),
-               ("anim_offset",c_uint32),
+class mdl_texture(Structure):
+#{
+   _fields_ = [("file",mdl_file),
+               ("type",c_uint32)]
+#}
 
-               ("entdata_size",c_uint32),
-               ("entdata_offset",c_uint32),
-               
-               ("strings_size",c_uint32),
-               ("strings_offset",c_uint32),
+class mdl_array(Structure):
+#{
+   _fields_ = [("file_offset",c_uint32),
+               ("item_count",c_uint32),
+               ("item_size",c_uint32),
+               ("name",c_byte*16)]
+#}
 
-               ("keyframe_count",c_uint32),
-               ("keyframe_offset",c_uint32),
+class mdl_header(Structure):
+#{
+   _fields_ = [("version",c_uint32),
+               ("arrays",mdl_array)]
+#}
 
-               ("vertex_count",c_uint32),
-               ("vertex_offset",c_uint32),
+class ent_spawn(Structure):
+#{
+   _fields_ = [("transform",mdl_transform),
+               ("pstr_name",c_uint32)]
+#}
 
-               ("indice_count",c_uint32),
-               ("indice_offset",c_uint32),
+class ent_light(Structure):
+#{
+   _fields_ = [("transform",mdl_transform),
+               ("daytime",c_uint32),
+               ("type",c_uint32),
+               ("colour",c_float*4),
+               ("angle",c_float),
+               ("range",c_float),
+               ("inverse_world",(c_float*3)*4),  # Runtime
+               ("angle_sin_cos",(c_float*2))]    # Runtime
+#}
 
-               ("pack_size",c_uint32),
-               ("pack_offset",c_uint32)]
+class version_refcount_union(Union):
+#{
+   _fields_ = [("timing_version",c_uint32),
+               ("ref_count",c_uint8)]
 #}
 
-class mdl_animation(Structure):
+class ent_gate(Structure):
 #{
-   _pack_ = 1
-   _fields_ = [("pstr_name",c_uint32),
-               ("length",c_uint32),
-               ("rate",c_float),
-               ("offset",c_uint32)]
+   _fields_ = [("type",c_uint32),
+               ("target", c_uint32),
+               ("dimensions", c_float*3),
+               ("co", (c_float*3)*2),
+               ("q", (c_float*4)*2),
+               ("to_world",(c_float*3)*4),
+               ("transport",(c_float*3)*4),
+               ("_anonymous_union",version_refcount_union),
+               ("timing_time",c_double),
+               ("routes",c_uint16*4)]
 #}
 
-class mdl_keyframe(Structure):
+class ent_route_node(Structure):
 #{
-   _pack_ = 1
    _fields_ = [("co",c_float*3),
-               ("q",c_float*4),
-               ("s",c_float*3)]
+               ("ref_count",c_uint8),
+               ("ref_total",c_uint8)]
 #}
 
-# ---------------------------------------------------------------------------- #
-#                                                                              #
-#                            Entity definitions                                #
-#                                                                              #
-# ---------------------------------------------------------------------------- #
-#
-# ctypes _fields_ defines the data which is filled in by:
-#  def encode_obj( _, node, node_def ):
-#
-# gizmos get drawn into the viewport via:
-#  @staticmethod
-#  def draw_scene_helpers( obj ):
-#
-# editor enterface, simiraliy:
-#  @staticmethod
-#  def editor_interface( layout, obj ):
-#
+class ent_path_index(Structure):
+#{
+   _fields_ = [("index",c_uint16)]
+#}
 
-# 000: Intrinsic
-# ---------------------------------------------------------------------------- #
+class ent_checkpoint(Structure):
+#{
+   _fields_ = [("gate_index",c_uint16),
+               ("path_start",c_uint16),
+               ("path_count",c_uint16)]
+#}
 
-#  Purpose: intrinsic bone type, stores collision information and limits too
-#
-class classtype_bone(Structure):
+class ent_route(Structure):
 #{
-   _pack_ = 1
-   _fields_ = [("flags",c_uint32),
-               ("ik_target",c_uint32),
-               ("ik_pole",c_uint32),
-               ("hitbox",(c_float*3)*2),
-               ("conevx",c_float*3),
-               ("conevy",c_float*3),
-               ("coneva",c_float*3),
-               ("conet",c_float)]
+   _fields_ = [("transform",mdl_transform),
+               ("pstr_name",c_uint32),
+               ("checkpoints_start",c_uint16),
+               ("checkpoints_count",c_uint16),
+               ("colour",c_float*4),
+               ("active",c_uint32), #runtime
+               ("factive",c_float),
+               ("board_transform",(c_float*3)*4),
+               ("sm",mdl_submesh),
+               ("latest_pass",c_double)]
+#}
 
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 1
+def obj_ent_type( obj ):
+#{
+   if obj.type == 'ARMATURE': return 'mdl_armature'
+   elif obj.type == 'LIGHT': return 'ent_light'
+   else: return obj.SR_data.ent_type
+#}
 
-      armature_def = node_def['linked_armature']
-      obj = node_def['bone']
-      
-      _.flags = node_def['deform']
-      
-      if 'ik_target' in node_def:
-      #{
-         _.flags |= 0x2
-         _.ik_target = armature_def['bones'].index( node_def['ik_target'] )
-         _.ik_pole   = armature_def['bones'].index( node_def['ik_pole'] )
-      #}
-      
-      # For ragdolls
-      #
-      if obj.cv_data.collider != 'collider_none':
-      #{
-         if obj.cv_data.collider == 'collider_box':
-            _.flags |= 0x4
-         else:
-            _.flags |= 0x8
-
-         _.hitbox[0][0] =  obj.cv_data.v0[0]
-         _.hitbox[0][1] =  obj.cv_data.v0[2]
-         _.hitbox[0][2] = -obj.cv_data.v1[1]
-         _.hitbox[1][0] =  obj.cv_data.v1[0]
-         _.hitbox[1][1] =  obj.cv_data.v1[2]
-         _.hitbox[1][2] = -obj.cv_data.v0[1]
-      #}
+def sr_filter_ent_type( obj, ent_type ):
+#{
+   if obj == bpy.context.active_object: return False
 
-      if obj.cv_data.con0:
-      #{
-         _.flags |= 0x100
-         _.conevx[0] =  obj.cv_data.conevx[0]
-         _.conevx[1] =  obj.cv_data.conevx[2]
-         _.conevx[2] = -obj.cv_data.conevx[1]
-         _.conevy[0] =  obj.cv_data.conevy[0]
-         _.conevy[1] =  obj.cv_data.conevy[2]
-         _.conevy[2] = -obj.cv_data.conevy[1]
-         _.coneva[0] =  obj.cv_data.coneva[0]
-         _.coneva[1] =  obj.cv_data.coneva[2]
-         _.coneva[2] = -obj.cv_data.coneva[1]
-         _.conet = obj.cv_data.conet
+   for c0 in obj.users_collection:#{
+      for c1 in bpy.context.active_object.users_collection:#{
+         if c0 == c1:#{
+            return ent_type == obj_ent_type( obj )
+         #}
       #}
    #}
+
+   return False
 #}
 
-#  Purpose: defines the allocation requirements for a skeleton
-#
-class classtype_skeleton(Structure):
+def compile_obj_transform( obj, transform ):
 #{
-   _pack_ = 1
-   _fields_ = [("channels",c_uint32),
-               ("ik_count",c_uint32),
-               ("collider_count",c_uint32),
-               ("anim_start",c_uint32),
-               ("anim_count",c_uint32)]
+   co = obj.matrix_world @ Vector((0,0,0))
+   q = obj.matrix_local.to_quaternion()
+   s = obj.scale
+   
+   # Setup transform
+   #
+   transform.co[0] =  co[0]
+   transform.co[1] =  co[2]
+   transform.co[2] = -co[1]
+   transform.q[0] =  q[1]
+   transform.q[1] =  q[3]
+   transform.q[2] = -q[2]
+   transform.q[3] =  q[0]
+   transform.s[0] = s[0]
+   transform.s[1] = s[2]
+   transform.s[2] = s[1]
+#}
 
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 2
-      
-      _.channels        = len( node_def['bones'] )
-      _.ik_count        = node_def['ik_count']
-      _.collider_count  = node_def['collider_count']
-      _.anim_start      = node_def['anim_start']
-      _.anim_count      = node_def['anim_count']
-   #}
+def int_align_to( v, align ):
+#{
+   while(v%align)!=0: v += 1
+   return v
 #}
 
-#  Purpose: links an mesh node to a type 11
-#
-class classtype_skin(Structure):
+def bytearray_align_to( buffer, align, w=b'\xaa' ):
 #{
-   _pack_ = 1
-   _fields_ = [("skeleton",c_uint32)]
+   while (len(buffer) % align) != 0: buffer.extend(w)
+   return buffer
+#}
 
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 3
-      
-      armature_def = node_def['linked_armature']
-      _.skeleton = armature_def['obj'].cv_data.uid
+def bytearray_print_hex( s, w=16 ):
+#{
+   for r in range((len(s)+(w-1))//w):#{
+      i0=(r+0)*w
+      i1=min((r+1)*w,len(s))
+      print( F'{r*w:06x}| \x1B[31m', end='')
+      print( F"{' '.join('{:02x}'.format(x) for x in s[i0:i1]):<48}",end='' )
+      print( "\x1B[0m", end='')
+      print( ''.join(chr(x) if (x>=33 and x<=126) else '.' for x in s[i0:i1] ) )
    #}
 #}
 
-#  Purpose: world light
-#
-class classtype_world_light( Structure ):
+def sr_compile_string( s ):
 #{
-   _pack_ = 1
-   _fields_ = [("type",c_uint32),
-               ("colour",c_float*4),
-               ("angle",c_float),
-               ("range",c_float)]
-
-   def encode_obj(_, node, node_def):
-   #{
-      node.classtype = 4
-
-      obj  = node_def['obj']
-      data = obj.data
-      _.colour[0] = data.color[0]
-      _.colour[1] = data.color[1]
-      _.colour[2] = data.color[2]
-      _.colour[3] = data.energy
-      _.range = data.cutoff_distance # this has to be manually set
-                                     # TODO: At some point, automate a min
-                                     #       threshold value
-
-      if obj.data.type == 'POINT':
-      #{
-         _.type = 0
-         _.angle = 0.0
-      #}
-      elif obj.data.type == 'SPOT':
-      #{
-         _.type = 1
-         _.angle = data.spot_size*0.5
-      #}
+   if s in sr_compile.string_cache: return sr_compile.string_cache[s]
+   
+   index = len( sr_compile.string_data )
+   sr_compile.string_cache[s] = index
+   sr_compile.string_data.extend( s.encode('utf-8') )
+   sr_compile.string_data.extend( b'\0' )
 
-      if data.cv_data.bp0:
-         _.type += 2
-   #}
+   bytearray_align_to( sr_compile.string_data, 4 )
+   return index
+#}
 
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      pass
-   #}
+def material_tex_image(v):
+#{
+    return {
+       "Image Texture":
+       {
+          "image": F"{v}"
+       }
+    }
 #}
 
-# 100: Gates
-# ---------------------------------------------------------------------------- #
+cxr_graph_mapping = \
+{
+   # Default shader setup 
+   "Principled BSDF":
+   {
+      "Base Color":
+      {
+         "Image Texture":
+         {
+            "image": "tex_diffuse"
+         },
+         "Mix":
+         {
+            "A": material_tex_image("tex_diffuse"),
+            "B": material_tex_image("tex_decal")
+         },
+      },
+      "Normal":
+      {
+         "Normal Map":
+         {
+            "Color": material_tex_image("tex_normal")
+         }
+      }
+   }
+}
 
-#  Purpose: A rift. must target another gate, the target gate can not have more 
-#           than one target nodes of its own.
+# https://harrygodden.com/git/?p=convexer.git;a=blob;f=__init__.py;#l1164
 #
-class classtype_gate(Structure):
+def material_info(mat):
 #{
-   _pack_ = 1
-   _fields_ = [("target",c_uint32),
-               ("dims",c_float*3)]
+   info = {}
 
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 100
+   # Using the cxr_graph_mapping as a reference, go through the shader
+   # graph and gather all $props from it.
+   #
+   def _graph_read( node_def, node=None, depth=0 ):#{
+      nonlocal mat
+      nonlocal info
+      
+      # Find rootnodes
+      #
+      if node == None:#{
+         _graph_read.extracted = []
 
-      obj = node_def['obj']
+         for node_idname in node_def:#{
+            for n in mat.node_tree.nodes:#{
+               if n.name == node_idname:#{
+                  node_def = node_def[node_idname]
+                  node = n
+                  break
+               #}
+            #}
+         #}
+      #}
 
-      if obj.cv_data.target != None:
-         _.target = obj.cv_data.target.cv_data.uid
+      for link in node_def:#{
+         link_def = node_def[link]
 
-      if obj.type == 'MESH':
-      #{
-         _.dims[0] = obj.data.cv_data.v0[0]
-         _.dims[1] = obj.data.cv_data.v0[1]
-         _.dims[2] = obj.data.cv_data.v0[2]
-      #}
-      else:
-      #{
-         _.dims[0] = obj.cv_data.v0[0]
-         _.dims[1] = obj.cv_data.v0[1]
-         _.dims[2] = obj.cv_data.v0[2]
+         if isinstance( link_def, dict ):#{
+            node_link = None
+            for x in node.inputs:#{
+               if isinstance( x, bpy.types.NodeSocketColor ):#{
+                  if link == x.name:#{
+                     node_link = x
+                     break
+                  #}
+               #}
+            #}
+
+            if node_link and node_link.is_linked:#{
+               # look for definitions for the connected node type
+               #
+               from_node = node_link.links[0].from_node
+               
+               node_name = from_node.name.split('.')[0]
+               if node_name in link_def:#{
+                  from_node_def = link_def[ node_name ]
+
+                  _graph_read( from_node_def, from_node, depth+1 )
+               #}
+               
+               # No definition! :(
+               #  TODO: Make a warning for this?
+            #}
+            else:#{
+               if "default" in link_def:#{
+                  prop = link_def['default']
+                  info[prop] = node_link.default_value
+               #}
+            #}
+         #}
+         else:#{
+            prop = link_def
+            info[prop] = getattr( node, link )
+         #}
       #}
    #}
 
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
+   _graph_read( cxr_graph_mapping )
+   return info
+#}
 
-      if obj.type == 'MESH':
-         dims = obj.data.cv_data.v0
-      else:
-         dims = obj.cv_data.v0
+def sr_pack_file( file, path, data ):
+#{
+   file.path = sr_compile_string( path )
+   file.pack_offset = len( sr_compile.pack_data )
+   file.pack_size = len( data )
 
-      vs = [None]*9
-      c = Vector((0,0,dims[2]))
+   sr_compile.pack_data.extend( data )
+   bytearray_align_to( sr_compile.pack_data, 16 )
+#}
 
-      vs[0] = obj.matrix_world @ Vector((-dims[0],0.0,-dims[1]+dims[2]))
-      vs[1] = obj.matrix_world @ Vector((-dims[0],0.0, dims[1]+dims[2]))
-      vs[2] = obj.matrix_world @ Vector(( dims[0],0.0, dims[1]+dims[2]))
-      vs[3] = obj.matrix_world @ Vector(( dims[0],0.0,-dims[1]+dims[2]))
-      vs[4] = obj.matrix_world @ (c+Vector((-1,0,-2)))
-      vs[5] = obj.matrix_world @ (c+Vector((-1,0, 2)))
-      vs[6] = obj.matrix_world @ (c+Vector(( 1,0, 2)))
-      vs[7] = obj.matrix_world @ (c+Vector((-1,0, 0)))
-      vs[8] = obj.matrix_world @ (c+Vector(( 1,0, 0)))
+def sr_compile_texture( img ):
+#{
+   if img == None:
+      return 0
 
-      indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
+   name = os.path.splitext( img.name )[0]
 
-      for l in indices:
-      #{
-         v0 = vs[l[0]]
-         v1 = vs[l[1]]
-         cv_view_verts += [(v0[0],v0[1],v0[2])]
-         cv_view_verts += [(v1[0],v1[1],v1[2])]
-         cv_view_colours += [(1,1,0,1),(1,1,0,1)]
-      #}
+   if name in sr_compile.texture_cache:
+      return sr_compile.texture_cache[name]
 
-      sw = (0.4,0.4,0.4,0.2)
-      if obj.cv_data.target != None:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, sw )
-   #}
+   texture_index = (len(sr_compile.texture_data)//sizeof(mdl_texture)) +1
 
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "target" )
+   tex = mdl_texture()
+   tex.type = 0
 
-      mesh = obj.data
-      layout.label( text=F"(i) Data is stored in {mesh.name}" )
-      layout.prop( mesh.cv_data, "v0", text="Gate dimensions" )
+   if sr_compile.pack_textures:#{
+      filedata = qoi_encode( img )
+      sr_pack_file( tex.file, name, filedata )
    #}
+
+   sr_compile.texture_cache[name] = texture_index
+   sr_compile.texture_data.extend( bytearray(tex) )
+   return texture_index
 #}
 
-class classtype_nonlocal_gate(classtype_gate):
+def sr_compile_material( mat ):
 #{
-   def encode_obj(_,node,node_def):
-   #{
-      node.classtype = 101
+   if mat == None: 
+      return 0
+   if mat.name in sr_compile.material_cache: 
+      return sr_compile.material_cache[mat.name]
 
-      obj = node_def['obj']
-      _.target = encoder_process_pstr( node_def['obj'].cv_data.strp )
+   index = (len(sr_compile.material_data)//sizeof(mdl_material))+1
+   sr_compile.material_cache[mat.name] = index
 
-      if obj.type == 'MESH':
-      #{
-         _.dims[0] = obj.data.cv_data.v0[0]
-         _.dims[1] = obj.data.cv_data.v0[1]
-         _.dims[2] = obj.data.cv_data.v0[2]
-      #}
-      else:
-      #{
-         _.dims[0] = obj.cv_data.v0[0]
-         _.dims[1] = obj.cv_data.v0[1]
-         _.dims[2] = obj.cv_data.v0[2]
-      #}
+   m = mdl_material()
+   m.pstr_name = sr_compile_string( mat.name )
+   
+   flags = 0x00
+   if mat.SR_data.collision:#{
+      flags |= 0x2
+      if mat.SR_data.skate_surface: flags |= 0x1
+      if mat.SR_data.grind_surface: flags |= (0x8|0x1)
    #}
 
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "strp", text="Nonlocal ID" )
+   if mat.SR_data.grow_grass: flags |= 0x4
+   m.flags = flags
 
-      mesh = obj.data
-      layout.label( text=F"(i) Data is stored in {mesh.name}" )
-      layout.prop( mesh.cv_data, "v0", text="Gate dimensions" )
-   #}
-#}
+   m.surface_prop = int(mat.SR_data.surface_prop)
 
-# 200: Spawns/Waypoints
-# ---------------------------------------------------------------------------- #
+   if mat.SR_data.shader == 'standard': m.shader = 0
+   if mat.SR_data.shader == 'standard_cutout': m.shader = 1
+   if mat.SR_data.shader == 'terrain_blend':#{
+      m.shader = 2
 
-#  Purpose: player can reset here, its a safe place
-#           spawns can share the same name, the closest one will be picked
-#           
-#           when the world loads it will pick the one named 'start' first.
-#
-class classtype_spawn(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("pstr_alias",c_uint32)]
+      m.colour[0] = pow( mat.SR_data.sand_colour[0], 1.0/2.2 )
+      m.colour[1] = pow( mat.SR_data.sand_colour[1], 1.0/2.2 )
+      m.colour[2] = pow( mat.SR_data.sand_colour[2], 1.0/2.2 )
+      m.colour[3] = 1.0
 
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 200
-      _.pstr_alias = encoder_process_pstr( node_def['obj'].cv_data.strp )
+      m.colour1[0] = mat.SR_data.blend_offset[0]
+      m.colour1[1] = mat.SR_data.blend_offset[1]
    #}
 
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
+   if mat.SR_data.shader == 'vertex_blend':#{
+      m.shader = 3
 
-      vs = [None]*4
-      vs[0] = obj.matrix_world @ Vector((0,0,0))
-      vs[1] = obj.matrix_world @ Vector((0,2,0))
-      vs[2] = obj.matrix_world @ Vector((0.5,1,0))
-      vs[3] = obj.matrix_world @ Vector((-0.5,1,0))
-      indices = [(0,1),(1,2),(1,3)]
+      m.colour1[0] = mat.SR_data.blend_offset[0]
+      m.colour1[1] = mat.SR_data.blend_offset[1]
+   #}
 
-      for l in indices:
-      #{
-         v0 = vs[l[0]]
-         v1 = vs[l[1]]
-         
-         cv_view_verts += [(v0[0],v0[1],v0[2])]
-         cv_view_verts += [(v1[0],v1[1],v1[2])]
-         cv_view_colours += [(0,1,1,1),(0,1,1,1)]
-      #}
+   if mat.SR_data.shader == 'water':#{
+      m.shader = 4
 
-      cv_draw_sphere( obj.location, 20.0, [0.1,0,0.9,0.4] )
+      m.colour[0]  = pow( mat.SR_data.shore_colour[0], 1.0/2.2 )
+      m.colour[1]  = pow( mat.SR_data.shore_colour[1], 1.0/2.2 )
+      m.colour[2]  = pow( mat.SR_data.shore_colour[2], 1.0/2.2 )
+      m.colour[3]  = 1.0
+      m.colour1[0] = pow( mat.SR_data.ocean_colour[0], 1.0/2.2 )
+      m.colour1[1] = pow( mat.SR_data.ocean_colour[1], 1.0/2.2 )
+      m.colour1[2] = pow( mat.SR_data.ocean_colour[2], 1.0/2.2 )
+      m.colour1[3] = 1.0
    #}
+   
+   inf = material_info( mat )
 
-   @staticmethod
-   def editor_interface( layout, obj ):
+   if mat.SR_data.shader == 'standard' or \
+      mat.SR_data.shader == 'standard_cutout' or \
+      mat.SR_data.shader == 'terrain_blend' or \
+      mat.SR_data.shader == 'vertex_blend':
    #{
-      layout.prop( obj.cv_data, "strp", text="Alias" )
+      if 'tex_diffuse' in inf: 
+         m.tex_diffuse = sr_compile_texture(inf['tex_diffuse'])
    #}
-#}
 
-# 300: Water
-# ---------------------------------------------------------------------------- #
+   sr_compile.material_data.extend( bytearray(m) )
+   return index
+#}
 
-#  Purpose: Tells the game to draw water HERE, at this entity. 
-#
-class classtype_water(Structure):
+def sr_armature_bones( armature ):
 #{
-   _pack_ = 1
-   _fields_ = [("temp",c_uint32)]
-
-   def encode_obj(_, node,node_def):
+   def _recurse_bone( b ):
    #{
-      node.classtype = 300
-      # no data, spooky
+      yield b
+      for c in b.children: yield from _recurse_bone( c )
    #}
-#}
 
-# 400: Routes
-# ---------------------------------------------------------------------------- #
+   for b in armature.data.bones:
+      if not b.parent:
+         yield from _recurse_bone( b )
+#}
 
-#  Purpose: Defines a route, its 'starting' point, and the colour to use for it
-#
-class classtype_route(Structure):
+def sr_compile_mesh( obj ):
 #{
-   _pack_ = 1
-   _fields_ = [("id_start",c_uint32),
-               ("pstr_name",c_uint32),
-               ("colour",c_float*3)]
+   node=mdl_mesh()
+   compile_obj_transform(obj, node.transform)
+   node.pstr_name = sr_compile_string(obj.name)
+   node.flags = 0
 
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 400
-      obj = node_def['obj']
+   can_use_cache = True
+   armature = None
+
+   for mod in obj.modifiers:#{
+      if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
+         mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
+         mod.type == 'ARRAY':
+      #{
+         can_use_cache = False
+      #}
 
-      _.colour[0] = obj.cv_data.colour[0]
-      _.colour[1] = obj.cv_data.colour[1]
-      _.colour[2] = obj.cv_data.colour[2]
-      _.pstr_name = encoder_process_pstr( obj.cv_data.strp )
+      if mod.type == 'ARMATURE': #{
+         node.flags = 1
+         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]
 
-      if obj.cv_data.target != None: 
-         _.id_start = obj.cv_data.target.cv_data.uid
+         POSE_OR_REST_CACHE = armature.data.pose_position
+         armature.data.pose_position = 'REST'
+      #}
    #}
 
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours, cv_view_course_i
+   # Check the cache first
+   #
+   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
+   #}
+
+   # Compile a whole new mesh
+   #
+   node.submesh_start = len(sr_compile.submesh_data)//sizeof(mdl_submesh)
+   node.submesh_count = 0
+
+   dgraph = bpy.context.evaluated_depsgraph_get()
+   data = obj.evaluated_get(dgraph).data
+   data.calc_loop_triangles()
+   data.calc_normals_split()
+   
+   # Mesh is split into submeshes based on their material
+   #
+   mat_list = data.materials if len(data.materials) > 0 else [None]
+   for material_id, mat in enumerate(mat_list): #{
+      mref = {}
+
+      sm = mdl_submesh()
+      sm.indice_start = len(sr_compile.indice_data)//sizeof(c_uint32)
+      sm.vertex_start = len(sr_compile.vertex_data)//sizeof(mdl_vert)
+      sm.vertex_count = 0
+      sm.indice_count = 0
+      sm.material_id = sr_compile_material( mat )
 
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, [1,1,1,1] )
+      INF=99999999.99999999
+      for i in range(3):#{
+         sm.bbx[0][i] =  INF
+         sm.bbx[1][i] = -INF
+      #}
       
-      # Tries to simulate how we do it in the game
+      # Keep a reference to very very very similar vertices
+      # i have no idea how to speed it up.
       #
-      stack = [None]*64
-      stack_i = [0]*64
-      stack[0] = obj.cv_data.target
-      si = 1
-      loop_complete = False
+      vertex_reference = {}
 
-      while si > 0:
-      #{
-         if stack_i[si-1] == 2:
-         #{
-            si -= 1
+      # Write the vertex / indice data
+      #
+      for tri_index, tri in enumerate(data.loop_triangles):#{
+         if tri.material_index != material_id:
             continue
 
-            if si == 0: # Loop failed to complete
-               break
-         #}
-
-         node = stack[si-1]
+         for j in range(3):#{
+            vert = data.vertices[tri.vertices[j]]
+            li = tri.loops[j]
+            vi = data.loops[li].vertex_index
+            
+            # Gather vertex information
+            #
+            co      = vert.co
+            norm    = data.loops[li].normal
+            uv      = (0,0)
+            colour  = (255,255,255,255)
+            groups  = [0,0,0,0]
+            weights = [0,0,0,0]
 
-         targets = [None,None]
-         targets[0] = node.cv_data.target
+            # Uvs
+            #
+            if data.uv_layers:
+               uv = data.uv_layers.active.data[li].uv
+            
+            # Vertex Colours
+            #
+            if data.vertex_colors:#{
+               colour = data.vertex_colors.active.data[li].color
+               colour = (int(colour[0]*255.0),\
+                         int(colour[1]*255.0),\
+                         int(colour[2]*255.0),\
+                         int(colour[3]*255.0))
+            #}
+            
+            # Weight groups: truncates to the 3 with the most influence. The
+            #                fourth bone ID is never used by the shader so it 
+            #                is always 0
+            #
+            if armature:#{
+               src_groups = [_ for _ in data.vertices[vi].groups \
+                              if obj.vertex_groups[_.group].name in \
+                                 rig_weight_groups ]
 
-         if node.cv_data.classtype == 'classtype_route_node':
-         #{
-            targets[1] = node.cv_data.target1
-         #}
-         
-         nextnode = targets[stack_i[si-1]]
-         stack_i[si-1] += 1
-
-         if nextnode != None: # branch
-         #{
-            if nextnode == stack[0]: # Loop completed
-            #{
-               loop_complete = True
-               break
+               weight_groups = sorted( src_groups, key = \
+                                       lambda a: a.weight, reverse=True )
+               tot = 0.0
+               for ml in range(3):#{
+                  if len(weight_groups) > ml:#{
+                     g = weight_groups[ml]
+                     name = obj.vertex_groups[g.group].name
+                     weight = g.weight
+                     weights[ml] = weight
+                     groups[ml] = rig_weight_groups.index(name)
+                     tot += weight
+                  #}
+               #}
+            
+               if len(weight_groups) > 0:#{
+                  inv_norm = (1.0/tot) * 65535.0
+                  for ml in range(3):#{
+                     weights[ml] = int( weights[ml] * inv_norm )
+                     weights[ml] = min( weights[ml], 65535 )
+                     weights[ml] = max( weights[ml], 0 )
+                  #}
+               #}
             #}
+            else:#{
+               li1 = tri.loops[(j+1)%3]
+               vi1 = data.loops[li1].vertex_index
+               e0 = data.edges[ data.loops[li].edge_index ]
 
-            valid=True
-            for sj in range(si):
-            #{
-               if stack[sj] == nextnode: # invalidated path
+               if e0.use_freestyle_mark and \
+                     ((e0.vertices[0] == vi and e0.vertices[1] == vi1) or \
+                      (e0.vertices[0] == vi1 and e0.vertices[1] == vi)):
                #{
-                  valid=False
-                  break
+                  weights[0] = 1
                #}
             #}
 
-            if valid:
-            #{
-               stack_i[si] = 0
-               stack[si] = nextnode
-               si += 1
-               continue
-            #}
-         #}
-      #}
+            TOLERENCE = float(10**4)
+            key = (int(co[0]*TOLERENCE+0.5),
+                   int(co[1]*TOLERENCE+0.5),
+                   int(co[2]*TOLERENCE+0.5),
+                   int(norm[0]*TOLERENCE+0.5),
+                   int(norm[1]*TOLERENCE+0.5),
+                   int(norm[2]*TOLERENCE+0.5),
+                   int(uv[0]*TOLERENCE+0.5),
+                   int(uv[1]*TOLERENCE+0.5),
+                   colour[0],  # these guys are already quantized
+                   colour[1],  # .
+                   colour[2],  # .
+                   colour[3],  # .
+                   weights[0], # v
+                   weights[1],
+                   weights[2],
+                   weights[3],
+                   groups[0],
+                   groups[1],
+                   groups[2],
+                   groups[3])
+
+            if key in vertex_reference:
+               index = vertex_reference[key]
+            else:#{
+               index = bytearray(c_uint32(sm.vertex_count))
+               sm.vertex_count+=1
+               
+               vertex_reference[key] = index
+               v = mdl_vert()
+               v.co[0]      =  co[0]
+               v.co[1]      =  co[2]
+               v.co[2]      = -co[1]
+               v.norm[0]    =  norm[0]
+               v.norm[1]    =  norm[2]
+               v.norm[2]    = -norm[1]
+               v.uv[0]      =  uv[0]
+               v.uv[1]      =  uv[1]
+               v.colour[0]  =  colour[0]
+               v.colour[1]  =  colour[1]
+               v.colour[2]  =  colour[2]
+               v.colour[3]  =  colour[3]
+               v.weights[0] =  weights[0]
+               v.weights[1] =  weights[1]
+               v.weights[2] =  weights[2]
+               v.weights[3] =  weights[3]
+               v.groups[0]  =  groups[0]
+               v.groups[1]  =  groups[1]
+               v.groups[2]  =  groups[2]
+               v.groups[3]  =  groups[3]
+               
+               for i in range(3):#{
+                  sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] )
+                  sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] )
+               #}
 
-      if loop_complete:
-      #{
-         cc = Vector((obj.cv_data.colour[0],\
-                      obj.cv_data.colour[1],\
-                      obj.cv_data.colour[2],\
-                      1.0))
-
-         for sj in range(si):
-         #{
-            sk = (sj+1)%si
-
-            if stack[sj].cv_data.classtype == 'classtype_gate' and \
-               stack[sk].cv_data.classtype == 'classtype_gate':
-            #{
-               dist = (stack[sj].location-stack[sk].location).magnitude
-               cv_draw_sbpath( stack[sj], stack[sk], cc*0.4, cc, dist, dist )
+               sr_compile.vertex_data.extend(bytearray(v))
             #}
-            else:
-               cv_draw_bpath( stack[sj], stack[sk], cc, cc )
+            
+            sm.indice_count += 1
+            sr_compile.indice_data.extend( index )
          #}
-
-         cv_view_course_i += 1
       #}
+      
+      # Make sure bounding box isn't -inf -> inf if no vertices
+      #
+      if sm.vertex_count == 0:
+         for j in range(2):
+            for i in range(3):
+               sm.bbx[j][i] = 0
+
+      # Add submesh to encoder
+      #
+      sr_compile.submesh_data.extend( bytearray(sm) )
+      node.submesh_count += 1
    #}
 
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "target", text="'Start' from" )
-      layout.prop( obj.cv_data, "colour" )
-      layout.prop( obj.cv_data, "strp", text="Name" )
+   if armature:#{
+      armature.data.pose_position = POSE_OR_REST_CACHE
    #}
+
+   # Save a reference to this node 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_data.extend(bytearray(node))
 #}
 
-#  Purpose: Defines a route node and links to up to two more nodes
-#
-class classtype_route_node(Structure):
+def sr_compile_armature( obj ):
 #{
-   _pack_ = 1
-   _fields_ = [("target",c_uint32),
-               ("target1",c_uint32)]
+   node = mdl_armature()
+   node.bone_start = len(sr_compile.bone_data)//sizeof(mdl_bone)
+   node.bone_count = 0
+   node.anim_start = len(sr_compile.anim_data)//sizeof(mdl_animation)
+   node.anim_count = 0
+   
+   bones = [_ for _ in sr_armature_bones(obj)]
+   bones_names = [None]+[_.name for _ in bones]
+
+   for b in bones:#{
+      bone = mdl_bone()
+      if b.use_deform: bone.flags = 0x1
+      if b.parent: bone.parent = bones_names.index(b.parent.name)
+
+      bone.collider = int(b.SR_data.collider)
+
+      if bone.collider>0:#{
+         bone.hitbox[0][0] =  b.SR_data.collider_min[0]
+         bone.hitbox[0][1] =  b.SR_data.collider_min[2]
+         bone.hitbox[0][2] = -b.SR_data.collider_max[1]
+         bone.hitbox[1][0] =  b.SR_data.collider_max[0]
+         bone.hitbox[1][1] =  b.SR_data.collider_max[2]
+         bone.hitbox[1][2] = -b.SR_data.collider_min[1]
+      #}
+  
+      if b.SR_data.cone_constraint:#{
+         bone.flags |= 0x4
+         bone.conevx[0] =  b.SR_data.conevx[0]
+         bone.conevx[1] =  b.SR_data.conevx[2]
+         bone.conevx[2] = -b.SR_data.conevx[1]
+         bone.conevy[0] =  b.SR_data.conevy[0]
+         bone.conevy[1] =  b.SR_data.conevy[2]
+         bone.conevy[2] = -b.SR_data.conevy[1]
+         bone.coneva[0] =  b.SR_data.coneva[0]
+         bone.coneva[1] =  b.SR_data.coneva[2]
+         bone.coneva[2] = -b.SR_data.coneva[1]
+         bone.conet     =  b.SR_data.conet
+      #}
 
-   def encode_obj(_, node,node_def):
-   #{
-      node.classtype = 401
-      obj = node_def['obj']
+      bone.co[0] =  b.head_local[0]
+      bone.co[1] =  b.head_local[2]
+      bone.co[2] = -b.head_local[1]
+      bone.end[0] =  b.tail_local[0] - bone.co[0]
+      bone.end[1] =  b.tail_local[2] - bone.co[1]
+      bone.end[2] = -b.tail_local[1] - bone.co[2]
+      bone.pstr_name = sr_compile_string( b.name )
+
+      for c in obj.pose.bones[b.name].constraints:#{
+         if c.type == 'IK':#{
+            bone.flags |= 0x2
+            bone.ik_target = bones_names.index(c.subtarget)
+            bone.ik_pole = bones_names.index(c.pole_subtarget)
+         #}
+      #}
 
-      if obj.cv_data.target != None:
-         _.target = obj.cv_data.target.cv_data.uid
-      if obj.cv_data.target1 != None: 
-         _.target1 = obj.cv_data.target1.cv_data.uid
+      node.bone_count += 1
+      sr_compile.bone_data.extend(bytearray(bone))
    #}
 
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
+   # Compile anims
+   #
+   if obj.animation_data and sr_compile.pack_animations: #{
+      # So we can restore later
+      #
+      previous_frame  = bpy.context.scene.frame_current
+      previous_action = obj.animation_data.action
+      POSE_OR_REST_CACHE = obj.data.pose_position
+      obj.data.pose_position = 'POSE'
+
+      for NLALayer in obj.animation_data.nla_tracks:#{
+         for NLAStrip in NLALayer.strips:#{
+            # set active
+            #
+            for a in bpy.data.actions:#{
+               if a.name == NLAStrip.name:#{
+                  obj.animation_data.action = a
+                  break
+               #}
+            #}
+            
+            # Clip to NLA settings
+            #
+            anim_start = int(NLAStrip.action_frame_start)
+            anim_end   = int(NLAStrip.action_frame_end)
 
-      sw = Vector((0.4,0.4,0.4,0.2))
-      sw2 = Vector((1.5,0.2,0.2,0.0))
-      if obj.cv_data.target != None:
-         cv_draw_bpath( obj, obj.cv_data.target, sw, sw )
-      if obj.cv_data.target1 != None:
-         cv_draw_bpath( obj, obj.cv_data.target1, sw, sw )
+            # Export strips
+            #
+            anim = mdl_animation()
+            anim.pstr_name = sr_compile_string( NLAStrip.action.name )
+            anim.rate = 30.0
+            anim.keyframe_start = len(sr_compile.keyframe_data)//\
+                                      sizeof(mdl_transform)
+            anim.length = anim_end-anim_start
+            
+            i = 0
+            # Export the keyframes
+            for frame in range(anim_start,anim_end):#{
+               bpy.context.scene.frame_set(frame)
+               
+               for rb in bones:#{
+                  pb = obj.pose.bones[rb.name]
+                  
+                  # relative bone matrix
+                  if rb.parent is not None:#{
+                     offset_mtx = rb.parent.matrix_local
+                     offset_mtx = offset_mtx.inverted_safe() @ \
+                                  rb.matrix_local
+
+                     inv_parent = pb.parent.matrix @ offset_mtx
+                     inv_parent.invert_safe()
+                     fpm = inv_parent @ pb.matrix 
+                  #}
+                  else:#{
+                     bone_mtx = rb.matrix.to_4x4()
+                     local_inv = rb.matrix_local.inverted_safe()
+                     fpm = bone_mtx @ local_inv @ pb.matrix
+                  #}
 
-      cv_draw_bhandle( obj,  1.0, (0.8,0.8,0.8,1.0) )
-      cv_draw_bhandle( obj, -1.0, (0.4,0.4,0.4,1.0) )
+                  loc, rot, sca = fpm.decompose()
+                  
+                  # rotation
+                  lc_m = pb.matrix_channel.to_3x3()
+                  if pb.parent is not None:#{
+                     smtx = pb.parent.matrix_channel.to_3x3()
+                     lc_m = smtx.inverted() @ lc_m
+                  #}
+                  rq = lc_m.to_quaternion()
+
+                  kf = mdl_transform()
+                  kf.co[0] =  loc[0]
+                  kf.co[1] =  loc[2]
+                  kf.co[2] = -loc[1]
+                  kf.q[0]  =  rq[1]
+                  kf.q[1]  =  rq[3]
+                  kf.q[2]  = -rq[2]
+                  kf.q[3]  =  rq[0]
+                  kf.s[0]  = sca[0]
+                  kf.s[1]  = sca[1]
+                  kf.s[2]  = sca[2]
+                  sr_compile.keyframe_data.extend(bytearray(kf))
+                  
+                  i+=1
+               #}
+            #}
+            
+            # Add to animation buffer
+            #
+            sr_compile.anim_data.extend(bytearray(anim))
+            node.anim_count += 1
 
-      p1 = obj.location+ \
-            obj.matrix_world.to_quaternion() @ Vector((0,0,-6+1.5))
-      cv_draw_arrow( obj.location, p1, sw )
+            # Report progress
+            #
+            print( F"[SR]    | anim( {NLAStrip.action.name} )" )
+         #}
+      #}
+      
+      # Restore context to how it was before
+      #
+      bpy.context.scene.frame_set( previous_frame )
+      obj.animation_data.action = previous_action
+      obj.data.pose_position = POSE_OR_REST_CACHE
    #}
 
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "target", text="Left" )
-      layout.prop( obj.cv_data, "target1", text="Right" )
-   #}
+   sr_compile.armature_data.extend(bytearray(node))
 #}
 
+def sr_ent_push( struct ):
+#{
+   clase = type(struct).__name__
 
-# 500: Audio
-# ---------------------------------------------------------------------------- #
+   if clase not in sr_compile.entity_data:#{
+      sr_compile.entity_data[ clase ] = bytearray()
+      sr_compile.entity_info[ clase ] = { 'size': sizeof(struct) }
+   #}
 
-AUDIO_SPRITE_CATEGORIES_ENUM = [
-      ('0', "Bird", ""),
-      ('1', "Nocturnal Bird", ""),
-      ('2', "Grass", ""),
-      ('3', "Wave",""),
-      ('4', "Wind",""),
-      ('5', "Wood Creaks", ""),
-   ]
-
-#    flags: 
-#           AUDIO_FLAG_LOOP        0x1
-#           AUDIO_FLAG_SPACIAL_3D  0x4  (Probably what you want)
-#
-class classtype_audio(Structure):
+   index = len(sr_compile.entity_data[ clase ])//sizeof(struct)
+   sr_compile.entity_data[ clase ].extend( bytearray(struct) )
+   return index
+#}
+
+def sr_array_title( arr, name, count, size, offset ):
 #{
-   _pack_ = 1
-   _fields_ = [("pstr_file",c_uint32),
-               ("flags",c_uint32),
-               ("volume",c_float)]
+   for i in range(len(name)):#{
+      arr.name[i] = ord(name[i])
+   #}
+   arr.file_offset = offset
+   arr.item_count = count
+   arr.item_size = size
+#}
 
-   dynamic_enum = [
-      ('0', "mono", ""),
-      ('1', "stereo", ""),
-      ('2', "remain compressed", ""),
-      ('3', "synthetic bird",""),
-   ]
+def sr_compile( collection ):
+#{
+   print( F"[SR] compiler begin ({collection.name}.mdl)" )
 
-   def encode_obj(_, node,node_def ):
-   #{
-      node.classtype = 500
+   #settings
+   sr_compile.pack_textures = collection.SR_data.pack_textures
+   sr_compile.pack_animations = collection.SR_data.animations
 
-      obj = node_def['obj']
+   # caches
+   sr_compile.string_cache = {}
+   sr_compile.mesh_cache = {}
+   sr_compile.material_cache = {}
+   sr_compile.texture_cache = {}
+   
+   # compiled data
+   sr_compile.mesh_data = bytearray()
+   sr_compile.submesh_data = bytearray()
+   sr_compile.vertex_data = bytearray()
+   sr_compile.indice_data = bytearray()
+   sr_compile.bone_data = bytearray()
+   sr_compile.material_data = bytearray()
+   sr_compile.armature_data = bytearray()
+   sr_compile.anim_data = bytearray()
+   sr_compile.keyframe_data = bytearray()
+   sr_compile.texture_data = bytearray()
+   
+   # just bytes not structures
+   sr_compile.string_data = bytearray()
+   sr_compile.pack_data = bytearray()
 
-      _.pstr_file = encoder_process_pstr( obj.cv_data.strp )
+   # variable
+   sr_compile.entity_data = {}
+   sr_compile.entity_info = {}
 
-      flags = 0x00
-      if obj.cv_data.bp0: flags |= 0x1
-      if obj.cv_data.bp1: flags |= 0x4
-      if obj.cv_data.bp2: flags |= 0x8
+   print( F"[SR] assign entity ID's" )
+   sr_compile.entities = {}
+   sr_compile.entity_ids = {}
 
-      if obj.cv_data.dynamic_enum == '1': flags |= 0x200
-      if obj.cv_data.dynamic_enum == '2': flags |= 0x400
-      if obj.cv_data.dynamic_enum == '3': flags |= 0x1000
+   mesh_count = 0
+   for obj in collection.all_objects: #{
+      if obj.type == 'MESH': mesh_count += 1
 
-      _.flags = flags
-      _.volume = obj.cv_data.fltp
-   #}
-   
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "strp", text = "File (.ogg/DATA)" )
+      ent_type = obj_ent_type( obj )
+      if ent_type == 'none': continue
 
-      layout.prop( obj.cv_data, "bp0", text = "Looping" )
-      layout.prop( obj.cv_data, "bp1", text = "3D Audio" )
-      layout.prop( obj.cv_data, "bp2", text = "Play here" )
-      layout.prop( obj.cv_data, "dynamic_enum" )
+      if ent_type not in sr_compile.entities: sr_compile.entities[ent_type] = []
+      sr_compile.entity_ids[obj.name] = len( sr_compile.entities[ent_type] )
+      sr_compile.entities[ent_type] += [obj]
+   #}
 
-      layout.prop( obj.cv_data, "fltp", text = "Volume (0-1)" )
+   print( F"[SR] Compiling geometry" )
+   i=0
+   for obj in collection.all_objects:#{
+      if obj.type == 'MESH':#{
+         i+=1
+         print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}', end='\r' )
+         sr_compile_mesh( obj )
+      #}
    #}
 
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
+   checkpoint_count = 0
+   pathindice_count = 0
 
-      if bpy.context.active_object == obj:
-         cv_draw_sphere( obj.location, obj.scale[0], [1,1,0,1] )
-   #}
-#}
+   for ent_type, arr in sr_compile.entities.items():#{
+      print(F"[SR] Compiling {len(arr)} {ent_type}{'s' if len(arr)>1 else ''}")
 
-class classtype_audio_sprite(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("audio",c_uint32),
-               ("category",c_uint32),
-               ("probability",c_float)]
+      for i in range(len(arr)):#{
+         obj = arr[i]
 
-   dynamic_enum = AUDIO_SPRITE_CATEGORIES_ENUM
+         print( F"[SR] {i+1: 3}/{len(arr)} {obj.name:<40} ",end='\r' )
 
-   def encode_obj(_, node,node_def ):
-   #{
-      node.classtype = 501
-      obj = node_def['obj']
+         if ent_type == 'mdl_armature': sr_compile_armature(obj)
+         elif ent_type == 'ent_light': #{
+            light = ent_light()
+            compile_obj_transform( obj, light.transform )
+            light.daytime = obj.data.SR_data.daytime
+            if obj.data.type == 'POINT':#{
+               light.type = 0
+            #}
+            elif obj.data.type == 'SPOT':#{
+               light.type = 1
+               light.angle = obj.data.spot_size*0.5
+            #}
+            light.range = obj.data.cutoff_distance
+            light.colour[0] = obj.data.color[0]
+            light.colour[1] = obj.data.color[1]
+            light.colour[2] = obj.data.color[2]
+            light.colour[3] = obj.data.energy
+            sr_ent_push( light )
+         #}
+         elif ent_type == 'ent_gate': #{
+            gate = ent_gate()
+            gate.type = 0
+            obj_data = obj.SR_data.ent_gate[0]
+            mesh_data = obj.data.SR_data.ent_gate[0]
+            if obj_data.target:#{
+               gate.target = sr_compile.entity_ids[obj_data.target.name]
+               gate.type = 1
+            #}
+            gate.dimensions[0] = mesh_data.dimensions[0]
+            gate.dimensions[1] = mesh_data.dimensions[1]
+            gate.dimensions[2] = mesh_data.dimensions[2]
 
-      _.category = int( obj.cv_data.dynamic_enum )
-      _.probability = obj.cv_data.fltp
+            q  = [obj.matrix_local.to_quaternion(), (0,0,0,1)]
+            co = [obj.matrix_world @ Vector((0,0,0)), (0,0,0)]
 
-      if obj.cv_data.target:
-         _.audio = obj.cv_data.target.cv_data.uid
-   #}
+            if obj_data.target:#{
+               q[1] = obj_data.target.matrix_local.to_quaternion()
+               co[1]= obj_data.target.matrix_world @ Vector((0,0,0))
+            #}
+            
+            # Setup transform
+            #
+            for x in range(2):#{
+               gate.co[x][0] =  co[x][0]
+               gate.co[x][1] =  co[x][2]
+               gate.co[x][2] = -co[x][1]
+               gate.q[x][0]  =  q[x][1]
+               gate.q[x][1]  =  q[x][3]
+               gate.q[x][2]  = -q[x][2]
+               gate.q[x][3]  =  q[x][0]
+            #}
 
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "dynamic_enum", text="Category" )
-      layout.prop( obj.cv_data, "target", text="Sound" )
-      layout.prop( obj.cv_data, "fltp", text="Probability" )
-   #}
+            sr_ent_push( gate )
+         #}
+         elif ent_type == 'ent_spawn': #{
+            spawn = ent_spawn()
+            compile_obj_transform( obj, spawn.transform )
+            obj_data = obj.SR_data.ent_spawn[0]
+            spawn.pstr_name = sr_compile_string( obj_data.name )
+            sr_ent_push( spawn )
+         #}
+         elif ent_type == 'ent_route': #{
+            obj_data = obj.SR_data.ent_route[0]
+            route = ent_route()
+            route.pstr_name = sr_compile_string( obj_data.alias ) #TODO
+            route.checkpoints_start = checkpoint_count
+            route.checkpoints_count = 0
+
+            for ci in range(3):
+               route.colour[ci] = obj_data.colour[ci]
+            route.colour[3] = 1.0
+
+            compile_obj_transform( obj, route.transform )
+
+            checkpoints = obj_data.gates
+            route_nodes = []
+
+            for uc in obj.users_collection[0].objects:#{
+               uc_type = obj_ent_type( uc )
+               if uc_type == 'ent_gate' or uc_type == 'ent_route_node':
+                  route_nodes += [uc]
+            #}
+            graph = node_graph( route_nodes )
 
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
+            for i in range(len(checkpoints)):#{
+               gi = checkpoints[i].target
+               gj = checkpoints[(i+1)%len(checkpoints)].target
+               gate = gi
 
-      purple = (0.5,0.2,1,1)
-      if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, purple, 0.1 )
-   #}
-#}
+               if gi:#{
+                  dest = gi.SR_data.ent_gate[0].target
+                  gi = dest
+               #}
 
+               if gi==gj: continue # error?
+               if not gi or not gj: continue
 
-# 600: Volumes
-# ---------------------------------------------------------------------------- #
+               checkpoint = ent_checkpoint()
+               checkpoint.gate_index = sr_compile.entity_ids[gate.name]
+               checkpoint.path_start = pathindice_count
+               checkpoint.path_count = 0
+               
+               path = dijkstra( graph, gj.name, gi.name )
+               if path:#{
+                  for pi in range(1,len(path)-1):#{
+                     pathindice = ent_path_index()
+                     pathindice.index = sr_compile.entity_ids[path[pi]]
+                     sr_ent_push( pathindice )
+
+                     checkpoint.path_count += 1
+                     pathindice_count += 1
+                  #}
+               #}
+               
+               sr_ent_push( checkpoint )
+               route.checkpoints_count += 1
+               checkpoint_count += 1
+            #}
 
-class classtype_volume_audio(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("category",c_uint32)]
+            sr_ent_push( route )
+         #}
+         elif ent_type == 'ent_route_node':#{
+            rn = ent_route_node()
+            rn.co[0] =  obj.location[0]
+            rn.co[1] =  obj.location[2]
+            rn.co[2] = -obj.location[1]
+            sr_ent_push( rn )
+         #}
+      #}
+   #}
+   
+   print( F"[SR] Writing file" )
 
-   dynamic_enum = AUDIO_SPRITE_CATEGORIES_ENUM
+   file_array_instructions = {}
+   file_offset = 0
 
-   def encode_obj(_, node,node_def ):
-   #{
-      node.classtype = 600
+   def _write_array( name, item_size, data ):#{
+      nonlocal file_array_instructions, file_offset
 
-      obj = node_def['obj']
-      _.category = int(obj.cv_data.dynamic_enum)
+      count = len(data)//item_size
+      file_array_instructions[name] = {'count':count, 'size':item_size,\
+                                       'data':data, 'offset': file_offset}
+      file_offset += len(data)
+      file_offset = int_align_to( file_offset, 8 )
    #}
 
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
-      cv_draw_ucube( obj.matrix_world, [1,0.8,0,1] )
-   #}
+   _write_array( 'strings', 1, sr_compile.string_data )
+   _write_array( 'mdl_mesh', sizeof(mdl_mesh), sr_compile.mesh_data )
+   _write_array( 'mdl_submesh', sizeof(mdl_submesh), sr_compile.submesh_data )
+   _write_array( 'mdl_material', sizeof(mdl_material), sr_compile.material_data)
+   _write_array( 'mdl_texture', sizeof(mdl_texture), sr_compile.texture_data)
+   _write_array( 'mdl_armature', sizeof(mdl_armature), sr_compile.armature_data)
+   _write_array( 'mdl_bone', sizeof(mdl_bone), sr_compile.bone_data )
 
-   @staticmethod
-   def editor_interface( layout, obj ):
-   #{
-      layout.prop( obj.cv_data, "dynamic_enum", text="Category" )
+   for name, buffer in sr_compile.entity_data.items():#{
+      _write_array( name, sr_compile.entity_info[name]['size'], buffer )
    #}
-#}
 
-class classtype_volume_event(Structure):
-#{
-   _pack_ = 1
-   _fields_ = [("event",c_uint32)]
+   _write_array( 'mdl_animation', sizeof(mdl_animation), sr_compile.anim_data)
+   _write_array( 'mdl_keyframe', sizeof(mdl_transform),sr_compile.keyframe_data)
+   _write_array( 'mdl_vert', sizeof(mdl_vert), sr_compile.vertex_data )
+   _write_array( 'mdl_indice', sizeof(c_uint32), sr_compile.indice_data )
+   _write_array( 'pack', 1, sr_compile.pack_data )
 
-   def encode_obj(_, node,node_def ):
-   #{
-      node.classtype = 601
-      obj = node_def['obj']
-      _.event = encoder_process_pstr( obj.cv_data.strp )
-   #}
+   header_size = int_align_to( sizeof(mdl_header), 8 )
+   index_size = int_align_to( sizeof(mdl_array)*len(file_array_instructions),8 )
 
-   @staticmethod
-   def draw_scene_helpers( obj ):
-   #{
-      global cv_view_verts, cv_view_colours
-      cv_draw_ucube( obj.matrix_world, [0.0,1.0,0,1] )
-   #}
-#}
+   folder = bpy.path.abspath(bpy.context.scene.SR_data.export_dir)
+   path = F"{folder}{collection.name}.mdl"
+   print( path )
 
-# ---------------------------------------------------------------------------- #
-#                                                                              #
-#                                Compiler section                              #
-#                                                                              #
-# ---------------------------------------------------------------------------- #
+   fp = open( path, "wb" )
+   header = mdl_header()
+   header.version = 40
+   sr_array_title( header.arrays, \
+                   'index', len(file_array_instructions), \
+                   sizeof(mdl_array), header_size )
 
-# Current encoder state
-#
-g_encoder = None
+   fp.write( bytearray_align_to( bytearray(header), 8 ) )
 
-# Reset encoder
-#
-def encoder_init( collection ):
-#{
-   global g_encoder
+   print( F'[SR] {"name":>16}|    count | offset' )
+   index = bytearray()
+   for name,info in file_array_instructions.items():#{
+      arr = mdl_array()
+      offset = info['offset'] + header_size + index_size
+      sr_array_title( arr, name, info['count'], info['size'], offset )
+      index.extend( bytearray(arr) )
 
-   g_encoder = \
-   {
-      # The actual file header
-      #
-      'header': mdl_header(),
+      print( F'[SR] {name:>16}| {info["count"]: 8} '+\
+             F'  0x{info["offset"]:02x}' )
+   #}
+   fp.write( bytearray_align_to( index, 8 ) )
+   #bytearray_print_hex( index )
 
-      # Options
-      #
-      'pack_textures': collection.cv_data.pack_textures,
+   for name,info in file_array_instructions.items():#{
+      fp.write( bytearray_align_to( info['data'], 8 ) )
+   #}
 
-      # Compiled data chunks (each can be read optionally by the client)
-      #
-      'data':
-      {
-         #1---------------------------------
-         'node': [],      # Metadata 'chunk'
-         'submesh': [],
-         'material': [],
-         'texture': [],
-         'anim': [],
-         'entdata': bytearray(), # variable width
-         'strings': bytearray(), # .
-         #2---------------------------------
-         'keyframe': [],  # Animations
-         #3---------------------------------
-         'vertex': [],    # Mesh data
-         'indice': [],
-         #4---------------------------------
-         'pack': bytearray()  # Other generic packed data
-      },
+   fp.close()
 
-      # All objects of the model in their final heirachy
-      #
-      "uid_count": 1,
-      "scene_graph":{},
-      "graph_lookup":{},
-      
-      # Allows us to reuse definitions
-      #
-      'string_cache':{},
-      'mesh_cache': {},
-      'material_cache': {},
-      'texture_cache': {}
-   }
+   print( '[SR] done' )
+#}
 
-   g_encoder['header'].identifier = 0xABCD0000
-   g_encoder['header'].version = 1
+class SR_SCENE_SETTINGS(bpy.types.PropertyGroup):
+#{
+   use_hidden: bpy.props.BoolProperty( name="use hidden", default=False )
+   export_dir: bpy.props.StringProperty( name="Export Dir", subtype='DIR_PATH' )
+   gizmos: bpy.props.BoolProperty( name="Draw Gizmos", default=True )
 
-   # Add fake NoneID material and texture
-   #
-   none_material = mdl_material()
-   none_material.pstr_name = encoder_process_pstr( "" )
-   none_material.texture_id = 0
-
-   none_texture = mdl_texture()
-   none_texture.pstr_name = encoder_process_pstr( "" )
-   none_texture.pack_offset = 0
-   none_texture.pack_length = 0
-   
-   g_encoder['data']['material'] += [none_material]
-   g_encoder['data']['texture']  += [none_texture]
+   panel: bpy.props.EnumProperty(
+        name='Panel',
+        description='',
+        items=[
+            ('EXPORT', 'Export', '', 'MOD_BUILD',0),
+            ('ENTITY', 'Entity', '', 'MONKEY',1),
+            ('SETTINGS', 'Settings', 'Settings', 'PREFERENCES',2),
+        ],
+    )
+#}
 
-   g_encoder['data']['pack'].extend( b'datapack\0\0\0\0\0\0\0\0' )
+class SR_COLLECTION_SETTINGS(bpy.types.PropertyGroup):
+#{
+   pack_textures: bpy.props.BoolProperty( name="Pack Textures", default=False )
+   animations:    bpy.props.BoolProperty( name="Export animation", default=True)
+#}
 
-   # Add root node
-   #
-   root = mdl_node()
-   root.co[0] = 0
-   root.co[1] = 0
-   root.co[2] = 0
-   root.q[0] = 0
-   root.q[1] = 0
-   root.q[2] = 0
-   root.q[3] = 1
-   root.s[0] = 1
-   root.s[1] = 1
-   root.s[2] = 1
-   root.pstr_name = encoder_process_pstr('')
-   root.submesh_start = 0
-   root.submesh_count = 0
-   root.offset = 0
-   root.classtype = 0
-   root.parent = 0xffffffff
-
-   g_encoder['data']['node'] += [root]
-#}
-
-
-# fill with 0x00 until a multiple of align. Returns how many bytes it added
-#
-def bytearray_align_to( buffer, align, offset=0 ):
+def sr_get_mirror_bone( bones ):
 #{
-   count = 0
+   side = bones.active.name[-1:]
+   other_name = bones.active.name[:-1]
+   if side == 'L': other_name += 'R'
+   elif side == 'R': other_name += 'L'
+   else: return None
 
-   while ((len(buffer)+offset) % align) != 0:
-   #{
-      buffer.extend( b'\0' )
-      count += 1
+   for b in bones:#{
+      if b.name == other_name:
+         return b
    #}
-
-   return count
+   
+   return None
 #}
 
-# Add a string to the string buffer except if it already exists there then we
-# just return its ID.
-#
-def encoder_process_pstr( s ):
+class SR_MIRROR_BONE_X(bpy.types.Operator):
 #{
-   global g_encoder
+   bl_idname="skaterift.mirror_bone"
+   bl_label="Mirror bone attributes - SkateRift"
 
-   cache = g_encoder['string_cache']
+   def execute(_,context):
+   #{
+      active_object = context.active_object
+      bones = active_object.data.bones
+      a = bones.active
+      b = sr_get_mirror_bone( bones )
 
-   if s in cache:
-      return cache[s]
-   
-   cache[s] = len( g_encoder['data']['strings'] )
+      if not b: return {'FINISHED'}
 
-   buffer = g_encoder['data']['strings']
-   buffer.extend( s.encode('utf-8') )
-   buffer.extend( b'\0' )
-   
-   bytearray_align_to( buffer, 4 )
-   return cache[s]
-#}
+      b.SR_data.collider = a.SR_data.collider
 
-def get_texture_resource_name( img ):
-#{
-   return os.path.splitext( img.name )[0]
-#}
+      def _v3copyflipy( a, b ):#{
+         b[0] =  a[0]
+         b[1] = -a[1]
+         b[2] =  a[2]
+      #}
 
-# Pack a texture
-#
-def encoder_process_texture( img ):
-#{
-   global g_encoder
+      _v3copyflipy( a.SR_data.collider_min, b.SR_data.collider_min )
+      _v3copyflipy( a.SR_data.collider_max, b.SR_data.collider_max )
+      b.SR_data.collider_min[1] = -a.SR_data.collider_max[1]
+      b.SR_data.collider_max[1] = -a.SR_data.collider_min[1]
 
-   if img == None:
-      return 0
+      b.SR_data.cone_constraint = a.SR_data.cone_constraint
 
-   cache = g_encoder['texture_cache']
-   buffer = g_encoder['data']['texture']
-   pack = g_encoder['data']['pack']
+      _v3copyflipy( a.SR_data.conevx, b.SR_data.conevy )
+      _v3copyflipy( a.SR_data.conevy, b.SR_data.conevx )
+      _v3copyflipy( a.SR_data.coneva, b.SR_data.coneva )
 
-   name = get_texture_resource_name( img )
+      b.SR_data.conet = a.SR_data.conet
 
-   if name in cache:
-      return cache[name]
-   
-   cache[name] = len( buffer )
-   
-   tex = mdl_texture()
-   tex.pstr_name = encoder_process_pstr( name )
+      # redraw
+      ob = bpy.context.scene.objects[0]
+      ob.hide_render = ob.hide_render
+      return {'FINISHED'}
+   #}
+#}
+
+class SR_COMPILE(bpy.types.Operator):
+#{
+   bl_idname="skaterift.compile_all"
+   bl_label="Compile All"
 
-   if g_encoder['pack_textures']:
+   def execute(_,context):
    #{
-      tex.pack_offset = len( pack )
-      pack.extend( qoi_encode( img ) )
-      tex.pack_length = len( pack ) - tex.pack_offset
-   #}
-   else:
-      tex.pack_offset = 0
+      view_layer = bpy.context.view_layer
+      for col in view_layer.layer_collection.children["export"].children:
+         if not col.hide_viewport or bpy.context.scene.SR_data.use_hidden:
+            sr_compile( bpy.data.collections[col.name] )
 
-   buffer += [ tex ]
-   return cache[name]
+      return {'FINISHED'}
+   #}
 #}
 
-def material_tex_image(v):
+class SR_COMPILE_THIS(bpy.types.Operator):
 #{
-    return {
-       "Image Texture":
-       {
-          "image": F"{v}"
-       }
-    }
-#}
+   bl_idname="skaterift.compile_this"
+   bl_label="Compile This collection"
 
-cxr_graph_mapping = \
-{
-   # Default shader setup 
-   "Principled BSDF":
-   {
-      "Base Color":
-      {
-         "Image Texture":
-         {
-            "image": "tex_diffuse"
-         },
-         "Mix":
-         {
-            "A": material_tex_image("tex_diffuse"),
-            "B": material_tex_image("tex_decal")
-         },
-      },
-      "Normal":
-      {
-         "Normal Map":
-         {
-            "Color": material_tex_image("tex_normal")
-         }
-      }
-   }
-}
+   def execute(_,context):
+   #{
+      col = bpy.context.collection
+      sr_compile( col )
 
-# https://harrygodden.com/git/?p=convexer.git;a=blob;f=__init__.py;#l1164
-#
-def material_info(mat):
+      return {'FINISHED'}
+   #}
+#}
+
+class SR_INTERFACE(bpy.types.Panel):
 #{
-   info = {}
+   bl_idname = "VIEW3D_PT_skate_rift"
+   bl_label = "Skate Rift"
+   bl_space_type = 'VIEW_3D'
+   bl_region_type = 'UI'
+   bl_category = "Skate Rift"
 
-   # Using the cv_graph_mapping as a reference, go through the shader
-   # graph and gather all $props from it.
-   #
-   def _graph_read( node_def, node=None, depth=0 ):
+   def draw(_, context):
    #{
-      nonlocal mat
-      nonlocal info
-      
-      # Find rootnodes
-      #
-      if node == None:
-      #{
-         _graph_read.extracted = []
+      # Compiler section
 
-         for node_idname in node_def:
-         #{
-            for n in mat.node_tree.nodes:
-            #{
-               if n.name == node_idname:
-               #{
-                  node_def = node_def[node_idname]
-                  node = n
-                  break
-               #}
+      row = _.layout.row()
+      row.scale_y = 1.75
+      row.prop( context.scene.SR_data, 'panel', expand=True )
+
+      if context.scene.SR_data.panel == 'SETTINGS': #{
+         _.layout.prop( context.scene.SR_data, 'gizmos' )
+      #}
+      elif context.scene.SR_data.panel == 'EXPORT': #{
+         _.layout.prop( context.scene.SR_data, "export_dir" )
+         col = bpy.context.collection
+
+         found_in_export = False
+         export_count = 0
+         view_layer = bpy.context.view_layer
+         for c1 in view_layer.layer_collection.children["export"].children: #{
+            if not c1.hide_viewport or bpy.context.scene.SR_data.use_hidden:
+               export_count += 1
+
+            if c1.name == col.name: #{
+               found_in_export = True
             #}
          #}
-      #}
 
-      for link in node_def:
-      #{
-         link_def = node_def[link]
+         box = _.layout.box()
+         row = box.row()
+         row.alignment = 'CENTER'
+         row.scale_y = 1.5
+
+         if found_in_export: #{
+            row.label( text=col.name + ".mdl" )
+            box.prop( col.SR_data, "pack_textures" )
+            box.prop( col.SR_data, "animations" )
+            box.operator( "skaterift.compile_this" )
+         #}
+         else: #{
+            row.enabled=False
+            row.label( text=col.name )
+
+            row = box.row()
+            row.enabled=False
+            row.alignment = 'CENTER'
+            row.scale_y = 1.5
+            row.label( text="This collection is not in the export group" )
+         #}
 
-         if isinstance( link_def, dict ):
-         #{
-            node_link = None
-            for x in node.inputs:
-            #{
-               if isinstance( x, bpy.types.NodeSocketColor ):
-               #{
-                  if link == x.name:
-                  #{
-                     node_link = x
-                     break
-                  #}
-               #}
-            #}
+         box = _.layout.box()
+         row = box.row()
 
-            if node_link and node_link.is_linked:
-            #{
-               # look for definitions for the connected node type
-               #
-               from_node = node_link.links[0].from_node
-               
-               node_name = from_node.name.split('.')[0]
-               if node_name in link_def:
-               #{
-                  from_node_def = link_def[ node_name ]
+         split = row.split( factor=0.3, align=True )
+         split.prop( context.scene.SR_data, "use_hidden", text="hidden" )
+         
+         row1 = split.row()
+         if export_count == 0:
+            row1.enabled=False
+         row1.operator( "skaterift.compile_all", \
+                           text=F"Compile all ({export_count} collections)" )
+      #}
+      elif context.scene.SR_data.panel == 'ENTITY': #{
+         active_object = context.active_object
+         if not active_object: return
 
-                  _graph_read( from_node_def, from_node, depth+1 )
-               #}
-               
-               # No definition! :(
-               #  TODO: Make a warning for this?
+         box = _.layout.box()
+         row = box.row()
+         row.alignment = 'CENTER'
+         row.label( text=active_object.name )
+         row.scale_y = 1.5
+
+         def _draw_prop_collection( data ): #{
+            nonlocal box
+            row = box.row()
+            row.alignment = 'CENTER'
+            row.enabled = False
+            row.scale_y = 1.5
+            row.label( text=F'{data[0]}' )
+            
+            if hasattr(type(data[0]),'sr_inspector'):#{
+               type(data[0]).sr_inspector( box, data )
             #}
-            else:
-            #{
-               if "default" in link_def:
-               #{
-                  prop = link_def['default']
-                  info[prop] = node_link.default_value
+            else:#{
+               for a in data[0].__annotations__:
+                  box.prop( data[0], a )
+            #}
+         #}
+
+         if active_object.type == 'ARMATURE': #{
+            if active_object.mode == 'POSE': #{
+               bones = active_object.data.bones
+               mb = sr_get_mirror_bone( bones )
+               if mb:#{
+                  box.operator( "skaterift.mirror_bone", \
+                                 text=F'Mirror attributes to {mb.name}' )
                #}
+
+               _draw_prop_collection( [bones.active.SR_data ] )
+            #}
+            else: #{
+               row = box.row()
+               row.alignment='CENTER'
+               row.scale_y=2.0
+               row.enabled=False
+               row.label( text="Enter pose mode to modify bone properties" )
             #}
          #}
-         else:
-         #{
-            prop = link_def
-            info[prop] = getattr( node, link )
+         elif active_object.type == 'LIGHT': #{
+            _draw_prop_collection( [active_object.data.SR_data] )
+         #}
+         elif active_object.type == 'EMPTY' or active_object.type == 'MESH': #{
+            box.prop( active_object.SR_data, "ent_type" )
+            ent_type = active_object.SR_data.ent_type
+            
+            col = getattr( active_object.SR_data, ent_type, None )
+            if col != None and len(col)!=0: _draw_prop_collection( col )
+
+            if active_object.type == 'MESH':#{
+               col = getattr( active_object.data.SR_data, ent_type, None )
+               if col != None and len(col)!=0: _draw_prop_collection( col )
+            #}
          #}
       #}
    #}
-
-   _graph_read( cxr_graph_mapping )
-   return info
 #}
 
-# Add a material to the material buffer. Returns 0 (None ID) if invalid
-#
-def encoder_process_material( mat ):
+class SR_MATERIAL_PANEL(bpy.types.Panel):
 #{
-   global g_encoder
-
-   if mat == None:
-      return 0
-
-   cache = g_encoder['material_cache']
-   buffer = g_encoder['data']['material']
-
-   if mat.name in cache:
-      return cache[mat.name]
-
-   cache[mat.name] = len( buffer )
-
-   dest = mdl_material()
-   dest.pstr_name = encoder_process_pstr( mat.name )
+   bl_label="Skate Rift material"
+   bl_idname="MATERIAL_PT_sr_material"
+   bl_space_type='PROPERTIES'
+   bl_region_type='WINDOW'
+   bl_context="material"
    
-   flags = 0x00
-   if mat.cv_data.collision: 
-      flags |= 0x2
-      if mat.cv_data.skate_surface: flags |= 0x1
-      if mat.cv_data.grind_surface: flags |= (0x8|0x1)
+   def draw(_,context):
+   #{
+      active_object = bpy.context.active_object
+      if active_object == None: return
+      active_mat = active_object.active_material
+      if active_mat == None: return
 
-   if mat.cv_data.grow_grass: flags |= 0x4
-   dest.flags = flags
+      info = material_info( active_mat )
 
-   if mat.cv_data.surface_prop == 'concrete': dest.surface_prop = 0
-   if mat.cv_data.surface_prop == 'wood': dest.surface_prop = 1
-   if mat.cv_data.surface_prop == 'grass': dest.surface_prop = 2
-   if mat.cv_data.surface_prop == 'tiles': dest.surface_prop = 3
+      if 'tex_diffuse' in info:#{
+         _.layout.label( icon='INFO', \
+            text=F"{info['tex_diffuse'].name} will be compiled" )
+      #}
 
-   if mat.cv_data.shader == 'standard': dest.shader = 0
-   if mat.cv_data.shader == 'standard_cutout': dest.shader = 1
-   if mat.cv_data.shader == 'terrain_blend': 
-   #{
-      dest.shader = 2
+      _.layout.prop( active_mat.SR_data, "shader" )
+      _.layout.prop( active_mat.SR_data, "surface_prop" )
+      _.layout.prop( active_mat.SR_data, "collision" )
 
-      dest.colour[0] = pow( mat.cv_data.sand_colour[0], 1.0/2.2 )
-      dest.colour[1] = pow( mat.cv_data.sand_colour[1], 1.0/2.2 )
-      dest.colour[2] = pow( mat.cv_data.sand_colour[2], 1.0/2.2 )
-      dest.colour[3] = 1.0
-
-      dest.colour1[0] = mat.cv_data.blend_offset[0]
-      dest.colour1[1] = mat.cv_data.blend_offset[1]
-   #}
-
-   if mat.cv_data.shader == 'vertex_blend':
-   #{
-      dest.shader = 3
-
-      dest.colour1[0] = mat.cv_data.blend_offset[0]
-      dest.colour1[1] = mat.cv_data.blend_offset[1]
-   #}
-
-   if mat.cv_data.shader == 'water':
-   #{
-      dest.shader = 4
-
-      dest.colour[0]  = pow( mat.cv_data.shore_colour[0], 1.0/2.2 )
-      dest.colour[1]  = pow( mat.cv_data.shore_colour[1], 1.0/2.2 )
-      dest.colour[2]  = pow( mat.cv_data.shore_colour[2], 1.0/2.2 )
-      dest.colour[3]  = 1.0
-      dest.colour1[0] = pow( mat.cv_data.ocean_colour[0], 1.0/2.2 )
-      dest.colour1[1] = pow( mat.cv_data.ocean_colour[1], 1.0/2.2 )
-      dest.colour1[2] = pow( mat.cv_data.ocean_colour[2], 1.0/2.2 )
-      dest.colour1[3] = 1.0
-   #}
-   
-   inf = material_info( mat )
-   
-   if mat.cv_data.shader == 'standard' or \
-      mat.cv_data.shader == 'standard_cutout' or \
-      mat.cv_data.shader == 'terrain_blend' or \
-      mat.cv_data.shader == 'vertex_blend':
-   #{
-      if 'tex_diffuse' in inf: 
-         dest.tex_diffuse = encoder_process_texture(inf['tex_diffuse'])
-   #}
-
-   buffer += [dest]
-   return cache[mat.name]
-#}
-
-# Create a tree structure containing all the objects in the collection
-#
-def encoder_build_scene_graph( collection ):
-#{
-   global g_encoder
-
-   print( "  creating scene graph" )
-
-   # initialize root
-   #
-   graph = g_encoder['scene_graph']
-   graph_lookup = g_encoder['graph_lookup']
-   graph["obj"] = None
-   graph["depth"] = 0
-   graph["children"] = []
-   graph["uid"] = 0
-   graph["parent"] = None
-
-   def _new_uid():
-   #{
-      global g_encoder
-      uid = g_encoder['uid_count']
-      g_encoder['uid_count'] += 1
-      return uid
-   #}
-
-   for obj in collection.all_objects:
-   #{
-      #if obj.parent: continue
-
-      def _extend( p, n, d ):
-      #{
-         nonlocal collection
-
-         uid = _new_uid()
-         tree = {}
-         tree["uid"] = uid
-         tree["children"] = []
-         tree["depth"] = d
-         tree["obj"] = n
-         tree["parent"] = p
-         n.cv_data.uid = uid
-
-         # Descend into amature
-         #
-         if n.type == 'ARMATURE':
-         #{
-            tree["bones"] = [None] # None is the root transform
-            tree["ik_count"] = 0
-            tree["collider_count"] = 0
-            tree["compile_animation"] = collection.cv_data.animations
-            
-            # Here also collects some information about constraints, ik and 
-            # counts colliders for the armature.
-            #
-            def _extendb( p, n, d ):
-            #{
-               nonlocal tree
-
-               btree = {}
-               btree["bone"] = n
-               btree["linked_armature"] = tree
-               btree["uid"] = _new_uid()
-               btree["children"] = []
-               btree["depth"] = d
-               btree["parent"] = p
-               tree["bones"] += [n.name]
-
-               for c in n.children:
-               #{
-                  _extendb( btree, c, d+1 )
-               #}
-
-               for c in tree['obj'].pose.bones[n.name].constraints:
-               #{
-                  if c.type == 'IK':
-                  #{
-                     btree["ik_target"] = c.subtarget
-                     btree["ik_pole"] = c.pole_subtarget
-                     tree["ik_count"] += 1
-                  #}
-               #}
-
-               if n.cv_data.collider != 'collider_none':
-                  tree['collider_count'] += 1
-
-               btree['deform'] = n.use_deform
-               p['children'] += [btree]
-            #}
-
-            for b in n.data.bones:
-               if not b.parent:
-                  _extendb( tree, b, d+1 )
-         #}
-         
-         # Recurse into children of this object
-         #
-         for obj1 in n.children:
-         #{
-            for c1 in obj1.users_collection:
-            #{
-               if c1 == collection:
-               #{
-                  _extend( tree, obj1, d+1 )
-                  break
-               #}
-            #}
-         #}
-
-         p["children"] += [tree]
-         graph_lookup[n] = tree
+      if active_mat.SR_data.collision:#{
+         _.layout.prop( active_mat.SR_data, "skate_surface" )
+         _.layout.prop( active_mat.SR_data, "grind_surface" )
+         _.layout.prop( active_mat.SR_data, "grow_grass" )
+      #}
 
+      if active_mat.SR_data.shader == "terrain_blend":#{
+         box = _.layout.box()
+         box.prop( active_mat.SR_data, "blend_offset" )
+         box.prop( active_mat.SR_data, "sand_colour" )
+      #}
+      elif active_mat.SR_data.shader == "vertex_blend":#{
+         box = _.layout.box()
+         box.label( icon='INFO', text="Uses vertex colours, the R channel" )
+         box.prop( active_mat.SR_data, "blend_offset" )
+      #}
+      elif active_mat.SR_data.shader == "water":#{
+         box = _.layout.box()
+         box.label( icon='INFO', text="Depth scale of 16 meters" )
+         box.prop( active_mat.SR_data, "shore_colour" )
+         box.prop( active_mat.SR_data, "ocean_colour" )
       #}
-
-      _extend( graph, obj, 1 )
-
-   #}
-#}
-
-
-# Kind of a useless thing i made but it looks cool and adds complexity!!1
-#
-def encoder_graph_iterator( root ):
-#{
-   for c in root['children']:
-   #{
-      yield c
-      yield from encoder_graph_iterator(c)
    #}
 #}
 
-
-# Push a vertex into the model file, or return a cached index (c_uint32)
-#
-def encoder_vertex_push( vertex_reference, co,norm,uv,colour,groups,weights ):
+def sr_get_type_enum( scene, context ):
 #{
-   global g_encoder
-   buffer = g_encoder['data']['vertex']
-
-   TOLERENCE = 4
-   m = float(10**TOLERENCE)
-   
-   # Would be nice to know if this can be done faster than it currently runs,
-   # its quite slow.
-   #
-   key = (int(co[0]*m+0.5),
-          int(co[1]*m+0.5),
-          int(co[2]*m+0.5),
-          int(norm[0]*m+0.5),
-          int(norm[1]*m+0.5),
-          int(norm[2]*m+0.5),
-          int(uv[0]*m+0.5),
-          int(uv[1]*m+0.5),
-          colour[0],    # these guys are already quantized
-          colour[1],    # .
-          colour[2],    # .
-          colour[3],    # .
-          weights[0],   # v
-          weights[1],
-          weights[2],
-          weights[3],
-          groups[0],
-          groups[1],
-          groups[2],
-          groups[3])
-
-   if key in vertex_reference:
-      return vertex_reference[key]
-   else:
-   #{
-      index = c_uint32( len(vertex_reference) )
-      vertex_reference[key] = index
-
-      v = mdl_vert()
-      v.co[0]       =  co[0]
-      v.co[1]       =  co[2]
-      v.co[2]       = -co[1]
-      v.norm[0]     =  norm[0]
-      v.norm[1]     =  norm[2]
-      v.norm[2]     = -norm[1]
-      v.uv[0]       =  uv[0]
-      v.uv[1]       =  uv[1]
-      v.colour[0]   =  colour[0]
-      v.colour[1]   =  colour[1]
-      v.colour[2]   =  colour[2]
-      v.colour[3]   =  colour[3]
-      v.weights[0]  =  weights[0]
-      v.weights[1]  =  weights[1]
-      v.weights[2]  =  weights[2]
-      v.weights[3]  =  weights[3]
-      v.groups[0]   =  groups[0]
-      v.groups[1]   =  groups[1]
-      v.groups[2]   =  groups[2]
-      v.groups[3]   =  groups[3]
-
-      buffer += [v]
-      return index
-   #}
-#}
+   items = [('none','None',"")]
+   mesh_entities=['ent_gate']
+   point_entities=['ent_spawn','ent_route_node','ent_route']
 
+   for e in point_entities: items += [(e,e,'')]
 
-# Compile a mesh (or use one from the cache) onto node, based on node_def
-# No return value
-#
-def encoder_compile_mesh( node, node_def ):
-#{
-   global g_encoder
-   
-   graph         = g_encoder['scene_graph']
-   graph_lookup  = g_encoder['graph_lookup']
-   mesh_cache    = g_encoder['mesh_cache']
-   obj           = node_def['obj']
-   armature_def  = None
-   can_use_cache = True
-   
-   # Check for modifiers that typically change the data per-instance
-   # there is no well defined rule for the choices here, its just what i've
-   # needed while producing the game.
-   #
-   # It may be possible to detect these cases automatically.
-   #
-   for mod in obj.modifiers:
-   #{
-      if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
-         mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
-         mod.type == 'ARRAY':
-      #{
-         can_use_cache = False
+   if context.scene.SR_data.panel == 'ENTITY': #{
+      if context.active_object.type == 'MESH': #{
+         for e in mesh_entities: items += [(e,e,'')]
       #}
-
-      if mod.type == 'ARMATURE':
-         armature_def = graph_lookup[mod.object]
-
-   # Check the cache first
-   #
-   if can_use_cache and (obj.data.name in mesh_cache):
-   #{
-      ref = mesh_cache[obj.data.name]
-      node.submesh_start = ref.submesh_start
-      node.submesh_count = ref.submesh_count
-      return
    #}
-
-   # Compile a whole new mesh
-   #
-   node.submesh_start = len( g_encoder['data']['submesh'] )
-   node.submesh_count = 0
-
-   dgraph = bpy.context.evaluated_depsgraph_get()
-   data = obj.evaluated_get(dgraph).data
-   data.calc_loop_triangles()
-   data.calc_normals_split()
-   
-   # Mesh is split into submeshes based on their material
-   #
-   mat_list = data.materials if len(data.materials) > 0 else [None]
-   for material_id, mat in enumerate(mat_list):
-   #{
-      mref = {}
-
-      sm = mdl_submesh()
-      sm.indice_start = len( g_encoder['data']['indice'] )
-      sm.vertex_start = len( g_encoder['data']['vertex'] )
-      sm.vertex_count = 0
-      sm.indice_count = 0
-      sm.material_id = encoder_process_material( mat )
-
-      for i in range(3):
-      #{
-         sm.bbx[0][i] =  999999
-         sm.bbx[1][i] = -999999
-      #}
-      
-      # Keep a reference to very very very similar vertices
-      #
-      vertex_reference = {}
-
-      # Write the vertex / indice data
-      #
-      for tri_index, tri in enumerate(data.loop_triangles):
-      #{
-         if tri.material_index != material_id:
-            continue
-
-         for j in range(3):
-         #{
-            vert = data.vertices[tri.vertices[j]]
-            li = tri.loops[j]
-            vi = data.loops[li].vertex_index
-            
-            # Gather vertex information
-            #
-            co      = vert.co
-            norm    = data.loops[li].normal
-            uv      = (0,0)
-            colour  = (255,255,255,255)
-            groups  = [0,0,0,0]
-            weights = [0,0,0,0]
-
-            # Uvs
-            #
-            if data.uv_layers:
-               uv = data.uv_layers.active.data[li].uv
-            
-            # Vertex Colours
-            #
-            if data.vertex_colors:
-            #{
-               colour = data.vertex_colors.active.data[li].color
-               colour = (int(colour[0]*255.0),\
-                         int(colour[1]*255.0),\
-                         int(colour[2]*255.0),\
-                         int(colour[3]*255.0))
-            #}
-            
-            # Weight groups: truncates to the 3 with the most influence. The
-            #                fourth bone ID is never used by the shader so it is
-            #                always 0
-            #
-            if armature_def:
-            #{
-               src_groups = [_ for _ in data.vertices[vi].groups \
-                              if obj.vertex_groups[_.group].name in \
-                                 armature_def['bones']]
-
-               weight_groups = sorted( src_groups, key = \
-                                       lambda a: a.weight, reverse=True )
-               tot = 0.0
-               for ml in range(3):
-               #{
-                  if len(weight_groups) > ml:
-                  #{
-                     g = weight_groups[ml]
-                     name = obj.vertex_groups[g.group].name
-                     weight = g.weight
-
-                     weights[ml] = weight
-                     groups[ml] = armature_def['bones'].index(name)
-                     tot += weight
-                  #}
-               #}
-            
-               if len(weight_groups) > 0:
-               #{
-                  inv_norm = (1.0/tot) * 65535.0
-                  for ml in range(3):
-                  #{
-                     weights[ml] = int( weights[ml] * inv_norm )
-                     weights[ml] = min( weights[ml], 65535 )
-                     weights[ml] = max( weights[ml], 0 )
-                  #}
-               #}
-            #}
-            else:
-            #{
-               li1 = tri.loops[(j+1)%3]
-               vi1 = data.loops[li1].vertex_index
-               e0 = data.edges[ data.loops[li].edge_index ]
-
-               if e0.use_freestyle_mark and \
-                     ((e0.vertices[0] == vi and e0.vertices[1] == vi1) or \
-                      (e0.vertices[0] == vi1 and e0.vertices[1] == vi)):
-               #{
-                  weights[0] = 1
-               #}
-            #}
-            
-            # Add vertex and expand bound box
-            #
-            index = encoder_vertex_push( vertex_reference, co, \
-                                                           norm, \
-                                                           uv, \
-                                                           colour, \
-                                                           groups, \
-                                                           weights )
-            g_encoder['data']['indice'] += [index]
-         #}
-      #}
-      
-      # How many unique verts did we add in total
-      #
-      sm.vertex_count = len(g_encoder['data']['vertex']) - sm.vertex_start
-      sm.indice_count = len(g_encoder['data']['indice']) - sm.indice_start
-      
-      # Make sure bounding box isn't -inf -> inf if no vertices
-      #
-      if sm.vertex_count == 0:
-         for j in range(2):
-            for i in range(3):
-               sm.bbx[j][i] = 0
-      else:
-      #{
-         for j in range(sm.vertex_count):
-         #{
-            vert = g_encoder['data']['vertex'][ sm.vertex_start + j ]
-
-            for i in range(3):
-            #{
-               sm.bbx[0][i] = min( sm.bbx[0][i], vert.co[i] )
-               sm.bbx[1][i] = max( sm.bbx[1][i], vert.co[i] )
-            #}
-         #}
-      #}
-      
-      # Add submesh to encoder
-      #
-      g_encoder['data']['submesh'] += [sm]
-      node.submesh_count += 1
-
+   else: #{
+      for e in mesh_entities: items += [(e,e,'')]
    #}
 
-   # Save a reference to this node since we want to reuse the submesh indices
-   # later.
-   g_encoder['mesh_cache'][obj.data.name] = node
+   return items
 #}
 
-
-def encoder_compile_ent_as( name, node, node_def ):
+def sr_on_type_change( _, context ):
 #{
-   global g_encoder
-
-   if name == 'classtype_none':
-   #{
-      node.offset = 0
-      node.classtype = 0
-      return
-   #}
-   elif name not in globals():
-   #{
-      print( "Classtype '" +name + "' is unknown!" )
-      return
+   obj = context.active_object
+   ent_type = obj.SR_data.ent_type
+   if ent_type == 'none': return
+   if obj.type == 'MESH':#{
+      col = getattr( obj.data.SR_data, ent_type, None )
+      if col != None and len(col)==0: col.add()
    #}
    
-   buffer = g_encoder['data']['entdata']
-   node.offset = len(buffer)
-
-   cl = globals()[ name ]
-   inst = cl()
-   inst.encode_obj( node, node_def )
-
-   buffer.extend( bytearray(inst) )
-   bytearray_align_to( buffer, 4 )
+   col = getattr( obj.SR_data, ent_type, None )
+   if col != None and len(col)==0: col.add()
 #}
 
-# Compiles animation data into model and gives us some extra node_def entries
-#
-def encoder_compile_armature( node, node_def ):
+class SR_OBJECT_ENT_SPAWN(bpy.types.PropertyGroup):
 #{
-   global g_encoder
-   
-   entdata       = g_encoder['data']['entdata']
-   animdata      = g_encoder['data']['anim']
-   keyframedata  = g_encoder['data']['keyframe']
-   mesh_cache    = g_encoder['mesh_cache']
-   obj           = node_def['obj']
-   bones         = node_def['bones']
-
-   # extra info
-   node_def['anim_start'] = len(animdata)
-   node_def['anim_count'] = 0
-   if not node_def['compile_animation']:
-   #{
-      return
-   #}  
-
-   # Compile anims
-   #
-   if obj.animation_data:
-   #{
-      # So we can restore later
-      #
-      previous_frame  = bpy.context.scene.frame_current
-      previous_action = obj.animation_data.action
-      POSE_OR_REST_CACHE = obj.data.pose_position
-      obj.data.pose_position = 'POSE'
-
-      for NLALayer in obj.animation_data.nla_tracks:
-      #{
-         for NLAStrip in NLALayer.strips:
-         #{
-            # set active
-            #
-            for a in bpy.data.actions:
-            #{
-               if a.name == NLAStrip.name:
-               #{
-                  obj.animation_data.action = a
-                  break
-               #}
-            #}
-            
-            # Clip to NLA settings
-            #
-            anim_start = int(NLAStrip.action_frame_start)
-            anim_end   = int(NLAStrip.action_frame_end)
-
-            # Export strips
-            #
-            anim = mdl_animation()
-            anim.pstr_name = encoder_process_pstr( NLAStrip.action.name )
-            anim.rate = 30.0
-            anim.offset = len(keyframedata)
-            anim.length = anim_end-anim_start
-            
-            # Export the keyframes
-            for frame in range(anim_start,anim_end):
-            #{
-               bpy.context.scene.frame_set(frame)
-               
-               for bone_name in bones:
-               #{
-                  for pb in obj.pose.bones:
-                  #{
-                     if pb.name != bone_name: continue
-
-                     rb = obj.data.bones[ bone_name ]
-                     
-                     # relative bone matrix
-                     if rb.parent is not None:
-                     #{
-                        offset_mtx = rb.parent.matrix_local
-                        offset_mtx = offset_mtx.inverted_safe() @ \
-                                     rb.matrix_local
-
-                        inv_parent = pb.parent.matrix @ offset_mtx
-                        inv_parent.invert_safe()
-                        fpm = inv_parent @ pb.matrix 
-                     #}
-                     else:
-                     #{
-                        bone_mtx = rb.matrix.to_4x4()
-                        local_inv = rb.matrix_local.inverted_safe()
-                        fpm = bone_mtx @ local_inv @ pb.matrix
-                     #}
-
-                     loc, rot, sca = fpm.decompose()
-
-                     # local position
-                     final_pos = Vector(( loc[0], loc[2], -loc[1] ))
-
-                     # rotation
-                     lc_m = pb.matrix_channel.to_3x3()
-                     if pb.parent is not None:
-                     #{
-                        smtx = pb.parent.matrix_channel.to_3x3()
-                        lc_m = smtx.inverted() @ lc_m
-                     #}
-                     rq = lc_m.to_quaternion()
-
-                     kf = mdl_keyframe()
-                     kf.co[0] =  final_pos[0]
-                     kf.co[1] =  final_pos[1]
-                     kf.co[2] =  final_pos[2]
-
-                     kf.q[0] =  rq[1]
-                     kf.q[1] =  rq[3]
-                     kf.q[2] = -rq[2]
-                     kf.q[3] =  rq[0]
-                     
-                     # scale
-                     kf.s[0] = sca[0]
-                     kf.s[1] = sca[2]
-                     kf.s[2] = sca[1]
-
-                     keyframedata += [kf]
-                     break
-                  #}
-               #}
-            #}
-            
-            # Add to animation buffer
-            #
-            animdata += [anim]
-            node_def['anim_count'] += 1
+   alias: bpy.props.StringProperty( name='alias' )
+#}
 
-            # Report progress
-            #
-            status_name = F"            " + " |"*(node_def['depth']-1)
-            print( F"{status_name} | *anim: {NLAStrip.action.name}" )
-         #}
-      #}
-      
-      # Restore context to how it was before
-      #
-      bpy.context.scene.frame_set( previous_frame )
-      obj.animation_data.action = previous_action
-      obj.data.pose_position = POSE_OR_REST_CACHE
-   #}
+class SR_OBJECT_ENT_GATE(bpy.types.PropertyGroup):
+#{
+   target: bpy.props.PointerProperty( \
+               type=bpy.types.Object, name="destination", \
+               poll=lambda self,obj: sr_filter_ent_type(obj,'ent_gate'))
 #}
 
-# We are trying to compile this node_def
-#
-def encoder_process_definition( node_def ):
+class SR_MESH_ENT_GATE(bpy.types.PropertyGroup):
 #{
-   global g_encoder
+   dimensions: bpy.props.FloatVectorProperty(name="dimensions",size=3)
+#}
 
-   # data sources for object/bone are taken differently
-   #
-   if 'obj' in node_def:
-   #{
-      obj      = node_def['obj']
-      obj_type = obj.type
-      obj_co   = obj.matrix_world @ Vector((0,0,0))
+class SR_OBJECT_ENT_ROUTE_ENTRY(bpy.types.PropertyGroup):
+#{
+   target: bpy.props.PointerProperty( \
+               type=bpy.types.Object, name='target', \
+               poll=lambda self,obj: sr_filter_ent_type(obj,'ent_gate'))
+#}
 
-      if obj_type == 'ARMATURE':
-         obj_classtype = 'classtype_skeleton'
-      elif obj_type == 'LIGHT':
-      #{
-         obj_classtype = 'classtype_world_light'
-      #}
-      else:
-      #{
-         obj_classtype = obj.cv_data.classtype
-
-         # Check for armature deform
-         #
-         for mod in obj.modifiers:
-         #{
-            if mod.type == 'ARMATURE':
-            #{
-               obj_classtype = 'classtype_skin'
-
-               # Make sure to freeze armature in rest while we collect 
-               # vertex information
-               #
-               armature_def = g_encoder['graph_lookup'][mod.object]
-               POSE_OR_REST_CACHE = armature_def['obj'].data.pose_position
-               armature_def['obj'].data.pose_position = 'REST'
-               node_def['linked_armature'] = armature_def
-               break
-            #}
-         #}
-      #}
-   #}
+class SR_UL_ROUTE_NODE_LIST(bpy.types.UIList):
+#{
+   bl_idname = 'SR_UL_ROUTE_NODE_LIST'
 
-   elif 'bone' in node_def:
+   def draw_item(_,context,layout,data,item,icon,active_data,active_propname):
    #{
-      obj      = node_def['bone']
-      obj_type = 'BONE'
-      obj_co   = obj.head_local
-      obj_classtype = 'classtype_bone'
+      layout.prop( item, 'target', text='', emboss=False )
    #}
+#}
 
-   # Create node
-   #
-   node = mdl_node()
-   node.pstr_name = encoder_process_pstr( obj.name )
-
-   if node_def["parent"]:
-      node.parent = node_def["parent"]["uid"]
-
-   # Setup transform
-   #
-   node.co[0] =  obj_co[0]
-   node.co[1] =  obj_co[2]
-   node.co[2] = -obj_co[1]
-   
-   # Convert rotation quat to our space type
-   #
-   quat = obj.matrix_local.to_quaternion()
-   node.q[0] =  quat[1]
-   node.q[1] =  quat[3]
-   node.q[2] = -quat[2]
-   node.q[3] =  quat[0]
+class SR_OT_ROUTE_LIST_NEW_ITEM(bpy.types.Operator):
+#{
+   bl_idname = "skaterift.new_entry"
+   bl_label = "Add gate"
    
-   # Bone scale is just a vector to the tail
-   #
-   if obj_type == 'BONE':
-   #{
-      node.s[0] =  obj.tail_local[0] - node.co[0]
-      node.s[1] =  obj.tail_local[2] - node.co[1]
-      node.s[2] = -obj.tail_local[1] - node.co[2]
+   def execute(self, context):#{
+      active_object = context.active_object
+      active_object.SR_data.ent_route[0].gates.add()
+      return{'FINISHED'}
    #}
-   else:
-   #{
-      node.s[0] = obj.scale[0]
-      node.s[1] = obj.scale[2]
-      node.s[2] = obj.scale[1]
+#}
+
+class SR_OT_ROUTE_LIST_DEL_ITEM(bpy.types.Operator):
+#{
+   bl_idname = "skaterift.del_entry"
+   bl_label = "Remove gate"
+
+   @classmethod 
+   def poll(cls, context):#{
+      active_object = context.active_object
+      if obj_ent_type == 'ent_gate':#{
+         return active_object.SR_data.ent_route[0].gates
+      #}
+      else: return False
    #}
    
-   # Report status
-   #
-   tot_uid   = g_encoder['uid_count']-1
-   obj_uid   = node_def['uid']
-   obj_depth = node_def['depth']-1
+   def execute(self, context):#{
+      active_object = context.active_object
+      lista = active_object.SR_data.ent_route[0].gates
+      index = active_object.SR_data.ent_route[0].gates_index
+      lista.remove(index) 
+      active_object.SR_data.ent_route[0].gates_index = \
+            min(max(0, index-1), len(lista) - 1)
+      return{'FINISHED'}
+   #}
+#}
 
-   status_id   = F"    [{obj_uid: 3}/{tot_uid}]" + " |"*obj_depth
-   status_name = status_id + F" L {obj.name}"
+class SR_OBJECT_ENT_ROUTE(bpy.types.PropertyGroup):
+#{
+   gates: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_ROUTE_ENTRY)
+   gates_index: bpy.props.IntProperty()
 
-   if obj_classtype != 'classtype_none': status_type = obj_classtype
-   else: status_type = obj_type
+   colour: bpy.props.FloatVectorProperty( \
+         name="Colour",\
+         subtype='COLOR',\
+         min=0.0,max=1.0,\
+         default=Vector((0.79,0.63,0.48)),\
+         description="Route colour"\
+   )
 
-   status_parent = F"{node.parent: 3}"
-   status_armref = ""
+   alias: bpy.props.StringProperty(\
+          name="Alias",\
+          default="Untitled Course")
 
-   if obj_classtype == 'classtype_skin':
-      status_armref = F" [armature -> {armature_def['obj'].cv_data.uid}]"
+   @staticmethod
+   def sr_inspector( layout, data ):
+   #{
+      layout.prop( data[0], 'alias' )
+      layout.prop( data[0], 'colour' )
 
-   print(F"{status_name:<32} {status_type:<22} {status_parent} {status_armref}")
+      layout.label( text='Checkpoints' )
+      layout.template_list('SR_UL_ROUTE_NODE_LIST', 'Checkpoints', \
+                            data[0], 'gates', data[0], 'gates_index', rows=5)
 
-   # Process mesh if needed
-   # 
-   if obj_type == 'MESH':
-   #{
-      encoder_compile_mesh( node, node_def )
-   #}
-   elif obj_type == 'ARMATURE':
-   #{
-      encoder_compile_armature( node, node_def )
+      row = layout.row()
+      row.operator( 'skaterift.new_entry', text='Add' )
+      row.operator( 'skaterift.del_entry', text='Remove' )
    #}
+#}
 
-   encoder_compile_ent_as( obj_classtype, node, node_def )
+class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
+#{
+   ent_gate: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GATE)
+   ent_spawn: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_SPAWN)
+   ent_route: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_ROUTE)
 
-   # Make sure to reset the armature we just mucked about with
-   #
-   if obj_classtype == 'classtype_skin':
-      armature_def['obj'].data.pose_position = POSE_OR_REST_CACHE
+   ent_type: bpy.props.EnumProperty(
+      name="Type",
+      items=[('none', 'None', '', 0),
+             ('ent_gate','Gate','', 1),
+             ('ent_spawn','Spawn','', 2),
+             ('ent_route_node', 'Route Node', '', 3 ),
+             ('ent_route', 'Route', '', 4)],
+      update=sr_on_type_change
+   )
+#}
 
-   g_encoder['data']['node'] += [node]
+class SR_MESH_PROPERTIES(bpy.types.PropertyGroup):
+#{
+   ent_gate: bpy.props.CollectionProperty(type=SR_MESH_ENT_GATE)
 #}
 
-# The post processing step or the pre processing to the writing step
-#
-def encoder_write_to_file( path ):
+class SR_LIGHT_PROPERTIES(bpy.types.PropertyGroup):
 #{
-   global g_encoder
-   
-   # Compile down to a byte array
-   #
-   header = g_encoder['header']
-   file_pos = sizeof(header)
-   file_data = bytearray()
-   print( "  Compositing data arrays" )
-   
-   for array_name in g_encoder['data']:
-   #{
-      file_pos += bytearray_align_to( file_data, 16, sizeof(header) )
-      arr = g_encoder['data'][array_name]
+   daytime: bpy.props.BoolProperty( name='Daytime' )
+#}
 
-      setattr( header, array_name + "_offset", file_pos )
+class SR_BONE_PROPERTIES(bpy.types.PropertyGroup):
+#{
+   collider: bpy.props.EnumProperty( name='Collider Type',
+                                     items=[('0','none',''),
+                                            ('1','box',''),
+                                            ('2','capsule','')])
 
-      print( F"    {array_name:<16} @{file_pos:> 8X}[{len(arr)}]" )
+   collider_min: bpy.props.FloatVectorProperty( name='Collider Min', size=3 )
+   collider_max: bpy.props.FloatVectorProperty( name='Collider Max', size=3 )
 
-      if isinstance( arr, bytearray ):
-      #{
-         setattr( header, array_name + "_size", len(arr) )
+   cone_constraint: bpy.props.BoolProperty( name='Cone constraint' )
 
-         file_data.extend( arr )
-         file_pos += len(arr)
-      #}
-      else:
-      #{
-         setattr( header, array_name + "_count", len(arr) )
+   conevx: bpy.props.FloatVectorProperty( name='vx' )
+   conevy: bpy.props.FloatVectorProperty( name='vy' )
+   coneva: bpy.props.FloatVectorProperty( name='va' )
+   conet:  bpy.props.FloatProperty( name='t' )
 
-         for item in arr:
-         #{
-            bbytes = bytearray(item)
-            file_data.extend( bbytes )
-            file_pos += sizeof(item)
-         #}
+   @staticmethod
+   def sr_inspector( layout, data ):
+   #{
+      data = data[0]
+      box = layout.box()
+      box.prop( data, 'collider' )
+
+      if int(data.collider)>0:#{
+         row = box.row()
+         row.prop( data, 'collider_min' )
+         row = box.row()
+         row.prop( data, 'collider_max' )
+      #}
+      
+      box = layout.box()
+      box.prop( data, 'cone_constraint' )
+      if data.cone_constraint:#{
+         row = box.row()
+         row.prop( data, 'conevx' )
+         row = box.row()
+         row.prop( data, 'conevy' )
+         row = box.row()
+         row.prop( data, 'coneva' )
+         box.prop( data, 'conet' )
       #}
    #}
-
-   # This imperitive for this field to be santized in the future!
-   #
-   header.file_length = file_pos
-
-   print( "  Writing file" )
-   # Write header and data chunk to file
-   #
-   fp = open( path, "wb" )
-   fp.write( bytearray( header ) )
-   fp.write( file_data )
-   fp.close()
 #}
 
-# Main compiler, uses string as the identifier for the collection
-# 
-def write_model(collection_name):
+class SR_MATERIAL_PROPERTIES(bpy.types.PropertyGroup):
 #{
-   global g_encoder
-   print( F"Model graph | Create mode '{collection_name}'" )
-   folder = bpy.path.abspath(bpy.context.scene.cv_data.export_dir)
-   path = F"{folder}{collection_name}.mdl"
-   print( path )
-   
-   collection = bpy.data.collections[collection_name]
-
-   encoder_init( collection )
-   encoder_build_scene_graph( collection )
-
-   # Compile 
-   #
-   print( "  Comping objects" )
-   it = encoder_graph_iterator( g_encoder['scene_graph'] )
-   for node_def in it:
-      encoder_process_definition( node_def )
-
-   # Write 
-   #
-   encoder_write_to_file( path )
+   shader: bpy.props.EnumProperty( 
+      name="Format", 
+      items = [
+      ('standard',"standard",''),
+      ('standard_cutout', "standard_cutout", ''),
+      ('terrain_blend', "terrain_blend", ''),
+      ('vertex_blend', "vertex_blend", ''),
+      ('water',"water",'')
+      ])
 
-   print( F"Completed {collection_name}.mdl" )
+   surface_prop: bpy.props.EnumProperty(
+      name="Surface Property",
+      items = [
+      ('0','concrete',''),
+      ('1','wood',''),
+      ('2','grass',''),
+      ('3','tiles','')
+      ])
+   
+   collision: bpy.props.BoolProperty( \
+         name="Collisions Enabled",\
+         default=True,\
+         description = "Can the player collide with this material"\
+   )
+   skate_surface: bpy.props.BoolProperty( \
+         name="Skate Surface", \
+         default=True,\
+         description = "Should the game try to target this surface?" \
+   )
+   grind_surface: bpy.props.BoolProperty( \
+         name="Grind Surface", \
+         default=False,\
+         description = "Grind face?" \
+   )
+   grow_grass: bpy.props.BoolProperty( \
+         name="Grow Grass", \
+         default=False,\
+         description = "Spawn grass sprites on this surface?" \
+   )
+   blend_offset: bpy.props.FloatVectorProperty( \
+         name="Blend Offset", \
+         size=2, \
+         default=Vector((0.5,0.0)),\
+         description="When surface is more than 45 degrees, add this vector " +\
+                     "to the UVs" \
+   )
+   sand_colour: bpy.props.FloatVectorProperty( \
+         name="Sand Colour",\
+         subtype='COLOR',\
+         min=0.0,max=1.0,\
+         default=Vector((0.79,0.63,0.48)),\
+         description="Blend to this colour near the 0 coordinate on UP axis"\
+   )
+   shore_colour: bpy.props.FloatVectorProperty( \
+         name="Shore Colour",\
+         subtype='COLOR',\
+         min=0.0,max=1.0,\
+         default=Vector((0.03,0.32,0.61)),\
+         description="Water colour at the shoreline"\
+   )
+   ocean_colour: bpy.props.FloatVectorProperty( \
+         name="Ocean Colour",\
+         subtype='COLOR',\
+         min=0.0,max=1.0,\
+         default=Vector((0.0,0.006,0.03)),\
+         description="Water colour in the deep bits"\
+   )
 #}
 
 # ---------------------------------------------------------------------------- #
@@ -2142,12 +1881,12 @@ def cv_draw_halfsphere( pos, tx, ty, tz, radius, colour ):
 
 # Draw transformed -1 -> 1 cube
 #
-def cv_draw_ucube( transform, colour ):
+def cv_draw_ucube( transform, colour, s=Vector((1,1,1)), o=Vector((0,0,0)) ):
 #{
    global cv_view_verts, cv_view_colours
 
-   a = Vector((-1,-1,-1))
-   b = Vector((1,1,1))
+   a = o + -1.0 * s
+   b = o +  1.0 * s
    
    vs = [None]*8
    vs[0] = transform @ Vector((a[0], a[1], a[2]))
@@ -2239,6 +1978,23 @@ def cv_draw_arrow( p0, p1, c0, size=0.15 ):
    cv_draw_lines()
 #}
 
+def cv_draw_line_dotted( p0, p1, c0, dots=10 ):
+#{
+   global cv_view_verts, cv_view_colours
+
+   for i in range(dots):#{
+      t0 = i/dots
+      t1 = (i+0.25)/dots
+
+      p2 = p0*(1.0-t0)+p1*t0
+      p3 = p0*(1.0-t1)+p1*t1
+
+      cv_view_verts += [p2,p3]
+      cv_view_colours += [c0,c0]
+   #}
+   cv_draw_lines()
+#}
+
 # Drawhandles of a bezier control point
 #
 def cv_draw_bhandle( obj, direction, colour ):
@@ -2332,8 +2088,7 @@ def draw_limit( obj, center, major, minor, amin, amax, colour ):
    ay = major*f
    ax = minor*f
 
-   for x in range(16):
-   #{
+   for x in range(16):#{
       t0 = x/16
       t1 = (x+1)/16
       a0 = amin*(1.0-t0)+amax*t0
@@ -2347,13 +2102,11 @@ def draw_limit( obj, center, major, minor, amin, amax, colour ):
       cv_view_verts += [p0,p1]
       cv_view_colours += [colour,colour]
 
-      if x == 0:
-      #{
+      if x == 0:#{
          cv_view_verts += [p0,center]
          cv_view_colours += [colour,colour]
       #}
-      if x == 15:
-      #{
+      if x == 15:#{
          cv_view_verts += [p1,center]
          cv_view_colours += [colour,colour]
       #}
@@ -2378,8 +2131,7 @@ def draw_cone_twist( center, vx, vy, va ):
    cv_view_verts += [center, center+va*size]
    cv_view_colours += [ (1,1,1,1), (1,1,1,1) ]
 
-   for x in range(32):
-   #{
+   for x in range(32):#{
       t0 = (x/32) * math.tau
       t1 = ((x+1)/32) * math.tau
 
@@ -2407,20 +2159,20 @@ def draw_skeleton_helpers( obj ):
 #{
    global cv_view_verts, cv_view_colours
 
-   if obj.data.pose_position != 'REST':
-   #{
+   if obj.data.pose_position != 'REST':#{
       return
    #}
 
-   for bone in obj.data.bones:
-   #{
+   for bone in obj.data.bones:#{
       c = bone.head_local
-      a = Vector((bone.cv_data.v0[0], bone.cv_data.v0[1], bone.cv_data.v0[2]))
-      b = Vector((bone.cv_data.v1[0], bone.cv_data.v1[1], bone.cv_data.v1[2]))
-
-      if bone.cv_data.collider == 'collider_box':
-      #{
-         
+      a = Vector((bone.SR_data.collider_min[0], 
+                  bone.SR_data.collider_min[1], 
+                  bone.SR_data.collider_min[2]))
+      b = Vector((bone.SR_data.collider_max[0], 
+                  bone.SR_data.collider_max[1], 
+                  bone.SR_data.collider_max[2]))
+
+      if bone.SR_data.collider == '1':#{
          vs = [None]*8
          vs[0]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+a[2]))
          vs[1]=obj.matrix_world@Vector((c[0]+a[0],c[1]+b[1],c[2]+a[2]))
@@ -2434,8 +2186,7 @@ def draw_skeleton_helpers( obj ):
          indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
                     (0,4),(1,5),(2,6),(3,7)]
 
-         for l in indices:
-         #{
+         for l in indices:#{
             v0 = vs[l[0]]
             v1 = vs[l[1]]
 
@@ -2444,593 +2195,318 @@ def draw_skeleton_helpers( obj ):
             cv_view_colours += [(0.5,0.5,0.5,0.5),(0.5,0.5,0.5,0.5)]
          #}
       #}
-      elif bone.cv_data.collider == 'collider_capsule':
-      #{
+      elif bone.SR_data.collider == '2':#{
          v0 = b-a
          major_axis = 0
-         largest = -1.0
-
-         for i in range(3):
-         #{
-            if abs(v0[i]) > largest:
-            #{
-               largest = abs(v0[i])
-               major_axis = i
-            #}
-         #}
-
-         v1 = Vector((0,0,0))
-         v1[major_axis] = 1.0
-
-         tx = Vector((0,0,0))
-         ty = Vector((0,0,0))
-
-         cv_tangent_basis( v1, tx, ty )
-         r = (abs(tx.dot( v0 )) + abs(ty.dot( v0 ))) * 0.25
-         l = v0[ major_axis ] - r*2
-
-         p0 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l*-0.5 )
-         p1 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l* 0.5 )
-
-         colour = [0.2,0.2,0.2,1.0]
-         colour[major_axis] = 0.5
-
-         cv_draw_halfsphere( p0, -v1, ty, tx, r, colour )
-         cv_draw_halfsphere( p1,  v1, ty, tx, r, colour )
-         cv_draw_line( p0+tx* r, p1+tx* r, colour )
-         cv_draw_line( p0+tx*-r, p1+tx*-r, colour )
-         cv_draw_line( p0+ty* r, p1+ty* r, colour )
-         cv_draw_line( p0+ty*-r, p1+ty*-r, colour )
-      #}
-      else:
-      #{
-         continue
-      #}
-
-      center = obj.matrix_world @ c
-      if bone.cv_data.con0:
-      #{
-         vx = Vector([bone.cv_data.conevx[_] for _ in range(3)])
-         vy = Vector([bone.cv_data.conevy[_] for _ in range(3)])
-         va = Vector([bone.cv_data.coneva[_] for _ in range(3)])
-         draw_cone_twist( center, vx, vy, va )
-
-         #draw_limit( obj, c, Vector((0,0,1)),Vector((0,-1,0)), \
-         #            bone.cv_data.mins[0], bone.cv_data.maxs[0], \
-         #            (1,0,0,1))
-         #draw_limit( obj, c, Vector((0,-1,0)),Vector((1,0,0)), \
-         #            bone.cv_data.mins[1], bone.cv_data.maxs[1], \
-         #            (0,1,0,1))
-         #draw_limit( obj, c, Vector((1,0,0)),Vector((0,0,1)), \
-         #            bone.cv_data.mins[2], bone.cv_data.maxs[2], \
-         #            (0,0,1,1))
-      #}
-   #}
-#}
-
-def cv_draw():
-#{
-   global cv_view_shader
-   global cv_view_verts
-   global cv_view_colours
-   global cv_view_course_i
-
-   cv_view_course_i = 0
-   cv_view_verts = []
-   cv_view_colours = []
-
-   cv_view_shader.bind()
-   gpu.state.depth_mask_set(False)
-   gpu.state.line_width_set(2.0)
-   gpu.state.face_culling_set('BACK')
-   gpu.state.depth_test_set('LESS')
-   gpu.state.blend_set('NONE')
-
-   for obj in bpy.context.collection.objects:
-   #{
-      if obj.type == 'ARMATURE':
-      #{
-         if obj.data.pose_position == 'REST':
-            draw_skeleton_helpers( obj )
-      #}
-      else:
-      #{
-         classtype = obj.cv_data.classtype
-         if (classtype != 'classtype_none') and (classtype in globals()):
-         #{
-            cl = globals()[ classtype ]
-
-            if getattr( cl, "draw_scene_helpers", None ):
-            #{
-               cl.draw_scene_helpers( obj )
-            #}
-         #}
-      #}
-   #}
-
-   cv_draw_lines()
-   return
-#}
-               
-
-# ---------------------------------------------------------------------------- #
-#                                                                              #
-#                                 Blender                                      #
-#                                                                              #
-# ---------------------------------------------------------------------------- #
-
-# Checks whether this object has a classtype assigned. we can only target other
-# classes
-def cv_poll_target(scene, obj):
-#{
-   if obj == bpy.context.active_object:
-      return False
-   if obj.cv_data.classtype == 'classtype_none':
-      return False
-
-   return True
-#}
-
-class CV_MESH_SETTINGS(bpy.types.PropertyGroup):
-#{
-   v0: bpy.props.FloatVectorProperty(name="v0",size=3)
-   v1: bpy.props.FloatVectorProperty(name="v1",size=3)
-   v2: bpy.props.FloatVectorProperty(name="v2",size=3)
-   v3: bpy.props.FloatVectorProperty(name="v3",size=3)
-#}
+         largest = -1.0
 
-class CV_LIGHT_SETTINGS(bpy.types.PropertyGroup):
-#{
-   bp0: bpy.props.BoolProperty( name="bp0" );
-#}
+         for i in range(3):#{
+            if abs(v0[i]) > largest:#{
+               largest = abs(v0[i])
+               major_axis = i
+            #}
+         #}
 
-class CV_LIGHT_PANEL(bpy.types.Panel):
-#{
-   bl_label="[Skate Rift]"
-   bl_idname="SCENE_PT_cv_light"
-   bl_space_type='PROPERTIES'
-   bl_region_type='WINDOW'
-   bl_context='data'
+         v1 = Vector((0,0,0))
+         v1[major_axis] = 1.0
 
-   def draw(_,context):
-   #{
-      active_object = context.active_object
-      if active_object == None: return
+         tx = Vector((0,0,0))
+         ty = Vector((0,0,0))
 
-      if active_object.type != 'LIGHT': return
+         cv_tangent_basis( v1, tx, ty )
+         r = (abs(tx.dot( v0 )) + abs(ty.dot( v0 ))) * 0.25
+         l = v0[ major_axis ] - r*2
 
-      data = active_object.data.cv_data
-      _.layout.prop( data, "bp0", text="Only on during night" )
-   #}
-#}
+         p0 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l*-0.5 )
+         p1 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l* 0.5 )
 
-def cv_variable_enum( scene, context ):
-#{
-   obj = context.object
-   invalid = [('0',"",""),
-              ('1',"",""),
-              ('2',"",""),
-              ('3',"",""),
-              ('4',"",""),
-              ('5',"",""),
-              ('6',"",""),
-              ('7',"",""),
-              ('8',"",""),
-              ('9',"","")]
+         colour = [0.2,0.2,0.2,1.0]
+         colour[major_axis] = 0.5
 
-   classtype = obj.cv_data.classtype
+         cv_draw_halfsphere( p0, -v1, ty, tx, r, colour )
+         cv_draw_halfsphere( p1,  v1, ty, tx, r, colour )
+         cv_draw_line( p0+tx* r, p1+tx* r, colour )
+         cv_draw_line( p0+tx*-r, p1+tx*-r, colour )
+         cv_draw_line( p0+ty* r, p1+ty* r, colour )
+         cv_draw_line( p0+ty*-r, p1+ty*-r, colour )
+      #}
+      else:#{
+         continue
+      #}
 
-   if classtype in globals():
-   #{
-      cl = globals()[ classtype ]
-      if getattr( cl, "dynamic_enum", None ):
-         if len(cl.dynamic_enum)>0:
-            return cl.dynamic_enum
+      center = obj.matrix_world @ c
+      if bone.SR_data.cone_constraint:#{
+         vx = Vector([bone.SR_data.conevx[_] for _ in range(3)])
+         vy = Vector([bone.SR_data.conevy[_] for _ in range(3)])
+         va = Vector([bone.SR_data.coneva[_] for _ in range(3)])
+         draw_cone_twist( center, vx, vy, va )
+      #}
    #}
-
-   return invalid
 #}
 
-class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
+def cv_ent_gate( obj ):
 #{
-   uid: bpy.props.IntProperty( name="" )
-
-   strp: bpy.props.StringProperty( name="strp" )
-   intp: bpy.props.IntProperty( name="intp" )
-   intp1: bpy.props.IntProperty( name="intp1" )
-   fltp: bpy.props.FloatProperty( name="fltp" )
-   bp0: bpy.props.BoolProperty( name="bp0" )
-   bp1: bpy.props.BoolProperty( name="bp1" )
-   bp2: bpy.props.BoolProperty( name="bp2" )
-   bp3: bpy.props.BoolProperty( name="bp3" )
-
-   target: bpy.props.PointerProperty( type=bpy.types.Object, name="target", \
-         poll=cv_poll_target )
-   target1: bpy.props.PointerProperty( type=bpy.types.Object, name="target1", \
-         poll=cv_poll_target )
-   target2: bpy.props.PointerProperty( type=bpy.types.Object, name="target2", \
-         poll=cv_poll_target )
-   target3: bpy.props.PointerProperty( type=bpy.types.Object, name="target3", \
-         poll=cv_poll_target )
+   global cv_view_verts, cv_view_colours
 
-   colour: bpy.props.FloatVectorProperty( name="colour",subtype='COLOR',\
-                                          min=0.0,max=1.0)
+   if obj.type != 'MESH': return
 
-   dynamic_enum: bpy.props.EnumProperty(
-      name="",
-      items = cv_variable_enum
-   )
+   mesh_data = obj.data.SR_data.ent_gate[0]
+   data = obj.SR_data.ent_gate[0]
+   dims = mesh_data.dimensions
 
-   classtype: bpy.props.EnumProperty(
-      name="Class", 
-      items = [
-      ('classtype_none',            "None",             ""), #000
-      ('classtype_gate',            "Gate",             ""), #100
-      ('classtype_nonlocal_gate',   "Gate:NonLocal",    ""), #101
-      ('classtype_spawn',           "Spawn",            ""), #200
-      ('classtype_water',           "Water Surface",    ""), #300
-      ('classtype_route',           "Route",            ""), #400
-      ('classtype_route_node',      "Route:Node",       ""), #401
-      ('classtype_audio',           "Audio:File",       ""), #500
-      ('classtype_audio_player',    "Audio:Player",     ""), #501
-      ('classtype_audio_sprite',    "Audio:Sprite",     ""), #502
-      ('classtype_volume_audio',    "Volume:Audio",     ""), #600
-      ('classtype_volume_event',    "Volume:Event",     ""), #601
-
-      ('300',                       "ERROR",            "",300)
-      ])
-#}
+   vs = [None]*9
+   c = Vector((0,0,dims[2]))
 
-class CV_BONE_SETTINGS(bpy.types.PropertyGroup):
-#{
-   collider: bpy.props.EnumProperty(
-      name="Collider Type", 
-      items = [
-      ('collider_none', "collider_none", "", 0),
-      ('collider_box', "collider_box", "", 1),
-      ('collider_capsule', "collider_capsule", "", 2),
-      ])
+   vs[0] = obj.matrix_world @ Vector((-dims[0],0.0,-dims[1]+dims[2]))
+   vs[1] = obj.matrix_world @ Vector((-dims[0],0.0, dims[1]+dims[2]))
+   vs[2] = obj.matrix_world @ Vector(( dims[0],0.0, dims[1]+dims[2]))
+   vs[3] = obj.matrix_world @ Vector(( dims[0],0.0,-dims[1]+dims[2]))
+   vs[4] = obj.matrix_world @ (c+Vector((-1,0,-2)))
+   vs[5] = obj.matrix_world @ (c+Vector((-1,0, 2)))
+   vs[6] = obj.matrix_world @ (c+Vector(( 1,0, 2)))
+   vs[7] = obj.matrix_world @ (c+Vector((-1,0, 0)))
+   vs[8] = obj.matrix_world @ (c+Vector(( 1,0, 0)))
 
-   v0: bpy.props.FloatVectorProperty(name="v0",size=3)
-   v1: bpy.props.FloatVectorProperty(name="v1",size=3)
+   indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
 
-   con0: bpy.props.BoolProperty(name="Constriant 0",default=False)
-   mins: bpy.props.FloatVectorProperty(name="mins",size=3)
-   maxs: bpy.props.FloatVectorProperty(name="maxs",size=3)
+   for l in indices:#{
+      v0 = vs[l[0]]
+      v1 = vs[l[1]]
+      cv_view_verts += [(v0[0],v0[1],v0[2])]
+      cv_view_verts += [(v1[0],v1[1],v1[2])]
+      cv_view_colours += [(1,1,0,1),(1,1,0,1)]
+   #}
 
-   conevx: bpy.props.FloatVectorProperty(name="conevx",size=3)
-   conevy: bpy.props.FloatVectorProperty(name="conevy",size=3)
-   coneva: bpy.props.FloatVectorProperty(name="coneva",size=3)
-   conet:  bpy.props.FloatProperty(name="conet")
+   sw = (0.4,0.4,0.4,0.2)
+   if data.target != None:
+      cv_draw_arrow( obj.location, data.target.location, sw )
 #}
 
-class CV_BONE_PANEL(bpy.types.Panel):
+def dijkstra( graph, start_node, target_node ):
 #{
-   bl_label="[Skate Rift]"
-   bl_idname="SCENE_PT_cv_bone"
-   bl_space_type='PROPERTIES'
-   bl_region_type='WINDOW'
-   bl_context='bone'
-
-   def draw(_,context):
-   #{
-      active_object = context.active_object
-      if active_object == None: return
-
-      bone = active_object.data.bones.active
-      if bone == None: return
+   unvisited = [_ for _ in graph]
+   shortest_path = {}
+   previous_nodes = {}
+   
+   for n in unvisited:
+      shortest_path[n] = 9999999.999999
+   shortest_path[start_node] = 0
+
+   while unvisited:#{
+      current_min_node = None
+      for n in unvisited:#{
+         if current_min_node == None:
+            current_min_node = n
+         elif shortest_path[n] < shortest_path[current_min_node]:
+            current_min_node = n
+      #}
 
-      _.layout.prop( bone.cv_data, "collider" )
-      _.layout.prop( bone.cv_data, "v0" )
-      _.layout.prop( bone.cv_data, "v1" )
+      for branch in graph[current_min_node]:#{
+         tentative_value = shortest_path[current_min_node]
+         tentative_value += graph[current_min_node][branch]
+         if tentative_value < shortest_path[branch]:#{
+            shortest_path[branch] = tentative_value
+            previous_nodes[branch] = current_min_node
+         #}
+      #}
 
-      _.layout.label( text="Angle Limits" )
-      _.layout.prop( bone.cv_data, "con0" )
+      unvisited.remove(current_min_node)
+   #}
+   
+   path = []
+   node = target_node
+   while node != start_node:#{
+      path.append(node)
 
-      _.layout.prop( bone.cv_data, "conevx" )
-      _.layout.prop( bone.cv_data, "conevy" )
-      _.layout.prop( bone.cv_data, "coneva" )
-      _.layout.prop( bone.cv_data, "conet" )
+      if node not in previous_nodes: return None
+      node = previous_nodes[node]
    #}
-#}
 
-class CV_SCENE_SETTINGS(bpy.types.PropertyGroup):
-#{
-   use_hidden: bpy.props.BoolProperty( name="use hidden", default=False )
-   export_dir: bpy.props.StringProperty( name="Export Dir", subtype='DIR_PATH' )
+   # Add the start node manually
+   path.append(start_node)
+   return path
 #}
 
-class CV_COLLECTION_SETTINGS(bpy.types.PropertyGroup):
+def node_graph( route_nodes ):
 #{
-   pack_textures: bpy.props.BoolProperty( name="Pack Textures", default=False )
-   animations:    bpy.props.BoolProperty( name="Export animation", default=True)
-#}
+   graph = {}
+   for n in route_nodes:
+      graph[n.name] = {}
 
-class CV_MATERIAL_SETTINGS(bpy.types.PropertyGroup):
-#{
-   shader: bpy.props.EnumProperty( 
-      name="Format", 
-      items = [
-      ('standard',"standard","",0),
-      ('standard_cutout', "standard_cutout", "", 1),
-      ('terrain_blend', "terrain_blend", "", 2),
-      ('vertex_blend', "vertex_blend", "", 3),
-      ('water',"water","",4),
-      ])
+   for i in range(len(route_nodes)-1):#{
+      for j in range(i+1, len(route_nodes)):#{
+         ni = route_nodes[i]
+         nj = route_nodes[j]
 
-   surface_prop: bpy.props.EnumProperty(
-      name="Surface Property",
-      items = [
-      ('concrete','concrete','',0),
-      ('wood','wood','',1),
-      ('grass','grass','',2),
-      ('tiles','tiles','',3)
-      ])
-   
-   collision: bpy.props.BoolProperty( \
-         name="Collisions Enabled",\
-         default=True,\
-         description = "Can the player collide with this material"\
-   )
-   skate_surface: bpy.props.BoolProperty( \
-         name="Skate Surface", \
-         default=True,\
-         description = "Should the game try to target this surface?" \
-   )
-   grind_surface: bpy.props.BoolProperty( \
-         name="Grind Surface", \
-         default=False,\
-         description = "Grind face?" \
-   )
-   grow_grass: bpy.props.BoolProperty( \
-         name="Grow Grass", \
-         default=False,\
-         description = "Spawn grass sprites on this surface?" \
-   )
-   blend_offset: bpy.props.FloatVectorProperty( \
-         name="Blend Offset", \
-         size=2, \
-         default=Vector((0.5,0.0)),\
-         description="When surface is more than 45 degrees, add this vector " +\
-                     "to the UVs" \
-   )
-   sand_colour: bpy.props.FloatVectorProperty( \
-         name="Sand Colour",\
-         subtype='COLOR',\
-         min=0.0,max=1.0,\
-         default=Vector((0.79,0.63,0.48)),\
-         description="Blend to this colour near the 0 coordinate on UP axis"\
-   )
-   shore_colour: bpy.props.FloatVectorProperty( \
-         name="Shore Colour",\
-         subtype='COLOR',\
-         min=0.0,max=1.0,\
-         default=Vector((0.03,0.32,0.61)),\
-         description="Water colour at the shoreline"\
-   )
-   ocean_colour: bpy.props.FloatVectorProperty( \
-         name="Ocean Colour",\
-         subtype='COLOR',\
-         min=0.0,max=1.0,\
-         default=Vector((0.0,0.006,0.03)),\
-         description="Water colour in the deep bits"\
-   )
-#}
+         v0 = ni.location - nj.location
 
-class CV_MATERIAL_PANEL(bpy.types.Panel):
-#{
-   bl_label="Skate Rift material"
-   bl_idname="MATERIAL_PT_cv_material"
-   bl_space_type='PROPERTIES'
-   bl_region_type='WINDOW'
-   bl_context="material"
-   
-   def draw(_,context):
-   #{
-      active_object = bpy.context.active_object
-      if active_object == None: return
-      active_mat = active_object.active_material
-      if active_mat == None: return
+         gate = None
 
-      info = material_info( active_mat )
+         if ni.SR_data.ent_type == 'ent_gate':
+            gate = ni
 
-      if 'tex_diffuse' in info:
-      #{
-         _.layout.label( icon='INFO', \
-            text=F"{info['tex_diffuse'].name} will be compiled" )
-      #}
+         if nj.SR_data.ent_type == 'ent_gate':#{
+            if gate: continue
+            gate = nj
+         #}
 
-      _.layout.prop( active_mat.cv_data, "shader" )
-      _.layout.prop( active_mat.cv_data, "surface_prop" )
-      _.layout.prop( active_mat.cv_data, "collision" )
+         if gate:#{
+            v1 = gate.matrix_world.to_3x3() @ Vector((0,-1,0))
+            if gate.SR_data.ent_gate[0].target:
+               if v1.dot(v0) > 0.0: continue
+            else:
+               if v1.dot(v0) < 0.0: continue
+         #}
 
-      if active_mat.cv_data.collision:
-         _.layout.prop( active_mat.cv_data, "skate_surface" )
-         _.layout.prop( active_mat.cv_data, "grind_surface" )
-         _.layout.prop( active_mat.cv_data, "grow_grass" )
+         dist = v0.magnitude
 
-      if active_mat.cv_data.shader == "terrain_blend":
-      #{
-         box = _.layout.box()
-         box.prop( active_mat.cv_data, "blend_offset" )
-         box.prop( active_mat.cv_data, "sand_colour" )
-      #}
-      elif active_mat.cv_data.shader == "vertex_blend":
-      #{
-         box = _.layout.box()
-         box.label( icon='INFO', text="Uses vertex colours, the R channel" )
-         box.prop( active_mat.cv_data, "blend_offset" )
-      #}
-      elif active_mat.cv_data.shader == "water":
-      #{
-         box = _.layout.box()
-         box.label( icon='INFO', text="Depth scale of 16 meters" )
-         box.prop( active_mat.cv_data, "shore_colour" )
-         box.prop( active_mat.cv_data, "ocean_colour" )
+         if dist > 25.0: continue
+         graph[route_nodes[i].name][route_nodes[j].name] = dist
+         graph[route_nodes[j].name][route_nodes[i].name] = dist
       #}
    #}
+
+   return graph
 #}
 
-class CV_OBJ_PANEL(bpy.types.Panel):
+def cv_draw_route( route, route_nodes ):
 #{
-   bl_label="Entity Config"
-   bl_idname="SCENE_PT_cv_entity"
-   bl_space_type='PROPERTIES'
-   bl_region_type='WINDOW'
-   bl_context="object"
-   
-   def draw(_,context):
-   #{
-      active_object = bpy.context.active_object
-      if active_object == None: return
-      if active_object.type == 'ARMATURE':
-      #{
-         row = _.layout.row()
-         row.enabled = False
-         row.label( text="This object has the intrinsic classtype of skeleton" )
-         return
-      #}
+   pole = Vector((0.2,0.2,20))
+   hat = Vector((20,2.0,0.2))
+   cc = route.SR_data.ent_route[0].colour
 
-      _.layout.prop( active_object.cv_data, "classtype" )
+   cv_draw_ucube( route.matrix_world, cc, Vector((20,1,10)) )
+   cv_draw_ucube( route.matrix_world, cc, pole, Vector((-20,1,-10)) )
+   cv_draw_ucube( route.matrix_world, cc, pole, Vector(( 20,1,-10)) )
+   cv_draw_ucube( route.matrix_world, cc, hat, Vector((0,-1, 10)) )
+   cv_draw_ucube( route.matrix_world, cc, hat, Vector((0,-1,-10)) )
 
-      classtype = active_object.cv_data.classtype
+   checkpoints = route.SR_data.ent_route[0].gates
+   graph = node_graph( route_nodes )
 
-      if (classtype != 'classtype_none') and (classtype in globals()):
-      #{
-         cl = globals()[ classtype ]
+   for i in range(len(checkpoints)):#{
+      gi = checkpoints[i].target
+      gj = checkpoints[(i+1)%len(checkpoints)].target
 
-         if getattr( cl, "editor_interface", None ):
-         #{
-            cl.editor_interface( _.layout, active_object )
-         #}
+      if gi:#{
+         dest = gi.SR_data.ent_gate[0].target
+         if dest:
+            cv_draw_line_dotted( gi.location, dest.location, cc )
+         gi = dest
       #}
-   #}
-#}
 
-class CV_COMPILE(bpy.types.Operator):
-#{
-   bl_idname="carve.compile_all"
-   bl_label="Compile All"
+      if gi==gj: continue # error?
+      if not gi or not gj: continue
 
-   def execute(_,context):
-   #{
-      view_layer = bpy.context.view_layer
-      for col in view_layer.layer_collection.children["export"].children:
-         if not col.hide_viewport or bpy.context.scene.cv_data.use_hidden:
-            write_model( col.name )
+      path = dijkstra( graph, gj.name, gi.name )
 
-      return {'FINISHED'}
+      if path:#{
+         for sj in range(len(path)-1):#{
+            o0 = bpy.data.objects[ path[sj] ]
+            o1 = bpy.data.objects[ path[sj+1] ]
+            cv_draw_arrow(o0.location,o1.location,cc,1.5)
+         #}
+      #}
+      else:#{
+         cv_draw_line_dotted( gi.location, gj.location, cc )
+      #}
    #}
 #}
 
-class CV_COMPILE_THIS(bpy.types.Operator):
+def cv_draw():
 #{
-   bl_idname="carve.compile_this"
-   bl_label="Compile This collection"
-
-   def execute(_,context):
-   #{
-      col = bpy.context.collection
-      write_model( col.name )
+   global cv_view_shader
+   global cv_view_verts
+   global cv_view_colours
+   global cv_view_course_i
 
-      return {'FINISHED'}
-   #}
-#}
+   cv_view_course_i = 0
+   cv_view_verts = []
+   cv_view_colours = []
 
-class CV_INTERFACE(bpy.types.Panel):
-#{
-   bl_idname = "VIEW3D_PT_carve"
-   bl_label = "Skate Rift"
-   bl_space_type = 'VIEW_3D'
-   bl_region_type = 'UI'
-   bl_category = "Skate Rift"
+   cv_view_shader.bind()
+   gpu.state.depth_mask_set(False)
+   gpu.state.line_width_set(2.0)
+   gpu.state.face_culling_set('BACK')
+   gpu.state.depth_test_set('LESS')
+   gpu.state.blend_set('NONE')
 
-   def draw(_, context):
-   #{
-      layout = _.layout
-      layout.prop( context.scene.cv_data, "export_dir" )
-      
-      col = bpy.context.collection
-      
-      found_in_export = False
-      export_count = 0
-      view_layer = bpy.context.view_layer
-      for c1 in view_layer.layer_collection.children["export"].children:
-      #{
-         if not c1.hide_viewport or bpy.context.scene.cv_data.use_hidden:
-            export_count += 1
+   route_nodes = []
+   routes = []
 
-         if c1.name == col.name:
-         #{
-            found_in_export = True
-         #}
+   for obj in bpy.context.collection.objects:#{
+      if obj.type == 'ARMATURE':#{
+         if obj.data.pose_position == 'REST':
+            draw_skeleton_helpers( obj )
       #}
+      else:#{
+         ent_type = obj_ent_type( obj )
 
-      box = layout.box()
-      if found_in_export:
-      #{
-         box.label( text=col.name + ".mdl" )
-         box.prop( col.cv_data, "pack_textures" )
-         box.prop( col.cv_data, "animations" )
-         box.operator( "carve.compile_this" )
-      #}
-      else:
-      #{
-         row = box.row()
-         row.enabled=False
-         row.label( text=col.name )
-         box.label( text="This collection is not in the export group" )
+         if ent_type == 'ent_gate':#{
+            cv_ent_gate( obj )
+            route_nodes += [obj]
+         #}
+         elif ent_type == 'ent_route_node':
+            route_nodes += [obj]
+         elif ent_type == 'ent_route':
+            routes += [obj]
       #}
-
-      box = layout.box()
-      row = box.row()
-
-      split = row.split( factor = 0.3, align=True )
-      split.prop( context.scene.cv_data, "use_hidden", text="hidden" )
-      
-      row1 = split.row()
-      if export_count == 0:
-         row1.enabled=False
-      row1.operator( "carve.compile_all", \
-                        text=F"Compile all ({export_count} collections)" )
    #}
-#}
+   
+   #cv_draw_route_map( route_nodes )
+   for route in routes:#{
+      cv_draw_route( route, route_nodes )
+   #}
 
+   cv_draw_lines()
+   return
+#}
 
-classes = [CV_OBJ_SETTINGS,CV_OBJ_PANEL,CV_COMPILE,CV_INTERFACE,\
-           CV_MESH_SETTINGS, CV_SCENE_SETTINGS, CV_BONE_SETTINGS,\
-           CV_BONE_PANEL, CV_COLLECTION_SETTINGS, CV_COMPILE_THIS,\
-           CV_MATERIAL_SETTINGS, CV_MATERIAL_PANEL, CV_LIGHT_SETTINGS,\
-           CV_LIGHT_PANEL]
+classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\
+            SR_COLLECTION_SETTINGS, SR_SCENE_SETTINGS, \
+            SR_COMPILE, SR_COMPILE_THIS, SR_MIRROR_BONE_X,\
+            \
+            SR_OBJECT_ENT_GATE, SR_MESH_ENT_GATE, SR_OBJECT_ENT_SPAWN, \
+            SR_OBJECT_ENT_ROUTE_ENTRY, SR_UL_ROUTE_NODE_LIST, \
+            SR_OBJECT_ENT_ROUTE, SR_OT_ROUTE_LIST_NEW_ITEM,
+            SR_OT_ROUTE_LIST_DEL_ITEM,\
+            \
+            SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, 
+            SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \
+           ]
 
 def register():
 #{
-   global cv_view_draw_handler
-
    for c in classes:
       bpy.utils.register_class(c)
 
-   bpy.types.Object.cv_data = bpy.props.PointerProperty(type=CV_OBJ_SETTINGS)
-   bpy.types.Mesh.cv_data = bpy.props.PointerProperty(type=CV_MESH_SETTINGS)
-   bpy.types.Scene.cv_data = bpy.props.PointerProperty(type=CV_SCENE_SETTINGS)
-   bpy.types.Bone.cv_data = bpy.props.PointerProperty(type=CV_BONE_SETTINGS)
-   bpy.types.Collection.cv_data = \
-         bpy.props.PointerProperty(type=CV_COLLECTION_SETTINGS)
-   bpy.types.Material.cv_data = \
-         bpy.props.PointerProperty(type=CV_MATERIAL_SETTINGS)
-   bpy.types.Light.cv_data = bpy.props.PointerProperty(type=CV_LIGHT_SETTINGS)
+   bpy.types.Scene.SR_data = \
+         bpy.props.PointerProperty(type=SR_SCENE_SETTINGS)
+   bpy.types.Collection.SR_data = \
+         bpy.props.PointerProperty(type=SR_COLLECTION_SETTINGS)
+
+   bpy.types.Object.SR_data = \
+         bpy.props.PointerProperty(type=SR_OBJECT_PROPERTIES)
+   bpy.types.Light.SR_data = \
+         bpy.props.PointerProperty(type=SR_LIGHT_PROPERTIES)
+   bpy.types.Bone.SR_data = \
+         bpy.props.PointerProperty(type=SR_BONE_PROPERTIES)
+   bpy.types.Mesh.SR_data = \
+         bpy.props.PointerProperty(type=SR_MESH_PROPERTIES)
+   bpy.types.Material.SR_data = \
+         bpy.props.PointerProperty(type=SR_MATERIAL_PROPERTIES)
 
+   global cv_view_draw_handler
    cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
       cv_draw,(),'WINDOW','POST_VIEW')
 #}
 
 def unregister():
 #{
-   global cv_view_draw_handler
-
    for c in classes:
       bpy.utils.unregister_class(c)
 
+   global cv_view_draw_handler
    bpy.types.SpaceView3D.draw_handler_remove(cv_view_draw_handler,'WINDOW')
 #}
 
@@ -3089,7 +2565,8 @@ def qoi_encode( img ):
 #{
    data = bytearray()
    
-   print(F"            . Encoding {img.name}.qoi[{img.size[0]},{img.size[1]}]")
+   print(F"{' ':<30}",end='\r')
+   print(F"[QOI] Encoding {img.name}.qoi[{img.size[0]},{img.size[1]}]",end='\r')
 
    index = [ qoi_rgba_t() for _ in range(64) ]
 
@@ -3115,11 +2592,9 @@ def qoi_encode( img ):
    px.a = c_uint8(255)
 
    px_len = img.size[0] * img.size[1]
-
    paxels = [ int(min(max(_,0),1)*255) for _ in img.pixels ]
 
-   for px_pos in range( px_len ):
-   #{
+   for px_pos in range( px_len ): #{
       idx = px_pos * img.channels
       nc = img.channels-1
 
@@ -3128,39 +2603,32 @@ def qoi_encode( img ):
       px.b = paxels[idx+min(2,nc)]
       px.a = paxels[idx+min(3,nc)]
 
-      if qoi_eq( px, px_prev ):
-      #{
+      if qoi_eq( px, px_prev ): #{
          run += 1
 
-         if (run == 62) or (px_pos == px_len-1):
-         #{
+         if (run == 62) or (px_pos == px_len-1): #{
             data.extend( bytearray( c_uint8(QOI_OP_RUN | (run-1))) )
             run = 0
          #}
       #}
-      else:
-      #{
-         if run > 0:
-         #{
+      else: #{
+         if run > 0: #{
             data.extend( bytearray( c_uint8(QOI_OP_RUN | (run-1))) )
             run = 0
          #}
 
          index_pos = qoi_colour_hash(px) % 64
 
-         if qoi_eq( index[index_pos], px ):
-         #{
+         if qoi_eq( index[index_pos], px ): #{
             data.extend( bytearray( c_uint8(QOI_OP_INDEX | index_pos)) )
          #}
-         else:
-         #{
+         else: #{
             index[ index_pos ].r = px.r
             index[ index_pos ].g = px.g
             index[ index_pos ].b = px.b
             index[ index_pos ].a = px.a
 
-            if px.a == px_prev.a:
-            #{
+            if px.a == px_prev.a: #{
                vr = int(px.r) - int(px_prev.r)
                vg = int(px.g) - int(px_prev.g)
                vb = int(px.b) - int(px_prev.b)
@@ -3184,16 +2652,14 @@ def qoi_encode( img ):
                   data.extend( bytearray( c_uint8(op) ) )
                   data.extend( bytearray( c_uint8(delta) ))
                #}
-               else:
-               #{
+               else: #{
                   data.extend( bytearray( c_uint8(QOI_OP_RGB) ) )
                   data.extend( bytearray( c_uint8(px.r) ))
                   data.extend( bytearray( c_uint8(px.g) ))
                   data.extend( bytearray( c_uint8(px.b) ))
                #}
             #}
-            else:
-            #{
+            else: #{
                data.extend( bytearray( c_uint8(QOI_OP_RGBA) ) )
                data.extend( bytearray( c_uint8(px.r) ))
                data.extend( bytearray( c_uint8(px.g) ))
@@ -3213,7 +2679,7 @@ def qoi_encode( img ):
    for i in range(7):
       data.extend( bytearray( c_uint8(0) ))
    data.extend( bytearray( c_uint8(1) ))
-   bytearray_align_to( data, 16, 0 )
+   bytearray_align_to( data, 16, b'\x00' )
 
    return data
 #}
diff --git a/entity.h b/entity.h
new file mode 100644 (file)
index 0000000..9af8e79
--- /dev/null
+++ b/entity.h
@@ -0,0 +1,100 @@
+#ifndef ENTITY_H
+#define ENTITY_H
+
+#include "model.h"
+
+typedef struct ent_spawn ent_spawn;
+typedef struct ent_light ent_light;
+typedef struct ent_gate ent_gate;
+typedef struct ent_route_node ent_route_node;
+typedef struct ent_path_index ent_path_index;
+typedef struct ent_checkpoint ent_checkpoint;
+typedef struct ent_route ent_route;
+
+struct ent_spawn{
+   mdl_transform transform;
+   u32 pstr_name;
+};
+
+enum light_type{
+   k_light_type_point = 0,
+   k_light_type_spot = 1
+};
+
+struct ent_light{
+   mdl_transform transform;
+   u32 daytime,
+       type;
+
+   v4f colour;
+   float angle,
+         range;
+
+   m4x3f inverse_world;
+   v2f angle_sin_cos;
+};
+
+enum gate_type{
+   k_gate_type_unlinked = 0,
+   k_gate_type_teleport = 1,
+   k_gate_type_nonlocal = 2
+};
+
+struct ent_gate{
+   u32 type,
+       target;
+
+   /* TODO: World index */
+
+   v3f dimensions,
+       co[2];
+
+   v4f q[2];
+
+   /* runtime */
+   m4x3f to_world, transport;
+
+   union{
+      u32 timing_version;
+
+      struct{
+         u8 ref_count, ref_total;
+      };
+   };
+
+   double timing_time;
+   u16 routes[4];       /* routes that pass through this gate */
+};
+
+struct ent_route_node{
+   v3f co;
+   u8 ref_count, ref_total;
+};
+
+struct ent_path_index{
+   u16 index;
+};
+
+struct ent_checkpoint{
+   u16 gate_index,
+       path_start,
+       path_count;
+};
+
+struct ent_route{
+   mdl_transform transform;
+   u32 pstr_name;
+   u16 checkpoints_start,
+       checkpoints_count;
+
+   v4f colour;
+
+   /* runtime */
+   u32 active_checkpoint;
+   float factive;
+   m4x3f board_transform;
+   mdl_submesh sm;
+   double latest_pass;
+};
+
+#endif /* ENTITY_H */
index 9d1ce851515e95b27ec1076ecbd0ff9a40c7b0be..01ea7eb1cd276addd97f03637c8e57a230cf94bd 100644 (file)
Binary files a/maps_src/mp_arizona.mdl and b/maps_src/mp_arizona.mdl differ
index 3b81176a7ba5b58df10af90442b1b116d8466856..46386d29f2a94f3c4fd00ac2fc5f31450272ea6a 100644 (file)
Binary files a/maps_src/mp_gridmap.mdl and b/maps_src/mp_gridmap.mdl differ
index b5569f6680389f02a73f70d08275cf3185f043cd..265f909634f84c33e2e2e5431a07b0ea23bd22f8 100644 (file)
Binary files a/maps_src/mp_home.mdl and b/maps_src/mp_home.mdl differ
index c6b5f5be87042eadd6aa14c340a9546144a801d4..e6919785d5e0fb98d1f47a07765264cf79aca937 100644 (file)
Binary files a/maps_src/mp_mtzero.mdl and b/maps_src/mp_mtzero.mdl differ
diff --git a/model.h b/model.h
index 6f99fb61d77044d489147a9fa9a9a32b97569a3a..c92960f04569b11ea2b8e5a6b62776c07e8f59dd 100644 (file)
--- a/model.h
+++ b/model.h
@@ -7,44 +7,6 @@
 
 #include "common.h"
 
-typedef struct glmesh glmesh;
-
-typedef struct mdl_vert mdl_vert;
-typedef struct mdl_submesh mdl_submesh;
-typedef struct mdl_material mdl_material;
-typedef struct mdl_node mdl_node;
-typedef struct mdl_file_header mdl_file_header;
-typedef struct mdl_animation mdl_animation;
-typedef struct mdl_keyframe mdl_keyframe;
-typedef struct mdl_texture mdl_texture;
-typedef struct mdl_context mdl_context;
-
-#define MDL_SIZE_MAX          0x1000000
-#define MDL_VERT_MAX          1000000
-#define MDL_INDICE_MAX        1000000
-#define MDL_MATERIAL_MAX      32
-#define MDL_NODE_MAX          4000
-#define MDL_SUBMESH_MAX       8000
-#define MDL_STRING_LENGTH_MAX 64
-
-enum classtype
-{
-   k_classtype_none                 = 000,
-   k_classtype_bone                 = 001,
-   k_classtype_skeleton             = 002,
-   k_classtype_skin                 = 003,
-   k_classtype_world_light          = 004,
-
-   k_classtype_gate                 = 100,
-   k_classtype_nonlocal_gate        = 101,
-   k_classtype_spawn                = 200,
-   k_classtype_water                = 300,
-   k_classtype_route                = 400,
-   k_classtype_route_node           = 401,
-   k_classtype_audio                = 500,
-   k_classtype_audio_sprite         = 502,
-   k_classtype_volume_audio         = 600,
-};
 
 enum mdl_shader
 {
@@ -71,26 +33,6 @@ enum material_flag
    k_material_flag_grind_surface    = 0x8
 };
 
-enum bone_flag
-{
-   k_bone_flag_deform               = 0x1,
-   k_bone_flag_ik                   = 0x2,
-   k_bone_flag_collider_box         = 0x4,
-   k_bone_flag_collider_capsule     = 0x8,
-   k_bone_flag_collider_reserved0   = 0x10,
-   k_bone_flag_collider_reserved1   = 0x20,
-   k_bone_flag_collider_reserved2   = 0x40,
-   k_bone_flag_collider_reserved3   = 0x80,
-   k_bone_flag_collider_any         = k_bone_flag_collider_box |
-                                      k_bone_flag_collider_capsule |
-                                      k_bone_flag_collider_reserved0 |
-                                      k_bone_flag_collider_reserved1 |
-                                      k_bone_flag_collider_reserved2 |
-                                      k_bone_flag_collider_reserved3,
-   k_bone_flag_cone_constraint      = 0x100,
-   k_bone_flag_force_u32            = 0xffffffff
-};
-
 #pragma pack(push,1)
 
 /* 48 byte */
@@ -105,24 +47,41 @@ struct mdl_vert
    u8  groups[4]; /* 4*8 */
 };
 
+#pragma pack(pop)
+
+typedef struct mdl_context mdl_context;
+typedef struct mdl_array_ptr mdl_array_ptr;
+typedef struct mdl_vert mdl_vert;
+typedef struct mdl_transform mdl_transform;
+typedef struct mdl_submesh mdl_submesh;
+typedef struct mdl_material mdl_material;
+typedef struct mdl_bone mdl_bone;
+typedef struct mdl_armature mdl_armature;
+typedef struct mdl_animation mdl_animation;
+typedef struct mdl_transform mdl_keyframe;
+typedef struct mdl_mesh mdl_mesh;
+typedef struct mdl_file mdl_file;
+typedef struct mdl_texture mdl_texture;
+typedef struct mdl_array mdl_array;
+typedef struct mdl_header mdl_header;
+
+struct mdl_transform
+{
+   v3f co, s;
+   v4f q;
+};
+
 struct mdl_submesh
 {
    u32 indice_start,
        indice_count,
        vertex_start,
        vertex_count;
-   
+
    boxf bbx;
    u32 material_id;
 };
 
-struct mdl_texture
-{
-   u32 pstr_name,
-       pack_offset,
-       pack_length;
-};
-
 struct mdl_material
 {
    u32 pstr_name,
@@ -134,193 +93,274 @@ struct mdl_material
        colour1;
 
    u32 tex_diffuse,
-       tex_decal,
-       tex_normal;
+       tex_none0,
+       tex_none1;
 };
 
-struct mdl_node
+struct mdl_bone
 {
-   v3f co;
-   v4f q;
-   v3f s;
-   
-   u32 sub_uid,         /* allocated in-file... too bad. */
-       submesh_start,
-       submesh_count,
-       classtype,
-       offset,
-       parent,
+   v3f co, end;
+   u32 parent,
+       collider,
+       ik_target,
+       ik_pole,
+       flags,
        pstr_name;
+
+   boxf hitbox;
+   v3f conevx, conevy, coneva;
+   float conet;
 };
 
-struct mdl_keyframe
+enum bone_flag
 {
-   v3f co;
-   v4f q;
-   v3f s;
+   k_bone_flag_deform               = 0x1,
+   k_bone_flag_ik                   = 0x2,
+   k_bone_flag_cone_constraint      = 0x4
+};
+
+enum bone_collider
+{
+   k_bone_collider_none = 0,
+   k_bone_collider_box = 1,
+   k_bone_collider_capsule = 2
+};
+         
+struct mdl_armature
+{
+   mdl_transform transform;
+   u32 bone_start,
+       bone_count,
+       anim_start,
+       anim_count;
 };
 
 struct mdl_animation
 {
    u32 pstr_name,
        length;
-
    float rate;
-
    u32 offset;
 };
 
-struct mdl_file_header
+struct mdl_mesh
 {
-   u32 identifier, version, file_length, pad0;
-
-   u32 
-       node_count,      node_offset,
-       submesh_count,   submesh_offset,
-       material_count,  material_offset,
-       texture_count,   texture_offset,
-       anim_count,      anim_offset,
-       entdata_size,    entdata_offset, 
-       strings_size,    strings_offset, 
-
-       keyframe_count,  keyframe_offset,
-
-       vertex_count,    vertex_offset,
-       indice_count,    indice_offset,
-
-       pack_size,       pack_offset;
+   mdl_transform transform;
+   u32 submesh_start,
+       submesh_count,
+       pstr_name,
+       flags,
+       armature_id;
 };
 
-/* 
- * Entity data structures
- */
-
-struct classtype_bone            /* 001 */
+struct mdl_file
 {
-   u32 flags,
-       ik_target,
-       ik_pole;
-
-   boxf hitbox;
+   u32 pstr_path,
+       pack_offset,
+       pack_size;
+};
 
-   v3f conevx, conevy, coneva;
-   float conet;
+struct mdl_texture
+{
+   mdl_file file;
+   u32 type;
 };
 
-struct classtype_skeleton        /* 002 */
+struct mdl_array
 {
-   u32 channels,
-       ik_count,
-       collider_count,
-       anim_start,
-       anim_count;
+   u32 file_offset,
+       item_count,
+       item_size;
+
+   char name[16];
 };
 
-struct classtype_skin            /* 003 */
+struct mdl_header
 {
-   u32 skeleton;
+   u32 version;
+   mdl_array index;
 };
 
-struct classtype_world_light     /* 004 */
+struct mdl_context
 {
-   enum light_type 
+   FILE *file;
+   mdl_header info;
+
+   struct mdl_array_ptr
    {
-      k_light_type_point,
-      k_light_type_spot,
-      k_light_type_point_nighttime_only,
-      k_light_type_spot_nighttime_only
-   } 
-   type;
-
-   v4f colour; /* RGB, Energy */
-   float angle, range;
+      void *data;
+      u32 count, stride;
+   }
+   index,
+
+   /* metadata */
+   strings,
+   meshs,
+   submeshs,
+   materials,
+   textures,
+   armatures,
+   bones,
+   animations,
+
+   /* animation buffers */
+   keyframes,
+
+   /* mesh buffers */
+   verts,
+   indices,
+   
+   /* pack data */
+   pack;
 };
 
 
-struct classtype_gate            /* 100, 101 */
+VG_STATIC void mdl_load_fatal_corrupt( mdl_context *mdl )
 {
-   u32 target;
-   v3f dims;
-};
+   fclose( mdl->file );
+   vg_file_print_invalid( mdl->file );
+   vg_fatal_exit_loop( "Corrupt model" );
+}
+
+/*
+ * Model implementation
+ */
 
-struct classtype_spawn           /* 200 */
+VG_STATIC void mdl_load_array_file( mdl_context *mdl, mdl_array_ptr *ptr,
+                                    mdl_array *arr, void *lin_alloc )
 {
-   u32 pstr_alias;
-};
+   if( arr->item_count ){
+      u32 size = arr->item_size*arr->item_count;
+      ptr->data = vg_linear_alloc( lin_alloc, vg_align8(size) );
+
+      fseek( mdl->file, arr->file_offset, SEEK_SET );
+      u64 l = fread( ptr->data, arr->item_size*arr->item_count, 1, mdl->file );
 
-struct classtype_water           /* 300 */
+      if( l != 1 )
+         mdl_load_fatal_corrupt( mdl );
+   }
+   else
+      ptr->data = NULL;
+
+   ptr->count = arr->item_count;
+   ptr->stride = arr->item_size;
+}
+
+VG_STATIC void *mdl_arritm( mdl_array_ptr *arr, u32 index )
 {
-   u32 temp;
-};
+   return ((u8 *)arr->data) + index*arr->stride;
+}
 
-struct classtype_route           /* 400 */
+VG_STATIC u32 mdl_arrcount( mdl_array_ptr *arr )
 {
-   u32 id_start;
-   u32 pstr_name;
-   v3f colour;
-};
+   return arr->count;
+}
 
-struct classtype_route_node      /* 401 */
+VG_STATIC int mdl_load_array( mdl_context *mdl, mdl_array_ptr *ptr,
+                              const char *name, void *lin_alloc )
 {
-   u32 target, target1;
-};
+   for( u32 i=0; i<mdl_arrcount(&mdl->index); i++ ){
+      mdl_array *arr = mdl_arritm( &mdl->index, i );
+      
+      if( !strncmp(arr->name,name,16) ){
+         mdl_load_array_file( mdl, ptr, arr, lin_alloc );
+         return 1;
+      }
+   }
 
+   ptr->data = NULL;
+   ptr->count = 0;
+   ptr->stride = 0;
+   return 0;
+}
 
-struct classtype_audio           /* 500 */
+VG_STATIC int mdl_load_mesh_block( mdl_context *mdl, void *lin_alloc )
 {
-   u32 pstr_file,
-       flags;
+   int success = 1;
 
-   float volume;
-};
+   success &= mdl_load_array( mdl, &mdl->verts,    "mdl_vert",   lin_alloc );
+   success &= mdl_load_array( mdl, &mdl->indices,  "mdl_indice", lin_alloc );
+
+   return success;
+}
 
-struct classtype_audio_sprite    /* 501 */
+VG_STATIC int mdl_load_metadata_block( mdl_context *mdl, void *lin_alloc )
 {
-   u32 audio,
-       category;
+   int success = 1;
 
-   float probability;
-};
+   success &= mdl_load_array( mdl, &mdl->strings,   "strings",      lin_alloc );
+   success &= mdl_load_array( mdl, &mdl->meshs,     "mdl_mesh",     lin_alloc );
+   success &= mdl_load_array( mdl, &mdl->submeshs,  "mdl_submesh",  lin_alloc );
+   success &= mdl_load_array( mdl, &mdl->materials, "mdl_material", lin_alloc );
+   success &= mdl_load_array( mdl, &mdl->textures,  "mdl_texture",  lin_alloc );
+   success &= mdl_load_array( mdl, &mdl->armatures, "mdl_armature", lin_alloc );
+   success &= mdl_load_array( mdl, &mdl->bones,     "mdl_bone",     lin_alloc );
+   success &= mdl_load_array( mdl, &mdl->animations,"mdl_animation",lin_alloc );
 
-struct classtype_volume_audio    /* 600 */
+   return success;
+}
+
+VG_STATIC int mdl_load_animation_block( mdl_context *mdl, void *lin_alloc )
 {
-   u32 category;
-};
+   return mdl_load_array( mdl, &mdl->keyframes, "mdl_keyframe", lin_alloc );
+}
 
-struct classtype_volume_event    /* 601 */
+VG_STATIC int mdl_load_pack_block( mdl_context *mdl, void *lin_alloc )
 {
-   u32 event;
-};
+   return mdl_load_array( mdl, &mdl->pack, "pack", lin_alloc );
+}
 
-#pragma pack(pop)
+/*
+ * if calling mdl_open, and the file does not exist, the game will fatal quit
+ */
+VG_STATIC void mdl_open( mdl_context *mdl, const char *path, void *lin_alloc )
+{
+   memset( mdl, 0, sizeof( mdl_context ) );
+   mdl->file = fopen( path, "rb" );
 
+   if( !mdl->file ){
+      vg_error( "mdl_open('%s'): %s\n", path, strerror(errno) );
+      vg_fatal_exit_loop( "see above for details" );
+   }
 
-struct mdl_context
-{
-   FILE *file;
-   mdl_file_header info;
+   u64 l = fread( &mdl->info, sizeof(mdl_header), 1, mdl->file );
+   if( l != 1 )
+      mdl_load_fatal_corrupt( mdl );
 
-   /* each buffer becomes availible after each _load function is called */
-   mdl_node       *node_buffer;     /* mdl_load_metadata() */
-   mdl_submesh    *submesh_buffer;
-   mdl_material   *material_buffer;
-   mdl_texture    *texture_buffer;
-   mdl_animation  *anim_buffer;
-   void           *entdata_buffer;
-   const char     *string_buffer;
+   mdl_load_array_file( mdl, &mdl->index, &mdl->info.index, lin_alloc );
+}
 
-   mdl_keyframe   *keyframe_buffer; /* mdl_load_anim_data() */
+/*
+ * close file handle
+ */
+VG_STATIC void mdl_close( mdl_context *mdl )
+{
+   fclose( mdl->file );
+   mdl->file = NULL;
+}
 
-   mdl_vert       *vertex_buffer;   /* mdl_load_mesh_data() */
-   u32            *index_buffer;
+/* useful things you can do with the model */
 
-   void           *pack;            /* mdl_load_pack_data() */
-};
+VG_STATIC void mdl_transform_m4x3( mdl_transform *transform, m4x3f mtx )
+{
+   q_m3x3( transform->q, mtx );
+   v3_muls( mtx[0], transform->s[0], mtx[0] );
+   v3_muls( mtx[1], transform->s[1], mtx[1] );
+   v3_muls( mtx[2], transform->s[2], mtx[2] );
+   v3_copy( transform->co, mtx[3] );
+}
+
+VG_STATIC const char *mdl_pstr( mdl_context *mdl, u32 pstr )
+{
+   return mdl_arritm( &mdl->strings, pstr );
+}
 
 /*
  * Simple mesh interface for OpenGL
+ * ----------------------------------------------------------------------------
  */
 
+typedef struct glmesh glmesh;
 struct glmesh
 {
    GLuint vao, vbo, ebo;
@@ -329,8 +369,8 @@ struct glmesh
 };
 
 VG_STATIC void mesh_upload( glmesh *mesh,
-                         mdl_vert *verts, u32 vert_count,
-                         u32 *indices, u32 indice_count )
+                            mdl_vert *verts, u32 vert_count,
+                            u32 *indices, u32 indice_count )
 {
    //assert( mesh->loaded == 0 );
 
@@ -402,8 +442,7 @@ VG_STATIC void mesh_draw( glmesh *mesh )
 
 VG_STATIC void mesh_free( glmesh *mesh )
 {
-   if( mesh->loaded )
-   {
+   if( mesh->loaded ){
       glDeleteVertexArrays( 1, &mesh->vao );
       glDeleteBuffers( 1, &mesh->ebo );
       glDeleteBuffers( 1, &mesh->vbo );
@@ -411,252 +450,23 @@ VG_STATIC void mesh_free( glmesh *mesh )
    }
 }
 
-VG_STATIC void mdl_load_fatal_corrupt( mdl_context *mdl )
-{
-   fclose( mdl->file );
-   vg_file_print_invalid( mdl->file );
-   vg_fatal_exit_loop( "Corrupt model" );
-}
-
-/*
- * Model implementation
- *
- * TODO.
- *
- * you have two api options for loading a model, first, the easy way:
- *       mdl_load ...
- *    will put the entire model straight into the linear_alloc
- *
- * or, to target different allocators:
- *
- *       mdl_open
- *       mdl_load_metadata
- *       mdl_load_vertex_data
- *       mdl_load_indice_data
- *       mdl_close
- *
- * these should ideally be called in quick succession to limit stalls.
- */
-
-/*
- * if calling mdl_open, and the file does not exist, the game will fatal quit
- */
-VG_STATIC void mdl_open( mdl_context *mdl, const char *path )
-{
-   memset( mdl, 0, sizeof( mdl_context ) );
-   mdl->file = fopen( path, "rb" );
-
-   if( !mdl->file )
-   {
-      vg_error( "mdl_open('%s'): %s\n", path, strerror(errno) );
-      vg_fatal_exit_loop( "see above for details" );
-   }
-
-   u64 l = fread( &mdl->info, sizeof(mdl_file_header), 1, mdl->file );
-   if( l != 1 )
-      mdl_load_fatal_corrupt( mdl );
-}
-
-/*
- * Load all metadata (everything up until the large buffers). Probs at most 50k
- */
-VG_STATIC void mdl_load_metadata( mdl_context *mdl, void *lin_alloc )
-{
-   assert( mdl->file );
-   
-   u64 lheader  = sizeof(mdl_file_header),
-       ldata    = mdl->info.keyframe_offset - lheader;
-
-   void *all_data = vg_linear_alloc( lin_alloc, ldata );
-
-   fseek( mdl->file, lheader, SEEK_SET );
-   u64 l = fread( all_data, ldata, 1, mdl->file );
-
-   if( l != 1 )
-   {
-      vg_file_print_invalid( mdl->file );
-      vg_fatal_exit_loop( "Corrupt model" );
-   }
-
-   mdl->node_buffer     = all_data + (mdl->info.node_offset     - lheader);
-   mdl->submesh_buffer  = all_data + (mdl->info.submesh_offset  - lheader);
-   mdl->material_buffer = all_data + (mdl->info.material_offset - lheader);
-   mdl->texture_buffer  = all_data + (mdl->info.texture_offset  - lheader);
-   mdl->anim_buffer     = all_data + (mdl->info.anim_offset     - lheader);
-   mdl->entdata_buffer  = all_data + (mdl->info.entdata_offset  - lheader);
-   mdl->string_buffer   = all_data + (mdl->info.strings_offset  - lheader);
-}
-
-/*
- * Load just the mesh data
- */
-VG_STATIC void mdl_load_mesh_data( mdl_context *mdl, void *lin_alloc )
-{
-   assert( mdl->file );
-   
-   u64 size_verts = vg_align8( mdl->info.vertex_count * sizeof(mdl_vert) ),
-       size_index = vg_align8( mdl->info.indice_count * sizeof(u32) );
-
-   mdl->vertex_buffer = vg_linear_alloc( lin_alloc, size_verts );
-   mdl->index_buffer  = vg_linear_alloc( lin_alloc, size_index );
-                                                    
-   {
-      fseek( mdl->file, mdl->info.vertex_offset, SEEK_SET );
-      u64 l = fread( mdl->vertex_buffer, size_verts, 1, mdl->file );
-      if( l != 1 )
-         mdl_load_fatal_corrupt( mdl );
-   }
-   {
-      fseek( mdl->file, mdl->info.indice_offset, SEEK_SET );
-      u64 l = fread( mdl->index_buffer, size_index, 1, mdl->file );
-      if( l != 1 )
-         mdl_load_fatal_corrupt( mdl );
-   }
-}
-
-/* 
- * Load animation data
- */
-VG_STATIC void mdl_load_anim_data( mdl_context *mdl, void *lin_alloc )
-{
-   assert( mdl->file );
-
-   if( mdl->info.keyframe_count == 0 )
-      return;
-   
-   u64 size_kf = vg_align8( mdl->info.keyframe_count * sizeof(mdl_keyframe) );
-   mdl->keyframe_buffer = vg_linear_alloc( lin_alloc, size_kf );
-                                                    
-   fseek( mdl->file, mdl->info.keyframe_offset, SEEK_SET );
-   u64 l = fread( mdl->keyframe_buffer, size_kf, 1, mdl->file );
-   if( l != 1 )
-      mdl_load_fatal_corrupt( mdl );
-}
-
-/*
- * Load pack contents
- *
- * TODO request specific files (low)
- */
-VG_STATIC void mdl_load_pack_data( mdl_context *mdl, void *lin_alloc )
-{
-   assert( mdl->file );
-
-   if( mdl->info.pack_size == 0 )
-      return;
-   
-   mdl->pack = vg_linear_alloc( lin_alloc, vg_align8( mdl->info.pack_size ) );
-   fseek( mdl->file, mdl->info.pack_offset, SEEK_SET );
-
-   u64 l = fread( mdl->pack, mdl->info.pack_size, 1, mdl->file );
-   if( l != 1 )
-      mdl_load_fatal_corrupt( mdl );
-}
-
-/*
- * close file handle
- */
-VG_STATIC void mdl_close( mdl_context *mdl )
-{
-   fclose( mdl->file );
-   mdl->file = NULL;
-}
-
-/* open a model. TODO: make this flags ( ANIM_DATA|MESH_DATA ... ) */
-VG_STATIC mdl_context *mdl_load_full( void *lin_alloc, const char *path )
-{
-   /* Inspect the header by opening it, give us the size needed */
-   mdl_context temp_ctx;
-   mdl_open( &temp_ctx, path );
-
-   /* create allocator */
-   u32 tot_size = temp_ctx.info.file_length + sizeof( mdl_context ) + 64;
-   void *data = vg_create_linear_allocator( lin_alloc, tot_size, 
-                                            VG_MEMORY_SYSTEM );
-
-   /* copy context and load all other data */
-   mdl_context *ctx = vg_linear_alloc( data, sizeof(mdl_context) );
-   memcpy( ctx, &temp_ctx, sizeof(mdl_context) );
-
-   mdl_load_metadata( ctx, data );
-   mdl_load_anim_data( ctx, data );
-   mdl_load_mesh_data( ctx, data );
-   mdl_load_pack_data( ctx, data );
-   mdl_close( ctx );
-
-   return ctx;
-}
-
-/*
- * Item getters
- * ----------------------------------------------------------------------------
- * TODO: Clamp access and oob errors
- */
-VG_STATIC const char *mdl_pstr( mdl_context *mdl, u32 pstr )
-{
-   return mdl->string_buffer + pstr;
-}
-
-VG_STATIC mdl_node *mdl_node_from_id( mdl_context *mdl, u32 id )
-{
-   return &mdl->node_buffer[id];
-}
-
-VG_STATIC mdl_node *mdl_node_from_name( mdl_context *mdl, const char *name )
-{
-   for( int i=0; i < mdl->info.node_count; i++ )
-   {
-      mdl_node *pnode = mdl_node_from_id( mdl, i );
-      
-      if( !strcmp( name, mdl_pstr( mdl, pnode->pstr_name )) )
-         return pnode;
-   }
-   
-   return NULL;
-}
-
-VG_STATIC mdl_submesh *mdl_node_submesh( mdl_context *mdl, 
-                                         mdl_node *node, u32 i )
-{
-   return &mdl->submesh_buffer[ node->submesh_start+i ];
-}
-
-VG_STATIC u32 *mdl_submesh_indices( mdl_context *mdl, mdl_submesh *sm )
-{
-   return &mdl->index_buffer[ sm->indice_start ];
-}
-
-VG_STATIC mdl_vert *mdl_submesh_vertices( mdl_context *mdl, mdl_submesh *sm )
-{
-   return &mdl->vertex_buffer[ sm->vertex_start ];
-}
-
-VG_STATIC void mdl_node_transform( mdl_node *pnode, m4x3f transform )
-{
-   q_m3x3( pnode->q, transform );
-   v3_muls( transform[0], pnode->s[0], transform[0] );
-   v3_muls( transform[1], pnode->s[1], transform[1] );
-   v3_muls( transform[2], pnode->s[2], transform[2] );
-   v3_copy( pnode->co, transform[3] );
-}
-
-/* upload a mesh based on file submesh */
-VG_STATIC void mdl_unpack_submesh( mdl_context *mdl, glmesh *mesh, 
-                                mdl_submesh *sm )
+VG_STATIC void mdl_draw_submesh( mdl_submesh *sm )
 {
-   mesh_upload( mesh, mdl_submesh_vertices( mdl, sm ), sm->vertex_count,
-                      mdl_submesh_indices( mdl, sm ), sm->indice_count );
+   mesh_drawn( sm->indice_start, sm->indice_count );
 }
 
-/* upload entire mesh from model */
+/* WARNING: Destructive! Only use this once and then discard the context. */
 VG_STATIC void mdl_unpack_glmesh( mdl_context *mdl, glmesh *mesh )
 {
-   u32 offset = mdl->submesh_buffer[0].vertex_count;
+   if( !mdl->submeshs.count )
+      vg_fatal_exit_loop( "Tried to unpack empty model file" );
 
-   for( int i=1; i< mdl->info.submesh_count; i++ )
-   {
-      mdl_submesh *sm = &mdl->submesh_buffer[i];
-      u32 *indices    =  mdl_submesh_indices( mdl, sm );
+   mdl_submesh *sm = mdl_arritm( &mdl->submeshs, 0 );
+   u32 offset = sm->vertex_count;
+
+   for( u32 i=1; i<mdl_arrcount( &mdl->submeshs ); i++ ){
+      mdl_submesh *sm = mdl_arritm( &mdl->submeshs, i );
+      u32 *indices    = mdl_arritm( &mdl->indices, sm->indice_start );
 
       for( u32 j=0; j<sm->indice_count; j++ )
          indices[j] += offset;
@@ -664,69 +474,19 @@ VG_STATIC void mdl_unpack_glmesh( mdl_context *mdl, glmesh *mesh )
       offset += sm->vertex_count;
    }
 
-   mesh_upload( mesh, mdl->vertex_buffer, mdl->info.vertex_count,
-                      mdl->index_buffer,  mdl->info.indice_count ); 
-}
-
-VG_STATIC void mdl_draw_submesh( mdl_submesh *sm )
-{
-   mesh_drawn( sm->indice_start, sm->indice_count );
-}
-
-VG_STATIC void *mdl_get_entdata( mdl_context *mdl, mdl_node *pnode )
-{
-   return mdl->entdata_buffer + pnode->offset;
-}
-
-VG_STATIC mdl_keyframe *mdl_get_animdata( mdl_context *mdl, mdl_animation *anim )
-{
-   return mdl->keyframe_buffer + anim->offset;
+   mesh_upload( mesh, mdl->verts.data, mdl->verts.count,
+                      mdl->indices.data, mdl->indices.count );
 }
 
-VG_STATIC void mdl_link_materials( mdl_context *root, mdl_context *child )
+VG_STATIC mdl_mesh *mdl_find_mesh( mdl_context *mdl, const char *name )
 {
-   u32 lookup[MDL_MATERIAL_MAX];
-   
-   for( int i=0; i<child->info.material_count; i++ )
-   {
-      mdl_material *mi = &child->material_buffer[i];
-      const char *si = mdl_pstr( child, mi->pstr_name );
-
-      lookup[i] = 0;
-
-      for( int j=0; j<root->info.material_count; j++ )
-      {
-         mdl_material *mj = &root->material_buffer[j];
-         const char *sj = mdl_pstr( root, mj->pstr_name );
-         
-         if( !strcmp( si, sj ) )
-         {
-            lookup[i] = j;
-            break;
-         }
-      }
-
-      if( lookup[i] == 0 && i != 0 )
-      {
-         vg_warn( "Could not link material '%s' (not present in root model)\n", 
-                   si );
+   for( u32 i=0; i<mdl_arrcount( &mdl->meshs ); i++ ){
+      mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i );
+      if( !strcmp( name, mdl_pstr( mdl, mesh->pstr_name ))){
+         return mesh;
       }
    }
-
-   for( int i=0; i<child->info.submesh_count; i++ )
-   {
-      mdl_submesh *sm = &child->submesh_buffer[i];
-      sm->material_id =  lookup[sm->material_id];
-   }
-}
-
-VG_STATIC void mdl_invert_uv_coordinates( mdl_context *mdl )
-{
-   for( int i=0; i<mdl->info.vertex_count; i++ )
-   {
-      mdl_vert *vert = &mdl->vertex_buffer[i];
-      vert->uv[1] = 1.0f-vert->uv[1];
-   }
+   return NULL;
 }
-
 #endif
diff --git a/models_src/ch_empty.mdl b/models_src/ch_empty.mdl
deleted file mode 100644 (file)
index d9fc227..0000000
Binary files a/models_src/ch_empty.mdl and /dev/null differ
index 6170a11f03d4e69fbf22e114f0d62bc9156a6eec..7fe4aa18ad94781f662bd5954cff13524e80e2e2 100644 (file)
Binary files a/models_src/ch_jordan.mdl and b/models_src/ch_jordan.mdl differ
diff --git a/models_src/ch_mike.mdl b/models_src/ch_mike.mdl
deleted file mode 100644 (file)
index bca6711..0000000
Binary files a/models_src/ch_mike.mdl and /dev/null differ
index e727a00356922d019d0bac8059d180e9756d6f3f..7914c673b6356f2a660765fcee625cf4b8b19db0 100644 (file)
Binary files a/models_src/ch_new.mdl and b/models_src/ch_new.mdl differ
index 13229be188e5bd1afb5177218211fe596d5f3e25..70355912bafaee36784164533d887b3126a11a3d 100644 (file)
Binary files a/models_src/ch_outlaw.mdl and b/models_src/ch_outlaw.mdl differ
diff --git a/models_src/mp_test.mdl b/models_src/mp_test.mdl
deleted file mode 100644 (file)
index 443babc..0000000
Binary files a/models_src/mp_test.mdl and /dev/null differ
diff --git a/models_src/rs_cars.mdl b/models_src/rs_cars.mdl
deleted file mode 100644 (file)
index 7ea1025..0000000
Binary files a/models_src/rs_cars.mdl and /dev/null differ
diff --git a/models_src/rs_chicken.mdl b/models_src/rs_chicken.mdl
deleted file mode 100644 (file)
index 19e1b59..0000000
Binary files a/models_src/rs_chicken.mdl and /dev/null differ
diff --git a/models_src/rs_foliage.mdl b/models_src/rs_foliage.mdl
deleted file mode 100644 (file)
index 5f1b81c..0000000
Binary files a/models_src/rs_foliage.mdl and /dev/null differ
index f3221b9c00c7e9016cba506a975d4d755849ad1a..2f0e62ba272fa33422983ac06da64dd60ed0067e 100644 (file)
Binary files a/models_src/rs_gate.mdl and b/models_src/rs_gate.mdl differ
diff --git a/models_src/rs_menu.mdl b/models_src/rs_menu.mdl
deleted file mode 100644 (file)
index 280af97..0000000
Binary files a/models_src/rs_menu.mdl and /dev/null differ
diff --git a/models_src/rs_scoretext.mdl b/models_src/rs_scoretext.mdl
deleted file mode 100644 (file)
index 113b53a..0000000
Binary files a/models_src/rs_scoretext.mdl and /dev/null differ
index 75288f23f12901b521dd80af39df11c798916193..894246f259c1fdd651e81c7a3f02f189715f8e3c 100644 (file)
Binary files a/models_src/rs_skydome.mdl and b/models_src/rs_skydome.mdl differ
diff --git a/models_src/rs_vig.mdl b/models_src/rs_vig.mdl
deleted file mode 100644 (file)
index c9ebbc2..0000000
Binary files a/models_src/rs_vig.mdl and /dev/null differ
index ee799d072e073c0d80270a8c2a8f464eb1fd9486..ef23ef43639eed3e367a80c432a0eaf2e3f94df1 100644 (file)
--- a/player.c
+++ b/player.c
@@ -184,7 +184,7 @@ VG_STATIC void player_apply_transport_to_cam( m4x3f transport )
 }
 
 __attribute__ ((deprecated))
-VG_STATIC void gate_rotate_angles( teleport_gate *gate, v3f angles, v3f d )
+VG_STATIC void gate_rotate_angles( ent_gate *gate, v3f angles, v3f d )
 {
    v3_copy( angles, d );
    return;
@@ -202,25 +202,26 @@ VG_STATIC void gate_rotate_angles( teleport_gate *gate, v3f angles, v3f d )
  * Applies gate transport to a player_interface
  */
 PLAYER_API
-void player__pass_gate( player_instance *player, struct gate_hit *hit )
+void player__pass_gate( player_instance *player, ent_gate *gate )
 {
-   player->gate_waiting = hit->gate;
+   player->gate_waiting = gate;
+   world_routes_activate_entry_gate( get_active_world(), gate );
 
-   m4x3_mulv( hit->gate->transport, player->tpv_lpf, player->tpv_lpf );
-   m3x3_mulv( hit->gate->transport, player->cam_velocity_smooth, 
-                                    player->cam_velocity_smooth );
+   m4x3_mulv( gate->transport, player->tpv_lpf, player->tpv_lpf );
+   m3x3_mulv( gate->transport, player->cam_velocity_smooth, 
+                               player->cam_velocity_smooth );
 
    m3x3_copy( player->basis, player->basis_gate );
 
    v4f q;
-   m3x3_q( hit->gate->transport, q );
+   m3x3_q( gate->transport, q );
    q_mul( q, player->qbasis, player->qbasis );
    q_normalize( player->qbasis );
    q_m3x3( player->qbasis, player->basis );
    m3x3_transpose( player->basis, player->invbasis );
 
-   if( hit->nonlocal )
-      world_global.active_world = hit->nonlocal->target_map_index;
+   if( gate->type == k_gate_type_nonlocal )
+      world_global.active_world = gate->target;
 }
 
 VG_STATIC void player__pre_render( player_instance *player )
@@ -238,8 +239,7 @@ VG_STATIC void player__pre_render( player_instance *player )
 
       struct skeleton *sk = &player->playeravatar->sk;
 
-      if( player->holdout_time > 0.0f )
-      {
+      if( player->holdout_time > 0.0f ){
          skeleton_lerp_pose( sk, res.pose, player->holdout_pose, 
                                            player->holdout_time, res.pose );
          player->holdout_time -= vg.frame_delta * 2.0f;
@@ -251,9 +251,7 @@ VG_STATIC void player__pre_render( player_instance *player )
       skeleton_apply_inverses( sk );
       skeleton_apply_transform( sk, transform );
 
-#if 0
       skeleton_debug( sk );
-#endif
    }
 
    if( _player_post_animate[ player->subsystem ] )
@@ -317,9 +315,9 @@ PLAYER_API void player__im_gui( player_instance *player )
 }
 
 PLAYER_API void player__spawn( player_instance *player, 
-                              struct respawn_point *rp )
+                               ent_spawn *rp )
 {
-   v3_copy( rp->co, player->rb.co );
+   v3_copy( rp->transform.co, player->rb.co );
    v3_zero( player->rb.v );
    v3_zero( player->rb.w );
    q_identity( player->rb.q );
index 8923cf9190f6352631567a7a14b78614b44fabea..1c02a4e55f1742d1fd0894c2ef22e17657bdd6c5 100644 (file)
--- a/player.h
+++ b/player.h
@@ -51,7 +51,7 @@ struct player_instance
          cam_land_punch,
          cam_land_punch_v;
 
-   teleport_gate *gate_waiting;
+   ent_gate *gate_waiting;
 
    /*
     * Input 
@@ -119,7 +119,7 @@ void (*_player_bind[])( player_instance *player ) =
 };
 
 VG_STATIC
-void (*_player_reset[])( player_instance *player, struct respawn_point *rp ) =
+void (*_player_reset[])( player_instance *player, ent_spawn *rp ) =
 {
    NULL,
    player__skate_reset,
index 8819772a78912f446bb89e8fcf3fb6178dc5d0b4..dbf1b53950124cb2528e629bf61faeddbd9ac304 100644 (file)
@@ -59,10 +59,10 @@ PLAYER_API void player__im_gui      ( player_instance *player );
  * ----------------------------------------------------------------------------
  */
 PLAYER_API void player__spawn       ( player_instance *player,
-                                      struct respawn_point *rp );
+                                      ent_spawn *rp );
 PLAYER_API void player__kill        ( player_instance *player );
 PLAYER_API void player__pass_gate   ( player_instance *player,
-                                      struct gate_hit *hit );
+                                      ent_gate *gate );
 
 /*
  * Utiltiy
index 5982eaeb8f21c8abfdde34c064d7c80cd760ad13..c80050373f13ccac5cf5f55aa5fa1471afed51da 100644 (file)
@@ -42,9 +42,10 @@ VG_STATIC void player_avatar_load( struct player_avatar *av, const char *path )
    /* load in reference player model, with animations and such */
    /* FIXME: This is allocated as un-freeable systems memory */
 
-   mdl_open( &av->meta, path );
-   mdl_load_metadata( &av->meta, vg_mem.rtmemory );
-   mdl_load_anim_data( &av->meta, vg_mem.rtmemory );
+   mdl_open( &av->meta, path, vg_mem.rtmemory );
+   mdl_load_metadata_block( &av->meta, vg_mem.rtmemory );
+   mdl_load_animation_block( &av->meta, vg_mem.rtmemory );
+   mdl_close( &av->meta );
 
    struct skeleton *sk = &av->sk;
    skeleton_setup( sk, vg_mem.rtmemory, &av->meta );
index 2bb0ae4220b1f7b3317f8b8dcbb490bdd58cdf96..3fd552471001449920d1fe49d589f5add73352d9 100644 (file)
@@ -42,8 +42,7 @@ VG_STATIC void player_init_ragdoll_bone_collider( struct skeleton_bone *bone,
 {
    m4x3_identity( rp->collider_mtx );
 
-   if( bone->flags & k_bone_flag_collider_box )
-   {
+   if( bone->collider == k_bone_collider_box ){
       v3f delta;
       v3_sub( bone->hitbox[1], bone->hitbox[0], delta );
       v3_muls( delta, 0.5f, delta );
@@ -56,18 +55,15 @@ VG_STATIC void player_init_ragdoll_bone_collider( struct skeleton_bone *bone,
       rp->rb.type = k_rb_shape_box;
       rp->colour = 0xffcccccc;
    }
-   else if( bone->flags & k_bone_flag_collider_capsule )
-   {
+   else if( bone->collider == k_bone_collider_capsule ){
       v3f v0, v1, tx, ty;
       v3_sub( bone->hitbox[1], bone->hitbox[0], v0 );
 
       int major_axis = 0;
       float largest = -1.0f;
 
-      for( int i=0; i<3; i ++ )
-      {
-         if( fabsf( v0[i] ) > largest )
-         {
+      for( int i=0; i<3; i ++ ){
+         if( fabsf( v0[i] ) > largest ){
             largest = fabsf( v0[i] );
             major_axis = i;
          }
@@ -133,14 +129,13 @@ VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
    rd->position_constraints_count = 0;
    rd->cone_constraints_count = 0;
 
-   for( u32 i=0; i<av->sk.bone_count; i ++ )
-   {
+   for( u32 i=0; i<av->sk.bone_count; i ++ ){
       struct skeleton_bone *bone = &av->sk.bones[i];
 
       /*
        * Bones with colliders
        */
-      if( !(bone->flags & k_bone_flag_collider_any) )
+      if( !(bone->collider) )
          continue;
 
       if( rd->part_count > vg_list_size(rd->parts) )
@@ -152,9 +147,6 @@ VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
 
       player_init_ragdoll_bone_collider( bone, rp );
       
-      struct mdl_node *pnode = mdl_node_from_id( &av->meta, bone->orig_node );
-      struct classtype_bone *inf = mdl_get_entdata( &av->meta, pnode );
-      
       /*
        * Bones with collider and parent
        */
@@ -163,27 +155,30 @@ VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
 
       rp->parent = ragdoll_bone_parent( rd, av, bone->parent );
       
-      /* Always assign a point-to-point constraint */
-      struct rb_constr_pos *c = 
-         &rd->position_constraints[ rd->position_constraints_count ++ ];
+      
+      if( bone->orig_bone->flags & k_bone_flag_cone_constraint ){
+         struct rb_constr_pos *c = 
+            &rd->position_constraints[ rd->position_constraints_count ++ ];
 
-      struct skeleton_bone *bj = &av->sk.bones[rp->bone_id];
-      struct ragdoll_part  *pp = &rd->parts[rp->parent];
-      struct skeleton_bone *bp = &av->sk.bones[pp->bone_id];
+         struct skeleton_bone *bj = &av->sk.bones[rp->bone_id];
+         struct ragdoll_part  *pp = &rd->parts[rp->parent];
+         struct skeleton_bone *bp = &av->sk.bones[pp->bone_id];
 
-      /* Convention: rba -- parent, rbb -- child */
-      c->rba = &pp->rb;
-      c->rbb = &rp->rb;
+         /* Convention: rba -- parent, rbb -- child */
+         c->rba = &pp->rb;
+         c->rbb = &rp->rb;
+
+         v3f delta;
+         v3_sub( bj->co, bp->co, delta );
+         m4x3_mulv( rp->inv_collider_mtx, (v3f){0.0f,0.0f,0.0f}, c->lcb );
+         m4x3_mulv( pp->inv_collider_mtx, delta, c->lca );
+
+
+         mdl_bone *inf = bone->orig_bone;
 
-      v3f delta;
-      v3_sub( bj->co, bp->co, delta );
-      m4x3_mulv( rp->inv_collider_mtx, (v3f){0.0f,0.0f,0.0f}, c->lcb );
-      m4x3_mulv( pp->inv_collider_mtx, delta, c->lca );
-      
-      if( inf->flags & k_bone_flag_cone_constraint )
-      {
          struct rb_constr_swingtwist *a = 
             &rd->cone_constraints[ rd->cone_constraints_count ++ ];
+
          a->rba = &pp->rb;
          a->rbb = &rp->rb;
          a->conet = cosf( inf->conet )-0.0001f;
@@ -216,8 +211,7 @@ VG_STATIC void player_setup_ragdoll_from_avatar( struct player_ragdoll *rd,
 VG_STATIC void copy_ragdoll_pose_to_avatar( struct player_ragdoll *rd,
                                             struct player_avatar *av )
 {
-   for( int i=0; i<rd->part_count; i++ )
-   {
+   for( int i=0; i<rd->part_count; i++ ){
       struct ragdoll_part *part = &rd->parts[i];
       m4x3f offset;
       m3x3_identity(offset);
@@ -235,8 +229,7 @@ VG_STATIC void copy_avatar_pose_to_ragdoll( struct player_avatar *av,
                                             struct player_ragdoll *rd,
                                             v3f velocity )
 {
-   for( int i=0; i<rd->part_count; i++ )
-   {
+   for( int i=0; i<rd->part_count; i++ ){
       struct ragdoll_part *part = &rd->parts[i];
 
       v3f pos, offset;
@@ -274,25 +267,31 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
    int run_sim = 0;
    ragdoll_frame ++;
 
-   if( ragdoll_frame >= k_ragdoll_div )
-   {
+   if( ragdoll_frame >= k_ragdoll_div ){
       ragdoll_frame = 0;
       run_sim = 1;
    }
 
    rb_solver_reset();
-   for( int i=0; i<rd->part_count; i ++ )
-   {
-      if( rb_global_has_space() )
-      {
+   for( int i=0; i<rd->part_count; i ++ ){
+      if( rb_global_has_space() ){
          rb_ct *buf = rb_global_buffer();
 
-         int l = rb_capsule__scene(  rd->parts[i].rb.to_world,
-                                    &rd->parts[i].rb.inf.capsule,
-                                     NULL, &world->rb_geo.inf.scene, buf );
+         int l;
+         
+         if( rd->parts[i].rb.type == k_rb_shape_capsule ){
+            l = rb_capsule__scene( rd->parts[i].rb.to_world,
+                                   &rd->parts[i].rb.inf.capsule,
+                                   NULL, &world->rb_geo.inf.scene, buf );
+         }
+         else if( rd->parts[i].rb.type == k_rb_shape_box ){
+            l = rb_box__scene( rd->parts[i].rb.to_world,
+                               rd->parts[i].rb.bbx,
+                               NULL, &world->rb_geo.inf.scene, buf );
+         }
+         else continue;
 
-         for( int j=0; j<l; j++ )
-         {
+         for( int j=0; j<l; j++ ){
             buf[j].rba = &rd->parts[i].rb;
             buf[j].rbb = &world->rb_geo;
          }
@@ -302,42 +301,42 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
    }
 
    /* 
-    * COLLISION DETECTION
+    * self-collision
     */
-   for( int i=0; i<rd->part_count-1; i ++ )
-   {
-      for( int j=i+1; j<rd->part_count; j ++ )
-      {
-         if( rd->parts[j].parent != i )
-         {
-            if( rb_global_has_space() )
-            {
-               rb_ct *buf = rb_global_buffer();
-
-               int l = rb_capsule__capsule( rd->parts[i].rb.to_world,
-                                            &rd->parts[i].rb.inf.capsule,
-                                            rd->parts[j].rb.to_world,
-                                            &rd->parts[j].rb.inf.capsule,
-                                            buf );
-
-               for( int k=0; k<l; k++ )
-               {
-                  buf[k].rba = &rd->parts[i].rb;
-                  buf[k].rbb = &rd->parts[j].rb;
-               }
-
-               rb_contact_count += l;
+   for( int i=0; i<rd->part_count-1; i ++ ){
+      for( int j=i+1; j<rd->part_count; j ++ ){
+         if( rd->parts[j].parent != i ){
+            if( !rb_global_has_space() )
+               break;
+
+            if( rd->parts[j].rb.type != k_rb_shape_capsule )
+               continue;
+
+            if( rd->parts[i].rb.type != k_rb_shape_capsule )
+               continue;
+
+            rb_ct *buf = rb_global_buffer();
+
+            int l = rb_capsule__capsule( rd->parts[i].rb.to_world,
+                                         &rd->parts[i].rb.inf.capsule,
+                                         rd->parts[j].rb.to_world,
+                                         &rd->parts[j].rb.inf.capsule,
+                                         buf );
+
+            for( int k=0; k<l; k++ ){
+               buf[k].rba = &rd->parts[i].rb;
+               buf[k].rbb = &rd->parts[j].rb;
             }
+
+            rb_contact_count += l;
          }
       }
    }
 
-   for( int j=0; j<rd->part_count; j++ )
-   {
+   for( int j=0; j<rd->part_count; j++ ){
       struct ragdoll_part *pj = &rd->parts[j];
 
-      if( run_sim )
-      {
+      if( run_sim ){
          v4f plane = {0.0f,1.0f,0.0f,0.0f};
          rb_effect_simple_bouyency( &pj->rb, plane, k_ragdoll_floatyiness,
                                                     k_ragdoll_floatydrag );
@@ -354,14 +353,12 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
    /* 
     * DEBUG
     */
-   if( k_ragdoll_debug_collider )
-   {
+   if( k_ragdoll_debug_collider ){
       for( u32 i=0; i<rd->part_count; i ++ )
          rb_debug( &rd->parts[i].rb, rd->parts[i].colour );
    }
 
-   if( k_ragdoll_debug_constraints )
-   {
+   if( k_ragdoll_debug_constraints ){
       rb_debug_position_constraints( rd->position_constraints, 
                                      rd->position_constraints_count );
 
@@ -372,10 +369,8 @@ VG_STATIC void player_ragdoll_iter( struct player_ragdoll *rd )
    /*
     * SOLVE CONSTRAINTS 
     */
-   if( run_sim )
-   {
-      for( int i=0; i<25; i++ )
-      {
+   if( run_sim ){
+      for( int i=0; i<16; i++ ){
          rb_solve_contacts( rb_contact_buffer, rb_contact_count );
          rb_solve_swingtwist_constraints( rd->cone_constraints, 
                                           rd->cone_constraints_count );
index 744b5eb050ca27c7fb5c040c21cffb2843b0aabf..c6e5db6ab6c188cd3cc901301b751fa530829ecb 100644 (file)
@@ -105,28 +105,25 @@ VG_STATIC int skate_grind_scansq( player_instance *player,
    v3_cross( plane, player->basis[1], support_axis );
    v3_normalize( support_axis );
    
-   while( bh_next( world->geo_bh, &it, box, &idx ) )
-   {
+   while( bh_next( world->geo_bh, &it, box, &idx ) ){
       u32 *ptri = &world->scene_geo->arrindices[ idx*3 ];
       v3f tri[3];
 
-      struct world_material *mat = world_tri_index_material(world,ptri[0]);
-      if( !(mat->info.flags & k_material_flag_skate_surface) )
+      struct world_surface *surf = world_tri_index_surface(world,ptri[0]);
+      if( !(surf->info.flags & k_material_flag_skate_surface) )
          continue;
 
       for( int j=0; j<3; j++ )
          v3_copy( world->scene_geo->arrvertices[ptri[j]].co, tri[j] );
 
-      for( int j=0; j<3; j++ )
-      {
+      for( int j=0; j<3; j++ ){
          int i0 = j,
              i1 = (j+1) % 3;
          
          struct grind_sample *sample = &samples[ sample_count ];
          v3f co;
 
-         if( plane_segment( plane, tri[i0], tri[i1], co ) )
-         {
+         if( plane_segment( plane, tri[i0], tri[i1], co ) ){
             v3f d;
             v3_sub( co, pos, d );
             if( v3_length2( d ) > r*r )
@@ -176,14 +173,12 @@ too_many_samples:
 
    int passed_samples = 0;
    
-   for( int i=0; i<sample_count-1; i++ )
-   {
+   for( int i=0; i<sample_count-1; i++ ){
       struct grind_sample *si, *sj;
 
       si = &samples[i];
 
-      for( int j=i+1; j<sample_count; j++ )
-      {
+      for( int j=i+1; j<sample_count; j++ ){
          if( i == j )
             continue;
 
@@ -452,11 +447,11 @@ void player__approximate_best_trajectory( player_instance *player )
             p->land_dist = t + k_trace_delta * t1;
 
             u32 vert_index = world->scene_geo->arrindices[ idx*3 ];
-            struct world_material *mat = 
-               world_tri_index_material( world, vert_index );
+            struct world_surface *surf = 
+               world_tri_index_surface( world, vert_index );
             
             /* Bias prediction towords ramps */
-            if( !(mat->info.flags & k_material_flag_skate_surface) )
+            if( !(surf->info.flags & k_material_flag_skate_surface) )
                p->score *= 10.0f;
 
             break;
@@ -472,14 +467,12 @@ void player__approximate_best_trajectory( player_instance *player )
          s->prediction_count --;
    }
 
-   if( grind_located )
-   {
+   if( grind_located ){
       /* calculate the exact solution(s) to jump onto that grind spot */
       struct land_prediction *p = &s->predictions[ s->prediction_count ];
       p->gravity = k_gravity;
 
-      if( solve_prediction_for_target( player, grind.co, 0.125f*VG_PIf, p ) )
-      {
+      if( solve_prediction_for_target( player, grind.co, 0.125f*VG_PIf, p ) ){
          v3_copy( grind.n, p->n );
 
          /* determine score */
@@ -498,8 +491,7 @@ void player__approximate_best_trajectory( player_instance *player )
 
    struct land_prediction *best = NULL;
 
-   for( int i=0; i<s->prediction_count; i ++ )
-   {
+   for( int i=0; i<s->prediction_count; i ++ ){
       struct land_prediction *p = &s->predictions[i];
 
       if( p->score < score_min )
@@ -509,8 +501,7 @@ void player__approximate_best_trajectory( player_instance *player )
       score_max = vg_maxf( score_max, p->score );
    }
 
-   for( int i=0; i<s->prediction_count; i ++ )
-   {
+   for( int i=0; i<s->prediction_count; i ++ ){
       struct land_prediction *p = &s->predictions[i];
       float s = p->score;
 
@@ -529,8 +520,7 @@ void player__approximate_best_trajectory( player_instance *player )
       p->colour |= 0xff000000;
    }
 
-   if( best )
-   {
+   if( best ){
       v3_copy( best->n, s->land_normal );
       v3_copy( best->v, player->rb.v );
       s->land_dist = best->land_dist;
@@ -540,21 +530,18 @@ void player__approximate_best_trajectory( player_instance *player )
       v2_normalize_clamp( steer );
       s->state.gravity_bias = best->gravity;
 
-      if( (fabsf(steer[1]) > 0.5f) && (s->land_dist >= 1.5f) )
-      {
+      if( (fabsf(steer[1]) > 0.5f) && (s->land_dist >= 1.5f) ){
          s->state.flip_rate = (1.0f/s->land_dist) * vg_signf(steer[1]) *
                                  s->state.reverse ;
          s->state.flip_time = 0.0f;
          v3_copy( player->rb.to_world[0], s->state.flip_axis );
       }
-      else
-      {
+      else{
          s->state.flip_rate = 0.0f;
          v3_zero( s->state.flip_axis );
       }
    }
-   else
-   {
+   else{
       v3_copy( player->basis[1], s->land_normal );
    }
 }
@@ -608,8 +595,7 @@ VG_STATIC void skate_apply_trick_model( player_instance *player )
    v3_muladds( s->board_trick_residuald, s->board_trick_residualv,
                k_rb_delta, s->board_trick_residuald );
 
-   if( s->state.activity == k_skate_activity_air )
-   {
+   if( s->state.activity == k_skate_activity_air ){
       if( v3_length2( s->state.trick_vel ) < 0.0001f )
          return;
 
@@ -620,8 +606,7 @@ VG_STATIC void skate_apply_trick_model( player_instance *player )
 
       float min_rate = 99999.0f;
 
-      for( int i=0; i<3; i++ )
-      {
+      for( int i=0; i<3; i++ ){
          float v = s->state.trick_vel[i];
          if( (v > 0.0f) && (v < min_rate) )
             min_rate = v;
@@ -636,8 +621,7 @@ VG_STATIC void skate_apply_trick_model( player_instance *player )
       v3_muladds( s->state.trick_euler, s->state.trick_vel, k_rb_delta,
                   s->state.trick_euler );
 
-      if( !carry_on && (s->state.trick_time + k_rb_delta >= next_end) )
-      {
+      if( !carry_on && (s->state.trick_time + k_rb_delta >= next_end) ){
          s->state.trick_time = 0.0f;
          s->state.trick_euler[0] = roundf( s->state.trick_euler[0] );
          s->state.trick_euler[1] = roundf( s->state.trick_euler[1] );
@@ -648,8 +632,7 @@ VG_STATIC void skate_apply_trick_model( player_instance *player )
 
       s->state.trick_time += k_rb_delta;
    }
-   else
-   {
+   else{
       if( (v3_length2(s->state.trick_vel) >= 0.0001f ) &&
           s->state.trick_time > 0.2f)
       {
@@ -670,8 +653,7 @@ VG_STATIC void skate_apply_grab_model( player_instance *player )
 
    float grabt = player->input_grab->axis.value;
 
-   if( grabt > 0.5f )
-   {
+   if( grabt > 0.5f ){
       v2_muladds( s->state.grab_mouse_delta, vg.mouse_delta, 0.02f, 
                   s->state.grab_mouse_delta );
 
@@ -699,24 +681,20 @@ VG_STATIC void skate_apply_steering_model( player_instance *player )
    float rate = 26.0f,
          top  = 1.0f;
 
-   if( s->state.activity == k_skate_activity_air )
-   {
+   if( s->state.activity == k_skate_activity_air ){
       rate = 6.0f * fabsf(steer);
       top  = 1.5f;
    }
-   else
-   {
+   else{
       /* rotate slower when grabbing on ground */
       steer *= (1.0f-(s->state.jump_charge+grab)*0.4f);
 
-      if( s->state.activity == k_skate_activity_grind_5050 )
-      {
+      if( s->state.activity == k_skate_activity_grind_5050 ){
          rate = 0.0f;
          top  = 0.0f;
       }
 
-      else if( s->state.activity >= k_skate_activity_grind_any )
-      {
+      else if( s->state.activity >= k_skate_activity_grind_any ){
          rate *= fabsf(steer);
 
          float a = 0.8f * -steer * k_rb_delta;
@@ -728,8 +706,7 @@ VG_STATIC void skate_apply_steering_model( player_instance *player )
          v3_normalize( s->grind_vec );
       }
 
-      else if( s->state.manual_direction )
-      {
+      else if( s->state.manual_direction ){
          rate = 35.0f;
          top  = 1.5f;
       }
@@ -773,8 +750,7 @@ VG_STATIC void skate_apply_friction_model( player_instance *player )
 
    /* Pushing additive force */
 
-   if( !player->input_jump->button.value )
-   {
+   if( !player->input_jump->button.value ){
       if( player->input_push->button.value || 
           (vg.time-s->state.start_push<0.75) )
       {
@@ -807,29 +783,25 @@ VG_STATIC void skate_apply_jump_model( player_instance *player )
    s->state.charging_jump = player->input_jump->button.value;
 
    /* Cannot charge this in air */
-   if( s->state.activity == k_skate_activity_air )
-   {
+   if( s->state.activity == k_skate_activity_air ){
       s->state.charging_jump = 0;
       return;
    }
 
-   if( s->state.charging_jump )
-   {
+   if( s->state.charging_jump ){
       s->state.jump_charge += k_rb_delta * k_jump_charge_speed;
 
       if( !charging_jump_prev )
          s->state.jump_dir = s->state.reverse>0.0f? 1: 0;
    }
-   else
-   {
+   else{
       s->state.jump_charge -= k_jump_charge_speed * k_rb_delta;
    }
 
    s->state.jump_charge = vg_clampf( s->state.jump_charge, 0.0f, 1.0f );
 
    /* player let go after charging past 0.2: trigger jump */
-   if( (!s->state.charging_jump) && (s->state.jump_charge > 0.2f) )
-   {
+   if( (!s->state.charging_jump) && (s->state.jump_charge > 0.2f) ){
       v3f jumpdir;
       
       /* Launch more up if alignment is up else improve velocity */
@@ -871,8 +843,7 @@ VG_STATIC void skate_apply_pump_model( player_instance *player )
 {
    struct player_skate *s = &player->_skate;
 
-   if( s->state.activity != k_skate_activity_ground )
-   {
+   if( s->state.activity != k_skate_activity_ground ){
       v3_zero( s->state.throw_v );
       return;
    }
@@ -881,24 +852,20 @@ VG_STATIC void skate_apply_pump_model( player_instance *player )
     *
     * TODO: Max speed boost
     */
-   if( player->input_grab->axis.value > 0.5f )
-   {
-      if( s->state.activity == k_skate_activity_ground )
-      {
+   if( player->input_grab->axis.value > 0.5f ){
+      if( s->state.activity == k_skate_activity_ground ){
          /* Throw */
          v3_muls( player->rb.to_world[1], k_mmthrow_scale, s->state.throw_v );
       }
    }
-   else
-   {
+   else{
       /* Collect */
       float doty = v3_dot( player->rb.to_world[1], s->state.throw_v );
       
       v3f Fl, Fv;
       v3_muladds( s->state.throw_v, player->rb.to_world[1], -doty, Fl);
 
-      if( s->state.activity == k_skate_activity_ground )
-      {
+      if( s->state.activity == k_skate_activity_ground ){
          v3_muladds( player->rb.v,     Fl,  k_mmcollect_lat, player->rb.v );
          v3_muladds( s->state.throw_v, Fl, -k_mmcollect_lat, s->state.throw_v );
       }
@@ -909,8 +876,7 @@ VG_STATIC void skate_apply_pump_model( player_instance *player )
    }
 
    /* Decay */
-   if( v3_length2( s->state.throw_v ) > 0.0001f )
-   {
+   if( v3_length2( s->state.throw_v ) > 0.0001f ){
       v3f dir;
       v3_copy( s->state.throw_v, dir );
       v3_normalize( dir );
@@ -960,8 +926,7 @@ VG_STATIC void skate_integrate( player_instance *player )
    float decay_rate = 1.0f - (k_rb_delta * 3.0f),
          decay_rate_y = 1.0f;
 
-   if( s->state.activity >= k_skate_activity_grind_any )
-   {
+   if( s->state.activity >= k_skate_activity_grind_any ){
       decay_rate = 1.0f-vg_lerpf( 3.0f, 20.0f, s->grind_strength ) * k_rb_delta;
       decay_rate_y = decay_rate;
    }
@@ -994,8 +959,7 @@ VG_STATIC void player__skate_pre_update( player_instance *player )
 {
    struct player_skate *s = &player->_skate;
 
-   if( vg_input_button_down( player->input_use ) )
-   {
+   if( vg_input_button_down( player->input_use ) ){
       player->subsystem = k_player_subsystem_walk;
 
       v3f angles;
@@ -1007,8 +971,7 @@ VG_STATIC void player__skate_pre_update( player_instance *player )
       return;
    }
 
-   if( vg_input_button_down( player->input_reset ) )
-   {
+   if( vg_input_button_down( player->input_reset ) ){
       player->rb.co[1] += 2.0f;
       s->state.cog[1] += 2.0f;
       q_axis_angle( player->rb.q, (v3f){1.0f,0.0f,0.0f}, VG_PIf * 0.25f );
@@ -1022,21 +985,17 @@ VG_STATIC void player__skate_pre_update( player_instance *player )
    if( (s->state.activity == k_skate_activity_air) && 
        (trick_id = player_skate_trick_input( player )) )
    {
-      if( (vg.time - s->state.jump_time) < 0.1f )
-      {
+      if( (vg.time - s->state.jump_time) < 0.1f ){
          v3_zero( s->state.trick_vel );
          s->state.trick_time = 0.0f;
 
-         if( trick_id == 1 )
-         {
+         if( trick_id == 1 ){
             s->state.trick_vel[0] = 3.0f;
          }
-         else if( trick_id == 2 )
-         {
+         else if( trick_id == 2 ){
             s->state.trick_vel[2] = 3.0f;
          }
-         else if( trick_id == 3 )
-         {
+         else if( trick_id == 3 ){
             s->state.trick_vel[0] = 2.0f;
             s->state.trick_vel[2] = 2.0f;
          }
@@ -1109,8 +1068,7 @@ VG_STATIC void player__skate_post_update( player_instance *player )
    audio_set_lfo_wave( 0, k_lfo_polynomial_bipolar, 
                         vg_lerpf( 250.0f, 80.0f, attn ) );
 
-   if( s->aud_main )
-   {
+   if( s->aud_main ){
       s->aud_main->colour = 0x00103efe;
       audio_channel_set_spacial( s->aud_main, player->rb.co, 40.0f );
       audio_channel_slope_volume( s->aud_main, 0.05f, vol_main );
@@ -1120,16 +1078,14 @@ VG_STATIC void player__skate_post_update( player_instance *player )
       audio_channel_set_sampling_rate( s->aud_main, rate );
    }
 
-   if( s->aud_slide )
-   {
+   if( s->aud_slide ){
       s->aud_slide->colour = 0x00103efe;
       audio_channel_set_spacial( s->aud_slide, player->rb.co, 40.0f );
       audio_channel_slope_volume( s->aud_slide, 0.05f, vol_slide );
       audio_channel_sidechain_lfo( s->aud_slide, 0, sidechain_amt );
    }
 
-   if( s->aud_air )
-   {
+   if( s->aud_air ){
       s->aud_air->colour = 0x00103efe;
       audio_channel_set_spacial( s->aud_air, player->rb.co, 40.0f );
       audio_channel_slope_volume( s->aud_air, 0.05f, vol_air );
@@ -2275,17 +2231,15 @@ begin_collision:;
 
    s->surface = k_surface_prop_concrete;
 
-   for( int i=0; i<manifold_len; i++ )
-   {
+   for( int i=0; i<manifold_len; i++ ){
       rb_ct *ct = &manifold[i];
-      struct world_material *surface_mat = world_contact_material( world, ct );
+      struct world_surface *surf = world_contact_surface( world, ct );
 
-      if( surface_mat->info.surface_prop != k_surface_prop_concrete )
-         s->surface = surface_mat->info.surface_prop;
+      if( surf->info.surface_prop != k_surface_prop_concrete )
+         s->surface = surf->info.surface_prop;
    }
 
-   for( int i=0; i<k_wheel_count; i++ )
-   {
+   for( int i=0; i<k_wheel_count; i++ ){
       m4x3f mtx;
       m3x3_copy( player->rb.to_world, mtx );
       m4x3_mulv( player->rb.to_world, wheels[i].pos, mtx[3] );
@@ -2297,10 +2251,10 @@ begin_collision:;
    skate_integrate( player );
    vg_line_pt3( s->state.cog, 0.02f, VG__WHITE );
 
-   struct gate_hit hit;
-   if( world_intersect_gates(world, player->rb.co, s->state.prev_pos, &hit) )
-   {
-      teleport_gate *gate = hit.gate;
+   ent_gate *gate = 
+      world_intersect_gates(world, player->rb.co, s->state.prev_pos );
+
+   if( gate ){
       m4x3_mulv( gate->transport, player->rb.co, player->rb.co );
       m3x3_mulv( gate->transport, player->rb.v,  player->rb.v );
       m4x3_mulv( gate->transport, s->state.cog,   s->state.cog );
@@ -2316,7 +2270,7 @@ begin_collision:;
       rb_update_transform( &player->rb );
 
       s->state_gate_storage = s->state;
-      player__pass_gate( player, &hit );
+      player__pass_gate( player, gate );
    }
 
    /* FIXME: Rate limit */
@@ -2766,13 +2720,13 @@ VG_STATIC void player__skate_clear_mechanics( player_instance *player )
 }
 
 VG_STATIC void player__skate_reset( player_instance *player,
-                                    struct respawn_point *rp )
+                                    ent_spawn *rp )
 {
    struct player_skate *s = &player->_skate;
    v3_muladds( player->rb.co, player->rb.to_world[1], 1.0f, s->state.cog );
    v3_zero( player->rb.v );
    v3_zero( s->state.cog_v );
-   v4_copy( rp->q, player->rb.q );
+   v4_copy( rp->transform.q, player->rb.q );
 
    s->state.activity = k_skate_activity_air;
    s->state.activity_prev = k_skate_activity_air;
index 867a23888eb751b501cf4ba432a724ea62bbb2ee..f1661d53c87c881c447263ecfe0ccc2d51927200 100644 (file)
@@ -170,7 +170,7 @@ VG_STATIC void player__skate_animate      ( player_instance *player,
                                             player_animation *anim );
 VG_STATIC void player__skate_post_animate ( player_instance *player );
 VG_STATIC void player__skate_reset        ( player_instance *player,
-                                            struct respawn_point *rp );
+                                            ent_spawn *rp );
 
 VG_STATIC void player__skate_clear_mechanics( player_instance *player );
 VG_STATIC void player__skate_reset_animator( player_instance *player );
index fbae15d4e1dc12b1c18bb21094d0dcdf645c89b6..6b7788c1405a9be7854e8af56382b8d47c24c12f 100644 (file)
@@ -432,15 +432,13 @@ VG_STATIC void player__walk_update( player_instance *player )
    v3_muls( right_dir,  walk[0], movedir );
    v3_muladds( movedir, forward_dir, walk[1], movedir );
 
-   if( w->state.activity == k_walk_activity_ground )
-   {
+   if( w->state.activity == k_walk_activity_ground ){
       v3_normalize( surface_avg );
 
       v3f tx, ty;
       rb_tangent_basis( surface_avg, tx, ty );
 
-      if( v2_length2(walk) > 0.001f )
-      {
+      if( v2_length2(walk) > 0.001f ){
          /* clip movement to the surface */
          float d = v3_dot(surface_avg,movedir);
          v3_muladds( movedir, surface_avg, -d, movedir );
@@ -450,30 +448,25 @@ VG_STATIC void player__walk_update( player_instance *player )
       nominal_speed = k_walkspeed;
 
       /* jump */
-      if( player->input_jump->button.value )
-      {
+      if( player->input_jump->button.value ){
          v3_muladds( player->rb.v, player->basis[1], 5.0f, player->rb.v );
          w->state.activity = k_walk_activity_air;
          accel_speed = k_walk_air_accel;
          nominal_speed = k_airspeed;
       }
-      else
-      {
+      else{
          player_friction( player->rb.v );
 
-         struct world_material *surface_mat = 
-                                       world_contact_material( world, manifold);
-         w->surface = surface_mat->info.surface_prop;
+         struct world_surface *surf = world_contact_surface( world, manifold );
+         w->surface = surf->info.surface_prop;
       }
    }
-   else
-   {
+   else{
       accel_speed = k_walk_air_accel;
       nominal_speed = k_airspeed;
    }
 
-   if( v2_length2(walk) > 0.001f )
-   {
+   if( v2_length2(walk) > 0.001f ){
       player_accelerate( player->rb.v, movedir, nominal_speed, accel_speed );
       v3_normalize( movedir );
    }
@@ -481,10 +474,8 @@ VG_STATIC void player__walk_update( player_instance *player )
    /*
     * Resolve velocity constraints
     */
-   for( int j=0; j<5; j++ )
-   {
-      for( int i=0; i<len; i++ )
-      {
+   for( int j=0; j<5; j++ ){
+      for( int i=0; i<len; i++ ){
          struct contact *ct = &manifold[i];
          
          /*normal */
@@ -580,10 +571,9 @@ VG_STATIC void player__walk_update( player_instance *player )
       }
    }
 
-   struct gate_hit hit;
-   if( world_intersect_gates(world, player->rb.co, w->state.prev_pos, &hit) )
-   {
-      teleport_gate *gate = hit.gate;
+   ent_gate *gate = 
+      world_intersect_gates( world, player->rb.co, w->state.prev_pos );
+   if( gate ){
       m4x3_mulv( gate->transport, player->rb.co, player->rb.co );
       m3x3_mulv( gate->transport, player->rb.v,  player->rb.v );
 
@@ -594,7 +584,7 @@ VG_STATIC void player__walk_update( player_instance *player )
       rb_update_transform( &player->rb );
 
       w->state_gate_storage = w->state;
-      player__pass_gate( player, &hit );
+      player__pass_gate( player, gate );
    }
 }
 
index 2e17120eda4f90439481869df6102b099c8719e7..fe8197569e689f87d316905f2ce4244b5b351e69 100644 (file)
@@ -604,19 +604,14 @@ VG_STATIC int rb_box_triangle_interval( v3f extent, v3f axis, v3f tri[3] )
 /*
  * Seperating axis test box vs triangle
  */
-VG_STATIC int rb_box_triangle_sat( rigidbody *rba, v3f tri_src[3] )
+VG_STATIC int rb_box_triangle_sat( v3f extent, v3f center,
+                                   m4x3f to_local, v3f tri_src[3] )
 {
    v3f tri[3];
 
-   v3f extent, c;
-   v3_sub( rba->bbx[1], rba->bbx[0], extent );
-   v3_muls( extent, 0.5f, extent );
-   v3_add( rba->bbx[0], extent, c );
-
-   for( int i=0; i<3; i++ )
-   {
-      m4x3_mulv( rba->to_local, tri_src[i], tri[i] );
-      v3_sub( tri[i], c, tri[i] );
+   for( int i=0; i<3; i++ ){
+      m4x3_mulv( to_local, tri_src[i], tri[i] );
+      v3_sub( tri[i], center, tri[i] );
    }
 
    /* u0, u1, u2 */
@@ -1581,6 +1576,154 @@ VG_STATIC int rb_sphere_scene( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
 }
 #endif
 
+VG_STATIC int rb_box__scene( m4x3f mtxA, boxf bbx,
+                             m4x3f mtxB, rb_scene *s, rb_ct *buf )
+{
+   scene *sc = s->bh_scene->user;
+   v3f tri[3];
+
+   v3f extent, center;
+   v3_sub( bbx[1], bbx[0], extent );
+   v3_muls( extent, 0.5f, extent );
+   v3_add( bbx[0], extent, center );
+
+   float r = v3_length(extent);
+   boxf world_bbx;
+   v3_fill( world_bbx[0], -r );
+   v3_fill( world_bbx[1],  r );
+   for( int i=0; i<2; i++ ){
+      v3_add( center, world_bbx[i], world_bbx[i] );
+      v3_add( mtxA[3], world_bbx[i], world_bbx[i] );
+   }
+
+   m4x3f to_local;
+   m4x3_invert_affine( mtxA, to_local );
+
+   bh_iter it;
+   bh_iter_init( 0, &it );
+   int idx;
+   int count = 0;
+
+   vg_line_boxf( world_bbx, VG__RED );
+   
+   while( bh_next( s->bh_scene, &it, world_bbx, &idx ) ){
+      u32 *ptri = &sc->arrindices[ idx*3 ];
+
+      for( int j=0; j<3; j++ )
+         v3_copy( sc->arrvertices[ptri[j]].co, tri[j] );
+
+      if( rb_box_triangle_sat( extent, center, to_local, tri ) ){
+         vg_line(tri[0],tri[1],0xff50ff00 );
+         vg_line(tri[1],tri[2],0xff50ff00 );
+         vg_line(tri[2],tri[0],0xff50ff00 );
+      }
+      else{
+         vg_line(tri[0],tri[1],0xff0000ff );
+         vg_line(tri[1],tri[2],0xff0000ff );
+         vg_line(tri[2],tri[0],0xff0000ff );
+         continue;
+      }
+
+      v3f v0,v1,n;
+      v3_sub( tri[1], tri[0], v0 );
+      v3_sub( tri[2], tri[0], v1 );
+      v3_cross( v0, v1, n );
+      v3_normalize( n );
+
+      /* find best feature */
+      float best = v3_dot( mtxA[0], n );
+      int axis = 0;
+
+      for( int i=1; i<3; i++ ){
+         float c = v3_dot( mtxA[i], n );
+
+         if( fabsf(c) > fabsf(best) ){
+            best = c;
+            axis = i;
+         }
+      }
+
+      v3f manifold[4];
+
+      if( axis == 0 ){
+         float px = best > 0.0f? bbx[0][0]: bbx[1][0];
+         manifold[0][0] = px;
+         manifold[0][1] = bbx[0][1];
+         manifold[0][2] = bbx[0][2];
+         manifold[1][0] = px;
+         manifold[1][1] = bbx[1][1];
+         manifold[1][2] = bbx[0][2];
+         manifold[2][0] = px;
+         manifold[2][1] = bbx[1][1];
+         manifold[2][2] = bbx[1][2];
+         manifold[3][0] = px;
+         manifold[3][1] = bbx[0][1];
+         manifold[3][2] = bbx[1][2];
+      }
+      else if( axis == 1 ){
+         float py = best > 0.0f? bbx[0][1]: bbx[1][1];
+         manifold[0][0] = bbx[0][0];
+         manifold[0][1] = py;
+         manifold[0][2] = bbx[0][2];
+         manifold[1][0] = bbx[1][0];
+         manifold[1][1] = py;
+         manifold[1][2] = bbx[0][2];
+         manifold[2][0] = bbx[1][0];
+         manifold[2][1] = py;
+         manifold[2][2] = bbx[1][2];
+         manifold[3][0] = bbx[0][0];
+         manifold[3][1] = py;
+         manifold[3][2] = bbx[1][2];
+      }
+      else{
+         float pz = best > 0.0f? bbx[0][2]: bbx[1][2];
+         manifold[0][0] = bbx[0][0];
+         manifold[0][1] = bbx[0][1];
+         manifold[0][2] = pz;
+         manifold[1][0] = bbx[1][0];
+         manifold[1][1] = bbx[0][1];
+         manifold[1][2] = pz;
+         manifold[2][0] = bbx[1][0];
+         manifold[2][1] = bbx[1][1];
+         manifold[2][2] = pz;
+         manifold[3][0] = bbx[0][0];
+         manifold[3][1] = bbx[1][1];
+         manifold[3][2] = pz;
+      }
+   
+      for( int j=0; j<4; j++ )
+         m4x3_mulv( mtxA, manifold[j], manifold[j] );
+
+      vg_line( manifold[0], manifold[1], 0xffffffff );
+      vg_line( manifold[1], manifold[2], 0xffffffff );
+      vg_line( manifold[2], manifold[3], 0xffffffff );
+      vg_line( manifold[3], manifold[0], 0xffffffff );
+
+      for( int j=0; j<4; j++ ){
+         rb_ct *ct = buf+count;
+
+         v3_copy( manifold[j], ct->co );
+         v3_copy( n, ct->n );
+
+         float l0 = v3_dot( tri[0], n ),
+               l1 = v3_dot( manifold[j], n );
+
+         ct->p = (l0-l1)*0.5f;
+         if( ct->p < 0.0f )
+            continue;
+
+         ct->type = k_contact_type_default;
+         count ++;
+
+         if( count >= 12 )
+            return count;
+      }
+   }
+   return count;
+}
+
+#if 0
+__attribute__ ((deprecated))
 VG_STATIC int rb_box_scene( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
 {
    scene *sc = rbb->inf.scene.bh_scene->user;
@@ -1723,6 +1866,7 @@ VG_STATIC int rb_box_scene( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
    }
    return count;
 }
+#endif
 
 VG_STATIC int rb_capsule__triangle( m4x3f mtxA, rb_capsule *c,
                                     v3f tri[3], rb_ct *buf )
@@ -1952,10 +2096,12 @@ VG_STATIC int rb_box_sphere( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
    return rb_sphere_box( rbb, rba, buf );
 }
 
+#if 0
 VG_STATIC int rb_scene_box( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
 {
    return rb_box_scene( rbb, rba, buf );
 }
+#endif
 
 #if 0
 VG_STATIC int (*rb_jump_table[4][4])( rigidbody *a, rigidbody *b, rb_ct *buf ) = 
diff --git a/scene.h b/scene.h
index fb758eb5a3e47edb0a6396db06886d1c1a99628e..cd1a8ea10689b5eb0c9303209b43dfa0b2c35ff3 100644 (file)
--- a/scene.h
+++ b/scene.h
@@ -78,8 +78,7 @@ VG_STATIC void scene_vert_pack_norm( scene_vert *vert, v3f norm )
 VG_STATIC void scene_add_mdl_submesh( scene *pscene, mdl_context *mdl, 
                                       mdl_submesh *sm, m4x3f transform )
 {
-   if( pscene->vertex_count + sm->vertex_count > pscene->max_vertices )
-   {
+   if( pscene->vertex_count + sm->vertex_count > pscene->max_vertices ){
       vg_error( "%u(current) + %u > %u\n", pscene->vertex_count,
                                            sm->vertex_count,
                                            pscene->max_vertices );
@@ -88,8 +87,7 @@ VG_STATIC void scene_add_mdl_submesh( scene *pscene, mdl_context *mdl,
       vg_fatal_exit_loop( "Scene vertex buffer overflow" );
    }
 
-   if( pscene->indice_count + sm->indice_count > pscene->max_indices )
-   {
+   if( pscene->indice_count + sm->indice_count > pscene->max_indices ){
       vg_error( "%u(current) + %u > %u\n", pscene->indice_count,
                                            sm->indice_count,
                                            pscene->max_indices );
@@ -98,10 +96,10 @@ VG_STATIC void scene_add_mdl_submesh( scene *pscene, mdl_context *mdl,
       vg_fatal_exit_loop( "Scene index buffer overflow" );
    }
 
-   mdl_vert   *src_verts =  mdl_submesh_vertices( mdl, sm );
+   mdl_vert   *src_verts = mdl_arritm( &mdl->verts, sm->vertex_start );
    scene_vert *dst_verts = &pscene->arrvertices[ pscene->vertex_count ];
 
-   u32 *src_indices    =  mdl_submesh_indices( mdl, sm ),
+   u32 *src_indices    =  mdl_arritm( &mdl->indices, sm->indice_start ),
        *dst_indices    = &pscene->arrindices[ pscene->indice_count ];
    
    /* Transform and place vertices */
@@ -116,8 +114,7 @@ VG_STATIC void scene_add_mdl_submesh( scene *pscene, mdl_context *mdl,
    v3_normalize( normal_matrix[1] );
    v3_normalize( normal_matrix[2] );
    
-   for( u32 i=0; i<sm->vertex_count; i++ )
-   {
+   for( u32 i=0; i<sm->vertex_count; i++ ){
       mdl_vert   *src   = &src_verts[ i ];
       scene_vert *pvert = &dst_verts[ i ];
 
index 57b45411a1e924f7f581cd75b96c8784c3bfed1e..5c06d73db33caf07636035c02042dd0a7a7388da 100644 (file)
@@ -19,6 +19,7 @@ layout (std140) uniform ub_world_lighting
 
    float g_water_fog;
    float g_time;
+   float g_realtime;
    float g_shadow_length;
    float g_shadow_spread;
 
@@ -122,13 +123,13 @@ vec3 scene_calculate_light( int light_index,
 
    float falloff = max( 0.0, 1.0-(dist2*light_co.w) );
 
-   if( light_dir.w < 0.999999 )
-   {
+   if( light_dir.w < 0.999999 ){
       float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );
       falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );
    }
 
-   return light_colour.rgb * attenuation * falloff;
+   return light_colour.rgb * attenuation * falloff 
+            * step( g_day_phase, light_colour.w );
 }
 
 vec3 scene_calculate_packed_light_patch( uint packed_index, 
@@ -138,20 +139,17 @@ vec3 scene_calculate_packed_light_patch( uint packed_index,
 
    vec3 l = vec3(0.0);
 
-   if( light_count >= 1u )
-   {
+   if( light_count >= 1u ){
       int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );
       int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );
       int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );
 
       l += scene_calculate_light( index_0, halfview, co, normal );
 
-      if( light_count >= 2u )
-      {
+      if( light_count >= 2u ){
          l += scene_calculate_light( index_1, halfview, co, normal );
 
-         if( light_count >= 3u )
-         {
+         if( light_count >= 3u ){
             l += scene_calculate_light( index_2, halfview, co, normal );
          }
       }
index d70953dd4696232f5a68f47dfe66298afc436784..23cba2514f184805f593d432f4e1dbaadcad6589 100644 (file)
@@ -105,6 +105,7 @@ static struct vg_shader _shader_model_character_view = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -201,7 +202,7 @@ static struct vg_shader _shader_model_character_view = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -286,13 +287,13 @@ static struct vg_shader _shader_model_character_view = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -302,20 +303,17 @@ static struct vg_shader _shader_model_character_view = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index 013c916a5e9d6a9af0e1aef89ec119541e1d7442..fc9c1ea326b904532f78be6249efb0e78f08e549 100644 (file)
@@ -42,6 +42,7 @@ static struct vg_shader _shader_model_gate = {
 "uniform float uTime;\n"
 "uniform vec3 uCam;\n"
 "uniform vec2 uInvRes;\n"
+"uniform vec4 uColour;\n"
 "\n"
 "in vec3 aNorm;\n"
 "in vec2 aUv;\n"
@@ -50,7 +51,7 @@ static struct vg_shader _shader_model_gate = {
 "void main()\n"
 "{\n"
 "   vec2 ssuv = gl_FragCoord.xy;\n"
-"   float opacity = 1.0-smoothstep(0.4,1.0,aUv.y);\n"
+"   float opacity = 1.0-smoothstep(0.0,1.0,aUv.y+uColour.a);\n"
 "   \n"
 "   vec3 vDither = vec3( dot( vec2( 171.0, 231.0 ), ssuv) );\n"
 "   float dither = fract( vDither.g / 71.0 ) - 0.5;\n"
@@ -58,7 +59,7 @@ static struct vg_shader _shader_model_gate = {
 "   if( opacity+dither<0.5 )\n"
 "      discard;\n"
 "\n"
-"   FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );\n"
+"   FragColor = uColour;\n"
 "}\n"
 ""},
 };
@@ -68,6 +69,7 @@ static GLuint _uniform_model_gate_uMdl;
 static GLuint _uniform_model_gate_uTime;
 static GLuint _uniform_model_gate_uCam;
 static GLuint _uniform_model_gate_uInvRes;
+static GLuint _uniform_model_gate_uColour;
 static void shader_model_gate_uPv(m4x4f m){
    glUniformMatrix4fv(_uniform_model_gate_uPv,1,GL_FALSE,(float*)m);
 }
@@ -83,6 +85,9 @@ static void shader_model_gate_uCam(v3f v){
 static void shader_model_gate_uInvRes(v2f v){
    glUniform2fv(_uniform_model_gate_uInvRes,1,v);
 }
+static void shader_model_gate_uColour(v4f v){
+   glUniform4fv(_uniform_model_gate_uColour,1,v);
+}
 static void shader_model_gate_register(void){
    vg_shader_register( &_shader_model_gate );
 }
@@ -93,5 +98,6 @@ static void shader_model_gate_link(void){
    _uniform_model_gate_uTime = glGetUniformLocation( _shader_model_gate.id, "uTime" );
    _uniform_model_gate_uCam = glGetUniformLocation( _shader_model_gate.id, "uCam" );
    _uniform_model_gate_uInvRes = glGetUniformLocation( _shader_model_gate.id, "uInvRes" );
+   _uniform_model_gate_uColour = glGetUniformLocation( _shader_model_gate.id, "uColour" );
 }
 #endif /* SHADER_model_gate_H */
index c455126174f55432ea07ee9f4b13afb28d3358af..772efcb5187ebad3029973f5d7da46567195971f 100644 (file)
@@ -3,6 +3,7 @@ out vec4 FragColor;
 uniform float uTime;
 uniform vec3 uCam;
 uniform vec2 uInvRes;
+uniform vec4 uColour;
 
 in vec3 aNorm;
 in vec2 aUv;
@@ -11,7 +12,7 @@ in vec3 aCo;
 void main()
 {
    vec2 ssuv = gl_FragCoord.xy;
-   float opacity = 1.0-smoothstep(0.4,1.0,aUv.y);
+   float opacity = 1.0-smoothstep(0.0,1.0,aUv.y+uColour.a);
    
    vec3 vDither = vec3( dot( vec2( 171.0, 231.0 ), ssuv) );
    float dither = fract( vDither.g / 71.0 ) - 0.5;
@@ -19,5 +20,5 @@ void main()
    if( opacity+dither<0.5 )
       discard;
 
-   FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );
+   FragColor = uColour;
 }
index 6fd769e8ea75b8c0cd66577409347428462ace66..2ddc5bd5dfe96aa4961947ffc00fe4d31699158a 100644 (file)
@@ -99,6 +99,7 @@ static struct vg_shader _shader_model_sky = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -195,7 +196,7 @@ static struct vg_shader _shader_model_sky = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -280,13 +281,13 @@ static struct vg_shader _shader_model_sky = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -296,20 +297,17 @@ static struct vg_shader _shader_model_sky = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index b525a0fdd6aef24cf256adf1ba6ab88d69462481..0a88b46f09c8e2239c5f8e4b561a19a51558e92f 100644 (file)
@@ -98,6 +98,7 @@ static struct vg_shader _shader_scene_depth = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -194,7 +195,7 @@ static struct vg_shader _shader_scene_depth = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -279,13 +280,13 @@ static struct vg_shader _shader_scene_depth = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -295,20 +296,17 @@ static struct vg_shader _shader_scene_depth = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index e450d17c3eaef530adb4c8c7c2b14517e6196812..b3bf28f49de9750f97dcecb8cbd9c209bd4d1488 100644 (file)
@@ -98,6 +98,7 @@ static struct vg_shader _shader_scene_position = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -194,7 +195,7 @@ static struct vg_shader _shader_scene_position = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -279,13 +280,13 @@ static struct vg_shader _shader_scene_position = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -295,20 +296,17 @@ static struct vg_shader _shader_scene_position = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index 3ce337b73d721e6dbc77641caf393b8c23c12229..93ed9b1380e3b5171e7111f9c3ed95cabc04ad6c 100644 (file)
@@ -8,6 +8,13 @@ uniform vec3 uBoard1;
 #include "common_scene.glsl"
 #include "motion_vectors_fs.glsl"
 
+float filtered_stripe( in float p, in float ddx, in float ddy )
+{
+   float w = max(abs(ddx), abs(ddy)) + 0.02;
+   float i = (abs(fract((p-0.5*w)/2.0)-0.5)-abs(fract((p+0.5*w)/2.0)-0.5))/w;
+   return 0.5 - i;
+}
+
 void main()
 {
    compute_motion_vectors();
@@ -32,9 +39,13 @@ void main()
    vfrag = pow(uColour.rgb,vec3(1.0/2.2));
    vfrag -= rgarbage.a*0.1;
 
-   if( wgarbage.g < 0.3 )
+   if( wgarbage.g < 0.1 )
       discard;
 
+   float movep = (aUv.x + abs(aUv.y-0.5)*0.4 - g_realtime)*2.0;
+   float stripe = filtered_stripe( movep, dFdx(movep), dFdy(movep) );
+   vfrag *= 0.9+stripe*uColour.a; 
+
    if( g_light_preview == 1 )
    {
       vfrag = vec3(0.5);
index bd67ea51e8f6a07f6479af7c94f8388931715a05..3f41ecab6ebd8deb0b6805c4f07bcb65bc9115a6 100644 (file)
@@ -99,6 +99,7 @@ static struct vg_shader _shader_scene_route = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -195,7 +196,7 @@ static struct vg_shader _shader_scene_route = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -280,13 +281,13 @@ static struct vg_shader _shader_scene_route = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -296,20 +297,17 @@ static struct vg_shader _shader_scene_route = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
@@ -428,6 +426,13 @@ static struct vg_shader _shader_scene_route = {
 "\n"
 "#line     10        0 \n"
 "\n"
+"float filtered_stripe( in float p, in float ddx, in float ddy )\n"
+"{\n"
+"   float w = max(abs(ddx), abs(ddy)) + 0.02;\n"
+"   float i = (abs(fract((p-0.5*w)/2.0)-0.5)-abs(fract((p+0.5*w)/2.0)-0.5))/w;\n"
+"   return 0.5 - i;\n"
+"}\n"
+"\n"
 "void main()\n"
 "{\n"
 "   compute_motion_vectors();\n"
@@ -452,9 +457,13 @@ static struct vg_shader _shader_scene_route = {
 "   vfrag = pow(uColour.rgb,vec3(1.0/2.2));\n"
 "   vfrag -= rgarbage.a*0.1;\n"
 "\n"
-"   if( wgarbage.g < 0.3 )\n"
+"   if( wgarbage.g < 0.1 )\n"
 "      discard;\n"
 "\n"
+"   float movep = (aUv.x + abs(aUv.y-0.5)*0.4 - g_realtime)*2.0;\n"
+"   float stripe = filtered_stripe( movep, dFdx(movep), dFdy(movep) );\n"
+"   vfrag *= 0.9+stripe*uColour.a; \n"
+"\n"
 "   if( g_light_preview == 1 )\n"
 "   {\n"
 "      vfrag = vec3(0.5);\n"
index 85f84673f7a74045cbf38309f26d288738683a21..656498d402009bed3a5e4f8c2bda2392dda3a0b1 100644 (file)
@@ -99,6 +99,7 @@ static struct vg_shader _shader_scene_standard = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -195,7 +196,7 @@ static struct vg_shader _shader_scene_standard = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -280,13 +281,13 @@ static struct vg_shader _shader_scene_standard = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -296,20 +297,17 @@ static struct vg_shader _shader_scene_standard = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index d35eb4a7c70eeb3f1e2d73424cc1fd90181d811f..74b36f64f264ce425bac5d733c46589987c3d141 100644 (file)
@@ -99,6 +99,7 @@ static struct vg_shader _shader_scene_standard_alphatest = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -195,7 +196,7 @@ static struct vg_shader _shader_scene_standard_alphatest = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -280,13 +281,13 @@ static struct vg_shader _shader_scene_standard_alphatest = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -296,20 +297,17 @@ static struct vg_shader _shader_scene_standard_alphatest = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index 99affba5216daf502c55a706d81007d4802908d4..2027fd2a3dc102c082cbb8a2cb250a18036f3e16 100644 (file)
@@ -100,6 +100,7 @@ static struct vg_shader _shader_scene_terrain = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -196,7 +197,7 @@ static struct vg_shader _shader_scene_terrain = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -281,13 +282,13 @@ static struct vg_shader _shader_scene_terrain = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -297,20 +298,17 @@ static struct vg_shader _shader_scene_terrain = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index d3e418620dba81de9dcaeefce80705273a4d1685..b350fda1d88011153a0affcdaa21d0259b4a44c2 100644 (file)
@@ -98,6 +98,7 @@ static struct vg_shader _shader_scene_vertex_blend = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -194,7 +195,7 @@ static struct vg_shader _shader_scene_vertex_blend = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -279,13 +280,13 @@ static struct vg_shader _shader_scene_vertex_blend = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -295,20 +296,17 @@ static struct vg_shader _shader_scene_vertex_blend = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index 0fe4e1787cfe6e27f2fa332ddadc32bb0a07c589..5d4a5d2eca2126bacf148802028b9f08bb3e61fb 100644 (file)
@@ -106,6 +106,7 @@ static struct vg_shader _shader_scene_water = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -202,7 +203,7 @@ static struct vg_shader _shader_scene_water = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -287,13 +288,13 @@ static struct vg_shader _shader_scene_water = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -303,20 +304,17 @@ static struct vg_shader _shader_scene_water = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index 6c4e30e32449c157986fed7132b08e695982dc3f..b02f8ca5b33cbd6551a05148c20ac4bc5a004bfc 100644 (file)
@@ -103,6 +103,7 @@ static struct vg_shader _shader_scene_water_fast = {
 "\n"
 "   float g_water_fog;\n"
 "   float g_time;\n"
+"   float g_realtime;\n"
 "   float g_shadow_length;\n"
 "   float g_shadow_spread;\n"
 "\n"
@@ -199,7 +200,7 @@ static struct vg_shader _shader_scene_water_fast = {
 "   return ambient + (light_sun + sky_reflection) * shadow;\n"
 "}\n"
 "\n"
-"#line     41        0 \n"
+"#line     42        0 \n"
 "\n"
 "float world_depth_sample( vec3 pos )\n"
 "{\n"
@@ -284,13 +285,13 @@ static struct vg_shader _shader_scene_water_fast = {
 "\n"
 "   float falloff = max( 0.0, 1.0-(dist2*light_co.w) );\n"
 "\n"
-"   if( light_dir.w < 0.999999 )\n"
-"   {\n"
+"   if( light_dir.w < 0.999999 ){\n"
 "      float spot_theta = max( 0.0, dot( light_delta, -light_dir.xyz ) );\n"
 "      falloff *= max( 0.0, (spot_theta - light_dir.w) / (1.0-light_dir.w) );\n"
 "   }\n"
 "\n"
-"   return light_colour.rgb * attenuation * falloff;\n"
+"   return light_colour.rgb * attenuation * falloff \n"
+"            * step( g_day_phase, light_colour.w );\n"
 "}\n"
 "\n"
 "vec3 scene_calculate_packed_light_patch( uint packed_index, \n"
@@ -300,20 +301,17 @@ static struct vg_shader _shader_scene_water_fast = {
 "\n"
 "   vec3 l = vec3(0.0);\n"
 "\n"
-"   if( light_count >= 1u )\n"
-"   {\n"
+"   if( light_count >= 1u ){\n"
 "      int index_0 = int( ((packed_index >>  2u) & 0x3ffu) * 3u );\n"
 "      int index_1 = int( ((packed_index >> 12u) & 0x3ffu) * 3u );\n"
 "      int index_2 = int( ((packed_index >> 22u) & 0x3ffu) * 3u );\n"
 "\n"
 "      l += scene_calculate_light( index_0, halfview, co, normal );\n"
 "\n"
-"      if( light_count >= 2u )\n"
-"      {\n"
+"      if( light_count >= 2u ){\n"
 "         l += scene_calculate_light( index_1, halfview, co, normal );\n"
 "\n"
-"         if( light_count >= 3u )\n"
-"         {\n"
+"         if( light_count >= 3u ){\n"
 "            l += scene_calculate_light( index_2, halfview, co, normal );\n"
 "         }\n"
 "      }\n"
index a6abbee67913f079006d93baf1e3477ffc771685..6343098c874d59ef03c12fe2426c7a6232f7986e 100644 (file)
@@ -72,17 +72,13 @@ VG_STATIC int __kill( int argc, const char *argv[] )
 
 VG_STATIC int __respawn( int argc, const char *argv[] )
 {
-   struct respawn_point *rp = NULL, *r;
-
+   ent_spawn *rp = NULL, *r;
    world_instance *world = get_active_world();
 
-   if( argc == 1 )
-   {
-      for( int i=0; i<world->spawn_count; i++ )
-      {
-         r = &world->spawns[i];
-         if( !strcmp( r->name, argv[0] ) )
-         {
+   if( argc == 1 ){
+      for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
+         r = mdl_arritm( &world->ent_spawn, i );
+         if( !strcmp( mdl_pstr(&world->meta, r->pstr_name),argv[0] ) ){
             rp = r;
             break;
          }
@@ -92,32 +88,27 @@ VG_STATIC int __respawn( int argc, const char *argv[] )
          vg_warn( "No spawn named '%s'\n", argv[0] );
    }
 
-   if( !rp )
-   {
+   if( !rp ){
       float min_dist = INFINITY;
 
-      for( int i=0; i<world->spawn_count; i++ )
-      {
-         r = &world->spawns[i];
-         float d = v3_dist2( r->co, localplayer.rb.co );
+      for( u32 i=0; i<mdl_arrcount(&world->ent_spawn); i++ ){
+         r = mdl_arritm( &world->ent_spawn, i );
+         float d = v3_dist2( r->transform.co, localplayer.rb.co );
          
-         vg_info( "Dist %s : %f\n", r->name, d );
-         if( d < min_dist )
-         {
+         if( d < min_dist ){
             min_dist = d;
             rp = r;
          }
       }
    }
 
-   if( !rp )
-   {
+   if( !rp ){
       vg_error( "No spawn found\n" );
 
-      if( !world->spawn_count )
+      if( !mdl_arrcount(&world->ent_spawn) )
          return 0;
 
-      rp = &world->spawns[0];
+      rp = mdl_arritm( &world->ent_spawn, 0 );
    }
 
    player__spawn( &localplayer, rp );
@@ -197,19 +188,19 @@ VG_STATIC void load_playermodels(void)
                ctx_outlaw,
                ctx_jordan;
 
-   mdl_open( &ctx_default, "models/ch_new.mdl" );
-   mdl_load_metadata( &ctx_default, vg_mem.scratch );
-   mdl_load_mesh_data( &ctx_default, vg_mem.scratch );
+   mdl_open( &ctx_default, "models/ch_new.mdl", vg_mem.scratch );
+   mdl_load_metadata_block( &ctx_default, vg_mem.scratch );
+   mdl_load_mesh_block( &ctx_default, vg_mem.scratch );
    mdl_close( &ctx_default );
 
-   mdl_open( &ctx_outlaw, "models/ch_outlaw.mdl" );
-   mdl_load_metadata( &ctx_outlaw, vg_mem.scratch );
-   mdl_load_mesh_data( &ctx_outlaw, vg_mem.scratch );
+   mdl_open( &ctx_outlaw, "models/ch_outlaw.mdl", vg_mem.scratch );
+   mdl_load_metadata_block( &ctx_outlaw, vg_mem.scratch );
+   mdl_load_mesh_block( &ctx_outlaw, vg_mem.scratch );
    mdl_close( &ctx_outlaw );
 
-   mdl_open( &ctx_jordan, "models/ch_jordan.mdl" );
-   mdl_load_metadata( &ctx_jordan, vg_mem.scratch );
-   mdl_load_mesh_data( &ctx_jordan, vg_mem.scratch );
+   mdl_open( &ctx_jordan, "models/ch_jordan.mdl", vg_mem.scratch );
+   mdl_load_metadata_block( &ctx_jordan, vg_mem.scratch );
+   mdl_load_mesh_block( &ctx_jordan, vg_mem.scratch );
    mdl_close( &ctx_jordan );
    
    vg_acquire_thread_sync();
@@ -258,14 +249,14 @@ VG_STATIC void vg_load(void)
    /* 'systems' are completely loaded now */
 
    /* load home world */
-   world_load( &world_global.worlds[0], "maps/mp_home.mdl" );
+   world_load( &world_global.worlds[0], "maps/mp_gridmap.mdl" );
 
 #if 0
    world_load( &world_global.worlds[1], "maps/mp_gridmap.mdl" );
    world_link_nonlocal_gates( 0, 1 );
-#endif
    world_load( &world_global.worlds[2], "maps/mp_mtzero.mdl" );
    world_link_nonlocal_gates( 0, 2 );
+#endif
 
    vg_console_load_autos();
 }
@@ -500,6 +491,13 @@ VG_STATIC void render_scene(void)
    glEnable( GL_DEPTH_TEST );
 
    world_instance *view_world = localplayer.viewable_world;
+
+   if( view_world == NULL ){
+      glClearColor( 0.25f, 0.25f, 0.0f, 1.0f );
+      glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT );
+      return;
+   }
+
    render_world( view_world, &main_camera );
 
    int player_transparent = 1,
@@ -662,13 +660,6 @@ VG_STATIC void vg_ui(void)
    }
 #endif
    
-   //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
-   if( cl_ui )
-   {
-      render_world_routes_ui( world );
-   }
-   //glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
-   
    audio_debug_soundscapes();
    render_view_framebuffer_ui();
 
index c5f59da39fcc31ab581e1e2396c73351b28bb863..2fcfb5023f171540b517c989a918914061a8c754 100644 (file)
@@ -18,9 +18,9 @@ struct skeleton
       int defer;
 
       mdl_keyframe kf;
+      mdl_bone *orig_bone;
 
-      u32 orig_node;
-
+      u32 collider;
       boxf hitbox;
       const char *name;
    }
@@ -55,8 +55,7 @@ struct skeleton
 
 VG_STATIC u32 skeleton_bone_id( struct skeleton *skele, const char *name )
 {
-   for( u32 i=1; i<skele->bone_count; i++ )
-   {
+   for( u32 i=1; i<skele->bone_count; i++ ){
       if( !strcmp( skele->bones[i].name, name ))
          return i;
    }
@@ -80,28 +79,26 @@ VG_STATIC void keyframe_copy_pose( mdl_keyframe *kfa, mdl_keyframe *kfb,
 VG_STATIC void keyframe_lerp_pose( mdl_keyframe *kfa, mdl_keyframe *kfb, 
                                    float t, mdl_keyframe *kfd, int count )
 {
-   if( t <= 0.01f )
-   {
+   if( t <= 0.0001f ){
       keyframe_copy_pose( kfa, kfd, count );
       return;
    }
-   else if( t >= 0.99f )
-   {
+   else if( t >= 0.9999f ){
       keyframe_copy_pose( kfb, kfd, count );
       return;
    }
 
-   for( int i=0; i<count; i++ )
-   {
+   for( int i=0; i<count; i++ ){
       v3_lerp( kfa[i].co, kfb[i].co, t, kfd[i].co );
       q_nlerp( kfa[i].q,  kfb[i].q,  t, kfd[i].q );
       v3_lerp( kfa[i].s,  kfb[i].s,  t, kfd[i].s );
    }
 }
 
-VG_STATIC void skeleton_lerp_pose( struct skeleton *skele,
-                                mdl_keyframe *kfa, mdl_keyframe *kfb, float t,
-                                mdl_keyframe *kfd )
+VG_STATIC 
+void skeleton_lerp_pose( struct skeleton *skele,
+                         mdl_keyframe *kfa, mdl_keyframe *kfb, float t,
+                         mdl_keyframe *kfd )
 {
    keyframe_lerp_pose( kfa, kfb, t, kfd, skele->bone_count-1 );
 }
@@ -156,27 +153,25 @@ typedef enum anim_apply
 }
 anim_apply;
 
-VG_STATIC int should_apply_bone( struct skeleton *skele, u32 id, anim_apply type )
+VG_STATIC 
+int should_apply_bone( struct skeleton *skele, u32 id, anim_apply type )
 {
    struct skeleton_bone *sb = &skele->bones[ id ],
                         *sp = &skele->bones[ sb->parent ];
 
-   if( type == k_anim_apply_defer_ik )
-   {
+   if( type == k_anim_apply_defer_ik ){
       if( ((sp->flags & k_bone_flag_ik) && !(sb->flags & k_bone_flag_ik)) 
           || sp->defer )
       {
          sb->defer = 1;
          return 0;
       }
-      else
-      {
+      else{
          sb->defer = 0;
          return 1;
       }
    }
-   else if( type == k_anim_apply_deffered_only )
-   {
+   else if( type == k_anim_apply_deffered_only ){
       if( sb->defer )
          return 1;
       else
@@ -190,16 +185,15 @@ VG_STATIC int should_apply_bone( struct skeleton *skele, u32 id, anim_apply type
  * Apply block of keyframes to skeletons final pose
  */
 VG_STATIC void skeleton_apply_pose( struct skeleton *skele, mdl_keyframe *pose,
-                                 anim_apply passtype )
+                                    anim_apply passtype )
 {
    m4x3_identity( skele->final_mtx[0] );
    skele->bones[0].defer = 0;
    skele->bones[0].flags &= ~k_bone_flag_ik;
 
-   for( int i=1; i<skele->bone_count; i++ )
-   {
+   for( u32 i=1; i<skele->bone_count; i++ ){
       struct skeleton_bone *sb = &skele->bones[i],
-                           *sp = &skele->bones[ sb->parent ];
+                           *sp = &skele->bones[sb->parent];
       
       if( !should_apply_bone( skele, i, passtype ) )
          continue;
@@ -244,8 +238,7 @@ VG_STATIC void skeleton_inverse_for_ik( struct skeleton *skele,
 VG_STATIC void skeleton_create_inverses( struct skeleton *skele )
 {
    /* IK: inverse 'plane-bone space' axis '(^axis,^bone,...)[base] */
-   for( int i=0; i<skele->ik_count; i++ )
-   {
+   for( u32 i=0; i<skele->ik_count; i++ ){
       struct skeleton_ik *ik = &skele->ik[i];
 
       m4x3f inverse;
@@ -263,10 +256,10 @@ VG_STATIC void skeleton_create_inverses( struct skeleton *skele )
 /*
  * Apply a model matrix to all bones, should be done last
  */
-VG_STATIC void skeleton_apply_transform( struct skeleton *skele, m4x3f transform )
+VG_STATIC 
+void skeleton_apply_transform( struct skeleton *skele, m4x3f transform )
 {
-   for( int i=0; i<skele->bone_count; i++ )
-   {
+   for( u32 i=0; i<skele->bone_count; i++ ){
       struct skeleton_bone *sb = &skele->bones[i];
       m4x3_mul( transform, skele->final_mtx[i], skele->final_mtx[i] );
    }
@@ -278,8 +271,7 @@ VG_STATIC void skeleton_apply_transform( struct skeleton *skele, m4x3f transform
  */
 VG_STATIC void skeleton_apply_inverses( struct skeleton *skele )
 {
-   for( int i=0; i<skele->bone_count; i++ )
-   {
+   for( u32 i=0; i<skele->bone_count; i++ ){
       struct skeleton_bone *sb = &skele->bones[i];
       m4x3f inverse;
       m3x3_identity( inverse );
@@ -294,8 +286,7 @@ VG_STATIC void skeleton_apply_inverses( struct skeleton *skele )
  */
 VG_STATIC void skeleton_apply_ik_pass( struct skeleton *skele )
 {
-   for( int i=0; i<skele->ik_count; i++ )
-   {
+   for( u32 i=0; i<skele->ik_count; i++ ){
       struct skeleton_ik *ik = &skele->ik[i];
       
       v3f v0, /* base -> target */
@@ -388,8 +379,7 @@ VG_STATIC void skeleton_apply_standard( struct skeleton *skele, mdl_keyframe *po
 VG_STATIC struct skeleton_anim *skeleton_get_anim( struct skeleton *skele,
                                                    const char *name )
 {
-   for( int i=0; i<skele->anim_count; i++ )
-   {
+   for( u32 i=0; i<skele->anim_count; i++ ){
       struct skeleton_anim *anim = &skele->anims[i];
 
       if( !strcmp( anim->name, name ) )
@@ -403,13 +393,24 @@ VG_STATIC struct skeleton_anim *skeleton_get_anim( struct skeleton *skele,
 }
 
 VG_STATIC void skeleton_alloc_from( struct skeleton *skele,
-                                 void *lin_alloc,
-                                 struct classtype_skeleton *inf )
+                                    void *lin_alloc,
+                                    mdl_context *mdl,
+                                    mdl_armature *armature )
 {
-   skele->bone_count     = inf->channels;
-   skele->ik_count       = inf->ik_count;
-   skele->collider_count = inf->collider_count;
-   skele->anim_count     = inf->anim_count;
+   skele->bone_count     = armature->bone_count+1;
+   skele->anim_count     = armature->anim_count;
+   skele->ik_count       = 0;
+   skele->collider_count = 0;
+
+   for( u32 i=0; i<armature->bone_count; i++ ){
+      mdl_bone *bone = mdl_arritm( &mdl->bones, armature->bone_start+i );
+
+      if( bone->flags & k_bone_flag_ik )
+         skele->ik_count ++;
+
+      if( bone->collider )
+         skele->collider_count ++;
+   }
 
    u32 bone_size = sizeof(struct skeleton_bone) * skele->bone_count,
        ik_size   = sizeof(struct skeleton_ik)   * skele->ik_count,
@@ -431,112 +432,58 @@ VG_STATIC void skeleton_fatal_err(void)
 VG_STATIC void skeleton_setup( struct skeleton *skele,
                                void *lin_alloc, mdl_context *mdl )
 {
-   u32 bone_count = 1, skeleton_root = 0, ik_count = 0, collider_count = 0;
+   u32 ik_count = 0, collider_count = 0;
    skele->bone_count = 0;
    skele->bones = NULL;
    skele->final_mtx = NULL;
    skele->anims = NULL;
 
-   struct classtype_skeleton *inf = NULL;
+   if( !mdl->armatures.count ){
+      vg_error( "No skeleton in model\n" );
+      skeleton_fatal_err();
+   }
 
-   for( u32 i=0; i<mdl->info.node_count; i++ )
-   {
-      mdl_node *pnode = mdl_node_from_id( mdl, i );
+   mdl_armature *armature = mdl_arritm( &mdl->armatures, 0 );
+   skeleton_alloc_from( skele, lin_alloc, mdl, armature );
 
-      if( pnode->classtype == k_classtype_skeleton )
-      {
-         inf = mdl_get_entdata( mdl, pnode );
-         skeleton_alloc_from( skele, lin_alloc, inf );
-         skeleton_root = i;
-      }
-      else if( skele->bone_count )
-      {
-         int is_bone = pnode->classtype == k_classtype_bone;
-
-         if( is_bone )
-         {
-            if( bone_count == skele->bone_count )
-            {
-               vg_error( "too many bones (%u/%u) @%s!\n", 
-                           bone_count, skele->bone_count,
-                           mdl_pstr( mdl, pnode->pstr_name ));
-
-               skeleton_fatal_err();
-            }
-
-            struct skeleton_bone *sb = &skele->bones[bone_count];
-            struct classtype_bone *bone_inf = mdl_get_entdata( mdl, pnode );
-
-            v3_copy( pnode->co, sb->co );
-            v3_copy( pnode->s, sb->end );
-            sb->parent = pnode->parent-skeleton_root;
-            sb->name   = mdl_pstr( mdl, pnode->pstr_name );
-            sb->flags  = bone_inf->flags;
-
-            if( sb->flags & k_bone_flag_ik )
-            {
-               skele->bones[ sb->parent ].flags |= k_bone_flag_ik;
-               
-               if( ik_count == skele->ik_count )
-               {
-                  vg_error( "Too many ik bones, corrupt model file\n" );
-                  skeleton_fatal_err();
-               }
-
-               struct skeleton_ik *ik = &skele->ik[ ik_count ++ ];
-               ik->upper = bone_count;
-               ik->lower = sb->parent;
-               ik->target = bone_inf->ik_target;
-               ik->pole = bone_inf->ik_pole;
-            }
-
-            sb->orig_node = i;
-            box_copy( bone_inf->hitbox, sb->hitbox );
-
-            if( bone_inf->flags & k_bone_flag_collider_any )
-            {
-               if( collider_count == skele->collider_count )
-               {
-                  vg_error( "Too many collider bones\n" );
-                  skeleton_fatal_err();
-               }
-
-               collider_count ++;
-            }
-
-            bone_count ++;
-         }
-         else
-         {
-            break;
+   for( u32 i=0; i<armature->bone_count; i++ ){
+      mdl_bone *bone = mdl_arritm( &mdl->bones, armature->bone_start+i );
+      struct skeleton_bone *sb = &skele->bones[i+1];
+
+      v3_copy( bone->co, sb->co );
+      v3_copy( bone->end, sb->end );
+
+      sb->parent = bone->parent;
+      sb->name   = mdl_pstr( mdl, bone->pstr_name );
+      sb->flags  = bone->flags;
+      sb->collider = bone->collider;
+      sb->orig_bone = bone;
+
+      if( sb->flags & k_bone_flag_ik ){
+         skele->bones[ sb->parent ].flags |= k_bone_flag_ik;
+         
+         if( ik_count == skele->ik_count ){
+            vg_error( "Too many ik bones, corrupt model file\n" );
+            skeleton_fatal_err();
          }
+
+         struct skeleton_ik *ik = &skele->ik[ ik_count ++ ];
+         ik->upper = i+1;
+         ik->lower = bone->parent;
+         ik->target = bone->ik_target;
+         ik->pole = bone->ik_pole;
       }
-   }
 
-   if( !inf )
-   {
-      vg_error( "No skeleton in model\n" );
-      skeleton_fatal_err();
-   }
+      box_copy( bone->hitbox, sb->hitbox );
 
-   if( collider_count != skele->collider_count )
-   {
-      vg_error( "Loaded %u colliders out of %u\n", collider_count,
-                                                   skele->collider_count );
-      skeleton_fatal_err();
-   }
-
-   if( bone_count != skele->bone_count )
-   {
-      vg_error( "Loaded %u bones out of %u\n", bone_count, skele->bone_count );
-      vg_fatal_exit_loop( "Skeleton setup failed" );
-      skeleton_fatal_err();
-   }
+      if( bone->collider ){
+         if( collider_count == skele->collider_count ){
+            vg_error( "Too many collider bones\n" );
+            skeleton_fatal_err();
+         }
 
-   if( ik_count != skele->ik_count )
-   {
-      vg_error( "Loaded %u ik bones out of %u\n", ik_count, skele->ik_count );
-      skeleton_fatal_err();
+         collider_count ++;
+      }
    }
 
    /* fill in implicit root bone */
@@ -547,14 +494,15 @@ VG_STATIC void skeleton_setup( struct skeleton *skele,
    skele->bones[0].name = "[root]";
    
    /* process animation quick refs */
-   for( int i=0; i<skele->anim_count; i++ )
-   {
-      mdl_animation *anim = &mdl->anim_buffer[ inf->anim_start + i ];
+   for( u32 i=0; i<skele->anim_count; i++ ){
+      mdl_animation *anim = 
+         mdl_arritm( &mdl->animations, armature->anim_start+i );
 
       skele->anims[i].rate       = anim->rate;
       skele->anims[i].length     = anim->length;
       skele->anims[i].name       = mdl_pstr(mdl, anim->pstr_name);
-      skele->anims[i].anim_data  = &mdl->keyframe_buffer[ anim->offset ];
+      skele->anims[i].anim_data  = 
+         mdl_arritm( &mdl->keyframes, anim->offset );
 
       vg_info( "animation[ %f, %u ] '%s'\n", anim->rate,
                                              anim->length,
@@ -568,26 +516,21 @@ VG_STATIC void skeleton_setup( struct skeleton *skele,
 
 VG_STATIC void skeleton_debug( struct skeleton *skele )
 {
-   for( int i=0; i<skele->bone_count; i ++ )
-   {
+   for( u32 i=1; i<skele->bone_count; i ++ ){
       struct skeleton_bone *sb = &skele->bones[i];
 
       v3f p0, p1;
       v3_copy( sb->co, p0 );
       v3_add( p0, sb->end, p1 );
-      //vg_line( p0, p1, 0xffffffff );
 
       m4x3_mulv( skele->final_mtx[i], p0, p0 );
       m4x3_mulv( skele->final_mtx[i], p1, p1 );
 
-      if( sb->flags & k_bone_flag_deform )
-      {
-         if( sb->flags & k_bone_flag_ik )
-         {
+      if( sb->flags & k_bone_flag_deform ){
+         if( sb->flags & k_bone_flag_ik ){
             vg_line( p0, p1, 0xff0000ff );
          }
-         else
-         {
+         else{
             vg_line( p0, p1, 0xffcccccc );
          }
       }
diff --git a/world.h b/world.h
index acfd462d4d2cb1e8746ee081d8ae1bec64fedf81..37ff044910b0c238750ec11e484c870f8f1ebf02 100644 (file)
--- a/world.h
+++ b/world.h
@@ -97,6 +97,7 @@ struct world_instance
 
       float g_water_fog;
       float g_time;
+      float g_realtime;
       float g_shadow_length;
       float g_shadow_spread;
 
@@ -134,24 +135,29 @@ struct world_instance
    /*
     * Main world .mdl 
     */
-   mdl_context *meta;
-
-   /*
-    * Materials / textures 
-    */
+   mdl_context meta;
 
    GLuint *textures;
    u32 texture_count;
 
-   struct world_material
+   struct world_surface
    {
       mdl_material info;
       mdl_submesh sm_geo,
                   sm_no_collide;
    }
-   * materials;
-   u32 material_count;
+   * surfaces;
+   u32 surface_count;
 
+   mdl_array_ptr ent_spawn,
+                 ent_gate,
+                 ent_light,
+                 ent_route_node,
+                 ent_path_index,
+                 ent_checkpoint,
+                 ent_route;
+
+#if 0
    /*
     * Named safe places to respawn
     */
@@ -214,9 +220,6 @@ struct world_instance
       struct classtype_world_light *inf;
       m4x3f inverse_world;
       v2f angle_sin_cos;
-
-      /* enabled.. etc? 
-       * TODO: we should order entities in the binary by their type */
    }
    * lights;
    u32 light_count;
@@ -225,6 +228,7 @@ struct world_instance
     * Routes (world_routes.h)
     * --------------------------------------------------------------------------
     */
+
    struct route_node
    {
       v3f co, right, up, h;
@@ -294,6 +298,7 @@ struct world_instance
    }
    *collectors;
    u32 collector_count;
+#endif
 
 
    /* logic 
@@ -329,15 +334,13 @@ VG_STATIC struct world_global
 
    /* rendering */
    glmesh skydome;
-   mdl_submesh dome_upper, dome_lower;
-
-   glmesh mesh_gate_surface;
+   glmesh mesh_gate;
+   mdl_submesh sm_gate_surface,
+               sm_gate_marker[4];
 
    double sky_time, sky_rate, sky_target_rate;
 
-   /* gates, TODO: active_gate should also know which instance */
-   u32 active_gate,
-       current_run_version;
+   u32 current_run_version;
    double time, rewind_from, rewind_to, last_use;
 
    /* water rendering */
@@ -358,28 +361,6 @@ VG_STATIC struct world_global
    }
    sfd;
 
-   /* timing bars, fixed maximum amount */
-   struct route_ui_bar
-   {
-      GLuint vao, vbo, ebo;
-
-      u32  indices_head;
-      u32  vertex_head;
-
-      struct route_ui_segment
-      {
-         float length;
-         u32 vertex_start, vertex_count,
-             index_start, index_count, notches;
-      }
-      segments[k_max_ui_segments];
-
-      u32 segment_start, segment_count, fade_start, fade_count;
-      double fade_timer_start;
-      float xpos;
-   }
-   ui_bars[16];
-
    v3f render_gate_pos;
    int active_route_board;
    int in_volume;
@@ -405,7 +386,7 @@ VG_STATIC
 int ray_hit_is_ramp( world_instance *world, ray_hit *hit );
 
 VG_STATIC 
-struct world_material *ray_hit_material( world_instance *world, ray_hit *hit );
+struct world_surface *ray_hit_surface( world_instance *world, ray_hit *hit );
 
 VG_STATIC 
 void ray_world_get_tri( world_instance *world, ray_hit *hit, v3f tri[3] );
@@ -454,14 +435,16 @@ VG_STATIC void world_init(void)
    vg_info( "Loading world resources\n" );
    
    vg_linear_clear( vg_mem.scratch );
-   mdl_context *msky = mdl_load_full( vg_mem.scratch, "models/rs_skydome.mdl" );
 
-   mdl_node *nupper = mdl_node_from_name( msky, "dome_complete" );
-   world_global.dome_upper = *mdl_node_submesh( msky, nupper, 0 );
+   mdl_context msky;
+   mdl_open( &msky, "models/rs_skydome.mdl", vg_mem.scratch );
+   mdl_load_metadata_block( &msky, vg_mem.scratch );
+   mdl_load_mesh_block( &msky, vg_mem.scratch );
+   mdl_close( &msky );
 
    vg_acquire_thread_sync();
    {
-      mdl_unpack_glmesh( msky, &world_global.skydome );
+      mdl_unpack_glmesh( &msky, &world_global.skydome );
    }
    vg_release_thread_sync();
 
@@ -469,7 +452,9 @@ VG_STATIC void world_init(void)
    vg_info( "Loading other world systems\n" );
 
    vg_loader_step( world_render_init, NULL );
+#if 0
    vg_loader_step( world_sfd_init, NULL );
+#endif
    vg_loader_step( world_water_init, NULL );
    vg_loader_step( world_gates_init, NULL );
    vg_loader_step( world_routes_init, NULL );
@@ -491,6 +476,7 @@ VG_STATIC void world_update( world_instance *world, v3f pos )
    struct ub_world_lighting *state = &world->ub_lighting;
 
    state->g_time = g_time;
+   state->g_realtime = vg.time;
    state->g_debug_indices = k_debug_light_indices;
    state->g_light_preview = k_light_preview;
    state->g_debug_complexity = k_debug_light_complexity;
@@ -521,12 +507,10 @@ VG_STATIC void world_update( world_instance *world, v3f pos )
                                     vg.time_delta * 5.0 );
 
    world_routes_update( world );
-#if 0
-   world_routes_debug();
-#endif
+   world_routes_debug( world );
    
-   if( world->route_count > 0 )
-   {
+#if 0
+   if( world->route_count > 0 ){
       int closest = 0;
       float min_dist = INFINITY;
 
@@ -563,7 +547,9 @@ VG_STATIC void world_update( world_instance *world, v3f pos )
          }
       }
    }
+#endif
    
+#if 0
    /* TODO: Bvh */
 
    static float random_accum = 0.0f;
@@ -571,8 +557,7 @@ VG_STATIC void world_update( world_instance *world, v3f pos )
 
    u32 random_ticks = 0;
 
-   while( random_accum > 0.1f )
-   {
+   while( random_accum > 0.1f ){
       random_accum -= 0.1f;
       random_ticks ++;
    }
@@ -650,11 +635,12 @@ VG_STATIC void world_update( world_instance *world, v3f pos )
                                                            0xff0000ff );
       }
    }
+#endif
 
+#if 0
    if( k_debug_light_indices )
    {
-      for( int i=0; i<world->light_count; i++ )
-      {
+      for( int i=0; i<world->light_count; i++ ){
          struct world_light *light = &world->lights[i];
          struct classtype_world_light *inf = light->inf;
 
@@ -672,6 +658,9 @@ VG_STATIC void world_update( world_instance *world, v3f pos )
    }
 
    world_global.in_volume = in_volume;
+#endif
+
+#if 0
    sfd_update();
 
    /* process soundscape transactions */
@@ -693,6 +682,7 @@ VG_STATIC void world_update( world_instance *world, v3f pos )
       }
    }
    audio_unlock();
+#endif
 }
 
 /* 
@@ -781,12 +771,12 @@ VG_STATIC int spherecast_world( world_instance *world,
 }
 
 VG_STATIC 
-struct world_material *world_tri_index_material( world_instance *world, 
+struct world_surface *world_tri_index_surface( world_instance *world, 
                                                  u32 index )
 {
-   for( int i=1; i<world->material_count; i++ )
+   for( int i=1; i<world->surface_count; i++ )
    {
-      struct world_material *mat = &world->materials[i];
+      struct world_surface *mat = &world->surfaces[i];
 
       if( (index >= mat->sm_geo.vertex_start) &&
           (index  < mat->sm_geo.vertex_start+mat->sm_geo.vertex_count ) )
@@ -795,20 +785,20 @@ struct world_material *world_tri_index_material( world_instance *world,
       }
    }
 
-   /* error material */
-   return &world->materials[0];
+   /* error surface */
+   return &world->surfaces[0];
 }
 
-VG_STATIC struct world_material *world_contact_material( world_instance *world,
+VG_STATIC struct world_surface *world_contact_surface( world_instance *world,
                                                          rb_ct *ct )
 {
-   return world_tri_index_material( world, ct->element_id );
+   return world_tri_index_surface( world, ct->element_id );
 }
 
-VG_STATIC struct world_material *ray_hit_material( world_instance *world,
-                                                   ray_hit *hit )
+VG_STATIC struct world_surface *ray_hit_surface( world_instance *world,
+                                                 ray_hit *hit )
 {
-   return world_tri_index_material( world, hit->tri[0] );
+   return world_tri_index_surface( world, hit->tri[0] );
 }
 
 #endif /* WORLD_H */
index bd4510b8aad89dd301235cad21b176e95a916d6c..f2a383c1f2856aea16a060f686d9e2f638bb52fa 100644 (file)
@@ -7,13 +7,14 @@
 
 #include "common.h"
 #include "model.h"
+#include "entity.h"
 #include "render.h"
 #include "camera.h"
 
 #include "shaders/model_gate.h"
 #include "world_water.h"
 
-VG_STATIC void gate_transform_update( teleport_gate *gate )
+VG_STATIC void gate_transform_update( ent_gate *gate )
 {
    m4x3f to_local, recv_to_world;
 
@@ -26,7 +27,8 @@ VG_STATIC void gate_transform_update( teleport_gate *gate )
    v3_copy( gate->co[1], recv_to_world[3] );
    m4x3_mul( recv_to_world, to_local, gate->transport );
 
-   m4x3_scalev( gate->to_world, (v3f){ gate->dims[0], gate->dims[1], 1.0f } );
+   m4x3_scalev( gate->to_world, (v3f){ gate->dimensions[0], 
+                                       gate->dimensions[1], 1.0f } );
 }
 
 VG_STATIC void world_gates_init(void)
@@ -36,17 +38,36 @@ VG_STATIC void world_gates_init(void)
    shader_model_gate_register();
 
    vg_linear_clear( vg_mem.scratch );
-   mdl_context *mgate = mdl_load_full( vg_mem.scratch, "models/rs_gate.mdl" );
+
+   mdl_context mgate;
+   mdl_open( &mgate, "models/rs_gate.mdl", vg_mem.scratch );
+   mdl_load_metadata_block( &mgate, vg_mem.scratch );
+   mdl_load_mesh_block( &mgate, vg_mem.scratch );
+
+   mdl_mesh *surface = mdl_find_mesh( &mgate, "rs_gate" );
+   mdl_submesh *sm = mdl_arritm(&mgate.submeshs,surface->submesh_start);
+   world_global.sm_gate_surface = *sm;
+
+   const char *names[] = { "rs_gate_marker", "rs_gate_marker.001", 
+                           "rs_gate_marker.002", "rs_gate_marker.003" };
+
+   for( int i=0; i<4; i++ ){
+      mdl_mesh *marker = mdl_find_mesh( &mgate, names[i] );
+      sm = mdl_arritm( &mgate.submeshs, marker->submesh_start );
+      world_global.sm_gate_marker[i] = *sm;
+   }
+
+   mdl_close( &mgate );
 
    vg_acquire_thread_sync();
    {
-      mdl_unpack_glmesh( mgate, &world_global.mesh_gate_surface );
+      mdl_unpack_glmesh( &mgate, &world_global.mesh_gate );
    }
    vg_release_thread_sync();
 }
 
 VG_STATIC int render_gate( world_instance *world_inside,
-                           teleport_gate *gate, camera *cam )
+                           ent_gate *gate, camera *cam )
 {
    v3f viewdir, gatedir;
    m3x3_mulv( cam->transform, (v3f){0.0f,0.0f,-1.0f}, viewdir );
@@ -109,6 +130,9 @@ VG_STATIC int render_gate( world_instance *world_inside,
       shader_model_gate_uPv( cam->mtx.pv );
       shader_model_gate_uMdl( gate->to_world );
       shader_model_gate_uCam( cam->pos );
+
+      /* TODO(ART IMPROVEMENT): animate alpha of this? */
+      shader_model_gate_uColour( (v4f){0.0f,1.0f,0.0f,0.0f} );
       shader_model_gate_uTime( vg.time*0.25f );
       shader_model_gate_uInvRes( (v2f){
             1.0f / (float)vg.window_x,
@@ -119,8 +143,8 @@ VG_STATIC int render_gate( world_instance *world_inside,
       glStencilFunc( GL_ALWAYS, 1, 0xFF ); 
       glStencilMask( 0xFF );
 
-      mesh_bind( &world_global.mesh_gate_surface );
-      mesh_draw( &world_global.mesh_gate_surface );
+      mesh_bind( &world_global.mesh_gate );
+      mdl_draw_submesh( &world_global.sm_gate_surface );
 
       glClear( GL_DEPTH_BUFFER_BIT );
       glStencilFunc( GL_EQUAL, 1, 0xFF );
@@ -147,8 +171,8 @@ VG_STATIC int render_gate( world_instance *world_inside,
    return 1;
 }
 
-VG_STATIC int gate_intersect_plane( teleport_gate *gate, v3f pos, v3f last,
-                                    v2f where )
+VG_STATIC int gate_intersect_plane( ent_gate *gate, 
+                                    v3f pos, v3f last, v2f where )
 {
    v4f surface;
    q_mulv( gate->q[0], (v3f){0.0f,0.0f,-1.0f}, surface );
@@ -168,11 +192,9 @@ VG_STATIC int gate_intersect_plane( teleport_gate *gate, v3f pos, v3f last,
 
    float d = v3_dot( surface, v0 );
 
-   if( d > 0.00001f )
-   {
+   if( d > 0.00001f ){
       float t = v3_dot(delta, surface) / d;
-      if( t >= 0.0f && t <= l )
-      {
+      if( t >= 0.0f && t <= l ){
          v3f local, rel;
          v3_muladds( last, v0, t, local );
          v3_sub( gate->co[0], local, rel );
@@ -190,14 +212,12 @@ VG_STATIC int gate_intersect_plane( teleport_gate *gate, v3f pos, v3f last,
    return 0;
 }
 
-VG_STATIC int gate_intersect( teleport_gate *gate, v3f pos, v3f last )
+VG_STATIC int gate_intersect( ent_gate *gate, v3f pos, v3f last )
 {
    v2f xy;
 
-   if( gate_intersect_plane( gate, pos, last, xy ) )
-   {
-      if( fabsf(xy[0]) <= 1.0f && fabsf(xy[1]) <= 1.0f )
-      {
+   if( gate_intersect_plane( gate, pos, last, xy ) ){
+      if( fabsf(xy[0]) <= 1.0f && fabsf(xy[1]) <= 1.0f ){
          return 1;
       }
    }
@@ -205,48 +225,23 @@ VG_STATIC int gate_intersect( teleport_gate *gate, v3f pos, v3f last )
    return 0;
 }
 
-struct gate_hit
-{
-   struct nonlocal_gate *nonlocal;
-   struct route_gate    *route;
-   teleport_gate *gate;
-};
-
 /* 
  * Intersect all gates in the world
  */
-VG_STATIC int world_intersect_gates( world_instance *world,
-                                     v3f pos, v3f last, struct gate_hit *hit )
+VG_STATIC ent_gate *world_intersect_gates( world_instance *world,
+                                           v3f pos, v3f last )
 {
-   for( int i=0; i<world->gate_count; i++ )
-   {
-      struct route_gate *rg = &world->gates[i];
-      teleport_gate *gate = &rg->gate;
-
-      if( gate_intersect( gate, pos, last ) )
-      {
-         hit->gate = gate;
-         hit->nonlocal = NULL;
-         hit->route = rg;
-         return 1;
-      }
-   }
+   for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
+      ent_gate *gate = mdl_arritm( &world->ent_gate, i );
+      if( gate->type == k_gate_type_unlinked )
+         continue;
 
-   for( int i=0; i<world->nonlocalgate_count; i++ )
-   {
-      struct nonlocal_gate *nlg = &world->nonlocal_gates[i];
-      teleport_gate *gate = &nlg->gate;
-
-      if( gate_intersect( gate, pos, last ) )
-      {
-         hit->gate = gate;
-         hit->nonlocal = nlg;
-         hit->route = NULL;
-         return 1;
-     }
+      if( gate_intersect( gate, pos, last ) ){
+         return gate;
+      }
    }
 
-   return 0;
+   return NULL;
 }
 
 #endif /* WORLD_GATE_H */
index fdbe9e58ee54aa6ddda195cb26a2aed06028de42..9aaa0f3a7168bf9bb246b5b23709e3a167ad53a8 100644 (file)
@@ -12,17 +12,14 @@ VG_STATIC void world_load( world_instance *world, const char *path );
 VG_STATIC void world_add_all_if_material( m4x3f transform, scene *pscene, 
                                           mdl_context *mdl, u32 id )
 {
-   for( int i=0; i<mdl->info.node_count; i++ )
-   {
-      mdl_node *pnode = mdl_node_from_id( mdl, i );
+   for( u32 i=0; i<mdl_arrcount(&mdl->meshs); i++ ){
+      mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i );
 
-      for( int j=0; j<pnode->submesh_count; j++ )
-      {
-         mdl_submesh *sm = mdl_node_submesh( mdl, pnode, j );
-         if( sm->material_id == id )
-         {
+      for( u32 j=0; j<mesh->submesh_count; j++ ){
+         mdl_submesh *sm = mdl_arritm( &mdl->submeshs, mesh->submesh_start+j );
+         if( sm->material_id == id ){
             m4x3f transform2;
-            mdl_node_transform( pnode, transform2 );
+            mdl_transform_m4x3( &mesh->transform, transform2 );
             m4x3_mul( transform, transform2, transform2 );
 
             scene_add_mdl_submesh( pscene, mdl, sm, transform2 );
@@ -90,25 +87,17 @@ VG_STATIC void world_add_blob( world_instance *world,
 
 /* Sprinkle foliage models over the map on terrain material */
 VG_STATIC void world_apply_procedural_foliage( world_instance *world,
-                                               struct world_material *mat )
+                                               struct world_surface *mat )
 {
    if( vg.quality_profile == k_quality_profile_low )
       return;
 
    vg_info( "Applying foliage (%u)\n", mat->info.pstr_name );
 
-   vg_linear_clear( vg_mem.scratch );
-
-   mdl_context *mfoliage = 
-      mdl_load_full( vg_mem.scratch, "models/rs_foliage.mdl");
-
    v3f volume;
    v3_sub( world->scene_geo->bbx[1], world->scene_geo->bbx[0], volume );
    volume[1] = 1.0f;
 
-   mdl_node *mblob = mdl_node_from_name( mfoliage, "blob" );
-   mdl_submesh *sm_blob = mdl_node_submesh( mfoliage, mblob, 0 );
-
    int count = 0;
 
    float area = volume[0]*volume[2];
@@ -116,8 +105,7 @@ VG_STATIC void world_apply_procedural_foliage( world_instance *world,
 
    vg_info( "Map area: %f. Max particles: %u\n", area, particles );
 
-   for( int i=0;i<particles;i++ )
-   {
+   for( u32 i=0; i<particles; i++ ){
       v3f pos;
       v3_mul( volume, (v3f){ vg_randf(), 1000.0f, vg_randf() }, pos );
       pos[1] = 1000.0f;
@@ -126,11 +114,9 @@ VG_STATIC void world_apply_procedural_foliage( world_instance *world,
       ray_hit hit;
       hit.dist = INFINITY;
 
-      if( ray_world( world, pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
-      {
-         struct world_material *m1 = ray_hit_material( world, &hit );
-         if((hit.normal[1] > 0.8f) && (m1 == mat) && (hit.pos[1] > 0.0f+10.0f))
-         {
+      if( ray_world( world, pos, (v3f){0.0f,-1.0f,0.0f}, &hit )){
+         struct world_surface *m1 = ray_hit_surface( world, &hit );
+         if((hit.normal[1] > 0.8f) && (m1 == mat) && (hit.pos[1] > 0.0f+10.0f)){
             world_add_blob( world, world->scene_no_collide, &hit );
             count ++;
          }
@@ -140,14 +126,20 @@ VG_STATIC void world_apply_procedural_foliage( world_instance *world,
    vg_info( "%d foliage models added\n", count );
 }
 
+#if 0
 VG_STATIC void world_ents_allocate( world_instance *world )
 {
    vg_info( "Allocating entities\n" );
 
-   /* count entites to allocate buffers for them.
-    * maybe in the future we just store these numbers in the model file...
+   /* --count entites to allocate buffers for them.--
+    * --maybe in the future we just store these numbers in the model file...--
+    *
+    *        ... 16.03.23: I HOPE YOUR FUCKING HAPPY
+    *        ... 22.03.23: incomprehensible pain
+    *        
+    * -- use this in world_routes too  --
     *
-    * TODO: use this in world_routes too */
+    */
 
    struct countable
    {
@@ -219,7 +211,9 @@ VG_STATIC void world_ents_allocate( world_instance *world )
                                   world->volume_count,
                                   1 );
 }
+#endif
 
+#if 0
 VG_STATIC void world_pct_spawn( world_instance *world, mdl_node *pnode )
 {
    struct respawn_point *rp = &world->spawns[ world->spawn_count ++ ];
@@ -376,7 +370,9 @@ VG_STATIC void world_link_nonlocal_gates( int index_a, int index_b )
       }
    }
 }
+#endif
 
+#if 0
 VG_STATIC float colour_luminance( v3f v )
 {
    return v3_dot( v, (v3f){0.2126f, 0.7152f, 0.0722f} );
@@ -416,6 +412,7 @@ VG_STATIC float calc_light_influence( world_instance *world, v3f position,
    else
       return 0.0f;
 }
+#endif
 
 VG_STATIC void world_generate( world_instance *world )
 {
@@ -434,15 +431,14 @@ VG_STATIC void world_generate( world_instance *world )
 
    vg_info( "Generating collidable geometry\n" );
    
+   for( u32 i=0; i<world->surface_count; i++ ){
+      struct world_surface *surf = &world->surfaces[ i ];
 
-   for( int i=0; i<world->material_count; i++ )
-   {
-      struct world_material *mat = &world->materials[ i ];
+      if( surf->info.flags & k_material_flag_collision )
+         world_add_all_if_material( midentity, world->scene_geo, 
+                                    &world->meta, i );
 
-      if( mat->info.flags & k_material_flag_collision )
-         world_add_all_if_material( midentity, world->scene_geo, world->meta, i );
-
-      scene_copy_slice( world->scene_geo, &mat->sm_geo );
+      scene_copy_slice( world->scene_geo, &surf->sm_geo );
    }
 
    /* compress that bad boy */
@@ -475,14 +471,12 @@ VG_STATIC void world_generate( world_instance *world )
    world->scene_no_collide = scene_init( world_global.generic_heap, 
                                          200000, 500000 );
 
-   for( int i=0; i<world->material_count; i++ )
-   {
-      struct world_material *mat = &world->materials[ i ];
+   for( u32 i=0; i<world->surface_count; i++ ){
+      struct world_surface *mat = &world->surfaces[ i ];
 
-      if( !(mat->info.flags & k_material_flag_collision) )
-      {
+      if( !(mat->info.flags & k_material_flag_collision) ){
          world_add_all_if_material( midentity, world->scene_no_collide, 
-                                    world->meta, i );
+                                    &world->meta, i );
       }
 
       if( mat->info.flags & k_material_flag_grow_grass )
@@ -530,8 +524,7 @@ VG_STATIC void world_compute_light_indices( world_instance *world )
 
    v3i icubes_min, icubes_max;
 
-   for( int i=0; i<3; i++ )
-   {
+   for( int i=0; i<3; i++ ){
       icubes_min[i] = cubes_min[i];
       icubes_max[i] = cubes_max[i];
    }
@@ -539,8 +532,7 @@ VG_STATIC void world_compute_light_indices( world_instance *world )
    v3i icubes_count;
    v3i_sub( icubes_max, icubes_min, icubes_count );
    
-   for( int i=0; i<3; i++ )
-   {
+   for( int i=0; i<3; i++ ){
       icubes_count[i] = VG_MIN( 128, icubes_count[i]+1 );
       cubes_max[i] = icubes_min[i] + icubes_count[i];
    }
@@ -548,8 +540,7 @@ VG_STATIC void world_compute_light_indices( world_instance *world )
    v3_muls( cubes_min, k_light_cube_size, cubes_min );
    v3_muls( cubes_max, k_light_cube_size, cubes_max );
 
-   for( int i=0; i<3; i++ )
-   {
+   for( int i=0; i<3; i++ ){
       float range = cubes_max[i]-cubes_min[i];
       world->ub_lighting.g_cube_inv_range[i] = 1.0f / range;
       world->ub_lighting.g_cube_inv_range[i] *= (float)icubes_count[i];
@@ -573,12 +564,9 @@ VG_STATIC void world_compute_light_indices( world_instance *world )
             cube_size );
    float bound_radius = v3_length( cube_size );
 
-   for( int iz = 0; iz<icubes_count[2]; iz ++ )
-   {
-      for( int iy = 0; iy<icubes_count[1]; iy++ )
-      {
-         for( int ix = 0; ix<icubes_count[0]; ix++ )
-         {
+   for( int iz = 0; iz<icubes_count[2]; iz ++ ){
+      for( int iy = 0; iy<icubes_count[1]; iy++ ){
+         for( int ix = 0; ix<icubes_count[0]; ix++ ){
             boxf bbx;
             v3_div( (v3f){ ix, iy, iz }, world->ub_lighting.g_cube_inv_range, 
                   bbx[0] );
@@ -599,21 +587,18 @@ VG_STATIC void world_compute_light_indices( world_instance *world )
             float influences[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
             const int N = vg_list_size( influences );
 
-            for( int j=0; j<world->light_count; j ++ )
-            {
-               struct world_light *light = &world->lights[j];
+            for( u32 j=0; j<mdl_arrcount(&world->ent_light); j ++ ){
+               ent_light *light = mdl_arritm( &world->ent_light, j );
                v3f closest;
-               closest_point_aabb( light->node->co, bbx, closest );
+               closest_point_aabb( light->transform.co, bbx, closest );
 
-               float dist = v3_dist( closest, light->node->co ),
+               float dist = v3_dist( closest, light->transform.co ),
                      influence = 1.0f/(dist+1.0f);
 
-               if( dist > light->inf->range )
+               if( dist > light->range )
                   continue;
 
-               if( (light->inf->type == k_light_type_spot) ||
-                   (light->inf->type == k_light_type_spot_nighttime_only) )
-               {
+               if( light->type == k_light_type_spot){
                   v3f local;
                   m4x3_mulv( light->inverse_world, center, local );
 
@@ -628,10 +613,8 @@ VG_STATIC void world_compute_light_indices( world_instance *world )
                   if( influence > influences[k] )
                      best_pos = k;
 
-               if( best_pos < N )
-               {
-                  for( int k=N-1; k>best_pos; k -- )
-                  {
+               if( best_pos < N ){
+                  for( int k=N-1; k>best_pos; k -- ){
                      influences[k] = influences[k-1];
                      indices[k] = indices[k-1];
                   }
@@ -686,6 +669,7 @@ VG_STATIC int reset_player( int argc, char const *argv[] );
 VG_STATIC void world_post_process( world_instance *world )
 {
    /* initialize audio if need be */
+#if 0
    audio_lock();
    for( int i=0; i<world->audio_things_count; i++ )
    {
@@ -704,6 +688,7 @@ VG_STATIC void world_post_process( world_instance *world )
       }
    }
    audio_unlock();
+#endif
 
    world_compute_light_indices( world );
 
@@ -711,8 +696,7 @@ VG_STATIC void world_post_process( world_instance *world )
    {
       /* create scene lighting buffer */
 
-      u32 size = VG_MAX(world->light_count,1) * sizeof(float)*12;
-
+      u32 size = VG_MAX(mdl_arrcount(&world->ent_light),1) * sizeof(float)*12;
       vg_info( "Upload %ubytes (lighting)\n", size );
 
       glGenBuffers( 1, &world->tbo_light_entities );
@@ -728,19 +712,14 @@ VG_STATIC void world_post_process( world_instance *world )
        */
 
       v4f *light_dst = glMapBuffer( GL_TEXTURE_BUFFER, GL_WRITE_ONLY );
-      
-      for( int i=0; i<world->light_count; i++ )
-      {
-         struct world_light *light = &world->lights[i];
-         struct classtype_world_light *inf = light->inf;
+      for( u32 i=0; i<mdl_arrcount(&world->ent_light); i++ ){
+         ent_light *light = mdl_arritm( &world->ent_light, i );
 
          /* colour  + night */
-         v3_muls( inf->colour, inf->colour[3] * 2.0f, light_dst[i*3+0] );
+         v3_muls( light->colour, light->colour[3] * 2.0f, light_dst[i*3+0] );
          light_dst[i*3+0][3] = -1.0f;
 
-         if( (inf->type == k_light_type_spot_nighttime_only) ||
-             (inf->type == k_light_type_point_nighttime_only ) )
-         {
+         if( !light->daytime ){
             u32 hash = (i * 29986577) & 0xff;
             float switch_on = hash;
                   switch_on *= (1.0f/255.0f);
@@ -749,12 +728,12 @@ VG_STATIC void world_post_process( world_instance *world )
          }
          
          /* position + 1/range^2 */
-         v3_copy( light->node->co, light_dst[i*3+1] );
-         light_dst[i*3+1][3] = 1.0f/(inf->range*inf->range);
+         v3_copy( light->transform.co, light_dst[i*3+1] );
+         light_dst[i*3+1][3] = 1.0f/(light->range*light->range);
 
          /* direction + angle */
-         q_mulv( light->node->q, (v3f){0.0f,-1.0f,0.0f}, light_dst[i*3+2]);
-         light_dst[i*3+2][3] = cosf( inf->angle );
+         q_mulv( light->transform.q, (v3f){0.0f,-1.0f,0.0f}, light_dst[i*3+2]);
+         light_dst[i*3+2][3] = cosf( light->angle );
       }
 
       glUnmapBuffer( GL_TEXTURE_BUFFER );
@@ -763,7 +742,6 @@ VG_STATIC void world_post_process( world_instance *world )
       glBindTexture( GL_TEXTURE_BUFFER, world->tex_light_entities );
       glTexBuffer( GL_TEXTURE_BUFFER, GL_RGBA32F, world->tbo_light_entities );
 
-
       /* Upload lighting uniform buffer */
       if( world->water.enabled )
          v4_copy( world->water.plane, world->ub_lighting.g_water_plane );
@@ -777,7 +755,6 @@ VG_STATIC void world_post_process( world_instance *world )
       info_vec[3] = 1.0f/ (bounds[1][2]-bounds[0][2]);
       v4_copy( info_vec, world->ub_lighting.g_depth_bounds );
 
-
       /* 
        * Rendering the depth map
        */
@@ -839,7 +816,10 @@ VG_STATIC void world_post_process( world_instance *world )
 VG_STATIC void world_process_resources( world_instance *world )
 {
    vg_info( "Loading textures\n" );
-   world->texture_count = world->meta->info.texture_count;
+
+   world->texture_count = 0;
+
+   world->texture_count = world->meta.textures.count+1;
    world->textures = vg_linear_alloc( world_global.generic_heap,
                                       sizeof(GLuint)*world->texture_count );
 
@@ -851,21 +831,20 @@ VG_STATIC void world_process_resources( world_instance *world )
       vg_tex2d_nearest();
       vg_tex2d_repeat();
 
-      for( int i=1; i<world->texture_count; i++ )
-      {
-         mdl_texture *tex = &world->meta->texture_buffer[i];
+      for( u32 i=0; i<mdl_arrcount(&world->meta.textures); i++ ){
+         mdl_texture *tex = mdl_arritm( &world->meta.textures, i );
 
-         if( !tex->pack_offset )
-         {
+         if( !tex->file.pack_size ){
             vg_release_thread_sync();
             vg_fatal_exit_loop( "World models must have packed textures!" );
          }
 
          vg_linear_clear( vg_mem.scratch );
-         world->textures[i] = vg_tex2d_new();
+         world->textures[i+1] = vg_tex2d_new();
          vg_tex2d_set_error();
-         vg_tex2d_qoi( world->meta->pack + tex->pack_offset, tex->pack_length,
-                       mdl_pstr( world->meta, tex->pstr_name ));
+         vg_tex2d_qoi( mdl_arritm( &world->meta.pack, tex->file.pack_offset ),
+                       tex->file.pack_size,
+                       mdl_pstr( &world->meta, tex->file.pstr_path ));
          vg_tex2d_nearest();
          vg_tex2d_repeat();
       }
@@ -874,25 +853,18 @@ VG_STATIC void world_process_resources( world_instance *world )
 
    vg_info( "Loading materials\n" );
 
-   u32 size = sizeof(struct world_material) * world->meta->info.material_count;
-   world->materials = vg_linear_alloc( world_global.generic_heap, size );
-                       
-   world->material_count = world->meta->info.material_count;
-   memset( world->materials, 0, size );
-
-   for( int i=1; i<world->material_count; i++ )
-      world->materials[i].info = world->meta->material_buffer[i];
+   world->surface_count = world->meta.materials.count+1;
+   world->surfaces = vg_linear_alloc( world_global.generic_heap,
+                           sizeof(struct world_surface)*world->surface_count );
 
    /* error material */
-   struct world_material *errmat = &world->materials[0];
-   v4_copy( (v4f){ 1.0f,0.0f,0.0f,1.0f }, errmat->info.colour );
-   v4_copy( (v4f){ 1.0f,0.0f,0.0f,1.0f }, errmat->info.colour1 );
-   errmat->info.flags = 0x00;
-   errmat->info.pstr_name = 0; /* useless? */
-   errmat->info.shader = -1;
-   errmat->info.tex_decal = 0;
-   errmat->info.tex_diffuse = 0;
-   errmat->info.tex_normal = 0;
+   struct world_surface *errmat = &world->surfaces[0];
+   memset( errmat, 0, sizeof(struct world_surface) );
+                       
+   for( u32 i=0; i<mdl_arrcount(&world->meta.materials); i++ ){
+      world->surfaces[i+1].info = 
+         *(mdl_material *)mdl_arritm( &world->meta.materials, i );
+   }
 }
 
 VG_STATIC void world_unload( world_instance *world )
@@ -913,21 +885,9 @@ VG_STATIC void world_unload( world_instance *world )
    world_global.rewind_from = 0.0;
    world_global.rewind_to = 0.0;
    world_global.last_use = 0.0;
-   world_global.active_gate = 0;
    world_global.current_run_version = 2;
    world_global.active_route_board = 0;
 
-   for( int i=0; i<vg_list_size(world_global.ui_bars); i++ )
-   {
-      struct route_ui_bar *uib = &world_global.ui_bars[i];
-      uib->segment_start = 0;
-      uib->segment_count = 0;
-      uib->fade_start = 0;
-      uib->fade_count = 0;
-      uib->fade_timer_start = 0.0;
-      uib->xpos = 0.0f;
-   }
-
    /* delete textures and meshes */
    glDeleteTextures( world->texture_count, world->textures );
 
@@ -944,8 +904,7 @@ VG_STATIC void world_unload( world_instance *world )
 
 VG_STATIC void world_clean( world_instance *world )
 {
-   /* clean dangling pointers */
-   world->meta = NULL;
+   memset( &world->meta, 0, sizeof(mdl_context) );
 
    /*
     * TODO: Theres probably a better way to do this? 
@@ -953,8 +912,8 @@ VG_STATIC void world_clean( world_instance *world )
 
    world->textures = NULL;
    world->texture_count = 0;
-   world->materials = NULL;
-   world->material_count = 0;
+   world->surfaces = NULL;
+   world->surface_count = 0;
    
    world->scene_geo = NULL;
    world->scene_no_collide = NULL;
@@ -964,39 +923,8 @@ VG_STATIC void world_clean( world_instance *world )
    world->volume_bh = NULL;
    world->audio_bh = NULL;
 
-   world->spawns = NULL;
-   world->spawn_count = 0;
-
-   world->audio_things = NULL;
-   world->audio_things_count = 0;
-
-   world->volumes = NULL;
-   world->volume_count = 0;
-
-   world->lights = NULL;
-   world->light_count = 0;
-
-   world->nodes = NULL;
-   world->node_count = 0;
-
-   world->routes = NULL;
-   world->route_count = 0;
-
-   world->gates = NULL;
-   world->gate_count = 0;
-
-   world->collectors = NULL;
-   world->collector_count = 0;
-
-   world->soundscapes = NULL;
-   world->soundscape_count = 0;
-
-   world->nonlocal_gates = NULL;
-   world->nonlocalgate_count = 0;
-
    world->water.enabled = 0;
 
-
    /* default lighting conditions 
     * -------------------------------------------------------------*/
    struct ub_world_lighting *state = &world->ub_lighting;
@@ -1019,24 +947,71 @@ VG_STATIC void world_clean( world_instance *world )
    v3_copy( (v3f){1.10f, 0.89f, 0.35f}, state->g_sun_colour );
 }
 
+VG_STATIC void world_entities_init( world_instance *world )
+{
+   /* lights */
+   for( u32 j=0; j<mdl_arrcount(&world->ent_light); j ++ ){
+      ent_light *light = mdl_arritm( &world->ent_light, j );
+
+      m4x3f to_world;
+      q_m3x3( light->transform.q, to_world );
+      v3_copy( light->transform.co, to_world[3] );
+      m4x3_invert_affine( to_world, light->inverse_world );
+
+      light->angle_sin_cos[0] = sinf( light->angle * 0.5f );
+      light->angle_sin_cos[1] = cosf( light->angle * 0.5f );
+   }
+
+   /* gates */
+   for( u32 j=0; j<mdl_arrcount(&world->ent_gate); j ++ ){
+      ent_gate *gate = mdl_arritm( &world->ent_gate, j );
+      gate_transform_update( gate );
+   }
+}
+
 VG_STATIC void world_load( world_instance *world, const char *path )
 {
    world_unload( world );
    world_clean( world );
 
-   world->meta = mdl_load_full( world_global.generic_heap, path );
    vg_info( "Loading world: %s\n", path );
 
+   mdl_open( &world->meta, path, world_global.generic_heap );
+   mdl_load_metadata_block( &world->meta, world_global.generic_heap );
+   mdl_load_animation_block( &world->meta, world_global.generic_heap );
+   mdl_load_mesh_block( &world->meta, world_global.generic_heap );
+   mdl_load_pack_block( &world->meta, world_global.generic_heap );
+
+   mdl_load_array( &world->meta, &world->ent_gate, 
+                   "ent_gate", world_global.generic_heap );
+   mdl_load_array( &world->meta, &world->ent_spawn,
+                   "ent_spawn", world_global.generic_heap );
+   mdl_load_array( &world->meta, &world->ent_light,
+                   "ent_light", world_global.generic_heap );
+
+   mdl_load_array( &world->meta, &world->ent_route_node,
+                   "ent_route_node", world_global.generic_heap );
+   mdl_load_array( &world->meta, &world->ent_path_index,
+                   "ent_path_index", world_global.generic_heap );
+   mdl_load_array( &world->meta, &world->ent_checkpoint,
+                   "ent_checkpoint", world_global.generic_heap );
+   mdl_load_array( &world->meta, &world->ent_route,
+                   "ent_route", world_global.generic_heap );
+
+   mdl_close( &world->meta );
+
    /* process resources from pack */
    world_process_resources( world );
 
+#if 0
    /* dynamic allocations */
    world_ents_allocate( world );
    world_routes_allocate( world );
 
    /* meta processing */
-   world_routes_process( world );
-   world_entities_process( world );
+#endif
+   world_routes_ent_init( world );
+   world_entities_init( world );
    
    /* main bulk */
    world_generate( world );
index 7eccc6e45518d9351722ea994146ee566dd3f745..31eba64401237a5cb48b9a7a79bb9fa4a3020afa 100644 (file)
@@ -113,7 +113,7 @@ VG_STATIC void bind_terrain_noise(void)
 }
 
 typedef void (*func_bind_point)( world_instance *world, 
-                                 struct world_material *mat );
+                                 struct world_surface *mat );
 
 VG_STATIC void world_render_if( world_instance *world,
                                 enum mdl_shader shader, 
@@ -121,9 +121,9 @@ VG_STATIC void world_render_if( world_instance *world,
                                 func_bind_point bind_point )
 {
    
-   for( int i=0; i<world->material_count; i++ )
+   for( int i=0; i<world->surface_count; i++ )
    {
-      struct world_material *mat = &world->materials[i];
+      struct world_surface *mat = &world->surfaces[i];
 
       if( mat->info.shader == shader )
       {
@@ -158,7 +158,7 @@ void world_render_both_stages(  world_instance *world,
 }
 
 VG_STATIC void bindpoint_diffuse_texture1( world_instance *world,
-                                           struct world_material *mat )
+                                           struct world_surface *mat )
 {
    glActiveTexture( GL_TEXTURE1 );
    glBindTexture( GL_TEXTURE_2D, world->textures[ mat->info.tex_diffuse ] );
@@ -259,7 +259,7 @@ VG_STATIC void render_world_alphatest( world_instance *world, camera *cam )
 }
 
 VG_STATIC void bindpoint_terrain( world_instance *world,
-                                  struct world_material *mat )
+                                  struct world_surface *mat )
 {
    glActiveTexture( GL_TEXTURE1 );
    glBindTexture( GL_TEXTURE_2D, world->textures[ mat->info.tex_diffuse ] );
@@ -340,7 +340,7 @@ VG_STATIC void render_sky( world_instance *world, camera *cam )
    glDisable( GL_DEPTH_TEST );
 
    mesh_bind( &world_global.skydome );
-   mdl_draw_submesh( &world_global.dome_upper );
+   mesh_draw( &world_global.skydome );
    
    glEnable( GL_DEPTH_TEST );
    glDepthMask( GL_TRUE );
@@ -350,48 +350,32 @@ VG_STATIC void render_world_gates( world_instance *world, camera *cam )
 {
    float closest = INFINITY;
 
-   struct teleport_gate *gate = NULL;
-   world_instance *dest_world = world;
+   struct ent_gate *gate = NULL;
 
-   for( int i=0; i<world->gate_count; i++ )
-   {
-      struct route_gate *rg = &world->gates[i];
-      float dist = v3_dist2( rg->gate.co[0], cam->transform[3] );
-
-      vg_line_pt3( rg->gate.co[0], 0.25f, VG__BLUE );
-
-      if( dist < closest )
-      {
-         closest = dist;
-         gate = &rg->gate;
-         dest_world = world;
-      }
-   }
-
-   for( int i=0; i<world->nonlocalgate_count; i++ )
-   {
-      struct nonlocal_gate *nlg = &world->nonlocal_gates[i];
+   for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
+      ent_gate *gi = mdl_arritm( &world->ent_gate, i );
 
-      if( !nlg->working )
-      {
-         vg_line_pt3( nlg->gate.co[0], 0.25f, VG__RED );
+      if( gi->type == k_gate_type_unlinked )
          continue;
-      }
-      else
-         vg_line_pt3( nlg->gate.co[0], 0.25f, VG__GREEN );
 
-      float dist = v3_dist2( nlg->gate.co[0], cam->transform[3] );
+      float dist = v3_dist2( gi->co[0], cam->transform[3] );
 
-      if( dist < closest )
-      {
+      vg_line_pt3( gi->co[0], 0.25f, VG__BLUE );
+
+      if( dist < closest ){
          closest = dist;
-         gate = &nlg->gate;
-         dest_world = &world_global.worlds[ nlg->target_map_index ];
+         gate = gi;
       }
    }
-
-   if( gate )
+   
+   if( gate ){
+#if 0
+      world_instance *dest_world = &world_global.worlds[ gate->world_index ];
       render_gate( dest_world, gate, cam );
+#else
+      render_gate( world, gate, cam );
+#endif
+   }
 }
 
 VG_STATIC void render_world( world_instance *world, camera *cam )
@@ -405,6 +389,7 @@ VG_STATIC void render_world( world_instance *world, camera *cam )
    render_terrain( world, cam );
 
    /* Render SFD's */
+#if 0
    int closest = 0;
    float min_dist = INFINITY;
 
@@ -423,6 +408,7 @@ VG_STATIC void render_world( world_instance *world, camera *cam )
    }
 
    sfd_render( cam, world->routes[closest].scoreboard_transform );
+#endif
 }
 
 VG_STATIC void render_world_depth( world_instance *world, camera *cam )
index 501915a0643859d812a41dd31d87a6cffd110ddc..b43ec0ee1fe63762acd9103971002f2e70906490 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
+ * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
  */
 
 #ifndef ROUTES_H
 #include "shaders/routeui.h"
 
 
-enum route_special_type
-{
-   k_route_special_type_none = 0,
-   k_route_special_type_gate = 1,
-   k_route_special_type_collector = 2
-};
-
-VG_STATIC void debug_sbpath( struct route_node *rna, struct route_node *rnb,
-                          u32 colour, float xoffset )
-{
-   v3f p0, h0, p1, h1, l, p;
-   
-   v3_copy( rna->co, p0 );
-   v3_muladds( rna->co, rna->h,  1.0f, h0 );
-   v3_copy( rnb->co, p1 );
-   v3_muladds( rnb->co, rnb->h, -1.0f, h1 );
-
-   v3_muladds( p0, rna->right, xoffset, p0 );
-   v3_muladds( h0, rna->right, xoffset, h0 );
-   v3_muladds( p1, rnb->right, xoffset, p1 );
-   v3_muladds( h1, rnb->right, xoffset, h1 );
-
-   v3_copy( p0, l );
-
-   for( int i=0; i<5; i++ )
-   {
-      float t = (float)(i+1)/5.0f;
-      eval_bezier_time( p0, p1, h0, h1, t, p );
-      vg_line( p, l, colour );
-      v3_copy( p, l );
-   }
-}
-
-/*
- * Get a list of node ids in stack, and return how many there is
- */
-VG_STATIC u32 world_routes_get_path( world_instance *world,
-                                     u32 starter, u32 stack[64] )
-{
-   u32 stack_i[64];
-
-   stack[0] = starter;
-   stack_i[0] = 0;
-
-   u32 si = 1;
-   int loop_complete = 0;
-
-   while( si )
-   {
-      if( stack_i[si-1] == 2 )
-      {
-         si --;
-         continue;
-      }
-
-      struct route_node *rn = &world->nodes[stack[si-1]];
-      u32 nextid = rn->next[stack_i[si-1]];
-      stack_i[si-1] ++;
-
-      if( nextid != 0xffffffff )
-      {
-         if( nextid == stack[0] )
-         {
-            loop_complete = 1;
-            break;
-         }
-
-         int valid = 1;
-         for( int sj=0; sj<si; sj++ )
-         {
-            if( stack[sj] == nextid )
-            {
-               valid = 0;
-               break;
-            }
-         }
-
-         if( valid )
-         {
-            stack_i[si] = 0;
-            stack[si] = nextid;
-            si ++;
-            continue;
-         }
-      }
-   }
-
-   if( loop_complete )
-      return si;
-
-   return 0;
-}
-
-/*
- * Free a segment from the UI bar to be reused later
- */
-VG_STATIC void world_routes_ui_popfirst( struct route_ui_bar *pui )
-{
-   if( pui->segment_count )
-   {
-      pui->segment_start ++;
-
-      if( pui->segment_start == 32 )
-         pui->segment_start = 0;
-
-      pui->segment_count --;
-   }
-}
-
-/*
- * Reset ui bar completely
- */
-VG_STATIC void world_routes_ui_clear( struct route_ui_bar *pui )
-{
-   pui->segment_start = (pui->segment_start + pui->segment_count) %
-                                                            k_max_ui_segments;
-   pui->segment_count = 0;
-}
-
-/*
- * Break a index range into two pieces over the edge of the maximum it can
- * store. s1 is 0 always, so its a ring buffer.
- */
-VG_STATIC void world_routes_ui_split_indices( u32 s0, u32 count, 
-                                              u32 *c0, u32 *c1 )
-{
-   *c0 = (VG_MIN( s0+count, k_route_ui_max_indices )) - s0;
-   *c1 = count-(*c0);
-}
-
-/*
- * Place a set of indices into gpu array automatically splits
- * across bounds
- */
-VG_STATIC void world_routes_ui_set_indices( struct route_ui_bar *pui, 
-                                         u16 *indices, u32 count )
-{
-   u32 c0, c1;
-   world_routes_ui_split_indices( pui->indices_head, count, &c0, &c1 );
-
-   glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, pui->ebo );
-
-   if( c0 )
-   {
-      glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, pui->indices_head*sizeof(u16),
-            c0*sizeof(u16), indices );
-   }
-
-   if( c1 )
-   {
-      glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0, c1*sizeof(u16), indices+c0 );
-      pui->indices_head = c1;
-   }
-   else
-      pui->indices_head += c0;
-}
-
-/*
- * Place a set of vertices into gpu array 
- */
-VG_STATIC u32 world_routes_ui_set_verts( struct route_ui_bar *pui,
-                                      v2f *verts, u32 count )
-{
-   if( pui->vertex_head + count >= k_route_ui_max_verts )
-      pui->vertex_head = 0;
-
-   u32 vert_start = pui->vertex_head;
-   pui->vertex_head += count;
-
-   glBindBuffer( GL_ARRAY_BUFFER, pui->vbo );
-   glBufferSubData( GL_ARRAY_BUFFER, (GLintptr)(vert_start*sizeof(v2f)),
-                        sizeof(v2f)*count, verts );
-
-   return vert_start;
-}
-
-/*
- * Update the last (count) vertices positions, does not add any.
- * Data must already be written to, and not cross either array boundaries.
- */
-VG_STATIC u32 world_routes_ui_update_verts( struct route_ui_bar *pui,
-                                         v2f *verts, u32 count )
-{
-   u32 vert_start = pui->vertex_head-count;
-
-   glBindBuffer( GL_ARRAY_BUFFER, pui->vbo );
-   glBufferSubData( GL_ARRAY_BUFFER, (GLintptr)(vert_start*sizeof(v2f)),
-                        sizeof(v2f)*count, verts );
-
-   return vert_start;
-}
-
-/* 
- * Current/active segment of this UI bar 
- */
-VG_STATIC struct route_ui_segment *world_routes_ui_curseg( 
-      struct route_ui_bar *pui )
-{
-   u32 index = (pui->segment_start+pui->segment_count-1)%k_max_ui_segments;
-   return &pui->segments[ index ];
-}
-
-/*
- * Start a new segment in the UI bar, will create a split on the last one if
- * there is one active currently. (api)
- */
-VG_STATIC void world_routes_ui_newseg( u32 route )
-{
-   struct route_ui_bar *pui = &world_global.ui_bars[route];
-   
-   glBindVertexArray( pui->vao );
-   if( pui->segment_count )
-   {
-      float const k_gap_width = 1.0f;
-
-      struct route_ui_segment *cseg = world_routes_ui_curseg( pui );
-      
-      v2f verts[2];
-      verts[0][0] =  cseg->length-k_gap_width;
-      verts[0][1] =  0.5f;
-      verts[1][0] =  cseg->length-k_gap_width;
-      verts[1][1] = -0.5f;
-
-      world_routes_ui_update_verts( pui, verts, 2 );
-   }
-
-   pui->segment_count ++;
-   struct route_ui_segment *segment = world_routes_ui_curseg( pui );
-   
-   v2f verts[4];
-   verts[0][0] =  0.0f;
-   verts[0][1] =  0.5f;
-   verts[1][0] =  0.0f;
-   verts[1][1] = -0.5f;
-   verts[2][0] =  0.0f;
-   verts[2][1] =  0.5f;
-   verts[3][0] =  0.0f;
-   verts[3][1] = -0.5f;
-
-   u32 vert_start = world_routes_ui_set_verts( pui, verts, 4 );
-
-   u16 indices[6];
-   indices[0] = vert_start + 0;
-   indices[1] = vert_start + 1;
-   indices[2] = vert_start + 3;
-   indices[3] = vert_start + 0;
-   indices[4] = vert_start + 3;
-   indices[5] = vert_start + 2;
-
-   segment->vertex_start = vert_start;
-   segment->vertex_count = 4;
-   segment->index_start  = pui->indices_head;
-   segment->index_count  = 6;
-   segment->notches      = 0;
-
-   world_routes_ui_set_indices( pui, indices, 6 );
-}
-
-/*
- * Extend the end of the bar 
- */
-VG_STATIC void world_routes_ui_updatetime( u32 route, float time )
-{
-   struct route_ui_bar *pui = &world_global.ui_bars[route];
-
-   v2f verts[2];
-   verts[0][0] =  time;
-   verts[0][1] =  0.5f;
-   verts[1][0] =  time;
-   verts[1][1] = -0.5f;
-
-   u32 vert_start = pui->vertex_head-2;
-
-   glBindVertexArray( pui->vao );
-   world_routes_ui_update_verts( pui, verts, 2 );
-
-   struct route_ui_segment *cseg = world_routes_ui_curseg( pui );
-   cseg->length = time;
-}
-
-VG_STATIC void world_routes_ui_draw_segment( struct route_ui_segment *segment )
-{
-   u32 c0, c1;
-   world_routes_ui_split_indices( segment->index_start, 
-                                  segment->index_count, &c0, &c1 );
-   if( c0 )
-      glDrawElements( GL_TRIANGLES, c0, GL_UNSIGNED_SHORT,
-                        (void *)(segment->index_start*sizeof(u16)));
-   if( c1 )
-      glDrawElements( GL_TRIANGLES, c1, GL_UNSIGNED_SHORT, (void *)(0) );
-}
-
-/*
- * Draws full bar at Y offset(offset).
- */
-VG_STATIC void world_routes_ui_draw( world_instance *world,
-                                     u32 route, v4f colour, float offset )
-{
-   float const k_bar_height = 0.05f,
-               k_bar_scale_x = 0.005f;
-
-   /* FIXME(10) ID mishmatch */
-   struct route *pr = &world->routes[route];
-   struct route_ui_bar *pui = &world_global.ui_bars[route];
-
-   float cx = pui->xpos;
-
-   shader_routeui_use();
-   glBindVertexArray( pui->vao );
-
-   float fade_amt = world_global.time - pui->fade_timer_start;
-   fade_amt = vg_clampf( fade_amt / 1.0f, 0.0f, 1.0f );
-   
-   float fade_block_size = 0.0f,
-         main_block_size = 0.0f;
-
-   for( u32 i=0; i<pui->fade_count; i++ )
-   {
-      u32 j = (pui->fade_start + i) % k_max_ui_segments;
-      struct route_ui_segment *segment = &pui->segments[j];
-
-      fade_block_size += segment->length;
-   }
-
-   cx -= fade_block_size * fade_amt;
-
-   v4f fade_colour;
-   v4_copy( colour, fade_colour );
-   fade_colour[3] *= 1.0f-fade_amt;
-
-   /* 1 minute timer */
-   float timer_delta = (world_global.time - world_global.last_use) * (1.0/45.0),
-         timer_scale = 1.0f - vg_minf( timer_delta, 1.0f );
-
-   /* 
-    * Draw fadeout bar
-    */
-
-   float height = pr->factive*k_bar_height * timer_scale,
-         base = -1.0f + (offset+0.5f)*k_bar_height * timer_scale;
-
-   shader_routeui_uColour( fade_colour );
-   for( u32 i=0; i<pui->fade_count; i++ )
-   {
-      u32 j = (pui->fade_start + i) % k_max_ui_segments;
-      struct route_ui_segment *segment = &pui->segments[j];
-
-      shader_routeui_uOffset( (v4f){ cx*k_bar_scale_x, base,
-                                     k_bar_scale_x, height } );
-
-      world_routes_ui_draw_segment( segment );
-      cx += segment->length;
-   }
-
-   /*
-    * Draw main bar 
-    */
-   shader_routeui_uColour( colour );
-   for( u32 i=0; i<pui->segment_count; i++ )
-   {
-      u32 j = (pui->segment_start + i) % k_max_ui_segments;
-      struct route_ui_segment *segment = &pui->segments[j];
-
-      shader_routeui_uOffset( (v4f){ cx*k_bar_scale_x, base,
-                                     k_bar_scale_x, height } );
-
-      world_routes_ui_draw_segment( segment );
-      cx += segment->length;
-
-      main_block_size += segment->length;
-   }
-
-   pui->xpos = vg_lerpf( pui->xpos, -main_block_size * 0.5f, 0.03f );
-}
-
 VG_STATIC void world_routes_local_set_record( world_instance *world,
                                               u32 route, double lap_time )
 {
+#if 0
    vg_success( "  NEW LAP TIME: %f\n", lap_time );
 
-   /* FIXME(10): ID mishmatch */
-   struct route *pr = &world->routes[route];
-
    if( pr->track_id != 0xffffffff )
    {
       double time_centiseconds = lap_time * 100.0;
@@ -428,125 +51,18 @@ VG_STATIC void world_routes_local_set_record( world_instance *world,
    {
       vg_warn( "There is no associated track for this record...\n" );
    }
+#endif
+   vg_warn( "set_record unimplemented\n" );
 }
 
-/* 
- * Will scan the whole run for two things;
- *   1: we set a new record for the total, complete loop around the course
- *   2: the time of each segment will be recorded into the data buffer
- *       (not implemented: TODO)
- */
-VG_STATIC void world_routes_verify_run( world_instance *world, u32 route )
-{
-   /* FIXME(10): ID mishmatch */
-   struct route *pr = &world->routes[route];
-   struct route_ui_bar *pui = &world_global.ui_bars[route];
-
-   u32 stack[64];
-   u32 si = world_routes_get_path( world, world->routes[route].start, stack );
-
-   /* 
-    * we only care about gates that ref gates, so shuffle down the array
-    */
-   struct route_timing *timings[64];
-   u32 sj = 0, maxv = 0, begin = 0;
-   for( u32 i=0; i<si; i++ )
-   {
-      struct route_node *inode = &world->nodes[stack[i]];
-
-      if( inode->special_type == k_route_special_type_collector )
-      {
-         timings[sj ++] = &world->collectors[ inode->special_id ].timing;
-      }
-      else if( inode->special_type == k_route_special_type_gate )
-      {
-         timings[sj ++] = &world->gates[inode->special_id].timing;
-      }
-   }
-   
-   for( u32 i=0; i<sj; i++ )
-   {
-      if( timings[i]->version > maxv )
-      {
-         maxv = timings[i]->version;
-         begin = i;
-      }
-   }
-
-   vg_info( "== begin verification (%u) ==\n", route );
-   vg_info( "  current version: %u\n", world_global.current_run_version );
-
-   int verified = 0;
-   if( timings[begin]->version == world_global.current_run_version )
-      verified = 1;
-
-   int valid_segment_count = 0;
-
-   double lap_time = 0.0;
-
-   for( u32 i=0; i<sj; i++ )
-   {
-      u32  j = (sj+begin-i-1) % sj,
-          j1 = (j+1) % sj;
-
-      double diff = 0.0;
-   
-      if( i<sj-1 )
-      {
-         /* j1v should equal jv+1 */
-         if( timings[j1]->version == timings[j]->version+1 )
-         {
-            diff = timings[j1]->time - timings[j]->time;
-            lap_time += diff;
-
-            if( verified && diff > 0.0 ) valid_segment_count ++;
-         }
-         else
-            verified = 0;
-      }
-      
-      if( verified )
-         vg_success( " [ %u %f ] %f\n", timings[j1]->time, 
-                                        timings[j1]->version, diff );
-      else
-         vg_warn( " [ %u %f ]\n", timings[j1]->time, timings[j1]->version );
-   }
-
-   pui->fade_start = pui->segment_start;
-   pui->fade_count = 0;
-   pui->fade_timer_start = world_global.time;
-
-   int orig_seg_count = pui->segment_count;
-
-   world_routes_ui_newseg( route );
-
-   if( verified )
-   {
-      world_routes_local_set_record( world, route, lap_time );
-      world_routes_ui_popfirst( pui );
-      pui->fade_count ++;
-   }
-   else
-      vg_info( "  ctime: %f\n", lap_time );
-
-   /* remove any excess we had from previous runs */
-   int to_remove = orig_seg_count-valid_segment_count;
-   for( int i=0; i<to_remove; i++ )
-   {
-      world_routes_ui_popfirst( pui );
-      pui->fade_count ++;
-   }
-
-   world->routes[route].latest_pass = world_global.time;
-}
 
 VG_STATIC void world_routes_clear( world_instance *world )
 {
-   for( u32 i=0; i<world->route_count; i++ )
-   {
-      struct route *route = &world->routes[i];
-      route->active = 0;
+   for( u32 i=0; i<mdl_arrcount( &world->ent_route ); i++ ){
+      ent_route *route = mdl_arritm( &world->ent_route, i );
+      route->active_checkpoint = 0xffffffff;
    }
+
    world_global.current_run_version += 4;
    world_global.last_use = 0.0;
 }
@@ -554,522 +70,415 @@ VG_STATIC void world_routes_clear( world_instance *world )
 /*
  * When going through a gate this is called for bookkeeping purposes
  */
-VG_STATIC void world_routes_activate_gate( world_instance *world, u32 id )
+VG_STATIC void world_routes_activate_entry_gate( world_instance *world, 
+                                                 ent_gate *rg )
 {
-   struct route_gate *rg = &world->gates[id];
-   struct route_node *pnode = &world->nodes[rg->node_id],
-                     *pdest = &world->nodes[pnode->next[0]];
+   ent_gate *dest = mdl_arritm( &world->ent_gate, rg->target );
 
    world_global.last_use = world_global.time;
-
-   struct route_collector *rc = &world->collectors[ pdest->special_id ];
-
-   world_global.active_gate = id;
-   rg->timing.version = world_global.current_run_version;
-   rg->timing.time = world_global.time;
-
-   for( u32 i=0; i<world->route_count; i++ )
-   {
-      struct route *route = &world->routes[i];
-      
-      int was_active = route->active;
-
-      route->active = 0;
-      for( u32 j=0; j<pdest->ref_count; j++ )
-      {
-         if( pdest->route_ids[j] == i )
-         {
-            world_routes_verify_run( world, i );
-            route->active = 1;
+   rg->timing_version = world_global.current_run_version;
+   rg->timing_time = world_global.time;
+
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+      ent_route *route = mdl_arritm( &world->ent_route, i );
+
+      u32 active_prev = route->active_checkpoint;
+      route->active_checkpoint = 0xffffffff;
+
+      for( u32 j=0; j<4; j++ ){
+         if( dest->routes[j] == i ){
+            for( u32 k=0; k<route->checkpoints_count; k++ ){
+               ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, 
+                                                 route->checkpoints_start+k );
+
+               ent_gate *gk = mdl_arritm( &world->ent_gate, cp->gate_index );
+               if( gk == rg ){
+                  route->active_checkpoint = k;
+                  break;
+               }
+            }
             break;
          }
       }
-
-      if( was_active && !route->active )
-      {
-         struct route_ui_bar *pui = &world_global.ui_bars[i];
-         pui->fade_start = pui->segment_start;
-         pui->fade_count = pui->segment_count;
-         pui->fade_timer_start = world_global.time;
-
-         world_routes_ui_clear( pui );
-         vg_success( "CLEARING -> %u %u \n", pui->fade_start,
-                                             pui->fade_count );
-      }
    }
    
    world_global.current_run_version ++;
 
-   rc->timing.version = world_global.current_run_version;
-   rc->timing.time = world_global.time;
+   dest->timing_version = world_global.current_run_version;
+   dest->timing_time = world_global.time;
    world_global.current_run_version ++;
 }
 
-/*
- * Notify the UI system that we've reset the player
- */
-VG_STATIC void world_routes_notify_reset(void)
-{
-   world_global.rewind_from = world_global.time;
-   world_global.rewind_to = world_global.last_use;
-}
-
-/* Rewind between the saved points in time */
-VG_STATIC void world_routes_rollback_time( double t )
-{
-   world_global.time = vg_lerp( world_global.rewind_to, 
-                                world_global.rewind_from, t );
-}
-
 /* draw lines along the paths */
 VG_STATIC void world_routes_debug( world_instance *world )
 {
-   for( int i=0; i<world->node_count; i++ )
-   {
-      struct route_node *rn = &world->nodes[i];
-      vg_line_pt3( rn->co, 1.0f, rn->special_type? 0xffffff00: 0xff00b2ff );
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route_node); i++ ){
+      ent_route_node *rn = mdl_arritm(&world->ent_route_node,i);
+      vg_line_pt3( rn->co, 0.25f, VG__WHITE );
    }
 
-   for( int i=0; i<world->route_count; i++ )
-   {
-      struct route *route = &world->routes[i];
-
-      u32 stack[64];
-      u32 si = world_routes_get_path( world, route->start, stack );
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+      ent_route *route = mdl_arritm(&world->ent_route, i);
 
       u32 colours[] = { 0xfff58142, 0xff42cbf5, 0xff42f56c, 0xfff542b3,
                         0xff5442f5 };
 
-      u32 cc = colours[i%vg_list_size(colours)];
+      u32 cc = 0xffcccccc;
+      if( route->active_checkpoint != 0xffffffff ){
+         cc = colours[i%vg_list_size(colours)];
+      }
 
-      for( int sj=0; sj<si; sj++ )
-      {
-         int sk = (sj+1)%si;
+      for( int i=0; i<route->checkpoints_count; i++ ){
+         int i0 = route->checkpoints_start+i,
+             i1 = route->checkpoints_start+((i+1)%route->checkpoints_count);
 
-         struct route_node *pj = &world->nodes[stack[sj]],
-                           *pk = &world->nodes[stack[sk]];
-         debug_sbpath( pj, pk, cc, (float)i );
-      }
-   }
+         ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0),
+                        *c1 = mdl_arritm(&world->ent_checkpoint, i1);
 
-   for( int i=0; i<world->node_count; i++ )
-   {
-      struct route_node *ri = &world->nodes[i],
-                        *rj = NULL;
-      
-      for( int j=0; j<2; j++ )
-      {
-         if( ri->next[j] != 0xffffffff )
-         {
-            rj = &world->nodes[ri->next[j]];
-            vg_line( ri->co, rj->co, 0x20ffffff );
+         ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index );
+         ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index );
+
+         v3f p0, p1;
+         v3_copy( start_gate->co[1], p0 );
+
+         for( int j=0; j<c0->path_count; j ++ ){
+            ent_path_index *index = mdl_arritm( &world->ent_path_index, 
+                                                c0->path_start+j );
+
+            ent_route_node *rn = mdl_arritm( &world->ent_route_node,
+                                             index->index );
+
+            v3_copy( rn->co, p1 );
+            vg_line( p0, p1, cc );
+            v3_copy( p1, p0 );
          }
+
+         v3_copy( end_gate->co[0], p1 );
+         vg_line( p0, p1, cc );
       }
    }
 }
 
-VG_STATIC void world_routes_create_mesh( world_instance *world, u32 route_id )
+VG_STATIC void world_routes_place_curve( world_instance *world,
+                                         v4f h[4], v3f n0, v3f n2 )
 {
-   struct route *route = &world->routes[ route_id ];
+   float t;
+   v3f p, pd;
+   int last_valid;
 
-   u32 stack[64];
-   u32 si = world_routes_get_path( world, route->start, stack );
+   float total_length = 0.0f,
+         travel_length = 0.0;
 
-   u32 last_valid = 0;
-
-   for( int sj=0; sj<si; sj++ )
-   {
-      int sk=(sj+1)%si;
-
-      struct route_node *rnj = &world->nodes[ stack[sj] ],
-                        *rnk = &world->nodes[ stack[sk] ],
-                        *rnl;
-      
-      if( rnj->special_type && rnk->special_type )
-      {
-         last_valid = 0;
-         continue;
-      }
+   v3f last;
+   v3_copy( h[0], last );
+   for( int it=0; it<128; it ++ ){
+      t = (float)(it+1) * (1.0f/128.0f);
+      eval_bezier3( h[0], h[1], h[2], t, p );
+      total_length += v3_dist( p, last );
+      v3_copy( p, last );
+   }
 
-      float base_x0 = (float)rnj->ref_count*-0.5f + (float)rnj->current_refs,
-            base_x1 = (float)rnk->ref_count*-0.5f + (float)rnk->current_refs;
+   float patch_size = 4.0f,
+         patch_count = ceilf( total_length / patch_size );
 
-      if( rnk->special_type )
-      {
-         rnl = &world->nodes[ rnk->next[0] ];
-         base_x1 = (float)rnl->ref_count*-0.5f + (float)rnl->current_refs;
-      }
+   t = 0.0f;
+   v3_copy( h[0], last );
 
-      if( sk == 0 )
-      {
-         base_x1 -= 1.0f;
-      }
+   for( int it=0; it<128; it ++ ){
+      float const k_sample_dist = 0.0025f,
+                  k_line_width = 1.5f;
 
-      v3f p0, h0, p1, h1, p, pd;
-      
-      v3_copy( rnj->co, p0 );
-      v3_muladds( rnj->co, rnj->h,  1.0f, h0 );
-      v3_copy( rnk->co, p1 );
-      v3_muladds( rnk->co, rnk->h, -1.0f, h1 );
+      eval_bezier3( h[0], h[1], h[2], t, p );
+      eval_bezier3( h[0], h[1], h[2], t+k_sample_dist, pd );
 
-      float t=0.0f;
-      int it = 0;
+      travel_length += v3_dist( p, last );
 
-      for( int it=0; it<256; it ++ )
-      {
-         float const k_sample_dist = 0.02f;
-         eval_bezier_time( p0,p1,h0,h1, t,p );
-         eval_bezier_time( p0,p1,h0,h1, t+k_sample_dist,pd );
+      float mod = k_sample_dist / v3_dist( p, pd );
 
-         float mod = k_sample_dist / v3_dist( p, pd );
+      v3f v0,up, right;
 
-         v3f v0,up, right;
-         v3_muls( rnj->up, 1.0f-t, up );
-         v3_muladds( up, rnk->up, t, up );
+      v3_muls( n0, -(1.0f-t), up );
+      v3_muladds( up, n2, -t, up );
+      v3_normalize( up );
 
-         v3_sub( pd,p,v0 );
-         v3_cross( up, v0, right );
-         v3_normalize( right );
+      v3_sub( pd,p,v0 );
+      v3_cross( up, v0, right );
+      v3_normalize( right );
 
-         float cur_x = (1.0f-t)*base_x0 + t*base_x1;
-         
-         v3f sc, sa, sb, down;
-         v3_muladds( p, right, cur_x, sc );
-         v3_muladds( sc, up, 1.5f, sc );
-         v3_muladds( sc, right,  0.45f, sa );
-         v3_muladds( sc, right, -0.45f, sb );
-         v3_muls( up, -1.0f, down );
+      float cur_x = (1.0f-t)*h[0][3] + t*h[2][3];
+      
+      v3f sc, sa, sb, down;
+      v3_muladds( p, right, cur_x * k_line_width, sc );
+      v3_muladds( sc, up, 1.5f, sc );
+      v3_muladds( sc, right, k_line_width*0.95f, sa );
+      v3_muladds( sc, right, 0.0f, sb );
+      v3_muls( up, -1.0f, down );
+      
+      ray_hit ha, hb;
+      ha.dist = 8.0f;
+      hb.dist = 8.0f;
+      if( ray_world( world, sa, down, &ha ) && 
+          ray_world( world, sb, down, &hb ))
+      {
+         scene_vert va, vb;
          
-         ray_hit ha, hb;
-         ha.dist = 8.0f;
-         hb.dist = 8.0f;
-         if( ray_world( world, sa, down, &ha ) && 
-             ray_world( world, sb, down, &hb ))
-         {
-            scene_vert va, vb;
-            
-            v3_muladds( ha.pos, up, 0.06f, va.co );
-            v3_muladds( hb.pos, up, 0.06f, vb.co );
-
-            scene_vert_pack_norm( &va, up );
-            scene_vert_pack_norm( &vb, up );
-            v2_zero( va.uv );
-            v2_zero( vb.uv );
-
-            scene_push_vert( world->scene_lines, &va );
-            scene_push_vert( world->scene_lines, &vb );
-
-            if( last_valid )
-            {
-               /* Connect them with triangles */
-               scene_push_tri( world->scene_lines, (u32[3]){ 
-                     last_valid+0-2, last_valid+1-2, last_valid+2-2} );
-               scene_push_tri( world->scene_lines, (u32[3]){ 
-                     last_valid+1-2, last_valid+3-2, last_valid+2-2} );
-            }
-            
-            last_valid = world->scene_lines->vertex_count;
+         v3_muladds( ha.pos, up, 0.06f, va.co );
+         v3_muladds( hb.pos, up, 0.06f, vb.co );
+
+         scene_vert_pack_norm( &va, up );
+         scene_vert_pack_norm( &vb, up );
+
+         float t1 = (travel_length / total_length) * patch_count;
+         va.uv[0] = t1;
+         va.uv[1] = 0.0f;
+         vb.uv[0] = t1;
+         vb.uv[1] = 1.0f;
+
+         scene_push_vert( world->scene_lines, &va );
+         scene_push_vert( world->scene_lines, &vb );
+
+         if( last_valid ){
+            /* Connect them with triangles */
+            scene_push_tri( world->scene_lines, (u32[3]){ 
+                  last_valid+0-2, last_valid+1-2, last_valid+2-2} );
+            scene_push_tri( world->scene_lines, (u32[3]){ 
+                  last_valid+1-2, last_valid+3-2, last_valid+2-2} );
          }
-         else
-            last_valid = 0;
+         
+         last_valid = world->scene_lines->vertex_count;
+      }
+      else
+         last_valid = 0;
 
-         t += 1.0f*mod;
+      if( t == 1.0f )
+         return;
 
-         if( t >= 1.0f )
-         {
-            /* TODO special case for end of loop, need to add triangles
-             *      between first and last rungs */
-            break;
-         }
-      }
+      t += 1.0f*mod;
+      if( t > 1.0f )
+         t = 1.0f;
 
-      rnj->current_refs ++;
+      v3_copy( p, last );
    }
-
-   scene_copy_slice( world->scene_lines, &route->sm );
 }
 
-/* 
- * Create the strips of colour that run through the world along course paths
- */
-VG_STATIC void world_routes_generate( world_instance *world )
+VG_STATIC void world_routes_create_mesh( world_instance *world, u32 route_id )
 {
-   vg_info( "Generating route meshes\n" );
-   world->scene_lines = scene_init( world_global.generic_heap, 200000, 300000 );
+   ent_route *route = mdl_arritm( &world->ent_route, route_id );
+   u32 last_valid = 0;
 
-   for( u32 i=0; i<world->route_count; i++ )
-      world_routes_create_mesh( world, i );
+   for( int i=0; i<route->checkpoints_count; i++ ){
+      int i0 = route->checkpoints_start+i,
+          i1 = route->checkpoints_start+((i+1)%route->checkpoints_count);
 
-   vg_acquire_thread_sync();
-   {
-      scene_upload( world->scene_lines, &world->mesh_route_lines );
-   }
-   vg_release_thread_sync();
-   vg_linear_del( world_global.generic_heap, world->scene_lines );
-}
-
-/* determine if special type is required for this gate */
-VG_STATIC enum route_special_type world_route_node_type( world_instance *world,
-                                                         mdl_node *pnode )
-{
-   if( pnode->classtype == k_classtype_gate )
-   {
-      struct classtype_gate *inf = mdl_get_entdata( world->meta, pnode );
+      ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0),
+                     *c1 = mdl_arritm(&world->ent_checkpoint, i1);
 
-      if( inf->target )
-      {
-         mdl_node *pother = mdl_node_from_id( world->meta, inf->target );
-         
-         if( pother->classtype == k_classtype_gate )
-         {
-            return k_route_special_type_gate;
-         }
-      }
+      ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index );
+      start_gate = mdl_arritm( &world->ent_gate, start_gate->target );
 
-      return k_route_special_type_collector;
-   }
+      ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index );
 
-   return k_route_special_type_none;
-}
+      v4f p[3];
 
-/* count entities and allocate correct amount of memory in advance */
-VG_STATIC void world_routes_allocate( world_instance *world )
-{
-   vg_info( "Allocating routes\n" );
+      v3_add( (v3f){0.0f,0.1f,0.0f}, start_gate->co[0], p[0] );
+      p[0][3]  = start_gate->ref_count;
+      p[0][3] -= (float)start_gate->ref_total * 0.5f;
+      start_gate->ref_count ++;
 
-   /* count */
-   u32 node_count       = 0,
-       route_count      = 0,
-       gate_count       = 0,
-       collector_count  = 0;
-   
-   for( int i=0; i<world->meta->info.node_count; i++ )
-   {
-      mdl_node *pnode = mdl_node_from_id( world->meta, i );
+      if( !c0->path_count )
+         continue;
 
-      if( pnode->classtype == k_classtype_route_node ||
-          pnode->classtype == k_classtype_gate )
-      {
-         pnode->sub_uid = node_count;
+      /* this is so that we get nice flow through the gates */
+      v3f temp_alignments[2];
+      ent_gate *both[] = { start_gate, end_gate };
 
-         enum route_special_type type = world_route_node_type( world, pnode );
+      for( int j=0; j<2; j++ ){
+         int pi = c0->path_start + ((j==1)? c0->path_count-1: 0);
 
-         if( type == k_route_special_type_gate )
-            gate_count ++;
-         else if( type == k_route_special_type_collector )
-            collector_count ++;
+         ent_path_index *index = mdl_arritm( &world->ent_path_index, pi );
+         ent_route_node *rn = mdl_arritm( &world->ent_route_node,
+                                          index->index );
+         v3f v0;
+         v3_sub( rn->co, both[j]->co[0], v0 );
+         float d = v3_dot( v0, both[j]->to_world[2] );
 
-         node_count ++;
+         v3_muladds( both[j]->co[0], both[j]->to_world[2], d, 
+                     temp_alignments[j] );
+         v3_add( (v3f){0.0f,0.1f,0.0f}, temp_alignments[j], temp_alignments[j]);
       }
-      else if( pnode->classtype == k_classtype_route )
-      {
-         route_count ++;
-      }
-   }
 
-   /* allocate */
-   u32 node_size        = node_count      * sizeof(struct route_node),
-       route_size       = route_count     * sizeof(struct route),
-       gate_size        = gate_count      * sizeof(struct route_gate),
-       collector_size   = collector_count * sizeof(struct route_collector);
-
-   world->nodes      = vg_linear_alloc( world_global.generic_heap, node_size );
-   world->routes     = vg_linear_alloc( world_global.generic_heap, route_size );
-   world->gates      = vg_linear_alloc( world_global.generic_heap, gate_size );
-   world->collectors = vg_linear_alloc( world_global.generic_heap, 
-                                        collector_size );
-}
-
-/* create node from mdl node */
-VG_STATIC struct route_node *world_routes_create_node( world_instance *world,
-                                                       mdl_node *pnode )
-{
-   struct route_node *rn = &world->nodes[ world->node_count ++ ];
 
-   m4x3f transform;
-   mdl_node_transform( pnode, transform );
+      for( int j=0; j<c0->path_count; j ++ ){
+         ent_path_index *index = mdl_arritm( &world->ent_path_index, 
+                                             c0->path_start+j );
+         ent_route_node *rn = mdl_arritm( &world->ent_route_node,
+                                          index->index );
+         if( j==0 || j==c0->path_count-1 )
+            if( j == 0 )
+               v3_copy( temp_alignments[0], p[1] );
+            else
+               v3_copy( temp_alignments[1], p[1] );
+         else
+            v3_copy( rn->co, p[1] );
 
-   v3_copy( transform[3], rn->co );
-   v3_copy( transform[0], rn->right );
-   v3_copy( transform[1], rn->up );
-   v3_muls( transform[2], -1.0f, rn->h );
-   v3_normalize( rn->right );
-   v3_normalize( rn->up );
+         p[1][3] = rn->ref_count;
+         p[1][3] -= (float)rn->ref_total * 0.5f;
+         rn->ref_count ++;
 
-   rn->next[0] = 0xffffffff;
-   rn->next[1] = 0xffffffff;
+         if( j+1 < c0->path_count ){
+            index = mdl_arritm( &world->ent_path_index, 
+                                c0->path_start+j+1 );
+            rn = mdl_arritm( &world->ent_route_node, index->index );
 
-   rn->special_type = 0;
-   rn->special_id = 0;
-   rn->current_refs = 0;
-   rn->ref_count = 0;
+            if( j+1 == c0->path_count-1 )
+               v3_lerp( p[1], temp_alignments[1], 0.5f, p[2] );
+            else
+               v3_lerp( p[1], rn->co, 0.5f, p[2] );
 
-   return rn;
-}
+            p[2][3] = rn->ref_count;
+            p[2][3] -= (float)rn->ref_total * 0.5f;
+         }
+         else{
+            v3_copy( end_gate->co[0], p[2] );
+            v3_add( (v3f){0.0f,0.1f,0.0f}, p[2], p[2] );
+            p[2][3] = end_gate->ref_count;
+            p[2][3] -= (float)end_gate->ref_total * 0.5f;
+            end_gate->ref_count ++;
+         }
 
-/* retrieve the correct node id from mdl subuid */
-VG_STATIC u32 world_routes_get_subuid( world_instance *world, u32 target )
-{
-   if( target == 0 )
-      return 0xffffffff;
-   else
-      return mdl_node_from_id( world->meta, target )->sub_uid;
-}
+         /* p0,p1,p2 bezier patch is complete
+          * --------------------------------------*/
+         v3f surf0, surf2, n0, n2;
 
-#if 0
-VG_STATIC void world_id_fixup( u32 *uid, mdl_context *mdl )
-{
-   if( *uid )
-      *uid = mdl_node_from_id( mdl, *uid )->sub_uid;
-   else
-      *uid = 0xffffffff;
-}
-#endif
+         if( bh_closest_point( world->geo_bh, p[0], surf0, 5.0f ) == -1 )
+            v3_add( (v3f){0.0f,-0.1f,0.0f}, p[0], surf0 );
 
-/* process gate attachement onto node */
-VG_STATIC void world_routes_process_gate( world_instance *world,
-                                          struct route_node *rn, 
-                                          mdl_node *pnode )
-{
-   struct classtype_gate *inf = mdl_get_entdata( world->meta, pnode );
+         if( bh_closest_point( world->geo_bh, p[2], surf2, 5.0f ) == -1 )
+            v3_add( (v3f){0.0f,-0.1f,0.0f}, p[2], surf2 );
 
-   /* H is later scaled based on link distance */
-   v3_normalize( rn->h );
+         v3_sub( surf0, p[0], n0 );
+         v3_sub( surf2, p[2], n2 );
+         v3_normalize( n0 );
+         v3_normalize( n2 );
 
-   rn->next[0] = world_routes_get_subuid( world, inf->target );
-   rn->next[1] = 0xffffffff;
-   rn->special_type = world_route_node_type( world, pnode );
+         world_routes_place_curve( world, p, n0, n2 );
 
-   /* process gate type */
-   if( rn->special_type == k_route_special_type_gate )
-   {
-      mdl_node *pother = mdl_node_from_id( world->meta, inf->target );
-      
-      struct route_gate *rg = &world->gates[ world->gate_count ];
+         /* --- */
+         v4_copy( p[2], p[0] );
+      }
+   }
 
-      rg->node_id = world->node_count-1;
-      rg->timing.time = 0.0;
-      rg->timing.version = 0;
+   scene_copy_slice( world->scene_lines, &route->sm );
+}
 
-      v3_copy( pnode->co,  rg->gate.co[0] );
-      v3_copy( pother->co, rg->gate.co[1] );
-      v4_copy( pnode->q,   rg->gate.q[0] );
-      v4_copy( pother->q,  rg->gate.q[1] );
-      v2_copy( inf->dims,  rg->gate.dims );
+/* 
+ * Create the strips of colour that run through the world along course paths
+ */
+VG_STATIC void world_routes_generate( world_instance *world )
+{
+   vg_info( "Generating route meshes\n" );
+   world->scene_lines = scene_init( world_global.generic_heap, 200000, 300000 );
 
-      gate_transform_update( &rg->gate );
-      rn->special_id = world->gate_count;
+   for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
+      ent_gate *gate = mdl_arritm( &world->ent_gate, i );
+      gate->ref_count = 0;
+      gate->ref_total = 0;
+   }
 
-      world->gate_count ++;
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route_node); i++ ){
+      ent_route_node *rn = mdl_arritm( &world->ent_route, i );
+      rn->ref_count = 0;
+      rn->ref_total = 0;
    }
 
-   /* process collector type */
-   else if( rn->special_type == k_route_special_type_collector )
-   {
-      struct route_collector *rc = 
-         &world->collectors[ world->collector_count ];
+   for( u32 k=0; k<mdl_arrcount(&world->ent_route); k++ ){
+      ent_route *route = mdl_arritm( &world->ent_route, k );
 
-      rc->timing.time = 0.0;
-      rc->timing.version = 0;
+      for( int i=0; i<route->checkpoints_count; i++ ){
+         int i0 = route->checkpoints_start+i,
+             i1 = route->checkpoints_start+((i+1)%route->checkpoints_count);
 
-      rn->special_id = world->collector_count;
-      world->collector_count ++;
-   }
-   else
-      vg_fatal_exit_loop( "Invalid state" );
-}
+         ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0),
+                        *c1 = mdl_arritm(&world->ent_checkpoint, i1);
 
-/* create route from node description */
-VG_STATIC void world_routes_create_route( world_instance *world,
-                                          mdl_node *pnode )
-{
-   mdl_context *mdl = world->meta;
+         ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index );
+         start_gate = mdl_arritm( &world->ent_gate, start_gate->target );
 
-   struct classtype_route *inf = mdl_get_entdata( mdl, pnode );
-   struct route *route = &world->routes[ world->route_count ];
-   memset( route, 0, sizeof(struct route) );
+         ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index );
+         start_gate->ref_total ++;
 
-   v3_copy( inf->colour, route->colour );
-   route->colour[3] = 1.0f;
-   route->track_id = 0xffffffff;
+         if( !c0->path_count )
+            continue;
 
-   for( u32 j=0; j<vg_list_size(track_infos); j++ )
-   {
-      if( !strcmp( mdl_pstr(mdl,pnode->pstr_name), track_infos[j].name ))
-      {
-         route->track_id = j;
-         break;
+         for( int j=0; j<c0->path_count; j ++ ){
+            ent_path_index *index = mdl_arritm( &world->ent_path_index, 
+                                                c0->path_start+j );
+            ent_route_node *rn = mdl_arritm( &world->ent_route_node,
+                                             index->index );
+            rn->ref_total ++;
+
+            if( j+1 < c0->path_count ){
+            }
+            else{
+               end_gate->ref_total ++;
+            }
+         }
       }
    }
 
-   route->start = world_routes_get_subuid( world, inf->id_start );
-   route->active = 0;
-   route->factive = 0.0f;
-   mdl_node_transform( pnode, route->scoreboard_transform );
-
-   struct route_ui_bar *pui = &world_global.ui_bars[ world->route_count ];
-   pui->indices_head = k_route_ui_max_indices - 9;
-   pui->vertex_head = k_route_ui_max_verts - 200;
-   pui->segment_start = 0;
-   pui->segment_count = 0;
-   pui->fade_start = 0;
-   pui->fade_count = 0;
-   pui->fade_timer_start = 0.0;
-
-   world->route_count ++;
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ )
+      world_routes_create_mesh( world, i );
+
+   vg_acquire_thread_sync();
+   {
+      scene_upload( world->scene_lines, &world->mesh_route_lines );
+   }
+   vg_release_thread_sync();
+   vg_linear_del( world_global.generic_heap, world->scene_lines );
 }
 
 /* load all routes from model header */
-VG_STATIC void world_routes_process( world_instance *world )
+VG_STATIC void world_routes_ent_init( world_instance *world )
 {
    vg_info( "Initializing routes\n" );
-   mdl_context *mdl = world->meta;
 
-   for( int i=0; i<mdl->info.node_count; i++ )
-   {
-      mdl_node *pnode = mdl_node_from_id(mdl,i);
-
-      if( pnode->classtype == k_classtype_route_node ||
-          pnode->classtype == k_classtype_gate )
-      {
-         struct route_node *rn = world_routes_create_node( world, pnode );
-
-         if( pnode->classtype == k_classtype_gate )
-         {
-            world_routes_process_gate( world, rn, pnode );
-         }
-         else
-         {
-            struct classtype_route_node *inf = mdl_get_entdata( mdl, pnode );
-            rn->next[0] = world_routes_get_subuid( world, inf->target  );
-            rn->next[1] = world_routes_get_subuid( world, inf->target1 );
-         }
-      }
-      else if( pnode->classtype == k_classtype_route )
-      {
-         world_routes_create_route( world, pnode );
+   for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
+      ent_gate *gate = mdl_arritm( &world->ent_gate, i );
+      for( u32 j=0; j<4; j++ ){
+         gate->routes[j] = 0xffff;
       }
    }
 
-   /*
-    * Gather references
-    */
-   for( int i=0; i<world->route_count; i++ )
-   {
-      struct route *route = &world->routes[i];
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+      ent_route *route = mdl_arritm(&world->ent_route,i);
 
-      u32 stack[64];
-      u32 si = world_routes_get_path( world, route->start, stack );
+      for( u32 j=0; j<route->checkpoints_count; j++ ){
+         u32 id = route->checkpoints_start + j;
+         ent_checkpoint *cp = mdl_arritm(&world->ent_checkpoint,id);
+         
+         ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index );
 
-      for( int sj=0; sj<si; sj++ )
-      {
-         struct route_node *rn = &world->nodes[ stack[sj] ];
-         rn->route_ids[ rn->ref_count ++ ] = i;
+         for( u32 k=0; k<4; k++ ){
+            if( gate->routes[k] == 0xffff ){
+               gate->routes[k] = i;
+               break;
+            }
+         }
+
+         if( gate->type == k_gate_type_teleport ){
+            gate = mdl_arritm(&world->ent_gate, gate->target );
 
-         if( rn->ref_count > 4 )
-            vg_warn( "Too many references on route node %i\n", i );
+            for( u32 k=0; k<4; k++ ){
+               if( gate->routes[k] == 0xffff ){
+                  gate->routes[k] = i;
+                  break;
+               }
+            }
+         }
       }
    }
+
+   world_routes_clear( world );
 }
 
 /* 
@@ -1080,60 +489,23 @@ VG_STATIC void world_routes_process( world_instance *world )
 
 VG_STATIC void world_routes_init(void)
 {
-   world_global.current_run_version = 2;
+   world_global.current_run_version = 200;
    world_global.time = RESET_MAX_TIME*2.0;
    world_global.last_use = 0.0;
 
    shader_scene_route_register();
    shader_routeui_register();
-
-   vg_acquire_thread_sync();
-   {
-      /* UI buffers */
-      for( int i=0; i<vg_list_size(world_global.ui_bars); i++ )
-      {
-         /* OpenGL strips */
-         struct route_ui_bar *pui = &world_global.ui_bars[i];
-
-         glGenVertexArrays( 1, &pui->vao );
-         glGenBuffers( 1, &pui->vbo );
-         glGenBuffers( 1, &pui->ebo );
-         glBindVertexArray( pui->vao );
-
-         size_t stride = sizeof(v2f);
-
-         glBindBuffer( GL_ARRAY_BUFFER, pui->vbo );
-         glBufferData( GL_ARRAY_BUFFER, k_route_ui_max_verts*stride, 
-                       NULL, GL_DYNAMIC_DRAW );
-
-         glBindVertexArray( pui->vao );
-         glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, pui->ebo );
-         glBufferData( GL_ELEMENT_ARRAY_BUFFER, 
-               k_route_ui_max_indices*sizeof(u16), NULL,
-               GL_DYNAMIC_DRAW );
-
-         glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, stride, (void *)0 );
-         glEnableVertexAttribArray( 0 );
-         VG_CHECK_GL_ERR();
-      }
-   }
-   vg_release_thread_sync();
 }
 
 VG_STATIC void world_routes_update( world_instance *world )
 {
    world_global.time += vg.time_delta;
 
-   for( int i=0; i<world->route_count; i++ )
-   {
-      struct route *route = &world->routes[i];
-      route->factive = vg_lerpf( route->factive, route->active, 
-                                 0.6f*vg.time_delta );
-
-      if( route->active )
-      {
-         world_routes_ui_updatetime(i, world_global.time - route->latest_pass );
-      }
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+      ent_route *route = mdl_arritm( &world->ent_route, i );
+      
+      int target = route->active_checkpoint == 0xffffffff? 0: 1;
+      route->factive = vg_lerpf( route->factive, target, 0.6f*vg.time_delta );
    }
 }
 
@@ -1170,34 +542,57 @@ VG_STATIC void render_world_routes( world_instance *world, camera *cam )
 
    mesh_bind( &world->mesh_route_lines );
 
-   for( int i=0; i<world->route_count; i++ )
-   {
-      struct route *route = &world->routes[i];
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+      ent_route *route = mdl_arritm( &world->ent_route, i );
 
       v4f colour;
       v3_lerp( (v3f){0.7f,0.7f,0.7f}, route->colour, route->factive, colour );
-      colour[3] = 1.0f;
+      colour[3] = route->factive*0.2f;
 
       shader_scene_route_uColour( colour );
       mdl_draw_submesh( &route->sm );
    }
-}
 
-VG_STATIC void render_world_routes_ui( world_instance *world )
-{
-   glEnable(GL_BLEND);
-   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-   glBlendEquation(GL_FUNC_ADD);
+   shader_model_gate_use();
+   shader_model_gate_uPv( cam->mtx.pv );
+   shader_model_gate_uCam( cam->pos );
+   shader_model_gate_uTime( vg.time*0.25f );
+   shader_model_gate_uInvRes( (v2f){
+         1.0f / (float)vg.window_x,
+         1.0f / (float)vg.window_y });
 
-   float active_offset = 0.0f;
-   for( int i=0; i<world->route_count; i++ )
-   {
-      struct route *route = &world->routes[i];
-      world_routes_ui_draw( world, i, route->colour, active_offset );
-      active_offset += route->factive;
-   }
+   mesh_bind( &world_global.mesh_gate );
+
+   /* skip writing into the motion vectors for this */
+   glDrawBuffers( 1, (GLenum[]){ GL_COLOR_ATTACHMENT0 } );
+
+   for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+      ent_route *route = mdl_arritm( &world->ent_route, i );
+
+      if( route->active_checkpoint != 0xffffffff ){
+         v4f colour;
+         float brightness = 0.3f + world->ub_lighting.g_day_phase;
+         v3_muls( route->colour, brightness, colour );
+         colour[3] = 1.0f-route->factive;
+
+         shader_model_gate_uColour( colour );
+
+         u32 next = route->checkpoints_start +
+                   (route->active_checkpoint+1) % route->checkpoints_count;
 
-   glDisable(GL_BLEND);
+         ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, next );
+         ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index );
+         shader_model_gate_uMdl( gate->to_world );
+
+         for( u32 j=0; j<4; j++ ){
+            if( gate->routes[j] == i ){
+               mdl_draw_submesh( &world_global.sm_gate_marker[j] );
+               break;
+            }
+         }
+      }
+   }
+   glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 } );
 }
 
 #endif /* ROUTES_H */
index 1b59e5a8c509cb477968473298b327b9d19def2d..88a86396e5fd700fdaef3fc0108dcf3301c94cb3 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "world.h"
 
+#if 0
 #if 0
 #include "shaders/scoretext.h"
 #include "shaders/vblend.h"
@@ -229,5 +230,6 @@ VG_STATIC void world_sfd_init(void)
    for( int i=0; i<w*h*2; i++ )
       world_global.sfd.buffer[i] = 0.0f;
 }
+#endif
 
 #endif /* SFD_H */
index 450c8c2cc2a7f147bc251d1bee65a64894b2a676..f333626087586a752d4aeeeb10e3aa9f3c2ce64e 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "world.h"
 
+#if 0
 /*
  * BVH implementation
  * ----------------------------------------------------------------------------
@@ -11,7 +12,9 @@
 VG_STATIC void volume_vg_expand_bound( void *user, boxf bound, u32 item_index )
 {
    world_instance *world = user;
-   struct world_volume *volume = &world->volumes[ item_index ];
+
+   ent_volume *volume_array = world_ent_array( world, k_ent_volume ),
+              *volume       = volume_array + item_index;
 
    m4x3_expand_aabb_point( volume->transform, bound, (v3f){ 1.0f, 1.0f, 1.0f} );
    m4x3_expand_aabb_point( volume->transform, bound, (v3f){ 1.0f, 1.0f,-1.0f} );
@@ -26,7 +29,9 @@ VG_STATIC void volume_vg_expand_bound( void *user, boxf bound, u32 item_index )
 VG_STATIC float volume_vg_centroid( void *user, u32 item_index, int axis )
 {
    world_instance *world = user;
-   struct world_volume *volume = &world->volumes[ item_index ];
+
+   ent_volume *volume_array = world_ent_array( world, k_ent_volume ),
+              *volume       = volume_array + item_index;
 
    return volume->transform[3][axis];
 }
@@ -34,9 +39,10 @@ VG_STATIC float volume_vg_centroid( void *user, u32 item_index, int axis )
 VG_STATIC void volume_vg_swap( void *user, u32 ia, u32 ib )
 {
    world_instance *world = user;
-   struct world_volume *a = &world->volumes[ ia ],
-                       *b = &world->volumes[ ib ],
-                       temp;
+   ent_volume *volume_array = world_ent_array( world, k_ent_volume ),
+              *a            = volume_array + ia,
+              *b            = volume_array + ib,
+              temp;
 
    temp = *a;
    *a = *b;
@@ -46,11 +52,12 @@ VG_STATIC void volume_vg_swap( void *user, u32 ia, u32 ib )
 VG_STATIC void volume_vg_debug( void *user, u32 item_index )
 {
    world_instance *world = user;
-   struct world_volume *zone = &world->volumes[ item_index ];
+   ent_volume *volume_array = world_ent_array( world, k_ent_volume ),
+              *volume       = volume_array + item_index;
 
-   vg_line_boxf_transformed( zone->transform, (boxf){{-1.0f,-1.0f,-1.0f},
-                                                     { 1.0f, 1.0f, 1.0f}}, 
-                                                     0xff00ff00 );
+   vg_line_boxf_transformed( volume->transform, (boxf){{-1.0f,-1.0f,-1.0f},
+                                                       { 1.0f, 1.0f, 1.0f}}, 
+                                                       0xff00ff00 );
 }
 
 VG_STATIC bh_system bh_system_volumes = 
@@ -62,5 +69,6 @@ VG_STATIC bh_system bh_system_volumes =
    .item_debug = volume_vg_debug,
    .cast_ray = NULL
 };
+#endif
 
 #endif /* WORLD_VOLUMES_H */
index 9e282be89f6eaf459aef241107c8aeee4345bbf7..22de2c9bfb01ee4c9b5c37ce0f55adf18277a403 100644 (file)
@@ -124,8 +124,7 @@ VG_STATIC void render_water_surface( world_instance *world, camera *cam )
    if( !world->water.enabled )
       return;
 
-   if( vg.quality_profile == k_quality_profile_high )
-   {
+   if( vg.quality_profile == k_quality_profile_high ){
       /* Draw surface */
       shader_scene_water_use();
       
@@ -165,12 +164,10 @@ VG_STATIC void render_water_surface( world_instance *world, camera *cam )
 
       mesh_bind( &world->mesh_no_collide );
 
-      for( int i=0; i<world->material_count; i++ )
-      {
-         struct world_material *mat = &world->materials[i];
+      for( int i=0; i<world->surface_count; i++ ){
+         struct world_surface *mat = &world->surfaces[i];
 
-         if( mat->info.shader == k_shader_water )
-         {
+         if( mat->info.shader == k_shader_water ){
             shader_scene_water_uShoreColour( mat->info.colour );
             shader_scene_water_uOceanColour( mat->info.colour1 );
 
@@ -180,8 +177,7 @@ VG_STATIC void render_water_surface( world_instance *world, camera *cam )
 
       glDisable(GL_BLEND);
    }
-   else if( vg.quality_profile == k_quality_profile_low )
-   {
+   else if( vg.quality_profile == k_quality_profile_low ){
       shader_scene_water_fast_use();
 
       vg_tex2d_bind( &tex_water_surf, 1 );
@@ -207,12 +203,10 @@ VG_STATIC void render_water_surface( world_instance *world, camera *cam )
 
       mesh_bind( &world->mesh_no_collide );
 
-      for( int i=0; i<world->material_count; i++ )
-      {
-         struct world_material *mat = &world->materials[i];
+      for( int i=0; i<world->surface_count; i++ ){
+         struct world_surface *mat = &world->surfaces[i];
 
-         if( mat->info.shader == k_shader_water )
-         {
+         if( mat->info.shader == k_shader_water ){
             shader_scene_water_fast_uShoreColour( mat->info.colour );
             shader_scene_water_fast_uOceanColour( mat->info.colour1 );