def spaceIsEmpty(self): volume = self.spaceCoordinates for mesh in cmds.listRelatives(NormalClass.modelName, children = True): print mesh print volume nearestnode = cmds.nearestPointOnMesh("pCone1", ip = [(volume[3]+volume[0])/2,(volume[4]+volume[1])/2,(volume[5]+volume[2])/2]) nearestposition = cmds.getAttr('%s.position' % nearestnode)[0] print nearestposition cmds.delete(nearestnode) if nearestposition[0] < volume[3] and nearestposition > volume[0] and nearestposition[1] < volume[4] and nearestposition > volume[1] and nearestposition[2] < volume[5] and nearestposition > volume[2]: self.isFree = False break
def run(*args, **kwargs): """Detect flipped (reversed) normals. Find polygon objects with normals that point away from the camera as long as that camera is outside the object. Any objects found should be subject to a visual inspection. The test is unable to determine the proper normal direction for flat objects. This test may give seemingly false positives if a node's parent transforms are scaled by a negative value. --- To fix a scene, run [Polygons] Normals->Reverse return result, len(nonconformingfaces), len(reversedmeshes) nonconformingfaces, reversedmeshes, err has_flippednormals([verbose=boolean]) -> boolean, int, int, list, list list """ t0 = float(time.time()) valid_kwargs = ["verbose", "diag"] for k, v in kwargs.iteritems(): if k not in valid_kwargs: raise TypeError("Invalid keyword argument %s" % k) result = False err = list() verbose = False batch = cmds.about(batch=True) # verbose defaults to False if verbose option not set in menu # or specified in cmdline try: verbose = kwargs["verbose"] print "verbose is:", verbose except KeyError: verbose = False if cmds.optionVar(exists="checkmateVerbosity"): verbose = cmds.optionVar(query="checkmateVerbosity") try: diag = kwargs["diag"] except KeyError: diag = False # require OpenMaya for vector math import maya.OpenMaya as om result = False # meshes = cmds.ls(type='mesh') meshes = [x for x in cmds.ls(typ="mesh", noIntermediate=True) if not cm.tests.shapes.mesh.isEmpty(x)] cmds.select(clear=True) # make sure undo is on cmds.undoInfo(state=True, infinity=True) # set up necessary plug-ins if not cmds.pluginInfo("nearestPointOnMesh", query=True, loaded=True): try: cmds.loadPlugin("nearestPointOnMesh") except NameError: pass err = list() nonconformingfaces = list() reversedmeshes = list() if not batch: # put a progress bar in try: gMainProgressBar = mel.eval("$tmp = $gMainProgressBar") except RuntimeError: pass if len(meshes) < 1: err.append(["#Warning: No meshes in scene"]) return result, 0, 0, [], [], err if not batch: try: cmds.progressBar( gMainProgressBar, edit=True, beginProgress=True, isInterruptable=True, status="checking for reversed normals...", maxValue=len(meshes), ) except UnboundLocalError: pass for mesh in meshes: if not batch: if cmds.progressBar(gMainProgressBar, query=True, isCancelled=True): break cmds.progressBar(gMainProgressBar, edit=True, step=1) if verbose: print mesh # unlock normals, not spec'd but should be in here cmds.polyNormalPerVertex(mesh, ufn=True) # first check that all normals are conformed # this requires undo # if conform gives a selection, we know that not all normals conform cmds.select(mesh, replace=True) faces = cmds.polyListComponentConversion(toFace=True) # polyNormal will select and reverse the nonconforming faces or do # nothing # deselect the mesh, keep only faces, if any try: cmds.polyNormal(faces, normalMode=2, userNormalMode=0, ch=0) except TypeError: pass cmds.select(mesh, deselect=True) try: selection = cmds.ls(selection=True, flatten=True) except TypeError: pass ######################################################################## # if we have selected faces, we can stop at this point, because it is # not clear what the right # orientation of the normals is, assume the selection is correct # and is only faces (needs additional check) if len(cmds.ls(selection=True, flatten=True)): result = True nonconformingfaces.extend(selection) # should probably reverse the faces again cmds.polyNormal(selection, normalMode=0, userNormalMode=0, ch=0) # continue with the next loop continue # only continue if there are no nonconforming normals, # we now only have an objects that is completely reversed else: # find a point that is guaranteed to be outside the object if verbose: print "checking mes:h %s" % mesh p = cmds.exactWorldBoundingBox(mesh) point = tuple([(p[3] + 0.1), (p[4] + 1.0), (p[5] + 0.1)]) if verbose: print point if diag: cmds.spaceLocator(p=point) # the result of nearestPointOnMesh is a nearestPointOnMesh node nearestnode = cmds.nearestPointOnMesh(mesh, inPosition=point) if verbose: print nearestnode # get the normal, face index and position at the nearest point # getattr resuturns a list of tuples, take the first index nearestnormal = cmds.getAttr("%s.normal" % nearestnode)[0] if verbose: print "nearest normal: ", nearestnormal nearestposition = cmds.getAttr("%s.position" % nearestnode)[0] if verbose: print "nearest position: ", nearestposition if diag: cmds.spaceLocator(p=nearestposition) nearestfaceindex = cmds.getAttr("%s.nearestFaceIndex" % nearestnode) face = "%s.f[%d]" % (mesh, nearestfaceindex) cmds.select(face) if verbose: print "face: %s" % face cmds.delete(nearestnode) # select the nearest face (is this really necessary?) # cmds.select('%s.f[%s]' % (mesh,nearestfaceindex)) # figure out the direction which the facenormal should be looking # (should be toward point) # the differnce between point and nearestposition is a vector delta = ((point[0] - nearestposition[0]), (point[1] - nearestposition[1]), (point[2] - nearestposition[2])) # to avoid selecting a point at which the normal is difficult to determine # find the centroid of the face and use that to find the direction and use the normal of the face, # not the normal at the nearest point (label, num, nearestnormalX, nearestnormalY, nearestnormalZ) = cmds.polyInfo(face, fn=True)[0].split() # there's no need to normalize it facenormal = om.MVector(float(nearestnormalX), float(nearestnormalY), float(nearestnormalZ)) direction = om.MVector(delta[0], delta[1], delta[2]) if verbose: print "facenormal :", facenormal print "direction :", direction # Calculate dot product as an indication of how similar the two # vectors are. acosd(dot) gives the angle between the two, 0 means # identitcal, -1 means they're at a 180 degree angle # positive values mean the face normal is looking out, # negative means the normal is reversed dot = facenormal * direction if verbose: print "dot: %f5.2" % dot if dot > 0: pass else: reversedmeshes.append(mesh) result = True err.append("# FAIL : %s has reversed normals #" % mesh) # kill the progressBar if not batch: cmds.progressBar(gMainProgressBar, edit=True, endProgress=True) # turn off selection constraints cmds.polySelectConstraint(disable=True) # to make it easy to fix the scene select everything that is reversed cmds.select(clear=True) try: cmds.select(nonconformingfaces, add=True) except TypeError: pass try: cmds.select(reversedmeshes, add=True) except TypeError: pass # print execution time print "%-24s : %.6f seconds" % ("f.normals.run()", (float(time.time()) - t0)) return result, len(nonconformingfaces), len(reversedmeshes), nonconformingfaces, reversedmeshes, err