IC Python API:Serialize Pose

From Reallusion Wiki!
Revision as of 21:14, 8 March 2020 by Chuck (RL) (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Main article: RL Python Samples.

Ic python api serialize pose 01.png

As of 3/9/2020, iClone's animation system utilizes a set of motion bones that drive the skin bones. This system makes it possible to re-target animation from one character to another, regardless of differences in proportions. For scripts that deal with character animations, you'll likely want to read the transform values of the motion bones. Under some circumstances, you may even need to save the bone transformation values to a file. In order to do that you must first serialize the transform data, because RLPy data structures are not conducive for file storage.

Serialization is the process of converting an object into a stream of bytes to store the object or transmit it to memory, a database, or a file. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called de-serialization.

This article will go over the serialization process for the current pose of a selected avatar. We will write the serialized motion bone data into a JSON structure and print it out in iClone's Console Log.

Required Modules

Besides the fundamental Reallusion Python module, we'll also need Pyside2 to build the user interface. We'll also need the json module to read/write JSON formatted data.

import RLPy
import json
from PySide2 import QtWidgets
from PySide2.shiboken2 import wrapInstance

User Interface

Ic python api serialize pose 02.png

Let's create a very simple interface with a button for serializing motion bone pose for the currently selected character.

# Create a dialog window
window = RLPy.RUi.CreateRDialog()
window.SetWindowTitle("Pose")

# Create Pyside layout for RDialog
dialog = wrapInstance(int(window.GetWindow()), QtWidgets.QDialog)
main_widget = QtWidgets.QWidget()
dialog.setFixedSize(120, 120)

serialize_button = QtWidgets.QPushButton("Serialize Pose")
serialize_button.setFixedHeight(24)
dialog.layout().addWidget(serialize_button)

window.Show()

Avatar Selection Check

Ic python api serialize pose 03.png

Before we can serialize the pose data, we'll need to first verify that a suitable character is selected. If a proper character is not selected, then create a window prompt with a warning.

def avatar_selection_check():
    items = RLPy.RScene.GetSelectedObjects()

    if len(items) == 1:
        object_type = items[0].GetType()

        if object_type == RLPy.EObjectType_Avatar:
            return items[0]

    RLPy.RUi.ShowMessageBox(
        "Pose Manager - Operation error!",
        "Please select a valid Avatar to serialize its pose.",
        RLPy.EMsgButton_Ok)
    return None

Serializing the Current Pose

Ic python api serialize pose 04.png

Now comes the all-important function for serializing the selected character's pose and printing the JSON data in iClone's Console Log:

def serialize_pose():
    '''
    Serialize bone transform data so that it can be saved.
    The serialized structure is the following:
        "pose":{
            "bone01": {"r": [0.0, 0.0, 0.0, 0.0], "t": [0.0, 0.0, 0.0]},
            "bone02": {"r": [0.0, 0.0, 0.0, 0.0], "t": [0.0, 0.0, 0.0]},
            "bone03": {"r": [0.0, 0.0, 0.0, 0.0], "t": [0.0, 0.0, 0.0]}
        }
    '''
    avatar = avatar_selection_check()

    if avatar is not None:
        data = {"pose": {}}
        motion_bones = avatar.GetSkeletonComponent().GetMotionBones()

        for bone in motion_bones:
            name = bone.GetName()
            local_transform = bone.LocalTransform()
            translation = local_transform.T()
            quaternion = local_transform.R()
            data["pose"][name] = {
                "r": [quaternion.x, quaternion.y, quaternion.z, quaternion.w],  # Save quaternion rotations
                "t": [translation.x, translation.y, translation.z]  # Save 3D vector translations
            }

        print(json.dumps(data, indent=4, sort_keys=True))


serialize_button.clicked.connect(serialize_pose)

Notice that the pose consist of recording the translation and quaternion rotational values for every single motion bone.

Everything Put Together

You can copy and paste the following code into a PY file and load it into iClone via Script > Load Python.

import RLPy
import json
from PySide2 import QtWidgets
from PySide2.shiboken2 import wrapInstance


# Create a dialog window
window = RLPy.RUi.CreateRDialog()
window.SetWindowTitle("Pose")

# Create Pyside layout for RDialog
dialog = wrapInstance(int(window.GetWindow()), QtWidgets.QDialog)
main_widget = QtWidgets.QWidget()
dialog.setFixedSize(120, 120)

serialize_button = QtWidgets.QPushButton("Serialize Pose")
serialize_button.setFixedHeight(24)
dialog.layout().addWidget(serialize_button)

window.Show()


def avatar_selection_check():
    items = RLPy.RScene.GetSelectedObjects()

    if len(items) == 1:
        object_type = items[0].GetType()

        if object_type == RLPy.EObjectType_Avatar:
            return items[0]

    RLPy.RUi.ShowMessageBox(
        "Pose Manager - Operation error!",
        "Please select a valid Avatar to serialize its pose.",
        RLPy.EMsgButton_Ok)
    return None


def serialize_pose():
    '''
    Serialize bone transform data so that it can be saved.
    The serialized structure is the following:
        "pose":{
            "bone01": {"r": [0.0, 0.0, 0.0, 0.0], "t": [0.0, 0.0, 0.0]},
            "bone02": {"r": [0.0, 0.0, 0.0, 0.0], "t": [0.0, 0.0, 0.0]},
            "bone03": {"r": [0.0, 0.0, 0.0, 0.0], "t": [0.0, 0.0, 0.0]}
        }
    '''
    avatar = avatar_selection_check()

    if avatar is not None:
        data = {"pose": {}}
        motion_bones = avatar.GetSkeletonComponent().GetMotionBones()

        for bone in motion_bones:
            name = bone.GetName()
            local_transform = bone.LocalTransform()
            translation = local_transform.T()
            quaternion = local_transform.R()
            data["pose"][name] = {
                "r": [quaternion.x, quaternion.y, quaternion.z, quaternion.w],  # Save quaternion rotations
                "t": [translation.x, translation.y, translation.z]  # Save 3D vector translations
            }

        print(json.dumps(data, indent=4, sort_keys=True))


serialize_button.clicked.connect(serialize_pose)

APIs Used

You can research the following references for the APIs deployed in this code.