something about metascenes/exporter
authorhgn <hgodden00@gmail.com>
Tue, 10 Dec 2024 11:49:53 +0000 (11:49 +0000)
committerhgn <hgodden00@gmail.com>
Tue, 10 Dec 2024 11:49:53 +0000 (11:49 +0000)
22 files changed:
content_skaterift/maps/dev_hub/main.mdl
content_skaterift/maps/mp_hq/main.mdl
content_skaterift/maps/mp_spawn/main.mdl
content_skaterift/metascenes/test_scene.ms
content_skaterift/models/rs_font.mdl
skaterift_blender/__init__.py
skaterift_blender/sr_bin.py
skaterift_blender/sr_main.py
skaterift_blender/sr_mat.py
skaterift_blender/sr_mdl.py [new file with mode: 0644]
skaterift_blender/sr_metascene.py
skaterift_blender/sr_route_graph.py [new file with mode: 0644]
src/ent_camera.c
src/ent_npc.c
src/ent_skateshop.c
src/entity.h
src/metascene.c
src/metascene.h
src/model.h
src/skaterift.c
src/workshop.c
src/world_load.c

index 28304f5d827738bf81b5d7c822e519657e00dad0..1159e0a0a35182de541a86b7c6c2ab676327299a 100644 (file)
Binary files a/content_skaterift/maps/dev_hub/main.mdl and b/content_skaterift/maps/dev_hub/main.mdl differ
index e64efab46a4fe2b3e9df445bf6177abe9d83bdce..d7e7800b90cdb65a897362c6ea3a9748889d8fb9 100644 (file)
Binary files a/content_skaterift/maps/mp_hq/main.mdl and b/content_skaterift/maps/mp_hq/main.mdl differ
index 74fb300edc4c9393851f66d429484b571f54aaca..7f454e0cb8fe6deaba9e348c4f7641e72b21a39a 100644 (file)
Binary files a/content_skaterift/maps/mp_spawn/main.mdl and b/content_skaterift/maps/mp_spawn/main.mdl differ
index da5827d6e481604c6b10b6bf7d0ae522ee4ff014..3f9eba06222265404e283f327d080c1b1a4a99f3 100644 (file)
Binary files a/content_skaterift/metascenes/test_scene.ms and b/content_skaterift/metascenes/test_scene.ms differ
index 7d2d8084a07476f0543800827b25f2c942071cd1..7316f26557573967a323e1ab772133c53da84299 100644 (file)
Binary files a/content_skaterift/models/rs_font.mdl and b/content_skaterift/models/rs_font.mdl differ
index 3017a739592e0d2620893471da09aee8e1b1b350..21fc38f9e09250c16057731c97c29d061904b737 100644 (file)
@@ -26,6 +26,8 @@ def _include( file ):
 #}
 
 _include( "sr_bin.py" )
+_include( "sr_mdl.py" )
+_include( "sr_route_graph.py" )
 _include( "sr_main.py" )
 _include( "sr_so.py" )
 _include( "sr_shader.py" )
index 729a2ef115ad6d4dde847517515fa382f961086b..74e452f0ebf3ca2a62b80678eea4777f50c7249b 100644 (file)
@@ -2,14 +2,15 @@ from ctypes import *
 
 print( "sr_bin" )
 
-class bin_string_cache:
+class _af_compiler:
+   pass
+
+def _af_compiler_init():
 #{
-   def __init__(_, alignment):
-   #{
-      _.table = {}
-      _.buffer = bytearray()
-      _.alignment = alignment
-   #}
+   _af_compiler.string_table = {}
+   _af_compiler.string_buffer = bytearray()
+   _af_compiler.arrays = { 'strings': _af_compiler.string_buffer }
+   _af_pack_string('nul')
 #}
 
 def int_align_to( v, align ):
@@ -40,17 +41,18 @@ def bytearray_print_hex( s, w=16 ):
    #}
 #}
 
-def pack_string( cache, s ):
+def _af_pack_string( s ):
 #{
-   if s in cache.table: return cache.table[s]
+   if s in _af_compiler.string_table: 
+      return _af_compiler.string_table[s]
    
-   index = len( cache.buffer )
-   cache.table[s] = index
-   cache.buffer.extend( c_uint32(hash_djb2(s)) )
-   cache.buffer.extend( s.encode('utf-8') )
-   cache.buffer.extend( b'\0' )
+   index = len( _af_compiler.string_buffer )
+   _af_compiler.string_table[s] = index
+   _af_compiler.string_buffer.extend( c_uint32(hash_djb2(s)) )
+   _af_compiler.string_buffer.extend( s.encode('utf-8') )
+   _af_compiler.string_buffer.extend( b'\0' )
 
-   bytearray_align_to( cache.buffer, cache.alignment )
+   bytearray_align_to( _af_compiler.string_buffer, 4 )
    return index
 #}
 
@@ -94,9 +96,13 @@ def str_into_buf( str, buf ):
 # header must have the attribute 'index' which is an array_file_meta()
 # arrays must be a dictionary with entries of bytearray or ctypes struct arrays
 #
-def array_file_write( path, header, arrays, padding=8 ):
+def array_file_write( path, header, padding=8 ):
 #{
-   num_arrays = len(arrays)
+   num_arrays = 0
+   for name, arr in _af_compiler.arrays.items():
+      if len(arr) > 0:
+         num_arrays += 1
+
    header_size = int_align_to( sizeof(header), padding )
    index_size = int_align_to( sizeof(array_file_meta)*num_arrays, padding )
 
@@ -105,7 +111,7 @@ def array_file_write( path, header, arrays, padding=8 ):
    # Header & index ptr
    # 
    header.index.file_offset = header_size
-   header.index.item_count = len(arrays)
+   header.index.item_count = len(_af_compiler.arrays)
    header.index.item_size = sizeof(array_file_meta)
    str_into_buf( 'index', header.index.name )
    fp.write( header )
@@ -114,8 +120,11 @@ def array_file_write( path, header, arrays, padding=8 ):
    # Create index
    #
    file_offset = header_size + index_size
-   for name, arr in arrays.items():
+   for name, arr in _af_compiler.arrays.items():
    #{
+      if len(arr) == 0: 
+         continue
+
       meta = array_file_meta()
       str_into_buf( name, meta.name )
       meta.file_offset = file_offset
@@ -135,8 +144,11 @@ def array_file_write( path, header, arrays, padding=8 ):
 
    # Write actual arrays
    #
-   for name, arr in arrays.items():
+   for name, arr in _af_compiler.arrays.items():
    #{
+      if len(arr) == 0: 
+         continue
+
       if type(arr) is bytearray:
       #{
          fp.write( arr )
index df5d9fc75a0fd86c512e7e05d830901da2be0f0b..32297b539ab233d7c8cb15ac85c2b00b6a7016ca 100644 (file)
@@ -25,7 +25,7 @@ sr_entity_list = [
    ('ent_skateshop',    'Skate Shop',     '', 12 ),
    ('ent_camera',       'Camera',         '', 13 ),
    ('ent_swspreview', 'Workshop Preview', '', 14 ),
-   ('ent_menuitem',     'Menu Item',      '', 15 ),
+   # unused 15 (previously menu item)
    ('ent_worldinfo',    'World Info',     '', 16 ),
    ('ent_ccmd',         'CCmd',           '', 17 ),
    ('ent_objective',    'Objective',      '', 18 ),
@@ -41,7 +41,7 @@ sr_entity_list = [
    # reserved 28.. armature
 ]
 
-MDL_VERSION_NR = 107
+MDL_VERSION_NR = 108
 SR_TRIGGERABLE = [ 'ent_audio', 'ent_ccmd', 'ent_gate', 'ent_challenge', \
                    'ent_relay', 'ent_skateshop', 'ent_objective', 'ent_route',\
                    'ent_miniworld', 'ent_region', 'ent_glider', 'ent_list',\
@@ -448,66 +448,9 @@ class ent_swspreview(Structure):
                ("id_display1",c_uint32)]
 #}
 
-# Menu
-# -----------------------------------------------------------------
-class ent_menuitem_visual(Structure):
-#{
-   _fields_ = [("pstr_name",c_uint32)]
-#}
-class ent_menuitem_slider(Structure):
-#{
-   _fields_ = [("id_min",c_uint32),
-               ("id_max",c_uint32),
-               ("id_handle",c_uint32),
-               ("pstr_data",c_uint32)]
-#}
-class ent_menuitem_button(Structure):
-#{
-   _fields_ = [("pstr",c_uint32),
-               ("stack_behaviour",c_uint32)]
-#}
-class ent_menuitem_checkmark(Structure):
-#{
-   _fields_ = [("id_check",c_uint32),
-               ("pstr_data",c_uint32),
-               ("offset",c_float*3)]
-#}
-class ent_menuitem_page(Structure):
-#{
-   _fields_ = [("pstr_name",c_uint32),
-               ("id_entrypoint",c_uint32),
-               ("id_viewpoint",c_uint32)]
-#}
-class ent_menuitem_binding(Structure):
-#{
-   _fields_ = [("pstr_bind",c_uint32),
-               ("font_variant",c_uint32)]
-#}
-class ent_menuitem_anon_union(Union):
-#{
-   _fields_ = [("slider",ent_menuitem_slider),
-               ("button",ent_menuitem_button),
-               ("checkmark",ent_menuitem_checkmark),
-               ("page",ent_menuitem_page),
-               ("visual",ent_menuitem_visual),
-               ("binding",ent_menuitem_binding)]
-#}
-class ent_menuitem(Structure):
-#{
-   _fields_ = [("type",c_uint32), ("groups",c_uint32),
-               ("id_links",c_uint32*4), 
-               ("factive",c_float), ("fvisible",c_float),
-               #-- TODO: Refactor this into a simple mesh structure
-               ("transform",mdl_transform),
-               ("submesh_start",c_uint32),("submesh_count",c_uint32),
-               ("_u64",c_uint64),
-               #-- end
-               ("_anonymous_union", ent_menuitem_anon_union)]
-#}
-
 class ent_camera(Structure):
 #{
-   _fields_ = [("transform",mdl_transform),
+   _fields_ = [("co",c_float*3),("r",c_float*3),
                ("fov",c_float)]
 #}
 
@@ -600,16 +543,6 @@ class ent_prop(Structure):#{
                ("pstr_alias",c_uint32)]
 #}
 
-def obj_ent_type( obj ):
-#{
-   if obj.type == 'ARMATURE': return 'mdl_armature'
-   elif obj.type == 'LIGHT': return 'ent_light'
-   elif obj.type == 'CAMERA': return 'ent_camera'
-   elif obj.type == 'LIGHT_PROBE' and obj.data.type == 'CUBEMAP':
-      return 'ent_cubemap'
-   else: return obj.SR_data.ent_type
-#}
-
 def sr_filter_ent_type( obj, ent_types ):
 #{
    if obj == bpy.context.active_object: return False
@@ -673,530 +606,13 @@ def compile_obj_transform( obj, transform ):
    transform.s[2] = s[1]
 #}
 
-def sr_compile_texture( img ):
-#{
-   if img == None:
-      return 0
-
-   name = os.path.splitext( img.name )[0]
-   if name in sr_compile.texture_cache:
-      return sr_compile.texture_cache[name]
-
-   texture_index = len(sr_compile.textures) +1
-
-   tex = mdl_texture()
-   tex.glname = 0
-
-   if sr_compile.pack_textures:
-   #{
-      filedata = qoi_encode( img )
-      sr_pack_file( tex.file, name, filedata )
-   #}
-
-   sr_compile.texture_cache[name] = texture_index
-   sr_compile.textures.append( tex )
-   return texture_index
-#}
-
-def sr_armature_bones( armature ):
-#{
-   def _recurse_bone( b ):
-   #{
-      yield b
-      for c in b.children: yield from _recurse_bone( c )
-   #}
-
-   for b in armature.data.bones:
-      if not b.parent:
-         yield from _recurse_bone( b )
-#}
-
-def sr_entity_id( obj ):#{
-   if not obj: return 0
-   
-   tipo = get_entity_enum_id( obj_ent_type(obj) )
-   index = sr_compile.entity_ids[ obj.name ]
-
-   return (tipo&0xffff)<<16 | (index&0xffff)
-#}
-
-# Returns submesh_start,count and armature_id
-def sr_compile_mesh_internal( obj ):
-#{
-   can_use_cache = True
-   armature = None
-
-   submesh_start = 0
-   submesh_count = 0
-   armature_id = 0
-
-   for mod in obj.modifiers:#{
-      if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
-         mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
-         mod.type == 'ARRAY':
-      #{
-         can_use_cache = False
-      #}
-
-      if mod.type == 'ARMATURE': #{
-         armature = mod.object
-         rig_weight_groups = \
-               ['0 [ROOT]']+[_.name for _ in sr_armature_bones(mod.object)]
-         armature_id = sr_entity_id( armature )
-
-         POSE_OR_REST_CACHE = armature.data.pose_position
-         armature.data.pose_position = 'REST'
-      #}
-   #}
-
-   # Check the cache first
-   #
-   if can_use_cache and (obj.data.name in sr_compile.mesh_cache):#{
-      ref = sr_compile.mesh_cache[obj.data.name]
-      submesh_start = ref[0]
-      submesh_count = ref[1]
-      return (submesh_start,submesh_count,armature_id)
-   #}
-
-   # Compile a whole new mesh
-   #
-   submesh_start = len( sr_compile.submeshes )
-   submesh_count = 0
-
-   dgraph = bpy.context.evaluated_depsgraph_get()
-   data = obj.evaluated_get(dgraph).data
-   data.calc_loop_triangles()
-
-   if bpy.app.version < (4,1,0):
-      data.calc_normals_split()
-   
-   # Mesh is split into submeshes based on their material
-   #
-   mat_list = data.materials if len(data.materials) > 0 else [None]
-   for material_id, mat in enumerate(mat_list): #{
-      mref = {}
-
-      sm = mdl_submesh()
-      sm.indice_start = len( sr_compile.indices )
-      sm.vertex_start = len( sr_compile.vertices )
-      sm.vertex_count = 0
-      sm.indice_count = 0
-      sm.material_id = sr_compile_material( mat )
-
-      INF=99999999.99999999
-      for i in range(3):#{
-         sm.bbx[0][i] =  INF
-         sm.bbx[1][i] = -INF
-      #}
-      
-      # Keep a reference to very very very similar vertices
-      # i have no idea how to speed it up.
-      #
-      vertex_reference = {}
-
-      # Write the vertex / indice data
-      #
-      for tri_index, tri in enumerate(data.loop_triangles):#{
-         if tri.material_index != material_id: continue
-
-         for j in range(3):#{
-            vert = data.vertices[tri.vertices[j]]
-            li = tri.loops[j]
-            vi = data.loops[li].vertex_index
-            
-            # Gather vertex information
-            #
-            co      = vert.co
-            norm    = data.loops[li].normal
-            uv      = (0,0)
-            colour  = (255,255,255,255)
-            groups  = [0,0,0,0]
-            weights = [0,0,0,0]
-
-            # Uvs
-            #
-            if data.uv_layers:
-               uv = data.uv_layers.active.data[li].uv
-            
-            # Vertex Colours
-            #
-            if data.vertex_colors:#{
-               colour = data.vertex_colors.active.data[li].color
-               colour = (int(colour[0]*255.0),\
-                         int(colour[1]*255.0),\
-                         int(colour[2]*255.0),\
-                         int(colour[3]*255.0))
-            #}
-            
-            # Weight groups: truncates to the 3 with the most influence. The
-            #                fourth bone ID is never used by the shader so it 
-            #                is always 0
-            #
-            if armature:#{
-               src_groups = [_ for _ in data.vertices[vi].groups \
-                              if obj.vertex_groups[_.group].name in \
-                                 rig_weight_groups ]
-
-               weight_groups = sorted( src_groups, key = \
-                                       lambda a: a.weight, reverse=True )
-               tot = 0.0
-               for ml in range(3):#{
-                  if len(weight_groups) > ml:#{
-                     g = weight_groups[ml]
-                     name = obj.vertex_groups[g.group].name
-                     weight = g.weight
-                     weights[ml] = weight
-                     groups[ml] = rig_weight_groups.index(name)
-                     tot += weight
-                  #}
-               #}
-            
-               if len(weight_groups) > 0:#{
-                  inv_norm = (1.0/tot) * 65535.0
-                  for ml in range(3):#{
-                     weights[ml] = int( weights[ml] * inv_norm )
-                     weights[ml] = min( weights[ml], 65535 )
-                     weights[ml] = max( weights[ml], 0 )
-                  #}
-               #}
-            #}
-            else:#{
-               li1 = tri.loops[(j+1)%3]
-               vi1 = data.loops[li1].vertex_index
-               e0 = data.edges[ data.loops[li].edge_index ]
-
-               if e0.use_freestyle_mark and \
-                     ((e0.vertices[0] == vi and e0.vertices[1] == vi1) or \
-                      (e0.vertices[0] == vi1 and e0.vertices[1] == vi)):
-               #{
-                  weights[0] = 1
-               #}
-            #}
-
-            TOLERENCE = float(10**4)
-            key = (int(co[0]*TOLERENCE+0.5),
-                   int(co[1]*TOLERENCE+0.5),
-                   int(co[2]*TOLERENCE+0.5),
-                   int(norm[0]*TOLERENCE+0.5),
-                   int(norm[1]*TOLERENCE+0.5),
-                   int(norm[2]*TOLERENCE+0.5),
-                   int(uv[0]*TOLERENCE+0.5),
-                   int(uv[1]*TOLERENCE+0.5),
-                   colour[0],  # these guys are already quantized
-                   colour[1],  # .
-                   colour[2],  # .
-                   colour[3],  # .
-                   weights[0], # v
-                   weights[1],
-                   weights[2],
-                   weights[3],
-                   groups[0],
-                   groups[1],
-                   groups[2],
-                   groups[3])
-
-            if key in vertex_reference:
-               index = vertex_reference[key]
-            else:#{
-               index = c_uint32(sm.vertex_count)
-               sm.vertex_count+=1
-               
-               vertex_reference[key] = index
-               v = mdl_vert()
-               v.co[0]      =  co[0]
-               v.co[1]      =  co[2]
-               v.co[2]      = -co[1]
-               v.norm[0]    =  norm[0]
-               v.norm[1]    =  norm[2]
-               v.norm[2]    = -norm[1]
-               v.uv[0]      =  uv[0]
-               v.uv[1]      =  uv[1]
-               v.colour[0]  =  colour[0]
-               v.colour[1]  =  colour[1]
-               v.colour[2]  =  colour[2]
-               v.colour[3]  =  colour[3]
-               v.weights[0] =  weights[0]
-               v.weights[1] =  weights[1]
-               v.weights[2] =  weights[2]
-               v.weights[3] =  weights[3]
-               v.groups[0]  =  groups[0]
-               v.groups[1]  =  groups[1]
-               v.groups[2]  =  groups[2]
-               v.groups[3]  =  groups[3]
-               
-               for i in range(3):#{
-                  sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] )
-                  sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] )
-               #}
-
-               sr_compile.vertices.append(v)
-            #}
-            
-            sm.indice_count += 1
-            sr_compile.indices.append( index )
-         #}
-      #}
-      
-      # Make sure bounding box isn't -inf -> inf if no vertices
-      #
-      if sm.vertex_count == 0:
-         for j in range(2):
-            for i in range(3):
-               sm.bbx[j][i] = 0
-
-      # Add submesh to encoder
-      #
-      sr_compile.submeshes.append( sm )
-      submesh_count += 1
-   #}
-
-   if armature:#{
-      armature.data.pose_position = POSE_OR_REST_CACHE
-   #}
-
-   # Save a reference to this mesh since we want to reuse the submesh indices
-   # later.
-   sr_compile.mesh_cache[obj.data.name]=(submesh_start,submesh_count)
-   return (submesh_start,submesh_count,armature_id)
-#}
-
-def sr_compile_mesh( obj ):
-#{
-   node=mdl_mesh()
-   compile_obj_transform(obj, node.transform)
-   node.pstr_name = sr_compile_string(obj.name)
-   ent_type = obj_ent_type( obj )
-
-   node.entity_id = 0
-
-   if ent_type != 'none':#{
-      ent_id_lwr = sr_compile.entity_ids[obj.name]
-      ent_id_upr = get_entity_enum_id( obj_ent_type(obj) )
-      node.entity_id = (ent_id_upr << 16) | ent_id_lwr
-   #}
-   
-   node.submesh_start, node.submesh_count, node.armature_id = \
-         sr_compile_mesh_internal( obj )
-
-   sr_compile.meshes.append( node )
-#}
-
-def sr_compile_fonts( collection ):
-#{
-   print( F"[SR] Compiling fonts" )
-
-   glyph_count = 0
-   variant_count = 0
-
-   for obj in collection.all_objects:#{
-      if obj_ent_type(obj) != 'ent_font': continue
-
-      data = obj.SR_data.ent_font[0]
-
-      font=ent_font()
-      font.alias = sr_compile_string( data.alias )
-      font.variant_start = variant_count
-      font.variant_count = 0
-      font.glyph_start = glyph_count
-
-      glyph_base = data.glyphs[0].utf32
-      glyph_range = data.glyphs[-1].utf32+1 - glyph_base
-
-      font.glyph_utf32_base = glyph_base
-      font.glyph_count = glyph_range
-
-      for i in range(len(data.variants)):#{
-         data_var = data.variants[i]
-         if not data_var.mesh: continue 
-
-         mesh = data_var.mesh.data
-
-         variant = ent_font_variant()
-         variant.name = sr_compile_string( data_var.tipo )
-
-         # fonts (variants) only support one material each
-         mat = None
-         if len(mesh.materials) != 0:
-            mat = mesh.materials[0]
-         variant.material_id = sr_compile_material( mat )
-
-         font.variant_count += 1
-
-         islands = mesh_utils.mesh_linked_triangles(mesh)
-         centroids = [Vector((0,0)) for _ in range(len(islands))]
-
-         for j in range(len(islands)):#{
-            for tri in islands[j]:#{
-               centroids[j].x += tri.center[0]
-               centroids[j].y += tri.center[2]
-            #}
-
-            centroids[j] /= len(islands[j])
-         #}
-
-         for j in range(glyph_range):#{
-            data_glyph = data.glyphs[j]
-            glyph = ent_glyph()
-            glyph.indice_start = len( sr_compile.indices )
-            glyph.indice_count = 0
-            glyph.size[0] = data_glyph.bounds[2]
-            glyph.size[1] = data_glyph.bounds[3]
-
-            vertex_reference = {}
-
-            for k in range(len(islands)):#{
-               if centroids[k].x < data_glyph.bounds[0] or \
-                  centroids[k].x > data_glyph.bounds[0]+data_glyph.bounds[2] or\
-                  centroids[k].y < data_glyph.bounds[1] or \
-                  centroids[k].y > data_glyph.bounds[1]+data_glyph.bounds[3]:
-               #{
-                  continue
-               #}
-
-               for l in range(len(islands[k])):#{
-                  tri = islands[k][l]
-                  for m in range(3):#{
-                     vert = mesh.vertices[tri.vertices[m]]
-                     li = tri.loops[m]
-                     vi = mesh.loops[li].vertex_index
-                     
-                     # Gather vertex information
-                     #
-                     co      = [vert.co[_] for _ in range(3)]
-                     co[0]  -= data_glyph.bounds[0]
-                     co[2]  -= data_glyph.bounds[1]
-                     norm    = mesh.loops[li].normal
-                     uv      = (0,0)
-                     if mesh.uv_layers: uv = mesh.uv_layers.active.data[li].uv
-
-                     TOLERENCE = float(10**4)
-                     key = (int(co[0]*TOLERENCE+0.5),
-                            int(co[1]*TOLERENCE+0.5),
-                            int(co[2]*TOLERENCE+0.5),
-                            int(norm[0]*TOLERENCE+0.5),
-                            int(norm[1]*TOLERENCE+0.5),
-                            int(norm[2]*TOLERENCE+0.5),
-                            int(uv[0]*TOLERENCE+0.5),
-                            int(uv[1]*TOLERENCE+0.5))
-
-                     if key in vertex_reference:
-                        index = vertex_reference[key]
-                     else:#{
-                        vindex = len( sr_compile.vertices )
-                        index = c_uint32(vindex)
-                        vertex_reference[key] = index
-                        v = mdl_vert()
-                        v.co[0]   =  co[0]
-                        v.co[1]   =  co[2]
-                        v.co[2]   = -co[1]
-                        v.norm[0] =  norm[0]
-                        v.norm[1] =  norm[2]
-                        v.norm[2] = -norm[1]
-                        v.uv[0]   =  uv[0]
-                        v.uv[1]   =  uv[1]
-                        
-                        sr_compile.vertices.append( v )
-                     #}
-                     
-                     glyph.indice_count += 1
-                     sr_compile.indices.append( index )
-                  #}
-               #}
-            #}
-            sr_ent_push( glyph )
-         #}
-         sr_ent_push( variant )
-      #}
-      sr_ent_push( font )
-   #}
-#}
-
-def sr_compile_menus( collection ):
-#{
-   print( "[SR1] Compiling menus" )
-   groups = []
-
-   for obj in collection.all_objects:#{
-      if obj_ent_type(obj) != 'ent_menuitem': continue
-      obj_data = obj.SR_data.ent_menuitem[0]
-
-      bitmask = 0x00000000
-
-      for col in obj.users_collection:#{
-         name = col.name
-         if name not in groups: groups.append( name )
-         bitmask |= (0x1 << groups.index(name))
-      #}
-
-      item = ent_menuitem()
-      item.type = int( obj_data.tipo )
-      item.groups = bitmask
-
-      compile_obj_transform( obj, item.transform )
-      if obj.type == 'MESH':#{
-         item.submesh_start, item.submesh_count, _ = \
-               sr_compile_mesh_internal( obj )
-      #}
-
-      if item.type == 1 or item.type == 2 or item.type == 7:#{
-         item_button = item._anonymous_union.button
-         item_button.pstr = sr_compile_string( obj_data.string )
-         item_button.stack_behaviour = int( obj_data.stack_behaviour )
-      #}
-      elif item.type == 0:#{
-         item_visual = item._anonymous_union.visual
-         item_visual.pstr_name = sr_compile_string( obj_data.string )
-      #}
-      elif item.type == 3:#{
-         item_checkmark = item._anonymous_union.checkmark
-         item_checkmark.pstr_data = sr_compile_string( obj_data.string )
-         item_checkmark.id_check = sr_entity_id( obj_data.checkmark )
-         delta = obj_data.checkmark.location - obj.location
-         item_checkmark.offset[0] =  delta[0]
-         item_checkmark.offset[1] =  delta[2]
-         item_checkmark.offset[2] = -delta[1]
-      #}
-      elif item.type == 4:#{
-         item_slider = item._anonymous_union.slider
-         item_slider.id_min = sr_entity_id( obj_data.slider_minloc )
-         item_slider.id_max = sr_entity_id( obj_data.slider_maxloc )
-         item_slider.id_handle = sr_entity_id( obj_data.slider_handle )
-         item_slider.pstr_data = sr_compile_string( obj_data.string )
-      #}
-      elif item.type == 5:#{
-         item_page = item._anonymous_union.page
-         item_page.pstr_name = sr_compile_string( obj_data.string )
-         item_page.id_entrypoint = sr_entity_id( obj_data.newloc )
-         item_page.id_viewpoint = sr_entity_id( obj_data.camera )
-      #}
-      elif item.type == 6:#{
-         item_binding = item._anonymous_union.binding
-         item_binding.pstr_bind = sr_compile_string( obj_data.string )
-         item_binding.font_variant = obj_data.font_variant
-      #}
-
-      if obj_data.link0:
-         item.id_links[0] = sr_entity_id( obj_data.link0 )
-      if obj_data.link1:
-         item.id_links[1] = sr_entity_id( obj_data.link1 )
-      if item.type != 4:#{
-         if obj_data.link2:
-            item.id_links[2] = sr_entity_id( obj_data.link2 )
-         if obj_data.link3:
-            item.id_links[3] = sr_entity_id( obj_data.link3 )
-      #}
-
-      sr_ent_push( item )
-   #}
-#}
-
 def sr_compile_armature( obj ):
 #{
    node = mdl_armature()
-   node.pstr_name = sr_compile_string( obj.name )
-   node.bone_start = len( sr_compile.bones )
+   node.pstr_name = _af_pack_string( obj.name )
+   node.bone_start = len( _mdl_compiler.bones )
    node.bone_count = 0
-   # node.anim_start = len(sr_compile.anim_data)//sizeof(mdl_animation)
+   # node.anim_start = len(_mdl_compiler.anim_data)//sizeof(mdl_animation)
    # node.anim_count = 0
    
    bones = [_ for _ in sr_armature_bones(obj)]
@@ -1241,7 +657,7 @@ def sr_compile_armature( obj ):
       bone.end[0] =  b.tail_local[0] - bone.co[0]
       bone.end[1] =  b.tail_local[2] - bone.co[1]
       bone.end[2] = -b.tail_local[1] - bone.co[2]
-      bone.pstr_name = sr_compile_string( b.name )
+      bone.pstr_name = _af_pack_string( b.name )
 
       for c in obj.pose.bones[b.name].constraints:
       #{
@@ -1254,125 +670,20 @@ def sr_compile_armature( obj ):
       #}
 
       node.bone_count += 1
-      sr_compile.bones.append( bone )
+      _mdl_compiler.bones.append( bone )
    #}
 
-   ### # Compile anims
-   ### #
-   ### if obj.animation_data and sr_compile.pack_animations: #{
-   ###    # So we can restore later
-   ###    #
-   ###    previous_frame  = bpy.context.scene.frame_current
-   ###    previous_action = obj.animation_data.action
-   ###    POSE_OR_REST_CACHE = obj.data.pose_position
-   ###    obj.data.pose_position = 'POSE'
-
-   ###    for NLALayer in obj.animation_data.nla_tracks:#{
-   ###       for NLAStrip in NLALayer.strips:#{
-   ###          # set active
-   ###          #
-   ###          for a in bpy.data.actions:#{
-   ###             if a.name == NLAStrip.name:#{
-   ###                obj.animation_data.action = a
-   ###                break
-   ###             #}
-   ###          #}
-   ###          
-   ###          # Clip to NLA settings
-   ###          #
-   ###          anim_start = int(NLAStrip.action_frame_start)
-   ###          anim_end   = int(NLAStrip.action_frame_end)
-
-   ###          # Export strips
-   ###          #
-   ###          anim = mdl_animation()
-   ###          anim.pstr_name = sr_compile_string( NLAStrip.action.name )
-   ###          anim.rate = 30.0
-   ###          anim.keyframe_start = len(sr_compile.keyframe_data)//\
-   ###                                    sizeof(mdl_transform)
-   ###          anim.length = anim_end-anim_start
-   ###          
-   ###          i = 0
-   ###          # Export the keyframes
-   ###          for frame in range(anim_start,anim_end):#{
-   ###             bpy.context.scene.frame_set(frame)
-   ###             
-   ###             for rb in bones:#{
-   ###                pb = obj.pose.bones[rb.name]
-   ###                
-   ###                # relative bone matrix
-   ###                if rb.parent is not None:#{
-   ###                   offset_mtx = rb.parent.matrix_local
-   ###                   offset_mtx = offset_mtx.inverted_safe() @ \
-   ###                                rb.matrix_local
-
-   ###                   inv_parent = pb.parent.matrix @ offset_mtx
-   ###                   inv_parent.invert_safe()
-   ###                   fpm = inv_parent @ pb.matrix 
-   ###                #}
-   ###                else:#{
-   ###                   bone_mtx = rb.matrix.to_4x4()
-   ###                   local_inv = rb.matrix_local.inverted_safe()
-   ###                   fpm = bone_mtx @ local_inv @ pb.matrix
-   ###                #}
-
-   ###                loc, rot, sca = fpm.decompose()
-   ###                
-   ###                # rotation
-   ###                lc_m = pb.matrix_channel.to_3x3()
-   ###                if pb.parent is not None:#{
-   ###                   smtx = pb.parent.matrix_channel.to_3x3()
-   ###                   lc_m = smtx.inverted() @ lc_m
-   ###                #}
-   ###                rq = lc_m.to_quaternion()
-   ###                q_normalize( rq )
-
-   ###                kf = mdl_transform()
-   ###                kf.co[0] =  loc[0]
-   ###                kf.co[1] =  loc[2]
-   ###                kf.co[2] = -loc[1]
-   ###                kf.q[0]  =  rq[1]
-   ###                kf.q[1]  =  rq[3]
-   ###                kf.q[2]  = -rq[2]
-   ###                kf.q[3]  =  rq[0]
-   ###                kf.s[0]  = sca[0]
-   ###                kf.s[1]  = sca[1]
-   ###                kf.s[2]  = sca[2]
-   ###                sr_compile.keyframes.append(kf)
-   ###                
-   ###                i+=1
-   ###             #}
-   ###          #}
-   ###          
-   ###          # Add to animation buffer
-   ###          #
-   ###          sr_compile.anim_data.extend(bytearray(anim))
-   ###          node.anim_count += 1
-
-   ###          # Report progress
-   ###          #
-   ###          print( F"[SR]    | anim( {NLAStrip.action.name} )" )
-   ###       #}
-   ###    #}
-   ###    
-   ###    # Restore context to how it was before
-   ###    #
-   ###    bpy.context.scene.frame_set( previous_frame )
-   ###    obj.animation_data.action = previous_action
-   ###    obj.data.pose_position = POSE_OR_REST_CACHE
-   ### #}
-
-   sr_compile.armatures.append( node )
+   _mdl_compiler.armatures.append( node )
 #}
 
 def sr_ent_push( struct ):
 #{
    clase = type(struct).__name__
 
-   if clase not in sr_compile.entity_data:
-      sr_compile.entity_data[ clase ] = [ struct ]
+   if clase not in _mdl_compiler.entity_data:
+      _mdl_compiler.entity_data[ clase ] = [ struct ]
    else:
-      sr_compile.entity_data[ clase ].append( struct )
+      _mdl_compiler.entity_data[ clase ].append( struct )
 #}
 
 def hash_djb2(s):
@@ -1386,651 +697,41 @@ def hash_djb2(s):
 
 def sr_pack_file( file, path, data ):
 #{
-   file.path = sr_compile_string( path )
-   file.pack_offset = len( sr_compile.pack_data )
+   file.path = _af_pack_string( path )
+   file.pack_offset = len( _mdl_compiler.pack_data )
    file.pack_size = len( data )
 
-   sr_compile.pack_data.extend( data )
-   bytearray_align_to( sr_compile.pack_data, 16 )
-#}
-
-# Smol wrapper so we don't have to edit everything
-def sr_compile_string( string ):
-#{
-   return pack_string( sr_compile.string_cache, string )
+   _mdl_compiler.pack_data.extend( data )
+   bytearray_align_to( _mdl_compiler.pack_data, 16 )
 #}
 
 def sr_compile( collection ):
 #{
    print( F"[SR] compiler begin ({collection.name}.mdl)" )
-   sr_lib_init()
 
-   #settings
-   sr_compile.pack_textures = collection.SR_data.pack_textures
-   sr_compile.pack_animations = collection.SR_data.animations
-
-   # caches
-   sr_compile.string_cache = bin_string_cache(alignment=4)
-   sr_compile.mesh_cache = {}
-   sr_compile.material_cache = {}
-   sr_compile.texture_cache = {}
-   
-   # compiled data
-   sr_compile.meshes = []
-   sr_compile.submeshes = []
-   sr_compile.vertices = []
-   sr_compile.indices = []
-   sr_compile.bones = []
-   sr_compile.materials = []
-   sr_compile.shader_data = bytearray()
-   sr_compile.armatures = []
-   #sr_compile.anim_data = bytearray()
-   #sr_compile.keyframe_data = bytearray()
-   sr_compile.textures = []
-   
-   # just bytes not structures
-   sr_compile.pack_data = bytearray()
-
-   # variable
-   sr_compile.entity_data = {}
-   sr_compile.entity_info = {}
-
-   print( F"[SR] assign entity ID's" )
-   sr_compile.entities = {}
-   sr_compile.entity_ids = {}
+   _af_compiler_init()
+   _mdl_compiler_init( collection.SR_data.pack_textures )
 
    # begin
    # -------------------------------------------------------
 
-   sr_compile_string( "null" )
-
-   mesh_count = 0
-   for obj in collection.all_objects: #{
-      if obj.type == 'MESH':#{
-         mesh_count += 1
-      #}
-
-      ent_type = obj_ent_type( obj )
-      if ent_type == 'none': continue
-
-      if ent_type not in sr_compile.entities: sr_compile.entities[ent_type] = []
-      sr_compile.entity_ids[obj.name] = len( sr_compile.entities[ent_type] )
-      sr_compile.entities[ent_type] += [obj]
-   #}
-
-   print( F"[SR] Compiling geometry" )
-   i=0
-   for obj in collection.all_objects:
-   #{
-      if obj.type == 'MESH':
-      #{
-         i+=1
-
-         ent_type = obj_ent_type( obj )
-
-         # entity ignore mesh list
-         #
-         if ent_type == 'ent_traffic': continue
-         if ent_type == 'ent_prop': continue
-         if ent_type == 'ent_font': continue
-         if ent_type == 'ent_font_variant': continue
-         if ent_type == 'ent_menuitem': continue
-         if ent_type == 'ent_objective': continue
-         if ent_type == 'ent_region': continue
-
-         #TODO: This is messy.
-         if ent_type == 'ent_gate':
-         #{
-            obj_data = obj.SR_data.ent_gate[0]
-            if obj_data.custom: continue
-         #}
-         #--------------------------
-
-         print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}' )
-         sr_compile_mesh( obj )
-      #}
-   #}
-
-   audio_clip_count = 0
-   entity_file_ref_count = 0
-
-   for ent_type, arr in sr_compile.entities.items():#{
-      print(F"[SR] Compiling {len(arr)} {ent_type}{'s' if len(arr)>1 else ''}")
+   for obj in collection.all_objects: 
+      _mdl_compiler_add_object( obj )
 
-      for i in range(len(arr)):#{
-         obj = arr[i]
-
-         print( F"[SR] {i+1: 3}/{len(arr)} {obj.name:<40} ",end='\r' )
-
-         if ent_type == 'mdl_armature': sr_compile_armature(obj)
-         elif ent_type == 'ent_light': #{
-            light = ent_light()
-            compile_obj_transform( obj, light.transform )
-            light.daytime = obj.data.SR_data.daytime
-            if obj.data.type == 'POINT':#{
-               light.type = 0
-            #}
-            elif obj.data.type == 'SPOT':#{
-               light.type = 1
-               light.angle = obj.data.spot_size*0.5
-            #}
-            light.range = obj.data.cutoff_distance
-            light.colour[0] = obj.data.color[0]
-            light.colour[1] = obj.data.color[1]
-            light.colour[2] = obj.data.color[2]
-            light.colour[3] = obj.data.energy
-            sr_ent_push( light )
-         #}
-         elif ent_type == 'ent_camera': #{
-            cam = ent_camera()
-            compile_obj_transform( obj, cam.transform )
-            cam.fov = obj.data.angle * 45.0
-            sr_ent_push(cam)
-         #}
-         elif ent_type == 'ent_gate': #{
-            gate = ent_gate()
-            obj_data = obj.SR_data.ent_gate[0]
-            mesh_data = obj.data.SR_data.ent_gate[0]
-
-            flags = 0x0000
-
-            if obj_data.tipo == 'default':#{
-               if obj_data.target:#{
-                  gate.target = sr_compile.entity_ids[obj_data.target.name]
-                  flags |= 0x0001
-               #}
-            #}
-            elif obj_data.tipo == 'nonlocal':#{
-               gate.target = 0
-               gate.key = sr_compile_string(obj_data.key)
-               flags |= 0x0002
-            #}
-
-            if obj_data.flip:   flags |= 0x0004
-            if obj_data.custom:#{
-               flags |= 0x0008
-               gate.submesh_start, gate.submesh_count, _ = \
-                     sr_compile_mesh_internal( obj )
-            #}
-            if obj_data.locked: flags |= 0x0010
-            gate.flags = flags
-            
-            gate.dimensions[0] = mesh_data.dimensions[0]
-            gate.dimensions[1] = mesh_data.dimensions[1]
-            gate.dimensions[2] = mesh_data.dimensions[2]
-
-            q  = [obj.matrix_local.to_quaternion(), (0,0,0,1)]
-            co = [obj.matrix_world @ Vector((0,0,0)), (0,0,0)]
-
-            if obj_data.target:#{
-               q[1] = obj_data.target.matrix_local.to_quaternion()
-               co[1]= obj_data.target.matrix_world @ Vector((0,0,0))
-            #}
-            
-            # Setup transform
-            #
-            for x in range(2):#{
-               gate.co[x][0] =  co[x][0]
-               gate.co[x][1] =  co[x][2]
-               gate.co[x][2] = -co[x][1]
-               gate.q[x][0]  =  q[x][1]
-               gate.q[x][1]  =  q[x][3]
-               gate.q[x][2]  = -q[x][2]
-               gate.q[x][3]  =  q[x][0]
-            #}
-
-            sr_ent_push( gate )
-         #}
-         elif ent_type == 'ent_spawn': #{
-            spawn = ent_spawn()
-            compile_obj_transform( obj, spawn.transform )
-            obj_data = obj.SR_data.ent_spawn[0]
-            spawn.pstr_name = sr_compile_string( obj_data.alias )
-            sr_ent_push( spawn )
-         #}
-         elif ent_type == 'ent_water':#{
-            water = ent_water()
-            compile_obj_transform( obj, water.transform )
-            water.max_dist = 0.0
-            sr_ent_push( water )
-         #}
-         elif ent_type == 'ent_audio':#{
-            obj_data = obj.SR_data.ent_audio[0]
-            audio = ent_audio()
-            compile_obj_transform( obj, audio.transform )
-            audio.clip_start = audio_clip_count
-            audio.clip_count = len(obj_data.files)
-            audio_clip_count += audio.clip_count
-            audio.max_channels = obj_data.max_channels
-            audio.volume = obj_data.volume
-
-            # TODO flags: 
-            #   - allow/disable doppler
-            #   - channel group tags with random colours
-            #   - transition properties
-
-            if obj_data.flag_loop: audio.flags |= 0x1
-            if obj_data.flag_nodoppler: audio.flags |= 0x2
-            if obj_data.flag_3d: audio.flags |= 0x4
-            if obj_data.flag_auto: audio.flags |= 0x8
-            if   obj_data.formato == '0': audio.flags |= 0x000
-            elif obj_data.formato == '1': audio.flags |= 0x400
-            elif obj_data.formato == '2': audio.flags |= 0x1000
-
-            audio.channel_behaviour = int(obj_data.channel_behaviour)
-            if audio.channel_behaviour >= 1:#{
-               audio.group = obj_data.group
-            #}
-            if audio.channel_behaviour == 2:#{
-               audio.crossfade = obj_data.transition_duration
-            #}
-            audio.probability_curve = int(obj_data.probability_curve)
-
-            for ci in range(audio.clip_count):#{
-               entry = obj_data.files[ci]
-               clip = ent_audio_clip()
-               clip.probability = entry.probability
-               if obj_data.formato == '2':#{
-                  sr_pack_file( clip._anon.file, '', vg_str_bin(entry.path) ) 
-               #}
-               else:#{
-                  clip._anon.file.path = sr_compile_string( entry.path )
-                  clip._anon.file.pack_offset = 0
-                  clip._anon.file.pack_size = 0
-               #}
-               sr_ent_push( clip )
-            #}
-            sr_ent_push( audio )
-         #}
-         elif ent_type == 'ent_volume':#{
-            obj_data = obj.SR_data.ent_volume[0]
-            volume = ent_volume()
-            volume.type = int(obj_data.subtype)
-            compile_obj_transform( obj, volume.transform )
-
-            if obj_data.target:#{
-               volume.target = sr_entity_id( obj_data.target )
-               volume._anon.trigger.event = obj_data.target_event
-               volume._anon.trigger.event_leave = obj_data.target_event_leave
-            #}
-
-            sr_ent_push(volume)
-         #}
-         elif ent_type == 'ent_marker':#{
-            marker = ent_marker()
-            marker.name = sr_compile_string( obj.SR_data.ent_marker[0].alias )
-            compile_obj_transform( obj, marker.transform )
-            sr_ent_push(marker)
-         #}
-         elif ent_type == 'ent_skateshop':#{
-            skateshop = ent_skateshop()
-            obj_data = obj.SR_data.ent_skateshop[0]
-            skateshop.type = int(obj_data.tipo)
-            if skateshop.type == 0:#{
-               boardshop = skateshop._anonymous_union.boards
-               boardshop.id_display = sr_entity_id( obj_data.mark_display )
-               boardshop.id_info = sr_entity_id( obj_data.mark_info )
-               boardshop.id_rack = sr_entity_id( obj_data.mark_rack )
-            #}
-            elif skateshop.type == 1:#{
-               charshop = skateshop._anonymous_union.character
-               charshop.id_display = sr_entity_id( obj_data.mark_display )
-               charshop.id_info = sr_entity_id( obj_data.mark_info )
-            #}
-            elif skateshop.type == 2:#{
-               worldshop = skateshop._anonymous_union.worlds
-               worldshop.id_display = sr_entity_id( obj_data.mark_display )
-               worldshop.id_info = sr_entity_id( obj_data.mark_info )
-            #}
-            elif skateshop.type == 3:#{
-               server = skateshop._anonymous_union.server
-               server.id_lever = sr_entity_id( obj_data.mark_display )
-            #}
-            skateshop.id_camera = sr_entity_id( obj_data.cam )
-            compile_obj_transform( obj, skateshop.transform )
-            sr_ent_push(skateshop)
-         #}
-         elif ent_type == 'ent_swspreview':#{
-            workshop_preview = ent_swspreview()
-            obj_data = obj.SR_data.ent_swspreview[0]
-            workshop_preview.id_display = sr_entity_id( obj_data.mark_display )
-            workshop_preview.id_display1 = sr_entity_id( obj_data.mark_display1)
-            workshop_preview.id_camera = sr_entity_id( obj_data.cam )
-            sr_ent_push( workshop_preview )
-         #}
-         elif ent_type == 'ent_worldinfo':#{
-            worldinfo = ent_worldinfo()
-            obj_data = obj.SR_data.ent_worldinfo[0]
-            worldinfo.pstr_name = sr_compile_string( obj_data.name )
-            worldinfo.pstr_author = sr_compile_string( obj_data.author )
-            worldinfo.pstr_desc = sr_compile_string( obj_data.desc )
-
-            flags = 0x00
-
-            if obj_data.fix_time:#{
-               worldinfo.timezone = obj_data.fixed_time
-               flags |= 0x1
-            #}
-            else:
-               worldinfo.timezone = obj_data.timezone
-
-            if obj_data.water_safe:
-               flags |= 0x2
-
-            worldinfo.flags = flags
-            worldinfo.pstr_skybox = sr_compile_string( obj_data.skybox )
-            sr_ent_push( worldinfo )
-         #}
-         elif ent_type == 'ent_ccmd':#{
-            ccmd = ent_ccmd()
-            obj_data = obj.SR_data.ent_ccmd[0]
-            ccmd.pstr_command = sr_compile_string( obj_data.command )
-            sr_ent_push( ccmd )
-         #}
-         elif ent_type == 'ent_objective':#{
-            objective = ent_objective()
-            obj_data = obj.SR_data.ent_objective[0]
-            objective.id_next = sr_entity_id( obj_data.proxima )
-            objective.id_win = sr_entity_id( obj_data.target )
-            objective.win_event = obj_data.target_event
-            objective.filter = int(obj_data.filtrar)
-            objective.filter2 = 0
-            objective.time_limit = obj_data.time_limit
-
-            compile_obj_transform( obj, objective.transform )
-            objective.submesh_start, objective.submesh_count, _ = \
-                  sr_compile_mesh_internal( obj )
-
-            sr_ent_push( objective )
-         #}
-         elif ent_type == 'ent_challenge':#{
-            challenge = ent_challenge()
-            obj_data = obj.SR_data.ent_challenge[0]
-            compile_obj_transform( obj, challenge.transform )
-            challenge.pstr_alias = sr_compile_string( obj_data.alias )
-            challenge.target = sr_entity_id( obj_data.target )
-            challenge.target_event = obj_data.target_event
-            challenge.reset = sr_entity_id( obj_data.reset )
-            challenge.reset_event = obj_data.reset_event
-            challenge.first = sr_entity_id( obj_data.first )
-            challenge.flags = 0x00
-            challenge.camera = sr_entity_id( obj_data.camera )
-            if obj_data.time_limit: challenge.flags |= 0x01
-            challenge.status = 0
-            sr_ent_push( challenge )
-         #}
-         elif ent_type == 'ent_region':#{
-            region = ent_region()
-            obj_data = obj.SR_data.ent_region[0]
-            compile_obj_transform( obj, region.transform )
-            region.submesh_start, region.submesh_count, _ = \
-                  sr_compile_mesh_internal( obj )
-            region.pstr_title = sr_compile_string( obj_data.title )
-            region.zone_volume = sr_entity_id( obj_data.zone_volume )
-            region.target0[0] = sr_entity_id( obj_data.target0 )
-            region.target0[1] = obj_data.target0_event
-            sr_ent_push( region )
-         #}
-         elif ent_type == 'ent_relay':#{
-            relay = ent_relay()
-            obj_data = obj.SR_data.ent_relay[0]
-            relay.targets[0][0] = sr_entity_id( obj_data.target0 )
-            relay.targets[1][0] = sr_entity_id( obj_data.target1 )
-            relay.targets[2][0] = sr_entity_id( obj_data.target2 )
-            relay.targets[3][0] = sr_entity_id( obj_data.target3 )
-            relay.targets[0][1] = obj_data.target0_event
-            relay.targets[1][1] = obj_data.target1_event
-            relay.targets[2][1] = obj_data.target2_event
-            relay.targets[3][1] = obj_data.target3_event
-            sr_ent_push( relay )
-         #}
-         # elif ent_type == 'ent_list':#{
-         #    lista = ent_list()
-         #    obj_data = obj.SR_data.ent_list[0]
-
-         #    lista.entity_ref_start = entity_file_ref_count
-         #    lista.entity_ref_count = len( obj_data.entities )
-         #    entity_file_ref_count += lista.entity_ref_count
-
-         #    for child in obj_data.entities:#{
-         #       reference_struct = file_entity_ref()
-         #       reference_struct.index = sr_entity_id( child.target )
-         #       sr_ent_push( reference_struct )
-         #    #}
-
-         #    sr_ent_push( lista )
-         # #}
-         elif ent_type == 'ent_glider':#{
-            glider = ent_glider()
-            compile_obj_transform( obj, glider.transform )
-            sr_ent_push( glider )
-         #}
-         elif ent_type == 'ent_npc':#{
-            obj_data = obj.SR_data.ent_npc[0]
-            npc = ent_npc()
-            compile_obj_transform( obj, npc.transform )
-            npc.id = obj_data.au
-            npc.context = obj_data.context
-            npc.camera = sr_entity_id( obj_data.cam )
-            sr_ent_push( npc )
-         #}
-         elif ent_type == 'ent_cubemap':#{
-            cubemap = ent_cubemap()
-            co = obj.matrix_world @ Vector((0,0,0))
-            cubemap.co[0] =  co[0]
-            cubemap.co[1] =  co[2]
-            cubemap.co[2] = -co[1]
-            cubemap.resolution = 0
-            cubemap.live = 60
-            sr_ent_push( cubemap )
-         #}
-         elif ent_type == 'ent_miniworld':#{
-            miniworld = ent_miniworld()
-            obj_data = obj.SR_data.ent_miniworld[0]
-
-            compile_obj_transform( obj, miniworld.transform )
-            miniworld.pstr_world = sr_compile_string( obj_data.world )
-            miniworld.proxy = sr_entity_id( obj_data.proxy )
-            miniworld.camera = sr_entity_id( obj_data.camera )
-            sr_ent_push( miniworld )
-         #}
-         elif ent_type == 'ent_prop':#{
-            prop = ent_prop()
-            obj_data = obj.SR_data.ent_prop[0]
-            compile_obj_transform( obj, prop.transform )
-            prop.submesh_start, prop.submesh_count, _ = \
-                  sr_compile_mesh_internal( obj )
-            prop.flags = obj_data.flags
-            prop.pstr_alias = sr_compile_string( obj_data.alias )
-            sr_ent_push( prop )
-         #}
-      #}
-   #}
-
-   sr_compile_menus( collection )
-   sr_compile_fonts( collection )
-
-   def _children( col ):#{
-      yield col
-      for c in col.children:#{
-         yield from _children(c)
-      #}
-   #}
-
-   checkpoint_count = 0
-   pathindice_count = 0
-   routenode_count = 0
-
-   for col in _children(collection):#{
-      print( F"Adding routes for subcollection: {col.name}" )
-      route_gates = []
-      route_curves = []
-      routes = []
-      traffics = []
-
-      for obj in col.objects:#{
-         if obj.type == 'ARMATURE': pass
-         else:#{
-            ent_type = obj_ent_type( obj )
-
-            if ent_type == 'ent_gate':
-               route_gates += [obj]
-            elif ent_type == 'ent_route_node':#{
-               if obj.type == 'CURVE':#{
-                  route_curves += [obj]
-               #}
-            #}
-            elif ent_type == 'ent_route':
-               routes += [obj]
-            elif ent_type == 'ent_traffic':
-               traffics += [obj]
-         #}
-      #}
-
-      dij = create_node_graph( route_curves, route_gates )
-      
-      for obj in routes:#{
-         obj_data = obj.SR_data.ent_route[0]
-         route = ent_route()
-         route.pstr_name = sr_compile_string( obj_data.alias )
-         route.checkpoints_start = checkpoint_count
-         route.checkpoints_count = 0
-         route.id_camera = sr_entity_id( obj_data.cam )
-
-         for ci in range(3):
-            route.colour[ci] = obj_data.colour[ci]
-         route.colour[3] = 1.0
-
-         compile_obj_transform( obj, route.transform )
-         checkpoints = obj_data.gates
-
-         for i in range(len(checkpoints)):#{
-            gi = checkpoints[i].target
-            gj = checkpoints[(i+1)%len(checkpoints)].target
-            gate = gi
-
-            if gi:#{
-               dest = gi.SR_data.ent_gate[0].target
-               gi = dest
-            #}
-
-            if gi==gj: continue # error?
-            if not gi or not gj: continue
-
-            checkpoint = ent_checkpoint()
-            checkpoint.gate_index = sr_compile.entity_ids[gate.name]
-            checkpoint.path_start = pathindice_count
-            checkpoint.path_count = 0
-            
-            path = solve_graph( dij, gi.name, gj.name )
-
-            if path:#{
-               for pi in range(len(path)):#{
-                  pathindice = ent_path_index()
-                  pathindice.index = routenode_count + path[pi]
-                  sr_ent_push( pathindice )
-
-                  checkpoint.path_count += 1
-                  pathindice_count += 1
-               #}
-            #}
-            
-            sr_ent_push( checkpoint )
-            route.checkpoints_count += 1
-            checkpoint_count += 1
-         #}
-
-         sr_ent_push( route )
-      #}
-
-      for obj in traffics:#{
-         traffic = ent_traffic()
-         compile_obj_transform( obj, traffic.transform )
-         traffic.submesh_start, traffic.submesh_count, _ = \
-               sr_compile_mesh_internal( obj )
-         
-         # find best subsection
-         
-         graph_keys = list(dij.graph)
-         min_dist = 100.0
-         best_point = 0
-
-         for j in range(len(dij.points)):#{
-            point = dij.points[j]
-            dist = (point-obj.location).magnitude
-
-            if dist < min_dist:#{
-               min_dist = dist
-               best_point = j
-            #}
-         #}
-
-         # scan to each edge
-         best_begin = best_point
-         best_end = best_point
-
-         while True:#{
-            map0 = dij.subsections[best_begin]
-            if map0[1] == -1: break
-            best_begin = map0[1]
-         #}
-         while True:#{
-            map1 = dij.subsections[best_end]
-            if map1[2] == -1: break
-            best_end = map1[2]
-         #}
-
-         traffic.start_node = routenode_count + best_begin
-         traffic.node_count = best_end - best_begin
-         traffic.index = best_point - best_begin
-         traffic.speed = obj.SR_data.ent_traffic[0].speed
-         traffic.t = 0.0
-
-         sr_ent_push(traffic)
-      #}
-
-      for point in dij.points:#{
-         rn = ent_route_node()
-         rn.co[0] =  point[0]
-         rn.co[1] =  point[2]
-         rn.co[2] = -point[1]
-         sr_ent_push( rn )
-      #}
-
-      routenode_count += len(dij.points)
-   #}
+   _mdl_compiler_compile_meshes()
+   _mdl_compiler_compile_entities()
+   _mdl_compiler_add_arrays()
 
    print( F"[SR] Writing file" )
 
-   arrays = {
-      'strings': sr_compile.string_cache.buffer,
-      'mdl_mesh': sr_compile.meshes,
-      'mdl_submesh': sr_compile.submeshes,
-      'mdl_material': sr_compile.materials,
-      'mdl_texture': sr_compile.textures,
-      'mdl_armature': sr_compile.armatures,
-      'mdl_bone': sr_compile.bones
-   }
-
-   for name, buf in sr_compile.entity_data.items():
-   #{
-      arrays[name] = buf
-   #}
-
-   arrays[ 'mdl_vert' ] = sr_compile.vertices
-   arrays[ 'mdl_indice' ] = sr_compile.indices
-   arrays[ 'pack' ] = sr_compile.pack_data
-   arrays[ 'shader_data' ] = sr_compile.shader_data
-
    folder = bpy.path.abspath(bpy.context.scene.SR_data.export_dir)
    path = F"{folder}{collection.name}.mdl"
    print( path )
-
    os.makedirs(os.path.dirname(path),exist_ok=True)
 
    header = array_file_header()
    header.version = MDL_VERSION_NR
-   array_file_write( path, header, arrays )
+   array_file_write( path, header )
 
    print( '[SR] done' )
 #}
@@ -3142,113 +1843,6 @@ class SR_OBJECT_ENT_WORKSHOP_PREVIEW(bpy.types.PropertyGroup):
            poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
 #}
 
-class SR_OBJECT_ENT_MENU_ITEM(bpy.types.PropertyGroup):
-#{
-   link0: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Link 0", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
-   link1: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Link 1", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
-   link2: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Link 2", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
-   link3: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Link 3", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
-
-   newloc: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="New location", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
-   stack_behaviour: bpy.props.EnumProperty( name='Stack Behaviour',
-                                 items=[('0','append',''),
-                                        ('1','replace','')])
-
-   camera: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Camera", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_camera']))
-
-   slider_minloc: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Slider min", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
-   slider_maxloc: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Slider max", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_marker']))
-   slider_handle: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Slider handle", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
-
-   checkmark: bpy.props.PointerProperty( \
-           type=bpy.types.Object, name="Checked", \
-           poll=lambda self,obj: sr_filter_ent_type(obj,['ent_menuitem']))
-
-   font_variant: bpy.props.IntProperty( name="Font Variant" )
-
-   string: bpy.props.StringProperty( name="String" )
-   tipo: bpy.props.EnumProperty( name='Type',
-                                 items=[('0','visual',''),
-                                        ('1','event button',''),
-                                        ('2','page button',''),
-                                        ('3','toggle', ''),
-                                        ('4','slider',''),
-                                        ('5','page',''),
-                                        ('6','binding',''),
-                                        ('7','visual(no colourize)','')])
-
-   @staticmethod
-   def sr_inspector( layout, data ):
-   #{
-      data = data[0]
-      box = layout.box()
-      box.prop( data, 'tipo' )
-
-      if data.tipo == '0' or data.tipo == '7':#{
-         box.prop( data, 'string', text='Name' )
-         return
-      #}
-      elif data.tipo == '1':#{
-         box.prop( data, 'string', text='Event' )
-      #}
-      elif data.tipo == '2':#{
-         box.prop( data, 'string', text='Page' )
-         box.prop( data, 'stack_behaviour' )
-      #}
-      elif data.tipo == '3':#{
-         box.prop( data, 'string', text='Data (i32)' )
-         box.prop( data, 'checkmark' )
-      #}
-      elif data.tipo == '4':#{
-         box.prop( data, 'string', text='Data (f32)' )
-         box.prop( data, 'slider_minloc' )
-         box.prop( data, 'slider_maxloc' )
-         box.prop( data, 'slider_handle' )
-         box = box.box()
-         box.label( text="Links" )
-         box.prop( data, 'link0', text='v0' )
-         box.prop( data, 'link1', text='v1' )
-         return
-      #}
-      elif data.tipo == '5':#{
-         box.prop( data, 'string', text='Page Name' )
-         box.prop( data, 'newloc', text='Entry Point' )
-         box.prop( data, 'camera', text='Viewpoint' )
-         return
-      #}
-      elif data.tipo == '6':#{
-         box.prop( data, 'string', text='ID' )
-         box.prop( data, 'font_variant' )
-         return
-      #}
-      
-      box = box.box()
-      box.label( text="Links" )
-      box.prop( data, 'link0' )
-      box.prop( data, 'link1' )
-      box.prop( data, 'link2' )
-      box.prop( data, 'link3' )
-   #}
-#}
-
 class SR_OBJECT_ENT_WORLD_INFO(bpy.types.PropertyGroup):
 #{
    name: bpy.props.StringProperty(name="Name")
@@ -3414,7 +2008,6 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
    ent_skateshop: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_SKATESHOP)
    ent_swspreview: \
          bpy.props.CollectionProperty(type=SR_OBJECT_ENT_WORKSHOP_PREVIEW)
-   ent_menuitem: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MENU_ITEM)
    ent_worldinfo: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_WORLD_INFO)
    ent_ccmd: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_CCMD)
    ent_objective: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_OBJECTIVE)
@@ -4152,186 +2745,6 @@ def cv_ent_volume( obj ):
    #}
 #}
 
-def dijkstra( graph, start_node, target_node ):
-#{
-   unvisited = [_ for _ in graph]
-   shortest_path = {}
-   previous_nodes = {}
-   
-   for n in unvisited:
-      shortest_path[n] = 9999999.999999
-   shortest_path[start_node] = 0
-
-   while unvisited:#{
-      current_min_node = None
-      for n in unvisited:#{
-         if current_min_node == None:
-            current_min_node = n
-         elif shortest_path[n] < shortest_path[current_min_node]:
-            current_min_node = n
-      #}
-
-      for branch in graph[current_min_node]:#{
-         tentative_value = shortest_path[current_min_node]
-         tentative_value += graph[current_min_node][branch]
-         if tentative_value < shortest_path[branch]:#{
-            shortest_path[branch] = tentative_value
-            previous_nodes[branch] = current_min_node
-         #}
-      #}
-
-      unvisited.remove(current_min_node)
-   #}
-   
-   path = []
-   node = target_node
-   while node != start_node:#{
-      path.append(node)
-
-      if node not in previous_nodes: return None
-      node = previous_nodes[node]
-   #}
-
-   # Add the start node manually
-   path.append(start_node)
-   return path
-#}
-
-class dij_graph():
-#{
-   def __init__(_,points,graph,subsections):#{
-      _.points = points
-      _.graph = graph
-      _.subsections = subsections
-   #}
-#}
-
-def create_node_graph( curves, gates ):
-#{
-   # add endpoints of curves
-   graph = {}
-   route_points = []
-   subsections = []
-   point_count = 0
-   spline_count = 0
-
-   for c in range(len(curves)):#{
-      for s in range(len(curves[c].data.splines)):#{
-         spline = curves[c].data.splines[s]
-         l = len(spline.points)
-         if l < 2: continue
-
-         dist = round(spline.calc_length(),2)
-
-         ia = point_count
-         ib = point_count+l-1
-
-         graph[ia] = { ib: dist }
-         graph[ib] = { ia: dist }
-         
-         for i in range(len(spline.points)):#{
-            wco = curves[c].matrix_world @ spline.points[i].co
-            route_points.append(Vector((wco[0],wco[1],wco[2]+0.5)))
-
-            previous = ia+i-1
-            proxima = ia+i+1
-
-            if i == 0: previous = -1
-            if i == len(spline.points)-1: proxima = -1
-
-            subsections.append((spline_count,previous,proxima))
-            point_count += 1
-         #}
-
-         spline_count += 1
-      #}
-   #}
-
-   # link endpoints
-   graph_keys = list(graph)
-   for i in range(len(graph_keys)-1):#{
-      for j in range(i+1, len(graph_keys)):#{
-         if i%2==0 and i+1==j: continue
-
-         ni = graph_keys[i]
-         nj = graph_keys[j]
-         pi = route_points[ni]
-         pj = route_points[nj]
-
-         dist = round((pj-pi).magnitude,2)
-
-         if dist < 10.0:#{
-            graph[ni][nj] = dist
-            graph[nj][ni] = dist
-         #}
-      #}
-   #}
-
-   # add and link gates( by name )
-   for gate in gates:#{
-      v1 = gate.matrix_world.to_3x3() @ Vector((0,1,0))
-      if gate.SR_data.ent_gate[0].target:
-         v1 = v1 * -1.0
-
-      graph[ gate.name ] = {}
-
-      for i in range(len(graph_keys)):#{
-         ni = graph_keys[i]
-         pi = route_points[ni]
-
-         v0 = pi-gate.location
-         if v0.dot(v1) < 0.0: continue
-
-         dist = round(v0.magnitude,2)
-
-         if dist < 10.0:#{
-            graph[ gate.name ][ ni ] = dist
-            graph[ ni ][ gate.name ] = dist
-         #}
-      #}
-   #}
-
-   return dij_graph(route_points,graph,subsections)
-#}
-
-def solve_graph( dij, start, end ):
-#{
-   path = dijkstra( dij.graph, end, start )
-   full = []
-
-   if path:#{
-      for sj in range(1,len(path)-2):#{
-         i0 = path[sj]
-         i1 = path[sj+1]
-         map0 = dij.subsections[i0]
-         map1 = dij.subsections[i1]
-
-         if map0[0] == map1[0]:#{
-            if map0[1] == -1: direction = 2
-            else: direction = 1
-            sent = 0
-
-            while True:#{
-               map0 = dij.subsections[i0]
-               i1 = map0[direction]
-               if i1 == -1: break
-               
-               full.append( i0 )
-               sent += 1
-               i0 = i1
-               if sent > 50: break
-            #}
-         #}
-         else:#{
-            full.append( i0 )
-         #}
-      #}
-
-      full.append( path[-2] )
-   #}
-   return full
-#}
-
 def cv_draw_route( route, dij ):
 #{
    pole = Vector((0.2,0.2,10))
@@ -4594,77 +3007,6 @@ def cv_draw():#{
                               (.5,.5,.5), 0.1 )
             #}
          #}
-         elif ent_type == 'ent_menuitem':#{
-            for i,col in enumerate(obj.users_collection):#{
-               colour32 = hash_djb2( col.name )
-               r = pow(((colour32    ) & 0xff) / 255.0, 2.2 )
-               g = pow(((colour32>>8 ) & 0xff) / 255.0, 2.2 )
-               b = pow(((colour32>>16) & 0xff) / 255.0, 2.2 )
-               cc = (r,g,b)
-               vs = [None for _ in range(8)]
-               scale = i*0.02
-               for j in range(8):#{
-                  v0 = Vector([(obj.bound_box[j][z]+\
-                        ((-1.0 if obj.bound_box[j][z]<0.0 else 1.0)*scale)) \
-                           for z in range(3)])
-                  vs[j] = obj.matrix_world @ v0
-               #}
-               indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\
-                          (0,4),(1,5),(2,6),(3,7)]
-               for l in indices:#{
-                  v0 = vs[l[0]]
-                  v1 = vs[l[1]]
-                  cv_view_verts += [(v0[0],v0[1],v0[2])]
-                  cv_view_verts += [(v1[0],v1[1],v1[2])]
-                  cv_view_colours += [cc,cc]
-               #}
-            #}
-            cv_draw_lines()
-            cc = (1.0,1.0,1.0)
-            data = obj.SR_data.ent_menuitem[0]
-            if data.tipo == '4':#{
-               if data.slider_minloc and data.slider_maxloc:#{
-                  v0 = data.slider_minloc.location
-                  v1 = data.slider_maxloc.location
-                  cv_draw_line( v0, v1, cc )
-               #}
-            #}
-
-            colour32 = hash_djb2(obj.name)
-            r = ((colour32    ) & 0xff) / 255.0
-            g = ((colour32>>8 ) & 0xff) / 255.0
-            b = ((colour32>>16) & 0xff) / 255.0
-            cc = (r,g,b)
-            origin = obj.location + (Vector((r,g,b))*2.0-Vector((1.0,1.0,1.0)))\
-                        * 0.04
-
-            size = 0.01
-
-            if data.tipo != '0':#{
-               if data.tipo == '4':#{
-                  if data.link0:#{
-                     cv_draw_arrow( origin, data.link0.location, cc, size )
-                  #}
-                  if data.link1:#{
-                     cv_draw_arrow( origin, data.link1.location, cc, size )
-                  #}
-               #}
-               else:#{
-                  if data.link0:#{
-                     cv_draw_arrow( origin, data.link0.location, cc, size )
-                  #}
-                  if data.link1:#{
-                     cv_draw_arrow( origin, data.link1.location, cc, size )
-                  #}
-                  if data.link2:#{
-                     cv_draw_arrow( origin, data.link2.location, cc, size )
-                  #}
-                  if data.link3:#{
-                     cv_draw_arrow( origin, data.link3.location, cc, size )
-                  #}
-               #}
-            #}
-         #}
       #}
    #}
 
@@ -4723,7 +3065,7 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\
             SR_OBJECT_ENT_GLYPH_ENTRY,\
             SR_UL_FONT_VARIANT_LIST,SR_UL_FONT_GLYPH_LIST,\
             SR_OBJECT_ENT_FONT,SR_OBJECT_ENT_TRAFFIC,SR_OBJECT_ENT_SKATESHOP,\
-            SR_OBJECT_ENT_WORKSHOP_PREVIEW,SR_OBJECT_ENT_MENU_ITEM,\
+            SR_OBJECT_ENT_WORKSHOP_PREVIEW,\
             SR_OBJECT_ENT_WORLD_INFO,SR_OBJECT_ENT_CCMD,\
             SR_OBJECT_ENT_OBJECTIVE,SR_OBJECT_ENT_CHALLENGE,\
             SR_OBJECT_ENT_REGION,\
index 8859ecf73b23671aec92979fccc1bf0cefd1a250..66e926181ac5b49e69c865197796de49a2eb161c 100644 (file)
@@ -136,16 +136,16 @@ def sr_compile_material( mat ):
 #{
    if mat == None: 
       return 0
-   if mat.name in sr_compile.material_cache: 
-      return sr_compile.material_cache[mat.name]
+   if mat.name in _mdl_compiler.material_cache: 
+      return _mdl_compiler.material_cache[mat.name]
 
    print( F'[SR] Compiling material {mat.name}' )
 
-   index = len( sr_compile.materials ) +1
-   sr_compile.material_cache[mat.name] = index
+   index = len( _mdl_compiler.materials ) +1
+   _mdl_compiler.material_cache[mat.name] = index
 
    m = mdl_material()
-   m.pstr_name = sr_compile_string( mat.name )
+   m.pstr_name = _af_pack_string( mat.name )
    
    flags = 0x00
    if mat.SR_data.collision:
@@ -235,11 +235,11 @@ def sr_compile_material( mat ):
 
    # sr_lib.vg_msg_print( byref(msg), msg.cur.co )
 
-   m.props.kvs.offset = len( sr_compile.shader_data )
+   m.props.kvs.offset = len( _mdl_compiler.shader_data )
    m.props.kvs.size = msg.cur.co
 
-   sr_compile.shader_data.extend( bytearray(buf[:msg.cur.co]) )
-   sr_compile.materials.append( m )
+   _mdl_compiler.shader_data.extend( bytearray(buf[:msg.cur.co]) )
+   _mdl_compiler.materials.append( m )
    return index
 #}
 
diff --git a/skaterift_blender/sr_mdl.py b/skaterift_blender/sr_mdl.py
new file mode 100644 (file)
index 0000000..16cdaea
--- /dev/null
@@ -0,0 +1,1063 @@
+class _mdl_compiler:
+   status = "None"
+
+def _mdl_compiler_init( pack_textures ):
+#{
+   sr_lib_init()
+
+   # input / settings
+   _mdl_compiler.pack_textures = pack_textures
+   _mdl_compiler.objects = []
+   _mdl_compiler.mesh_objects = []
+
+   # caches
+   _mdl_compiler.mesh_cache = {}
+   _mdl_compiler.material_cache = {}
+   _mdl_compiler.texture_cache = {}
+   _route_graphs_init()
+   
+   # compiled data
+   _mdl_compiler.meshes = []
+   _mdl_compiler.submeshes = []
+   _mdl_compiler.vertices = []
+   _mdl_compiler.indices = []
+   _mdl_compiler.bones = []
+   _mdl_compiler.materials = []
+   _mdl_compiler.shader_data = bytearray()
+   _mdl_compiler.armatures = []
+   _mdl_compiler.textures = []
+   _mdl_compiler.pack_data = bytearray()
+
+   #_mdl_compiler.audio_clips = []
+   #_mdl_compiler.glyphs = []
+   #_mdl_compiler.font_variants = []
+
+   # entity table
+   _mdl_compiler.entities = {}    # entity_type -> [ objects ]
+   _mdl_compiler.entity_data = {} # entity_type -> [ compiled objects ]
+   _mdl_compiler.entity_ids = {}  # object name -> entity ID
+#}
+
+def _mdl_compiler_ent_count( type ):
+#{
+   if type in _mdl_compiler.entity_data:
+      return len( _mdl_compiler.entity_data[type] )
+   else: return 0
+#}
+
+# TODO: move to sr_entity.py
+def obj_ent_type( obj ):
+#{
+   if obj.type == 'ARMATURE': return 'mdl_armature'
+   elif obj.type == 'LIGHT': return 'ent_light'
+   elif obj.type == 'CAMERA': return 'ent_camera'
+   elif obj.type == 'LIGHT_PROBE' and (obj.data.type in ['SPHERE','CUBEMAP']):
+      return 'ent_cubemap'
+   else: return obj.SR_data.ent_type
+#}
+
+def _mdl_compiler_add_object( obj ):
+#{
+   _mdl_compiler.objects.append( obj )
+   ent_type = obj_ent_type( obj )
+
+   if ent_type != 'none': 
+   #{
+      if ent_type not in _mdl_compiler.entities: 
+         _mdl_compiler.entities[ent_type] = []
+
+      _mdl_compiler.entity_ids[obj.name] = len(_mdl_compiler.entities[ent_type])
+      _mdl_compiler.entities[ent_type].append( obj )
+   #}
+
+   # add to mesh list?
+   if obj.type == 'MESH':
+   #{
+      add_mesh = True
+      if ent_type in [ 'ent_traffic', 'ent_prop', 'ent_font', \
+                       'ent_font_variant', 'ent_objective', \
+                       'ent_region' ]:
+      #{
+         add_mesh = False
+      #}
+
+      if ent_type == 'ent_gate':
+      #{
+         obj_data = obj.SR_data.ent_gate[0]
+         if obj_data.custom: 
+            add_mesh = False
+      #}
+      
+      if add_mesh:
+         _mdl_compiler.mesh_objects.append( obj )
+   #}
+#}
+
+def sr_compile_texture( img ):
+#{
+   if img == None:
+      return 0
+
+   name = os.path.splitext( img.name )[0]
+   if name in _mdl_compiler.texture_cache:
+      return _mdl_compiler.texture_cache[name]
+
+   texture_index = len(_mdl_compiler.textures) +1
+
+   tex = mdl_texture()
+   tex.glname = 0
+
+   if _mdl_compiler.pack_textures:
+   #{
+      filedata = qoi_encode( img )
+      sr_pack_file( tex.file, name, filedata )
+   #}
+
+   _mdl_compiler.texture_cache[name] = texture_index
+   _mdl_compiler.textures.append( tex )
+   return texture_index
+#}
+
+def sr_armature_bones( armature ):
+#{
+   def _recurse_bone( b ):
+   #{
+      yield b
+      for c in b.children: yield from _recurse_bone( c )
+   #}
+
+   for b in armature.data.bones:
+      if not b.parent:
+         yield from _recurse_bone( b )
+#}
+
+def sr_entity_id( obj ):
+#{
+   if not obj: return 0
+   
+   tipo = get_entity_enum_id( obj_ent_type(obj) )
+   index = _mdl_compiler.entity_ids[ obj.name ]
+
+   return (tipo&0xffff)<<16 | (index&0xffff)
+#}
+
+# Returns submesh_start,count and armature_id
+def mdl_compile_mesh_internal( obj ):
+#{
+   can_use_cache = True
+   armature = None
+
+   submesh_start = 0
+   submesh_count = 0
+   armature_id = 0
+
+   for mod in obj.modifiers:#{
+      if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
+         mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
+         mod.type == 'ARRAY':
+      #{
+         can_use_cache = False
+      #}
+
+      if mod.type == 'ARMATURE': #{
+         armature = mod.object
+         rig_weight_groups = \
+               ['0 [ROOT]']+[_.name for _ in sr_armature_bones(mod.object)]
+         armature_id = sr_entity_id( armature )
+
+         POSE_OR_REST_CACHE = armature.data.pose_position
+         armature.data.pose_position = 'REST'
+      #}
+   #}
+
+   # Check the cache first
+   #
+   if can_use_cache and (obj.data.name in _mdl_compiler.mesh_cache):#{
+      ref = _mdl_compiler.mesh_cache[obj.data.name]
+      submesh_start = ref[0]
+      submesh_count = ref[1]
+      return (submesh_start,submesh_count,armature_id)
+   #}
+
+   # Compile a whole new mesh
+   #
+   submesh_start = len( _mdl_compiler.submeshes )
+   submesh_count = 0
+
+   dgraph = bpy.context.evaluated_depsgraph_get()
+   data = obj.evaluated_get(dgraph).data
+   data.calc_loop_triangles()
+
+   if bpy.app.version < (4,1,0):
+      data.calc_normals_split()
+   
+   # Mesh is split into submeshes based on their material
+   #
+   mat_list = data.materials if len(data.materials) > 0 else [None]
+   for material_id, mat in enumerate(mat_list): #{
+      mref = {}
+
+      sm = mdl_submesh()
+      sm.indice_start = len( _mdl_compiler.indices )
+      sm.vertex_start = len( _mdl_compiler.vertices )
+      sm.vertex_count = 0
+      sm.indice_count = 0
+      sm.material_id = sr_compile_material( mat )
+
+      INF=99999999.99999999
+      for i in range(3):#{
+         sm.bbx[0][i] =  INF
+         sm.bbx[1][i] = -INF
+      #}
+      
+      # Keep a reference to very very very similar vertices
+      # i have no idea how to speed it up.
+      #
+      vertex_reference = {}
+
+      # Write the vertex / indice data
+      #
+      for tri_index, tri in enumerate(data.loop_triangles):#{
+         if tri.material_index != material_id: continue
+
+         for j in range(3):#{
+            vert = data.vertices[tri.vertices[j]]
+            li = tri.loops[j]
+            vi = data.loops[li].vertex_index
+            
+            # Gather vertex information
+            #
+            co      = vert.co
+            norm    = data.loops[li].normal
+            uv      = (0,0)
+            colour  = (255,255,255,255)
+            groups  = [0,0,0,0]
+            weights = [0,0,0,0]
+
+            # Uvs
+            #
+            if data.uv_layers:
+               uv = data.uv_layers.active.data[li].uv
+            
+            # Vertex Colours
+            #
+            if data.vertex_colors:#{
+               colour = data.vertex_colors.active.data[li].color
+               colour = (int(colour[0]*255.0),\
+                         int(colour[1]*255.0),\
+                         int(colour[2]*255.0),\
+                         int(colour[3]*255.0))
+            #}
+            
+            # Weight groups: truncates to the 3 with the most influence. The
+            #                fourth bone ID is never used by the shader so it 
+            #                is always 0
+            #
+            if armature:#{
+               src_groups = [_ for _ in data.vertices[vi].groups \
+                              if obj.vertex_groups[_.group].name in \
+                                 rig_weight_groups ]
+
+               weight_groups = sorted( src_groups, key = \
+                                       lambda a: a.weight, reverse=True )
+               tot = 0.0
+               for ml in range(3):#{
+                  if len(weight_groups) > ml:#{
+                     g = weight_groups[ml]
+                     name = obj.vertex_groups[g.group].name
+                     weight = g.weight
+                     weights[ml] = weight
+                     groups[ml] = rig_weight_groups.index(name)
+                     tot += weight
+                  #}
+               #}
+            
+               if len(weight_groups) > 0:#{
+                  inv_norm = (1.0/tot) * 65535.0
+                  for ml in range(3):#{
+                     weights[ml] = int( weights[ml] * inv_norm )
+                     weights[ml] = min( weights[ml], 65535 )
+                     weights[ml] = max( weights[ml], 0 )
+                  #}
+               #}
+            #}
+            else:#{
+               li1 = tri.loops[(j+1)%3]
+               vi1 = data.loops[li1].vertex_index
+               e0 = data.edges[ data.loops[li].edge_index ]
+
+               if e0.use_freestyle_mark and \
+                     ((e0.vertices[0] == vi and e0.vertices[1] == vi1) or \
+                      (e0.vertices[0] == vi1 and e0.vertices[1] == vi)):
+               #{
+                  weights[0] = 1
+               #}
+            #}
+
+            TOLERENCE = float(10**4)
+            key = (int(co[0]*TOLERENCE+0.5),
+                   int(co[1]*TOLERENCE+0.5),
+                   int(co[2]*TOLERENCE+0.5),
+                   int(norm[0]*TOLERENCE+0.5),
+                   int(norm[1]*TOLERENCE+0.5),
+                   int(norm[2]*TOLERENCE+0.5),
+                   int(uv[0]*TOLERENCE+0.5),
+                   int(uv[1]*TOLERENCE+0.5),
+                   colour[0],  # these guys are already quantized
+                   colour[1],  # .
+                   colour[2],  # .
+                   colour[3],  # .
+                   weights[0], # v
+                   weights[1],
+                   weights[2],
+                   weights[3],
+                   groups[0],
+                   groups[1],
+                   groups[2],
+                   groups[3])
+
+            if key in vertex_reference:
+               index = vertex_reference[key]
+            else:#{
+               index = c_uint32(sm.vertex_count)
+               sm.vertex_count+=1
+               
+               vertex_reference[key] = index
+               v = mdl_vert()
+               v.co[0]      =  co[0]
+               v.co[1]      =  co[2]
+               v.co[2]      = -co[1]
+               v.norm[0]    =  norm[0]
+               v.norm[1]    =  norm[2]
+               v.norm[2]    = -norm[1]
+               v.uv[0]      =  uv[0]
+               v.uv[1]      =  uv[1]
+               v.colour[0]  =  colour[0]
+               v.colour[1]  =  colour[1]
+               v.colour[2]  =  colour[2]
+               v.colour[3]  =  colour[3]
+               v.weights[0] =  weights[0]
+               v.weights[1] =  weights[1]
+               v.weights[2] =  weights[2]
+               v.weights[3] =  weights[3]
+               v.groups[0]  =  groups[0]
+               v.groups[1]  =  groups[1]
+               v.groups[2]  =  groups[2]
+               v.groups[3]  =  groups[3]
+               
+               for i in range(3):#{
+                  sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] )
+                  sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] )
+               #}
+
+               _mdl_compiler.vertices.append(v)
+            #}
+            
+            sm.indice_count += 1
+            _mdl_compiler.indices.append( index )
+         #}
+      #}
+      
+      # Make sure bounding box isn't -inf -> inf if no vertices
+      #
+      if sm.vertex_count == 0:
+         for j in range(2):
+            for i in range(3):
+               sm.bbx[j][i] = 0
+
+      # Add submesh to encoder
+      #
+      _mdl_compiler.submeshes.append( sm )
+      submesh_count += 1
+   #}
+
+   if armature:#{
+      armature.data.pose_position = POSE_OR_REST_CACHE
+   #}
+
+   # Save a reference to this mesh since we want to reuse the submesh indices
+   # later.
+   _mdl_compiler.mesh_cache[obj.data.name]=(submesh_start,submesh_count)
+   return (submesh_start,submesh_count,armature_id)
+#}
+
+def mdl_compile_mesh( obj ):
+#{
+   node=mdl_mesh()
+   compile_obj_transform(obj, node.transform)
+   node.pstr_name = _af_pack_string(obj.name)
+   ent_type = obj_ent_type( obj )
+
+   node.entity_id = 0
+
+   if ent_type != 'none':#{
+      ent_id_lwr = _mdl_compiler.entity_ids[obj.name]
+      ent_id_upr = get_entity_enum_id( obj_ent_type(obj) )
+      node.entity_id = (ent_id_upr << 16) | ent_id_lwr
+   #}
+   
+   node.submesh_start, node.submesh_count, node.armature_id = \
+         mdl_compile_mesh_internal( obj )
+
+   _mdl_compiler.meshes.append( node )
+#}
+
+# compile non-entity mesh data
+def _mdl_compiler_compile_meshes():
+#{
+   i=0
+   for obj in _mdl_compiler.mesh_objects:
+   #{
+      i+=1
+      print( F'[SR] {i: 3}/{len(_mdl_compiler.mesh_objects)} {obj.name:<40}' )
+      mdl_compile_mesh( obj )
+   #}
+#}
+
+def _ent_font_compile( obj ):
+#{
+   data = obj.SR_data.ent_font[0]
+
+   font=ent_font()
+   font.alias = _af_pack_string( data.alias )
+   font.variant_start = _mdl_compiler_ent_count( 'ent_font_variant' )
+   font.variant_count = 0
+   font.glyph_start = _mdl_compiler_ent_count( 'ent_glyph' )
+
+   glyph_base = data.glyphs[0].utf32
+   glyph_range = data.glyphs[-1].utf32+1 - glyph_base
+
+   font.glyph_utf32_base = glyph_base
+   font.glyph_count = glyph_range
+
+   for i in range(len(data.variants)):
+   #{
+      data_var = data.variants[i]
+      if not data_var.mesh: continue 
+
+      mesh = data_var.mesh.data
+
+      variant = ent_font_variant()
+      variant.name = _af_pack_string( data_var.tipo )
+
+      # fonts (variants) only support one material each
+      mat = None
+      if len(mesh.materials) != 0:
+         mat = mesh.materials[0]
+      variant.material_id = sr_compile_material( mat )
+
+      font.variant_count += 1
+
+      islands = mesh_utils.mesh_linked_triangles(mesh)
+      centroids = [Vector((0,0)) for _ in range(len(islands))]
+
+      for j in range(len(islands)):#{
+         for tri in islands[j]:#{
+            centroids[j].x += tri.center[0]
+            centroids[j].y += tri.center[2]
+         #}
+
+         centroids[j] /= len(islands[j])
+      #}
+
+      for j in range(glyph_range):#{
+         data_glyph = data.glyphs[j]
+         glyph = ent_glyph()
+         glyph.indice_start = len( _mdl_compiler.indices )
+         glyph.indice_count = 0
+         glyph.size[0] = data_glyph.bounds[2]
+         glyph.size[1] = data_glyph.bounds[3]
+
+         vertex_reference = {}
+
+         for k in range(len(islands)):#{
+            if centroids[k].x < data_glyph.bounds[0] or \
+               centroids[k].x > data_glyph.bounds[0]+data_glyph.bounds[2] or\
+               centroids[k].y < data_glyph.bounds[1] or \
+               centroids[k].y > data_glyph.bounds[1]+data_glyph.bounds[3]:
+            #{
+               continue
+            #}
+
+            for l in range(len(islands[k])):#{
+               tri = islands[k][l]
+               for m in range(3):#{
+                  vert = mesh.vertices[tri.vertices[m]]
+                  li = tri.loops[m]
+                  vi = mesh.loops[li].vertex_index
+                  
+                  # Gather vertex information
+                  #
+                  co      = [vert.co[_] for _ in range(3)]
+                  co[0]  -= data_glyph.bounds[0]
+                  co[2]  -= data_glyph.bounds[1]
+                  norm    = mesh.loops[li].normal
+                  uv      = (0,0)
+                  if mesh.uv_layers: uv = mesh.uv_layers.active.data[li].uv
+
+                  TOLERENCE = float(10**4)
+                  key = (int(co[0]*TOLERENCE+0.5),
+                         int(co[1]*TOLERENCE+0.5),
+                         int(co[2]*TOLERENCE+0.5),
+                         int(norm[0]*TOLERENCE+0.5),
+                         int(norm[1]*TOLERENCE+0.5),
+                         int(norm[2]*TOLERENCE+0.5),
+                         int(uv[0]*TOLERENCE+0.5),
+                         int(uv[1]*TOLERENCE+0.5))
+
+                  if key in vertex_reference:
+                     index = vertex_reference[key]
+                  else:#{
+                     vindex = len( _mdl_compiler.vertices )
+                     index = c_uint32(vindex)
+                     vertex_reference[key] = index
+                     v = mdl_vert()
+                     v.co[0]   =  co[0]
+                     v.co[1]   =  co[2]
+                     v.co[2]   = -co[1]
+                     v.norm[0] =  norm[0]
+                     v.norm[1] =  norm[2]
+                     v.norm[2] = -norm[1]
+                     v.uv[0]   =  uv[0]
+                     v.uv[1]   =  uv[1]
+                     
+                     _mdl_compiler.vertices.append( v )
+                  #}
+                  
+                  glyph.indice_count += 1
+                  _mdl_compiler.indices.append( index )
+               #}
+            #}
+         #}
+         sr_ent_push( glyph )
+      #}
+      sr_ent_push( variant )
+   #}
+   sr_ent_push( font )
+#}
+
+def _mdl_compiler_compile_entities():
+#{
+   for ent_type, arr in _mdl_compiler.entities.items():
+   #{
+      print(F"[SR] Compiling {len(arr)} {ent_type}{'s' if len(arr)>1 else ''}")
+
+      for i in range(len(arr)):
+      #{
+         obj = arr[i]
+
+         print( F"[SR] {i+1: 3}/{len(arr)} {obj.name:<40} ",end='\r' )
+
+         if ent_type == 'mdl_armature': sr_compile_armature(obj)
+         elif ent_type == 'ent_light': 
+         #{
+            light = ent_light()
+            compile_obj_transform( obj, light.transform )
+            light.daytime = obj.data.SR_data.daytime
+            if obj.data.type == 'POINT':#{
+               light.type = 0
+            #}
+            elif obj.data.type == 'SPOT':#{
+               light.type = 1
+               light.angle = obj.data.spot_size*0.5
+            #}
+            light.range = obj.data.cutoff_distance
+            light.colour[0] = obj.data.color[0]
+            light.colour[1] = obj.data.color[1]
+            light.colour[2] = obj.data.color[2]
+            light.colour[3] = obj.data.energy
+            sr_ent_push( light )
+         #}
+         elif ent_type == 'ent_camera': #{
+            cam = ent_camera()
+
+            trans = mdl_transform()
+            compile_obj_transform( obj, trans )
+
+            def v3_muls( a, b, d ):
+               for i in range(3):
+                  d[i] = a[i]*b
+            def v3_add( a, b, d ):
+               for i in range(3):
+                  d[i] = a[i]+b[i]
+            def v3_cross( a, b, dest ):
+            #{
+               x = a[1]*b[2] - a[2]*b[1]
+               y = a[2]*b[0] - a[0]*b[2]
+               z = a[0]*b[1] - a[1]*b[0]
+               dest[0] = x
+               dest[1] = y
+               dest[2] = z
+            #}
+            def v3_dot( a,b ):
+               return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
+
+            v1 = [0,0,0]
+            v2 = [0,0,0]
+            v = [0,-1,0]
+
+            v3_muls( trans.q, 2.0*v3_dot(trans.q,v), v1 )
+            v3_muls( v, trans.q[3]*trans.q[3] - v3_dot(trans.q,trans.q), v2 )
+            v3_add( v1, v2, v1 )
+            v3_cross( trans.q, v, v2 )
+            v3_muls( v2, 2.0*trans.q[3], v2 )
+            v3_add( v1, v2, v )
+
+            cam.r[0] = math.atan2( v[0], -v[2] )
+            cam.r[1] = math.atan2( -v[1], math.sqrt( v[0]*v[0] + v[2]*v[2] ) )
+            cam.r[2] = 0.0
+
+            cam.co[0] = trans.co[0]
+            cam.co[1] = trans.co[1]
+            cam.co[2] = trans.co[2]
+
+            cam.fov = obj.data.angle * 45.0
+            sr_ent_push(cam)
+         #}
+         elif ent_type == 'ent_gate': #{
+            gate = ent_gate()
+            obj_data = obj.SR_data.ent_gate[0]
+            mesh_data = obj.data.SR_data.ent_gate[0]
+
+            flags = 0x0000
+
+            if obj_data.tipo == 'default':#{
+               if obj_data.target:#{
+                  gate.target = _mdl_compiler.entity_ids[obj_data.target.name]
+                  flags |= 0x0001
+               #}
+            #}
+            elif obj_data.tipo == 'nonlocal':#{
+               gate.target = 0
+               gate.key = _af_pack_string(obj_data.key)
+               flags |= 0x0002
+            #}
+
+            if obj_data.flip:   flags |= 0x0004
+            if obj_data.custom:#{
+               flags |= 0x0008
+               gate.submesh_start, gate.submesh_count, _ = \
+                     mdl_compile_mesh_internal( obj )
+            #}
+            if obj_data.locked: flags |= 0x0010
+            gate.flags = flags
+            
+            gate.dimensions[0] = mesh_data.dimensions[0]
+            gate.dimensions[1] = mesh_data.dimensions[1]
+            gate.dimensions[2] = mesh_data.dimensions[2]
+
+            q  = [obj.matrix_local.to_quaternion(), (0,0,0,1)]
+            co = [obj.matrix_world @ Vector((0,0,0)), (0,0,0)]
+
+            if obj_data.target:#{
+               q[1] = obj_data.target.matrix_local.to_quaternion()
+               co[1]= obj_data.target.matrix_world @ Vector((0,0,0))
+            #}
+            
+            # Setup transform
+            #
+            for x in range(2):#{
+               gate.co[x][0] =  co[x][0]
+               gate.co[x][1] =  co[x][2]
+               gate.co[x][2] = -co[x][1]
+               gate.q[x][0]  =  q[x][1]
+               gate.q[x][1]  =  q[x][3]
+               gate.q[x][2]  = -q[x][2]
+               gate.q[x][3]  =  q[x][0]
+            #}
+
+            sr_ent_push( gate )
+         #}
+         elif ent_type == 'ent_spawn': #{
+            spawn = ent_spawn()
+            compile_obj_transform( obj, spawn.transform )
+            obj_data = obj.SR_data.ent_spawn[0]
+            spawn.pstr_name = _af_pack_string( obj_data.alias )
+            sr_ent_push( spawn )
+         #}
+         elif ent_type == 'ent_water':#{
+            water = ent_water()
+            compile_obj_transform( obj, water.transform )
+            water.max_dist = 0.0
+            sr_ent_push( water )
+         #}
+         elif ent_type == 'ent_audio':#{
+            obj_data = obj.SR_data.ent_audio[0]
+            audio = ent_audio()
+            compile_obj_transform( obj, audio.transform )
+            audio.clip_start = _mdl_compiler_ent_count( 'ent_audio_clip' )
+            audio.clip_count = len( obj_data.files )
+            audio.max_channels = obj_data.max_channels
+            audio.volume = obj_data.volume
+
+            # TODO flags: 
+            #   - allow/disable doppler
+            #   - channel group tags with random colours
+            #   - transition properties
+
+            if obj_data.flag_loop: audio.flags |= 0x1
+            if obj_data.flag_nodoppler: audio.flags |= 0x2
+            if obj_data.flag_3d: audio.flags |= 0x4
+            if obj_data.flag_auto: audio.flags |= 0x8
+            if   obj_data.formato == '0': audio.flags |= 0x000
+            elif obj_data.formato == '1': audio.flags |= 0x400
+            elif obj_data.formato == '2': audio.flags |= 0x1000
+
+            audio.channel_behaviour = int(obj_data.channel_behaviour)
+            if audio.channel_behaviour >= 1:#{
+               audio.group = obj_data.group
+            #}
+            if audio.channel_behaviour == 2:#{
+               audio.crossfade = obj_data.transition_duration
+            #}
+            audio.probability_curve = int(obj_data.probability_curve)
+
+            for ci in range(audio.clip_count):#{
+               entry = obj_data.files[ci]
+               clip = ent_audio_clip()
+               clip.probability = entry.probability
+               if obj_data.formato == '2':
+               #{
+                  sr_pack_file( clip._anon.file, '', vg_str_bin(entry.path) ) 
+               #}
+               else:
+               #{
+                  clip._anon.file.path = _af_pack_string( entry.path )
+                  clip._anon.file.pack_offset = 0
+                  clip._anon.file.pack_size = 0
+               #}
+               sr_ent_push( clip )
+            #}
+            sr_ent_push( audio )
+         #}
+         elif ent_type == 'ent_volume':#{
+            obj_data = obj.SR_data.ent_volume[0]
+            volume = ent_volume()
+            volume.type = int(obj_data.subtype)
+            compile_obj_transform( obj, volume.transform )
+
+            if obj_data.target:#{
+               volume.target = sr_entity_id( obj_data.target )
+               volume._anon.trigger.event = obj_data.target_event
+               volume._anon.trigger.event_leave = obj_data.target_event_leave
+            #}
+
+            sr_ent_push(volume)
+         #}
+         elif ent_type == 'ent_marker':#{
+            marker = ent_marker()
+            marker.name = _af_pack_string( obj.SR_data.ent_marker[0].alias )
+            compile_obj_transform( obj, marker.transform )
+            sr_ent_push(marker)
+         #}
+         elif ent_type == 'ent_skateshop':#{
+            skateshop = ent_skateshop()
+            obj_data = obj.SR_data.ent_skateshop[0]
+            skateshop.type = int(obj_data.tipo)
+            if skateshop.type == 0:#{
+               boardshop = skateshop._anonymous_union.boards
+               boardshop.id_display = sr_entity_id( obj_data.mark_display )
+               boardshop.id_info = sr_entity_id( obj_data.mark_info )
+               boardshop.id_rack = sr_entity_id( obj_data.mark_rack )
+            #}
+            elif skateshop.type == 1:#{
+               charshop = skateshop._anonymous_union.character
+               charshop.id_display = sr_entity_id( obj_data.mark_display )
+               charshop.id_info = sr_entity_id( obj_data.mark_info )
+            #}
+            elif skateshop.type == 2:#{
+               worldshop = skateshop._anonymous_union.worlds
+               worldshop.id_display = sr_entity_id( obj_data.mark_display )
+               worldshop.id_info = sr_entity_id( obj_data.mark_info )
+            #}
+            elif skateshop.type == 3:#{
+               server = skateshop._anonymous_union.server
+               server.id_lever = sr_entity_id( obj_data.mark_display )
+            #}
+            skateshop.id_camera = sr_entity_id( obj_data.cam )
+            compile_obj_transform( obj, skateshop.transform )
+            sr_ent_push(skateshop)
+         #}
+         elif ent_type == 'ent_swspreview':#{
+            workshop_preview = ent_swspreview()
+            obj_data = obj.SR_data.ent_swspreview[0]
+            workshop_preview.id_display = sr_entity_id( obj_data.mark_display )
+            workshop_preview.id_display1 = sr_entity_id( obj_data.mark_display1)
+            workshop_preview.id_camera = sr_entity_id( obj_data.cam )
+            sr_ent_push( workshop_preview )
+         #}
+         elif ent_type == 'ent_worldinfo':#{
+            worldinfo = ent_worldinfo()
+            obj_data = obj.SR_data.ent_worldinfo[0]
+            worldinfo.pstr_name = _af_pack_string( obj_data.name )
+            worldinfo.pstr_author = _af_pack_string( obj_data.author )
+            worldinfo.pstr_desc = _af_pack_string( obj_data.desc )
+
+            flags = 0x00
+
+            if obj_data.fix_time:#{
+               worldinfo.timezone = obj_data.fixed_time
+               flags |= 0x1
+            #}
+            else:
+               worldinfo.timezone = obj_data.timezone
+
+            if obj_data.water_safe:
+               flags |= 0x2
+
+            worldinfo.flags = flags
+            worldinfo.pstr_skybox = _af_pack_string( obj_data.skybox )
+            sr_ent_push( worldinfo )
+         #}
+         elif ent_type == 'ent_ccmd':#{
+            ccmd = ent_ccmd()
+            obj_data = obj.SR_data.ent_ccmd[0]
+            ccmd.pstr_command = _af_pack_string( obj_data.command )
+            sr_ent_push( ccmd )
+         #}
+         elif ent_type == 'ent_objective':#{
+            objective = ent_objective()
+            obj_data = obj.SR_data.ent_objective[0]
+            objective.id_next = sr_entity_id( obj_data.proxima )
+            objective.id_win = sr_entity_id( obj_data.target )
+            objective.win_event = obj_data.target_event
+            objective.filter = int(obj_data.filtrar)
+            objective.filter2 = 0
+            objective.time_limit = obj_data.time_limit
+
+            compile_obj_transform( obj, objective.transform )
+            objective.submesh_start, objective.submesh_count, _ = \
+                  mdl_compile_mesh_internal( obj )
+
+            sr_ent_push( objective )
+         #}
+         elif ent_type == 'ent_challenge':#{
+            challenge = ent_challenge()
+            obj_data = obj.SR_data.ent_challenge[0]
+            compile_obj_transform( obj, challenge.transform )
+            challenge.pstr_alias = _af_pack_string( obj_data.alias )
+            challenge.target = sr_entity_id( obj_data.target )
+            challenge.target_event = obj_data.target_event
+            challenge.reset = sr_entity_id( obj_data.reset )
+            challenge.reset_event = obj_data.reset_event
+            challenge.first = sr_entity_id( obj_data.first )
+            challenge.flags = 0x00
+            challenge.camera = sr_entity_id( obj_data.camera )
+            if obj_data.time_limit: challenge.flags |= 0x01
+            challenge.status = 0
+            sr_ent_push( challenge )
+         #}
+         elif ent_type == 'ent_region':#{
+            region = ent_region()
+            obj_data = obj.SR_data.ent_region[0]
+            compile_obj_transform( obj, region.transform )
+            region.submesh_start, region.submesh_count, _ = \
+                  mdl_compile_mesh_internal( obj )
+            region.pstr_title = _af_pack_string( obj_data.title )
+            region.zone_volume = sr_entity_id( obj_data.zone_volume )
+            region.target0[0] = sr_entity_id( obj_data.target0 )
+            region.target0[1] = obj_data.target0_event
+            sr_ent_push( region )
+         #}
+         elif ent_type == 'ent_relay':#{
+            relay = ent_relay()
+            obj_data = obj.SR_data.ent_relay[0]
+            relay.targets[0][0] = sr_entity_id( obj_data.target0 )
+            relay.targets[1][0] = sr_entity_id( obj_data.target1 )
+            relay.targets[2][0] = sr_entity_id( obj_data.target2 )
+            relay.targets[3][0] = sr_entity_id( obj_data.target3 )
+            relay.targets[0][1] = obj_data.target0_event
+            relay.targets[1][1] = obj_data.target1_event
+            relay.targets[2][1] = obj_data.target2_event
+            relay.targets[3][1] = obj_data.target3_event
+            sr_ent_push( relay )
+         #}
+         elif ent_type == 'ent_glider':
+         #{
+            glider = ent_glider()
+            compile_obj_transform( obj, glider.transform )
+            sr_ent_push( glider )
+         #}
+         elif ent_type == 'ent_npc':
+         #{
+            obj_data = obj.SR_data.ent_npc[0]
+            npc = ent_npc()
+            compile_obj_transform( obj, npc.transform )
+            npc.id = obj_data.au
+            npc.context = obj_data.context
+            npc.camera = sr_entity_id( obj_data.cam )
+            sr_ent_push( npc )
+         #}
+         elif ent_type == 'ent_cubemap':
+         #{
+            cubemap = ent_cubemap()
+            co = obj.matrix_world @ Vector((0,0,0))
+            cubemap.co[0] =  co[0]
+            cubemap.co[1] =  co[2]
+            cubemap.co[2] = -co[1]
+            cubemap.resolution = 0
+            cubemap.live = 60
+            sr_ent_push( cubemap )
+         #}
+         elif ent_type == 'ent_miniworld':
+         #{
+            miniworld = ent_miniworld()
+            obj_data = obj.SR_data.ent_miniworld[0]
+
+            compile_obj_transform( obj, miniworld.transform )
+            miniworld.pstr_world = _af_pack_string( obj_data.world )
+            miniworld.proxy = sr_entity_id( obj_data.proxy )
+            miniworld.camera = sr_entity_id( obj_data.camera )
+            sr_ent_push( miniworld )
+         #}
+         elif ent_type == 'ent_prop':
+         #{
+            prop = ent_prop()
+            obj_data = obj.SR_data.ent_prop[0]
+            compile_obj_transform( obj, prop.transform )
+            prop.submesh_start, prop.submesh_count, _ = \
+                  mdl_compile_mesh_internal( obj )
+            prop.flags = obj_data.flags
+            prop.pstr_alias = _af_pack_string( obj_data.alias )
+            sr_ent_push( prop )
+         #}
+         elif ent_type == 'ent_font':
+         #{
+            _ent_font_compile( obj )
+         #}
+         elif ent_type == 'ent_route':
+         #{
+            obj_data = obj.SR_data.ent_route[0]
+            route = ent_route()
+            route.pstr_name = _af_pack_string( obj_data.alias )
+            route.checkpoints_start = \
+                  _mdl_compiler_ent_count( 'ent_checkpoint' )
+            route.checkpoints_count = 0
+            route.id_camera = sr_entity_id( obj_data.cam )
+
+            for ci in range(3):
+               route.colour[ci] = obj_data.colour[ci]
+            route.colour[3] = 1.0
+
+            compile_obj_transform( obj, route.transform )
+            checkpoints = obj_data.gates
+
+            dij,dij_base = _get_route_graph( obj.users_collection[0] )
+
+            for i in range(len(checkpoints)):
+            #{
+               gi = checkpoints[i].target
+               gj = checkpoints[(i+1)%len(checkpoints)].target
+               gate = gi
+
+               if gi:
+               #{
+                  dest = gi.SR_data.ent_gate[0].target
+                  gi = dest
+               #}
+
+               if gi==gj: continue # error?
+               if not gi or not gj: continue
+
+               checkpoint = ent_checkpoint()
+               checkpoint.gate_index = _mdl_compiler.entity_ids[gate.name]
+               checkpoint.path_start = \
+                     _mdl_compiler_ent_count( 'ent_path_index' )
+               checkpoint.path_count = 0
+               
+               path = solve_graph( dij, gi.name, gj.name )
+
+               if path:
+               #{
+                  for pi in range(len(path)):
+                  #{
+                     pathindice = ent_path_index()
+                     pathindice.index = dij_base + path[pi]
+                     sr_ent_push( pathindice )
+
+                     checkpoint.path_count += 1
+                  #}
+               #}
+               
+               sr_ent_push( checkpoint )
+               route.checkpoints_count += 1
+            #}
+            sr_ent_push( route )
+         #}
+         elif ent_type == 'ent_traffic':
+         #{
+            traffic = ent_traffic()
+            compile_obj_transform( obj, traffic.transform )
+            traffic.submesh_start, traffic.submesh_count, _ = \
+                  mdl_compile_mesh_internal( obj )
+            
+            # find best subsection
+            dij,dij_base = _get_route_graph( obj.users_collection[0] )
+            graph_keys = list(dij.graph)
+            min_dist = 100.0
+            best_point = 0
+
+            for j in range(len(dij.points)):
+            #{
+               point = dij.points[j]
+               dist = (point-obj.location).magnitude
+
+               if dist < min_dist:
+               #{
+                  min_dist = dist
+                  best_point = j
+               #}
+            #}
+
+            # scan to each edge
+            best_begin = best_point
+            best_end = best_point
+
+            while True:
+            #{
+               map0 = dij.subsections[best_begin]
+               if map0[1] == -1: break
+               best_begin = map0[1]
+            #}
+            while True:
+            #{
+               map1 = dij.subsections[best_end]
+               if map1[2] == -1: break
+               best_end = map1[2]
+            #}
+
+            traffic.start_node = dij_base + best_begin
+            traffic.node_count = best_end - best_begin
+            traffic.index = best_point - best_begin
+            traffic.speed = obj.SR_data.ent_traffic[0].speed
+            traffic.t = 0.0
+
+            sr_ent_push(traffic)
+         #}
+      #}
+   #}
+#}
+
+def _mdl_compiler_add_arrays():
+#{
+   arrays = {
+      'mdl_mesh': _mdl_compiler.meshes,
+      'mdl_submesh': _mdl_compiler.submeshes,
+      'mdl_material': _mdl_compiler.materials,
+      'mdl_texture': _mdl_compiler.textures,
+      'mdl_armature': _mdl_compiler.armatures,
+      'mdl_bone': _mdl_compiler.bones,
+   }
+
+   for name, buf in _mdl_compiler.entity_data.items():
+   #{
+      arrays[name] = buf
+   #}
+
+   arrays[ 'mdl_vert' ] = _mdl_compiler.vertices
+   arrays[ 'mdl_indice' ] = _mdl_compiler.indices
+   arrays[ 'pack' ] = _mdl_compiler.pack_data
+   arrays[ 'shader_data' ] = _mdl_compiler.shader_data
+
+   _af_compiler.arrays.update( arrays )
+#}
index 6efeb95f9241a91a3ff8cb1f11960b1a0d1b782f..94e587a3bcb0d5dc34019b41b2796d54f1dc42cc 100644 (file)
@@ -26,8 +26,6 @@ class _ms_compiler:
 
 def _ms_compiler_init():
 #{
