from ctypes import *
from mathutils import *
from gpu_extras.batch import batch_for_shader
+from bpy_extras import mesh_utils
bl_info = {
"name":"Skaterift .mdl exporter",
"category":"Import/Export",
}
-sr_entity_alias = {
- 'ent_gate': 1,
- 'ent_spawn': 2,
- 'ent_route_node': 3,
- 'ent_route': 4,
- 'ent_water': 5,
- 'ent_volume': 6,
- 'ent_audio': 7,
- 'ent_marker': 8
-}
+sr_entity_list = [
+ ('none', 'None', '', 0 ),
+ ('ent_gate', 'Gate', '', 1 ),
+ ('ent_spawn', 'Spawn Point', '', 2 ),
+ ('ent_route_node', 'Routing Path', '', 3 ),
+ ('ent_route', 'Skate Course', '', 4 ),
+ ('ent_water', 'Water Surface', '', 5 ),
+ ('ent_volume', 'Volume/Trigger', '', 6 ),
+ ('ent_audio', 'Audio', '', 7 ),
+ ('ent_marker', 'Marker', '', 8 ),
+ ('ent_font', 'Font', '', 9 ),
+ ('ent_font_variant', 'Font:Variant', '', 10 ),
+ ('ent_traffic', 'Traffic Model', '', 11 ),
+]
+
+def get_entity_enum_id( alias ):
+#{
+ for et in sr_entity_list:#{
+ if et[0] == alias:#{
+ return et[3]
+ #}
+ #}
+
+ return 0
+#}
class mdl_vert(Structure): # 48 bytes. Quite large. Could compress
#{ # the normals and uvs to i16s. Not an
("vertex_start",c_uint32),
("vertex_count",c_uint32),
("bbx",(c_float*3)*2),
- ("material_id",c_uint32)] # index into the material array
+ ("material_id",c_uint16), # index into the material array
+ ("flags",c_uint16)]
#}
class mdl_material(Structure):
("submesh_start",c_uint32),
("submesh_count",c_uint32),
("pstr_name",c_uint32),
- ("flags",c_uint32),
+ ("entity_id",c_uint32),
("armature_id",c_uint32)]
#}
class mdl_texture(Structure):
#{
_fields_ = [("file",mdl_file),
- ("type",c_uint32)]
+ ("glname",c_uint32)]
#}
class mdl_array(Structure):
("transport",(c_float*3)*4),
("_anonymous_union",version_refcount_union),
("timing_time",c_double),
- ("routes",c_uint16*4)]
+ ("routes",c_uint16*4),
+ ("route_count",c_uint8)]
#}
class ent_route_node(Structure):
("name",c_uint32)]
#}
+class ent_glyph(Structure):
+#{
+ _fields_ = [("size",c_float*2),
+ ("indice_start",c_uint32),
+ ("indice_count",c_uint32)]
+#}
+
+class ent_font_variant(Structure):
+#{
+ _fields_ = [("name",c_uint32),
+ ("material_id",c_uint32)]
+#}
+
+class ent_font(Structure):
+#{
+ _fields_ = [("alias",c_uint32),
+ ("variant_start",c_uint32),
+ ("variant_count",c_uint32),
+ ("glyph_start",c_uint32),
+ ("glyph_count",c_uint32),
+ ("glyph_utf32_base",c_uint32)]
+#}
+
+class ent_traffic(Structure):
+#{
+ _fields_ = [("transform",mdl_transform),
+ ("submesh_start",c_uint32),
+ ("submesh_count",c_uint32),
+ ("start_node",c_uint32),
+ ("node_count",c_uint32),
+ ("speed",c_float),
+ ("t",c_float),
+ ("index",c_uint32)]
+#}
+
def obj_ent_type( obj ):
#{
if obj.type == 'ARMATURE': return 'mdl_armature'
texture_index = (len(sr_compile.texture_data)//sizeof(mdl_texture)) +1
tex = mdl_texture()
- tex.type = 0
+ tex.glname = 0
if sr_compile.pack_textures:#{
filedata = qoi_encode( img )
flags = 0x00
if mat.SR_data.collision:#{
- flags |= 0x2
- if mat.SR_data.skate_surface: flags |= 0x1
- if mat.SR_data.grind_surface: flags |= (0x8|0x1)
+ flags |= 0x2 # collision flag
+ if (mat.SR_data.shader != 'invisible') and \
+ (mat.SR_data.shader != 'boundary'):#{
+ if mat.SR_data.skate_surface: flags |= 0x1
+ if mat.SR_data.grow_grass: flags |= 0x4
+ if mat.SR_data.grind_surface: flags |= 0x8
+ #}
+ if mat.SR_data.shader == 'invisible': flags |= 0x10
+ if mat.SR_data.shader == 'boundary': flags |= (0x10|0x20)
#}
- if mat.SR_data.grow_grass: flags |= 0x4
m.flags = flags
m.surface_prop = int(mat.SR_data.surface_prop)
m.colour1[2] = pow( mat.SR_data.ocean_colour[2], 1.0/2.2 )
m.colour1[3] = 1.0
#}
+
+ if mat.SR_data.shader == 'invisible':#{
+ m.shader = 5
+ #}
+
+ if mat.SR_data.shader == 'boundary':#{
+ m.shader = 6
+ #}
inf = material_info( mat )
yield from _recurse_bone( b )
#}
-def sr_compile_mesh( obj ):
+# Returns submesh_start,count and armature_id
+def sr_compile_mesh_internal( obj ):
#{
- node=mdl_mesh()
- compile_obj_transform(obj, node.transform)
- node.pstr_name = sr_compile_string(obj.name)
- node.flags = 0
-
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 \
#}
if mod.type == 'ARMATURE': #{
- node.flags = 1
armature = mod.object
rig_weight_groups = \
['0 [ROOT]']+[_.name for _ in sr_armature_bones(mod.object)]
- node.armature_id = sr_compile.entity_ids[armature.name]
+ armature_id = sr_compile.entity_ids[armature.name]
POSE_OR_REST_CACHE = armature.data.pose_position
armature.data.pose_position = 'REST'
#
if can_use_cache and (obj.data.name in sr_compile.mesh_cache):#{
ref = sr_compile.mesh_cache[obj.data.name]
- node.submesh_start = ref[0]
- node.submesh_count = ref[1]
- sr_compile.mesh_data.extend(bytearray(node))
- return
+ submesh_start = ref[0]
+ submesh_count = ref[1]
+ return (submesh_start,submesh_count,armature_id)
#}
# Compile a whole new mesh
#
- node.submesh_start = len(sr_compile.submesh_data)//sizeof(mdl_submesh)
- node.submesh_count = 0
+ submesh_start = len(sr_compile.submesh_data)//sizeof(mdl_submesh)
+ submesh_count = 0
dgraph = bpy.context.evaluated_depsgraph_get()
data = obj.evaluated_get(dgraph).data
# Write the vertex / indice data
#
for tri_index, tri in enumerate(data.loop_triangles):#{
- if tri.material_index != material_id:
- continue
+ if tri.material_index != material_id: continue
for j in range(3):#{
vert = data.vertices[tri.vertices[j]]
# Add submesh to encoder
#
sr_compile.submesh_data.extend( bytearray(sm) )
- node.submesh_count += 1
+ submesh_count += 1
#}
if armature:#{
armature.data.pose_position = POSE_OR_REST_CACHE
#}
- # Save a reference to this node since we want to reuse the submesh indices
+ # Save a reference to this mesh since we want to reuse the submesh indices
# later.
- sr_compile.mesh_cache[obj.data.name]=(node.submesh_start,node.submesh_count)
+ 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.mesh_data.extend(bytearray(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 - 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.indice_data)//sizeof(c_uint32)
+ 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.vertex_data)//sizeof(mdl_vert)
+ index = bytearray(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.vertex_data.extend(bytearray(v))
+ #}
+
+ glyph.indice_count += 1
+ sr_compile.indice_data.extend( index )
+ #}
+ #}
+ #}
+ sr_ent_push( glyph )
+ #}
+ sr_ent_push( variant )
+ #}
+ sr_ent_push( font )
+ #}
+#}
+
def sr_compile_armature( obj ):
#{
node = mdl_armature()
mesh_count = 0
for obj in collection.all_objects: #{
- if obj.type == 'MESH': mesh_count += 1
+ if obj.type == 'MESH':#{
+ mesh_count += 1
+ #}
ent_type = obj_ent_type( obj )
if ent_type == 'none': continue
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_font': continue
+ if ent_type == 'ent_font_variant': continue
+ #--------------------------
+
print( F'[SR] {i: 3}/{mesh_count} {obj.name:<40}', end='\r' )
sr_compile_mesh( obj )
#}
#}
elif ent_type == 'ent_gate': #{
gate = ent_gate()
- gate.type = 0
obj_data = obj.SR_data.ent_gate[0]
mesh_data = obj.data.SR_data.ent_gate[0]
- if obj_data.target:#{
- gate.target = sr_compile.entity_ids[obj_data.target.name]
- gate.type = 1
+
+ if obj_data.tipo == 'default':#{
+ if obj_data.target:#{
+ gate.target = sr_compile.entity_ids[obj_data.target.name]
+ gate.type = 1
+ #}
#}
+ elif obj_data.tipo == 'nonlocal':#{
+ gate.target = sr_compile_string(obj_data.key)
+ gate.type = 2
+ #}
+ else: gate.type = 0
+
gate.dimensions[0] = mesh_data.dimensions[0]
gate.dimensions[1] = mesh_data.dimensions[1]
gate.dimensions[2] = mesh_data.dimensions[2]
if obj_data.target:#{
target = obj_data.target
- volume.target.type = sr_entity_alias[obj_ent_type(target)]
+ volume.target.type = get_entity_enum_id( obj_ent_type(target) )
volume.target.index = sr_compile.entity_ids[ target.name ]
#}
#}
#}
+ sr_compile_fonts(collection)
+
def _children( col ):#{
yield col
for c in col.children:#{
route_gates = []
route_curves = []
routes = []
+ traffics = []
for obj in col.objects:#{
if obj.type == 'ARMATURE': pass
#}
elif ent_type == 'ent_route':
routes += [obj]
+ elif ent_type == 'ent_traffic':
+ traffics += [obj]
#}
#}
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]
routenode_count += len(dij.points)
#}
-
print( F"[SR] Writing file" )
file_array_instructions = {}
fp = open( path, "wb" )
header = mdl_header()
- header.version = 40
+ header.version = 100
sr_array_title( header.arrays, \
'index', len(file_array_instructions), \
sizeof(mdl_array), header_size )
active_object = context.active_object
if not active_object: return
+ _.layout.operator( 'skaterift.copy_entity_data', \
+ text=F'Copy entity data to {len(context.selected_objects)-1} '+\
+ F'other objects' )
+
box = _.layout.box()
row = box.row()
row.alignment = 'CENTER'
_.layout.prop( active_mat.SR_data, "collision" )
if active_mat.SR_data.collision:#{
- _.layout.prop( active_mat.SR_data, "skate_surface" )
- _.layout.prop( active_mat.SR_data, "grind_surface" )
- _.layout.prop( active_mat.SR_data, "grow_grass" )
+ box = _.layout.box()
+ row = box.row()
+
+ if (active_mat.SR_data.shader != 'invisible') and \
+ (active_mat.SR_data.shader != 'boundary'):#{
+ row.prop( active_mat.SR_data, "skate_surface" )
+ row.prop( active_mat.SR_data, "grind_surface" )
+ row.prop( active_mat.SR_data, "grow_grass" )
+ #}
#}
if active_mat.SR_data.shader == "terrain_blend":#{
target: bpy.props.PointerProperty( \
type=bpy.types.Object, name="destination", \
poll=lambda self,obj: sr_filter_ent_type(obj,['ent_gate']))
+
+ key: bpy.props.StringProperty()
+ tipo: bpy.props.EnumProperty(items=(('default', 'Default', ""),
+ ('nonlocal', 'Non-Local', ""),))
+
+ @staticmethod
+ def sr_inspector( layout, data ):
+ #{
+ box = layout.box()
+ box.prop( data[0], 'tipo', text="subtype" )
+
+ if data[0].tipo == 'default': box.prop( data[0], 'target' )
+ elif data[0].tipo == 'nonlocal': box.prop( data[0], 'key' )
+ #}
#}
class SR_MESH_ENT_GATE(bpy.types.PropertyGroup):
#}
#}
+def internal_listdel_execute(self,context,ent_name,collection_name):
+#{
+ active_object = context.active_object
+ data = getattr(active_object.SR_data,ent_name)[0]
+ lista = getattr(data,collection_name)
+ index = getattr(data,F'{collection_name}_index')
+
+ lista.remove(index)
+
+ setattr(data,F'{collection_name}_index', min(max(0,index-1), len(lista)-1))
+ return{'FINISHED'}
+#}
+
+def internal_listadd_execute(self,context,ent_name,collection_name):
+#{
+ active_object = context.active_object
+ getattr(getattr(active_object.SR_data,ent_name)[0],collection_name).add()
+ return{'FINISHED'}
+#}
+
+def copy_propgroup( de, to ):
+#{
+ for a in de.__annotations__:#{
+ if isinstance(getattr(de,a), bpy.types.bpy_prop_collection):#{
+ ca = getattr(de,a)
+ cb = getattr(to,a)
+
+ while len(cb) != len(ca):#{
+ if len(cb) < len(ca): cb.add()
+ else: cb.remove(0)
+ #}
+ for i in range(len(ca)):#{
+ copy_propgroup(ca[i],cb[i])
+ #}
+ #}
+ else:#{
+ setattr(to,a,getattr(de,a))
+ #}
+ #}
+#}
+
+class SR_OT_COPY_ENTITY_DATA(bpy.types.Operator):
+#{
+ bl_idname = "skaterift.copy_entity_data"
+ bl_label = "Copy entity data"
+
+ def execute(self, context):#{
+ data = context.active_object.SR_data
+ new_type = data.ent_type
+ print( F"Copy entity data from: {context.active_object.name}" )
+
+ for obj in context.selected_objects:#{
+ if obj != context.active_object:#{
+ print( F" To: {obj.name}" )
+
+ obj.SR_data.ent_type = new_type
+
+ if active_object.type == 'MESH':#{
+ col = getattr( obj.data.SR_data, new_type, None )
+ if col != None and len(col)==0: col.add()
+ mdata = context.active_object.data.SR_data
+ copy_propgroup( getattr(mdata,new_type)[0], col[0] )
+ #}
+
+ col = getattr( obj.SR_data, new_type, None )
+ if col != None and len(col)==0: col.add()
+ copy_propgroup( getattr(data,new_type)[0], col[0] )
+ #}
+ #}
+ return{'FINISHED'}
+ #}
+#}
+
class SR_OT_ROUTE_LIST_NEW_ITEM(bpy.types.Operator):
#{
bl_idname = "skaterift.new_entry"
bl_label = "Add gate"
def execute(self, context):#{
- active_object = context.active_object
- active_object.SR_data.ent_route[0].gates.add()
- return{'FINISHED'}
+ return internal_listadd_execute(self,context,'ent_route','gates')
#}
#}
@classmethod
def poll(cls, context):#{
active_object = context.active_object
- if obj_ent_type(active_object) == 'ent_gate':#{
+ if obj_ent_type(active_object) == 'ent_route':#{
return active_object.SR_data.ent_route[0].gates
#}
else: return False
#}
def execute(self, context):#{
- active_object = context.active_object
- lista = active_object.SR_data.ent_route[0].gates
- index = active_object.SR_data.ent_route[0].gates_index
- lista.remove(index)
- active_object.SR_data.ent_route[0].gates_index = \
- min(max(0, index-1), len(lista) - 1)
- return{'FINISHED'}
+ return internal_listdel_execute(self,context,'ent_route','gates')
#}
#}
bl_label = "Add file"
def execute(self, context):#{
- active_object = context.active_object
- active_object.SR_data.ent_audio[0].files.add()
- return{'FINISHED'}
+ return internal_listadd_execute(self,context,'ent_audio','files')
#}
#}
#}
def execute(self, context):#{
+ return internal_listdel_execute(self,context,'ent_audio','files')
+ return{'FINISHED'}
+ #}
+#}
+
+class SR_OT_GLYPH_LIST_NEW_ITEM(bpy.types.Operator):
+#{
+ bl_idname = "skaterift.gl_new_entry"
+ bl_label = "Add glyph"
+
+ def execute(self, context):#{
+ active_object = context.active_object
+
+ font = active_object.SR_data.ent_font[0]
+ font.glyphs.add()
+
+ if len(font.glyphs) > 1:#{
+ prev = font.glyphs[-2]
+ cur = font.glyphs[-1]
+
+ cur.bounds = prev.bounds
+ cur.utf32 = prev.utf32+1
+ #}
+
+ return{'FINISHED'}
+ #}
+#}
+
+class SR_OT_GLYPH_LIST_DEL_ITEM(bpy.types.Operator):
+#{
+ bl_idname = "skaterift.gl_del_entry"
+ bl_label = "Remove Glyph"
+
+ @classmethod
+ def poll(cls, context):#{
+ active_object = context.active_object
+ if obj_ent_type(active_object) == 'ent_font':#{
+ return active_object.SR_data.ent_font[0].glyphs
+ #}
+ else: return False
+ #}
+
+ def execute(self, context):#{
+ return internal_listdel_execute(self,context,'ent_font','glyphs')
+ #}
+#}
+
+class SR_OT_GLYPH_LIST_MOVE_ITEM(bpy.types.Operator):
+#{
+ bl_idname = "skaterift.gl_move_item"
+ bl_label = "aa"
+ direction: bpy.props.EnumProperty(items=(('UP', 'Up', ""),
+ ('DOWN', 'Down', ""),))
+
+ @classmethod
+ def poll(cls, context):#{
+ active_object = context.active_object
+ if obj_ent_type(active_object) == 'ent_font':#{
+ return active_object.SR_data.ent_font[0].glyphs
+ #}
+ else: return False
+ #}
+
+ def execute(_, context):#{
active_object = context.active_object
- lista = active_object.SR_data.ent_audio[0].files
- index = active_object.SR_data.ent_audio[0].file_index
- lista.remove(index)
- active_object.SR_data.ent_audio[0].file_index = \
- min(max(0, index-1), len(lista) - 1)
+ data = active_object.SR_data.ent_font[0]
+
+ index = data.glyphs_index
+ neighbor = index + (-1 if _.direction == 'UP' else 1)
+ data.glyphs.move( neighbor, index )
+
+ list_length = len(data.glyphs) - 1
+ new_index = index + (-1 if _.direction == 'UP' else 1)
+
+ data.glyphs_index = max(0, min(new_index, list_length))
+
return{'FINISHED'}
#}
#}
+class SR_OT_FONT_VARIANT_LIST_NEW_ITEM(bpy.types.Operator):
+#{
+ bl_idname = "skaterift.fv_new_entry"
+ bl_label = "Add variant"
+
+ def execute(self, context):#{
+ return internal_listadd_execute(self,context,'ent_font','variants')
+ #}
+#}
+
+class SR_OT_FONT_VARIANT_LIST_DEL_ITEM(bpy.types.Operator):
+#{
+ bl_idname = "skaterift.fv_del_entry"
+ bl_label = "Remove variant"
+
+ @classmethod
+ def poll(cls, context):#{
+ active_object = context.active_object
+ if obj_ent_type(active_object) == 'ent_font':#{
+ return active_object.SR_data.ent_font[0].variants
+ #}
+ else: return False
+ #}
+
+ def execute(self, context):#{
+ return internal_listdel_execute(self,context,'ent_font','variants')
+ #}
+#}
+
class SR_OBJECT_ENT_AUDIO_FILE_ENTRY(bpy.types.PropertyGroup):
#{
path: bpy.props.StringProperty( name="Path" )
#}
#}
+class SR_UL_FONT_VARIANT_LIST(bpy.types.UIList):
+#{
+ bl_idname = 'SR_UL_FONT_VARIANT_LIST'
+
+ def draw_item(_,context,layout,data,item,icon,active_data,active_propname):
+ #{
+ layout.prop( item, 'mesh', emboss=False )
+ layout.prop( item, 'tipo' )
+ #}
+#}
+
+class SR_UL_FONT_GLYPH_LIST(bpy.types.UIList):
+#{
+ bl_idname = 'SR_UL_FONT_GLYPH_LIST'
+
+ def draw_item(_,context,layout,data,item,icon,active_data,active_propname):
+ #{
+ s0 = layout.split(factor=0.3)
+ c = s0.column()
+ s1 = c.split(factor=0.3)
+ c = s1.column()
+ row = c.row()
+ lbl = chr(item.utf32) if item.utf32 >= 32 and item.utf32 <= 126 else 'ERR'
+ row.label(text=lbl)
+ c = s1.column()
+ c.prop( item, 'utf32', text='', emboss=True )
+ c = s0.column()
+ row = c.row()
+ row.prop( item, 'bounds', text='', emboss=False )
+ #}
+#}
class SR_OBJECT_ENT_ROUTE(bpy.types.PropertyGroup):
#{
class SR_OBJECT_ENT_AUDIO(bpy.types.PropertyGroup):
#{
files: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_AUDIO_FILE_ENTRY)
- file_index: bpy.props.IntProperty()
+ files_index: bpy.props.IntProperty()
flag_3d: bpy.props.BoolProperty( name="3D audio",default=True )
flag_loop: bpy.props.BoolProperty( name="Loop",default=False )
alias: bpy.props.StringProperty()
#}
+class SR_OBJECT_ENT_GLYPH(bpy.types.PropertyGroup):
+#{
+ mini: bpy.props.FloatVectorProperty(size=2)
+ maxi: bpy.props.FloatVectorProperty(size=2)
+ utf32: bpy.props.IntProperty()
+#}
+
+class SR_OBJECT_ENT_GLYPH_ENTRY(bpy.types.PropertyGroup):
+#{
+ bounds: bpy.props.FloatVectorProperty(size=4,subtype='NONE')
+ utf32: bpy.props.IntProperty()
+#}
+
+class SR_OBJECT_ENT_FONT_VARIANT(bpy.types.PropertyGroup):
+#{
+ mesh: bpy.props.PointerProperty(type=bpy.types.Object)
+ tipo: bpy.props.StringProperty()
+#}
+
+class SR_OBJECT_ENT_FONT(bpy.types.PropertyGroup):
+#{
+ variants: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_FONT_VARIANT)
+ glyphs: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLYPH_ENTRY)
+ alias: bpy.props.StringProperty()
+
+ glyphs_index: bpy.props.IntProperty()
+ variants_index: bpy.props.IntProperty()
+
+ @staticmethod
+ def sr_inspector( layout, data ):
+ #{
+ layout.prop( data[0], 'alias' )
+
+ layout.label( text='Variants' )
+ layout.template_list('SR_UL_FONT_VARIANT_LIST', 'Variants', \
+ data[0], 'variants', data[0], 'variants_index',\
+ rows=5 )
+ row = layout.row()
+ row.operator( 'skaterift.fv_new_entry', text='Add' )
+ row.operator( 'skaterift.fv_del_entry', text='Remove' )
+
+ layout.label( text='ASCII Glyphs' )
+ layout.template_list('SR_UL_FONT_GLYPH_LIST', 'Glyphs', \
+ data[0], 'glyphs', data[0], 'glyphs_index', rows=5)
+
+ row = layout.row()
+ row.operator( 'skaterift.gl_new_entry', text='Add' )
+ row.operator( 'skaterift.gl_del_entry', text='Remove' )
+ row.operator( 'skaterift.gl_move_item', text='^' ).direction='UP'
+ row.operator( 'skaterift.gl_move_item', text='v' ).direction='DOWN'
+ #}
+#}
+
+class SR_OBJECT_ENT_TRAFFIC(bpy.types.PropertyGroup):
+#{
+ speed: bpy.props.FloatProperty(default=1.0)
+#}
+
class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup):
#{
ent_gate: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GATE)
ent_volume: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_VOLUME)
ent_audio: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_AUDIO)
ent_marker: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_MARKER)
-
+ ent_glyph: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GLYPH)
+ ent_font: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_FONT)
+ ent_traffic: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_TRAFFIC)
ent_type: bpy.props.EnumProperty(
name="Type",
- items=[('none', 'None', '', 0),
- ('ent_gate','Gate','', 1),
- ('ent_spawn','Spawn','', 2),
- ('ent_route_node', 'Route Node', '', 3 ),
- ('ent_route', 'Route', '', 4),
- ('ent_water', 'Water Surface', '', 5),
- ('ent_volume', 'Volume', '', 6 ),
- ('ent_audio', 'Audio Files', '', 7),
- ('ent_marker', 'Marker', '', 8)],
+ items=sr_entity_list,
update=sr_on_type_change
)
#}
('standard_cutout', "standard_cutout", ''),
('terrain_blend', "terrain_blend", ''),
('vertex_blend', "vertex_blend", ''),
- ('water',"water",'')
+ ('water',"water",''),
+ ('invisible','Invisible',''),
+ ('boundary','Boundary','')
])
surface_prop: bpy.props.EnumProperty(
collision: bpy.props.BoolProperty( \
name="Collisions Enabled",\
default=True,\
- description = "Can the player collide with this material"\
+ description = "Can the player collide with this material?"\
)
skate_surface: bpy.props.BoolProperty( \
- name="Skate Surface", \
+ name="Skate Target", \
default=True,\
description = "Should the game try to target this surface?" \
)
grind_surface: bpy.props.BoolProperty( \
- name="Grind Surface", \
- default=False,\
- description = "Grind face?" \
+ name="Grindable", \
+ default=True,\
+ description = "Can you grind on this surface?" \
)
grow_grass: bpy.props.BoolProperty( \
name="Grow Grass", \
if obj.SR_data.ent_audio[0].flag_3d:
cv_draw_sphere( obj.location, obj.scale[0], (1,1,0) )
#}
+ elif ent_type == 'ent_font':#{
+ data = obj.SR_data.ent_font[0]
+
+ for i in range(len(data.variants)):#{
+ sub = data.variants[i].mesh
+ if not sub: continue
+
+ for ch in data.glyphs:#{
+ mini = (ch.bounds[0],ch.bounds[1])
+ maxi = (ch.bounds[2]+mini[0],ch.bounds[3]+mini[1])
+ p0 = sub.matrix_world @ Vector((mini[0],0.0,mini[1]))
+ p1 = sub.matrix_world @ Vector((maxi[0],0.0,mini[1]))
+ p2 = sub.matrix_world @ Vector((maxi[0],0.0,maxi[1]))
+ p3 = sub.matrix_world @ Vector((mini[0],0.0,maxi[1]))
+
+ if i == data.variants_index: cc = (0.5,0.5,0.5)
+ else: cc = (0,0,0)
+
+ cv_view_verts += [p0,p1,p1,p2,p2,p3,p3,p0]
+ cv_view_colours += [cc,cc,cc,cc,cc,cc,cc,cc]
+ #}
+ #}
+ #}
#}
#}
SR_OBJECT_ENT_GATE, SR_MESH_ENT_GATE, SR_OBJECT_ENT_SPAWN, \
SR_OBJECT_ENT_ROUTE_ENTRY, SR_UL_ROUTE_NODE_LIST, \
SR_OBJECT_ENT_ROUTE, SR_OT_ROUTE_LIST_NEW_ITEM,\
+ SR_OT_GLYPH_LIST_NEW_ITEM, SR_OT_GLYPH_LIST_DEL_ITEM,\
+ SR_OT_GLYPH_LIST_MOVE_ITEM,\
SR_OT_AUDIO_LIST_NEW_ITEM,SR_OT_AUDIO_LIST_DEL_ITEM,\
- SR_OBJECT_ENT_VOLUME,
+ SR_OT_FONT_VARIANT_LIST_NEW_ITEM,SR_OT_FONT_VARIANT_LIST_DEL_ITEM,\
+ SR_OT_COPY_ENTITY_DATA, \
+ SR_OBJECT_ENT_VOLUME, \
SR_UL_AUDIO_LIST, SR_OBJECT_ENT_AUDIO_FILE_ENTRY,\
SR_OT_ROUTE_LIST_DEL_ITEM,\
- SR_OBJECT_ENT_AUDIO,SR_OBJECT_ENT_MARKER,\
+ SR_OBJECT_ENT_AUDIO,SR_OBJECT_ENT_MARKER,SR_OBJECT_ENT_GLYPH,\
+ SR_OBJECT_ENT_FONT_VARIANT,
+ 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_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES,
SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \