ahdouaeaiwe
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_export.py
index 05f687a42530d0b9b670fcc6b54621b10f32bcb1..57bf3663268e59886532494bf8ef4caecb59b4da 100644 (file)
@@ -77,7 +77,7 @@ class mdl_header(Structure):
 class classtype_gate(Structure):
    _pack_ = 1
    _fields_ = [("target",c_uint32),
-               ("target1",c_uint32)]
+               ("dims",c_float*3)]
 
 class classtype_block(Structure):
    _pack_ = 1
@@ -96,14 +96,32 @@ class classtype_car_path(Structure):
    _fields_ = [("target",c_uint32),
                ("target1",c_uint32)]
 
+class classtype_instance(Structure):
+   _pack_ = 1
+   _fields_ = [("pstr_file",c_uint32)]
+
+class classtype_capsule(Structure):
+   _pack_ = 1
+   _fields_ = [("height",c_float),
+               ("radius",c_float)]
+
+class classtype_route_node(Structure):
+   _pack_ = 1
+   _fields_ = [("target",c_uint32),
+               ("target1",c_uint32)]
+
+class classtype_route(Structure):
+   _pack_ = 1
+   _fields_ = [("pstr_name",c_uint32),
+               ("id_start",c_uint32),
+               ("colour",c_float*3)]
+
 # Exporter
 # ==============================================================================
 
 def write_model(name):
    print( F"Create mode {name}" )
 
-   collection = bpy.data.collections[name]
-   
    header = mdl_header()
    header.identifier = 0xABCD0000
    header.version = 0
@@ -179,6 +197,8 @@ def write_model(name):
    # Do exporting
    #
    print( "  assigning ids" )
+   collection = bpy.data.collections[name]
+   
    header.node_count = 1
    for obj in collection.all_objects:
       obj.cv_data.uid = header.node_count
@@ -210,11 +230,7 @@ def write_model(name):
       node.offset = entdata_length
       classtype = obj.cv_data.classtype
 
-      if classtype == 'k_classtype_none':
-         node.classtype = 0
-         node.offset = 0
-
-      elif classtype == 'k_classtype_gate':
+      if classtype == 'k_classtype_gate':
          node.classtype = 1
          entdata_length += sizeof( classtype_gate )
 
@@ -223,6 +239,15 @@ def write_model(name):
          if obj.cv_data.target != None:
             gate.target = obj.cv_data.target.cv_data.uid
 
+         if obj.type == 'MESH':
+            gate.dims[0] = obj.data.cv_data.v0[0]
+            gate.dims[1] = obj.data.cv_data.v0[1]
+            gate.dims[2] = obj.data.cv_data.v0[2]
+         else:
+            gate.dims[0] = obj.cv_data.v0[0]
+            gate.dims[1] = obj.cv_data.v0[1]
+            gate.dims[2] = obj.cv_data.v0[2]
+
          entdata_buffer += [gate]
 
       elif classtype == 'k_classtype_block':
@@ -260,6 +285,46 @@ def write_model(name):
             pn.target1 = obj.cv_data.target1.cv_data.uid
 
          entdata_buffer += [pn]
+      elif obj.is_instancer:
+         target = obj.instance_collection
+
+         node.classtype = 6
+         entdata_length += sizeof( classtype_instance )
+
+         inst = classtype_instance()
+         inst.pstr_file = emplace_string( F"models/{target.name}.mdl" )
+         entdata_buffer += [inst]
+      elif classtype == 'k_classtype_capsule':
+         node.classtype = 7
+      elif classtype == 'k_classtype_route_node':
+         node.classtype = 8
+         entdata_length += sizeof( classtype_route_node )
+
+         rn = classtype_route_node()
+         if obj.cv_data.target != None: 
+            rn.target = obj.cv_data.target.cv_data.uid
+         if obj.cv_data.target1 != None: 
+            rn.target1 = obj.cv_data.target1.cv_data.uid
+
+         entdata_buffer += [rn]
+      elif classtype == 'k_classtype_route':
+         node.classtype = 9
+         entdata_length += sizeof( classtype_route )
+         r = classtype_route()
+         r.pstr_name = emplace_string("not-implemented")
+         r.colour[0] = obj.cv_data.colour[0]
+         r.colour[1] = obj.cv_data.colour[1]
+         r.colour[2] = obj.cv_data.colour[2]
+
+         if obj.cv_data.target != None: 
+            r.id_start = obj.cv_data.target.cv_data.uid
+
+         entdata_buffer += [r]
+
+      # classtype == 'k_classtype_none':
+      else:
+         node.classtype = 0
+         node.offset = 0
 
       # Process meshes
       #
