From: hgn Date: Tue, 10 Dec 2024 11:49:53 +0000 (+0000) Subject: something about metascenes/exporter X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=591fe92605c2022b4e8b5bddff8faee93cb758cf;p=carveJwlIkooP6JGAAIwe30JlM.git something about metascenes/exporter --- diff --git a/content_skaterift/maps/dev_hub/main.mdl b/content_skaterift/maps/dev_hub/main.mdl index 28304f5..1159e0a 100644 Binary files a/content_skaterift/maps/dev_hub/main.mdl and b/content_skaterift/maps/dev_hub/main.mdl differ diff --git a/content_skaterift/maps/mp_hq/main.mdl b/content_skaterift/maps/mp_hq/main.mdl index e64efab..d7e7800 100644 Binary files a/content_skaterift/maps/mp_hq/main.mdl and b/content_skaterift/maps/mp_hq/main.mdl differ diff --git a/content_skaterift/maps/mp_spawn/main.mdl b/content_skaterift/maps/mp_spawn/main.mdl index 74fb300..7f454e0 100644 Binary files a/content_skaterift/maps/mp_spawn/main.mdl and b/content_skaterift/maps/mp_spawn/main.mdl differ diff --git a/content_skaterift/metascenes/test_scene.ms b/content_skaterift/metascenes/test_scene.ms index da5827d..3f9eba0 100644 Binary files a/content_skaterift/metascenes/test_scene.ms and b/content_skaterift/metascenes/test_scene.ms differ diff --git a/content_skaterift/models/rs_font.mdl b/content_skaterift/models/rs_font.mdl index 7d2d808..7316f26 100644 Binary files a/content_skaterift/models/rs_font.mdl and b/content_skaterift/models/rs_font.mdl differ diff --git a/skaterift_blender/__init__.py b/skaterift_blender/__init__.py index 3017a73..21fc38f 100644 --- a/skaterift_blender/__init__.py +++ b/skaterift_blender/__init__.py @@ -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" ) diff --git a/skaterift_blender/sr_bin.py b/skaterift_blender/sr_bin.py index 729a2ef..74e452f 100644 --- a/skaterift_blender/sr_bin.py +++ b/skaterift_blender/sr_bin.py @@ -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 ) diff --git a/skaterift_blender/sr_main.py b/skaterift_blender/sr_main.py index df5d9fc..32297b5 100644 --- a/skaterift_blender/sr_main.py +++ b/skaterift_blender/sr_main.py @@ -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,\ diff --git a/skaterift_blender/sr_mat.py b/skaterift_blender/sr_mat.py index 8859ecf..66e9261 100644 --- a/skaterift_blender/sr_mat.py +++ b/skaterift_blender/sr_mat.py @@ -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 index 0000000..16cdaea --- /dev/null +++ b/skaterift_blender/sr_mdl.py @@ -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 ) +#} diff --git a/skaterift_blender/sr_metascene.py b/skaterift_blender/sr_metascene.py index 6efeb95..94e587a 100644 --- a/skaterift_blender/sr_metascene.py +++ b/skaterift_blender/sr_metascene.py @@ -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 index 0000000..3e3ffc6 --- /dev/null +++ b/skaterift_blender/sr_route_graph.py @@ -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 +#} + diff --git a/src/ent_camera.c b/src/ent_camera.c index d23d8d8..d68341f 100644 --- a/src/ent_camera.c +++ b/src/ent_camera.c @@ -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; } diff --git a/src/ent_npc.c b/src/ent_npc.c index 9cf7a0a..2c04b18 100644 --- a/src/ent_npc.c +++ b/src/ent_npc.c @@ -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 ); diff --git a/src/ent_skateshop.c b/src/ent_skateshop.c index 19ec518..e75af5f 100644 --- a/src/ent_skateshop.c +++ b/src/ent_skateshop.c @@ -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 */ diff --git a/src/entity.h b/src/entity.h index d7d7360..3339019 100644 --- a/src/entity.h +++ b/src/entity.h @@ -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, diff --git a/src/metascene.c b/src/metascene.c index 5556566..b3a932f 100644 --- a/src/metascene.c +++ b/src/metascene.c @@ -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; itarget = 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; jdata_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; jdata_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; + jcurves.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; ico, VG__RED, 0.2f ); + } } void cutscene_render( world_instance *world, vg_camera *cam ) diff --git a/src/metascene.h b/src/metascene.h index abcc7c2..45e19b6 100644 --- a/src/metascene.h +++ b/src/metascene.h @@ -30,7 +30,9 @@ struct ms_context strips, tracks, keyframes, - curves; + curves, + + cameras; /* kinda temp? */ }; struct ms_instance diff --git a/src/model.h b/src/model.h index 8ec5714..fdf1608 100644 --- a/src/model.h +++ b/src/model.h @@ -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" diff --git a/src/skaterift.c b/src/skaterift.c index 27dd723..b84c41f 100644 --- a/src/skaterift.c +++ b/src/skaterift.c @@ -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; } diff --git a/src/workshop.c b/src/workshop.c index cef2cf7..2d3c3d2 100644 --- a/src/workshop.c +++ b/src/workshop.c @@ -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; diff --git a/src/world_load.c b/src/world_load.c index 1dbd315..23f1f1b 100644 --- a/src/world_load.c +++ b/src/world_load.c @@ -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; ient_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 );