angle limit constraint
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_export.py
index 3f2dd793d04ff12d7f8e9091a0189a9d3530cb79..04fd6c344b69245f172dc139801d24196f5c1b04 100644 (file)
@@ -144,18 +144,19 @@ class classtype_skeleton(Structure):
    _pack_ = 1
    _fields_ = [("channels",c_uint32),
                ("ik_count",c_uint32),
+               ("collider_count",c_uint32),
                ("anim_start",c_uint32),
                ("anim_count",c_uint32)]
 
 class classtype_bone(Structure):
-   _pack_ = 1
-   _fields_ = [("deform",c_uint32)]
-
-class classtype_ik_bone(Structure):
    _pack_ = 1
    _fields_ = [("deform",c_uint32),
-               ("target",c_uint32),
-               ("pole",c_uint32)]
+               ("ik_target",c_uint32),
+               ("ik_pole",c_uint32),
+               ("collider",c_uint32),
+               ("use_limits",c_uint32),
+               ("angle_limits",(c_float*3)*2),
+               ("hitbox",(c_float*3)*2)]
 
 # Exporter
 # ==============================================================================
@@ -281,6 +282,7 @@ def write_model(collection_name):
             if n.type == 'ARMATURE':
                tree["bones"] = [None] # None is the root transform
                tree["ik_count"] = 0
+               tree["collider_count"] = 0
 
                def _extendb( p, n, d ):
                   nonlocal tree
@@ -302,6 +304,9 @@ def write_model(collection_name):
                         btree["pole"] = c.pole_subtarget
                         tree["ik_count"] += 1
 
+                  if n.cv_data.collider:
+                     tree['collider_count'] += 1
+
                   btree['deform'] = n.use_deform
                   p['children'] += [btree]
 
@@ -371,10 +376,7 @@ def write_model(collection_name):
          node.parent = node_def["parent"]["uid"]
 
       if objt == 'BONE':
-         if 'target' in node_def:
-            classtype = 'k_classtype_ik_bone'
-         else:
-            classtype = 'k_classtype_bone'
+         classtype = 'k_classtype_bone'
       elif objt == 'ARMATURE':
          classtype = 'k_classtype_skeleton'
       else:
@@ -399,6 +401,8 @@ def write_model(collection_name):
             if mod.type == 'ARMATURE':
                classtype = 'k_classtype_skin'
                armature_def = graph_lookup[mod.object]
+               POSE_OR_REST_CACHE = armature_def['obj'].data.pose_position
+
                armature_def['obj'].data.pose_position = 'REST'
 
          if can_use_cache and obj.data.name in mesh_cache:
@@ -580,7 +584,7 @@ def write_model(collection_name):
       s005 = ""
 
       if classtype == 'k_classtype_skin':
-         armature_def['obj'].data.pose_position = 'POSE'
+         armature_def['obj'].data.pose_position = POSE_OR_REST_CACHE
          s005 = F" [armature -> {armature_def['obj'].cv_data.uid}]"
 
       scmp = F"{s002:<32} {s003:<22} {s004} {s005}"
@@ -614,6 +618,7 @@ def write_model(collection_name):
          bones = armature_def['bones']
          skeleton.channels = len(bones)
          skeleton.ik_count = armature_def["ik_count"]
+         skeleton.collider_count = armature_def["collider_count"]
          
          if armature.animation_data:
             previous_frame = bpy.context.scene.frame_current
@@ -698,18 +703,49 @@ def write_model(collection_name):
          
          bone = classtype_bone()
          bone.deform = node_def['deform']
-         entdata_buffer += [bone]
-
-      elif classtype == 'k_classtype_ik_bone':
-         node.classtype = 13
-         entdata_length += sizeof( classtype_ik_bone )
          
