Base
This commit is contained in:
0
scripts/dnalib/__init__.py
Normal file
0
scripts/dnalib/__init__.py
Normal file
371
scripts/dnalib/behavior.py
Normal file
371
scripts/dnalib/behavior.py
Normal file
@@ -0,0 +1,371 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Optional, cast
|
||||
|
||||
from dna import BinaryStreamReader as DNAReader
|
||||
|
||||
from .definition import Definition
|
||||
from .layer import Layer
|
||||
|
||||
|
||||
class Behavior(Definition):
|
||||
"""
|
||||
@type reader: BinaryStreamReader
|
||||
@param reader: The binary stream reader being used
|
||||
|
||||
@type gui_to_raw: ConditionalTable
|
||||
@param gui_to_raw: Mapping data about gui to raw values
|
||||
|
||||
@type psd: PSDMatrix
|
||||
@param psd: The data representing Pose Space Deformation
|
||||
|
||||
@type blend_shapes: BlendShapesData
|
||||
@param blend_shapes: The data representing blend shapes
|
||||
|
||||
@type animated_maps: AnimatedMapsConditionalTable
|
||||
@param animated_maps: The data representing animated maps
|
||||
|
||||
@type joints: JointGroups
|
||||
@param joints: The data representing joints
|
||||
"""
|
||||
|
||||
def __init__(self, reader: DNAReader, layers: Optional[List[Layer]]) -> None:
|
||||
super().__init__(reader, layers)
|
||||
|
||||
self.gui_to_raw = ConditionalTable()
|
||||
self.psd = PSDMatrix()
|
||||
self.blend_shapes = BlendShapesData()
|
||||
self.animated_maps_conditional_table = AnimatedMapsConditionalTable()
|
||||
self.joint_groups = JointGroups()
|
||||
self.behavior_read = False
|
||||
|
||||
def start_read(self) -> None:
|
||||
super().start_read()
|
||||
self.behavior_read = False
|
||||
|
||||
def is_read(self) -> bool:
|
||||
return super().is_read() and self.behavior_read
|
||||
|
||||
def read(self) -> None:
|
||||
"""
|
||||
Starts reading in the behavior part of the DNA
|
||||
"""
|
||||
super().read()
|
||||
|
||||
if not self.behavior_read and self.layer_enabled(Layer.behavior):
|
||||
self.behavior_read = True
|
||||
self.add_gui_to_raw()
|
||||
self.add_psd()
|
||||
self.add_joint_groups()
|
||||
self.add_blend_shapes()
|
||||
self.add_animated_maps_conditional_table()
|
||||
|
||||
def get_animated_map_lods(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getAnimatedMapLODs())
|
||||
|
||||
def get_animated_map_from_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getAnimatedMapFromValues())
|
||||
|
||||
def get_animated_map_to_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getAnimatedMapToValues())
|
||||
|
||||
def get_animated_map_slope_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getAnimatedMapSlopeValues())
|
||||
|
||||
def get_animated_map_cut_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getAnimatedMapCutValues())
|
||||
|
||||
def get_animated_map_input_indices(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getAnimatedMapInputIndices())
|
||||
|
||||
def get_animated_map_output_indices(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getAnimatedMapOutputIndices())
|
||||
|
||||
def get_gui_to_raw_from_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getGUIToRawFromValues())
|
||||
|
||||
def get_gui_to_raw_to_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getGUIToRawToValues())
|
||||
|
||||
def gget_gui_to_raw_slope_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getGUIToRawSlopeValues())
|
||||
|
||||
def get_gui_to_raw_cut_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getGUIToRawCutValues())
|
||||
|
||||
def get_gui_to_raw_input_indices(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getGUIToRawInputIndices())
|
||||
|
||||
def get_gui_to_raw_output_indices(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getGUIToRawOutputIndices())
|
||||
|
||||
def get_psd_count(self) -> int:
|
||||
return cast(int, self.reader.getPSDCount())
|
||||
|
||||
def get_psd_row_indices(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getPSDRowIndices())
|
||||
|
||||
def get_psd_column_indices(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getPSDColumnIndices())
|
||||
|
||||
def get_psd_values(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getPSDValues())
|
||||
|
||||
def get_blend_shape_channel_lods(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getBlendShapeChannelLODs())
|
||||
|
||||
def get_blend_shape_channel_input_indices(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getBlendShapeChannelInputIndices())
|
||||
|
||||
def get_blend_shape_channel_output_indices(self) -> List[int]:
|
||||
return cast(List[int], self.reader.getBlendShapeChannelOutputIndices())
|
||||
|
||||
def get_joint_row_count(self) -> int:
|
||||
return cast(int, self.reader.getJointRowCount())
|
||||
|
||||
def get_joint_column_count(self) -> int:
|
||||
return cast(int, self.reader.getJointColumnCount())
|
||||
|
||||
def get_joint_variable_attribute_indices(self) -> int:
|
||||
return cast(int, self.reader.getJointVariableAttributeIndices())
|
||||
|
||||
def get_joint_group_count(self) -> int:
|
||||
return cast(int, self.reader.getJointGroupCount())
|
||||
|
||||
def get_joint_group_logs(self, joint_group_index: int) -> List[int]:
|
||||
return cast(List[int], self.reader.getJointGroupLODs(joint_group_index))
|
||||
|
||||
def get_joint_group_input_indices(self, joint_group_index: int) -> List[int]:
|
||||
return cast(List[int], self.reader.getJointGroupInputIndices(joint_group_index))
|
||||
|
||||
def get_joint_group_output_indices(self, joint_group_index: int) -> List[int]:
|
||||
return cast(
|
||||
List[int], self.reader.getJointGroupOutputIndices(joint_group_index)
|
||||
)
|
||||
|
||||
def get_joint_group_values(self, joint_group_index: int) -> List[float]:
|
||||
return cast(List[float], self.reader.getJointGroupValues(joint_group_index))
|
||||
|
||||
def get_joint_group_joint_indices(self, joint_group_index: int) -> List[int]:
|
||||
return cast(List[int], self.reader.getJointGroupJointIndices(joint_group_index))
|
||||
|
||||
def add_gui_to_raw(self) -> None:
|
||||
"""Reads in the gui to raw mapping"""
|
||||
|
||||
self.reader.gui_to_raw = ConditionalTable(
|
||||
inputs=self.get_gui_to_raw_input_indices(),
|
||||
outputs=self.get_gui_to_raw_output_indices(),
|
||||
from_values=self.get_gui_to_raw_from_values(),
|
||||
to_values=self.get_gui_to_raw_to_values(),
|
||||
slope_values=self.gget_gui_to_raw_slope_values(),
|
||||
cut_values=self.get_gui_to_raw_cut_values(),
|
||||
)
|
||||
|
||||
def add_psd(self) -> None:
|
||||
"""Reads in the PSD part of the behavior"""
|
||||
|
||||
self.psd = PSDMatrix(
|
||||
count=self.get_psd_count(),
|
||||
rows=self.get_psd_row_indices(),
|
||||
columns=self.get_psd_column_indices(),
|
||||
values=self.get_psd_values(),
|
||||
)
|
||||
|
||||
def add_joint_groups(self) -> None:
|
||||
"""Reads in the joints part of the behavior"""
|
||||
|
||||
self.joint_groups.joint_row_count = self.reader.getJointRowCount()
|
||||
self.joint_groups.joint_column_count = self.reader.getJointColumnCount()
|
||||
for lod in range(self.get_lod_count()):
|
||||
self.joint_groups.joint_variable_attribute_indices.append(
|
||||
self.reader.getJointVariableAttributeIndices(lod)
|
||||
)
|
||||
for joint_group_index in range(self.get_joint_group_count()):
|
||||
self.joint_groups.joint_groups.append(
|
||||
JointGroup(
|
||||
lods=self.get_joint_group_logs(joint_group_index),
|
||||
inputs=self.get_joint_group_input_indices(joint_group_index),
|
||||
outputs=self.get_joint_group_output_indices(joint_group_index),
|
||||
values=self.get_joint_group_values(joint_group_index),
|
||||
joints=self.get_joint_group_joint_indices(joint_group_index),
|
||||
)
|
||||
)
|
||||
|
||||
def add_blend_shapes(self) -> None:
|
||||
"""Reads in the blend shapes part of the behavior"""
|
||||
|
||||
self.blend_shapes = BlendShapesData(
|
||||
lods=self.get_blend_shape_channel_lods(),
|
||||
inputs=self.get_blend_shape_channel_input_indices(),
|
||||
outputs=self.get_blend_shape_channel_output_indices(),
|
||||
)
|
||||
|
||||
def add_animated_maps_conditional_table(self) -> None:
|
||||
"""Reads in the animated maps part of the behavior"""
|
||||
|
||||
self.reader.animated_maps_conditional_table = AnimatedMapsConditionalTable(
|
||||
lods=self.get_animated_map_lods(),
|
||||
conditional_table=ConditionalTable(
|
||||
from_values=self.get_animated_map_from_values(),
|
||||
to_values=self.get_animated_map_to_values(),
|
||||
slope_values=self.get_animated_map_slope_values(),
|
||||
cut_values=self.get_animated_map_cut_values(),
|
||||
inputs=self.get_animated_map_input_indices(),
|
||||
outputs=self.get_animated_map_output_indices(),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConditionalTable:
|
||||
"""
|
||||
A model class for holding various values
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type from_values: List[float]
|
||||
@param from_values: The list of values
|
||||
|
||||
@type to_values: List[float]
|
||||
@param to_values: The list of values
|
||||
|
||||
@type slope_values: List[float]
|
||||
@param slope_values: The list of slope values
|
||||
|
||||
@type cut_values: List[float]
|
||||
@param cut_values: The list of cut values
|
||||
|
||||
@type inputs: List[int]
|
||||
@param inputs: The indices of inputs
|
||||
|
||||
@type outputs: List[int]
|
||||
@param outputs: The indices of outputs
|
||||
"""
|
||||
|
||||
from_values: List[float] = field(default_factory=list)
|
||||
to_values: List[float] = field(default_factory=list)
|
||||
slope_values: List[float] = field(default_factory=list)
|
||||
cut_values: List[float] = field(default_factory=list)
|
||||
inputs: List[int] = field(default_factory=list)
|
||||
outputs: List[int] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PSDMatrix:
|
||||
"""
|
||||
A model class for holding data about Pose Space Deformation
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type count: int
|
||||
@param count: The list of values
|
||||
|
||||
@type rows: List[int]
|
||||
@param rows: List of row indices used for storing values
|
||||
|
||||
@type columns: List[int]
|
||||
@param columns: List of row indices used for storing values
|
||||
|
||||
@type values: List[float]
|
||||
@param values: The list of values, that can be accessed from the row and column index
|
||||
"""
|
||||
|
||||
count: Optional[int] = field(default=None)
|
||||
rows: List[int] = field(default_factory=list)
|
||||
columns: List[int] = field(default_factory=list)
|
||||
values: List[float] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class JointGroup:
|
||||
"""
|
||||
A model class for holding data about joint groups
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type lods: List[int]
|
||||
@param lods: A list of lod indices that the joint group is contained within
|
||||
|
||||
@type values: List[float]
|
||||
@param values: A list of values
|
||||
|
||||
@type joints: List[int]
|
||||
@param joints: A list of joint indices
|
||||
|
||||
@type inputs: List[int]
|
||||
@param inputs: The indices of inputs
|
||||
|
||||
@type outputs: List[int]
|
||||
@param outputs: The indices of outputs
|
||||
"""
|
||||
|
||||
lods: List[int] = field(default_factory=list)
|
||||
values: List[float] = field(default_factory=list)
|
||||
joints: List[int] = field(default_factory=list)
|
||||
inputs: List[int] = field(default_factory=list)
|
||||
outputs: List[int] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BlendShapesData:
|
||||
"""
|
||||
A model class for holding data about blend shapes
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type lods: List[int]
|
||||
@param lods: A list of lod indices that the blend shapes are contained within
|
||||
|
||||
@type inputs: List[int]
|
||||
@param inputs: The indices of inputs
|
||||
|
||||
@type outputs: List[int]
|
||||
@param outputs: The indices of outputs
|
||||
"""
|
||||
|
||||
lods: List[int] = field(default_factory=list)
|
||||
inputs: List[int] = field(default_factory=list)
|
||||
outputs: List[int] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AnimatedMapsConditionalTable:
|
||||
"""
|
||||
A model class for holding data about animated maps
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type lods: List[int]
|
||||
@param lods: A list of lod indices that the blend shapes are contained within
|
||||
|
||||
@type conditional_table: ConditionalTable
|
||||
@param conditional_table: Data needed for animated maps
|
||||
"""
|
||||
|
||||
lods: List[int] = field(default_factory=list)
|
||||
conditional_table: ConditionalTable = field(default_factory=ConditionalTable)
|
||||
|
||||
|
||||
@dataclass
|
||||
class JointGroups:
|
||||
"""
|
||||
A model class for storing data about joints
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type joint_row_count: int
|
||||
@param joint_row_count: The row count of the matrix that stores the joints data
|
||||
|
||||
@type joint_column_count: int
|
||||
@param joint_column_count: The column count of the matrix that stores the joints data
|
||||
|
||||
@type joint_variable_attribute_indices: List[List[int]]
|
||||
@param joint_variable_attribute_indices: List of joint variable attribute indices per LOD
|
||||
|
||||
@type joint_groups: List[JointGroup]
|
||||
@param joint_groups: The list of joint groups
|
||||
"""
|
||||
|
||||
joint_row_count: Optional[int] = field(default=None)
|
||||
joint_column_count: Optional[int] = field(default=None)
|
||||
joint_variable_attribute_indices: List[List[int]] = field(default_factory=list)
|
||||
joint_groups: List[JointGroup] = field(default_factory=list)
|
333
scripts/dnalib/definition.py
Normal file
333
scripts/dnalib/definition.py
Normal file
@@ -0,0 +1,333 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional, Tuple, cast
|
||||
|
||||
from dna import BinaryStreamReader as DNAReader
|
||||
from dna import MeshBlendShapeChannelMapping
|
||||
|
||||
from ..model import Point3
|
||||
from .descriptor import Descriptor
|
||||
from .layer import Layer
|
||||
|
||||
|
||||
class Definition(Descriptor):
|
||||
"""
|
||||
A class used for reading and accessing the definition part of the DNA file
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type reader: BinaryStreamReader
|
||||
@param reader: The binary stream reader being used
|
||||
|
||||
@type definition: DefinitionModel
|
||||
@param definition: The object that holds the definition data read from the DNA file
|
||||
|
||||
@type joints: Joints
|
||||
@param joints: The data about joints
|
||||
|
||||
@type blend_shape_channels: GeometryEntity
|
||||
@param blend_shape_channels: The names and indices of blend shape channels
|
||||
|
||||
@type animated_maps: GeometryEntity
|
||||
@param animated_maps: The names and indices of animated maps
|
||||
|
||||
@type meshes: GeometryEntity
|
||||
@param meshes: The names and indices of the meshes
|
||||
|
||||
@type gui_control_names: List[str]
|
||||
@param gui_control_names: The list of gui control names
|
||||
|
||||
@type raw_control_names: List[str]
|
||||
@param raw_control_names: The list of raw control names
|
||||
|
||||
@type mesh_blend_shape_channel_mapping: List[Tuple[int, int]]
|
||||
@param mesh_blend_shape_channel_mapping: Mapping of mesh index to the blend shape channel index
|
||||
|
||||
@type mesh_blend_shape_channel_mapping_indices_for_lod: List[List[int]]
|
||||
@param mesh_blend_shape_channel_mapping_indices_for_lod: The list of blend shape channel mapping indices by lod
|
||||
|
||||
@type neutral_joint_translations: List[Point3]
|
||||
@param neutral_joint_translations: The list of neutral joint translations
|
||||
|
||||
@type neutral_joint_rotations: List[Point3]
|
||||
@param neutral_joint_rotations: The list of neutral joint rotations
|
||||
"""
|
||||
|
||||
def __init__(self, reader: DNAReader, layers: Optional[List[Layer]]) -> None:
|
||||
super().__init__(reader, layers)
|
||||
self.joints = Joints()
|
||||
self.blend_shape_channels = GeometryEntity()
|
||||
self.animated_maps = GeometryEntity()
|
||||
self.meshes = GeometryEntity()
|
||||
self.meshes_mapping: Dict[str, int] = {}
|
||||
|
||||
self.gui_control_names: List[str] = []
|
||||
self.raw_control_names: List[str] = []
|
||||
|
||||
self.mesh_blend_shape_channel_mapping: List[Tuple[int, int]] = []
|
||||
self.mesh_blend_shape_channel_mapping_indices_for_lod: List[List[int]] = []
|
||||
|
||||
self.neutral_joint_translations: List[Point3] = []
|
||||
self.neutral_joint_rotations: List[Point3] = []
|
||||
self.definition_read = False
|
||||
|
||||
def start_read(self) -> None:
|
||||
super().start_read()
|
||||
self.definition_read = False
|
||||
|
||||
def is_read(self) -> bool:
|
||||
return super().is_read() and self.definition_read
|
||||
|
||||
def read(self) -> None:
|
||||
"""
|
||||
Starts reading in the definition part of the DNA
|
||||
|
||||
@rtype: DefinitionModel
|
||||
@returns: the instance of the created definition model
|
||||
"""
|
||||
super().read()
|
||||
|
||||
if not self.definition_read and self.layer_enabled(Layer.definition):
|
||||
self.definition_read = True
|
||||
self.add_controls()
|
||||
self.add_joints()
|
||||
self.add_blend_shape_channels()
|
||||
self.add_animated_maps()
|
||||
self.add_meshes()
|
||||
self.add_mesh_blend_shape_channel_mapping()
|
||||
self.add_neutral_joints()
|
||||
|
||||
def get_lod_count(self) -> int:
|
||||
return cast(int, self.reader.getLODCount())
|
||||
|
||||
def get_gui_control_count(self) -> int:
|
||||
return cast(int, self.reader.getGUIControlCount())
|
||||
|
||||
def get_gui_control_name(self, index: int) -> str:
|
||||
return cast(str, self.reader.getGUIControlName(index))
|
||||
|
||||
def get_raw_control_count(self) -> int:
|
||||
return cast(int, self.reader.getRawControlCount())
|
||||
|
||||
def get_raw_control_name(self, index: int) -> str:
|
||||
return cast(str, self.reader.getRawControlName(index))
|
||||
|
||||
def get_raw_control_names(self) -> List[str]:
|
||||
names = []
|
||||
for i in range(self.get_raw_control_count()):
|
||||
names.append(self.get_raw_control_name(i))
|
||||
return names
|
||||
|
||||
def get_neutral_joint_translation(self, index: int) -> Point3:
|
||||
translation = cast(List[float], self.reader.getNeutralJointTranslation(index))
|
||||
return Point3(translation[0], translation[1], translation[2])
|
||||
|
||||
def get_neutral_joint_translation_xs(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getNeutralJointTranslationXs())
|
||||
|
||||
def get_neutral_joint_translation_ys(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getNeutralJointTranslationYs())
|
||||
|
||||
def get_neutral_joint_translation_zs(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getNeutralJointTranslationZs())
|
||||
|
||||
def get_neutral_joint_rotation(self, index: int) -> Point3:
|
||||
translation = cast(List[float], self.reader.getNeutralJointRotation(index))
|
||||
return Point3(translation[0], translation[1], translation[2])
|
||||
|
||||
def get_neutral_joint_rotation_xs(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getNeutralJointRotationXs())
|
||||
|
||||
def get_neutral_joint_rotation_ys(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getNeutralJointRotationYs())
|
||||
|
||||
def get_neutral_joint_rotation_zs(self) -> List[float]:
|
||||
return cast(List[float], self.reader.getNeutralJointRotationZs())
|
||||
|
||||
def get_mesh_blend_shape_channel_mapping_count(self) -> int:
|
||||
return cast(int, self.reader.getMeshBlendShapeChannelMappingCount())
|
||||
|
||||
def get_mesh_blend_shape_channel_mapping(
|
||||
self, index: int
|
||||
) -> MeshBlendShapeChannelMapping:
|
||||
return cast(
|
||||
MeshBlendShapeChannelMapping,
|
||||
self.reader.getMeshBlendShapeChannelMapping(index),
|
||||
)
|
||||
|
||||
def get_mesh_blend_shape_channel_mapping_for_lod(self, lod: int) -> List[int]:
|
||||
return cast(
|
||||
List[int], self.reader.getMeshBlendShapeChannelMappingIndicesForLOD(lod)
|
||||
)
|
||||
|
||||
def get_joint_count(self) -> int:
|
||||
return cast(int, self.reader.getJointCount())
|
||||
|
||||
def get_joint_name(self, index: int) -> str:
|
||||
return cast(str, self.reader.getJointName(index))
|
||||
|
||||
def get_joint_parent_index(self, index: int) -> int:
|
||||
return cast(int, self.reader.getJointParentIndex(index))
|
||||
|
||||
def get_joint_indices_for_lod(self, index: int) -> List[int]:
|
||||
return cast(List[int], self.reader.getJointIndicesForLOD(index))
|
||||
|
||||
def get_blend_shape_channel_count(self) -> int:
|
||||
return cast(int, self.reader.getBlendShapeChannelCount())
|
||||
|
||||
def get_blend_shape_channel_name(self, index: int) -> str:
|
||||
return cast(str, self.reader.getBlendShapeChannelName(index))
|
||||
|
||||
def get_mesh_count(self) -> int:
|
||||
return cast(int, self.reader.getMeshCount())
|
||||
|
||||
def get_mesh_name(self, index: int) -> str:
|
||||
return cast(str, self.reader.getMeshName(index))
|
||||
|
||||
def get_mesh_indices_for_lod(self, index: int) -> List[int]:
|
||||
return cast(List[int], self.reader.getMeshIndicesForLOD(index))
|
||||
|
||||
def get_blend_shape_channel_indices_for_lod(self, index: int) -> List[int]:
|
||||
return cast(List[int], self.reader.getBlendShapeChannelIndicesForLOD(index))
|
||||
|
||||
def get_animated_map_count(self) -> int:
|
||||
return cast(int, self.reader.getAnimatedMapCount())
|
||||
|
||||
def get_animated_map_name(self, index: int) -> str:
|
||||
return cast(str, self.reader.getAnimatedMapName(index))
|
||||
|
||||
def get_animated_map_names(self) -> List[str]:
|
||||
names = []
|
||||
for i in range(self.get_animated_map_count()):
|
||||
names.append(self.get_animated_map_name(i))
|
||||
return names
|
||||
|
||||
def get_animated_map_indices_for_lod(self, index: int) -> List[int]:
|
||||
return cast(List[int], self.reader.getAnimatedMapIndicesForLOD(index))
|
||||
|
||||
def get_translation_unit(self) -> int:
|
||||
return cast(int, self.reader.getTranslationUnit())
|
||||
|
||||
def get_rotation_unit(self) -> int:
|
||||
return cast(int, self.reader.getRotationUnit())
|
||||
|
||||
def add_neutral_joints(self) -> None:
|
||||
"""Reads in the neutral joints part of the definition"""
|
||||
|
||||
neutral_joint_translation_xs = self.get_neutral_joint_translation_xs()
|
||||
neutral_joint_translation_ys = self.get_neutral_joint_translation_ys()
|
||||
neutral_joint_translation_zs = self.get_neutral_joint_translation_zs()
|
||||
neutral_joint_translation_count_x = len(neutral_joint_translation_xs)
|
||||
for index in range(neutral_joint_translation_count_x):
|
||||
self.neutral_joint_translations.append(
|
||||
Point3(
|
||||
x=neutral_joint_translation_xs[index],
|
||||
y=neutral_joint_translation_ys[index],
|
||||
z=neutral_joint_translation_zs[index],
|
||||
)
|
||||
)
|
||||
neutral_joint_rotation_xs = self.get_neutral_joint_rotation_xs()
|
||||
neutral_joint_rotation_ys = self.get_neutral_joint_rotation_ys()
|
||||
neutral_joint_rotation_zs = self.get_neutral_joint_rotation_zs()
|
||||
neutral_joint_rotation_count_x = len(neutral_joint_rotation_xs)
|
||||
for index in range(neutral_joint_rotation_count_x):
|
||||
self.neutral_joint_rotations.append(
|
||||
Point3(
|
||||
x=neutral_joint_rotation_xs[index],
|
||||
y=neutral_joint_rotation_ys[index],
|
||||
z=neutral_joint_rotation_zs[index],
|
||||
)
|
||||
)
|
||||
|
||||
def add_mesh_blend_shape_channel_mapping(self) -> None:
|
||||
"""Reads in the mesh blend shape channel mapping"""
|
||||
|
||||
for index in range(self.get_mesh_blend_shape_channel_mapping_count()):
|
||||
mapping = self.get_mesh_blend_shape_channel_mapping(index)
|
||||
self.mesh_blend_shape_channel_mapping.append(
|
||||
(mapping.meshIndex, mapping.blendShapeChannelIndex)
|
||||
)
|
||||
for lod in range(self.get_lod_count()):
|
||||
self.mesh_blend_shape_channel_mapping_indices_for_lod.append(
|
||||
self.get_mesh_blend_shape_channel_mapping_for_lod(lod)
|
||||
)
|
||||
|
||||
def add_meshes(self) -> None:
|
||||
"""Reads in the meshes of the definition"""
|
||||
|
||||
for index in range(self.get_mesh_count()):
|
||||
mesh_name = self.get_mesh_name(index)
|
||||
self.meshes.names.append(mesh_name)
|
||||
self.meshes_mapping[mesh_name] = index
|
||||
for index in range(self.get_lod_count()):
|
||||
self.meshes.lod_indices.append(self.get_mesh_indices_for_lod(index))
|
||||
|
||||
def add_animated_maps(self) -> None:
|
||||
"""Reads in the animated maps of the definition"""
|
||||
|
||||
for index in range(self.get_animated_map_count()):
|
||||
self.animated_maps.names.append(self.get_animated_map_name(index))
|
||||
for index in range(self.get_lod_count()):
|
||||
self.animated_maps.lod_indices.append(
|
||||
self.get_animated_map_indices_for_lod(index)
|
||||
)
|
||||
|
||||
def add_blend_shape_channels(self) -> None:
|
||||
"""Reads in the neutral joints part of the definition"""
|
||||
|
||||
for index in range(self.get_blend_shape_channel_count()):
|
||||
self.blend_shape_channels.names.append(
|
||||
self.get_blend_shape_channel_name(index)
|
||||
)
|
||||
for index in range(self.get_lod_count()):
|
||||
self.blend_shape_channels.lod_indices.append(
|
||||
self.get_blend_shape_channel_indices_for_lod(index)
|
||||
)
|
||||
|
||||
def add_joints(self) -> None:
|
||||
"""Reads in the joints of the definition"""
|
||||
|
||||
for index in range(self.get_joint_count()):
|
||||
self.joints.names.append(self.get_joint_name(index))
|
||||
self.joints.parent_index.append(self.get_joint_parent_index(index))
|
||||
for index in range(self.get_lod_count()):
|
||||
self.joints.lod_indices.append(self.get_joint_indices_for_lod(index))
|
||||
|
||||
def add_controls(self) -> None:
|
||||
"""Reads in the gui and raw controls of the definition"""
|
||||
|
||||
for index in range(self.get_gui_control_count()):
|
||||
self.gui_control_names.append(self.get_gui_control_name(index))
|
||||
for index in range(self.get_raw_control_count()):
|
||||
self.raw_control_names.append(self.get_raw_control_name(index))
|
||||
|
||||
|
||||
@dataclass
|
||||
class GeometryEntity:
|
||||
"""
|
||||
A model class for holding names and indices
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type names: List[str]
|
||||
@param names: List of names
|
||||
|
||||
@type lod_indices: List[List[int]]
|
||||
@param lod_indices: List of indices per lod
|
||||
"""
|
||||
|
||||
names: List[str] = field(default_factory=list)
|
||||
lod_indices: List[List[int]] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Joints(GeometryEntity):
|
||||
"""
|
||||
A model class for holding data about the joints
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type parent_index: List[int]
|
||||
@param parent_index: List of parent indices for each joint index
|
||||
"""
|
||||
|
||||
parent_index: List[int] = field(default_factory=list)
|
129
scripts/dnalib/descriptor.py
Normal file
129
scripts/dnalib/descriptor.py
Normal file
@@ -0,0 +1,129 @@
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from dna import BinaryStreamReader as DNAReader
|
||||
|
||||
from ..dnalib.layer import Layer
|
||||
|
||||
|
||||
class Descriptor:
|
||||
"""
|
||||
A class used for reading and accessing the descriptor part of the DNA file
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
||||
@type name: str
|
||||
@param name: The name of the character
|
||||
|
||||
@type archetype: int
|
||||
@param archetype: A value that represents the archetype of the character
|
||||
|
||||
@type gender: int
|
||||
@param gender: A value that represents the gender of the character
|
||||
|
||||
@type age: int
|
||||
@param age: The age of the character
|
||||
|
||||
@type metadata: Dict[str, str]
|
||||
@param metadata: Metadata stored for the character
|
||||
|
||||
@type translation_unit: int
|
||||
@param translation_unit: The translation unit that was used for creating the character
|
||||
|
||||
@type rotation_unit: int
|
||||
@param rotation_unit: The translation unit that was used for creating the character
|
||||
|
||||
@type coordinate_system: Tuple[int, int, int]
|
||||
@param coordinate_system: A tuple representing the coordinate system
|
||||
|
||||
@type lod_count: int
|
||||
@param lod_count: The number of LODs for the characters
|
||||
|
||||
@type db_max_lod:int
|
||||
@param db_max_lod: A LOD constraint representing the greatest LOD we wish wish to produce (ie. if the value is n, the potential LODs are 0, 1, .. n-1)
|
||||
|
||||
@type db_complexity: str
|
||||
@param db_complexity: Will be used in future
|
||||
|
||||
@type db_name: str
|
||||
@param db_name: DB identifier
|
||||
"""
|
||||
|
||||
def __init__(self, reader: DNAReader, layers: Optional[List[Layer]]) -> None:
|
||||
self.reader = reader
|
||||
self.layers = layers
|
||||
self.name: Optional[str] = None
|
||||
self.archetype: Optional[int] = None
|
||||
self.gender: Optional[int] = None
|
||||
self.age: Optional[int] = None
|
||||
self.metadata: Dict[str, str] = {}
|
||||
|
||||
self.translation_unit: Optional[int] = None
|
||||
self.rotation_unit: Optional[int] = None
|
||||
|
||||
self.coordinate_system: Optional[Tuple[int, int, int]] = None
|
||||
|
||||
self.lod_count: Optional[int] = None
|
||||
self.db_max_lod: Optional[int] = None
|
||||
self.db_complexity: Optional[str] = None
|
||||
self.db_name: Optional[str] = None
|
||||
self.descriptor_read = False
|
||||
|
||||
def start_read(self) -> None:
|
||||
self.descriptor_read = False
|
||||
|
||||
def is_read(self) -> bool:
|
||||
return self.descriptor_read
|
||||
|
||||
def layer_enabled(self, layer: Layer) -> bool:
|
||||
return layer in self.layers or Layer.all in self.layers
|
||||
|
||||
def read(self) -> None:
|
||||
"""
|
||||
Starts reading in the descriptor part of the DNA
|
||||
|
||||
@rtype: DescriptorModel
|
||||
@returns: the instance of the created descriptor model
|
||||
"""
|
||||
|
||||
if not self.descriptor_read and self.layer_enabled(Layer.descriptor):
|
||||
self.descriptor_read = True
|
||||
self.add_basic_data()
|
||||
self.add_metadata()
|
||||
self.add_geometry_data()
|
||||
self.add_db_data()
|
||||
|
||||
def add_basic_data(self) -> None:
|
||||
"""Reads in the character name, archetype, gender and age"""
|
||||
|
||||
self.name = self.reader.getName()
|
||||
self.archetype = self.reader.getArchetype()
|
||||
self.gender = self.reader.getGender()
|
||||
self.age = self.reader.getAge()
|
||||
|
||||
def add_metadata(self) -> None:
|
||||
"""Reads in the metadata provided from the DNA file"""
|
||||
|
||||
for i in range(self.reader.getMetaDataCount()):
|
||||
key = self.reader.getMetaDataKey(i)
|
||||
self.metadata[key] = self.reader.getMetaDataValue(key)
|
||||
|
||||
def add_geometry_data(self) -> None:
|
||||
"""Sets the translation unit, rotation unit, and coordinate system from the DNA file"""
|
||||
|
||||
self.translation_unit = self.reader.getTranslationUnit()
|
||||
self.rotation_unit = self.reader.getRotationUnit()
|
||||
coordinate_system = self.reader.getCoordinateSystem()
|
||||
self.coordinate_system = (
|
||||
coordinate_system.xAxis,
|
||||
coordinate_system.yAxis,
|
||||
coordinate_system.zAxis,
|
||||
)
|
||||
|
||||
def add_db_data(self) -> None:
|
||||
"""Reads in the db data from the DNA file"""
|
||||
|
||||
self.lod_count = self.reader.getLODCount()
|
||||
self.db_max_lod = self.reader.getDBMaxLOD()
|
||||
self.db_complexity = self.reader.getDBComplexity()
|
||||
self.db_name = self.reader.getDBName()
|
250
scripts/dnalib/dnalib.py
Normal file
250
scripts/dnalib/dnalib.py
Normal file
@@ -0,0 +1,250 @@
|
||||
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
|
||||
|
||||
|
||||
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()
|
||||
|
||||
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
|
||||
|
||||
def is_read(self) -> bool:
|
||||
return Behavior.is_read(self) and Geometry.is_read(self)
|
||||
|
||||
def read(self) -> None:
|
||||
if not self.is_read():
|
||||
self.start_read()
|
||||
Behavior.read(self)
|
||||
Geometry.read(self)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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))
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def get_vertex_texture_coordinates_for_mesh(self, mesh_index: int) -> List[UV]:
|
||||
return self.geometry_meshes[mesh_index].topology.texture_coordinates
|
||||
|
||||
def get_vertex_positions_for_mesh_index(self, mesh_index: int) -> List[Point3]:
|
||||
return self.geometry_meshes[mesh_index].topology.positions
|
||||
|
||||
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
|
||||
]
|
||||
|
||||
def get_faces(self, mesh_index: int) -> List[List[int]]:
|
||||
return self.geometry_meshes[mesh_index].topology.face_vertex_layouts
|
||||
|
||||
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
|
||||
|
||||
def get_layouts_for_mesh_index(self, mesh_index: int) -> List[Layout]:
|
||||
return self.geometry_meshes[mesh_index].topology.layouts
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
def has_blend_shapes(self, mesh_index: int) -> bool:
|
||||
return (
|
||||
len([bs.channel for bs in self.geometry_meshes[mesh_index].blend_shapes])
|
||||
> 0
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def get_blend_shapes(self, mesh_index: int) -> List[BlendShape]:
|
||||
return self.geometry_meshes[mesh_index].blend_shapes
|
||||
|
||||
def get_mesh_id_from_mesh_name(self, mesh_name: str) -> Optional[int]:
|
||||
return self.meshes_mapping.get(mesh_name, None)
|
283
scripts/dnalib/geometry.py
Normal file
283
scripts/dnalib/geometry.py
Normal file
@@ -0,0 +1,283 @@
|
||||
from typing import Dict, List, Optional, Tuple, cast
|
||||
|
||||
from dna import BinaryStreamReader as DNAReader
|
||||
|
||||
from ..model import UV, BlendShape, Layout, Mesh, Point3, SkinWeightsData, Topology
|
||||
from .definition import Definition
|
||||
from .layer import Layer
|
||||
|
||||
|
||||
class Geometry(Definition):
|
||||
def __init__(self, reader: DNAReader, layers: Optional[List[Layer]]) -> None:
|
||||
super().__init__(reader, layers)
|
||||
self.geometry_meshes: List[Mesh] = []
|
||||
self.geometry_read = False
|
||||
|
||||
def start_read(self) -> None:
|
||||
super().start_read()
|
||||
self.geometry_read = False
|
||||
|
||||
def is_read(self) -> bool:
|
||||
return super().is_read() and self.geometry_read
|
||||
|
||||
def read(self) -> None:
|
||||
"""
|
||||
Starts reading in the mesh from the geometry part of the DNA
|
||||
"""
|
||||
super().read()
|
||||
|
||||
if not self.geometry_read and self.layer_enabled(Layer.geometry):
|
||||
self.geometry_read = True
|
||||
self.geometry_meshes = []
|
||||
for lod in range(self.get_lod_count()):
|
||||
for mesh_index in self.get_mesh_indices_for_lod(lod):
|
||||
self.geometry_meshes.append(self.add_mesh(mesh_index))
|
||||
|
||||
def get_maximum_influence_per_vertex(self, mesh_index: int) -> int:
|
||||
return cast(int, self.reader.getMaximumInfluencePerVertex(meshIndex=mesh_index))
|
||||
|
||||
def get_vertex_position_count(self, mesh_index: int) -> int:
|
||||
return cast(int, self.reader.getVertexPositionCount(mesh_index))
|
||||
|
||||
def get_skin_weights_values(
|
||||
self, mesh_index: int, vertex_index: int
|
||||
) -> List[float]:
|
||||
return cast(
|
||||
List[float],
|
||||
self.reader.getSkinWeightsValues(
|
||||
meshIndex=mesh_index, vertexIndex=vertex_index
|
||||
),
|
||||
)
|
||||
|
||||
def get_skin_weights_joint_indices(
|
||||
self, mesh_index: int, vertex_index: int
|
||||
) -> List[int]:
|
||||
return cast(
|
||||
List[int],
|
||||
self.reader.getSkinWeightsJointIndices(
|
||||
meshIndex=mesh_index, vertexIndex=vertex_index
|
||||
),
|
||||
)
|
||||
|
||||
def get_vertex_texture_coordinate_count(self, mesh_index: int) -> int:
|
||||
return cast(
|
||||
int, self.reader.getVertexTextureCoordinateCount(meshIndex=mesh_index)
|
||||
)
|
||||
|
||||
def get_vertex_texture_coordinate(
|
||||
self, mesh_index: int, texture_coordinate_index: int
|
||||
) -> Tuple[float, float]:
|
||||
return cast(
|
||||
Tuple[float, float],
|
||||
self.reader.getVertexTextureCoordinate(
|
||||
meshIndex=mesh_index, textureCoordinateIndex=texture_coordinate_index
|
||||
),
|
||||
)
|
||||
|
||||
def get_face_count(self, mesh_index: int) -> int:
|
||||
return cast(int, self.reader.getFaceCount(meshIndex=mesh_index))
|
||||
|
||||
def get_face_vertex_layout_indices(
|
||||
self, mesh_index: int, face_index: int
|
||||
) -> List[int]:
|
||||
return cast(
|
||||
List[int],
|
||||
self.reader.getFaceVertexLayoutIndices(
|
||||
meshIndex=mesh_index, faceIndex=face_index
|
||||
),
|
||||
)
|
||||
|
||||
def get_vertex_layout(
|
||||
self, mesh_index: int, layout_index: int
|
||||
) -> Tuple[int, int, int]:
|
||||
return cast(
|
||||
Tuple[int, int, int],
|
||||
self.reader.getVertexLayout(meshIndex=mesh_index, layoutIndex=layout_index),
|
||||
)
|
||||
|
||||
def get_vertex_layout_count(self, mesh_index: int) -> int:
|
||||
return cast(int, self.reader.getVertexLayoutCount(meshIndex=mesh_index))
|
||||
|
||||
def get_vertex_position(
|
||||
self, mesh_index: int, vertex_index: int
|
||||
) -> Tuple[float, float, float]:
|
||||
return cast(
|
||||
Tuple[float, float, float],
|
||||
self.reader.getVertexPosition(
|
||||
meshIndex=mesh_index, vertexIndex=vertex_index
|
||||
),
|
||||
)
|
||||
|
||||
def get_blend_shape_target_vertex_indices(
|
||||
self, mesh_index: int, blend_shape_target_index: int
|
||||
) -> List[int]:
|
||||
return cast(
|
||||
List[int],
|
||||
self.reader.getBlendShapeTargetVertexIndices(
|
||||
meshIndex=mesh_index, blendShapeTargetIndex=blend_shape_target_index
|
||||
),
|
||||
)
|
||||
|
||||
def get_blend_shape_target_delta_count(
|
||||
self, mesh_index: int, blend_shape_target_index: int
|
||||
) -> int:
|
||||
return cast(
|
||||
int,
|
||||
self.reader.getBlendShapeTargetDeltaCount(
|
||||
meshIndex=mesh_index, blendShapeTargetIndex=blend_shape_target_index
|
||||
),
|
||||
)
|
||||
|
||||
def get_blend_shape_target_delta(
|
||||
self, mesh_index: int, blend_shape_target_index: int, delta_index: int
|
||||
) -> Tuple[int, int, int]:
|
||||
return cast(
|
||||
Tuple[int, int, int],
|
||||
self.reader.getBlendShapeTargetDelta(
|
||||
meshIndex=mesh_index,
|
||||
blendShapeTargetIndex=blend_shape_target_index,
|
||||
deltaIndex=delta_index,
|
||||
),
|
||||
)
|
||||
|
||||
def get_blend_shape_target_count(self, mesh_index: int) -> int:
|
||||
return cast(int, self.reader.getBlendShapeTargetCount(meshIndex=mesh_index))
|
||||
|
||||
def get_blend_shape_channel_index(
|
||||
self, mesh_index: int, blend_shape_target_index: int
|
||||
) -> int:
|
||||
return cast(
|
||||
int,
|
||||
self.reader.getBlendShapeChannelIndex(
|
||||
meshIndex=mesh_index, blendShapeTargetIndex=blend_shape_target_index
|
||||
),
|
||||
)
|
||||
|
||||
def add_mesh(self, mesh_index: int) -> Mesh:
|
||||
mesh = Mesh()
|
||||
mesh.name = self.get_mesh_name(mesh_index)
|
||||
mesh.topology = self.add_mesh_topology(mesh_index)
|
||||
mesh.skin_weights = self.add_mesh_skin_weights(mesh_index)
|
||||
mesh.blend_shapes = self.add_mesh_blend_shapes(mesh_index)
|
||||
return mesh
|
||||
|
||||
def add_mesh_skin_weights(self, mesh_index: int) -> SkinWeightsData:
|
||||
"""Reads in the skin weights"""
|
||||
skin_weights = SkinWeightsData()
|
||||
for vertex_index in range(self.get_vertex_position_count(mesh_index)):
|
||||
skin_weights.values.append(
|
||||
self.get_skin_weights_values(mesh_index, vertex_index)
|
||||
)
|
||||
skin_weights.joint_indices.append(
|
||||
self.get_skin_weights_joint_indices(mesh_index, vertex_index)
|
||||
)
|
||||
|
||||
return skin_weights
|
||||
|
||||
def add_mesh_topology(self, mesh_index: int) -> Topology:
|
||||
"""Reads in the positions, texture coordinates, normals, layouts and face vertex layouts"""
|
||||
topology = Topology()
|
||||
topology.positions = self.add_positions(mesh_index)
|
||||
topology.texture_coordinates = self.add_texture_coordinates(mesh_index)
|
||||
topology.layouts = self.add_layouts(mesh_index)
|
||||
topology.face_vertex_layouts = self.add_face_vertex_layouts(mesh_index)
|
||||
return topology
|
||||
|
||||
def add_face_vertex_layouts(self, mesh_index: int) -> List[List[int]]:
|
||||
"""Reads in the face vertex layouts"""
|
||||
face_vertex_layouts = []
|
||||
|
||||
for face_index in range(self.get_face_count(mesh_index)):
|
||||
face_vertex_layouts.append(
|
||||
self.get_face_vertex_layout_indices(mesh_index, face_index)
|
||||
)
|
||||
|
||||
return face_vertex_layouts
|
||||
|
||||
def add_layouts(self, mesh_index: int) -> List[Layout]:
|
||||
"""Reads in the vertex layouts"""
|
||||
layouts = []
|
||||
|
||||
for layout_index in range(self.get_vertex_layout_count(mesh_index)):
|
||||
(
|
||||
position_id,
|
||||
texture_coordinate_id,
|
||||
_,
|
||||
) = self.get_vertex_layout(mesh_index, layout_index)
|
||||
layouts.append(
|
||||
Layout(
|
||||
position_index=position_id,
|
||||
texture_coordinate_index=texture_coordinate_id,
|
||||
)
|
||||
)
|
||||
return layouts
|
||||
|
||||
def add_texture_coordinates(self, mesh_index: int) -> List[UV]:
|
||||
"""Reads in the texture coordinates"""
|
||||
texture_coordinates = []
|
||||
for texture_coordinate_index in range(
|
||||
self.get_vertex_texture_coordinate_count(mesh_index)
|
||||
):
|
||||
u, v = self.get_vertex_texture_coordinate(
|
||||
mesh_index, texture_coordinate_index
|
||||
)
|
||||
texture_coordinates.append(UV(u=u, v=v))
|
||||
return texture_coordinates
|
||||
|
||||
def add_positions(self, mesh_index: int) -> List[Point3]:
|
||||
"""Reads in the vertex positions"""
|
||||
|
||||
positions = []
|
||||
for vertex_index in range(self.get_vertex_position_count(mesh_index)):
|
||||
x, y, z = self.get_vertex_position(mesh_index, vertex_index)
|
||||
positions.append(Point3(x=x, y=y, z=z))
|
||||
return positions
|
||||
|
||||
def read_target_deltas(
|
||||
self, mesh_index: int, blend_shape_target_index: int
|
||||
) -> Dict[int, Point3]:
|
||||
"""
|
||||
Reads in the target deltas
|
||||
|
||||
@rtype: Dict[int, Point3]
|
||||
@returns: Mapping of vertex indices to positions
|
||||
"""
|
||||
|
||||
result: Dict[int, Point3] = {}
|
||||
|
||||
vertices = self.get_blend_shape_target_vertex_indices(
|
||||
mesh_index, blend_shape_target_index
|
||||
)
|
||||
|
||||
blend_shape_target_delta_count = self.get_blend_shape_target_delta_count(
|
||||
mesh_index, blend_shape_target_index
|
||||
)
|
||||
for delta_index in range(blend_shape_target_delta_count):
|
||||
x, y, z = self.get_blend_shape_target_delta(
|
||||
mesh_index, blend_shape_target_index, delta_index
|
||||
)
|
||||
result[vertices[delta_index]] = Point3(x=x, y=y, z=z)
|
||||
return result
|
||||
|
||||
def add_mesh_blend_shapes(self, mesh_index: int) -> List[BlendShape]:
|
||||
"""
|
||||
Reads in the blend shapes
|
||||
|
||||
@type mesh_index: int
|
||||
@param mesh_index: The mesh index
|
||||
"""
|
||||
|
||||
blend_shape_target_count = self.get_blend_shape_target_count(mesh_index)
|
||||
blend_shapes = []
|
||||
for blend_shape_target_index in range(blend_shape_target_count):
|
||||
blend_shapes.append(
|
||||
BlendShape(
|
||||
channel=self.get_blend_shape_channel_index(
|
||||
mesh_index, blend_shape_target_index
|
||||
),
|
||||
deltas=self.read_target_deltas(
|
||||
mesh_index, blend_shape_target_index
|
||||
),
|
||||
)
|
||||
)
|
||||
return blend_shapes
|
9
scripts/dnalib/layer.py
Normal file
9
scripts/dnalib/layer.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Layer(Enum):
|
||||
descriptor = 1
|
||||
definition = 2
|
||||
behavior = 3
|
||||
geometry = 4
|
||||
all = 5
|
Reference in New Issue
Block a user