Forum: Poser Python Scripting


Subject: How to mirror a light

sergio777 opened this issue on Sep 18, 2021 ยท 13 posts


adp001 posted Sun, 19 September 2021 at 2:21 PM

Reworked the whole thing (rotation seems to work now) and splitted it into single functions that can be used independently (copy_trans(...), copy_rot(...), copy_scales(...), mirror_trans(...), mirror_rot(...)).

I also added a selection dialog.


from __future__ import print_function, division

try:
    import poser
except ImportError:
    # Not required while inside Poser Python, but very helpful for external editors.
    # See https://adp.spdns.org?#FakePoser
    from PoserLibs import POSER_FAKE as poser

import wx, sys

if sys.version_info.major > 2:
    # Python 3 (Poser 12 and above)
    basestring = str

SCENE = poser.Scene()
P_ROTS = poser.kParmCodeXROT, poser.kParmCodeYROT, poser.kParmCodeZROT
P_TRANS = poser.kParmCodeXTRAN, poser.kParmCodeYTRAN, poser.kParmCodeZTRAN
P_SCALES = poser.kParmCodeXSCALE, poser.kParmCodeYSCALE, poser.kParmCodeZSCALE, poser.kParmCodeASCALE


def copy_trans(source_obj, target_obj):
    for code in P_TRANS:
        target_obj.ParameterByCode(code).SetValue(source_obj.ParameterByCode(code).Value())


def copy_rot(source_obj, target_obj):
    for code in P_ROTS:
        target_obj.ParameterByCode(code).SetValue(source_obj.ParameterByCode(code).Value())


def copy_scales(source_obj, target_obj):
    for code in P_SCALES:
        target_obj.ParameterByCode(code).SetValue(source_obj.ParameterByCode(code).Value())


def mirror_trans(source_obj, target_obj, axis="XYZ", copyflag=False):
    if isinstance(axis, basestring):
        axis = [(ord(c) - ord("X")) for c in axis.upper()]
    assert isinstance(axis, (list, tuple)), \
        "Must be a string ('XYZ') or a list of integers (0, 1, 2)."

    for idx, code in enumerate(P_TRANS):
        if idx in axis:
            target_obj.ParameterByCode(code).SetValue(-source_obj.ParameterByCode(code).Value())
        elif copyflag:
            target_obj.ParameterByCode(code).SetValue(source_obj.ParameterByCode(code).Value())


def mirror_rot(source_obj, target_obj, axis="XYZ", copyflag=False):
    if isinstance(axis, basestring):
        axis = [(ord(c) - ord("X")) for c in axis.upper()]
    assert isinstance(axis, (list, tuple)), \
        "Must be a string ('XYZ') or a list of integers (0, 1, 2)."

    for idx, code in enumerate(P_ROTS):
        if idx in axis:
            p = source_obj.ParameterByCode(code)
            v = p.Value() % 360
            if v < 0:
                v = 360 - v
            if idx == 2:  # Z rotation
                target_obj.ParameterByCode(code).SetValue((180 - v) % 360)
            else:
                target_obj.ParameterByCode(code).SetValue((180 + v) % 360)
        elif copyflag:
            target_obj.ParameterByCode(code).SetValue(source_obj.ParameterByCode(code).Value())


def copy_light(obj, copyparms=True):
    """
    Create a new light object and copy as many attributes as possible from
    the old light to the new one. Attention: Maybe not all attributes are reachable
    via Python!
    """
    assert obj.IsLight()
    new_light = SCENE.CreateLight()

    for attr in dir(poser.ActorType):
        if attr.startswith("Set") and any((_p in attr for _p in
                                           ("Ambient", "Light", "Display", "Atmosphere",
                                            "Shading", "Shadow", "Visible", "RayTrace"))):
            getattr(new_light, attr)(getattr(obj, attr[3:])())
            # Attention: Don't use the above unfiltered! Your Poser may crash!

    if copyparms:
        # Copy objects parameters if required.
        for new_parm in new_light.Parameters():
            old_parm = obj.Parameter(new_parm.Name())
            if not old_parm:
                continue

            for p in dir(poser.ParmType):
                # Copy all attribute values of parameter we have a "Set.." function for.
                if p.startswith("Set"):
                    # Try to get the value by shorten the attribute name
                    # (by removing "Set").
                    try:
                        value = getattr(old_parm, p[3:])()
                        # Because lights has a different parameter set
                        # than other parameters this may fail without consequences.
                    except poser.error:
                        pass
                    except AttributeError:
                        pass
                    else:
                        # Set the value.
                        getattr(new_parm, p)(value)

    return new_light


def copy_mirrored_light(obj):
    new_light = copy_light(obj, copyparms=True)  # copyparms to get scales and position copied
    mirror_trans(obj, new_light, axis="X")
    mirror_rot(obj, new_light, axis="XYZ")

    return new_light


#### A bit wxPython stuff ###

with wx.SingleChoiceDialog(None, message="Select a light to mirror", caption="Mirror Light",
                           choices=[o.Name() for o in SCENE.Lights()]) as dlg:
    if dlg.ShowModal() == wx.ID_OK:
        try:
            light = SCENE.Actor(dlg.GetStringSelection())
        except poser.error:
            print("Selected light not available: '%s'" % dlg.GetStringSelection())
            new_light = None
        else:
            new_light = copy_mirrored_light(light)