Skip to content

Commit

Permalink
Only triangulate n-gons when necessary for calc_tangents (godotengine…
Browse files Browse the repository at this point in the history
…#379)

* Export from mesh.loop_triangles instead of mesh.polygons

This shouldn't change anything atm since the mesh is triangulated
beforehand, but this will allow us a non-triangulated mesh in the
future, which avoids problems where the mesh triangulation is wrong;
loop_triangles is always the "true" triangulation.

* Use mesh.loop_triangles for concave_shape

No longer need to ask the mesh converter to triangulate.

* Triangulate meshes if necessary for tangents

This means it is no longer required to ask for triangulation if
asking for tangents.

* Remove triangulate argument; now only triangulate for tangents

It is now as if it were always False. Triangulation will only
occur if needed for calc_tangents.

This was only True in mesh.py. Now that it is False there,
triangulate_mesh will be skipped for any meshes without (>4)-gons,
which solves issues of bad triangulation/damaged normals for these
meshes.

* Only triangulate n-gons when we can't calc_tangents

This should confine damage caused by triangulation to n-gons only.

* Warn when triangulating that exported n-gons may look wrong

* Test: make update-examples
  • Loading branch information
scurest committed Nov 25, 2020
1 parent 27ffa28 commit a353957
Show file tree
Hide file tree
Showing 58 changed files with 425 additions and 421 deletions.
26 changes: 12 additions & 14 deletions io_scene_godot/converters/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,17 @@ def export_morphs(self, export_settings, surfaces):

surfaces_morph_data = self.intialize_surfaces_morph_data(surfaces)

for face in shape_key_mesh.polygons:
shape_key_mesh.calc_loop_triangles()

for tri in shape_key_mesh.loop_triangles:
surface_index = self.mesh_resource.get_surface_id(
face.material_index
tri.material_index
)

surface = surfaces[surface_index]
morph = surfaces_morph_data[surface_index]

for loop_id in range(face.loop_total):
loop_index = face.loop_start + loop_id
for loop_index in tri.loops:
new_vert = Vertex.create_from_mesh_loop(
shape_key_mesh,
loop_index,
Expand All @@ -261,24 +262,24 @@ def generate_surfaces(self, escn_file, export_settings, mesh):
"""
surfaces = []

for face_index in range(len(mesh.polygons)):
face = mesh.polygons[face_index]
mesh.calc_loop_triangles()

for tri in mesh.loop_triangles:
# Find a surface that matches the material, otherwise create a new
# surface for it
surface_index = self.mesh_resource.get_surface_id(
face.material_index
tri.material_index
)
if surface_index is None:
surface_index = len(surfaces)
self.mesh_resource.set_surface_id(
face.material_index, surface_index
tri.material_index, surface_index
)
surface = Surface()
surface.id = surface_index
surfaces.append(surface)
if mesh.materials:
mat = mesh.materials[face.material_index]
mat = mesh.materials[tri.material_index]
if (mat is not None and
export_settings['material_mode'] != 'NONE'):
surface.material = export_material(
Expand All @@ -291,9 +292,7 @@ def generate_surfaces(self, escn_file, export_settings, mesh):
surface = surfaces[surface_index]
vertex_indices = []

for loop_id in range(face.loop_total):
loop_index = face.loop_start + loop_id

for loop_index in tri.loops:
new_vert = Vertex.create_from_mesh_loop(
mesh,
loop_index,
Expand All @@ -312,8 +311,7 @@ def generate_surfaces(self, escn_file, export_settings, mesh):

vertex_indices.append(vertex_index)

if len(vertex_indices) > 2: # Only triangles and above
surface.vertex_data.indices.append(vertex_indices)
surface.vertex_data.indices.append(vertex_indices)

if (export_settings['use_export_shape_key'] and
has_shape_keys(self.object.data)):
Expand Down
7 changes: 3 additions & 4 deletions io_scene_godot/converters/physics.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ def generate_convex_shape(escn_file, export_settings, bl_object):
col_shape = None
mesh_converter = MeshConverter(bl_object, export_settings)
mesh = mesh_converter.to_mesh(
triangulate=False,
preserve_vertex_groups=False,
calculate_tangents=False
)
Expand Down Expand Up @@ -178,14 +177,14 @@ def generate_concave_shape(escn_file, export_settings, bl_object):
col_shape = None
mesh_converter = MeshConverter(bl_object, export_settings)
mesh = mesh_converter.to_mesh(
triangulate=True,
preserve_vertex_groups=False,
calculate_tangents=False
)
if mesh is not None and mesh.polygons:
vert_array = list()
for poly in mesh.polygons:
for vert_id in reversed(poly.vertices):
mesh.calc_loop_triangles()
for tri in mesh.loop_triangles:
for vert_id in reversed(tri.vertices):
vert_array.append(mesh.vertices[vert_id].co)

col_shape = InternalResource("ConcavePolygonShape", mesh.name)
Expand Down
25 changes: 16 additions & 9 deletions io_scene_godot/converters/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Util functions and structs shared by multiple resource converters"""

import logging
import bpy
import bmesh

Expand Down Expand Up @@ -31,12 +32,12 @@ def restore_modifier_config(obj, modifier_config_cache):
mod.show_viewport = modifier_config_cache[i]


def triangulate_mesh(mesh):
"""Triangulate a mesh"""
def triangulate_ngons(mesh):
"""Triangulate n-gons in a mesh"""
tri_mesh = bmesh.new()
tri_mesh.from_mesh(mesh)
bmesh.ops.triangulate(
tri_mesh, faces=tri_mesh.faces, quad_method="ALTERNATE")
ngons = [face for face in tri_mesh.faces if len(face.verts) > 4]
bmesh.ops.triangulate(tri_mesh, faces=ngons, quad_method="ALTERNATE")
tri_mesh.to_mesh(mesh)
tri_mesh.free()
if bpy.app.version[1] > 80:
Expand Down Expand Up @@ -117,7 +118,7 @@ def __init__(self, obj, export_settings):
self.use_export_shape_key = export_settings['use_export_shape_key']
self.has_tangents = False

def to_mesh(self, triangulate=True, preserve_vertex_groups=True,
def to_mesh(self, preserve_vertex_groups=True,
calculate_tangents=True, shape_key_index=0):
"""Evaluates object & converts to final mesh, ready for export.
The mesh is only temporary, call to_mesh_clear() afterwards."""
Expand Down Expand Up @@ -152,13 +153,19 @@ def to_mesh(self, triangulate=True, preserve_vertex_groups=True,
# mesh result can be none if the source geometry has no faces, so we
# need to consider this if we want a robust exporter.
if mesh is not None:
if triangulate:
triangulate_mesh(mesh)

self.has_tangents = bool(mesh.uv_layers) and bool(mesh.polygons)
if calculate_tangents:
if self.has_tangents:
mesh.calc_tangents()
try:
mesh.calc_tangents()
except RuntimeError:
# Mesh must have n-gons
logging.warning(
"Mesh had n-gons and had to be triangulated to "
"calculate tangents; n-gons may look wrong."
)
triangulate_ngons(mesh)
mesh.calc_tangents()
else:
mesh.calc_normals_split()

Expand Down
Loading

0 comments on commit a353957

Please sign in to comment.