+ # 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.armature_data.extend(bytearray(node))
+#}
+
+def sr_ent_push( struct ):
+#{
+ clase = type(struct).__name__
+
+ if clase not in sr_compile.entity_data:#{
+ sr_compile.entity_data[ clase ] = bytearray()
+ sr_compile.entity_info[ clase ] = { 'size': sizeof(struct) }
+ #}
+
+ index = len(sr_compile.entity_data[ clase ])//sizeof(struct)
+ sr_compile.entity_data[ clase ].extend( bytearray(struct) )
+ return index
+#}
+
+def sr_array_title( arr, name, count, size, offset ):
+#{
+ for i in range(len(name)):#{
+ arr.name[i] = ord(name[i])
+ #}
+ arr.file_offset = offset
+ arr.item_count = count
+ arr.item_size = size
+#}
+
+def sr_compile( collection ):
+#{
+ print( F"[SR] compiler begin ({collection.name}.mdl)" )
+
+ #settings
+ sr_compile.pack_textures = collection.SR_data.pack_textures
+ sr_compile.pack_animations = collection.SR_data.animations
+
+ # caches
+ sr_compile.string_cache = {}
+ sr_compile.mesh_cache = {}
+ sr_compile.material_cache = {}
+ sr_compile.texture_cache = {}
+
+ # compiled data
+ sr_compile.mesh_data = bytearray()
+ sr_compile.submesh_data = bytearray()
+ sr_compile.vertex_data = bytearray()
+ sr_compile.indice_data = bytearray()
+ sr_compile.bone_data = bytearray()
+ sr_compile.material_data = bytearray()
+ sr_compile.armature_data = bytearray()
+ sr_compile.anim_data = bytearray()
+ sr_compile.keyframe_data = bytearray()
+ sr_compile.texture_data = bytearray()
+
+ # just bytes not structures
+ sr_compile.string_data = bytearray()
+ sr_compile.pack_data = bytearray()
+
+ # variable
+ sr_compile.entity_data = {}
+ sr_compile.entity_info = {}
+
+ print( F"[SR] assign entity ID's" )
+ sr_compile.entities = {}
+ sr_compile.entity_ids = {}
+
+ 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
+ print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}', end='\r' )
+ sr_compile_mesh( obj )
+ #}
+ #}
+
+ checkpoint_count = 0
+ pathindice_count = 0
+ audio_clip_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 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_gate': #{
+ gate = ent_gate()
+ gate.type = 0
+ obj_data = obj.SR_data.ent_gate[0]
+ mesh_data = obj.data.SR_data.ent_gate[0]
+ if obj_data.target:#{
+ gate.target = sr_compile.entity_ids[obj_data.target.name]
+ gate.type = 1
+ #}
+ gate.dimensions[0] = mesh_data.dimensions[0]
+ gate.dimensions[1] = mesh_data.dimensions[1]
+ gate.dimensions[2] = mesh_data.dimensions[2]
+
+ 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.name )
+ sr_ent_push( spawn )
+ #}
+ elif ent_type == 'ent_route': #{
+ obj_data = obj.SR_data.ent_route[0]
+ route = ent_route()
+ route.pstr_name = sr_compile_string( obj_data.alias )
+ route.checkpoints_start = checkpoint_count
+ route.checkpoints_count = 0
+
+ for ci in range(3):
+ route.colour[ci] = obj_data.colour[ci]
+ route.colour[3] = 1.0
+
+ compile_obj_transform( obj, route.transform )
+
+ checkpoints = obj_data.gates
+ route_nodes = []
+
+ for uc in obj.users_collection[0].objects:#{
+ uc_type = obj_ent_type( uc )
+ if uc_type == 'ent_gate' or uc_type == 'ent_route_node':
+ route_nodes += [uc]
+ #}
+ graph = node_graph( route_nodes )
+
+ 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 = dijkstra( graph, gj.name, gi.name )
+ if path:#{
+ for pi in range(1,len(path)-1):#{
+ pathindice = ent_path_index()
+ pathindice.index = sr_compile.entity_ids[path[pi]]
+ sr_ent_push( pathindice )
+
+ checkpoint.path_count += 1
+ pathindice_count += 1
+ #}
+ #}
+
+ sr_ent_push( checkpoint )
+ route.checkpoints_count += 1
+ checkpoint_count += 1
+ #}
+
+ sr_ent_push( route )
+ #}
+ elif ent_type == 'ent_route_node':#{
+ rn = ent_route_node()
+ rn.co[0] = obj.location[0]
+ rn.co[1] = obj.location[2]
+ rn.co[2] = -obj.location[1]
+ sr_ent_push( rn )
+ #}
+ 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:#{
+ target = obj_data.target
+ volume.target.type = sr_entity_alias[obj_ent_type(target)]
+ volume.target.index = sr_compile.entity_ids[ target.name ]
+ #}
+
+ sr_ent_push(volume)
+ #}
+ #}
+ #}
+
+ print( F"[SR] Writing file" )
+
+ file_array_instructions = {}
+ file_offset = 0
+
+ def _write_array( name, item_size, data ):#{
+ nonlocal file_array_instructions, file_offset
+
+ count = len(data)//item_size
+ file_array_instructions[name] = {'count':count, 'size':item_size,\
+ 'data':data, 'offset': file_offset}
+ file_offset += len(data)
+ file_offset = int_align_to( file_offset, 8 )
+ #}
+
+ _write_array( 'strings', 1, sr_compile.string_data )
+ _write_array( 'mdl_mesh', sizeof(mdl_mesh), sr_compile.mesh_data )
+ _write_array( 'mdl_submesh', sizeof(mdl_submesh), sr_compile.submesh_data )
+ _write_array( 'mdl_material', sizeof(mdl_material), sr_compile.material_data)
+ _write_array( 'mdl_texture', sizeof(mdl_texture), sr_compile.texture_data)
+ _write_array( 'mdl_armature', sizeof(mdl_armature), sr_compile.armature_data)
+ _write_array( 'mdl_bone', sizeof(mdl_bone), sr_compile.bone_data )
+
+ for name, buffer in sr_compile.entity_data.items():#{
+ _write_array( name, sr_compile.entity_info[name]['size'], buffer )
+ #}
+
+ _write_array( 'mdl_animation', sizeof(mdl_animation), sr_compile.anim_data)
+ _write_array( 'mdl_keyframe', sizeof(mdl_transform),sr_compile.keyframe_data)
+ _write_array( 'mdl_vert', sizeof(mdl_vert), sr_compile.vertex_data )
+ _write_array( 'mdl_indice', sizeof(c_uint32), sr_compile.indice_data )
+ _write_array( 'pack', 1, sr_compile.pack_data )
+
+ header_size = int_align_to( sizeof(mdl_header), 8 )
+ index_size = int_align_to( sizeof(mdl_array)*len(file_array_instructions),8 )
+
+ folder = bpy.path.abspath(bpy.context.scene.SR_data.export_dir)
+ path = F"{folder}{collection.name}.mdl"
+ print( path )