detect overlapping subparts

This commit is contained in:
David Rose 2008-11-19 20:26:48 +00:00
parent 38b0200c36
commit c70aadbd89

View File

@ -21,6 +21,9 @@ class Actor(DirectObject, NodePath):
animLoaderOptions = LoaderOptions(LoaderOptions.LFSearch |
LoaderOptions.LFReportErrors |
LoaderOptions.LFConvertAnim)
validateSubparts = ConfigVariableBool('validate-subparts', True)
class PartDef:
"""Instances of this class are stored within the
@ -1078,17 +1081,88 @@ class Actor(DirectObject, NodePath):
else:
Actor.notify.warning("no joint named %s!" % (jointName))
def getJoints(self, jointName):
def getJoints(self, partName = None, jointName = '*', lodName = None):
""" Returns the list of all joints, from the named part or
from all parts, that match the indicated jointName. The
jointName may include pattern characters like *. """
joints=[]
for lod in self.__partBundleDict.values():
for part in lod.values():
partBundle=part.getBundle()
pattern = GlobPattern(jointName)
if lodName == None:
# Get all LOD's
partBundleDicts = self.__partBundleDict.values()
else:
# Get one LOD.
partBundleDict = self.__partBundleDict.get(lodName)
if not partBundleDict:
Actor.notify.warning("couldn't find lod: %s" % (lodName))
return []
partBundleDicts = [partBundleDict]
for partBundleDict in partBundleDicts:
parts = []
if partName:
subpartDef = self.__subpartDict.get(partName, None)
if not subpartDef:
# Whole part
subset = None
partDef = partBundleDict.get(partName)
else:
# Sub-part
subset = subpartDef.subset
partDef = partBundleDict.get(subpartDef.truePartName)
if not partDef:
Actor.notify.warning("no part named %s!" % (partName))
return []
parts = [partDef]
else:
subset = None
parts = partBundleDict.values()
for partData in parts:
partBundle = partData.getBundle()
if not pattern.hasGlobCharacters() and not subset:
# The simple case.
joint = partBundle.findChild(jointName)
if(joint):
if joint:
joints.append(joint)
else:
# The more complex case.
isIncluded = True
if subset:
isIncluded = subset.isIncludeEmpty()
self.__getPartJoints(joints, pattern, partBundle, subset, isIncluded)
return joints
def getOverlappingJoints(self, partNameA, partNameB, jointName = '*', lodName = None):
""" Returns the set of joints, matching jointName, that are
shared between partNameA and partNameB. """
jointsA = set(self.getJoints(partName = partNameA, jointName = jointName, lodName = lodName))
jointsB = set(self.getJoints(partName = partNameB, jointName = jointName, lodName = lodName))
return jointsA & jointsB
def __getPartJoints(self, joints, pattern, partNode, subset, isIncluded):
""" Recursively walks the joint hierarchy to look for matching
joint names, implementing getJoints(). """
name = partNode.getName()
if subset:
# Constrain the traversal just to the named subset.
if subset.matchesInclude(name):
isIncluded = True
elif subset.matchesExclude(name):
isIncluded = False
if isIncluded and pattern.matches(name):
joints.append(partNode)
for child in partNode.getChildren():
self.__getPartJoints(joints, pattern, child, subset, isIncluded)
def getJointTransform(self, partName, jointName, lodName='lodRoot'):
partBundleDict=self.__partBundleDict.get(lodName)
if not partBundleDict:
@ -1845,7 +1919,7 @@ class Actor(DirectObject, NodePath):
def makeSubpart(self, partName, includeJoints, excludeJoints = [],
parent="modelRoot"):
parent="modelRoot", overlapping = False):
"""Defines a new "part" of the Actor that corresponds to the
same geometry as the named parent part, but animates only a
@ -1868,6 +1942,10 @@ class Actor(DirectObject, NodePath):
of its descendents), even if a parent joint was named by
includeJoints.
if overlapping is False, an error is raised (in the dev build)
if this subpart shares joints with any other subparts. If
overlapping is True, no such error is raised.
parent is the actual partName that this subpart is based
on."""
@ -1883,6 +1961,15 @@ class Actor(DirectObject, NodePath):
self.__subpartDict[partName] = Actor.SubpartDef(parent, subset)
if __dev__ and not overlapping and self.validateSubparts.getValue():
# Without the overlapping flag True, we're not allowed to
# define overlapping sub-parts. Verify that we haven't.
for otherPartName, otherPartDef in self.__subpartDict.items():
if otherPartName != partName and otherPartDef.truePartName == parent:
joints = self.getOverlappingJoints(partName, otherPartName)
if joints:
raise StandardError, 'Overlapping joints: %s and %s' % (partName, otherPartName)
def setSubpartsComplete(self, flag):
"""Sets the subpartsComplete flag. This affects the behavior
@ -1907,11 +1994,49 @@ class Actor(DirectObject, NodePath):
self.__subpartsComplete = flag
if __dev__ and self.__subpartsComplete and self.validateSubparts.getValue():
# If we've specified any parts at all so far, make sure we've
# specified all of them.
if self.__subpartDict:
self.verifySubpartsComplete()
def getSubpartsComplete(self):
"""See setSubpartsComplete()."""
return self.__subpartsComplete
def verifySubpartsComplete(self, partName = None, lodName = None):
""" Ensures that each joint is defined by at least one
subPart. Prints a warning if this is not the case. """
if partName:
assert partName not in self.__subpartDict
partNames = [partName]
else:
if lodName:
partNames = self.__partBundleDict[lodName].keys()
else:
partNames = self.__partBundleDict.values()[0].keys()
for partName in partNames:
subJoints = set()
for subPartName, subPartDef in self.__subpartDict.items():
if subPartName != partName and subPartDef.truePartName == partName:
subJoints |= set(self.getJoints(partName = subPartName, lodName = lodName))
allJoints = set(self.getJoints(partName = partName, lodName = lodName))
diff = allJoints.difference(subJoints)
for joint in list(diff):
if joint.getName() == '<skeleton>' or joint.getName() == 'morph' or isinstance(joint, PartBundle):
# We'll allow these special-case joints, which are
# usually unanimated root nodes, to remain
# uncovered without complaining.
diff.remove(joint)
if diff:
self.notify.warning('Uncovered joints: %s' % (list(diff)))
def loadAnims(self, anims, partName="modelRoot", lodName="lodRoot"):
"""loadAnims(self, string:string{}, string='modelRoot',
string='lodRoot')