def exportSMURFScene(entities, path): """Exports an arranged scene into SMURFS. It will export only entities with a valid entity/name, and entity/type property. :param selected_only: If True only selected entities get exported. :type selected_only: bool :param subfolder: If True the models are exported into separate subfolders :type subfolder: bool """ # TODO path consistency (Windows) with open(path + '.smurfs', 'w') as outputfile: sceneinfo = "# SMURF scene created at " + path + " " + datetime.now( ).strftime("%Y%m%d_%H:%M") + "\n" log(sceneinfo, "INFO") sceneinfo += "# created with Phobos " + version + " - https://github.com/rock-simulation/phobos\n\n" securepath(path) log("Exporting scene to " + path + '.smurfs', "INFO") outputfile.write(sceneinfo) epsilon = 10**(-bpy.data.worlds[0].phobosexportsettings.decimalPlaces ) # TODO: implement this separately entitiesdict = epsilonToZero( {'entities': entities}, epsilon, bpy.data.worlds[0].phobosexportsettings.decimalPlaces) outputfile.write(yaml.dump(entitiesdict))
def exportSMURFsScene(entities, path, selected_only=True, subfolder=True): """Exports an arranged scene into SMURFS. It will export only entities with a valid entity/name, and entity/type property. :param selected_only: If True only selected entities get exported. :type selected_only: bool :param subfolder: If True the models are exported into separate subfolders :type subfolder: bool """ outputlist = [] # identify all entities in the scene entities = [ e for e in [obj for obj in bpy.context.scene.objects if isEntity(obj)] if ((selected_only and e.select) or not selected_only) ] if len(entities) == 0: log("There are no entities to export!", "WARNING", __name__ + ".exportSMURFsScene") return log("Exporting scene to " + path, "INFO", "exportSMURFsScene") for entity in entities: log("Exporting " + str(entity["entity/name"]) + " to SMURFS", "INFO") if entity["entity/type"] in entity_types: if hasattr(entity_types[entity["entity/type"]], 'deriveEntity'): entry = entity_types[entity["entity/type"]].deriveEntity( entity, path, subfolder) # known entity export else: log("Required method " "deriveEntity" " not implemented", "ERROR") else: # generic entity export entry = deriveGenericEntity(entity) outputlist.append(entry) with open( os.path.join(path, bpy.data.worlds['World'].sceneName + '.smurfs'), 'w') as outputfile: sceneinfo = "# SMURF scene " + bpy.data.worlds[ 'World'].sceneName + "; created " + datetime.now().strftime( "%Y%m%d_%H:%M") + "\n" sceneinfo += "# created with Phobos " + version + " - https://github.com/rock-simulation/phobos\n\n" outputfile.write(sceneinfo) epsilon = 10**(-bpy.data.worlds[0].phobosexportsettings.decimalPlaces ) # TODO: implement this separately entitiesdict = epsilonToZero( {'entities': outputlist}, epsilon, bpy.data.worlds[0].phobosexportsettings.decimalPlaces) outputfile.write(yaml.dump(entitiesdict))
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 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 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 buildRobotDictionary(): """Builds a python dictionary representation of a Blender robot model for export and inspection.""" objectlist = bpy.context.selected_objects #notifications, faulty_objects = robotupdate.updateModel(bpy.context.selected_objects) #print(notifications) robot = {'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'groups': {}, 'chains': {}, 'lights': {} } #save timestamped version of model robot["date"] = datetime.now().strftime("%Y%m%d_%H:%M") root = selectionUtils.getRoot(bpy.context.selected_objects[0]) if root.phobostype != 'link': raise Exception("Found no 'link' object as root of the robot model.") else: if 'modelname' in root: robot['modelname'] = root["modelname"] else: robot['modelname'] = 'unnamed_robot' # digest all the links to derive link and joint information print('\nParsing links, joints and motors...') for obj in bpy.context.selected_objects: if obj.phobostype == 'link': link, joint, motor = deriveKinematics(obj) robot['links'][namingUtils.getObjectName(obj, phobostype="link")] = link # it's important that this is really the object's name if joint: # joint can be None if link is a root robot['joints'][joint['name']] = joint if motor: robot['motors'][joint['name']] = motor obj.select = False # add inertial information to link print('\n\nParsing inertials...') for l in robot['links']: #link = bpy.data.objects[l] NEW NAMING! link = selectionUtils.getObjectByName(l)[0] if selectionUtils.getObjectByName(l) is not None else "ERROR!" inertials = selectionUtils.getImmediateChildren(link, ['inertial']) if len(inertials) == 1: props, parent = deriveDictEntry(inertials[0]) if not (props is None or parent is None): # this may be the case if there is inertia information missing robot['links'][namingUtils.getObjectName(parent)]['inertial'] = props inertials[0].select = False elif len(inertials) > 1: for i in inertials: if namingUtils.getObjectName(i, phobostype="inertial") == 'inertial_' + l: props, parent = deriveDictEntry(i) robot['links'][namingUtils.getObjectName(parent, phobostype="link")]['inertial'] = props # FIXME: this has to be re-implemented #if linkinertial == None: # mass, com, inertia = inertia.fuseInertiaData(inertials) # parent = inertials[0].parent # matrix_local = mathutils.Matrix.Translation(mathutils.Vector(com)) # pose = {} # pose['matrix'] = [list(vector) for vector in list(matrix_local)] # pose['translation'] = list(matrix_local.to_translation()) # pose['rotation_euler'] = list(matrix_local.to_euler()) # pose['rotation_quaternion'] = list(matrix_local.to_quaternion()) # props = {'mass': mass, 'pose': pose, 'inertia': inertia} # robot['links'][parent.name]['inertial'] = props for i in inertials: i.select = False # complete link information by parsing visuals and collision objects print('\n\nParsing visual and collision (approximation) objects...') for obj in bpy.context.selected_objects: print("Parsing object " + namingUtils.getObjectName(obj)) if obj.phobostype in ['visual', 'collision']: props, parent = deriveDictEntry(obj) robot['links'][namingUtils.getObjectName(parent, phobostype="link")][obj.phobostype][namingUtils.getObjectName(obj, phobostype=obj.phobostype)] = props obj.select = False elif obj.phobostype == 'approxsphere': props, parent = deriveDictEntry(obj) robot['links'][namingUtils.getObjectName(parent)]['approxcollision'].append(props) obj.select = False # 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 print('\n\nParsing sensors and controllers...') for obj in bpy.context.selected_objects: if obj.phobostype in ['sensor', 'controller']: robot[obj.phobostype+'s'][namingUtils.getObjectName(obj)] = deriveDictEntry(obj) obj.select = False # parse materials print('\n\nParsing materials...') robot['materials'] = collectMaterials(objectlist) for obj in objectlist: if obj.phobostype == 'visual' and len(obj.data.materials) > 0: mat = obj.data.materials[0] if not namingUtils.getObjectName(mat) in robot['materials']: robot['materials'][namingUtils.getObjectName(mat)] = deriveMaterial(mat) #this should actually never happen robot['links'][namingUtils.getObjectName(obj.parent)]['visual'][namingUtils.getObjectName(obj, phobostype="visual")]['material'] = namingUtils.getObjectName(mat) # gather information on groups of objects print('\n\nParsing groups...') for group in bpy.data.groups: # TODO: get rid of the "data" part if len(group.objects) > 0 and namingUtils.getObjectName(group) != "RigidBodyWorld": robot['groups'][namingUtils.getObjectName(group)] = deriveGroupEntry(group) # gather information on chains of objects print('\n\nParsing chains...') chains = [] for obj in bpy.data.objects: if obj.phobostype == 'link' and 'endChain' in obj: chains.extend(deriveChainEntry(obj)) for chain in chains: robot['chains'][chain['name']] = chain # gather information on global lights print('\n\nParsing lights...') for obj in bpy.context.selected_objects: if obj.phobostype == 'light': robot['lights'][namingUtils.getObjectName(obj)] = deriveLight(obj) robot['poses'] = deriveStoredPoses() #shorten numbers in dictionary to n decimalPlaces and return it print('\n\nRounding numbers...') epsilon = 10**(-bpy.data.worlds[0].decimalPlaces) # TODO: implement this separately return generalUtils.epsilonToZero(robot, epsilon, bpy.data.worlds[0].decimalPlaces)
def buildRobotDictionary(): """Builds a python dictionary representation of a Blender robot model for export and inspection. """ objectlist = bpy.context.selected_objects #notifications, faulty_objects = robotupdate.updateModel(bpy.context.selected_objects) #print(notifications) robot = { 'links': {}, 'joints': {}, 'sensors': {}, 'motors': {}, 'controllers': {}, 'materials': {}, 'groups': {}, 'chains': {}, 'lights': {} } #save timestamped version of model robot["date"] = datetime.now().strftime("%Y%m%d_%H:%M") root = selectionUtils.getRoot(bpy.context.selected_objects[0]) if root.phobostype != 'link': raise Exception("Found no 'link' object as root of the robot model.") else: if 'modelname' in root: robot['modelname'] = root["modelname"] else: robot['modelname'] = 'unnamed_robot' # digest all the links to derive link and joint information print('\nParsing links, joints and motors...') for obj in bpy.context.selected_objects: if obj.phobostype == 'link': link, joint, motor = deriveKinematics(obj) robot['links'][namingUtils.getObjectName( obj, phobostype="link" )] = link # it's important that this is really the object's name if joint: # joint can be None if link is a root robot['joints'][joint['name']] = joint if motor: robot['motors'][joint['name']] = motor obj.select = False # add inertial information to link print('\n\nParsing inertials...') for l in robot['links']: #link = bpy.data.objects[l] NEW NAMING! link = selectionUtils.getObjectByName( l)[0] if selectionUtils.getObjectByName( l) is not None else "ERROR!" inertials = selectionUtils.getImmediateChildren(link, ['inertial']) if len(inertials) == 1: props, parent = deriveDictEntry(inertials[0]) if not ( props is None or parent is None ): # this may be the case if there is inertia information missing robot['links'][namingUtils.getObjectName( parent)]['inertial'] = props inertials[0].select = False elif len(inertials) > 1: for i in inertials: if i.name == 'inertial_' + l: props, parent = deriveDictEntry(i) robot['links'][namingUtils.getObjectName( parent, phobostype="link")]['inertial'] = props # FIXME: this has to be re-implemented #if linkinertial == None: # mass, com, inertia = inertia.fuseInertiaData(inertials) # parent = inertials[0].parent # matrix_local = mathutils.Matrix.Translation(mathutils.Vector(com)) # pose = {} # pose['matrix'] = [list(vector) for vector in list(matrix_local)] # pose['translation'] = list(matrix_local.to_translation()) # pose['rotation_euler'] = list(matrix_local.to_euler()) # pose['rotation_quaternion'] = list(matrix_local.to_quaternion()) # props = {'mass': mass, 'pose': pose, 'inertia': inertia} # robot['links'][parent.name]['inertial'] = props for i in inertials: i.select = False # complete link information by parsing visuals and collision objects print('\n\nParsing visual and collision (approximation) objects...') capsules_list = [] for obj in bpy.context.selected_objects: print("Parsing object " + namingUtils.getObjectName(obj)) if obj.phobostype in ['visual', 'collision']: props, parent = deriveDictEntry(obj) if all([ key in props for key in ['cylinder', 'sphere1', 'sphere2'] ]): # this is the case with simulated capsules capsules_list.append({ 'link': parent.name, 'name': props['cylinder']['name'][:-len('_cylinder')], 'radius': props['cylinder']['geometry']['radius'], 'length': props['cylinder']['geometry']['length'] + 2 * props['cylinder']['geometry']['radius'], #'bitmask': props['cylinder']['bitmask'] }) for key in props: robot['links'][namingUtils.getObjectName( parent, phobostype="link")][obj.phobostype][key] = props[key] else: robot['links'][namingUtils.getObjectName( parent, phobostype="link")][obj.phobostype][ namingUtils.getObjectName( obj, phobostype=obj.phobostype)] = props obj.select = False elif obj.phobostype == 'approxsphere': props, parent = deriveDictEntry(obj) robot['links'][namingUtils.getObjectName( parent)]['approxcollision'].append(props) obj.select = False robot['capsules'] = capsules_list # 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 print('\n\nParsing sensors and controllers...') for obj in bpy.context.selected_objects: if obj.phobostype in ['sensor', 'controller']: robot[obj.phobostype + 's'][namingUtils.getObjectName(obj)] = deriveDictEntry(obj) obj.select = False # parse materials print('\n\nParsing materials...') robot['materials'] = collectMaterials(objectlist) for obj in objectlist: if obj.phobostype == 'visual' and len(obj.data.materials) > 0: mat = obj.data.materials[0] if not namingUtils.getObjectName(mat) in robot['materials']: robot['materials'][namingUtils.getObjectName( mat)] = deriveMaterial( mat) #this should actually never happen robot['links'][namingUtils.getObjectName( obj.parent)]['visual'][namingUtils.getObjectName( obj, phobostype="visual" )]['material'] = namingUtils.getObjectName(mat) # gather information on groups of objects print('\n\nParsing groups...') for group in bpy.data.groups: # TODO: get rid of the "data" part if len(group.objects) > 0 and namingUtils.getObjectName( group) != "RigidBodyWorld": robot['groups'][namingUtils.getObjectName( group)] = deriveGroupEntry(group) # gather information on chains of objects print('\n\nParsing chains...') chains = [] for obj in bpy.data.objects: if obj.phobostype == 'link' and 'endChain' in obj: chains.extend(deriveChainEntry(obj)) for chain in chains: robot['chains'][chain['name']] = chain # gather information on global lights print('\n\nParsing lights...') for obj in bpy.context.selected_objects: if obj.phobostype == 'light': robot['lights'][namingUtils.getObjectName(obj)] = deriveLight(obj) robot['poses'] = deriveStoredPoses() #shorten numbers in dictionary to n decimalPlaces and return it print('\n\nRounding numbers...') epsilon = 10**(-bpy.data.worlds[0].decimalPlaces ) # TODO: implement this separately return generalUtils.epsilonToZero(robot, epsilon, bpy.data.worlds[0].decimalPlaces)