Correct skeleton imports, though what to do with re-parenting and skeleton impurities such as effector nulls still uncertain

This commit is contained in:
William Herald Snyder 2020-11-25 23:10:14 -05:00
parent 049803f750
commit 8273e01167
5 changed files with 170 additions and 26 deletions

View File

@ -2,7 +2,7 @@
saved to a .msh file. """ saved to a .msh file. """
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List from typing import List, Tuple
from enum import Enum from enum import Enum
from mathutils import Vector, Quaternion from mathutils import Vector, Quaternion
@ -44,6 +44,8 @@ class GeometrySegment:
triangles: List[List[int]] = field(default_factory=list) triangles: List[List[int]] = field(default_factory=list)
triangle_strips: List[List[int]] = None triangle_strips: List[List[int]] = None
weights: List[List[Tuple[int, float]]] = None
@dataclass @dataclass
class CollisionPrimitive: class CollisionPrimitive:
""" Class representing a 'SWCI' section in a .msh file. """ """ Class representing a 'SWCI' section in a .msh file. """

View File

@ -5,6 +5,16 @@ from .msh_model import *
from .msh_utilities import * from .msh_utilities import *
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
def convert_vector_space_(vec: Vector) -> Vector:
return Vector((-vec.x, vec.z, vec.y))
def convert_rotation_space_(quat: Quaternion) -> Quaternion:
return Quaternion((-quat.w, quat.x, -quat.z, -quat.y))
def model_transform_to_matrix(transform: ModelTransform):
return Matrix.Translation(convert_vector_space_(transform.translation)) @ convert_rotation_space_(transform.rotation).to_matrix().to_4x4()
def scale_segments(scale: Vector, segments: List[GeometrySegment]): def scale_segments(scale: Vector, segments: List[GeometrySegment]):
""" Scales are positions in the GeometrySegment list. """ """ Scales are positions in the GeometrySegment list. """

View File

@ -19,7 +19,7 @@ class Reader:
padding_length = 4 - (self.size % 4) if self.size % 4 > 0 else 0 padding_length = 4 - (self.size % 4) if self.size % 4 > 0 else 0
self.end_pos = self.size_pos + padding_length + self.size + 8 self.end_pos = self.size_pos + padding_length + self.size + 8
print(self.indent + "Begin " + self.header + ", Size: " + str(self.size) + ", Pos: " + str(self.size_pos)) #print(self.indent + "Begin " + self.header + ", Size: " + str(self.size) + ", Pos: " + str(self.size_pos))
return self return self
@ -28,7 +28,7 @@ class Reader:
if self.size > self.MAX_SIZE: if self.size > self.MAX_SIZE:
raise OverflowError(f".msh file overflowed max size. size = {self.size} MAX_SIZE = {self.MAX_SIZE}") raise OverflowError(f".msh file overflowed max size. size = {self.size} MAX_SIZE = {self.MAX_SIZE}")
print(self.indent + "End " + self.header) #print(self.indent + "End " + self.header)
self.file.seek(self.end_pos) self.file.seek(self.end_pos)

View File

@ -57,11 +57,12 @@ def read_scene(input_file) -> Scene:
with hedr.read_child() as skl2: with hedr.read_child() as skl2:
num_bones = skl2.read_u32() num_bones = skl2.read_u32()
scene.skeleton = [skl2.read_u32(5)[0] for i in range(num_bones)] scene.skeleton = [skl2.read_u32(5)[0] for i in range(num_bones)]
print("Skeleton models: ") #print("Skeleton models: ")
for crc_hash in scene.skeleton: for crc_hash in scene.skeleton:
for model in scene.models: for model in scene.models:
if crc_hash == crc(model.name): if crc_hash == crc(model.name):
print("\t" + model.name + " with type: " + str(model.model_type)) pass
#print("\t" + model.name + " with type: " + str(model.model_type))
elif "ANM2" in next_header: elif "ANM2" in next_header:
with hedr.read_child() as anm2: with hedr.read_child() as anm2:
@ -70,11 +71,7 @@ def read_scene(input_file) -> Scene:
else: else:
with hedr.read_child() as null: with hedr.read_child() as null:
pass pass
print("Models indexed by ENVLs: ")
for envl_index in set(envls):
print("\t" + scene.models[envl_index].name)
return scene return scene
@ -285,9 +282,24 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
segm.skip_bytes(-2) segm.skip_bytes(-2)
elif "WGHT" in next_header: elif "WGHT" in next_header:
with segm.read_child() as null: with segm.read_child() as wght:
pass pass
'''
geometry_seg.weights = []
num_weights = wght.read_u32()
for _ in range(num_weights):
weight_set = []
for _ in range(4):
index = wght.read_u32()
value = wght.read_f32()
if value > 0.000001:
weight_set.append((index,value))
geometry_seg.weights.append(weight_set)
'''
else: else:
with segm.read_child() as unknown: with segm.read_child() as unknown:
pass pass

View File

@ -18,6 +18,102 @@ import os
def refined_skeleton_to_armature(refined_skeleton : List[Model], model_map):
armature = bpy.data.armatures.new("skeleton")
armature_obj = bpy.data.objects.new("skeleton", armature)
bpy.context.view_layer.active_layer_collection.collection.objects.link(armature_obj)
armature_obj.select_set(True)
bpy.context.view_layer.objects.active = armature_obj
bpy.ops.object.mode_set(mode='EDIT')
for bone in refined_skeleton:
edit_bone = armature.edit_bones.new(bone.name)
if bone.parent:
edit_bone.parent = armature.edit_bones[bone.parent]
edit_bone.head = model_map[bone.name].matrix_world.translation
bone_children = [b for b in get_model_children(bone, refined_skeleton)]
if len(bone_children) > 0:
edit_bone.tail = Vector((0.0,0.0,0.0))
for bone_child in bone_children:
edit_bone.tail += model_map[bone_child.name].matrix_world.translation
edit_bone.tail = edit_bone.tail / len(bone_children)
else:
edit_bone.tail = model_map[bone.name].matrix_world @ Vector((-0.2,0.0,0.0))
bpy.ops.object.mode_set(mode='OBJECT')
armature_obj.select_set(True)
bpy.context.view_layer.update()
def extract_refined_skeleton(scene: Scene):
model_dict = {}
children_dict = {}
skeleton_models = []
for model in scene.models:
model_dict[model.name] = model
children_dict[model.name] = []
for model in scene.models:
if model.parent:
children_dict[model.parent].append(model)
if crc(model.name) in scene.skeleton:
skeleton_models.append(model)
refined_skeleton_models = []
for bone in skeleton_models:
if bone.parent:
curr_ancestor = model_dict[bone.parent]
stacked_transform = model_transform_to_matrix(bone.transform)
while True:
if crc(curr_ancestor.name) in scene.skeleton or "dummyroot" in curr_ancestor.name.lower():
new_model = Model()
new_model.name = bone.name
new_model.parent = curr_ancestor.name if "dummyroot" not in curr_ancestor.name.lower() else ""
loc, rot, _ = stacked_transform.decompose()
new_model.transform.rotation = rot
new_model.transform.translation = loc
refined_skeleton_models.append(new_model)
break
else:
curr_ancestor = model_dict[curr_ancestor.parent]
stacked_transform = model_transform_to_matrix(curr_ancestor.transform) @ stacked_transform
return sort_by_parent(refined_skeleton_models)
def extract_models(scene: Scene, materials_map): def extract_models(scene: Scene, materials_map):
model_map = {} model_map = {}
@ -25,7 +121,13 @@ def extract_models(scene: Scene, materials_map):
for model in sort_by_parent(scene.models): for model in sort_by_parent(scene.models):
new_obj = None new_obj = None
if model.name.startswith("p_") or "collision" in model.name: if "bone_l_toe" in model.name:
loc = get_model_world_matrix(model, scene.models).translation
print("World bone_l_toe: " + str(loc))
if model.name.startswith("p_") or "collision" in model.name or model.name.startswith("c_") or model.name.startswith("sv_"):
continue continue
if model.model_type == ModelType.STATIC or model.model_type == ModelType.SKIN: if model.model_type == ModelType.STATIC or model.model_type == ModelType.SKIN:
@ -37,37 +139,47 @@ def extract_models(scene: Scene, materials_map):
mat_name = "" mat_name = ""
full_texcoords = []
for i,seg in enumerate(model.geometry): for i,seg in enumerate(model.geometry):
if i == 0: if i == 0:
mat_name = seg.material_name mat_name = seg.material_name
verts += [tuple(convert_vector_space(v)) for v in seg.positions] verts += [tuple(convert_vector_space(v)) for v in seg.positions]
if seg.texcoords is not None:
full_texcoords += seg.texcoords
else:
full_texcoords += [(0.0,0.0) for _ in range(len(seg.positions))]
faces += [tuple([ind + offset for ind in tri]) for tri in seg.triangles] faces += [tuple([ind + offset for ind in tri]) for tri in seg.triangles]
offset += len(seg.positions) offset += len(seg.positions)
new_mesh.from_pydata(verts, [], faces) new_mesh.from_pydata(verts, [], faces)
new_mesh.update() new_mesh.update()
new_mesh.validate() new_mesh.validate()
'''
edit_mesh = bmesh.new() if len(full_texcoords) > 0:
edit_mesh.from_mesh(new_mesh)
uvlayer = edit_mesh.loops.layers.uv.verify() edit_mesh = bmesh.new()
edit_mesh.from_mesh(new_mesh)
for edit_mesh_face in edit_mesh.faces: uvlayer = edit_mesh.loops.layers.uv.verify()
mesh_face = faces[edit_mesh_face.index]
for i,loop in enumerate(edit_mesh_face.loops): for edit_mesh_face in edit_mesh.faces:
texcoord = seg.texcoords[mesh_face[i]] mesh_face = faces[edit_mesh_face.index]
loop[uvlayer].uv = tuple([texcoord.x, texcoord.y])
edit_mesh.to_mesh(new_mesh) for i,loop in enumerate(edit_mesh_face.loops):
edit_mesh.free()
''' texcoord = full_texcoords[mesh_face[i]]
loop[uvlayer].uv = tuple([texcoord.x, texcoord.y])
edit_mesh.to_mesh(new_mesh)
edit_mesh.free()
new_obj = bpy.data.objects.new(new_mesh.name, new_mesh) new_obj = bpy.data.objects.new(new_mesh.name, new_mesh)
@ -102,6 +214,8 @@ def extract_models(scene: Scene, materials_map):
bpy.context.collection.objects.link(new_obj) bpy.context.collection.objects.link(new_obj)
return model_map
def extract_materials(folder_path: str, scene: Scene) -> Dict[str,bpy.types.Material]: def extract_materials(folder_path: str, scene: Scene) -> Dict[str,bpy.types.Material]:
@ -135,7 +249,13 @@ def extract_scene(filepath: str, scene: Scene):
folder = os.path.join(os.path.dirname(filepath),"") folder = os.path.join(os.path.dirname(filepath),"")
matmap = extract_materials(folder,scene) matmap = extract_materials(folder,scene)
extract_models(scene, matmap)
model_map = extract_models(scene, matmap)
skel = extract_refined_skeleton(scene)
refined_skeleton_to_armature(skel, model_map)