X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=blender_export.py;h=c8639711a06a352c4a852945ea929c22e693aecd;hb=6b9993651343af73bd48e2213910bbaadb41edaf;hp=3b3f5cd9393f28fc995c555944426b0d2bdb3266;hpb=620beae94b362ae4d4c42e5ba3329a6211d67a8f;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/blender_export.py b/blender_export.py index 3b3f5cd..c863971 100644 --- a/blender_export.py +++ b/blender_export.py @@ -3,6 +3,7 @@ import cProfile 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", @@ -16,6 +17,18 @@ bl_info = { "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, + 'ent_glyph': 9 +} + class mdl_vert(Structure): # 48 bytes. Quite large. Could compress #{ # the normals and uvs to i16s. Not an _pack_ = 1 # real issue, yet. @@ -160,7 +173,8 @@ class ent_gate(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): @@ -175,6 +189,26 @@ class ent_path_index(Structure): _fields_ = [("index",c_uint16)] #} +class vg_audio_clip(Structure): +#{ + _fields_ = [("path",c_uint64), + ("flags",c_uint32), + ("size",c_uint32), + ("data",c_uint64)] +#} + +class union_file_audio_clip(Union): +#{ + _fields_ = [("file",mdl_file), + ("reserved",vg_audio_clip)] +#} + +class ent_audio_clip(Structure): +#{ + _fields_ = [("_anon",union_file_audio_clip), + ("probability",c_float)] +#} + class ent_checkpoint(Structure): #{ _fields_ = [("gate_index",c_uint16), @@ -182,7 +216,7 @@ class ent_checkpoint(Structure): ("path_count",c_uint16)] #} -class ent_route(Structure): +class ent_route(Structure): #{ _fields_ = [("transform",mdl_transform), ("pstr_name",c_uint32), @@ -196,6 +230,91 @@ class ent_route(Structure): ("latest_pass",c_double)] #} +class ent_water(Structure): +#{ + _fields_ = [("transform",mdl_transform), + ("max_dist",c_float), + ("reserved0",c_uint32), + ("reserved1",c_uint32)] +#} + +class volume_trigger(Structure): +#{ + _fields_ = [("event",c_uint32), + ("blank",c_uint32)] +#} + +class volume_particles(Structure): +#{ + _fields_ = [("blank",c_uint32), + ("blank2",c_uint32)] +#} + +class volume_union(Union): +#{ + _fields_ = [("trigger",volume_trigger), + ("particles",volume_particles)] +#} + +class ent_index(Structure): +#{ + _fields_ = [("type",c_uint32), + ("index",c_uint32)] +#} + +class ent_volume(Structure): +#{ + _fields_ = [("transform",mdl_transform), + ("to_world",(c_float*3)*4), + ("to_local",(c_float*3)*4), + ("type",c_uint32), + ("target",ent_index), + ("_anon",volume_union)] +#} + +class ent_audio(Structure): +#{ + _fields_ = [("transform",mdl_transform), + ("flags",c_uint32), + ("clip_start",c_uint32), + ("clip_count",c_uint32), + ("volume",c_float), + ("crossfade",c_float), + ("channel_behaviour",c_uint32), + ("group",c_uint32), + ("probability_curve",c_uint32), + ("max_channels",c_uint32)] +#} + +class ent_marker(Structure): +#{ + _fields_ = [("transform",mdl_transform), + ("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)] +#} + def obj_ent_type( obj ): #{ if obj.type == 'ARMATURE': return 'mdl_armature' @@ -203,14 +322,14 @@ def obj_ent_type( obj ): else: return obj.SR_data.ent_type #} -def sr_filter_ent_type( obj, ent_type ): +def sr_filter_ent_type( obj, ent_types ): #{ if obj == bpy.context.active_object: return False for c0 in obj.users_collection:#{ for c1 in bpy.context.active_object.users_collection:#{ if c0 == c1:#{ - return ent_type == obj_ent_type( obj ) + return obj_ent_type( obj ) in ent_types #} #} #} @@ -366,9 +485,6 @@ def material_info(mat): _graph_read( from_node_def, from_node, depth+1 ) #} - - # No definition! :( - # TODO: Make a warning for this? #} else:#{ if "default" in link_def:#{ @@ -388,6 +504,17 @@ def material_info(mat): return info #} +def vg_str_bin( s ): +#{ + decoded = bytearray() + for i in range(len(s)//2):#{ + c = (ord(s[i*2+0])-0x41) + c |= (ord(s[i*2+1])-0x41)<<4 + decoded.extend(bytearray(c_uint8(c))) #?? + #} + return decoded +#} + def sr_pack_file( file, path, data ): #{ file.path = sr_compile_string( path ) @@ -587,8 +714,7 @@ def sr_compile_mesh( obj ): # 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]] @@ -751,6 +877,136 @@ def sr_compile_mesh( 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() @@ -1003,8 +1259,7 @@ def sr_compile( collection ): #} #} - checkpoint_count = 0 - pathindice_count = 0 + audio_clip_count = 0 for ent_type, arr in sr_compile.entities.items():#{ print(F"[SR] Compiling {len(arr)} {ent_type}{'s' if len(arr)>1 else ''}") @@ -1035,13 +1290,21 @@ def sr_compile( collection ): #} 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] @@ -1072,79 +1335,189 @@ def sr_compile( collection ): spawn = ent_spawn() compile_obj_transform( obj, spawn.transform ) obj_data = obj.SR_data.ent_spawn[0] - spawn.pstr_name = sr_compile_string( obj_data.name ) + spawn.pstr_name = sr_compile_string( obj_data.alias ) sr_ent_push( spawn ) #} - elif ent_type == 'ent_route': #{ - obj_data = obj.SR_data.ent_route[0] - route = ent_route() - route.pstr_name = sr_compile_string( obj_data.alias ) #TODO - route.checkpoints_start = checkpoint_count - route.checkpoints_count = 0 - - for ci in range(3): - route.colour[ci] = obj_data.colour[ci] - route.colour[3] = 1.0 - - compile_obj_transform( obj, route.transform ) - - checkpoints = obj_data.gates - route_nodes = [] - - for uc in obj.users_collection[0].objects:#{ - uc_type = obj_ent_type( uc ) - if uc_type == 'ent_gate' or uc_type == 'ent_route_node': - route_nodes += [uc] + 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 #} - graph = node_graph( route_nodes ) + 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 ) - for i in range(len(checkpoints)):#{ - gi = checkpoints[i].target - gj = checkpoints[(i+1)%len(checkpoints)].target - gate = gi + if obj_data.target:#{ + target = obj_data.target + volume.target.type = sr_entity_alias[obj_ent_type(target)] + volume.target.index = sr_compile.entity_ids[ target.name ] + #} - if gi:#{ - dest = gi.SR_data.ent_gate[0].target - gi = dest - #} + 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) + #} + #} + #} - if gi==gj: continue # error? - if not gi or not gj: continue + sr_compile_fonts(collection) - checkpoint = ent_checkpoint() - checkpoint.gate_index = sr_compile.entity_ids[gate.name] - checkpoint.path_start = pathindice_count - checkpoint.path_count = 0 - - path = dijkstra( graph, gj.name, gi.name ) - if path:#{ - for pi in range(1,len(path)-1):#{ - pathindice = ent_path_index() - pathindice.index = sr_compile.entity_ids[path[pi]] - sr_ent_push( pathindice ) - - checkpoint.path_count += 1 - pathindice_count += 1 - #} + 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 = [] + + 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] #} - - sr_ent_push( checkpoint ) - route.checkpoints_count += 1 - checkpoint_count += 1 #} - - sr_ent_push( route ) + elif ent_type == 'ent_route': + routes += [obj] #} - elif ent_type == 'ent_route_node':#{ - rn = ent_route_node() - rn.co[0] = obj.location[0] - rn.co[1] = obj.location[2] - rn.co[2] = -obj.location[1] - sr_ent_push( rn ) + #} + + 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 + + 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 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) #} - + + print( F"[SR] Writing file" ) file_array_instructions = {} @@ -1401,6 +1774,10 @@ class SR_INTERFACE(bpy.types.Panel): 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' @@ -1446,7 +1823,7 @@ class SR_INTERFACE(bpy.types.Panel): elif active_object.type == 'LIGHT': #{ _draw_prop_collection( [active_object.data.SR_data] ) #} - elif active_object.type == 'EMPTY' or active_object.type == 'MESH': #{ + elif active_object.type in ['EMPTY','CURVE','MESH']:#{ box.prop( active_object.SR_data, "ent_type" ) ent_type = active_object.SR_data.ent_type @@ -1516,7 +1893,7 @@ class SR_MATERIAL_PANEL(bpy.types.Panel): def sr_get_type_enum( scene, context ): #{ items = [('none','None',"")] - mesh_entities=['ent_gate'] + mesh_entities=['ent_gate','ent_water'] point_entities=['ent_spawn','ent_route_node','ent_route'] for e in point_entities: items += [(e,e,'')] @@ -1556,7 +1933,21 @@ class SR_OBJECT_ENT_GATE(bpy.types.PropertyGroup): #{ target: bpy.props.PointerProperty( \ type=bpy.types.Object, name="destination", \ - poll=lambda self,obj: sr_filter_ent_type(obj,'ent_gate')) + 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): @@ -1568,7 +1959,7 @@ class SR_OBJECT_ENT_ROUTE_ENTRY(bpy.types.PropertyGroup): #{ target: bpy.props.PointerProperty( \ type=bpy.types.Object, name='target', \ - poll=lambda self,obj: sr_filter_ent_type(obj,'ent_gate')) + poll=lambda self,obj: sr_filter_ent_type(obj,['ent_gate'])) #} class SR_UL_ROUTE_NODE_LIST(bpy.types.UIList): @@ -1581,15 +1972,86 @@ class SR_UL_ROUTE_NODE_LIST(bpy.types.UIList): #} #} +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') #} #} @@ -1601,23 +2063,203 @@ class SR_OT_ROUTE_LIST_DEL_ITEM(bpy.types.Operator): @classmethod def poll(cls, context):#{ active_object = context.active_object - if obj_ent_type == '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):#{ + return internal_listdel_execute(self,context,'ent_route','gates') + #} +#} + +class SR_OT_AUDIO_LIST_NEW_ITEM(bpy.types.Operator): +#{ + bl_idname = "skaterift.al_new_entry" + bl_label = "Add file" + + def execute(self, context):#{ + return internal_listadd_execute(self,context,'ent_audio','files') + #} +#} + +class SR_OT_AUDIO_LIST_DEL_ITEM(bpy.types.Operator): +#{ + bl_idname = "skaterift.al_del_entry" + bl_label = "Remove file" + + @classmethod + def poll(cls, context):#{ + active_object = context.active_object + if obj_ent_type(active_object) == 'ent_audio':#{ + return active_object.SR_data.ent_audio[0].files + #} + else: return False + #} + + 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_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) + 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" ) + probability: bpy.props.FloatProperty( name="Probability",default=100.0 ) +#} + +class SR_UL_AUDIO_LIST(bpy.types.UIList): +#{ + bl_idname = 'SR_UL_AUDIO_LIST' + + def draw_item(_,context,layout,data,item,icon,active_data,active_propname): + #{ + split = layout.split(factor=0.7) + c = split.column() + c.prop( item, 'path', text='', emboss=False ) + c = split.column() + c.prop( item, 'probability', text='%', emboss=True ) + #} +#} + +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): #{ gates: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_ROUTE_ENTRY) @@ -1651,19 +2293,185 @@ class SR_OBJECT_ENT_ROUTE(bpy.types.PropertyGroup): #} #} +class SR_OBJECT_ENT_VOLUME(bpy.types.PropertyGroup): +#{ + subtype: bpy.props.EnumProperty( + name="Subtype", + items=[('0','Trigger',''), + ('1','Particles (0.1s)','')] + ) + + target: bpy.props.PointerProperty( \ + type=bpy.types.Object, name="Target", \ + poll=lambda self,obj: sr_filter_ent_type(obj,['ent_audio'])) + + @staticmethod + def sr_inspector( layout, data ): + #{ + data = data[0] + layout.prop( data, 'subtype' ) + layout.prop( data, 'target' ) + #} +#} + +class SR_OBJECT_ENT_AUDIO(bpy.types.PropertyGroup): +#{ + files: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_AUDIO_FILE_ENTRY) + files_index: bpy.props.IntProperty() + + flag_3d: bpy.props.BoolProperty( name="3D audio",default=True ) + flag_loop: bpy.props.BoolProperty( name="Loop",default=False ) + flag_auto: bpy.props.BoolProperty( name="Play at start",default=False ) + flag_nodoppler: bpy.props.BoolProperty( name="No Doppler",default=False ) + + group: bpy.props.IntProperty( name="Group ID", default=0 ) + formato: bpy.props.EnumProperty( + name="Format", + items=[('0','Uncompressed Mono',''), + ('1','Compressed Vorbis',''), + ('2','[vg] Bird Synthesis','')] + ) + probability_curve: bpy.props.EnumProperty( + name="Probability Curve", + items=[('0','Constant',''), + ('1','Wildlife Daytime',''), + ('2','Wildlife Nighttime','')]) + channel_behaviour: bpy.props.EnumProperty( + name="Channel Behaviour", + items=[('0','Unlimited',''), + ('1','Discard if group full', ''), + ('2','Crossfade if group full','')]) + + transition_duration: bpy.props.FloatProperty(name="Transition Time",\ + default=0.2) + + max_channels: bpy.props.IntProperty( name="Max Channels", default=1 ) + volume: bpy.props.FloatProperty( name="Volume",default=1.0 ) + + @staticmethod + def sr_inspector( layout, data ): + #{ + layout.prop( data[0], 'formato' ) + layout.prop( data[0], 'volume' ) + + box = layout.box() + box.label( text='Channels' ) + split = box.split(factor=0.3) + c = split.column() + c.prop( data[0], 'max_channels' ) + c = split.column() + c.prop( data[0], 'channel_behaviour', text='Behaviour' ) + if data[0].channel_behaviour >= '1': + box.prop( data[0], 'group' ) + if data[0].channel_behaviour == '2': + box.prop( data[0], 'transition_duration' ) + + box = layout.box() + box.label( text='Flags' ) + box.prop( data[0], 'flag_3d' ) + if data[0].flag_3d: box.prop( data[0], 'flag_nodoppler' ) + + box.prop( data[0], 'flag_loop' ) + box.prop( data[0], 'flag_auto' ) + + split = layout.split(factor=0.7) + c = split.column() + c.label( text='Filepath' ) + c = split.column() + c.label( text='Chance (0.1s)' ) + + layout.prop( data[0], 'probability_curve' ) + + layout.template_list('SR_UL_AUDIO_LIST', 'Files', \ + data[0], 'files', data[0], 'file_index', rows=5) + + row = layout.row() + row.operator( 'skaterift.al_new_entry', text='Add' ) + row.operator( 'skaterift.al_del_entry', text='Remove' ) + #} +#} + +class SR_OBJECT_ENT_MARKER(bpy.types.PropertyGroup): +#{ + 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_PROPERTIES(bpy.types.PropertyGroup): #{ ent_gate: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_GATE) ent_spawn: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_SPAWN) ent_route: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_ROUTE) - + 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_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_route', 'Route', '', 4), + ('ent_water', 'Water Surface', '', 5), + ('ent_volume', 'Volume', '', 6 ), + ('ent_audio', 'Audio Files', '', 7), + ('ent_marker', 'Marker', '', 8), + ('ent_font', 'Font', '', 9), + ('ent_font_variant','Font variant','',10)], update=sr_on_type_change ) #} @@ -1819,8 +2627,7 @@ def cv_draw_sphere( pos, radius, colour ): pi = 3.14159265358979323846264 - for i in range(16): - #{ + for i in range(16):#{ t = ((i+1.0) * 1.0/16.0) * pi * 2.0 s = math.sin(t) c = math.cos(t) @@ -1854,8 +2661,7 @@ def cv_draw_halfsphere( pos, tx, ty, tz, radius, colour ): pi = 3.14159265358979323846264 - for i in range(16): - #{ + for i in range(16):#{ t = ((i+1.0) * 1.0/16.0) * pi s = math.sin(t) c = math.cos(t) @@ -1902,8 +2708,7 @@ def cv_draw_ucube( transform, colour, s=Vector((1,1,1)), o=Vector((0,0,0)) ): 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: - #{ + for l in indices:#{ v0 = vs[l[0]] v1 = vs[l[1]] cv_view_verts += [(v0[0],v0[1],v0[2])] @@ -1939,14 +2744,12 @@ def cv_draw_line2( p0, p1, c0, c1 ): # def cv_tangent_basis( n, tx, ty ): #{ - if abs( n[0] ) >= 0.57735027: - #{ + if abs( n[0] ) >= 0.57735027:#{ tx[0] = n[1] tx[1] = -n[0] tx[2] = 0.0 #} - else: - #{ + else:#{ tx[0] = 0.0 tx[1] = n[2] tx[2] = -n[1] @@ -1976,7 +2779,7 @@ def cv_draw_arrow( p0, p1, c0, size=0.15 ): cv_view_verts += [p0,p1, midpt+(tx-n)*size,midpt, midpt+(-tx-n)*size,midpt ] cv_view_colours += [c0,c0,c0,c0,c0,c0] - cv_draw_lines() + #cv_draw_lines() #} def cv_draw_line_dotted( p0, p1, c0, dots=10 ): @@ -1993,7 +2796,7 @@ def cv_draw_line_dotted( p0, p1, c0, dots=10 ): cv_view_verts += [p2,p3] cv_view_colours += [c0,c0] #} - cv_draw_lines() + #cv_draw_lines() #} # Drawhandles of a bezier control point @@ -2018,8 +2821,7 @@ def cv_draw_bezier( p0,h0,p1,h1,c0,c1 ): global cv_view_verts, cv_view_colours last = p0 - for i in range(10): - #{ + for i in range(10):#{ t = (i+1)/10 a0 = 1-t @@ -2130,7 +2932,7 @@ def draw_cone_twist( center, vx, vy, va ): size = 0.12 cv_view_verts += [center, center+va*size] - cv_view_colours += [ (1,1,1,1), (1,1,1,1) ] + cv_view_colours += [ (1,1,1), (1,1,1) ] for x in range(32):#{ t0 = (x/32) * math.tau @@ -2148,7 +2950,7 @@ def draw_cone_twist( center, vx, vy, va ): col1 = ( abs(c1), abs(s1), 0.0, 1.0 ) cv_view_verts += [center, p0, p0, p1] - cv_view_colours += [ (0,0,0,0), col0, col0, col1 ] + cv_view_colours += [ (0,0,0), col0, col0, col1 ] #} cv_draw_lines() @@ -2193,7 +2995,7 @@ def draw_skeleton_helpers( obj ): cv_view_verts += [(v0[0],v0[1],v0[2])] cv_view_verts += [(v1[0],v1[1],v1[2])] - cv_view_colours += [(0.5,0.5,0.5,0.5),(0.5,0.5,0.5,0.5)] + cv_view_colours += [(0.5,0.5,0.5),(0.5,0.5,0.5)] #} #} elif bone.SR_data.collider == '2':#{ @@ -2221,7 +3023,7 @@ def draw_skeleton_helpers( obj ): p0 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l*-0.5 ) p1 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l* 0.5 ) - colour = [0.2,0.2,0.2,1.0] + colour = [0.2,0.2,0.2] colour[major_axis] = 0.5 cv_draw_halfsphere( p0, -v1, ty, tx, r, colour ) @@ -2270,19 +3072,50 @@ def cv_ent_gate( obj ): indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)] + r3d = bpy.context.area.spaces.active.region_3d + + p0 = r3d.view_matrix.inverted().translation + v0 = (obj.matrix_world@Vector((0,0,0))) - p0 + v1 = obj.matrix_world.to_3x3() @ Vector((0,1,0)) + + if v0.dot(v1) > 0.0: cc = (0,1,0) + else: cc = (1,0,0) + 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 += [(1,1,0,1),(1,1,0,1)] + cv_view_colours += [cc,cc] #} - sw = (0.4,0.4,0.4,0.2) + sw = (0.4,0.4,0.4) if data.target != None: cv_draw_arrow( obj.location, data.target.location, sw ) #} +def cv_ent_volume( obj ): +#{ + global cv_view_verts, cv_view_colours + + data = obj.SR_data.ent_volume[0] + + if data.subtype == '0':#{ + cv_draw_ucube( obj.matrix_world, (0,1,0) ) + + if data.target:#{ + cv_draw_line( obj.location, data.target.location, (0,1,0) ) + #} + #} + elif data.subtype == '1':#{ + cv_draw_ucube( obj.matrix_world, (1,1,0) ) + + if data.target:#{ + cv_draw_line( obj.location, data.target.location, (1,1,0) ) + #} + #} +#} + def dijkstra( graph, start_node, target_node ): #{ unvisited = [_ for _ in graph] @@ -2328,53 +3161,148 @@ def dijkstra( graph, start_node, target_node ): return path #} -def node_graph( route_nodes ): +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 = {} - for n in route_nodes: - graph[n.name] = {} + route_points = [] + subsections = [] + point_count = 0 + spline_count = 0 - for i in range(len(route_nodes)-1):#{ - for j in range(i+1, len(route_nodes)):#{ - ni = route_nodes[i] - nj = route_nodes[j] + 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 - v0 = ni.location - nj.location + dist = round(spline.calc_length(),2) - gate = None + ia = point_count + ib = point_count+l-1 - if ni.SR_data.ent_type == 'ent_gate': - gate = ni + 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 nj.SR_data.ent_type == 'ent_gate':#{ - if gate: continue - gate = nj + if i == 0: previous = -1 + if i == len(spline.points)-1: proxima = -1 + + subsections.append((spline_count,previous,proxima)) + point_count += 1 #} - if gate:#{ - v1 = gate.matrix_world.to_3x3() @ Vector((0,-1,0)) - if gate.SR_data.ent_gate[0].target: - if v1.dot(v0) > 0.0: continue - else: - if v1.dot(v0) < 0.0: continue + 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 ] = {} - dist = v0.magnitude + for i in range(len(graph_keys)):#{ + ni = graph_keys[i] + pi = route_points[ni] - if dist > 25.0: continue - graph[route_nodes[i].name][route_nodes[j].name] = dist - graph[route_nodes[j].name][route_nodes[i].name] = dist + 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 graph + 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, route_nodes ): +def cv_draw_route( route, dij ): #{ pole = Vector((0.2,0.2,10)) hat = Vector((1,8,0.2)) - cc = route.SR_data.ent_route[0].colour + cc = (route.SR_data.ent_route[0].colour[0], + route.SR_data.ent_route[0].colour[1], + route.SR_data.ent_route[0].colour[2]) cv_draw_ucube(route.matrix_world,cc,Vector((0.5,-7.5,6)),\ Vector((0,-6.5,5.5))) @@ -2384,7 +3312,6 @@ def cv_draw_route( route, route_nodes ): cv_draw_ucube(route.matrix_world,cc,hat, Vector((-0.5,-6.5,-1)) ) checkpoints = route.SR_data.ent_route[0].gates - graph = node_graph( route_nodes ) for i in range(len(checkpoints)):#{ gi = checkpoints[i].target @@ -2400,13 +3327,17 @@ def cv_draw_route( route, route_nodes ): if gi==gj: continue # error? if not gi or not gj: continue - path = dijkstra( graph, gj.name, gi.name ) + path = solve_graph( dij, gi.name, gj.name ) if path:#{ - for sj in range(len(path)-1):#{ - o0 = bpy.data.objects[ path[sj] ] - o1 = bpy.data.objects[ path[sj+1] ] - cv_draw_arrow(o0.location,o1.location,cc,1.5) + cv_draw_arrow(gi.location,dij.points[path[0]],cc,1.5) + cv_draw_arrow(dij.points[path[len(path)-1]],gj.location,cc,1.5) + for j in range(len(path)-1):#{ + i0 = path[j] + i1 = path[j+1] + o0 = dij.points[ i0 ] + o1 = dij.points[ i1 ] + cv_draw_arrow(o0,o1,cc,1.5) #} #} else:#{ @@ -2433,7 +3364,8 @@ def cv_draw(): gpu.state.depth_test_set('LESS') gpu.state.blend_set('NONE') - route_nodes = [] + route_gates = [] + route_curves = [] routes = [] for obj in bpy.context.collection.objects:#{ @@ -2446,18 +3378,53 @@ def cv_draw(): if ent_type == 'ent_gate':#{ cv_ent_gate( obj ) - route_nodes += [obj] + route_gates += [obj] + #} + elif ent_type == 'ent_route_node':#{ + if obj.type == 'CURVE':#{ + route_curves += [obj] + #} #} - elif ent_type == 'ent_route_node': - route_nodes += [obj] elif ent_type == 'ent_route': routes += [obj] + elif ent_type == 'ent_volume':#{ + cv_ent_volume( obj ) + #} + elif ent_type == 'ent_audio':#{ + 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] + #} + #} + #} #} #} + + dij = create_node_graph( route_curves, route_gates ) #cv_draw_route_map( route_nodes ) for route in routes:#{ - cv_draw_route( route, route_nodes ) + cv_draw_route( route, dij ) #} cv_draw_lines() @@ -2470,8 +3437,20 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\ \ 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_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_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_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_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \