Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building a mirror table takes WAY too long #483

Open
tbttfox opened this issue Feb 5, 2025 · 2 comments
Open

Building a mirror table takes WAY too long #483

tbttfox opened this issue Feb 5, 2025 · 2 comments

Comments

@tbttfox
Copy link

tbttfox commented Feb 5, 2025

There are 1148 controls on my rig, and it takes almost 45 seconds to build the mirror table

It looks like you're creating and deleting 4 helper transform nodes for every unmirrored control.
One here
https://github.com/krathjen/studiolibrary/blob/main/src/mutils/mirrortable.py#L525
And one for each of the 3 axisWorldPosition calls

And 6 helper transform nodes for every mirrored control. Two for every call to isAxisMirrored
https://github.com/krathjen/studiolibrary/blob/main/src/mutils/mirrortable.py#L505

For the mirrored controls, it looks like you're getting the worldspace pivot, then the worldspace axes, and subtracting points to get the worldspace axis vectors... But the result of that vector subtraction will just be one of the rows of the worldspace matrix.
Your isAxisMirrored function could be boiled down to this:

mp = mirrorPlane

# find the index of the first 1 ... you could just pass 0, 1, or 2 instead of the axis, and you wouldn't need this
axisIndex = axis.index(max(axis))

start = 4 * axisIndex
srcWmat = maya.cmds.xform(srcObj, query=True, worldSpace=True, matrix=True)
v1 = srcWmat[start: start + 3]
v1 = [m*v for m, v in zip(mp, v1)]

dstWmat = maya.cmds.xform(dstObj, query=True, worldSpace=True, matrix=True)
v2 = dstWmat[start : start + 3]
d = sum(p*q for p, q in zip(v1, v2))

if d >= 0.0:
    return False
return True

For the unmirrored controls, it looks like what you're doing is getting the axis directions of the control.
Same deal. You can get those axis directions directly from the worldspace matrix.
And then you convert those values to string for some reason? You could simplify that with [round(i, 3) for i in t1]... Actually, why are those values being rounded at all? I feel like those could just be left alone.
So I think you could replace MirrorTable._calculateMirrorAxis with this:

result = [1, 1, 1]
wsmat = maya.cmds.xform(obj, query=True, matrix=True, worldSpace=True)
if mirrorPlane == MirrorPlane.YZ:  # [-1, 1, 1]:
    offset = 0
elif mirrorPlane == MirrorPlane.XZ:  # [1, -1, 1]:
    offset = 1
elif mirrorPlane == MirrorPlane.XY:  # [1, 1, -1]:
    offset = 2
v = [wsmat[0 + offset], wsmat[4 + offset], wsmat[8 + offset]]
i = MirrorTable.maxIndex(v)
result[i] = -1
return result

With these changes, it goes from 45 seconds down to less than 1 for me.
I may be missing some nuance somewhere with pivots and rounding (hence why I'm writing an issue, and not a PR), but this would be a welcome change.

@tbttfox
Copy link
Author

tbttfox commented Feb 5, 2025

So, I got it building a 1:1 match of the mirror table down to 0.07 seconds.
The trick to split that last second was getting all the world matrices in one call using cmds.getAttr on the .worldMatrix[0] plugs.

After some more consolidation and simplification, I got to this

from maya import cmds
from mutils.mirrortable import MirrorTable, MirrorPlane

def batchBuildMirrorTableData(ctrls, mirrorPlane, leftSide, rightSide):
    """Batch build the mirror table object data

    Arguments:
        ctrls (list[str]): The list of control objects
        mirrorPlane (MirrorPlane): The plane to build the mirror table for
        leftSide (str): The wildcard string matching the left side rig controls
        rightSide (str): The wildcard string matching the right side rig controls

    Returns:
        dict: The MirrorTable object dictionary
    """
    mp = mirrorPlane
    mms = [i + '.worldMatrix[0]' for i in ctrls]
    wss = cmds.getAttr(mms)
    if len(mms * 16) != len(wss):
        raise ValueError("Some passed control objects aren't transformable")

    # Build a dictionary mapping the object name to the worldspace matrix
    allmats = {obj.split('.')[0]: wss[i * 16: (i + 1) * 16] for i, obj in enumerate(mms)}

    if mp == MirrorPlane.YZ:
        offset = 0
    elif mp == MirrorPlane.XZ:
        offset = 1
    elif mp == MirrorPlane.XY:
        offset = 2
    else:
        raise ValueError("Unknown Mirror Plane")

    objects = {}
    for srcObj in ctrls:
        dstObj = MirrorTable._mirrorObject(srcObj, leftSide, rightSide)
        result = [1, 1, 1]
        if dstObj is None:
            wsmat = allmats[srcObj]
            # get the absolute values of a vertical column of the matrix
            v = [abs(n) for n in wsmat[offset: 12: 4]]
            result[v.index(max(v))] = -1
        else:
            for axisIndex in range(3):
                start = 4 * axisIndex
                v1 = allmats[srcObj][start: start + 3]
                v2 = allmats[dstObj][start: start + 3]
                d = sum(m*p*q for m, p, q in zip(mp, v1, v2))
                if d < 0.0:
                    result[axisIndex] = -1
        objects[srcObj] = {'mirrorAxis': result}
    return objects


ctrls = cmds.ls('Ctrl_*', type='transform')
mirrorPlane = MirrorPlane.YZ
leftSide = "Ctrl_L"
rightSide = "Ctrl_R"
batchBuildMirrorTableData(ctrls, mirrorPlane, leftSide, rightSide)

@krathjen
Copy link
Owner

krathjen commented Feb 7, 2025

Hey Tyler, thanks for sharing! I'll take a closer look after we finish testing a major update we’re currently working on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants