diff --git a/io_scene_psk_psa/psk/builder.py b/io_scene_psk_psa/psk/builder.py index 623ae66..95e4b2d 100644 --- a/io_scene_psk_psa/psk/builder.py +++ b/io_scene_psk_psa/psk/builder.py @@ -150,6 +150,8 @@ def build_psk(context, options: PskBuildOptions) -> PskBuildResult: for object_index, input_mesh_object in enumerate(input_objects.mesh_objects): + should_flip_normals = False + # MATERIALS material_indices = [material_names.index(material_slot.material.name) for material_slot in input_mesh_object.material_slots] @@ -177,8 +179,16 @@ def build_psk(context, options: PskBuildOptions) -> PskBuildResult: mesh_object.matrix_world = input_mesh_object.matrix_world scale = (input_mesh_object.scale.x, input_mesh_object.scale.y, input_mesh_object.scale.z) - if any(map(lambda x: x < 0, scale)): - result.warnings.append(f'Mesh "{input_mesh_object.name}" has negative scaling which may result in inverted normals.') + + # Negative scaling in Blender results in inverted normals after the scale is applied. However, if the scale + # is not applied, the normals will appear unaffected in the viewport. The evaluated mesh data used in the + # export will have the scale applied, but this behavior is not obvious to the user. + # + # In order to have the exporter be as WYSIWYG as possible, we need to check for negative scaling and invert + # the normals if necessary. If two axes have negative scaling and the third has positive scaling, the + # normals will be correct. We can detect this by checking if the number of negative scaling axes is odd. If + # it is, we need to invert the normals of the mesh by swapping the order of the vertices in each face. + should_flip_normals = sum(1 for x in scale if x < 0) % 2 == 1 # Copy the vertex groups for vertex_group in input_mesh_object.vertex_groups: @@ -233,6 +243,7 @@ def build_psk(context, options: PskBuildOptions) -> PskBuildResult: # FACES poly_groups, groups = mesh_data.calc_smooth_groups(use_bitflags=True) + psk_face_start_index = len(psk.faces) for f in mesh_data.loop_triangles: face = Psk.Face() face.material_index = material_indices[f.material_index] @@ -242,6 +253,11 @@ def build_psk(context, options: PskBuildOptions) -> PskBuildResult: face.smoothing_groups = poly_groups[f.polygon_index] psk.faces.append(face) + if should_flip_normals: + # Invert the normals of the faces. + for face in psk.faces[psk_face_start_index:]: + face.wedge_indices[0], face.wedge_indices[2] = face.wedge_indices[2], face.wedge_indices[0] + # WEIGHTS if armature_object is not None: armature_data = typing.cast(Armature, armature_object.data)