sergio777 opened this issue on Sep 18, 2021 ยท 13 posts
sergio777 posted Sat, 18 September 2021 at 2:43 PM
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?
structure posted Sat, 18 September 2021 at 7:30 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 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
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
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
There are some other things that are not copied. Not only materials.
But this was not asked for. The question was:
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