Skip to content

Commit

Permalink
plugin - import mdl2s as collections, not scenes
Browse files Browse the repository at this point in the history
  • Loading branch information
HENDRIX-ZT2 committed Dec 20, 2023
1 parent ae732fd commit 85651f9
Show file tree
Hide file tree
Showing 18 changed files with 602 additions and 613 deletions.
3 changes: 3 additions & 0 deletions generated/formats/ms2/compounds/PcMeshData.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ def write_pc_array(self, arr):
self.buffer_info.verts.write(padding)
offset = self.buffer_info.verts.tell()
self.buffer_info.verts.write(arr.tobytes())
# to be safe, also write padding at the end
padding = get_padding(self.buffer_info.verts.tell(), alignment=16)
self.buffer_info.verts.write(padding)
return offset // 16

def read_verts(self):
Expand Down
2 changes: 1 addition & 1 deletion plugin/export_banis.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def save(filepath=""):
corrector = Corrector(False)
scene = bpy.context.scene
bones_data = {}
b_armature_ob = get_armature(scene)
b_armature_ob = get_armature(scene.objects)
if not b_armature_ob:
logging.warning(f"No armature was found in scene '{scene.name}' - did you delete it?")
return "Failed, no armature"
Expand Down
2 changes: 1 addition & 1 deletion plugin/export_manis.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def save(filepath=""):
folder, manis_name = os.path.split(filepath)
scene = bpy.context.scene
bones_data = {}
b_armature_ob = get_armature(scene)
b_armature_ob = get_armature(scene.objects)
if not b_armature_ob:
logging.warning(f"No armature was found in scene '{scene.name}' - did you delete it?")
else:
Expand Down
39 changes: 20 additions & 19 deletions plugin/export_ms2.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,42 +94,43 @@ def save(filepath='', backup_original=True, apply_transforms=False, update_rig=F
logging.info(f"Exporting {filepath}...")

ms2.read_editable = True
found_scenes = 0
found_mdl2s = 0

model_info_lut = {model_info.name: model_info for model_info in ms2.model_infos}
for scene in bpy.data.scenes:
scene = bpy.context.scene
for mdl2_coll in scene.collection.children:
if from_scratch:
set_game(ms2.context, scene.cobra.game)
set_game(ms2.info, scene.cobra.game)
model_info = ModelInfo(ms2.context)
model_info.name = scene.name
model_info.name = mdl2_coll.name
model_info.bone_info = BoneInfo(ms2.context)
model_info.model = Model(ms2.context, model_info)
ms2.model_infos.append(model_info)
else:
if scene.name not in model_info_lut:
logging.warning(f"Scene '{scene.name}' was not found in the MS2 file, skipping")
if mdl2_coll.name not in model_info_lut:
logging.warning(f"Collection '{mdl2_coll.name}' was not found in the MS2 file, skipping")
continue
model_info = model_info_lut[scene.name]
model_info = model_info_lut[mdl2_coll.name]

found_scenes += 1
logging.info(f"Exporting scene {scene.name}")
found_mdl2s += 1
logging.info(f"Exporting {mdl2_coll.name}")

# make active scene
bpy.context.window.scene = scene
# todo - account for nesting, share with lod gen
# make all collections visible in view_layer to ensure applying modifiers works
view_collections = bpy.context.view_layer.layer_collection.children

view_states = [coll.exclude for coll in view_collections]
for coll in view_collections:
coll.exclude = False
model_info.render_flag._value = get_property(scene, "render_flag")
model_info.render_flag._value = get_property(mdl2_coll, "render_flag")
# ensure that we have objects in the scene
if not has_objects_in_scene(scene):
raise AttributeError(f"No objects in scene '{scene.name}', nothing to export!")
if not has_objects_in_scene(mdl2_coll.objects):
raise AttributeError(f"No objects in collection '{mdl2_coll.name}', nothing to export!")

b_armature_ob = get_armature(scene)
b_armature_ob = get_armature(mdl2_coll.objects)
if not b_armature_ob:
logging.warning(f"No armature was found in scene '{scene.name}' - did you delete it?")
logging.warning(f"No armature was found in collection '{mdl2_coll.name}' - did you delete it?")
else:
# clear pose
for pbone in b_armature_ob.pose.bones:
Expand All @@ -145,7 +146,7 @@ def save(filepath='', backup_original=True, apply_transforms=False, update_rig=F
bounds = []
lod_collections = []
for lod_i in range(6):
lod_coll = get_collection_endswith(scene, f"_LOD{lod_i}")
lod_coll = get_collection_endswith(scene, f"{mdl2_coll.name}_L{lod_i}")
if not lod_coll:
break
lod_collections.append(lod_coll)
Expand Down Expand Up @@ -214,15 +215,15 @@ def save(filepath='', backup_original=True, apply_transforms=False, update_rig=F
# write ms2, backup should have been created earlier
ms2.save(filepath)
# print(ms2)
if found_scenes:
if found_mdl2s:
messages.add(f"Finished MS2 export in {time.time() - start_time:.2f} seconds")
else:
mdl2_names = sorted(model_info_lut.keys())
mdl2_names_str = '\n'.join(mdl2_names)
raise AttributeError(
f"Found no scenes matching MDL2s in MS2:\n"
f"Found no collections matching MDL2s in MS2:\n"
f"{mdl2_names_str}\n"
f"Rename your scenes to match the MDL2s")
f"Rename your collections to match the MDL2s")
return messages


2 changes: 1 addition & 1 deletion plugin/import_banis.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
def load(files=[], filepath="", set_fps=False):
use_armature = True
scene = bpy.context.scene
b_armature_ob = get_armature(scene)
b_armature_ob = get_armature(scene.objects)

bones_table, p_bones = get_bones_table(b_armature_ob)
bone_names = [tup[1] for tup in bones_table]
Expand Down
2 changes: 1 addition & 1 deletion plugin/import_manis.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def load(files=[], filepath="", set_fps=False):
scene = bpy.context.scene

bones_data = {}
b_armature_ob = get_armature(scene)
b_armature_ob = get_armature(scene.objects)
if not b_armature_ob:
logging.warning(f"No armature was found in scene '{scene.name}' - did you delete it?")
b_cam_data = bpy.data.cameras.new("ManisCamera")
Expand Down
75 changes: 19 additions & 56 deletions plugin/import_ms2.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@

import bpy
# import bmesh
import numpy as np

from generated.formats.ms2.compounds.packing_utils import has_nan
from plugin.modules_import.armature import import_armature, append_armature_modifier, import_vertex_groups, \
get_bone_names
from plugin.utils.hair import add_psys
from plugin.modules_import.material import import_material
from plugin.utils.shell import is_fin, num_fur_as_weights, is_shell, gauge_uv_scale_wrapper
from plugin.utils.object import create_ob, create_scene, create_collection, mesh_from_data, set_collection_visibility
from plugin.utils.object import create_ob, create_scene, create_collection, set_collection_visibility
from generated.formats.ms2 import Ms2File
from generated.formats.ms2.enums.MeshFormat import MeshFormat

Expand All @@ -21,25 +20,26 @@ def load(filepath="", use_custom_normals=False, mirror_mesh=False):
messages = set()
start_time = time.time()
in_dir, ms2_name = os.path.split(filepath)
ms2_basename = os.path.splitext(ms2_name)[0]
ms2 = Ms2File()
ms2.load(filepath, read_editable=True)
scene = create_scene(ms2_basename, len(ms2.modelstream_names), ms2.context.version)
bpy.context.window.scene = scene
# print(ms2)
created_materials = {}
for mdl2_name, model_info in zip(ms2.mdl_2_names, ms2.model_infos):
scene = create_scene(mdl2_name, int(model_info.render_flag), len(ms2.modelstream_names), ms2.context.version)
bpy.context.window.scene = scene

for model_info in ms2.model_infos:
mdl2_coll = create_collection(scene, model_info.name)
mdl2_coll["render_flag"] = int(model_info.render_flag)
bone_names = get_bone_names(model_info)
b_armature_obj = import_armature(scene, model_info, bone_names)
b_armature_obj = import_armature(scene, model_info, bone_names, mdl2_coll)

mesh_dict = {}
ob_dict = {}
# print(model_info)
# print(model_info.model)
# print("mdl2.mesh.meshes",mdl2.mesh.meshes)
for lod_i, m_lod in enumerate(model_info.model.lods):
logging.info(f"Importing LOD{lod_i}")
lod_coll = create_collection(scene, f"LOD{lod_i}")
lod_coll = create_collection(scene, f"{model_info.name}_L{lod_i}", mdl2_coll)
# skip other shells for JWE2
obs = []
for m_ob in m_lod.objects:
Expand All @@ -53,19 +53,15 @@ def load(filepath="", use_custom_normals=False, mirror_mesh=False):
for ob_i, m_ob in enumerate(obs):
mesh = m_ob.mesh
# print(mesh)
# lod_i = mesh.lod_index
# logging.debug(f"flag {mesh.flag}")
mesh_name = f"{mdl2_name}_model{m_ob.mesh_index}"
# continue
mesh_name = f"{model_info.name}_model{m_ob.mesh_index}"
if m_ob.mesh_index in mesh_dict:
b_me = mesh_dict[m_ob.mesh_index]

# create object and mesh from data
else:
b_me = bpy.data.meshes.new(mesh_name)
# cast array to prevent truth check in from_pydata
b_me.from_pydata(mesh.vertices, [], tuple(mesh.tris))
# print(mesh.vertices, [], tuple(mesh.tris))
try:
# store mesh unknowns
# cast the bitfield to int
Expand All @@ -87,49 +83,35 @@ def load(filepath="", use_custom_normals=False, mirror_mesh=False):
# link material to mesh
import_material(created_materials, in_dir, b_me, m_ob.material)

if m_ob.mesh_index not in ob_dict:
b_ob = create_ob(scene, f"{mdl2_name}_lod{lod_i}_ob{ob_i}", b_me, coll=lod_coll)
if m_ob.mesh_index in ob_dict:
b_ob = ob_dict[m_ob.mesh_index]
else:
b_ob = create_ob(scene, f"{model_info.name}_ob{ob_i}_L{lod_i}", b_me, coll=lod_coll)
b_ob.parent = b_armature_obj

ob_dict[m_ob.mesh_index] = b_ob
try:
import_vertex_groups(b_ob, mesh, bone_names)
# import_face_maps(b_ob, mesh)
import_shapekeys(b_ob, mesh)
# link to armature, only after mirror so the order is good and weights are mirrored
append_armature_modifier(b_ob, b_armature_obj)
if mirror_mesh:
append_bisect_modifier(b_ob)
ob_postpro(b_ob, mirror_mesh, use_custom_normals)
# from plugin.modules_import.tangents import visualize_tangents
# ob2, me2 = visualize_tangents(b_ob.name, mesh.vertices, mesh.normals, mesh.tangents)
except:
logging.exception("Some mesh data failed")
ob_dict[m_ob.mesh_index] = b_ob
# from plugin.modules_import.tangents import visualize_tangents
# ob2, me2 = visualize_tangents(b_ob.name, mesh.vertices, mesh.normals, mesh.tangents)
# if mesh.flag == 517:
# ob2, me2 = visualize_foliage_field(b_ob.name, mesh.vertices, mesh.colors)
else:
b_ob = ob_dict[m_ob.mesh_index]
# we can't assume that the first ob referencing this mesh has it already
# we can't assume that the first ob referencing this mesh has fur already
if ms2.context.version > 32 and is_shell(b_ob):
logging.debug(f"{b_ob.name} has shells, adding psys")
add_psys(b_ob, mesh.fur_length)
coll_name = f"{scene.name}_LOD{lod_i}"
# show lod 0, hide the others
set_collection_visibility(scene, coll_name, lod_i != 0)
set_collection_visibility(scene, lod_coll.name, lod_i != 0)
gauge_uv_scale_wrapper()
messages.add(f"Imported {ms2_name} in {time.time() - start_time:.2f} seconds")
return messages


def import_face_maps(b_ob, mesh):
if hasattr(mesh, "face_maps"):
for map_name, face_indices in mesh.face_maps.items():
b_face_map = b_ob.face_maps.new(name=map_name)
b_face_map.add(face_indices)
# for ind in face_indices:
# b_face_map.add(ind)


def per_loop(b_me, per_vertex_input):
return [c for col in [per_vertex_input[l.vertex_index] for l in b_me.loops] for c in col]

Expand Down Expand Up @@ -229,22 +211,3 @@ def append_bisect_modifier(b_ob):
# mod.use_x = True
mod.use_axis = (True, False, False)
mod.merge_threshold = 0.001


def unpack_swizzle_vectorized_col(arr):
arr[:] = arr[:, (0, 2, 1, 3)]
arr[:, (0, 1)] *= -1.0


def visualize_foliage_field(name, vertices, colors):
colors *= 2.0
colors -= 1.0
unpack_swizzle_vectorized_col(colors)
out_verts = []
out_faces = []
v_len = 0.1
for i, (v, n) in enumerate(zip(vertices, colors)):
out_verts.append(v)
out_verts.append(v+v_len*n[:3]*n[3])
out_faces.append((i * 2, i*2 + 1,))
return mesh_from_data(bpy.context.scene, f"{name}_vecs", out_verts, out_faces, wireframe=False)
35 changes: 17 additions & 18 deletions plugin/modules_export/armature.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ def assign_p_bone_indices(b_armature_ob):
p_bone["index"] = i


def get_armature(scene):
src_armatures = [ob for ob in scene.objects if type(ob.data) == bpy.types.Armature]
def get_armature(objects):
src_armatures = [ob for ob in objects if type(ob.data) == bpy.types.Armature]
# do we have armatures?
if src_armatures:
# see if one of these is selected
Expand Down Expand Up @@ -89,7 +89,7 @@ def export_bones_custom(b_armature_ob, model_info):
bone_info.zeros_padding.arg = bone_info.zeros_count
# paddings are taken care of automatically during writing
export_ik(b_armature_ob, bone_info)
export_joints(bone_info, corrector)
export_joints(bone_info, corrector, b_armature_ob)


def add_parents(bones_with_ik, p_bone, count):
Expand Down Expand Up @@ -153,14 +153,14 @@ def check_ik_name(name):
ik_link.matrix.set_rows(def_mat.transposed())


def export_joints(bone_info, corrector):
logging.info("Exporting joints")
scene = bpy.context.scene
joint_coll = get_collection_endswith(scene, "_joints")
if not joint_coll:
def export_joints(bone_info, corrector, b_armature_ob):
logging.info(f"Exporting joints for {b_armature_ob.name}")
joint_obs = [ob for ob in b_armature_ob.children if ob.type == "EMPTY"]
if not joint_obs:
return
b_armature_basename = b_armature_ob.name.split("_armature")[0]
joints = bone_info.joints
bone_info.joint_count = joints.joint_count = len(joint_coll.objects)
bone_info.joint_count = joints.joint_count = len(joint_obs)
joints.reset_field("joint_transforms")
joints.reset_field("rigid_body_pointers")
joints.reset_field("rigid_body_list")
Expand All @@ -170,9 +170,8 @@ def export_joints(bone_info, corrector):
# reset bone -> joint mapping since we don't catch them all if we loop over existing joints
joints.bone_to_joint[:] = -1
bone_lut = {bone.name: bone_index for bone_index, bone in enumerate(bone_info.bones)}
for joint_i, joint_info in enumerate(joints.joint_infos):
b_joint = joint_coll.objects[joint_i]
joint_info.name = bone_name_for_ovl(get_joint_name(b_joint))
for joint_i, (joint_info, b_joint) in enumerate(zip(joints.joint_infos, joint_obs)):
joint_info.name = bone_name_for_ovl(get_joint_name(b_armature_basename, b_joint))
joint_info.index = joint_i
joint_info.bone_name = bone_name_for_ovl(b_joint.parent_bone)
try:
Expand Down Expand Up @@ -202,8 +201,8 @@ def export_joints(bone_info, corrector):
else:
hitcheck.surface_name = surface_name
hitcheck.classification_name = classification_name
hitcheck.name = get_joint_name(b_hitcheck)
export_hitcheck(b_hitcheck, hitcheck, corrector)
hitcheck.name = get_joint_name(b_armature_basename, b_hitcheck)
export_hitcheck(b_hitcheck, hitcheck, corrector, b_armature_basename)

rb = joints.rigid_body_list[joint_i]
if b_joint.children:
Expand All @@ -224,14 +223,14 @@ def export_joints(bone_info, corrector):
# update ragdoll constraints, relies on previously updated joints
corrector_rag = CorrectorRagdoll(False)
j_map = {j.name: j for j in joints.joint_infos}
joints_with_ragdoll_constraints = [b_joint for b_joint in joint_coll.objects if b_joint.rigid_body_constraint]
joints_with_ragdoll_constraints = [b_joint for b_joint in joint_obs if b_joint.rigid_body_constraint]
joints.num_ragdoll_constraints = len(joints_with_ragdoll_constraints)
joints.reset_field("ragdoll_constraints")
for rd, b_joint in zip(joints.ragdoll_constraints, joints_with_ragdoll_constraints):
rbc = b_joint.rigid_body_constraint
# get the joint empties, which are the parents of the respective rigidbody objects
child_joint_name = bone_name_for_ovl(get_joint_name(rbc.object1.parent))
parent_joint_name = bone_name_for_ovl(get_joint_name(rbc.object2.parent))
child_joint_name = bone_name_for_ovl(get_joint_name(b_armature_basename, rbc.object1.parent))
parent_joint_name = bone_name_for_ovl(get_joint_name(b_armature_basename, rbc.object2.parent))
rd.child.joint = j_map[child_joint_name]
rd.parent.joint = j_map[parent_joint_name]
rd.child.index = rd.child.joint.index
Expand Down Expand Up @@ -273,7 +272,7 @@ def export_joints(bone_info, corrector):

# find the root joint, assuming the first one with least parents
parents_map = []
for joint_i, b_joint in enumerate(joint_coll.objects):
for joint_i, b_joint in enumerate(joint_obs):
b_bone = b_joint.parent.data.bones[b_joint.parent_bone]
num_parents = len(b_bone.parent_recursive)
parents_map.append((num_parents, joint_i))
Expand Down
4 changes: 2 additions & 2 deletions plugin/modules_export/collision.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def get_bounds(bounds, swizzle_func=pack_swizzle):
return bounds_max, bounds_min


def export_hitcheck(b_obj, hitcheck, corrector):
hitcheck.name = get_joint_name(b_obj)
def export_hitcheck(b_obj, hitcheck, corrector, b_armature_basename):
hitcheck.name = get_joint_name(b_armature_basename, b_obj)
b_rb = b_obj.rigid_body
if not b_rb:
raise AttributeError(f"No rigid body on {b_obj.name} - can't identify collision type.")
Expand Down
Loading

0 comments on commit 85651f9

Please sign in to comment.