scraped a small amount of crap
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_export.py
index dcab66f51953ffbf08fa2b2d080b4e34328995ab..cbd2a1989087302c7844d535e44cd73b5aaf1a3a 100644 (file)
@@ -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 )
@@ -1305,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 = {}
@@ -1609,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
             
@@ -1970,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)
@@ -1977,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",
@@ -1987,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
    )
 #}
@@ -2143,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)
@@ -2178,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)
@@ -2226,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])]
@@ -2263,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]
@@ -2300,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 ):
@@ -2317,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
@@ -2342,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
 
@@ -2454,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
@@ -2472,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()
@@ -2517,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':#{
@@ -2545,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 )
@@ -2594,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 )
 #}
@@ -2674,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 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]
 
-         dist = v0.magnitude
+         v0 = pi-gate.location
+         if v0.dot(v1) < 0.0: continue
 
-         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
+         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 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)))
@@ -2730,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
@@ -2746,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:#{
@@ -2779,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:#{
@@ -2792,10 +2946,13 @@ 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':#{
@@ -2807,10 +2964,12 @@ def cv_draw():
          #}
       #}
    #}
+
+   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()
@@ -2828,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 \