def deriveChainEntry(obj): """Derives a phobos dict entry for a kinematic chain ending in the provided object. :param obj: :return: """ returnchains = [] if 'endChain' in obj: chainlist = obj['endChain'] for chainName in chainlist: chainclosed = False parent = obj chain = { 'name': chainName, 'start': '', 'end': nUtils.getObjectName(obj), 'elements': [] } while not chainclosed: if parent.parent is None: # FIXME: use effectiveParent log("Unclosed chain, aborting parsing chain " + chainName, "ERROR", "deriveChainEntry") chain = None break chain['elements'].append(parent.name) parent = parent.parent # FIXME: use effectiveParent if 'startChain' in parent: startchain = parent['startChain'] if chainName in startchain: chain['start'] = nUtils.getObjectName(parent) chain['elements'].append(nUtils.getObjectName(parent)) chainclosed = True if chain is not None: returnchains.append(chain) return returnchains
def deriveDictEntry(obj): """Derives a phobos dictionary entry from the provided object. :param obj: The object to derive the dict entry (phobos data structure) from. :type obj: bpy_types.Object :return: tuple """ try: if obj.phobostype == 'inertial': props = deriveInertial(obj) elif obj.phobostype == 'visual': props = deriveVisual(obj) elif obj.phobostype == 'collision': props = deriveCollision(obj) elif obj.phobostype == 'approxsphere': props = deriveApproxsphere(obj) elif obj.phobostype == 'sensor': props = deriveSensor(obj) elif obj.phobostype == 'controller': props = deriveController(obj) elif obj.phobostype == 'light': props = deriveLight(obj) except KeyError: log("A KeyError occurred due to unspecifiable missing model data.", "DEBUG", "deriveDictEntry") return None, None return props
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 getRoot(obj=None): """ Returns the root of an object, i.e. the first going up the tree containing a model name or entity name. If there is no such object up the tree, the tree's top-most object is returned. If no object is given, find root of the active object. If there is no active object, throw an error. :param obj: The object to find the root for. :type obj: bpy.types.Object. :return: The root object. """ if not obj: for anobj in bpy.context.scene.objects: if bpy.context.scene.objects.active == anobj: obj = anobj break else: log("No root object found! Check your object selection", "ERROR") return None child = obj while child.parent and not ('modelname' in child or 'entity/name' in child): child = child.parent return child
def loadPose(modelname, posename): """ Load and apply a robot's stored pose. :param modelname: The model's name. :type modelname: str. :param posename: The name the pose is stored under. :type posename: str. :return Nothing. """ load_file = bUtils.readTextFile(modelname + '::poses') if load_file == '': log('No poses stored.', 'ERROR', 'loadPose') return poses = yaml.load(load_file) try: pose = poses[posename] prev_mode = bpy.context.mode bpy.ops.object.mode_set(mode='POSE') for obj in sUtils.getObjectsByPhobostypes(['link']): if nUtils.getObjectName(obj, 'joint') in pose['joints']: obj.pose.bones['Bone'].rotation_mode = 'XYZ' obj.pose.bones['Bone'].rotation_euler.y = pose['joints'][ nUtils.getObjectName(obj, 'joint')] bpy.ops.object.mode_set(mode=prev_mode) except KeyError: log('No pose with name ' + posename + ' stored for model ' + modelname, 'ERROR', "loadPose")
def addNamespace(obj): """This function namespaces a given blender object. :param obj: The object to namespace. :type obj: bpy.types.Object """ types = defs.subtypes name = obj.name root = selection.getRoot(obj) namespace = root[ "entity/name"] if root != None and "entity/name" in root else None if not namespace: log( "The obj " + getObjectName(obj) + "has no namespace to append to. Aborting.", "ERROR") return obj.name = namespace + "::" + name for ptype in types: typetag = ptype + "/type" nametag = ptype + "/name" if (typetag in obj or ("phobostype" in obj and obj.phobostype == ptype)) and nametag not in obj: obj[nametag] = name
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 loadPose(modelname, posename): """ Load and apply a robot's stored pose. :param modelname: The model's name. :type modelname: str. :param posename: The name the pose is stored under. :type posename: str. :return Nothing. """ load_file = bUtils.readTextFile(modelname + '::poses') if load_file == '': log('No poses stored.', 'ERROR', 'loadPose') return poses = yaml.load(load_file) try: pose = poses[posename] prev_mode = bpy.context.mode bpy.ops.object.mode_set(mode='POSE') for obj in sUtils.getObjectsByPhobostypes(['link']): if nUtils.getObjectName(obj, 'joint') in pose['joints']: obj.pose.bones['Bone'].rotation_mode = 'XYZ' obj.pose.bones['Bone'].rotation_euler.y = pose['joints'][nUtils.getObjectName(obj, 'joint')] bpy.ops.object.mode_set(mode=prev_mode) except KeyError: log('No pose with name ' + posename + ' stored for model ' + modelname, 'ERROR', "loadPose")
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): startLog(self) location = bpy.context.scene.cursor_location objects = [] controllers = [] for obj in bpy.context.selected_objects: if obj.phobostype == "controller": controllers.append(obj) else: objects.append(obj) if len(controllers) <= 0: blenderUtils.createPrimitive("controller", "sphere", self.controller_scale, defs.layerTypes["sensor"], "controller", location) bpy.context.scene.objects.active.phobostype = "controller" bpy.context.scene.objects.active.name = "controller" controllers.append(bpy.context.scene.objects.active) #empty index list so enable robotupdate of controller for ctrl in controllers: for key in ctrl.keys(): if key.find("index") >= 0: del ctrl[key] log("Deleting " + str(key) + " in " + ctrl.name, "INFO") i = 1 for obj in objects: if obj.phobostype == "link": ctrl["index"+(str(i) if i >= 10 else "0"+str(i))] = namingUtils.getObjectName(obj) i += 1 log("Added joints to (new) controller(s).", "INFO") #for prop in defs.controllerProperties[self.controller_type]: # for ctrl in controllers: # ctrl[prop] = defs.controllerProperties[prop] endLog() return {'FINISHED'}
def deriveMotor(obj, joint): """This function derives a motor from an object and joint. :param obj: The blender object to derive the motor from. :type obj: bpy_types.Object :param joint: The phobos joint to derive the constraints from. :type joint: dict :return: dict """ props = initObjectProperties(obj, phobostype='motor', ignoretypes=['link', 'joint']) if len(props) > 1: # if there are any 'motor' tags and not only a name props['joint'] = obj['joint/name'] if 'joint/name' in obj else obj.name try: if props['type'] == 'PID': if 'limits' in joint: props['minValue'] = joint['limits']['lower'] props['maxValue'] = joint['limits']['upper'] elif props['type'] == 'DC': props['minValue'] = 0 props['maxValue'] = props["maxSpeed"] except KeyError: log("Missing data in motor " + obj.name + '. No motor created.', "WARNING", "deriveMotor") return None return props else: return None # return None if no motor is attached
def deriveChainEntry(obj): """Derives a phobos dict entry for a kinematic chain ending in the provided object. :param obj: :return: """ returnchains = [] if 'endChain' in obj: chainlist = obj['endChain'] for chainName in chainlist: chainclosed = False parent = obj chain = {'name': chainName, 'start': '', 'end': nUtils.getObjectName(obj), 'elements': []} while not chainclosed: if parent.parent is None: # FIXME: use effectiveParent log("Unclosed chain, aborting parsing chain " + chainName, "ERROR", "deriveChainEntry") chain = None break chain['elements'].append(parent.name) parent = parent.parent # FIXME: use effectiveParent if 'startChain' in parent: startchain = parent['startChain'] if chainName in startchain: chain['start'] = nUtils.getObjectName(parent) chain['elements'].append(nUtils.getObjectName(parent)) chainclosed = True if chain is not None: returnchains.append(chain) return returnchains
def deriveTextData(modelname): """ Collect additional data stored for a specific model. :param modelname: Name of the model for which data should be derived. :return: A dictionary containing additional data. """ datadict = {} datatextfiles = [ text for text in bpy.data.texts if text.name.startswith(modelname + '::') ] for text in datatextfiles: try: dataname = text.name.split('::')[-1] except IndexError: log("Possibly invalidly named model data text file: " + modelname, "WARNING", "deriveTextData") try: data = yaml.load(bUtils.readTextFile(text.name)) except yaml.scanner.ScannerError: log("Invalid formatting of data file: " + dataname, "ERROR", "deriveTextData") if data: datadict[dataname] = data return datadict
def deriveCollision(obj): """This function derives the collision information from an object. :param obj: The blender object to derive the collision information from. :type obj: bpy_types.Object :return: dict """ try: collision = initObjectProperties(obj, phobostype='collision', ignoretypes='geometry') collision['geometry'] = deriveGeometry(obj) collision['pose'] = deriveObjectPose(obj) # the bitmask is cut to length = 16 and reverted for int parsing try: collision['bitmask'] = int( ''.join([ '1' if group else '0' for group in obj.rigid_body.collision_groups[:16] ])[::-1], 2) except AttributeError: pass except KeyError: log("Missing data in collision object " + obj.name, "ERROR", "deriveCollision") return None return collision
def openScriptInEditor(scriptname): if scriptname in bpy.data.texts: for area in bpy.context.screen.areas: if area.type == "TEXT_EDITOR": area.spaces.active.text = bpy.data.texts[scriptname] else: log("There is no script named " + scriptname + "!", "ERROR")
def deriveMaterial(mat): """This function takes a blender material and creates a phobos representation from it :param mat: The blender material to derive a phobos material from :type mat: bpy.types.Material :return: dict """ material = initObjectProperties(mat, 'material') material['name'] = mat.name material['diffuseColor'] = dict(zip(['r', 'g', 'b'], [mat.diffuse_intensity * num for num in list(mat.diffuse_color)])) material['ambientColor'] = dict(zip(['r', 'g', 'b'], [mat.ambient * mat.diffuse_intensity * num for num in list(mat.diffuse_color)])) material['specularColor'] = dict(zip(['r', 'g', 'b'], [mat.specular_intensity * num for num in list(mat.specular_color)])) if mat.emit > 0: material['emissionColor'] = dict(zip(['r', 'g', 'b'], [mat.emit * mat.specular_intensity * num for num in list(mat.specular_color)])) material['shininess'] = mat.specular_hardness/2 if mat.use_transparency: material['transparency'] = 1.0-mat.alpha for tex in mat.texture_slots: # there are always 18 slots, regardless of whether they are filled or not if tex is not None: try: if tex.use_map_color_diffuse: # regular diffuse color texture material['diffuseTexture'] = mat.texture_slots[0].texture.image.filepath.replace('//', '') # grab the first texture if tex.use_map_normal: # normal map material['normalTexture'] = mat.texture_slots[0].texture.image.filepath.replace('//', '') # grab the first texture if tex.use_map_displacement: # displacement map material['displacementTexture'] = mat.texture_slots[0].texture.image.filepath.replace('//', '') # grab the first texture except (KeyError, AttributeError): log("None or incomplete texture data for material " + nUtils.getObjectName(mat, 'material'), "WARNING", "deriveMaterial") return material
def execute(self, context): startLog(self) self.distance, self.distVector = generalUtils.distance( bpy.context.selected_objects) log("distance: " + str(self.distance) + ", " + str(self.distVector), "INFO") endLog() return {'FINISHED'}
def execute(self, context): startLog(self) selectionUtils.selectByName(self.errorObj) for message in defs.checkMessages[self.errorObj]: log(message, 'INFO') endLog() return {'FINISHED'}
def execute(self, context): startLog(self) for obj in bpy.context.selected_objects: if obj.phobostype == 'collision' or obj.phobostype == 'visual': obj['geometry/type'] = self.geomType else: log("The object '" + obj.name + "' is no collision or visual") endLog() return {'FINISHED'}
def execute(self, context): startLog(self) root = selectionUtils.getRoot(bpy.context.active_object) if root == None: log("Could not set modelname due to missing root link. No name was set.", "ERROR") return {'FINISHED'} root["modelname"] = self.modelname endLog() return {'FINISHED'}
def calculateSum(objects, numeric_prop): """Returns sum of *numeric_prop* in *objects*.""" numsum = 0 for obj in objects: try: numsum += obj[numeric_prop] except KeyError: log("The object '" + getObjectName(obj) + "' has not property '" + numeric_prop + "'") return numsum
def execute(self, context): startLog(self) file = self.filepath.split("/")[-1] if self.filepath.endswith(".bake"): zipF = zipfile.ZipFile(self.filepath, mode="r") zipF.extractall(path=os.path.join(self.libpath, file.split(".")[0])) else: log("This is no robot bake!", "ERROR") endLog() return {"FINISHED"}
def execute(self, context): startLog(self) root = sUtils.getRoot(context.selected_objects[0]) if root.phobostype != 'link': log("Selection includes objects not parented to any model root, please adapt selection.", "ERROR", "ExportModelOperator") else: model, objectlist = robotdictionary.buildModelDictionary(root) exporter.export(model, objectlist) endLog() return {'FINISHED'}
def execute(self, context): startLog(self) root = sUtils.getRoot(context.active_object) if root == None: log( "Could not set modelname due to missing root link. No name was set.", "ERROR") return {'FINISHED'} root["modelname"] = self.modelname endLog() return {'FINISHED'}
def deriveJointType(joint, adjust=False): """ Derives the type of the joint defined by the armature object 'joint' based on the constraints defined in the joint. :param joint: The joint you want to derive its type from. :type joint: bpy_types.Object :param adjust: Decides whether or not the type of the joint is adjusted after detecting (without checking whether the property "type" was previously defined in the armature or not). :type adjust: bool. :return: tuple(2) -- jtype, crot """ jtype = 'floating' # 'universal' in MARS nomenclature cloc = None crot = None limrot = None # we pick the first bone in the armature as there is only one for c in joint.pose.bones[0].constraints: if c.type == 'LIMIT_LOCATION': cloc = [ c.use_min_x and c.min_x == c.max_x, c.use_min_y and c.min_y == c.max_y, c.use_min_z and c.min_z == c.max_z ] elif c.type == 'LIMIT_ROTATION': limrot = c crot = [ c.use_limit_x and (c.min_x != 0 or c.max_x != 0), c.use_limit_y and (c.min_y != 0 or c.max_y != 0), c.use_limit_z and (c.min_z != 0 or c.max_z != 0) ] ncloc = sum(cloc) if cloc else None ncrot = sum(( limrot.use_limit_x, limrot.use_limit_y, limrot.use_limit_z, )) if limrot else None if cloc: # = if there is any constraint at all, as all joints but floating ones have translation limits if ncloc == 3: # fixed or revolute if ncrot == 3: if sum(crot) > 0: jtype = 'revolute' else: jtype = 'fixed' elif ncrot == 2: jtype = 'continuous' elif ncloc == 2: jtype = 'prismatic' elif ncloc == 1: jtype = 'planar' if adjust: joint['joint/type'] = jtype log( "Set type of joint '" + namingUtils.getObjectName(joint) + "'to '" + jtype + "'.", "INFO") return jtype, crot
def assignMaterial(obj, materialname): if materialname not in bpy.data.materials: if materialname in defs.defaultmaterials: materials.createPhobosMaterials() else: # print("###ERROR: material to be assigned does not exist.") log("Material to be assigned does not exist.", "ERROR") return None obj.data.materials.append(bpy.data.materials[materialname]) if bpy.data.materials[materialname].use_transparency: obj.show_transparent = True
def calculateSum(objects, numeric_prop): """Returns sum of *numeric_prop* in *objects*. """ numsum = 0 for obj in objects: try: numsum += obj[numeric_prop] except KeyError: log("The object '" + namingUtils.getObjectName(obj) + "' has not property '" + numeric_prop + "'") return numsum
def execute(self, context): startLog(self) root = sUtils.getRoot(context.selected_objects[0]) if root.phobostype != 'link': log( "Selection includes objects not parented to any model root, please adapt selection.", "ERROR", "ExportModelOperator") else: model, objectlist = robotdictionary.buildModelDictionary(root) exporter.export(model, objectlist) endLog() return {'FINISHED'}
def calculateSum(objects, numeric_prop): """Returns sum of *numeric_prop* in *objects*. """ numsum = 0 for obj in objects: try: numsum += obj[numeric_prop] except KeyError: log(obj.phobostype + " object " + obj.name + " does not contain '" + numeric_prop + "'", "WARNING", "calculateSum") return numsum
def find_in_list(alist, prop, value): """Returns the index of the first object in a list which has a field named *prop* with value *value*. If no such object is found, returns -1.""" n = -1 for i in range(len(alist)): try: if alist[i][prop] == value: n = i break except KeyError: log("The object at index " + str(i) + " has no property " + str(prop)) return n
def deriveMaterial(mat): """This function takes a blender material and creates a phobos representation from it :param mat: The blender material to derive a phobos material from :type mat: bpy.types.Material :return: dict """ material = initObjectProperties(mat, 'material') material['name'] = mat.name material['diffuseColor'] = dict( zip(['r', 'g', 'b'], [mat.diffuse_intensity * num for num in list(mat.diffuse_color)])) material['ambientColor'] = dict( zip(['r', 'g', 'b'], [ mat.ambient * mat.diffuse_intensity * num for num in list(mat.diffuse_color) ])) material['specularColor'] = dict( zip(['r', 'g', 'b'], [mat.specular_intensity * num for num in list(mat.specular_color)])) if mat.emit > 0: material['emissionColor'] = dict( zip(['r', 'g', 'b'], [ mat.emit * mat.specular_intensity * num for num in list(mat.specular_color) ])) material['shininess'] = mat.specular_hardness / 2 if mat.use_transparency: material['transparency'] = 1.0 - mat.alpha for tex in mat.texture_slots: # there are always 18 slots, regardless of whether they are filled or not if tex is not None: try: if tex.use_map_color_diffuse: # regular diffuse color texture material['diffuseTexture'] = mat.texture_slots[ 0].texture.image.filepath.replace( '//', '') # grab the first texture if tex.use_map_normal: # normal map material['normalTexture'] = mat.texture_slots[ 0].texture.image.filepath.replace( '//', '') # grab the first texture if tex.use_map_displacement: # displacement map material['displacementTexture'] = mat.texture_slots[ 0].texture.image.filepath.replace( '//', '') # grab the first texture except (KeyError, AttributeError): log( "None or incomplete texture data for material " + nUtils.getObjectName(mat, 'material'), "WARNING", "deriveMaterial") return material
def readTextFile(textfilename): """This function returns the content of a specified text file. :param textfilename: The blender textfiles name. :type textfilename: str :return: str - the textfiles content. """ try: return "\n".join([l.body for l in bpy.data.texts[textfilename].lines]) except KeyError: log("No text file " + textfilename + " found. Setting", "INFO") return ""
def readTextFile(textfilename): """This function returns the content of a specified text file. :param textfilename: The blender textfiles name. :type textfilename: str :return: str - the textfiles content. """ try: return "\n".join([l.body for l in bpy.data.texts[textfilename].lines]) except KeyError: log("No text file " + textfilename + " found. Setting", "ERROR") return ""
def getRoots(): """ Returns a list of all of the current scene's root links, i.e. links containing a model name or entity name. :return: List of all root links. """ roots = [obj for obj in bpy.context.scene.objects if isModelRoot(obj)] if not roots: log("Phobos: No root objects found.", "WARNING", "getRoots") else: log("Phobos: Found " + str(len(roots)) + " root object(s)", "INFO", "getRoots") return roots # TODO: Should we change this and all other list return values in a tuple or generator expression?
def execute(self, context): startLog(self) roots = set() for obj in bpy.context.selected_objects: roots.add(selectionUtils.getRoot(obj)) if len(roots) > 0: selectionUtils.selectObjects(list(roots), True) bpy.context.scene.objects.active = list(roots)[0] else: # bpy.ops.error.message('INVOKE_DEFAULT', type="ERROR", message="Couldn't find any root object.") log("Couldn't find any root object.", "ERROR") endLog() return {'FINISHED'}
def deriveController(obj): """This function derives a controller from a given blender object :param obj: The blender object to derive the controller from. :type obj: bpy_types.Object :return: dict """ try: props = initObjectProperties(obj, phobostype='controller') except KeyError: log("Missing data in controller " + obj.name, "ERROR", "deriveController") return None return props
def execute(self, context): startLog(self) messages = {} dic = robotdictionary.buildRobotDictionary() validator.check_dict(dic, defs.dictConstraints, messages) defs.checkMessages = messages if len(list(messages.keys())) > 0 else {"NoObject": []} for entry in messages: log("Errors in object " + entry + ":", 'INFO') for error in messages[entry]: log(error, 'INFO') endLog() return {'FINISHED'}
def getRoot(obj=None): """Finds the root object of a model given one of the model elements is selected or provided""" if obj == None: for anobj in bpy.data.objects: # TODO: this is not the best list to iterate over (there might be multiple scenes) if (anobj.select): obj = anobj child = obj if child == None: log("No root object found! Check your object selection", "ERROR") return None while child.parent != None: child = child.parent return child
def execute(self, context): startLog(self) roots = set() for obj in bpy.context.selected_objects: roots.add(getRoot(obj)) if len(roots) > 0: selectObjects(list(roots), True) bpy.context.scene.objects.active = list(roots)[0] else: # bpy.ops.error.message('INVOKE_DEFAULT', type="ERROR", message="Couldn't find any root object.") log("Couldn't find any root object.", "ERROR") endLog() return {'FINISHED'}
def deriveSensor(obj): """This function derives a sensor from a given blender object :param obj: The blender object to derive the sensor from. :type obj: bpy_types.Object :return: dict """ try: props = initObjectProperties(obj, phobostype='sensor') props['link'] = nUtils.getObjectName(sUtils.getEffectiveParent(obj)) except KeyError: log("Missing data in sensor " + obj.name, "ERROR", "deriveSensor") return None return props
def execute(self, context): startLog(self) messages = {} root = sUtils.getRoot(context.selected_objects[0]) model, objectlist = robotdictionary.buildModelDictionary(root) validator.check_dict(model, defs.dictConstraints, messages) defs.checkMessages = messages if len(list(messages.keys())) > 0 else {"NoObject": []} for entry in messages: log("Errors in object " + entry + ":", 'INFO') for error in messages[entry]: log(error, 'INFO') endLog() return {'FINISHED'}
def calculateSum(objects, numeric_prop): """Returns sum of *numeric_prop* in *objects*. """ numsum = 0 for obj in objects: try: numsum += obj[numeric_prop] except KeyError: log( obj.phobostype + " object " + obj.name + " does not contain '" + numeric_prop + "'", "WARNING", "calculateSum") return numsum
def addNamespace(obj): types = defs.subtypes name = obj.name root = selection.getRoot(obj) namespace = root["modelname"] if root != None and "modelname" in root else None if not namespace: log("The obj " + getObjectName(obj) + "has no namespace to append to. Aborting.", "ERROR") return obj.name = namespace + "::" + name for pType in types: typeTag = pType + "/type" nameTag = pType + "/name" if (typeTag in obj or ("phobostype" in obj and obj.phobostype == pType)) and nameTag not in obj: obj[nameTag] = name
def openScriptInEditor(scriptname): """This function opens a script/textfile in an open blender text window. Nothing happens if there is no available text window. :param scriptname: The scripts name. :type scriptname: str """ if scriptname in bpy.data.texts: for area in bpy.context.screen.areas: if area.type == 'TEXT_EDITOR': area.spaces.active.text = bpy.data.texts[scriptname] else: log("There is no script named " + scriptname + "!", "ERROR")
def createPrimitive(pname, ptype, psize, player=0, pmaterial="None", plocation=(0, 0, 0), protation=(0, 0, 0), verbose=False): """Generates the primitive specified by the input parameters :param pname: The primitives new name. :type pname: str :param ptype: The new primitives type. Can be one of *box, sphere, cylinder, cone, disc* :type ptype: str :param psize: The new primitives size. Depending on the ptype it can be either a single float or a tuple. :type psize: float or list :param player: The layer bitmask for the new blender object. :param pmaterial: The new primitives material. :param plocation: The new primitives location. :type plocation: tuple :param protation: The new primitives rotation. :type protation: tuple :return: bpy.types.Object - the new blender object. """ if verbose: log(ptype + psize, "INFO", "createPrimitive") try: # n_layer = bpy.context.scene.active_layer n_layer = int(player) except ValueError: n_layer = defs.layerTypes[player] players = defLayers([n_layer]) bpy.context.scene.layers[n_layer] = True # the layer has to be active to prevent problems with object placement if ptype == "box": bpy.ops.mesh.primitive_cube_add(layers=players, location=plocation, rotation=protation) obj = bpy.context.object obj.dimensions = psize if ptype == "sphere": bpy.ops.mesh.primitive_uv_sphere_add(size=psize, layers=players, location=plocation, rotation=protation) elif ptype == "cylinder": bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=psize[0], depth=psize[1], layers=players, location=plocation, rotation=protation) elif ptype == "cone": bpy.ops.mesh.primitive_cone_add(vertices=32, radius=psize[0], depth=psize[1], cap_end=True, layers=players, location=plocation, rotation=protation) elif ptype == 'disc': bpy.ops.mesh.primitive_circle_add(vertices=psize[1], radius=psize[0], fill_type='TRIFAN', location=plocation, rotation=protation, layers=players) bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) obj = bpy.context.object obj.name = pname if pmaterial != 'None': assignMaterial(obj, pmaterial) return obj
def deriveJointType(joint, adjust=False): """ Derives the type of the joint defined by the armature object 'joint' based on the constraints defined in the joint. :param joint: The joint you want to derive its type from. :type joint: bpy_types.Object :param adjust: Decides whether or not the type of the joint is adjusted after detecting (without checking whether the property "type" was previously defined in the armature or not). :type adjust: bool. :return: tuple(2) -- jtype, crot """ jtype = "floating" # 'universal' in MARS nomenclature cloc = None crot = None limrot = None # we pick the first bone in the armature as there is only one for c in joint.pose.bones[0].constraints: if c.type == "LIMIT_LOCATION": cloc = [ c.use_min_x and c.min_x == c.max_x, c.use_min_y and c.min_y == c.max_y, c.use_min_z and c.min_z == c.max_z, ] elif c.type == "LIMIT_ROTATION": limrot = c crot = [ c.use_limit_x and (c.min_x != 0 or c.max_x != 0), c.use_limit_y and (c.min_y != 0 or c.max_y != 0), c.use_limit_z and (c.min_z != 0 or c.max_z != 0), ] ncloc = sum(cloc) if cloc else None ncrot = sum((limrot.use_limit_x, limrot.use_limit_y, limrot.use_limit_z)) if limrot else None if cloc: # = if there is any constraint at all, as all joints but floating ones have translation limits if ncloc == 3: # fixed or revolute if ncrot == 3: if sum(crot) > 0: jtype = "revolute" else: jtype = "fixed" elif ncrot == 2: jtype = "continuous" elif ncloc == 2: jtype = "prismatic" elif ncloc == 1: jtype = "planar" if adjust: joint["joint/type"] = jtype log("Set type of joint '" + namingUtils.getObjectName(joint) + "'to '" + jtype + "'.", "INFO") return jtype, crot
def execute(self, context): startLog(self) for obj in context.selected_objects: try: phobosType = obj.phobostype if phobosType != 'controller' and phobosType != 'undefined': layers = 20 * [False] layers[defs.layerTypes[phobosType]] = True obj.layers = layers if phobosType == 'undefined': log("The phobostype of the object '" + obj.name + "' is undefined") except AttributeError: log("The object '" + obj.name + "' has no phobostype", "ERROR") # Handle this as error or warning? endLog() return {'FINISHED'}
def securepath(path): """This function checks whether a path exists or not. If it doesn't the functions creates the path. :param path: The path you want to check for existence *DIRECTORIES ONLY* :type path: str :return: String -- the path given as parameter, but secured by expanding ~ constructs. """ if not os.path.exists(path): try: os.makedirs(path) except NotADirectoryError: log(path + " is not a valid directory", "ERROR", "securepath") return os.path.expanduser(path)
def find_in_list(alist, prop, value): """Returns the index of the first object in a list which has a field named *prop* with value *value*. If no such object is found, returns -1. """ n = -1 for i in range(len(alist)): try: if alist[i][prop] == value: n = i break except KeyError: log("The object at index " + str(i) + " has no property " + str(prop)) return n
def execute(self, context): startLog(self) messages = {} dic = robotdictionary.buildRobotDictionary() validator.check_dict(dic, defs.dictConstraints, messages) defs.checkMessages = messages if len(list(messages.keys())) > 0 else { "NoObject": [] } for entry in messages: log("Errors in object " + entry + ":", 'INFO') for error in messages[entry]: log(error, 'INFO') endLog() return {'FINISHED'}
def deriveInertial(obj): """This function derives the inertial from the given object. :param obj: The object to derive the inertial from. :type obj: bpy_types.Object :return: dict """ try: props = initObjectProperties(obj, phobostype='inertial') props['inertia'] = list(map(float, obj['inertial/inertia'])) props['pose'] = deriveObjectPose(obj) except KeyError as e: log("Missing data in inertial object " + obj.name + str(e), "ERROR", "deriveInertial") return None return props
def deriveVisual(obj): """This function derives the visual information from an object. :param obj: The blender object to derive the visuals from. :type obj: bpy_types.Object :return: dict """ try: visual = initObjectProperties(obj, phobostype='visual', ignoretypes='geometry') visual['geometry'] = deriveGeometry(obj) visual['pose'] = deriveObjectPose(obj) if obj.lod_levels: if 'lodmaxdistances' in obj: maxdlist = obj['lodmaxdistances'] else: maxdlist = [ obj.lod_levels[i + 1].distance for i in range(len(obj.lod_levels) - 1) ] + [100.0] lodlist = [] for i in range(len(obj.lod_levels)): filename = obj.lod_levels[i].object.data.name if bpy.data.worlds[0].useObj: filename += ".obj" elif bpy.data.worlds[0].useBobj: filename += ".bobj" elif bpy.data.worlds[0].useStl: filename += ".stl" elif bpy.data.worlds[0].useDae: filename += ".dae" else: filename += ".obj" lodlist.append({ 'start': obj.lod_levels[i].distance, 'end': maxdlist[i], 'filename': os.path.join('meshes', filename) }) visual['lod'] = lodlist except KeyError: log("Missing data in visual object " + obj.name, "ERROR", "deriveVisual") return None return visual