""" Utilities for operating on msh_model objects. """ from typing import List from .msh_model import * from .msh_utilities import * import mathutils import math from mathutils import Vector, Matrix # Convert model with geometry to null. # Currently not used, but could be necessary in the future. def make_null(model : Model): model.model_type = ModelType.NULL bone_map = None geometry = None # I think this is all we need to check for to avoid # common ZE/ZETools crashes... def validate_geometry_segment(segment : GeometrySegment) -> bool: if not segment.positions or not segment.triangle_strips: return False else: return True def inject_dummy_data(model : Model): """ Adds a triangle and material to the model (scene root). Needed to export zenasst-compatible skeletons. """ model.hidden = True dummy_seg = GeometrySegment() dummy_seg.material_name = "" dummy_seg.positions = [Vector((0.0,0.1,0.0)), Vector((0.1,0.0,0.0)), Vector((0.0,0.0,0.1))] dummy_seg.normals = [Vector((0.0,1.0,0.0)), Vector((1.0,0.0,0.0)), Vector((0.0,0.0,1.0))] dummy_seg.texcoords = [Vector((0.1,0.1)), Vector((0.2,0.2)), Vector((0.3,0.3))] tri = [[0,1,2]] dummy_seg.triangles = tri dummy_seg.polygons = tri dummy_seg.triangle_strips = tri model.geometry = [dummy_seg] model.model_type = ModelType.STATIC def convert_vector_space(vec: Vector) -> Vector: return Vector((-vec.x, vec.z, vec.y)) def convert_scale_space(vec: Vector) -> Vector: return Vector(vec.xzy) def convert_rotation_space(quat: Quaternion) -> Quaternion: return Quaternion((-quat.w, quat.x, -quat.z, -quat.y)) def model_transform_to_matrix(transform: ModelTransform): return Matrix.Translation(convert_vector_space(transform.translation)) @ convert_rotation_space(transform.rotation).to_matrix().to_4x4() def scale_segments(scale: Vector, segments: List[GeometrySegment]): """ Scales are positions in the GeometrySegment list. """ for segment in segments: segment.positions = [mul_vec(pos, scale) for pos in segment.positions] def get_model_world_matrix(model: Model, models: List[Model]) -> Matrix: """ Gets a Blender Matrix for transforming the model into world space. """ transform_stack: List[ModelTransform] = [model.transform] transform_stack.extend((parent.transform for parent in get_model_ancestors(model, models))) world_matrix: Matrix = Matrix() for transform in reversed(transform_stack): translation_matrix = Matrix.Translation(transform.translation) rotation_matrix = transform.rotation.to_matrix().to_4x4() world_matrix = world_matrix @ (translation_matrix @ rotation_matrix) return world_matrix def sort_by_parent(models: List[Model]) -> List[Model]: """ Sorts a Model list so that models are ordered by their parent. Required for some tools to be able to load .msh files. """ sorted_models: List[Model] = [] for root in get_root_models(models): def add_children(model: Model): nonlocal sorted_models for child in get_model_children(model, models): sorted_models.append(child) add_children(child) sorted_models.append(root) add_children(root) return sorted_models def reparent_model_roots(models: List[Model]) -> List[Model]: """ Reparents all root models in a list to a new empty node. """ new_root: Model = Model() new_root.name = get_unique_scene_root_name(models) for model in models: if not model.parent: model.parent = new_root.name models.insert(0, new_root) return models def has_multiple_root_models(models: List[Model]) -> bool: """ Checks if a list of Model objects has multiple roots. """ return sum(1 for root in get_root_models(models)) > 1 def get_root_models(models: List[Model]) -> Model: """ Generator. Returns all Model objects in a list with no parent. """ for model in models: if model.parent == "": yield model def get_model_children(parent: Model, models: List[Model]) -> Model: """ Generator. Returns all Model objects in a list whose parent is the supplied model. """ for model in models: if parent.name == model.parent: yield model def get_model_ancestors(child: Model, models: List[Model]) -> Model: """ Generator. Yields the parent for a model, then yields the parent's parent, repeating until at the root model. """ for model in models: if child.parent == model.name: child = model yield model def get_unique_scene_root_name(models: List[Model]) -> Model: """ Returns a unique model name of the form of either "SceneRoot" or "SceneRoot{i}". """ name: str = "SceneRoot" if is_model_name_unused(name, models): return name for i in range(len(models) + 1): name = f"SceneRoot{i}" if is_model_name_unused(name, models): return name return name def is_model_name_unused(name: str, models: List[Model]) -> bool: """ Checks if there is no Model using a name in a list of models. """ for model in models: if model.name == name: return False return True