Skip to content

Commit

Permalink
ms2 - export MeshCollision
Browse files Browse the repository at this point in the history
  • Loading branch information
HENDRIX-ZT2 committed Nov 11, 2023
1 parent 2a686af commit 0e11083
Show file tree
Hide file tree
Showing 12 changed files with 73 additions and 55 deletions.
2 changes: 1 addition & 1 deletion generated/formats/base/compounds/PadAlign.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def read_fields(cls, stream, instance):

@classmethod
def write_fields(cls, stream, instance):
logging.info(f"Aligning to {instance.template.__class__.__name__} as {instance.get_pad(stream)}")
# logging.debug(f"Aligning to {instance.template.__class__.__name__} as {instance.get_pad(stream)}")
instance.data = ZERO * instance.get_pad(stream)
stream.write(instance.data)

Expand Down
2 changes: 1 addition & 1 deletion generated/formats/ms2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ def update_buffer_2_bytes(self):
self.info.vertex_buffer_count = max_stream_index + 1
# this is the rule for JWE2, except trike93 STATIC=0
self.info.static_buffer_index = max_stream_index
self.num_streams = max_stream_index
self.num_streams = max(0, max_stream_index)
self.reset_field("buffer_pointers")
self.reset_field("buffer_infos")
self.reset_field("modelstream_names")
Expand Down
8 changes: 4 additions & 4 deletions generated/formats/ms2/compounds/MeshCollision.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ def __init__(self, context, arg=0, template=None, set_default=True):
# offset of mesh
self.offset = name_type_map['Vector3'](self.context, 0, None)

# shared among (all?) redwoods
self.unk_1 = Array(self.context, 0, None, (0,), name_type_map['MeshCollisionIndex'])
# seems to be constant
self.indices = Array(self.context, 0, None, (0,), name_type_map['MeshCollisionIndex'])

# found in PC FR_GrandCarousel
self.unk_2 = Array(self.context, 0, None, (0,), name_type_map['Uint'])
Expand Down Expand Up @@ -49,7 +49,7 @@ def _get_attribute_list(cls):
yield from super()._get_attribute_list()
yield 'rotation', name_type_map['Matrix33'], (0, None), (False, None), (None, None)
yield 'offset', name_type_map['Vector3'], (0, None), (False, None), (None, None)
yield 'unk_1', Array, (0, None, (3,), name_type_map['MeshCollisionIndex']), (False, None), (None, None)
yield 'indices', Array, (0, None, (3,), name_type_map['MeshCollisionIndex']), (False, None), (None, None)
yield 'unk_2', Array, (0, None, (3,), name_type_map['Uint']), (False, None), (lambda context: context.version == 32, None)
yield 'vertex_count', name_type_map['Uint64'], (0, None), (False, None), (None, None)
yield 'tri_count', name_type_map['Uint64'], (0, None), (False, None), (None, None)
Expand All @@ -68,7 +68,7 @@ def _get_filtered_attribute_list(cls, instance, include_abstract=True):
yield from super()._get_filtered_attribute_list(instance, include_abstract)
yield 'rotation', name_type_map['Matrix33'], (0, None), (False, None)
yield 'offset', name_type_map['Vector3'], (0, None), (False, None)
yield 'unk_1', Array, (0, None, (3,), name_type_map['MeshCollisionIndex']), (False, None)
yield 'indices', Array, (0, None, (3,), name_type_map['MeshCollisionIndex']), (False, None)
if instance.context.version == 32:
yield 'unk_2', Array, (0, None, (3,), name_type_map['Uint']), (False, None)
yield 'vertex_count', name_type_map['Uint64'], (0, None), (False, None)
Expand Down
27 changes: 18 additions & 9 deletions generated/formats/ms2/compounds/ModelReader.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ def get_padding(self, stream, alignment=16, rel=None):
rel = self.start_of_buffer
abs_offset = stream.tell()
relative_offset = abs_offset - rel
# currently no other way to predict the padding, no correlation to joint count
padding_len = get_padding_size(relative_offset, alignment=alignment)
# logging.debug(f"abs {abs_offset} rel {relative_offset}")
logging.debug(f"Aligning to {alignment} from {abs_offset} to {abs_offset+padding_len} ({padding_len} bytes)")
Expand All @@ -196,6 +195,15 @@ def get_padding(self, stream, alignment=16, rel=None):
logging.warning(f"Padding is nonzero {padding} at offset {abs_offset}")
# raise AttributeError(f"Padding is nonzero {padding} at offset {abs_offset}")

