STRP importing, Teancum's highsinger and xizor break implying adaptations needed for ZETools-exported MSHs
This commit is contained in:
commit
3dda2a0d77
|
@ -100,15 +100,15 @@ class ExportMSH(Operator, ExportHelper):
|
||||||
default=True
|
default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
export_animated: BoolProperty(
|
export_with_animation: BoolProperty(
|
||||||
name="Export Animated Object",
|
name="Export With Animation",
|
||||||
description="Always check if the object will be animated.",
|
description="Includes animation data extracted from the action currently set on armature.",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
export_skeleton_only: BoolProperty(
|
export_as_skeleton: BoolProperty(
|
||||||
name="Export Skeleton",
|
name="Export Objects As Skeleton",
|
||||||
description="Check if you intend to export skeleton data only.",
|
description="Check if you intend to export skeleton data for consumption by ZenAsset.",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -122,9 +122,9 @@ class ExportMSH(Operator, ExportHelper):
|
||||||
generate_triangle_strips=self.generate_triangle_strips,
|
generate_triangle_strips=self.generate_triangle_strips,
|
||||||
apply_modifiers=self.apply_modifiers,
|
apply_modifiers=self.apply_modifiers,
|
||||||
export_target=self.export_target,
|
export_target=self.export_target,
|
||||||
skel_only=self.export_skeleton_only
|
skel_only=self.export_as_skeleton,
|
||||||
|
export_anim=self.export_with_animation
|
||||||
),
|
),
|
||||||
is_animated=self.export_animated
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
|
@ -12,7 +12,7 @@ from .msh_utilities import *
|
||||||
from .msh_model_gather import *
|
from .msh_model_gather import *
|
||||||
|
|
||||||
|
|
||||||
def extract_anim(armature: bpy.types.Armature) -> Animation:
|
def extract_anim(armature: bpy.types.Armature, root_name: str) -> Animation:
|
||||||
|
|
||||||
action = armature.animation_data.action
|
action = armature.animation_data.action
|
||||||
anim = Animation();
|
anim = Animation();
|
||||||
|
@ -27,19 +27,23 @@ def extract_anim(armature: bpy.types.Armature) -> Animation:
|
||||||
|
|
||||||
anim.end_index = num_frames - 1
|
anim.end_index = num_frames - 1
|
||||||
|
|
||||||
anim.bone_transforms["DummyRoot"] = []
|
anim.bone_frames[root_name] = ([], [])
|
||||||
for bone in armature.data.bones:
|
for bone in armature.data.bones:
|
||||||
anim.bone_transforms[bone.name] = []
|
anim.bone_frames[bone.name] = ([], [])
|
||||||
|
|
||||||
for frame in range(num_frames):
|
for frame in range(num_frames):
|
||||||
|
|
||||||
#if frame % 10 == 0:
|
|
||||||
# print("Sample frame {}:".format(frame))
|
|
||||||
|
|
||||||
frame_time = framerange.x + frame * increment
|
frame_time = framerange.x + frame * increment
|
||||||
bpy.context.scene.frame_set(frame_time)
|
bpy.context.scene.frame_set(frame_time)
|
||||||
|
|
||||||
anim.bone_transforms["DummyRoot"].append(ModelTransform())
|
|
||||||
|
rframe_dummy = RotationFrame(frame, convert_rotation_space(Quaternion()))
|
||||||
|
tframe_dummy = TranslationFrame(frame, Vector((0.0,0.0,0.0)))
|
||||||
|
|
||||||
|
anim.bone_frames[root_name][0].append(tframe_dummy)
|
||||||
|
anim.bone_frames[root_name][1].append(rframe_dummy)
|
||||||
|
|
||||||
|
|
||||||
for bone in armature.pose.bones:
|
for bone in armature.pose.bones:
|
||||||
|
|
||||||
transform = bone.matrix
|
transform = bone.matrix
|
||||||
|
@ -49,13 +53,11 @@ def extract_anim(armature: bpy.types.Armature) -> Animation:
|
||||||
|
|
||||||
loc, rot, _ = transform.decompose()
|
loc, rot, _ = transform.decompose()
|
||||||
|
|
||||||
xform = ModelTransform()
|
rframe = RotationFrame(frame, convert_rotation_space(rot))
|
||||||
xform.rotation = convert_rotation_space(rot)
|
tframe = TranslationFrame(frame, convert_vector_space(loc))
|
||||||
xform.translation = convert_vector_space(loc)
|
|
||||||
|
|
||||||
#if frame % 10 == 0:
|
anim.bone_frames[bone.name][0].append(tframe)
|
||||||
# print("\t{:10}: loc {:15} rot {:15}".format(bone.name, vec_to_str(xform.translation), quat_to_str(xform.rotation)))
|
anim.bone_frames[bone.name][1].append(rframe)
|
||||||
|
|
||||||
anim.bone_transforms[bone.name].append(xform)
|
|
||||||
|
|
||||||
return anim
|
return anim
|
||||||
|
|
|
@ -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, Tuple
|
from typing import List, Tuple, Dict
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from mathutils import Vector, Quaternion
|
from mathutils import Vector, Quaternion
|
||||||
|
|
||||||
|
@ -52,7 +52,6 @@ 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:
|
||||||
|
@ -80,14 +79,27 @@ class Model:
|
||||||
collisionprimitive: CollisionPrimitive = None
|
collisionprimitive: CollisionPrimitive = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RotationFrame:
|
||||||
|
|
||||||
|
index : int = 0
|
||||||
|
rotation : Quaternion = field(default_factory=Quaternion)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TranslationFrame:
|
||||||
|
|
||||||
|
index : int = 0
|
||||||
|
translation : Vector = field(default_factory=Vector)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Animation:
|
class Animation:
|
||||||
""" Class representing 'CYCL' + 'KFR3' sections in a .msh file """
|
""" Class representing 'CYCL' + 'KFR3' sections in a .msh file """
|
||||||
|
|
||||||
name: str = "fullanimation"
|
name: str = "fullanimation"
|
||||||
bone_transforms: Dict[str, List[ModelTransform]] = field(default_factory=dict)
|
bone_frames: Dict[str, Tuple[List[TranslationFrame], List[RotationFrame]]] = field(default_factory=dict)
|
||||||
|
|
||||||
framerate: float = 29.97
|
framerate: float = 29.97
|
||||||
start_index : int = 0
|
start_index : int = 0
|
||||||
end_index : int = 0
|
end_index : int = 0
|
||||||
|
|
|
@ -14,7 +14,7 @@ SKIPPED_OBJECT_TYPES = {"LATTICE", "CAMERA", "LIGHT", "SPEAKER", "LIGHT_PROBE"}
|
||||||
MESH_OBJECT_TYPES = {"MESH", "CURVE", "SURFACE", "META", "FONT", "GPENCIL"}
|
MESH_OBJECT_TYPES = {"MESH", "CURVE", "SURFACE", "META", "FONT", "GPENCIL"}
|
||||||
MAX_MSH_VERTEX_COUNT = 32767
|
MAX_MSH_VERTEX_COUNT = 32767
|
||||||
|
|
||||||
def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool) -> List[Model]:
|
def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool) -> Tuple[List[Model], bpy.types.Object]:
|
||||||
""" Gathers the Blender objects from the current scene and returns them as a list of
|
""" Gathers the Blender objects from the current scene and returns them as a list of
|
||||||
Model objects. """
|
Model objects. """
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
|
|
||||||
models_list: List[Model] = []
|
models_list: List[Model] = []
|
||||||
|
|
||||||
|
armature_found = None
|
||||||
|
|
||||||
for uneval_obj in select_objects(export_target):
|
for uneval_obj in select_objects(export_target):
|
||||||
if uneval_obj.type in SKIPPED_OBJECT_TYPES and uneval_obj.name not in parents:
|
if uneval_obj.type in SKIPPED_OBJECT_TYPES and uneval_obj.name not in parents:
|
||||||
continue
|
continue
|
||||||
|
@ -36,6 +38,7 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
|
|
||||||
if obj.type == "ARMATURE":
|
if obj.type == "ARMATURE":
|
||||||
models_list += expand_armature(obj)
|
models_list += expand_armature(obj)
|
||||||
|
armature_found = obj
|
||||||
continue
|
continue
|
||||||
|
|
||||||
model = Model()
|
model = Model()
|
||||||
|
@ -44,7 +47,6 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
model.hidden = get_is_model_hidden(obj)
|
model.hidden = get_is_model_hidden(obj)
|
||||||
|
|
||||||
transform = obj.matrix_local
|
transform = obj.matrix_local
|
||||||
transform_reset = Matrix.Identity(4)
|
|
||||||
|
|
||||||
if obj.parent_bone:
|
if obj.parent_bone:
|
||||||
model.parent = obj.parent_bone
|
model.parent = obj.parent_bone
|
||||||
|
@ -52,18 +54,8 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
# matrix_local, when called on an armature child also parented to a bone, appears to be broken.
|
# matrix_local, when called on an armature child also parented to a bone, appears to be broken.
|
||||||
# At the very least, the results contradict the docs...
|
# At the very least, the results contradict the docs...
|
||||||
armature_relative_transform = obj.parent.matrix_world.inverted() @ obj.matrix_world
|
armature_relative_transform = obj.parent.matrix_world.inverted() @ obj.matrix_world
|
||||||
bone_relative_transform = obj.parent.data.bones[obj.parent_bone].matrix_local.inverted() @ armature_relative_transform
|
transform = obj.parent.data.bones[obj.parent_bone].matrix_local.inverted() @ armature_relative_transform
|
||||||
|
|
||||||
transform = bone_relative_transform
|
|
||||||
|
|
||||||
'''
|
|
||||||
# Since the transforms of direct bone children are discarded by ZEngine (but not ZEditor), we apply the transform
|
|
||||||
# before geometry extraction, then apply the inversion after.
|
|
||||||
if obj.type in MESH_OBJECT_TYPES:
|
|
||||||
obj.data.transform(bone_relative_transform)
|
|
||||||
transform_reset = bone_relative_transform.inverted()
|
|
||||||
transform = Matrix.Identity(4)
|
|
||||||
'''
|
|
||||||
else:
|
else:
|
||||||
if obj.parent is not None:
|
if obj.parent is not None:
|
||||||
if obj.parent.type == "ARMATURE":
|
if obj.parent.type == "ARMATURE":
|
||||||
|
@ -95,8 +87,6 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
if obj.vertex_groups:
|
if obj.vertex_groups:
|
||||||
model.bone_map = [group.name for group in obj.vertex_groups]
|
model.bone_map = [group.name for group in obj.vertex_groups]
|
||||||
|
|
||||||
obj.data.transform(transform_reset)
|
|
||||||
|
|
||||||
|
|
||||||
if get_is_collision_primitive(obj):
|
if get_is_collision_primitive(obj):
|
||||||
model.collisionprimitive = get_collision_primitive(obj)
|
model.collisionprimitive = get_collision_primitive(obj)
|
||||||
|
@ -104,7 +94,9 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
|
|
||||||
models_list.append(model)
|
models_list.append(model)
|
||||||
|
|
||||||
return models_list
|
|
||||||
|
return (models_list, armature_found)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_parents_set() -> Set[str]:
|
def create_parents_set() -> Set[str]:
|
||||||
|
|
|
@ -3,12 +3,13 @@ import io
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
class Reader:
|
class Reader:
|
||||||
def __init__(self, file, parent=None, indent=0):
|
def __init__(self, file, parent=None, indent=0, debug=False):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.size: int = 0
|
self.size: int = 0
|
||||||
self.size_pos = None
|
self.size_pos = None
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.indent = " " * indent #for print debugging
|
self.indent = " " * indent #for print debugging
|
||||||
|
self.debug = debug
|
||||||
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
@ -19,7 +20,8 @@ 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))
|
if self.debug:
|
||||||
|
print(self.indent + "Begin " + self.header + ", Size: " + str(self.size) + ", Pos: " + str(self.size_pos))
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -28,7 +30,9 @@ 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)
|
if self.debug:
|
||||||
|
print(self.indent + "End " + self.header)
|
||||||
|
|
||||||
self.file.seek(self.end_pos)
|
self.file.seek(self.end_pos)
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +88,7 @@ class Reader:
|
||||||
|
|
||||||
|
|
||||||
def read_child(self):
|
def read_child(self):
|
||||||
child = Reader(self.file, parent=self, indent=int(len(self.indent) / 2) + 1)
|
child = Reader(self.file, parent=self, indent=int(len(self.indent) / 2) + 1, debug=self.debug)
|
||||||
return child
|
return child
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +99,13 @@ class Reader:
|
||||||
def peak_next_header(self):
|
def peak_next_header(self):
|
||||||
buf = self.read_bytes(4);
|
buf = self.read_bytes(4);
|
||||||
self.file.seek(-4,1)
|
self.file.seek(-4,1)
|
||||||
return buf.decode("utf-8")
|
try:
|
||||||
|
result = buf.decode("utf-8")
|
||||||
|
except:
|
||||||
|
result = ""
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def could_have_child(self):
|
def could_have_child(self):
|
||||||
|
|
|
@ -6,7 +6,7 @@ from typing import List, Dict
|
||||||
from copy import copy
|
from copy import copy
|
||||||
import bpy
|
import bpy
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from .msh_model import Model
|
from .msh_model import Model, Animation
|
||||||
from .msh_model_gather import gather_models
|
from .msh_model_gather import gather_models
|
||||||
from .msh_model_utilities import sort_by_parent, has_multiple_root_models, reparent_model_roots, get_model_world_matrix
|
from .msh_model_utilities import sort_by_parent, has_multiple_root_models, reparent_model_roots, get_model_world_matrix
|
||||||
from .msh_model_triangle_strips import create_models_triangle_strips
|
from .msh_model_triangle_strips import create_models_triangle_strips
|
||||||
|
@ -44,9 +44,11 @@ class Scene:
|
||||||
materials: Dict[str, Material] = field(default_factory=dict)
|
materials: Dict[str, Material] = field(default_factory=dict)
|
||||||
models: List[Model] = field(default_factory=list)
|
models: List[Model] = field(default_factory=list)
|
||||||
|
|
||||||
|
animation: Animation = None
|
||||||
|
|
||||||
skeleton: List[int] = field(default_factory=list)
|
skeleton: List[int] = field(default_factory=list)
|
||||||
|
|
||||||
def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str, skel_only: bool) -> Scene:
|
def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str, skel_only: bool, export_anim: bool) -> Scene:
|
||||||
""" Create a msh Scene from the active Blender scene. """
|
""" Create a msh Scene from the active Blender scene. """
|
||||||
|
|
||||||
scene = Scene()
|
scene = Scene()
|
||||||
|
@ -55,7 +57,7 @@ def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_t
|
||||||
|
|
||||||
scene.materials = gather_materials()
|
scene.materials = gather_materials()
|
||||||
|
|
||||||
scene.models = gather_models(apply_modifiers=apply_modifiers, export_target=export_target, skeleton_only=skel_only)
|
scene.models, armature_obj = gather_models(apply_modifiers=apply_modifiers, export_target=export_target, skeleton_only=skel_only)
|
||||||
scene.models = sort_by_parent(scene.models)
|
scene.models = sort_by_parent(scene.models)
|
||||||
|
|
||||||
if generate_triangle_strips:
|
if generate_triangle_strips:
|
||||||
|
@ -71,12 +73,17 @@ def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_t
|
||||||
|
|
||||||
scene.materials = remove_unused_materials(scene.materials, scene.models)
|
scene.materials = remove_unused_materials(scene.materials, scene.models)
|
||||||
|
|
||||||
#creates a dummy basepose if no Action is selected
|
|
||||||
if "Armature" in bpy.context.scene.objects.keys():
|
|
||||||
scene.anims = [extract_anim(bpy.context.scene.objects["Armature"])]
|
|
||||||
|
|
||||||
root = scene.models[0]
|
root = scene.models[0]
|
||||||
|
|
||||||
|
if export_anim:
|
||||||
|
if armature_obj is not None:
|
||||||
|
scene.animation = extract_anim(armature_obj, root.name)
|
||||||
|
else:
|
||||||
|
raise Exception("Export Error: Could not find an armature object from which to export an animation!")
|
||||||
|
|
||||||
if skel_only and root.model_type == ModelType.NULL:
|
if skel_only and root.model_type == ModelType.NULL:
|
||||||
|
# For ZenAsset
|
||||||
inject_dummy_data(root)
|
inject_dummy_data(root)
|
||||||
|
|
||||||
return scene
|
return scene
|
||||||
|
|
|
@ -12,6 +12,8 @@ from .crc import *
|
||||||
|
|
||||||
model_counter = 0
|
model_counter = 0
|
||||||
|
|
||||||
|
mndx_remap = {}
|
||||||
|
|
||||||
|
|
||||||
def read_scene(input_file) -> Scene:
|
def read_scene(input_file) -> Scene:
|
||||||
|
|
||||||
|
@ -19,10 +21,13 @@ def read_scene(input_file) -> Scene:
|
||||||
scene.models = []
|
scene.models = []
|
||||||
scene.materials = {}
|
scene.materials = {}
|
||||||
|
|
||||||
|
global mndx_remap
|
||||||
|
mndx_remap = {}
|
||||||
|
|
||||||
global model_counter
|
global model_counter
|
||||||
model_counter = 0
|
model_counter = 0
|
||||||
|
|
||||||
with Reader(file=input_file) as hedr:
|
with Reader(file=input_file, debug=True) as hedr:
|
||||||
|
|
||||||
while hedr.could_have_child():
|
while hedr.could_have_child():
|
||||||
|
|
||||||
|
@ -54,8 +59,7 @@ def read_scene(input_file) -> Scene:
|
||||||
scene.models.append(_read_modl(modl, materials_list))
|
scene.models.append(_read_modl(modl, materials_list))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
with hedr.read_child() as unknown:
|
msh2.skip_bytes(1)
|
||||||
pass
|
|
||||||
|
|
||||||
elif "SKL2" in next_header:
|
elif "SKL2" in next_header:
|
||||||
with hedr.read_child() as skl2:
|
with hedr.read_child() as skl2:
|
||||||
|
@ -67,16 +71,28 @@ def read_scene(input_file) -> Scene:
|
||||||
_read_anm2(anm2, scene.models)
|
_read_anm2(anm2, scene.models)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
with hedr.read_child() as null:
|
hedr.skip_bytes(1)
|
||||||
pass
|
|
||||||
|
|
||||||
if scene.skeleton:
|
if scene.skeleton:
|
||||||
print("Skeleton models: ")
|
print("Skeleton models: ")
|
||||||
for model in scene.models:
|
for model in scene.models:
|
||||||
if crc(model.name) in scene.skeleton:
|
for i in range(len(scene.skeleton)):
|
||||||
print("\t" + model.name)
|
if crc(model.name) == scene.skeleton[i]:
|
||||||
|
print("\t" + model.name)
|
||||||
|
if model.model_type == ModelType.SKIN:
|
||||||
|
scene.skeleton.pop(i)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
for model in scene.models:
|
||||||
|
if model.geometry:
|
||||||
|
for seg in model.geometry:
|
||||||
|
if seg.weights:
|
||||||
|
for weight_set in seg.weights:
|
||||||
|
for vweight in weight_set:
|
||||||
|
vweight.bone = mndx_remap[vweight.bone]
|
||||||
|
|
||||||
return scene
|
return scene
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,7 +151,7 @@ def _read_matd(matd: Reader) -> Material:
|
||||||
mat.texture3 = tx3d.read_string()
|
mat.texture3 = tx3d.read_string()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
matd.skip_bytes(4)
|
matd.skip_bytes(1)
|
||||||
|
|
||||||
return mat
|
return mat
|
||||||
|
|
||||||
|
@ -154,9 +170,14 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model:
|
||||||
|
|
||||||
elif "MNDX" in next_header:
|
elif "MNDX" in next_header:
|
||||||
with modl.read_child() as mndx:
|
with modl.read_child() as mndx:
|
||||||
|
index = mndx.read_u32()
|
||||||
|
|
||||||
global model_counter
|
global model_counter
|
||||||
if mndx.read_u32() - 1 != model_counter:
|
print(mndx.indent + "MNDX doesn't match counter, expected: {} found: {}".format(model_counter, index))
|
||||||
print("MODEL INDEX DIDNT MATCH COUNTER!")
|
|
||||||
|
global mndx_remap
|
||||||
|
mndx_remap[index] = model_counter
|
||||||
|
|
||||||
model_counter += 1
|
model_counter += 1
|
||||||
|
|
||||||
elif "NAME" in next_header:
|
elif "NAME" in next_header:
|
||||||
|
@ -191,18 +212,20 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model:
|
||||||
elif "ENVL" in next_header_geom:
|
elif "ENVL" in next_header_geom:
|
||||||
with geom.read_child() as envl:
|
with geom.read_child() as envl:
|
||||||
num_indicies = envl.read_u32()
|
num_indicies = envl.read_u32()
|
||||||
envelope += [envl.read_u32() - 1 for _ in range(num_indicies)]
|
envelope += [envl.read_u32() for _ in range(num_indicies)]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
with geom.read_child() as null:
|
geom.skip_bytes(1)
|
||||||
pass
|
#with geom.read_child() as null:
|
||||||
|
#pass
|
||||||
|
|
||||||
for seg in model.geometry:
|
for seg in model.geometry:
|
||||||
if seg.weights:
|
if seg.weights and envelope:
|
||||||
for weight_set in seg.weights:
|
for weight_set in seg.weights:
|
||||||
for i in range(len(weight_set)):
|
for i in range(len(weight_set)):
|
||||||
weight = weight_set[i]
|
vertex_weight = weight_set[i]
|
||||||
weight_set[i] = (envelope[weight[0]], weight[1])
|
index = vertex_weight.bone
|
||||||
|
weight_set[i] = VertexWeight(vertex_weight.weight, envelope[vertex_weight.bone])
|
||||||
|
|
||||||
elif "SWCI" in next_header:
|
elif "SWCI" in next_header:
|
||||||
prim = CollisionPrimitive()
|
prim = CollisionPrimitive()
|
||||||
|
@ -214,10 +237,9 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model:
|
||||||
model.collisionprimitive = prim
|
model.collisionprimitive = prim
|
||||||
|
|
||||||
else:
|
else:
|
||||||
with modl.read_child() as null:
|
modl.skip_bytes(1)
|
||||||
pass
|
|
||||||
|
|
||||||
print("Reading model " + model.name + " of type: " + str(model.model_type)[10:])
|
print(modl.indent + "Read model " + model.name + " of type: " + str(model.model_type)[10:])
|
||||||
|
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
@ -293,11 +315,45 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
|
||||||
geometry_seg.triangles.append(ndxt.read_u16(3))
|
geometry_seg.triangles.append(ndxt.read_u16(3))
|
||||||
|
|
||||||
elif "STRP" in next_header:
|
elif "STRP" in next_header:
|
||||||
with segm.read_child() as strp:
|
strips : List[List[int]] = []
|
||||||
pass
|
|
||||||
|
|
||||||
if segm.read_u16 != 0: #trailing 0 bug https://schlechtwetterfront.github.io/ze_filetypes/msh.html#STRP
|
with segm.read_child() as strp:
|
||||||
segm.skip_bytes(-2)
|
num_indicies = strp.read_u32()
|
||||||
|
|
||||||
|
num_indicies_read = 0
|
||||||
|
|
||||||
|
curr_strip = []
|
||||||
|
previous_flag = False
|
||||||
|
|
||||||
|
if num_indicies > 0:
|
||||||
|
index, index1 = strp.read_u16(2)
|
||||||
|
curr_strip = [index & 0x7fff, index1 & 0x7fff]
|
||||||
|
num_indicies_read += 2
|
||||||
|
|
||||||
|
for i in range(num_indicies - 2):
|
||||||
|
index = strp.read_u16(1)
|
||||||
|
|
||||||
|
if index & 0x8000 > 0:
|
||||||
|
index = index & 0x7fff
|
||||||
|
|
||||||
|
if previous_flag:
|
||||||
|
previous_flag = False
|
||||||
|
curr_strip.append(index)
|
||||||
|
strips.append(curr_strip[:-2])
|
||||||
|
curr_strip = curr_strip[-2:]
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
previous_flag = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
previous_flag = False
|
||||||
|
|
||||||
|
curr_strip.append(index)
|
||||||
|
|
||||||
|
geometry_seg.triangle_strips = strips
|
||||||
|
|
||||||
|
#if segm.read_u16 != 0: #trailing 0 bug https://schlechtwetterfront.github.io/ze_filetypes/msh.html#STRP
|
||||||
|
# segm.skip_bytes(-2)
|
||||||
|
|
||||||
elif "WGHT" in next_header:
|
elif "WGHT" in next_header:
|
||||||
with segm.read_child() as wght:
|
with segm.read_child() as wght:
|
||||||
|
@ -312,13 +368,12 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
|
||||||
value = wght.read_f32()
|
value = wght.read_f32()
|
||||||
|
|
||||||
if value > 0.000001:
|
if value > 0.000001:
|
||||||
weight_set.append((index,value))
|
weight_set.append(VertexWeight(value,index))
|
||||||
|
|
||||||
geometry_seg.weights.append(weight_set)
|
geometry_seg.weights.append(weight_set)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
with segm.read_child() as null:
|
segm.skip_bytes(1)
|
||||||
pass
|
|
||||||
|
|
||||||
return geometry_seg
|
return geometry_seg
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,10 @@ from .msh_material import *
|
||||||
from .msh_writer import Writer
|
from .msh_writer import Writer
|
||||||
from .msh_utilities import *
|
from .msh_utilities import *
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
from .crc import *
|
from .crc import *
|
||||||
|
|
||||||
def save_scene(output_file, scene: Scene, is_animated: bool):
|
|
||||||
=======
|
|
||||||
def save_scene(output_file, scene: Scene):
|
def save_scene(output_file, scene: Scene):
|
||||||
>>>>>>> mshread
|
|
||||||
""" Saves scene to the supplied file. """
|
""" Saves scene to the supplied file. """
|
||||||
|
|
||||||
with Writer(file=output_file, chunk_id="HEDR") as hedr:
|
with Writer(file=output_file, chunk_id="HEDR") as hedr:
|
||||||
|
@ -36,16 +33,15 @@ def save_scene(output_file, scene: Scene):
|
||||||
with msh2.create_child("MODL") as modl:
|
with msh2.create_child("MODL") as modl:
|
||||||
_write_modl(modl, model, index, material_index, model_index)
|
_write_modl(modl, model, index, material_index, model_index)
|
||||||
|
|
||||||
if is_animated:
|
if scene.animation is not None:
|
||||||
with hedr.create_child("SKL2") as skl2:
|
with hedr.create_child("SKL2") as skl2:
|
||||||
_write_skl2(skl2, scene)
|
_write_skl2(skl2, scene.animation)
|
||||||
|
|
||||||
with hedr.create_child("BLN2") as bln2:
|
with hedr.create_child("BLN2") as bln2:
|
||||||
_write_bln2(bln2, scene)
|
_write_bln2(bln2, scene.animation)
|
||||||
|
|
||||||
with hedr.create_child("ANM2") as anm2: #simple for now
|
with hedr.create_child("ANM2") as anm2: #simple for now
|
||||||
for anim in scene.anims:
|
_write_anm2(anm2, scene.animation)
|
||||||
_write_anm2(anm2, anim)
|
|
||||||
|
|
||||||
with hedr.create_child("CL1L"):
|
with hedr.create_child("CL1L"):
|
||||||
pass
|
pass
|
||||||
|
@ -251,23 +247,23 @@ def _write_envl(envl: Writer, model: Model, model_index: Dict[str, int]):
|
||||||
'''
|
'''
|
||||||
SKELETON CHUNKS
|
SKELETON CHUNKS
|
||||||
'''
|
'''
|
||||||
def _write_bln2(bln2: Writer, scene: Scene):
|
def _write_bln2(bln2: Writer, anim: Animation):
|
||||||
bones = scene.anims[0].bone_transforms.keys()
|
bones = anim.bone_frames.keys()
|
||||||
bln2.write_u32(len(bones))
|
bln2.write_u32(len(bones))
|
||||||
|
|
||||||
for boneName in bones:
|
for boneName in bones:
|
||||||
bln2.write_u32(crc(boneName), 0)
|
bln2.write_u32(crc(boneName), 0)
|
||||||
|
|
||||||
def _write_skl2(skl2: Writer, scene: Scene):
|
def _write_skl2(skl2: Writer, anim: Animation):
|
||||||
bones = scene.anims[0].bone_transforms.keys()
|
bones = anim.bone_frames.keys()
|
||||||
skl2.write_u32(len(bones))
|
skl2.write_u32(len(bones))
|
||||||
|
|
||||||
for boneName in bones:
|
for boneName in bones:
|
||||||
skl2.write_u32(crc(boneName), 0) #default values
|
skl2.write_u32(crc(boneName), 0) #default values from docs
|
||||||
skl2.write_f32(1.0, 0.0, 0.0) #from docs
|
skl2.write_f32(1.0, 0.0, 0.0)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
ANIMATION CHUNK
|
ANIMATION CHUNKS
|
||||||
'''
|
'''
|
||||||
def _write_anm2(anm2: Writer, anim: Animation):
|
def _write_anm2(anm2: Writer, anim: Animation):
|
||||||
|
|
||||||
|
@ -286,19 +282,21 @@ def _write_anm2(anm2: Writer, anim: Animation):
|
||||||
|
|
||||||
with anm2.create_child("KFR3") as kfr3:
|
with anm2.create_child("KFR3") as kfr3:
|
||||||
|
|
||||||
kfr3.write_u32(len(anim.bone_transforms.keys()))
|
kfr3.write_u32(len(anim.bone_frames))
|
||||||
|
|
||||||
for boneName in anim.bone_transforms.keys():
|
for boneName in anim.bone_frames:
|
||||||
kfr3.write_u32(crc(boneName))
|
kfr3.write_u32(crc(boneName))
|
||||||
kfr3.write_u32(0) #what is keyframe type?
|
kfr3.write_u32(0) #what is keyframe type?
|
||||||
|
|
||||||
num_frames = 1 + anim.end_index - anim.start_index
|
translation_frames, rotation_frames = anim.bone_frames[boneName]
|
||||||
kfr3.write_u32(num_frames, num_frames) #basic testing
|
|
||||||
|
|
||||||
for i, xform in enumerate(anim.bone_transforms[boneName]):
|
kfr3.write_u32(len(translation_frames), len(rotation_frames))
|
||||||
kfr3.write_u32(i)
|
|
||||||
kfr3.write_f32(xform.translation.x, xform.translation.y, xform.translation.z)
|
for frame in translation_frames:
|
||||||
|
kfr3.write_u32(frame.index)
|
||||||
|
kfr3.write_f32(frame.translation.x, frame.translation.y, frame.translation.z)
|
||||||
|
|
||||||
|
for frame in rotation_frames:
|
||||||
|
kfr3.write_u32(frame.index)
|
||||||
|
kfr3.write_f32(frame.rotation.x, frame.rotation.y, frame.rotation.z, frame.rotation.w)
|
||||||
|
|
||||||
for i, xform in enumerate(anim.bone_transforms[boneName]):
|
|
||||||
kfr3.write_u32(i)
|
|
||||||
kfr3.write_f32(xform.rotation.x, xform.rotation.y, xform.rotation.z, xform.rotation.w)
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ def extract_refined_skeleton(scene: Scene):
|
||||||
if seg.weights:
|
if seg.weights:
|
||||||
for weight_set in seg.weights:
|
for weight_set in seg.weights:
|
||||||
for weight in weight_set:
|
for weight in weight_set:
|
||||||
model_weighted_to = scene.models[weight[0]]
|
model_weighted_to = scene.models[weight.bone]
|
||||||
|
|
||||||
if crc(model_weighted_to.name) not in scene.skeleton:
|
if crc(model_weighted_to.name) not in scene.skeleton:
|
||||||
scene.skeleton.append(crc(model_weighted_to.name))
|
scene.skeleton.append(crc(model_weighted_to.name))
|
||||||
|
@ -164,7 +164,14 @@ def extract_models(scene: Scene, materials_map):
|
||||||
else:
|
else:
|
||||||
full_texcoords += [(0.0,0.0) for _ in range(len(seg.positions))]
|
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]
|
if seg.triangles:
|
||||||
|
faces += [tuple([ind + offset for ind in tri]) for tri in seg.triangles]
|
||||||
|
else:
|
||||||
|
for strip in seg.triangle_strips:
|
||||||
|
for i in range(len(strip) - 2):
|
||||||
|
face = tuple([offset + strip[j] for j in range(i,i+3)])
|
||||||
|
print("strip face: " + str(face))
|
||||||
|
faces.append(face)
|
||||||
|
|
||||||
offset += len(seg.positions)
|
offset += len(seg.positions)
|
||||||
|
|
||||||
|
@ -199,13 +206,13 @@ def extract_models(scene: Scene, materials_map):
|
||||||
for offset in weights_offsets:
|
for offset in weights_offsets:
|
||||||
for i, weight_set in enumerate(weights_offsets[offset]):
|
for i, weight_set in enumerate(weights_offsets[offset]):
|
||||||
for weight in weight_set:
|
for weight in weight_set:
|
||||||
index = weight[0]
|
index = weight.bone
|
||||||
|
|
||||||
if index not in vertex_groups_indicies:
|
if index not in vertex_groups_indicies:
|
||||||
model_name = scene.models[index].name
|
model_name = scene.models[index].name
|
||||||
vertex_groups_indicies[index] = new_obj.vertex_groups.new(name=model_name)
|
vertex_groups_indicies[index] = new_obj.vertex_groups.new(name=model_name)
|
||||||
|
|
||||||
vertex_groups_indicies[index].add([offset + i], weight[1], 'ADD')
|
vertex_groups_indicies[index].add([offset + i], weight.weight, 'ADD')
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Assign Materials - will do per segment later...
|
Assign Materials - will do per segment later...
|
||||||
|
@ -277,8 +284,8 @@ def extract_scene(filepath: str, scene: Scene):
|
||||||
skel = extract_refined_skeleton(scene)
|
skel = extract_refined_skeleton(scene)
|
||||||
armature = refined_skeleton_to_armature(skel, model_map)
|
armature = refined_skeleton_to_armature(skel, model_map)
|
||||||
|
|
||||||
|
reparent_obj = None
|
||||||
for model in scene.models:
|
for model in scene.models:
|
||||||
reparent_obj = None
|
|
||||||
if model.model_type == ModelType.SKIN:
|
if model.model_type == ModelType.SKIN:
|
||||||
|
|
||||||
if model.parent:
|
if model.parent:
|
||||||
|
@ -314,8 +321,9 @@ def extract_scene(filepath: str, scene: Scene):
|
||||||
|
|
||||||
for model in scene.models:
|
for model in scene.models:
|
||||||
if model.name in bpy.data.objects:
|
if model.name in bpy.data.objects:
|
||||||
if model.hidden and len(bpy.data.objects[model.name].children) == 0:
|
obj = bpy.data.objects[model.name]
|
||||||
bpy.data.objects[model.name].hide_set(True)
|
if get_is_model_hidden(obj) and len(obj.children) == 0:
|
||||||
|
obj.hide_set(True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue