bugs/map edits
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_export.py
index cf6d3f7dd9d4fcde68765723ec608ed3ffa2291a..d0a1c8aa50932b67a7cd4389865dedc983aed23c 100644 (file)
@@ -2,7 +2,7 @@
 # =============================================================================
 # 
 # Copyright  .        . .       -----, ,----- ,---.   .---.
-# 2021-2022  |\      /| |           /  |      |    | |    /|
+# 2021-2023  |\      /| |           /  |      |    | |    /|
 #            | \    / | +--        /   +----- +---'  |   / |
 #            |  \  /  | |         /    |      |   \  |  /  |
 #            |   \/   | |        /     |      |    \ | /   |
@@ -255,6 +255,40 @@ class classtype_gate(Structure):
    #}
 #}
 
+class classtype_nonlocal_gate(classtype_gate):
+#{
+   def encode_obj(_,node,node_def):
+   #{
+      node.classtype = 300
+
+      obj = node_def['obj']
+      _.target = encoder_process_pstr( node_def['obj'].cv_data.strp )
+
+      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]
+      #}
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      layout.prop( obj.cv_data, "strp", text="Nonlocal ID" )
+
+      mesh = obj.data
+      layout.label( text=F"(i) Data is stored in {mesh.name}" )
+      layout.prop( mesh.cv_data, "v0", text="Gate dimensions" )
+   #}
+#}
+
 # Classtype 3
 #
 #  Purpose: player can reset here, its a safe place
@@ -548,13 +582,14 @@ class classtype_skeleton(Structure):
 class classtype_bone(Structure):
 #{
    _pack_ = 1
-   _fields_ = [("deform",c_uint32),
+   _fields_ = [("flags",c_uint32),
                ("ik_target",c_uint32),
                ("ik_pole",c_uint32),
-               ("collider",c_uint32),
-               ("use_limits",c_uint32),
-               ("angle_limits",(c_float*3)*2),
-               ("hitbox",(c_float*3)*2)]
+               ("hitbox",(c_float*3)*2),
+               ("conevx",c_float*3),
+               ("conevy",c_float*3),
+               ("coneva",c_float*3),
+               ("conet",c_float)]
 
    def encode_obj(_, node,node_def):
    #{
@@ -563,19 +598,24 @@ class classtype_bone(Structure):
       armature_def = node_def['linked_armature']
       obj = node_def['bone']
       
-      _.deform = node_def['deform']
+      _.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:
+      if obj.cv_data.collider != 'collider_none':
       #{
-         _.collider = 1
+         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]
@@ -586,13 +626,17 @@ class classtype_bone(Structure):
 
       if obj.cv_data.con0:
       #{
-         _.use_limits = 1 
-         _.angle_limits[0][0] =  obj.cv_data.mins[0]
-         _.angle_limits[0][1] =  obj.cv_data.mins[2]
-         _.angle_limits[0][2] = -obj.cv_data.maxs[1]
-         _.angle_limits[1][0] =  obj.cv_data.maxs[0]
-         _.angle_limits[1][1] =  obj.cv_data.maxs[2]
-         _.angle_limits[1][2] = -obj.cv_data.mins[1]
+         _.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
       #}
    #}
 #}
@@ -619,8 +663,10 @@ class classtype_trigger(Structure):
       global cv_view_verts, cv_view_colours
       cv_draw_ucube( obj.matrix_world, [0,1,0,1] )
 
+      white = (1,1,1,1)
+
       if obj.cv_data.target:
-         cv_draw_arrow( obj.location, obj.cv_data.target.location, [1,1,1,1] )
+         cv_draw_arrow( obj.location, obj.cv_data.target.location, white, 0.7 )
    #}
 
    @staticmethod
@@ -654,7 +700,340 @@ class classtype_logic_achievement(Structure):
    #}
 #}
 
-# Classtype 102
+class union_128bit_data(Union):
+#{
+   _pack_ = 1
+   _fields_ = [("f32",c_float),
+               ("u32",c_uint32),
+               ("i32",c_int32),
+               ("v4f",c_float*4)]
+#}
+
+# Class type 105
+#
+#  Purpose:
+#
+class classtype_logic_wire(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("next",c_uint32),
+               ("function",c_uint32),
+               ("data",union_128bit_data),
+               ("data_type",c_uint32),
+               ("enabled",c_uint32)]
+
+   function_enum = [('0',"pass along",""),
+                    ('1',"enable",""),
+                    ('2',"disable",""),
+                    ('3',"","")]
+
+   def encode_obj(_,node,node_def):
+   #{
+      node.classtype = 105
+
+      obj = node_def['obj']
+
+      if obj.cv_data.target:  _.next = obj.cv_data.target.cv_data.uid
+
+      _.data_type = obj.cv_data.intp1
+      _.function = int(obj.cv_data.function)
+      _.enabled = obj.cv_data.bp0
+
+      if _.data_type == 1:                  # an integer
+         _.data.i32 = obj.cv_data.intp
+      elif _.data_type == 2:                # a number
+         _.data.f32 = obj.cv_data.fltp
+      elif _.data_type == 3:                # a target
+         if obj.cv_data.target2:
+            _.data.u32 = obj.cv_data.target2.cv_data.uid
+      elif _.data_type == 4:                # a string
+         _.data.u32 = encoder_process_pstr( obj.cv_data.strp )
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      layout.prop( obj.cv_data, "bp0", text="Start disabled" )
+      box = layout.box()
+      box.label( text="Target" )
+      box.prop( obj.cv_data, "target", text="connection" )
+
+      row = box.row()
+      if not obj.cv_data.target:
+         row.enabled=False
+      row.prop( obj.cv_data, "function", text="function" )
+
+      box = layout.box()
+      box.label( text="Data packet" )
+      box.prop( obj.cv_data, "intp1", text="type" )
+
+      if obj.cv_data.intp1 == 1:
+         box.prop( obj.cv_data, "intp", text="Signed Integer" )
+      elif obj.cv_data.intp1 == 2:
+         box.prop( obj.cv_data, "fltp", text="Float" )
+      elif obj.cv_data.intp1 == 3:
+         box.prop( obj.cv_data, "target2", text="Object reference" )
+      elif obj.cv_data.intp1 == 4:
+         box.prop( obj.cv_data, "strp", text="String" )
+      else:
+      #{
+         row = box.row()
+         row.enabled=False
+         row.label( text="this wire will not impart any data" )
+      #}
+   #}
+
+   @staticmethod
+   def draw_scene_helpers( obj ):
+   #{
+      global cv_view_verts, cv_view_colours
+
+      white = (1,1,1,1)
+      purple = (0.5,0.2,1,1)
+
+      if obj.cv_data.target:
+         cv_draw_arrow( obj.location, obj.cv_data.target.location, white, 0.7 )
+      if (obj.cv_data.target2) and (obj.cv_data.intp1 == 3):
+         cv_draw_arrow( obj.cv_data.target2.location, obj.location,purple, 0.7 )
+   #}
+
+   @staticmethod
+   def get_targeted_methods( scene, context ):
+   #{
+      obj = context.object
+      invalid = [('0',"",""),
+                 ('1',"",""),
+                 ('2',"",""),
+                 ('3',"","")]
+
+      if obj.cv_data.target:
+      #{
+         classtype = obj.cv_data.target.cv_data.classtype
+         if classtype == 'classtype_none' or classtype not in globals():
+         #{
+            return invalid
+         #}
+         else:
+         #{
+            cl = globals()[ classtype ]
+            if getattr( cl, "function_enum", None ):
+            #{
+               return cl.function_enum
+            #}
+            else:
+            #{
+               return invalid
+            #}
+         #}
+      #}
+      else:
+      #{
+         return invalid
+      #}
+   #}
+#}
+
+# Class type 108
+#
+#  Purpose:
+#
+class classtype_particle_box(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("target",c_uint32),
+               ("rate",c_float)]
+
+   function_enum = [('0',"set rate",""),
+                    ('1',"",""),
+                    ('2',"",""),
+                    ('3',"","")]
+
+   def encode_obj(_, node,node_def ):
+   #{
+      node.classtype = 108
+
+      obj = node_def['obj']
+
+      _.rate = obj.cv_data.fltp
+      if obj.cv_data.target:
+         _.target = obj.cv_data.target.cv_data.uid
+   #}
+
+   @staticmethod
+   def draw_scene_helpers( obj ):
+   #{
+      global cv_view_verts, cv_view_colours
+      cv_draw_ucube( obj.matrix_world, [1,0.8,0,1] )
+
+      white = (1,1,1,1)
+      if obj.cv_data.target:
+         cv_draw_arrow( obj.location, obj.cv_data.target.location, white, 0.7 )
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      layout.prop( obj.cv_data, "target", text="Triggers" )
+      layout.prop( obj.cv_data, "fltp", text="count per second" )
+   #}
+#}
+
+# Class type 109
+#
+#  Purpose:
+#
+class classtype_signal_splitter(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("next",c_uint32*4)]
+
+   function_enum = [('0',"pass along",""),
+                    ('1',"",""),
+                    ('2',"",""),
+                    ('3',"","")]
+
+   def encode_obj(_,node,node_def):
+   #{
+      node.classtype = 109
+
+      obj = node_def['obj']
+
+      if obj.cv_data.target:   _.next[0] = obj.cv_data.target.cv_data.uid
+      if obj.cv_data.target1:  _.next[1] = obj.cv_data.target1.cv_data.uid
+      if obj.cv_data.target2:  _.next[2] = obj.cv_data.target2.cv_data.uid
+      if obj.cv_data.target3:  _.next[3] = obj.cv_data.target3.cv_data.uid
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      layout.label( text="The split signals will run in order" )
+      layout.prop( obj.cv_data, "target", text="#0" )
+      layout.prop( obj.cv_data, "target1", text="#1" )
+      layout.prop( obj.cv_data, "target2", text="#2" )
+      layout.prop( obj.cv_data, "target3", text="#3" )
+   #}
+
+   @staticmethod
+   def draw_scene_helpers( obj ):
+   #{
+      global cv_view_verts, cv_view_colours
+
+      c0 = (1,0.5,0.2,1)
+      c1 = (0.8,1,0.1,1)
+      c2 = (0.3,0.9,0.4,1)
+      c3 = (0.1,0.4,1.0,1)
+
+      if obj.cv_data.target:
+         cv_draw_arrow( obj.location, obj.cv_data.target.location, c0, 0.7 )
+      if obj.cv_data.target1:
+         cv_draw_arrow( obj.location, obj.cv_data.target1.location, c1, 0.7 )
+      if obj.cv_data.target2:
+         cv_draw_arrow( obj.location, obj.cv_data.target2.location, c2, 0.7 )
+      if obj.cv_data.target3:
+         cv_draw_arrow( obj.location, obj.cv_data.target3.location, c3, 0.7 )
+   #}
+#}
+
+# Class type 106
+#
+#  Purpose:
+#
+class classtype_soundscape(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("max_instances",c_uint32),
+               ("allow_transitions",c_uint32),
+               ("transition_duration",c_float),
+               ("label",c_uint32)]
+
+   function_enum = [('0',"play",""),
+                    ('1',"set position",""),
+                    ('2',"",""),
+                    ('3',"","")]
+
+   def encode_obj(_,node,node_def):
+   #{
+      node.classtype = 106
+
+      obj = node_def['obj']
+
+      _.max_instances = obj.cv_data.intp
+      _.allow_transitions = obj.cv_data.bp0
+      _.transition_duration = obj.cv_data.fltp
+      _.label = encoder_process_pstr( obj.cv_data.strp )
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      layout.prop( obj.cv_data, "intp", text="max instances" )
+      layout.prop( obj.cv_data, "strp", text="label" )
+
+      box = layout.box()
+      box.label( text="If its a 3d sound, where can it spawn?" )
+      box.prop( obj.cv_data, "bp1", text="Only in water" )
+      box.prop( obj.cv_data, "bp2", text="Only on grass" )
+      box.prop( obj.cv_data, "bp3", text="Only on wood" )
+
+      box = layout.box()
+      box.prop( obj.cv_data, "bp0", text="allow transitions" )
+
+      row = box.row()
+      if not obj.cv_data.bp0:
+         row.enabled=False
+      row.prop( obj.cv_data, "fltp", text="transition duration" )
+   #}
+#}
+
+class classtype_logic_chances(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("targets",c_uint32*2),
+               ("p",c_float)]
+
+   function_enum = [('0',"pass along",""),
+                    ('1',"set ratio",""),
+                    ('2',"",""),
+                    ('3',"","")]
+
+   def encode_obj(_,node,node_def):
+   #{
+      node.classtype = 107
+
+      obj = node_def['obj']
+
+      if obj.cv_data.target:  _.targets[0] = obj.cv_data.target.cv_data.uid
+      if obj.cv_data.target1: _.targets[1] = obj.cv_data.target1.cv_data.uid
+      
+      _.p = obj.cv_data.fltp
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      box = layout.box()
+      box.prop( obj.cv_data, "target", text="red" )
+      box.prop( obj.cv_data, "target1", text="black" )
+      box.prop( obj.cv_data, "fltp", text="p(red)" )
+   #}
+
+   @staticmethod
+   def draw_scene_helpers( obj ):
+   #{
+      global cv_view_verts, cv_view_colours
+
+      red = (1,0,0,1)
+      black = (0,0,0,1)
+
+      if obj.cv_data.target:
+         cv_draw_arrow( obj.location, obj.cv_data.target.location, red, 0.7 )
+      if obj.cv_data.target1:
+         cv_draw_arrow( obj.location, obj.cv_data.target1.location, black, 0.7 )
+   #}
+#}
+
+# Classtype 102 [ DEPRECATED ]
 #
 #  Purpose: sends a signal to another entity
 #
@@ -712,7 +1091,6 @@ class classtype_logic_relay(Structure):
 #   volume: not used if has 3D flag
 #    flags: 
 #           AUDIO_FLAG_LOOP        0x1
-#           AUDIO_FLAG_ONESHOT     0x2  (DONT USE THIS, it breaks semaphores)
 #           AUDIO_FLAG_SPACIAL_3D  0x4  (Probably what you want)
 #           AUDIO_FLAG_AUTO_START  0x8  (Play when the world starts)
 #           ......
@@ -738,6 +1116,13 @@ class classtype_audio(Structure):
       if obj.cv_data.bp1: flags |= 0x4
       if obj.cv_data.bp2: flags |= 0x8
 
+      if obj.cv_data.audio_format == 'stereo':
+         flags |= 0x200
+      if obj.cv_data.audio_format == 'remain compressed':
+         flags |= 0x400
+      if obj.cv_data.audio_format == 'synthetic bird':
+         flags |= 0x1000
+
       _.flags = flags
       _.volume = obj.cv_data.fltp
    #}
@@ -745,11 +1130,14 @@ class classtype_audio(Structure):
    @staticmethod
    def editor_interface( layout, obj ):
    #{
-      layout.prop( obj.cv_data, "strp" )
+      layout.prop( obj.cv_data, "strp", text = "File (.ogg)" )
 
       layout.prop( obj.cv_data, "bp0", text = "Looping" )
       layout.prop( obj.cv_data, "bp1", text = "3D Audio" )
       layout.prop( obj.cv_data, "bp2", text = "Auto Start" )