def align_to(self, stream, alignment=16, rel=None):
if rel is None:
rel = self.start_of_buffer
abs_offset = stream.tell()
relative_offset = abs_offset - rel
padding_len = get_padding_size(relative_offset, alignment=alignment)
logging.debug(f"Aligning to {alignment} from {abs_offset} to {abs_offset+padding_len} ({padding_len} bytes)")
stream.write(b'\x00' * padding_len)

def read_hitcheck_verts(self, bone_info, stream):
try:
start = stream.tell()
Expand All @@ -208,7 +216,7 @@ def read_hitcheck_verts(self, bone_info, stream):
logging.debug(f"Reading vertices for {hitcheck.dtype.name} at {stream.tell()}")
hitcheck.collider.vertices = Array.from_stream(stream, self.context, 0, None, (hitcheck.collider.vertex_count, 3), Float)
# logging.debug(f"End of vertices at {stream.tell()}")
if hitcheck.dtype in (CollisionType.MESH_COLLISION,):
elif hitcheck.dtype in (CollisionType.MESH_COLLISION,):
self.get_padding(stream, alignment=16)
logging.debug(f"Reading vertices for {hitcheck.dtype.name} at {stream.tell()}")
# logging.debug(f"Hitcheck {hitcheck.collider}")
Expand All @@ -223,8 +231,12 @@ def write_hitcheck_verts(self, bone_info, stream):
logging.debug(f"Writing additional hitcheck data")
for hitcheck in self.get_hitchecks(bone_info):
if hitcheck.dtype in (CollisionType.CONVEX_HULL_P_C, CollisionType.CONVEX_HULL):
logging.debug(f"Writing vertices for {hitcheck.dtype}")
if is_pc(self.context):
self.align_to(stream, alignment=16)
Array.to_stream(hitcheck.collider.vertices, stream, self.context, dtype=Float)
elif hitcheck.dtype in (CollisionType.MESH_COLLISION,):
self.align_to(stream, alignment=16)
MeshCollisionData.to_stream(hitcheck.collider.data, stream, self.context)

@classmethod
def write_fields(cls, stream, instance):
Expand All @@ -237,6 +249,8 @@ def write_fields(cls, stream, instance):
for model_info in instance.arg.model_infos:
model_info.model.to_stream(model_info.model, stream, instance.context)
instance.bone_info_start = stream.tell()
# the models are not part of the buffer
instance.start_of_buffer = stream.tell()
for model_info in instance.arg.model_infos:
# check if they have a different bone info
if previous_bone_info is not model_info.bone_info:
Expand All @@ -245,12 +259,7 @@ def write_fields(cls, stream, instance):
logging.debug(f"BONE INFO {i} starts at {stream.tell()}")
model_info.bone_info.to_stream(model_info.bone_info, stream, instance.context)
instance.write_hitcheck_verts(model_info.bone_info, stream)
# PZ lion needs padding after last boneinfo, crashes if missing, adding probably won't hurt other cases
# if i + 1 < len(instance.bone_infos):
relative_offset = stream.tell() - instance.bone_info_start
padding = get_padding(relative_offset)
logging.debug(f"Writing padding {padding}")
stream.write(padding)
instance.align_to(stream, alignment=16)
i += 1
else:
logging.debug(f"{model_info.name} reuses previous bone_info")
Expand Down
2 changes: 1 addition & 1 deletion generated/formats/ms2/ms2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@
JWE2: 188 bytes
<add name="rotation" type="Matrix33"/>
<add name="offset" type="Vector3" >offset of mesh</add>
<add name="unk1" type="MeshCollisionIndex" arr1="3" >shared among (all?) redwoods</add>
<add name="indices" type="MeshCollisionIndex" arr1="3" >seems to be constant</add>
<add name="unk2" type="uint" arr1="3" vercond="#PC#">found in PC FR_GrandCarousel</add>
<add name="vertex count" type="uint64"/>
<add name="tri count" type="uint64"/>
Expand Down
1 change: 1 addition & 0 deletions plugin/export_ms2.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ def save(filepath='', backup_original=True, apply_transforms=False, update_rig=F
# reset to original state
for coll, state in zip(view_collections, view_states):
coll.exclude = state
print(ms2)
# write ms2, backup should have been created earlier
ms2.save(filepath)
if found_scenes:
Expand Down
51 changes: 25 additions & 26 deletions plugin/modules_export/collision.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from generated.formats.ms2.compounds.ConvexHull import ConvexHull
from generated.formats.ms2.compounds.Cylinder import Cylinder
from generated.formats.ms2.compounds.MeshCollision import MeshCollision
from generated.formats.ms2.compounds.MeshCollisionData import MeshCollisionData
from generated.formats.ms2.compounds.Sphere import Sphere
from generated.formats.ms2.compounds.packing_utils import pack_swizzle, pack_swizzle_collision
from generated.formats.ms2.enums.CollisionType import CollisionType
Expand All @@ -17,7 +18,7 @@


def export_bounds(bounds, model_info):
logging.info("Exporting bounds")
logging.debug("Exporting bounds")
bounds_max, bounds_min = get_bounds(bounds)
center = (bounds_min+bounds_max)/2
model_info = model_info
Expand All @@ -26,11 +27,12 @@ def export_bounds(bounds, model_info):
model_info.radius = (center-bounds_max).length*0.77


def assign_bounds(model_info, bounds_max, bounds_min):
model_info.bounds_max.set(bounds_max)
model_info.bounds_min.set(bounds_min)
model_info.bounds_max_repeat.set(bounds_max)
model_info.bounds_min_repeat.set(bounds_min)
def assign_bounds(target, bounds_max, bounds_min):
target.bounds_max.set(bounds_max)
target.bounds_min.set(bounds_min)
if hasattr(target, "bounds_max_repeat"):
target.bounds_max_repeat.set(bounds_max)
target.bounds_min_repeat.set(bounds_min)


def get_bounds(bounds):
Expand All @@ -45,9 +47,6 @@ def get_bounds(bounds):
return bounds_max, bounds_min


# print(model_info)


def export_hitcheck(b_obj, hitcheck, corrector):
hitcheck.name = get_joint_name(b_obj)
b_rb = b_obj.rigid_body
Expand Down Expand Up @@ -83,7 +82,7 @@ def get_collider_matrix(b_hitcheck):

def export_spherebv(b_obj, hitcheck):
hitcheck.dtype = CollisionType.SPHERE
hitcheck.collider = Sphere(hitcheck.context)
hitcheck.reset_field("collider")

matrix = get_collider_matrix(b_obj)
hitcheck.collider.radius = b_obj.dimensions.x / 2
Expand All @@ -93,7 +92,7 @@ def export_spherebv(b_obj, hitcheck):

def export_boxbv(b_obj, hitcheck, corrector):
hitcheck.dtype = CollisionType.BOUNDING_BOX
hitcheck.collider = BoundingBox(hitcheck.context)
hitcheck.reset_field("collider")

matrix = get_collider_matrix(b_obj)
c = hitcheck.collider.center
Expand All @@ -111,13 +110,13 @@ def set_rot_matrix(b_matrix_4x4, m_rot_3x3, corrector):

def export_capsulebv(b_obj, hitcheck):
hitcheck.dtype = CollisionType.CAPSULE
hitcheck.collider = Capsule(hitcheck.context)
hitcheck.reset_field("collider")
_capsule_transform(b_obj, hitcheck)


def export_cylinderbv(b_obj, hitcheck):
hitcheck.dtype = CollisionType.CYLINDER
hitcheck.collider = Cylinder(hitcheck.context)
hitcheck.reset_field("collider")
_capsule_transform(b_obj, hitcheck)
# sole difference
hitcheck.collider.extent = b_obj.dimensions.z
Expand All @@ -129,43 +128,43 @@ def export_meshbv(b_obj, hitcheck, corrector):
matrix = get_collider_matrix(b_obj)

hitcheck.dtype = CollisionType.MESH_COLLISION
hitcheck.collider = MeshCollision(hitcheck.context)
hitcheck.reset_field("collider")
coll = hitcheck.collider

for i in range(3):
coll.indices[i].index = i+1
bounds_max, bounds_min = get_bounds((b_obj.bound_box, ))
assign_bounds(coll, bounds_max, bounds_min)

# export rotation
set_rot_matrix(matrix, hitcheck.collider.rotation, corrector)
# export translation
c = coll.offset
c.x, c.y, c.z = pack_swizzle(matrix.translation)
# export vertices
coll.offset.set(pack_swizzle(matrix.translation))
# export geometry
coll.vertex_count = len(eval_me.vertices)
coll.vertices.resize((coll.vertex_count, 3))
coll.tri_count = len(eval_me.polygons)
coll.data = MeshCollisionData(coll.context, coll)
for vert_i, vert in enumerate(eval_me.vertices):
coll.vertices[vert_i, :] = pack_swizzle(vert.co)
coll.data.vertices[vert_i, :] = pack_swizzle_collision(vert.co)
# export triangles
coll.tri_count = len(eval_me.polygons)
coll.triangles.resize((coll.tri_count, 3))
for face_i, face in enumerate(eval_me.polygons):
coll.triangles[face_i, :] = face.vertices
coll.data.triangles[face_i, :] = face.vertices
assert len(face.vertices) == 3
print(coll)
print(coll.data)


def export_hullbv(b_obj, hitcheck, corrector):
me = b_obj.data
matrix = get_collider_matrix(b_obj)

hitcheck.dtype = CollisionType.CONVEX_HULL
hitcheck.collider = ConvexHull(hitcheck.context)
hitcheck.reset_field("collider")
coll = hitcheck.collider

# export rotation
set_rot_matrix(matrix, hitcheck.collider.rotation, corrector)
# export translation
c = coll.offset
c.x, c.y, c.z = pack_swizzle(matrix.translation)
coll.offset.set(pack_swizzle(matrix.translation))
# export vertices
coll.vertex_count = len(me.vertices)
coll.vertices = np.empty((coll.vertex_count, 3), dtype="float")
Expand Down
2 changes: 1 addition & 1 deletion source/formats/base/compounds/PadAlign.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def read_fields(cls, stream, instance):

@classmethod
def write_fields(cls, stream, instance):
logging.info(f"Aligning to {instance.template.__class__.__name__} as {instance.get_pad(stream)}")
# logging.debug(f"Aligning to {instance.template.__class__.__name__} as {instance.get_pad(stream)}")
instance.data = ZERO * instance.get_pad(stream)
stream.write(instance.data)

Expand Down
2 changes: 1 addition & 1 deletion source/formats/ms2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def update_buffer_2_bytes(self):
self.info.vertex_buffer_count = max_stream_index + 1
# this is the rule for JWE2, except trike93 STATIC=0
self.info.static_buffer_index = max_stream_index
self.num_streams = max_stream_index
self.num_streams = max(0, max_stream_index)
self.reset_field("buffer_pointers")
self.reset_field("buffer_infos")
self.reset_field("modelstream_names")
Expand Down
27 changes: 18 additions & 9 deletions source/formats/ms2/compounds/ModelReader.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ def get_padding(self, stream, alignment=16, rel=None):
rel = self.start_of_buffer
abs_offset = stream.tell()
relative_offset = abs_offset - rel
# currently no other way to predict the padding, no correlation to joint count
padding_len = get_padding_size(relative_offset, alignment=alignment)
# logging.debug(f"abs {abs_offset} rel {relative_offset}")
logging.debug(f"Aligning to {alignment} from {abs_offset} to {abs_offset+padding_len} ({padding_len} bytes)")
Expand All @@ -184,6 +183,15 @@ def get_padding(self, stream, alignment=16, rel=None):
logging.warning(f"Padding is nonzero {padding} at offset {abs_offset}")
# raise AttributeError(f"Padding is nonzero {padding} at offset {abs_offset}")

def align_to(self, stream, alignment=16, rel=None):
if rel is None:
rel = self.start_of_buffer
abs_offset = stream.tell()
relative_offset = abs_offset - rel
padding_len = get_padding_size(relative_offset, alignment=alignment)
logging.debug(f"Aligning to {alignment} from {abs_offset} to {abs_offset+padding_len} ({padding_len} bytes)")
stream.write(b'\x00' * padding_len)

def read_hitcheck_verts(self, bone_info, stream):
try:
start = stream.tell()
Expand All @@ -196,7 +204,7 @@ def read_hitcheck_verts(self, bone_info, stream):
logging.debug(f"Reading vertices for {hitcheck.dtype.name} at {stream.tell()}")
hitcheck.collider.vertices = Array.from_stream(stream, self.context, 0, None, (hitcheck.collider.vertex_count, 3), Float)
# logging.debug(f"End of vertices at {stream.tell()}")
if hitcheck.dtype in (CollisionType.MESH_COLLISION,):
elif hitcheck.dtype in (CollisionType.MESH_COLLISION,):
self.get_padding(stream, alignment=16)
logging.debug(f"Reading vertices for {hitcheck.dtype.name} at {stream.tell()}")
# logging.debug(f"Hitcheck {hitcheck.collider}")
Expand All @@ -211,8 +219,12 @@ def write_hitcheck_verts(self, bone_info, stream):
logging.debug(f"Writing additional hitcheck data")
for hitcheck in self.get_hitchecks(bone_info):
if hitcheck.dtype in (CollisionType.CONVEX_HULL_P_C, CollisionType.CONVEX_HULL):
logging.debug(f"Writing vertices for {hitcheck.dtype}")
if is_pc(self.context):
self.align_to(stream, alignment=16)
Array.to_stream(hitcheck.collider.vertices, stream, self.context, dtype=Float)
elif hitcheck.dtype in (CollisionType.MESH_COLLISION,):
self.align_to(stream, alignment=16)
MeshCollisionData.to_stream(hitcheck.collider.data, stream, self.context)

@classmethod
def write_fields(cls, stream, instance):
Expand All @@ -225,6 +237,8 @@ def write_fields(cls, stream, instance):
for model_info in instance.arg.model_infos:
model_info.model.to_stream(model_info.model, stream, instance.context)
instance.bone_info_start = stream.tell()
# the models are not part of the buffer
instance.start_of_buffer = stream.tell()
for model_info in instance.arg.model_infos:
# check if they have a different bone info
if previous_bone_info is not model_info.bone_info:
Expand All @@ -233,12 +247,7 @@ def write_fields(cls, stream, instance):
logging.debug(f"BONE INFO {i} starts at {stream.tell()}")
model_info.bone_info.to_stream(model_info.bone_info, stream, instance.context)
instance.write_hitcheck_verts(model_info.bone_info, stream)
# PZ lion needs padding after last boneinfo, crashes if missing, adding probably won't hurt other cases
# if i + 1 < len(instance.bone_infos):
relative_offset = stream.tell() - instance.bone_info_start
padding = get_padding(relative_offset)
logging.debug(f"Writing padding {padding}")
stream.write(padding)
instance.align_to(stream, alignment=16)
i += 1
else:
logging.debug(f"{model_info.name} reuses previous bone_info")
Expand Down
2 changes: 1 addition & 1 deletion source/formats/ms2/ms2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@
JWE2: 188 bytes
<add name="rotation" type="Matrix33"/>
<add name="offset" type="Vector3" >offset of mesh</add>
<add name="unk1" type="MeshCollisionIndex" arr1="3" >shared among (all?) redwoods</add>
<add name="indices" type="MeshCollisionIndex" arr1="3" >seems to be constant</add>
<add name="unk2" type="uint" arr1="3" vercond="#PC#">found in PC FR_GrandCarousel</add>
<add name="vertex count" type="uint64"/>
<add name="tri count" type="uint64"/>
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f40bc5d2f - Sat Nov 11 11:32:11 2023 +0100
2a686af15 - Sat Nov 11 12:23:01 2023 +0100

0 comments on commit 0e11083

Please sign in to comment.