From 20a8e9a69d5953b7e3b60fcdff8330c54a2a3e22 Mon Sep 17 00:00:00 2001 From: Pierce Brooks Date: Sat, 13 Jan 2024 17:01:50 -0500 Subject: [PATCH] fix some exporter inheritance mistakes and improve bvh export functionality with extra options for misc downstream project pipeline compliance --- makehuman/plugins/9_export_bvh/__init__.py | 14 +++++++---- makehuman/plugins/9_export_ogre/__init__.py | 3 +-- makehuman/shared/bvh.py | 26 +++++++++++++++++---- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/makehuman/plugins/9_export_bvh/__init__.py b/makehuman/plugins/9_export_bvh/__init__.py index 421ece339..d59253e19 100644 --- a/makehuman/plugins/9_export_bvh/__init__.py +++ b/makehuman/plugins/9_export_bvh/__init__.py @@ -52,6 +52,8 @@ class BvhConfig(ExportConfig): def __init__(self): ExportConfig.__init__(self) self.useRelPaths = True + self.allowDummyJoints = True + self.sortJointChildrenByName = False class ExporterBVH(Exporter): def __init__(self): @@ -63,13 +65,17 @@ def __init__(self): def build(self, options, taskview): import gui + Exporter.build(self, options, taskview) self.taskview = taskview #self.exportAnimations = options.addWidget(gui.CheckBox("Animations", True)) - self.feetOnGround = options.addWidget(gui.CheckBox("Feet on ground", True)) + self.allowDummyJoints = options.addWidget(gui.CheckBox("Allow dummy joints", True)) + self.sortJointChildrenByName = options.addWidget(gui.CheckBox("Sort joint children by name", False)) def getConfig(self): cfg = BvhConfig() cfg.feetOnGround = self.feetOnGround.selected + cfg.allowDummyJoints = self.allowDummyJoints.selected + cfg.sortJointChildrenByName = self.sortJointChildrenByName.selected cfg.scale,cfg.unit = self.taskview.getScale() #cfg.exportAnimations = self.exportAnimations.selected cfg.exportAnimations = False @@ -90,7 +96,7 @@ def export(self, human, filename): for animName in human.getAnimations(): fn = baseFilename + "_%s.bvh" % animName log.message("Exporting file %s.", fn) - bvhData = bvh.createFromSkeleton(skel, human.getAnimation(animName)) + bvhData = bvh.createFromSkeleton(skel, human.getAnimation(animName), cfg.allowDummyJoints, cfg.sortJointChildrenByName) if cfg.scale != 1: bvhData.scale(cfg.scale) if cfg.feetOnGround: @@ -100,9 +106,9 @@ def export(self, human, filename): fn = filename("bvh") log.message("Exporting file %s.", fn) if human.isPosed(): - bvhData = bvh.createFromSkeleton(skel, human.getActiveAnimation()) + bvhData = bvh.createFromSkeleton(skel, human.getActiveAnimation(), cfg.allowDummyJoints, cfg.sortJointChildrenByName) else: - bvhData = bvh.createFromSkeleton(skel) + bvhData = bvh.createFromSkeleton(skel, None, cfg.allowDummyJoints, cfg.sortJointChildrenByName) if cfg.scale != 1: bvhData.scale(cfg.scale) if cfg.feetOnGround: diff --git a/makehuman/plugins/9_export_ogre/__init__.py b/makehuman/plugins/9_export_ogre/__init__.py index 4cb47ccaf..1af53b8a8 100644 --- a/makehuman/plugins/9_export_ogre/__init__.py +++ b/makehuman/plugins/9_export_ogre/__init__.py @@ -66,9 +66,8 @@ def export(self, human, filename): mh2ogre.exportOgreMesh(filename("mesh.xml"), cfg) def build(self, options, taskview): - import gui + Exporter.build(self, options, taskview) self.taskview = taskview - self.feetOnGround = options.addWidget(gui.CheckBox("Feet on ground", True)) def getConfig(self): cfg = OgreConfig() diff --git a/makehuman/shared/bvh.py b/makehuman/shared/bvh.py index 0cc5a4f25..3f267fd73 100644 --- a/makehuman/shared/bvh.py +++ b/makehuman/shared/bvh.py @@ -56,6 +56,7 @@ class BVH(): """ def __init__(self, name="Untitled"): self.name = name + self.filepath = "" self.joints = {} # Lookup dict to find joints by name self.bvhJoints = [] # List of joints in the order in which they were defined in the BVH file (important for MOTION data parsing) self.jointslist = [] # Cached breadth-first list of all joints @@ -64,6 +65,7 @@ def __init__(self, name="Untitled"): self.frameTime = -1 self.frames = [] + self.sortChildrenByName = False self.convertFromZUp = False # Set to true to convert the coordinates from a Z-is-up coordinate system. Most motion capture data uses Y-is-up, though. self.allowTranslation = "onlyroot" # Joints to accept translation animation data for @@ -272,6 +274,9 @@ def getJointsBVHOrder(self): """ Retrieve joints as they were ordered in the BVH hierarchy definition. """ + if self.bvhJoints is None and not self.rootJoint is None: + self.bvhJoints = [] + self.rootJoint.recollect() return self.bvhJoints def fromFile(self, filepath): @@ -281,6 +286,7 @@ def fromFile(self, filepath): specified BVH file. """ import os + self.filepath = filepath self.name = os.path.splitext(os.path.basename(filepath))[0] if self.convertFromZUp == "auto": autoAxis = True @@ -355,7 +361,7 @@ def _autoGuessCoordinateSystem(self): log.debug("Cannot use reference joint %s for determining axis system, it is an end-effector (has no children)" % ref_joint.name) ref_joint = None if ref_joint is None: - log.warning("Could not auto guess axis system for BVH file %s because no known joint name is found. Using Y up as default axis orientation." % filepath) + log.warning("Could not auto guess axis system for BVH file %s because no known joint name is found. Using Y up as default axis orientation." % self.filepath) else: tail_joint = ref_joint.children[0] direction = tail_joint.position - ref_joint.position @@ -366,7 +372,7 @@ def _autoGuessCoordinateSystem(self): # Z-up return True - def fromSkeleton(self, skel, animationTrack=None, dummyJoints=True): + def fromSkeleton(self, skel, animationTrack=None, dummyJoints=True, sortChildrenByName=False): """ Construct a BVH object from a skeleton structure and optionally an animation track. If no animation track is specified, a dummy animation @@ -388,6 +394,8 @@ def fromSkeleton(self, skel, animationTrack=None, dummyJoints=True): NOTE: Make sure that the skeleton has only one root. """ + self.sortChildrenByName = sortChildrenByName + # Traverse skeleton joints in depth-first order for jointName in skel.getJointNames(): bone = skel.getBone(jointName) @@ -478,6 +486,8 @@ def writeToFile(self, filename): f.write('Frames: %s\n' % self.frameCount) f.write('Frame Time: %f\n' % self.frameTime) + if self.sortChildrenByName: + self.bvhJoints = None allJoints = [joint for joint in self.getJointsBVHOrder() if not joint.isEndConnector()] jointsData = [joint.matrixPoses for joint in allJoints] nJoints = len(jointsData) @@ -758,6 +768,9 @@ def calculateFrames(self): def addChild(self, joint): self.children.append(joint) joint.parent = self + if self.skeleton.sortChildrenByName: + import operator + self.children.sort(key=operator.attrgetter('name')) def getName(self): return self.name @@ -774,6 +787,11 @@ def hasChildren(self): def isEndConnector(self): return not self.hasChildren() + def recollect(self): + self.skeleton.bvhJoints.append(self) + for child in self.children: + child.recollect() + def load(filename, convertFromZUp="auto", allowTranslation="onlyroot"): """ @@ -790,7 +808,7 @@ def load(filename, convertFromZUp="auto", allowTranslation="onlyroot"): result.fromFile(filename) return result -def createFromSkeleton(skel, animationTrack=None, dummyJoints=True): +def createFromSkeleton(skel, animationTrack=None, dummyJoints=True, sortChildrenByName=False): result = BVH() - result.fromSkeleton(skel, animationTrack, dummyJoints) + result.fromSkeleton(skel, animationTrack, dummyJoints, sortChildrenByName) return result