+      layout.prop( obj.cv_data, "audio_format" )
+
+      layout.prop( obj.cv_data, "fltp", text = "Volume (0-1)" )
    #}
 
    @staticmethod
@@ -761,6 +1149,83 @@ class classtype_audio(Structure):
    #}
 #}
 
+
+# Classtype 200
+# 
+#  Purpose: world light
+#
+class classtype_world_light( Structure ):
+#{
+   _pack_ = 1
+   _fields_ = [("type",c_uint32),
+               ("colour",c_float*4),
+               ("angle",c_float),
+               ("range",c_float)]
+
+   def encode_obj(_, node, node_def):
+   #{
+      node.classtype = 200
+
+      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 data.cv_data.bp0:
+         _.type += 2
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      pass
+   #}
+#}
+
+# Classtype 201
+# 
+#  Purpose: lighting settings for world
+#
+class classtype_lighting_info(Structure):
+#{
+   _pack_ = 1
+   _fields_ = [("colours",(c_float*3)*3),
+               ("directions",(c_float*2)*3),
+               ("states",c_uint32*3),
+               ("shadow_spread",c_float),
+               ("shadow_length",c_float),
+               ("ambient",c_float*3)]
+
+   def encode_obj(_, node, node_def):
+   #{
+      node.classtype = 201
+
+      # TODO
+   #}
+
+   @staticmethod
+   def editor_interface( layout, obj ):
+   #{
+      pass
+   #}
+#}
+
 class classtype_spawn_link(Structure):
 #{
    _pack_ = 1
@@ -1009,8 +1474,8 @@ cxr_graph_mapping = \
          },
          "Mix":
          {
-            "Color1": material_tex_image("tex_diffuse"),
-            "Color2": material_tex_image("tex_decal")
+            "A": material_tex_image("tex_diffuse"),
+            "B": material_tex_image("tex_decal")
          },
       },
       "Normal":
@@ -1063,9 +1528,20 @@ def material_info(mat):
 
          if isinstance( link_def, dict ):
          #{
-            node_link = node.inputs[link]
+            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.is_linked:
+            if node_link and node_link.is_linked:
             #{
                # look for definitions for the connected node type
                #
@@ -1135,6 +1611,7 @@ def encoder_process_material( 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 mat.cv_data.shader == 'standard': dest.shader = 0
    if mat.cv_data.shader == 'standard_cutout': dest.shader = 1
@@ -1216,10 +1693,12 @@ def encoder_build_scene_graph( collection ):
 
    for obj in collection.all_objects:
    #{
-      if obj.parent: continue
+      #if obj.parent: continue
 
       def _extend( p, n, d ):
       #{
+         nonlocal collection
+
          uid = _new_uid()
          tree = {}
          tree["uid"] = uid
@@ -1228,7 +1707,7 @@ def encoder_build_scene_graph( collection ):
          tree["obj"] = n
          tree["parent"] = p
          n.cv_data.uid = uid
-         
+
          # Descend into amature
          #
          if n.type == 'ARMATURE':
@@ -1236,6 +1715,7 @@ def encoder_build_scene_graph( collection ):
             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.
@@ -1268,7 +1748,7 @@ def encoder_build_scene_graph( collection ):
                   #}
                #}
 
-               if n.cv_data.collider:
+               if n.cv_data.collider != 'collider_none':
                   tree['collider_count'] += 1
 
                btree['deform'] = n.use_deform
@@ -1284,7 +1764,6 @@ def encoder_build_scene_graph( collection ):
          #
          for obj1 in n.children:
          #{
-            nonlocal collection
             for c1 in obj1.users_collection:
             #{
                if c1 == collection:
@@ -1646,7 +2125,12 @@ def encoder_compile_armature( node, node_def ):
    # 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:
@@ -1784,10 +2268,14 @@ def encoder_process_definition( node_def ):
    #{
       obj      = node_def['obj']
       obj_type = obj.type
-      obj_co   = obj.location
+      obj_co   = obj.matrix_world @ Vector((0,0,0))
 
       if obj_type == 'ARMATURE':
          obj_classtype = 'classtype_skeleton'
+      elif obj_type == 'LIGHT':
+      #{
+         obj_classtype = 'classtype_world_light'
+      #}
       else:
       #{
          obj_classtype = obj.cv_data.classtype
@@ -2030,6 +2518,44 @@ def cv_draw_sphere( pos, radius, colour ):
    cv_draw_lines()
 #}
 
+# Draw axis alligned sphere at position with radius
+#
+def cv_draw_halfsphere( pos, tx, ty, tz, radius, colour ):
+#{
+   global cv_view_verts, cv_view_colours
+   
+   ly = pos + tz*radius
+   lx = pos + ty*radius
+   lz = pos + tz*radius
+   
+   pi = 3.14159265358979323846264
+
+   for i in range(16):
+   #{
+      t = ((i+1.0) * 1.0/16.0) * pi
+      s = math.sin(t)
+      c = math.cos(t)
+
+      s1 = math.sin(t*2.0)
+      c1 = math.cos(t*2.0)
+
+      py = pos + s*tx*radius +                c *tz*radius
+      px = pos + s*tx*radius + c *ty*radius 
+      pz = pos +               s1*ty*radius + c1*tz*radius
+
+      cv_view_verts += [ px, lx ]
+      cv_view_verts += [ py, ly ]
+      cv_view_verts += [ pz, lz ]
+
+      cv_view_colours += [ colour, colour, colour, colour, colour, colour ]
+
+      ly = py
+      lx = px
+      lz = pz
+   #}
+   cv_draw_lines()
+#}
+
 # Draw transformed -1 -> 1 cube
 #
 def cv_draw_ucube( transform, colour ):
@@ -2058,7 +2584,7 @@ def cv_draw_ucube( transform, colour ):
       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,0,1),(0,1,0,1)]
