Getting geo data from intersections on the road to making cleaner wraps…


So the rabbit trail from over the weekend proved to not be the answer to my original problem as hoped. Namely I was still getting geo movement from far off regions when baking blendshapes to non-similar geo (think a sphere placed on a body).

As such, my next plan to resolve this was to create a specific conforming geo piece to wrap to then wrap my nonconforming object to. To do this, I need a way to  find the geo closest to my geo I wanted to transfer the blendshapes too and so wrote a new function for this that would:

  • Search target geo against a source geo piece to find geo from each target within the source by two methods:
    • boundingBox.contains – by vert
    • rayCasting  – by the compass vectors to make sure it is completely within the sourceObject
  • Translate that data to verts,edges, faces
  • Have a method to expand that data:
    • selection traversing
    • softSelection radius

Lessons learned:

  • bounding box checking is much much faster so use that mode unless you just have to have a more precise idea of what verts are inside the mesh.
  • Not a lot of specific info I could find on some of these concepts and so wanted to share to save someone else time and deadends

Here’s part one of this issue which is housed at cgm.core.lib.geo_Utils.get_contained. There are too many dependencies to include them all but you can get this gist from the code.

def get_contained(sourceObj= None, targets = None, mode = 0, returnMode = 0, selectReturn = True,
                  expandBy = None, expandAmount = 0):
    """
    Method for checking targets componeents or entirty are within a source object.

    :parameters:
        sourceObj(str): Object to check against
        targets(list): list of objects to check
        mode(int):search by
           0: rayCast interior
           1: bounding box -- THIS IS MUCH FASTER
        returnMode(int):Data to return 
           0:obj
           1:face
           2:edge
           3:verts/cv
        selectReturn(bool): whether to select the return or not
        expandBy(str): None
                       expandSelection: uses polyTraverse to grow selection
                       softSelect: use softSelection with linear falloff by the expandAmount Distance
        expandAmount(float/int): amount to expand

    :returns
        list items matching conditions
        
    :acknowledgements
    http://forums.cgsociety.org/archive/index.php?t-904223.html
    http://maya-tricks.blogspot.com/2009/04/python.html -- idea of raycasting to resolve if in interior of object
    http://forums.cgsociety.org/archive/index.php?t-1065459.html
    
    :TODO
    Only works with mesh currently
    """      
    __l_returnModes = ['obj/transform','face','edge/span','vert/cv']
    __l_modes = ['raycast interior','bounding box']
    __l_expandBy = [None, 'expandSelection','softSelect']
    result = []
    
    _mode = cgmValid.valueArg(mode, inRange=[0,1], noneValid=False, calledFrom = 'get_contained')    
    log.info("mode: {0}".format(_mode))
    
    _returnMode = cgmValid.valueArg(returnMode, inRange=[0,3], noneValid=False, calledFrom = 'get_contained')
    log.info("returnMode: {0}".format(_returnMode))
    
    _selectReturn = cgmValid.boolArg(selectReturn, calledFrom='get_contained')
    
    _expandBy = None
    if expandBy is not None:
        if expandBy in __l_expandBy:
            _expandBy = expandBy
        else:
            raise ValueError,"'{0}' expandBy arg not found in : {1}".format(expandBy, __l_expandBy)
        
    #Get our objects if we don't have them
    if sourceObj is None and targets is None:
        _sel = mc.ls(sl=True)
        sourceObj = _sel[0]
        targets = _sel[1:]

    targets = cgmValid.listArg(targets)#...Validate our targets as a list
    l_targetCounts = []
    
    for o in targets:
        _d = cgmValid.MeshDict(o)
        l_targetCounts.append(_d['pointCountPerShape'][0])
        
    sel = OM.MSelectionList()#..make selection list
    for i,o in enumerate([sourceObj] + targets):
        try:
            sel.add(o)#...add objs
        except Exception,err:   
            raise Exception,"{0} fail. {1}".format(o,err)

    _dagPath = OM.MDagPath()#...mesh path holder
    matching = []#...our match holder
    _l_found = OM.MSelectionList()#...new list for found matches
    
    guiFactory.doProgressWindow(winName='get_contained', 
                                statusMessage='Progress...', 
                                startingProgress=1, 
                                interruptableState=True)
    if _mode is 1:
        log.info('bounding box mode...')        
        try:#Get our source bb info
            sel.getDagPath(0,_dagPath)
            fnMesh_source = OM.MFnMesh(_dagPath)
            matrix_source = OM.MMatrix(_dagPath.inclusiveMatrix())    
            
            bb_source = fnMesh_source.boundingBox()
            bb_source.transformUsing(matrix_source) 
                
            sel.remove(0)#...remove the source
            
        except Exception,err:
            raise Exception,"Source validation fail | {0}".format(err)
       
        for i in xrange(sel.length()):
            _tar = targets[i]
            _vtxCount = l_targetCounts[i]
            log.info("Checking '{0}'".format(_tar))
            
            guiFactory.doUpdateProgressWindow("Checking {0}".format(_tar), i, 
                                              sel.length(), 
                                              reportItem=False)
            
            sel.getDagPath(i, _dagPath)#...get the target 
            
            fnMesh_target = OM.MFnMesh(_dagPath)#...get the FnMesh for the target
            fnMesh_target.setObject(_dagPath)
            pArray_target = OM.MPointArray()#...data array  
            fnMesh_target.getPoints(pArray_target)#...get comparing data
            
            matrix_target = OM.MMatrix(_dagPath.inclusiveMatrix())    
            fnMesh_target = OM.MFnMesh(_dagPath)
            bb_target = fnMesh_source.boundingBox()
            bb_target.transformUsing(matrix_target)         
            
            if bb_source.contains( cgmOM.Point(mc.xform(_tar, q=True, ws=True, t=True))) or bb_source.intersects(bb_target):
                if _returnMode is 0:#...object
                    _l_found.add(_dagPath)
                    continue
            iter = OM.MItGeometry(_dagPath)
            while not iter.isDone():
                vert = iter.position(OM.MSpace.kWorld)
                if bb_source.contains(vert):
                    _l_found.add(_dagPath, iter.currentItem())
                iter.next()                
                
    elif _mode is 0:
        log.info('Ray cast Mode...')
        sel.remove(0)#...remove the source        
        for i in xrange(sel.length()):
            _tar = targets[i]
            _vtxCount = l_targetCounts[i]            
            log.info("Checking '{0}'".format(_tar))
            sel.getDagPath(i, _dagPath)#...get the target 
            fnMesh_target = OM.MFnMesh(_dagPath)#...get the FnMesh for the target
            fnMesh_target.setObject(_dagPath)
            
            guiFactory.doUpdateProgressWindow("Checking {0}".format(_tar), i, 
                                              sel.length(), 
                                              reportItem=False)
            
            iter = OM.MItGeometry(_dagPath)
            _cnt = 0            
            if _returnMode is 0:#...if the object intersects
                _found = False
                while not iter.isDone():
                    guiFactory.doUpdateProgressWindow("Checking vtx[{0}]".format(_cnt), _cnt, 
                                                      _vtxCount, 
                                                      reportItem=False)          
                    _cnt +=1
                    vert = iter.position(OM.MSpace.kWorld)
                    _inside = True                           
                    for v in cgmValid.d_stringToVector.itervalues():
                        d_return = cgmRAYS.findMeshIntersection(sourceObj, vert, 
                                                                v, 
                                                                maxDistance=10000, 
                                                                tolerance=.1)
                        if not d_return.get('hit'):#...if we miss once, it's not inside
                            _inside = False
                    if _inside:
                        _l_found.add(_dagPath)
                        _found = True
                    iter.next()                              
            else:#...vert/edge/face mode...
                while not iter.isDone():
                    guiFactory.doUpdateProgressWindow("Checking vtx[{0}]".format(_cnt), _cnt,  
                                                      _vtxCount, 
                                                      reportItem=False)          
                    _cnt +=1                    
                    vert = iter.position(OM.MSpace.kWorld)
                    _good = True                           
                    p = cgmOM.Point(vert)
                    for v in cgmValid.d_stringToVector.itervalues():
                        d_return = cgmRAYS.findMeshIntersection(sourceObj, vert, 
                                                                v, 
                                                                maxDistance=10000, 
                                                                tolerance=.1)
                        if not d_return.get('hit'):#...if we miss once, it's not inside
                            _good = False
                    if _good:
                        _l_found.add(_dagPath, iter.currentItem())
                    iter.next()  
                
    guiFactory.doCloseProgressWindow()
    
    #Post processing =============================================================================
    _l_found.getSelectionStrings(matching)#...push data to strings    
    log.info("Found {0} vers contained...".format(len(matching)))
    
    #Expand ========================================================================================
    if _expandBy is not None and returnMode > 0:
        log.info("Expanding result by '{0}'...".format(_expandBy))   
        _sel = mc.ls(sl=True) or []        
        _expandFactor = int(expandAmount)  
        mc.select(matching)        
        if _expandBy is 'expandSelection':
            for i in range(_expandFactor):
                mel.eval("PolySelectTraverse 1;")
            matching = mc.ls(sl = True)
        if _expandBy is 'softSelect':
            mc.softSelect(softSelectEnabled=True,ssc='1,0,0,0,1,2', softSelectFalloff = 1, softSelectDistance = _expandFactor)
            matching = cgmSELECT.get_softSelectionItems()   
            mc.softSelect(softSelectEnabled=False)
        #if _sel:mc.select(_sel)   

    if _returnMode > 0 and _returnMode is not 3 and matching:#...need to convert
        log.info("Return conversion necessary")
        if _returnMode is 1:#...face
            matching = mc.polyListComponentConversion(matching, fv=True, tf=True, internal = True)
        elif _returnMode is 2:#...edge
            matching = mc.polyListComponentConversion(matching, fv=True, te=True, internal = True )

    if _selectReturn and matching:
        mc.select(matching)
    return matching

Up next is setting up a new wrap setup with this data. Will post when that’s done.

 

Comments are closed.