-   _ms_compiler.strings = bin_string_cache(alignment=4)
-
    _ms_compiler.action_cache = {} # actions point to (our) strips
    _ms_compiler.strips = []
    _ms_compiler.tracks = []
@@ -131,8 +129,7 @@ def _metascene_armature_anims( obj, instance_id, override_id ):
          out_strip = ms_strip()
          out_strip.offset = math.floor( NLAStrip.frame_start )
          out_strip.length = math.ceil( NLAStrip.frame_end - out_strip.offset )
-         out_strip.pstr_name = \
-               pack_string( _ms_compiler.strings, NLAStrip.name )
+         out_strip.pstr_name = _af_pack_string( NLAStrip.name )
          out_strip.instance_id = instance_id
          out_strip.object_id = override_id
 
@@ -145,8 +142,7 @@ def _metascene_armature_anims( obj, instance_id, override_id ):
          out_strip.data_mode = 0
          out_strip.data_start = len( _ms_compiler.keyframes )
          out_strip.data_count = len( bones )
-         out_strip.pstr_internal_name = \
-            pack_string( _ms_compiler.strings, action.name )
+         out_strip.pstr_internal_name = _af_pack_string( action.name )
          
          # Clip to NLA settings
          #
@@ -229,29 +225,38 @@ def _metascene_compile_action_curves( out_strip, action ):
    out_strip.data_mode = 1
    out_strip.data_start = len(_ms_compiler.tracks)
    out_strip.data_count = len(action.fcurves)