@@ -269,8 +334,15 @@ def write_model(name):
       if obj.type == 'MESH':
          default_mat = c_uint32(69)
          default_mat.name = ""
-
-         if obj.data.name in mesh_cache:
+         
+         # Dont use the cache if we have modifiers that affect the normals
+         #
+         use_cache = True
+         for mod in obj.modifiers:
+            if mod.type == 'DATA_TRANSFER':
+               use_cache = False
+
+         if use_cache and obj.data.name in mesh_cache:
             ref = mesh_cache[obj.data.name]
             node.submesh_start = ref.submesh_start
             node.submesh_count = ref.submesh_count
@@ -406,7 +478,7 @@ def write_model(name):
 
    header.file_length = fpos
 
-   fp = open(F"/home/harry/Documents/carve/models/{name}.mdl", "wb")
+   fp = open(F"/home/harry/Documents/carve/models_src/{name}.mdl", "wb")
    fp.write( bytearray( header ) )
    
    for node in node_buffer:
@@ -438,22 +510,48 @@ def cv_draw():
    gpu.state.depth_mask_set(False)
    gpu.state.line_width_set(2.0)
    gpu.state.face_culling_set('BACK')
-   gpu.state.depth_test_set('NONE')
+   gpu.state.depth_test_set('LESS')
    gpu.state.blend_set('NONE')
 
    verts = []
    colours = []
 
-   def drawbezier(p0,h0,p1,h1,c0,c1):
+   #def drawbezier(p0,h0,p1,h1,c0,c1):
+   #   nonlocal verts, colours
+
+   #   verts += [p0]
+   #   verts += [h0]
+   #   colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
+   #   verts += [p1]
+   #   verts += [h1]
+   #   colours += [(1.0,1.0,1,1),(1,1,1,1)]
+   #   
+   #   last = p0
+   #   for i in range(10):
+   #      t = (i+1)/10
+   #      a0 = 1-t
+
+   #      tt = t*t
+   #      ttt = tt*t
+   #      p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0
+   #      verts += [(last[0],last[1],last[2])]
+   #      verts += [(p[0],p[1],p[2])]
+   #      colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
+   #      last = p
+
+   course_count = 0
+
+   def drawbhandle(obj, direction, colour):
       nonlocal verts, colours
-
+      p0 = obj.location
+      h0 = obj.matrix_world @ Vector((0,direction,0))
       verts += [p0]
       verts += [h0]
-      colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)]
-      verts += [p1]
-      verts += [h1]
-      colours += [(1.0,1.0,1,1),(1,1,1,1)]
-      
+      colours += [colour,colour]
+
+   def drawbezier(p0,h0,p1,h1,c0,c1):
+      nonlocal verts, colours
+
       last = p0
       for i in range(10):
          t = (i+1)/10
@@ -467,14 +565,75 @@ def cv_draw():
          colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)]
          last = p
 
+   def drawsbpath(o0,o1,c0,c1,s0,s1):
+      nonlocal course_count
+      
+      offs = ((course_count % 2)*2-1) * course_count * 0.02
+
+      p0 = o0.matrix_world @ Vector((offs,  0,0))
+      h0 = o0.matrix_world @ Vector((offs, s0,0))
+      p1 = o1.matrix_world @ Vector((offs,  0,0))
+      h1 = o1.matrix_world @ Vector((offs,-s1,0))
+      drawbezier(p0,h0,p1,h1,c0,c1)
+
+   def drawbpath(o0,o1,c0,c1):
+      drawsbpath(o0,o1,c0,c1,1.0,1.0)
+
+   def drawbline(p0,p1,c0,c1):
+      nonlocal verts, colours
+      verts += [p0,p1]
+      colours += [c0,c1]
+
    for obj in bpy.context.collection.objects:
+
       if obj.cv_data.classtype == 'k_classtype_gate':
+         if obj.type == 'MESH':
+            dims = obj.data.cv_data.v0
+         else:
+            dims = obj.cv_data.v0
+
+         vs = [None]*9
+         c = Vector((0,0,dims[2]))
+
+         vs[0] = obj.matrix_world @ Vector((-dims[0],0.0,-dims[1]+dims[2]))
+         vs[1] = obj.matrix_world @ Vector((-dims[0],0.0, dims[1]+dims[2]))
+         vs[2] = obj.matrix_world @ Vector(( dims[0],0.0, dims[1]+dims[2]))
+         vs[3] = obj.matrix_world @ Vector(( dims[0],0.0,-dims[1]+dims[2]))
+         vs[4] = obj.matrix_world @ (c+Vector((-1,0,-2)))
+         vs[5] = obj.matrix_world @ (c+Vector((-1,0, 2)))
+         vs[6] = obj.matrix_world @ (c+Vector(( 1,0, 2)))
+         vs[7] = obj.matrix_world @ (c+Vector((-1,0, 0)))
+         vs[8] = obj.matrix_world @ (c+Vector(( 1,0, 0)))
+
+         indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)]
+
+         for l in indices:
+            v0 = vs[l[0]]
+            v1 = vs[l[1]]
+            verts += [(v0[0],v0[1],v0[2])]
+            verts += [(v1[0],v1[1],v1[2])]
+            colours += [(1,1,0,1),(1,1,0,1)]
+
+         sw = (0.4,0.4,0.4,0.2)
          if obj.cv_data.target != None:
-            p0 = obj.location
-            p1 = obj.cv_data.target.location
-            verts += [(p0[0],p0[1],p0[2])]
-            verts += [(p1[0],p1[1],p1[2])]
-            colours += [(0,1,0,1.0),(1,0,0,1.0)]
+            drawbline( obj.location, obj.cv_data.target.location, sw,sw )
+
+      elif obj.cv_data.classtype == 'k_classtype_route_node':
+         sw = Vector((0.4,0.4,0.4,0.2))
+         sw2 = Vector((1.5,0.2,0.2,0.0))
+         if obj.cv_data.target != None:
+            drawbpath( obj, obj.cv_data.target, sw, sw )
+         if obj.cv_data.target1 != None:
+            drawbpath( obj, obj.cv_data.target1, sw, sw )
+
+         drawbhandle( obj,  1.0, (0.8,0.8,0.8,1.0) )
+         drawbhandle( obj, -1.0, (0.4,0.4,0.4,1.0) )
+
+         p1 = obj.location+ \
+               obj.matrix_world.to_quaternion() @ Vector((0,0,-6+1.5))
+         drawbline( obj.location, p1, sw,sw2 )
+
+
       elif obj.cv_data.classtype == 'k_classtype_block':
          a = obj.data.cv_data.v0
          b = obj.data.cv_data.v1
@@ -499,6 +658,31 @@ def cv_draw():
             verts += [(v1[0],v1[1],v1[2])]
             colours += [(1,1,0,1),(1,1,0,1)]
 