+      cv_view_colours += [colour, colour]
    #}
    cv_draw_lines()
 #}
@@ -2085,9 +2611,9 @@ def cv_draw_line2( p0, p1, c0, c1 ):
    cv_draw_lines()
 #}
 
-# Just the tx because we dont really need ty for this app
+# 
 #
-def cv_tangent_basis_tx( n, tx ):
+def cv_tangent_basis( n, tx, ty ):
 #{
    if abs( n[0] ) >= 0.57735027:
    #{
@@ -2103,11 +2629,16 @@ def cv_tangent_basis_tx( n, tx ):
    #}
 
    tx.normalize()
+   _ty = n.cross( tx )
+
+   ty[0] = _ty[0]
+   ty[1] = _ty[1]
+   ty[2] = _ty[2]
 #}
 
 # Draw coloured arrow
 #
-def cv_draw_arrow( p0, p1, c0 ):
+def cv_draw_arrow( p0, p1, c0, size=0.15 ):
 #{
    global cv_view_verts, cv_view_colours
 
@@ -2116,9 +2647,10 @@ def cv_draw_arrow( p0, p1, c0 ):
    n.normalize()
 
    tx = Vector((1,0,0))
-   cv_tangent_basis_tx( n, tx )
+   ty = Vector((1,0,0))
+   cv_tangent_basis( n, tx, ty )
    
-   cv_view_verts += [p0,p1, midpt+(tx-n)*0.15,midpt, midpt+(-tx-n)*0.15,midpt ]
+   cv_view_verts += [p0,p1, midpt+(tx-n)*size,midpt, midpt+(-tx-n)*size,midpt ]
    cv_view_colours += [c0,c0,c0,c0,c0,c0]
    cv_draw_lines()
 #}
@@ -2249,19 +2781,61 @@ def draw_limit( obj, center, major, minor, amin, amax, colour ):
    cv_draw_lines()
 #}
 
