mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
detect overlapping subparts
This commit is contained in:
parent
38b0200c36
commit
c70aadbd89
@ -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,18 +1081,89 @@ 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()
|
||||
joint=partBundle.findChild(jointName)
|
||||
if(joint):
|
||||
joints.append(joint)
|
||||
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:
|
||||
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'):
|
||||
def getJointTransform(self, partName, jointName, lodName='lodRoot'):
|
||||
partBundleDict=self.__partBundleDict.get(lodName)
|
||||
if not partBundleDict:
|
||||
Actor.notify.warning("no lod named: %s" % (lodName))
|
||||
@ -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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user