Fixed an issue with the order of texture vertex indices when one or more objects has negative scaling
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from bpy.types import Object
|
from bpy.types import Object, Context
|
||||||
|
|
||||||
from .ase import *
|
from .ase import *
|
||||||
import bpy
|
import bpy
|
||||||
import bmesh
|
import bmesh
|
||||||
import math
|
import math
|
||||||
from mathutils import Matrix
|
from mathutils import Matrix, Vector
|
||||||
|
|
||||||
|
SMOOTHING_GROUP_MAX = 32
|
||||||
|
|
||||||
class ASEBuilderError(Exception):
|
class ASEBuilderError(Exception):
|
||||||
pass
|
pass
|
||||||
@@ -20,14 +21,15 @@ class ASEBuilderOptions(object):
|
|||||||
|
|
||||||
|
|
||||||
class ASEBuilder(object):
|
class ASEBuilder(object):
|
||||||
def build(self, context, options: ASEBuilderOptions, objects: Iterable[Object]):
|
def build(self, context: Context, options: ASEBuilderOptions, objects: Iterable[Object]):
|
||||||
ase = ASE()
|
ase = ASE()
|
||||||
|
|
||||||
main_geometry_object = None
|
main_geometry_object = None
|
||||||
for selected_object in objects:
|
|
||||||
if selected_object is None or selected_object.type != 'MESH':
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
mesh_objects = [obj for obj in objects if obj.type == 'MESH']
|
||||||
|
context.window_manager.progress_begin(0, len(mesh_objects))
|
||||||
|
|
||||||
|
for object_index, selected_object in enumerate(mesh_objects):
|
||||||
# Evaluate the mesh after modifiers are applied
|
# Evaluate the mesh after modifiers are applied
|
||||||
if options.use_raw_mesh_data:
|
if options.use_raw_mesh_data:
|
||||||
mesh_object = selected_object
|
mesh_object = selected_object
|
||||||
@@ -85,15 +87,22 @@ class ASEBuilder(object):
|
|||||||
material_indices.append(material_index)
|
material_indices.append(material_index)
|
||||||
|
|
||||||
mesh_data.calc_loop_triangles()
|
mesh_data.calc_loop_triangles()
|
||||||
|
|
||||||
|
# Calculate smoothing groups.
|
||||||
poly_groups, groups = mesh_data.calc_smooth_groups(use_bitflags=False)
|
poly_groups, groups = mesh_data.calc_smooth_groups(use_bitflags=False)
|
||||||
|
|
||||||
|
# Figure out how many scaling axes are negative.
|
||||||
|
# This is important for calculating the normals of the mesh.
|
||||||
|
_, _, scale = vertex_transform.decompose()
|
||||||
|
negative_scaling_axes = sum([1 for x in scale if x < 0])
|
||||||
|
should_invert_normals = negative_scaling_axes % 2 == 1
|
||||||
|
|
||||||
|
loop_triangle_index_order = (2, 1, 0) if should_invert_normals else (0, 1, 2)
|
||||||
|
|
||||||
# Faces
|
# Faces
|
||||||
for face_index, loop_triangle in enumerate(mesh_data.loop_triangles):
|
for face_index, loop_triangle in enumerate(mesh_data.loop_triangles):
|
||||||
face = ASEFace()
|
face = ASEFace()
|
||||||
face.a = geometry_object.vertex_offset + mesh_data.loops[loop_triangle.loops[0]].vertex_index
|
face.a, face.b, face.c = map(lambda j: geometry_object.vertex_offset + mesh_data.loops[loop_triangle.loops[j]].vertex_index, loop_triangle_index_order)
|
||||||
face.b = geometry_object.vertex_offset + mesh_data.loops[loop_triangle.loops[1]].vertex_index
|
|
||||||
face.c = geometry_object.vertex_offset + mesh_data.loops[loop_triangle.loops[2]].vertex_index
|
|
||||||
if not geometry_object.is_collision:
|
if not geometry_object.is_collision:
|
||||||
face.material_index = material_indices[loop_triangle.material_index]
|
face.material_index = material_indices[loop_triangle.material_index]
|
||||||
# The UT2K4 importer only accepts 32 smoothing groups. Anything past this completely mangles the
|
# The UT2K4 importer only accepts 32 smoothing groups. Anything past this completely mangles the
|
||||||
@@ -103,29 +112,21 @@ class ASEBuilder(object):
|
|||||||
# This may result in bad calculated normals on export in rare cases. For example, if a face with a
|
# This may result in bad calculated normals on export in rare cases. For example, if a face with a
|
||||||
# smoothing group of 3 is adjacent to a face with a smoothing group of 35 (35 % 32 == 3), those faces
|
# smoothing group of 3 is adjacent to a face with a smoothing group of 35 (35 % 32 == 3), those faces
|
||||||
# will be treated as part of the same smoothing group.
|
# will be treated as part of the same smoothing group.
|
||||||
face.smoothing = (poly_groups[loop_triangle.polygon_index] - 1) % 32
|
face.smoothing = (poly_groups[loop_triangle.polygon_index] - 1) % SMOOTHING_GROUP_MAX
|
||||||
geometry_object.faces.append(face)
|
geometry_object.faces.append(face)
|
||||||
|
|
||||||
# Figure out how many scaling axes are negative.
|
|
||||||
# This is important for calculating the normals of the mesh.
|
|
||||||
_, _, scale = vertex_transform.decompose()
|
|
||||||
negative_scaling_axes = sum([1 for x in scale if x < 0])
|
|
||||||
should_invert_normals = negative_scaling_axes % 2 == 1
|
|
||||||
|
|
||||||
if should_invert_normals:
|
|
||||||
for face in geometry_object.faces:
|
|
||||||
face.a, face.c = face.c, face.a
|
|
||||||
|
|
||||||
if not geometry_object.is_collision:
|
if not geometry_object.is_collision:
|
||||||
# Normals
|
# Normals
|
||||||
for face_index, loop_triangle in enumerate(mesh_data.loop_triangles):
|
for face_index, loop_triangle in enumerate(mesh_data.loop_triangles):
|
||||||
face_normal = ASEFaceNormal()
|
face_normal = ASEFaceNormal()
|
||||||
face_normal.normal = loop_triangle.normal
|
face_normal.normal = loop_triangle.normal
|
||||||
face_normal.vertex_normals = []
|
face_normal.vertex_normals = []
|
||||||
for i in range(3):
|
for i in loop_triangle_index_order:
|
||||||
vertex_normal = ASEVertexNormal()
|
vertex_normal = ASEVertexNormal()
|
||||||
vertex_normal.vertex_index = geometry_object.vertex_offset + mesh_data.loops[loop_triangle.loops[i]].vertex_index
|
vertex_normal.vertex_index = geometry_object.vertex_offset + mesh_data.loops[loop_triangle.loops[i]].vertex_index
|
||||||
vertex_normal.normal = loop_triangle.split_normals[i]
|
vertex_normal.normal = loop_triangle.split_normals[i]
|
||||||
|
if should_invert_normals:
|
||||||
|
vertex_normal.normal = (-Vector(vertex_normal.normal)).to_tuple()
|
||||||
face_normal.vertex_normals.append(vertex_normal)
|
face_normal.vertex_normals.append(vertex_normal)
|
||||||
geometry_object.face_normals.append(face_normal)
|
geometry_object.face_normals.append(face_normal)
|
||||||
|
|
||||||
@@ -140,18 +141,9 @@ class ASEBuilder(object):
|
|||||||
|
|
||||||
# Texture Faces
|
# Texture Faces
|
||||||
for loop_triangle in mesh_data.loop_triangles:
|
for loop_triangle in mesh_data.loop_triangles:
|
||||||
if should_invert_normals:
|
geometry_object.texture_vertex_faces.append(
|
||||||
geometry_object.texture_vertex_faces.append((
|
tuple(map(lambda l: geometry_object.texture_vertex_offset + loop_triangle.loops[l], loop_triangle_index_order))
|
||||||
geometry_object.texture_vertex_offset + loop_triangle.loops[2],
|
)
|
||||||
geometry_object.texture_vertex_offset + loop_triangle.loops[1],
|
|
||||||
geometry_object.texture_vertex_offset + loop_triangle.loops[0]
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
geometry_object.texture_vertex_faces.append((
|
|
||||||
geometry_object.texture_vertex_offset + loop_triangle.loops[0],
|
|
||||||
geometry_object.texture_vertex_offset + loop_triangle.loops[1],
|
|
||||||
geometry_object.texture_vertex_offset + loop_triangle.loops[2]
|
|
||||||
))
|
|
||||||
|
|
||||||
# Vertex Colors
|
# Vertex Colors
|
||||||
if len(mesh_data.vertex_colors) > 0:
|
if len(mesh_data.vertex_colors) > 0:
|
||||||
@@ -164,6 +156,10 @@ class ASEBuilder(object):
|
|||||||
geometry_object.texture_vertex_offset += len(mesh_data.loops)
|
geometry_object.texture_vertex_offset += len(mesh_data.loops)
|
||||||
geometry_object.vertex_offset = len(geometry_object.vertices)
|
geometry_object.vertex_offset = len(geometry_object.vertices)
|
||||||
|
|
||||||
|
context.window_manager.progress_update(object_index)
|
||||||
|
|
||||||
|
context.window_manager.progress_end()
|
||||||
|
|
||||||
if len(ase.geometry_objects) == 0:
|
if len(ase.geometry_objects) == 0:
|
||||||
raise ASEBuilderError('At least one mesh object must be selected')
|
raise ASEBuilderError('At least one mesh object must be selected')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user