csaez / rigicon

Curve/icon library for Softimage.
GNU General Public License v3.0
3 stars 3 forks source link

Maya Port #1

Open tokejepsen opened 10 years ago

tokejepsen commented 10 years ago

As far as I can see this is a matter of getting the Icon class ported, then the rest should kinda fall into place. There are a couple of xsi specific stuff, where the main one being the class inheritance 'SIWrapper'. Question is whether doing a complete copy class or find a solution that'll support both xsi and maya?

csaez commented 10 years ago

Hi Toke, I would create a maya package in wishlib.ma mirroring the softimage stuff and then just switching the imports as needed (perhaps I'll do something similar to solve the new pyqt/pyside dependencies in naming). Anyway, I don't want to drag you down on this, so if you just want to get up and running in maya don't worry about cross compatibility for now and do your changes in a new branch ("maya port" or something descriptive), that gives me some extra flexibility to merge things back later. Cheers!

tokejepsen commented 10 years ago

Have u got the meta data on the nodes the user interacts with or have u got a separate meta DAT node tree?

csaez commented 10 years ago

It's on a custom property set under the node the user interact with. Softimage architecture is a bit different than maya.

tokejepsen commented 10 years ago

I was thinking of subclassing Red9 MetaClass, which would cover all meta data handling but it would make the package dependent on Red9. Would this be a problem?

csaez commented 10 years ago

I'm not familiar with red9 internals and can't anticipate how much it will change the way the system works (I'd love to keep it similar to the softimage flavor, for maintainability purposes).

But yeah, do it! your fork, your branch, your call ;-)

tokejepsen commented 10 years ago

Wondering whether the shape data could be fbx files, to be cross app compatible?

csaez commented 10 years ago

Hi Toke, What would be the benefits of fbx over a pure-data approach?

tokejepsen commented 10 years ago

I guess its just me trying to find an easy way to transfer the curves, as I currently cant figure out how to use the pure data.

csaez commented 10 years ago

I'm not a maya guy and there are probably better ways to do this, but there you go:

import maya.cmds as cmds

def create_curve(data):
    crvs = list()
    steps = data[2]
    for i in range(data[0]):
        pts = zip(*data[1][:-1])[sum(steps[:i]):sum(steps[:i + 1])]
        if data[5][i]:  # closed curve
            pts.append(pts[0])
        crvs.append(cmds.curve(degree=1, p=pts))
    return crvs

# data sample comes from rigicon/config/Main.py
data = (
    5, ((0.8367750255781012, 0.9850154250578123, 0.8367750255781012, 0.8367750255781012, 0.6848036994132266, 0.6848036994132266, 0.8367750255781012, 0.1403339738478253, 2.0557509712894571e-16, -0.1403339738478249, -0.07598566308243708, -0.0759856630824371, 0.07598566308243748, 0.0759856630824375, 0.4250041231209123, 0.49975197930551396, 0.5552946471895436, 0.589497653194905, 0.6010465949820787, 0.589497653194905, 0.5552946471895436, 0.4997519793055139, 0.4250041231209121, 0.3339235968298319, 0.23001057397909178, 0.11725837376175113, -4.294984652014647e-17, -0.1172583737617512, -0.2300105739790919, -0.33392359682983197, -0.42500412312091224, -0.49975197930551396, -0.5552946471895437, -0.5894976531949051, -0.6010465949820788, -0.5894976531949043, -0.5552946471895429, -0.49975197930551324, -0.42500412312091157, -0.33392359682983136, -0.23001057397909133, -0.11725837376175074, 3.5776505878362874e-16, 0.11725837376175147, 0.23001057397909203, 0.3339235968298321, 0.1403339738478253, 2.0557509712894571e-16, -0.1403339738478249, -0.07598566308243708, -0.0759856630824371, 0.07598566308243748, 0.0759856630824375, -0.836775025578101, -0.985015425057812, -0.836775025578101, -0.836775025578101, -0.6848036994132264, -0.6848036994132264, -0.836775025578101), (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), (-0.14033397384782506, 5.019713570867176e-17, 0.14033397384782517, 0.07598566308243734, 0.07598566308243734, -0.07598566308243723, -0.07598566308243723, -0.8367750255781011, -0.9850154250578121, -0.8367750255781011, -0.8367750255781011, -0.6848036994132265, -0.6848036994132265, -0.8367750255781011, -0.42500412312091185, -0.3339235968298317, -0.23001057397909158, -0.11725837376175093, 1.9782894292585206e-16, 0.11725837376175134, 0.230010573979092, 0.3339235968298321, 0.4250041231209123, 0.499751979305514, 0.5552946471895437, 0.589497653194905,
        0.6010465949820788, 0.589497653194905, 0.5552946471895437, 0.49975197930551396, 0.4250041231209122, 0.33392359682983197, 0.23001057397909186, 0.11725837376175118, -2.3409843159650097e-17, -0.11725837376175137, -0.23001057397909191, -0.3339235968298319, -0.42500412312091207, -0.4997519793055137, -0.5552946471895434, -0.5894976531949047, -0.6010465949820785, -0.5894976531949045, -0.5552946471895432, -0.4997519793055135, 0.8367750255781011, 0.9850154250578121, 0.8367750255781011, 0.8367750255781011, 0.6848036994132265, 0.6848036994132265, 0.8367750255781011, 0.14033397384782545, 3.719014270909097e-16, -0.14033397384782478, -0.07598566308243695, -0.07598566308243701, 0.07598566308243757, 0.07598566308243762), (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)), (7, 7, 32, 7, 7), ((0.0, 0.0, 0.0, 0.0, 0.0), (1.0, 1.0, 1.0, 1.0, 1.0), (2.0, 2.0, 2.0, 2.0, 2.0), (3.0, 3.0, 3.0, 3.0, 3.0), (4.0, 4.0, 4.0, 4.0, 4.0), (5.0, 5.0, 5.0, 5.0, 5.0), (6.0, 6.0, 6.0, 6.0, 6.0), (7.0, 7.0, 7.0, 7.0, 7.0), (None, None, 8.0, None, None), (None, None, 9.0, None, None), (None, None, 10.0, None, None), (None, None, 11.0, None, None), (None, None, 12.0, None, None), (None, None, 13.0, None, None), (None, None, 14.0, None, None), (None, None, 15.0, None, None), (None, None, 16.0, None, None), (None, None, 17.0, None, None), (None, None, 18.0, None, None), (None, None, 19.0, None, None), (None, None, 20.0, None, None), (None, None, 21.0, None, None), (None, None, 22.0, None, None), (None, None, 23.0, None, None), (None, None, 24.0, None, None), (None, None, 25.0, None, None), (None, None, 26.0, None, None), (None, None, 27.0, None, None), (None, None, 28.0, None, None), (None, None, 29.0, None, None), (None, None, 30.0, None, None), (None, None, 31.0, None, None), (None, None, 32.0, None, None)), (8, 8, 33, 8, 8), (True, True, True, True, True), (1, 1, 1, 1, 1), (1, 1, 1, 1, 1))
create_curve(data)
tokejepsen commented 10 years ago

Thats seems to work great! Cheers for the help.

csaez commented 10 years ago

Hey Toke, I just refactored a bit how shapes are stored/managed, they are now standard JSON files (python dicts) and are stored in the user home directory (C:\Users\user_name\rigicon) in order to ensure that the user has write permissions (the library module should take care of that for you).

Here is a working maya snippet:

from maya import cmds
from rigicon import library

def create_curve(data):
    xfo = cmds.createNode("transform")
    for i in range(data["Count"]):
        start = sum(data["NbControlPoints"][:i])
        end = sum(data["NbControlPoints"][:i + 1])
        pts = zip(*data["ControlPoints"][:-1])[start:end]
        if data["Closed"][i]:
            pts.append(pts[0])
        crv = cmds.curve(degree=data["Degree"][i], p=pts)
        shp = cmds.listRelatives(crv, fullPath=True, shapes=True)[0]
        cmds.parent((shp, xfo), r=True, s=True)
        cmds.delete(crv)
    return cmds.rename(xfo, data.get("Name"))

data = library.get_item("Main")
create_curve(data)

Sorry for the trouble, Cheers!

tokejepsen commented 10 years ago

Thats cool:) I guess u wanted to unify the data format and placement?

