Wed, Mar 18, 3:47 PM CDT

Renderosity Forums / Poser Python Scripting



Welcome to the Poser Python Scripting Forum

Forum Moderators: Lobo3433, Staff Forum Coordinators: Anim8dtoon

Poser Python Scripting F.A.Q (Last Updated: 2026 Mar 06 2:31 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: Exporting posed and premorphed figure to modeller


adp001 ( ) posted Sat, 28 November 2020 at 2:59 AM · edited Wed, 18 March 2026 at 3:47 PM

I am currently working on transferring Poser models to Blender with as little effort as possible. For this purpose I'm using parts of "PoMo" (FullbodyMorphs_wx4.py) from the beginning of the year, besides some other scripts. Again I stumbled over the problem with the unused vertices that Poser does not remove from the "UniMesh".

Old thread: https://www.renderosity.com/rr/mod/forumpro/?thread_id=2941172

Now I was forced to take care of the problem and came up with the following, really simple solution:

    def used_list(_geom):
        """
        Return a list with 1 at positions of used vertices
        and 0 at unused vertices.
        """
        sets = _geom.Sets()
        ar = np.zeros(_geom.NumVertices(), np.int8)
        for poly in _geom.Polygons():
            start = poly.Start()
            numv = poly.NumVertices()
            for _idx in sets[start:start + numv]:
                ar[_idx] = 1
        return ar

The returned list is then used in the following function, which creates a crosslist:

    def crosslist(_used, _verts):
        cl = np.zeros(len(_verts), np.int32)
        not_empty_idx = 0
        for _idx in range(len(_verts)):
            if _used[_idx] != 0:
                cl[_idx] = not_empty_idx
                not_empty_idx += 1
        return cl

And the problem is solved. Quick and simple.

The new crosslist is used in the above mentioned script just like the previously created crosslist. But I don't save it anymore, I create it every time anew. The recalculation costs almost not noticeably more time.




adp001 ( ) posted Sat, 28 November 2020 at 3:16 AM · edited Sat, 28 November 2020 at 3:18 AM

Explanation:

Function used_list() creates an array of the length of the vertices in the geometry, initialized with zeros. Then the polygons returned by Poser in UniMesh are scanned and the array is set to 1 where a polygon actually uses a vertex. Not used indices are later skipped (vertices not written).

Function crosslist() now uses this list instead of checking vertices for [0.0, 0.0, 0.0]. This caused problems if a polygon actually used [0.0, 0.0, 0.0].




adp001 ( ) posted Sat, 28 November 2020 at 3:20 AM · edited Sat, 28 November 2020 at 3:22 AM

The core of an export function then looks like this:

        # exporting vertices
        used = used_list(vertices)
        for idx, vertex in enumerate(vertices):
            if used[idx] != 0:
                print("v {} {} {}".format(*vertex), file=fh)




adp001 ( ) posted Sat, 28 November 2020 at 3:27 AM

The "sets" must be rewritten like this:

        vertex_crosslist = crosslist(used, vertices)
        sets = geom.Sets()
        for idx in range(len(sets)):
            sets[idx] = vertex_crosslist[sets[idx]]




adp001 ( ) posted Sat, 28 November 2020 at 1:49 PM · edited Sat, 28 November 2020 at 1:50 PM

A complete script based on "Script Buttons" can be downloaded from:

https://adp.spdns.org/#ObjImExport

Script Buttons: https://www.renderosity.com/rr/mod/forumpro?thread_id=2954892




cantonesejim ( ) posted Fri, 01 January 2021 at 10:06 PM · edited Thu, 12 February 2026 at 3:02 PM

I've been having an issue with the import figure button in Poser 12. It keeps erroring out when it tries to load the vertex data. Clipboard01.jpg


adp001 ( ) posted Sat, 02 January 2021 at 10:20 PM

As far as I can see, it seems like something is wrong with your file written from you modeller.

Can you import this file directly into Poser (with the "Import OBJ" function)?

For speed reasons the import function in the script does not check for problems or error conditions if only the file exists.

Script should run fine with P11 and P12. I didn't test P12, but an adapted form of the script in question (im_export.py) runs natively with Python 3.9.




cantonesejim ( ) posted Mon, 04 January 2021 at 4:40 PM

Thank you for the response, i checked the files and the vertex numbers add up and everything but i think it might be my poser install. Reinstalling and checking, if it doesn't make a difference i'll try in P11.


adp001 ( ) posted Mon, 23 August 2021 at 4:22 PM · edited Thu, 12 February 2026 at 3:02 PM

Updated.

Klick to Download.

Now able to deal with more then one figure in the scene. Each figure will have a seperate entry in the config file for pathes and filenames.

Bildschirmfoto vom 2021-08-23 23-30-46.png




bwldrd ( ) posted Mon, 23 August 2021 at 4:41 PM

TYVM :)

--------------------------------------------------------------------------------

Consider me insane if you wish, but is your reality any better?


adp001 ( ) posted Mon, 23 August 2021 at 5:26 PM

Forgotten to mention: If you used it before better delete the config file ("imex.config", same folder where your script is stored).




adp001 ( ) posted Mon, 23 August 2021 at 5:36 PM · edited Mon, 23 August 2021 at 5:39 PM

If you want to join several morphs into one full body morph, do the following:

  1. Dial in all morphs you need to shape your character.
  2. Uncheck "Silent Im-/Export".
  3. Export to "Figure_Morphed.obj".

Don't move your figure from now on!

  1. Set all morphs you want to combine (and later delete) to zero (don't use dials, insert a "0" to be sure it is really 0).
  2. Export as "Figure_Unmorphed.obj".
  3. Set a Morphname for the final morph.
  4. Import "Figure_Morphed.obj".

You may now delete all the morphs you have set to zero before.




adp001 ( ) posted Tue, 24 August 2021 at 3:54 PM

Update

Click to download

Supports Images, Bump and Transpareny for import into Blender better (mtl file).

Switch "Copy Imagefiles" on and off, because it makes no sense to copy the same images again and again to the same folder.




odf ( ) posted Tue, 24 August 2021 at 6:26 PM

That sounds great. I'll definitely try that out and study the code.

-- I'm not mad at you, just Westphalian.


adp001 ( ) posted Fri, 10 September 2021 at 7:25 PM · edited Thu, 12 February 2026 at 3:02 PM

New version with some updates.

Most widgets are now "ownerdrawn". Should run with Poser 11 and 12. Don't know how it works with iOS.

Bildschirmfoto vom 2021-09-11 02-22-24.png




adp001 ( ) posted Fri, 10 September 2021 at 7:30 PM

The morphname field can now automaticly increment (Checkbox autoincrement). If auto increment is enabled, two digits separated by a dot are appended to the name (if they are not already there). The number is incremented with each export.




adp001 ( ) posted Fri, 10 September 2021 at 7:45 PM

If "Export Groups" is enabled, Blender, for example, can automatically make vertex groups out of them.

"Do not always leave "X Center == 0" switched on. It makes sense if, for example, the center vertices of a head were moved during morphing (they are at X position 0). This happens easily when working with the Soft-Brush. I often use the soft brush on one half of the head (or chest) and then let Poser calculate the other half of the face/chest with the morph tool. This solves the problem of not being able to work symetrically with posed figures in Blender (and other modelers).

Translated with www.DeepL.com/Translator (free version)




adp001 ( ) posted Fri, 10 September 2021 at 7:49 PM

"Auto Sync" makes sure the figure in the script is always the one you have selected in Poser. Attention: Each figure has it's own morphname. If you change the figure, the morphname is also changed (switched to what you used before).




adp001 ( ) posted Sat, 11 September 2021 at 4:49 AM · edited Sat, 11 September 2021 at 4:51 AM

How to install:

The script consists of a main script ("Run.py") and a folder containing the required script parts to be loaded ("local_libs"). Create a separate folder for the script.Mine is called: "IM-EXporter". Unpack the ZIP file into this folder. From Poser you can then:

  1. start the script "Run.py" via Posers filemenu "File->Run Python Script...", or
  2. add "Run.py" to the file "/Runtime/Python/poserScripts/mainButtons.py" and add the Poser Python script buttons (my preferred method).

Path structure:

IM-EXporter
....local_libs
........__init__.py
........im_export.py
........wxPrepare.py
........WXTools.py
....Run.py

im_export.py contains anything to export a model and import a morph. The script strictly separates between UI part and the part that does the actual work. The result is that the import and export functions can be executed stand alone without any restrictions. And so of course can be used in your own script via "include".

wxPrePare.py contains functions that allows the script to work with both wxPython from Poser11 and Poser12 (i.e. Python 2 and Python 3).

WXTools.py contains wxPython functions and recreations of some standard controls ("ownerdrawn").




odf ( ) posted Sat, 11 September 2021 at 5:27 AM

adp001 posted at 5:25AM Sat, 11 September 2021 - #4427160

How to install:

Thanks! I was going to ask about that, not having done anything with Poser Python for a decade. Also this looks like a nice demo on how to load local libs in scripts. If I don't get too distracted by other things, I'll give this a try tomorrow.

-- I'm not mad at you, just Westphalian.


odf ( ) posted Sat, 11 September 2021 at 10:55 PM

Hey adp001, is it possible that you packaged an outdated Run.py with the latest version? It's trying to read the IsChecked property of a CheckboxCtrl, which only has IsSelected. Also it seems to register native checkbox callbacks that CheckboxCtril does not have. I have less than zero knowledge of wx, though, so I may be completely misunderstanding something.

I managed to get the script to export a mesh with a morph on it, but none of the various option buttons seemed to do anything.

-- I'm not mad at you, just Westphalian.


odf ( ) posted Sat, 11 September 2021 at 11:04 PM · edited Sat, 11 September 2021 at 11:11 PM

Ah, never mind, I wasn't seeing the complete interface. Looks like it's all working after my little tweaks.

ETA: Oh, "Import Morph" only seems to create a master channel with no dependencies. What am I doing wrong?

-- I'm not mad at you, just Westphalian.


adp001 ( ) posted Sun, 12 September 2021 at 8:49 AM

odf posted at 8:42AM Sun, 12 September 2021 - #4427212

Ah, never mind, I wasn't seeing the complete interface. Looks like it's all working after my little tweaks.

ETA: Oh, "Import Morph" only seems to create a master channel with no dependencies. What am I doing wrong?

Shouldn't be empty. Only if there is nothing :)

The script checks for empty morphs and skips a morph if no deltas are created.

I tested it some minutes ago with a fresh figure. But to be sure I repacked anything so it reflects the newest changes also (added the current filenames in the tooltips for Ex-/Import; helps if "silent mode" is on). Just re-download please.




adp001 ( ) posted Sun, 12 September 2021 at 8:52 AM

If it still does not work for you, there is a small chance that P12 does something I didn't catch. But I'm sure we can find out what goes wrong.




adp001 ( ) posted Sun, 12 September 2021 at 8:54 AM

odf posted at 8:53AM Sun, 12 September 2021 - #4427212

ETA: Oh, "Import Morph" only seems to create a master channel with no dependencies. What am I doing wrong?

If you import the same obj file you have exported, than the result is zero and no morphs are created :)




odf ( ) posted Sun, 12 September 2021 at 5:49 PM

adp001 posted at 5:47PM Sun, 12 September 2021 - #4427226

odf posted at 8:53AM Sun, 12 September 2021 - #4427212

ETA: Oh, "Import Morph" only seems to create a master channel with no dependencies. What am I doing wrong?

If you import the same obj file you have exported, than the result is zero and no morphs are created :)

Maybe I'm not understanding the concept correctly. I apply some morphs to my figure, export, check in Blender that the morphs have been applied, set all morphs back to zero, import the OBJ that I've previously exported. That's not enough?

-- I'm not mad at you, just Westphalian.


odf ( ) posted Sun, 12 September 2021 at 7:07 PM

Okay, turns out the only thing I really needed to change (on Poser 12.0.500) was the import wxPrepare in WXTools.py to import local_libs.wxPrepare. When loading the script I get an error complaining about a checkbox event with no object that I ignore, then the GUI for the tool appears and I only have to move it to a spot where I can see everything.

I have now managed to import a morph from an OBJ file using the tool. Do I understand correctly that I have to export the unmorphed figure each time directly before I import a new morph? Why is that necessary? I'm assuming some weirdness of the Poser interface?

-- I'm not mad at you, just Westphalian.


adp001 ( ) posted Sun, 12 September 2021 at 8:03 PM · edited Sun, 12 September 2021 at 8:04 PM

odf posted at 7:11PM Sun, 12 September 2021 - #4427269

Okay, turns out the only thing I really needed to change (on Poser 12.0.500) was the import wxPrepare in WXTools.py to import local_libs.wxPrepare.

Ups! Yes, Python 12 has different import logic. But I thought I catched that.... Thanks for finding that out! Will change it tomorrow.

When loading the script I get an error complaining about a checkbox event with no object that I ignore, then the GUI for the tool appears and I only have to move it to a spot where I can see everything.

Problem is that if an error is generated while wxPython is active, Poser Python often can't recover. Silly things may happen then :)

I have now managed to import a morph from an OBJ file using the tool. Do I understand correctly that I have to export the unmorphed figure each time directly before I import a new morph? Why is that necessary? I'm assuming some weirdness of the Poser interface?

No. One export at the start of a session. Pre-morphed or posed. Exports are the Reference used in the modeller. What you add or subtract to this reference is your imported morph. It's late here in germany and I'm pretty tired. I think I do something like a manual the next days (online and "realtime", but in German).

The gist of it (in technical terms) is: morph = numpy.array(imported_vertices) - numpy.array(exported_vertices)




odf ( ) posted Sun, 12 September 2021 at 8:17 PM

adp001 posted at 8:11PM Sun, 12 September 2021 - #4427273

odf posted at 7:11PM Sun, 12 September 2021 - #4427269

Okay, turns out the only thing I really needed to change (on Poser 12.0.500) was the import wxPrepare in WXTools.py to import local_libs.wxPrepare.

Ups! Yes, Python 12 has different import logic. But I thought I catched that.... Thanks for finding that out! Will change it tomorrow.

When loading the script I get an error complaining about a checkbox event with no object that I ignore, then the GUI for the tool appears and I only have to move it to a spot where I can see everything.

Problem is that if an error is generated while wxPython is active, Poser Python often can't recover. Silly things may happen then :)

Just checking obj for None before trying to access its name field got rid of that error.

I have now managed to import a morph from an OBJ file using the tool. Do I understand correctly that I have to export the unmorphed figure each time directly before I import a new morph? Why is that necessary? I'm assuming some weirdness of the Poser interface?

No. One export at the start of a session. Pre-morphed or posed. Exports are the Reference used in the modeller. What you add or subtract to this reference is your imported morph. It's late here in germany and I'm pretty tired. I think I do something like a manual the next days (online and "realtime", but in German).

The gist of it (in technical terms) is: morph = numpy.array(imported_vertices) - numpy.array(exported_vertices)

Okay, that's making more sense to me now. It might be useful to make that more explicit in the interface. Maybe separate between reference and regular exports, display the file name of the reference export for the current figure, and if none exists either refuse to import morphs, issue a warning, or automatically create one.

-- I'm not mad at you, just Westphalian.


adp001 ( ) posted Mon, 13 September 2021 at 11:38 PM · edited Mon, 13 September 2021 at 11:39 PM

Fixed.

I guess I didn't get enough sleep the last few days and created a pretty creative error.

Everything worked for me as long as I didn't restart Poser. That rarely happens here, because Windows with active Poser runs in a virtual machine on a separate server and is rarely shut down. Except when I brought Poser to a halt using Python :) And that's exactly what happened, after which this script here suddenly stopped working for me too and I got a chance to find the error.

Download the fixed version here.




RAMWorks ( ) posted Tue, 28 December 2021 at 1:51 PM

Thank you so much.  Wonderful that you've written python that runs in both Poser 11 and 12.  That's fantastic.  For the time being I'm doing all my content creation from Poser 11 and most of the scripts that are being written new these days are all for Poser 12 but I don't use it for production, YET!  

---Wolff On The Prowl---

My Store is HERE

My Freebies are HERE  


cantonesejim ( ) posted Fri, 04 November 2022 at 5:51 PM

any luck getting it to run on the latest version of 12? i keep getting this error:

  File "D:\Poser 12\Runtime\Python\poserScripts\IM_EXPORT\WxMain.py", line 33, in <module>

    reload(im_export)

NameError: name 'reload' is not defined

worked fine before upgrading


adp001 ( ) posted Tue, 08 November 2022 at 2:53 AM

Sorry for the late replay. I didn't get a notification.

I uploaded a version I'm using with P12 at the end of October:

https://adp.spdns.org/ImExporter_28102022.zip




adp001 ( ) posted Tue, 08 November 2022 at 3:57 AM

If you prefer to use the version you used before, mark line 33 as a comment (insert "#" as the first character of the line):

old: reload(im_export)
new: # reload(im_export)

Do so with all lines starting with "reload" if required.




adp001 ( ) posted Tue, 08 November 2022 at 3:59 AM

Updated my website. The latest version should work with P11 and P12.




bwldrd ( ) posted Tue, 08 November 2022 at 5:53 AM

Thank you very much.


--------------------------------------------------------------------------------

Consider me insane if you wish, but is your reality any better?


moatmai ( ) posted Thu, 17 July 2025 at 6:16 PM

@adp001 Apparently your website is gone and so is the script?

If you're able to supply it again, it would be much appreciated.


HartyBart ( ) posted Fri, 18 July 2025 at 6:15 AM

Here is the August 2021 version of im_export.py, which I have in my P12 folder and so presumably works with P12 and P13...


from __future__ import print_function, division

import os
import sys
import time
from shutil import copyfile

import numpy as np
import wx

if sys.version_info.major > 2:
    basestring = str
    map = lambda a, b: [a(_b) for _b in b]

try:
    import poser
except ImportError:
    import POSER_FAKE as poser

IMEX_CONFIG = globals().setdefault("IMEX_CONFIG", dict())
NP_PRECISION = np.float32
PRECISION = 6
vertex_frmt = " ".join(["{:.%df}" % PRECISION] * 3)
tex_vertex_frmt = " ".join(["{:.%df}" % PRECISION] * 2)


def set_precision(decimal_places):
    global vertex_frmt, tex_vertex_frmt
    vertex_frmt = " ".join(["{:.%df}" % decimal_places] * 3)
    tex_vertex_frmt = " ".join(["{:.%df}" % decimal_places] * 2)


def ErrDialog(err, msg=None):
    dlg = wx.MessageDialog(None, caption=err, message=msg, style=wx.ICON_ERROR)
    dlg.ShowModal()
    dlg.Close()
    return False


def get_or_create_morphdial(actor, parametername):
    assert isinstance(actor, poser.ActorType)
    p = actor.Parameter(parametername)
    if not p:
        actor.SpawnTarget(parametername)
        p = actor.Parameter(parametername)
    return p


def collect_geometry(figure, use_world=True):
    if figure is None:
        return None

    def np_vertex(v):
        return np.array((v.X(), v.Y(), v.Z()), NP_PRECISION)

    def used_list(_geom):
        """
        Return a list with 1 at positions of used vertices
        and 0 at unused vertices.
        """
        sets = _geom.Sets()
        ar = np.zeros(_geom.NumVertices(), np.int8)
        for poly in _geom.Polygons():
            start = poly.Start()
            numv = poly.NumVertices()
            for _idx in sets[start:start + numv]:
                ar[_idx] = 1
        return ar

    def crosslist(_used):
        length = len(_used)
        cl = np.zeros(length, np.int32)
        not_empty_idx = 0
        for _idx in range(length):
            if _used[_idx] != 0:
                cl[_idx] = not_empty_idx
                not_empty_idx += 1
        return cl

    geom, actorlist, actor_indices = figure.UnimeshInfo()
    used = used_list(geom)

    if use_world:
        newverts = np.zeros((geom.NumVertices(), 3), NP_PRECISION)
        for ac_idx, ac in enumerate(actorlist):
            ac_worldverts = ac.Geometry().WorldVertices()
            for i, vertex_idx in enumerate(actor_indices[ac_idx]):
                newverts[vertex_idx] = np_vertex(ac_worldverts[i])
    else:
        newverts = np.array([[_v.X(), _v.Y(), _v.Z()]
                               for _v in geom.Vertices()], NP_PRECISION)

    return dict(vertices=newverts,
                geom=geom,
                actorlist=actorlist,
                actor_indices=actor_indices,
                crosslist=crosslist(used),
                used=used
                )


def write_matfile(filename, materials):
    """
    Write out a simple material-file.
    """

    def move_image(map2move, imgpath="./"):
        fname = os.path.join(imgpath, os.path.basename(map2move))

        if os.path.exists(fname):
            if os.path.getmtime(map2move) == os.path.getmtime(fname):
                copyfile(map2move, fname)
        else:
            copyfile(map2move, fname)
        return fname

    try:
        tmp = open(filename, "w")
        tmp.close()
    except IOError:
        return ErrDialog("File Error.", "Can't create or write to file '{}'.\n"
                                        "Make sure directory '{}' exist and is writable.".
                         format(filename, os.path.dirname(filename)))

    with open(filename, "w") as mfh:
        move_tex = IMEX_CONFIG.get("CTRL_MoveTextures", False)
        imgpath = os.path.dirname(filename)

        for mat in materials:  # type: poser.MaterialType
            print("newmtl", mat.Name(), file=mfh)
            print("Ns", mat.Ns(), file=mfh)
            print("Ka", "0 0 0", file=mfh)
            print("Kd", " ".join(map(str, mat.DiffuseColor())), file=mfh)
            print("Ks", "0 0 0", file=mfh)
            if mat.TransparencyMax():
                print("D", mat.TransparencyMax(), file=mfh)

            if mat.TransparencyMapFileName():
                texmap = mat.TextureMapFileName()
                if move_tex:
                    texmap = os.path.basename(move_image(texmap, imgpath))
                print("map_D", texmap, file=mfh)

            if mat.TextureMapFileName():
                texmap = mat.TextureMapFileName()
                if move_tex:
                    texmap = os.path.basename(move_image(texmap, imgpath))
                print("map_Kd", texmap, file=mfh)

            if mat.BumpMapFileName():
                texmap = mat.BumpMapFileName()
                if move_tex:
                    texmap = os.path.basename(move_image(texmap, imgpath))
                print("map_Bump -bm %s" % (mat.BumpStrength() / 10.0), texmap, file=mfh)


def read_vertices(filename):
    """
    Read Wavefront obj-file saved to file. Typically a figure exported
    from Poser and modified with an external modeller (Blender etc).
    """

    vertices = list()
    try:
        with open(filename, "r") as fh:
            for line in fh:
                if not line:
                    break
                c, _, v = line.strip().partition(" ")
                if c == "v":
                    vertices.append(map(float, v.split()))

                # Remove following line if vertices are not in one block,
                # so the whole file is processed.
                elif c in ("vt", "vn", "f"):
                    break
    except IndexError:
        return ErrDialog("Vertex Error.",
                         "Vertices in file '%filename' corrupted.")
    except IOError:
        return ErrDialog("File Error.",
                         "File '{}' does not exist or is not accessible.".
                         format(filename))

    return np.array(vertices, NP_PRECISION)


def do_export(figure, filename=None, scale=1, use_groups=False, use_mat=True,
              resetmorph_first=None):
    assert isinstance(figure, poser.FigureType)
    if filename is None:
        filename = figure.Name() + ".obj"

    if not os.path.exists(os.path.dirname(filename)):
        return ErrDialog("Export Error", "Given path\n%s\ndoes not exist." % filename)

    oldvalues = list()
    if resetmorph_first:
        # Use this if you want to save the model without
        # the actual morph (morphname must be given in resetmorph_first).
        for ac in figure.Actors():
            for parm in ac.Parameters():
                if parm.Name() == resetmorph_first:
                    oldvalues.append((ac.Name(), parm.Value()))
                    parm.SetValue(0)

    unigeom = collect_geometry(figure=figure, use_world=True)
    vertices = unigeom.get("vertices") * scale
    geom = unigeom.get("geom")  # type: poser.GeomType
    vertex_crosslist = unigeom.get("crosslist")
    used = unigeom.get("used")

    with open(filename, "w") as fh:
        print("### Date    : %s" % time.asctime(), file=fh)
        print("### Figure  : %s" % figure.Name(), file=fh)
        print("### Vertices: %s" % len(vertices), file=fh)

        if use_mat and geom.Materials():
            matfile = filename.rsplit(".", 1)[0] + ".mtl"
            write_matfile(matfile, geom.Materials())
            print("mtllib ./" + os.path.basename(matfile), file=fh)

        print("o %s" % figure.Name(), file=fh)
        for idx, vertex in enumerate(vertices):
            if used[idx] != 0:
                print("v", vertex_frmt.format(*vertex), file=fh)

        if use_mat:
            for idx, tvert in enumerate(geom.TexVertices()):
                print("vt {} {}".format(tvert.U(), tvert.V()), file=fh)

        current_groups = list()
        current_mat = list()

        s_done = False
        polys = geom.Polygons()
        tpolys = geom.TexPolygons()
        sets = geom.Sets()
        for idx in range(len(sets)):
            sets[idx] = vertex_crosslist[sets[idx]]
        tsets = geom.TexSets()

        for index, poly in enumerate(polys):
            if use_groups:
                if poly.Groups() != current_groups:
                    current_groups = poly.Groups()
                    print("g", ", ".join(current_groups), file=fh)

            if use_mat:
                if poly.MaterialName() != current_mat:
                    current_mat = poly.MaterialName()
                    print("usemtl", current_mat, file=fh)

            if not s_done:
                print("s 1", file=fh)
                s_done = True

            line = [str(sets[idx + poly.Start()] + 1) for idx in range(poly.NumVertices())]
            if use_mat:
                tpoly = tpolys[index]
                for tidx, v in enumerate((tsets[idx + tpoly.Start()] + 1) for idx in range(tpoly.NumTexVertices())):
                    line[tidx] += "/%d" % v

            print("f", " ".join(map(str, line)), file=fh)

    if resetmorph_first:
        for ac_name, value in oldvalues:
            figure.Actor(ac_name).Parameter(resetmorph_first).SetValue(value)

    return filename


def do_import(figure, new_filename, old_filename, morphname,
              add_morphdata=False, force_center=False, scale=1, PRECISION=6):
    assert isinstance(figure, poser.FigureType)

    verts_new = read_vertices(new_filename)
    verts_old = read_vertices(old_filename)
    if len(verts_old) != len(verts_new):
        ErrDialog("Vertices mismatch.", "Old number of vertices: {}."
                                        "New number of vertices: {}.".
                  format(len(verts_old), len(verts_new)))
        return

    vertices, geom, actorlist, actor_indices, \
    verts_crosslist, used = collect_geometry(figure, use_world=False).values()

    # Overwrite vertices with the diff of loaded files.
    # The original vertices are of no use here.
    vertices = (verts_new - verts_old) / int(scale)
    del verts_new
    del verts_old

    body = figure.RootActor()
    masterdial = body.Parameter(morphname)
    if masterdial is None:
        body.CreateValueParameter(morphname)
    masterdial = body.Parameter(morphname)
    if masterdial is None:
        return ErrDialog("Morph Error.", "Can't find or create morph in body actor.")

    for actor_idx, actor in enumerate(actorlist):
        morph = list()
        for i, v_idx in enumerate(actor_indices[actor_idx]):
            x, y, z = map(lambda a: round(a, PRECISION),
                          vertices[verts_crosslist[v_idx]])
            if x != 0 or y != 0 or z != 0:
                morph.append((i, x, y, z))

        if len(morph) == 0:
            continue

        morphparm = get_or_create_morphdial(actor, morphname) # type: poser.ParmType
        zero_idx = set()
        if force_center:
            for idx, v in enumerate(actor.Geometry().Vertices()):
                if v.X() == 0:
                    zero_idx.add(idx)

        if morphparm is None:
            return ErrDialog("Morph Error", "Can't create Morphtarget.")
        if not morphparm.IsMorphTarget():
            return ErrDialog("Morph error.", "Morph Parametername ('%s')\n"
                                             "already exist but is not a morph."
                             % morphname)

        if add_morphdata:
            # Leave the old morph as it is and add the new data to it.
            # Use this if the figure is again exported (and reloaded in
            # the modeller) so that the diff between the old and new morph
            # is just your last change made in the modeller; the morph
            # in the figure is not just overwritten.
            for i, x, y, z in morph:
                xx, yy, zz = morphparm.MorphTargetDelta(i)
                if force_center and i in zero_idx:
                    xx = 0
                morphparm.SetMorphTargetDelta(i, x + xx, y + yy, z + zz)
        else:
            for i in range(actor.Geometry().NumVertices()):
                morphparm.SetMorphTargetDelta(i, 0, 0, 0)
            for i, x, y, z in morph:
                if force_center and i in zero_idx:
                    x = 0
                morphparm.SetMorphTargetDelta(i, x, y, z)

        if force_center:
            fix_morph_at_zero_x(actor, morphparm)

        while morphparm.NumValueOperations():
            morphparm.DeleteValueOperation(0)

        morphparm.AddValueOperation(poser.kValueOpTypeCodeKEY, masterdial)
        vop = morphparm.ValueOperations()[0]
        vop.InsertKey(0, 0)
        vop.InsertKey(1, 1)

    masterdial.SetMinValue(0)
    masterdial.SetMaxValue(1)
    masterdial.SetForceLimits(1)
    masterdial.SetValue(1)
    return morphname


def fix_morph_at_zero_x(poser_obj, morph2fix=None):
    """
    Replace morph X movement where original vertices are at zero position.

    """
    def fix_actor(ac, morphname=morph2fix):
        def fix_morph(morph, indices):
            for vert_idx in indices:
                x, y, z = morph.MorphTargetDelta(vert_idx)
                if x != 0:
                    morph.SetMorphTargetDelta(vert_idx, 0, y, z)

        if ac.IsBodyPart() and hasattr(ac, "Geometry") and ac.Geometry() is not None:
            vert_indices = []
            for idx, v in enumerate(ac.Geometry().Vertices()):
                if v.X() == 0:
                    vert_indices.append(idx)

            if isinstance(morphname, basestring):
                fix_morph(ac.Parameter(morphname), vert_indices)
            elif isinstance(morphname, poser.ParmType):
                fix_morph(morphname, vert_indices)
            elif morphname is None:
                for morph in (p for p in ac.Parameters() if p.IsMorphTarget()):  # type: poser.ParmType
                    fix_morph(morph, vert_indices)

    if isinstance(poser_obj, poser.FigureType):
        for ac in poser_obj.Actors(): # type: poser.ActorType
            fix_actor(ac)
    elif isinstance(poser_obj, poser.ActorType):
        fix_actor(poser_obj)



Learn the Secrets of Poser 11 and Line-art Filters.


moatmai ( ) posted Fri, 18 July 2025 at 8:14 AM
HartyBart posted at 6:15 AM Fri, 18 July 2025 - #4497906

Here is the August 2021 version of im_export.py, which I have in my P12 folder and so presumably works with P12 and P13...

You're a saint. Thank you!


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.