routes
authorhgn <hgodden00@gmail.com>
Sat, 30 Jul 2022 10:14:12 +0000 (11:14 +0100)
committerhgn <hgodden00@gmail.com>
Sat, 30 Jul 2022 10:14:12 +0000 (11:14 +0100)
22 files changed:
blender_export.py
character.h
common.h
main.c
model.h
models/ch_default.mdl
models/cubeh.mdl
models/epic_scene.mdl [new file with mode: 0644]
models/mp_dev.mdl
models/rs_vig.mdl
physics_test.h
player.h
rigidbody.h
scene.h
shaders/alphatest.h [new file with mode: 0644]
shaders/std_alphatest.fs [new file with mode: 0644]
shaders/vblend.fs
shaders/vblend.h
textures/alphatest.png [new file with mode: 0644]
vg.conf
world.h
world_routes.h [new file with mode: 0644]

index 8424753135935754e843bddc6d6f53b79329069c..e52757b16d6edbbb1b12e3ca3456376c59ae3463 100644 (file)
@@ -77,7 +77,7 @@ class mdl_header(Structure):
 class classtype_gate(Structure):
    _pack_ = 1
    _fields_ = [("target",c_uint32),
-               ("target1",c_uint32)]
+               ("dims",c_float*3)]
 
 class classtype_block(Structure):
    _pack_ = 1
@@ -100,6 +100,21 @@ class classtype_instance(Structure):
    _pack_ = 1
    _fields_ = [("pstr_file",c_uint32)]
 
+class classtype_capsule(Structure):
+   _pack_ = 1
+   _fields_ = [("height",c_float),
+               ("radius",c_float)]
+
+class classtype_route_node(Structure):
+   _pack_ = 1
+   _fields_ = [("target",c_uint32),
+               ("target1",c_uint32)]
+
+class classtype_route(Structure):
+   _pack_ = 1
+   _fields_ = [("pstr_name",c_uint32),
+               ("id_start",c_uint32)]
+
 # Exporter
 # ==============================================================================
 
@@ -223,6 +238,15 @@ def write_model(name):
          if obj.cv_data.target != None:
             gate.target = obj.cv_data.target.cv_data.uid
 
+         if obj.type == 'MESH':
+            gate.dims[0] = obj.data.cv_data.v0[0]
+            gate.dims[1] = obj.data.cv_data.v0[1]
+            gate.dims[2] = obj.data.cv_data.v0[2]
+         else:
+            gate.dims[0] = obj.cv_data.v0[0]
+            gate.dims[1] = obj.cv_data.v0[1]
+            gate.dims[2] = obj.cv_data.v0[2]
+
          entdata_buffer += [gate]
 
       elif classtype == 'k_classtype_block':
@@ -269,6 +293,28 @@ def write_model(name):
          inst = classtype_instance()
          inst.pstr_file = emplace_string( F"models/{target.name}.mdl" )
          entdata_buffer += [inst]
+      elif classtype == 'k_classtype_capsule':
+         node.classtype = 7
+      elif classtype == 'k_classtype_route_node':
+         node.classtype = 8
+         entdata_length += sizeof( classtype_route_node )
+
+         rn = classtype_route_node()
+         if obj.cv_data.target != None: 
+            rn.target = obj.cv_data.target.cv_data.uid
+         if obj.cv_data.target1 != None: 
+            rn.target1 = obj.cv_data.target1.cv_data.uid
+
+         entdata_buffer += [rn]
+      elif classtype == 'k_classtype_route':
+         node.classtype = 9
+         entdata_length += sizeof( classtype_route )
+         r = classtype_route()
+         r.pstr_name = emplace_string("not-implemented")
+         if obj.cv_data.target != None: 
+            r.target = obj.cv_data.target.cv_data.uid
+
+         entdata_buffer += [r]
 
       # classtype == 'k_classtype_none':
       else:
@@ -283,8 +329,15 @@ def write_model(name):
       if obj.type == 'MESH':
          default_mat = c_uint32(69)
          default_mat.name = ""
-
-         if obj.data.name in mesh_cache:
+         
+         # Dont use the cache if we have modifiers that affect the normals
+         #
+         use_cache = True
+         for mod in obj.modifiers:
+            if mod.type == 'DATA_TRANSFER':
+               use_cache = False
+
+         if 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
@@ -452,22 +505,48 @@ def cv_draw():
    gpu.state.depth_mask_set(False)
    gpu.state.line_width_set(2.0)
    gpu.state.face_culling_set('BACK')
-   gpu.state.depth_test_set('NONE')
+   gpu.state.depth_test_set('LESS')
    gpu.state.blend_set('NONE')
 
    verts = []
    colours = []
 
-   def drawbezier(p0,h0,p1,h1,c0,c1):
+   #def drawbezier(p0,h0,p1,h1,c0,c1):
+   #   nonlocal verts, colours
+
+   #   verts += [p0]
+   #   verts += [h0]
+   #   colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
+   #   verts += [p1]
+   #   verts += [h1]
+   #   colours += [(1.0,1.0,1,1),(1,1,1,1)]
+   #   
+   #   last = p0
+   #   for i in range(10):
+   #      t = (i+1)/10
+   #      a0 = 1-t
+
+   #      tt = t*t
+   #      ttt = tt*t
+   #      p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
+   #      verts += [(last[0],last[1],last[2])]
+   #      verts += [(p[0],p[1],p[2])]
+   #      colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
+   #      last = p
+
+   course_count = 0
+
+   def drawbhandle(obj, direction, colour):
       nonlocal verts, colours
-
+      p0 = obj.location
+      h0 = obj.matrix_world @ Vector((0,direction,0))
       verts += [p0]
       verts += [h0]
-      colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
-      verts += [p1]
-      verts += [h1]
-      colours += [(1.0,1.0,1,1),(1,1,1,1)]
-      
+      colours += [colour,colour]
+
+   def drawbezier(p0,h0,p1,h1,c0,c1):
+      nonlocal verts, colours
+
       last = p0
       for i in range(10):
          t = (i+1)/10
@@ -481,14 +560,70 @@ def cv_draw():
          colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
          last = p
 
+   def drawsbpath(o0,o1,c0,c1,s0,s1):
+      nonlocal course_count
+      
+      offs = ((course_count % 2)*2-1) * course_count * 0.02
+
+      p0 = o0.matrix_world @ Vector((offs,  0,0))
+      h0 = o0.matrix_world @ Vector((offs, s0,0))
+      p1 = o1.matrix_world @ Vector((offs,  0,0))
+      h1 = o1.matrix_world @ Vector((offs,-s1,0))
+      drawbezier(p0,h0,p1,h1,c0,c1)
+
+   def drawbpath(o0,o1,c0,c1):
+      drawsbpath(o0,o1,c0,c1,1.0,1.0)
+
+   def drawbline(o0,o1,c0,c1):
+      nonlocal verts, colours
+      verts += [o0.location]
+      verts += [o1.location]
+      colours += [c0,c1]
+   
    for obj in bpy.context.collection.objects:
-      if obj.cv_data.classtype == 'k_classtype_gate':
+      if obj.cv_data.classtype == 'k_classtype_gate' and False:
          if obj.cv_data.target != None:
             p0 = obj.location
             p1 = obj.cv_data.target.location
-            verts += [(p0[0],p0[1],p0[2])]
-            verts += [(p1[0],p1[1],p1[2])]
-            colours += [(0,1,0,1.0),(1,0,0,1.0)]
+
+            for i in range(20):
+               t = i/20.0
+               t1 = (i+0.5)/20.0
+               
+               pa = p0*t+p1*(1.0-t)
+               pb = p0*t1+p1*(1.0-t1)
+
+               verts += [(pa[0],pa[1],pa[2])]
+               verts += [(pb[0],pb[1],pb[2])]
+               colours += [(0,1,0,1.0),(1,0,0,1.0)]
+
+         if obj.type == 'MESH':
+            dims = obj.data.cv_data.v0
+         else:
+            dims = obj.cv_data.v0
+
+         vs = [None]*9
+         c = Vector((0,0,dims[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)))
+
+         indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
+
+         for l in indices:
+            v0 = vs[l[0]]
+            v1 = vs[l[1]]
+            verts += [(v0[0],v0[1],v0[2])]
+            verts += [(v1[0],v1[1],v1[2])]
+            colours += [(1,1,0,1),(1,1,0,1)]
+
       elif obj.cv_data.classtype == 'k_classtype_block':
          a = obj.data.cv_data.v0
          b = obj.data.cv_data.v1
@@ -513,6 +648,31 @@ def cv_draw():
             verts += [(v1[0],v1[1],v1[2])]
             colours += [(1,1,0,1),(1,1,0,1)]
 
+      elif obj.cv_data.classtype == 'k_classtype_capsule':
+         h = obj.data.cv_data.v0[0]
+         r = obj.data.cv_data.v0[1]
+
+         vs = [None]*10
+         vs[0] = obj.matrix_world @ Vector((0.0,0.0, h*0.5  ))
+         vs[1] = obj.matrix_world @ Vector((0.0,0.0,-h*0.5  ))
+         vs[2] = obj.matrix_world @ Vector((  r,0.0, h*0.5-r))
+         vs[3] = obj.matrix_world @ Vector(( -r,0.0, h*0.5-r))
+         vs[4] = obj.matrix_world @ Vector((  r,0.0,-h*0.5+r))
+         vs[5] = obj.matrix_world @ Vector(( -r,0.0,-h*0.5+r))
+         vs[6] = obj.matrix_world @ Vector((0.0, r , h*0.5-r))
+         vs[7] = obj.matrix_world @ Vector((0.0,-r , h*0.5-r))
+         vs[8] = obj.matrix_world @ Vector((0.0, r ,-h*0.5+r))
+         vs[9] = obj.matrix_world @ Vector((0.0,-r ,-h*0.5+r))
+
+         indices = [(0,1),(2,3),(4,5),(6,7),(8,9)]
+
+         for l in indices:
+            v0 = vs[l[0]]
+            v1 = vs[l[1]]
+            verts += [(v0[0],v0[1],v0[2])]
+            verts += [(v1[0],v1[1],v1[2])]
+            colours += [(0.5,1,0,1),(0.5,1,0,1)]
+
       elif obj.cv_data.classtype == 'k_classtype_spawn':
          vs = [None]*4
          vs[0] = obj.matrix_world @ Vector((0,0,0))
@@ -527,30 +687,100 @@ def cv_draw():
             verts += [(v1[0],v1[1],v1[2])]
             colours += [(0,1,1,1),(0,1,1,1)]
 
-      elif obj.cv_data.classtype == 'k_classtype_car_path':
-         p0 = obj.location
-         h0 = obj.matrix_world @ Vector((1,0,0))
+      elif obj.cv_data.classtype == 'k_classtype_route':
+         vs = [None]*2
+         vs[0] = obj.location
+         vs[1] = obj.cv_data.target.location
+         indices = [(0,1)]
+         for l in indices:
+            v0 = vs[l[0]]
+            v1 = vs[l[1]]
+            verts += [(v0[0],v0[1],v0[2])]
+            verts += [(v1[0],v1[1],v1[2])]
+            colours += [(0,1,1,1),(0,1,1,1)]
+
+         stack = [None]*64
+         stack_i = [0]*64
+         stack[0] = obj.cv_data.target
+         si = 1
+         loop_complete = False
+
+         while si > 0:
+            node = stack[si-1]
+
+            targets = [None,None]
+            targets[0] = node.cv_data.target
+
+            if node.cv_data.classtype == 'k_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
+
+               valid=True
+               for sj in range(si):
+                  if stack[sj] == nextnode: # invalidated path
+                     valid=False
+                     break
+
+               if valid:
+                  stack_i[si] = 0
+                  stack[si] = nextnode
+                  si += 1
+                  continue
+
+            if stack_i[si-1] == 2:
+               si -= 1
+
+               if si == 0: # Loop failed to complete
+                  break
 
-         v0 = obj.matrix_world.to_quaternion() @ Vector((1,0,0))
+         if loop_complete:
+            course_colours = [Vector((0,0.8,0.2,1.0)),  \
+                              Vector((0,0.3,0.9,1.0)),  \
+                              Vector((0.4,0.0,0.8,1.0)),\
+                              Vector((0.5,0.8,0.0,1.0)),\
+                              Vector((0.0,0.7,0.6,1.0)),\
+                              Vector((0.2,0.9,0.5,1.0)) ]
+
+            cc = course_colours[ course_count % len(course_colours) ]
+
+            for sj in range(si):
+               sk = (sj+1)%si
+
+               if stack[sj].cv_data.classtype == 'k_classtype_gate' and \
+                  stack[sk].cv_data.classtype == 'k_classtype_gate':
+                  dist = (stack[sj].location-stack[sk].location).magnitude
+                  drawsbpath( stack[sj], stack[sk], cc*0.4, cc, dist, dist )
+
+               else:
+                  drawbpath( stack[sj], stack[sk], cc, cc )
+
+            course_count += 1
+
+      elif obj.cv_data.classtype == 'k_classtype_car_path':
+         v0 = obj.matrix_world.to_quaternion() @ Vector((0,1,0))
          c0 = Vector((v0.x*0.5+0.5, v0.y*0.5+0.5, 0.0, 1.0))
+         drawbhandle( obj, 1.0, (0.9,0.9,0.9,1.0) )
 
          if obj.cv_data.target != None:
-            p1 = obj.cv_data.target.location
-            h1 = obj.cv_data.target.matrix_world @ Vector((-1,0,0))
-
-            v1 = obj.cv_data.target.matrix_world.to_quaternion()@Vector((1,0,0))
+            v1 = obj.cv_data.target.matrix_world.to_quaternion()@Vector((0,1,0))
             c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
 
-            drawbezier( p0, h0, p1, h1, c0, c1 )
+            drawbhandle( obj.cv_data.target, -1.0, (0.5,0.5,0.5,1.0) )
+            drawbpath( obj, obj.cv_data.target, c0, c1 )
 
          if obj.cv_data.target1 != None:
-            p1 = obj.cv_data.target1.location
-            h1 = obj.cv_data.target1.matrix_world @ Vector((-1,0,0))
-
-            v1 = obj.cv_data.target1.matrix_world.to_quaternion()@Vector((1,0,0))
+            v1 = obj.cv_data.target1.matrix_world.to_quaternion()@Vector((0,1,0))
             c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
 
-            drawbezier( p0, h0, p1, h1, c0, c1 )
+            drawbhandle( obj.cv_data.target1, -1.0, (0.5,0.5,0.5,1.0) )
+            drawbpath( obj, obj.cv_data.target1, c0, c1 )
 
    lines = batch_for_shader(\
          cv_view_shader, 'LINES', \
@@ -587,7 +817,10 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
       ('k_classtype_block', "k_classtype_block", "", 2),
       ('k_classtype_spawn', "k_classtype_spawn", "", 3),
       ('k_classtype_water', "k_classtype_water", "", 4),
-      ('k_classtype_car_path', "k_classtype_car_path", "", 5)
+      ('k_classtype_car_path', "k_classtype_car_path", "", 5),
+      ('k_classtype_capsule', "k_classtype_capsule", "", 7 ),
+      ('k_classtype_route_node', "k_classtype_route_node", "", 8 ),
+      ('k_classtype_route', "k_classtype_route", "", 9 )
       ])
 
 class CV_OBJ_PANEL(bpy.types.Panel):
@@ -604,9 +837,19 @@ class CV_OBJ_PANEL(bpy.types.Panel):
 
       if active_object.cv_data.classtype == 'k_classtype_gate':
          _.layout.prop( active_object.cv_data, "target" )
-      elif active_object.cv_data.classtype == 'k_classtype_car_path':
+
+         mesh = active_object.data
+         _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
+         _.layout.prop( mesh.cv_data, "v0" )
+
+      elif active_object.cv_data.classtype == 'k_classtype_car_path' or \
+           active_object.cv_data.classtype == 'k_classtype_route_node':
          _.layout.prop( active_object.cv_data, "target" )
          _.layout.prop( active_object.cv_data, "target1" )
+
+      elif active_object.cv_data.classtype == 'k_classtype_route':
+         _.layout.prop( active_object.cv_data, "target" )
+
       elif active_object.cv_data.classtype == 'k_classtype_block':
          mesh = active_object.data
 
@@ -615,6 +858,10 @@ class CV_OBJ_PANEL(bpy.types.Panel):
          _.layout.prop( mesh.cv_data, "v1" )
          _.layout.prop( mesh.cv_data, "v2" )
          _.layout.prop( mesh.cv_data, "v3" )
+      elif active_object.cv_data.classtype == 'k_classtype_capsule':
+         mesh = active_object.data
+         _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
+         _.layout.prop( mesh.cv_data, "v0" )
 
 class CV_INTERFACE(bpy.types.Panel):
    bl_idname = "VIEW3D_PT_carve"
index 3858f24b9b3382ae8261cc524095fe8fde1118c1..df05295611492ecb41cbafafd266bf9623830c50 100644 (file)
@@ -913,9 +913,6 @@ static void character_debug_ragdoll( struct character *ch )
 
 static void character_ragdoll_iter( struct character *ch )
 {
-   /* TODO: Lots of the RB functions unimplemented here currently */
-
-   return;
    rb_solver_reset();
 
    for( int i=0; i<PART_COUNT; i++ )
@@ -933,7 +930,7 @@ static void character_ragdoll_iter( struct character *ch )
          shoe_vel[i] = v3_length( ch->ragdoll[i].v );
    
    /* This used to be 20 iterations */
-   for( int i=0; i<5; i++ )
+   for( int i=0; i<10; i++ )
    {
       float const k_springfactor = 1.0f/20.0f;
       
index c22fd453d34a2dc778e7d78110a5eaa2ad908ae2..952b51d8b6b6053db9580acbb1d02740dc04e2ff 100644 (file)
--- a/common.h
+++ b/common.h
@@ -17,7 +17,10 @@ enum classtype
    k_classtype_spawn = 3,
    k_classtype_water = 4,
    k_classtype_car_path = 5,
-   k_classtype_instance = 6
+   k_classtype_instance = 6,
+   k_classtype_capsule = 7,
+   k_classtype_route_node = 8,
+   k_classtype_route = 9
 };
 
 /* TODO: he needs a home somewhere */
diff --git a/main.c b/main.c
index d8a2cebab93c4ce3c1c832a8796067ea6328a798..e053c42177fb515e180930775d1c2486e251b948 100644 (file)
--- a/main.c
+++ b/main.c
@@ -276,7 +276,7 @@ static void render_main_game(void)
    {
       m4x4_projection( vg_pv, gpipeline.fov, 
             (float)vg_window_x / (float)vg_window_y, 
-            0.01f, 100.0f );
+            0.04f, 600.0f );
       m4x4_mul( vg_pv, world_4x4, vg_pv );
    }
    draw_player();
diff --git a/model.h b/model.h
index 4fdd860e0f31df3437727fbfeb2c2936ca111d81..f8b09b85fcbdb2af8f8d5ae6f0753b6114bdc0dc 100644 (file)
--- a/model.h
+++ b/model.h
@@ -83,6 +83,7 @@ struct classtype_block
 struct classtype_gate
 {
    u32 target;
+   v3f dims;
 };
 
 struct classtype_spawn
@@ -105,6 +106,22 @@ struct classtype_instance
    u32 pstr_file;
 };
 
+struct classtype_capsule
+{
+   float height, radius;
+};
+
+struct classtype_route_node
+{
+   u32 target, target1;
+};
+
+struct classtype_route
+{
+   u32 pstr_name;
+   u32 id_start;
+};
+
 #pragma pack(pop)
 
 /*
index 4257a587b60ea9dd042bb5c0124efa35f524dfde..f425d65da4fb0c826ed9d84b0be0799a8be09419 100644 (file)
Binary files a/models/ch_default.mdl and b/models/ch_default.mdl differ
index 66380cbce43a8cf3324cef46baf6d055b0cd5e1a..9f0a8457bc8b2c6e1ecd450ba0755be4520fe5e7 100644 (file)
Binary files a/models/cubeh.mdl and b/models/cubeh.mdl differ
diff --git a/models/epic_scene.mdl b/models/epic_scene.mdl
new file mode 100644 (file)
index 0000000..7b868e2
Binary files /dev/null and b/models/epic_scene.mdl differ
index 65cfcf4f1f46cd27937e4b44b7eb3c919d8c007f..6e1b7045adee921c38924bd203dc9c78762f0946 100644 (file)
Binary files a/models/mp_dev.mdl and b/models/mp_dev.mdl differ
index 37bde7bd62688de7b4280dcc4cd64f5642274007..1a31cd9f438ac79d3dc986aa63531cc368fd0907 100644 (file)
Binary files a/models/rs_vig.mdl and b/models/rs_vig.mdl differ
index d3b9f0640b97f035acfa5253e4660afe9bcb2c63..33a6b6b7f8c56b70a75470da8e96ea55b3d9eb90 100644 (file)
@@ -19,6 +19,26 @@ rigidbody blocky =
       .is_world = 1
    };
 
+rigidbody marko = 
+{
+   .type = k_rb_shape_box,
+   .bbx = {{-0.5f,-0.5f,-0.5f},{0.5f,0.5f,0.5f}},
+   .co = {-36.0f,8.0f,-36.0f},
+   .q = {0.0f,0.0f,0.0f,1.0f},
+   .is_world = 0
+};
+
+scene epic_scene;
+
+rigidbody epic_scene_rb = 
+{
+   .type = k_rb_shape_scene,
+   .co = {0.0f,0.0f,0.0f},
+   .q = {0.0f,0.0f,0.0f,1.0f},
+   .is_world = 1,
+   .inf.scene = { .pscene = &epic_scene }
+};
+
 rigidbody funnel[4] = {
    {
       .type = k_rb_shape_box,
@@ -100,6 +120,30 @@ static void physics_test_start(void)
    rb_init( &ball1 );
    rb_init( &jeff1 );
    rb_init( &blocky );
+
+   scene_init( &epic_scene );
+
+   mdl_header *mdl = mdl_load( "models/epic_scene.mdl" );
+
+   m4x3f transform;
+   m4x3_identity( transform );
+
+   for( int i=0; i<mdl->node_count; i++ )
+   {
+      mdl_node *pnode = mdl_node_from_id( mdl, i );
+
+      for( int j=0; j<pnode->submesh_count; j++ )
+      {
+         mdl_submesh *sm = mdl_node_submesh( mdl, pnode, j );
+         scene_add_submesh( &epic_scene, mdl, sm, transform );
+      }
+   }
+
+   free( mdl );
+   scene_bh_create( &epic_scene );
+
+   rb_init( &epic_scene_rb );
+   rb_init( &marko );
 }
 
 static void physics_test_update(void)
@@ -107,7 +151,6 @@ static void physics_test_update(void)
    player_freecam();
    player_camera_update();
 
-   
    for( int i=0; i<4; i++ )
       rb_debug( &funnel[i], 0xff0060e0 );
    rb_debug( &ground, 0xff00ff00 );
@@ -117,20 +160,11 @@ static void physics_test_update(void)
    rb_debug( &blocky, 0xffcccccc );
    rb_debug( &jeff1, 0xff00ffff );
 
-   for( int i=0; i<vg_list_size(jeffs); i++ )
-   {
-      rb_debug( &jeffs[i], (u32[]){ 0xff0000ff, 0xff00ff00, 0xff00ffff,
-                                   0xffff0000, 0xffff00ff, 0xffffff00,
-                                   }[i%6] );
-      rb_iter( jeffs+i );
-   }
+   rb_debug( &epic_scene_rb, 0xffcccccc );
+   rb_debug( &marko, 0xffffcc00 );
 
    {
 
-   rb_iter( &ball );
-   rb_iter( &ball1 );
-   rb_iter( &jeff1 );
-
    rb_solver_reset();
 
    for( int i=0; i<4; i++ )
@@ -168,27 +202,36 @@ static void physics_test_update(void)
    rb_collide( &ball, &ground );
    rb_collide( &ball1, &ground );
    rb_collide( &ball1, &ball );
+   rb_collide( &marko, &epic_scene_rb );
 
    rb_presolve_contacts( rb_contact_buffer, rb_contact_count );
    for( int i=0; i<8; i++ )
       rb_solve_contacts( rb_contact_buffer, rb_contact_count );
 
+
+   /* ITERATE */
+   {
    for( int i=0; i<vg_list_size(jeffs); i++ )
    {
-      rb_update_transform(jeffs+i);
+      rb_debug( &jeffs[i], (u32[]){ 0xff0000ff, 0xff00ff00, 0xff00ffff,
+                                   0xffff0000, 0xffff00ff, 0xffffff00,
+                                   }[i%6] );
+      rb_iter( jeffs+i );
    }
 
-   rb_update_transform( &ball );
-   rb_update_transform( &ball1 );
-   rb_update_transform( &jeff1 );
-
+   rb_iter( &ball );
+   rb_iter( &ball1 );
+   rb_iter( &jeff1 );
+   rb_iter( &marko );
    }
-
+   
+   /* POSITION OVERRIDE */
+   {
    if(glfwGetKey( vg_window, GLFW_KEY_L ))
    {
-      m4x3_mulv( player.camera, (v3f){0.0f,0.0f,-5.0f}, jeff1.co );
-      v3_zero( jeff1.v );
-      v3_zero( jeff1.w );
+      m4x3_mulv( player.camera, (v3f){0.0f,0.0f,-5.0f}, marko.co );
+      v3_zero( marko.v );
+      v3_zero( marko.w );
    }
    if(glfwGetKey( vg_window, GLFW_KEY_K ))
    {
@@ -207,6 +250,20 @@ static void physics_test_update(void)
    {
       reorg_jeffs();
    }
+   }
+   
+   /* UPDATE TRANSFORMS */
+   for( int i=0; i<vg_list_size(jeffs); i++ )
+   {
+      rb_update_transform(jeffs+i);
+   }
+
+   rb_update_transform( &ball );
+   rb_update_transform( &ball1 );
+   rb_update_transform( &jeff1 );
+   rb_update_transform( &marko );
+
+   }
 }
 
 static void physics_test_render(void)
index 2860d1934cbbf217bb652ffcb8562255f168da38..b70717eb5076b0fbc8aa3cae33f07f47fe5f29c3 100644 (file)
--- a/player.h
+++ b/player.h
@@ -450,39 +450,6 @@ static void player_physics(void)
    len += rb_sphere_vs_scene( rbf, &world.rb_geo, manifold+len );
    len += rb_sphere_vs_scene( rbb, &world.rb_geo, manifold+len );
 
-#if 0
-   for( int i=0; i<len; i++ )
-   {
-      u32 *ptri = &world.geo.indices[ geo[i]*3 ];
-
-      for( int j=0; j<3; j++ )
-         v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
-
-      vg_line(tri[0],tri[1],0xff00ff00 );
-      vg_line(tri[1],tri[2],0xff00ff00 );
-      vg_line(tri[2],tri[0],0xff00ff00 );
-
-      v3f temp;
-      v3_copy( player.rb.co, temp );
-
-      for( int j=0; j<2; j++ )
-      {
-         if(manifold_count >= vg_list_size(manifold))
-         {
-            vg_error("Manifold overflow!\n");
-            break;
-         }
-
-         rb_ct *ct = &manifold[manifold_count];
-         v3_copy( poles[j], player.rb.co );
-
-         manifold_count += rb_sphere_vs_triangle( &player.rb, tri, ct );
-      }
-
-      v3_copy( temp, player.rb.co );
-   }
-#endif
-
    rb_presolve_contacts( manifold, len );
    v3f surface_avg = {0.0f, 0.0f, 0.0f};
 
@@ -493,8 +460,17 @@ static void player_physics(void)
    else
    {
       for( int i=0; i<len; i++ )
+      {
          v3_add( manifold[i].n, surface_avg, surface_avg );
 
+         if( manifold[i].element_id <= world.sm_geo_std_oob.vertex_count )
+         {
+            player.is_dead = 1;
+            character_ragdoll_copypose( &player.mdl, player.rb.v );
+            return;
+         }
+      }
+
       v3_normalize( surface_avg );
 
       if( v3_dot( player.rb.v, surface_avg ) > 0.5f )
index 14bc0606411480e866bbb36c9ea73a13750c6d75..2714c430b1c341421228205278594ed287506c06 100644 (file)
@@ -93,6 +93,7 @@ static struct contact
    v3f co, n;
    v3f t[2];
    float mass_total, p, bias, norm_impulse, tangent_impulse[2];
+   u32 element_id;
 }
 rb_contact_buffer[256];
 static int rb_contact_count = 0;
