X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=blender_export.py;h=3f2dd793d04ff12d7f8e9091a0189a9d3530cb79;hb=3d5597be2fd4b5d9ea3bf8863e15cc9bc8123755;hp=eec7a49a2a76c81f4aecc76dd8586eddbfb650b8;hpb=d045af680c6b8ca267a7aded69e2e510e659d2ab;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/blender_export.py b/blender_export.py index eec7a49..3f2dd79 100644 --- a/blender_export.py +++ b/blender_export.py @@ -47,7 +47,7 @@ class mdl_node(Structure): ("submesh_count",c_uint32), ("classtype",c_uint32), ("offset",c_uint32), - ("children",c_uint32), + ("parent",c_uint32), ("pstr_name",c_uint32)] class mdl_header(Structure): @@ -70,13 +70,27 @@ class mdl_header(Structure): ("node_count",c_uint32), ("node_offset",c_uint32), + ("anim_count",c_uint32), + ("anim_offset",c_uint32), + ("strings_offset",c_uint32), ("entdata_offset",c_uint32), - - ("anim_count",c_uint32), - ("anim_offset",c_uint32) + ("animdata_offset",c_uint32) ] +class mdl_animation(Structure): + _pack_ = 1 + _fields_ = [("pstr_name",c_uint32), + ("length",c_uint32), + ("rate",c_float), + ("offset",c_uint32)] + +class mdl_keyframe(Structure): + _pack_ = 1 + _fields_ = [("co",c_float*3), + ("q",c_float*4), + ("s",c_float*3)] + # Entity types # ========================================== @@ -128,13 +142,21 @@ class classtype_skin(Structure): class classtype_skeleton(Structure): _pack_ = 1 - _fields_ = [("anim_start",c_uint32), + _fields_ = [("channels",c_uint32), + ("ik_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)] + # Exporter # ============================================================================== @@ -165,6 +187,10 @@ def write_model(collection_name): entdata_buffer = [] entdata_length = 0 + anim_buffer = [] + animdata_length = 0 + animdata_buffer = [] + def emplace_string( s ): nonlocal string_cache, strings_buffer @@ -230,7 +256,13 @@ def write_model(collection_name): return uid print( " creating scene graph" ) - graph = {"obj": None, "depth": 0, "children": [], "uid": _uid()} + graph = {} + graph["obj"] = None + graph["depth"] = 0 + graph["children"] = [] + graph["uid"] = _uid() + graph["parent"] = None + graph_lookup = {} # object can lookup its graph def here for obj in collection.all_objects: @@ -238,25 +270,41 @@ def write_model(collection_name): def _extend( p, n, d ): uid = _uid() - tree = {"obj":n, "depth": d, "children":[], "uid": uid} + tree = {} + tree["uid"] = uid + tree["children"] = [] + tree["depth"] = d + tree["obj"] = n + tree["parent"] = p n.cv_data.uid = uid if n.type == 'ARMATURE': tree["bones"] = [None] # None is the root transform + tree["ik_count"] = 0 def _extendb( p, n, d ): nonlocal tree - btree = {"bone":n, "depth": d, "children":[], "uid": _uid()} + btree = {} + btree["bone"] = n + btree["uid"] = _uid() + btree["children"] = [] + btree["depth"] = d + btree["parent"] = p + tree["bones"] += [n.name] + for c in n.children: _extendb( btree, c, d+1 ) + for c in tree['obj'].pose.bones[n.name].constraints: + if c.type == 'IK': + btree["target"] = c.subtarget + btree["pole"] = c.pole_subtarget + tree["ik_count"] += 1 + btree['deform'] = n.use_deform p['children'] += [btree] - if n.use_deform: - tree["bones"] += [n.name] - for b in n.data.bones: if not b.parent: _extendb( tree, b, d+1 ) @@ -277,7 +325,7 @@ def write_model(collection_name): it = _graph_iter(graph) - root.children = len(graph['children']) + root.parent = 0xffffffff # Compile # ============================================== @@ -309,9 +357,9 @@ def write_model(collection_name): node.q[3] = quat[0] if objt == 'BONE': - node.s[0] = obj.tail[0] - node.s[1] = obj.tail[2] - node.s[2] = -obj.tail[1] + node.s[0] = obj.tail_local[0] - node.co[0] + node.s[1] = obj.tail_local[2] - node.co[1] + node.s[2] = -obj.tail_local[1] - node.co[2] else: node.s[0] = obj.scale[0] node.s[1] = obj.scale[2] @@ -319,8 +367,14 @@ def write_model(collection_name): node.pstr_name = emplace_string( obj.name ) + if node_def["parent"]: + node.parent = node_def["parent"]["uid"] + if objt == 'BONE': - classtype = 'k_classtype_bone' + if 'target' in node_def: + classtype = 'k_classtype_ik_bone' + else: + classtype = 'k_classtype_bone' elif objt == 'ARMATURE': classtype = 'k_classtype_skeleton' else: @@ -345,6 +399,7 @@ def write_model(collection_name): if mod.type == 'ARMATURE': classtype = 'k_classtype_skin' armature_def = graph_lookup[mod.object] + armature_def['obj'].data.pose_position = 'REST' if can_use_cache and obj.data.name in mesh_cache: ref = mesh_cache[obj.data.name] @@ -521,11 +576,14 @@ def write_model(collection_name): s001 = F" L {obj.name}" s002 = s000+s001 s003 = F"{disptype}" - s004 = "" + s004 = F"{node.parent: 3}" + s005 = "" + if classtype == 'k_classtype_skin': - s004 = F"-> {armature_def['obj'].cv_data.uid}" + armature_def['obj'].data.pose_position = 'POSE' + s005 = F" [armature -> {armature_def['obj'].cv_data.uid}]" - scmp = F"{s002:<32} {s003:<16} {s004}" + scmp = F"{s002:<32} {s003:<22} {s004} {s005}" print( scmp ) if classtype == 'k_classtype_INSTANCE' or \ @@ -549,10 +607,88 @@ def write_model(collection_name): elif classtype == 'k_classtype_skeleton': node.classtype = 11 entdata_length += sizeof( classtype_skeleton ) - skeleton = classtype_skeleton() - skeleton.anim_start = 0 - skeleton.anim_count = 0 + + armature_def = graph_lookup[obj] + armature = obj + bones = armature_def['bones'] + skeleton.channels = len(bones) + skeleton.ik_count = armature_def["ik_count"] + + if armature.animation_data: + previous_frame = bpy.context.scene.frame_current + previous_action = armature.animation_data.action + + skeleton.anim_start = len(anim_buffer) + skeleton.anim_count = 0 + + for NLALayer in obj.animation_data.nla_tracks: + for NLAStrip in NLALayer.strips: + # Use action + for a in bpy.data.actions: + if a.name == NLAStrip.name: + armature.animation_data.action = a + break + + anim_start = int(NLAStrip.action_frame_start) + anim_end = int(NLAStrip.action_frame_end) + + # export strips + anim = mdl_animation() + anim.pstr_name = emplace_string( NLAStrip.action.name ) + anim.rate = 30.0 + anim.offset = animdata_length + anim.length = anim_end-anim_start + + # Export the fucking keyframes + for frame in range(anim_start,anim_end): + bpy.context.scene.frame_set(frame) + + for bone_name in bones: + for pb in armature.pose.bones: + if pb.name == bone_name: + rb = armature.data.bones[ bone_name ] + + loc, rot, sca = pb.matrix_basis.decompose() + + # local position + vp = rb.matrix @ loc + final_pos = Vector(( vp[0], vp[2], -vp[1] )) + + # rotation + lc_m = pb.matrix_channel.to_3x3() + if pb.parent is not None: + smtx = pb.parent.matrix_channel.to_3x3() + lc_m = smtx.inverted() @ lc_m + rq = lc_m.to_quaternion() + + kf = mdl_keyframe() + kf.co[0] = final_pos[0] + kf.co[1] = final_pos[1] + kf.co[2] = final_pos[2] + + kf.q[0] = rq[1] + kf.q[1] = rq[3] + kf.q[2] = -rq[2] + kf.q[3] = rq[0] + + # scale + kf.s[0] = sca[0] + kf.s[1] = sca[2] + kf.s[2] = sca[1] + + animdata_buffer += [kf] + animdata_length += sizeof(mdl_keyframe) + break + + anim_buffer += [anim] + skeleton.anim_count += 1 + + s000 = F" [{uid: 3}/{header.node_count-1}]" + " |"*(depth-1) + print( F"{s000} | *anim: {NLAStrip.action.name}" ) + + bpy.context.scene.frame_set( previous_frame ) + armature.animation_data.action = previous_action entdata_buffer += [skeleton] @@ -561,9 +697,20 @@ def write_model(collection_name): entdata_length += sizeof( classtype_bone ) bone = classtype_bone() - bone.use_deform = node_def['deform'] + 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'] + + entdata_buffer += [ikbone] + elif classtype == 'k_classtype_gate': node.classtype = 1 entdata_length += sizeof( classtype_gate ) @@ -669,6 +816,8 @@ def write_model(collection_name): # Write data arrays # + header.anim_count = len(anim_buffer) + print( "Writing data" ) fpos = sizeof(header) @@ -684,6 +833,10 @@ def write_model(collection_name): header.material_offset = fpos fpos += sizeof(mdl_material)*header.material_count + print( F"Animation count: {header.anim_count}" ) + header.anim_offset = fpos + fpos += sizeof(mdl_animation)*header.anim_count + print( F"Entdata length: {entdata_length}" ) header.entdata_offset = fpos fpos += entdata_length @@ -695,6 +848,10 @@ def write_model(collection_name): print( F"Indice count: {header.indice_count}" ) header.indice_offset = fpos fpos += sizeof(c_uint32)*header.indice_count + + print( F"Keyframe count: {animdata_length}" ) + header.animdata_offset = fpos + fpos += animdata_length print( F"Strings length: {len(strings_buffer)}" ) header.strings_offset = fpos @@ -713,12 +870,17 @@ def write_model(collection_name): fp.write( bytearray(sm) ) for mat in material_buffer: fp.write( bytearray(mat) ) + for a in anim_buffer: + fp.write( bytearray(a) ) for ed in entdata_buffer: fp.write( bytearray(ed) ) for v in vertex_buffer: fp.write( bytearray(v) ) for i in indice_buffer: fp.write( bytearray(i) ) + for kf in animdata_buffer: + fp.write( bytearray(kf) ) + fp.write( strings_buffer ) fp.close()