csaez commented 10 years ago

Yes, some cleanup was needed :) json files are way better suited for data storage (pure data, language-independent) and dicts are much more flexible than tuples (fixed index). It was the lazy me from the past, trying to save a couple of lines of code through storing the arrays in the right order, but not anymore! :D

tokejepsen commented 10 years ago

just to clearify, you are storing all the data you need in a single string on a node in XSI?

csaez commented 10 years ago

Yes, sometimes it is not that straight forward because softimage objects are COM objects and cannot be re-instantiated directly, but for python types I'm storing its repr version as a string in a softimage node. This is the module in charge of the whole thing: http://github.com/csaez/wishlib/blob/master/wishlib/si/siwrapper.py

tokejepsen commented 10 years ago

in the encoding and decoding on the string, you have a prefix "si:", is that to make the data unique?

csaez commented 10 years ago

It's an identifier for softimage objects. I also added a namespace string to everything in order to make data unique (self.namespace), that way I can use reserved attr names without issues (like 'Name').

tokejepsen commented 10 years ago

I was wondering whether I could get a raw string before decoding from an siwrapper instance?

also does this FreezeObj; https://github.com/csaez/rigicon/blob/master/rigicon/icon.py#L106, collapse the construction history or preserve it?

csaez commented 10 years ago

Hey Toke,

Sure, the raw string is returned by the encode function (line 68), I'm calling it on the setattr method (line 172), but you can easily subclass SIWrapper and override that method to fit your needs (perhaps pass the data to Red9).

FreezeObj collapses the construction history, I'm doing that because softimage doesn't have shape/transform in separated nodes, so I've to collapse the shape's history in order to overwrite it with data from the library.

I'm thinking on porting my stuff to maya (unfortunatelly softimage is coming to its eol), so maybe we can work together on this! I'm a complete newbie coding for maya, but rigicon looks like a nice project to get started. What do you think?

tokejepsen commented 10 years ago

yeah, I'm definitely up for working together on this. Atm when I'm going through the code, I'm trying to abstract/refactor any application specific code out so it'll be easier to port to other apps.

let me know what where you want to start

tokejepsen commented 10 years ago

also I meant raw string that get put on the Properties in XSI. Finding it difficult to visualize the dataIn and dataOut in the encode/decode.

tokejepsen commented 10 years ago

Hey Cesar.

Don't need a raw string as I finally understand what's going on in the wrapper:) Not entirely sure the storing references to object in as a string is going to work in Maya with references, but we could use a message attribute to link to objects.

csaez commented 10 years ago

Cool! I'm rewriting the qt bindings (wishlib.qt), the current implementation kinda works but is not ready yet (there are some limitations with named imports). I'll start working on the rigicon port as soon as I finish this (actually I'm using rigicon's ui to test the pyside side of the bindings within maya).

tokejepsen commented 10 years ago

One problem we are going to have is the coloring of the curves. With the default setup in Maya you have limited colors to choose from. You can alter those color slots, but that modifies the color for all curve using that slot. A possible solution is to make a custom plugin for the curves.

tokejepsen commented 10 years ago

So im looking into how to make the data unique and in XSI you have the custom property and below that you have the parameters. Im guessing this makes the traversing go something similar to; obj > 'RigIcon_Data' > Parameters? Since this is not possible in Maya, I would suggest to have a network node linked to a message attribute on the obj. The other solution, which Im not a fan of, is to have some other namespace system for the attribute names.

csaez commented 10 years ago

Yes, I think we can workaround the color issue replacing the color picker by the one maya provides (which already returns a valid color i guess), I don't know how it works internally but we can add a color_index to the metadata or just get the index from a color table (i.e. color_table.index(rgb)).

You are right in regards of how softimage parameters work, I thought we could easily replace softimage parameters by maya custom attributes, but if that is somehow problematic go ahead and implement it the other way around.

I think the metadata class should provide an in-built solution for the namespace issues, they will happen no matter how we ended up implementing the data storage (e.g. storing a reference to node outside the scope of the container itself).

