Skip to content

Commit

Permalink
Shifter embed guide information in rig Closes #248
Browse files Browse the repository at this point in the history
  • Loading branch information
miquelcampos committed Sep 6, 2023
1 parent 07ed74c commit 88bf4c8
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 23 deletions.
17 changes: 17 additions & 0 deletions release/scripts/mgear/shifter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from mgear import shifter_epic_components
from mgear.shifter import naming
import importlib
from mgear.core import utils

PY2 = sys.version_info[0] == 2

Expand Down Expand Up @@ -353,6 +354,18 @@ def postCustomStep(self):
customSteps = [cs.replace("\\", "/") for cs in customSteps]
self.customStep(customSteps)

# @utils.timeFunc
def get_guide_data(self):
"""Get the guide data
Returns:
str: The guide data
"""
if self.guide.guide_template_dict:
return json.dumps(self.guide.guide_template_dict)
else:
return json.dumps(self.guide.get_guide_template_dict())

def initialHierarchy(self):
"""Build the initial hierarchy of the rig.
Expand Down Expand Up @@ -422,6 +435,10 @@ def initialHierarchy(self):
self.rigCtlTags = self.model.addAttr("rigCtlTags", at="message", m=1)
self.rigScriptNodes = self.model.addAttr("rigScriptNodes", at="message", m=1)

self.guide_data_att = attribute.addAttribute(
self.model, "guide_data", "string", self.get_guide_data()
)

# ------------------------- -------------------------
# Global Ctl
if self.options["worldCtl"]:
Expand Down
22 changes: 2 additions & 20 deletions release/scripts/mgear/shifter/game_tools_disconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
from mgear.core import pyqt
from mgear.shifter.utils import get_deformer_joints
from mgear.vendor.Qt import QtCore, QtWidgets

if sys.version_info[0] == 2:
Expand Down Expand Up @@ -488,27 +489,8 @@ def exportAssetAssembly(name, rigTopNode, meshTopNode, path, postScript=None):
# check the folder and script
# if the target name exist abort and request another name

deformer_jnts_node = None
deformer_jnts = get_deformer_joints(rigTopNode)

for i in range(0, 100):
try:
potential_node = rigTopNode.rigGroups[i].connections()[0]
except IndexError:
break

if potential_node.name().endswith("_deformers_grp"):
deformer_jnts_node = potential_node
break

if deformer_jnts_node:
deformer_jnts = deformer_jnts_node.members()
else:
deformer_jnts = None

if not deformer_jnts:
pm.displayError(
"{} is empty. The tool can't find any joint".format(meshTopNode)
)
# export connections and cut joint connections
file_path = os.path.join(path, name + ".jmm")
dm_nodes = exportConnections(
Expand Down
5 changes: 2 additions & 3 deletions release/scripts/mgear/shifter/guide.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,9 +439,6 @@ def addParameters(self):
self.p_joint_padding = self.addParam(
"joint_index_padding", "long", 0, 0, 99)




def setFromSelection(self):
"""Set the guide hierarchy from selection."""
selection = pm.ls(selection=True)
Expand Down Expand Up @@ -553,6 +550,8 @@ def setFromHierarchy(self, root, branch=True):

def set_from_dict(self, guide_template_dict):

self.guide_template_dict = guide_template_dict

r_dict = guide_template_dict['guide_root']

self.setParamDefValuesFromDict(r_dict["param_values"])
Expand Down
86 changes: 86 additions & 0 deletions release/scripts/mgear/shifter/guide_manager.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import sys
import json

from mgear.core import pyqt


import pymel.core as pm
from pymel.core import datatypes
from mgear.core import transform

import mgear
from mgear import shifter
import mgear.shifter.io as sio
from mgear.shifter.utils import get_root_joint

##############################
# Helper Functions
Expand Down Expand Up @@ -157,3 +162,84 @@ def extract_controls(*args):
pm.displayWarning(
"{}: Is not a valid mGear control".format(x.name())
)


# Extract guide from rigs


def extract_guide_from_rig(*args):
"""
Extract guide data from a selected or default rig root and import guide.
Returns:
pm.PyNode: Returns the rig root node if successful, otherwise None.
"""
selection = pm.ls(selection=True)
if not selection:
selection = pm.ls("rig")
if not selection or not selection[0].hasAttr("is_rig"):
mgear.log(
"Not rig root selected or found.\nSelect the rig root",
mgear.sev_error,
)
return
if selection[0].hasAttr("is_rig"):
guide_dict = selection[0].guide_data.get()
sio.import_guide_template(conf=json.loads(guide_dict))
return selection[0]


def get_ordered_child(jnt):
"""
Retrieve ordered child nodes under a joint.
Args:
jnt (pm.PyNode): The joint node to start search from.
Returns:
list: List of ordered child nodes if node is joint.
"""
if jnt.type() == "joint":
pm.select(jnt, hi=True, r=True)
return pm.selected()
else:
pm.displayWarning("Object: {} is not of type joint".format(jnt.name()))


def match_guide_to_joint_pos_ori(jnt_list, ori=False):
"""
Match guide positions and orientations to joint nodes.
Args:
jnt_list (list): List of joint nodes.
ori (bool): Whether to match orientation. Default is True.
"""
pm.displayInfo(
"Only EPIC components can be match. Other component will be skipped"
)
if jnt_list:
for j in jnt_list:
if j.hasAttr("guide_relative"):
for g_relative in pm.ls(j.guide_relative.get()):
if g_relative.hasAttr("isGearGuide"):
gmw = g_relative.getMatrix(worldSpace=True)
if ori:
tm = datatypes.TransformationMatrix(gmw)
sWM = j.getMatrix(worldSpace=True)
sWM = transform.setMatrixScale(
sWM, tm.getScale(space="world")
)
else:
jwp = j.getTranslation(space="world")
sWM = transform.setMatrixPosition(gmw, jwp)
g_relative.setMatrix(sWM, worldSpace=True)


def extract_match_guide_from_rig(*args):
"""
Extract and match guide data based on joint positions and orientations.
"""
rig_root = extract_guide_from_rig()
if rig_root:
root_jnt = get_root_joint(rig_root)
match_guide_to_joint_pos_ori(get_ordered_child(root_jnt))
21 changes: 21 additions & 0 deletions release/scripts/mgear/shifter/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ def install():
str_export_guide_template,
"mgear_log-out.svg",
),
(
"Extract Guide From Rig",
str_extract_guide_from_rig,
"mgear_download.svg",
),
(
"Extract and Match Guide From Rig",
str_extract_match_guide_from_rig,
"mgear_download.svg",
),
("-----", None),
(None, guide_template_samples_submenu),
("-----", None),
Expand Down Expand Up @@ -331,3 +341,14 @@ def guide_template_samples_submenu(parent_menu_id):
from mgear.shifter.game_tools_fbx import fbx_exporter
fbx_exporter.openFBXExporter()
"""


str_extract_guide_from_rig = """
from mgear.shifter import guide_manager
guide_manager.extract_guide_from_rig()
"""

str_extract_match_guide_from_rig = """
from mgear.shifter import guide_manager
guide_manager.extract_match_guide_from_rig()
"""
62 changes: 62 additions & 0 deletions release/scripts/mgear/shifter/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from pymel import core as pm


def get_deformer_joint_grp(rigTopNode):
"""
Searches for a node group that ends with "_deformers_grp" under the given rigTopNode.
Args:
rigTopNode (pm.PyNode): The top-level rig node where we start the search.
Returns:
pm.PyNode or None: Returns the deformer joints node if found, otherwise returns None.
"""
deformer_jnts_node = None
for i in range(0, 100):
try:
potential_node = rigTopNode.rigGroups[i].connections()[0]
except IndexError:
break

if potential_node.name().endswith("_deformers_grp"):
deformer_jnts_node = potential_node
break
return deformer_jnts_node


def get_deformer_joints(rigTopNode):
"""
Retrieves the deformer joints under the given rigTopNode.
Args:
rigTopNode (pm.PyNode): The top-level rig node to search under.
Returns:
list or None: Returns a list of deformer joints if found, otherwise displays an error and returns None.
"""
deformer_jnts_node = get_deformer_joint_grp(rigTopNode)
if deformer_jnts_node:
deformer_jnts = deformer_jnts_node.members()
else:
deformer_jnts = None

if not deformer_jnts:
pm.displayError(
"{} is empty. The tool can't find any joint".format(rigTopNode)
)
return deformer_jnts


def get_root_joint(rigTopNode):
"""
Retrieves the root joint of the rig from the rigTopNode.
Args:
rigTopNode (pm.PyNode): The top-level rig node to search under.
Returns:
pm.PyNode: Returns the root joint node.
"""
jnt_org = rigTopNode.jnt_vis.listConnections()[0]
root_jnt = jnt_org.child(0)
return root_jnt

0 comments on commit 88bf4c8

Please sign in to comment.