Base
This commit is contained in:
4
scripts/builder/maya/__init__.py
Normal file
4
scripts/builder/maya/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import *
|
424
scripts/builder/maya/mesh.py
Normal file
424
scripts/builder/maya/mesh.py
Normal file
@@ -0,0 +1,424 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Tuple
|
||||
|
||||
from maya import cmds
|
||||
from maya.api.OpenMaya import MDagModifier, MFnDagNode, MFnMesh, MObject, MPoint
|
||||
|
||||
from ...builder.maya.util import Maya
|
||||
from ...common import SKIN_WEIGHT_PRINT_RANGE
|
||||
from ...dnalib.dnalib import DNA
|
||||
from ...model import Point3
|
||||
|
||||
|
||||
@dataclass
|
||||
class Mesh:
|
||||
"""
|
||||
A model class for holding data needed in the mesh building process
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type dna_vertex_positions: List[Point3]
|
||||
@param dna_vertex_positions: Data representing the positions of the vertices
|
||||
|
||||
@type dna_vertex_layout_positions: List[int]
|
||||
@param dna_vertex_layout_positions: Data representing layout position indices of vertices
|
||||
|
||||
@type polygon_faces: List[int]
|
||||
@param polygon_faces: List of lengths of vertex layout indices
|
||||
|
||||
@type polygon_connects: List[int]
|
||||
@param polygon_connects: List of vertex layout position indices
|
||||
|
||||
@type derived_mesh_names: List[str]
|
||||
@param derived_mesh_names: List of mesh names
|
||||
"""
|
||||
|
||||
dna_vertex_positions: List[Point3] = field(default_factory=list)
|
||||
dna_vertex_layout_positions: List[int] = field(default_factory=list)
|
||||
polygon_faces: List[int] = field(default_factory=list)
|
||||
polygon_connects: List[int] = field(default_factory=list)
|
||||
derived_mesh_names: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
class MayaMesh:
|
||||
"""
|
||||
A builder class used for adding joints to the scene
|
||||
|
||||
Attributes
|
||||
----------
|
||||
@type mesh_index: int
|
||||
@param mesh_index: The index of the mesh
|
||||
|
||||
@type dna: DNA
|
||||
@param dna: Instance of DNA
|
||||
|
||||
@type blend_shape_group_prefix: str
|
||||
@param blend_shape_group_prefix: prefix string for blend shape group
|
||||
|
||||
@type blend_shape_name_postfix: str
|
||||
@param blend_shape_name_postfix: postfix string for blend shape name
|
||||
|
||||
@type skin_cluster_suffix: str
|
||||
@param skin_cluster_suffix: postfix string for skin cluster name
|
||||
|
||||
@type data: Mesh
|
||||
@param data: mesh data used in the mesh creation process
|
||||
|
||||
@type fn_mesh: om.MFnMesh
|
||||
@param fn_mesh: OpenMaya class used for creating the mesh
|
||||
|
||||
@type mesh_object: om.MObject
|
||||
@param mesh_object: the object representing the mesh
|
||||
|
||||
@type dag_modifier: om.MDagModifier
|
||||
@param dag_modifier: OpenMaya class used for naming the mesh
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mesh_index: int,
|
||||
dna: DNA,
|
||||
blend_shape_group_prefix: str,
|
||||
blend_shape_name_postfix: str,
|
||||
skin_cluster_suffix: str,
|
||||
) -> None:
|
||||
self.mesh_index = mesh_index
|
||||
self.data: Mesh = Mesh()
|
||||
self.fn_mesh = MFnMesh()
|
||||
self.mesh_object: MObject = None
|
||||
self.dag_modifier: MDagModifier = None
|
||||
self.dna = dna
|
||||
self.blend_shape_group_prefix = blend_shape_group_prefix
|
||||
self.blend_shape_name_postfix = blend_shape_name_postfix
|
||||
self.skin_cluster_suffix = skin_cluster_suffix
|
||||
|
||||
def create_neutral_mesh(self) -> MObject:
|
||||
"""
|
||||
Creates the neutral mesh using the config provided for this builder class object
|
||||
|
||||
@rtype: om.MObject
|
||||
@returns: the instance of the created mesh object
|
||||
"""
|
||||
self.prepare_mesh()
|
||||
self.mesh_object = self.create_mesh_object()
|
||||
self.dag_modifier = self.rename_mesh()
|
||||
self.add_texture_coordinates()
|
||||
return self.mesh_object
|
||||
|
||||
def create_mesh_object(self) -> MObject:
|
||||
"""
|
||||
Gets a list of points that represent the vertex positions.
|
||||
|
||||
@rtype: MObject
|
||||
@returns: Maya objects representing maya mesh functions and the created maya mesh object.
|
||||
"""
|
||||
|
||||
mesh_object = self.fn_mesh.create(
|
||||
self.get_vertex_positions_from_dna_vertex_positions(),
|
||||
self.data.polygon_faces,
|
||||
self.data.polygon_connects,
|
||||
)
|
||||
|
||||
return mesh_object
|
||||
|
||||
def get_vertex_positions_from_dna_vertex_positions(self) -> List[MPoint]:
|
||||
"""
|
||||
Gets a list of points that represent the vertex positions.
|
||||
|
||||
@rtype: List[MPoint]
|
||||
@returns: List of maya point objects.
|
||||
"""
|
||||
|
||||
vertex_positions = []
|
||||
for position in self.data.dna_vertex_positions:
|
||||
vertex_positions.append(
|
||||
MPoint(
|
||||
position.x,
|
||||
position.y,
|
||||
position.z,
|
||||
)
|
||||
)
|
||||
return vertex_positions
|
||||
|
||||
def rename_mesh(self) -> MDagModifier:
|
||||
"""
|
||||
Renames the initial mesh object that was created to the name from the configuration.
|
||||
|
||||
@rtype: Tuple[MDagModifier]
|
||||
@returns: Maya object representing the dag modifier.
|
||||
"""
|
||||
|
||||
mesh_name = self.dna.get_mesh_name(self.mesh_index)
|
||||
dag_modifier = MDagModifier()
|
||||
dag_modifier.renameNode(self.mesh_object, mesh_name)
|
||||
dag_modifier.doIt()
|
||||
return dag_modifier
|
||||
|
||||
def prepare_mesh(self) -> None:
|
||||
"""
|
||||
Gets a list of points that represent the vertex positions.
|
||||
|
||||
"""
|
||||
|
||||
logging.info("==============================")
|
||||
mesh_name = self.dna.get_mesh_name(self.mesh_index)
|
||||
logging.info(f"adding mesh: {mesh_name}")
|
||||
self.data.dna_vertex_positions = self.dna.get_vertex_positions_for_mesh_index(
|
||||
self.mesh_index
|
||||
)
|
||||
self.data.dna_vertex_layout_positions = (
|
||||
self.dna.get_vertex_layout_positions_for_mesh_index(self.mesh_index)
|
||||
)
|
||||
|
||||
(
|
||||
self.data.polygon_faces,
|
||||
self.data.polygon_connects,
|
||||
) = self.dna.get_polygon_faces_and_connects(self.mesh_index)
|
||||
|
||||
def add_texture_coordinates(self) -> None:
|
||||
"""
|
||||
Method for adding texture coordinates.
|
||||
|
||||
"""
|
||||
|
||||
logging.info("adding texture coordinates...")
|
||||
|
||||
(
|
||||
texture_coordinate_us,
|
||||
texture_coordinate_vs,
|
||||
texture_coordinate_indices,
|
||||
) = self.get_texture_data()
|
||||
|
||||
self.fn_mesh.setUVs(texture_coordinate_us, texture_coordinate_vs)
|
||||
self.fn_mesh.assignUVs(self.data.polygon_faces, texture_coordinate_indices)
|
||||
|
||||
mesh_name = self.dna.get_mesh_name(self.mesh_index)
|
||||
|
||||
cmds.select(mesh_name, replace=True)
|
||||
cmds.polyMergeUV(mesh_name, distance=0.01, constructionHistory=False)
|
||||
|
||||
def get_texture_data(self) -> Tuple[List[float], List[float], List[int]]:
|
||||
"""
|
||||
Gets the data needed for the creation of textures.
|
||||
|
||||
@rtype: Tuple[List[float], List[float], List[int]] @returns: The tuple containing the list of texture
|
||||
coordinate Us, the list of texture coordinate Vs and the list of texture coordinate indices.
|
||||
"""
|
||||
|
||||
texture_coordinates = self.dna.get_vertex_texture_coordinates_for_mesh(
|
||||
self.mesh_index
|
||||
)
|
||||
dna_faces = self.dna.get_faces(self.mesh_index)
|
||||
|
||||
coordinate_indices = []
|
||||
for layout_id in range(
|
||||
len(self.dna.get_layouts_for_mesh_index(self.mesh_index))
|
||||
):
|
||||
coordinate_indices.append(
|
||||
self.dna.get_texture_coordinate_index(self.mesh_index, layout_id)
|
||||
)
|
||||
|
||||
texture_coordinate_us = []
|
||||
texture_coordinate_vs = []
|
||||
texture_coordinate_indices = []
|
||||
|
||||
index_counter = 0
|
||||
|
||||
for vertices_layout_index_array in dna_faces:
|
||||
for vertex_layout_index_array in vertices_layout_index_array:
|
||||
texture_coordinate = texture_coordinates[
|
||||
coordinate_indices[vertex_layout_index_array]
|
||||
]
|
||||
texture_coordinate_us.append(texture_coordinate.u)
|
||||
texture_coordinate_vs.append(texture_coordinate.v)
|
||||
texture_coordinate_indices.append(index_counter)
|
||||
index_counter += 1
|
||||
|
||||
return texture_coordinate_us, texture_coordinate_vs, texture_coordinate_indices
|
||||
|
||||
def add_blend_shapes(self, add_mesh_name_to_blend_shape_channel_name: bool) -> None:
|
||||
"""Adds blend shapes to the mesh"""
|
||||
if self.dna.has_blend_shapes(self.mesh_index):
|
||||
self.create_blend_shapes(add_mesh_name_to_blend_shape_channel_name)
|
||||
self.create_blend_shape_node()
|
||||
|
||||
def create_blend_shape_node(self) -> None:
|
||||
"""
|
||||
Creates a blend shape node.
|
||||
"""
|
||||
mesh_name = self.dna.get_mesh_name(self.mesh_index)
|
||||
|
||||
nodes = []
|
||||
for derived_mesh_name in self.data.derived_mesh_names:
|
||||
nodes.append(derived_mesh_name)
|
||||
|
||||
cmds.select(nodes, replace=True)
|
||||
|
||||
cmds.select(mesh_name, add=True)
|
||||
cmds.blendShape(name=f"{mesh_name}{self.blend_shape_name_postfix}")
|
||||
cmds.delete(f"{self.blend_shape_group_prefix}{mesh_name}")
|
||||
|
||||
def create_blend_shapes(
|
||||
self, add_mesh_name_to_blend_shape_channel_name: bool
|
||||
) -> None:
|
||||
"""
|
||||
Builds all the derived meshes using the provided mesh and the blend shapes data of the DNA.
|
||||
|
||||
@type add_mesh_name_to_blend_shape_channel_name: bool
|
||||
@param add_mesh_name_to_blend_shape_channel_name: A flag representing whether mesh name of blend shape channel is added to name when creating it
|
||||
"""
|
||||
|
||||
logging.info("adding derived meshes...")
|
||||
|
||||
group: str = cmds.group(
|
||||
empty=True,
|
||||
name=f"{self.blend_shape_group_prefix}{self.dna.get_mesh_name(self.mesh_index)}",
|
||||
)
|
||||
|
||||
self.data.derived_mesh_names = []
|
||||
blend_shapes = self.dna.get_blend_shapes(self.mesh_index)
|
||||
for blend_shape_target_index, blend_shape in enumerate(blend_shapes):
|
||||
|
||||
self.create_blend_shape(
|
||||
blend_shape_target_index,
|
||||
blend_shape.channel,
|
||||
group,
|
||||
add_mesh_name_to_blend_shape_channel_name,
|
||||
)
|
||||
cmds.setAttr(f"{group}.visibility", 0)
|
||||
|
||||
def create_blend_shape(
|
||||
self,
|
||||
blend_shape_target_index: int,
|
||||
blend_shape_channel: int,
|
||||
group: str,
|
||||
add_mesh_name_to_blend_shape_channel_name: bool,
|
||||
) -> None:
|
||||
"""
|
||||
Builds a single derived mesh using the provided mesh and the blend shape data of the DNA.
|
||||
|
||||
|
||||
@type blend_shape_target_index: int
|
||||
@param blend_shape_target_index: Used for getting a delta value representing the value change concerning the blend shape.
|
||||
|
||||
@type blend_shape_channel: int
|
||||
@param blend_shape_channel: Used for getting the blend shape name from the DNA.
|
||||
|
||||
@type group: str
|
||||
@param group: The transform the new meshes will be added to.
|
||||
|
||||
@type add_mesh_name_to_blend_shape_channel_name: bool
|
||||
@param add_mesh_name_to_blend_shape_channel_name: A flag representing whether mesh name of blend shape channel is added to name when creating it
|
||||
"""
|
||||
|
||||
new_vert_layout = self.get_vertex_positions_from_dna_vertex_positions()
|
||||
|
||||
zipped_deltas = self.dna.get_blend_shape_target_deltas_with_vertex_id(
|
||||
self.mesh_index, blend_shape_target_index
|
||||
)
|
||||
for zipped_delta in zipped_deltas:
|
||||
delta: Point3 = zipped_delta[1]
|
||||
new_vert_layout[zipped_delta[0]] += MPoint(
|
||||
delta.x,
|
||||
delta.y,
|
||||
delta.z,
|
||||
)
|
||||
|
||||
new_mesh = self.fn_mesh.create(
|
||||
new_vert_layout, self.data.polygon_faces, self.data.polygon_connects
|
||||
)
|
||||
derived_name = self.dna.get_blend_shape_channel_name(blend_shape_channel)
|
||||
name = (
|
||||
f"{self.dna.geometry_meshes[self.mesh_index].name}__{derived_name}"
|
||||
if add_mesh_name_to_blend_shape_channel_name
|
||||
else derived_name
|
||||
)
|
||||
self.dag_modifier.renameNode(new_mesh, name)
|
||||
self.dag_modifier.doIt()
|
||||
|
||||
dag = MFnDagNode(Maya.get_element(group))
|
||||
dag.addChild(new_mesh)
|
||||
|
||||
self.data.derived_mesh_names.append(name)
|
||||
|
||||
def add_skin_cluster(self, joint_names: List[str], joint_ids: List[int]) -> None:
|
||||
"""
|
||||
Adds skin cluster to the mesh
|
||||
|
||||
@type joint_names: List[str]
|
||||
@param joint_names: Joint names needed for adding the skin cluster
|
||||
|
||||
@type joint_ids: List[int]
|
||||
@param joint_ids: Joint indices needed for setting skin weights
|
||||
"""
|
||||
|
||||
mesh_name = self.dna.get_mesh_name(self.mesh_index)
|
||||
|
||||
self._add_skin_cluster(mesh_name, joint_names)
|
||||
self.set_skin_weights(mesh_name, joint_ids)
|
||||
|
||||
def _add_skin_cluster(self, mesh_name: str, joint_names: List[str]) -> None:
|
||||
"""
|
||||
Creates a skin cluster object.
|
||||
|
||||
@type mesh_name: str
|
||||
@param mesh_name: The mesh name that is used for skin cluster naming.
|
||||
|
||||
@type joints: List[Joint]
|
||||
@param joints: List of joints used for adding the skin cluster.
|
||||
"""
|
||||
|
||||
logging.info("adding skin cluster...")
|
||||
maximum_influences = self.dna.get_maximum_influence_per_vertex(self.mesh_index)
|
||||
|
||||
cmds.select(joint_names[0], replace=True)
|
||||
|
||||
cmds.select(mesh_name, add=True)
|
||||
skin_cluster = cmds.skinCluster(
|
||||
toSelectedBones=True,
|
||||
name=f"{mesh_name}_{self.skin_cluster_suffix}",
|
||||
maximumInfluences=maximum_influences,
|
||||
skinMethod=0,
|
||||
obeyMaxInfluences=True,
|
||||
)
|
||||
cmds.skinCluster(
|
||||
skin_cluster, edit=True, addInfluence=joint_names[1:], weight=0
|
||||
)
|
||||
|
||||
def set_skin_weights(self, mesh_name: str, joint_ids: List[int]) -> None:
|
||||
"""
|
||||
Sets the skin weights attributes.
|
||||
|
||||
@type mesh_name: str
|
||||
@param mesh_name: The mesh name that is used for getting the skin cluster name.
|
||||
|
||||
@type joint_ids: List[int]
|
||||
@param joint_ids: List of joint indices used for setting the skin weight attribute.
|
||||
"""
|
||||
|
||||
logging.info("adding skin weights...")
|
||||
skin_weights = self.dna.get_skin_weight_matrix_for_mesh(self.mesh_index)
|
||||
|
||||
# import skin weights
|
||||
temp_str = f"{mesh_name}_{self.skin_cluster_suffix}.wl["
|
||||
for vertex_id, skin_weight in enumerate(skin_weights):
|
||||
if not (vertex_id + 1) % SKIN_WEIGHT_PRINT_RANGE:
|
||||
logging.info(f"\t{vertex_id + 1} / {len(skin_weights)}")
|
||||
vertex_infos = skin_weight
|
||||
|
||||
# set all skin weights to zero
|
||||
vertex_string = f"{temp_str}{str(vertex_id)}].w["
|
||||
cmds.setAttr(f"{vertex_string}0]", 0.0)
|
||||
|
||||
# import skin weights
|
||||
for vertex_info in vertex_infos:
|
||||
cmds.setAttr(
|
||||
f"{vertex_string}{str(joint_ids.index(vertex_info[0]))}]",
|
||||
float(vertex_info[1]),
|
||||
)
|
||||
if len(skin_weights) % SKIN_WEIGHT_PRINT_RANGE != 0:
|
||||
logging.info(f"\t{len(skin_weights)} / {len(skin_weights)}")
|
204
scripts/builder/maya/skin_weights.py
Normal file
204
scripts/builder/maya/skin_weights.py
Normal file
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from typing import List, Tuple, Union
|
||||
|
||||
from maya import cmds, mel
|
||||
from maya.api.OpenMaya import MFnMesh, MGlobal
|
||||
from maya.api.OpenMayaAnim import MFnSkinCluster
|
||||
|
||||
from ...builder.maya.util import Maya
|
||||
from ...common import DNAViewerError
|
||||
|
||||
|
||||
class MayaSkinWeights:
|
||||
"""
|
||||
A class used for reading and storing skin weight related data needed for adding skin clusters
|
||||
"""
|
||||
|
||||
no_of_influences: int
|
||||
skinning_method: int
|
||||
joints: List[str]
|
||||
vertices_info: List[List[Union[int, float]]]
|
||||
|
||||
def __init__(self, skin_cluster: MFnSkinCluster, mesh_name: str) -> None:
|
||||
self.no_of_influences = cmds.skinCluster(skin_cluster.name(), q=True, mi=True)
|
||||
|
||||
self.skinning_method = cmds.skinCluster(skin_cluster.name(), q=True, sm=True)
|
||||
|
||||
self.joints = self.get_skin_cluster_influence(skin_cluster)
|
||||
|
||||
self.vertices_info = self.get_skin_weights_for_mesh_name(
|
||||
skin_cluster, mesh_name
|
||||
)
|
||||
|
||||
def get_skin_cluster_influence(self, skin_cluster: MFnSkinCluster) -> List[str]:
|
||||
"""
|
||||
Gets a list of joint names that are influences to the skin cluster.
|
||||
|
||||
@type skin_cluster: MFnSkinCluster
|
||||
@param skin_cluster: The functionalities of a maya skin cluster object
|
||||
|
||||
@rtype: List[str]
|
||||
@returns: The list if names of the joints that influence the skin cluster
|
||||
"""
|
||||
|
||||
influences: List[str] = cmds.skinCluster(skin_cluster.name(), q=True, inf=True)
|
||||
if influences and not isinstance(influences[0], str):
|
||||
influences = [obj.name() for obj in influences]
|
||||
return influences
|
||||
|
||||
def get_skin_weights_for_mesh_name(
|
||||
self,
|
||||
skin_cluster: MFnSkinCluster,
|
||||
mesh_name: str,
|
||||
) -> List[List[Union[int, float]]]:
|
||||
"""
|
||||
Gets the skin weights concerning the given mesh.
|
||||
|
||||
@type skin_cluster: MFnSkinCluster
|
||||
@param skin_cluster: The functionalities of a maya skin cluster object
|
||||
|
||||
@type mesh_name: str
|
||||
@param mesh_name: The name of the mesh
|
||||
|
||||
@rtype: List[List[Union[int, float]]]
|
||||
@returns: A list of list of weight indices and the weight values
|
||||
"""
|
||||
|
||||
mesh = Maya.get_element(mesh_name)
|
||||
components = MGlobal.getSelectionListByName(f"{mesh_name}.vtx[*]").getComponent(
|
||||
0
|
||||
)[1]
|
||||
weights_data, chunk = skin_cluster.getWeights(mesh, components)
|
||||
iterator = [
|
||||
weights_data[i : i + chunk] for i in range(0, len(weights_data), chunk)
|
||||
]
|
||||
|
||||
vertices_info = []
|
||||
for weights in iterator:
|
||||
vertex_weights: List[float] = []
|
||||
vertices_info.append(vertex_weights)
|
||||
|
||||
for i, weight in enumerate(weights):
|
||||
if weight:
|
||||
vertex_weights.append(i)
|
||||
vertex_weights.append(weight)
|
||||
return vertices_info
|
||||
|
||||
|
||||
def get_skin_weights_data(mesh_name: str) -> Tuple[MFnMesh, MFnSkinCluster]:
|
||||
"""
|
||||
Gets the maya objects that manipulate the mesh node and the skin cluster for a given mesh name.
|
||||
|
||||
@type mesh_name: str
|
||||
@param mesh_name: The name of the mesh
|
||||
|
||||
@rtype: Tuple[MFnMesh, MFnSkinCluster]
|
||||
@returns: The maya object that manipulate the mesh node and the skin cluster for a given mesh name.
|
||||
"""
|
||||
|
||||
skin_cluster_name = mel.eval(f"findRelatedSkinCluster {mesh_name}")
|
||||
if skin_cluster_name:
|
||||
skin_cluster = MFnSkinCluster(Maya.get_element(skin_cluster_name))
|
||||
mesh_node = MFnMesh(Maya.get_element(mesh_name))
|
||||
return mesh_node, skin_cluster
|
||||
raise DNAViewerError(f"Unable to find skin for given mesh: {mesh_name}")
|
||||
|
||||
|
||||
def get_skin_weights_from_scene(mesh_name: str) -> MayaSkinWeights:
|
||||
"""
|
||||
Gets the instance of this class filled with data from the scene for a given mesh name.
|
||||
|
||||
@type mesh_name: str
|
||||
@param mesh_name: The mesh name
|
||||
|
||||
@rtype: MayaSkinWeights
|
||||
@returns: An instance of this class with the data from the scene
|
||||
"""
|
||||
|
||||
_, skin_cluster = get_skin_weights_data(mesh_name)
|
||||
|
||||
return MayaSkinWeights(skin_cluster, mesh_name)
|
||||
|
||||
|
||||
def get_file_joint_mappings(
|
||||
skin_weights: MayaSkinWeights, skin_cluster: MFnSkinCluster
|
||||
) -> List[int]:
|
||||
"""
|
||||
Returns a list of object indices representing the influences concerning the joint names specified in the skin weight model.
|
||||
|
||||
@type skin_weights: MayaSkinWeights
|
||||
@param skin_weights: The instance of the model storing data about skin weights
|
||||
|
||||
@type skin_cluster: MFnSkinCluster
|
||||
@param skin_cluster: An object for working with functions concerning a skin cluster in maya
|
||||
|
||||
@rtype: List[int]
|
||||
@returns: a list of indices representing the influences concerning the given joints
|
||||
"""
|
||||
|
||||
file_joint_mapping: List[int] = []
|
||||
for joint_name in skin_weights.joints:
|
||||
file_joint_mapping.append(
|
||||
skin_cluster.indexForInfluenceObject(Maya.get_element(joint_name))
|
||||
)
|
||||
return file_joint_mapping
|
||||
|
||||
|
||||
def set_skin_weights_to_scene(mesh_name: str, skin_weights: MayaSkinWeights) -> None:
|
||||
"""
|
||||
Sets the skin weights to the scene.
|
||||
|
||||
@type mesh_name: str
|
||||
@param mesh_name: The mesh name
|
||||
|
||||
@type skin_weights: MayaSkinWeights
|
||||
@param skin_weights: The object containing data that need to be set to the scene.
|
||||
"""
|
||||
|
||||
mesh_node, skin_cluster = get_skin_weights_data(mesh_name)
|
||||
|
||||
file_joint_mapping = get_file_joint_mappings(skin_weights, skin_cluster)
|
||||
|
||||
import_skin_weights(skin_cluster, mesh_node, skin_weights, file_joint_mapping)
|
||||
|
||||
logging.info("Set skin weights ended.")
|
||||
|
||||
|
||||
def import_skin_weights(
|
||||
skin_cluster: MFnSkinCluster,
|
||||
mesh_node: MFnMesh,
|
||||
skin_weights: MayaSkinWeights,
|
||||
file_joint_mapping: List[int],
|
||||
) -> None:
|
||||
"""
|
||||
Imports the skin weights to the scene using the joint mapping and the data provided in the model containing the weights.
|
||||
|
||||
@type skin_cluster: MFnSkinCluster
|
||||
@param skin_cluster: An object for working with functions concerning a skin cluster in maya
|
||||
|
||||
@type mesh_node: MFnMesh
|
||||
@param mesh_node: An object for working with functions concerning meshes in maya
|
||||
|
||||
@type skin_weights: MayaSkinWeights
|
||||
@param skin_weights: The instance of the model storing data about skin weights
|
||||
|
||||
@type file_joint_mapping: List[int]
|
||||
@param file_joint_mapping: a list of indices representing the influences concerning joints
|
||||
"""
|
||||
|
||||
temp_str = f"{skin_cluster.name()}.wl["
|
||||
for vtx_id in range(cmds.polyEvaluate(mesh_node.name(), vertex=True)):
|
||||
vtx_info = skin_weights.vertices_info[vtx_id]
|
||||
|
||||
vtx_str = f"{temp_str}{str(vtx_id)}].w["
|
||||
|
||||
cmds.setAttr(f"{vtx_str}0]", 0.0)
|
||||
|
||||
for i in range(0, len(vtx_info), 2):
|
||||
cmds.setAttr(
|
||||
f"{vtx_str}{str(file_joint_mapping[int(vtx_info[i])])}]",
|
||||
vtx_info[i + 1],
|
||||
)
|
84
scripts/builder/maya/util.py
Normal file
84
scripts/builder/maya/util.py
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Union
|
||||
|
||||
from maya.api.OpenMaya import (
|
||||
MDagPath,
|
||||
MFnDagNode,
|
||||
MFnTransform,
|
||||
MGlobal,
|
||||
MSpace,
|
||||
MVector,
|
||||
)
|
||||
|
||||
from ...common import DNAViewerError
|
||||
|
||||
|
||||
class Maya:
|
||||
"""A utility class used for interfacing with maya transforms"""
|
||||
|
||||
@staticmethod
|
||||
def get_element(name: str) -> Union[MDagPath, MFnDagNode]:
|
||||
"""gets the Union[MDagPath, MFnDagNode] object of the element with the given name
|
||||
|
||||
@type name: str
|
||||
@param name: The name of the element to be retrieved
|
||||
|
||||
@rtype: Union[MDagPath, MFnDagNode]
|
||||
@returns: A OpenMaya object representing the given element
|
||||
"""
|
||||
try:
|
||||
sellist = MGlobal.getSelectionListByName(name)
|
||||
except Exception as exception:
|
||||
raise DNAViewerError(f"Element with name:{name} not found!") from exception
|
||||
|
||||
try:
|
||||
return sellist.getDagPath(0)
|
||||
except Exception:
|
||||
return sellist.getDependNode(0)
|
||||
|
||||
@staticmethod
|
||||
def get_transform(name: str) -> MFnTransform:
|
||||
"""gets the transform of the element with the given name
|
||||
|
||||
@type element: str
|
||||
@param element: The element name that we want the transform of
|
||||
|
||||
@rtype: MFnTransform
|
||||
@returns: A MFnTransform object representing the given elements transform
|
||||
"""
|
||||
return MFnTransform(Maya.get_element(name))
|
||||
|
||||
@staticmethod
|
||||
def get_translation(element: str, space: int = MSpace.kObject) -> MVector:
|
||||
"""gets the translation of the element with the given name
|
||||
|
||||
@type element: str
|
||||
@param element: The element name that we want the translation of
|
||||
|
||||
@type space: str
|
||||
@param space: A string value representing the translation space (default is "world")
|
||||
|
||||
@rtype: MVector
|
||||
@returns: A MVector object representing the given elements translation
|
||||
"""
|
||||
return MFnTransform(Maya.get_element(element)).translation(space)
|
||||
|
||||
@staticmethod
|
||||
def set_translation(
|
||||
element: str, translation: MVector, space: int = MSpace.kObject
|
||||
) -> None:
|
||||
"""sets the translation of the element with the given name
|
||||
|
||||
@type element: str
|
||||
@param element: The element name that we want to set the translation of
|
||||
|
||||
@type translation: MVector
|
||||
@param translation: The new translation value
|
||||
|
||||
@type space: str
|
||||
@param space: A string value representing the translation space (default is "object")
|
||||
"""
|
||||
element_obj = Maya.get_transform(element)
|
||||
element_obj.setTranslation(translation, space)
|
Reference in New Issue
Block a user