Maya Eyedroper tool

So one of the artists mentioned that having an eyedropper would be good for a vertex color tool I’m making a prototype for at work. Now I know that the eyedropper tool works in the Paint Color Vertex Tool but from the looks of it, I cant seem to capture the value or find a command to call it up. Any thoughts? been slowly reading through the various mel scripts seeing where its called and think I pinpoint it to artAttrColorPerVertexCallback where a command seems to be concatenated. ($artCommand + " -e -pickValue currentCtx") trying to figure out what command that is haha.

Looking into it a bit more and seems like pymel has this documented but maya docs theres nothing. Support and Problem Solving | Autodesk Support

I had a quick look into this in case you haven’t solved it already. I got something ugly but half way working.

It’s in MEL because it’s mostly just hacked together bits of artAttrColorPerVertexValues.mel and artAttrColorPerVertexToolScript.mel. As you were saying it appears that a lot of the artAttrPaintVertexCtx flags are not documented.


global proc nm_colourPickedCB(string $ctx)
{
    // Most of this logic is stolen from artAttrColorPerVertexValues.mel
    
    float $rgba[] = `artAttrPaintVertexCtx -q -colorRGBAValue $ctx`;
    float $alpha[] = `artAttrPaintVertexCtx -q -colorAlphaValue $ctx`;
    float $rgb[] = `artAttrPaintVertexCtx -q -colorRGBValue $ctx`;
    // Print the picked value
    if (size($rgba) == 4)
        print ("// Picked RGBA colour = r:"+$rgba[0]+", g:"+$rgba[1]+", b:"+$rgba[2]+", a:"+$rgba[3]+"
");
    else if (size($rgb) == 3)
        print ("// Picked RGB colour = r:"+$rgb[0]+", g:"+$rgb[1]+", b:"+$rgb[2]+"
");
    else if (size($alpha) == 1)
        print ("// Picked ALPHA colour = a:"+$alpha[0]+"
");
    
    // ...Update GUI with picked value
    // exit the artAttrPaintVertexCtx
    // restore the original tool etc etc.
}
// Set up the tool context
artAttrColorPerVertexToolScript(4);
// Initialise the UI for current selection
// (I'm just using this to ensure the RGB/RGBA/A mode is set to match the object)
artAttrColorPerVertexToolScript(3);
// Get the name of the current context
string $ctx = currentCtx();
// Create a callback fired when the tool changes (probably not ideal)
string $cmd = ("nm_colourPickedCB(\""+$ctx+"\");");
scriptJob -runOnce true -e "ToolChanged" $cmd;
// Switch to the eyedropper mode
artAttrPaintVertexCtx -e -pickValue $ctx;

Unfortunatly the ToolChanged event doesn’t get fired when you select something with the eyedropper so you need to switch entirely out of the artAttrPaintVertexCtx before it will print the result. I also tried setting up a callback via the -afterStrokeCmd flag of the artAttrPaintVertexCtx but it never got called as far as I could see (I may have just been doing something wrong though).

One potential solution to call something after the eyedropper is done would be to modify the artisanBrushValues() proc directly and add your callback in there since that function gets called when you eyedropper something.

Are they wanting to pick the color from the closest vertex or just any color on the screen? if the latter, try:

http://download.autodesk.com/global/docs/maya2013/en_us/CommandsPython/grabColor.html

Hey thanks Warheart. Yeah I actually accidentally stumbled on grabColor as TheMaxx pointed out and it looked the easiest without adding scriptjobs and whatnot into the mix. Thanks again for the help :D!

Well goddam it. GrabColor doesnt seem to output the right color values. There’s always some discrepancy. Tried looking into draggerCtx in hopes of using the mouse position but had no luck with that as I got everything came nothing clsoe to the vertex world space pos. Anyone know if I can do a simple raycast in mel/python or is that something more API. Thinking of doing a raycast and then trying to determine the closest vertice and quering its vertex color. If not, well guess its back to hacking through scriptjobs :stuck_out_tongue:

So i’ve been looking into raycasts and got almost everything working. I got the faces being clicked on registering correctly. Trying to figure out how to convert the hit point from an Mpoint array to something maya commands can use. Also trying to get an elegant way to get the mouse position. Dragger context has been giving me error upon error and seems like I need to figure out how to run it once and delete the context. First foray into Maya API haha though quite fun.

Mind posting the code you’re working with so far? I’ve never done anything with raycasts and it sounds interesting!

sure capper I don’t mind. A lot of it is hacked from stuff I found from various places though Im starting to understand it slowly. Apparently just printing hit_pnt printed out the type but doing hit_pnt.x prints the value haha. Still gotta get the selection and drag context working correctly so thats hard coded in right now. Though can now do a simple distance check against the face vertices which is simple. Also I should comment :smiley: I really need to check out Maya’s API more. Sounds like a time to get Chad’s dvd’s :wink:


def selectByColorValue(*args):
    print("SELECTING BY COLOR 2")
    
    '''
    if(cmds.draggerContext('sampleContext', exists=True)):
        cmds.deleteUI('sampleContext')
    cmds.draggerContext( 'sampleContext', pressCommand='SampleContextPress()', cursor='hand', projection="world" );
    cmds.setToolTo('sampleContext')
    '''
    
    pressPosition = [880.0, 356.0, 0.0]
    
    # grabbing current 3d view 
    active_view = mui.M3dView.active3dView()
    
    # Screen position of mouse
    x = int(pressPosition[0])
    y = int(pressPosition[1])
    print x,y
    
    #making my ray source and direction
    ray_source = om.MPoint()
    ray_direction = om.MVector()
    
    #Converting to world
    active_view.viewToWorld(x,y, ray_source, ray_direction)
    
    #get mesh dag path
    sel = om.MSelectionList()
    sel.add("pSphereShape1")
    mesh_dag = om.MDagPath()
    sel.getDagPath(0,mesh_dag)
				
    #cast the ray at the mesh and find out if and where it hits
    hit_pnt, face_idx = closest_mesh_intersection(mesh_dag, ray_source, ray_direction)
    rayHitPosition = [(hit_pnt.x),(hit_pnt.y),(hit_pnt.z)]
    
    print "HIT AT: " + str(rayHitPosition)
    print "FACE AT: " + str(face_idx)
    
def closest_mesh_intersection(mesh_dag, ray_source, ray_direction):
    meshFn = om.MFnMesh(mesh_dag)
    
    #Making my Hit Point
    hit_point = om.MFloatPoint()   
   
    ray_source_float = om.MFloatPoint(ray_source.x, ray_source.y, ray_source.z)
    ray_direction_float = om.MFloatVector(ray_direction.x, ray_direction.y, ray_direction.z)
    
    #Pointer nonsense. 
    face_idx_util = om.MScriptUtil()
    face_idx_util.createFromInt(-1)
    face_int_ptr = face_idx_util.asIntPtr()
    
    #Args for closest Interestion
    meshFn.closestIntersection(
            ray_source_float,#const MFloatPoint & 	raySource,
            ray_direction_float,#const MFloatVector & 	rayDirection,
            None, #const MIntArray * 	faceIds,
            None, #const MIntArray * 	triIds,
            False, #bool 	idsSorted,
            om.MSpace().kWorld, #MSpace::Space 	space,
            9999, #float 	maxParam,
            False, #bool 	testBothDirections,
            None, #MMeshIsectAccelParams * 	accelParams,
            hit_point, #MFloatPoint & 	hitPoint,
            None, #float * 	hitRayParam,
            face_int_ptr, #int * 	hitFace,
            None, #int * 	hitTriangle,
            None, #float * 	hitBary1,
            None, #float * 	hitBary2,
            .000001 #float 	tolerance = 1e-6,
            )
    
    #Again more pointer nonsense. Need to look into this more.
    face_idx = face_idx_util.getInt(face_int_ptr)

    return (hit_point, face_idx)

Anyone have any idea why SelectFromScreen might not work? I’m pretty sure its coming from the mouse position I’m providing as I used Nathan Horne’s example and just plugging in the values gives me an empty list but giving a mouse position a bit too the left or right works. Also when really zoomed in on the object, get no hits. Any other way to get an object name without selecting it under the mouse? I saw claydough’s example but it works of pre-selection hilighting.

Here’s some updated code:


def contextPress():
    pressPosition = cmds.draggerContext( 'sampleContext', query=True, anchorPoint=True)	
    print ("Press: " + str(pressPosition))
    # grabbing current 3d view 
    active_view = mui.M3dView.active3dView()
    
    # Screen position of mouse
    x = int(pressPosition[0])
    y = int(pressPosition[1])
    print x,y
    
    #making my ray source and direction
    ray_source = om.MPoint()
    ray_direction = om.MVector()
    
    #Converting to world
    active_view.viewToWorld(x,y, ray_source, ray_direction)
    
    '''
    #get mesh dag path
    sel = om.MSelectionList()
    om.MGlobal.getActiveSelectionList(sel)
    '''
    
    #Select from screen    
    om.MGlobal.selectFromScreen(x,y, om.MGlobal.kReplaceList)
    objects = om.MSelectionList()
    om.MGlobal.getActiveSelectionList(objects)
    print objects.length()
    
    #restore selection if there was a selection
    #om.MGlobal.setActiveSelectionList(sel, om.MGlobal.kReplaceList)
    
    #Convert from MSelectionList object to string
    fromScreen = []
    objects.getSelectionStrings(fromScreen)
    #sel.add(fromScreen[0])

    mesh_dag = om.MDagPath()
    objects.getDagPath(0,mesh_dag)
				
    #cast the ray at the mesh and find out if and where it hits
    hit_pnt, face_idx = closest_mesh_intersection(mesh_dag, ray_source, ray_direction)
    rayHitPosition = [(hit_pnt.x),(hit_pnt.y),(hit_pnt.z)]
    
    print "HIT AT: " + str(rayHitPosition)
    print "FACE AT: " + str(face_idx)
    
    cmds.setToolTo('moveSuperContext')
    print("CHANGED TOOL")
    
    
def selectByColorValue(*args):
    print("SELECTING BY COLOR 2")
    if(cmds.draggerContext('sampleContext', exists=True)):
        cmds.deleteUI('sampleContext')
    cmds.draggerContext( 'sampleContext', pressCommand = "contextPress()", cursor='hand', projection = "s")
    cmds.setToolTo('sampleContext')

def closest_mesh_intersection(mesh_dag, ray_source, ray_direction):
    meshFn = om.MFnMesh(mesh_dag)
    
    #Making my Hit Point
    hit_point = om.MFloatPoint()   
   
    ray_source_float = om.MFloatPoint(ray_source.x, ray_source.y, ray_source.z)
    ray_direction_float = om.MFloatVector(ray_direction.x, ray_direction.y, ray_direction.z)
    
    #Pointer nonsense. 
    face_idx_util = om.MScriptUtil()
    face_idx_util.createFromInt(-1)
    face_int_ptr = face_idx_util.asIntPtr()
    
    #Args for closest Interestion
    meshFn.closestIntersection(
            ray_source_float,#const MFloatPoint & 	raySource,
            ray_direction_float,#const MFloatVector & 	rayDirection,
            None, #const MIntArray * 	faceIds,
            None, #const MIntArray * 	triIds,
            False, #bool 	idsSorted,
            om.MSpace().kWorld, #MSpace::Space 	space,
            9999, #float 	maxParam,
            False, #bool 	testBothDirections,
            None, #MMeshIsectAccelParams * 	accelParams,
            hit_point, #MFloatPoint & 	hitPoint,
            None, #float * 	hitRayParam,
            face_int_ptr, #int * 	hitFace,
            None, #int * 	hitTriangle,
            None, #float * 	hitBary1,
            None, #float * 	hitBary2,
            .000001 #float 	tolerance = 1e-6,
            )
    
    #Again more pointer nonsense. Need to look into this more.
    face_idx = face_idx_util.getInt(face_int_ptr)

    return (hit_point, face_idx)

EDIT: Got it to work. Seems like looking at the Maya Api docs and referencing the marquee tool, it was probably using kWireframeSelectMethod instead of kSurfaceSelectMethod. This maya api stuff is quite fun haha.