class classtype_route(Structure):
_pack_ = 1
- _fields_ = [("pstr_name",c_uint32),
- ("id_start",c_uint32),
+ _fields_ = [("id_start",c_uint32),
("colour",c_float*3)]
class classtype_skin(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
# ==============================================================================
def emplace_material( mat ):
nonlocal material_cache, material_buffer
+ if mat == None:
+ return 0
+
if mat.name in material_cache:
return material_cache[mat.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
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]
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:
can_use_cache = True
for mod in obj.modifiers:
- if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP':
+ if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
+ mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
+ mod.type == 'ARRAY':
can_use_cache = False
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:
# WEight groups
#
if armature_def:
- weight_groups = sorted( data.vertices[vi].groups, key = \
+ src_groups = [_ for _ in data.vertices[vi].groups \
+ if obj.vertex_groups[_.group].name in \
+ armature_def['bones']]
+
+ weight_groups = sorted( src_groups, key = \
lambda a: a.weight, reverse=True )
tot = 0.0
for ml in range(3):
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}"
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
for pb in armature.pose.bones:
if pb.name == bone_name:
rb = armature.data.bones[ bone_name ]
-
- loc, rot, sca = pb.matrix_basis.decompose()
+
+ # relative bone matrix
+ if rb.parent is not None:
+ offset_mtx = rb.parent.matrix_local
+ offset_mtx = offset_mtx.inverted_safe() @ \
+ rb.matrix_local
+
+ inv_parent = pb.parent.matrix @ offset_mtx
+ inv_parent.invert_safe()
+ fpm = inv_parent @ pb.matrix
+ else:
+ bone_mtx = rb.matrix.to_4x4()
+ local_inv = rb.matrix_local.inverted_safe()
+ fpm = bone_mtx @ local_inv @ pb.matrix
+
+ loc, rot, sca = fpm.decompose()
# local position
- vp = rb.matrix @ loc
- final_pos = Vector(( vp[0], vp[2], -vp[1] ))
+ final_pos = Vector(( loc[0], loc[2], -loc[1] ))
# rotation
lc_m = pb.matrix_channel.to_3x3()
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
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]
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 and obj.data.pose_position == 'REST':
+ 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':
('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)
+
+ con0: bpy.props.BoolProperty(name="Constriant 0",default=False)
+ mins: bpy.props.FloatVectorProperty(name="mins",size=3)
+ maxs: bpy.props.FloatVectorProperty(name="maxs",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, "con0" )
+ _.layout.prop( bone.cv_data, "mins" )
+ _.layout.prop( bone.cv_data, "maxs" )
+
class CV_SCENE_SETTINGS(bpy.types.PropertyGroup):
use_hidden: bpy.props.BoolProperty( name="use hidden", default=False )
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
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')