X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=blender_export.py;h=cbd2a1989087302c7844d535e44cd73b5aaf1a3a;hb=49f76c732d2c4ba2f7f772656831f1855521417c;hp=a367cc169a6863b09db6445269a5da7b9e798566;hpb=4b8aac300ee193cfa12011dfe0238cfe7d7ffce7;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/blender_export.py b/blender_export.py index a367cc1..cbd2a19 100644 --- a/blender_export.py +++ b/blender_export.py @@ -23,7 +23,8 @@ sr_entity_alias = { 'ent_route': 4, 'ent_water': 5, 'ent_volume': 6, - 'ent_audio': 7 + 'ent_audio': 7, + 'ent_marker': 8 } class mdl_vert(Structure): # 48 bytes. Quite large. Could compress @@ -282,6 +283,12 @@ class ent_audio(Structure): ("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' @@ -1097,8 +1104,6 @@ def sr_compile( collection ): #} #} - checkpoint_count = 0 - pathindice_count = 0 audio_clip_count = 0 for ent_type, arr in sr_compile.entities.items():#{ @@ -1167,76 +1172,9 @@ 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 ) - 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] - #} - graph = node_graph( route_nodes ) - - 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 = 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 - #} - #} - - sr_ent_push( checkpoint ) - route.checkpoints_count += 1 - checkpoint_count += 1 - #} - - sr_ent_push( route ) - #} - 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 ) - #} elif ent_type == 'ent_water':#{ water = ent_water() compile_obj_transform( obj, water.transform ) @@ -1249,6 +1187,7 @@ def sr_compile( collection ): 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 @@ -1265,6 +1204,15 @@ def sr_compile( collection ): 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() @@ -1295,9 +1243,116 @@ def sr_compile( collection ): 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) + #} #} #} - + + 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] + #} + #} + elif ent_type == 'ent_route': + routes += [obj] + #} + #} + + dij = create_node_graph( route_curves, route_gates ) + + for obj in routes:#{ + obj_data = obj.SR_data.ent_route[0] + route = ent_route() + route.pstr_name = sr_compile_string( obj_data.alias ) + route.checkpoints_start = checkpoint_count + route.checkpoints_count = 0 + + 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 = {} @@ -1599,7 +1654,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 @@ -1892,6 +1947,8 @@ class SR_OBJECT_ENT_AUDIO(bpy.types.PropertyGroup): 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',''), @@ -1928,6 +1985,8 @@ class SR_OBJECT_ENT_AUDIO(bpy.types.PropertyGroup): 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' ) @@ -1956,6 +2015,11 @@ class SR_OBJECT_ENT_AUDIO(bpy.types.PropertyGroup): #} #} +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) @@ -1963,6 +2027,7 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup): 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", @@ -1973,7 +2038,8 @@ class SR_OBJECT_PROPERTIES(bpy.types.PropertyGroup): ('ent_route', 'Route', '', 4), ('ent_water', 'Water Surface', '', 5), ('ent_volume', 'Volume', '', 6 ), - ('ent_audio', 'Audio Files', '', 7)], + ('ent_audio', 'Audio Files', '', 7), + ('ent_marker', 'Marker', '', 8)], update=sr_on_type_change ) #} @@ -2129,8 +2195,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) @@ -2164,8 +2229,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) @@ -2212,8 +2276,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])] @@ -2249,14 +2312,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] @@ -2286,7 +2347,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 ): @@ -2303,7 +2364,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 @@ -2328,8 +2389,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 @@ -2440,7 +2500,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 @@ -2458,7 +2518,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() @@ -2503,7 +2563,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':#{ @@ -2531,7 +2591,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 ) @@ -2580,15 +2640,24 @@ 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 ) #} @@ -2660,53 +2729,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 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 - 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] + dist = round(spline.calc_length(),2) - v0 = ni.location - nj.location + ia = point_count + ib = point_count+l-1 + + graph[ia] = { ib: dist } + graph[ib] = { ia: dist } + + for i in range(len(spline.points)):#{ + wco = curves[c].matrix_world @ spline.points[i].co + route_points.append(Vector((wco[0],wco[1],wco[2]+0.5))) - gate = None + previous = ia+i-1 + proxima = ia+i+1 - if ni.SR_data.ent_type == 'ent_gate': - gate = ni + if i == 0: previous = -1 + if i == len(spline.points)-1: proxima = -1 - if nj.SR_data.ent_type == 'ent_gate':#{ - if gate: continue - gate = nj + 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))) @@ -2716,7 +2880,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 @@ -2732,13 +2895,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:#{ @@ -2765,7 +2932,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:#{ @@ -2778,24 +2946,30 @@ 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':#{ - cv_draw_sphere( obj.location, obj.scale[0], (1,1,0) ) + 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() @@ -2813,7 +2987,7 @@ classes = [ SR_INTERFACE, SR_MATERIAL_PANEL,\ 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_AUDIO,SR_OBJECT_ENT_MARKER,\ \ SR_OBJECT_PROPERTIES, SR_LIGHT_PROPERTIES, SR_BONE_PROPERTIES, SR_MESH_PROPERTIES, SR_MATERIAL_PROPERTIES \