|
@@ -1,15 +1,15 @@
|
|
|
"""Exports a normal triangle mesh"""
|
|
|
import logging
|
|
|
import bpy
|
|
|
-import bmesh
|
|
|
import mathutils
|
|
|
|
|
|
from .material import export_material
|
|
|
from ..structures import (
|
|
|
Array, NodeTemplate, InternalResource, Map, gamma_correct)
|
|
|
-from . import physics
|
|
|
-from . import armature
|
|
|
-from . import animation
|
|
|
+from .utils import MeshConverter, MeshResourceKey
|
|
|
+from .physics import has_physics, export_physics_properties
|
|
|
+from .armature import generate_bones_mapping
|
|
|
+from .animation import export_animation_data
|
|
|
|
|
|
MAX_BONE_PER_VERTEX = 4
|
|
|
|
|
@@ -21,8 +21,8 @@ def export_mesh_node(escn_file, export_settings, obj, parent_gd_node):
|
|
|
# If this mesh object has physics properties, we need to export them first
|
|
|
# because they need to be higher in the scene-tree
|
|
|
|
|
|
- if physics.has_physics(obj):
|
|
|
- parent_gd_node = physics.export_physics_properties(
|
|
|
+ if has_physics(obj):
|
|
|
+ parent_gd_node = export_physics_properties(
|
|
|
escn_file, export_settings, obj, parent_gd_node
|
|
|
)
|
|
|
# skip wire mesh which is used as collision mesh
|
|
@@ -59,7 +59,7 @@ def export_mesh_node(escn_file, export_settings, obj, parent_gd_node):
|
|
|
|
|
|
# Transform of rigid mesh is moved up to its collision
|
|
|
# shapes.
|
|
|
- if physics.has_physics(obj):
|
|
|
+ if has_physics(obj):
|
|
|
mesh_node['transform'] = mathutils.Matrix.Identity(4)
|
|
|
else:
|
|
|
mesh_node['transform'] = obj.matrix_local
|
|
@@ -69,25 +69,13 @@ def export_mesh_node(escn_file, export_settings, obj, parent_gd_node):
|
|
|
# export shape key animation
|
|
|
if (export_settings['use_export_shape_key'] and
|
|
|
has_shape_keys(obj.data)):
|
|
|
- animation.export_animation_data(
|
|
|
+ export_animation_data(
|
|
|
escn_file, export_settings, mesh_node,
|
|
|
obj.data.shape_keys, 'shapekey')
|
|
|
|
|
|
return mesh_node
|
|
|
|
|
|
|
|
|
-def triangulate_mesh(mesh):
|
|
|
- """Triangulate a mesh"""
|
|
|
- tri_mesh = bmesh.new()
|
|
|
- tri_mesh.from_mesh(mesh)
|
|
|
- bmesh.ops.triangulate(
|
|
|
- tri_mesh, faces=tri_mesh.faces, quad_method="ALTERNATE")
|
|
|
- tri_mesh.to_mesh(mesh)
|
|
|
- tri_mesh.free()
|
|
|
-
|
|
|
- mesh.update(calc_loop_triangles=True)
|
|
|
-
|
|
|
-
|
|
|
def fix_vertex(vtx):
|
|
|
"""Changes a single position vector from y-up to z-up"""
|
|
|
return mathutils.Vector((vtx.x, vtx.z, -vtx.y))
|
|
@@ -119,85 +107,12 @@ def export_object_link_material(escn_file, export_settings, mesh_object,
|
|
|
)
|
|
|
|
|
|
|
|
|
-def get_applicable_modifiers(obj, export_settings):
|
|
|
- """Returns a list of all the modifiers that'll be applied to the final
|
|
|
- godot mesh"""
|
|
|
- ignore_modifiers = []
|
|
|
- if not export_settings['use_mesh_modifiers']:
|
|
|
- return []
|
|
|
- if "ARMATURE" in export_settings['object_types']:
|
|
|
- ignore_modifiers.append(bpy.types.ArmatureModifier)
|
|
|
- ignore_modifiers = tuple(ignore_modifiers)
|
|
|
- return [m for m in obj.modifiers if not isinstance(m, ignore_modifiers)
|
|
|
- and m.show_viewport]
|
|
|
-
|
|
|
-
|
|
|
-def record_modifier_config(obj):
|
|
|
- """Returns modifiers viewport visibility config"""
|
|
|
- modifier_config_cache = []
|
|
|
- for mod in obj.modifiers:
|
|
|
- modifier_config_cache.append(mod.show_viewport)
|
|
|
- return modifier_config_cache
|
|
|
-
|
|
|
-
|
|
|
-def restore_modifier_config(obj, modifier_config_cache):
|
|
|
- """Applies modifiers viewport visibility config"""
|
|
|
- for i, mod in enumerate(obj.modifiers):
|
|
|
- mod.show_viewport = modifier_config_cache[i]
|
|
|
-
|
|
|
-
|
|
|
def has_shape_keys(object_data):
|
|
|
"""Determine if object data has shape keys"""
|
|
|
return (hasattr(object_data, "shape_keys") and
|
|
|
object_data.shape_keys is not None)
|
|
|
|
|
|
|
|
|
-class MeshResourceKey:
|
|
|
- """Produces a key based on an mesh object's data, every different
|
|
|
- Mesh Resource would have a unique key"""
|
|
|
-
|
|
|
- def __init__(self, rsc_type, obj, export_settings):
|
|
|
- mesh_data = obj.data
|
|
|
-
|
|
|
- # Resource type included because same blender mesh may be used as
|
|
|
- # MeshResource or CollisionShape, but they are different resource
|
|
|
- gd_rsc_type = rsc_type
|
|
|
-
|
|
|
- # Here collect info of all the modifiers applied on the mesh.
|
|
|
- # Modifiers along with the original mesh data would determine
|
|
|
- # the evaluated mesh.
|
|
|
- mod_info_list = list()
|
|
|
- for modifier in get_applicable_modifiers(obj, export_settings):
|
|
|
- # Modifier name indicates its type, its an identifier
|
|
|
- mod_info_list.append(modifier.name)
|
|
|
-
|
|
|
- # First property is always 'rna_type', skip it
|
|
|
- for prop in modifier.bl_rna.properties.keys()[1:]:
|
|
|
- # Note that Property may be `BoolProperty`,
|
|
|
- # `CollectionProperty`, `EnumProperty`, `FloatProperty`,
|
|
|
- # `IntProperty`, `PointerProperty`, `StringProperty`"
|
|
|
- # Most of them are primary type when accessed with `getattr`,
|
|
|
- # so they are fine to be hashed.
|
|
|
- # For `PointerProperty`, it is mostly an bpy.types.ID, hash it
|
|
|
- # would get its python object identifier, which is also good.
|
|
|
- # For `CollectionProperty`, it would make more sense to
|
|
|
- # traversal it, however, we cut down it here to allow
|
|
|
- # some of mesh resource not be shared because of simplicity
|
|
|
- mod_info_list.append(getattr(modifier, prop))
|
|
|
-
|
|
|
- self._data = tuple([mesh_data, gd_rsc_type] + mod_info_list)
|
|
|
- # Precalculate the hash now for better efficiency later
|
|
|
- self._hash = hash(self._data)
|
|
|
-
|
|
|
- def __hash__(self):
|
|
|
- return self._hash
|
|
|
-
|
|
|
- def __eq__(self, other):
|
|
|
- # pylint: disable=protected-access
|
|
|
- return (self.__class__ == other.__class__ and
|
|
|
- self._data == other._data)
|
|
|
-
|
|
|
-
|
|
|
class ArrayMeshResource(InternalResource):
|
|
|
"""Godot ArrayMesh resource, containing surfaces"""
|
|
|
|
|
@@ -232,8 +147,7 @@ class ArrayMeshResourceExporter:
|
|
|
if armature_obj is None:
|
|
|
return
|
|
|
|
|
|
- bones_mapping = armature.generate_bones_mapping(export_settings,
|
|
|
- armature_obj)
|
|
|
+ bones_mapping = generate_bones_mapping(export_settings, armature_obj)
|
|
|
for bl_bone_name, gd_bone_id in bones_mapping.items():
|
|
|
group = self.object.vertex_groups.get(bl_bone_name)
|
|
|
if group is not None:
|
|
@@ -310,7 +224,7 @@ class ArrayMeshResourceExporter:
|
|
|
)
|
|
|
|
|
|
mesh_converter = MeshConverter(self.object, export_settings)
|
|
|
- shape_key_mesh = mesh_converter.to_mesh(index)
|
|
|
+ shape_key_mesh = mesh_converter.to_mesh(shape_key_index=index)
|
|
|
|
|
|
surfaces_morph_data = self.intialize_surfaces_morph_data(surfaces)
|
|
|
|
|
@@ -675,68 +589,3 @@ class Vertex:
|
|
|
self.uv = []
|
|
|
self.bones = []
|
|
|
self.weights = []
|
|
|
-
|
|
|
-
|
|
|
-class MeshConverter:
|
|
|
- """MeshConverter evaulates and converts objects to meshes, triangulates
|
|
|
- and calculates tangents"""
|
|
|
-
|
|
|
- def __init__(self, obj, export_settings):
|
|
|
- self.object = obj
|
|
|
- self.eval_object = None
|
|
|
- self.use_mesh_modifiers = export_settings["use_mesh_modifiers"]
|
|
|
- self.use_export_shape_key = export_settings['use_export_shape_key']
|
|
|
- self.has_tangents = False
|
|
|
-
|
|
|
- def to_mesh(self, shape_key_index=0, calculate_tangents=True):
|
|
|
- """Evaluates object & converts to final mesh, ready for export.
|
|
|
- The mesh is only temporary, call to_mesh_clear() afterwards."""
|
|
|
- # set shape key to basis key which would have index 0
|
|
|
- orig_shape_key_index = self.object.active_shape_key_index
|
|
|
- self.object.show_only_shape_key = True
|
|
|
- self.object.active_shape_key_index = shape_key_index
|
|
|
-
|
|
|
- self.eval_object = self.object
|
|
|
-
|
|
|
- modifier_config_cache = None
|
|
|
- if not self.use_mesh_modifiers:
|
|
|
- modifier_config_cache = record_modifier_config(self.object)
|
|
|
- for mod in self.object.modifiers:
|
|
|
- mod.show_viewport = False
|
|
|
-
|
|
|
- depsgraph = bpy.context.view_layer.depsgraph
|
|
|
- depsgraph.update()
|
|
|
- self.eval_object = self.object.evaluated_get(depsgraph)
|
|
|
-
|
|
|
- # These parameters are required for preserving vertex groups.
|
|
|
- mesh = self.eval_object.to_mesh(
|
|
|
- preserve_all_data_layers=True, depsgraph=depsgraph)
|
|
|
-
|
|
|
- if not self.use_mesh_modifiers:
|
|
|
- restore_modifier_config(self.object, modifier_config_cache)
|
|
|
-
|
|
|
- self.has_tangents = False
|
|
|
-
|
|
|
- # 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:
|
|
|
- triangulate_mesh(mesh)
|
|
|
-
|
|
|
- self.has_tangents = mesh.uv_layers and mesh.polygons
|
|
|
- if calculate_tangents:
|
|
|
- if self.has_tangents:
|
|
|
- mesh.calc_tangents()
|
|
|
- else:
|
|
|
- mesh.calc_normals_split()
|
|
|
-
|
|
|
- self.object.show_only_shape_key = False
|
|
|
- self.object.active_shape_key_index = orig_shape_key_index
|
|
|
-
|
|
|
- return mesh
|
|
|
-
|
|
|
- def to_mesh_clear(self):
|
|
|
- """Clears the temporary generated mesh from memory"""
|
|
|
- if self.object is None:
|
|
|
- return
|
|
|
- self.eval_object.to_mesh_clear()
|
|
|
- self.object = self.eval_object = None
|