+# Cone and twist limit
+#
+def draw_cone_twist( center, vx, vy, va ):
+#{
+   global cv_view_verts, cv_view_colours
+   axis = vy.cross( vx )
+   axis.normalize()
+
+   size = 0.12
+
+   cv_view_verts += [center, center+va*size]
+   cv_view_colours += [ (1,1,1,1), (1,1,1,1) ]
+
+   for x in range(32):
+   #{
+      t0 = (x/32) * math.tau
+      t1 = ((x+1)/32) * math.tau
+
+      c0 = math.cos(t0)
+      s0 = math.sin(t0)
+      c1 = math.cos(t1)
+      s1 = math.sin(t1)
+      
+      p0 = center + (axis + vx*c0 + vy*s0).normalized() * size
+      p1 = center + (axis + vx*c1 + vy*s1).normalized() * size
+
+      col0 = ( abs(c0), abs(s0), 0.0, 1.0 )
+      col1 = ( abs(c1), abs(s1), 0.0, 1.0 )
+
+      cv_view_verts += [center, p0, p0, p1]
+      cv_view_colours += [ (0,0,0,0), col0, col0, col1 ]
+   #}
+
+   cv_draw_lines()
+#}
+
 # Draws constraints and stuff for the skeleton. This isnt documented and wont be
 #
 def draw_skeleton_helpers( obj ):
 #{
    global cv_view_verts, cv_view_colours
 
+   if obj.data.pose_position != 'REST':
+   #{
+      return
+   #}
+
    for bone in obj.data.bones:
    #{
-      if bone.cv_data.collider and (obj.data.pose_position == 'REST'):
+      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':
       #{
-         c = bone.head_local
-         a = bone.cv_data.v0
-         b = bone.cv_data.v1
          
          vs = [None]*8
          vs[0]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+a[2]))
@@ -2285,20 +2859,67 @@ def draw_skeleton_helpers( obj ):
             cv_view_verts += [(v1[0],v1[1],v1[2])]
             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':
+      #{
+         v0 = b-a
+         major_axis = 0
+         largest = -1.0
 
-         center = obj.matrix_world @ c
-         if bone.cv_data.con0:
+         for i in range(3):
          #{
-            draw_limit( obj, c, Vector((0,1,0)),Vector((0,0,1)), \
-                        bone.cv_data.mins[0], bone.cv_data.maxs[0], \
-                        (1,0,0,1))
-            draw_limit( obj, c, Vector((0,0,1)),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,1,0)), \
-                        bone.cv_data.mins[2], bone.cv_data.maxs[2], \
-                        (0,0,1,1))
+            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))
       #}
    #}
 #}
@@ -2374,12 +2995,38 @@ class CV_MESH_SETTINGS(bpy.types.PropertyGroup):
    v3: bpy.props.FloatVectorProperty(name="v3",size=3)
 #}
 
+class CV_LIGHT_SETTINGS(bpy.types.PropertyGroup):
+#{
+   bp0: bpy.props.BoolProperty( name="bp0" );
+#}
+
+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'
+
+   def draw(_,context):
+   #{
+      active_object = context.active_object
+      if active_object == None: return
+
+      if active_object.type != 'LIGHT': return
+
+      data = active_object.data.cv_data
+      _.layout.prop( data, "bp0", text="Only on during night" )
+   #}
+#}
+
 class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
 #{
    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" )
@@ -2398,6 +3045,11 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
    colour: bpy.props.FloatVectorProperty( name="colour",subtype='COLOR',\
                                           min=0.0,max=1.0)
 
+   function: bpy.props.EnumProperty(
+      name="Function",
+      items= classtype_logic_wire.get_targeted_methods
+   )
+
    classtype: bpy.props.EnumProperty(
       name="Format", 
       items = [
@@ -2411,24 +3063,51 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
       ('classtype_trigger',"classtype_trigger","",100),
       ('classtype_logic_achievement',"classtype_logic_achievement","",101),
       ('classtype_logic_relay',"classtype_logic_relay","",102),
+      ('classtype_logic_wire',"classtype_logic_wire","",105),
+      ('classtype_soundscape',"classtype_soundscape","",106),
+      ('classtype_logic_chances',"classtype_logic_chances","",107),
+      ('classtype_particle_box',"classtype_particle_box","",108),
+      ('classtype_signal_splitter',"classtype_signal_splitter","",109),
       ('classtype_spawn_link',"classtype_spawn_link","",150),
+      ('classtype_nonlocal_gate', "classtype_nonlocal_gate", "", 300)
+      ])
+
+   audio_format: bpy.props.EnumProperty(
+      name="Loaded format",
+      items = [
+         ('mono', "mono", "", 0),
+         ('stereo', "stereo", "", 1),
+         ('remain compressed', "remain compressed", "", 2),
+         ('synthetic bird',"synthetic bird","",3)
       ])
 #}
 
 class CV_BONE_SETTINGS(bpy.types.PropertyGroup):
 #{
-   collider: bpy.props.BoolProperty(name="Collider",default=False)
+   collider: bpy.props.EnumProperty(
+      name="Collider Type", 
+      items = [
+      ('collider_none', "collider_none", "", 0),
+      ('collider_box', "collider_box", "", 1),
+      ('collider_capsule', "collider_capsule", "", 2),
+      ])
+
    v0: bpy.props.FloatVectorProperty(name="v0",size=3)
    v1: bpy.props.FloatVectorProperty(name="v1",size=3)
 
    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)
+
+   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")
 #}
 
 class CV_BONE_PANEL(bpy.types.Panel):
 #{
-   bl_label="Bone Config"
+   bl_label="[Skate Rift]"
    bl_idname="SCENE_PT_cv_bone"
    bl_space_type='PROPERTIES'
    bl_region_type='WINDOW'
@@ -2448,8 +3127,11 @@ class CV_BONE_PANEL(bpy.types.Panel):
 
       _.layout.label( text="Angle Limits" )
       _.layout.prop( bone.cv_data, "con0" )
-      _.layout.prop( bone.cv_data, "mins" )
-      _.layout.prop( bone.cv_data, "maxs" )
+
+      _.layout.prop( bone.cv_data, "conevx" )
+      _.layout.prop( bone.cv_data, "conevy" )
+      _.layout.prop( bone.cv_data, "coneva" )
+      _.layout.prop( bone.cv_data, "conet" )
    #}
 #}
 
@@ -2462,6 +3144,7 @@ class CV_SCENE_SETTINGS(bpy.types.PropertyGroup):
 class CV_COLLECTION_SETTINGS(bpy.types.PropertyGroup):
 #{
    pack_textures: bpy.props.BoolProperty( name="Pack Textures", default=False )
+   animations:    bpy.props.BoolProperty( name="Export animation", default=True)
 #}
 
 class CV_MATERIAL_SETTINGS(bpy.types.PropertyGroup):
@@ -2481,7 +3164,8 @@ class CV_MATERIAL_SETTINGS(bpy.types.PropertyGroup):
       items = [
       ('concrete','concrete','',0),
       ('wood','wood','',1),
-      ('grass','grass','',2)
+      ('grass','grass','',2),
+      ('tiles','tiles','',3)
       ])
    
    collision: bpy.props.BoolProperty( \
@@ -2551,6 +3235,12 @@ class CV_MATERIAL_PANEL(bpy.types.Panel):
 
       info = material_info( active_mat )
 
+      if 'tex_diffuse' in info:
+      #{
+         _.layout.label( icon='INFO', \
+            text=F"{info['tex_diffuse'].name} will be compiled" )
+      #}
+
       _.layout.prop( active_mat.cv_data, "shader" )
       _.layout.prop( active_mat.cv_data, "surface_prop" )
       _.layout.prop( active_mat.cv_data, "collision" )
@@ -2682,6 +3372,7 @@ class CV_INTERFACE(bpy.types.Panel):
       #{
          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:
@@ -2710,7 +3401,8 @@ class CV_INTERFACE(bpy.types.Panel):
 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_MATERIAL_SETTINGS, CV_MATERIAL_PANEL, CV_LIGHT_SETTINGS,\
+           CV_LIGHT_PANEL]
 
 def register():
 #{
@@ -2727,6 +3419,7 @@ def register():
          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)
 
    cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
       cv_draw,(),'WINDOW','POST_VIEW')