I feel more confortable iterating over small chunks of working code rather than discussing every detail beforehand, it is less blocking and way more productive imho.

So please, go ahead, try new things, break stuff, experiment, have fun and send me pull requests as soon as your code is working... doesn't matter if it's not perfect (mine never is), we will get there ;-)

tokejepsen commented 10 years ago

Got a working version of the icon class, and was going to move onto the gui side while improving the code. But did you say you had a working version of the gui?

csaez commented 10 years ago

Hey Toke, that's great! Yes, I've been working on the gui and also doing some cleanup in order to get some room for the maya implementation, take a look at the maya branch.

tokejepsen commented 10 years ago

I've done a dialog for the color index, but I can't seem to figure out how to return a value and close the dialog:

import pymel.core as pm
import maya.OpenMayaUI as omui

from PySide import QtGui, QtCore
from shiboken import wrapInstance

def maya_main_window():
    main_window_ptr = omui.MQtUtil.mainWindow()
    return wrapInstance(long(main_window_ptr), QtGui.QWidget)

class Window(QtGui.QDialog):

    def __init__(self, parent=maya_main_window()):
        super(Window, self).__init__(parent)

        self.setWindowTitle('Colour Picker')
        self.setup()

    def setup(self):
        self.layout = QtGui.QGridLayout()
        self.setLayout(self.layout)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)

        for index in range(1, 31):
            btn = QtGui.QPushButton(str(index))
            palette = QtGui.QPalette(btn.palette())
            qColor = QtGui.QColor('blue')
            color = pm.colorIndex(index, q=True)

            qColor.setRgbF(color[0], color[1], color[2])
            palette.setColor(btn.backgroundRole(), qColor)
            btn.setPalette(palette)
            btn.setMaximumHeight(999)

            btn.clicked.connect(self.returnValue)

            if index < 7:
                self.layout.addWidget(btn, 0, index - 1)
            elif index < 13:
                self.layout.addWidget(btn, 1, index - 7)
            elif index < 19:
                self.layout.addWidget(btn, 2, index - 13)
            elif index < 25:
                self.layout.addWidget(btn, 3, index - 19)
            else:
                self.layout.addWidget(btn, 4, index - 25)

    def returnValue(self):
        #return int(self.sender().text())
        self.close()

win = Window()
print win.exec_()
csaez commented 10 years ago

Hey Toke, It seems like you have to use a class method, here's the gui code i did on my side:

from wishlib import anchor
from wishlib.qt import QtCore, QtGui
from pymel import core as pm

class MPalette(QtGui.QDialog):

    def __init__(self, parent=None):
        super(MPalette, self).__init__(parent)
        self._color = -1
        self.setupUi()
        for i, b in enumerate(self.buttons):
            self.setButtonColor(
                b, [x * 255 for x in pm.colorIndex(i + 1, q=True)])
            b.clicked.connect(lambda index=i + 1: self.accept(index))

    def setupUi(self):
        self.setWindowTitle("ColorPicker")
        self.gridLayout = QtGui.QGridLayout(self)
        self.buttons = list()
        for i in range(31):
            btn = QtGui.QPushButton(self)
            sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum,
                                           QtGui.QSizePolicy.Maximum)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(
                btn.sizePolicy().hasHeightForWidth())
            btn.setSizePolicy(sizePolicy)
            btn.setMaximumSize(QtCore.QSize(25, 25))
            btn.setText(str(i+1))
            self.gridLayout.addWidget(btn, int(i / 8), i % 8, 1, 1)
            self.buttons.append(btn)

    def setButtonColor(self, button, color):
        s = "background-color: rgb({0}, {1}, {2});".format(*color)
        button.setStyleSheet(s)

    def accept(self, index=None):
        self._color = index
        super(MPalette, self).accept()

    @classmethod
    def getColor(cls, parent):
        palette = cls(parent)
        palette.exec_()
        return palette._color

print MPalette.getColor(parent=anchor())
tokejepsen commented 10 years ago

Awesome:) I'll need to read up on class methods now.

tokejepsen commented 10 years ago

with the last pull request we have a working version of the rigicon. There are still fixes and testing, but it should work with riglab? ill start having a look at riglab from now on