-         ikbone = classtype_ik_bone()
-         ikbone.target = armature_def['bones'].index( node_def['target'] )
-         ikbone.pole   = armature_def['bones'].index( node_def['pole'] )
-         ikbone.deform = node_def['deform']
+         if 'target' in node_def:
+            bone.ik_target = armature_def['bones'].index( node_def['target'] )
+            bone.ik_pole   = armature_def['bones'].index( node_def['pole'] )
+         else:
+            bone.ik_target = 0
+            bone.ik_pole = 0
+
+         bone.collider = 1 if obj.cv_data.collider else 0
+         if obj.cv_data.collider:
+            bone.hitbox[0][0] =  obj.cv_data.v0[0]
+            bone.hitbox[0][1] =  obj.cv_data.v0[2]
+            bone.hitbox[0][2] = -obj.cv_data.v1[1]
+            bone.hitbox[1][0] =  obj.cv_data.v1[0]
+            bone.hitbox[1][1] =  obj.cv_data.v1[2]
+            bone.hitbox[1][2] = -obj.cv_data.v0[1]
+         else:
+            bone.hitbox[0][0] = 0.0
+            bone.hitbox[0][1] = 0.0
+            bone.hitbox[0][2] = 0.0
+            bone.hitbox[1][0] = 0.0
+            bone.hitbox[1][1] = 0.0
+            bone.hitbox[1][2] = 0.0
+
+         if obj.cv_data.con0:
+            bone.use_limits = 1 
+            bone.angle_limits[0][0] =  obj.cv_data.mins[0]
+            bone.angle_limits[0][1] =  obj.cv_data.mins[2]
+            bone.angle_limits[0][2] = -obj.cv_data.maxs[1]
+            bone.angle_limits[1][0] =  obj.cv_data.maxs[0]
+            bone.angle_limits[1][1] =  obj.cv_data.maxs[2]
+            bone.angle_limits[1][2] = -obj.cv_data.mins[1]
+         else:
+            bone.use_limits = 0
+            bone.angle_limits[0][0] = 0.0
+            bone.angle_limits[0][1] = 0.0
+            bone.angle_limits[0][2] = 0.0
+            bone.angle_limits[1][0] = 0.0
+            bone.angle_limits[1][1] = 0.0
+            bone.angle_limits[1][2] = 0.0
 
-         entdata_buffer += [ikbone]
+         bone.deform = node_def['deform']
+         entdata_buffer += [bone]
 
       elif classtype == 'k_classtype_gate':
          node.classtype = 1
@@ -973,6 +1009,76 @@ def cv_draw():
       colours += [c0,c1]
 
    for obj in bpy.context.collection.objects:
+      if obj.type == 'ARMATURE':
+         for bone in obj.data.bones:
+            if bone.cv_data.collider:
+               c = bone.head_local
+               a = bone.cv_data.v0
+               b = bone.cv_data.v1
+               
+               vs = [None]*8
+               vs[0]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+a[2]))
+               vs[1]=obj.matrix_world@Vector((c[0]+a[0],c[1]+b[1],c[2]+a[2]))
+               vs[2]=obj.matrix_world@Vector((c[0]+b[0],c[1]+b[1],c[2]+a[2]))
+               vs[3]=obj.matrix_world@Vector((c[0]+b[0],c[1]+a[1],c[2]+a[2]))
+               vs[4]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+b[2]))
+               vs[5]=obj.matrix_world@Vector((c[0]+a[0],c[1]+b[1],c[2]+b[2]))
+               vs[6]=obj.matrix_world@Vector((c[0]+b[0],c[1]+b[1],c[2]+b[2]))
+               vs[7]=obj.matrix_world@Vector((c[0]+b[0],c[1]+a[1],c[2]+b[2]))
+
+               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:
+                  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,0.5,0.5,0.5),(0.5,0.5,0.5,0.5)]
+
+               center=obj.matrix_world@c
+               
+               def _angle_lim( major, minor, amin, amax, colour ):
+                  nonlocal verts, colours
+                  f = 0.05
+                  ay = major*f
+                  ax = minor*f
+
+                  for x in range(16):
+                     t0 = x/16
+                     t1 = (x+1)/16
+                     a0 = amin*(1.0-t0)+amax*t0
+                     a1 = amin*(1.0-t1)+amax*t1
+
+                     p0 = c + major*f*math.cos(a0) + minor*f*math.sin(a0)
+                     p1 = c + major*f*math.cos(a1) + minor*f*math.sin(a1)
+
+                     p0=obj.matrix_world @ p0
+                     p1=obj.matrix_world @ p1
+                     verts += [p0,p1]
+                     colours += [colour,colour]
+
+                     if x == 0:
+                        verts += [p0,c]
+                        colours += [colour,colour]
+                     if x == 15:
+                        verts += [p1,c]
+                        colours += [colour,colour]
+
+                  verts += [c+major*1.2*f,c+major*f*0.8]
+                  colours += [colour,colour]
+                  
+               if bone.cv_data.con0:
+                  _angle_lim( Vector((0,1,0)),Vector((0,0,1)), \
+                              bone.cv_data.mins[0], bone.cv_data.maxs[0], \
+                              (1,0,0,1))
+                  _angle_lim( Vector((0,0,1)),Vector((1,0,0)), \
+                              bone.cv_data.mins[1], bone.cv_data.maxs[1], \
+                              (0,1,0,1))
+                  _angle_lim( Vector((1,0,0)),Vector((0,1,0)), \
+                              bone.cv_data.mins[2], bone.cv_data.maxs[2], \
+                              (0,0,1,1))
+               
 
       if obj.cv_data.classtype == 'k_classtype_gate':
          if obj.type == 'MESH':
@@ -1225,6 +1331,52 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
       ('k_classtype_SKIN',"","",12)
       ])
 
+class CV_BONE_SETTINGS(bpy.types.PropertyGroup):
+   collider: bpy.props.BoolProperty(name="Collider",default=False)
+   v0: bpy.props.FloatVectorProperty(name="v0",size=3)
+   v1: bpy.props.FloatVectorProperty(name="v1",size=3)
+
+   mins: bpy.props.FloatVectorProperty(name="mins",size=3)
+   maxs: bpy.props.FloatVectorProperty(name="maxs",size=3)
+
+   con0: bpy.props.BoolProperty(name="Constriant 0",default=False)
+   c0: bpy.props.FloatVectorProperty(name="dir",size=3)
+   s0: bpy.props.FloatVectorProperty(name="limits",size=3)
+
+   con1: bpy.props.BoolProperty(name="Constriant 1",default=False)
+   c1: bpy.props.FloatVectorProperty(name="dir",size=3)
+   s1: bpy.props.FloatVectorProperty(name="limits",size=3)
+
+class CV_BONE_PANEL(bpy.types.Panel):
+   bl_label="Bone Config"
+   bl_idname="SCENE_PT_cv_bone"
+   bl_space_type='PROPERTIES'
+   bl_region_type='WINDOW'
+   bl_context='bone'
+
+   def draw(_,context):
+      active_object = context.active_object
+      if active_object == None: return
+
+      bone = active_object.data.bones.active
+      if bone == None: return
+
+      _.layout.prop( bone.cv_data, "collider" )
+      _.layout.prop( bone.cv_data, "v0" )
+      _.layout.prop( bone.cv_data, "v1" )
+
+      _.layout.label( text="Angle Limits" )
+      _.layout.prop( bone.cv_data, "mins" )
+      _.layout.prop( bone.cv_data, "maxs" )
+      
+      _.layout.prop( bone.cv_data, "con0" )
+      _.layout.prop( bone.cv_data, "c0" )
+      _.layout.prop( bone.cv_data, "s0" )
+
+      _.layout.prop( bone.cv_data, "con1" )
+      _.layout.prop( bone.cv_data, "c1" )
+      _.layout.prop( bone.cv_data, "s1" )
+
 class CV_SCENE_SETTINGS(bpy.types.PropertyGroup):
    use_hidden: bpy.props.BoolProperty( name="use hidden", default=False )
 
@@ -1300,7 +1452,8 @@ class CV_COMPILE(bpy.types.Operator):
       return {'FINISHED'}
 
 classes = [CV_OBJ_SETTINGS,CV_OBJ_PANEL,CV_COMPILE,CV_INTERFACE,\
-           CV_MESH_SETTINGS, CV_SCENE_SETTINGS]
+           CV_MESH_SETTINGS, CV_SCENE_SETTINGS, CV_BONE_SETTINGS,\
+           CV_BONE_PANEL]
 
 def register():
    global cv_view_draw_handler
@@ -1311,6 +1464,7 @@ def register():
    bpy.types.Object.cv_data = bpy.props.PointerProperty(type=CV_OBJ_SETTINGS)
    bpy.types.Mesh.cv_data = bpy.props.PointerProperty(type=CV_MESH_SETTINGS)
    bpy.types.Scene.cv_data = bpy.props.PointerProperty(type=CV_SCENE_SETTINGS)
+   bpy.types.Bone.cv_data = bpy.props.PointerProperty(type=CV_BONE_SETTINGS)
 
    cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
       cv_draw,(),'WINDOW','POST_VIEW')