-   out_strip.pstr_internal_name = \
-         pack_string( _ms_compiler.strings, action.name )
+   out_strip.pstr_internal_name = _af_pack_string( action.name )
 
    for fcurve in action.fcurves:
    #{
-      id = F"{fcurve.data_path}:{fcurve.array_index}"
+      name = fcurve.data_path
+      index = fcurve.array_index
+      mul = 1.0
+
+      if name == 'location': 
+      #{
+         index = [0,2,1][ index ]
+         mul = [1,1,-1][ index ]
+      #}
+
+      id = F"{name}:{index}"
       print( F"      Appending curve '{id}'" )
       
       out_track = ms_track()
       out_track.keyframe_start = len( _ms_compiler.curve_keyframes )
       out_track.keyframe_count = 0
-      out_track.pstr_datapath = pack_string( _ms_compiler.strings, id )
+      out_track.pstr_datapath = _af_pack_string( id )
       out_track.semantic_type = 0
 
       for kf in fcurve.keyframe_points:
       #{
          out_keyframe = ms_curve_keyframe()
          out_keyframe.co[0] = kf.co[0]
-         out_keyframe.co[1] = kf.co[1]
+         out_keyframe.co[1] = kf.co[1] * mul
          out_keyframe.l[0] = kf.handle_left[0]  #TODO: clipping.
-         out_keyframe.l[1] = kf.handle_left[1]
+         out_keyframe.l[1] = kf.handle_left[1] * mul
          out_keyframe.r[0] = kf.handle_right[0]
-         out_keyframe.r[1] = kf.handle_right[1]
+         out_keyframe.r[1] = kf.handle_right[1] * mul
 
          _ms_compiler.curve_keyframes.append(out_keyframe)
          out_track.keyframe_count += 1
@@ -281,11 +286,10 @@ def _metascene_camera_anims( obj, entity_id ):
          out_strip = ms_strip()
          _metascene_compile_action_curves( out_strip, NLAStrip.action )
          out_strip.instance_id = 0xffffffff
-         out_strip.object_id = 0 # TODO
+         out_strip.object_id = entity_id
          out_strip.offset = math.floor( NLAStrip.frame_start )
          out_strip.length = math.ceil( NLAStrip.frame_end - out_strip.offset )
-         out_strip.pstr_name = \
-               pack_string( _ms_compiler.strings, NLAStrip.name )
+         out_strip.pstr_name = _af_pack_string( NLAStrip.name )
 
          _ms_compiler.strips.append( out_strip )
       #}
@@ -300,11 +304,10 @@ def _metascene_camera_anims( obj, entity_id ):
          out_strip = ms_strip()
          _metascene_compile_action_curves( out_strip, NLAStrip.action )
          out_strip.instance_id = 0xffffffff
-         out_strip.object_id = 0 # TODO
+         out_strip.object_id = entity_id
          out_strip.offset = math.floor( NLAStrip.frame_start )
          out_strip.length = math.ceil( NLAStrip.frame_end - out_strip.offset )
-         out_strip.pstr_name = \
-               pack_string( _ms_compiler.strings, NLAStrip.name )
+         out_strip.pstr_name = _af_pack_string( NLAStrip.name )
          _ms_compiler.strips.append( out_strip )
       #}
    #}
@@ -321,7 +324,9 @@ def obj_realname( obj ):
 def _sr_export_metascene( path ):
 #{
    print( "\nCompiling meta-scene\n----------------------------------------" )
+   _af_compiler_init()
    _ms_compiler_init()
+   _mdl_compiler_init( False )
    
    def descend( col, depth=0, instance=None, instance_id=-1 ):
    #{
@@ -334,7 +339,7 @@ def _sr_export_metascene( path ):
          #{
             instance_id = len( _ms_compiler.instances )
             instance = ms_instance()
-            instance.pstr_name = pack_string( _ms_compiler.strings, col_name )
+            instance.pstr_name = _af_pack_string( col_name )
             instance.override_start = len( _ms_compiler.overrides )
             instance.override_count = 0
             created_instance = True
@@ -358,6 +363,9 @@ def _sr_export_metascene( path ):
          obj_name = obj_realname(o)
          print( "  "*depth + F"{obj_name} ('{o.type}') {data_mode}" )
 
+         if data_mode == 'regular':
+            _mdl_compiler_add_object( o )
+
          if data_mode == 'override':
          #{
             key = col_name + ':' + obj_name
@@ -369,7 +377,7 @@ def _sr_export_metascene( path ):
             override_id = instance.override_count
             init = ms_override()
             init.entity_type = get_entity_enum_id( obj_ent_type( o ) )
-            init.pstr_name = pack_string( _ms_compiler.strings, obj_name )
+            init.pstr_name = _af_pack_string( obj_name )
             compile_obj_transform( o, init.transform )
 
             if o.type == 'ARMATURE':
@@ -397,7 +405,7 @@ def _sr_export_metascene( path ):
                return False
             #}
 
-            _metascene_camera_anims( o, 0 )
+            _metascene_camera_anims( o, sr_entity_id(o) )
          #}
       #}
 
@@ -433,7 +441,6 @@ def _sr_export_metascene( path ):
    info.framerate = bpy.context.scene.render.fps
 
    arrays = {
-      'strings': _ms_compiler.strings.buffer,
       'ms_strip': _ms_compiler.strips,
       'ms_track': _ms_compiler.tracks,
       'ms_keyframe': _ms_compiler.keyframes,
@@ -443,9 +450,15 @@ def _sr_export_metascene( path ):
       'ms_scene_info': [ info ]
    }
 
+   _af_compiler.arrays.update( arrays )
+
+   # Embedded model stuff
+   _mdl_compiler_compile_entities()
+   _mdl_compiler_add_arrays()
+
    header = array_file_header()
    header.version = 2
-   array_file_write( path, header, arrays )
+   array_file_write( path, header )
 
    _ms_compiler.status = F"Written to {path}"
 
diff --git a/skaterift_blender/sr_route_graph.py b/skaterift_blender/sr_route_graph.py
new file mode 100644 (file)
index 0000000..3e3ffc6
--- /dev/null
@@ -0,0 +1,231 @@
+class _route_graphs:
+   pass
+
+def _route_graphs_init():
+#{
+   _route_graphs.collections = {}
+#}
+
+def _get_route_graph( col ):
+#{
+   if col.name in _route_graphs.collections:
+      return _route_graphs.collections[ col.name ]
+
+   curves = []
+   gates = []
+
+   for obj in col.objects:
+   #{
+      if obj.type == 'ARMATURE': pass # ??
+      else:
+      #{
+         ent_type = obj_ent_type( obj )
+
+         if ent_type == 'ent_gate':
+            gates.append( obj )
+         elif ent_type == 'ent_route_node':
+         #{
+            if obj.type == 'CURVE':
+            #{
+               curves.append( obj )
+            #}
+         #}
+      #}
+   #}
+
+   dij = create_node_graph( curves, gates )
+   base_rn = _mdl_compiler_ent_count( 'ent_route_node' )
+
+   for point in dij.points:
+   #{
+      rn = ent_route_node()
+      rn.co[0] =  point[0]
+      rn.co[1] =  point[2]
+      rn.co[2] = -point[1]
+      sr_ent_push( rn )
+   #}
+
+   _route_graphs.collections[ col.name ] = ( dij, base_rn )
+   return _route_graphs.collections[ col.name ]
+#}
+
+def dijkstra( graph, start_node, target_node ):
+#{
+   unvisited = [_ for _ in graph]
+   shortest_path = {}
+   previous_nodes = {}
+   
+   for n in unvisited:
+      shortest_path[n] = 9999999.999999
+   shortest_path[start_node] = 0
+
+   while unvisited:#{
+      current_min_node = None
+      for n in unvisited:#{
+         if current_min_node == None:
+            current_min_node = n
+         elif shortest_path[n] < shortest_path[current_min_node]:
+            current_min_node = n
+      #}
+
+      for branch in graph[current_min_node]:#{
+         tentative_value = shortest_path[current_min_node]
+         tentative_value += graph[current_min_node][branch]
+         if tentative_value < shortest_path[branch]:#{
+            shortest_path[branch] = tentative_value
+            previous_nodes[branch] = current_min_node
+         #}
+      #}
+
+      unvisited.remove(current_min_node)
+   #}
+   
+   path = []
+   node = target_node
+   while node != start_node:#{
+      path.append(node)
+
+      if node not in previous_nodes: return None
+      node = previous_nodes[node]
+   #}
+
+   # Add the start node manually
+   path.append(start_node)
+   return path
+#}
+
+class dij_graph():
+#{
+   def __init__(_,points,graph,subsections):#{
+      _.points = points
+      _.graph = graph
+      _.subsections = subsections
+   #}
+#}
+
+def create_node_graph( curves, gates ):
+#{
+   # add endpoints of curves
+   graph = {}
+   route_points = []
+   subsections = []
+   point_count = 0
+   spline_count = 0
+
+   for c in range(len(curves)):#{
+      for s in range(len(curves[c].data.splines)):#{
+         spline = curves[c].data.splines[s]
+         l = len(spline.points)
+         if l < 2: continue
+
+         dist = round(spline.calc_length(),2)
+
+         ia = point_count
+         ib = point_count+l-1
+
+         graph[ia] = { ib: dist }
+         graph[ib] = { ia: dist }
+         
+         for i in range(len(spline.points)):#{
+            wco = curves[c].matrix_world @ spline.points[i].co
+            route_points.append(Vector((wco[0],wco[1],wco[2]+0.5)))
+
+            previous = ia+i-1
+            proxima = ia+i+1
+
+            if i == 0: previous = -1
+            if i == len(spline.points)-1: proxima = -1
+
+            subsections.append((spline_count,previous,proxima))
+            point_count += 1
+         #}
+
+         spline_count += 1
+      #}
+   #}
+
+   # link endpoints
+   graph_keys = list(graph)
+   for i in range(len(graph_keys)-1):#{
+      for j in range(i+1, len(graph_keys)):#{
+         if i%2==0 and i+1==j: continue
+
+         ni = graph_keys[i]
+         nj = graph_keys[j]
+         pi = route_points[ni]
+         pj = route_points[nj]
+
+         dist = round((pj-pi).magnitude,2)
+
+         if dist < 10.0:#{
+            graph[ni][nj] = dist
+            graph[nj][ni] = dist
+         #}
+      #}
+   #}
+
+   # add and link gates( by name )
+   for gate in gates:#{
+      v1 = gate.matrix_world.to_3x3() @ Vector((0,1,0))
+      if gate.SR_data.ent_gate[0].target:
+         v1 = v1 * -1.0
+
+      graph[ gate.name ] = {}
+
+      for i in range(len(graph_keys)):#{
+         ni = graph_keys[i]
+         pi = route_points[ni]
+
+         v0 = pi-gate.location
+         if v0.dot(v1) < 0.0: continue
+
+         dist = round(v0.magnitude,2)
+
+         if dist < 10.0:#{
+            graph[ gate.name ][ ni ] = dist
+            graph[ ni ][ gate.name ] = dist
+         #}
+      #}
+   #}
+
+   return dij_graph(route_points,graph,subsections)
+#}
+
+def solve_graph( dij, start, end ):
+#{
+   path = dijkstra( dij.graph, end, start )
+   full = []
+
+   if path:#{
+      for sj in range(1,len(path)-2):#{
+         i0 = path[sj]
+         i1 = path[sj+1]
+         map0 = dij.subsections[i0]
+         map1 = dij.subsections[i1]
+
+         if map0[0] == map1[0]:#{
+            if map0[1] == -1: direction = 2
+            else: direction = 1
+            sent = 0
+
+            while True:#{
+               map0 = dij.subsections[i0]
+               i1 = map0[direction]
+               if i1 == -1: break
+               
+               full.append( i0 )
+               sent += 1
+               i0 = i1
+               if sent > 50: break
+            #}
+         #}
+         else:#{
+            full.append( i0 )
+         #}
+      #}
+
+      full.append( path[-2] )
+   #}
+   return full
+#}
+
index d23d8d82d92e29597a5eb5634f597ec33ccb16e1..d68341ffb7f0863461dbd0aa0372e59efb8f18f8 100644 (file)
@@ -2,9 +2,7 @@
 
 void ent_camera_unpack( ent_camera *ent, vg_camera *cam )
 {
-   v3f dir = {0.0f,-1.0f,0.0f};
-   mdl_transform_vector( &ent->transform, dir, dir );
-   v3_angles( dir, cam->angles );
-   v3_copy( ent->transform.co, cam->pos );
+   v3_copy( ent->co, cam->pos ); /* wow */
+   v3_copy( ent->r, cam->angles );
    cam->fov = ent->fov;
 }
index 9cf7a0a3db7caff7d5cf0d76ab9ea032134a3a90..2c04b18eaca7aff3f0836663c8a999c3290bf76c 100644 (file)
@@ -207,9 +207,10 @@ void ent_npc_preupdate( ent_focus_context *ctx )
       f64 t = (vg.time - volc_start_preview) * 0.5;
       skeleton_sample_anim_clamped( sk, &anim_tutorial_cam, t, pose.keyframes );
       
+#if 0
       ent_camera *cam = af_arritm( &world->ent_camera, 
                                      mdl_entity_id_id(ent->camera) );
-      v3_copy( pose.keyframes[0].co, cam->transform.co );
+      v3_copy( pose.keyframes[0].co, cam->co );
 
       v4f qp;
       q_axis_angle( qp, (v3f){1,0,0}, VG_TAUf*0.25f );
@@ -217,6 +218,7 @@ void ent_npc_preupdate( ent_focus_context *ctx )
       q_normalize( cam->transform.q );
 
       v3_add( ent->transform.co, cam->transform.co, cam->transform.co );
+#endif
    }
 
    world_entity_focus_camera( world, ent->camera );
index 19ec5184eb025c23c8ebbfb4c7a554f17ca1f91d..e75af5fbaaf12a6ba0ec9cc2258c148602e1d953 100644 (file)
@@ -220,10 +220,7 @@ void ent_skateshop_preupdate( ent_focus_context *ctx )
    /* camera positioning */
    ent_camera *ref = af_arritm( &world->ent_camera, 
                                  mdl_entity_id_id(shop->id_camera) );
-      
-   v3f dir = {0.0f,-1.0f,0.0f};
-   mdl_transform_vector( &ref->transform, dir, dir );
-   v3_angles( dir, world_static.focus_cam.angles );
+   v3_copy( ref->r, world_static.focus_cam.angles );
 
    v3f lookat;
    if( shop->type == k_skateshop_type_boardshop ||
@@ -232,8 +229,9 @@ void ent_skateshop_preupdate( ent_focus_context *ctx )
                                     mdl_entity_id_id(shop->boards.id_display) );
       v3_sub( display->transform.co, localplayer.rb.co, lookat );
    }
-   else if( shop->type == k_skateshop_type_charshop ){
-      v3_sub( ref->transform.co, localplayer.rb.co, lookat );
+   else if( shop->type == k_skateshop_type_charshop )
+   {
+      v3_sub( ref->co, localplayer.rb.co, lookat );
    }
    else if( shop->type == k_skateshop_type_server ){
       ent_prop *prop = af_arritm( &world->ent_prop,
@@ -246,7 +244,7 @@ void ent_skateshop_preupdate( ent_focus_context *ctx )
    q_axis_angle( localplayer.rb.q, (v3f){0.0f,1.0f,0.0f}, 
                  atan2f(lookat[0],lookat[2]) );
 
-   v3_copy( ref->transform.co, world_static.focus_cam.pos );
+   v3_copy( ref->co, world_static.focus_cam.pos );
    world_static.focus_cam.fov = ref->fov;
 
    /* input */
index d7d7360cea4982d36d0fb2e17d07d37abe24809f..3339019b76afd423b73f79a781026349637b9102 100644 (file)
@@ -63,7 +63,7 @@ enum entity_alias{
    k_ent_cubemap     = 21,
    k_ent_miniworld   = 22,
    k_ent_prop        = 23,
-   k_ent_list        = 24,
+   k_ent_UNUSED0     = 24,
    k_ent_region      = 25,
    k_ent_glider      = 26,
    k_ent_npc         = 27,
@@ -71,6 +71,38 @@ enum entity_alias{
    k_ent_max
 };
 
+const char *_entity_alias_str[] =
+{
+   [k_ent_none] = "none/null",
+   [k_ent_gate] = "ent_gate",
+   [k_ent_spawn] = "ent_spawn",
+   [k_ent_route_node] = "ent_route_node",
+   [k_ent_route] = "ent_route",
+   [k_ent_water] = "ent_water",
+   [k_ent_volume] = "ent_volume",
+   [k_ent_audio] = "ent_audio",
+   [k_ent_marker] = "ent_marker",
+   [k_ent_font] = "ent_font",
+   [k_ent_font_variant] = "ent_font_variant",
+   [k_ent_traffic] = "ent_traffic",
+   [k_ent_skateshop] = "ent_skateshop",
+   [k_ent_camera] = "ent_camera",
+   [k_ent_swspreview] = "ent_swspreview",
+   [k_ent_menuitem] = "ent_menuitem",
+   [k_ent_worldinfo] = "ent_worldinfo",
+   [k_ent_ccmd] = "ent_ccmd",
+   [k_ent_objective] = "ent_objective",
+   [k_ent_challenge] = "ent_challenge",
+   [k_ent_relay] = "ent_relay",
+   [k_ent_cubemap] = "ent_cubemap",
+   [k_ent_miniworld] = "ent_miniworld",
+   [k_ent_prop] = "ent_prop",
+   [k_ent_region] = "ent_region",
+   [k_ent_glider] = "ent_glider",
+   [k_ent_npc] = "ent_npc",
+   [k_ent_armature] = "mdl_armature"
+};
+
 typedef struct ent_call ent_call;
 typedef enum entity_call_result entity_call_result;
 enum entity_call_result 
@@ -345,11 +377,31 @@ struct ent_traffic{
    u32 index;     /* into the path */
 };
 
-struct ent_camera{
+struct ent_camera
+{
+   v3f co, r;
+   f32 fov;
+};
+
+#if (MDL_VERSION_MIN <= 107)
+struct ent_camera_v107
+{
    mdl_transform transform;
    float fov;
 };
 
+static inline void 
+fix_ent_camera_v107( struct ent_camera_v107 *old, ent_camera *new )
+{
+   v3f dir = {0.0f,-1.0f,0.0f};
+   mdl_transform_vector( &old->transform, dir, dir );
+   v3_angles( dir, new->r );
+   v3_copy( old->transform.co, new->co );
+   new->fov = old->fov;
+}
+
+#endif
+
 enum ent_menuitem_type{
    k_ent_menuitem_type_visual       = 0,
    k_ent_menuitem_type_event_button = 1,
index 5556566e70ad0fc0b3179bcb66181dd66e798132..b3a932ff2e8501f209e415a48c9a2288d76230c4 100644 (file)
@@ -10,6 +10,7 @@ void metascene_load( ms_context *ms, const char *path, void *alloc )
    AF_LOAD_ARRAY_STRUCT( &ms->af, &ms->strips, ms_strip, alloc );
    AF_LOAD_ARRAY_STRUCT( &ms->af, &ms->tracks, ms_track, alloc );
    AF_LOAD_ARRAY_STRUCT( &ms->af, &ms->keyframes, ms_keyframe, alloc );
+   AF_LOAD_ARRAY_STRUCT( &ms->af, &ms->cameras, ent_camera, alloc );
    af_load_array( &ms->af, &ms->curves, "ms_curves", 
                    alloc, sizeof(ms_curve_keyframe) );
    af_close( &ms->af );
@@ -68,6 +69,8 @@ struct
          {
             ms_track *track;
             f32 *target;
+            u32 keyframe;
+            u32 semantic;
          }
          curves;
 
@@ -398,6 +401,103 @@ void cutscene_render_instance( struct cs_instance *ins,
    }
 }
 
+#define CS_LOCATION 0
+#define CS_ANGLES 4
+#define CS_FOV 8
+
+struct cs_link_info
+{
+   f32 *target;
+   u32 semantic_type;
+};
+
+static bool link_internal_datapath( struct cs_asoc *asoc, const char *datapath,
+                                    struct cs_link_info *out_link )
+{
+   VG_ASSERT( asoc->entity_type == k_ent_camera );
+
+   ent_camera *cam = af_arritm( &_cutscene.meta.cameras, asoc->entity_index );
+
+   vg_info( "Linking %d#%d:'%s'\n", 
+         asoc->entity_type, asoc->entity_index, datapath );
+
+   struct
+   {
+      const char *prefix;
+      f32 *arr;
+      u32 semantic;
+   }
+   reference[] =
+   {
+      { "location:", cam->co, CS_LOCATION },
+      { "rotation_euler:", cam->r, CS_ANGLES },
+      { "lens:", &cam->fov, CS_FOV }
+   };
+
+   for( u32 i=0; i<VG_ARRAY_LEN(reference); i ++ )
+   {
+      u32 len = strlen( reference[i].prefix );
+      if( !strncmp( reference[i].prefix, datapath, len ) )
+      {
+         i32 offset = atoi( datapath + len );
+         out_link->target = reference[i].arr + offset;
+         out_link->semantic_type = reference[i].semantic + offset;
+         return 1;
+      }
+   }
+
+   return 0;
+}
+
+f32 explicit_bezier( f32 A[2], f32 B[2], f32 C[2], f32 D[2], f32 x )
+{
+   f32 dAxDx = D[0]-A[0],
+       unitBx = (B[0] - A[0]) / dAxDx,
+       unitCx = (C[0] - A[0]) / dAxDx,
+
+       /* cubic coefficients */
+       a =  3.0f*unitBx - 3.0f*unitCx + 1.0f,
+       b = -6.0f*unitBx + 3.0f*unitCx,
+       c =  3.0f*unitBx,
+       d = -(x - A[0]) / dAxDx,
+
+        t0 = 0.0f,
+       Ft0 = d,
+        t1 = 1.0f,
+       Ft1 = a+b+c+d,
+        tc, Ftcx;
+
+   /* Illinois method to find root */
+   for( u32 j=0; j<8; j ++ )
+   {
+      tc = t1 - Ft1*(t1-t0)/(Ft1-Ft0);
+      Ftcx = tc*tc*tc*a + tc*tc*b + tc*c + d;
+
+      if( fabsf(Ftcx) < 0.00001f )
+         break;
+      
+      if( Ft1*Ftcx < 0.0f )
+      {
+         t0 = t1;
+         Ft0 = Ft1;
+      }
+      else
+         Ft0 *= 0.5f;
+
+      t1 = tc;
+      Ft1 = Ftcx;
+   }
+
+   /* Evaluate parametric bezier */
+   f32 t2 = tc*tc,
+       t3 = tc*tc*tc;
+   
+   return  D[1] * t3
+         + C[1] * (-3.0f*t3 + 3.0f*t2)
+         + B[1] * ( 3.0f*t3 - 6.0f*t2 + 3.0f*tc)
+         + A[1] * (-1.0f*t3 + 3.0f*t2 - 3.0f*tc + 1.0f);
+}
+
 void cutscene_update( f32 delta )
 {
    _cutscene.time += delta;
@@ -446,52 +546,71 @@ void cutscene_update( f32 delta )
 
       if( strip->instance_id == 0xffffffff )
       {
-         // TODO
-         _cutscene.strip ++;
-         continue;
-      }
+         /* internal link */
+         struct cs_asoc asoc;
+         _cutscene_get_strip_asoc( strip, &asoc );
 
-      struct cs_instance *ins = &_cutscene.instances[ strip->instance_id ];
+         if( strip->data_mode == 1 )
+         {
+            for( u32 j=0; j<strip->data_count; j ++ )
+            {
+               ms_track *track = af_arritm( &_cutscene.meta.tracks,
+                     strip->data_start + j );
+               
+               VG_ASSERT( _cutscene.active_samplers < 
+                           VG_ARRAY_LEN(_cutscene.samplers) );
+
+               struct cs_sampler *samp =
+                  &_cutscene.samplers[ _cutscene.active_samplers ++ ];
+               samp->strip = strip;
+               samp->curves.track = track;
+
+               const char *datapath = 
+                  af_str( &_cutscene.meta.af, track->pstr_datapath );
+               
+               struct cs_link_info link;
+               VG_ASSERT( link_internal_datapath( &asoc, datapath, &link ) );
+
+               samp->curves.target = link.target;
+               samp->curves.semantic = link.semantic_type;
+               samp->curves.keyframe = 0;
+               samp->override = asoc.override;
+               VG_ASSERT( samp->curves.target );
+            }
+         }
+         else VG_ASSERT(0);
+      }
+      else
+      {
+         /* external link */
+         struct cs_instance *ins = &_cutscene.instances[ strip->instance_id ];
 
-      struct cs_asoc asoc;
-      _cutscene_get_strip_asoc( strip, &asoc );
-      VG_ASSERT( asoc.entity_type == 28 );
+         struct cs_asoc asoc;
+         _cutscene_get_strip_asoc( strip, &asoc );
+         VG_ASSERT( asoc.entity_type == 28 );
 
-      if( strip->data_mode == 1 )
-      {
-         for( u32 j=0; j<strip->data_count; j ++ )
+         if( strip->data_mode == 1 )
+         {
+            VG_ASSERT(0);
+         }
+         else
          {
-            ms_track *track = af_arritm( &_cutscene.meta.tracks,
-                  strip->data_start + j );
-            
             VG_ASSERT( _cutscene.active_samplers < 
                         VG_ARRAY_LEN(_cutscene.samplers) );
 
-            struct cs_sampler *samp =
+            struct cs_sampler *samp = 
                &_cutscene.samplers[ _cutscene.active_samplers ++ ];
+
+            struct model_ref *ref = &_cutscene.refs[ ins->ref_id ];
+            struct cs_skeleton *skele = &ref->skeletons[ asoc.entity_index ];
+
             samp->strip = strip;
-            samp->curves.track = track;
-            samp->curves.target = NULL; /* DOTO */
+            samp->skeleton.skinning_data = 
+               &ins->skinning_data[ skele->skinning_offset ];
+            samp->skeleton.ref_sk = &skele->sk;
             samp->override = asoc.override;
          }
       }
-      else
-      {
-         VG_ASSERT( _cutscene.active_samplers < 
-                     VG_ARRAY_LEN(_cutscene.samplers) );
-
-         struct cs_sampler *samp = 
-            &_cutscene.samplers[ _cutscene.active_samplers ++ ];
-
-         struct model_ref *ref = &_cutscene.refs[ ins->ref_id ];
-         struct cs_skeleton *skele = &ref->skeletons[ asoc.entity_index ];
-
-         samp->strip = strip;
-         samp->skeleton.skinning_data = 
-            &ins->skinning_data[ skele->skinning_offset ];
-         samp->skeleton.ref_sk = &skele->sk;
-         samp->override = asoc.override;
-      }
       
       _cutscene.strip ++;
    }
@@ -501,37 +620,155 @@ void cutscene_update( f32 delta )
    {
       struct cs_sampler *samp = &_cutscene.samplers[ i ];
 
-      struct skeleton_anim temp_anim = 
+      if( samp->strip->data_mode == 0 )
       {
-         .strip = samp->strip,
-         .framerate = _cutscene.meta.info.framerate,
-         .keyframes_base = af_arritm( &_cutscene.meta.keyframes,
-                     samp->strip->data_start )
-      };
+         struct skeleton_anim temp_anim = 
+         {
+            .strip = samp->strip,
+            .framerate = _cutscene.meta.info.framerate,
+            .keyframes_base = af_arritm( &_cutscene.meta.keyframes,
+                        samp->strip->data_start )
+         };
 
-      f32 t  = _cutscene.time;
-          t -= (f32)samp->strip->offset / _cutscene.meta.info.framerate;
+         f32 t  = _cutscene.time;
+             t -= (f32)samp->strip->offset / _cutscene.meta.info.framerate;
 
-      struct skeleton *ref_sk = samp->skeleton.ref_sk;
-      m4x3f *final_mtx = samp->skeleton.skinning_data;
+         struct skeleton *ref_sk = samp->skeleton.ref_sk;
+         m4x3f *final_mtx = samp->skeleton.skinning_data;
 
-      ms_keyframe pose[32];
-      skeleton_sample_anim( ref_sk, &temp_anim, t, pose );
+         ms_keyframe pose[32];
+         skeleton_sample_anim( ref_sk, &temp_anim, t, pose );
 
-      skeleton_apply_pose( ref_sk, pose, 
-                           k_anim_apply_defer_ik, final_mtx );
-      skeleton_apply_ik_pass( ref_sk, final_mtx );
-      skeleton_apply_pose( ref_sk, pose,
-                           k_anim_apply_deffered_only, final_mtx );
-      skeleton_apply_inverses( ref_sk, final_mtx );
+         skeleton_apply_pose( ref_sk, pose, 
+                              k_anim_apply_defer_ik, final_mtx );
+         skeleton_apply_ik_pass( ref_sk, final_mtx );
+         skeleton_apply_pose( ref_sk, pose,
+                              k_anim_apply_deffered_only, final_mtx );
+         skeleton_apply_inverses( ref_sk, final_mtx );
 
-      if( samp->override )
+         if( samp->override )
+         {
+            m4x3f mmdl;
+            mdl_transform_m4x3( &samp->override->transform, mmdl );
+            skeleton_apply_transform( ref_sk, mmdl, final_mtx );
+         }
+      }
+      else
       {
-         m4x3f mmdl;
-         mdl_transform_m4x3( &samp->override->transform, mmdl );
-         skeleton_apply_transform( ref_sk, mmdl, final_mtx );
+         f32 scene_t = _cutscene.time * _cutscene.meta.info.framerate,
+             t = scene_t - samp->strip->offset;
+
+         ms_curve_keyframe *kl = af_arritm( &_cutscene.meta.curves, 
+                  samp->curves.track->keyframe_start + samp->curves.keyframe ),
+                           *kr = NULL;
+
+         if( t > kl->co[0] )
+         {
+            if( samp->curves.track->keyframe_count > 1 )
+            {
+               for( u32 j=samp->curves.keyframe+1; 
+                        j<samp->curves.track->keyframe_count; j ++ )
+               {
+                  kr = af_arritm( &_cutscene.meta.curves, 
+                                  samp->curves.track->keyframe_start + j );
+
+                  if( kr->co[0] <= t )
+                  {
+                     kl = kr;
+                     kr = NULL;
+                     samp->curves.keyframe = j;
+                  }
+                  else break;
+               }
+            }
+         }
+
+         if( kl && kr )
+         {
+#if 0
+            f32 A = kl->co[0],
+                D = kr->co[0],
+                L = D-A,
+                B = (kl->r[0] - A) / L,
+                C = (kr->l[0] - A) / L,
+                a = 1.0f + 3.0f*B - 3.0f*C,
+                b = -6.0f*B + 3.0f*C,
+                c = 3.0f*B,
+                d = -(t - A) / L;
+
+/* ILLINOIS */
+            f32 fa = d,
+                fb = a+b+c+d,
+                xMin = 0.0f,
+                xMax = 1.0f,
+                e,x1;
+
+            for( u32 j=0; j<6; j ++ )
+            {
+               x1 = xMax - fb*(xMax-xMin)/(fb-fa);
+               e  = x1*x1*x1*a + x1*x1*b + x1*c + d;
+
+               if( fabsf(e) < 0.0001f )
+                  break;
+
+               if( fb*e < 0.0f )
+               {
+                  xMin = xMax;
+                  fa = fb;
+               }
+               else
+                  fa = fa*0.5f;
+
+               xMax = x1;
+               fb = e;
+            }
+            
+/* BISECTION */
+#if 0
+            /* One day I will have my revenge on cubics I swear */
+            f32 x1 = 0.5f,
+                xMin = 0.0f,
+                xMax = 1.0f,
+                e;
+            for( u32 j=0; j<16; j ++ )
+            {
+               e = x1*x1*x1*a + x1*x1*b + x1*c + d;
+               if( e > 0.0f ) xMax = x1;
+               else           xMin = x1;
+               x1 = (xMin+xMax)*0.5f;
+            }
+#endif
+
+            if( samp->curves.semantic == CS_LOCATION+1 )
+               vg_info( "convergence: %.9f\n", e );
+
+            f32 x2 = x1*x1,
+                x3 = x2*x1,
+                Ay = kl->co[1],
+                By = kl->r[1],
+                Cy = kr->l[1],
+                Dy = kr->co[1],
+                y =    Dy*x3 
+                     + Cy*(-3.0f*x3 + 3.0f*x2) 
+                     + By*( 3.0f*x3 - 6.0f*x2 + 3.0f*x1)
+                     + Ay*(-x3      + 3.0f*x2 - 3.0f*x1 + 1.0f );
+#endif
+
+            *samp->curves.target = 
+               explicit_bezier( kl->co, kl->r, kr->l, kr->co, t );
+         }
+         else
+         {
+            *samp->curves.target = kl->co[1];
+         }
       }
    }
+
+   for( u32 i=0; i<af_arrcount( &_cutscene.meta.cameras ); i ++ )
+   {
+      ent_camera *cam = af_arritm( &_cutscene.meta.cameras, i );
+      vg_line_cross( cam->co, VG__RED, 0.2f );
+   }
 }
 
 void cutscene_render( world_instance *world, vg_camera *cam )
index abcc7c250171d9db82a13b03c06b017248650cbb..45e19b6bca552cdc8f61baa0cdccb1ce79033891 100644 (file)
@@ -30,7 +30,9 @@ struct ms_context
                          strips,
                          tracks,
                          keyframes,
-                         curves;
+                         curves,
+
+                         cameras; /* kinda temp? */
 };
 
 struct ms_instance
index 8ec57145b25dffcfd372bc84bc80d42e72309a6b..fdf1608235df7967070041a251953361ca329dc2 100644 (file)
@@ -5,7 +5,7 @@
 #pragma once
 
 #define MDL_VERSION_MIN 101
-#define MDL_VERSION_NR 107
+#define MDL_VERSION_NR 108
 
 #include "array_file.h"
 
index 27dd7234b953f8cb6dc5ceed3e32bab9d631417b..b84c41f0d10afcd4b671fb6ee907e6e3a21676f4 100644 (file)
@@ -236,7 +236,7 @@ void vg_pre_update(void)
    {
       if( menu.bg_cam )
       {
-         v3_copy( menu.bg_cam->transform.co, listen_co );
+         v3_copy( menu.bg_cam->co, listen_co );
       }
       else target = 0;
    }
index cef2cf77e4033c72328c8b0bf39e39aec7fa78a3..2d3c3d2570f0fc2ac1d8dde2847c0139f62bf43a 100644 (file)
@@ -1004,7 +1004,7 @@ static void workshop_render_board_preview(void)
 
    vg_camera cam;
    v3f basevector;
-   v3_sub( display->transform.co, ref->transform.co, basevector );
+   v3_sub( display->transform.co, ref->co, basevector );
    float dist = v3_length( basevector );
 
    v3f baseangles;
index 1dbd3155f8cc51574bf5cfe1b5802290ca3442de..23f1f1be2d965ed307003a6bdf3ab4a5c2501779 100644 (file)
@@ -47,6 +47,22 @@ static void world_instance_load_mdl( u32 instance_id, const char *path ){
 
    AF_LOAD_ARRAY_STRUCT( af, &world->ent_gate,      ent_gate,       heap );
    AF_LOAD_ARRAY_STRUCT( af, &world->ent_camera,    ent_camera,     heap );
+
+#if (MDL_VERSION_MIN <= 107)
+   if( meta->version <= 107 )
+   {
+      array_file_ptr legacy_cameras;
+      af_load_array( af, &legacy_cameras, "ent_camera",
+            vg_mem.scratch, sizeof(struct ent_camera_v107) );
+
+      for( u32 i=0; i<af_arrcount(&legacy_cameras); i ++ )
+      {
+         fix_ent_camera_v107( af_arritm( &legacy_cameras, i ), 
+                              af_arritm( &world->ent_camera, i ) );
+      }
+   }
+#endif
+
    AF_LOAD_ARRAY_STRUCT( af, &world->ent_spawn,     ent_spawn,      heap );
    AF_LOAD_ARRAY_STRUCT( af, &world->ent_light,     ent_light,      heap );
    AF_LOAD_ARRAY_STRUCT( af, &world->ent_route_node,ent_route_node, heap );