IC Python API:Local Move

From Reallusion Wiki!
Revision as of 23:50, 26 September 2019 by Chuck (RL) (Talk | contribs) (Updating the UI)

Jump to: navigation, search

Main article: RL Python Samples.

Required Modules

Besides the fundamental Reallusion Python module, we'll also need Pyside2 and os to read the QT UI file and build the user interface. We'll also need a global variable to store the various designated objects in the scene.

import RLPy
import os
from PySide2 import *
from PySide2.shiboken2 import wrapInstance

Local Position

In order to display the correct local position of an object we can't rely on iClone's RLPy.RINode.LocalTransform() because everything in an iClone scene is considered as belonging to the global transform-space. This has the tendency to align local-space to world-space coordinates because, technically, it's always parented. In order to get the true local position of an object, we'll need a special function:

def local_position(obj):
    # New matrix for the transform-space
    transform = obj.WorldTransform()
    parent_matrix = transform.Matrix()
    parent_matrix.SetTranslate(RLPy.RVector3.ZERO)

    # Get local-space position by multiplying world-space with the inverse transform-space
    relative_matrix = transform.Matrix() * parent_matrix.Inverse()

    return relative_matrix.GetTranslate()

Local to World Translate

We'll also need a function to transform local-space to world-space coordinates so we can drive an object's translation with local-space coordinates, much like iClone's Local Move.

def local_to_world_translate(obj, local_pos):
    # New matrix for the transform-space
    transform = obj.WorldTransform()
    transform_matrix = transform.Matrix()
    transform_matrix.SetTranslate(RLPy.RVector3.ZERO)

    # New matrix for local-space position
    local_matrix = RLPy.RMatrix4()
    local_matrix.MakeIdentity()
    local_matrix.SetTranslate(local_pos)

    # Get world-space position by multiplying local-space with the transform-space
    world_matrix = local_matrix * transform_matrix

    return world_matrix.GetTranslate()

Local Move

Now, we'll need a function for local transformation where we can just set the local-space coordinates to drive an object in world-space. This function hinges on the local_to_world_transform function mentioned above.

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

    if len(items) > 0:
        local_position = RLPy.RVector3(widget.moveX.value(), widget.moveY.value(), widget.moveZ.value())
        world_position = local_to_world_translate(items[0], local_position)
        current_time = RLPy.RGlobal.GetTime()

        # Set positional keys
        t_control = items[0].GetControl("Transform")
        t_data_block = t_control.GetDataBlock()
        t_data_block.SetData("Position/PositionX", current_time, RLPy.RVariant(world_position.x))
        t_data_block.SetData("Position/PositionY", current_time, RLPy.RVariant(world_position.y))
        t_data_block.SetData("Position/PositionZ", current_time, RLPy.RVariant(world_position.z))

        # Force update iClone native UI
        RLPy.RGlobal.SetTime(RLPy.RGlobal.GetTime() + RLPy.RTime(1))
        RLPy.RGlobal.SetTime(RLPy.RGlobal.GetTime() - RLPy.RTime(1))

Event Callbacks

We'll also need a RLPy.REventCallback class to update our user interface.

class EventCallback(RLPy.REventCallback):
    def __init__(self):
        RLPy.REventCallback.__init__(self)

    def OnObjectDataChanged(self):
        update_ui()

    def OnCurrentTimeChanged(self, fTime):
        update_ui()

    def OnObjectAdded(self):
        update_ui()

    def OnObjectDeleted(self):
        update_ui()

    def OnObjectSelectionChanged(self):
        update_ui()

Dialog Event Callbacks

In order to clean up the aforementioned event callbacks, we'll need to unregister it when the window is closed.

class DialogEventCallback(RLPy.RDialogCallback):
    def __init__(self):
        RLPy.RDialogCallback.__init__(self)

    def OnDialogHide(self):
        RLPy.REventHandler.UnregisterCallback(event_callback_id)
        return True

Updating the UI

We'll need function for updating our custom user interface.

def update_ui():
    items = RLPy.RScene.GetSelectedObjects()
    local_pos = RLPy.RVector3.ZERO

    if len(items) > 0:
        local_pos = local_position(items[0])

    for ind, val in enumerate([widget.moveX, widget.moveY, widget.moveZ]):
        val.blockSignals(True)
        val.setValue(local_pos[ind])
        val.blockSignals(False)

Notice that we block the signals on the qt widgets so we don't get a double feedback, and we unblock it when the parameters have been changed.

Building the UI

Everything Put Together

APIs Used

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