@@ -163,7 +164,8 @@ static void rb_init( rigidbody *rb )
    }
    else
    {
-      rb->inv_mass = 1.0f/(8.0f*volume);
+      rb->inv_mass = 1.0f/(8.0f*volume);  /* TODO: Things get weird when mass
+                                                   passes a certain point??? */
    }
 
    v3_zero( rb->v );
@@ -424,6 +426,136 @@ static void closest_on_triangle( v3f p, v3f tri[3], v3f dest )
    v3_muladds( dest, ac, w, dest );
 }
 
+/* TODO */
+static void closest_on_triangle_1( v3f p, v3f tri[3], v3f dest )
+{
+   v3f ab, ac, ap;
+   float d1, d2;
+
+   /* Region outside A */
+   v3_sub( tri[1], tri[0], ab );
+   v3_sub( tri[2], tri[0], ac );
+   v3_sub( p, tri[0], ap );
+   
+   d1 = v3_dot(ab,ap);
+   d2 = v3_dot(ac,ap);
+   if( d1 <= 0.0f && d2 <= 0.0f ) 
+   {
+      v3_copy( tri[0], dest );
+      return;
+   }
+
+   /* Region outside B */
+   v3f bp;
+   float d3, d4;
+
+   v3_sub( p, tri[1], bp );
+   d3 = v3_dot( ab, bp );
+   d4 = v3_dot( ac, bp );
+
+   if( d3 >= 0.0f && d4 <= d3 )
+   {
+      v3_copy( tri[1], dest );
+      return;
+   }
+   
+   /* Edge region of AB */
+   float vc = d1*d4 - d3*d2;
+   if( vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f )
+   {
+      float v = d1 / (d1-d3);
+      v3_muladds( tri[0], ab, v, dest );
+      return;
+   }
+
+   /* Region outside C */
+   v3f cp;
+   float d5, d6;
+   v3_sub( p, tri[2], cp );
+   d5 = v3_dot(ab, cp);
+   d6 = v3_dot(ac, cp);
+   
+   if( d6 >= 0.0f && d5 <= d6 )
+   {
+      v3_copy( tri[2], dest );
+      return;
+   }
+
+   /* Region of AC */
+   float vb = d5*d2 - d1*d6;
+   if( vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f )
+   {
+      float w = d2 / (d2-d6);
+      v3_muladds( tri[0], ac, w, dest );
+      return;
+   }
+
+   /* Region of BC */
+   float va = d3*d6 - d5*d4;
+   if( va <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f )
+   {
+      float w = (d4-d3) / ((d4-d3) + (d5-d6));
+      v3f bc;
+      v3_sub( tri[2], tri[1], bc );
+      v3_muladds( tri[1], bc, w, dest );
+      return;
+   }
+
+   /* P inside region, Q via barycentric coordinates uvw */
+   float d = 1.0f/(va+vb+vc),
+         v = vb*d,
+         w = vc*d;
+
+   v3_muladds( tri[0], ab, v, dest );
+   v3_muladds( dest, ac, w, dest );
+}
+
+static int rb_intersect_planes( v4f p0, v4f p1, v4f p2, v3f p )
+{
+   v3f u;
+   v3_cross( p1, p2, u );
+   float d = v3_dot( p0, u );
+
+   if( fabsf(d) < 0.0001f ) 
+      return 0;
+
+   v3_muls( u, p0[3], p );
+
+   v3f v0, v1;
+   v3_muls( p1, p2[3], v0 );
+   v3_muladds( v0, p2, -p1[3], v0 );
+   v3_cross( p0, v0, v1 );
+   v3_add( v1, p, p );
+   v3_muls( p, 1.0f/d, p );
+
+   return 1;
+}
+
+int rb_intersect_planes_1( v4f a, v4f b, v4f c, v3f p )
+{
+   float const epsilon = 0.001;
+        
+   v3f x, bc, ca, ab;
+   float d;
+        
+   v3_cross( a, b, x );
+   d = v3_dot( x, c );
+        
+   if( d < epsilon && d > -epsilon ) return 0;
+        
+   v3_cross(b,c,bc);
+   v3_cross(c,a,ca);
+   v3_cross(a,b,ab);
+
+   v3_muls(       bc, -a[3], p );
+   v3_muladds( p, ca, -b[3], p );
+   v3_muladds( p, ab, -c[3], p );
+
+   v3_negate( p, p );
+   v3_divs( p, d, p );
+        
+   return 1;
+}
 /*
  * Contact generators
  *
@@ -911,7 +1043,8 @@ static int rb_sphere_vs_scene( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
       vg_line(tri[0],tri[1],0xff00ff00 );
       vg_line(tri[1],tri[2],0xff00ff00 );
       vg_line(tri[2],tri[0],0xff00ff00 );
-
+      
+      buf[count].element_id = ptri[0];
       count += rb_sphere_vs_triangle( rba, rbb, tri, buf+count );
 
       if( count == 12 )
@@ -924,6 +1057,166 @@ static int rb_sphere_vs_scene( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
    return count;
 }
 
+static float rb_box_plane_interval( rigidbody *rba, v4f p )
+{
+   /* TODO: Make boxes COG aligned as is every other shape. 
+    *       or create COG vector.
+    * TODO: Make forward actually point in the right fucking direction. */
+   v3f e,c;
+   v3_sub( rba->bbx[1], rba->bbx[0], e );
+   v3_muls( e, 0.5f, e );
+   v3_add( rba->bbx[0], e, c );
+   m4x3_mulv( rba->to_world, c, c );
+
+   float r = 
+          e[0]*fabsf( v3_dot(p, rba->right)) +
+          e[1]*fabsf( v3_dot(p, rba->up)) +
+          e[2]*fabsf(-v3_dot(p, rba->forward)),
+   s = v3_dot( p, c ) - p[3];
+   
+   return r-s;
+}
+
+static int rb_box_vs_scene( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+#if 1
+   scene *sc = rbb->inf.scene.pscene;
+   
+   u32 geo[128];
+   v3f tri[3];
+   int len = bh_select( &sc->bhtris, rba->bbx_world, geo, 128 );
+   
+   vg_info( "%d\n", len );
+
+   int count = 0;
+
+   for( int i=0; i<len; i++ )
+   {
+      u32 *ptri = &sc->indices[ geo[i]*3 ];
+
+      for( int j=0; j<3; j++ )
+         v3_copy( sc->verts[ptri[j]].co, tri[j] );
+
+      vg_line(tri[0],tri[1],0xff00ff00 );
+      vg_line(tri[1],tri[2],0xff00ff00 );
+      vg_line(tri[2],tri[0],0xff00ff00 );
+
+      //count += rb_sphere_vs_triangle( rba, rbb, tri, buf+count );
+      //
+      
+      /* TODO: SAT test first */
+      
+      /* 
+       * each pair of faces on the box vs triangle normal
+       */
+
+      v3f v0, v1;
+      v4f tn;
+
+      v3_sub( tri[1],tri[0], v0 );
+      v3_sub( tri[2],tri[0], v1 );
+      v3_cross( v0, v1, tn );
+      v3_normalize( tn );
+      
+      tn[3] = v3_dot( tn, tri[0] );
+
+      v4f pa, pb, pc, pd, pe, pf;
+      v3_copy( rba->right, pa );
+      v3_muls( rba->right, -1.0f, pb );
+      v3_copy( rba->up, pc );
+      v3_muls( rba->up, -1.0f, pd );
+      v3_copy( rba->forward, pf );
+      v3_muls( rba->forward, -1.0f, pe );
+
+      float dx = v3_dot( rba->co, rba->right ),
+            dy = v3_dot( rba->co, rba->up ),
+            dz = -v3_dot( rba->co, rba->forward );
+
+      pa[3] =  dx + rba->bbx[1][0];
+      pb[3] = -dx - rba->bbx[0][0];
+      pc[3] =  dy + rba->bbx[1][1];
+      pd[3] = -dy - rba->bbx[0][1];
+      pe[3] =  dz + rba->bbx[1][2];
+      pf[3] = -dz - rba->bbx[0][2];
+
+      float *pairs[][2] = { {pc, pa}, {pc,pe}, {pc,pb}, {pc,pf},
+                            {pd, pa}, {pd,pe}, {pd,pb}, {pd,pf},
+                            {pf, pa}, {pa,pe}, {pe,pb}, {pb,pf}};
+      for( int j=0; j<vg_list_size(pairs); j++ )
+      {
+         v3f p;
+
+         if( rb_intersect_planes_1( pairs[j][0], pairs[j][1], tn, p ))
+         {
+            v3f p_tri, p_box;
+            closest_point_obb( p, rba, p_box );
+            closest_on_triangle_1( p, tri, p_tri );
+
+            //vg_line_pt3( p, 0.1f, 0xffeeaaff );
+
+            if( v3_dist( p_tri, p ) < 0.001f && v3_dist( p_box, p ) < 0.001f )
+            {
+               if( count == 12 )
+               {
+                  vg_warn( "Exceeding box_vs_scene capacity."
+                           "Geometry too dense!\n" );
+                  return count;
+               }
+
+               rb_ct *ct = buf+count;
+
+               v3_copy( tn, ct->n );
+               v3_copy( p_box, ct->co );
+
+               ct->p = rb_box_plane_interval( rba, tn );
+               ct->rba = rba;
+               ct->rbb = rbb;
+               count ++;
+            }
+         }
+      }
+   }
+#else
+
+   v3f pts[8]; 
+   float *p000 = pts[0], *p001 = pts[1], *p010 = pts[2], *p011 = pts[3],
+         *p100 = pts[4], *p101 = pts[5], *p110 = pts[6], *p111 = pts[7];
+
+   p000[0]=rba->bbx[0][0];p000[1]=rba->bbx[0][1];p000[2]=rba->bbx[0][2];
+   p001[0]=rba->bbx[0][0];p001[1]=rba->bbx[0][1];p001[2]=rba->bbx[1][2];
+   p010[0]=rba->bbx[0][0];p010[1]=rba->bbx[1][1];p010[2]=rba->bbx[0][2];
+   p011[0]=rba->bbx[0][0];p011[1]=rba->bbx[1][1];p011[2]=rba->bbx[1][2];
+   p100[0]=rba->bbx[1][0];p100[1]=rba->bbx[0][1];p100[2]=rba->bbx[0][2];
+   p101[0]=rba->bbx[1][0];p101[1]=rba->bbx[0][1];p101[2]=rba->bbx[1][2];
+   p110[0]=rba->bbx[1][0];p110[1]=rba->bbx[1][1];p110[2]=rba->bbx[0][2];
+   p111[0]=rba->bbx[1][0];p111[1]=rba->bbx[1][1];p111[2]=rba->bbx[1][2];
+
+   int count = 0;
+   for( int i=0; i<8; i++ )
+   {
+      m4x3_mulv( rba->to_world, pts[i], pts[i] );
+
+      vg_line_pt3( pts[i], 0.1f, 0xffff00ff );
+
+      if( pts[i][1] < 0.0f )
+      {
+         rb_ct *ct = buf+count;
+
+         v3_copy( (v3f){0.0f,1.0f,0.0f}, ct->n );
+         v3_copy( pts[i], ct->co );
+         
+         ct->p = 0.0f-pts[i][1];
+         ct->rba = rba;
+         ct->rbb = rbb;
+         count ++;
+      }
+   }
+
+#endif
+
+   return count;
+}
+
 static int RB_MATRIX_ERROR( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
 {
    vg_error( "Collision type is unimplemented between types %d and %d\n",
@@ -947,13 +1240,18 @@ static int rb_box_vs_sphere( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
    return rb_sphere_vs_box( rbb, rba, buf );
 }
 
+static int rb_scene_vs_box( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+   return rb_box_vs_scene( rbb, rba, buf );
+}
+
 static int (*rb_jump_table[4][4])( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
 = {
-              /* box */         /* Sphere */         /* Capsule */
-/*box    */ { RB_MATRIX_ERROR,  rb_box_vs_sphere,    rb_box_vs_capsule,    RB_MATRIX_ERROR },
+              /* box */         /* Sphere */         /* Capsule */         /* Mesh */
+/*box    */ { RB_MATRIX_ERROR,  rb_box_vs_sphere,    rb_box_vs_capsule,    rb_box_vs_scene },
 /*sphere */ { rb_sphere_vs_box, rb_sphere_vs_sphere, rb_sphere_vs_capsule, rb_sphere_vs_scene },
 /*capsule*/ { rb_capsule_vs_box,rb_capsule_vs_sphere,rb_capsule_vs_capsule,RB_MATRIX_ERROR },
-/*mesh   */ { RB_MATRIX_ERROR,  RB_MATRIX_ERROR,     RB_MATRIX_ERROR,      RB_MATRIX_ERROR }
+/*mesh   */ { rb_scene_vs_box,  RB_MATRIX_ERROR,     RB_MATRIX_ERROR,      RB_MATRIX_ERROR }
 };
 
 static int rb_collide( rigidbody *rba, rigidbody *rbb )
@@ -1279,7 +1577,7 @@ static void rb_standard_impulse( rb_ct *ct, v3f da, v3f db, v3f impulse )
  */
 static void rb_solve_contacts( rb_ct *buf, int len )
 {
-   float k_friction = 0.1f;
+   float k_friction = 0.5f;
 
    /* Friction Impulse */
    for( int i=0; i<len; i++ )
diff --git a/scene.h b/scene.h
index 3e3829ecc526c3299cdb9bc8b69db7df09468ac7..aaecfcce803f3fe04b6af604b83600bb002ac071 100644 (file)
--- a/scene.h
+++ b/scene.h
@@ -114,6 +114,12 @@ static void scene_add_submesh( scene *pscene, mdl_header *mdl,
          &pscene->vertex_cap, sm->vertex_count, sizeof(mdl_vert) );
    pscene->indices = buffer_reserve( pscene->indices, pscene->indice_count,
          &pscene->indice_cap, sm->indice_count, sizeof(u32) );
+
+   m3x3f normal_matrix;
+   m3x3_copy( transform, normal_matrix );
+   v3_normalize( normal_matrix[0] );
+   v3_normalize( normal_matrix[1] );
+   v3_normalize( normal_matrix[2] );
    
    /* Transform and place vertices */
    mdl_vert *src_verts = mdl_submesh_vertices( mdl, sm );
@@ -130,7 +136,7 @@ static void scene_add_submesh( scene *pscene, mdl_header *mdl,
                *src = &src_verts[ i ];
 
       m4x3_mulv( transform, src->co, pvert->co );
-      m3x3_mulv( transform, src->norm, pvert->norm );
+      m3x3_mulv( normal_matrix, src->norm, pvert->norm );
 
       v4_copy( src->colour, pvert->colour );
       v2_copy( src->uv, pvert->uv );
diff --git a/shaders/alphatest.h b/shaders/alphatest.h
new file mode 100644 (file)
index 0000000..3039b48
--- /dev/null
@@ -0,0 +1,228 @@
+#ifndef SHADER_alphatest_H
+#define SHADER_alphatest_H
+static void shader_alphatest_link(void);
+static void shader_alphatest_register(void);
+static struct vg_shader _shader_alphatest = {
+   .name = "alphatest",
+   .link = shader_alphatest_link,
+   .vs = 
+{
+.orig_file = "../shaders/standard.vs",
+.static_src = 
+"layout (location=0) in vec3 a_co;\n"
+"layout (location=1) in vec3 a_norm;\n"
+"layout (location=2) in vec4 a_colour;\n"
+"layout (location=3) in vec2 a_uv;\n"
+"\n"
+"#line      2        0 \n"
+"\n"
+"uniform mat4 uPv;\n"
+"uniform mat4x3 uMdl;\n"
+"\n"
+"out vec4 aColour;\n"
+"out vec2 aUv;\n"
+"out vec3 aNorm;\n"
+"out vec3 aCo;\n"
+"out vec3 aWorldCo;\n"
+"\n"
+"void main()\n"
+"{\n"
+"   vec3 world_pos = uMdl * vec4(a_co,1.0);\n"
+"   gl_Position = uPv * vec4( world_pos, 1.0 );\n"
+"   aColour = a_colour;\n"
+"   aUv = a_uv;\n"
+"   aNorm = mat3(uMdl) * a_norm;\n"
+"   aCo = a_co;\n"
+"   aWorldCo = world_pos;\n"
+"}\n"
+""},
+   .fs = 
+{
+.orig_file = "../shaders/std_alphatest.fs",
+.static_src = 
+"out vec4 FragColor;\n"
+"\n"
+"uniform sampler2D uTexGarbage;\n"
+"uniform sampler2D uTexMain;\n"
+"uniform vec3 uCamera;\n"
+"uniform vec4 uPlane;\n"
+"\n"
+"in vec4 aColour;\n"
+"in vec2 aUv;\n"
+"in vec3 aNorm;\n"
+"in vec3 aCo;\n"
+"in vec3 aWorldCo;\n"
+"\n"
+"#line       1        1 \n"
+"layout (std140) uniform ub_world_lighting\n"
+"{\n"
+"   vec4 g_light_colours[3];\n"
+"   vec4 g_light_directions[3];\n"
+"   vec4 g_ambient_colour;\n"
+"\n"
+"   vec4 g_water_plane;\n"
+"   vec4 g_depth_bounds;\n"
+"   float g_water_fog;\n"
+"   int g_light_count;\n"
+"   int g_light_preview;\n"
+"};\n"
+"\n"
+"uniform sampler2D g_world_depth;\n"
+"\n"
+"// Standard diffuse + spec models\n"
+"// ==============================\n"
+"\n"
+"vec3 do_light_diffuse( vec3 vfrag, vec3 wnormal )\n"
+"{\n"
+"   vec3 vtotal = g_ambient_colour.rgb;\n"
+"\n"
+"   for( int i=0; i<g_light_count; i++ )\n"
+"   {\n"
+"      vec3 vcolour = g_light_colours[i].rgb;\n"
+"      vec3 vdir = g_light_directions[i].xyz;\n"
+"\n"
+"      float flight = max(dot( vdir, wnormal )*0.75+0.25,0.0);\n"
+"      vtotal += vcolour*flight;\n"
+"   }\n"
+"\n"
+"   return vfrag * vtotal;\n"
+"}\n"
+"\n"
+"vec3 do_light_spec( vec3 vfrag, vec3 wnormal, vec3 halfview, float fintensity )\n"
+"{\n"
+"   vec3 vcolour = g_light_colours[0].rgb;\n"
+"   vec3 vdir = g_light_directions[0].xyz;\n"
+"\n"
+"   vec3 specdir = reflect( -vdir, wnormal );\n"
+"   float spec = pow(max(dot( halfview, specdir ), 0.0), 10.0);\n"
+"   return vfrag + vcolour*spec*fintensity;\n"
+"}\n"
+"\n"
+"float world_depth_sample( vec3 pos )\n"
+"{\n"
+"   vec2 depth_coord = (pos.xz - g_depth_bounds.xy) * g_depth_bounds.zw; \n"
+"   return texture( g_world_depth, depth_coord ).r;\n"
+"}\n"
+"\n"
+"float shadow_sample( vec3 vdir )\n"
+"{\n"
+"   vec3 sample_pos = aWorldCo + vdir;\n"
+"   float height_sample = world_depth_sample( sample_pos );\n"
+"\n"
+"   float fdelta = height_sample - sample_pos.y;\n"
+"   return clamp( fdelta, 0.1, 0.2 )-0.1;\n"
+"}\n"
+"\n"
+"vec3 do_light_shadowing_old( vec3 vfrag )\n"
+"{\n"
+"   float faccum = 0.0;\n"
+"   faccum += shadow_sample( vec3( 0.0, 0.5, 0.0 ));\n"
+"   faccum += shadow_sample( vec3( 2.0, 0.3, 0.0 ));\n"
+"   faccum += shadow_sample( vec3( 3.0, 1.0, 0.0 ));\n"
+"   faccum += shadow_sample( vec3( 5.0, 1.0, 0.0 ));\n"
+"   faccum += shadow_sample( vec3( 0.0, 0.5, 0.0 )*1.5);\n"
+"   faccum += shadow_sample( vec3( 2.0, 0.3, 0.0 )*1.5);\n"
+"   faccum += shadow_sample( vec3( 3.0, 1.0, 0.0 )*1.5);\n"
+"   faccum += shadow_sample( vec3( 5.0, 1.0, 0.0 )*1.5);\n"
+"   return mix( vfrag, g_ambient_colour.rgb, faccum );\n"
+"}\n"
+"\n"
+"vec3 do_light_shadowing( vec3 vfrag )\n"
+"{\n"
+"   float fspread = g_light_colours[0].w;\n"
+"   vec3  vdir = g_light_directions[0].xyz;\n"
+"   float flength = g_light_directions[0].w;\n"
+"\n"
+"   float famt = 0.0;\n"
+"   famt+=shadow_sample((vdir+vec3(-0.563, 0.550, 0.307)*fspread)*flength*0.1);\n"
+"   famt+=shadow_sample((vdir+vec3( 0.808, 0.686, 0.346)*fspread)*flength*0.2);\n"
+"   famt+=shadow_sample((vdir+vec3( 0.787, 0.074,-0.065)*fspread)*flength*0.3);\n"
+"   famt+=shadow_sample((vdir+vec3(-0.593, 0.071,-0.425)*fspread)*flength*0.4);\n"
+"   famt+=shadow_sample((vdir+vec3(-0.790,-0.933,-0.875)*fspread)*flength*0.5);\n"
+"   famt+=shadow_sample((vdir+vec3( 0.807,-0.690, 0.472)*fspread)*flength*0.6);\n"
+"   famt+=shadow_sample((vdir+vec3( 0.522,-0.379, 0.350)*fspread)*flength*0.7);\n"
+"   famt+=shadow_sample((vdir+vec3( 0.483, 0.201, 0.306)*fspread)*flength*0.8);\n"
+"   return mix( vfrag, g_ambient_colour.rgb, famt );\n"
+"}\n"
+"\n"
+"vec3 apply_fog( vec3 vfrag, float fdist )\n"
+"{\n"
+"   float dist = pow(fdist*0.0008,1.2);\n"
+"   return mix( vfrag, vec3(0.55,0.76,1.0), min( 1.0, dist ) );\n"
+"}\n"
+"\n"
+"#line     15        0 \n"
+"\n"
+"void main()\n"
+"{\n"
+"   vec3 vfrag = vec3(0.5,0.5,0.5);\n"
+"   vec4 vsamplemain = texture( uTexMain, aUv );\n"
+"   vec3 qnorm = normalize(aNorm);\n"
+"\n"
+"   if( vsamplemain.a < 0.15 )\n"
+"     discard;\n"
+"\n"
+"   vfrag = vsamplemain.rgb;\n"
+"\n"
+"   if( g_light_preview == 1 )\n"
+"   {\n"
+"      vfrag = vec3(0.5);\n"
+"   }\n"
+"\n"
+"   // Lighting\n"
+"   vec3 halfview = uCamera - aWorldCo;\n"
+"   float fdist = length( halfview );\n"
+"   halfview /= fdist;\n"
+"\n"
+"   vfrag = do_light_diffuse( vfrag, qnorm );\n"
+"   vfrag = do_light_spec( vfrag, qnorm, halfview, 0.1 );\n"
+"   vfrag = do_light_shadowing( vfrag );\n"
+"   vfrag = apply_fog( vfrag, fdist );\n"
+"\n"
+"   FragColor = vec4(vfrag, 1.0);\n"
+"}\n"
+""},
+};
+
+static GLuint _uniform_alphatest_uPv;
+static GLuint _uniform_alphatest_uMdl;
+static GLuint _uniform_alphatest_uTexGarbage;
+static GLuint _uniform_alphatest_uTexMain;
+static GLuint _uniform_alphatest_uCamera;
+static GLuint _uniform_alphatest_uPlane;
+static GLuint _uniform_alphatest_g_world_depth;
+static void shader_alphatest_uPv(m4x4f m){
+   glUniformMatrix4fv( _uniform_alphatest_uPv, 1, GL_FALSE, (float *)m );
+}
+static void shader_alphatest_uMdl(m4x3f m){
+   glUniformMatrix4x3fv( _uniform_alphatest_uMdl, 1, GL_FALSE, (float *)m );
+}
+static void shader_alphatest_uTexGarbage(int i){
+   glUniform1i( _uniform_alphatest_uTexGarbage, i );
+}
+static void shader_alphatest_uTexMain(int i){
+   glUniform1i( _uniform_alphatest_uTexMain, i );
+}
+static void shader_alphatest_uCamera(v3f v){
+   glUniform3fv( _uniform_alphatest_uCamera, 1, v );
+}
+static void shader_alphatest_uPlane(v4f v){
+   glUniform4fv( _uniform_alphatest_uPlane, 1, v );
+}
+static void shader_alphatest_g_world_depth(int i){
+   glUniform1i( _uniform_alphatest_g_world_depth, i );
+}
+static void shader_alphatest_register(void){
+   vg_shader_register( &_shader_alphatest );
+}
+static void shader_alphatest_use(void){ glUseProgram(_shader_alphatest.id); }
+static void shader_alphatest_link(void){
+   _uniform_alphatest_uPv = glGetUniformLocation( _shader_alphatest.id, "uPv" );
+   _uniform_alphatest_uMdl = glGetUniformLocation( _shader_alphatest.id, "uMdl" );
+   _uniform_alphatest_uTexGarbage = glGetUniformLocation( _shader_alphatest.id, "uTexGarbage" );
+   _uniform_alphatest_uTexMain = glGetUniformLocation( _shader_alphatest.id, "uTexMain" );
+   _uniform_alphatest_uCamera = glGetUniformLocation( _shader_alphatest.id, "uCamera" );
+   _uniform_alphatest_uPlane = glGetUniformLocation( _shader_alphatest.id, "uPlane" );
+   _uniform_alphatest_g_world_depth = glGetUniformLocation( _shader_alphatest.id, "g_world_depth" );
+}
+#endif /* SHADER_alphatest_H */
diff --git a/shaders/std_alphatest.fs b/shaders/std_alphatest.fs
new file mode 100644 (file)
index 0000000..7881b51
--- /dev/null
@@ -0,0 +1,43 @@
+out vec4 FragColor;
+
+uniform sampler2D uTexGarbage;
+uniform sampler2D uTexMain;
+uniform vec3 uCamera;
+uniform vec4 uPlane;
+
+in vec4 aColour;
+in vec2 aUv;
+in vec3 aNorm;
+in vec3 aCo;
+in vec3 aWorldCo;
+
+#include "common_world.glsl"
+
+void main()
+{
+   vec3 vfrag = vec3(0.5,0.5,0.5);
+   vec4 vsamplemain = texture( uTexMain, aUv );
+   vec3 qnorm = normalize(aNorm);
+
+   if( vsamplemain.a < 0.15 )
+     discard;
+
+   vfrag = vsamplemain.rgb;
+
+   if( g_light_preview == 1 )
+   {
+      vfrag = vec3(0.5);
+   }
+
+   // Lighting
+   vec3 halfview = uCamera - aWorldCo;
+   float fdist = length( halfview );
+   halfview /= fdist;
+
+   vfrag = do_light_diffuse( vfrag, qnorm );
+   vfrag = do_light_spec( vfrag, qnorm, halfview, 0.1 );
+   vfrag = do_light_shadowing( vfrag );
+   vfrag = apply_fog( vfrag, fdist );
+
+   FragColor = vec4(vfrag, 1.0);
+}
index 75eed174b3145681621a88296205008cc4840f69..a30f83c9ee29f12d5020f206c92b649c6da1821d 100644 (file)
@@ -3,7 +3,6 @@ out vec4 FragColor;
 uniform sampler2D uTexGarbage;
 uniform sampler2D uTexGradients;
 uniform vec3 uCamera;
-uniform vec4 uPlane;
 
 in vec4 aColour;
 in vec2 aUv;
index 5738ef11c135e48f4cb385dedc450e18690d36bf..53154f6370469c6ccc0518109af339d220d40f98 100644 (file)
@@ -45,7 +45,6 @@ static struct vg_shader _shader_vblend = {
 "uniform sampler2D uTexGarbage;\n"
 "uniform sampler2D uTexGradients;\n"
 "uniform vec3 uCamera;\n"
-"uniform vec4 uPlane;\n"
 "\n"
 "in vec4 aColour;\n"
 "in vec2 aUv;\n"
@@ -151,7 +150,7 @@ static struct vg_shader _shader_vblend = {
 "   return mix( vfrag, vec3(0.55,0.76,1.0), min( 1.0, dist ) );\n"
 "}\n"
 "\n"
-"#line     15        0 \n"
+"#line     14        0 \n"
 "\n"
 "void main()\n"
 "{\n"
@@ -203,7 +202,6 @@ static GLuint _uniform_vblend_uMdl;
 static GLuint _uniform_vblend_uTexGarbage;
 static GLuint _uniform_vblend_uTexGradients;
 static GLuint _uniform_vblend_uCamera;
-static GLuint _uniform_vblend_uPlane;
 static GLuint _uniform_vblend_g_world_depth;
 static void shader_vblend_uPv(m4x4f m){
    glUniformMatrix4fv( _uniform_vblend_uPv, 1, GL_FALSE, (float *)m );
@@ -220,9 +218,6 @@ static void shader_vblend_uTexGradients(int i){
 static void shader_vblend_uCamera(v3f v){
    glUniform3fv( _uniform_vblend_uCamera, 1, v );
 }
-static void shader_vblend_uPlane(v4f v){
-   glUniform4fv( _uniform_vblend_uPlane, 1, v );
-}
 static void shader_vblend_g_world_depth(int i){
    glUniform1i( _uniform_vblend_g_world_depth, i );
 }
@@ -236,7 +231,6 @@ static void shader_vblend_link(void){
    _uniform_vblend_uTexGarbage = glGetUniformLocation( _shader_vblend.id, "uTexGarbage" );
    _uniform_vblend_uTexGradients = glGetUniformLocation( _shader_vblend.id, "uTexGradients" );
    _uniform_vblend_uCamera = glGetUniformLocation( _shader_vblend.id, "uCamera" );
-   _uniform_vblend_uPlane = glGetUniformLocation( _shader_vblend.id, "uPlane" );
    _uniform_vblend_g_world_depth = glGetUniformLocation( _shader_vblend.id, "g_world_depth" );
 }
 #endif /* SHADER_vblend_H */
diff --git a/textures/alphatest.png b/textures/alphatest.png
new file mode 100644 (file)
index 0000000..d27c828
Binary files /dev/null and b/textures/alphatest.png differ
diff --git a/vg.conf b/vg.conf
index f5874ed041895dcfd1148a4470a01f4985c21cea..42c7f0d456e57008c973ca698770c50976f30916 100644 (file)
--- a/vg.conf
+++ b/vg.conf
@@ -5,6 +5,7 @@ shader blit blit.vs blit.fs
 shader fscolour blit.vs colour.fs
 shader terrain standard.vs terrain.fs
 shader vblend standard.vs vblend.fs
+shader alphatest standard.vs std_alphatest.fs
 shader standard standard.vs standard.fs
 shader unlit standard.vs unlit.fs
 shader character character.vs character.fs
diff --git a/world.h b/world.h
index e8c47d22085b8edad828aeccdaac3082739d97a0..45003cccbe9d87afa6b4fed9c0912bfec860d454 100644 (file)
--- a/world.h
+++ b/world.h
@@ -14,7 +14,9 @@ static int ray_world( v3f pos, v3f dir, ray_hit *hit );
 #include "bvh.h"
 #include "lighting.h"
 #include "model.h"
-#include "traffic.h"
+
+#include "traffic.h" /*TODO: -> world_traffic.h */
+#include "world_routes.h"
 
 #include "shaders/terrain.h"
 #include "shaders/sky.h"
@@ -23,6 +25,7 @@ static int ray_world( v3f pos, v3f dir, ray_hit *hit );
 #include "shaders/vblend.h"
 #include "shaders/gpos.h"
 #include "shaders/fscolour.h"
+#include "shaders/alphatest.h"
 
 static struct gworld
 {
@@ -36,6 +39,13 @@ static struct gworld
    spawns[32];
    u32 spawn_count;
 
+   struct subworld_routes routes;
+
+   /* ...
+   struct subworld_spawns system_spawns;
+   struct subworld_physics system_physics;
+   */
+
    teleport_gate gates[64];
    u32 gate_count;
 
@@ -43,17 +53,18 @@ static struct gworld
    traffic_node traffic[128];
    u32 traffic_count;
 
+#if 0
    traffic_driver van_man[6];
+#endif
    
    /* Physics */
-   rigidbody temp_rbs[128];
-   u32 rb_count;
    
    /* Rendering & geometry */
    scene geo, foliage;
    rigidbody rb_geo;
 
-   mdl_submesh sm_geo_std_oob, sm_geo_std, sm_geo_vb;
+   mdl_submesh sm_geo_std_oob, sm_geo_std, sm_geo_vb,
+               sm_foliage_main, sm_foliage_alphatest;
 
    glmesh skybox, skydome;
    mdl_submesh dome_upper, dome_lower;
@@ -76,12 +87,18 @@ static struct gworld
 }
 world;
 
+static struct subworld_routes *subworld_routes(void) { return &world.routes; }
+
+
 vg_tex2d tex_terrain_colours = { .path = "textures/gradients.qoi",
                                  .flags = VG_TEXTURE_CLAMP|VG_TEXTURE_NEAREST };
 
 vg_tex2d tex_terrain_noise = { .path = "textures/garbage.qoi",
                                  .flags = VG_TEXTURE_NEAREST };
 
+vg_tex2d tex_alphatest = { .path = "textures/alphatest.qoi",
+                                 .flags = VG_TEXTURE_NEAREST };
+
 static void ray_world_get_tri( ray_hit *hit, v3f tri[3] )
 {
    for( int i=0; i<3; i++ )
@@ -105,6 +122,7 @@ static void world_register(void)
    shader_planeinf_register();
    shader_gpos_register();
    shader_fscolour_register();
+   shader_alphatest_register();
 }
 
 static void world_free(void)
@@ -153,9 +171,8 @@ static void add_all_if_material( m4x3f transform, scene *pscene,
    }
 }
 
-static void world_apply_foliage(void)
+static void world_apply_procedural_foliage(void)
 {
-   scene_init( &world.foliage );
    mdl_header *mfoliage = mdl_load("models/rs_foliage.mdl");
 
    v3f volume;
@@ -197,8 +214,6 @@ static void world_apply_foliage(void)
          }
       }
    }
-
-   scene_upload( &world.foliage );
    free( mfoliage );
 }
 
@@ -208,7 +223,6 @@ static void world_load(void)
 
    world.spawn_count = 0;
    world.gate_count = 0;
-   world.rb_count = 0;
    world.traffic_count = 0;
    world.instance_cache = NULL;
    
@@ -221,6 +235,7 @@ static void world_load(void)
       
       if( pnode->classtype == k_classtype_none )
       {}
+#if 0
       else if( pnode->classtype == k_classtype_gate )
       {
          struct classtype_gate *entgate = mdl_get_entdata( mworld, pnode );
@@ -240,21 +255,7 @@ static void world_load(void)
             gate_transform_update( gate );
          }
       }
-      else if( pnode->classtype == k_classtype_block )
-      {
-         struct classtype_block *block = mdl_get_entdata( mworld, pnode );
-
-         m4x3f transform;
-         mdl_node_transform( pnode, transform );
-
-         rigidbody *rb = &world.temp_rbs[ world.rb_count ++ ];
-
-         box_copy( block->bbx, rb->bbx ); /* TODO: apply scale */
-         v3_copy( pnode->co, rb->co );
-         rb_init( rb );
-         v4_copy( pnode->q, rb->q );
-         rb_update_transform( rb );
-      }
+#endif
       else if( pnode->classtype == k_classtype_spawn )
       {
          struct respawn_point *rp = &world.spawns[ world.spawn_count ++ ];
@@ -345,15 +346,17 @@ static void world_load(void)
          }
       }
    }
-   
+
    world.instance_cache = buffer_fix( world.instance_cache, 
                                       world.instance_cache_count,
                                      &world.instance_cache_cap,
                                      sizeof( struct instance_cache ) );
 
+#if 0
    traffic_finalize( world.traffic, world.traffic_count );
    for( int i=0; i<vg_list_size(world.van_man); i++ )
       world.van_man[i].current =&world.traffic[vg_randint(world.traffic_count)];
+#endif
 
    /* 
     * Compile meshes into the world scenes
@@ -362,7 +365,8 @@ static void world_load(void)
 
    u32 mat_surf = 0,
        mat_surf_oob = 0,
-       mat_vertex_blend = 0;
+       mat_vertex_blend = 0,
+       mat_alphatest = 0;
 
    for( int i=1; i<mworld->material_count; i++ )
    {
@@ -375,11 +379,10 @@ static void world_load(void)
          mat_surf_oob = i;
       else if( !strcmp( "vertex_blend", mat_name ))
          mat_vertex_blend = i;
+      else if( !strcmp( "alphatest", mat_name ))
+         mat_alphatest = i;
    }
 
-   vg_info( "surf %d\noob %d\nvert_blend %d\n", mat_surf, mat_surf_oob,
-                                                mat_vertex_blend );
-
    m4x3f midentity;
    m4x3_identity( midentity );
 
@@ -400,7 +403,24 @@ static void world_load(void)
    scene_upload( &world.geo );
    scene_bh_create( &world.geo );
 
-   world_apply_foliage();
+
+   /* Foliage /nocollide layer.
+    * TODO: Probably should have material traits for this
+    */
+   scene_init( &world.foliage );
+
+   world_apply_procedural_foliage();
+   scene_copy_slice( &world.foliage, &world.sm_foliage_main );
+
+   add_all_if_material( midentity, &world.foliage, mworld, mat_alphatest );
+   scene_copy_slice( &world.foliage, &world.sm_foliage_alphatest );
+
+   scene_upload( &world.foliage );
+   world_routes_init( mworld );
+
+   for( int i=0; i<world.instance_cache_count; i++ )
+      free( world.instance_cache[i].mdl );
+
    free( world.instance_cache );
    free( mworld );
 
@@ -465,12 +485,24 @@ static void world_load(void)
 
    q_identity(world.mr_ball.q);
    rb_init( &world.mr_ball );
+
+   /*
+    * Setup scene collider 
+    */
+   v3_zero( world.rb_geo.co );
+   q_identity( world.rb_geo.q );
+
+   world.rb_geo.type = k_rb_shape_scene;
+   world.rb_geo.inf.scene.pscene = &world.geo;
+   world.rb_geo.is_world = 1;
+   rb_init( &world.rb_geo );
 }
 
 static void world_init(void)
 {
    vg_tex2d_init( (vg_tex2d *[]){ &tex_terrain_colours, 
-                                  &tex_terrain_noise }, 2 );
+                                  &tex_terrain_noise,
+                                  &tex_alphatest }, 3 );
 
    mdl_header *mcars = mdl_load( "models/rs_cars.mdl" );
    mdl_unpack_glmesh( mcars, &world.cars );
@@ -488,21 +520,12 @@ static void world_init(void)
    world.dome_lower = *mdl_node_submesh( msky, nlower, 0 );
    world.dome_upper = *mdl_node_submesh( msky, nupper, 0 );
    free(msky);
-
-   /*
-    * Setup scene collider 
-    */
-   v3_zero( world.rb_geo.co );
-   q_identity( world.rb_geo.q );
-
-   world.rb_geo.type = k_rb_shape_scene;
-   world.rb_geo.inf.scene.pscene = &world.geo;
-   world.rb_geo.is_world = 1;
-   rb_init( &world.rb_geo );
 }
 
 static void world_update(void)
 {
+   world_routes_debug();
+
 #if 0
    rb_solver_reset();
    rb_build_manifold_terrain_sphere( &world.mr_ball );
@@ -513,13 +536,13 @@ static void world_update(void)
    rb_iter( &world.mr_ball );
    rb_update_transform( &world.mr_ball );
    rb_debug( &world.mr_ball, 0 );
-#endif
 
    for( int i=0; i<vg_list_size(world.van_man); i++ )
    {
       traffic_drive( &world.van_man[i] );
       traffic_visualize_car( &world.van_man[i] );
    }
+#endif
 }
 
 /*
@@ -552,11 +575,36 @@ static void render_world_vb( m4x4f projection, v3f camera )
 
    mesh_bind( &world.cars );
 
+#if 0
    for( int i=0; i<vg_list_size(world.van_man); i++ )
    {
       shader_vblend_uMdl( world.van_man[i].transform );
       mdl_draw_submesh( &world.car_holden );
    }
+#endif
+}
+
+static void render_world_alphatest( m4x4f projection, v3f camera )
+{
+   m4x3f identity_matrix;
+   m4x3_identity( identity_matrix );
+
+   shader_alphatest_use();
+   shader_alphatest_uTexGarbage(0);
+   shader_alphatest_uTexMain(1);
+   shader_link_standard_ub( _shader_alphatest.id, 2 );
+
+   vg_tex2d_bind( &tex_terrain_noise, 0 );
+   vg_tex2d_bind( &tex_alphatest, 1 );
+
+   shader_alphatest_uPv( projection );
+   shader_alphatest_uMdl( identity_matrix );
+   shader_alphatest_uCamera( camera );
+
+   glDisable(GL_CULL_FACE);
+   scene_bind( &world.foliage );
+   mdl_draw_submesh( &world.sm_foliage_alphatest );
+   glEnable(GL_CULL_FACE);
 }
 
 static void render_terrain( m4x4f projection, v3f camera )
@@ -578,9 +626,10 @@ static void render_terrain( m4x4f projection, v3f camera )
    mdl_draw_submesh( &world.sm_geo_std_oob );
    mdl_draw_submesh( &world.sm_geo_std );
 
+   /* TODO: Dont draw in reflection */
    glDisable(GL_CULL_FACE);
    scene_bind( &world.foliage );
