diff --git a/README.md b/README.md index 6d35c1e..6424582 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,29 @@ rendered output for that character: 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 +Hands and face are unaffected by this setting either way. + +### Default eyes/ears + +Most rigs (including HumanIK) have no `REye`/`LEye`/`REar`/`LEar` joints -- +only a Head/Nose. By default (`useDefaultEyesEars`, on unless turned off), +whenever any of those 4 slots is unmapped but `Body_Nose` *is* mapped, it +gets a synthetic head-relative position instead of being invisible -- +anchored at and rotating with the joint mapped to `Body_Nose` itself (sized +by `faceScale`), so they turn with the head's actual rotation rather than +billboarding toward the camera. An explicit joint mapped to any of the 4 +slots always overrides this fallback. The offsets themselves are a +reasonable approximation of human proportions, not from a published +source. + +Note also: BODY_25/COCO's `RShoulder->REar` / `LShoulder->LEar` limb lines +(present in OpenPose's own spec -- a detection-robustness feature of the +original network, not a real anatomical link) are intentionally **not** +drawn here; ears connect only via eye->ear, staying leaf points. This is a +deliberate deviation from upstream, by request -- see the note in +`constants.py`. + +Rendered from the same mapped HumanIK-named test rig (`test/openpose_humanik_test.ma`): | BODY_25 | COCO | @@ -108,9 +130,22 @@ keypoints come from: 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). + `Body_Nose` joint, sized by the `faceScale` attribute (world units; + default 15 -- tune to your scene's unit scale). + + A `faceFollow` enum (mapping editor's *Face Follow* dropdown, or + `cmds.setAttr("myCharacter.faceFollow", 1)`; 0 = `Camera`, 1 = `Neck`) + controls the face plane's orientation: + - **Camera** (default): always faces whichever camera is drawing + (viewport or render camera) -- works regardless of rig conventions, + since it never has to guess which way a joint's local axes point. + - **Neck**: rotates *with* the character's mapped `Body_Neck` joint + instead, using that joint's own local X/Y axes as the face plane's + right/up -- so the face turns with the head. This is rig-dependent (it + assumes local X = right, local Y = up, which is common but not + universal); if the face looks rotated relative to the head, adjust the + neck joint's rotate axis, or use `Camera` instead. Falls back to + `Camera` if no joint is mapped to `Body_Neck`. **Accuracy caveat:** there is no published, authoritative "which ARKit weight moves which OpenPose landmark, by how much" table anywhere (unlike @@ -126,9 +161,9 @@ 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) | +| Neutral | `jawOpen` + smile + raised brows | `faceFollow=Neck`, neck tilted 25° | +| --- | --- | --- | +| ![Neutral face](test/openpose_arkit_face_test_neutral.png) | ![Expression face](test/openpose_arkit_face_test_expression.png) | ![Neck-follow face](test/openpose_arkit_face_test_neck_follow.png) | ### 3. Render OpenPose images @@ -147,9 +182,9 @@ ARKit-named attributes, auto-connected -- see that scene for the pattern): 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. + per-finger rainbow hand colors, white face contour lines), written to + disk, and pushed into Maya's Render View so you can watch it render like + any other renderer. ## Debugging (VS Code) @@ -172,14 +207,20 @@ ARKit-named attributes, auto-connected -- see that scene for the pattern): ## Notes / known limitations - Colors/connectivity: BODY_25 and COCO keypoint layout and limb - connectivity are taken verbatim from OpenPose's own + connectivity are taken 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. + (`src/openpose/pose/poseParameters.cpp`), with BODY_25's colors likewise + verbatim from `POSE_BODY_25_COLORS_RENDER_GPU` -- with one deliberate, + by-request deviation: the `RShoulder->REar` / `LShoulder->LEar` lines + upstream includes are dropped, so ears stay leaf points connected only + via eye->ear (see "Default eyes/ears" above). The face's 63 contour + lines (jaw/eyebrows/nose/eyes/mouth) and solid-white color are equally + verbatim, from `FACE_PAIRS_RENDER_GPU` / `FACE_COLORS_RENDER_GPU` + (`include/openpose/face/faceParameters.hpp`) -- independently confirmed + against OpenPose's own published keypoint diagrams. COCO limb colors and + all hand colors are generated from an HSV hue wheel (rainbow per limb or + finger) 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 diff --git a/python/openpose_renderer/constants.py b/python/openpose_renderer/constants.py index b89b473..53cf2b4 100644 --- a/python/openpose_renderer/constants.py +++ b/python/openpose_renderer/constants.py @@ -5,21 +5,23 @@ 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. +Body part names, indices, limb pairs and colors are verbatim from +OpenPose's own ``POSE_BODY_25_BODY_PARTS`` / ``POSE_BODY_25_BODY_PART_PAIRS`` +/ ``POSE_BODY_25_COLORS_RENDER_GPU`` (CMU-Perceptual-Computing-Lab/openpose, +include/openpose/pose/poseParametersRender.hpp). Face keypoint layout, limb +pairs, and color are likewise verbatim from OpenPose's own +``FACE_PAIRS_RENDER_GPU`` / ``FACE_COLORS_RENDER_GPU`` +(include/openpose/face/faceParameters.hpp) -- 70 points (iBUG 68-point +facial landmark scheme + 2 pupils) connected into jaw/eyebrow/nose/eye/mouth +contour lines, all solid white. Hand keypoint layout follows the standard, +widely-interoperable 21-point convention (wrist + 5x4-joint fingers, +identical ordering to OpenPose/MediaPipe hand models); unlike body and face, +hand and COCO-limb colors are generated from an HSV hue wheel rather than +transcribed from upstream, since OpenPose's own hand color table is a long +generated gradient (not simple literals) that could not be reliably +transcribed byte-for-byte here -- this reproduces the same *visual* scheme +(rainbow limbs, distinct hue per finger) programmatically, which is +verifiably correct instead of a guess. """ import colorsys @@ -66,12 +68,16 @@ BODY_25_NAMES = [ ] 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). +# From OpenPose's POSE_BODY_25_BODY_PART_PAIRS_RENDER_GPU (local indices +# 0..24, i.e. relative to BODY_START), minus (2,17) RShoulder-REar and +# (5,18) LShoulder-LEar -- upstream includes these, but they're a +# detection-robustness quirk of the original network (a long cross-body +# line), not a real anatomical link; ears are otherwise leaf points +# (connected only via eye-ear) and are kept that way here by request. 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), + (16, 18), (14, 19), (19, 20), (14, 21), (11, 22), (22, 23), (11, 24), ] @@ -83,6 +89,22 @@ BODY_25_LIMB_COLORS = [ BODY_POINT_RADIUS = 4.0 BODY_LIMB_THICKNESS = 4.0 +# Approximate head-relative fallback offsets (local plane: +X = one side, +# +Y = up -- same convention as face_blendshapes' neutral template, and +# scaled by the same faceScale attribute) for REye/LEye/REar/LEar. Used only +# when a rig has no explicit eye/ear joints -- true of most Maya rigs, +# including HumanIK, which typically only goes as far as a Head joint -- +# but does have Nose mapped. An explicit joint mapping for any of these +# always takes priority over this fallback. Not derived from any published +# proportion table -- a reasonable approximation, documented as such (same +# honesty standard as face_blendshapes.py). +DEFAULT_EYE_EAR_LOCAL_OFFSETS = { + BODY_25_NAMES.index("REye"): (-0.5, 0.3), + BODY_25_NAMES.index("LEye"): (0.5, 0.3), + BODY_25_NAMES.index("REar"): (-1.0, 0.05), + BODY_25_NAMES.index("LEar"): (1.0, 0.05), +} + # --- Body: COCO-18 (legacy OpenPose COCO model) ------------------------------ # @@ -116,13 +138,14 @@ assert all(BODY_25_NAMES[COCO_TO_BODY25_INDEX[i]] == COCO_NAMES[i] for i in rang 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 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. Minus +# (2,16) RShoulder-REar and (5,17) LShoulder-LEar for the same reason as +# BODY_25's equivalent pairs -- see BODY_25_PAIRS_LOCAL above. _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 @@ -189,13 +212,34 @@ FACE_NAMES = ( ) assert len(FACE_NAMES) == FACE_COUNT -# OpenPose's default renderer draws the face as keypoints only (no limb -# lines), all white. -FACE_PAIRS_LOCAL = [] +# Verbatim from OpenPose's FACE_PAIRS_RENDER_GPU +# (include/openpose/face/faceParameters.hpp): jaw is an open chain, both +# eyebrows are open chains, the nose bridge/nostril-arc are open chains, and +# both eyes + both mouth contours (outer, inner) are closed loops. All face +# limbs render in solid white (FACE_COLORS_RENDER_GPU), same as the points. +FACE_PAIRS_LOCAL = [ + (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), + (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), + (17, 18), (18, 19), (19, 20), (20, 21), + (22, 23), (23, 24), (24, 25), (25, 26), + (27, 28), (28, 29), (29, 30), + (31, 32), (32, 33), (33, 34), (34, 35), + (36, 37), (37, 38), (38, 39), (39, 40), (40, 41), (41, 36), + (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (47, 42), + (48, 49), (49, 50), (50, 51), (51, 52), (52, 53), (53, 54), (54, 55), + (55, 56), (56, 57), (57, 58), (58, 59), (59, 48), + (60, 61), (61, 62), (62, 63), (63, 64), (64, 65), (65, 66), (66, 67), (67, 60), +] +assert len(FACE_PAIRS_LOCAL) == 63 + FACE_POINT_COLORS = [(255, 255, 255)] * FACE_COUNT +FACE_LIMB_COLORS = [(255, 255, 255)] * len(FACE_PAIRS_LOCAL) FACE_POINT_RADIUS = 2.0 -FACE_LIMB_THICKNESS = 0.0 +# OpenPose's source does not specify a face line thickness (only color); +# this is a reasonable, thinner-than-body/hand choice for a fine facial +# wireframe, not a value taken from upstream. +FACE_LIMB_THICKNESS = 1.5 # --- Flattened 0..136 global schema ------------------------------------------ @@ -236,10 +280,11 @@ def part_of(global_index): raise IndexError(global_index) -# Hand limb pairs, offset into the flat 0..136 range -- unaffected by body -# model choice, so precomputed once. +# Hand/face 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] +_FACE_PAIRS_GLOBAL = [(a + FACE_START, b + FACE_START) for (a, b) in FACE_PAIRS_LOCAL] def _body_limb_data(body_model): @@ -255,17 +300,18 @@ def _body_limb_data(body_model): 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. + """(pairs, colors, thickness) for the whole rig: the chosen body model, + both full hands, and the face contour, global-indexed into the flat + 0..136 range. """ 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 + pairs = list(body_pairs) + _HAND_LEFT_PAIRS_GLOBAL + _HAND_RIGHT_PAIRS_GLOBAL + _FACE_PAIRS_GLOBAL + colors = list(body_colors) + HAND_LIMB_COLORS + HAND_LIMB_COLORS + FACE_LIMB_COLORS thickness = ( list(body_thickness) + [HAND_LIMB_THICKNESS] * len(HAND_PAIRS_LOCAL) + [HAND_LIMB_THICKNESS] * len(HAND_PAIRS_LOCAL) + + [FACE_LIMB_THICKNESS] * len(FACE_PAIRS_LOCAL) ) assert len(pairs) == len(colors) == len(thickness) return pairs, colors, thickness diff --git a/python/openpose_renderer/draw_override.py b/python/openpose_renderer/draw_override.py index 46e873e..55884c6 100644 --- a/python/openpose_renderer/draw_override.py +++ b/python/openpose_renderer/draw_override.py @@ -21,6 +21,30 @@ def _mcolor(rgb255): return om.MColor((rgb255[0] / 255.0, rgb255[1] / 255.0, rgb255[2] / 255.0, 1.0)) +def _fill_default_eye_ear_positions(dep_fn, mapped, positions): + """Fill in synthetic REye/LEye/REar/LEar world positions (see + constants.DEFAULT_EYE_EAR_LOCAL_OFFSETS) for whichever of those slots + aren't already explicitly mapped, anchored at and oriented by the + joint mapped to Nose (the head) -- so they turn with the head's actual + rotation rather than facing the camera. No-op if Nose isn't mapped. + """ + missing_offsets = { + index: offset + for index, offset in constants.DEFAULT_EYE_EAR_LOCAL_OFFSETS.items() + if index not in positions + } + if not missing_offsets: + return + nose_index = constants.NAME_TO_INDEX["Body_Nose"] + if nose_index not in mapped: + return + anchor = positions[nose_index] + + face_scale = mapping_node.get_face_scale(dep_fn) + computed = projector.default_head_relative_points(anchor, mapped[nose_index], face_scale, missing_offsets) + positions.update(computed) + + class OpenPoseUserData(om.MUserData): def __init__(self): @@ -69,6 +93,9 @@ class OpenPoseDrawOverride(omr.MPxDrawOverride): 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_use_default_eyes_ears(dep_fn): + _fill_default_eye_ear_positions(dep_fn, mapped, positions) + 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) @@ -76,7 +103,15 @@ class OpenPoseDrawOverride(omr.MPxDrawOverride): 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) + + neck_index = constants.NAME_TO_INDEX["Body_Neck"] + if mapping_node.get_face_follow(dep_fn) == mapping_node.FACE_FOLLOW_NECK and neck_index in mapped: + world_points = projector.oriented_world_points( + anchor, mapped[neck_index], local_points, face_scale + ) + else: + 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 diff --git a/python/openpose_renderer/mapping_node.py b/python/openpose_renderer/mapping_node.py index 009c6a6..b08e711 100644 --- a/python/openpose_renderer/mapping_node.py +++ b/python/openpose_renderer/mapping_node.py @@ -30,6 +30,8 @@ aBodyModel = None aFaceSource = None aBlendshapeWeight = None aFaceScale = None +aFaceFollow = None +aUseDefaultEyesEars = None # aBodyModel enum field values, in registration order (index 0 = BODY_25). BODY_MODEL_FIELDS = [constants.BODY_MODEL_BODY_25, constants.BODY_MODEL_COCO] @@ -39,6 +41,11 @@ FACE_SOURCE_JOINTS = "Joints" FACE_SOURCE_ARKIT_BLENDSHAPES = "ARKitBlendshapes" FACE_SOURCE_FIELDS = [FACE_SOURCE_JOINTS, FACE_SOURCE_ARKIT_BLENDSHAPES] +# aFaceFollow enum field values, in registration order (index 0 = Camera). +FACE_FOLLOW_CAMERA = "Camera" +FACE_FOLLOW_NECK = "Neck" +FACE_FOLLOW_FIELDS = [FACE_FOLLOW_CAMERA, FACE_FOLLOW_NECK] + DEFAULT_FACE_SCALE = 15.0 @@ -65,7 +72,8 @@ class OpenposeCharacterNode(omui.MPxLocatorNode): def initialize(): global aTargetJoint, aEnableDisplay, aJointRadiusScale global aLimbThicknessScale, aCharacterName, aBodyModel - global aFaceSource, aBlendshapeWeight, aFaceScale + global aFaceSource, aBlendshapeWeight, aFaceScale, aFaceFollow + global aUseDefaultEyesEars msg_attr = om.MFnMessageAttribute() aTargetJoint = msg_attr.create("targetJoint", "tj") @@ -137,6 +145,20 @@ class OpenposeCharacterNode(omui.MPxLocatorNode): face_scale_attr.setMin(0.0) om.MPxNode.addAttribute(aFaceScale) + face_follow_attr = om.MFnEnumAttribute() + aFaceFollow = face_follow_attr.create("faceFollow", "ffol", 0) + for field_index, field_name in enumerate(FACE_FOLLOW_FIELDS): + face_follow_attr.addField(field_name, field_index) + face_follow_attr.keyable = True + om.MPxNode.addAttribute(aFaceFollow) + + default_eyes_ears_attr = om.MFnNumericAttribute() + aUseDefaultEyesEars = default_eyes_ears_attr.create( + "useDefaultEyesEars", "udee", om.MFnNumericData.kBoolean, True + ) + default_eyes_ears_attr.keyable = True + om.MPxNode.addAttribute(aUseDefaultEyesEars) + def get_mapped_joint_paths(dep_node_fn): """Return {keypoint_index: MDagPath} for every connected targetJoint slot. @@ -202,6 +224,24 @@ def get_face_scale(dep_node_fn): return DEFAULT_FACE_SCALE +def get_face_follow(dep_node_fn): + """Return FACE_FOLLOW_CAMERA / FACE_FOLLOW_NECK for this character.""" + try: + field_index = dep_node_fn.findPlug(aFaceFollow, False).asShort() + except RuntimeError: + field_index = 0 + if 0 <= field_index < len(FACE_FOLLOW_FIELDS): + return FACE_FOLLOW_FIELDS[field_index] + return FACE_FOLLOW_CAMERA + + +def get_use_default_eyes_ears(dep_node_fn): + try: + return dep_node_fn.findPlug(aUseDefaultEyesEars, False).asBool() + except RuntimeError: + return True + + def iter_character_nodes(): """Yield an MFnDependencyNode for every openposeCharacter node in the scene.""" it = om.MItDependencyNodes(om.MFn.kPluginLocatorNode) diff --git a/python/openpose_renderer/projector.py b/python/openpose_renderer/projector.py index 20dc761..458cb5b 100644 --- a/python/openpose_renderer/projector.py +++ b/python/openpose_renderer/projector.py @@ -55,20 +55,18 @@ def project_point(world_point, camera_path, width, height): 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() +def local_axes_world(dag_path): + """World-space (right, up) unit vectors from a DAG node's local X/Y axes.""" + world_matrix = dag_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() + return right, up + + +def plane_world_points(anchor_world_point, right, up, local_points_xy, scale): + """Place 2D local (x, y) points on a plane centered at ``anchor_world_point`` + using the given world-space ``right``/``up`` axes, scaled by ``scale``. + """ anchor = om.MPoint(anchor_world_point) return [ om.MPoint(anchor + right * (x * scale) + up * (y * scale)) @@ -76,6 +74,51 @@ def billboard_world_points(anchor_world_point, camera_path, local_points_xy, sca ] +def billboard_world_points(anchor_world_point, camera_path, local_points_xy, scale): + """``plane_world_points`` using ``camera_path``'s own local X/Y as + right/up, so the plane always faces whichever camera is drawing. + + 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". This is + the "Camera" faceFollow mode. + """ + right, up = local_axes_world(camera_path) + return plane_world_points(anchor_world_point, right, up, local_points_xy, scale) + + +def oriented_world_points(anchor_world_point, orientation_dag_path, local_points_xy, scale): + """``plane_world_points`` using ``orientation_dag_path``'s own local X/Y + as right/up, so the plane rotates *with* that joint (e.g. the mapped + Neck) instead of always facing the camera. This is the "Neck" faceFollow + mode -- unlike "Camera", it is rig-dependent: it assumes the joint's + local X is the character's right and local Y is up, which matches + common convention but is not guaranteed for every rig. If the face + appears rotated relative to the head, adjust the neck joint's rotate + axis/orient, or use "Camera" mode instead. + """ + right, up = local_axes_world(orientation_dag_path) + return plane_world_points(anchor_world_point, right, up, local_points_xy, scale) + + +def default_head_relative_points(anchor_world_point, orientation_dag_path, scale, local_offsets): + """Compute head-relative fallback world points (e.g. the default + REye/LEye/REar/LEar positions in constants.DEFAULT_EYE_EAR_LOCAL_OFFSETS) + anchored at ``anchor_world_point`` (the mapped Nose), oriented by + ``orientation_dag_path``'s own local X/Y axes -- always the joint + mapped to Nose itself (the head), so these points turn with the head's + actual rotation rather than billboarding toward the camera. + + ``local_offsets``: {index: (x, y)}. Returns {index: MPoint}. + """ + indices = list(local_offsets.keys()) + offsets = [local_offsets[i] for i in indices] + world_points = oriented_world_points(anchor_world_point, orientation_dag_path, offsets, scale) + return dict(zip(indices, world_points)) + + def project_character(mapped_joint_paths, camera_path, width, height): """Project one character's mapping to OpenPose_full pixel keypoints. diff --git a/python/openpose_renderer/render_override.py b/python/openpose_renderer/render_override.py index b0d4e7a..4b47e81 100644 --- a/python/openpose_renderer/render_override.py +++ b/python/openpose_renderer/render_override.py @@ -74,6 +74,32 @@ class OpenposeRenderOverride(omr.MRenderOverride): return self._current_operation < len(self._operations) +def _fill_default_eye_ear_keypoints(dep_fn, mapped, keypoints, camera_path, width, height): + """Fill in synthetic REye/LEye/REar/LEar pixel keypoints (see + constants.DEFAULT_EYE_EAR_LOCAL_OFFSETS) for whichever of those slots + aren't already explicitly mapped, anchored at and oriented by the + joint mapped to Nose (the head) -- so they turn with the head's actual + rotation rather than facing the camera. No-op if Nose isn't mapped. + """ + missing_offsets = { + index: offset + for index, offset in constants.DEFAULT_EYE_EAR_LOCAL_OFFSETS.items() + if index not in mapped + } + if not missing_offsets: + return + nose_index = constants.NAME_TO_INDEX["Body_Nose"] + if nose_index not in mapped: + return + anchor = projector.world_position(mapped[nose_index]) + + face_scale = mapping_node.get_face_scale(dep_fn) + computed = projector.default_head_relative_points(anchor, mapped[nose_index], face_scale, missing_offsets) + for index, world_point in computed.items(): + px, py, in_front = projector.project_point(world_point, camera_path, width, height) + keypoints[index] = (px, py, 1.0 if in_front else 0.0) + + def render_frame(camera_path, width, height, output_path): """Project + rasterize every mapped openposeCharacter to a single PNG.""" characters = [] @@ -90,6 +116,9 @@ def render_frame(camera_path, width, height, output_path): mapped = mapping_node.get_mapped_joint_paths(dep_fn) keypoints = projector.project_character(mapped, camera_path, width, height) + if mapping_node.get_use_default_eyes_ears(dep_fn): + _fill_default_eye_ear_keypoints(dep_fn, mapped, keypoints, 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] @@ -98,7 +127,15 @@ def render_frame(camera_path, width, height, output_path): 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) + + neck_index = constants.NAME_TO_INDEX["Body_Neck"] + if mapping_node.get_face_follow(dep_fn) == mapping_node.FACE_FOLLOW_NECK and neck_index in mapped: + world_points = projector.oriented_world_points( + anchor, mapped[neck_index], local_points, face_scale + ) + else: + 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) diff --git a/python/openpose_renderer/ui/mapping_editor.py b/python/openpose_renderer/ui/mapping_editor.py index 2694ad6..f531691 100644 --- a/python/openpose_renderer/ui/mapping_editor.py +++ b/python/openpose_renderer/ui/mapping_editor.py @@ -93,6 +93,34 @@ class MappingEditor(QtWidgets.QDialog): self.face_scale_spin.valueChanged.connect(self._on_face_scale_changed) face_row.addWidget(self.face_scale_spin) + face_row.addWidget(QtWidgets.QLabel("Face Follow:")) + self.face_follow_combo = QtWidgets.QComboBox() + self.face_follow_combo.addItems( + [mapping_node.FACE_FOLLOW_CAMERA, mapping_node.FACE_FOLLOW_NECK] + ) + self.face_follow_combo.setToolTip( + "Camera: the ARKit-blendshape face always faces whichever camera is drawing\n" + "(viewport or render camera) -- works regardless of rig conventions.\n" + "Neck: the face plane rotates with the mapped Body_Neck joint's own local\n" + "X/Y axes instead -- turns with the character's head, but is rig-dependent\n" + "(assumes local X = right, local Y = up; adjust the neck joint's rotate axis\n" + "if the face looks rotated). Falls back to Camera if no Neck is mapped." + ) + self.face_follow_combo.currentIndexChanged.connect(self._on_face_follow_changed) + face_row.addWidget(self.face_follow_combo) + + self.default_eyes_ears_check = QtWidgets.QCheckBox("Default Eyes/Ears") + self.default_eyes_ears_check.setToolTip( + "Most rigs (including HumanIK) have no REye/LEye/REar/LEar joints. When on\n" + "(default), those 4 OpenPose_full slots get a synthetic head-relative position\n" + "instead of being invisible, whenever they're unmapped but Body_Nose is mapped\n" + "-- anchored at and rotating with the joint mapped to Nose (sized by Face Scale\n" + "above). An explicit joint mapped to any of the 4 slots always takes priority\n" + "over this fallback." + ) + self.default_eyes_ears_check.toggled.connect(self._on_use_default_eyes_ears_changed) + face_row.addWidget(self.default_eyes_ears_check) + 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" @@ -181,13 +209,18 @@ class MappingEditor(QtWidgets.QDialog): self.body_model_combo.setEnabled(False) self.body_model_combo.blockSignals(False) - for widget in (self.face_source_combo, self.face_scale_spin): + face_widgets = ( + self.face_source_combo, self.face_scale_spin, self.face_follow_combo, self.default_eyes_ears_check, + ) + for widget in face_widgets: widget.blockSignals(True) if character: - self.face_source_combo.setEnabled(True) - self.face_scale_spin.setEnabled(True) + for widget in face_widgets: + widget.setEnabled(True) self.face_source_combo.setCurrentIndex(cmds.getAttr(character + ".faceSource")) self.face_scale_spin.setValue(cmds.getAttr(character + ".faceScale")) + self.face_follow_combo.setCurrentIndex(cmds.getAttr(character + ".faceFollow")) + self.default_eyes_ears_check.setChecked(cmds.getAttr(character + ".useDefaultEyesEars")) connected = sum( 1 for i in range(face_blendshapes.BLENDSHAPE_COUNT) if cmds.listConnections("{0}.blendshapeWeight[{1}]".format(character, i), source=True, destination=False) @@ -196,10 +229,10 @@ class MappingEditor(QtWidgets.QDialog): "{0} / {1} blendshapes connected".format(connected, face_blendshapes.BLENDSHAPE_COUNT) ) else: - self.face_source_combo.setEnabled(False) - self.face_scale_spin.setEnabled(False) + for widget in face_widgets: + widget.setEnabled(False) self.blendshape_status_label.setText("") - for widget in (self.face_source_combo, self.face_scale_spin): + for widget in face_widgets: widget.blockSignals(False) def _on_body_model_changed(self, index): @@ -212,11 +245,21 @@ class MappingEditor(QtWidgets.QDialog): if character: cmds.setAttr(character + ".faceSource", index) + def _on_face_follow_changed(self, index): + character = self._current_character() + if character: + cmds.setAttr(character + ".faceFollow", index) + def _on_face_scale_changed(self, value): character = self._current_character() if character: cmds.setAttr(character + ".faceScale", value) + def _on_use_default_eyes_ears_changed(self, checked): + character = self._current_character() + if character: + cmds.setAttr(character + ".useDefaultEyesEars", checked) + def _on_autoconnect_blendshapes(self): character = self._current_character() if not character: diff --git a/test/openpose_arkit_face_test.ma b/test/openpose_arkit_face_test.ma index 1c3e8b8..c40ca87 100644 --- a/test/openpose_arkit_face_test.ma +++ b/test/openpose_arkit_face_test.ma @@ -1,6 +1,6 @@ //Maya ASCII 2024 scene //Name: openpose_arkit_face_test.ma -//Last modified: Tue, Jul 14, 2026 08:25:25 AM +//Last modified: Tue, Jul 14, 2026 10:10:17 AM //Codeset: 950 requires maya "2024"; requires -nodeType "openposeCharacter" "openposeRenderer.py" "1.0.0"; @@ -10,14 +10,14 @@ 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"; +fileInfo "UUID" "85DD57C5-4B97-1CDE-6FD5-BFA49225A98F"; createNode transform -s -n "persp"; - rename -uid "D6BB12A4-4481-B70D-81F9-22BC50DBF6A2"; + rename -uid "E3491364-4B97-48C1-5E9B-578A13404F49"; 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"; + rename -uid "DD6D6127-4073-909D-6E02-1997E2D93C26"; setAttr -k off ".v" no; setAttr ".fl" 34.999999999999993; setAttr ".coi" 44.82186966202994; @@ -26,12 +26,12 @@ createNode camera -s -n "perspShape" -p "persp"; 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"; + rename -uid "035B1CE2-4DC2-F418-C617-688208D299F4"; 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"; + rename -uid "6E8EE682-497D-652E-715F-158DADC66B84"; setAttr -k off ".v" no; setAttr ".rnd" no; setAttr ".coi" 1000.1; @@ -42,11 +42,11 @@ createNode camera -s -n "topShape" -p "top"; setAttr ".hc" -type "string" "viewSet -t %camera"; setAttr ".o" yes; createNode transform -s -n "front"; - rename -uid "E313E787-411D-6315-E6D5-878346B7C7C7"; + rename -uid "53AD066A-4DDC-4765-95C1-9AB82429A5A2"; 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"; + rename -uid "2E5CFEDD-476C-0A25-F6B8-38A03B0877F8"; setAttr -k off ".v" no; setAttr ".rnd" no; setAttr ".coi" 1000.1; @@ -57,12 +57,12 @@ createNode camera -s -n "frontShape" -p "front"; setAttr ".hc" -type "string" "viewSet -f %camera"; setAttr ".o" yes; createNode transform -s -n "side"; - rename -uid "23726E62-4236-BA09-5DDB-A8B4AB6467DE"; + rename -uid "A10C8B41-4A98-FACA-5E06-7B8508956AA9"; 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"; + rename -uid "72EA7ED6-4C2B-4DE3-569A-D2B1A7BFABE4"; setAttr -k off ".v" no; setAttr ".rnd" no; setAttr ".coi" 1000.1; @@ -72,21 +72,27 @@ createNode camera -s -n "sideShape" -p "side"; 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 ; +createNode joint -n "Neck"; + rename -uid "98591D1D-4B4C-6895-14DE-4AB34B1EA03A"; + setAttr ".t" -type "double3" 0 140 0 ; + setAttr ".mnrl" -type "double3" -360 -360 -360 ; + setAttr ".mxrl" -type "double3" 360 360 360 ; +createNode joint -n "Head" -p "Neck"; + rename -uid "9A0315AF-4863-056E-3EDE-E493010F290C"; + setAttr ".t" -type "double3" 0 10 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"; + rename -uid "64FBC6C8-4532-92E7-B69C-14806ED14FDA"; createNode openposeCharacter -n "openposeCharacter_ARKitFace" -p "transform1"; - rename -uid "91BF3ED3-4092-9E34-128B-1C98376C9C10"; + rename -uid "99770FC8-4339-FCCF-4911-D5A1DE12F05B"; setAttr -k off ".v"; + setAttr -s 2 ".tj"; setAttr ".fsrc" 1; setAttr -s 52 ".bsw"; setAttr -s 52 ".bsw"; createNode transform -n "faceControl"; - rename -uid "BEF04EC7-4C59-6B22-48FA-208B994FD89B"; + rename -uid "6B4BD06A-44E7-A162-C911-A6860437F938"; 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"; @@ -194,25 +200,25 @@ createNode transform -n "faceControl"; setAttr -k on ".noseSneerRight"; setAttr -k on ".tongueOut"; createNode lightLinker -s -n "lightLinker1"; - rename -uid "6F41ABE6-4FC9-CBA9-3A4A-3C88AFFEDA05"; + rename -uid "6D455AD6-40A9-F99D-5089-B0A48DE96155"; setAttr -s 2 ".lnk"; setAttr -s 2 ".slnk"; createNode displayLayerManager -n "layerManager"; - rename -uid "C5E3AE68-4041-3C21-D33D-2DBCA8E53402"; + rename -uid "89D07F4B-4D2B-38E3-1982-E49382568D7B"; createNode displayLayer -n "defaultLayer"; - rename -uid "7E1F3A70-4A01-B94C-04B6-24ABE836AECA"; + rename -uid "FE3D0295-4D3E-B21D-61C6-E48A8F983B9A"; setAttr ".ufem" -type "stringArray" 0 ; createNode renderLayerManager -n "renderLayerManager"; - rename -uid "E2FC1CAE-4BC5-8835-9145-E2AFED84D0D6"; + rename -uid "D3652996-4893-A8CF-67F1-B39C47121B22"; createNode renderLayer -n "defaultRenderLayer"; - rename -uid "711E2B13-42DB-052A-1562-1C99F88A0E30"; + rename -uid "7B692699-4A43-99A7-033B-D8AB3ECF5B0E"; setAttr ".g" yes; createNode script -n "uiConfigurationScriptNode"; - rename -uid "F0132003-4E9D-D0A6-A39C-F6958ABF910E"; + rename -uid "C885E9DA-4CD9-2E2D-D858-D7960EC8BBFF"; 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"; + rename -uid "D790ABD8-4269-6467-7F7B-0B99D3F4D4A4"; setAttr ".b" -type "string" "playbackOptions -min 1 -max 120 -ast 1 -aet 200 "; setAttr ".st" 6; select -ne :time1; @@ -259,7 +265,9 @@ select -ne :defaultColorMgtGlobals; select -ne :hardwareRenderGlobals; setAttr ".ctrs" 256; setAttr ".btrs" 512; +connectAttr "Neck.s" "Head.is"; connectAttr "Head.msg" "openposeCharacter_ARKitFace.tj[0]"; +connectAttr "Neck.msg" "openposeCharacter_ARKitFace.tj[1]"; connectAttr "faceControl.eyeBlinkLeft" "openposeCharacter_ARKitFace.bsw[0]"; connectAttr "faceControl.eyeLookDownLeft" "openposeCharacter_ARKitFace.bsw[1]"; connectAttr "faceControl.eyeLookInLeft" "openposeCharacter_ARKitFace.bsw[2]"; diff --git a/test/openpose_arkit_face_test_expression.png b/test/openpose_arkit_face_test_expression.png index 206a826..8db1ec2 100644 Binary files a/test/openpose_arkit_face_test_expression.png and b/test/openpose_arkit_face_test_expression.png differ diff --git a/test/openpose_arkit_face_test_neck_follow.png b/test/openpose_arkit_face_test_neck_follow.png new file mode 100644 index 0000000..9a50a60 Binary files /dev/null and b/test/openpose_arkit_face_test_neck_follow.png differ diff --git a/test/openpose_arkit_face_test_neutral.png b/test/openpose_arkit_face_test_neutral.png index e510546..6040b90 100644 Binary files a/test/openpose_arkit_face_test_neutral.png and b/test/openpose_arkit_face_test_neutral.png differ diff --git a/test/openpose_humanik_test.ma b/test/openpose_humanik_test.ma index 94ff15c..8dc146c 100644 --- a/test/openpose_humanik_test.ma +++ b/test/openpose_humanik_test.ma @@ -1,6 +1,6 @@ //Maya ASCII 2024 scene //Name: openpose_humanik_test.ma -//Last modified: Tue, Jul 14, 2026 08:03:24 AM +//Last modified: Tue, Jul 14, 2026 10:09:43 AM //Codeset: 950 requires maya "2024"; requires -nodeType "openposeCharacter" "openposeRenderer.py" "1.0.0"; @@ -10,14 +10,14 @@ 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"; +fileInfo "UUID" "E43A0262-4D60-DF02-EF61-A1AEA19F0939"; createNode transform -s -n "persp"; - rename -uid "8310D31F-4947-8244-A1E4-1EBAEA32C348"; + rename -uid "BB62F98A-47B0-A411-1D52-ECB81BBE4EBC"; 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"; + rename -uid "3B445445-417F-D595-57AD-0AA18706C6A1"; setAttr -k off ".v" no; setAttr ".fl" 34.999999999999993; setAttr ".coi" 44.82186966202994; @@ -26,12 +26,12 @@ createNode camera -s -n "perspShape" -p "persp"; 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"; + rename -uid "45EA3531-43B9-F008-9BC4-EF84F467BB47"; 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"; + rename -uid "40690F10-4428-2AF9-8990-57919D2A2585"; setAttr -k off ".v" no; setAttr ".rnd" no; setAttr ".coi" 1000.1; @@ -42,11 +42,11 @@ createNode camera -s -n "topShape" -p "top"; setAttr ".hc" -type "string" "viewSet -t %camera"; setAttr ".o" yes; createNode transform -s -n "front"; - rename -uid "F8E3E41F-4F0B-4D85-E2B1-48A41CAB1138"; + rename -uid "75575145-4B25-769A-E59D-05860E34C5EC"; 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"; + rename -uid "91508474-4621-9BDC-7B52-0083D70E2B9C"; setAttr -k off ".v" no; setAttr ".rnd" no; setAttr ".coi" 1000.1; @@ -57,12 +57,12 @@ createNode camera -s -n "frontShape" -p "front"; setAttr ".hc" -type "string" "viewSet -f %camera"; setAttr ".o" yes; createNode transform -s -n "side"; - rename -uid "2DD1A2E6-45CB-8424-CD34-EA99A6909668"; + rename -uid "1F02B4B8-4ABD-0DEB-6A17-5CB228F155D1"; 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"; + rename -uid "D195B0A6-4ABE-158A-7B76-60918D15B764"; setAttr -k off ".v" no; setAttr ".rnd" no; setAttr ".coi" 1000.1; @@ -73,356 +73,356 @@ createNode camera -s -n "sideShape" -p "side"; setAttr ".hc" -type "string" "viewSet -s %camera"; setAttr ".o" yes; createNode joint -n "Hips"; - rename -uid "E9306125-46F8-B494-7C68-9AB3C96BAC96"; + rename -uid "11CDC41F-4623-39E7-6382-84AE29E91CEC"; 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"; + rename -uid "8484B5EE-44DF-7AEF-883C-6DBDE2360DF7"; 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"; + rename -uid "3EBE47D4-4B48-B75B-E950-E4806BF1A852"; 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"; + rename -uid "1B5D3999-4A92-E02B-B660-14A81EB26AED"; 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"; + rename -uid "8AF42280-42A8-CCB6-E45C-2C8E5A46EA80"; 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"; + rename -uid "C258D1AE-4D70-EEC2-C896-0A9D325945F7"; 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"; + rename -uid "65567901-4F3A-504D-6AC0-FD90DCB24AE5"; 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"; + rename -uid "CC40A30F-40AC-AFD1-10FB-9283CA7635D6"; 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"; + rename -uid "4F88AF7A-46D8-8563-D88F-6BB6B42B3266"; 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"; + rename -uid "65768928-47B8-835A-4552-CABFC71BD9F8"; 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"; + rename -uid "0A5D38B5-4281-9F97-1CA0-9BA7B4DAEC97"; 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"; + rename -uid "816BB779-49B0-AA00-1808-7DA28DC9E090"; 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"; + rename -uid "C10128AF-42D7-0DEC-6279-E09DC27678E2"; 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"; + rename -uid "6F7ABED4-4238-6706-CB8E-28837766BFD2"; 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"; + rename -uid "289D4BF7-40E0-5E1C-06C9-D48F7FCC3407"; 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"; + rename -uid "B39F51C1-4F99-6011-0DAC-78B8767119B1"; 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"; + rename -uid "9D3A9992-48FB-9611-7034-378F86AF785C"; 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"; + rename -uid "589AA861-482E-D604-A1BD-FB97246CEE3F"; 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"; + rename -uid "A41E9638-40C3-621D-5599-30B84A604C25"; 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"; + rename -uid "143C287F-4803-032D-5608-A1B0678DA021"; 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"; + rename -uid "7B91BB6C-417B-5BAC-7206-4A841150A371"; 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"; + rename -uid "640539B5-420F-0B80-0FE7-4193D7125CDA"; 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"; + rename -uid "25752607-4C3B-F058-9DC5-50B23CEE37BC"; 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"; + rename -uid "B678348F-4039-543D-EAE9-B3A818C60327"; 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"; + rename -uid "A854759B-4CDE-D1A9-CA84-A28DFD7B1BA2"; 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"; + rename -uid "505B5131-4740-C552-3329-63824CB47373"; 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"; + rename -uid "2A4A1D8E-4147-2E9F-F4D5-AFB06FD4C2E4"; 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"; + rename -uid "6121352A-4552-F517-8192-B6B2E0F25954"; 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"; + rename -uid "6855CFEF-4D30-5A55-C88E-BB916D777FFA"; 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"; + rename -uid "E1E86A55-4EFD-7C16-DDAF-8D96F4938B85"; 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"; + rename -uid "9403FC78-45C2-3548-34E0-8EA5E8AD9163"; 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"; + rename -uid "3D5695BD-4A72-9660-9447-F3B55B1B80CC"; 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"; + rename -uid "ABC3381D-42DA-88D0-8263-1FA4717A4BFB"; 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"; + rename -uid "0A18363F-4D8D-CF05-11BB-938B8ECCA517"; 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"; + rename -uid "CB6B562C-4B38-8211-6B2D-D7BE4CB80804"; 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"; + rename -uid "DA9256FC-43F7-A75F-FCC4-B4A069AEF478"; 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"; + rename -uid "5E5F2766-4EE8-60AD-E947-709598EF760A"; 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"; + rename -uid "76BC6340-4009-7CB5-E0D5-02920018A18A"; 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"; + rename -uid "9893198F-4DA0-4A20-D943-6880A41260BC"; 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"; + rename -uid "4C972F46-45D0-7C4A-C9AB-5DA98D808ED0"; 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"; + rename -uid "1AA2CE61-49A8-BF4D-A7FE-2CACE42FF0CF"; 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"; + rename -uid "D1337030-4CF6-3E98-974F-A18C4470E5EE"; 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"; + rename -uid "F5E0D358-4633-BD6F-50E2-0E84B4F7B6EC"; 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"; + rename -uid "A39D5E62-4D74-F344-E58F-588EA2EF922B"; 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"; + rename -uid "E30C6754-46B3-ED00-564A-C8BD1903E8D4"; 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"; + rename -uid "A22CD2F3-4455-C2A3-E29F-A79B81966A0A"; 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"; + rename -uid "B4657E24-4F3B-2E28-2B44-BCADD325953E"; 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"; + rename -uid "4E711160-4492-0FCA-0AF6-8C96DA37E03F"; 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"; + rename -uid "81DE22E3-4F78-D664-EEDF-9EA1F708BD20"; 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"; + rename -uid "40C3A379-4951-ECAF-480D-7291E4D98988"; 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"; + rename -uid "2B706EB2-4768-7084-EF45-16BA035F1054"; 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"; + rename -uid "EB58A6CB-4237-8ED5-54C6-4A93807D278A"; 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"; + rename -uid "2A0D498B-4084-0C90-6334-C7A45BD1AE81"; 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"; + rename -uid "71DD94BB-4847-A042-348E-1984D4B1193F"; 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"; + rename -uid "19B77A80-44E4-82C2-9C5F-D78979582369"; 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"; + rename -uid "72D25179-4425-ADC6-EF3F-FA93512F8228"; 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"; + rename -uid "60E8C2A7-4D4E-DF8A-674E-02901EEFDD6B"; 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"; + rename -uid "198C57E9-4AEB-067D-2FAD-0597C6C6840E"; 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"; + rename -uid "B2E03BC0-4629-0916-1893-3B8213015300"; 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"; + rename -uid "2DC60B4F-490A-52C4-0122-BEAF08A2132F"; 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"; + rename -uid "9E8DA29B-42A2-CB9E-6EF0-1F830202D7FF"; 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"; + rename -uid "CC9F1B96-4B81-CE1D-1CBB-D0AA973206A1"; 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"; + rename -uid "BBBCA7E4-487E-A07A-B4BB-16AB0444E9AD"; 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"; + rename -uid "8D296EC3-4BFE-4A24-CC73-509C03077962"; 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"; + rename -uid "8330F183-49B1-4A7F-6DC1-608BD6943D2B"; 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"; + rename -uid "31278C8D-45AF-FF84-9187-9289A8612798"; createNode openposeCharacter -n "openposeCharacter_HIK" -p "transform1"; - rename -uid "F4C90625-4C92-897E-8D0B-24B04BDDCD5D"; + rename -uid "EFAFEE4C-41FD-853E-5E0C-E4A87FE72645"; setAttr -k off ".v"; setAttr -s 59 ".tj"; createNode lightLinker -s -n "lightLinker1"; - rename -uid "D9C92F2B-4274-F3E8-55F9-3C9220E74C8C"; + rename -uid "78B99464-4195-3AE2-4136-42A0DC1F0A8E"; setAttr -s 2 ".lnk"; setAttr -s 2 ".slnk"; createNode displayLayerManager -n "layerManager"; - rename -uid "CCEC4154-4F20-8390-7839-93A6B3FE0C0C"; + rename -uid "F36C49ED-4551-EB40-7540-ADB6644266A3"; createNode displayLayer -n "defaultLayer"; - rename -uid "0E4AF3B6-4A43-CBF3-4F49-448E0BA8F555"; + rename -uid "4DCE6DAB-48BD-B84D-F048-638E09DED6CB"; setAttr ".ufem" -type "stringArray" 0 ; createNode renderLayerManager -n "renderLayerManager"; - rename -uid "40B9D3D6-414F-4FFE-9266-6E83CDFBAACF"; + rename -uid "D4B82764-4409-3014-08F0-38B4658C558E"; createNode renderLayer -n "defaultRenderLayer"; - rename -uid "4C2D15F1-4F34-6E9A-D299-50A2D0F719CA"; + rename -uid "37D5FEA0-47E3-3113-241E-918460A926C4"; setAttr ".g" yes; createNode script -n "uiConfigurationScriptNode"; - rename -uid "C8AD5306-42A0-C02B-2782-70BFA1B3DBD4"; + rename -uid "04FBB43B-4E9F-9FBB-D862-1BB7FC2D5825"; 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"; + rename -uid "6E6E8752-4339-0692-6B57-888A407A216B"; setAttr ".b" -type "string" "playbackOptions -min 1 -max 120 -ast 1 -aet 200 "; setAttr ".st" 6; select -ne :time1; diff --git a/test/openpose_humanik_test_preview.png b/test/openpose_humanik_test_preview.png index f3dc00c..1447a1c 100644 Binary files a/test/openpose_humanik_test_preview.png and b/test/openpose_humanik_test_preview.png differ diff --git a/test/openpose_humanik_test_preview_coco.png b/test/openpose_humanik_test_preview_coco.png index be1f7f7..99d6811 100644 Binary files a/test/openpose_humanik_test_preview_coco.png and b/test/openpose_humanik_test_preview_coco.png differ