Poser Python API ImExporter constant shenanigans
As part of my current project to directly load DSON content into Poser without resorting to the DSON Importer (which D3D's DSON Loader relies on), I've had to resort (as indeed DSON Importer does) to creating and importing a Wavefront OBJ file to get grouped figure meshes into Poser, since the Python API has no method of assigning vertices to polygon groups (and no, the Grouping Tool can't be manipulated from python to do that either). I've run into some confusion with setting the Import Options for the Wavefront OBJ importer and the named constants defined in the Python API not matching the positional parameters they represent.
Having created an obj file containing body-part-grouped facets in a temporary location, it should be straightforward to load it into Poser in just a few statements:
imEx = scene.ImExporter() imOpt = imEx.ImportOptions( objExt, None ) imOpt[ poser.kImOptCodeCENTERED ] = 0 imOpt[ poser.kImOptCodePLACEONFLOOR ] = 0 imOpt[ poser.kImOptCodePERCENTFIGSIZE ] = 0#100.0 imOpt[ poser.kImOptCodeSCALEABSOLUTE ] = 0.0 # This is offset X imOpt[ poser.kImOptCodeOFFSETX ] = 0.0 # This is offset Y imOpt[ poser.kImOptCodeOFFSETY ] = 0.0 # This is offset Z imOpt[ poser.kImOptCodeOFFSETZ ] = 0.0 imOpt[ poser.kImOptCodeWELDIDENTICALVERTS ] = 0 imOpt[ poser.kImOptCodeMAKEPOLYNORMSCONSISTENT ] = 0 imOpt[ poser.kImOptCodeFLIPNORMS ] = 0 imOpt[ poser.kImOptCodeFLIPUTEXTCOORDS ] = 0 imOpt[ poser.kImOptCodeFLIPVTEXTCOORDS ] = 0 imEx.Import( objExt, None, objFile, imOpt ) newProp = scene.Actor( objName )
Now, this appears to do what I need, but the confusion arises when I've run the script and then look at the import options when I select Import Wavefront OBJ from the Poser menus directly:
[Well great! Apparently I didn't have enough privileges to upload a 34kb .png file in the previous post! WTF?]
Anyway, the screenshot above, which sadly doesn't have the result that exactly matches the code fragment in the previous post [Curse you file upload weirdness], shows an Offset X value which resulted from setting the ImportOption corresponding to the poser.kImOptCodeSCALEABSOLUTE to -0.5. What???
The end of line comments show a few of the parameters seem to be offset (Oh Irony) from the option which they actually effect.
There also don't seem to be 13 separate constants which would correspond to the controls of the Import Options dialog. I've looked at the XML which defines that dialogue, and it seems absolutely standard and corresponds exactly to what is seen on the screen. The two radio buttons would correspond to a single boolean value which should have been poser.kImOptCodeSCALEABSOLUTE, but changing that option applies to the X Offset. There also doesn't seem to be a constant which matches the override checkbox for the X, Y & Z Offsets, so I assume setting them all to zero turns that off, but they don't match either.
Has anyone played around with driving the ImExporter Import function from within Python? And decoded/debugged the ImportOptions settings?
Don't know if this is a similar question to yours > https://forum.smithmicro.com/topic/1833/poserpython-importer-exporter-methods
I suppose the example scripts that stefan mentions in the last comment that you can find in the poser runtime under > /Runtime/Python/poserScripts/RenderControl/exportImportOBJ.py.
might be helpful.
still doesn't explain the details or why the Poser Python and its Manual is a bit meh after all these years.
Hello again - missed the edit time -
I found this post on Renderosity from 2014 > Reading a geometry from a file > https://www.renderosity.com/mod/forumpro/?thread_id=2885593
The Python forum at Renderosity isn't as active as it used to be.
To view the older post - when you go to the Python forum there are two date range boxes that won't hold the dates when using the drop down calenders,
but if you type in the dates > 02/02/2001 - 08/04/2017
then mouse click on - Browse archives
you'll see all the posts
Hope it helps
@adi Thanks for your replies. None of the responses in the linked threads actually manages to confirm my discovery, which implies that, at least in the 'obj' importer case, insufficient unit testing has been performed to confirm that all of the documented constants apply to the input options their names imply.
imEx = scene.ImExporter() imOpt = imEx.ImportOptions( objExt, None ) imOpt[ poser.kImOptCodeCENTERED ] = 0 # This is Centred (9001) imOpt[ poser.kImOptCodePLACEONFLOOR ] = 0 # This is Place on floor (9002) imOpt[ poser.kImOptCodePERCENTFIGSIZE ] = 0#100.0 imOpt[ poser.kImOptCodeSCALEABSOLUTE ] = 0.0 # This is offset X (9004) imOpt[ poser.kImOptCodeOFFSETX ] = 0.0 # This is offset Y (9004) imOpt[ poser.kImOptCodeOFFSETY ] = 0.0 # This is offset Z (9004) imOpt[ poser.kImOptCodeOFFSETZ ] = 0 # This is Weld (9005) imOpt[ poser.kImOptCodeWELDIDENTICALVERTS ] = 0 # This is Normals Consistent (9006) imOpt[ poser.kImOptCodeMAKEPOLYNORMSCONSISTENT ] = 0 # This is Flip Normals (9007) imOpt[ poser.kImOptCodeFLIPNORMS ] = 0 # This is Flip U (9008) imOpt[ poser.kImOptCodeFLIPUTEXTCOORDS ] = 0 # This is Flip V (9009) imOpt[ poser.kImOptCodeFLIPVTEXTCOORDS ] = 0 # This does nothing print imOpt imEx.Import( objExt, None, objFile, imOpt )
From my testing, only the first three constants actually have the effect their name implies. The rest, from poser.kImOptCodeSCALEABSOLUTE onward, appear to have their encoded options dict key value too large by 1. This kind of consequence typically implies that a code improvement for usability made complaints go away, but didn't propagate into the documentation, which is now WRONG! [Insert Gomer Pyle style "Surprise, surprise!"] I suspect the confusions (yes, multiple) arise from the Figure Scale or Absolute Scale radio buttons which form a single, apparently unrepresented control for deciding how to interpret the Size Percentage option, and the similarly unrepresented check box for applying or ignoring the X, Y & Z Offset inputs, as per the screen shot in my second post.
It appears that the normal convention applied by the many, great and good Pythonistas posting in the linked threads is to just zero all the input options to get the unmodified mesh imported directly into a new Poser prop's geometry. It obviously works, but only because there must be special case code parsing the import options which detects when the percentage is set to 0 and uses the existing, absolute scale of the imported object. Even the example scripts bundled with Python only set the first three options and leave the rest unchanged, so they never expose the problem.
Using the constants to assign the import options, SCALEABSOLUTE sets the X Offset, OFFSETX sets the Y Offset, ... and so on, with FLIPVTEXTCOORDS ignored and doing nothing. Not Good. Distinctly, extremely un-good!
So, you try to do the right thing, writing self-documenting code by using named constants to define import options and it blows up in your face.
Oh, I should hasten to add! This is not a documentation problem at all! These constants have the WRONG value to do the job their name implies. Python API bug ahoy! [Gomer Pyle, again]
Sorry the links didn't help.
One observation though - have you by chance mixed up your code comments a bit from SCALEABSOLUTE onwards you seem to be one line ahead>
imOpt[ poser.kImOptCodeSCALEABSOLUTE ] = 0.0 # This is offset X (9004) - This is Scale Absolute -
imOpt[ poser.kImOptCodeOFFSETX ] = 0.0 # This is offset Y (9004) - This is offset X -
Hopefully one of the few remaining poser python gurus will be able help -
or perhaps drop the new Poser team a message, perhaps they'll be willing to update the API.
I'm not in America so I've never seen The Andy Griffith Show sorry
@adi well spotted, and no, that is exactly what I'm saying is the problem. Yes, thank you, the links did help, but mostly to confirm that the problem had been overlooked for a long time.
Those constant names evaluate to key values which are out by one from the keys in the ImportOptions dict they are supposed to represent. (At least on MacOS Poser Pro 126.96.36.199999)
My added comments at the end of the lines are the results of my changing single parameter values when I run the script, and then manually opening the Import Wavefront Obj dialog and recording which parameters had changed. The effect is especially noticeable when I put the 50% value into
imOpt[ poser.kImOptCodeSCALEABSOLUTE ] = 0.5
expecting that the imported prop would be half it's original size, only to find that it was 100% size and had been translated on the X-axis by 0.5 PNU!
Subsequent, individual parameter tests convinced me I was not losing my marbles, but the constants were not affecting the options their name implied they should.
Don't worry, I'm not in the USA either, and Gomer Pyle was so far back that my memories are Black and White TV. I didn't even recognise "The Andy Griffiths Show" name, just a memory of hearing Jim Nabors' character (when he wasn't emitting his other trademark 'Well Gooooooollleeeee!') (very unPC now, but unremarkable then) ;-)
I figured that was the error but wanted to check because it's kind of dumb to be honest.
I'm among the 'most people' who have never noticed it's broken in the last 20 years as I use the Import box with nothing checked when loading an object in Poser because I model to the size and position I want - sorry for not noticing
Hope you get it figured out
@adi but the dialog version isn't broken, just the python API constant definitions. The dialog has obviously been exhaustively tested, but the constants are a bolt-on afterthought, like some documentation tends to be, or else it issues statements without explanation of the terminology, because the developers are too close to the code, and probably too busy bug-fixing to give the documentation writers the details they need to give more that Microsoft answers. I.e. accurate, but unhelpful.
I'm on windows, trying to compare with your experience. I'm curious what your Poser says when you run this code;
for i in xrange(12): print i, imex.ImportOptionString('obj', 'Wavefront', i)
1 Place on floor
2 BVH Export/Import Options
3 Scale the motion capture data automatically?
7 Weld identical vertices
8 Make polygon normals consistent
9 Flip normals
10 Flip U Texture Coordinates
11 Flip V Texture Coordinates
for i in xrange(12): print i, imex.ImportOptionString('obj', 'Wavefront', i)
Ha ha, been there, done that:
">>> for opt,val in imopt.items():
... opt, imex.ImportOptionString('obj',None,opt), val
(0, u'Centered', 1)
(1, u'Place on floor', 0)
(2, u'BVH Export/Import Options', 100.0)
(3, u'Scale the motion capture data automatically?', 0)
(4, u'OffsetX', 0.0)
(5, u'OffsetY', 0.0)
(6, u'OffsetZ', 1.401298464324817e-45)
(7, u'Weld identical vertices', 0)
(8, u'Make polygon normals consistent', 0)
(9, u'Flip normals', 0)
(10, u'Flip U Texture Coordinates', 0)
(11, u'Flip V Texture Coordinates', 1)
[Bloody '>>>' python prompt is interpreted like a 'quote' in this editor, have to prefix with a "]
As I mentioned above, I've even looked at the XML definition for the dialog comparing its command codes (9000+), but they're absolutely nothing to do with the python API, just the dialog, which works exactly as expected.
@bagginsbill I checked what happens in PPro2014 as well, for comparison (since the dialog is missing the radio buttons):
'>>> scene = poser.Scene()
'>>> imex = scene.ImExporter()
'>>> for opt in xrange(12):
u'Place on floor'
u'Percent of standard figure size'
u'Weld identical vertices'
u'Make polygon normals consistent'
u'Flip U Texture Coordinates'
u'Flip V Texture Coordinates'
Traceback (most recent call last):
File "<input>", line 2, in <module>
error: Enum value not found
So none of the extraneous BVH Export/Import Options from that version of Poser, but my script complains that the poser module has to kImOptCodeSCALEABSOLUTE attribute, so I'll have to put some version detection code in there.