def execute(self, context): """ Args: context: Returns: """ selection = [] if self.modelname: log("phobos: Selecting model" + self.modelname, "INFO") roots = sUtils.getRoots() for root in roots: if nUtils.getModelName(root) == self.modelname: selection = sUtils.getChildren(root) else: log("No model name provided, deriving from selection...", "INFO") roots = set() for obj in bpy.context.selected_objects: roots.add(sUtils.getRoot(obj)) for root in list(roots): selection.extend(sUtils.getChildren(root)) sUtils.selectObjects(list(selection), True) return {'FINISHED'}
def storePose(root, posename): """Stores the current pose of all of a model's selected joints. Existing poses of the same name will be overwritten. Args: root(bpy_types.Object): root of the model the pose belongs to posename(str): name the pose will be stored under Returns: : Nothing. """ if root: filename = nUtils.getModelName(root) + '::poses' posedict = json.loads(bUtils.readTextFile(filename)) if not posedict: posedict = {posename: {'name': posename, 'joints': {}}} else: posedict[posename] = {'name': posename, 'joints': {}} links = sUtils.getChildren(root, ('link', ), True, False) sUtils.selectObjects([root] + links, clear=True, active=0) bpy.ops.object.mode_set(mode='POSE') for link in (link for link in links if 'joint/type' in link and link['joint/type'] not in ['fixed', 'floating']): link.pose.bones['Bone'].rotation_mode = 'XYZ' posedict[posename]['joints'][nUtils.getObjectName( link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y bpy.ops.object.mode_set(mode='OBJECT') posedict = gUtils.roundFloatsInDict( posedict, ioUtils.getExpSettings().decimalPlaces) bUtils.updateTextFile(filename, json.dumps(posedict)) else: log("No model root provided to store the pose for", "ERROR")
def execute(self, context): """ Args: context: Returns: """ root = sUtils.getRoot(context.selected_objects[0]) objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) sUtils.selectObjects(objectlist) poses = models.getPoses(root['model/name']) i = 1 for pose in poses: sUtils.selectObjects([root] + objectlist, clear=True, active=0) models.loadPose(root['model/name'], pose) parameter = self.decimate_ratio if self.decimate_type == 'UNSUBDIV': parameter = self.decimate_iteration elif self.decimate_type == 'DISSOLVE': parameter = self.decimate_angle_limit exporter.bakeModel( objectlist, root['model/name'], pose, decimate_type=self.decimate_type, decimate_parameter=parameter, ) display.setProgress(i / len(poses)) i += 1 sUtils.selectObjects([root] + objectlist, clear=True, active=0) bpy.ops.scene.reload_models_and_poses_operator() return {'FINISHED'}
def execute(self, context): root = sUtils.getRoot(context.selected_objects[0]) objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) sUtils.selectObjects(objectlist) poses = models.getPoses(root['modelname']) i = 1 for pose in poses: sUtils.selectObjects([root] + objectlist, clear=True, active=0) models.loadPose(root['modelname'], pose) parameter = self.decimate_ratio if self.decimate_type == 'UNSUBDIV': parameter = self.decimate_iteration elif self.decimate_type == 'DISSOLVE': parameter = self.decimate_angle_limit exporter.bakeModel(objectlist, root['modelname'], pose, decimate_type=self.decimate_type, decimate_parameter=parameter) display.setProgress(i / len(poses)) i += 1 sUtils.selectObjects([root] + objectlist, clear=True, active=0) bpy.ops.scene.reload_models_and_poses_operator() return {'FINISHED'}
def execute(self, context): """ Args: context: Returns: """ root = sUtils.getRoot(context.selected_objects[0]) modelsPosesColl = bUtils.getPhobosPreferences().models_poses activeModelPoseIndex = bpy.context.scene.active_ModelPose selected_robot = modelsPosesColl[bpy.data.images[activeModelPoseIndex].name] objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) sUtils.selectObjects([root] + objectlist, clear=True, active=0) models.loadPose(selected_robot.robot_name, selected_robot.label) parameter = self.decimate_ratio if self.decimate_type == 'UNSUBDIV': parameter = self.decimate_iteration elif self.decimate_type == 'DISSOLVE': parameter = self.decimate_angle_limit exporter.bakeModel( objectlist, root['model/name'], selected_robot.label, decimate_type=self.decimate_type, decimate_parameter=parameter, ) sUtils.selectObjects([root] + objectlist, clear=True, active=0) bpy.ops.scene.reload_models_and_poses_operator() return {'FINISHED'}
def storePose(modelname, posename): """ Stores the current pose of all of a robot's selected joints. Existing poses of the same name will be overwritten. :param modelname: The robot the pose belongs to. :type modelname: str. :param posename: The name the pose will be stored under. :type posename: str. :return: Nothing. """ rootlink = None for root in sUtils.getRoots(): if root['modelname'] == modelname: rootlink = root if rootlink: filename = modelname + '::poses' posedict = yaml.load(bUtils.readTextFile(filename)) if not posedict: posedict = {posename: {'name': posename, 'joints': {}}} else: posedict[posename] = {'name': posename, 'joints': {}} bpy.ops.object.mode_set(mode='POSE') links = sUtils.getChildren(rootlink, ('link',), True, False) for link in (link for link in links if 'joint/type' in link and link['joint/type'] not in ['fixed', 'floating']): link.pose.bones['Bone'].rotation_mode = 'XYZ' posedict[posename]['joints'][nUtils.getObjectName(link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y bUtils.updateTextFile(filename, yaml.dump(posedict, default_flow_style=False)) else: log("No model root could be found to store the pose for", "ERROR", "storePose")
def execute(self, context): startLog(self) objlist = context.selected_objects if self.complete: roots = list( set([sUtils.getRoot(obj) for obj in context.selected_objects])) if None in roots: roots.remove(None) objlist = [ elem for sublist in [sUtils.getChildren(root) for root in roots] for elem in sublist ] objnames = [o.name for o in bpy.data.objects] for obj in objlist: if "::" in obj.name: if nUtils.namesAreExplicit({obj.name.split("::")[-1]}, objnames): nUtils.removeNamespace(obj) else: log( "Cannot remove namespace from " + obj.name + ". Name wouldn't be explicit", "ERROR") else: nUtils.addNamespace(obj) endLog() return {'FINISHED'}
def execute(self, context): """ Args: context: Returns: """ if self.complete: roots = set([sUtils.getRoot(obj) for obj in context.selected_objects]) - {None} objects = set() for root in roots: objects = objects | set(sUtils.getChildren(root)) objlist = list(objects) else: objlist = [bpy.context.active_object] for obj in objlist: try: entityname = sUtils.getRoot(obj)['entity/name'] except (KeyError, TypeError): entityname = '' log(nUtils.getObjectName(obj) + " is not part of a well-defined entity.", "WARNING") namespace = self.namespace if self.namespace else entityname nUtils.toggleNamespace(obj, namespace) return {'FINISHED'}
def storePose(robot_name, pose_name): """ Store the current pose of all of a robot's selected links. Existing poses of the same name will be overwritten. :param robot_name: The robot the pose belongs to. :type robot_name: str. :param pose_name: The name the pose will be stored under. :type pose_name: str. :return: Nothing. """ file_name = 'robot_poses_' + robot_name load_file = blenderUtils.readTextFile(file_name) if load_file == '': poses = {} else: poses = yaml.load(load_file) new_pose = {} prev_mode = bpy.context.mode bpy.ops.object.mode_set(mode='POSE') for root in selectionUtils.getRoots(): if root['modelname'] == robot_name: links = selectionUtils.getChildren(root) for link in links: if link.select and link.phobostype == 'link': link.pose.bones['Bone'].rotation_mode = 'XYZ' new_pose[namingUtils.getObjectName(link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y bpy.ops.object.mode_set(mode=prev_mode) poses[pose_name] = new_pose blenderUtils.updateTextFile(file_name, yaml.dump(poses))
def storePose(robot_name, pose_name): """ Store the current pose of all of a robot's selected links. Existing poses of the same name will be overwritten. :param robot_name: The robot the pose belongs to. :type robot_name: str. :param pose_name: The name the pose will be stored under. :type pose_name: str. :return: Nothing. """ file_name = 'robot_poses_' + robot_name load_file = blenderUtils.readTextFile(file_name) if load_file == '': poses = {} else: poses = yaml.load(load_file) new_pose = {} prev_mode = bpy.context.mode bpy.ops.object.mode_set(mode='POSE') for root in selectionUtils.getRoots(): if root['modelname'] == robot_name: links = selectionUtils.getChildren(root) for link in links: if link.select and link.phobostype == 'link': link.pose.bones['Bone'].rotation_mode = 'XYZ' new_pose[namingUtils.getObjectName( link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y bpy.ops.object.mode_set(mode=prev_mode) poses[pose_name] = new_pose blenderUtils.updateTextFile(file_name, yaml.dump(poses))
def execute(self, context): root = sUtils.getRoot(context.selected_objects[0]) modelsPosesColl = bpy.context.user_preferences.addons[ 'phobos'].preferences.models_poses activeModelPoseIndex = bpy.context.scene.active_ModelPose selected_robot = modelsPosesColl[ bpy.data.images[activeModelPoseIndex].name] objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) sUtils.selectObjects([root] + objectlist, clear=True, active=0) models.loadPose(selected_robot.robot_name, selected_robot.label) parameter = self.decimate_ratio if self.decimate_type == 'UNSUBDIV': parameter = self.decimate_iteration elif self.decimate_type == 'DISSOLVE': parameter = self.decimate_angle_limit exporter.bakeModel(objectlist, root['modelname'], selected_robot.label, decimate_type=self.decimate_type, decimate_parameter=parameter) sUtils.selectObjects([root] + objectlist, clear=True, active=0) bpy.ops.scene.reload_models_and_poses_operator() return {'FINISHED'}
def storePose(modelname, posename): """ Stores the current pose of all of a robot's selected joints. Existing poses of the same name will be overwritten. :param modelname: The robot the pose belongs to. :type modelname: str. :param posename: The name the pose will be stored under. :type posename: str. :return: Nothing. """ rootlink = None for root in sUtils.getRoots(): if root['modelname'] == modelname: rootlink = root if rootlink: filename = modelname + '::poses' posedict = yaml.load(bUtils.readTextFile(filename)) if not posedict: posedict = {posename: {'name': posename, 'joints': {}}} else: posedict[posename] = {'name': posename, 'joints': {}} bpy.ops.object.mode_set(mode='POSE') links = sUtils.getChildren(rootlink, ('link', ), True, False) for link in (link for link in links if 'joint/type' in link and link['joint/type'] not in ['fixed', 'floating']): link.pose.bones['Bone'].rotation_mode = 'XYZ' posedict[posename]['joints'][nUtils.getObjectName( link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y bUtils.updateTextFile(filename, yaml.dump(posedict, default_flow_style=False)) else: log("No model root could be found to store the pose for", "ERROR")
def execute(self, context): selection = [] if self.modelname: print("phobos: Selecting model", self.modelname) roots = selectionUtils.getRoots() for root in roots: if root["modelname"] == self.modelname: selection = selectionUtils.getChildren(root) else: print("phobos: No model name provided, deriving from selection...") roots = set() for obj in bpy.context.selected_objects: print("Selecting", selectionUtils.getRoot(obj).name) roots.add(selectionUtils.getRoot(obj)) for root in list(roots): selection.extend(selectionUtils.getChildren(root)) selectionUtils.selectObjects(list(selection), True) return {'FINISHED'}
def execute(self, context): selection = [] if self.modelname: print("phobos: Selecting model", self.modelname) roots = sUtils.getRoots() for root in roots: if root["modelname"] == self.modelname: selection = sUtils.getChildren(root) else: print("phobos: No model name provided, deriving from selection...") roots = set() for obj in bpy.context.selected_objects: print("Selecting", sUtils.getRoot(obj).name) roots.add(sUtils.getRoot(obj)) for root in list(roots): selection.extend(sUtils.getChildren(root)) sUtils.selectObjects(list(selection), True) return {'FINISHED'}
def execute(self, context): startLog(self) objlist = context.selected_objects if self.complete: roots = list(set([selectionUtils.getRoot(obj) for obj in context.selected_objects])) if None in roots: roots.remove(None) objlist = [elem for sublist in [selectionUtils.getChildren(root) for root in roots] for elem in sublist] objnames = [o.name for o in bpy.data.objects] for obj in objlist: if "::" in obj.name: if namingUtils.namesAreExplicit({obj.name.split("::")[-1]}, objnames): namingUtils.removeNamespace(obj) else: log("Cannot remove namespace from " + obj.name + ". Name wouldn't be explicit", "ERROR") else: namingUtils.addNamespace(obj) endLog() return {'FINISHED'}
def execute(self, context): roots = ioUtils.getExportModels() if not roots: log("No properly defined models selected or present in scene.", 'ERROR') return {'CANCELLED'} elif not self.exportall: roots = [ root for root in roots if root['modelname'] == self.modelname ] if len(roots) > 1: log( "Ambiguous model definitions: " + self.modelname + " exists " + str(len(roots)) + " times.", "ERROR") return {'CANCELLED'} for root in roots: # setup paths exportpath = ioUtils.getExportPath() if not securepath(exportpath): log("Could not secure path to export to.", "ERROR") continue log("Export path: " + exportpath, "DEBUG") ioUtils.exportModel(models.deriveModelDictionary(root), exportpath) # select all exported models after export is done if ioUtils.getExpSettings().selectedOnly: for root in roots: objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) sUtils.selectObjects(objectlist, clear=False) else: bpy.ops.object.select_all(action='DESELECT') for root in roots: sUtils.selectObjects(list([root]), False) bpy.ops.phobos.select_model() # report success to user log("Export successful.", "INFO") return {'FINISHED'}
def storePose(root, posename): """Stores the current pose of all of a model's selected joints. Existing poses of the same name will be overwritten. Args: root(bpy_types.Object): root of the model the pose belongs to posename(str): name the pose will be stored under Returns: : Nothing. """ if root: filename = nUtils.getModelName(root) + '::poses' posedict = yaml.load(bUtils.readTextFile(filename)) if not posedict: posedict = {posename: {'name': posename, 'joints': {}}} else: posedict[posename] = {'name': posename, 'joints': {}} links = sUtils.getChildren(root, ('link',), True, False) sUtils.selectObjects([root] + links, clear=True, active=0) bpy.ops.object.mode_set(mode='POSE') for link in ( link for link in links if 'joint/type' in link and link['joint/type'] not in ['fixed', 'floating'] ): link.pose.bones['Bone'].rotation_mode = 'XYZ' posedict[posename]['joints'][nUtils.getObjectName(link, 'joint')] = link.pose.bones[ 'Bone' ].rotation_euler.y bpy.ops.object.mode_set(mode='OBJECT') posedict = gUtils.roundFloatsInDict(posedict, ioUtils.getExpSettings().decimalPlaces) bUtils.updateTextFile(filename, yaml.dump(posedict, default_flow_style=False)) else: log("No model root provided to store the pose for", "ERROR")
def exportSMURFsScene(selected_only=True, subfolders=True): #TODO: Refactoring needed!!! """Exports all robots in a scene in *.smurfs format. :param selected_only: Decides if only models with selected root links are exported. :param subfolders: If True, the export is structured with subfolders for each model. """ objects = {} models = {} # models to be exported by name instances = [] #the instances to export for root in selectionUtils.getRoots(): if (not (selected_only and not root.select)): if "modelname" in root: objects[root['modelname']] = selectionUtils.getChildren(root) if not root['modelname'] in models: models[root['modelname']] = [root] else: models[root['modelname']].append(root) elif "reference" in root: instances.append(root["reference"]) if not root['reference'] in models: models[root['reference']] = [root] else: models[root['reference']].append(root) entities = [] for modelname in models: entitylist = models[modelname] unnamed_entities = 0 for entity in entitylist: if 'entityname' in entity: entityname = entity['entityname'] else: entityname = modelname+'_'+str(unnamed_entities) unnamed_entities += 1 entitypose = robotdictionary.deriveObjectPose(entity) uri = os.path.join(modelname, modelname+'.smurf') if subfolders else modelname+'.smurf' scenedict = {'name': entityname, 'type': 'smurf', 'file': uri, 'anchor': entity['anchor'] if 'anchor' in entity else 'none', 'position': entitypose['translation'], 'rotation': entitypose['rotation_quaternion'], 'pose': 'default'} # TODO: implement multiple poses entities.append(scenedict) if bpy.data.worlds[0].relativePath: outpath = securepath(os.path.expanduser(os.path.join(bpy.path.abspath("//"), bpy.data.worlds[0].path))) else: outpath = securepath(os.path.expanduser(bpy.data.worlds[0].path)) with open(os.path.join(outpath, bpy.data.worlds['World'].sceneName + '.smurfs'), 'w') as outputfile: outputfile.write("# SMURF scene: '" + bpy.data.worlds['World'].sceneName + "'; created " + datetime.now().strftime("%Y%m%d_%H:%M") + "\n") outputfile.write("# created with Phobos " + defs.version + " - https://github.com/rock-simulation/phobos\n\n") outputfile.write(yaml.dump({'entities': entities})) for modelname in objects: smurf_outpath = securepath(os.path.join(outpath, modelname) if subfolders else outpath) selectionUtils.selectObjects(objects[modelname], True) export(smurf_outpath) for instance in set(instances).difference(set(objects)): libpath = os.path.join(os.path.dirname(__file__), "lib") if os.path.isdir(os.path.join(outpath, instance)): shutil.rmtree(os.path.join(outpath, instance)) shutil.copytree(os.path.join(libpath, instance), os.path.join(outpath, instance))
def buildModelDictionary(root): """Builds a python dictionary representation of a SMURF model for export and inspection. :param root: bpy.types.objects :return: dict """ #os.system('clear') robot = {'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'lights': {}, 'groups': {}, 'chains': {} } # timestamp of model robot["date"] = datetime.now().strftime("%Y%m%d_%H:%M") if root.phobostype != 'link': log("Found no 'link' object as root of the robot model.", "ERROR", "buildModelDictionary") raise Exception(root.name + " is no valid root link.") else: if 'modelname' in root: robot['modelname'] = root["modelname"] else: log("No name for the model defines, setting to 'unnamed_model'", "WARNING", "buildModelDictionary") robot['modelname'] = 'unnamed_model' log("Creating dictionary for robot " + robot['modelname'] + " from object " + root.name, "INFO", "buildModelDictionary") # create tuples of objects belonging to model objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) linklist = [link for link in objectlist if link.phobostype == 'link'] # digest all the links to derive link and joint information log("Parsing links, joints and motors...", "INFO", "buildModelDictionary") for link in linklist: # parse link and extract joint and motor information linkdict, jointdict, motordict = deriveKinematics(link) robot['links'][linkdict['name']] = linkdict if jointdict: # joint will be None if link is a root robot['joints'][jointdict['name']] = jointdict if motordict: # motor will be None if no motor is attached or link is a root robot['motors'][motordict['name']] = motordict # add inertial information to link try: # if this link-inertial object is no present, we ignore the inertia! inertial = bpy.context.scene.objects['inertial_' + linkdict['name']] props = deriveDictEntry(inertial) if props is not None: robot['links'][linkdict['name']]['inertial'] = props except KeyError: log("No inertia for link " + linkdict['name'], "WARNING", "buildModelDictionary") # we need to combine inertia if certain objects are left out, and overwrite it inertials = (i for i in objectlist if i.phobostype == 'inertial' and "inertial/inertia" in i) editlinks = {} for i in inertials: if i.parent not in linklist: realparent = sUtils.getEffectiveParent(i) if realparent: parentname = nUtils.getObjectName(realparent) if parentname in editlinks: editlinks[parentname].append(i) else: editlinks[parentname] = [i] for linkname in editlinks: inertials = editlinks[linkname] try: inertials.append(bpy.context.scene.objects['inertial_' + linkname]) except KeyError: pass mv, cv, iv = inertia.fuseInertiaData(inertials) iv = inertia.inertiaMatrixToList(iv) if mv is not None and cv is not None and iv is not None: robot['links'][linkname]['inertial'] = {'mass': mv, 'inertia': iv, 'pose': {'translation': list(cv), 'rotation_euler': [0, 0, 0] } } # complete link information by parsing visuals and collision objects log("Parsing visual and collision (approximation) objects...", "INFO", "buildModelDictionary") for obj in objectlist: try: if obj.phobostype in ['visual', 'collision']: props = deriveDictEntry(obj) parentname = nUtils.getObjectName(sUtils.getEffectiveParent(obj)) robot['links'][parentname][obj.phobostype][nUtils.getObjectName(obj)] = props elif obj.phobostype == 'approxsphere': props = deriveDictEntry(obj) parentname = nUtils.getObjectName(sUtils.getEffectiveParent(obj)) robot['links'][parentname]['approxcollision'].append(props) except KeyError: try: log(parentname + " not found", "ERROR") except TypeError: log("No parent found for " + obj.name, "ERROR") # combine collision information for links for linkname in robot['links']: link = robot['links'][linkname] bitmask = 0 for collname in link['collision']: try: bitmask = bitmask | link['collision'][collname]['bitmask'] except KeyError: pass link['collision_bitmask'] = bitmask # parse sensors and controllers log("Parsing sensors and controllers...", "INFO", "buildModelDictionary") for obj in objectlist: if obj.phobostype in ['sensor', 'controller']: props = deriveDictEntry(obj) robot[obj.phobostype+'s'][nUtils.getObjectName(obj)] = props # parse materials log("Parsing materials...", "INFO", "buildModelDictionary") robot['materials'] = collectMaterials(objectlist) for obj in objectlist: if obj.phobostype == 'visual' and len(obj.data.materials) > 0: mat = obj.data.materials[0] matname = nUtils.getObjectName(mat, 'material') if matname not in robot['materials']: robot['materials'][matname] = deriveMaterial(mat) # this should actually never happen linkname = nUtils.getObjectName(sUtils.getEffectiveParent(obj)) robot['links'][linkname]['visual'][nUtils.getObjectName(obj)]['material'] = matname # gather information on groups of objects log("Parsing groups...", "INFO", "buildModelDictionary") for group in bpy.data.groups: # TODO: get rid of the "data" part and check for relation to robot if len(group.objects) > 0 and nUtils.getObjectName(group, 'group') != "RigidBodyWorld": robot['groups'][nUtils.getObjectName(group, 'group')] = deriveGroupEntry(group) # gather information on chains of objects log("Parsing chains...", "INFO", "buildModelDictionary") chains = [] for obj in objectlist: if obj.phobostype == 'link' and 'endChain' in obj: chains.extend(deriveChainEntry(obj)) for chain in chains: robot['chains'][chain['name']] = chain # gather information on lights log("Parsing lights...", "INFO", "buildModelDictionary") for obj in objectlist: if obj.phobostype == 'light': robot['lights'][nUtils.getObjectName(obj)] = deriveLight(obj) # add additional data to model robot.update(deriveTextData(robot['modelname'])) # shorten numbers in dictionary to n decimalPlaces and return it log("Rounding numbers...", "INFO", "buildModelDictionary") epsilon = 10**(-bpy.data.worlds[0].decimalPlaces) # TODO: implement this separately return epsilonToZero(robot, epsilon, bpy.data.worlds[0].decimalPlaces), objectlist
def deriveModelDictionary(root, name='', objectlist=[]): """Returns a dictionary representation of a Phobos model. If name is not specified, it overrides the modelname in the root. If the modelname is not defined at all, 'unnamed' will be used instead. Args: root(bpy_types.Object): root object of the model name(str, optional): name for the derived model (Default value = '') objectlist(list: bpy_types.Object): objects to derive the model from objectlist: (Default value = []) Returns: """ if root.phobostype not in ['link', 'submodel']: log(root.name + " is no valid 'link' or 'submodel' object.", "ERROR") return None # define model name if name: modelname = name elif 'model/name' in root: modelname = root['model/name'] else: modelname = 'unnamed' # define model version if 'model/version' in root: modelversion = root['model/version'] else: modelversion = 'undefined' modeldescription = bUtils.readTextFile('README.md') model = { 'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'meshes': {}, 'lights': {}, 'groups': {}, 'chains': {}, 'date': datetime.now().strftime("%Y%m%d_%H:%M"), 'name': modelname, 'version': modelversion, 'description': modeldescription, } log( "Creating dictionary for model '" + modelname + "' with root '" + root.name + "'.", 'INFO', prefix="\n", ) # create tuples of objects belonging to model if not objectlist: objectlist = sUtils.getChildren( root, selected_only=ioUtils.getExpSettings().selectedOnly, include_hidden=False ) linklist = [link for link in objectlist if link.phobostype == 'link'] # digest all the links to derive link and joint information log("Parsing links, joints and motors... " + (str(len(linklist))) + " total.", "INFO") for link in linklist: # parse link information (including inertia) model['links'][nUtils.getObjectName(link, 'link')] = deriveLink( link, logging=True, objectlist=objectlist ) # parse joint and motor information if sUtils.getEffectiveParent(link): # joint may be None if link is a root # to prevent confusion links are always defining also joints jointdict = deriveJoint(link, logging=True, adjust=True) log(" Setting joint type '{}' for link.".format(jointdict['type']), 'DEBUG') # first check if we have motor information in the joint properties # if so they can be extended/overwritten by motor objects later on if '$motor' in jointdict: motordict = jointdict['$motor'] # at least we need a type property if 'type' in motordict: # if no name is given derive it from the joint if not 'name' in motordict: motordict["name"] = jointdict['name'] model['motors'][motordict['name']] = motordict # link the joint by name: motordict['joint'] = jointdict['name'] del jointdict['$motor'] model['joints'][jointdict['name']] = jointdict for mot in [child for child in link.children if child.phobostype == 'motor']: motordict = motormodel.deriveMotor(mot, jointdict) # motor may be None if no motor is attached if motordict: log(" Added motor {} to link.".format(motordict['name']), 'DEBUG') if motordict['name'] in model["motors"]: model['motors'][motordict['name']].update(motordict) else: model['motors'][motordict['name']] = motordict # parse sensors and controllers sencons = [obj for obj in objectlist if obj.phobostype in ['sensor', 'controller']] log("Parsing sensors and controllers... {} total.".format(len(sencons)), 'INFO') for obj in sencons: props = deriveDictEntry(obj, names=True, objectlist=objectlist) model[obj.phobostype + 's'][nUtils.getObjectName(obj)] = props # parse materials log("Parsing materials...", 'INFO') model['materials'] = collectMaterials(objectlist) for obj in objectlist: if obj.phobostype == 'visual': mat = obj.active_material if mat: if mat.name not in model['materials']: model['materials'][mat.name] = deriveMaterial(mat) linkname = nUtils.getObjectName( sUtils.getEffectiveParent(obj, ignore_selection=bool(objectlist)) ) model['links'][linkname]['visual'][nUtils.getObjectName(obj)][ 'material' ] = mat.name # identify unique meshes log("Parsing meshes...", "INFO") for obj in objectlist: try: if ( (obj.phobostype == 'visual' or obj.phobostype == 'collision') and (obj['geometry/type'] == 'mesh') and (obj.data.name not in model['meshes']) ): model['meshes'][obj.data.name] = obj #todo2.9: for lod in obj.lod_levels: # if lod.object.data.name not in model['meshes']: # model['meshes'][lod.object.data.name] = lod.object except KeyError: log("Undefined geometry type in object " + obj.name, "ERROR") # gather information on groups of objects log("Parsing groups...", 'INFO') #todo2.9: TODO: get rid of the "data" part and check for relation to robot # for group in bpy.data.groups: # # skip empty groups # if not group.objects: # continue # # handle submodel groups separately from other groups # if 'submodeltype' in group.keys(): # continue # # TODO create code to derive Submodels # # model['submodels'] = deriveSubmodel(group) # elif nUtils.getObjectName(group, 'group') != "RigidBodyWorld": # model['groups'][nUtils.getObjectName(group, 'group')] = deriveGroupEntry(group) # gather information on chains of objects log("Parsing chains...", "INFO") chains = [] for obj in objectlist: if obj.phobostype == 'link' and 'endChain' in obj: chains.extend(deriveChainEntry(obj)) for chain in chains: model['chains'][chain['name']] = chain # gather information on lights log("Parsing lights...", "INFO") for obj in objectlist: if obj.phobostype == 'light': model['lights'][nUtils.getObjectName(obj)] = deriveLight(obj) # gather submechanism information from links log("Parsing submechanisms...", "INFO") def getSubmechanisms(link): """ Args: link: Returns: """ if 'submechanism/name' in link.keys(): submech = { 'type': link['submechanism/type'], 'contextual_name': link['submechanism/name'], 'name': link['submechanism/subtype'] if 'submechanism/subtype' in link else link['submechanism/type'], 'jointnames_independent': [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/independent'] ], 'jointnames_spanningtree': [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/spanningtree'] ], 'jointnames_active': [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/active'] ], # TODO: this should work in almost all cases, still a bit of a hack: 'file_path': '../submechanisms/urdf/' + link['submechanism/name'] + '.urdf', } log(' ' + submech['contextual_name'], 'DEBUG') else: submech = None mechanisms = [submech] if submech else [] for c in link.children: if c.phobostype in ['link', 'interface'] and c in objectlist: mechanisms.extend(getSubmechanisms(c)) return mechanisms model['submechanisms'] = getSubmechanisms(root) # add additional data to model model.update(deriveTextData(model['name'])) # shorten numbers in dictionary to n decimalPlaces and return it log("Rounding numbers to {} digits.".format(ioUtils.getExpSettings().decimalPlaces), 'INFO') model = roundFloatsInDict(model, ioUtils.getExpSettings().decimalPlaces) log("Sorting objects.", 'DEBUG') model = sortListsInDict(model) return model
def deriveModelDictionaryFromAssemblies(modelname): model = { 'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'meshes': {}, 'lights': {}, 'groups': {}, 'chains': {} } model['date'] = datetime.now().strftime("%Y%m%d_%H:%M") model['name'] = modelname assemblies = [a for a in bpy.data.objects if a.phobostype == 'assembly'] for a in assemblies: print('-----------------------', a.name, a['assemblyname'], '\n') rootlink = [ r for r in bpy.data.objects if sUtils.isRoot(r) and r['modelname'] == a['assemblyname'] ][0] adict = buildModelDictionary(rootlink) for l in adict['links']: model['links'][namespaced(l, a.name)] = namespaceLink( adict['links'][l], a.name) for j in adict['joints']: model['joints'][namespaced(j, a.name)] = namespaceJoint( adict['joints'][j], a.name) for m in adict['motors']: model['motors'][namespaced(m, a.name)] = namespaceMotor( adict['motors'][m], a.name) for mat in adict['materials']: if mat not in model['materials']: model['materials'][mat] = adict['materials'][mat] for mesh in adict['meshes']: model['meshes'][namespaced(mesh, a.name)] = adict['meshes'][mesh] print('\n\n') for a in assemblies: rootlink = [ r for r in bpy.data.objects if sUtils.isRoot(r) and r['modelname'] == a['assemblyname'] ][0] if a.parent: #print('combining...:', a.name) #print([l for l in model['links']]) parentassemblyname = a.parent.parent.parent['assemblyname'] #print(parentassemblyname) parentinterfacename = a.parent.parent['interface/name'] #print(parentinterfacename) parentassembly = [ r for r in bpy.data.objects if sUtils.isRoot(r) and r['modelname'] == parentassemblyname ][0] #print(parentassembly) parentinterface = [ i for i in sUtils.getChildren(parentassembly, ('interface', )) if i['interface/name'] == parentinterfacename ][0] #print(parentinterface) parentlinkname = parentinterface.parent.name #print(parentlinkname) # derive link pose for root link matrix = eUtils.getCombinedTransform(a, a.parent.parent.parent) pose = { 'rawmatrix': matrix, 'matrix': [list(vector) for vector in list(matrix)], 'translation': list(matrix.to_translation()), 'rotation_euler': list(matrix.to_euler()), 'rotation_quaternion': list(matrix.to_quaternion()) } model['links'][namespaced(rootlink.name, a.name)]['pose'] = pose # derive additional joint model['joints'][a.name] = deriveJoint(rootlink) #print(yaml.dump(model['joints'][a.name])) model['joints'][a.name]['name'] = namespaced(rootlink.name, a.name) model['joints'][a.name]['parent'] = namespaced( parentlinkname, a.parent.parent.parent.name) model['joints'][a.name]['child'] = namespaced( rootlink.name, a.name) #print(yaml.dump(model['joints'][a.name])) #print('######################') #for j in model['joints']: # print(model['joints'][j]['name'], model['joints'][j]['child'], model['joints'][j]['child']) #print('######################') return model
def execute(self, context): """ Args: context: Returns: """ roots = ioUtils.getExportModels() if not roots: log("No properly defined models selected or present in scene.", 'ERROR') return {'CANCELLED'} elif not self.exportall: roots = [root for root in roots if nUtils.getModelName(root) == self.modelname] if len(roots) > 1: log( "Ambiguous model definitions: " + self.modelname + " exists " + str(len(roots)) + " times.", "ERROR", ) return {'CANCELLED'} for root in roots: # setup paths exportpath = ioUtils.getExportPath() if not securepath(exportpath): log("Could not secure path to export to.", "ERROR") continue log("Export path: " + exportpath, "DEBUG") ioUtils.exportModel(models.deriveModelDictionary(root), exportpath) # select all exported models after export is done if ioUtils.getExpSettings().selectedOnly: for root in roots: objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) sUtils.selectObjects(objectlist, clear=False) else: bpy.ops.object.select_all(action='DESELECT') for root in roots: sUtils.selectObjects(list([root]), False) bpy.ops.phobos.select_model() # TODO: Move mesh export to individual formats? This is practically SMURF # export meshes in selected formats # for meshtype in meshes.mesh_types: # mesh_path = ioUtils.getOutputMeshpath(meshtype) # try: # typename = "export_mesh_" + meshtype # if getattr(bpy.data.worlds[0], typename): # securepath(mesh_path) # for meshname in model['meshes']: # meshes.mesh_types[meshtype]['export'](model['meshes'][meshname], mesh_path) # except KeyError: # log("No export function available for selected mesh function: " + meshtype, # "ERROR", "ExportModelOperator") # print(sys.exc_info()[0]) # TODO: Move texture export to individual formats? This is practically SMURF # export textures # if ioUtils.textureExportEnabled(): # texture_path = '' # for materialname in model['materials']: # mat = model['materials'][materialname] # for texturetype in ['diffuseTexture', 'normalTexture', 'displacementTexture']: # if texturetype in mat: # texpath = os.path.join(os.path.expanduser(bpy.path.abspath('//')), mat[texturetype]) # if os.path.isfile(texpath): # if texture_path == '': # texture_path = securepath(os.path.join(export_path, 'textures')) # log("Exporting textures to " + texture_path, "INFO", "ExportModelOperator") # try: # shutil.copy(texpath, os.path.join(texture_path, os.path.basename(mat[texturetype]))) # except shutil.SameFileError: # log("{} already in place".format(texturetype), "INFO", "ExportModelOperator") # report success to user log("Export successful.", "INFO", end="\n\n") return {'FINISHED'}
def buildModelDictionary(root): """Builds a python dictionary representation of a SMURF model for export and inspection. :param root: bpy.types.objects :return: dict """ #os.system('clear') robot = { 'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'lights': {}, 'groups': {}, 'chains': {} } # timestamp of model robot["date"] = datetime.now().strftime("%Y%m%d_%H:%M") if root.phobostype != 'link': log("Found no 'link' object as root of the robot model.", "ERROR", "buildModelDictionary") raise Exception(root.name + " is no valid root link.") else: if 'modelname' in root: robot['modelname'] = root["modelname"] else: log("No name for the model defines, setting to 'unnamed_model'", "WARNING", "buildModelDictionary") robot['modelname'] = 'unnamed_model' log( "Creating dictionary for robot " + robot['modelname'] + " from object " + root.name, "INFO", "buildModelDictionary") # create tuples of objects belonging to model objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) linklist = [link for link in objectlist if link.phobostype == 'link'] # digest all the links to derive link and joint information log("Parsing links, joints and motors...", "INFO", "buildModelDictionary") for link in linklist: # parse link and extract joint and motor information linkdict, jointdict, motordict = deriveKinematics(link) robot['links'][linkdict['name']] = linkdict if jointdict: # joint will be None if link is a root robot['joints'][jointdict['name']] = jointdict if motordict: # motor will be None if no motor is attached or link is a root robot['motors'][motordict['name']] = motordict # add inertial information to link try: # if this link-inertial object is no present, we ignore the inertia! inertial = bpy.context.scene.objects['inertial_' + linkdict['name']] props = deriveDictEntry(inertial) if props is not None: robot['links'][linkdict['name']]['inertial'] = props except KeyError: log("No inertia for link " + linkdict['name'], "WARNING", "buildModelDictionary") # we need to combine inertia if certain objects are left out, and overwrite it inertials = (i for i in objectlist if i.phobostype == 'inertial' and "inertial/inertia" in i) editlinks = {} for i in inertials: if i.parent not in linklist: realparent = sUtils.getEffectiveParent(i) if realparent: parentname = nUtils.getObjectName(realparent) if parentname in editlinks: editlinks[parentname].append(i) else: editlinks[parentname] = [i] for linkname in editlinks: inertials = editlinks[linkname] try: inertials.append(bpy.context.scene.objects['inertial_' + linkname]) except KeyError: pass mv, cv, iv = inertia.fuseInertiaData(inertials) iv = inertia.inertiaMatrixToList(iv) if mv is not None and cv is not None and iv is not None: robot['links'][linkname]['inertial'] = { 'mass': mv, 'inertia': iv, 'pose': { 'translation': list(cv), 'rotation_euler': [0, 0, 0] } } # complete link information by parsing visuals and collision objects log("Parsing visual and collision (approximation) objects...", "INFO", "buildModelDictionary") for obj in objectlist: try: if obj.phobostype in ['visual', 'collision']: props = deriveDictEntry(obj) parentname = nUtils.getObjectName( sUtils.getEffectiveParent(obj)) robot['links'][parentname][obj.phobostype][ nUtils.getObjectName(obj)] = props elif obj.phobostype == 'approxsphere': props = deriveDictEntry(obj) parentname = nUtils.getObjectName( sUtils.getEffectiveParent(obj)) robot['links'][parentname]['approxcollision'].append(props) except KeyError: try: log(parentname + " not found", "ERROR") except TypeError: log("No parent found for " + obj.name, "ERROR") # combine collision information for links for linkname in robot['links']: link = robot['links'][linkname] bitmask = 0 for collname in link['collision']: try: bitmask = bitmask | link['collision'][collname]['bitmask'] except KeyError: pass link['collision_bitmask'] = bitmask # parse sensors and controllers log("Parsing sensors and controllers...", "INFO", "buildModelDictionary") for obj in objectlist: if obj.phobostype in ['sensor', 'controller']: props = deriveDictEntry(obj) robot[obj.phobostype + 's'][nUtils.getObjectName(obj)] = props # parse materials log("Parsing materials...", "INFO", "buildModelDictionary") robot['materials'] = collectMaterials(objectlist) for obj in objectlist: if obj.phobostype == 'visual' and len(obj.data.materials) > 0: mat = obj.data.materials[0] matname = nUtils.getObjectName(mat, 'material') if matname not in robot['materials']: robot['materials'][matname] = deriveMaterial( mat) # this should actually never happen linkname = nUtils.getObjectName(sUtils.getEffectiveParent(obj)) robot['links'][linkname]['visual'][nUtils.getObjectName( obj)]['material'] = matname # gather information on groups of objects log("Parsing groups...", "INFO", "buildModelDictionary") for group in bpy.data.groups: # TODO: get rid of the "data" part and check for relation to robot if len(group.objects) > 0 and nUtils.getObjectName( group, 'group') != "RigidBodyWorld": robot['groups'][nUtils.getObjectName( group, 'group')] = deriveGroupEntry(group) # gather information on chains of objects log("Parsing chains...", "INFO", "buildModelDictionary") chains = [] for obj in objectlist: if obj.phobostype == 'link' and 'endChain' in obj: chains.extend(deriveChainEntry(obj)) for chain in chains: robot['chains'][chain['name']] = chain # gather information on lights log("Parsing lights...", "INFO", "buildModelDictionary") for obj in objectlist: if obj.phobostype == 'light': robot['lights'][nUtils.getObjectName(obj)] = deriveLight(obj) # add additional data to model robot.update(deriveTextData(robot['modelname'])) # shorten numbers in dictionary to n decimalPlaces and return it log("Rounding numbers...", "INFO", "buildModelDictionary") epsilon = 10**(-bpy.data.worlds[0].decimalPlaces ) # TODO: implement this separately return epsilonToZero(robot, epsilon, bpy.data.worlds[0].decimalPlaces), objectlist
def deriveModelDictionaryFromSubmodel(modelname): model = { 'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'meshes': {}, 'lights': {}, 'groups': {}, 'chains': {} } # collect general model properties model['date'] = datetime.now().strftime("%Y%m%d_%H:%M") model['name'] = modelname # collect all submodels submodels = [a for a in bpy.data.objects if a.phobostype == 'submodel'] # namespace links, joints, motors etc for each submodel for subm in submodels: print('-----------------------', subm.name, subm['submodelname'], '\n') rootlink = [ r for r in bpy.data.objects if sUtils.isRoot(r) and r['modelname'] == subm['submodelname'] ][0] adict = buildModelDictionary(rootlink) for l in adict['links']: model['links'][namespaced(l, subm.name)] = namespaceLink( adict['links'][l], subm.name) for j in adict['joints']: model['joints'][namespaced(j, subm.name)] = namespaceJoint( adict['joints'][j], subm.name) for m in adict['motors']: model['motors'][namespaced(m, subm.name)] = namespaceMotor( adict['motors'][m], subm.name) for mat in adict['materials']: if mat not in model['materials']: model['materials'][mat] = adict['materials'][mat] for mesh in adict['meshes']: model['meshes'][namespaced(mesh, subm.name)] = adict['meshes'][mesh] print('\n\n') for subm in submodels: rootlink = [ r for r in bpy.data.objects if sUtils.isRoot(r) and r['modelname'] == subm['submodelname'] ][0] if subm.parent: # get interfaces and parents parentsubmodelname = subm.parent.parent.parent['submodelname'] parentinterfacename = subm.parent.parent['interface/name'] parentsubmodel = [ r for r in bpy.data.objects if sUtils.isRoot(r) and r['modelname'] == parentsubmodelname ][0] parentinterface = [ i for i in sUtils.getChildren(parentsubmodel, ('interface', )) if i['interface/name'] == parentinterfacename ][0] parentlinkname = parentinterface.parent.name # derive link pose for root link matrix = eUtils.getCombinedTransform(subm, subm.parent.parent.parent) pose = { 'rawmatrix': matrix, 'matrix': [list(vector) for vector in list(matrix)], 'translation': list(matrix.to_translation()), 'rotation_euler': list(matrix.to_euler()), 'rotation_quaternion': list(matrix.to_quaternion()) } model['links'][namespaced(rootlink.name, subm.name)]['pose'] = pose # derive additional joint model['joints'][a.name] = deriveJoint(rootlink) #print(yaml.dump(model['joints'][a.name])) model['joints'][a.name]['name'] = namespaced(rootlink.name, a.name) model['joints'][a.name]['parent'] = namespaced( parentlinkname, a.parent.parent.parent.name) model['joints'][a.name]['child'] = namespaced( rootlink.name, a.name) #print(yaml.dump(model['joints'][a.name])) #print('######################') #for j in model['joints']: # print(model['joints'][j]['name'], model['joints'][j]['child'], model['joints'][j]['child']) #print('######################') return model
def deriveModelDictionary(root, name='', objectlist=[]): """Returns a dictionary representation of a Phobos model. If name is not specified, it overrides the modelname in the root. If the modelname is not defined at all, 'unnamed' will be used instead. Args: root(bpy_types.Object): root object of the model name(str): name for the derived model objectlist(list: bpy_types.Object): objects to derive the model from """ if root.phobostype not in ['link', 'submodel']: log(root.name + " is no valid 'link' or 'submodel' object.", "ERROR") return None # define model name if name: modelname = name elif 'modelname' in root: modelname = root['modelname'] else: modelname = 'unnamed' model = { 'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'meshes': {}, 'lights': {}, 'groups': {}, 'chains': {}, 'date': datetime.now().strftime("%Y%m%d_%H:%M"), 'name': modelname } log( "Creating dictionary for model '" + modelname + "' with root '" + root.name + "'.", 'INFO') # create tuples of objects belonging to model if not objectlist: objectlist = sUtils.getChildren( root, selected_only=ioUtils.getExpSettings().selectedOnly, include_hidden=False) linklist = [link for link in objectlist if link.phobostype == 'link'] # digest all the links to derive link and joint information log( "Parsing links, joints and motors... " + (str(len(linklist))) + " total.", "INFO") for link in linklist: # parse link information (including inertia) model['links'][nUtils.getObjectName(link, 'link')] = deriveLink(link) if sUtils.getEffectiveParent(link): # joint may be None if link is a root jointdict = deriveJoint(link) model['joints'][jointdict['name']] = jointdict motordict = deriveMotor(link, jointdict) # motor may be None if no motor is attached if motordict: model['motors'][motordict['name']] = motordict # combine inertia for each link, taking into account inactive links inertials = (i for i in objectlist if i.phobostype == 'inertial' and 'inertial/inertia' in i) editlinks = {} for i in inertials: if i.parent not in linklist: realparent = sUtils.getEffectiveParent( i, ignore_selection=bool(objectlist)) if realparent: parentname = nUtils.getObjectName(realparent) if parentname in editlinks: editlinks[parentname].append(i) else: editlinks[parentname] = [i] for linkname in editlinks: inertials = editlinks[linkname] try: inertials.append(bpy.context.scene.objects['inertial_' + linkname]) except KeyError: pass # get inertia data mass, com, inertia = inertiamodel.fuse_inertia_data(inertials) if not any(mass, com, inertia): continue # add inertia to model inertia = inertiamodel.inertiaMatrixToList(inertia) model['links'][linkname]['inertial'] = { 'mass': mass, 'inertia': inertia, 'pose': { 'translation': list(com), 'rotation_euler': [0, 0, 0] } } # complete link information by parsing visuals and collision objects log("Parsing visual and collision (approximation) objects...", 'INFO') for obj in objectlist: if obj.phobostype in ['visual', 'collision']: props = deriveDictEntry(obj) parentname = nUtils.getObjectName( sUtils.getEffectiveParent(obj, ignore_selection=bool(objectlist))) model['links'][parentname][obj.phobostype][nUtils.getObjectName( obj)] = props elif obj.phobostype == 'approxsphere': props = deriveDictEntry(obj) parentname = nUtils.getObjectName( sUtils.getEffectiveParent(obj, ignore_selection=bool(objectlist))) model['links'][parentname]['approxcollision'].append(props) # combine collision information for links for linkname in model['links']: link = model['links'][linkname] bitmask = 0 for collname in link['collision']: try: # bitwise OR to add all collision layers bitmask = bitmask | link['collision'][collname]['bitmask'] except KeyError: pass link['collision_bitmask'] = bitmask # parse sensors and controllers log("Parsing sensors and controllers...", 'INFO') for obj in objectlist: if obj.phobostype in ['sensor', 'controller']: props = deriveDictEntry(obj) model[obj.phobostype + 's'][nUtils.getObjectName(obj)] = props # parse materials log("Parsing materials...", 'INFO') model['materials'] = collectMaterials(objectlist) for obj in objectlist: if obj.phobostype == 'visual': mat = obj.active_material if mat: if mat.name not in model['materials']: model['materials'][mat.name] = deriveMaterial(mat) linkname = nUtils.getObjectName( sUtils.getEffectiveParent( obj, ignore_selection=bool(objectlist))) model['links'][linkname]['visual'][nUtils.getObjectName( obj)]['material'] = mat.name # identify unique meshes log("Parsing meshes...", "INFO") for obj in objectlist: try: if ((obj.phobostype == 'visual' or obj.phobostype == 'collision') and (obj['geometry/type'] == 'mesh') and (obj.data.name not in model['meshes'])): model['meshes'][obj.data.name] = obj for lod in obj.lod_levels: if lod.object.data.name not in model['meshes']: model['meshes'][lod.object.data.name] = lod.object except KeyError: log("Undefined geometry type in object " + obj.name, "ERROR") # gather information on groups of objects log("Parsing groups...", 'INFO') # TODO: get rid of the "data" part and check for relation to robot for group in bpy.data.groups: # skip empty groups if not group.objects: continue # handle submodel groups separately from other groups if 'submodeltype' in group.keys(): continue # TODO create code to derive Submodels # model['submodels'] = deriveSubmodel(group) elif nUtils.getObjectName(group, 'group') != "RigidBodyWorld": model['groups'][nUtils.getObjectName( group, 'group')] = deriveGroupEntry(group) # gather information on chains of objects log("Parsing chains...", "INFO") chains = [] for obj in objectlist: if obj.phobostype == 'link' and 'endChain' in obj: chains.extend(deriveChainEntry(obj)) for chain in chains: model['chains'][chain['name']] = chain # gather information on lights log("Parsing lights...", "INFO") for obj in objectlist: if obj.phobostype == 'light': model['lights'][nUtils.getObjectName(obj)] = deriveLight(obj) # gather submechanism information from links log("Parsing submechanisms...", "INFO") def getSubmechanisms(link): if 'submechanism/name' in link.keys(): submech = { 'type': link['submechanism/type'], 'contextual_name': link['submechanism/name'], 'name': link['submechanism/subtype'] if 'submechanism/subtype' in link else link['submechanism/type'], 'jointnames_independent': [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/independent'] ], 'jointnames_spanningtree': [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/spanningtree'] ], 'jointnames_active': [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/active'] ], # TODO: this should work in almost all cases, still a bit of a hack: 'file_path': '../submechanisms/urdf/' + link['submechanism/name'] + '.urdf' } log(' ' + submech['contextual_name'], 'DEBUG') else: submech = None mechanisms = [submech] if submech else [] for c in link.children: if c.phobostype in ['link', 'interface'] and c in objectlist: mechanisms.extend(getSubmechanisms(c)) return mechanisms model['submechanisms'] = getSubmechanisms(root) # add additional data to model model.update(deriveTextData(model['name'])) # shorten numbers in dictionary to n decimalPlaces and return it log("Rounding numbers...", "INFO") return roundFloatsInDict(model, ioUtils.getExpSettings().decimalPlaces)
def buildModelDictionary(root): """Builds a python dictionary representation of a Phobos model. :param root: bpy.types.objects :return: dict """ # TODO remove this comment # os.system('clear') model = { 'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'meshes': {}, 'lights': {}, 'groups': {}, 'chains': {} } # timestamp of model model["date"] = datetime.now().strftime("%Y%m%d_%H:%M") if root.phobostype not in ['link', 'submodel']: log("Found no 'link' or 'submodel' object as root of the robot model.", "ERROR") raise Exception(root.name + " is no valid root link.") else: if 'modelname' in root: model['name'] = root["modelname"] else: log("No name for the model defines, setting to 'unnamed_model'", "WARNING") model['name'] = 'unnamed_model' log( "Creating dictionary for robot " + model['name'] + " from object " + root.name, "INFO") # create tuples of objects belonging to model objectlist = sUtils.getChildren( root, selected_only=ioUtils.getExpSettings().selectedOnly, include_hidden=False) linklist = [link for link in objectlist if link.phobostype == 'link'] # digest all the links to derive link and joint information log("Parsing links, joints and motors..." + (str(len(linklist))), "INFO") for link in linklist: # parse link and extract joint and motor information linkdict, jointdict, motordict = deriveKinematics(link) model['links'][linkdict['name']] = linkdict # joint will be None if link is a root if jointdict: model['joints'][jointdict['name']] = jointdict # motor will be None if no motor is attached or link is a root if motordict: model['motors'][motordict['name']] = motordict # add inertial information to link # if this link-inertial object is no present, we ignore the inertia! try: inertial = bpy.context.scene.objects['inertial_' + linkdict['name']] props = deriveDictEntry(inertial) if props is not None: model['links'][linkdict['name']]['inertial'] = props except KeyError: log("No inertia for link " + linkdict['name'], "WARNING") # combine inertia if certain objects are left out, and overwrite it inertials = (i for i in objectlist if i.phobostype == 'inertial' and "inertial/inertia" in i) editlinks = {} for i in inertials: if i.parent not in linklist: realparent = sUtils.getEffectiveParent(i) if realparent: parentname = nUtils.getObjectName(realparent) if parentname in editlinks: editlinks[parentname].append(i) else: editlinks[parentname] = [i] for linkname in editlinks: inertials = editlinks[linkname] try: inertials.append(bpy.context.scene.objects['inertial_' + linkname]) except KeyError: pass mv, cv, iv = inertiamodel.fuseInertiaData(inertials) iv = inertiamodel.inertiaMatrixToList(iv) if mv is not None and cv is not None and iv is not None: model['links'][linkname]['inertial'] = { 'mass': mv, 'inertia': iv, 'pose': { 'translation': list(cv), 'rotation_euler': [0, 0, 0] } } # complete link information by parsing visuals and collision objects log("Parsing visual and collision (approximation) objects...", "INFO") for obj in objectlist: # try: if obj.phobostype in ['visual', 'collision']: props = deriveDictEntry(obj) parentname = nUtils.getObjectName(sUtils.getEffectiveParent(obj)) model['links'][parentname][obj.phobostype][nUtils.getObjectName( obj)] = props elif obj.phobostype == 'approxsphere': props = deriveDictEntry(obj) parentname = nUtils.getObjectName(sUtils.getEffectiveParent(obj)) model['links'][parentname]['approxcollision'].append(props) # TODO delete me? # except KeyError: # try: # log(parentname + " not found", "ERROR") # except TypeError: # log("No parent found for " + obj.name, "ERROR") # combine collision information for links for linkname in model['links']: link = model['links'][linkname] bitmask = 0 for collname in link['collision']: try: # bitwise OR to add all collision layers bitmask = bitmask | link['collision'][collname]['bitmask'] except KeyError: pass link['collision_bitmask'] = bitmask # parse sensors and controllers log("Parsing sensors and controllers...", "INFO") for obj in objectlist: if obj.phobostype in ['sensor', 'controller']: props = deriveDictEntry(obj) model[obj.phobostype + 's'][nUtils.getObjectName(obj)] = props # parse materials log("Parsing materials...", "INFO") model['materials'] = collectMaterials(objectlist) for obj in objectlist: if obj.phobostype == 'visual': mat = obj.active_material try: if mat.name not in model['materials']: # this should actually never happen model['materials'][mat.name] = deriveMaterial(mat) linkname = nUtils.getObjectName(sUtils.getEffectiveParent(obj)) model['links'][linkname]['visual'][nUtils.getObjectName( obj)]['material'] = mat.name except AttributeError: log("Could not parse material for object " + obj.name, "ERROR") # identify unique meshes log("Parsing meshes...", "INFO") for obj in objectlist: try: if ((obj.phobostype == 'visual' or obj.phobostype == 'collision') and (obj['geometry/type'] == 'mesh') and (obj.data.name not in model['meshes'])): model['meshes'][obj.data.name] = obj for lod in obj.lod_levels: if lod.object.data.name not in model['meshes']: model['meshes'][lod.object.data.name] = lod.object except KeyError: log("Undefined geometry type in object " + obj.name, "ERROR") # gather information on groups of objects log("Parsing groups...", "INFO") # TODO: get rid of the "data" part and check for relation to robot for group in bpy.data.groups: # skip empty groups if not group.objects: continue # handle submodel groups separately from other groups if 'submodeltype' in group.keys(): continue # TODO create code to derive Submodels # model['submodels'] = deriveSubmodel(group) elif nUtils.getObjectName(group, 'group') != "RigidBodyWorld": model['groups'][nUtils.getObjectName( group, 'group')] = deriveGroupEntry(group) # gather information on chains of objects log("Parsing chains...", "INFO") chains = [] for obj in objectlist: if obj.phobostype == 'link' and 'endChain' in obj: chains.extend(deriveChainEntry(obj)) for chain in chains: model['chains'][chain['name']] = chain # gather information on lights log("Parsing lights...", "INFO") for obj in objectlist: if obj.phobostype == 'light': model['lights'][nUtils.getObjectName(obj)] = deriveLight(obj) # gather submechanism information from links log("Parsing submechanisms...", "INFO") submechanisms = [] for link in linklist: if 'submechanism/name' in link.keys(): #for key in [key for key in link.keys() if key.startswith('submechanism/')]: # submechanisms.append({key.replace('submechanism/', ''): value # for key, value in link.items()}) submech = { 'name': link['submechanism/category'], 'type': link['submechanism/type'], 'contextual_name': link['submechanism/name'], 'jointnames_independent': [j.name for j in link['submechanism/independent']], 'jointnames_spanningtree': [j.name for j in link['submechanism/spanningtree']], 'jointnames_active': [j.name for j in link['submechanism/active']] } submechanisms.append(submech) model['submechanisms'] = submechanisms # add additional data to model model.update(deriveTextData(model['name'])) # shorten numbers in dictionary to n decimalPlaces and return it log("Rounding numbers...", "INFO") # TODO: implement this separately epsilon = 10**(-ioUtils.getExpSettings().decimalPlaces) return epsilonToZero(model, epsilon, ioUtils.getExpSettings().decimalPlaces)
def execute(self, context): """ Args: context: Returns: """ roots = ioUtils.getExportModels() if not roots: log("No properly defined models selected or present in scene.", 'ERROR') return {'CANCELLED'} elif not self.exportall: roots = [ root for root in roots if nUtils.getModelName(root) == self.modelname ] if len(roots) > 1: log( "Ambiguous model definitions: " + self.modelname + " exists " + str(len(roots)) + " times.", "ERROR", ) return {'CANCELLED'} for root in roots: # setup paths exportpath = ioUtils.getExportPath() if not securepath(exportpath): log("Could not secure path to export to.", "ERROR") continue log("Export path: " + exportpath, "DEBUG") ioUtils.exportModel(models.deriveModelDictionary(root), exportpath) # select all exported models after export is done if ioUtils.getExpSettings().selectedOnly: for root in roots: objectlist = sUtils.getChildren(root, selected_only=True, include_hidden=False) sUtils.selectObjects(objectlist, clear=False) else: bpy.ops.object.select_all(action='DESELECT') for root in roots: sUtils.selectObjects(list([root]), False) bpy.ops.phobos.select_model() # TODO: Move mesh export to individual formats? This is practically SMURF # export meshes in selected formats # for meshtype in meshes.mesh_types: # mesh_path = ioUtils.getOutputMeshpath(meshtype) # try: # typename = "export_mesh_" + meshtype # if getattr(bpy.data.worlds[0], typename): # securepath(mesh_path) # for meshname in model['meshes']: # meshes.mesh_types[meshtype]['export'](model['meshes'][meshname], mesh_path) # except KeyError: # log("No export function available for selected mesh function: " + meshtype, # "ERROR", "ExportModelOperator") # print(sys.exc_info()[0]) # TODO: Move texture export to individual formats? This is practically SMURF # export textures # if ioUtils.textureExportEnabled(): # texture_path = '' # for materialname in model['materials']: # mat = model['materials'][materialname] # for texturetype in ['diffuseTexture', 'normalTexture', 'displacementTexture']: # if texturetype in mat: # texpath = os.path.join(os.path.expanduser(bpy.path.abspath('//')), mat[texturetype]) # if os.path.isfile(texpath): # if texture_path == '': # texture_path = securepath(os.path.join(export_path, 'textures')) # log("Exporting textures to " + texture_path, "INFO", "ExportModelOperator") # try: # shutil.copy(texpath, os.path.join(texture_path, os.path.basename(mat[texturetype]))) # except shutil.SameFileError: # log("{} already in place".format(texturetype), "INFO", "ExportModelOperator") # report success to user log("Export successful.", "INFO", end="\n\n") return {'FINISHED'}
def deriveModelDictionary(root, name='', objectlist=[]): """Derives a dictionary representation of a Phobos model. If name is not specified, it overrides the modelname in the root. If the modelname is not defined at all, 'unnamed' will be used instead. :param root: root object of the model :type root: bpy.types.Object :param name: name for the derived model :type name: str :param objectlist: objects to derive the model from :type objectlist: list of bpy.types.Object :return: representation of the model based on the root object :rtype: dict """ if root.phobostype not in ['link', 'submodel']: log(root.name + " is no valid 'link' or 'submodel' object.", "ERROR") return None # define model name if name: modelname = name elif 'modelname' in root: modelname = root['modelname'] else: modelname = 'unnamed' model = { 'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'meshes': {}, 'lights': {}, 'groups': {}, 'chains': {}, 'date': datetime.now().strftime("%Y%m%d_%H:%M"), 'name': modelname } log( "Creating dictionary for model " + modelname + " with root " + root.name + ".", 'INFO') # create tuples of objects belonging to model if not objectlist: objectlist = sUtils.getChildren( root, selected_only=ioUtils.getExpSettings().selectedOnly, include_hidden=False) linklist = [link for link in objectlist if link.phobostype == 'link'] # digest all the links to derive link and joint information log( "Parsing links, joints and motors... " + (str(len(linklist))) + " total.", "INFO") for link in linklist: # parse link information (including inertia) model['links'][nUtils.getObjectName(link, 'link')] = derive_link(link) if sUtils.getEffectiveParent(link): # joint may be None if link is a root jointdict = deriveJoint(link) model['joints'][jointdict['name']] = jointdict motordict = deriveMotor(link, jointdict) # motor may be None if no motor is attached if motordict: model['motors'][motordict['name']] = motordict # TODO what was this supposed to do? # as it is only ever used by deriveSubmechanism we might want to move it...? # combine inertia if certain objects are left out, and overwrite it # inertials = (i for i in objectlist if i.phobostype == 'inertial' and 'inertia' in i) # editlinks = {} # for i in inertials: # if i.parent not in linklist: # realparent = sUtils.getEffectiveParent(i, ignore_selection=bool(objectlist)) # if realparent: # parentname = nUtils.getObjectName(realparent) # if parentname in editlinks: # editlinks[parentname].append(i) # else: # editlinks[parentname] = [i] # for linkname in editlinks: # inertials = editlinks[linkname] # try: # inertials.append(bpy.context.scene.objects['inertial_' + linkname]) # except KeyError: # pass # # get inertia data # mass, com, inertia = inertiamodel.fuse_inertia_data(inertials) # if not any(mass, com, inertia): # continue # # add inertia to model # inertia = inertiamodel.inertiaMatrixToList(inertia) # model['links'][linkname]['inertial'] = { # 'mass': mass, 'inertia': inertia, # 'pose': {'translation': list(com), # 'rotation_euler': [0, 0, 0]} # } # complete link information by parsing visuals and collision objects log("Parsing visual and collision (approximation) objects...", 'INFO') for obj in objectlist: if obj.phobostype in ['visual', 'collision']: props = deriveDictEntry(obj) parentname = nUtils.getObjectName( sUtils.getEffectiveParent(obj, ignore_selection=bool(objectlist))) model['links'][parentname][obj.phobostype][nUtils.getObjectName( obj)] = props elif obj.phobostype == 'approxsphere': props = deriveDictEntry(obj) parentname = nUtils.getObjectName( sUtils.getEffectiveParent(obj, ignore_selection=bool(objectlist))) model['links'][parentname]['approxcollision'].append(props) # combine collision information for links for linkname in model['links']: link = model['links'][linkname] bitmask = 0 for collname in link['collision']: try: # bitwise OR to add all collision layers bitmask = bitmask | link['collision'][collname]['bitmask'] except KeyError: pass link['collision_bitmask'] = bitmask # parse sensors and controllers log("Parsing sensors and controllers...", 'INFO') for obj in objectlist: if obj.phobostype in ['sensor', 'controller']: props = deriveDictEntry(obj) model[obj.phobostype + 's'][nUtils.getObjectName(obj)] = props # parse materials log("Parsing materials...", 'INFO') model['materials'] = collectMaterials(objectlist) for obj in objectlist: if obj.phobostype == 'visual': mat = obj.active_material try: if mat.name not in model['materials']: # this should actually never happen model['materials'][mat.name] = deriveMaterial(mat) linkname = nUtils.getObjectName( sUtils.getEffectiveParent( obj, ignore_selection=bool(objectlist))) model['links'][linkname]['visual'][nUtils.getObjectName( obj)]['material'] = mat.name except AttributeError: log("Could not parse material for object " + obj.name, "ERROR") # identify unique meshes log("Parsing meshes...", "INFO") for obj in objectlist: try: if ((obj.phobostype == 'visual' or obj.phobostype == 'collision') and (obj['geometry/type'] == 'mesh') and (obj.data.name not in model['meshes'])): model['meshes'][obj.data.name] = obj for lod in obj.lod_levels: if lod.object.data.name not in model['meshes']: model['meshes'][lod.object.data.name] = lod.object except KeyError: log("Undefined geometry type in object " + obj.name, "ERROR") # gather information on groups of objects log("Parsing groups...", 'INFO') # TODO: get rid of the "data" part and check for relation to robot for group in bpy.data.groups: # skip empty groups if not group.objects: continue # handle submodel groups separately from other groups if 'submodeltype' in group.keys(): continue # TODO create code to derive Submodels # model['submodels'] = deriveSubmodel(group) elif nUtils.getObjectName(group, 'group') != "RigidBodyWorld": model['groups'][nUtils.getObjectName( group, 'group')] = deriveGroupEntry(group) # gather information on chains of objects log("Parsing chains...", "INFO") chains = [] for obj in objectlist: if obj.phobostype == 'link' and 'endChain' in obj: chains.extend(deriveChainEntry(obj)) for chain in chains: model['chains'][chain['name']] = chain # gather information on lights log("Parsing lights...", "INFO") for obj in objectlist: if obj.phobostype == 'light': model['lights'][nUtils.getObjectName(obj)] = deriveLight(obj) # gather submechanism information from links log("Parsing submechanisms...", "INFO") submechanisms = [] for link in linklist: if 'submechanism/name' in link: indep = [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/independent'] ] spann = [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/spanningtree'] ] active = [ nUtils.getObjectName(j, 'joint') for j in link['submechanism/active'] ] submech = { 'type': link['submechanism/type'], 'contextual_name': link['submechanism/name'], 'jointnames_independent': indep, 'jointnames_spanningtree': spann, 'jointnames_active': active } submechanisms.append(submech) model['submechanisms'] = submechanisms # add additional data to model model.update(deriveTextData(model['name'])) # shorten numbers in dictionary to n decimalPlaces and return it log("Rounding numbers...", "INFO") # TODO: implement this separately return roundFloatsInDict(model, ioUtils.getExpSettings().decimalPlaces)
def deriveEntity(entity, outpath, savetosubfolder): """Derives the dictionary for a SMURF entity from the phobos model dictionary. :param entity: The smurf root object. :type entity: bpy.types.Object :param outpath: The path to export the smurf to. :type outpath: str :param savetosubfolder: If True the export path has a subfolder for this smurf entity. :type savetosubfolder: bool :return: dict - An entry for the scenes entitiesList """ smurf = entity # determine outpath for the smurf export # differentiate between full model and baked reference if "entity/isReference" in smurf: bpy.ops.scene.reload_models_and_poses_operator() modelsPosesColl = bpy.context.user_preferences.addons[ "phobos"].preferences.models_poses for robot_model in modelsPosesColl: if (smurf["modelname"] == robot_model.robot_name) and (smurf["entity/pose"] == robot_model.label): entitypose = models.deriveObjectPose(smurf) entry = models.initObjectProperties(smurf, 'entity', ['link', 'joint', 'motor']) entry.pop("isReference") entry['file'] = os.path.join( os.path.relpath(robot_model.path, outpath), smurf["modelname"] + ".smurf") if 'parent' not in entry and 'joint/type' in smurf and smurf[ 'joint/type'] == 'fixed': entry['parent'] = 'world' entry["position"] = entitypose["translation"] entry["rotation"] = entitypose["rotation_quaternion"] ''' with open(os.path.join(os.path.dirname(defs.__file__), "RobotLib.yml"), "r") as f: robots = yaml.load(f.read()) sourcepath = robots[smurf["modelname"]] for filename in os.listdir(sourcepath): fullpath = os.path.join(sourcepath, filename) if os.path.isfile(fullpath): shutil.copy2(fullpath, os.path.join(smurf_outpath, filename)) else: # remove old folders to prevent errors in copytree shutil.rmtree(os.path.join(smurf_outpath, filename), True) shutil.copytree(fullpath, os.path.join(smurf_outpath, filename)) ''' else: smurf_outpath = securepath( os.path.join(outpath, entity["modelname"] ) if savetosubfolder else outpath) log("smurf_outpath: " + outpath, "DEBUG", "exportSMURFsScene") log( "Exporting " + smurf["entity/name"] + " as a smurf entity to " + smurf_outpath, "INFO", "deriveSMURFEntity", "\n\n") subfolder = smurf["modelname"] if savetosubfolder else "" sUtils.selectObjects(sUtils.getChildren(smurf), clear=True) # re-select for mesh export model, objectlist = models.buildModelDictionary(smurf) export( model, objectlist, smurf_outpath) # FIXME: this is the export function from entities! entitypose = models.deriveObjectPose(smurf) entry = models.initObjectProperties(smurf, 'entity', ['link', 'joint', 'motor']) entry['file'] = (os.path.join(subfolder, smurf["modelname"] + ".smurf") if os.path.isfile(smurf_outpath) else os.path.join( subfolder, "smurf", smurf["modelname"] + ".smurf")) if 'parent' not in entry and 'joint/type' in smurf and smurf[ 'joint/type'] == 'fixed': entry['parent'] = 'world' entry["position"] = entitypose["translation"] entry["rotation"] = entitypose["rotation_quaternion"] return entry