Tue, Jan 7, 10:46 AM CST

Renderosity Forums / Poser Python Scripting



Welcome to the Poser Python Scripting Forum

Forum Moderators: Staff

Poser Python Scripting F.A.Q (Last Updated: 2024 Dec 02 3:16 pm)

We now have a ProPack Section in the Poser FreeStuff.
Check out the new Poser Python Wish List thread. If you have an idea for a script, jot it down and maybe someone can write it. If you're looking to write a script, check out this thread for useful suggestions.

Also, check out the official Python site for interpreters, sample code, applications, cool links and debuggers. This is THE central site for Python.

You can now attach text files to your posts to pass around scripts. Just attach the script as a txt file like you would a jpg or gif. Since the forum will use a random name for the file in the link, you should give instructions on what the file name should be and where to install it. Its a good idea to usually put that info right in the script file as well.

Checkout the Renderosity MarketPlace - Your source for digital art content!



Subject: How to mirror a light


sergio777 ( ) posted Sat, 18 September 2021 at 2:43 PM · edited Tue, 07 January 2025 at 10:39 AM

I can move a light manually easily but sometimes it's not so accurate, so I tried to make a script to mirror a light by changing the sign of the Yrotation or/and Trans values, but it didn't work. Does anyone have any idea how to do it?


5naPRqtc04UKYgCxLg4jczfTIExmd3jaw7FB6VBZ.jpg


structure ( ) posted Sat, 18 September 2021 at 7:30 PM · edited Sat, 18 September 2021 at 7:56 PM
Forum Coordinator

it seems that you can only mirror the parameters if you mirror them separately. and don't forget to scene.Draw() or scene.DrawAll() at the end.


```

if transitions().IsYRotate( parm ):

    parm.SetValue( -parm.Value() )

if transitions().IsXTranslate( parm ):

    parm.SetValue( -parm.Value() )

```


and the forum appears to be borked again.

Locked Out


structure ( ) posted Sat, 18 September 2021 at 7:56 PM · edited Sat, 18 September 2021 at 8:06 PM
Forum Coordinator


this works for me

Locked Out


sergio777 ( ) posted Sat, 18 September 2021 at 11:12 PM

Thanks for your answer Structure, the code works fine as long as the light parameter Zrot is 0, in cases where Z rotatation is different from zero the results are wrong.

There should be some way to translate any light position to one where the Zrot parameter is 0 and then run the script, but I haven't figured out how to do it.


adp001 ( ) posted Sun, 19 September 2021 at 12:51 AM · edited Sun, 19 September 2021 at 12:52 AM

from __future__ import print_function, division

import 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


def copy_light(obj):
    """
    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:])())

    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, trans="XYZ", rot="XYZ"):
    if isinstance(trans, basestring):
        trans = [(ord(c) - ord("X")) for c in trans.upper()]
    assert isinstance(trans, (list, tuple))
    if isinstance(rot, basestring):
        rot = [(ord(c) - ord("X")) for c in rot.upper()]
    assert isinstance(rot, (list, tuple))

    new_light = copy_light(obj)
    for idx, code in enumerate(P_TRANS):
        if idx in trans:
            p = new_light.ParameterByCode(code)
            p.SetValue(-p.Value())

    for idx, code in enumerate(P_ROTS):
        if idx in rot:
            p = new_light.ParameterByCode(code)
            v = p.Value() % 360
            p.SetValue(180-v)
    return new_light


light = copy_mirrored_light(SCENE.Actor("Light 1"), trans="X", rot="XYZ")




adp001 ( ) posted Sun, 19 September 2021 at 1:18 AM

The above function "copy_mirrored_light" takes an existing light and creates a mirrored copy of it. Parameter "trans" is a string containing the translation axis to mirror (X, Y and Z in one string, like "XYZ"). The same for "rot", the rotation axis. The last line above shows the most typical parameters: trans="X" and rot="XYZ".

Instead of a string a tuple or list with up to 3 values (0=="X", 1=="Y", 2=="Z") is also fine.




adp001 ( ) posted Sun, 19 September 2021 at 2:02 AM · edited Sun, 19 September 2021 at 2:05 AM

There is an error in the code above. Mirror rotation does not work correct. Seems we have to go the hard way and use Quaternions :)





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)




sergio777 ( ) posted Sun, 19 September 2021 at 3:42 PM

Thanks Adp001, the solution is pretty good, but it fails when using HDR, IBL lights or image based lights because those kind of lights contain a texture attached, also I noticed that it doesn't copy the params where the color picker is used like Color, Diffuse and Specular values.


adp001 ( ) posted Sun, 19 September 2021 at 11:14 PM · edited Sun, 19 September 2021 at 11:15 PM

There are some other things that are not copied. Not only materials.

But this was not asked for. The question was:

How to mirror a light

Which means the job is more than done.




sergio777 ( ) posted Mon, 20 September 2021 at 12:39 PM


Adp001, technically an IBL light is a light too isn't it?


adp001 ( ) posted Mon, 20 September 2021 at 5:00 PM

Pretty much anything can be made into a light using emitters.

As I wrote in the source as a comment: Poser Python does not allow access to everything. Or only in a very cumbersome way.
So there is nothing else than manual work (and the hope that there will be a corresponding function in Poser Python someday):

Use the menu item "Copy Object" in Poser to get a 1:1 copy. And then apply the mirror functions (that's the reason why I split this into single functions).




adp001 ( ) posted Sat, 25 September 2021 at 1:06 AM

To copy materials from one Poser object to another, see:

https://www.renderosity.com/forums/threads/2959992/something-more-about-poser-materials




Privacy Notice

This site uses cookies to deliver the best experience. Our own cookies make user accounts and other features possible. Third-party cookies are used to display relevant ads and to analyze how Renderosity is used. By using our site, you acknowledge that you have read and understood our Terms of Service, including our Cookie Policy and our Privacy Policy.