+      elif obj.cv_data.classtype == 'k_classtype_capsule':
+         h = obj.data.cv_data.v0[0]
+         r = obj.data.cv_data.v0[1]
+
+         vs = [None]*10
+         vs[0] = obj.matrix_world @ Vector((0.0,0.0, h*0.5  ))
+         vs[1] = obj.matrix_world @ Vector((0.0,0.0,-h*0.5  ))
+         vs[2] = obj.matrix_world @ Vector((  r,0.0, h*0.5-r))
+         vs[3] = obj.matrix_world @ Vector(( -r,0.0, h*0.5-r))
+         vs[4] = obj.matrix_world @ Vector((  r,0.0,-h*0.5+r))
+         vs[5] = obj.matrix_world @ Vector(( -r,0.0,-h*0.5+r))
+         vs[6] = obj.matrix_world @ Vector((0.0, r , h*0.5-r))
+         vs[7] = obj.matrix_world @ Vector((0.0,-r , h*0.5-r))
+         vs[8] = obj.matrix_world @ Vector((0.0, r ,-h*0.5+r))
+         vs[9] = obj.matrix_world @ Vector((0.0,-r ,-h*0.5+r))
+
+         indices = [(0,1),(2,3),(4,5),(6,7),(8,9)]
+
+         for l in indices:
+            v0 = vs[l[0]]
+            v1 = vs[l[1]]
+            verts += [(v0[0],v0[1],v0[2])]
+            verts += [(v1[0],v1[1],v1[2])]
+            colours += [(0.5,1,0,1),(0.5,1,0,1)]
+
       elif obj.cv_data.classtype == 'k_classtype_spawn':
          vs = [None]*4
          vs[0] = obj.matrix_world @ Vector((0,0,0))
@@ -512,31 +696,98 @@ def cv_draw():
             verts += [(v0[0],v0[1],v0[2])]
             verts += [(v1[0],v1[1],v1[2])]
             colours += [(0,1,1,1),(0,1,1,1)]
+      
+      elif obj.cv_data.classtype == 'k_classtype_route':
+         vs = [None]*2
+         vs[0] = obj.location
+         vs[1] = obj.cv_data.target.location
+         indices = [(0,1)]
+         for l in indices:
+            v0 = vs[l[0]]
+            v1 = vs[l[1]]
+            verts += [(v0[0],v0[1],v0[2])]
+            verts += [(v1[0],v1[1],v1[2])]
+            colours += [(0,1,1,1),(0,1,1,1)]
 
-      elif obj.cv_data.classtype == 'k_classtype_car_path':
-         p0 = obj.location
-         h0 = obj.matrix_world @ Vector((1,0,0))
+         stack = [None]*64
+         stack_i = [0]*64
+         stack[0] = obj.cv_data.target
+         si = 1
+         loop_complete = False
+
+         while si > 0:
+            if stack_i[si-1] == 2:
+               si -= 1
+               continue
+
+               if si == 0: # Loop failed to complete
+                  break
+
+            node = stack[si-1]
+
+            targets = [None,None]
+            targets[0] = node.cv_data.target
+
+            if node.cv_data.classtype == 'k_classtype_route_node':
+               targets[1] = node.cv_data.target1
+            
+            nextnode = targets[stack_i[si-1]]
+            stack_i[si-1] += 1
+
+            if nextnode != None: # branch
+               if nextnode == stack[0]: # Loop completed
+                  loop_complete = True
+                  break
+
+               valid=True
+               for sj in range(si):
+                  if stack[sj] == nextnode: # invalidated path
+                     valid=False
+                     break
+
+               if valid:
+                  stack_i[si] = 0
+                  stack[si] = nextnode
+                  si += 1
+                  continue
+
+         if loop_complete:
+            cc = Vector((obj.cv_data.colour[0],\
+                         obj.cv_data.colour[1],\
+                         obj.cv_data.colour[2],\
+                         1.0))
+
+            for sj in range(si):
+               sk = (sj+1)%si
 
-         v0 = obj.matrix_world.to_quaternion() @ Vector((1,0,0))
+               if stack[sj].cv_data.classtype == 'k_classtype_gate' and \
+                  stack[sk].cv_data.classtype == 'k_classtype_gate':
+                  dist = (stack[sj].location-stack[sk].location).magnitude
+                  drawsbpath( stack[sj], stack[sk], cc*0.4, cc, dist, dist )
+
+               else:
+                  drawbpath( stack[sj], stack[sk], cc, cc )
+
+            course_count += 1
+
+      elif obj.cv_data.classtype == 'k_classtype_car_path':
+         v0 = obj.matrix_world.to_quaternion() @ Vector((0,1,0))
          c0 = Vector((v0.x*0.5+0.5, v0.y*0.5+0.5, 0.0, 1.0))
+         drawbhandle( obj, 1.0, (0.9,0.9,0.9,1.0) )
 
          if obj.cv_data.target != None:
-            p1 = obj.cv_data.target.location
-            h1 = obj.cv_data.target.matrix_world @ Vector((-1,0,0))
-
-            v1 = obj.cv_data.target.matrix_world.to_quaternion()@Vector((1,0,0))
+            v1 = obj.cv_data.target.matrix_world.to_quaternion()@Vector((0,1,0))
             c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
 
-            drawbezier( p0, h0, p1, h1, c0, c1 )
+            drawbhandle( obj.cv_data.target, -1.0, (0.5,0.5,0.5,1.0) )
+            drawbpath( obj, obj.cv_data.target, c0, c1 )
 
          if obj.cv_data.target1 != None:
-            p1 = obj.cv_data.target1.location
-            h1 = obj.cv_data.target1.matrix_world @ Vector((-1,0,0))
-
-            v1 = obj.cv_data.target1.matrix_world.to_quaternion()@Vector((1,0,0))
+            v1 = obj.cv_data.target1.matrix_world.to_quaternion()@Vector((0,1,0))
             c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0))
 
-            drawbezier( p0, h0, p1, h1, c0, c1 )
+            drawbhandle( obj.cv_data.target1, -1.0, (0.5,0.5,0.5,1.0) )
+            drawbpath( obj, obj.cv_data.target1, c0, c1 )
 
    lines = batch_for_shader(\
          cv_view_shader, 'LINES', \
@@ -565,6 +816,9 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
    target1: bpy.props.PointerProperty( type=bpy.types.Object, name="target1", \
          poll=cv_poll_target )
 
+   colour: bpy.props.FloatVectorProperty(name="colour",subtype='COLOR',\
+                                         min=0.0,max=1.0)
+
    classtype: bpy.props.EnumProperty(
       name="Format", 
       items = [
@@ -573,7 +827,10 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
       ('k_classtype_block', "k_classtype_block", "", 2),
       ('k_classtype_spawn', "k_classtype_spawn", "", 3),
       ('k_classtype_water', "k_classtype_water", "", 4),
-      ('k_classtype_car_path', "k_classtype_car_path", "", 5)
+      ('k_classtype_car_path', "k_classtype_car_path", "", 5),
+      ('k_classtype_capsule', "k_classtype_capsule", "", 7 ),
+      ('k_classtype_route_node', "k_classtype_route_node", "", 8 ),
+      ('k_classtype_route', "k_classtype_route", "", 9 )
       ])
 
 class CV_OBJ_PANEL(bpy.types.Panel):
@@ -590,9 +847,20 @@ class CV_OBJ_PANEL(bpy.types.Panel):
 
       if active_object.cv_data.classtype == 'k_classtype_gate':
          _.layout.prop( active_object.cv_data, "target" )
-      elif active_object.cv_data.classtype == 'k_classtype_car_path':
+
+         mesh = active_object.data
+         _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
+         _.layout.prop( mesh.cv_data, "v0" )
+
+      elif active_object.cv_data.classtype == 'k_classtype_car_path' or \
+           active_object.cv_data.classtype == 'k_classtype_route_node':
          _.layout.prop( active_object.cv_data, "target" )
          _.layout.prop( active_object.cv_data, "target1" )
+
+      elif active_object.cv_data.classtype == 'k_classtype_route':
+         _.layout.prop( active_object.cv_data, "target" )
+         _.layout.prop( active_object.cv_data, "colour" )
+
       elif active_object.cv_data.classtype == 'k_classtype_block':
          mesh = active_object.data
 
@@ -601,6 +869,10 @@ class CV_OBJ_PANEL(bpy.types.Panel):
          _.layout.prop( mesh.cv_data, "v1" )
          _.layout.prop( mesh.cv_data, "v2" )
          _.layout.prop( mesh.cv_data, "v3" )
+      elif active_object.cv_data.classtype == 'k_classtype_capsule':
+         mesh = active_object.data
+         _.layout.label( text=F"(i) Data is stored in {mesh.name}" )
+         _.layout.prop( mesh.cv_data, "v0" )
 
 class CV_INTERFACE(bpy.types.Panel):
    bl_idname = "VIEW3D_PT_carve"