Add OpenPose_full renderer plugin for Maya

Maya API 2.0 plugin that maps a skeleton to OpenPose_full keypoints
(BODY_25 or COCO body, 21+21 hand, 70 face) via an openposeCharacter
node, with a live Viewport 2.0 draw override, an "OpenPose" viewport
renderer plus openposeRenderSequence for PNG output, and a PySide2
mapping editor. Face can also be driven procedurally from ARKit's 52
blendshape weights instead of facial joints, for rigs with no facial
skeleton. Includes VS Code debug configs and Maya test scenes/renders.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 08:41:57 +08:00
commit e2f2e6668a
23 changed files with 2999 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
__pycache__/
*.pyc
*.pyo
+46
View File
@@ -0,0 +1,46 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Maya: Attach (debugpy)",
"type": "debugpy",
"request": "attach",
"connect": { "host": "localhost", "port": 5678 },
"pathMappings": [
{ "localRoot": "${workspaceFolder}/python", "remoteRoot": "${workspaceFolder}/python" },
{ "localRoot": "${workspaceFolder}/plug-ins", "remoteRoot": "${workspaceFolder}/plug-ins" }
],
"justMyCode": false
},
{
"name": "mayapy 2024: Run current file",
"type": "debugpy",
"request": "launch",
"python": "C:\\Program Files\\Autodesk\\Maya2024\\bin\\mayapy.exe",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": false,
"env": { "PYTHONPATH": "${workspaceFolder}/python" }
},
{
"name": "mayapy 2023: Run current file",
"type": "debugpy",
"request": "launch",
"python": "C:\\Program Files\\Autodesk\\Maya2023\\bin\\mayapy.exe",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": false,
"env": { "PYTHONPATH": "${workspaceFolder}/python" }
},
{
"name": "mayapy 2022: Run current file",
"type": "debugpy",
"request": "launch",
"python": "C:\\Program Files\\Autodesk\\Maya2022\\bin\\mayapy.exe",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": false,
"env": { "PYTHONPATH": "${workspaceFolder}/python" }
}
]
}
+4
View File
@@ -0,0 +1,4 @@
{
"python.defaultInterpreterPath": "C:\\Program Files\\Autodesk\\Maya2024\\bin\\mayapy.exe",
"python.analysis.extraPaths": ["${workspaceFolder}/python"]
}
+176
View File
@@ -0,0 +1,176 @@
# OpenPose Renderer for Maya
Turns an animated Maya skeleton into OpenPose-style stick-figure images (up
to 25 BODY_25 body/foot + 21+21 hand + 70 face = 137 keypoints), for use
with ControlNet and similar tools. Built entirely on Maya's Python API 2.0.
Targets Maya 2022-2024 (Python 3.9/3.10, PySide2 -- PySide6 also detected
automatically if present).
Body output supports two OpenPose skeleton formats, selectable per
character (see *Body model* below): **BODY_25** (25 points, includes
MidHip + feet) and **COCO** (18 points, no MidHip/feet, Neck connects
directly to each hip -- OpenPose's original/legacy body model). Both use
the exact same joint mapping; switching formats does not require remapping.
## Install
1. Copy (or symlink) this whole `PoseRenderer` folder somewhere on disk.
2. Add its `plug-ins` folder to `MAYA_PLUG_IN_PATH`, e.g. in
`Maya.env`:
```
MAYA_PLUG_IN_PATH = C:\path\to\PoseRenderer\plug-ins
```
(The plugin adds its own `python/` folder to `sys.path` automatically at
load time -- you do not need to set `PYTHONPATH` yourself.)
3. In Maya: **Windows > Settings/Preferences > Plug-in Manager**, find
`openposeRenderer.py`, and check *Loaded* (and *Auto load* if desired).
## Usage
### 1. Map a character
- Run `cmds.openposeCreateCharacter(name="myCharacter")` (or use the
mapping editor's *Create Character* button) to create one
`openposeCharacter` node per person you want to output. Scenes with
multiple `openposeCharacter` nodes render as multi-person OpenPose output.
- Open the mapping editor: `cmds.openposeShowMappingEditor()`.
- Select the character node in the dropdown, select a joint in the
viewport, click *Set from Selected* next to the OpenPose_full slot it
corresponds to. Repeat for as many of the 137 slots as your rig has
joints for -- unmapped slots simply render with zero confidence (no
point/limb drawn), matching how OpenPose itself represents undetected
keypoints. Face and hand slots are optional; body-only rigs work fine.
- Mappings are plain Maya connections (`joint.message ->
openposeCharacter.targetJoint[i]`), so they save with the scene and
survive joint renames/reparenting.
### 2. Viewport preview
With an `openposeCharacter` node in the scene, a colored OpenPose-style
skeleton overlay is drawn live over the mapped joints in every viewport
(toggle per-character via the node's `enableDisplay` attribute; tune with
`jointRadiusScale` / `limbThicknessScale`). This is for QA of the mapping,
independent of rendering.
### Body model (BODY_25 / COCO)
Each `openposeCharacter` node has a `bodyModel` enum attribute (`BODY_25`,
the default, or `COCO`), settable in the mapping editor's *Body Model*
dropdown or directly via `cmds.setAttr("myCharacter.bodyModel", 1)` (0 =
BODY_25, 1 = COCO). It controls both the live viewport overlay and the
rendered output for that character:
- **BODY_25**: all 25 body/foot points -- MidHip, hips/knees/ankles, eyes,
ears, and the 6 foot points (heels + big/small toes).
- **COCO**: the original 18-point OpenPose body model -- no MidHip, no
feet, and the neck connects directly to each hip instead of through a
pelvis point. Slots you mapped for MidHip/feet are simply not drawn in
this mode; nothing needs to be remapped to switch.
Hands and face are unaffected by this setting either way. Rendered from the
same mapped HumanIK-named test rig (`test/openpose_humanik_test.ma`):
| BODY_25 | COCO |
| --- | --- |
| ![BODY_25 output](test/openpose_humanik_test_preview.png) | ![COCO output](test/openpose_humanik_test_preview_coco.png) |
### Face source: facial joints, or ARKit 52 blendshapes
Most facial rigs have no joints at all -- they're driven entirely by
blendShape targets, often named per Apple's ARKit convention (from an
iPhone/TrueDepth mocap pipeline, Live Link Face, or a hand-keyed rig built
to match it). Each `openposeCharacter` has a `faceSource` enum (mapping
editor's *Face Source* dropdown, or `cmds.setAttr("myCharacter.faceSource",
1)`; 0 = `Joints`, 1 = `ARKitBlendshapes`) controlling where its 70 face
keypoints come from:
- **Joints** (default): unchanged from above -- map `Face_*` slots to
facial joints if your rig has them.
- **ARKitBlendshapes**: the 70 face keypoints are instead computed
procedurally from 52 connectable `blendshapeWeight[0..51]` float slots
(in Apple's canonical `ARFaceAnchor.BlendShapeLocation` order/naming --
`eyeBlinkLeft`, `jawOpen`, `mouthSmileLeft`, etc.). Select your
blendShape node (or an ARKit-mocap-driven control) and click *Auto-Connect
ARKit Blendshapes from Selected* in the mapping editor -- it connects
every one of the 52 canonical names that exists as an attribute on the
selected node. The resulting face is anchored at the character's mapped
`Body_Nose` joint and billboards to always face whichever camera is
drawing (viewport or render camera), sized by the `faceScale` attribute
(world units; default 15 -- tune to your scene's unit scale).
**Accuracy caveat:** there is no published, authoritative "which ARKit
weight moves which OpenPose landmark, by how much" table anywhere (unlike
BODY_25/COCO, which OpenPose's own source defines exactly). The neutral
face template and per-blendshape displacement rules in
`face_blendshapes.py` are a hand-authored, geometrically-reasoned
approximation -- each shape nudges only the anatomically obvious nearby
landmarks in an intuitive direction. It produces a recognizable, responsive
OpenPose-style face suitable for ControlNet conditioning, but is not a
biomechanically precise simulation. `jawForward` and `tongueOut` have no
representable effect on a frontal 2D landmark set and are no-ops.
Rendered from `test/openpose_arkit_face_test.ma` (a control node with all 52
ARKit-named attributes, auto-connected -- see that scene for the pattern):
| Neutral | `jawOpen` + smile + raised brows |
| --- | --- |
| ![Neutral face](test/openpose_arkit_face_test_neutral.png) | ![Expression face](test/openpose_arkit_face_test_expression.png) |
### 3. Render OpenPose images
- Switch a viewport panel's **Renderer** menu to **OpenPose** to make it
the active/selectable renderer for that panel (this is the Viewport 2.0
render override integration point).
- To actually generate the OpenPose PNG sequence, run:
```python
cmds.openposeRenderSequence(
startFrame=1, endFrame=48,
camera="renderCam", # optional; defaults to the active view's camera
outputDirectory="C:/out/openpose", # optional; defaults to <workspace>/images/openpose
showInRenderView=True,
)
```
Each frame is projected through the given camera at the scene's render
resolution (`defaultResolution` node), rasterized as a black-background,
OpenPose-colored image (limbs under joints, canonical BODY_25 palette,
per-finger rainbow hand colors, white face dots), written to disk, and
pushed into Maya's Render View so you can watch it render like any other
renderer.
## Debugging (VS Code)
`.vscode/launch.json` provides:
- **`Maya: Attach (debugpy)`** -- attaches to a running, interactive Maya.
One-time setup: `"<Maya install>\bin\mayapy.exe" -m pip install debugpy`.
Each session, run `scripts/start_debug_server.py` inside Maya's Script
Editor first (see that file's docstring), then launch this config from
VS Code to hit breakpoints anywhere in `plug-ins/` or
`python/openpose_renderer/` -- including the draw override and render
override callbacks, which only run inside a real Maya viewport.
- **`mayapy 2022/2023/2024: Run current file`** -- runs the file you have
open through that Maya version's own `mayapy.exe` interpreter (with
`python/` on `PYTHONPATH`), for headless scripts that call
`maya.standalone.initialize()` themselves. Useful for quick iteration on
`constants.py`/`projector.py`/`rasterizer.py` without opening Maya's UI.
## Notes / known limitations
- Colors/connectivity: BODY_25 and COCO keypoint layout and limb
connectivity are taken verbatim from OpenPose's own
`POSE_BODY_25_BODY_PART_PAIRS` / COCO section of `POSE_BODY_PART_PAIRS`
(`src/openpose/pose/poseParameters.cpp`). BODY_25's colors are likewise
verbatim from `POSE_BODY_25_COLORS_RENDER_GPU`. COCO limb colors, and all
hand/face colors, are generated from an HSV hue wheel (rainbow per limb
or finger, white face dots) reproducing OpenPose's own visual scheme
rather than a hardcoded transcription of its longer, generated gradient
tables.
- Projection does not attempt to replicate Maya's film-gate/overscan/fit
reconciliation pixel-for-pixel -- for correct results, set the render
camera's film aspect ratio to match the scene's render resolution, as you
would for any Maya render.
- The "OpenPose" Renderer-menu entry uses a standard scene/HUD/present
Viewport 2.0 operation chain (so switching to it always leaves the panel
usable); the actual OpenPose image generation happens via
`openposeRenderSequence`, not via GPU render-target pixel substitution
inside the viewport override.
+75
View File
@@ -0,0 +1,75 @@
"""Maya plugin entry point: OpenPose_full renderer (Maya API 2.0).
Registers, in order:
* the ``openposeCharacter`` mapping node (mapping_node.py)
* its Viewport 2.0 draw override, the live skeleton overlay (draw_override.py)
* the ``openposeCreateCharacter`` / ``openposeShowMappingEditor`` commands
and the mapping editor UI (commands.py, ui/mapping_editor.py)
* the "OpenPose" viewport renderer + ``openposeRenderSequence`` command
(render_override.py)
See ../README.md for install and usage instructions.
Note: the ``python/`` package directory is added to sys.path using
MFnPlugin.loadPath() inside initializePlugin(), not module-level __file__ --
Maya's plugin loader does not reliably set __file__ when it execs a .py
plugin, so the package import is deferred until loadPath() is available.
"""
import os
import sys
import maya.api.OpenMaya as om
maya_useNewAPI = True
VENDOR = "OpenPose Renderer Plugin"
VERSION = "1.0.0"
# Populated by _import_submodules() once the plugin's own directory is known.
_MODULES = []
def _import_submodules(fn_plugin):
python_dir = os.path.normpath(os.path.join(fn_plugin.loadPath(), "..", "python"))
if python_dir not in sys.path:
sys.path.insert(0, python_dir)
from openpose_renderer import mapping_node
from openpose_renderer import draw_override
from openpose_renderer import commands
from openpose_renderer import render_override
return [mapping_node, draw_override, commands, render_override]
def initializePlugin(m_object):
fn_plugin = om.MFnPlugin(m_object, VENDOR, VERSION, "Any")
global _MODULES
_MODULES = _import_submodules(fn_plugin)
for module in _MODULES:
try:
module.register(fn_plugin)
except Exception as error:
om.MGlobal.displayError(
"openposeRenderer: failed to register {0}: {1}".format(module.__name__, error)
)
raise
def uninitializePlugin(m_object):
fn_plugin = om.MFnPlugin(m_object)
errors = []
for module in reversed(_MODULES):
try:
module.deregister(fn_plugin)
except Exception as error:
errors.append("{0}: {1}".format(module.__name__, error))
if errors:
message = "openposeRenderer: errors during uninitialize -- " + "; ".join(errors)
om.MGlobal.displayError(message)
raise RuntimeError(message)
+1
View File
@@ -0,0 +1 @@
"""OpenPose_full renderer plugin for Maya (Python API 2.0)."""
+91
View File
@@ -0,0 +1,91 @@
"""MEL/Python-visible commands: ``openposeCreateCharacter`` (undoable node
creation) and ``openposeShowMappingEditor`` (launches the mapping UI).
"""
import maya.api.OpenMaya as om
from . import mapping_node
class CreateCharacterCommand(om.MPxCommand):
kCmdName = "openposeCreateCharacter"
def __init__(self):
om.MPxCommand.__init__(self)
self._modifiers = []
self._created_node_name = None
@staticmethod
def creator():
return CreateCharacterCommand()
@staticmethod
def createSyntax():
syntax = om.MSyntax()
syntax.addFlag("-n", "-name", om.MSyntax.kString)
return syntax
def doIt(self, args):
parser = om.MArgParser(self.syntax(), args)
want_name = parser.flagArgumentString("-n", 0) if parser.isFlagSet("-n") else None
self._modifiers = []
# For a shape type, MDagModifier.createNode() returns the
# auto-created *parent transform*, not the shape -- the shape itself
# is its only child, and only exists once doIt() has run.
create_modifier = om.MDagModifier()
transform_obj = create_modifier.createNode(mapping_node.NODE_NAME)
create_modifier.doIt()
self._modifiers.append(create_modifier)
shape_obj = om.MFnDagNode(transform_obj).child(0)
if want_name:
rename_modifier = om.MDagModifier()
rename_modifier.renameNode(shape_obj, want_name)
rename_modifier.doIt()
self._modifiers.append(rename_modifier)
self._created_node_name = om.MFnDagNode(shape_obj).name()
self.setResult(self._created_node_name)
def redoIt(self):
for modifier in self._modifiers:
modifier.doIt()
def undoIt(self):
for modifier in reversed(self._modifiers):
modifier.undoIt()
def isUndoable(self):
return True
class ShowMappingEditorCommand(om.MPxCommand):
kCmdName = "openposeShowMappingEditor"
@staticmethod
def creator():
return ShowMappingEditorCommand()
def doIt(self, args):
from .ui import mapping_editor
mapping_editor.show()
def isUndoable(self):
return False
def register(fn_plugin):
fn_plugin.registerCommand(
CreateCharacterCommand.kCmdName, CreateCharacterCommand.creator, CreateCharacterCommand.createSyntax
)
fn_plugin.registerCommand(ShowMappingEditorCommand.kCmdName, ShowMappingEditorCommand.creator)
def deregister(fn_plugin):
fn_plugin.deregisterCommand(ShowMappingEditorCommand.kCmdName)
fn_plugin.deregisterCommand(CreateCharacterCommand.kCmdName)
+289
View File
@@ -0,0 +1,289 @@
"""OpenPose_full (137-point) keypoint schema.
Layout: 25 BODY_25 body/foot points + 21 left-hand + 21 right-hand + 70 face
points, in that order, giving a flat 0..136 global keypoint index used
everywhere else in this plugin (mapping node attribute slots, draw override,
projector, rasterizer).
Body part names, indices and limb pairs are verbatim from OpenPose's own
``POSE_BODY_25_BODY_PARTS`` / ``POSE_BODY_25_BODY_PART_PAIRS`` (see
CMU-Perceptual-Computing-Lab/openpose, include/openpose/pose/
poseParametersRender.hpp). Hand and face keypoint layouts follow the
standard, widely-interoperable 21-point hand (wrist + 5x4-joint fingers,
identical ordering to OpenPose/MediaPipe hand models) and 70-point face
(iBUG 68-point facial landmark scheme + 2 pupils, OpenPose's own face model)
conventions.
Colors are generated from an HSV hue wheel per part rather than
hardcoded from upstream source: OpenPose's own hand/face color tables are
long generated gradients (not simple literals) that could not be reliably
transcribed byte-for-byte here, so we reproduce the same *visual* scheme
(rainbow limbs, distinct hue per finger, white face dots) programmatically,
which is verifiably correct instead of a guess.
"""
import colorsys
# --- Part sizes / offsets into the flat 0..136 keypoint index ---------------
BODY_COUNT = 25
HAND_COUNT = 21
FACE_COUNT = 70
BODY_START = 0
HAND_LEFT_START = BODY_START + BODY_COUNT # 25
HAND_RIGHT_START = HAND_LEFT_START + HAND_COUNT # 46
FACE_START = HAND_RIGHT_START + HAND_COUNT # 67
TOTAL_KEYPOINTS = FACE_START + FACE_COUNT # 137
PART_BODY = "body"
PART_HAND_LEFT = "hand_left"
PART_HAND_RIGHT = "hand_right"
PART_FACE = "face"
# (part id, start index, count) in draw order
PARTS = [
(PART_BODY, BODY_START, BODY_COUNT),
(PART_HAND_LEFT, HAND_LEFT_START, HAND_COUNT),
(PART_HAND_RIGHT, HAND_RIGHT_START, HAND_COUNT),
(PART_FACE, FACE_START, FACE_COUNT),
]
def _hsv255(hue, saturation=1.0, value=1.0):
r, g, b = colorsys.hsv_to_rgb(hue % 1.0, saturation, value)
return (int(round(r * 255)), int(round(g * 255)), int(round(b * 255)))
# --- Body: BODY_25 -----------------------------------------------------------
BODY_25_NAMES = [
"Nose", "Neck", "RShoulder", "RElbow", "RWrist", "LShoulder", "LElbow",
"LWrist", "MidHip", "RHip", "RKnee", "RAnkle", "LHip", "LKnee", "LAnkle",
"REye", "LEye", "REar", "LEar", "LBigToe", "LSmallToe", "LHeel",
"RBigToe", "RSmallToe", "RHeel",
]
assert len(BODY_25_NAMES) == BODY_COUNT
# Verbatim from OpenPose's POSE_BODY_25_BODY_PART_PAIRS_RENDER_GPU (local
# indices 0..24, i.e. relative to BODY_START).
BODY_25_PAIRS_LOCAL = [
(1, 8), (1, 2), (1, 5), (2, 3), (3, 4), (5, 6), (6, 7), (8, 9), (9, 10),
(10, 11), (8, 12), (12, 13), (13, 14), (1, 0), (0, 15), (15, 17), (0, 16),
(16, 18), (2, 17), (5, 18), (14, 19), (19, 20), (14, 21), (11, 22),
(22, 23), (11, 24),
]
BODY_25_POINT_COLORS = [_hsv255(i / BODY_COUNT) for i in range(BODY_COUNT)]
BODY_25_LIMB_COLORS = [
_hsv255(i / len(BODY_25_PAIRS_LOCAL)) for i in range(len(BODY_25_PAIRS_LOCAL))
]
BODY_POINT_RADIUS = 4.0
BODY_LIMB_THICKNESS = 4.0
# --- Body: COCO-18 (legacy OpenPose COCO model) ------------------------------
#
# COCO is *not* simply "BODY_25 with some points removed": it has its own
# limb connectivity (Neck connects directly to each hip -- there is no
# MidHip point at all -- and there are no foot points). Both the point names
# and the COCO section of the limb-pair table are verbatim from OpenPose's
# own POSE_COCO_BODY_PARTS / POSE_BODY_PART_PAIRS,
# src/openpose/pose/poseParameters.cpp.
#
# COCO mode does not need its own joint-mapping slots: it reuses the exact
# same BODY_25-indexed targetJoint mapping every character already has, and
# is purely a different "which of those points + which limbs to draw" view
# over it (COCO_TO_BODY25_INDEX below is that view).
COCO_COUNT = 18
COCO_NAMES = [
"Nose", "Neck", "RShoulder", "RElbow", "RWrist", "LShoulder", "LElbow",
"LWrist", "RHip", "RKnee", "RAnkle", "LHip", "LKnee", "LAnkle", "REye",
"LEye", "REar", "LEar",
]
assert len(COCO_NAMES) == COCO_COUNT
# COCO local index -> BODY_25 local index (== global index, since the body
# part starts at 0). BODY_25's MidHip (8) and the six foot points (19-24)
# have no COCO equivalent and are simply absent from this list.
COCO_TO_BODY25_INDEX = [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
assert len(COCO_TO_BODY25_INDEX) == COCO_COUNT
assert all(BODY_25_NAMES[COCO_TO_BODY25_INDEX[i]] == COCO_NAMES[i] for i in range(COCO_COUNT))
COCO_ACTIVE_BODY25_INDICES = sorted(COCO_TO_BODY25_INDEX)
# Verbatim (COCO section of POSE_BODY_PART_PAIRS), expressed in COCO-local
# 0..17 indices, then translated to BODY_25-local/global indices so it can
# be used directly against the same joint mapping as BODY_25 mode.
_COCO_PAIRS_COCO_SPACE = [
(1, 2), (1, 5), (2, 3), (3, 4), (5, 6), (6, 7), (1, 8), (8, 9), (9, 10),
(1, 11), (11, 12), (12, 13), (1, 0), (0, 14), (14, 16), (0, 15), (15, 17),
(2, 16), (5, 17),
]
COCO_PAIRS_LOCAL = [
(COCO_TO_BODY25_INDEX[a], COCO_TO_BODY25_INDEX[b]) for (a, b) in _COCO_PAIRS_COCO_SPACE
]
COCO_LIMB_COLORS = [_hsv255(i / len(COCO_PAIRS_LOCAL)) for i in range(len(COCO_PAIRS_LOCAL))]
BODY_MODEL_BODY_25 = "BODY_25"
BODY_MODEL_COCO = "COCO"
BODY_MODELS = [BODY_MODEL_BODY_25, BODY_MODEL_COCO]
# --- Hands: 21 points each, standard wrist + 5 x 4-joint fingers ------------
_FINGER_NAMES = ["Thumb", "Index", "Middle", "Ring", "Pinky"]
HAND_NAMES = ["Wrist"] + [
"{0}{1}".format(finger, joint)
for finger in _FINGER_NAMES
for joint in range(1, 5)
]
assert len(HAND_NAMES) == HAND_COUNT
# (0=wrist, 1-4=thumb, 5-8=index, 9-12=middle, 13-16=ring, 17-20=pinky)
HAND_PAIRS_LOCAL = []
for _finger_i in range(5):
_base = 1 + _finger_i * 4
HAND_PAIRS_LOCAL += [
(0, _base), (_base, _base + 1), (_base + 1, _base + 2),
(_base + 2, _base + 3),
]
assert len(HAND_PAIRS_LOCAL) == 20
def _hand_point_color(local_index):
if local_index == 0:
return (255, 255, 255) # wrist
finger = (local_index - 1) // 4
joint = (local_index - 1) % 4 # 0 (nearest wrist) .. 3 (tip)
hue = finger / 5.0
value = 0.4 + 0.2 * joint # brighter towards the fingertip
return _hsv255(hue, 1.0, value)
HAND_POINT_COLORS = [_hand_point_color(i) for i in range(HAND_COUNT)]
HAND_LIMB_COLORS = [
_hand_point_color(j) for (_, j) in HAND_PAIRS_LOCAL
] # colored by the joint further from the wrist
HAND_POINT_RADIUS = 3.0
HAND_LIMB_THICKNESS = 2.0
# --- Face: 70 points (iBUG 68-point landmarks + 2 pupils) -------------------
FACE_NAMES = (
["Jaw{0}".format(i) for i in range(17)]
+ ["REyebrow{0}".format(i) for i in range(5)]
+ ["LEyebrow{0}".format(i) for i in range(5)]
+ ["Nose{0}".format(i) for i in range(9)]
+ ["REye{0}".format(i) for i in range(6)]
+ ["LEye{0}".format(i) for i in range(6)]
+ ["Mouth{0}".format(i) for i in range(20)]
+ ["RPupil", "LPupil"]
)
assert len(FACE_NAMES) == FACE_COUNT
# OpenPose's default renderer draws the face as keypoints only (no limb
# lines), all white.
FACE_PAIRS_LOCAL = []
FACE_POINT_COLORS = [(255, 255, 255)] * FACE_COUNT
FACE_POINT_RADIUS = 2.0
FACE_LIMB_THICKNESS = 0.0
# --- Flattened 0..136 global schema ------------------------------------------
KEYPOINT_NAMES = (
["Body_{0}".format(n) for n in BODY_25_NAMES]
+ ["HandLeft_{0}".format(n) for n in HAND_NAMES]
+ ["HandRight_{0}".format(n) for n in HAND_NAMES]
+ ["Face_{0}".format(n) for n in FACE_NAMES]
)
assert len(KEYPOINT_NAMES) == TOTAL_KEYPOINTS
NAME_TO_INDEX = {name: i for i, name in enumerate(KEYPOINT_NAMES)}
KEYPOINT_PART = (
[PART_BODY] * BODY_COUNT
+ [PART_HAND_LEFT] * HAND_COUNT
+ [PART_HAND_RIGHT] * HAND_COUNT
+ [PART_FACE] * FACE_COUNT
)
KEYPOINT_POINT_COLORS = (
BODY_25_POINT_COLORS + HAND_POINT_COLORS + HAND_POINT_COLORS + FACE_POINT_COLORS
)
KEYPOINT_POINT_RADIUS = (
[BODY_POINT_RADIUS] * BODY_COUNT
+ [HAND_POINT_RADIUS] * HAND_COUNT
+ [HAND_POINT_RADIUS] * HAND_COUNT
+ [FACE_POINT_RADIUS] * FACE_COUNT
)
def part_of(global_index):
"""Return the part id (PART_BODY / PART_HAND_LEFT / ...) for a keypoint index."""
for part_id, start, count in PARTS:
if start <= global_index < start + count:
return part_id
raise IndexError(global_index)
# Hand limb pairs, offset into the flat 0..136 range -- unaffected by body
# model choice, so precomputed once.
_HAND_LEFT_PAIRS_GLOBAL = [(a + HAND_LEFT_START, b + HAND_LEFT_START) for (a, b) in HAND_PAIRS_LOCAL]
_HAND_RIGHT_PAIRS_GLOBAL = [(a + HAND_RIGHT_START, b + HAND_RIGHT_START) for (a, b) in HAND_PAIRS_LOCAL]
def _body_limb_data(body_model):
"""(pairs, colors, thickness) for just the body part, global-indexed."""
if body_model == BODY_MODEL_COCO:
pairs = COCO_PAIRS_LOCAL # already translated to BODY_25-local/global indices
colors = COCO_LIMB_COLORS
else:
pairs = [(a + BODY_START, b + BODY_START) for (a, b) in BODY_25_PAIRS_LOCAL]
colors = BODY_25_LIMB_COLORS
thickness = [BODY_LIMB_THICKNESS] * len(pairs)
return pairs, colors, thickness
def full_limb_data(body_model=BODY_MODEL_BODY_25):
"""(pairs, colors, thickness) for the whole rig: the chosen body model
plus both full hands, global-indexed into the flat 0..136 range. Faces
render as dots only and contribute no limbs.
"""
body_pairs, body_colors, body_thickness = _body_limb_data(body_model)
pairs = list(body_pairs) + _HAND_LEFT_PAIRS_GLOBAL + _HAND_RIGHT_PAIRS_GLOBAL
colors = list(body_colors) + HAND_LIMB_COLORS + HAND_LIMB_COLORS
thickness = (
list(body_thickness)
+ [HAND_LIMB_THICKNESS] * len(HAND_PAIRS_LOCAL)
+ [HAND_LIMB_THICKNESS] * len(HAND_PAIRS_LOCAL)
)
assert len(pairs) == len(colors) == len(thickness)
return pairs, colors, thickness
def active_point_indices(body_model=BODY_MODEL_BODY_25):
"""Global keypoint indices to draw as points for the given body model.
Hands and face are always fully active; the body part is either all 25
BODY_25 points or just the 18 COCO points (no MidHip, no feet).
"""
if body_model == BODY_MODEL_COCO:
body_indices = list(COCO_ACTIVE_BODY25_INDICES)
else:
body_indices = list(range(BODY_START, BODY_START + BODY_COUNT))
return (
body_indices
+ list(range(HAND_LEFT_START, HAND_LEFT_START + HAND_COUNT))
+ list(range(HAND_RIGHT_START, HAND_RIGHT_START + HAND_COUNT))
+ list(range(FACE_START, FACE_START + FACE_COUNT))
)
+135
View File
@@ -0,0 +1,135 @@
"""Viewport 2.0 draw override for ``openposeCharacter``: a live, colored
skeleton overlay drawn in world space on top of the mapped joints, so a rig
can be visually QA'd against the OpenPose_full mapping without rendering.
This is independent of the render override (render_override.py) -- it always
draws (when the node's enableDisplay attr is on) in every viewport, in 3D
world space, and tracks animation live via prepareForDraw() being re-run
whenever the node is dirtied.
"""
import maya.api.OpenMaya as om
import maya.api.OpenMayaRender as omr
from . import constants
from . import face_blendshapes
from . import mapping_node
from . import projector
def _mcolor(rgb255):
return om.MColor((rgb255[0] / 255.0, rgb255[1] / 255.0, rgb255[2] / 255.0, 1.0))
class OpenPoseUserData(om.MUserData):
def __init__(self):
om.MUserData.__init__(self, False) # Maya should not delete after one draw
self.lines = [] # [(p0, p1, MColor, thickness), ...]
self.points = [] # [(p, MColor, pointSize), ...]
class OpenPoseDrawOverride(omr.MPxDrawOverride):
@staticmethod
def creator(obj):
return OpenPoseDrawOverride(obj)
def __init__(self, obj):
omr.MPxDrawOverride.__init__(self, obj, None, isAlwaysDirty=True)
def supportedDrawAPIs(self):
return omr.MRenderer.kAllDevices
def isBounded(self, obj_path, camera_path):
# Joints being displayed can be anywhere in the scene relative to
# this node's own (irrelevant) transform -- don't cull on bbox.
return False
def hasUIDrawables(self):
return True
def prepareForDraw(self, obj_path, camera_path, frame_context, old_data):
data = old_data if isinstance(old_data, OpenPoseUserData) else OpenPoseUserData()
data.lines = []
data.points = []
dep_fn = om.MFnDependencyNode(obj_path.node())
try:
enabled = dep_fn.findPlug(mapping_node.aEnableDisplay, False).asBool()
except RuntimeError:
enabled = True
if not enabled:
return data
radius_scale = dep_fn.findPlug(mapping_node.aJointRadiusScale, False).asFloat()
thickness_scale = dep_fn.findPlug(mapping_node.aLimbThicknessScale, False).asFloat()
body_model = mapping_node.get_body_model(dep_fn)
mapped = mapping_node.get_mapped_joint_paths(dep_fn)
positions = {index: projector.world_position(path) for index, path in mapped.items()}
if mapping_node.get_face_source(dep_fn) == mapping_node.FACE_SOURCE_ARKIT_BLENDSHAPES:
nose_index = constants.NAME_TO_INDEX["Body_Nose"]
anchor = positions.get(nose_index)
if anchor is not None:
weights = mapping_node.get_blendshape_weights(dep_fn)
face_scale = mapping_node.get_face_scale(dep_fn)
local_points = face_blendshapes.compute_face_local_points(weights)
world_points = projector.billboard_world_points(anchor, camera_path, local_points, face_scale)
for offset, world_point in enumerate(world_points):
positions[constants.FACE_START + offset] = world_point
active_indices = set(constants.active_point_indices(body_model))
pairs, colors, thickness_list = constants.full_limb_data(body_model)
for pair_i, (a, b) in enumerate(pairs):
if a in positions and b in positions:
thickness = thickness_list[pair_i] * thickness_scale
if thickness <= 0.0:
continue
data.lines.append((positions[a], positions[b], _mcolor(colors[pair_i]), thickness))
for index, pos in positions.items():
if index not in active_indices:
continue
radius = constants.KEYPOINT_POINT_RADIUS[index] * radius_scale
if radius <= 0.0:
continue
data.points.append((pos, _mcolor(constants.KEYPOINT_POINT_COLORS[index]), radius * 2.0))
return data
def addUIDrawables(self, obj_path, draw_manager, frame_context, data):
if not isinstance(data, OpenPoseUserData):
return
draw_manager.beginDrawable()
draw_manager.setDepthPriority(omr.MRenderItem.sActiveWireDepthPriority)
for p0, p1, color, thickness in data.lines:
draw_manager.setColor(color)
draw_manager.setLineWidth(thickness)
draw_manager.line(p0, p1)
for pos, color, point_size in data.points:
draw_manager.setColor(color)
draw_manager.setPointSize(point_size)
draw_manager.point(pos)
draw_manager.endDrawable()
def register(fn_plugin):
omr.MDrawRegistry.registerDrawOverrideCreator(
mapping_node.DRAW_CLASSIFICATION,
mapping_node.DRAW_REGISTRANT_ID,
OpenPoseDrawOverride.creator,
)
def deregister(fn_plugin):
omr.MDrawRegistry.deregisterDrawOverrideCreator(
mapping_node.DRAW_CLASSIFICATION,
mapping_node.DRAW_REGISTRANT_ID,
)
@@ -0,0 +1,235 @@
"""Drives the 70-point OpenPose_full face part from ARKit's 52 facial
blendshape weights, for rigs that animate the face with blendShape targets
(or live ARKit/iPhone TrueDepth mocap) instead of facial joints.
``ARKIT_BLENDSHAPE_NAMES`` (order, spelling) is verbatim from Apple's own
``ARFaceAnchor.BlendShapeLocation`` enumeration.
There is no published, authoritative "which ARKit weight moves which
OpenPose/iBUG landmark, by how much" table anywhere -- unlike BODY_25/COCO,
which OpenPose's own source defines precisely, this is a genuinely
unstandardized problem. ``NEUTRAL_FACE_TEMPLATE`` and ``BLENDSHAPE_OFFSETS``
below are therefore a hand-authored, geometrically-reasoned *approximation*
(each shape nudges only the anatomically obvious nearby landmarks, in an
intuitive direction) -- good enough to produce a recognizable, responsive
OpenPose-style face for ControlNet conditioning, but not a biomechanically
accurate face simulation. A few shapes with no representable effect on a
frontal 2D landmark set (``jawForward``, ``tongueOut``, ``mouthFunnel`` --
visually redundant with ``mouthPucker`` on this coarse a template) are
intentionally no-ops.
"""
import math
from . import constants
# Verbatim from Apple's ARFaceAnchor.BlendShapeLocation.
ARKIT_BLENDSHAPE_NAMES = [
# Left eye
"eyeBlinkLeft", "eyeLookDownLeft", "eyeLookInLeft", "eyeLookOutLeft",
"eyeLookUpLeft", "eyeSquintLeft", "eyeWideLeft",
# Right eye
"eyeBlinkRight", "eyeLookDownRight", "eyeLookInRight", "eyeLookOutRight",
"eyeLookUpRight", "eyeSquintRight", "eyeWideRight",
# Jaw
"jawForward", "jawLeft", "jawRight", "jawOpen",
# Mouth
"mouthClose", "mouthFunnel", "mouthPucker", "mouthLeft", "mouthRight",
"mouthSmileLeft", "mouthSmileRight", "mouthFrownLeft", "mouthFrownRight",
"mouthDimpleLeft", "mouthDimpleRight", "mouthStretchLeft", "mouthStretchRight",
"mouthRollLower", "mouthRollUpper", "mouthShrugLower", "mouthShrugUpper",
"mouthPressLeft", "mouthPressRight", "mouthLowerDownLeft", "mouthLowerDownRight",
"mouthUpperUpLeft", "mouthUpperUpRight",
# Brow
"browDownLeft", "browDownRight", "browInnerUp", "browOuterUpLeft", "browOuterUpRight",
# Cheek
"cheekPuff", "cheekSquintLeft", "cheekSquintRight",
# Nose
"noseSneerLeft", "noseSneerRight",
# Tongue
"tongueOut",
]
assert len(ARKIT_BLENDSHAPE_NAMES) == 52
BLENDSHAPE_COUNT = 52
BLENDSHAPE_NAME_TO_INDEX = {name: i for i, name in enumerate(ARKIT_BLENDSHAPE_NAMES)}
_FACE_INDEX = {name: i for i, name in enumerate(constants.FACE_NAMES)}
# Mouth outer contour = Mouth0..11 (12 pts), inner contour = Mouth12..19 (8
# pts). Within the outer contour: 0 = left corner, 1-5 = upper lip (left to
# right), 6 = right corner, 7-11 = lower lip (right to left).
_MOUTH_UPPER_OUTER = [0, 1, 2, 3, 4, 5, 6]
_MOUTH_LOWER_OUTER = [6, 7, 8, 9, 10, 11, 0]
def _mouth_outer(i):
return "Mouth{0}".format(i)
def _mouth_inner(i):
return "Mouth{0}".format(12 + i)
def _arc(cx, cy, rx, ry, start_deg, end_deg, count):
points = []
for i in range(count):
t = i / (count - 1) if count > 1 else 0.0
angle = math.radians(start_deg + (end_deg - start_deg) * t)
points.append((cx + rx * math.cos(angle), cy + ry * math.sin(angle)))
return points
def _build_neutral_template():
"""70 (x, y) points in a local, camera-facing "face plane" (roughly a
unit square, +X = screen-right, +Y = up, origin ~ between the eyes), in
the same order as constants.FACE_NAMES. Proportions are a plausible
generic face, not a specific published dataset's exact mean shape.
"""
template = [None] * constants.FACE_COUNT
# Jaw0..16: jawline, ear to ear around the chin.
for i in range(17):
angle = math.radians(180 + 180 * i / 16.0)
x = 1.05 * math.cos(angle)
y = -0.15 + 0.85 * math.sin(angle)
template[_FACE_INDEX["Jaw{0}".format(i)]] = (x, y)
# Eyebrows: shallow arcs above each eye. Right eyebrow sits at negative
# local X, left eyebrow mirrors at positive X (an arbitrary but
# internally consistent left/right convention, same idea as BODY_25).
for i, (x, y) in enumerate(_arc(-0.5, 0.62, 0.32, 0.10, 200, 340, 5)):
template[_FACE_INDEX["REyebrow{0}".format(i)]] = (x, y)
for i, (x, y) in enumerate(_arc(0.5, 0.62, 0.32, 0.10, -20, 140, 5)):
template[_FACE_INDEX["LEyebrow{0}".format(i)]] = (x, y)
# Nose: bridge (top to tip) then the nostril-base arc.
for i in range(4):
t = i / 3.0
template[_FACE_INDEX["Nose{0}".format(i)]] = (0.0, 0.42 - 0.38 * t)
for i, (x, y) in enumerate(_arc(0.0, -0.02, 0.20, 0.06, 200, 340, 5)):
template[_FACE_INDEX["Nose{0}".format(4 + i)]] = (x, y)
# Eyes: 6 points each (outer corner, 2x upper lid, inner corner, 2x lower lid).
for i, (x, y) in enumerate(_arc(-0.5, 0.32, 0.30, 0.14, 0, 300, 6)):
template[_FACE_INDEX["REye{0}".format(i)]] = (x, y)
for i, (x, y) in enumerate(_arc(0.5, 0.32, 0.30, 0.14, 180, -120, 6)):
template[_FACE_INDEX["LEye{0}".format(i)]] = (x, y)
# Mouth: 12-point outer contour, then 8-point inner contour.
for i, (x, y) in enumerate(_arc(0.0, -0.55, 0.42, 0.20, 0, 300, 12)):
template[_FACE_INDEX["Mouth{0}".format(i)]] = (x, y)
for i, (x, y) in enumerate(_arc(0.0, -0.55, 0.28, 0.11, 0, 315, 8)):
template[_FACE_INDEX["Mouth{0}".format(12 + i)]] = (x, y)
template[_FACE_INDEX["RPupil"]] = (-0.5, 0.32)
template[_FACE_INDEX["LPupil"]] = (0.5, 0.32)
assert all(p is not None for p in template)
return template
NEUTRAL_FACE_TEMPLATE = _build_neutral_template()
def _sign(value):
return 1.0 if value >= 0 else -1.0
# {blendshape_name: [(face_point_name, dx, dy), ...]} -- displacement applied
# at weight 1.0, linearly scaled by the live weight. See module docstring for
# the honesty caveat on precision.
BLENDSHAPE_OFFSETS = {
# --- Eyes: eyelid closure / widening (upper lid moves toward or away
# from the lower lid; lower lid nudges slightly the opposite way).
"eyeBlinkLeft": [("LEye1", 0.0, -0.10), ("LEye2", 0.0, -0.10)],
"eyeBlinkRight": [("REye1", 0.0, -0.10), ("REye2", 0.0, -0.10)],
"eyeWideLeft": [("LEye1", 0.0, 0.05), ("LEye2", 0.0, 0.05), ("LEye4", 0.0, -0.03), ("LEye5", 0.0, -0.03)],
"eyeWideRight": [("REye1", 0.0, 0.05), ("REye2", 0.0, 0.05), ("REye4", 0.0, -0.03), ("REye5", 0.0, -0.03)],
"eyeSquintLeft": [("LEye1", 0.0, -0.04), ("LEye2", 0.0, -0.04), ("LEye4", 0.0, 0.02), ("LEye5", 0.0, 0.02)],
"eyeSquintRight": [("REye1", 0.0, -0.04), ("REye2", 0.0, -0.04), ("REye4", 0.0, 0.02), ("REye5", 0.0, 0.02)],
# Gaze direction moves the pupil point within the eye socket.
"eyeLookDownLeft": [("LPupil", 0.0, -0.06)],
"eyeLookUpLeft": [("LPupil", 0.0, 0.06)],
"eyeLookInLeft": [("LPupil", -0.08, 0.0)],
"eyeLookOutLeft": [("LPupil", 0.08, 0.0)],
"eyeLookDownRight": [("RPupil", 0.0, -0.06)],
"eyeLookUpRight": [("RPupil", 0.0, 0.06)],
"eyeLookInRight": [("RPupil", 0.08, 0.0)],
"eyeLookOutRight": [("RPupil", -0.08, 0.0)],
# --- Brows.
"browDownLeft": [("LEyebrow{0}".format(i), 0.0, -0.08) for i in range(5)],
"browDownRight": [("REyebrow{0}".format(i), 0.0, -0.08) for i in range(5)],
"browInnerUp": [("LEyebrow0", 0.0, 0.09), ("REyebrow4", 0.0, 0.09)],
"browOuterUpLeft": [("LEyebrow3", 0.0, 0.08), ("LEyebrow4", 0.0, 0.08)],
"browOuterUpRight": [("REyebrow0", 0.0, 0.08), ("REyebrow1", 0.0, 0.08)],
# --- Jaw. jawForward has no representable 2D effect (frontal view) --
# intentionally omitted below.
"jawOpen": (
[("Jaw{0}".format(i), 0.0, -0.30) for i in range(6, 11)]
+ [(_mouth_outer(i), 0.0, -0.22) for i in _MOUTH_LOWER_OUTER]
+ [(_mouth_inner(i), 0.0, -0.16) for i in range(4, 7)]
),
"jawLeft": [("Jaw{0}".format(i), 0.06, 0.0) for i in range(6, 11)],
"jawRight": [("Jaw{0}".format(i), -0.06, 0.0) for i in range(6, 11)],
# --- Mouth shape.
"mouthSmileLeft": [(_mouth_outer(0), 0.05, 0.10), (_mouth_outer(1), 0.03, 0.06), (_mouth_outer(11), 0.03, 0.06)],
"mouthSmileRight": [(_mouth_outer(6), -0.05, 0.10), (_mouth_outer(5), -0.03, 0.06), (_mouth_outer(7), -0.03, 0.06)],
"mouthFrownLeft": [(_mouth_outer(0), 0.02, -0.08)],
"mouthFrownRight": [(_mouth_outer(6), -0.02, -0.08)],
"mouthLeft": [(_mouth_outer(i), 0.08, 0.0) for i in range(12)],
"mouthRight": [(_mouth_outer(i), -0.08, 0.0) for i in range(12)],
"mouthDimpleLeft": [(_mouth_outer(0), 0.02, 0.02)],
"mouthDimpleRight": [(_mouth_outer(6), -0.02, 0.02)],
"mouthStretchLeft": [(_mouth_outer(0), 0.10, 0.0)],
"mouthStretchRight": [(_mouth_outer(6), -0.10, 0.0)],
"mouthPucker": [
(_mouth_outer(i), -0.10 * _sign(NEUTRAL_FACE_TEMPLATE[_FACE_INDEX[_mouth_outer(i)]][0]), 0.0)
for i in range(12)
],
"mouthFunnel": [], # visually redundant with mouthPucker on this coarse template
"mouthClose": (
[(_mouth_outer(i), 0.0, -0.02) for i in _MOUTH_UPPER_OUTER]
+ [(_mouth_outer(i), 0.0, 0.02) for i in _MOUTH_LOWER_OUTER]
),
"mouthRollLower": [(_mouth_outer(i), 0.0, 0.03) for i in _MOUTH_LOWER_OUTER],
"mouthRollUpper": [(_mouth_outer(i), 0.0, -0.03) for i in _MOUTH_UPPER_OUTER],
"mouthShrugLower": [(_mouth_outer(i), 0.0, -0.04) for i in _MOUTH_LOWER_OUTER],
"mouthShrugUpper": [(_mouth_outer(i), 0.0, 0.04) for i in _MOUTH_UPPER_OUTER],
"mouthPressLeft": [(_mouth_outer(0), 0.02, -0.01)],
"mouthPressRight": [(_mouth_outer(6), -0.02, -0.01)],
"mouthLowerDownLeft": [(_mouth_outer(7), 0.0, -0.06), (_mouth_outer(8), 0.0, -0.06)],
"mouthLowerDownRight": [(_mouth_outer(10), 0.0, -0.06), (_mouth_outer(11), 0.0, -0.06)],
"mouthUpperUpLeft": [(_mouth_outer(1), 0.0, 0.05), (_mouth_outer(2), 0.0, 0.05)],
"mouthUpperUpRight": [(_mouth_outer(4), 0.0, 0.05), (_mouth_outer(5), 0.0, 0.05)],
# --- Cheeks / nose: subtle.
"cheekPuff": [("Jaw{0}".format(i), 0.03, 0.0) for i in (3, 4)] + [("Jaw{0}".format(i), -0.03, 0.0) for i in (12, 13)],
"cheekSquintLeft": [("LEye4", 0.0, 0.02), ("LEye5", 0.0, 0.02)],
"cheekSquintRight": [("REye4", 0.0, 0.02), ("REye5", 0.0, 0.02)],
"noseSneerLeft": [("Nose7", 0.0, 0.03), ("Nose8", 0.0, 0.03)],
"noseSneerRight": [("Nose4", 0.0, 0.03), ("Nose5", 0.0, 0.03)],
# --- No 2D-representable effect on a frontal landmark set.
"jawForward": [],
"tongueOut": [],
}
assert set(BLENDSHAPE_OFFSETS.keys()) == set(ARKIT_BLENDSHAPE_NAMES)
def compute_face_local_points(weights):
"""``weights``: {blendshape_name: 0..1}. Returns 70 (x, y) local points
(constants.FACE_NAMES order), the neutral template plus every active
blendshape's weighted displacement.
"""
points = list(NEUTRAL_FACE_TEMPLATE)
for name, weight in weights.items():
if not weight:
continue
for point_name, dx, dy in BLENDSHAPE_OFFSETS.get(name, ()):
idx = _FACE_INDEX[point_name]
x, y = points[idx]
points[idx] = (x + dx * weight, y + dy * weight)
return points
+228
View File
@@ -0,0 +1,228 @@
"""``openposeCharacter`` node: per-character OpenPose_full joint mapping.
A pure MPxLocatorNode with one connectable message-attribute slot per
OpenPose_full keypoint (``targetJoint[0..136]``). The mapping lives entirely
as Maya connections (joint.message -> openposeCharacter.targetJoint[i]) so it
survives joint renames/reparenting and round-trips through the scene file
with no separate config to keep in sync. The node has no compute() output --
it is a passive connection target read directly by the draw override
(live viewport skeleton) and the render override (OpenPose image output).
"""
import maya.api.OpenMaya as om
import maya.api.OpenMayaUI as omui
from . import constants
from . import face_blendshapes
NODE_NAME = "openposeCharacter"
NODE_ID = om.MTypeId(0x0012F5C0)
DRAW_CLASSIFICATION = "drawdb/geometry/openposeCharacter"
DRAW_REGISTRANT_ID = "openposeRendererPlugin"
# Attribute handles, filled in by initialize()
aTargetJoint = None
aEnableDisplay = None
aJointRadiusScale = None
aLimbThicknessScale = None
aCharacterName = None
aBodyModel = None
aFaceSource = None
aBlendshapeWeight = None
aFaceScale = None
# aBodyModel enum field values, in registration order (index 0 = BODY_25).
BODY_MODEL_FIELDS = [constants.BODY_MODEL_BODY_25, constants.BODY_MODEL_COCO]
# aFaceSource enum field values, in registration order (index 0 = Joints).
FACE_SOURCE_JOINTS = "Joints"
FACE_SOURCE_ARKIT_BLENDSHAPES = "ARKitBlendshapes"
FACE_SOURCE_FIELDS = [FACE_SOURCE_JOINTS, FACE_SOURCE_ARKIT_BLENDSHAPES]
DEFAULT_FACE_SCALE = 15.0
class OpenposeCharacterNode(omui.MPxLocatorNode):
kTypeName = NODE_NAME
kTypeId = NODE_ID
def __init__(self):
omui.MPxLocatorNode.__init__(self)
def compute(self, plug, data_block):
# Pure passive mapping node: nothing to compute.
return None
def isBounded(self):
return False
@staticmethod
def creator():
return OpenposeCharacterNode()
@staticmethod
def initialize():
global aTargetJoint, aEnableDisplay, aJointRadiusScale
global aLimbThicknessScale, aCharacterName, aBodyModel
global aFaceSource, aBlendshapeWeight, aFaceScale
msg_attr = om.MFnMessageAttribute()
aTargetJoint = msg_attr.create("targetJoint", "tj")
msg_attr.array = True
# indexMatters already defaults to True (sparse, index-preserving
# array) -- explicitly assigning True here throws kInvalidParameter
# in Maya's Python binding, so it is deliberately left untouched.
msg_attr.storable = True
msg_attr.readable = True
msg_attr.writable = True
om.MPxNode.addAttribute(aTargetJoint)
num_attr = om.MFnNumericAttribute()
aEnableDisplay = num_attr.create(
"enableDisplay", "ed", om.MFnNumericData.kBoolean, True
)
num_attr.keyable = True
om.MPxNode.addAttribute(aEnableDisplay)
aJointRadiusScale = num_attr.create(
"jointRadiusScale", "jrs", om.MFnNumericData.kFloat, 1.0
)
num_attr.keyable = True
num_attr.setMin(0.0)
om.MPxNode.addAttribute(aJointRadiusScale)
aLimbThicknessScale = num_attr.create(
"limbThicknessScale", "lts", om.MFnNumericData.kFloat, 1.0
)
num_attr.keyable = True
num_attr.setMin(0.0)
om.MPxNode.addAttribute(aLimbThicknessScale)
str_data = om.MFnStringData()
empty_string_obj = str_data.create("")
typed_attr = om.MFnTypedAttribute()
aCharacterName = typed_attr.create(
"characterName", "cn", om.MFnData.kString, empty_string_obj
)
typed_attr.storable = True
om.MPxNode.addAttribute(aCharacterName)
enum_attr = om.MFnEnumAttribute()
aBodyModel = enum_attr.create("bodyModel", "bmd", 0)
for field_index, field_name in enumerate(BODY_MODEL_FIELDS):
enum_attr.addField(field_name, field_index)
enum_attr.keyable = True
om.MPxNode.addAttribute(aBodyModel)
face_source_attr = om.MFnEnumAttribute()
aFaceSource = face_source_attr.create("faceSource", "fsrc", 0)
for field_index, field_name in enumerate(FACE_SOURCE_FIELDS):
face_source_attr.addField(field_name, field_index)
face_source_attr.keyable = True
om.MPxNode.addAttribute(aFaceSource)
weight_attr = om.MFnNumericAttribute()
aBlendshapeWeight = weight_attr.create("blendshapeWeight", "bsw", om.MFnNumericData.kFloat, 0.0)
weight_attr.array = True
weight_attr.keyable = True
weight_attr.setMin(0.0)
weight_attr.setMax(1.0)
om.MPxNode.addAttribute(aBlendshapeWeight)
face_scale_attr = om.MFnNumericAttribute()
aFaceScale = face_scale_attr.create("faceScale", "fsc", om.MFnNumericData.kFloat, DEFAULT_FACE_SCALE)
face_scale_attr.keyable = True
face_scale_attr.setMin(0.0)
om.MPxNode.addAttribute(aFaceScale)
def get_mapped_joint_paths(dep_node_fn):
"""Return {keypoint_index: MDagPath} for every connected targetJoint slot.
``dep_node_fn`` is an MFnDependencyNode wrapping an openposeCharacter.
Unmapped keypoint indices are simply absent from the returned dict.
"""
result = {}
plug = dep_node_fn.findPlug(aTargetJoint, False)
for logical_index in plug.getExistingArrayAttributeIndices():
if logical_index < 0 or logical_index >= constants.TOTAL_KEYPOINTS:
continue
element_plug = plug.elementByLogicalIndex(logical_index)
if not element_plug.isConnected:
continue
source_plug = element_plug.source()
if source_plug.isNull:
continue
source_node = source_plug.node()
if not source_node.hasFn(om.MFn.kDagNode):
continue
dag_fn = om.MFnDagNode(source_node)
result[logical_index] = dag_fn.getPath()
return result
def get_body_model(dep_node_fn):
"""Return constants.BODY_MODEL_BODY_25 / BODY_MODEL_COCO for this character."""
try:
field_index = dep_node_fn.findPlug(aBodyModel, False).asShort()
except RuntimeError:
field_index = 0
if 0 <= field_index < len(BODY_MODEL_FIELDS):
return BODY_MODEL_FIELDS[field_index]
return constants.BODY_MODEL_BODY_25
def get_face_source(dep_node_fn):
"""Return FACE_SOURCE_JOINTS / FACE_SOURCE_ARKIT_BLENDSHAPES for this character."""
try:
field_index = dep_node_fn.findPlug(aFaceSource, False).asShort()
except RuntimeError:
field_index = 0
if 0 <= field_index < len(FACE_SOURCE_FIELDS):
return FACE_SOURCE_FIELDS[field_index]
return FACE_SOURCE_JOINTS
def get_blendshape_weights(dep_node_fn):
"""Return {arkit_blendshape_name: weight} for all 52 slots (0.0 default)."""
plug = dep_node_fn.findPlug(aBlendshapeWeight, False)
weights = {}
for index, name in enumerate(face_blendshapes.ARKIT_BLENDSHAPE_NAMES):
element_plug = plug.elementByLogicalIndex(index)
weights[name] = element_plug.asFloat()
return weights
def get_face_scale(dep_node_fn):
try:
return dep_node_fn.findPlug(aFaceScale, False).asFloat()
except RuntimeError:
return DEFAULT_FACE_SCALE
def iter_character_nodes():
"""Yield an MFnDependencyNode for every openposeCharacter node in the scene."""
it = om.MItDependencyNodes(om.MFn.kPluginLocatorNode)
while not it.isDone():
obj = it.thisNode()
fn = om.MFnDependencyNode(obj)
if fn.typeName == NODE_NAME:
yield fn
it.next()
def register(fn_plugin):
fn_plugin.registerNode(
NODE_NAME,
NODE_ID,
OpenposeCharacterNode.creator,
OpenposeCharacterNode.initialize,
om.MPxNode.kLocatorNode,
DRAW_CLASSIFICATION,
)
def deregister(fn_plugin):
fn_plugin.deregisterNode(NODE_ID)
+96
View File
@@ -0,0 +1,96 @@
"""World-space joint -> 2D pixel projection for a given camera + resolution.
Pure math, no drawing. Shared by the viewport draw override (which draws in
3D world space directly and does not strictly need this) and the render
override (which needs actual 2D pixel coordinates to rasterize the
OpenPose-style image). Kept independent of "which camera" / "which
character" decisions -- callers supply an explicit camera path and mapping.
"""
import maya.api.OpenMaya as om
from . import constants
DEFAULT_RESOLUTION_NODE = "defaultResolution"
def get_render_resolution():
"""Return (width, height) in pixels from the scene's defaultResolution node."""
sel = om.MSelectionList()
sel.add(DEFAULT_RESOLUTION_NODE)
fn = om.MFnDependencyNode(sel.getDependNode(0))
width = fn.findPlug("width", False).asInt()
height = fn.findPlug("height", False).asInt()
return max(1, width), max(1, height)
def world_position(dag_path):
"""World-space MPoint for any DAG transform (including joints)."""
world_matrix = dag_path.inclusiveMatrix()
return om.MPoint(om.MTransformationMatrix(world_matrix).translation(om.MSpace.kWorld))
def project_point(world_point, camera_path, width, height):
"""Project a world-space MPoint through ``camera_path`` to pixel coordinates.
Returns (pixel_x, pixel_y, in_front_of_camera).
"""
camera_space = om.MPoint(world_point) * camera_path.inclusiveMatrixInverse()
camera_fn = om.MFnCamera(camera_path)
# projectionMatrix() returns an MFloatMatrix; MPoint's matrix multiply
# operator only supports MMatrix, hence the explicit conversion.
projection_matrix = om.MMatrix(camera_fn.projectionMatrix())
clip = om.MPoint(camera_space) * projection_matrix
if abs(clip.w) < 1e-9:
return 0.0, 0.0, False
ndc_x = clip.x / clip.w
ndc_y = clip.y / clip.w
pixel_x = (ndc_x * 0.5 + 0.5) * width
pixel_y = (1.0 - (ndc_y * 0.5 + 0.5)) * height
# The camera looks down its own -Z axis, so a point in front of it has
# negative camera-space Z.
in_front = camera_space.z < 0.0
return pixel_x, pixel_y, in_front
def billboard_world_points(anchor_world_point, camera_path, local_points_xy, scale):
"""Place 2D local (x, y) points on a plane centered at ``anchor_world_point``
that always faces ``camera_path`` (camera-space X/Y used as the plane's
right/up axes), scaled by ``scale``.
Used to draw/render the procedural ARKit-blendshape face without needing
any facial joint orientation -- it billboards toward whichever camera is
currently drawing (the active viewport camera for the live overlay, the
render camera for output), sidestepping the rig-specific, unanswerable
question of "which way does this head joint's local frame face".
"""
world_matrix = camera_path.inclusiveMatrix()
right = (om.MVector(1.0, 0.0, 0.0) * world_matrix).normal()
up = (om.MVector(0.0, 1.0, 0.0) * world_matrix).normal()
anchor = om.MPoint(anchor_world_point)
return [
om.MPoint(anchor + right * (x * scale) + up * (y * scale))
for x, y in local_points_xy
]
def project_character(mapped_joint_paths, camera_path, width, height):
"""Project one character's mapping to OpenPose_full pixel keypoints.
``mapped_joint_paths``: {keypoint_index: MDagPath}, as returned by
``mapping_node.get_mapped_joint_paths``.
Returns a list of length ``constants.TOTAL_KEYPOINTS`` of
(pixel_x, pixel_y, confidence) tuples. Unmapped or off-camera keypoints
get confidence 0.0 at (0.0, 0.0), matching OpenPose's own convention for
undetected keypoints.
"""
keypoints = [(0.0, 0.0, 0.0)] * constants.TOTAL_KEYPOINTS
for index, joint_path in mapped_joint_paths.items():
world_point = world_position(joint_path)
pixel_x, pixel_y, in_front = project_point(world_point, camera_path, width, height)
confidence = 1.0 if in_front else 0.0
keypoints[index] = (pixel_x, pixel_y, confidence)
return keypoints
+143
View File
@@ -0,0 +1,143 @@
"""Draws OpenPose_full-style skeleton images (black background, colored
limbs/joints) and writes them out via maya.api.OpenMaya.MImage.
This is a self-contained software rasterizer (no OpenGL/render-target
dependency) operating on a flat RGBA bytearray, so it can be driven equally
from a batch/offline context or from inside a render override. Confidence-0
keypoints (unmapped OpenPose_full slots) are simply not drawn, and any limb
whose either endpoint is confidence-0 is skipped -- matching how OpenPose
itself omits undetected points/limbs.
"""
import maya.api.OpenMaya as om
from . import constants
BYTES_PER_PIXEL = 4 # RGBA
def _new_buffer(width, height):
buf = bytearray(width * height * BYTES_PER_PIXEL)
for i in range(0, len(buf), BYTES_PER_PIXEL):
buf[i + 3] = 255 # opaque black background
return buf
def _set_pixel(buffer, width, height, x, y, color):
if x < 0 or x >= width or y < 0 or y >= height:
return
offset = (y * width + x) * BYTES_PER_PIXEL
buffer[offset] = color[0]
buffer[offset + 1] = color[1]
buffer[offset + 2] = color[2]
buffer[offset + 3] = 255
def _draw_circle(buffer, width, height, cx, cy, radius, color):
if radius <= 0:
return
x_min = max(0, int(cx - radius))
x_max = min(width - 1, int(cx + radius))
y_min = max(0, int(cy - radius))
y_max = min(height - 1, int(cy + radius))
radius_sq = radius * radius
for y in range(y_min, y_max + 1):
dy = y + 0.5 - cy
for x in range(x_min, x_max + 1):
dx = x + 0.5 - cx
if dx * dx + dy * dy <= radius_sq:
_set_pixel(buffer, width, height, x, y, color)
def _draw_line(buffer, width, height, x0, y0, x1, y1, thickness, color):
if thickness <= 0:
return
half = max(thickness * 0.5, 0.5)
x_min = max(0, int(min(x0, x1) - half))
x_max = min(width - 1, int(max(x0, x1) + half))
y_min = max(0, int(min(y0, y1) - half))
y_max = min(height - 1, int(max(y0, y1) + half))
dx = x1 - x0
dy = y1 - y0
length_sq = dx * dx + dy * dy
half_sq = half * half
if length_sq < 1e-9:
_draw_circle(buffer, width, height, x0, y0, half, color)
return
for y in range(y_min, y_max + 1):
py = y + 0.5
for x in range(x_min, x_max + 1):
px = x + 0.5
t = ((px - x0) * dx + (py - y0) * dy) / length_sq
t = max(0.0, min(1.0, t))
proj_x = x0 + t * dx
proj_y = y0 + t * dy
dist_sq = (px - proj_x) ** 2 + (py - proj_y) ** 2
if dist_sq <= half_sq:
_set_pixel(buffer, width, height, x, y, color)
def draw_character(buffer, width, height, keypoints, radius_scale=1.0, thickness_scale=1.0,
body_model=constants.BODY_MODEL_BODY_25):
"""Draw one character's OpenPose skeleton into an existing RGBA buffer.
``keypoints``: list of length constants.TOTAL_KEYPOINTS of
(pixel_x, pixel_y, confidence), as returned by
``projector.project_character``. ``body_model`` selects BODY_25 (all 25
body/foot points) or COCO (18 points, no MidHip/feet, different limb
connectivity) -- hands and face are unaffected either way.
"""
pairs, colors, thickness_list = constants.full_limb_data(body_model)
for pair_i, (a, b) in enumerate(pairs):
xa, ya, ca = keypoints[a]
xb, yb, cb = keypoints[b]
if ca <= 0.0 or cb <= 0.0:
continue
thickness = thickness_list[pair_i] * thickness_scale
_draw_line(buffer, width, height, xa, ya, xb, yb, thickness, colors[pair_i])
active_indices = constants.active_point_indices(body_model)
for index in active_indices:
x, y, confidence = keypoints[index]
if confidence <= 0.0:
continue
radius = constants.KEYPOINT_POINT_RADIUS[index] * radius_scale
_draw_circle(buffer, width, height, x, y, radius, constants.KEYPOINT_POINT_COLORS[index])
def render_characters_to_buffer(characters, width, height):
"""``characters``: iterable of (keypoints, radius_scale, thickness_scale, body_model).
Returns a flat RGBA bytearray of size width*height*4.
"""
buffer = _new_buffer(width, height)
for keypoints, radius_scale, thickness_scale, body_model in characters:
draw_character(buffer, width, height, keypoints, radius_scale, thickness_scale, body_model)
return buffer
def _flip_rows(buffer, width, height):
"""MImage.setPixels() treats row 0 as the bottom of the image (Maya's
classic bottom-up convention); everything else here (and every PNG
reader downstream) treats row 0 as the top. Flip row order once, right
before handing pixels to MImage, so the written file is a normal
top-down image.
"""
row_bytes = width * BYTES_PER_PIXEL
flipped = bytearray(len(buffer))
for y in range(height):
src = y * row_bytes
dst = (height - 1 - y) * row_bytes
flipped[dst:dst + row_bytes] = buffer[src:src + row_bytes]
return flipped
def render_characters_to_file(characters, width, height, file_path, file_format="png"):
"""Render all characters and write the result to ``file_path``."""
buffer = render_characters_to_buffer(characters, width, height)
image = om.MImage()
image.setPixels(_flip_rows(buffer, width, height), width, height)
image.writeToFile(file_path, outputFormat=file_format)
+214
View File
@@ -0,0 +1,214 @@
"""Maya renderer integration for OpenPose_full output.
Two pieces:
* ``OpenposeRenderOverride`` -- a Viewport 2.0 ``MRenderOverride`` registered
as "OpenPose" in the viewport panel's Renderer dropdown, using the same
minimal scene/HUD/present operation chain as Maya's own reference render
override sample. This makes "OpenPose" a normal, selectable renderer
rather than gambling on undocumented GPU render-target pixel readback
from Python for the actual image content (see the plan's flagged risk).
* ``render_frame`` / the ``openposeRenderSequence`` command -- the actual
OpenPose image generation: projects every ``openposeCharacter`` in the
scene through a given camera and rasterizes them (projector.py +
rasterizer.py, pure software, no GPU dependency) to a PNG per frame, and
pushes each frame into Maya's Render View via
``renderWindowEditor(loadImage=...)`` -- the same mechanism third-party
renderers use to display progress there, so the images are visible in the
normal render workflow, not just written silently to disk.
"""
import os
import maya.api.OpenMaya as om
import maya.api.OpenMayaRender as omr
import maya.cmds as cmds
from . import constants
from . import face_blendshapes
from . import mapping_node
from . import projector
from . import rasterizer
UI_NAME = "OpenPose"
RENDER_OVERRIDE_NAME = "openposeRenderOverride"
_override_instance = None
class OpenposeRenderOverride(omr.MRenderOverride):
def __init__(self, name):
omr.MRenderOverride.__init__(self, name)
self._operations = [
omr.MSceneRender("openposeSceneRender"),
omr.MHUDRender(),
omr.MPresentTarget("openposePresentTarget"),
]
self._current_operation = -1
def uiName(self):
return UI_NAME
def supportedDrawAPIs(self):
return omr.MRenderer.kAllDevices
def setup(self, destination):
return
def cleanup(self):
self._current_operation = -1
def startOperationIterator(self):
self._current_operation = 0
return True
def renderOperation(self):
if 0 <= self._current_operation < len(self._operations):
return self._operations[self._current_operation]
return None
def nextRenderOperation(self):
self._current_operation += 1
return self._current_operation < len(self._operations)
def render_frame(camera_path, width, height, output_path):
"""Project + rasterize every mapped openposeCharacter to a single PNG."""
characters = []
for dep_fn in mapping_node.iter_character_nodes():
try:
enabled = dep_fn.findPlug(mapping_node.aEnableDisplay, False).asBool()
except RuntimeError:
enabled = True
if not enabled:
continue
radius_scale = dep_fn.findPlug(mapping_node.aJointRadiusScale, False).asFloat()
thickness_scale = dep_fn.findPlug(mapping_node.aLimbThicknessScale, False).asFloat()
body_model = mapping_node.get_body_model(dep_fn)
mapped = mapping_node.get_mapped_joint_paths(dep_fn)
keypoints = projector.project_character(mapped, camera_path, width, height)
if mapping_node.get_face_source(dep_fn) == mapping_node.FACE_SOURCE_ARKIT_BLENDSHAPES:
nose_index = constants.NAME_TO_INDEX["Body_Nose"]
nose_x, nose_y, nose_confidence = keypoints[nose_index]
if nose_confidence > 0.0 and nose_index in mapped:
anchor = projector.world_position(mapped[nose_index])
weights = mapping_node.get_blendshape_weights(dep_fn)
face_scale = mapping_node.get_face_scale(dep_fn)
local_points = face_blendshapes.compute_face_local_points(weights)
world_points = projector.billboard_world_points(anchor, camera_path, local_points, face_scale)
for offset, world_point in enumerate(world_points):
px, py, in_front = projector.project_point(world_point, camera_path, width, height)
keypoints[constants.FACE_START + offset] = (px, py, 1.0 if in_front else 0.0)
characters.append((keypoints, radius_scale, thickness_scale, body_model))
rasterizer.render_characters_to_file(characters, width, height, output_path)
return output_path
def _default_output_dir():
images_rule = cmds.workspace(fileRuleEntry="images", query=True) or "images"
workspace_root = cmds.workspace(query=True, rootDirectory=True)
out_dir = os.path.join(workspace_root, images_rule, "openpose")
if not os.path.isdir(out_dir):
os.makedirs(out_dir)
return out_dir
def _resolve_camera_path(camera_name):
if camera_name:
selection = om.MSelectionList()
selection.add(camera_name)
dag_path = selection.getDagPath(0)
if dag_path.apiType() == om.MFn.kTransform:
dag_path.extendToShape()
return dag_path
import maya.api.OpenMayaUI as omui
return omui.M3dView.active3dView().getCamera()
class RenderSequenceCommand(om.MPxCommand):
kCmdName = "openposeRenderSequence"
@staticmethod
def creator():
return RenderSequenceCommand()
@staticmethod
def createSyntax():
syntax = om.MSyntax()
syntax.addFlag("-s", "-startFrame", om.MSyntax.kDouble)
syntax.addFlag("-e", "-endFrame", om.MSyntax.kDouble)
syntax.addFlag("-cam", "-camera", om.MSyntax.kString)
syntax.addFlag("-dir", "-outputDirectory", om.MSyntax.kString)
syntax.addFlag("-sw", "-showInRenderView", om.MSyntax.kBoolean)
return syntax
def isUndoable(self):
return False
def doIt(self, args):
parser = om.MArgParser(self.syntax(), args)
start = (
parser.flagArgumentDouble("-s", 0)
if parser.isFlagSet("-s")
else cmds.playbackOptions(query=True, minTime=True)
)
end = (
parser.flagArgumentDouble("-e", 0)
if parser.isFlagSet("-e")
else cmds.playbackOptions(query=True, maxTime=True)
)
camera_name = parser.flagArgumentString("-cam", 0) if parser.isFlagSet("-cam") else None
out_dir = parser.flagArgumentString("-dir", 0) if parser.isFlagSet("-dir") else _default_output_dir()
show_in_render_view = parser.flagArgumentBool("-sw", 0) if parser.isFlagSet("-sw") else True
if not os.path.isdir(out_dir):
os.makedirs(out_dir)
camera_path = _resolve_camera_path(camera_name)
width, height = projector.get_render_resolution()
scene_path = cmds.file(query=True, sceneName=True)
scene_name = os.path.splitext(os.path.basename(scene_path))[0] if scene_path else "openpose"
scene_name = scene_name or "openpose"
if show_in_render_view:
cmds.RenderViewWindow()
original_time = cmds.currentTime(query=True)
written = []
frame = int(start)
while frame <= int(end):
cmds.currentTime(frame)
file_path = os.path.join(out_dir, "{0}.{1:04d}.png".format(scene_name, frame))
render_frame(camera_path, width, height, file_path)
written.append(file_path)
if show_in_render_view:
cmds.renderWindowEditor("renderView", edit=True, loadImage=file_path)
frame += 1
cmds.currentTime(original_time)
self.setResult(written)
def register(fn_plugin):
global _override_instance
fn_plugin.registerCommand(
RenderSequenceCommand.kCmdName, RenderSequenceCommand.creator, RenderSequenceCommand.createSyntax
)
_override_instance = OpenposeRenderOverride(RENDER_OVERRIDE_NAME)
omr.MRenderer.registerOverride(_override_instance)
def deregister(fn_plugin):
global _override_instance
if _override_instance is not None:
omr.MRenderer.deregisterOverride(_override_instance)
_override_instance = None
fn_plugin.deregisterCommand(RenderSequenceCommand.kCmdName)
@@ -0,0 +1,304 @@
"""PySide2 tool for mapping Maya joints onto the 137 OpenPose_full keypoint
slots of a selected ``openposeCharacter`` node.
Mapping edits go through ``cmds.connectAttr``/``disconnectAttr`` (not raw
API calls) so every edit is a normal, undoable Maya scene operation.
"""
from functools import partial
import maya.cmds as cmds
import maya.OpenMayaUI as omui
try:
from PySide2 import QtCore, QtWidgets
from shiboken2 import wrapInstance
except ImportError:
from PySide6 import QtCore, QtWidgets
from shiboken6 import wrapInstance
from .. import constants
from .. import face_blendshapes
from .. import mapping_node
_PART_LABELS = {
constants.PART_BODY: "Body",
constants.PART_HAND_LEFT: "Left Hand",
constants.PART_HAND_RIGHT: "Right Hand",
constants.PART_FACE: "Face",
}
_window_instance = None
def _maya_main_window():
ptr = omui.MQtUtil.mainWindow()
return wrapInstance(int(ptr), QtWidgets.QWidget)
def _target_plug(character, index):
return "{0}.targetJoint[{1}]".format(character, index)
class MappingEditor(QtWidgets.QDialog):
def __init__(self, parent=None):
super(MappingEditor, self).__init__(parent or _maya_main_window())
self.setWindowTitle("OpenPose Mapping Editor")
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
self.resize(620, 720)
self._row_items = {} # index -> QTreeWidgetItem
self._build_ui()
self.refresh_characters()
def _build_ui(self):
layout = QtWidgets.QVBoxLayout(self)
char_row = QtWidgets.QHBoxLayout()
char_row.addWidget(QtWidgets.QLabel("Character:"))
self.character_combo = QtWidgets.QComboBox()
self.character_combo.currentIndexChanged.connect(self.refresh_mapping)
char_row.addWidget(self.character_combo, 1)
refresh_btn = QtWidgets.QPushButton("Refresh")
refresh_btn.clicked.connect(self.refresh_characters)
char_row.addWidget(refresh_btn)
create_btn = QtWidgets.QPushButton("Create Character")
create_btn.clicked.connect(self._on_create_character)
char_row.addWidget(create_btn)
char_row.addWidget(QtWidgets.QLabel("Body Model:"))
self.body_model_combo = QtWidgets.QComboBox()
self.body_model_combo.addItems(constants.BODY_MODELS)
self.body_model_combo.currentIndexChanged.connect(self._on_body_model_changed)
char_row.addWidget(self.body_model_combo)
layout.addLayout(char_row)
face_row = QtWidgets.QHBoxLayout()
face_row.addWidget(QtWidgets.QLabel("Face Source:"))
self.face_source_combo = QtWidgets.QComboBox()
self.face_source_combo.addItems(
[mapping_node.FACE_SOURCE_JOINTS, mapping_node.FACE_SOURCE_ARKIT_BLENDSHAPES]
)
self.face_source_combo.currentIndexChanged.connect(self._on_face_source_changed)
face_row.addWidget(self.face_source_combo)
face_row.addWidget(QtWidgets.QLabel("Face Scale:"))
self.face_scale_spin = QtWidgets.QDoubleSpinBox()
self.face_scale_spin.setRange(0.0, 100000.0)
self.face_scale_spin.setValue(mapping_node.DEFAULT_FACE_SCALE)
self.face_scale_spin.valueChanged.connect(self._on_face_scale_changed)
face_row.addWidget(self.face_scale_spin)
autoconnect_btn = QtWidgets.QPushButton("Auto-Connect ARKit Blendshapes from Selected")
autoconnect_btn.setToolTip(
"Connects the selected node's attributes to this character's 52 blendshapeWeight\n"
"slots wherever an attribute matching the canonical ARKit name (e.g. jawOpen,\n"
"mouthSmileLeft) exists on it -- typically a blendShape node whose targets are\n"
"named per the ARKit convention, or an ARKit-mocap-driven control."
)
autoconnect_btn.clicked.connect(self._on_autoconnect_blendshapes)
face_row.addWidget(autoconnect_btn)
self.blendshape_status_label = QtWidgets.QLabel("")
face_row.addWidget(self.blendshape_status_label)
face_row.addStretch(1)
layout.addLayout(face_row)
self.tree = QtWidgets.QTreeWidget()
self.tree.setColumnCount(3)
self.tree.setHeaderLabels(["Keypoint", "Mapped Joint", ""])
self.tree.setColumnWidth(0, 220)
self.tree.setColumnWidth(1, 180)
layout.addWidget(self.tree, 1)
self._build_rows()
def _build_rows(self):
self.tree.clear()
self._row_items = {}
part_items = {}
for part_id, _start, _count in constants.PARTS:
part_item = QtWidgets.QTreeWidgetItem([_PART_LABELS[part_id], "", ""])
self.tree.addTopLevelItem(part_item)
part_items[part_id] = part_item
for index in range(constants.TOTAL_KEYPOINTS):
part_id = constants.part_of(index)
row = QtWidgets.QTreeWidgetItem([constants.KEYPOINT_NAMES[index], "<unmapped>", ""])
part_items[part_id].addChild(row)
button_widget = QtWidgets.QWidget()
button_layout = QtWidgets.QHBoxLayout(button_widget)
button_layout.setContentsMargins(0, 0, 0, 0)
set_btn = QtWidgets.QPushButton("Set from Selected")
set_btn.clicked.connect(partial(self._on_set_joint, index))
clear_btn = QtWidgets.QPushButton("Clear")
clear_btn.clicked.connect(partial(self._on_clear_joint, index))
button_layout.addWidget(set_btn)
button_layout.addWidget(clear_btn)
self.tree.setItemWidget(row, 2, button_widget)
self._row_items[index] = row
self.tree.expandAll()
def _current_character(self):
return self.character_combo.currentText() or None
def refresh_characters(self):
current = self._current_character()
self.character_combo.blockSignals(True)
self.character_combo.clear()
characters = sorted(cmds.ls(type="openposeCharacter") or [])
self.character_combo.addItems(characters)
if current in characters:
self.character_combo.setCurrentText(current)
self.character_combo.blockSignals(False)
self.refresh_mapping()
def refresh_mapping(self):
character = self._current_character()
for index, row in self._row_items.items():
label = "<unmapped>"
if character:
plug = _target_plug(character, index)
sources = cmds.listConnections(plug, source=True, destination=False) or []
if sources:
label = sources[0]
row.setText(1, label)
self.body_model_combo.blockSignals(True)
if character:
self.body_model_combo.setEnabled(True)
self.body_model_combo.setCurrentIndex(cmds.getAttr(character + ".bodyModel"))
else:
self.body_model_combo.setEnabled(False)
self.body_model_combo.blockSignals(False)
for widget in (self.face_source_combo, self.face_scale_spin):
widget.blockSignals(True)
if character:
self.face_source_combo.setEnabled(True)
self.face_scale_spin.setEnabled(True)
self.face_source_combo.setCurrentIndex(cmds.getAttr(character + ".faceSource"))
self.face_scale_spin.setValue(cmds.getAttr(character + ".faceScale"))
connected = sum(
1 for i in range(face_blendshapes.BLENDSHAPE_COUNT)
if cmds.listConnections("{0}.blendshapeWeight[{1}]".format(character, i), source=True, destination=False)
)
self.blendshape_status_label.setText(
"{0} / {1} blendshapes connected".format(connected, face_blendshapes.BLENDSHAPE_COUNT)
)
else:
self.face_source_combo.setEnabled(False)
self.face_scale_spin.setEnabled(False)
self.blendshape_status_label.setText("")
for widget in (self.face_source_combo, self.face_scale_spin):
widget.blockSignals(False)
def _on_body_model_changed(self, index):
character = self._current_character()
if character:
cmds.setAttr(character + ".bodyModel", index)
def _on_face_source_changed(self, index):
character = self._current_character()
if character:
cmds.setAttr(character + ".faceSource", index)
def _on_face_scale_changed(self, value):
character = self._current_character()
if character:
cmds.setAttr(character + ".faceScale", value)
def _on_autoconnect_blendshapes(self):
character = self._current_character()
if not character:
QtWidgets.QMessageBox.warning(self, "OpenPose", "Create or select a character first.")
return
selected = cmds.ls(selection=True)
if not selected:
QtWidgets.QMessageBox.warning(
self, "OpenPose", "Select a blendShape node (or control) with ARKit-named attributes first."
)
return
source_node = selected[0]
connected_count = 0
for index, name in enumerate(face_blendshapes.ARKIT_BLENDSHAPE_NAMES):
if not cmds.attributeQuery(name, node=source_node, exists=True):
continue
plug = "{0}.blendshapeWeight[{1}]".format(character, index)
existing = cmds.listConnections(plug, source=True, destination=False, plugs=True) or []
for src in existing:
cmds.disconnectAttr(src, plug)
cmds.connectAttr("{0}.{1}".format(source_node, name), plug, force=True)
connected_count += 1
self.refresh_mapping()
QtWidgets.QMessageBox.information(
self, "OpenPose",
"Connected {0} / {1} ARKit blendshapes from '{2}'.".format(
connected_count, face_blendshapes.BLENDSHAPE_COUNT, source_node
),
)
def _on_create_character(self):
name, ok = QtWidgets.QInputDialog.getText(
self, "Create Character", "Name:", text="openposeCharacter1"
)
if not ok:
return
created = cmds.openposeCreateCharacter(name=name) if name else cmds.openposeCreateCharacter()
if isinstance(created, list): # MPxCommand string results come back as a 1-item list
created = created[0]
self.refresh_characters()
combo_index = self.character_combo.findText(created)
if combo_index >= 0:
self.character_combo.setCurrentIndex(combo_index)
def _on_set_joint(self, index):
character = self._current_character()
if not character:
QtWidgets.QMessageBox.warning(self, "OpenPose", "Create or select a character first.")
return
selected = cmds.ls(selection=True, type="joint")
if not selected:
QtWidgets.QMessageBox.warning(self, "OpenPose", "Select a joint first.")
return
joint = selected[0]
plug = _target_plug(character, index)
existing = cmds.listConnections(plug, source=True, destination=False, plugs=True) or []
for src in existing:
cmds.disconnectAttr(src, plug)
cmds.connectAttr(joint + ".message", plug, force=True)
self.refresh_mapping()
def _on_clear_joint(self, index):
character = self._current_character()
if not character:
return
plug = _target_plug(character, index)
existing = cmds.listConnections(plug, source=True, destination=False, plugs=True) or []
for src in existing:
cmds.disconnectAttr(src, plug)
self.refresh_mapping()
def show():
global _window_instance
if _window_instance is not None:
try:
_window_instance.close()
_window_instance.deleteLater()
except RuntimeError:
pass
_window_instance = MappingEditor()
_window_instance.show()
return _window_instance
+28
View File
@@ -0,0 +1,28 @@
"""Run this inside interactive Maya (Script Editor, Python tab) to enable
the "Maya: Attach (debugpy)" launch config in .vscode/launch.json.
One-time setup -- install debugpy into Maya's own Python environment
(adjust the path for your Maya version):
"C:\\Program Files\\Autodesk\\Maya2024\\bin\\mayapy.exe" -m pip install debugpy
Then, each Maya session, before attaching from VS Code:
exec(open(r"C:\\workspace\\PoseRenderer\\scripts\\start_debug_server.py").read())
Breakpoints hit in plug-ins/ or python/openpose_renderer/ (e.g. inside
draw_override.py's prepareForDraw, or a command's doIt) will then pause in
VS Code once you run "Maya: Attach (debugpy)".
"""
import debugpy
_ALREADY_LISTENING_ATTR = "_openpose_renderer_debugpy_listening"
if not getattr(debugpy, _ALREADY_LISTENING_ATTR, False):
debugpy.listen(("localhost", 5678))
setattr(debugpy, _ALREADY_LISTENING_ATTR, True)
print("[openpose_renderer] debugpy listening on localhost:5678 -- "
"run 'Maya: Attach (debugpy)' in VS Code to connect.")
else:
print("[openpose_renderer] debugpy already listening on localhost:5678.")
+331
View File
@@ -0,0 +1,331 @@
//Maya ASCII 2024 scene
//Name: openpose_arkit_face_test.ma
//Last modified: Tue, Jul 14, 2026 08:25:25 AM
//Codeset: 950
requires maya "2024";
requires -nodeType "openposeCharacter" "openposeRenderer.py" "1.0.0";
currentUnit -l centimeter -a degree -t film;
fileInfo "application" "maya";
fileInfo "product" "Maya 2024";
fileInfo "version" "2024";
fileInfo "cutIdentifier" "202310181224-69282f2959";
fileInfo "osv" "Windows 10 Pro v2009 (Build: 19045)";
fileInfo "UUID" "93C6E059-4B38-C365-F7D5-0FAD491D0DEB";
createNode transform -s -n "persp";
rename -uid "D6BB12A4-4481-B70D-81F9-22BC50DBF6A2";
setAttr ".v" no;
setAttr ".t" -type "double3" 28 21 28 ;
setAttr ".r" -type "double3" -27.938352729602379 44.999999999999972 -5.172681101354183e-14 ;
createNode camera -s -n "perspShape" -p "persp";
rename -uid "3B79812D-4CE2-EF69-FD29-D3819FB20809";
setAttr -k off ".v" no;
setAttr ".fl" 34.999999999999993;
setAttr ".coi" 44.82186966202994;
setAttr ".imn" -type "string" "persp";
setAttr ".den" -type "string" "persp_depth";
setAttr ".man" -type "string" "persp_mask";
setAttr ".hc" -type "string" "viewSet -p %camera";
createNode transform -s -n "top";
rename -uid "7D9B1797-4CCF-180D-3705-9C9C4F956631";
setAttr ".v" no;
setAttr ".t" -type "double3" 0 1000.1 0 ;
setAttr ".r" -type "double3" -90 0 0 ;
createNode camera -s -n "topShape" -p "top";
rename -uid "02429A50-43C6-9AEF-C9A9-0EA56778BABE";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 1000.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "top";
setAttr ".den" -type "string" "top_depth";
setAttr ".man" -type "string" "top_mask";
setAttr ".hc" -type "string" "viewSet -t %camera";
setAttr ".o" yes;
createNode transform -s -n "front";
rename -uid "E313E787-411D-6315-E6D5-878346B7C7C7";
setAttr ".v" no;
setAttr ".t" -type "double3" 0 0 1000.1 ;
createNode camera -s -n "frontShape" -p "front";
rename -uid "CEB81F55-4481-E767-A275-09ACF3D57563";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 1000.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "front";
setAttr ".den" -type "string" "front_depth";
setAttr ".man" -type "string" "front_mask";
setAttr ".hc" -type "string" "viewSet -f %camera";
setAttr ".o" yes;
createNode transform -s -n "side";
rename -uid "23726E62-4236-BA09-5DDB-A8B4AB6467DE";
setAttr ".v" no;
setAttr ".t" -type "double3" 1000.1 0 0 ;
setAttr ".r" -type "double3" 0 90 0 ;
createNode camera -s -n "sideShape" -p "side";
rename -uid "0EF9B535-4F25-E296-6FE9-17897B0CD5F8";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 1000.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "side";
setAttr ".den" -type "string" "side_depth";
setAttr ".man" -type "string" "side_mask";
setAttr ".hc" -type "string" "viewSet -s %camera";
setAttr ".o" yes;
createNode joint -n "Head";
rename -uid "10AD57B3-4A53-D241-702A-E4B87D617D75";
setAttr ".t" -type "double3" 0 150 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode transform -n "transform1";
rename -uid "AC0B13DC-4C96-222F-1C95-D881328B821A";
createNode openposeCharacter -n "openposeCharacter_ARKitFace" -p "transform1";
rename -uid "91BF3ED3-4092-9E34-128B-1C98376C9C10";
setAttr -k off ".v";
setAttr ".fsrc" 1;
setAttr -s 52 ".bsw";
setAttr -s 52 ".bsw";
createNode transform -n "faceControl";
rename -uid "BEF04EC7-4C59-6B22-48FA-208B994FD89B";
addAttr -ci true -sn "eyeBlinkLeft" -ln "eyeBlinkLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeLookDownLeft" -ln "eyeLookDownLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeLookInLeft" -ln "eyeLookInLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeLookOutLeft" -ln "eyeLookOutLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeLookUpLeft" -ln "eyeLookUpLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeSquintLeft" -ln "eyeSquintLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeWideLeft" -ln "eyeWideLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeBlinkRight" -ln "eyeBlinkRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeLookDownRight" -ln "eyeLookDownRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeLookInRight" -ln "eyeLookInRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeLookOutRight" -ln "eyeLookOutRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeLookUpRight" -ln "eyeLookUpRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeSquintRight" -ln "eyeSquintRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "eyeWideRight" -ln "eyeWideRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "jawForward" -ln "jawForward" -min 0 -max 1 -at "float";
addAttr -ci true -sn "jawLeft" -ln "jawLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "jawRight" -ln "jawRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "jawOpen" -ln "jawOpen" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthClose" -ln "mouthClose" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthFunnel" -ln "mouthFunnel" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthPucker" -ln "mouthPucker" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthLeft" -ln "mouthLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthRight" -ln "mouthRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthSmileLeft" -ln "mouthSmileLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthSmileRight" -ln "mouthSmileRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthFrownLeft" -ln "mouthFrownLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthFrownRight" -ln "mouthFrownRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthDimpleLeft" -ln "mouthDimpleLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthDimpleRight" -ln "mouthDimpleRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthStretchLeft" -ln "mouthStretchLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthStretchRight" -ln "mouthStretchRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthRollLower" -ln "mouthRollLower" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthRollUpper" -ln "mouthRollUpper" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthShrugLower" -ln "mouthShrugLower" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthShrugUpper" -ln "mouthShrugUpper" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthPressLeft" -ln "mouthPressLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthPressRight" -ln "mouthPressRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthLowerDownLeft" -ln "mouthLowerDownLeft" -min 0 -max 1
-at "float";
addAttr -ci true -sn "mouthLowerDownRight" -ln "mouthLowerDownRight" -min 0 -max
1 -at "float";
addAttr -ci true -sn "mouthUpperUpLeft" -ln "mouthUpperUpLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "mouthUpperUpRight" -ln "mouthUpperUpRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "browDownLeft" -ln "browDownLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "browDownRight" -ln "browDownRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "browInnerUp" -ln "browInnerUp" -min 0 -max 1 -at "float";
addAttr -ci true -sn "browOuterUpLeft" -ln "browOuterUpLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "browOuterUpRight" -ln "browOuterUpRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "cheekPuff" -ln "cheekPuff" -min 0 -max 1 -at "float";
addAttr -ci true -sn "cheekSquintLeft" -ln "cheekSquintLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "cheekSquintRight" -ln "cheekSquintRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "noseSneerLeft" -ln "noseSneerLeft" -min 0 -max 1 -at "float";
addAttr -ci true -sn "noseSneerRight" -ln "noseSneerRight" -min 0 -max 1 -at "float";
addAttr -ci true -sn "tongueOut" -ln "tongueOut" -min 0 -max 1 -at "float";
setAttr -k on ".eyeBlinkLeft";
setAttr -k on ".eyeLookDownLeft";
setAttr -k on ".eyeLookInLeft";
setAttr -k on ".eyeLookOutLeft";
setAttr -k on ".eyeLookUpLeft";
setAttr -k on ".eyeSquintLeft";
setAttr -k on ".eyeWideLeft" 0.69999998807907104;
setAttr -k on ".eyeBlinkRight";
setAttr -k on ".eyeLookDownRight";
setAttr -k on ".eyeLookInRight";
setAttr -k on ".eyeLookOutRight";
setAttr -k on ".eyeLookUpRight";
setAttr -k on ".eyeSquintRight";
setAttr -k on ".eyeWideRight" 0.69999998807907104;
setAttr -k on ".jawForward";
setAttr -k on ".jawLeft";
setAttr -k on ".jawRight";
setAttr -k on ".jawOpen" 0.60000002384185791;
setAttr -k on ".mouthClose";
setAttr -k on ".mouthFunnel";
setAttr -k on ".mouthPucker";
setAttr -k on ".mouthLeft";
setAttr -k on ".mouthRight";
setAttr -k on ".mouthSmileLeft" 1;
setAttr -k on ".mouthSmileRight" 1;
setAttr -k on ".mouthFrownLeft";
setAttr -k on ".mouthFrownRight";
setAttr -k on ".mouthDimpleLeft";
setAttr -k on ".mouthDimpleRight";
setAttr -k on ".mouthStretchLeft";
setAttr -k on ".mouthStretchRight";
setAttr -k on ".mouthRollLower";
setAttr -k on ".mouthRollUpper";
setAttr -k on ".mouthShrugLower";
setAttr -k on ".mouthShrugUpper";
setAttr -k on ".mouthPressLeft";
setAttr -k on ".mouthPressRight";
setAttr -k on ".mouthLowerDownLeft";
setAttr -k on ".mouthLowerDownRight";
setAttr -k on ".mouthUpperUpLeft";
setAttr -k on ".mouthUpperUpRight";
setAttr -k on ".browDownLeft";
setAttr -k on ".browDownRight";
setAttr -k on ".browInnerUp" 0.80000001192092896;
setAttr -k on ".browOuterUpLeft" 0.60000002384185791;
setAttr -k on ".browOuterUpRight" 0.60000002384185791;
setAttr -k on ".cheekPuff";
setAttr -k on ".cheekSquintLeft";
setAttr -k on ".cheekSquintRight";
setAttr -k on ".noseSneerLeft";
setAttr -k on ".noseSneerRight";
setAttr -k on ".tongueOut";
createNode lightLinker -s -n "lightLinker1";
rename -uid "6F41ABE6-4FC9-CBA9-3A4A-3C88AFFEDA05";
setAttr -s 2 ".lnk";
setAttr -s 2 ".slnk";
createNode displayLayerManager -n "layerManager";
rename -uid "C5E3AE68-4041-3C21-D33D-2DBCA8E53402";
createNode displayLayer -n "defaultLayer";
rename -uid "7E1F3A70-4A01-B94C-04B6-24ABE836AECA";
setAttr ".ufem" -type "stringArray" 0 ;
createNode renderLayerManager -n "renderLayerManager";
rename -uid "E2FC1CAE-4BC5-8835-9145-E2AFED84D0D6";
createNode renderLayer -n "defaultRenderLayer";
rename -uid "711E2B13-42DB-052A-1562-1C99F88A0E30";
setAttr ".g" yes;
createNode script -n "uiConfigurationScriptNode";
rename -uid "F0132003-4E9D-D0A6-A39C-F6958ABF910E";
setAttr ".b" -type "string" "// Maya Mel UI Configuration File.\n// No UI generated in batch mode.\n";
setAttr ".st" 3;
createNode script -n "sceneConfigurationScriptNode";
rename -uid "5E8354DD-4768-B99B-F133-489A3680AEB2";
setAttr ".b" -type "string" "playbackOptions -min 1 -max 120 -ast 1 -aet 200 ";
setAttr ".st" 6;
select -ne :time1;
setAttr ".o" 1;
setAttr ".unw" 1;
select -ne :hardwareRenderingGlobals;
setAttr ".otfna" -type "stringArray" 22 "NURBS Curves" "NURBS Surfaces" "Polygons" "Subdiv Surface" "Particles" "Particle Instance" "Fluids" "Strokes" "Image Planes" "UI" "Lights" "Cameras" "Locators" "Joints" "IK Handles" "Deformers" "Motion Trails" "Components" "Hair Systems" "Follicles" "Misc. UI" "Ornaments" ;
setAttr ".otfva" -type "Int32Array" 22 0 1 1 1 1 1
1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 ;
setAttr ".fprt" yes;
setAttr ".rtfm" 1;
select -ne :renderPartition;
setAttr -s 2 ".st";
select -ne :renderGlobalsList1;
select -ne :defaultShaderList1;
setAttr -s 5 ".s";
select -ne :postProcessList1;
setAttr -s 2 ".p";
select -ne :defaultRenderingList1;
select -ne :standardSurface1;
setAttr ".bc" -type "float3" 0.40000001 0.40000001 0.40000001 ;
setAttr ".sr" 0.5;
select -ne :initialShadingGroup;
setAttr ".ro" yes;
select -ne :initialParticleSE;
setAttr ".ro" yes;
select -ne :defaultRenderGlobals;
addAttr -ci true -h true -sn "dss" -ln "defaultSurfaceShader" -dt "string";
setAttr ".dss" -type "string" "standardSurface1";
select -ne :defaultResolution;
setAttr ".w" 512;
setAttr ".h" 512;
setAttr ".pa" 1;
select -ne :defaultColorMgtGlobals;
setAttr ".cfe" yes;
setAttr ".cfp" -type "string" "<MAYA_RESOURCES>/OCIO-configs/Maya2022-default/config.ocio";
setAttr ".vtn" -type "string" "ACES 1.0 SDR-video (sRGB)";
setAttr ".vn" -type "string" "ACES 1.0 SDR-video";
setAttr ".dn" -type "string" "sRGB";
setAttr ".wsn" -type "string" "ACEScg";
setAttr ".otn" -type "string" "ACES 1.0 SDR-video (sRGB)";
setAttr ".potn" -type "string" "ACES 1.0 SDR-video (sRGB)";
select -ne :hardwareRenderGlobals;
setAttr ".ctrs" 256;
setAttr ".btrs" 512;
connectAttr "Head.msg" "openposeCharacter_ARKitFace.tj[0]";
connectAttr "faceControl.eyeBlinkLeft" "openposeCharacter_ARKitFace.bsw[0]";
connectAttr "faceControl.eyeLookDownLeft" "openposeCharacter_ARKitFace.bsw[1]";
connectAttr "faceControl.eyeLookInLeft" "openposeCharacter_ARKitFace.bsw[2]";
connectAttr "faceControl.eyeLookOutLeft" "openposeCharacter_ARKitFace.bsw[3]";
connectAttr "faceControl.eyeLookUpLeft" "openposeCharacter_ARKitFace.bsw[4]";
connectAttr "faceControl.eyeSquintLeft" "openposeCharacter_ARKitFace.bsw[5]";
connectAttr "faceControl.eyeWideLeft" "openposeCharacter_ARKitFace.bsw[6]";
connectAttr "faceControl.eyeBlinkRight" "openposeCharacter_ARKitFace.bsw[7]";
connectAttr "faceControl.eyeLookDownRight" "openposeCharacter_ARKitFace.bsw[8]";
connectAttr "faceControl.eyeLookInRight" "openposeCharacter_ARKitFace.bsw[9]";
connectAttr "faceControl.eyeLookOutRight" "openposeCharacter_ARKitFace.bsw[10]";
connectAttr "faceControl.eyeLookUpRight" "openposeCharacter_ARKitFace.bsw[11]";
connectAttr "faceControl.eyeSquintRight" "openposeCharacter_ARKitFace.bsw[12]";
connectAttr "faceControl.eyeWideRight" "openposeCharacter_ARKitFace.bsw[13]";
connectAttr "faceControl.jawForward" "openposeCharacter_ARKitFace.bsw[14]";
connectAttr "faceControl.jawLeft" "openposeCharacter_ARKitFace.bsw[15]";
connectAttr "faceControl.jawRight" "openposeCharacter_ARKitFace.bsw[16]";
connectAttr "faceControl.jawOpen" "openposeCharacter_ARKitFace.bsw[17]";
connectAttr "faceControl.mouthClose" "openposeCharacter_ARKitFace.bsw[18]";
connectAttr "faceControl.mouthFunnel" "openposeCharacter_ARKitFace.bsw[19]";
connectAttr "faceControl.mouthPucker" "openposeCharacter_ARKitFace.bsw[20]";
connectAttr "faceControl.mouthLeft" "openposeCharacter_ARKitFace.bsw[21]";
connectAttr "faceControl.mouthRight" "openposeCharacter_ARKitFace.bsw[22]";
connectAttr "faceControl.mouthSmileLeft" "openposeCharacter_ARKitFace.bsw[23]";
connectAttr "faceControl.mouthSmileRight" "openposeCharacter_ARKitFace.bsw[24]";
connectAttr "faceControl.mouthFrownLeft" "openposeCharacter_ARKitFace.bsw[25]";
connectAttr "faceControl.mouthFrownRight" "openposeCharacter_ARKitFace.bsw[26]";
connectAttr "faceControl.mouthDimpleLeft" "openposeCharacter_ARKitFace.bsw[27]";
connectAttr "faceControl.mouthDimpleRight" "openposeCharacter_ARKitFace.bsw[28]"
;
connectAttr "faceControl.mouthStretchLeft" "openposeCharacter_ARKitFace.bsw[29]"
;
connectAttr "faceControl.mouthStretchRight" "openposeCharacter_ARKitFace.bsw[30]"
;
connectAttr "faceControl.mouthRollLower" "openposeCharacter_ARKitFace.bsw[31]";
connectAttr "faceControl.mouthRollUpper" "openposeCharacter_ARKitFace.bsw[32]";
connectAttr "faceControl.mouthShrugLower" "openposeCharacter_ARKitFace.bsw[33]";
connectAttr "faceControl.mouthShrugUpper" "openposeCharacter_ARKitFace.bsw[34]";
connectAttr "faceControl.mouthPressLeft" "openposeCharacter_ARKitFace.bsw[35]";
connectAttr "faceControl.mouthPressRight" "openposeCharacter_ARKitFace.bsw[36]";
connectAttr "faceControl.mouthLowerDownLeft" "openposeCharacter_ARKitFace.bsw[37]"
;
connectAttr "faceControl.mouthLowerDownRight" "openposeCharacter_ARKitFace.bsw[38]"
;
connectAttr "faceControl.mouthUpperUpLeft" "openposeCharacter_ARKitFace.bsw[39]"
;
connectAttr "faceControl.mouthUpperUpRight" "openposeCharacter_ARKitFace.bsw[40]"
;
connectAttr "faceControl.browDownLeft" "openposeCharacter_ARKitFace.bsw[41]";
connectAttr "faceControl.browDownRight" "openposeCharacter_ARKitFace.bsw[42]";
connectAttr "faceControl.browInnerUp" "openposeCharacter_ARKitFace.bsw[43]";
connectAttr "faceControl.browOuterUpLeft" "openposeCharacter_ARKitFace.bsw[44]";
connectAttr "faceControl.browOuterUpRight" "openposeCharacter_ARKitFace.bsw[45]"
;
connectAttr "faceControl.cheekPuff" "openposeCharacter_ARKitFace.bsw[46]";
connectAttr "faceControl.cheekSquintLeft" "openposeCharacter_ARKitFace.bsw[47]";
connectAttr "faceControl.cheekSquintRight" "openposeCharacter_ARKitFace.bsw[48]"
;
connectAttr "faceControl.noseSneerLeft" "openposeCharacter_ARKitFace.bsw[49]";
connectAttr "faceControl.noseSneerRight" "openposeCharacter_ARKitFace.bsw[50]";
connectAttr "faceControl.tongueOut" "openposeCharacter_ARKitFace.bsw[51]";
relationship "link" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message";
relationship "link" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message";
relationship "shadowLink" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message";
relationship "shadowLink" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message";
connectAttr "layerManager.dli[0]" "defaultLayer.id";
connectAttr "renderLayerManager.rlmi[0]" "defaultRenderLayer.rlid";
connectAttr "defaultRenderLayer.msg" ":defaultRenderingList1.r" -na;
// End of openpose_arkit_face_test.ma
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

+600
View File
@@ -0,0 +1,600 @@
//Maya ASCII 2024 scene
//Name: openpose_humanik_test.ma
//Last modified: Tue, Jul 14, 2026 08:03:24 AM
//Codeset: 950
requires maya "2024";
requires -nodeType "openposeCharacter" "openposeRenderer.py" "1.0.0";
currentUnit -l centimeter -a degree -t film;
fileInfo "application" "maya";
fileInfo "product" "Maya 2024";
fileInfo "version" "2024";
fileInfo "cutIdentifier" "202310181224-69282f2959";
fileInfo "osv" "Windows 10 Pro v2009 (Build: 19045)";
fileInfo "UUID" "B6CCE6EA-431E-6CE9-7360-46BABE0883C3";
createNode transform -s -n "persp";
rename -uid "8310D31F-4947-8244-A1E4-1EBAEA32C348";
setAttr ".v" no;
setAttr ".t" -type "double3" 28 21 28 ;
setAttr ".r" -type "double3" -27.938352729602379 44.999999999999972 -5.172681101354183e-14 ;
createNode camera -s -n "perspShape" -p "persp";
rename -uid "82EA3896-4D08-DC2C-28CD-599B24956C13";
setAttr -k off ".v" no;
setAttr ".fl" 34.999999999999993;
setAttr ".coi" 44.82186966202994;
setAttr ".imn" -type "string" "persp";
setAttr ".den" -type "string" "persp_depth";
setAttr ".man" -type "string" "persp_mask";
setAttr ".hc" -type "string" "viewSet -p %camera";
createNode transform -s -n "top";
rename -uid "DABFE07D-478C-AA01-1F9C-678755F64C09";
setAttr ".v" no;
setAttr ".t" -type "double3" 0 1000.1 0 ;
setAttr ".r" -type "double3" -90 0 0 ;
createNode camera -s -n "topShape" -p "top";
rename -uid "D3D3EFDD-4F6D-F68D-9220-0DA16D0A1F71";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 1000.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "top";
setAttr ".den" -type "string" "top_depth";
setAttr ".man" -type "string" "top_mask";
setAttr ".hc" -type "string" "viewSet -t %camera";
setAttr ".o" yes;
createNode transform -s -n "front";
rename -uid "F8E3E41F-4F0B-4D85-E2B1-48A41CAB1138";
setAttr ".v" no;
setAttr ".t" -type "double3" 0 0 1000.1 ;
createNode camera -s -n "frontShape" -p "front";
rename -uid "CCC3EC0C-46DA-91A7-0DCC-81BC4C7FAF07";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 1000.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "front";
setAttr ".den" -type "string" "front_depth";
setAttr ".man" -type "string" "front_mask";
setAttr ".hc" -type "string" "viewSet -f %camera";
setAttr ".o" yes;
createNode transform -s -n "side";
rename -uid "2DD1A2E6-45CB-8424-CD34-EA99A6909668";
setAttr ".v" no;
setAttr ".t" -type "double3" 1000.1 0 0 ;
setAttr ".r" -type "double3" 0 90 0 ;
createNode camera -s -n "sideShape" -p "side";
rename -uid "DC0110C9-4CDD-4029-B41F-65ABE3304C2D";
setAttr -k off ".v" no;
setAttr ".rnd" no;
setAttr ".coi" 1000.1;
setAttr ".ow" 30;
setAttr ".imn" -type "string" "side";
setAttr ".den" -type "string" "side_depth";
setAttr ".man" -type "string" "side_mask";
setAttr ".hc" -type "string" "viewSet -s %camera";
setAttr ".o" yes;
createNode joint -n "Hips";
rename -uid "E9306125-46F8-B494-7C68-9AB3C96BAC96";
setAttr ".t" -type "double3" 0 100 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "Spine" -p "Hips";
rename -uid "58DE271D-43EC-C3AB-7A53-B7BF3FA13790";
setAttr ".t" -type "double3" 0 8 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "Spine1" -p "Spine";
rename -uid "F6BC0D78-4658-A609-465B-39ADC157E71F";
setAttr ".t" -type "double3" 0 8 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "Spine2" -p "Spine1";
rename -uid "73D40132-43A4-BB37-4641-9898153CE359";
setAttr ".t" -type "double3" 0 8 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "Neck" -p "Spine2";
rename -uid "50248927-4A10-1E1A-B0EC-9F9433272563";
setAttr ".t" -type "double3" 0 14 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "Head" -p "Neck";
rename -uid "E2E49FA6-40DF-644F-C277-F1A5600E55BA";
setAttr ".t" -type "double3" 0 10 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "HeadTop_End" -p "Head";
rename -uid "9F15C7EE-4C19-3979-4323-0F9EB7F7D30F";
setAttr ".t" -type "double3" 0 17 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftShoulder" -p "Spine2";
rename -uid "9D4188CC-43BF-05F9-8A26-E9B00AC84698";
setAttr ".t" -type "double3" 6 6 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftArm" -p "LeftShoulder";
rename -uid "A82601D5-44B9-0B04-B4DB-B281736DFCD9";
setAttr ".t" -type "double3" 12 -2 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftForeArm" -p "LeftArm";
rename -uid "531ACB20-4CA6-6BE7-8AD6-3F97594BB8DD";
setAttr ".t" -type "double3" 24 -2 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHand" -p "LeftForeArm";
rename -uid "5584D550-4C96-1A13-866F-45A6511F910E";
setAttr ".t" -type "double3" 22 -2 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandThumb1" -p "LeftHand";
rename -uid "6D4E3EC0-4A52-74B4-205B-0681F5FFDE44";
setAttr ".t" -type "double3" 2 -2 4 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandThumb2" -p "LeftHandThumb1";
rename -uid "AA96287C-47EC-C043-93CD-BC9A89DE027E";
setAttr ".t" -type "double3" 3.0000000000000142 -1 2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandThumb3" -p "LeftHandThumb2";
rename -uid "4B6B9912-4582-8B2D-961F-AEBAD5C512E8";
setAttr ".t" -type "double3" 2.9999999999999716 -1 2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandThumb4" -p "LeftHandThumb3";
rename -uid "B01DBD78-4847-78DD-78C8-D2B20522113E";
setAttr ".t" -type "double3" 3.0000000000000142 -1 2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandIndex1" -p "LeftHand";
rename -uid "A03138BA-4885-2601-C006-D88B90383B56";
setAttr ".t" -type "double3" 6 0 3 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandIndex2" -p "LeftHandIndex1";
rename -uid "398FBD7F-4423-1D30-6922-A590AA87CA98";
setAttr ".t" -type "double3" 5.9999999999999858 0 0.99999999999999956 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandIndex3" -p "LeftHandIndex2";
rename -uid "9BB07943-436F-A380-3B61-9A94703D5365";
setAttr ".t" -type "double3" 5.0000000000000142 0 1.0000000000000009 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandIndex4" -p "LeftHandIndex3";
rename -uid "4517123B-47F2-4473-C397-368B225A3EBF";
setAttr ".t" -type "double3" 4.9999999999999858 0 1 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandMiddle1" -p "LeftHand";
rename -uid "8A3473A1-4000-3BDE-62C8-B2842706D6E1";
setAttr ".t" -type "double3" 7 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandMiddle2" -p "LeftHandMiddle1";
rename -uid "97989261-45CF-6F6D-44AE-A291F202CBBA";
setAttr ".t" -type "double3" 6 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandMiddle3" -p "LeftHandMiddle2";
rename -uid "26E91C0B-44E4-B3CE-3BEE-0A9BB5B04F72";
setAttr ".t" -type "double3" 5 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandMiddle4" -p "LeftHandMiddle3";
rename -uid "AA8533FE-4725-477F-0D4C-A3850047AC5D";
setAttr ".t" -type "double3" 5 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandRing1" -p "LeftHand";
rename -uid "EC92DECD-4CDD-7220-9D22-4DB5884D3B6F";
setAttr ".t" -type "double3" 6 0 -3 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandRing2" -p "LeftHandRing1";
rename -uid "7DC27F02-4D46-0B9C-6D2C-5ABA3C58B0D5";
setAttr ".t" -type "double3" 5.9999999999999858 0 -0.99999999999999956 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandRing3" -p "LeftHandRing2";
rename -uid "C07A75D5-4F15-44BA-BA70-A38B4A5BD455";
setAttr ".t" -type "double3" 5.0000000000000142 0 -1.0000000000000009 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandRing4" -p "LeftHandRing3";
rename -uid "19BE3F34-47D8-3586-C9A2-9FBDFA49E044";
setAttr ".t" -type "double3" 4.9999999999999858 0 -1 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandPinky1" -p "LeftHand";
rename -uid "7179691A-4710-483B-418D-C5BEC7AF33D2";
setAttr ".t" -type "double3" 5 0 -6 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandPinky2" -p "LeftHandPinky1";
rename -uid "6AA6C96A-47A4-0573-057C-709DA0C4094D";
setAttr ".t" -type "double3" 4.9999999999999858 0 -2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandPinky3" -p "LeftHandPinky2";
rename -uid "6A9BC4C2-4EFF-8778-C75F-808DAE8134C6";
setAttr ".t" -type "double3" 4.0000000000000284 1.4210854715202004e-14 -2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftHandPinky4" -p "LeftHandPinky3";
rename -uid "CA4A295D-447B-5283-3FB0-7C91D3D7CD74";
setAttr ".t" -type "double3" 3.9999999999999858 -1.4210854715202004e-14 -2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightShoulder" -p "Spine2";
rename -uid "BE79F239-48FB-A0A7-806C-5BAAB007AAEA";
setAttr ".t" -type "double3" -6 6 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightArm" -p "RightShoulder";
rename -uid "70793BE9-41F1-1093-61E1-1096408735AB";
setAttr ".t" -type "double3" -12 -2 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightForeArm" -p "RightArm";
rename -uid "891BC1DC-43C2-7372-FC15-1B99956F3C2E";
setAttr ".t" -type "double3" -24 -2 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHand" -p "RightForeArm";
rename -uid "DA45E6E9-4B12-EA7D-F342-20AE197FA29B";
setAttr ".t" -type "double3" -22 -2 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandThumb1" -p "RightHand";
rename -uid "8E544F46-4E34-33B3-83D1-25998605482F";
setAttr ".t" -type "double3" -2 -2 4 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandThumb2" -p "RightHandThumb1";
rename -uid "43B6E543-4D87-4B53-F755-6EB549FB3BC4";
setAttr ".t" -type "double3" -3.0000000000000142 -1 2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandThumb3" -p "RightHandThumb2";
rename -uid "DC49883E-48D7-286B-5F07-D9982CADE0AF";
setAttr ".t" -type "double3" -2.9999999999999716 -1 2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandThumb4" -p "RightHandThumb3";
rename -uid "5D206C3A-421E-9936-D390-6B9CB23810DF";
setAttr ".t" -type "double3" -3.0000000000000142 -1 2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandIndex1" -p "RightHand";
rename -uid "8C06B2EA-4B98-E406-F401-949FB260613D";
setAttr ".t" -type "double3" -6 0 3 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandIndex2" -p "RightHandIndex1";
rename -uid "73F71279-406F-09AD-701D-0BA6B49BE549";
setAttr ".t" -type "double3" -5.9999999999999858 0 0.99999999999999956 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandIndex3" -p "RightHandIndex2";
rename -uid "061EF514-4CAA-A47F-DEB6-BFAD2F263C96";
setAttr ".t" -type "double3" -5.0000000000000142 0 1.0000000000000009 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandIndex4" -p "RightHandIndex3";
rename -uid "DDF6214D-4359-D570-5413-7C94AB13AC38";
setAttr ".t" -type "double3" -4.9999999999999858 0 1 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandMiddle1" -p "RightHand";
rename -uid "6506A5F1-4593-D2B3-C7C5-B4AB35ED7D37";
setAttr ".t" -type "double3" -7 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandMiddle2" -p "RightHandMiddle1";
rename -uid "08A64278-4C70-F47A-D18F-CC9C8411BE57";
setAttr ".t" -type "double3" -6 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandMiddle3" -p "RightHandMiddle2";
rename -uid "748CD3DA-4B74-0088-C9AB-FA8AE8D7CAB5";
setAttr ".t" -type "double3" -5 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandMiddle4" -p "RightHandMiddle3";
rename -uid "7633A974-417A-20FF-334F-C2BB7A7036D6";
setAttr ".t" -type "double3" -5 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandRing1" -p "RightHand";
rename -uid "867831A1-417A-2205-69D2-43B66EBD926A";
setAttr ".t" -type "double3" -6 0 -3 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandRing2" -p "RightHandRing1";
rename -uid "18D84F5D-47EC-EEC5-8502-4E870F9DE8FE";
setAttr ".t" -type "double3" -5.9999999999999858 0 -0.99999999999999956 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandRing3" -p "RightHandRing2";
rename -uid "5F1CCA7A-48C7-8646-DD9D-EABF4C7AB79E";
setAttr ".t" -type "double3" -5.0000000000000142 0 -1.0000000000000009 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandRing4" -p "RightHandRing3";
rename -uid "91E8358F-4CA2-A7FC-8E68-EB85D4CEE45A";
setAttr ".t" -type "double3" -4.9999999999999858 0 -1 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandPinky1" -p "RightHand";
rename -uid "A6572AF3-4928-F15A-30CC-EA8C0149BBDC";
setAttr ".t" -type "double3" -5 0 -6 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandPinky2" -p "RightHandPinky1";
rename -uid "AC21159E-45C1-65B8-2EA9-858DD060F467";
setAttr ".t" -type "double3" -4.9999999999999858 0 -2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandPinky3" -p "RightHandPinky2";
rename -uid "279FA6BA-44C6-3A12-0CEF-E2A65DD954D3";
setAttr ".t" -type "double3" -4.0000000000000284 1.4210854715202004e-14 -2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightHandPinky4" -p "RightHandPinky3";
rename -uid "81A4FC64-4F40-282A-062F-8897DF25690D";
setAttr ".t" -type "double3" -3.9999999999999858 -1.4210854715202004e-14 -2 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftUpLeg" -p "Hips";
rename -uid "C6D9AA89-47BB-9803-CA4F-5CAEFC4BD087";
setAttr ".t" -type "double3" 9 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftLeg" -p "LeftUpLeg";
rename -uid "657ECF49-4370-B8E1-6E3C-C1AF7725A5B5";
setAttr ".t" -type "double3" -1.7763568394002505e-15 -45 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftFoot" -p "LeftLeg";
rename -uid "FCB58E90-49E7-F954-C7DF-6FA091557209";
setAttr ".t" -type "double3" 1.7763568394002505e-15 -45 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftToeBase" -p "LeftFoot";
rename -uid "56B5AE88-4263-7496-B6FE-8B854CB1C5E0";
setAttr ".t" -type "double3" 1.7763568394002505e-15 -10 14 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "LeftToe_End" -p "LeftToeBase";
rename -uid "F71FD7B7-48CD-2E65-44BD-CE94B6622567";
setAttr ".t" -type "double3" -3.5527136788005009e-15 0 10 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightUpLeg" -p "Hips";
rename -uid "A0973F04-41A3-B049-0942-25A06B02173D";
setAttr ".t" -type "double3" -9 0 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightLeg" -p "RightUpLeg";
rename -uid "D8909A18-4033-2D8A-76C0-8583C5A52D03";
setAttr ".t" -type "double3" 1.7763568394002505e-15 -45 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightFoot" -p "RightLeg";
rename -uid "9706929B-41F9-72B6-AF92-33AE9BFDBD5E";
setAttr ".t" -type "double3" -1.7763568394002505e-15 -45 0 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightToeBase" -p "RightFoot";
rename -uid "105CF3D4-40DB-FED2-236B-888495BFF390";
setAttr ".t" -type "double3" -1.7763568394002505e-15 -10 14 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode joint -n "RightToe_End" -p "RightToeBase";
rename -uid "C8B059A4-47AA-8A97-8733-D9BE21B009F7";
setAttr ".t" -type "double3" 3.5527136788005009e-15 0 10 ;
setAttr ".mnrl" -type "double3" -360 -360 -360 ;
setAttr ".mxrl" -type "double3" 360 360 360 ;
createNode transform -n "transform1";
rename -uid "9D123D7D-4CC9-093E-A509-0690E79315F7";
createNode openposeCharacter -n "openposeCharacter_HIK" -p "transform1";
rename -uid "F4C90625-4C92-897E-8D0B-24B04BDDCD5D";
setAttr -k off ".v";
setAttr -s 59 ".tj";
createNode lightLinker -s -n "lightLinker1";
rename -uid "D9C92F2B-4274-F3E8-55F9-3C9220E74C8C";
setAttr -s 2 ".lnk";
setAttr -s 2 ".slnk";
createNode displayLayerManager -n "layerManager";
rename -uid "CCEC4154-4F20-8390-7839-93A6B3FE0C0C";
createNode displayLayer -n "defaultLayer";
rename -uid "0E4AF3B6-4A43-CBF3-4F49-448E0BA8F555";
setAttr ".ufem" -type "stringArray" 0 ;
createNode renderLayerManager -n "renderLayerManager";
rename -uid "40B9D3D6-414F-4FFE-9266-6E83CDFBAACF";
createNode renderLayer -n "defaultRenderLayer";
rename -uid "4C2D15F1-4F34-6E9A-D299-50A2D0F719CA";
setAttr ".g" yes;
createNode script -n "uiConfigurationScriptNode";
rename -uid "C8AD5306-42A0-C02B-2782-70BFA1B3DBD4";
setAttr ".b" -type "string" "// Maya Mel UI Configuration File.\n// No UI generated in batch mode.\n";
setAttr ".st" 3;
createNode script -n "sceneConfigurationScriptNode";
rename -uid "58E5288A-41F1-3CA0-2020-0F9236560092";
setAttr ".b" -type "string" "playbackOptions -min 1 -max 120 -ast 1 -aet 200 ";
setAttr ".st" 6;
select -ne :time1;
setAttr ".o" 1;
setAttr ".unw" 1;
select -ne :hardwareRenderingGlobals;
setAttr ".otfna" -type "stringArray" 22 "NURBS Curves" "NURBS Surfaces" "Polygons" "Subdiv Surface" "Particles" "Particle Instance" "Fluids" "Strokes" "Image Planes" "UI" "Lights" "Cameras" "Locators" "Joints" "IK Handles" "Deformers" "Motion Trails" "Components" "Hair Systems" "Follicles" "Misc. UI" "Ornaments" ;
setAttr ".otfva" -type "Int32Array" 22 0 1 1 1 1 1
1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 ;
setAttr ".fprt" yes;
setAttr ".rtfm" 1;
select -ne :renderPartition;
setAttr -s 2 ".st";
select -ne :renderGlobalsList1;
select -ne :defaultShaderList1;
setAttr -s 5 ".s";
select -ne :postProcessList1;
setAttr -s 2 ".p";
select -ne :defaultRenderingList1;
select -ne :standardSurface1;
setAttr ".bc" -type "float3" 0.40000001 0.40000001 0.40000001 ;
setAttr ".sr" 0.5;
select -ne :initialShadingGroup;
setAttr ".ro" yes;
select -ne :initialParticleSE;
setAttr ".ro" yes;
select -ne :defaultRenderGlobals;
addAttr -ci true -h true -sn "dss" -ln "defaultSurfaceShader" -dt "string";
setAttr ".dss" -type "string" "standardSurface1";
select -ne :defaultResolution;
setAttr ".pa" 1;
select -ne :defaultColorMgtGlobals;
setAttr ".cfe" yes;
setAttr ".cfp" -type "string" "<MAYA_RESOURCES>/OCIO-configs/Maya2022-default/config.ocio";
setAttr ".vtn" -type "string" "ACES 1.0 SDR-video (sRGB)";
setAttr ".vn" -type "string" "ACES 1.0 SDR-video";
setAttr ".dn" -type "string" "sRGB";
setAttr ".wsn" -type "string" "ACEScg";
setAttr ".otn" -type "string" "ACES 1.0 SDR-video (sRGB)";
setAttr ".potn" -type "string" "ACES 1.0 SDR-video (sRGB)";
select -ne :hardwareRenderGlobals;
setAttr ".ctrs" 256;
setAttr ".btrs" 512;
connectAttr "Hips.s" "Spine.is";
connectAttr "Spine.s" "Spine1.is";
connectAttr "Spine1.s" "Spine2.is";
connectAttr "Spine2.s" "Neck.is";
connectAttr "Neck.s" "Head.is";
connectAttr "Head.s" "HeadTop_End.is";
connectAttr "Spine2.s" "LeftShoulder.is";
connectAttr "LeftShoulder.s" "LeftArm.is";
connectAttr "LeftArm.s" "LeftForeArm.is";
connectAttr "LeftForeArm.s" "LeftHand.is";
connectAttr "LeftHand.s" "LeftHandThumb1.is";
connectAttr "LeftHandThumb1.s" "LeftHandThumb2.is";
connectAttr "LeftHandThumb2.s" "LeftHandThumb3.is";
connectAttr "LeftHandThumb3.s" "LeftHandThumb4.is";
connectAttr "LeftHand.s" "LeftHandIndex1.is";
connectAttr "LeftHandIndex1.s" "LeftHandIndex2.is";
connectAttr "LeftHandIndex2.s" "LeftHandIndex3.is";
connectAttr "LeftHandIndex3.s" "LeftHandIndex4.is";
connectAttr "LeftHand.s" "LeftHandMiddle1.is";
connectAttr "LeftHandMiddle1.s" "LeftHandMiddle2.is";
connectAttr "LeftHandMiddle2.s" "LeftHandMiddle3.is";
connectAttr "LeftHandMiddle3.s" "LeftHandMiddle4.is";
connectAttr "LeftHand.s" "LeftHandRing1.is";
connectAttr "LeftHandRing1.s" "LeftHandRing2.is";
connectAttr "LeftHandRing2.s" "LeftHandRing3.is";
connectAttr "LeftHandRing3.s" "LeftHandRing4.is";
connectAttr "LeftHand.s" "LeftHandPinky1.is";
connectAttr "LeftHandPinky1.s" "LeftHandPinky2.is";
connectAttr "LeftHandPinky2.s" "LeftHandPinky3.is";
connectAttr "LeftHandPinky3.s" "LeftHandPinky4.is";
connectAttr "Spine2.s" "RightShoulder.is";
connectAttr "RightShoulder.s" "RightArm.is";
connectAttr "RightArm.s" "RightForeArm.is";
connectAttr "RightForeArm.s" "RightHand.is";
connectAttr "RightHand.s" "RightHandThumb1.is";
connectAttr "RightHandThumb1.s" "RightHandThumb2.is";
connectAttr "RightHandThumb2.s" "RightHandThumb3.is";
connectAttr "RightHandThumb3.s" "RightHandThumb4.is";
connectAttr "RightHand.s" "RightHandIndex1.is";
connectAttr "RightHandIndex1.s" "RightHandIndex2.is";
connectAttr "RightHandIndex2.s" "RightHandIndex3.is";
connectAttr "RightHandIndex3.s" "RightHandIndex4.is";
connectAttr "RightHand.s" "RightHandMiddle1.is";
connectAttr "RightHandMiddle1.s" "RightHandMiddle2.is";
connectAttr "RightHandMiddle2.s" "RightHandMiddle3.is";
connectAttr "RightHandMiddle3.s" "RightHandMiddle4.is";
connectAttr "RightHand.s" "RightHandRing1.is";
connectAttr "RightHandRing1.s" "RightHandRing2.is";
connectAttr "RightHandRing2.s" "RightHandRing3.is";
connectAttr "RightHandRing3.s" "RightHandRing4.is";
connectAttr "RightHand.s" "RightHandPinky1.is";
connectAttr "RightHandPinky1.s" "RightHandPinky2.is";
connectAttr "RightHandPinky2.s" "RightHandPinky3.is";
connectAttr "RightHandPinky3.s" "RightHandPinky4.is";
connectAttr "Hips.s" "LeftUpLeg.is";
connectAttr "LeftUpLeg.s" "LeftLeg.is";
connectAttr "LeftLeg.s" "LeftFoot.is";
connectAttr "LeftFoot.s" "LeftToeBase.is";
connectAttr "LeftToeBase.s" "LeftToe_End.is";
connectAttr "Hips.s" "RightUpLeg.is";
connectAttr "RightUpLeg.s" "RightLeg.is";
connectAttr "RightLeg.s" "RightFoot.is";
connectAttr "RightFoot.s" "RightToeBase.is";
connectAttr "RightToeBase.s" "RightToe_End.is";
connectAttr "Head.msg" "openposeCharacter_HIK.tj[0]";
connectAttr "Neck.msg" "openposeCharacter_HIK.tj[1]";
connectAttr "RightArm.msg" "openposeCharacter_HIK.tj[2]";
connectAttr "RightForeArm.msg" "openposeCharacter_HIK.tj[3]";
connectAttr "RightHand.msg" "openposeCharacter_HIK.tj[4]";
connectAttr "LeftArm.msg" "openposeCharacter_HIK.tj[5]";
connectAttr "LeftForeArm.msg" "openposeCharacter_HIK.tj[6]";
connectAttr "LeftHand.msg" "openposeCharacter_HIK.tj[7]";
connectAttr "Hips.msg" "openposeCharacter_HIK.tj[8]";
connectAttr "RightUpLeg.msg" "openposeCharacter_HIK.tj[9]";
connectAttr "RightLeg.msg" "openposeCharacter_HIK.tj[10]";
connectAttr "RightFoot.msg" "openposeCharacter_HIK.tj[11]";
connectAttr "LeftUpLeg.msg" "openposeCharacter_HIK.tj[12]";
connectAttr "LeftLeg.msg" "openposeCharacter_HIK.tj[13]";
connectAttr "LeftFoot.msg" "openposeCharacter_HIK.tj[14]";
connectAttr "LeftToeBase.msg" "openposeCharacter_HIK.tj[19]";
connectAttr "RightToeBase.msg" "openposeCharacter_HIK.tj[22]";
connectAttr "LeftHand.msg" "openposeCharacter_HIK.tj[25]";
connectAttr "LeftHandThumb1.msg" "openposeCharacter_HIK.tj[26]";
connectAttr "LeftHandThumb2.msg" "openposeCharacter_HIK.tj[27]";
connectAttr "LeftHandThumb3.msg" "openposeCharacter_HIK.tj[28]";
connectAttr "LeftHandThumb4.msg" "openposeCharacter_HIK.tj[29]";
connectAttr "LeftHandIndex1.msg" "openposeCharacter_HIK.tj[30]";
connectAttr "LeftHandIndex2.msg" "openposeCharacter_HIK.tj[31]";
connectAttr "LeftHandIndex3.msg" "openposeCharacter_HIK.tj[32]";
connectAttr "LeftHandIndex4.msg" "openposeCharacter_HIK.tj[33]";
connectAttr "LeftHandMiddle1.msg" "openposeCharacter_HIK.tj[34]";
connectAttr "LeftHandMiddle2.msg" "openposeCharacter_HIK.tj[35]";
connectAttr "LeftHandMiddle3.msg" "openposeCharacter_HIK.tj[36]";
connectAttr "LeftHandMiddle4.msg" "openposeCharacter_HIK.tj[37]";
connectAttr "LeftHandRing1.msg" "openposeCharacter_HIK.tj[38]";
connectAttr "LeftHandRing2.msg" "openposeCharacter_HIK.tj[39]";
connectAttr "LeftHandRing3.msg" "openposeCharacter_HIK.tj[40]";
connectAttr "LeftHandRing4.msg" "openposeCharacter_HIK.tj[41]";
connectAttr "LeftHandPinky1.msg" "openposeCharacter_HIK.tj[42]";
connectAttr "LeftHandPinky2.msg" "openposeCharacter_HIK.tj[43]";
connectAttr "LeftHandPinky3.msg" "openposeCharacter_HIK.tj[44]";
connectAttr "LeftHandPinky4.msg" "openposeCharacter_HIK.tj[45]";
connectAttr "RightHand.msg" "openposeCharacter_HIK.tj[46]";
connectAttr "RightHandThumb1.msg" "openposeCharacter_HIK.tj[47]";
connectAttr "RightHandThumb2.msg" "openposeCharacter_HIK.tj[48]";
connectAttr "RightHandThumb3.msg" "openposeCharacter_HIK.tj[49]";
connectAttr "RightHandThumb4.msg" "openposeCharacter_HIK.tj[50]";
connectAttr "RightHandIndex1.msg" "openposeCharacter_HIK.tj[51]";
connectAttr "RightHandIndex2.msg" "openposeCharacter_HIK.tj[52]";
connectAttr "RightHandIndex3.msg" "openposeCharacter_HIK.tj[53]";
connectAttr "RightHandIndex4.msg" "openposeCharacter_HIK.tj[54]";
connectAttr "RightHandMiddle1.msg" "openposeCharacter_HIK.tj[55]";
connectAttr "RightHandMiddle2.msg" "openposeCharacter_HIK.tj[56]";
connectAttr "RightHandMiddle3.msg" "openposeCharacter_HIK.tj[57]";
connectAttr "RightHandMiddle4.msg" "openposeCharacter_HIK.tj[58]";
connectAttr "RightHandRing1.msg" "openposeCharacter_HIK.tj[59]";
connectAttr "RightHandRing2.msg" "openposeCharacter_HIK.tj[60]";
connectAttr "RightHandRing3.msg" "openposeCharacter_HIK.tj[61]";
connectAttr "RightHandRing4.msg" "openposeCharacter_HIK.tj[62]";
connectAttr "RightHandPinky1.msg" "openposeCharacter_HIK.tj[63]";
connectAttr "RightHandPinky2.msg" "openposeCharacter_HIK.tj[64]";
connectAttr "RightHandPinky3.msg" "openposeCharacter_HIK.tj[65]";
connectAttr "RightHandPinky4.msg" "openposeCharacter_HIK.tj[66]";
relationship "link" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message";
relationship "link" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message";
relationship "shadowLink" ":lightLinker1" ":initialShadingGroup.message" ":defaultLightSet.message";
relationship "shadowLink" ":lightLinker1" ":initialParticleSE.message" ":defaultLightSet.message";
connectAttr "layerManager.dli[0]" "defaultLayer.id";
connectAttr "renderLayerManager.rlmi[0]" "defaultRenderLayer.rlid";
connectAttr "defaultRenderLayer.msg" ":defaultRenderingList1.r" -na;
// End of openpose_humanik_test.ma
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB