"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
+}
+
class mdl_vert(Structure): # 48 bytes. Quite large. Could compress
#{ # the normals and uvs to i16s. Not an
_pack_ = 1 # real issue, yet.
_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),
("path_count",c_uint16)]
#}
-class ent_route(Structure):
+class ent_route(Structure):
#{
_fields_ = [("transform",mdl_transform),
("pstr_name",c_uint32),
("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)]
+#}
+
def obj_ent_type( obj ):
#{
if obj.type == 'ARMATURE': return 'mdl_armature'
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
#}
#}
#}
_graph_read( from_node_def, from_node, depth+1 )
#}
-
- # No definition! :(
- # TODO: Make a warning for this?
#}
else:#{
if "default" in link_def:#{
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 )
if mat.name in sr_compile.material_cache:
return sr_compile.material_cache[mat.name]
- print( "\n"+ mat.name+"\n" )
-
index = (len(sr_compile.material_data)//sizeof(mdl_material))+1
sr_compile.material_cache[mat.name] = index
#}
#}
- 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 ''}")
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
+ #}
+ audio.probability_curve = int(obj_data.probability_curve)
+
+ for ci in range(audio.clip_count):#{
+ entry = obj_data.files[ci]
+ clip = ent_audio_clip()
+ clip.probability = entry.probability
+ if obj_data.formato == '2':#{
+ sr_pack_file( clip._anon.file, '', vg_str_bin(entry.path) )
+ #}
+ else:#{
+ clip._anon.file.path = sr_compile_string( entry.path )
+ clip._anon.file.pack_offset = 0
+ clip._anon.file.pack_size = 0
+ #}
+ sr_ent_push( clip )
+ #}
+ sr_ent_push( audio )
+ #}
+ elif ent_type == 'ent_volume':#{
+ obj_data = obj.SR_data.ent_volume[0]
+ volume = ent_volume()
+ volume.type = int(obj_data.subtype)
+ compile_obj_transform( obj, volume.transform )
+
+ if obj_data.target:#{
+ target = obj_data.target
+ volume.target.type = sr_entity_alias[obj_ent_type(target)]
+ volume.target.index = sr_compile.entity_ids[ target.name ]
#}
- graph = node_graph( route_nodes )
- for i in range(len(checkpoints)):#{
- gi = checkpoints[i].target
- gj = checkpoints[(i+1)%len(checkpoints)].target
- gate = gi
+ 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:#{
- dest = gi.SR_data.ent_gate[0].target
- gi = dest
- #}
+ def _children( col ):#{
+ yield col
+ for c in col.children:#{
+ yield from _children(c)
+ #}
+ #}
- if gi==gj: continue # error?
- if not gi or not gj: continue
+ checkpoint_count = 0
+ pathindice_count = 0
+ routenode_count = 0
- 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
- #}
+ 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 = {}
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
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,'')]
#{
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']))
#}
class SR_MESH_ENT_GATE(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):
@classmethod
def poll(cls, context):#{
active_object = context.active_object
- if obj_ent_type == 'ent_gate':#{
+ if obj_ent_type(active_object) == 'ent_gate':#{
return active_object.SR_data.ent_route[0].gates
#}
else: return False
#}
#}
+class SR_OT_AUDIO_LIST_NEW_ITEM(bpy.types.Operator):
+#{
+ bl_idname = "skaterift.al_new_entry"
+ bl_label = "Add file"
+
+ def execute(self, context):#{
+ active_object = context.active_object
+ active_object.SR_data.ent_audio[0].files.add()
+ return{'FINISHED'}
+ #}
+#}
+
+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):#{
+ 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)
+ return{'FINISHED'}
+ #}
+#}
+
+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_OBJECT_ENT_ROUTE(bpy.types.PropertyGroup):
#{
gates: bpy.props.CollectionProperty(type=SR_OBJECT_ENT_ROUTE_ENTRY)
#}
#}
+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)
+ file_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_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_type: bpy.props.EnumProperty(
name="Type",
('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)],
update=sr_on_type_change
)
#}
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)
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)
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])]
#
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]
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 ):
cv_view_verts += [p2,p3]
cv_view_colours += [c0,c0]
#}
- cv_draw_lines()
+ #cv_draw_lines()
#}
# Drawhandles of a bezier control point
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
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
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()
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':#{
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 )
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]
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 ] = {}
+
+ 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 = v0.magnitude
+ dist = round(v0.magnitude,2)
- 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
+ if dist < 10.0:#{
+ graph[ gate.name ][ ni ] = dist
+ graph[ ni ][ gate.name ] = dist
+ #}
#}
#}
- return graph
+ return dij_graph(route_points,graph,subsections)
#}
-def cv_draw_route( route, route_nodes ):
+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))
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)))
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
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:#{
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:#{
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) )
+ #}
#}
#}
+
+ 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()
\
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_AUDIO_LIST_NEW_ITEM,SR_OT_AUDIO_LIST_DEL_ITEM,\
+ 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_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES,
SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \