Mon, Nov 25, 5:17 PM 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 Sep 18 2:50 am)

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: WIP: Poser <-> Blender Bridge


  • 1
  • 2
adp001 ( ) posted Fri, 27 December 2019 at 3:10 PM · edited Mon, 25 November 2024 at 5:07 PM

Bildschirmfoto von 2019-12-27 21-41-23.png

Can export current state of a figure (posed, morphed) to wavefront OBJ file.

Exported file can be imported in any modeler (tested with C4D and Blender). Make sure your modeler does not touch vertex-order. For Blender import set scale to 1 or 10. For C4D something between 100 and 1000.

Use the same scale for export and import.

Change the figure as you like (morph), but don't move, rotate or scale the figure in your modeler. If you do, make sure you set anything back to zero before you make the export.

Export from your modeller to wavefront OBJ with another name (don't overwrite the file exported from Poser!).

Back in Poser press "Import Morph". You can change the name of the morph before importing.

Select the file you saved from your modeler. Any changes you made in your modeller are extracted and put into the right actor as morph.

Pressed buttons (Import/Export) will change color to red as long as export or import runs (just a second or two).

Don't touch the exported obj-file before you have imported your morph. The file is required for the import process, because the difference between the saved file from your modeller (the morph) and the previousely saved file from Poser is your actual morph (file B - file A == morph).

Script follows with the next post.




adp001 ( ) posted Fri, 27 December 2019 at 3:11 PM
from __future__ import print_function

try:
    import poser
except ImportError:
    raise RuntimeError("Script must run in Poser.")

import wx
import wx.aui
import sys
import os
import time
import json
import numpy as NP

CONFIG = dict()
BASEPATH = os.path.abspath(os.path.dirname(sys.argv[0]))
SCRIPT_FILENAME = os.path.basename(sys.argv[0])
CONFIG_FILENAME = SCRIPT_FILENAME.rsplit(".")[0] + ".cfg"

# Forced floatingpoint precision. Mainly to help avoiding floatingpoint
# errors while reading files from external modelers.
PRECISION = 8
NP_PRECISION = NP.float32


def ErrDialog(err, msg=None):
    dlg = wx.MessageDialog(None, err, style=wx.ICON_ERROR)
    if msg:
        dlg.SetMessage(msg)

    dlg.ShowModal()
    dlg.Close()


def read_config():
    global CONFIG
    fname = os.path.join(BASEPATH, CONFIG_FILENAME)
    if os.path.isfile(fname):
        try:
            with open(fname, "r") as fh:
                CONFIG = json.load(fh, encoding="utf-8")
        except IOError:
            pass


def write_config():
    global CONFIG
    fname = os.path.join(BASEPATH, CONFIG_FILENAME)
    try:
        with open(fname, "w") as fh:
            json.dump(CONFIG, fh)
    except IOError:
        ErrDialog("File Error.", "Can't write configfile '{}.".format(fname))


def write_matfile(filename, materials):
    """
    Write out a simple material-file.
    """
    try:
        open(filename, "w")
    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:
        for mat in materials:
            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.TextureMapFileName():
                print("map_Kd", mat.TextureMapFileName(), file=mfh)
            if mat.BumpMapFileName():
                print("map_Bump", mat.BumpMapFileName(), file=mfh)


def collect_geometry(figure):
    np_vertex = lambda v: NP.array((v.X(), v.Y(), v.Z()), NP_PRECISION)

    geom, actorlist, actor_indices = figure.UnimeshInfo()
    verts = NP.zeros((geom.NumVertices(), 3), NP_PRECISION)

    for actor_idx, actor in enumerate(actorlist):
        world_verts = actor.Geometry().WorldVertices()
        for i, vertex_idx in enumerate(actor_indices[actor_idx]):
            verts[vertex_idx] = np_vertex(world_verts[i])

    return dict(vertices=verts,
                geom=geom,
                actorlist=actorlist,
                actor_indices=actor_indices)


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).
    """
    try:
        open(filename, "r")
    except IOError:
        raise RuntimeError("File '{}' does not exist or is not accessible.".
                           format(filename))

    vertices = list()
    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

    return NP.array(vertices, NP_PRECISION)


def do_export(figure):
    """
    Export figure to Wavefront obj file.
    """
    assert isinstance(figure, poser.FigureType)
    figurename = figure.Name()
    parms = collect_geometry(figure)
    vertices = parms["vertices"]
    geom = parms["geom"]
    use_material = CONFIG.get("CTRL_ExportTexture", True)
    use_groups = CONFIG.get("CTRL_ExportGroups", False)
    morphname = CONFIG["CTRL_MorphName"].strip()
    vertices *= int(CONFIG.get("Scale", 100))

    if CONFIG.get("CTRL_SingleSelectFile", True):

        with wx.FileDialog(None, "Export Wavefront file",
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT | wx.FD_CHANGE_DIR,
                           defaultDir=CONFIG.get("ExportPath", BASEPATH),
                           defaultFile="{}-{}.obj".format(figurename, morphname)
                           ) as dlg:
            if dlg.ShowModal() == wx.ID_CANCEL:
                return

            CONFIG["ExportPath"] = dlg.GetPath()
            filename = dlg.GetFilename()
    else:
        filename = os.path.join(CONFIG["ExportPath"], "{}-{}.obj".format(figurename, morphname))

    try:
        open(filename, "w")
    except IOError:
        ErrDialog("Can't create or write to file '{}'.",
                  "Maybe you have to select another directory first.")
        return

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

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

        for vertex in vertices:
            print("v {} {} {}".format(*vertex), file=fh)

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

        current_groups = list()
        current_mat = list()
        if not use_groups:
            print("g", figurename, file=fh)

        polys = geom.Polygons()
        tpolys = geom.TexPolygons()
        sets = geom.Sets()
        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_material:
                if poly.MaterialName() != current_mat:
                    current_mat = poly.MaterialName()
                    print("usemtl", current_mat, file=fh)

            line = [str(sets[idx + poly.Start()] + 1) for idx in range(poly.NumVertices())]
            if use_material:
                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)

    CONFIG["LastExported"] = filename
    return filename


def do_import(figure, morphname):
    assert isinstance(figure, poser.FigureType)
    figurename = figure.Name()
    geom, actorlist, actor_indices = figure.UnimeshInfo()

    if CONFIG.get("CTRL_SingleSelectFile", True):
        old_filename = CONFIG.get("LastExported", None)
        if not old_filename:
            with wx.FileDialog(None, "Import Original exported Wavefront file",
                               style=wx.FD_OPEN | wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST,
                               defaultDir=CONFIG.get("ImportPath", BASEPATH),
                               defaultFile="{}-{}.obj".format(figurename, morphname)
                               ) as dlg:
                if dlg.ShowModal() == wx.ID_CANCEL:
                    return
                old_filename = dlg.GetFilename()

        with wx.FileDialog(None, "Import Wavefront file as morph",
                           style=wx.FD_OPEN | wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST,
                           defaultDir=CONFIG.get("ImportPath", BASEPATH),
                           defaultFile="{}-{}_mod.obj".format(figurename, morphname)
                           ) as dlg:
            if dlg.ShowModal() == wx.ID_CANCEL:
                return

            CONFIG["ExportPath"] = dlg.GetPath()
            new_filename = dlg.GetFilename()
    else:
        old_filename = os.path.join(CONFIG["ExportPath"], "{}-{}.obj".format(figurename, morphname))
        new_filename = os.path.join(CONFIG["ImportPath"], "{}-{}_mod.obj".format(figurename, morphname))

    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 = (verts_new - verts_old) / int(CONFIG.get("Scale", 100))
    del verts_new
    del verts_old

    body = figure.ParentActor()
    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[v_idx])
            if x != 0 or y != 0 or z != 0:
                morph.append((i, x, y, z))

        if len(morph) == 0:
            continue

        morphparm = actor.Parameter(morphname)
        if morphparm is None:
            actor.SpawnTarget(morphname)
            morphparm = actor.Parameter(morphname)

        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)
        for i in range(actor.Geometry().NumVertices()):
            morphparm.SetMorphTargetDelta(i, 0, 0, 0)
        for i, x, y, z in morph:
            morphparm.SetMorphTargetDelta(i, x, y, z)

        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(-.5)
    masterdial.SetMaxValue(1.0)


APP_NAME = "Figure Importer/Exporter"
STD_COLOR = 120, 120, 120
LBL_COLOR = 80, 80, 80
BG_COLOR = 75, 75, 75
HL_COLOR = 100, 100, 150
FG_COLOR = 200, 200, 200
TX_COLOR = 0xfe, 0xfe, 0xfe
STOPP_UPDATE_UI = False


class GridBagManager(object):
    __slots__ = "parent", "sizer", "flag", "border", "current_row", "font"

    def __init__(self, *args, **kwargs):
        for varname in self.__slots__:
            setattr(self, varname, kwargs.get(varname, None))

        for idx, entry in enumerate(args):
            if self.__slots__[idx] not in kwargs:
                setattr(self, self.__slots__[idx], entry)

        self.current_row = 0

        assert isinstance(self.parent, wx.Panel)
        assert isinstance(self.sizer, wx.GridBagSizer)
        assert isinstance(self.flag, int)
        assert isinstance(self.border, int)

    def addrow(self, *args, **kwargs):
        row = int(kwargs.get("row", self.current_row))
        col = int(kwargs.get("startcol", 0))
        flag = kwargs.get("flag", self.flag)
        font = kwargs.pop("font", self.font)
        widgets = []

        for idx, widget in enumerate(args):
            if font: widget.SetFont(font)

            self.sizer.Add(widget, pos=(row, col + idx),
                           flag=flag,
                           border=self.border)

            widgets.append(widget)

        self.current_row += 1
        return widgets


def setTooltip(ctrl, text):
    if text and ctrl:
        t = wx.ToolTip(text)
        t.SetAutoPop(5000)
        ctrl.SetToolTip(t)
    return ctrl


def ColoredCtrl(ctrl, **kwargs):
    ctrl.SetBackgroundColour(kwargs.pop("bgcolor", BG_COLOR))
    ctrl.SetForegroundColour(kwargs.pop("fgcolor", FG_COLOR))
    return setTooltip(ctrl, kwargs.pop("tooltip", None))


def LabelCtrl(parent, **kwargs):
    bg_color = kwargs.pop("bgcolor", BG_COLOR)
    fg_color = kwargs.pop("fgcolor", FG_COLOR)
    font = kwargs.pop("font", SYS_FONT)
    tooltip = kwargs.pop("tooltip", None)
    ctrl = wx.StaticText(parent, **kwargs)
    ctrl.SetFont(font)
    ctrl.SetBackgroundColour(bg_color)
    ctrl.SetForegroundColour(fg_color)
    return setTooltip(ctrl, tooltip)


def PrepCtrl(parent, ctrl, **kwargs):
    bind = kwargs.pop("bind", [])
    fgcolor = kwargs.pop("fgcolor", FG_COLOR)
    bgcolor = kwargs.pop("bgcolor", BG_COLOR)
    font = kwargs.pop("font", None)
    value = kwargs.pop("value", None)
    tooltip = kwargs.pop("tooltip", None)

    ctrl = ctrl(parent, **kwargs)

    if bind:
        if isinstance(bind[0], (tuple, list)):
            for bind_entry in bind:
                assert len(bind_entry) == 2
                ctrl.Bind(bind_entry[0], bind_entry[1])
        else:
            assert len(bind) == 2
            ctrl.Bind(bind[0], bind[1])

    ctrl.SetForegroundColour(fgcolor)
    ctrl.SetBackgroundColour(bgcolor)
    if font:
        ctrl.SetFont(font)
    if value:
        ctrl.SetValue(value)
    return setTooltip(ctrl, tooltip)


class AppPanel(wx.Panel):
    _CONFIGCHANGED = False

    def __init__(self, parent,
                 name="Fullbody Morphs",
                 style=wx.DEFAULT,
                 size=(270, 400),
                 **kwargs):
        super(self.__class__, self).__init__(parent=parent,
                                             id=wx.ID_ANY,
                                             name=name,
                                             style=style,
                                             size=size,
                                             **kwargs)
        read_config()
        self.stdfont = SYS_FONT

        self.gb_manager = GridBagManager(parent=self,
                                         sizer=wx.GridBagSizer(0, 0),
                                         flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
                                         border=2
                                         )

        self.figurelist = PrepCtrl(self, wx.Choice, choices=[],
                                   style=wx.CB_SORT | wx.NO_BORDER,
                                   size=(80, 20), name="figurelist",
                                   font=self.stdfont.SetPointSize(8),
                                   tooltip="Right click to reload Figurelist.",
                                   bind=((wx.EVT_RIGHT_UP, self.updateFigurelist),
                                         (wx.EVT_CHOICE, self.onChoice))
                                   )
        self.updateFigurelist()

        self.initUI(self.gb_manager)

        self.Bind(wx.EVT_WINDOW_DESTROY, self.onClose)
        self.Bind(wx.EVT_MOUSE_EVENTS, lambda ev: ev.StopPropagation())
        self.Bind(wx.EVT_SIZE, self.onSize)
        self.updateUI()
        self.Layout()

    def initUI(self, gbm):
        assert isinstance(gbm, GridBagManager)
        self.SetBackgroundColour(BG_COLOR)
        self.SetForegroundColour(FG_COLOR)
        self.SetFont(self.stdfont)
        btn_size = (80, 15)

        gbm.addrow(PrepCtrl(self, wx.Button, label="Export Figure",
                            name="BTN_ExportFigure",
                            style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=(50, 30),
                            bgcolor=STD_COLOR, fgcolor=(0xD0, 0xC0, 0x30),
                            tooltip="Export currently selected figure.",
                            bind=((wx.EVT_LEFT_UP, self.onExportModel),
                                  (wx.EVT_ENTER_WINDOW, self.onEnter_button),
                                  (wx.EVT_LEAVE_WINDOW, self.onLeave_button))
                            ),
                   PrepCtrl(self, wx.Button, label="Import Morph",
                            name="BTN_ImportMorph",
                            style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=(50, 30),
                            bgcolor=STD_COLOR, fgcolor=(0xD0, 0xC0, 0x30),
                            tooltip="Import modified figure as morph.",
                            bind=((wx.EVT_LEFT_UP, self.onImportModel),
                                  (wx.EVT_ENTER_WINDOW, self.onEnter_button),
                                  (wx.EVT_LEAVE_WINDOW, self.onLeave_button))
                            ))

        gbm.addrow(LabelCtrl(self, label="Figure",
                             fgcolor=STD_COLOR, name="LBL_FigureList"),
                   self.figurelist)

        gbm.addrow(LabelCtrl(self, label="Morphname",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   ColoredCtrl(wx.TextCtrl(self, value=CONFIG.get("CTRL_MorphName", "Morph"),
                                           name="CTRL_MorphName", style=wx.BORDER_SIMPLE),
                               bgcolor=BG_COLOR, fgcolor=TX_COLOR))

        gbm.addrow(LabelCtrl(self, label="Increment Morphnon each import",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   PrepCtrl(self, wx.CheckBox, value=CONFIG.get("CTRL_RenumberMorph", False),
                            name="CTRL_RenumberMorph", style=wx.NO_BORDER,
                            bind=((wx.EVT_CHECKBOX, self.onCheckBox),)))

        gbm.addrow(PrepCtrl(self, wx.Button, label="Export Path",
                            style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=btn_size,
                            bgcolor=STD_COLOR, fgcolor=FG_COLOR,
                            tooltip="Path to export current figure to.",
                            bind=((wx.EVT_LEFT_UP, self.onExportPath),
                                  (wx.EVT_ENTER_WINDOW, self.onEnter_button),
                                  (wx.EVT_LEAVE_WINDOW, self.onLeave_button))
                            ),
                   PrepCtrl(self, wx.Button, label="Import Path",
                            style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=btn_size,
                            bgcolor=STD_COLOR, fgcolor=FG_COLOR,
                            tooltip="Path where exported figures are stored.",
                            bind=((wx.EVT_LEFT_UP, self.onImportPath),
                                  (wx.EVT_ENTER_WINDOW, self.onEnter_button),
                                  (wx.EVT_LEAVE_WINDOW, self.onLeave_button))
                            ))

        gbm.addrow(LabelCtrl(self, label="Use Fileselection",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER,
                            value=CONFIG.get("CTRL_SingleSelectFile", True),
                            name="CTRL_SingleSelectFile",
                            tooltip="Select file on each Import/Export",
                            bind=((wx.EVT_CHECKBOX, self.onCheckBox),)))

        gbm.addrow(LabelCtrl(self, label="Export groups",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER,
                            value=CONFIG.get("CTRL_ExportGroups", False),
                            name="CTRL_ExportGroups",
                            tooltip="Export actor groups",
                            bind=((wx.EVT_CHECKBOX, self.onCheckBox),)))

        gbm.addrow(LabelCtrl(self, label="Export UV",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER,
                            value=CONFIG.get("CTRL_ExportTexture", True),
                            name="CTRL_ExportTexture",
                            bind=((wx.EVT_CHECKBOX, self.onCheckBox),)))

        gbm.addrow(LabelCtrl(self, label="LBL_Scale",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   ColoredCtrl(wx.TextCtrl(self, name="CTRL_Scale",
                                           value=str(CONFIG.get("Scale", 1)),
                                           style=wx.SIMPLE_BORDER | wx.TE_RIGHT),
                               fgcolor=FG_COLOR, bgcolor=BG_COLOR))

        gbm.sizer.AddGrowableCol(0)
        gbm.sizer.AddGrowableCol(1)

        sz = wx.BoxSizer(wx.VERTICAL)
        sz.AddMany([
            (gbm.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 10),
        ])

        sz.SetSizeHints(self)
        self.SetSizer(sz)
        wx.CallAfter(self.updateUI)

    def onSize(self, ev):
        CONFIG["Perspective"] = aui.SavePaneInfo(paneInfo)
        self._CONFIGCHANGED = True
        ev.Skip()

    def updateUI(self):
        obj = self.FindWindowByName("LBL_FigureList")
        if obj:
            c = (0xff, 0, 0) if self.figurelist.GetCount() == 0 else STD_COLOR
            obj.SetForegroundColour(c)

        if self._CONFIGCHANGED:
            write_config()
            self._CONFIGCHANGED = False

        if not STOPP_UPDATE_UI:
            wx.CallLater(300, self.updateUI)

    def onChoice(self, ev):
        choice = ev.GetEventObject()
        if choice.name == "figurelist":  # type: wx.Choice
            idx = choice.GetCurrentSelection()
            if idx == -1:
                CONFIG["figure"] = ""
            else:
                CONFIG["figure"] = choice.GetString(idx)

    def onRadioBox(self, ev):
        try:
            obj = ev.GetEventObject()
            CONFIG[obj.GetName()] = obj.GetValue()
        except Exception:
            raise
        finally:
            ev.Skip()

    def onCheckBox(self, ev):
        try:
            obj = ev.GetEventObject()
            CONFIG[obj.GetName()] = obj.GetValue()
        except Exception:
            raise
        finally:
            ev.Skip()

    def onExportModel(self, ev):
        """:type ev: wx.Event"""
        try:
            idx = self.figurelist.GetCurrentSelection()
            if idx < 0:
                ErrDialog("No figure selected.")
                return

            sc = wx.FindWindowByName("CTRL_Scale")
            CONFIG["Scale"] = sc.GetValue()

            obj = wx.FindWindowByName("CTRL_MorphName")
            CONFIG["CTRL_MorphName"] = obj.GetValue()
            obj = ev.GetEventObject()
            obj.SetBackgroundColour((0xff, 0, 0))
            obj.Update()
            figurename = self.figurelist.GetString(idx)
            figure = poser.Scene().Figure(figurename.strip())
            do_export(figure)
            obj.SetBackgroundColour(STD_COLOR)
            obj.Update()

        except Exception:
            raise
        finally:
            ev.Skip()

    def onImportModel(self, ev):
        try:
            sc = wx.FindWindowByName("CTRL_Scale")
            CONFIG["Scale"] = sc.GetValue()
            idx = self.figurelist.GetCurrentSelection()
            if idx < 0:
                ErrDialog("No figure selected.")
                return

            figurename = self.figurelist.GetString(idx)
            figure = poser.Scene().Figure(figurename.strip())
            obj = wx.FindWindowByName("CTRL_MorphName")
            morphname = obj.GetValue().strip() if obj else "MORPH"

            obj = ev.GetEventObject()
            obj.SetBackgroundColour((0xff, 0, 0))
            obj.Update()
            do_import(figure, morphname)
            obj.SetBackgroundColour(STD_COLOR)

            check = wx.FindWindowByName("CTRL_RenumberMorph")
            if check and check.GetValue():
                morphname, _, nr = morphname.partition(" ")
                morphname += " " + str(int(nr or 0) + 1)
                obj.SetValue(morphname)
        except Exception:
            raise
        finally:
            ev.Skip()

    def onExportPath(self, ev):
        try:
            defaultpath = CONFIG.get("ExportPath",
                                     CONFIG.get("ImportPath",
                                                CONFIG.get("BASEPATH", BASEPATH)))
            with wx.DirDialog(self,
                              "Choose Export Directory", defaultpath,
                              style=wx.DD_DEFAULT_STYLE
                              ) as dlg:
                if dlg.ShowModal() == wx.ID_OK:
                    CONFIG["ExportPath"] = dlg.GetPath()
                    self._CONFIGCHANGED = True

        except Exception:
            raise

    def onImportPath(self, ev):
        try:
            defaultpath = CONFIG.get("ImportPath",
                                     CONFIG.get("ExportPath",
                                                CONFIG.get("BASEPATH", BASEPATH)))
            with wx.DirDialog(None,
                              "Choose Import Directory", defaultpath,
                              style=wx.DD_DEFAULT_STYLE
                              ) as dlg:
                if dlg.ShowModal() == wx.ID_OK:
                    CONFIG["ImportPath"] = dlg.GetPath()
        except Exception:
            raise
        finally:
            ev.Skip()

    def onEnter_button(self, ev):
        obj = ev.GetEventObject()
        obj.SetBackgroundColour(HL_COLOR)
        ev.Skip()

    def onLeave_button(self, event):
        obj = event.GetEventObject()
        obj.SetBackgroundColour(STD_COLOR)
        event.Skip()

    def onClose(self, event):
        global STOPP_UPDATE_UI
        STOPP_UPDATE_UI = True

        CONFIG["CTRL_MorphName"] = wx.FindWindowByName("CTRL_MorphName").GetValue()
        CONFIG["Scale"] = wx.FindWindowByName("CTRL_Scale").GetValue()
        CONFIG["Perspective"] = aui.SavePaneInfo(paneInfo)

        write_config()

    def updateFigurelist(self, *ev):
        if poser:
            while self.figurelist.GetCount():
                self.figurelist.Delete(0)

            for idx, fig in enumerate(poser.Scene().Figures()):
                self.figurelist.Append(fig.Name())
                if fig == poser.Scene().CurrentFigure():
                    self.figurelist.SetSelection(idx)


if __name__ == "__main__":
    read_config()
    aui = poser.WxAuiManager()
    root = aui.GetManagedWindow()
    SYS_FONT = root.GetFont()

    paneInfo = aui.GetPane(APP_NAME)
    if paneInfo.IsOk():
        aui.ClosePane(paneInfo)

    paneInfo = wx.aui.AuiPaneInfo()
    paneInfo.name = APP_NAME
    paneInfo.Resizable(True).FloatingSize(wx.Size(250, 280)).BestSize(wx.Size(200, 230))
    paneInfo.Movable(True).DestroyOnClose(True)
    paneInfo.Name(APP_NAME).Caption("+++ NEED A NAME FOR THIS +++")

    savedinfo = CONFIG.get("Perspective", None)
    if savedinfo is not None:
        aui.LoadPaneInfo(savedinfo, paneInfo)

    MainPanel = AppPanel(parent=root, name=APP_NAME)
    aui.AddPane(MainPanel, paneInfo)
    paneInfo.Show()
    aui.Update()




adp001 ( ) posted Fri, 27 December 2019 at 3:14 PM

The script is still WIP. Please report errors or problems.

Feel free to make suggestions.




adp001 ( ) posted Fri, 27 December 2019 at 3:40 PM

This crappy editor here isn't able to handle sourcecode!!!

All Linefeed characters in my script where removed (written as backslash N – the backslash is cutted out).

So, If text appears strange, insert a backslash in front of this missplaced N.




structure ( ) posted Fri, 27 December 2019 at 3:44 PM · edited Fri, 27 December 2019 at 3:46 PM
Forum Coordinator

the backslash has always been a problem

"\"

I used a double backslash for this

Locked Out


adp001 ( ) posted Fri, 27 December 2019 at 3:57 PM

Oh, nice to know. Ok for one-liner. But with a whole script (732 line like above)?

There are plenty perfectly working javascript-editors out there. Can't believe we have to deal with something like this.




FVerbaas ( ) posted Sat, 28 December 2019 at 6:38 AM · edited Sat, 28 December 2019 at 6:45 AM
Forum Coordinator

This can become very interesting. If I use Marvelous Designer as my external modeller, let my figure in Poser and the corresponding avatar in MD both do the dance along the JCM poses (a fixed list) and compare the fully draped garment geometry in MD with a conformed and elementary rigged version in Poser, the result should be the (post transform) JCM's for the clothing, right?

I have scripts to let MD jackhammer along a series of avatar poses and produce garment obj files for any of them. As long as I keep the vertex order when rigging in Poser ( combine .obj file with skeleton .cr2), I can load them as a morph.

Production cycle for a garment would then become:

  • Make garment geometry, UV mapping and texture in MD,

  • Export a zero copy and the posed versions,

  • In Poser make an elementary conformer: (group the zero version and generate a .cr2 with bones definitions, copy the joint setup and initial morphs), conform, and then

  • run a difference script like this to make the final garment. Need to convert post transform into pre-transform of course.

Sure there will be plenty hurdles and rivers to cross, but after creation of the garment in MD the only steps needing user intervention are those where joint info and morphs are copied. Both essentially are just button press and are needed only once for a garment.


FVerbaas ( ) posted Sat, 28 December 2019 at 11:16 AM · edited Sat, 28 December 2019 at 11:19 AM
Forum Coordinator

Following answer in other thread: fully understood. MD just happens to be my generation tool of choice, because it is top line and after using it for 10 or so years I feel no urge to change (as long as they keep up the Python API. ;-)) Same function could be performed by VWD, Poser cloth room, or Blender cloth simulation once that comes to steam. The idea of the post was to illustrate the potential.


adp001 ( ) posted Sat, 28 December 2019 at 11:34 AM

Oh, my post was no offense.

MD does surly a good job. But isn't the cheapest piece of software ($50 monthly subscription, or $300 per year). A little much for someone who only makes a handful of clothes :)




FVerbaas ( ) posted Sat, 28 December 2019 at 11:59 AM · edited Sat, 28 December 2019 at 12:03 PM
Forum Coordinator

Never taken as an offence and fully understood. Price is pretty steep (but not as steep as you indicate). Until December 31st. it is US$350.- now for 'perpetual' step-in, and then US$ 200.- each 3-4 years if you are happy with the version you have (max. 3 back from current), or (from the top of my head US$75.- (or US$125.-?) each year for update from previous version. Version step-up is every year, normally in October-November.
Prices are ex. VAT.

Had I not been in the position where I am in now, I would surely be looking for alternatives
.


FVerbaas ( ) posted Sat, 28 December 2019 at 1:07 PM · edited Sat, 28 December 2019 at 1:08 PM
Forum Coordinator

adp001 posted at 7:57PM Sat, 28 December 2019 - #4374716

The script is still WIP. Please report errors or problems.

Feel free to make suggestions.

Well then, here goes:

Could it be there is an error if the selected figure is not the first one in the list:

Aantekening 2019-12-28 195634.jpg

I have two figures in the scene: An LF figure and a conformer. They show up nicely in the drop-down, but if I select any of the figures the above error is thrown.

Also noted that figures added when the script is already running do not show up in the drop-down list.


adp001 ( ) posted Sat, 28 December 2019 at 6:37 PM

Thanks for testing. Choice in this Poser wx-version needs "GetName". Fixed.

Also fixed some other hickups. The linefeed-problem is also fixed.

There is no scanning for new figures in this scriptversion. You have to right-click on the dropdown list. In a later version I'll make the whole thing an Addon. An addon gets info directly from Poser if changes where made (like a figure loaded/deleted).

New script follows.




adp001 ( ) posted Sat, 28 December 2019 at 6:37 PM
from __future__ import print_function

try:
    import poser
except ImportError:
    raise RuntimeError("Script must run in Poser.")

import wx
import wx.aui
import sys
import os
import time
import json
import numpy as NP

CONFIG = dict()
BASEPATH = os.path.abspath(os.path.dirname(sys.argv[0]))
SCRIPT_FILENAME = os.path.basename(sys.argv[0])
CONFIG_FILENAME = SCRIPT_FILENAME.rsplit(".")[0] + ".cfg"

# Forced floatingpoint precision. Mainly to help avoiding floatingpoint
# errors while reading files from external modelers.
PRECISION = 8
NP_PRECISION = NP.float32

_LF = chr(10)


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


_INFODISPLAY = None


def InfoDisplay(parent, msg, time2display=1000):
    global _INFODISPLAY
    if _INFODISPLAY is None:
        _INFODISPLAY = wx.Dialog(parent,
                                 style=wx.STAY_ON_TOP | wx.DIALOG_NO_PARENT)

        _INFODISPLAY.Center()


def read_config():
    global CONFIG
    fname = os.path.join(BASEPATH, CONFIG_FILENAME)
    if os.path.isfile(fname):
        try:
            with open(fname, "r") as fh:
                CONFIG = json.load(fh, encoding="utf-8")
        except IOError:
            pass


def write_config():
    global CONFIG
    fname = os.path.join(BASEPATH, CONFIG_FILENAME)
    try:
        with open(fname, "w") as fh:
            json.dump(CONFIG, fh)
    except IOError:
        ErrDialog("File Error.", "Can't write configfile '{}.".format(fname))


def write_matfile(filename, materials):
    """
    Write out a simple material-file.
    """
    try:
        open(filename, "w")
    except IOError:
        return ErrDialog("File Error.", "Can't create or write to file '{}'." + _LF +
                         "Make sure directory '{}' exist and is writable.".
                         format(filename, os.path.dirname(filename)))

    with open(filename, "w") as mfh:
        for mat in materials:
            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.TextureMapFileName():
                print("map_Kd", mat.TextureMapFileName(), file=mfh)
            if mat.BumpMapFileName():
                print("map_Bump", mat.BumpMapFileName(), file=mfh)


def collect_geometry(figure):
    if figure is None:
        return None
    np_vertex = lambda v: NP.array((v.X(), v.Y(), v.Z()), NP_PRECISION)
    geom, actorlist, actor_indices = figure.UnimeshInfo()
    verts = NP.zeros((geom.NumVertices(), 3), NP_PRECISION)

    for actor_idx, actor in enumerate(actorlist):
        world_verts = actor.Geometry().WorldVertices()
        for i, vertex_idx in enumerate(actor_indices[actor_idx]):
            verts[vertex_idx] = np_vertex(world_verts[i])

    return dict(vertices=verts,
                geom=geom,
                actorlist=actorlist,
                actor_indices=actor_indices)


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.",
                         "Vertice 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):
    """
    Export figure to Wavefront obj file.
    """
    assert isinstance(figure, poser.FigureType)
    figurename = figure.Name()
    parms = collect_geometry(figure)
    vertices = parms["vertices"]
    geom = parms["geom"]
    use_material = CONFIG.get("CTRL_ExportTexture", True)
    use_groups = CONFIG.get("CTRL_ExportGroups", False)
    morphname = CONFIG["CTRL_MorphName"].strip()
    vertices *= int(CONFIG.get("Scale", 100))

    if CONFIG.get("CTRL_SingleSelectFile", True):

        with wx.FileDialog(None, "Export Wavefront file",
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT | wx.FD_CHANGE_DIR,
                           defaultDir=CONFIG.get("ExportPath", BASEPATH),
                           defaultFile="{}-{}.obj".format(figurename, morphname)
                           ) as dlg:
            if dlg.ShowModal() == wx.ID_CANCEL:
                return

            CONFIG["ExportPath"] = dlg.GetPath()
            filename = dlg.GetFilename()
    else:
        filename = os.path.join(CONFIG["ExportPath"], "{}-{}.obj".format(figurename, morphname))

    try:
        open(filename, "w")
    except IOError:
        ErrDialog("Can't create or write to file '{}'.",
                  "Maybe you have to select another directory first.")
        return

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

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

        for vertex in vertices:
            print("v {} {} {}".format(*vertex), file=fh)

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

        current_groups = list()
        current_mat = list()
        if not use_groups:
            print("g", figurename, file=fh)

        polys = geom.Polygons()
        tpolys = geom.TexPolygons()
        sets = geom.Sets()
        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_material:
                if poly.MaterialName() != current_mat:
                    current_mat = poly.MaterialName()
                    print("usemtl", current_mat, file=fh)

            line = [str(sets[idx + poly.Start()] + 1) for idx in range(poly.NumVertices())]
            if use_material:
                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)

    CONFIG["LastExported"] = filename
    return filename


def do_import(figure, morphname):
    assert isinstance(figure, poser.FigureType)
    figurename = figure.Name()
    geom, actorlist, actor_indices = figure.UnimeshInfo()

    if CONFIG.get("CTRL_SingleSelectFile", True):
        old_filename = CONFIG.get("LastExported", None)
        if not old_filename:
            with wx.FileDialog(None, "Import Original exported Wavefront file",
                               style=wx.FD_OPEN | wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST,
                               defaultDir=CONFIG.get("ImportPath", BASEPATH),
                               defaultFile="{}-{}.obj".format(figurename, morphname)
                               ) as dlg:
                if dlg.ShowModal() == wx.ID_CANCEL:
                    return
                old_filename = dlg.GetFilename()

        with wx.FileDialog(None, "Import Wavefront file as morph",
                           style=wx.FD_OPEN | wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST,
                           defaultDir=CONFIG.get("ImportPath", BASEPATH),
                           defaultFile="{}-{}_mod.obj".format(figurename, morphname)
                           ) as dlg:
            if dlg.ShowModal() == wx.ID_CANCEL:
                return

            CONFIG["ImportPath"] = dlg.GetPath()
            new_filename = dlg.GetFilename()
    else:
        old_filename = os.path.join(CONFIG["ExportPath"], "{}-{}.obj".format(figurename, morphname))
        new_filename = os.path.join(CONFIG["ImportPath"], "{}-{}_mod.obj".format(figurename, morphname))

    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 = (verts_new - verts_old) / int(CONFIG.get("Scale", 100))
    del verts_new
    del verts_old

    body = figure.ParentActor()
    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[v_idx])
            if x != 0 or y != 0 or z != 0:
                morph.append((i, x, y, z))

        if len(morph) == 0:
            continue

        morphparm = actor.Parameter(morphname)
        if morphparm is None:
            actor.SpawnTarget(morphname)
            morphparm = actor.Parameter(morphname)

        if morphparm is None:
            return ErrDialog("Morph Error", "Can't create Morphtarget.")
        if not morphparm.IsMorphTarget():
            return ErrDialog("Morph error.", "Morph Parametername ('%s')" + _LF +
                             "already exist but is not a morph"
                             % morphname)
        for i in range(actor.Geometry().NumVertices()):
            morphparm.SetMorphTargetDelta(i, 0, 0, 0)
        for i, x, y, z in morph:
            morphparm.SetMorphTargetDelta(i, x, y, z)

        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(-.5)
    masterdial.SetMaxValue(1.0)


APP_NAME = "Figure Importer/Exporter"
STD_COLOR = 120, 120, 120
LBL_COLOR = 80, 80, 80
BG_COLOR = 75, 75, 75
HL_COLOR = 100, 100, 150
FG_COLOR = 200, 200, 200
TX_COLOR = 0xfe, 0xfe, 0xfe
STOPP_UPDATE_UI = False


class GridBagManager(object):
    __slots__ = "parent", "sizer", "flag", "border", "current_row", "font"

    def __init__(self, *args, **kwargs):
        for varname in self.__slots__:
            setattr(self, varname, kwargs.get(varname, None))

        for idx, entry in enumerate(args):
            if self.__slots__[idx] not in kwargs:
                setattr(self, self.__slots__[idx], entry)

        self.current_row = 0

        assert isinstance(self.parent, wx.Panel)
        assert isinstance(self.sizer, wx.GridBagSizer)
        assert isinstance(self.flag, int)
        assert isinstance(self.border, int)

    def addrow(self, *args, **kwargs):
        row = int(kwargs.get("row", self.current_row))
        col = int(kwargs.get("startcol", 0))
        flag = kwargs.get("flag", self.flag)
        font = kwargs.pop("font", self.font)
        widgets = []

        for idx, widget in enumerate(args):
            if font: widget.SetFont(font)

            self.sizer.Add(widget, pos=(row, col + idx),
                           flag=flag,
                           border=self.border)

            widgets.append(widget)

        self.current_row += 1
        return widgets


def setTooltip(ctrl, text):
    if text and ctrl:
        t = wx.ToolTip(text)
        t.SetAutoPop(5000)
        ctrl.SetToolTip(t)
    return ctrl


def ColoredCtrl(ctrl, **kwargs):
    ctrl.SetBackgroundColour(kwargs.pop("bgcolor", BG_COLOR))
    ctrl.SetForegroundColour(kwargs.pop("fgcolor", FG_COLOR))
    return setTooltip(ctrl, kwargs.pop("tooltip", None))


def LabelCtrl(parent, **kwargs):
    bg_color = kwargs.pop("bgcolor", BG_COLOR)
    fg_color = kwargs.pop("fgcolor", FG_COLOR)
    font = kwargs.pop("font", SYS_FONT)
    tooltip = kwargs.pop("tooltip", None)
    ctrl = wx.StaticText(parent, **kwargs)
    ctrl.SetFont(font)
    ctrl.SetBackgroundColour(bg_color)
    ctrl.SetForegroundColour(fg_color)
    return setTooltip(ctrl, tooltip)


def PrepCtrl(parent, ctrl, **kwargs):
    bind = kwargs.pop("bind", [])
    fgcolor = kwargs.pop("fgcolor", FG_COLOR)
    bgcolor = kwargs.pop("bgcolor", BG_COLOR)
    font = kwargs.pop("font", None)
    value = kwargs.pop("value", None)
    tooltip = kwargs.pop("tooltip", None)

    ctrl = ctrl(parent, **kwargs)

    if bind:
        if isinstance(bind[0], (tuple, list)):
            for bind_entry in bind:
                assert len(bind_entry) == 2
                ctrl.Bind(bind_entry[0], bind_entry[1])
        else:
            assert len(bind) == 2
            ctrl.Bind(bind[0], bind[1])

    ctrl.SetForegroundColour(fgcolor)
    ctrl.SetBackgroundColour(bgcolor)
    if font:
        ctrl.SetFont(font)
    if value:
        ctrl.SetValue(value)
    return setTooltip(ctrl, tooltip)


class AppPanel(wx.Panel):
    _CONFIGCHANGED = False

    def __init__(self, parent,
                 name="Fullbody Morphs",
                 style=wx.DEFAULT,
                 size=(270, 400),
                 **kwargs):
        super(self.__class__, self).__init__(parent=parent,
                                             id=wx.ID_ANY,
                                             name=name,
                                             style=style,
                                             size=size,
                                             **kwargs)
        read_config()
        self.stdfont = SYS_FONT

        self.gb_manager = GridBagManager(parent=self,
                                         sizer=wx.GridBagSizer(0, 0),
                                         flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
                                         border=2
                                         )

        self.figurelist = PrepCtrl(self, wx.Choice, choices=[],
                                   style=wx.CB_SORT | wx.NO_BORDER,
                                   size=(80, 20), name="figurelist",
                                   font=self.stdfont.SetPointSize(8),
                                   tooltip="Right click to reload Figurelist.",
                                   bind=((wx.EVT_RIGHT_UP, self.updateFigurelist),
                                         (wx.EVT_CHOICE, self.onChoice))
                                   )
        self.updateFigurelist()

        self.initUI(self.gb_manager)

        self.Bind(wx.EVT_WINDOW_DESTROY, self.onClose)
        self.Bind(wx.EVT_MOUSE_EVENTS, lambda ev: ev.StopPropagation())
        self.Bind(wx.EVT_SIZE, self.onSize)
        self.updateUI()
        self.Layout()

    def initUI(self, gbm):
        assert isinstance(gbm, GridBagManager)
        self.SetBackgroundColour(BG_COLOR)
        self.SetForegroundColour(FG_COLOR)
        self.SetFont(self.stdfont)
        btn_size = (80, 15)

        gbm.addrow(PrepCtrl(self, wx.Button, label="Export Figure",
                            name="BTN_ExportFigure",
                            style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=(50, 30),
                            bgcolor=STD_COLOR, fgcolor=(0xD0, 0xC0, 0x30),
                            tooltip="Export currently selected figure.",
                            bind=((wx.EVT_LEFT_UP, self.onExportModel),
                                  (wx.EVT_ENTER_WINDOW, self.onEnter_button),
                                  (wx.EVT_LEAVE_WINDOW, self.onLeave_button))
                            ),
                   PrepCtrl(self, wx.Button, label="Import Morph",
                            name="BTN_ImportMorph",
                            style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=(50, 30),
                            bgcolor=STD_COLOR, fgcolor=(0xD0, 0xC0, 0x30),
                            tooltip="Import modified figure as morph.",
                            bind=((wx.EVT_LEFT_UP, self.onImportModel),
                                  (wx.EVT_ENTER_WINDOW, self.onEnter_button),
                                  (wx.EVT_LEAVE_WINDOW, self.onLeave_button))
                            ))

        gbm.addrow(LabelCtrl(self, label="Figure",
                             fgcolor=STD_COLOR, name="LBL_FigureList"),
                   self.figurelist)

        gbm.addrow(LabelCtrl(self, label="Morphname",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   ColoredCtrl(wx.TextCtrl(self, value=CONFIG.get("CTRL_MorphName", "Morph"),
                                           name="CTRL_MorphName", style=wx.BORDER_SIMPLE),
                               bgcolor=BG_COLOR, fgcolor=TX_COLOR))

        gbm.addrow(LabelCtrl(self, label="Increment Morph" + _LF + "on each import",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   PrepCtrl(self, wx.CheckBox, value=CONFIG.get("CTRL_RenumberMorph", False),
                            name="CTRL_RenumberMorph", style=wx.NO_BORDER,
                            bind=((wx.EVT_CHECKBOX, self.onCheckBox),)))

        gbm.addrow(PrepCtrl(self, wx.Button, label="Export Path",
                            style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=btn_size,
                            bgcolor=STD_COLOR, fgcolor=FG_COLOR,
                            tooltip="Path to export current figure to.",
                            bind=((wx.EVT_LEFT_UP, self.onExportPath),
                                  (wx.EVT_ENTER_WINDOW, self.onEnter_button),
                                  (wx.EVT_LEAVE_WINDOW, self.onLeave_button))
                            ),
                   PrepCtrl(self, wx.Button, label="Import Path",
                            style=wx.BORDER_NONE | wx.BU_EXACTFIT, size=btn_size,
                            bgcolor=STD_COLOR, fgcolor=FG_COLOR,
                            tooltip="Path where exported figures are stored.",
                            bind=((wx.EVT_LEFT_UP, self.onImportPath),
                                  (wx.EVT_ENTER_WINDOW, self.onEnter_button),
                                  (wx.EVT_LEAVE_WINDOW, self.onLeave_button))
                            ))

        gbm.addrow(LabelCtrl(self, label="Use Fileselection",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER,
                            value=CONFIG.get("CTRL_SingleSelectFile", True),
                            name="CTRL_SingleSelectFile",
                            tooltip="Select file on each Import/Export",
                            bind=((wx.EVT_CHECKBOX, self.onCheckBox),)))

        gbm.addrow(LabelCtrl(self, label="Export groups",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER,
                            value=CONFIG.get("CTRL_ExportGroups", False),
                            name="CTRL_ExportGroups",
                            tooltip="Export actor groups",
                            bind=((wx.EVT_CHECKBOX, self.onCheckBox),)))

        gbm.addrow(LabelCtrl(self, label="Export UV",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   PrepCtrl(self, wx.CheckBox, style=wx.NO_BORDER,
                            value=CONFIG.get("CTRL_ExportTexture", True),
                            name="CTRL_ExportTexture",
                            bind=((wx.EVT_CHECKBOX, self.onCheckBox),)))

        gbm.addrow(LabelCtrl(self, label="LBL_Scale",
                             fgcolor=STD_COLOR, bgcolor=BG_COLOR),
                   ColoredCtrl(wx.TextCtrl(self, name="CTRL_Scale",
                                           value=str(CONFIG.get("Scale", 1)),
                                           style=wx.SIMPLE_BORDER | wx.TE_RIGHT),
                               fgcolor=FG_COLOR, bgcolor=BG_COLOR))

        gbm.sizer.AddGrowableCol(0)
        gbm.sizer.AddGrowableCol(1)

        sz = wx.BoxSizer(wx.VERTICAL)
        sz.AddMany([
            (gbm.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, 10),
        ])

        sz.SetSizeHints(self)
        self.SetSizer(sz)
        wx.CallAfter(self.updateUI)

    def onSize(self, ev):
        CONFIG["Perspective"] = aui.SavePaneInfo(paneInfo)
        self._CONFIGCHANGED = True
        ev.Skip()

    def updateUI(self):
        obj = self.FindWindowByName("LBL_FigureList")
        if obj:
            c = (0xff, 0, 0) if self.figurelist.GetCount() == 0 else STD_COLOR
            obj.SetForegroundColour(c)

        if self._CONFIGCHANGED:
            write_config()
            self._CONFIGCHANGED = False

        if not STOPP_UPDATE_UI:
            wx.CallLater(300, self.updateUI)

    def onChoice(self, ev):
        choice = ev.GetEventObject()

        if choice.GetName() == "figurelist":  # type: wx.Choice
            idx = choice.GetCurrentSelection()
            if idx == -1:
                CONFIG["figure"] = ""
            else:
                CONFIG["figure"] = choice.GetString(idx)

    def onRadioBox(self, ev):
        try:
            obj = ev.GetEventObject()
            CONFIG[obj.GetName()] = obj.GetValue()
        except Exception:
            raise
        finally:
            ev.Skip()

    def onCheckBox(self, ev):
        try:
            obj = ev.GetEventObject()
            CONFIG[obj.GetName()] = obj.GetValue()
        except Exception:
            raise
        finally:
            ev.Skip()

    def onExportModel(self, ev):
        """:type ev: wx.Event"""
        try:
            idx = self.figurelist.GetCurrentSelection()
            if idx < 0:
                ErrDialog("No figure selected.")
                return

            sc = wx.FindWindowByName("CTRL_Scale")
            CONFIG["Scale"] = sc.GetValue()

            obj = wx.FindWindowByName("CTRL_MorphName")
            CONFIG["CTRL_MorphName"] = obj.GetValue()
            obj = ev.GetEventObject()
            obj.SetBackgroundColour((0xff, 0, 0))
            obj.Update()
            figurename = self.figurelist.GetString(idx)
            figure = poser.Scene().Figure(figurename.strip())
            do_export(figure)
            obj.SetBackgroundColour(STD_COLOR)
            obj.Update()

        except Exception:
            raise
        finally:
            ev.Skip()

    def onImportModel(self, ev):
        try:
            sc = wx.FindWindowByName("CTRL_Scale")
            CONFIG["Scale"] = sc.GetValue()
            idx = self.figurelist.GetCurrentSelection()
            if idx < 0:
                ErrDialog("No figure selected.")
                return

            figurename = self.figurelist.GetString(idx)
            figure = poser.Scene().Figure(figurename.strip())
            obj = wx.FindWindowByName("CTRL_MorphName")
            morphname = obj.GetValue().strip() if obj else "MORPH"

            obj = ev.GetEventObject()
            obj.SetBackgroundColour((0xff, 0, 0))
            obj.Update()
            do_import(figure, morphname)
            obj.SetBackgroundColour(STD_COLOR)

            check = wx.FindWindowByName("CTRL_RenumberMorph")
            if check and check.GetValue():
                morphname, _, nr = morphname.partition(" ")
                morphname += " " + str(int(nr or 0) + 1)
                obj.SetValue(morphname)
        except Exception:
            raise
        finally:
            ev.Skip()

    def onExportPath(self, ev):
        try:
            defaultpath = CONFIG.get("ExportPath",
                                     CONFIG.get("ImportPath",
                                                CONFIG.get("BASEPATH", BASEPATH)))
            with wx.DirDialog(self,
                              "Choose Export Directory", defaultpath,
                              style=wx.DD_DEFAULT_STYLE
                              ) as dlg:
                if dlg.ShowModal() == wx.ID_OK:
                    CONFIG["ExportPath"] = dlg.GetPath()
                    self._CONFIGCHANGED = True

        except Exception:
            raise

    def onImportPath(self, ev):
        try:
            defaultpath = CONFIG.get("ImportPath",
                                     CONFIG.get("ExportPath",
                                                CONFIG.get("BASEPATH", BASEPATH)))
            with wx.DirDialog(None,
                              "Choose Import Directory", defaultpath,
                              style=wx.DD_DEFAULT_STYLE
                              ) as dlg:
                if dlg.ShowModal() == wx.ID_OK:
                    CONFIG["ImportPath"] = dlg.GetPath()
        except Exception:
            raise
        finally:
            ev.Skip()

    def onEnter_button(self, ev):
        obj = ev.GetEventObject()
        obj.SetBackgroundColour(HL_COLOR)
        ev.Skip()

    def onLeave_button(self, event):
        obj = event.GetEventObject()
        obj.SetBackgroundColour(STD_COLOR)
        event.Skip()

    def onClose(self, event):
        global STOPP_UPDATE_UI
        STOPP_UPDATE_UI = True

        CONFIG["CTRL_MorphName"] = wx.FindWindowByName("CTRL_MorphName").GetValue()
        CONFIG["Scale"] = wx.FindWindowByName("CTRL_Scale").GetValue()
        CONFIG["Perspective"] = aui.SavePaneInfo(paneInfo)

        write_config()

    def updateFigurelist(self, *ev):
        if poser:
            while self.figurelist.GetCount():
                self.figurelist.Delete(0)

            for idx, fig in enumerate(poser.Scene().Figures()):
                self.figurelist.Append(fig.Name())
                if fig == poser.Scene().CurrentFigure():
                    self.figurelist.SetSelection(idx)


if __name__ == "__main__":
    read_config()
    aui = poser.WxAuiManager()
    root = aui.GetManagedWindow()
    SYS_FONT = root.GetFont()

    paneInfo = aui.GetPane(APP_NAME)
    if paneInfo.IsOk():
        aui.ClosePane(paneInfo)

    paneInfo = wx.aui.AuiPaneInfo()
    paneInfo.name = APP_NAME
    paneInfo.Resizable(True).FloatingSize(wx.Size(250, 280)).BestSize(wx.Size(200, 230))
    paneInfo.Movable(True).DestroyOnClose(True)
    paneInfo.Name(APP_NAME).Caption("+++ NEED A NAME FOR THIS +++")

    savedinfo = CONFIG.get("Perspective", None)
    if savedinfo is not None:
        aui.LoadPaneInfo(savedinfo, paneInfo)

    MainPanel = AppPanel(parent=root, name=APP_NAME)
    aui.AddPane(MainPanel, paneInfo)
    paneInfo.Show()
    aui.Update()




FVerbaas ( ) posted Sun, 29 December 2019 at 2:08 PM · edited Sun, 29 December 2019 at 2:11 PM
Forum Coordinator

OK. Works with different figures now.

Ran into one issue up to now: Had to change line 655: It made it to call SetLabel() and not SetValue()

Aantekening 2019-12-29 211035.jpg

My interest by the way is of pure self-interest. I need to update 'MDBridge for Poser' with something more robust. The thing was set up as a proof of concept. Do not look at the code. It does the trick though and responses are positive.

This Blender bridge is a kickstart of what I had in mind for the 'real' version. I spent ages trying to get around the bugs in the Poser importer-exporter and more in particular the morph loader. Had to make workarounds there.

I do hope it is allowed to copy your good practice.


FVerbaas ( ) posted Sun, 29 December 2019 at 2:43 PM
Forum Coordinator

With credits as due, of course.


adp001 ( ) posted Sun, 29 December 2019 at 2:45 PM

This Blender bridge is a kickstart of what I had in mind for the 'real' version. I spent ages trying to get around the bugs in the Poser importer-exporter and more in particular the morph loader. Had to make workarounds there.

And when I look at the code, I myself am amazed at how simple it is.

Anyway: I'm always glad to support creation of freeware (the reason while I post here) :) Give credit in your docu/sourecode as usual and anybody will be happy.




adp001 ( ) posted Sun, 29 December 2019 at 2:53 PM

To get around the problems with the RR editor, and because it's stupid to post such big sources all the time, I'm in the process of setting up a simple webserver on a Odroid-C2 lying around here and putting it on the net. Just have to secure it properly.

My next update will be downloadable from there (with your change - thanks for that!).




adp001 ( ) posted Sun, 29 December 2019 at 2:56 PM

By the way interesting that MD uses Qt. I wish we had that with Poser too (instead of the stupid wxWindow).




infinity10 ( ) posted Mon, 30 December 2019 at 4:32 AM

wow

Eternal Hobbyist

 


TaishoBee ( ) posted Mon, 30 December 2019 at 4:40 AM

exciting!

Poser Pro 11 (always updated to current version available) . Laptop Specs: Intel Core i7-7700 CPU @ 3.60GHz 3.60 GHz :: 16 GB Ram :: Windows 10 Pro


FVerbaas ( ) posted Mon, 30 December 2019 at 4:43 AM
Forum Coordinator

They are using a lightweight version called PythonQt. It does not support all of Qt though. There appears to be no list of classes not supported so it is a bit of guess and miss.

MD on the other hand does not support all of Python. import os for example will not work. File access must go via PythonQt functions. But I better not go off topic.


adp001 ( ) posted Mon, 30 December 2019 at 8:08 AM

I had a quick look into the C++ Code. Seems they support QtQml/QtQuick. Is Javascript allowed in QML-Widgets?

Talking about Python can never be off topic :)




adp001 ( ) posted Mon, 30 December 2019 at 8:09 AM

@TaishoBee and @infinity10:

Thanks for interest.




adp001 ( ) posted Mon, 30 December 2019 at 8:33 AM

FVerbaas posted at 3:19PM Mon, 30 December 2019 - #4374962

They are using a lightweight version called PythonQt.

More import question is: Will they upgrade from QT4 to QT5?

QT5 supports allmost anything one needs for fast 3D display and computing.

Maybe a RPC-Interface QT5 <-> Poser could be something.




adp001 ( ) posted Mon, 30 December 2019 at 8:39 AM · edited Mon, 30 December 2019 at 8:40 AM

FVerbaas posted at 3:37PM Mon, 30 December 2019 - #4374922

OK. Works with different figures now.

Ran into one issue up to now: Had to change line 655: It made it to call SetLabel() and not SetValue()

Aantekening 2019-12-29 211035.jpg

SetLabel isn't right. But the wrong obj is changed. It has to be:

check.SetValue(morphname)
# **not** obj.SetValue




FVerbaas ( ) posted Mon, 30 December 2019 at 9:27 AM
Forum Coordinator

adp001 posted at 4:26PM Mon, 30 December 2019 - #4374976

SetLabel isn't right. But the wrong obj is changed. It has to be:

check.SetValue(morphname)
# **not** obj.SetValue

OK. Will change


FVerbaas ( ) posted Mon, 30 December 2019 at 10:05 AM · edited Mon, 30 December 2019 at 10:06 AM
Forum Coordinator

adp001 posted at 4:27PM Mon, 30 December 2019 - #4374975

More import question is: Will they upgrade from QT4 to QT5?

I do not think MD will be doing that. I have the impression the Python API is not high on their list and they rely on development by an external party. I wrote the MD side bridgehead the way I wrote it, linking in one layer below the level they suggest, The level they suggest to use is/was plain buggy. I heard of no-one who used it with success.
Note that the API came with MD7 in fall 2017, yet they use Python 2.7. I do not see them changing unless they are requested by an important large client.

QT5 supports allmost anything one needs for fast 3D display and computing.

Maybe a RPC-Interface QT5 <-> Poser could be something.

Complete new Poser interface based on QT5, right? You could propose this to Jenn. ;-)


adp001 ( ) posted Mon, 30 December 2019 at 11:18 AM

FVerbaas posted at 6:07PM Mon, 30 December 2019 - #4374983

adp001 posted at 4:27PM Mon, 30 December 2019 - #4374975

More import question is: Will they upgrade from QT4 to QT5?

I do not think MD will be doing that. I have the impression the Python API is not high on their list and they rely on development by an external party. I wrote the MD side bridgehead the way I wrote it, linking in one layer below the level they suggest, The level they suggest to use is/was plain buggy. I heard of no-one who used it with success.
Note that the API came with MD7 in fall 2017, yet they use Python 2.7. I do not see them changing unless they are requested by an important large client.

Changing from Python 2.7 to 3.x needs a lot of manpower.

And including QT5 in commercial software costs money. Maybe this is a point too.

QT5 supports allmost anything one needs for fast 3D display and computing.

Maybe a RPC-Interface QT5 <-> Poser could be something.

Complete new Poser interface based on QT5, right? You could propose this to Jenn. ;-)

No. Just a bridge to bring things in and out. But with a more direct binding (changes made in Poser are reflected directly over the bridge and vice versa). I started something like this years ago for C4D (very basic and for a special need).

I don't think Bondware has an interest in QT, because, as said: it cost money to implement it.




adp001 ( ) posted Mon, 30 December 2019 at 1:40 PM · edited Mon, 30 December 2019 at 1:42 PM

Actual sourcecode can be downloaded from

adp.spdns.org

Above webpage is completly free of ads and trackers.

(sorry, no direct links allowed at RR)




FVerbaas ( ) posted Mon, 30 December 2019 at 2:12 PM
Forum Coordinator

I assume MD will therefore be on Python 2.7 and QT4 for the foreseeable future.


FVerbaas ( ) posted Mon, 30 December 2019 at 2:23 PM · edited Mon, 30 December 2019 at 2:24 PM
Forum Coordinator

FVerbaas posted at 9:12PM Mon, 30 December 2019 - #4374978

adp001 posted at 4:26PM Mon, 30 December 2019 - #4374976

SetLabel isn't right. But the wrong obj is changed. It has to be:

check.SetValue(morphname)
# **not** obj.SetValue

OK. Will change

Wait a minute: _check _is a checkbox that can be checked, or not.

morphname is a string which was just assembled, with incrementing counter, which makes no sense as a value for a checkbox.

Does that string not need to go into the text box obj?


adp001 ( ) posted Mon, 30 December 2019 at 5:16 PM

You are totally right. The whole thing got out of my control :)

Here is the correct part:

    def onImportModel(self, ev):
        try:
            sc = wx.FindWindowByName("CTRL_Scale")
            CONFIG["Scale"] = sc.GetValue()
            idx = self.figurelist.GetCurrentSelection()
            if idx < 0:
                ErrDialog("No figure selected.")
                return

            figurename = self.figurelist.GetString(idx)
            figure = poser.Scene().Figure(figurename.strip())
            morph_ctrl = wx.FindWindowByName("CTRL_MorphName")
            morphname = morph_ctrl.GetValue().strip() if morph_ctrl else "MORPH"

            obj = ev.GetEventObject()
            obj.SetBackgroundColour((0xff, 0, 0))
            obj.Update()
            do_import(figure, morphname)
            obj.SetBackgroundColour(STD_COLOR)

            check = wx.FindWindowByName("CTRL_RenumberMorph")
            if check and check.GetValue():
                morphname, _, nr = morphname.partition(" ")
                morphname += " " + str(int(nr or 0) + 1)
                morph_ctrl.SetValue(morphname)
        except Exception:
            raise
        finally:
            ev.Skip()




adp001 ( ) posted Mon, 30 December 2019 at 5:17 PM

And now I'm going to sleep for several days...




emjay247 ( ) posted Sun, 12 January 2020 at 7:27 PM

Hi there. I am interested in this project and am using a Mac. Are there special installation instructions for this osX? I installed the script to my Poser runtime and it calls up, but the GUI does not appear the same as the screenshot in the first post of this thread. I downloaded the script from the adp.spdns.org site link and would like to bridge it with blender 2.81

Please let me know if you have any suggestions or updates. Thanks!


adp001 ( ) posted Sun, 12 January 2020 at 8:06 PM

emjay247 posted at 3:06AM Mon, 13 January 2020 - #4376389

Hi there. I am interested in this project and am using a Mac. Are there special installation instructions for this osX? I installed the script to my Poser runtime and it calls up, but the GUI does not appear the same as the screenshot in the first post of this thread. I downloaded the script from the adp.spdns.org site link and would like to bridge it with blender 2.81

Please let me know if you have any suggestions or updates. Thanks!

No, there is nothing special for osX.

Can you post a screenshot so I can see what you mean?




emjay247 ( ) posted Mon, 13 January 2020 at 7:59 AM

Screen Shot 2020-01-12 at 10.13.02 PM.png

Good Morning,

I was able to get the script to work, though it doesn't look the same as yours (see screenshot). For some reason, when I set the file path, Poser reverts it back to it's own folder in applications. I was having trouble finding the exported obj because it was NOT sending it to the file path I assigned (shared folder). I tested it with two figures (LaFemme and Angela by Ali) and they both interchanged well with blender as long as you keep the same vertex order on import and export.

Thank you for sharing this script. This will help immensely with body morph creation!


adp001 ( ) posted Mon, 13 January 2020 at 9:25 AM

emjay247 posted at 4:03PM Mon, 13 January 2020 - #4376406

I was able to get the script to work, though it doesn't look the same as yours (see screenshot).

Seems to me that wxPython does funny things with scaled widgets (Buttons etc). At least this version used by Poser (a real old version, by the way). Maybe it's better to not scale/manipulate the widgets on OSX. But I'm really not good with UIs (lack of interest ;) )

For some reason, when I set the file path, Poser reverts it back to it's own folder in applications. I was having trouble finding the exported obj because it was NOT sending it to the file path I assigned (shared folder).

Yes, this is an error in the script. I changed it already and I do the update within a couple of minutes.

I tested it with two figures (LaFemme and Angela by Ali) and they both interchanged well with blender as long as you keep the same vertex order on import and export.

Yes, same old rule for any morph procedure ;)




adp001 ( ) posted Mon, 13 January 2020 at 9:42 AM

emjay247 posted at 4:39PM Mon, 13 January 2020 - #4376406

For some reason, when I set the file path, Poser reverts it back to it's own folder in applications. I was having trouble finding the exported obj because it was NOT sending it to the file path I assigned (shared folder).

In your screenshot "Use Fileselection" is not checked. Please try it. The script will give you ex- and import dialogs if checked.




emjay247 ( ) posted Mon, 13 January 2020 at 1:54 PM

Ok, so that's what that button does. Thanks for the explanation. Should it be selected by default for new users? I'll let you know if there is any other trouble understanding how this works. Thanks again, this script will be very useful.


adp001 ( ) posted Mon, 13 January 2020 at 7:48 PM

emjay247 posted at 2:37AM Tue, 14 January 2020 - #4376460

Ok, so that's what that button does. Thanks for the explanation. Should it be selected by default for new users? I'll let you know if there is any other trouble understanding how this works. Thanks again, this script will be very useful.

Yes please. let me know.

And feel free to make sugestions. Even for correct lettering on buttons etc (I have serious problems with english).

I'm really open for improvement proposals.

Would be nice if someone could write some sort of a short manual.

Within the next days I will look into the Mac-OS problem wxPython seems to have.




Letterworks ( ) posted Tue, 14 January 2020 at 3:09 PM

I have to say I'm extremely excite by this script a way import and export with reverse deformation is sorely needed in poser.I down loaded your scripts and tried for over an hour to get them to work. I tried to exprt with groups, I tried to export with the default settings I tried several figures and modelling tools. I tried to export to a number of newly created folders, and old working folders. However every time I tried to import a morph I got an error saying (to paraphrase) that the exported vertices number didn't match the import number.

I'm open (very) to accept this as user error, but I can't think of anything else to try. I get the with both the Manipulate morph and the FullBody morph scripts.

Any suggestions would be welcome as I really want to see this work?

Mike


adp001 ( ) posted Tue, 14 January 2020 at 3:52 PM · edited Tue, 14 January 2020 at 3:53 PM

Letterworks posted at 10:41PM Tue, 14 January 2020 - #4376631

However every time I tried to import a morph I got an error saying (to paraphrase) that the exported vertices number didn't match the import number.

Sounds like your modeler "saved" some vertices. Most modelers (including Blender) have an option named "Keep vertex order" or something like that. You have to check this (import and export).

Problem is: While Poser creates the unimesh, several vertices become dispensable (those vertices where bodyparts are glued together). But they decided not to discard those vertices, but set them to all zero and put them in the file. Modeler don't like that and tend to ignore these redundant vertices. Except: If you check "Keep vertex order" :) Same game on export. Modelers don't output wastful vertices. Except you force them.




Letterworks ( ) posted Tue, 14 January 2020 at 4:19 PM

adp001, I will look at that, mostly I use Silo, I also have Carrara 8 for some testing and have never noticed those commands but I will look, thanks for the quick reply


adp001 ( ) posted Tue, 14 January 2020 at 6:14 PM

I did a search with "silo vertex order". On a DAZ page a user has a similar problem with Genesis import/export. According to another user, Silo has that option to preserve vertex-order.




jartz ( ) posted Wed, 15 January 2020 at 1:39 PM

I would like to try your script. Should I use the text created on the first page? Is there an updated one?

____________________________________________________________________________________________________________________________

Asus N50-600 - Intel Core i5-8400 CPU @ 2.80GHz · Windows 10 Home/11 upgrade 64-bit · 16GB DDR4 RAM · 1TB SSD and 1TB HDD; Graphics: NVIDIA Geforce GTX 1060 - 6GB GDDR5 VRAM; Software: Poser Pro 11x


adp001 ( ) posted Thu, 16 January 2020 at 12:17 AM

jartz posted at 7:13AM Thu, 16 January 2020 - #4376720

I would like to try your script. Should I use the text created on the first page? Is there an updated one?

Yes, you can find the last version on my website as a zip-file (see my footer). Posting here makes problems because the Rendo-Editor kills some parts from posted sourcecode.




jartz ( ) posted Thu, 16 January 2020 at 1:47 AM

Okay under the Pose Modeler bridge. Great, I'll check it out.

____________________________________________________________________________________________________________________________

Asus N50-600 - Intel Core i5-8400 CPU @ 2.80GHz · Windows 10 Home/11 upgrade 64-bit · 16GB DDR4 RAM · 1TB SSD and 1TB HDD; Graphics: NVIDIA Geforce GTX 1060 - 6GB GDDR5 VRAM; Software: Poser Pro 11x


adp001 ( ) posted Sat, 18 January 2020 at 12:48 PM · edited Sat, 18 January 2020 at 12:48 PM

Letterworks posted at 7:46PM Sat, 18 January 2020 - #4376631

I have to say I'm extremely excite by this script a way import and export with reverse deformation is sorely needed in poser.I down loaded your scripts and tried for over an hour to get them to work. I tried to exprt with groups, I tried to export with the default settings I tried several figures and modelling tools. I tried to export to a number of newly created folders, and old working folders. However every time I tried to import a morph I got an error saying (to paraphrase) that the exported vertices number didn't match the import number.

Problem solved. New version on my website: PoMo18012020.zip




adp001 ( ) posted Tue, 21 January 2020 at 7:06 AM

adp001 posted at 2:05PM Tue, 21 January 2020 - #4377063

Letterworks posted at 7:46PM Sat, 18 January 2020 - #4376631

I have to say I'm extremely excite by this script a way import and export with reverse deformation is sorely needed in poser.I down loaded your scripts and tried for over an hour to get them to work. I tried to exprt with groups, I tried to export with the default settings I tried several figures and modelling tools. I tried to export to a number of newly created folders, and old working folders. However every time I tried to import a morph I got an error saying (to paraphrase) that the exported vertices number didn't match the import number.

Problem solved. New version on my website: PoMo18012020.zip

Quoting myself:

This version should only be used if your modeler has problems with the standard version.




false1 ( ) posted Tue, 04 February 2020 at 11:36 AM · edited Tue, 04 February 2020 at 11:38 AM

Screen Shot 2020-02-04 at 11.39.12 AM.png

I got this to work on my Mac but not without poking and prodding. I'm using the wx3 version of your script. The buttons act kinda funny. Sometimes I have to click the light gray area AROUND the button rather than the button itself. Also I have to click "use fileselection" when exporting but click it off when importing. When importing it will give me an error that includes "file not found" if I navigate to the folder and click the modified file itself. If I tick off the checkbox it will find the file to be imported but only if it's named exactly like the exported file with _mod at the end. Maybe the wx4 would work better. I couldn't get that one to work but I understand the quirks a bit better now.

I've used it with LaFemme, but also with an older, heavily morphed, Chip character. So it's actually quite amazing that this is possible at all. Thanks for working on this

________________________________

My DeviantArt Gallery

My Website


adp001 ( ) posted Tue, 04 February 2020 at 12:52 PM

I know there is a problem with OSX. Apple does not use standards while handle UI-buttons. And the old wxPython version in Poser has no workaround. I can't work on a fix, because I use Linux and Win, not OSX. So I'm not able to do something.

But the script is more a prof of concept anyway. So someone with OSX may do an interface.




  • 1
  • 2

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.