Source code for dna_viewer.dnalib.dnalib

from typing import List, Optional, Tuple

from dna import BinaryStreamReader as DNAReader
from dna import DataLayer_All, FileStream, Status

from ..common import DNAViewerError
from ..model import UV, BlendShape, Joint, Layout, Point3
from .behavior import Behavior
from .geometry import Geometry
from .layer import Layer


[docs]class DNA(Behavior, Geometry): """ A class used for accessing data in DNA file. @type dna_path: str @param dna_path: The path of the DNA file @type layers: Optional[List[Layer]] @param layers: List of parts of DNA to be loaded. If noting is passed, whole DNA is going to be loaded. Same as passing Layer.all. """ def __init__(self, dna_path: str, layers: Optional[List[Layer]] = None) -> None: self.path = dna_path self.reader = self.create_reader(dna_path) layers = layers or [Layer.all] Behavior.__init__(self, self.reader, layers) Geometry.__init__(self, self.reader, layers) self.read()
[docs] def create_reader(self, dna_path: str) -> DNAReader: """ Creates a stream reader needed for reading values from the DNA file. @type dna_path: str @param dna_path: The path of the DNA file @rtype: DNA @returns: The reader needed for reading values from the DNA file """ stream = FileStream( dna_path, FileStream.AccessMode_Read, FileStream.OpenMode_Binary ) reader = DNAReader(stream, DataLayer_All) reader.read() if not Status.isOk(): status = Status.get() raise RuntimeError(f"Error loading DNA: {status.message}") return reader
[docs] def is_read(self) -> bool: return Behavior.is_read(self) and Geometry.is_read(self)
[docs] def read(self) -> None: if not self.is_read(): self.start_read() Behavior.read(self) Geometry.read(self)
[docs] def read_all_neutral_joints(self) -> List[Joint]: joints = [] for i in range(self.get_joint_count()): name = self.get_joint_name(i) translation = self.get_neutral_joint_translation(i) orientation = self.get_neutral_joint_rotation(i) parent_name = self.get_joint_name(self.get_joint_parent_index(i)) joint = Joint( name=name, translation=translation, orientation=orientation, parent_name=parent_name, ) joints.append(joint) return joints
[docs] def get_all_skin_weights_joint_indices_for_mesh( self, mesh_index: int ) -> List[List[int]]: return self.geometry_meshes[mesh_index].skin_weights.joint_indices
[docs] def get_blend_shape_target_deltas_with_vertex_id( self, mesh_index: int, blend_shape_target_index: int ) -> List[Tuple[int, Point3]]: blend_shape = self.geometry_meshes[mesh_index].blend_shapes[ blend_shape_target_index ] indices = list(blend_shape.deltas.keys()) deltas: List[Point3] = [] for i in indices: deltas.append(blend_shape.deltas[i]) if not deltas: return [] return list(zip(indices, deltas))
[docs] def get_all_skin_weights_values_for_mesh( self, mesh_index: int ) -> List[List[float]]: skin_weight_values = [] mesh = self.geometry_meshes[mesh_index] for i in range(len(mesh.topology.positions)): skin_weight_values.append(mesh.skin_weights.values[i]) return skin_weight_values
[docs] def get_skin_weight_matrix_for_mesh( self, mesh_index: int ) -> List[List[Tuple[int, float]]]: vertex_position_count = len(self.geometry_meshes[mesh_index].topology.positions) joint_indices = self.get_all_skin_weights_joint_indices_for_mesh(mesh_index) if len(joint_indices) != vertex_position_count: raise DNAViewerError( "Number of joint indices and vertex count don't match!" ) skin_weight_values = self.get_all_skin_weights_values_for_mesh(mesh_index) if len(skin_weight_values) != vertex_position_count: raise DNAViewerError( "Number of skin weight values and vertex count don't match!" ) if len(joint_indices) != len(skin_weight_values): raise DNAViewerError( "Number of skin weight values and joint indices count don't match for vertex!" ) weight_matrix = [] for indices, values in zip(joint_indices, skin_weight_values): if not indices: raise DNAViewerError( "JointIndexArray for vertex can't be less than one!" ) vertex_weights = [] for joint_index, skin_weight_value in zip(indices, values): vertex_weights.append((joint_index, skin_weight_value)) weight_matrix.append(vertex_weights) return weight_matrix
[docs] def get_vertex_texture_coordinates_for_mesh(self, mesh_index: int) -> List[UV]: return self.geometry_meshes[mesh_index].topology.texture_coordinates
[docs] def get_vertex_positions_for_mesh_index(self, mesh_index: int) -> List[Point3]: return self.geometry_meshes[mesh_index].topology.positions
[docs] def get_vertex_layout_positions_for_mesh_index(self, mesh_index: int) -> List[int]: return [ item.position_index for item in self.geometry_meshes[mesh_index].topology.layouts ]
[docs] def get_faces(self, mesh_index: int) -> List[List[int]]: return self.geometry_meshes[mesh_index].topology.face_vertex_layouts
[docs] def get_polygon_faces_and_connects( self, mesh_index: int = None, dna_faces: List[List[int]] = None, dna_vertex_layout_positions: List[int] = None, ) -> Tuple[List[int], List[int]]: if mesh_index is None: if None in (dna_faces, dna_vertex_layout_positions): raise DNAViewerError( "get_polygon_faces_and_connects -> Must provide either mesh_index or dna_faces and dna_vertex_layout_positions" ) if dna_faces is None: dna_faces = self.get_faces(mesh_index) if dna_vertex_layout_positions is None: dna_vertex_layout_positions = ( self.get_vertex_layout_positions_for_mesh_index(mesh_index) ) polygon_faces = [] polygon_connects = [] for vertices_layout_index_array in dna_faces: polygon_faces.append(len(vertices_layout_index_array)) for vertex_layout_index_array in vertices_layout_index_array: polygon_connects.append( dna_vertex_layout_positions[vertex_layout_index_array] ) return polygon_faces, polygon_connects
[docs] def get_layouts_for_mesh_index(self, mesh_index: int) -> List[Layout]: return self.geometry_meshes[mesh_index].topology.layouts
[docs] def get_texture_coordinate_index(self, mesh_index: int, layout_id: int) -> int: return ( self.geometry_meshes[mesh_index] .topology.layouts[layout_id] .texture_coordinate_index )
[docs] def has_blend_shapes(self, mesh_index: int) -> bool: return ( len([bs.channel for bs in self.geometry_meshes[mesh_index].blend_shapes]) > 0 )
[docs] def get_lowest_lod_containing_meshes( self, mesh_indices: List[int] ) -> Optional[int]: unique_mesh_indices = set(mesh_indices) for lod in range(self.get_lod_count()): if any(list(unique_mesh_indices & set(self.get_mesh_indices_for_lod(lod)))): return lod return None
[docs] def get_meshes_by_lods(self, mesh_indices: List[int]) -> List[List[int]]: result_list = [] for lod in range(self.get_lod_count()): temp = list(set(mesh_indices) & set(self.get_mesh_indices_for_lod(lod))) result_list.append(temp) return result_list
[docs] def get_all_meshes_grouped_by_lod(self) -> List[List[int]]: """ Gets the list of list of mesh indices grouped by the lod number. @type dna: DNA @param dna: Instance of DNA. @rtype: List[List[int]] @returns: The list of list of mesh indices grouped by the lod number """ result: List[List[int]] = [] for lod in range(self.get_lod_count()): mesh_indices = [] for mesh_index in self.get_mesh_indices_for_lod(lod): mesh_indices.append(mesh_index) result.append(mesh_indices) return result
[docs] def get_blend_shapes(self, mesh_index: int) -> List[BlendShape]: return self.geometry_meshes[mesh_index].blend_shapes
[docs] def get_mesh_id_from_mesh_name(self, mesh_name: str) -> Optional[int]: return self.meshes_mapping.get(mesh_name, None)