-   scene_draw( &world.foliage );
+   mdl_draw_submesh( &world.sm_foliage_main );
    glEnable(GL_CULL_FACE);
 }
 
@@ -643,6 +692,7 @@ static void render_world( m4x4f projection, m4x3f camera )
 {
    render_sky( camera );
    render_world_vb( projection, camera[3] );
+   render_world_alphatest( projection, camera[3] );
    render_terrain( projection, camera[3] );
 }
 
diff --git a/world_routes.h b/world_routes.h
new file mode 100644 (file)
index 0000000..429c0fb
--- /dev/null
@@ -0,0 +1,189 @@
+#ifndef ROUTES_H
+#define ROUTES_H
+
+#include "common.h"
+#include "model.h"
+#include "gate.h"
+
+struct subworld_routes
+{
+   struct route_node
+   {
+      v3f co, right, h;
+      u32 next[2];
+
+      u32 is_gate, gate_id;
+   }
+   *nodes;
+
+   u32 node_count,
+       node_cap;
+
+   struct route
+   {
+      const char *name;
+      v4f colour;
+
+      u32 start;
+   }
+   *routes;
+
+   u32 route_count,
+       route_cap;
+
+   struct route_gate
+   {
+      teleport_gate gate;
+      
+      u32 route_ids[4];    /* Gates can be linked into up to four routes */
+      u32 route_count,
+          node_id;
+   }
+   *gates;
+
+   u32 gate_count,
+       gate_cap;
+};
+
+static struct subworld_routes *subworld_routes(void);
+
+/* 
+ * TODO list:
+ *      when a gate is passed through it needs to trigger into an active state
+ */
+
+static void world_routes_debug(void)
+{
+   struct subworld_routes *r = subworld_routes();
+
+   for( int i=0; i<r->node_count; i++ )
+   {
+      struct route_node *rn = &r->nodes[i];
+      vg_line_pt3( rn->co, 1.0f, rn->is_gate? 0xffffff00: 0xff00b2ff );
+   }
+}
+
+static void world_routes_free(void)
+{
+   struct subworld_routes *r = subworld_routes();
+
+   free( r->nodes );
+   free( r->routes );
+   free( r->gates );
+}
+
+static void world_id_fixup( u32 *uid, mdl_header *mdl )
+{
+   if( *uid )
+      *uid = mdl_node_from_id( mdl, *uid )->sub_uid;
+}
+
+static void world_routes_init( mdl_header *mdl )
+{
+   struct subworld_routes *r = subworld_routes();
+   r->nodes = NULL;
+   r->node_count = 0;
+   r->node_cap = 0;
+   r->routes = NULL;
+   r->route_count = 0;
+   r->route_cap = 0;
+   r->gates = NULL;
+   r->gate_count = 0;
+   r->gate_cap = 0;
+
+   for( int i=0; i<mdl->node_count; i++ )
+   {
+      mdl_node *pnode = mdl_node_from_id(mdl,i);
+      m4x3f transform;
+
+      if( pnode->classtype == k_classtype_route_node ||
+          pnode->classtype == k_classtype_gate )
+      {
+         mdl_node_transform( pnode, transform );
+         pnode->sub_uid = r->node_count;
+         
+         r->nodes = buffer_reserve( r->nodes, r->node_count, &r->node_cap, 1,
+                                    sizeof( struct route_node ) );
+
+         struct route_node *rn = &r->nodes[r->node_count];
+
+         v3_copy( transform[0], rn->right );
+         v3_normalize( rn->right );
+         v3_copy( transform[2], rn->h );
+         v3_copy( transform[3], rn->co );
+
+         if( pnode->classtype == k_classtype_gate )
+         {
+            r->gates = buffer_reserve( r->gates, r->gate_count, &r->gate_cap,
+                                       1, sizeof( struct route_gate ) );
+
+            struct classtype_gate *inf = mdl_get_entdata( mdl, pnode );
+
+            /* H is later scaled based on link distance */
+            v3_normalize( rn->h );
+            rn->next[0] = inf->target;
+            rn->next[1] = 0;
+            rn->gate_id = r->gate_count;
+            rn->is_gate = 1;
+
+            struct route_gate *rg = &r->gates[r->gate_count];
+            rg->node_id = r->node_count;
+            
+            /* TODO */
+
+            r->gate_count ++;
+         }
+         else
+         {
+            struct classtype_route_node *inf = mdl_get_entdata( mdl, pnode );
+            rn->next[0] = inf->target;
+            rn->next[1] = inf->target1;
+            rn->is_gate = 0;
+         }
+
+         r->node_count ++;
+      }
+      else if( pnode->classtype == k_classtype_route )
+      {
+         struct classtype_route *inf = mdl_get_entdata( mdl, pnode );
+         r->routes = buffer_reserve( r->routes, r->route_count, &r->route_cap,
+                                     1, sizeof( struct route ) );
+
+         struct route *route = &r->routes[r->route_count];
+
+         v4_zero( route->colour );
+         route->name = NULL;
+         route->start = inf->id_start;
+
+         r->route_count ++;
+      }
+   }
+
+   /* 
+    * Apply correct system-local ids
+    */
+   for( int i=0; i<r->node_count; i++ )
+   {
+      struct route_node *rn = &r->nodes[i];
+      
+      for( int j=0; j<2; j++ )
+         world_id_fixup( &rn->next[j], mdl );
+
+      if( rn->is_gate )
+         world_id_fixup( &rn->gate_id, mdl );
+   }
+
+   for( int i=0; i<r->gate_count; i++ )
+   {
+      struct route_gate *rg = &r->gates[i];
+      world_id_fixup( &rg->node_id, mdl );
+   }
+
+   for( int i=0; i<r->route_count; i++ )
+   {
+      struct route *route = &r->routes[i];
+      world_id_fixup( &route->start, mdl );
+   }
+}
+
+#endif /* ROUTES_H */