We've Moved


The blog has been retired - it's up for legacy reasons, but these days I'm blogging at blog.theodox.com. All of the content from this site has been replicated there, and that's where all of the new content will be posted. The new feed is here . I'm experimenting with crossposting from the live site, but if you want to keep up to date use blog.theodox.com or just theodox.com
Showing posts with label bugs. Show all posts
Showing posts with label bugs. Show all posts

Tuesday, October 27, 2015

Grrrr..... Maya!!!

Some kinds of pain are just occasional: you stub your toe or bump your head, ouch, and then its over. Other kinds of pain aren't as sharp or as sudden... but they're chronic.  That persistent twinge in your lower back may not hurt as much as a twisted ankle - but it's going to be there forever (at least unless you get in to power Yoga, or so my wife claims).
 Maya is old enough to have a few of those chronic pains, and I just ran in to one which -- once we debugged it and figured it out -- I realized has been a constant irritant for at least the last decade and if my creaky old memory does not lie was a distinct pain in the butt as long ago as 2002. In another context I might even have been able to diagnose it but instead we spent a ton of time and energy working around an unexpected behavior which is, in fact, purely standard Maya. It's stupid Maya, but it's standard too.  Maya, alas, is double plus ungood about mixing per-face and per-object material assignments. So, I figured I'd document this here for future sufferers: it might not ease the pain much, but at least you'll know you're not crazy.
 The basic problem is that assigning materials to faces and to objects use slightly different mechanisms. If you check your hypergraph you'll see that per-face assignments connect to their shadingGroup nodes through the compInstObjectGroups[] attribute while object-level assigmemts go through the similar-but-not-identical instObjectGroups attribute (if you're looking for these in the docs, the component cone is inherited from the geometryShape class and the object version comes from dagNode).
 As long as you're working with one object at a time this isn't a problem. However, if you're duplicating or copy-pasting nodes, there's a gotcha:  If you ever try to merge meshes which have a mix of per-face and per-object assignments, Maya will magically "remember" old per-face assigments in the combined mesh.  If you're a masochist, here's the repro:


  1. create a object, give it a couple of different materials on different faces
  2. duplicate it a couple of times
  3. assign a per-object material to the duplicates, overriding the original per-face assignments
  4. combine all the meshes.
  5. Et voƮla! The cloned meshes revert to their original assignments


What appears to happen is that those compInstObjectGroups connections are driven by hidden groupID nodes which don't get deleted when the per-face assignments are overridden by the per-object ones in step (3) .  They stick around even though they aren't being used, and when the mesh is combined they step right back into their original roles.

If you're doing this interactively it's an annoyance. If you're got tools that do things like auto-combine meshes to cut down on transform load in your game.... well, it's a source of some surprising bugs and equally surprising bursts of profanity.  But at least it'ss predictable.

The workaround:  Before doing any mesh combination, delete the history and something harmless to this history of the meshes you're about to combine. (I use a triangulate step, since this happens only at export time) . That kills the rogue <code>groupID</code> nodes and keeps the combined mesh looking the way you intended.

Sheesh. What a way to make a living.

Thursday, September 4, 2014

2015 Bug watch: ls()

For people switching to Maya 2015 here's an irritating bug in the 2015 Maya python layer.

In all Mayas before 2015 (as far as I can check, anyway), calling cmds.ls() with a string that was not a valid Maya object name was allowed. You could for example, call

cmds.ls("@")

and you'd get back an empty array. In 2015, however, it looks like they have changed the way maya.cmds is converting the string into a dag node reference; it you call the same thing in 2015 you'll get this instead:


# Error: Syntax error: unexpected end @ at position 1 while parsing:
# ; ; @
# ; ; ^
# : @
# Traceback (most recent call last):
# ; File "", line 1, in 
# RuntimeError: Syntax error: unexpected end @ at position 1 while parsing:
# ; ; @
# ; ; ^
# : @ #


This is a bit more serious than it seems at first glance, because ls is such a common command. Any ls operation which includes a string that starts with anything other than a letter or a number with raise an exception, so there are a lot of places which used to just chug along silently that are going to start raising exceptions.

My workaround is to patch cmds.ls on startup so that it safely renames any bad string before passing them to Maya.  I do this in my bootstrap routine so I don't have to chase down every occurrence of ls anywhere in my code  (1,001 of them, or so PyCharm tells me...).


import re
import maya.cmds as cmds

VALID_OBJECT = re.compile("""^[|]?([^a-zA-Z_\?\*\:\|])|([^a-zA-Z0-9_\?\*\:\|\.\[\]])""")
as_u = lambda p: p if not hasattr(p, 'addPrefix') else unicode(p)

def safe_ls(*args, **kwargs):
    '''
    Patches maya 2015 cmds.ls so that it does not except when passed illegal name characters.
    '''
    if not len(args):
        return _BASE_LS(**kwargs)
    if len(args) == 1 and hasattr(args[0], '__iter__'):
       args = args[0]
    test_args = [VALID_OBJECT.sub('_', as_u(i)) for i in args]
    return _BASE_LS(test_args, **kwargs)gs)

cmds.ls = safe_ls

This makes sure that existing code works as it did before and I don't think it will break anything, since the invalid character strings were never going to be ls'ed into anything anyway.  Ordinarily I'm not a big fan of magical behind the scenes fixes but this is a pretty serious change to the behavior of ls which doesn't seem like an intentional upgrade so much as an oversight on Autodesk's part. So, at least until the old behavior comes back I'm gonna try it.

Update: Hat tip to +Robert White for pointing out that the original regex I posted did not handle namespaces. Code above includes the fix.  Never would have figured it out without Pythex!

Update 2: Updated the safe_ls procedure to handle more of the allowable syntax in older mayas


Friday, June 20, 2014

From the annals of bug subtlety

... comes an object lesson in why it's nice to have a debugger.

I'm porting a biggish python codebase to support multiple OSs  and maya versions.  As I move things around I try to use the opportunity to shore up test coverage.  And it feels like the most boring chore imaginable, until something like this crops up.

I've got an exporter framework that I share between projects and files, and when I was moving from 2011- only to multiple version.  It's important code, so it's tested - but the test is really dumb: it creates a test scene, imports the test framework, and calls one function. But running the tests in 2014 never works - even though I can manually execute the exact steps in a regular copy of maya and all is well.

So I threw it under the debugger -- PyCharm FTW!  -- and started stepping through. No dice, everything seemed OK but still the test failed: it could not find my test objects. Finally, in desperation, I started stepping though the test and issuing an ls() after every step... and I found that the break wasn't caused by running code - it was caused by importing my module.  I didn't call it - just imported it.  WTF?

It turns out that importing PyMel was wiping my test scene in 2014! The tests all run under maya.standalone, and the bug only shows up there, which is why just doing it by hand in maya wasn't showing the same symptoms.

Here's my repro case:

import maya.cmds as cmds
cmds.polyCube()
# [u'pCube1', u'polyCube1']
cmds.ls(type='transform')
# [u'front', u'pCube1', u'persp', u'side', u'top']
import pymel.core as pm
cmds.ls(type='transform')
# [u'front', u'persp', u'side', u'top']

This is a 100% repro in maya.standalone - but not in GUI maya, where the bug does not occur.Is this true for everybody else ?   The workaround is to import pymel earlier so that the destruction doesn't affect anything important. 

But... ouch!