#}
_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" )
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 ):
#}
#}
-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
#}
# 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 )
# 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 )
# 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
# 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 )
('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 ),
# 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',\
("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)]
#}
("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
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)]
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:
#{
#}
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):
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' )
#}
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")
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)
#}
#}
-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))
(.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 )
- #}
- #}
- #}
- #}
#}
#}
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,\
#{
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:
# 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
#}
--- /dev/null
+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 )
+#}
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 = []
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
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
#
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
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 )
#}
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 )
#}
#}
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 ):
#{
#{
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
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
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':
return False
#}
- _metascene_camera_anims( o, 0 )
+ _metascene_camera_anims( o, sr_entity_id(o) )
#}
#}
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,
'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}"
--- /dev/null
+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
+#}
+
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;
}
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 );
q_normalize( cam->transform.q );
v3_add( ent->transform.co, cam->transform.co, cam->transform.co );
+#endif
}
world_entity_focus_camera( world, ent->camera );
/* 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 ||
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,
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 */
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,
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
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,
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 );
{
ms_track *track;
f32 *target;
+ u32 keyframe;
+ u32 semantic;
}
curves;
}
}
+#define CS_LOCATION 0
+#define CS_ANGLES 4
+#define CS_FOV 8
+
+struct cs_link_info
+{
+ f32 *target;
+ u32 semantic_type;
+};
+
+static bool link_internal_datapath( struct cs_asoc *asoc, const char *datapath,
+ struct cs_link_info *out_link )
+{
+ VG_ASSERT( asoc->entity_type == k_ent_camera );
+
+ ent_camera *cam = af_arritm( &_cutscene.meta.cameras, asoc->entity_index );
+
+ vg_info( "Linking %d#%d:'%s'\n",
+ asoc->entity_type, asoc->entity_index, datapath );
+
+ struct
+ {
+ const char *prefix;
+ f32 *arr;
+ u32 semantic;
+ }
+ reference[] =
+ {
+ { "location:", cam->co, CS_LOCATION },
+ { "rotation_euler:", cam->r, CS_ANGLES },
+ { "lens:", &cam->fov, CS_FOV }
+ };
+
+ for( u32 i=0; i<VG_ARRAY_LEN(reference); i ++ )
+ {
+ u32 len = strlen( reference[i].prefix );
+ if( !strncmp( reference[i].prefix, datapath, len ) )
+ {
+ i32 offset = atoi( datapath + len );
+ out_link->target = reference[i].arr + offset;
+ out_link->semantic_type = reference[i].semantic + offset;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+f32 explicit_bezier( f32 A[2], f32 B[2], f32 C[2], f32 D[2], f32 x )
+{
+ f32 dAxDx = D[0]-A[0],
+ unitBx = (B[0] - A[0]) / dAxDx,
+ unitCx = (C[0] - A[0]) / dAxDx,
+
+ /* cubic coefficients */
+ a = 3.0f*unitBx - 3.0f*unitCx + 1.0f,
+ b = -6.0f*unitBx + 3.0f*unitCx,
+ c = 3.0f*unitBx,
+ d = -(x - A[0]) / dAxDx,
+
+ t0 = 0.0f,
+ Ft0 = d,
+ t1 = 1.0f,
+ Ft1 = a+b+c+d,
+ tc, Ftcx;
+
+ /* Illinois method to find root */
+ for( u32 j=0; j<8; j ++ )
+ {
+ tc = t1 - Ft1*(t1-t0)/(Ft1-Ft0);
+ Ftcx = tc*tc*tc*a + tc*tc*b + tc*c + d;
+
+ if( fabsf(Ftcx) < 0.00001f )
+ break;
+
+ if( Ft1*Ftcx < 0.0f )
+ {
+ t0 = t1;
+ Ft0 = Ft1;
+ }
+ else
+ Ft0 *= 0.5f;
+
+ t1 = tc;
+ Ft1 = Ftcx;
+ }
+
+ /* Evaluate parametric bezier */
+ f32 t2 = tc*tc,
+ t3 = tc*tc*tc;
+
+ return D[1] * t3
+ + C[1] * (-3.0f*t3 + 3.0f*t2)
+ + B[1] * ( 3.0f*t3 - 6.0f*t2 + 3.0f*tc)
+ + A[1] * (-1.0f*t3 + 3.0f*t2 - 3.0f*tc + 1.0f);
+}
+
void cutscene_update( f32 delta )
{
_cutscene.time += delta;
if( strip->instance_id == 0xffffffff )
{
- // TODO
- _cutscene.strip ++;
- continue;
- }
+ /* internal link */
+ struct cs_asoc asoc;
+ _cutscene_get_strip_asoc( strip, &asoc );
- struct cs_instance *ins = &_cutscene.instances[ strip->instance_id ];
+ if( strip->data_mode == 1 )
+ {
+ for( u32 j=0; j<strip->data_count; j ++ )
+ {
+ ms_track *track = af_arritm( &_cutscene.meta.tracks,
+ strip->data_start + j );
+
+ VG_ASSERT( _cutscene.active_samplers <
+ VG_ARRAY_LEN(_cutscene.samplers) );
+
+ struct cs_sampler *samp =
+ &_cutscene.samplers[ _cutscene.active_samplers ++ ];
+ samp->strip = strip;
+ samp->curves.track = track;
+
+ const char *datapath =
+ af_str( &_cutscene.meta.af, track->pstr_datapath );
+
+ struct cs_link_info link;
+ VG_ASSERT( link_internal_datapath( &asoc, datapath, &link ) );
+
+ samp->curves.target = link.target;
+ samp->curves.semantic = link.semantic_type;
+ samp->curves.keyframe = 0;
+ samp->override = asoc.override;
+ VG_ASSERT( samp->curves.target );
+ }
+ }
+ else VG_ASSERT(0);
+ }
+ else
+ {
+ /* external link */
+ struct cs_instance *ins = &_cutscene.instances[ strip->instance_id ];
- struct cs_asoc asoc;
- _cutscene_get_strip_asoc( strip, &asoc );
- VG_ASSERT( asoc.entity_type == 28 );
+ struct cs_asoc asoc;
+ _cutscene_get_strip_asoc( strip, &asoc );
+ VG_ASSERT( asoc.entity_type == 28 );
- if( strip->data_mode == 1 )
- {
- for( u32 j=0; j<strip->data_count; j ++ )
+ if( strip->data_mode == 1 )
+ {
+ VG_ASSERT(0);
+ }
+ else
{
- ms_track *track = af_arritm( &_cutscene.meta.tracks,
- strip->data_start + j );
-
VG_ASSERT( _cutscene.active_samplers <
VG_ARRAY_LEN(_cutscene.samplers) );
- struct cs_sampler *samp =
+ struct cs_sampler *samp =
&_cutscene.samplers[ _cutscene.active_samplers ++ ];
+
+ struct model_ref *ref = &_cutscene.refs[ ins->ref_id ];
+ struct cs_skeleton *skele = &ref->skeletons[ asoc.entity_index ];
+
samp->strip = strip;
- samp->curves.track = track;
- samp->curves.target = NULL; /* DOTO */
+ samp->skeleton.skinning_data =
+ &ins->skinning_data[ skele->skinning_offset ];
+ samp->skeleton.ref_sk = &skele->sk;
samp->override = asoc.override;
}
}
- else
- {
- VG_ASSERT( _cutscene.active_samplers <
- VG_ARRAY_LEN(_cutscene.samplers) );
-
- struct cs_sampler *samp =
- &_cutscene.samplers[ _cutscene.active_samplers ++ ];
-
- struct model_ref *ref = &_cutscene.refs[ ins->ref_id ];
- struct cs_skeleton *skele = &ref->skeletons[ asoc.entity_index ];
-
- samp->strip = strip;
- samp->skeleton.skinning_data =
- &ins->skinning_data[ skele->skinning_offset ];
- samp->skeleton.ref_sk = &skele->sk;
- samp->override = asoc.override;
- }
_cutscene.strip ++;
}
{
struct cs_sampler *samp = &_cutscene.samplers[ i ];
- struct skeleton_anim temp_anim =
+ if( samp->strip->data_mode == 0 )
{
- .strip = samp->strip,
- .framerate = _cutscene.meta.info.framerate,
- .keyframes_base = af_arritm( &_cutscene.meta.keyframes,
- samp->strip->data_start )
- };
+ struct skeleton_anim temp_anim =
+ {
+ .strip = samp->strip,
+ .framerate = _cutscene.meta.info.framerate,
+ .keyframes_base = af_arritm( &_cutscene.meta.keyframes,
+ samp->strip->data_start )
+ };
- f32 t = _cutscene.time;
- t -= (f32)samp->strip->offset / _cutscene.meta.info.framerate;
+ f32 t = _cutscene.time;
+ t -= (f32)samp->strip->offset / _cutscene.meta.info.framerate;
- struct skeleton *ref_sk = samp->skeleton.ref_sk;
- m4x3f *final_mtx = samp->skeleton.skinning_data;
+ struct skeleton *ref_sk = samp->skeleton.ref_sk;
+ m4x3f *final_mtx = samp->skeleton.skinning_data;
- ms_keyframe pose[32];
- skeleton_sample_anim( ref_sk, &temp_anim, t, pose );
+ ms_keyframe pose[32];
+ skeleton_sample_anim( ref_sk, &temp_anim, t, pose );
- skeleton_apply_pose( ref_sk, pose,
- k_anim_apply_defer_ik, final_mtx );
- skeleton_apply_ik_pass( ref_sk, final_mtx );
- skeleton_apply_pose( ref_sk, pose,
- k_anim_apply_deffered_only, final_mtx );
- skeleton_apply_inverses( ref_sk, final_mtx );
+ skeleton_apply_pose( ref_sk, pose,
+ k_anim_apply_defer_ik, final_mtx );
+ skeleton_apply_ik_pass( ref_sk, final_mtx );
+ skeleton_apply_pose( ref_sk, pose,
+ k_anim_apply_deffered_only, final_mtx );
+ skeleton_apply_inverses( ref_sk, final_mtx );
- if( samp->override )
+ if( samp->override )
+ {
+ m4x3f mmdl;
+ mdl_transform_m4x3( &samp->override->transform, mmdl );
+ skeleton_apply_transform( ref_sk, mmdl, final_mtx );
+ }
+ }
+ else
{
- m4x3f mmdl;
- mdl_transform_m4x3( &samp->override->transform, mmdl );
- skeleton_apply_transform( ref_sk, mmdl, final_mtx );
+ f32 scene_t = _cutscene.time * _cutscene.meta.info.framerate,
+ t = scene_t - samp->strip->offset;
+
+ ms_curve_keyframe *kl = af_arritm( &_cutscene.meta.curves,
+ samp->curves.track->keyframe_start + samp->curves.keyframe ),
+ *kr = NULL;
+
+ if( t > kl->co[0] )
+ {
+ if( samp->curves.track->keyframe_count > 1 )
+ {
+ for( u32 j=samp->curves.keyframe+1;
+ j<samp->curves.track->keyframe_count; j ++ )
+ {
+ kr = af_arritm( &_cutscene.meta.curves,
+ samp->curves.track->keyframe_start + j );
+
+ if( kr->co[0] <= t )
+ {
+ kl = kr;
+ kr = NULL;
+ samp->curves.keyframe = j;
+ }
+ else break;
+ }
+ }
+ }
+
+ if( kl && kr )
+ {
+#if 0
+ f32 A = kl->co[0],
+ D = kr->co[0],
+ L = D-A,
+ B = (kl->r[0] - A) / L,
+ C = (kr->l[0] - A) / L,
+ a = 1.0f + 3.0f*B - 3.0f*C,
+ b = -6.0f*B + 3.0f*C,
+ c = 3.0f*B,
+ d = -(t - A) / L;
+
+/* ILLINOIS */
+ f32 fa = d,
+ fb = a+b+c+d,
+ xMin = 0.0f,
+ xMax = 1.0f,
+ e,x1;
+
+ for( u32 j=0; j<6; j ++ )
+ {
+ x1 = xMax - fb*(xMax-xMin)/(fb-fa);
+ e = x1*x1*x1*a + x1*x1*b + x1*c + d;
+
+ if( fabsf(e) < 0.0001f )
+ break;
+
+ if( fb*e < 0.0f )
+ {
+ xMin = xMax;
+ fa = fb;
+ }
+ else
+ fa = fa*0.5f;
+
+ xMax = x1;
+ fb = e;
+ }
+
+/* BISECTION */
+#if 0
+ /* One day I will have my revenge on cubics I swear */
+ f32 x1 = 0.5f,
+ xMin = 0.0f,
+ xMax = 1.0f,
+ e;
+ for( u32 j=0; j<16; j ++ )
+ {
+ e = x1*x1*x1*a + x1*x1*b + x1*c + d;
+ if( e > 0.0f ) xMax = x1;
+ else xMin = x1;
+ x1 = (xMin+xMax)*0.5f;
+ }
+#endif
+
+ if( samp->curves.semantic == CS_LOCATION+1 )
+ vg_info( "convergence: %.9f\n", e );
+
+ f32 x2 = x1*x1,
+ x3 = x2*x1,
+ Ay = kl->co[1],
+ By = kl->r[1],
+ Cy = kr->l[1],
+ Dy = kr->co[1],
+ y = Dy*x3
+ + Cy*(-3.0f*x3 + 3.0f*x2)
+ + By*( 3.0f*x3 - 6.0f*x2 + 3.0f*x1)
+ + Ay*(-x3 + 3.0f*x2 - 3.0f*x1 + 1.0f );
+#endif
+
+ *samp->curves.target =
+ explicit_bezier( kl->co, kl->r, kr->l, kr->co, t );
+ }
+ else
+ {
+ *samp->curves.target = kl->co[1];
+ }
}
}
+
+ for( u32 i=0; i<af_arrcount( &_cutscene.meta.cameras ); i ++ )
+ {
+ ent_camera *cam = af_arritm( &_cutscene.meta.cameras, i );
+ vg_line_cross( cam->co, VG__RED, 0.2f );
+ }
}
void cutscene_render( world_instance *world, vg_camera *cam )
strips,
tracks,
keyframes,
- curves;
+ curves,
+
+ cameras; /* kinda temp? */
};
struct ms_instance
#pragma once
#define MDL_VERSION_MIN 101
-#define MDL_VERSION_NR 107
+#define MDL_VERSION_NR 108
#include "array_file.h"
{
if( menu.bg_cam )
{
- v3_copy( menu.bg_cam->transform.co, listen_co );
+ v3_copy( menu.bg_cam->co, listen_co );
}
else target = 0;
}
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;
AF_LOAD_ARRAY_STRUCT( af, &world->ent_gate, ent_gate, heap );
AF_LOAD_ARRAY_STRUCT( af, &world->ent_camera, ent_camera, heap );
+
+#if (MDL_VERSION_MIN <= 107)
+ if( meta->version <= 107 )
+ {
+ array_file_ptr legacy_cameras;
+ af_load_array( af, &legacy_cameras, "ent_camera",
+ vg_mem.scratch, sizeof(struct ent_camera_v107) );
+
+ for( u32 i=0; i<af_arrcount(&legacy_cameras); i ++ )
+ {
+ fix_ent_camera_v107( af_arritm( &legacy_cameras, i ),
+ af_arritm( &world->ent_camera, i ) );
+ }
+ }
+#endif
+
AF_LOAD_ARRAY_STRUCT( af, &world->ent_spawn, ent_spawn, heap );
AF_LOAD_ARRAY_STRUCT( af, &world->ent_light, ent_light, heap );
AF_LOAD_ARRAY_STRUCT( af, &world->ent_route_node,ent_route_node, heap );