def addAnnotationObject(obj, annotation, name=None, size=0.1, namespace=None): """Add a new annotation object with the specified annotations to the object. The annotation object will receive 'annotation_object' as its default name, unless a name is provided. Naming is done using :func:`phobos.utils.naming.safelyName`. The annotation object will be scaled according to the **size** parameter. If ``namespace`` is provided, the annotations will be saved with this string prepended. This is done using :func:`addAnnotation`. Args: obj(bpy.types.Object): object to add annotation object to annotation(dict): annotations that will be added name(str, optional): name for the new annotation object (Default value = None) size(int/float, optional): size of the new annotation object (Default value = 0.1) namespace(str, optional): namespace that will be prepended to the annotations (Default value = None) Returns: : bpy.types.Object - the new annotation object """ loc = obj.matrix_world.to_translation() if not name: name = obj.name + '_annotation_object' annot_obj = bUtils.createPrimitive( name, 'box', [1, 1, 1], defs.layerTypes['annotation'], plocation=loc, phobostype='annotation', ) annot_obj.scale = (size, ) * 3 resource = ioUtils.getResource(['annotation', namespace.split('/')[-1]]) if resource: annot_obj.data = resource.data else: annot_obj.data = ioUtils.getResource(['annotation', 'default']).data # make sure all layers are enabled for parenting originallayers = {} for name, coll in bpy.context.window.view_layer.layer_collection.children.items( ): originallayers[name] = coll.exclude coll.exclude = False # parent annotation object parentObjectsTo(annot_obj, obj) # Restore original layers for key, value in originallayers.items(): bpy.context.window.view_layer.layer_collection.children[ key].exclude = value addAnnotation(annot_obj, annotation, namespace=namespace) return annot_obj
def addAnnotationObject(obj, annotation, name=None, size=0.1, namespace=None): """Add a new annotation object with the specified annotations to the object. The annotation object will receive 'annotation_object' as its default name, unless a name is provided. Naming is done using :func:`phobos.utils.naming.safelyName`. The annotation object will be scaled according to the **size** parameter. If ``namespace`` is provided, the annotations will be saved with this string prepended. This is done using :func:`addAnnotation`. Args: obj(bpy.types.Object): object to add annotation object to annotation(dict): annotations that will be added name(str, optional): name for the new annotation object (Default value = None) size(int/float, optional): size of the new annotation object (Default value = 0.1) namespace(str, optional): namespace that will be prepended to the annotations (Default value = None) Returns: : bpy.types.Object - the new annotation object """ loc = obj.matrix_world.to_translation() if not name: name = obj.name + '_annotation_object' annot_obj = bUtils.createPrimitive( name, 'box', [1, 1, 1], defs.layerTypes['annotation'], plocation=loc, phobostype='annotation', ) annot_obj.scale = (size,) * 3 resource = ioUtils.getResource(['annotation', namespace.split('/')[-1]]) if resource: annot_obj.data = resource.data else: annot_obj.data = ioUtils.getResource(['annotation', 'default']).data # make sure all layers are enabled for parenting originallayers = list(bpy.context.scene.layers) bpy.context.scene.layers = [True for i in range(20)] # parent annotation object parentObjectsTo(annot_obj, obj) bpy.context.scene.layers = originallayers addAnnotation(annot_obj, annotation, namespace=namespace) return annot_obj
def validateJointType(link, adjust=False): """Validate the joint type of the specified link. If adjust is `True`, the validation errors are fixed on the fly. Args: link(bpy.types.Object): link representing the joint to validate adjust(bool, optional): if True, the validation errors are fixed on the fly. (Default value = False) Returns: : list(ValidateMessage) -- validation errors """ import phobos.model.joints as jointmodel import phobos.utils.io as ioUtils errors = [] if 'joint/type' not in link: errors.append( ValidateMessage("No joint type specified!", 'WARNING', link, "phobos.define_joint_constraints", {})) # make sure we get no KeyErrors from this if adjust: link['joint/type'] = 'undefined' joint_type, crot = jointmodel.getJointType(link) # warn user if the constraints do not match the specified joint type if 'joint/type' in link and joint_type != link['joint/type']: if not adjust: errors.append( ValidateMessage( "The specified joint type does not match the constraints:", 'WARNING', link, None, { 'log_info': str("'" + link['joint/type'] + "' should be set to '" + joint_type + "' instead.") }, )) else: # fix joint type and assign new resource object link['joint/type'] = joint_type resource_obj = ioUtils.getResource(('joint', joint_type)) if resource_obj: log("Assigned resource to {}.".format(link.name), 'DEBUG') link.pose.bones[0].custom_shape = resource_obj errors.append( ValidateMessage("Adjusted joint type to '" + joint_type + "'.", 'INFO', link, None, {})) return errors
def createInterface(ifdict, parent=None): """Create an interface object and optionally parent to existing object. ifdict is expected as: | **type**: str | **direction**: str | **model**: str | **name**: str | **parent**: bpy.types.Object (optional) | **scale**: float (optional) Args: ifdict(dict): interface data parent(bpy.types.Object, optional): designated parent object (Default value = None) Returns: bpy.data.Object: newly created interface object """ if not parent: try: parent = ifdict['parent'] assert isinstance(parent, bpy.types.Object) except (AttributeError, AssertionError, KeyError): parent = None location = parent.matrix_world.translation if parent else mathutils.Vector( ) rotation = parent.matrix_world.to_euler() if parent else mathutils.Euler() model = ifdict['model'] if 'model' in ifdict else 'default' templateobj = ioUtils.getResource( ('interface', model, ifdict['direction'])) scale = ifdict['scale'] if 'scale' in ifdict else 1.0 ifobj = bUtils.createPrimitive( ifdict['name'], 'box', (1.0, 1.0, 1.0), defs.layerTypes['interface'], plocation=location, protation=rotation, phobostype='interface', ) nUtils.safelyName(ifobj, ifdict['name'], 'interface') ifobj.data = templateobj.data ifobj.scale = (scale, ) * 3 ifobj['interface/type'] = ifdict['type'] ifobj['interface/direction'] = ifdict['direction'] if parent is not None: ifobj['interface/parent'] = parent.name parentObjectsTo(ifobj, parent) bpy.ops.object.make_single_user(object=True, obdata=True)
def createInterface(ifdict, parent=None): """Create an interface object and optionally parent to existing object. ifdict is expected as: | **type**: str | **direction**: str | **model**: str | **name**: str | **parent**: bpy.types.Object (optional) | **scale**: float (optional) Args: ifdict(dict): interface data parent(bpy.types.Object, optional): designated parent object (Default value = None) Returns: bpy.data.Object: newly created interface object """ if not parent: try: parent = ifdict['parent'] assert isinstance(parent, bpy.types.Object) except (AttributeError, AssertionError, KeyError): parent = None location = parent.matrix_world.translation if parent else mathutils.Vector() rotation = parent.matrix_world.to_euler() if parent else mathutils.Euler() model = ifdict['model'] if 'model' in ifdict else 'default' templateobj = ioUtils.getResource(('interface', model, ifdict['direction'])) scale = ifdict['scale'] if 'scale' in ifdict else 1.0 ifobj = bUtils.createPrimitive( ifdict['name'], 'box', (1.0, 1.0, 1.0), defs.layerTypes['interface'], plocation=location, protation=rotation, phobostype='interface', ) nUtils.safelyName(ifobj, ifdict['name'], 'interface') ifobj.data = templateobj.data ifobj.scale = (scale,) * 3 ifobj['interface/type'] = ifdict['type'] ifobj['interface/direction'] = ifdict['direction'] bpy.ops.object.make_single_user(object=True, obdata=True)
def execute(self, context): """ Args: context: Returns: """ root = sUtils.getRoot(context.active_object) if root: root["model/name"] = self.modelname # write model information to new root root.pose.bones[0].custom_shape = ioUtils.getResource(('link', 'root')) else: log("Could not set modelname due to missing root link. No name was set.", "ERROR") return {'FINISHED'}
def createSensor(sensor, reference, origin=mathutils.Matrix()): """This function creates a new sensor specified by its parameters. The sensor dictionary has to contain these keys: *name*: name of the new sensor *type*: type specifier of the sensor *shape*: a shape specifier for the sensor *props*: custom properties to be written to the sensor object Args: sensor(dict): phobos representation of the new sensor reference(bpy_types.Object): object to add a parent relationship to origin(mathutils.Matrix, optional): new sensors origin (Default value = mathutils.Matrix()) Returns: : The newly created sensor object """ layers = defs.layerTypes['sensor'] bUtils.toggleLayer(layers, value=True) # create sensor object if sensor['shape'].startswith('resource'): newsensor = bUtils.createPrimitive( sensor['name'], 'box', [1, 1, 1], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=sensor['material'], phobostype='sensor', ) # use resource name provided as: "resource:whatever_name" resource_obj = ioUtils.getResource( ['sensor'] + sensor['shape'].split('://')[1].split('_')) if resource_obj: log("Assigned resource mesh and materials to new sensor object.", 'DEBUG') newsensor.data = resource_obj.data newsensor.scale = (sensor['size'], ) * 3 else: log( "Could not use resource mesh for sensor. Default cube used instead.", 'WARNING') else: newsensor = bUtils.createPrimitive( sensor['name'], sensor['shape'], sensor['size'], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=sensor['material'], phobostype='sensor', ) # assign the parent if available if reference is not None: eUtils.parentObjectsTo(newsensor, reference) # TODO we need to deal with other types of parameters for sensors # TODO cameraRotLock() use or dispose? # contact, force and torque sensors (or unknown sensors) # else: # newsensor = bUtils.createPrimitive( # sensor['name'], 'ico', 0.05, layers, 'phobos_sensor', # origin.to_translation(), protation=origin.to_euler()) # if sensor['name'] == 'Joint6DOF': # # TODO delete me? handle this # #newsensor['sensor/nodes'] = nUtils.getObjectName(reference) # pass # elif 'Node' in sensor['type']: # newsensor['sensor/nodes'] = sorted([nUtils.getObjectName(ref) for ref in reference]) # elif 'Joint' in sensor['type'] or 'Motor' in sensor['type']: # newsensor['sensor/joints'] = sorted([nUtils.getObjectName(ref) for ref in reference]) # elif sensor['type'] in ['Joint6DOF']: # for obj in context.selected_objects: # if obj.phobostype == 'link': # sensor['name'] = "sensor_joint6dof_" + nUtils.getObjectName(obj, phobostype="joint") # sensors.createSensor(sensor, obj, obj.matrix_world) # elif 'Node' in sensor['type']: # sensors.createSensor(sensor, [obj for obj in context.selected_objects if obj.phobostype == 'collision'], # mathutils.Matrix.Translation(context.scene.cursor_location)) # elif 'Motor' in sensor['type'] or 'Joint' in sensor['type']: # sensors.createSensor(sensor, [obj for obj in context.selected_objects if obj.phobostype == 'link'], # mathutils.Matrix.Translation(context.scene.cursor_location)) # set sensor properties newsensor.phobostype = 'sensor' newsensor.name = sensor['name'] newsensor['sensor/name'] = sensor['name'] newsensor['sensor/type'] = sensor['type'] # write the custom properties to the sensor eUtils.addAnnotation(newsensor, sensor['props'], namespace='sensor') # throw warning if type is not known # TODO we need to link this error to the sensor type specifications if sensor['type'] not in [ key.lower() for key in defs.def_settings['sensors'] ]: log( "Sensor " + sensor['name'] + " is of unknown/custom type: " + sensor['type'] + ".", 'WARNING', ) # select the new sensor sUtils.selectObjects([newsensor], clear=True, active=0) return newsensor
def createController(controller, reference, origin=mathutils.Matrix(), annotations=None): """This function creates a new controller specified by its parameters. If an annotation category or the keyword 'all' is specified, the respective annotations for the controller will be added as objects. Args: controller(dict): phobos representation of the new controller reference(bpy_types.Object): object to add a parent relationship to origin(mathutils.Matrix, optional): new controllers origin (Default value = mathutils.Matrix()) annotations(list(str, optional): list of annotation keys or 'all' to add to as annotation objects (Default value = None) Returns: : bpy.types.Object -- new created controller object """ layers = defs.layerTypes['controller'] bUtils.toggleLayer(layers, value=True) # create controller object if controller['shape'].startswith('resource'): newcontroller = bUtils.createPrimitive( controller['name'], 'box', [1, 1, 1], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=controller['material'], phobostype='controller', ) # use resource name provided as: "resource:whatever_name" resource_obj = ioUtils.getResource( ['controller'] + controller['shape'].split('://')[1].split('_') ) if resource_obj: log("Assigned resource mesh and materials to new controller object.", 'DEBUG') newcontroller.data = resource_obj.data newcontroller.scale = (controller['size'],) * 3 else: log("Could not use resource mesh for controller. Default cube used instead.", 'WARNING') else: newcontroller = bUtils.createPrimitive( controller['name'], controller['shape'], controller['size'], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=controller['material'], phobostype='controller', ) newcontroller.name = controller['name'] newcontroller['controller/type'] = controller['type'] # write the custom properties to the controller eUtils.addAnnotation(newcontroller, controller['props'], namespace='controller') if controller['annotations'] and annotations: if annotations == 'all': keys = controller['annotations'].keys() elif isinstance(annotations, list): keys = [key for key in annotations if key in controller['annotations']] else: keys = [] for key in keys: eUtils.addAnnotationObject( newcontroller, controller['annotations'][key], namespace='controller/' + key ) # assign the parent if available if reference is not None: sUtils.selectObjects([newcontroller, reference], clear=True, active=1) if reference.phobostype == 'link': bpy.ops.object.parent_set(type='BONE_RELATIVE') else: bpy.ops.object.parent_set(type='OBJECT') return newcontroller
def setJointConstraints( joint, jointtype, lower=0.0, upper=0.0, spring=0.0, damping=0.0, maxeffort_approximation=None, maxspeed_approximation=None, ): """Sets the constraints for a given joint and jointtype. If the joint type is not recognized, the constraints will match a floating joint. The approximation for maximum effort/speed requires a dictionary with two entries (*function* *coefficients*). Based on the joint type, the respective resource object is applied to the link. Args: joint(bpy_types.Object): link object containing the joint to be edited jointtype(str): joint type (revolute, continuous, prismatic, fixed, floating, planar) lower(float, optional): lower limit of the constraint (defaults to 0.) upper(float, optional): upper limit of the constraint (defaults to 0.) spring(float, optional): spring stiffness for the joint (Default value = 0.0) damping(float, optional): spring damping for the joint (Default value = 0.0) maxeffort_approximation(dict, optional): function and coefficients for maximum effort (Default value = None) maxspeed_approximation(dict, optional): function and coefficients for maximum speed (Default value = None) Returns: """ if joint.phobostype != 'link': log("Cannot set joint constraints. Not a link: {}".format(joint), 'ERROR') return log("Setting joint constraints at link {}.".format(joint.name), 'DEBUG') bpy.ops.object.mode_set(mode='POSE') # remove existing constraints from bone for cons in joint.pose.bones[0].constraints: joint.pose.bones[0].constraints.remove(cons) # add spring & damping if jointtype in ['revolute', 'prismatic'] and (spring or damping): try: bpy.ops.rigidbody.constraint_add(type='GENERIC_SPRING') bpy.context.object.rigid_body_constraint.spring_stiffness_y = spring bpy.context.object.rigid_body_constraint.spring_damping_y = damping except RuntimeError: log("No Blender Rigid Body World present, only adding custom properties.", 'ERROR') # TODO we should make sure that the rigid body constraints gets changed # if the values below are changed manually by the user joint['joint/dynamics/springStiffness'] = spring joint['joint/dynamics/springDamping'] = damping joint['joint/dynamics/spring_const_constraint_axis1'] = spring # FIXME: this is a hack joint[ 'joint/dynamics/damping_const_constraint_axis1' ] = damping # FIXME: this is a hack, too # set constraints accordingly if jointtype == 'revolute': set_revolute(joint, lower, upper) elif jointtype == 'continuous': set_continuous(joint) elif jointtype == 'prismatic': set_prismatic(joint, lower, upper) elif jointtype == 'fixed': set_fixed(joint) elif jointtype == 'floating': # 6DOF pass elif jointtype == 'planar': set_planar(joint) else: log("Unknown joint type for joint " + joint.name + ". Behaviour like floating.", 'WARNING') joint['joint/type'] = jointtype bpy.ops.object.mode_set(mode='OBJECT') # check for approximation functions of effort and speed if jointtype in ['revolute', 'continuous', 'prismatic']: if maxeffort_approximation: if all(elem in ['function', 'coefficients'] for elem in maxeffort_approximation): joint['joint/maxeffort_approximation'] = maxeffort_approximation['function'] joint['joint/maxeffort_coefficients'] = maxeffort_approximation['coefficients'] else: log( "Approximation for max effort ill-defined in joint object {}.".format( joint.name ), 'ERROR', ) if maxspeed_approximation: if all(elem in ['function', 'coefficients'] for elem in maxspeed_approximation): joint['joint/maxspeed_approximation'] = maxspeed_approximation['function'] joint['joint/maxspeed_coefficients'] = maxspeed_approximation['coefficients'] else: log( "Approximation for max speed ill-defined in joint object {}.".format( joint.name ), 'ERROR', ) # set link/joint visualization resource_obj = ioUtils.getResource(('joint', jointtype)) if resource_obj: log("Assigned resource to {}.".format(joint.name), 'DEBUG') joint.pose.bones[0].custom_shape = resource_obj
def setJointConstraints( joint, jointtype, lower=0.0, upper=0.0, spring=0.0, damping=0.0, maxeffort_approximation=None, maxspeed_approximation=None, ): """Sets the constraints for a given joint and jointtype. If the joint type is not recognized, the constraints will match a floating joint. The approximation for maximum effort/speed requires a dictionary with two entries (*function* *coefficients*). Based on the joint type, the respective resource object is applied to the link. Args: joint(bpy_types.Object): link object containing the joint to be edited jointtype(str): joint type (revolute, continuous, prismatic, fixed, floating, planar) lower(float, optional): lower limit of the constraint (defaults to 0.) upper(float, optional): upper limit of the constraint (defaults to 0.) spring(float, optional): spring stiffness for the joint (Default value = 0.0) damping(float, optional): spring damping for the joint (Default value = 0.0) maxeffort_approximation(dict, optional): function and coefficients for maximum effort (Default value = None) maxspeed_approximation(dict, optional): function and coefficients for maximum speed (Default value = None) Returns: """ if joint.phobostype != 'link': log("Cannot set joint constraints. Not a link: {}".format(joint), 'ERROR') return log("Setting joint constraints at link {}.".format(joint.name), 'DEBUG') bpy.ops.object.mode_set(mode='POSE') # remove existing constraints from bone for cons in joint.pose.bones[0].constraints: joint.pose.bones[0].constraints.remove(cons) # add spring & damping if jointtype in ['revolute', 'prismatic'] and (spring or damping): try: bpy.ops.rigidbody.constraint_add(type='GENERIC_SPRING') bpy.context.object.rigid_body_constraint.spring_stiffness_y = spring bpy.context.object.rigid_body_constraint.spring_damping_y = damping except RuntimeError: log( "No Blender Rigid Body World present, only adding custom properties.", 'ERROR') # TODO we should make sure that the rigid body constraints gets changed # if the values below are changed manually by the user joint['joint/dynamics/springStiffness'] = spring joint['joint/dynamics/springDamping'] = damping joint[ 'joint/dynamics/spring_const_constraint_axis1'] = spring # FIXME: this is a hack joint[ 'joint/dynamics/damping_const_constraint_axis1'] = damping # FIXME: this is a hack, too # set constraints accordingly if jointtype == 'revolute': set_revolute(joint, lower, upper) elif jointtype == 'continuous': set_continuous(joint) elif jointtype == 'prismatic': set_prismatic(joint, lower, upper) elif jointtype == 'fixed': set_fixed(joint) elif jointtype == 'floating': # 6DOF pass elif jointtype == 'planar': set_planar(joint) else: log( "Unknown joint type for joint " + joint.name + ". Behaviour like floating.", 'WARNING') joint['joint/type'] = jointtype bpy.ops.object.mode_set(mode='OBJECT') # check for approximation functions of effort and speed if jointtype in ['revolute', 'continuous', 'prismatic']: if maxeffort_approximation: if all(elem in ['function', 'coefficients'] for elem in maxeffort_approximation): joint[ 'joint/maxeffort_approximation'] = maxeffort_approximation[ 'function'] joint[ 'joint/maxeffort_coefficients'] = maxeffort_approximation[ 'coefficients'] else: log( "Approximation for max effort ill-defined in joint object {}." .format(joint.name), 'ERROR', ) if maxspeed_approximation: if all(elem in ['function', 'coefficients'] for elem in maxspeed_approximation): joint['joint/maxspeed_approximation'] = maxspeed_approximation[ 'function'] joint['joint/maxspeed_coefficients'] = maxspeed_approximation[ 'coefficients'] else: log( "Approximation for max speed ill-defined in joint object {}." .format(joint.name), 'ERROR', ) # set link/joint visualization resource_obj = ioUtils.getResource(('joint', jointtype)) if resource_obj: log("Assigned resource to {}.".format(joint.name), 'DEBUG') joint.pose.bones[0].custom_shape = resource_obj
def createMotor(motor, parentobj, origin=mathutils.Matrix(), addcontrollers=False): """This function creates a new motor specified by its parameters. If *addcontrollers* is set, a controller object will be created from the controller definition which is specified in the motor dictionary (key *controller*). Args: motor(dict): phobos representation of the new motor. parentobj(bpy_types.Object): object to parent new motor to origin(mathutils.Matrix, optional): new motors origin (Default value = mathutils.Matrix()) addcontrollers(bool, optional): whether to add the defined controller as object (Default value = False) Returns: bpy.types.Object: new motor object or a list of the new motor_object and the new controller object """ layers = defs.layerTypes['motor'] bUtils.toggleLayer(layers, value=True) # create motor object if motor['shape'].startswith('resource'): newmotor = bUtils.createPrimitive( motor['name'], 'box', [1, 1, 1], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=motor['material'], phobostype='motor', ) # use resource name provided as: "resource:whatever_name" resource_obj = ioUtils.getResource(['motor'] + motor['shape'].split('://')[1].split('_')) if resource_obj: log("Assigned resource mesh and materials to new motor object.", 'DEBUG') newmotor.data = resource_obj.data newmotor.scale = (motor['size'],) * 3 else: log("Could not use resource mesh for motor. Default cube used instead.", 'WARNING') else: newmotor = bUtils.createPrimitive( motor['name'], motor['shape'], motor['size'], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=motor['material'], phobostype='motor', ) # assign the parent if available if parentobj is not None: sUtils.selectObjects([newmotor, parentobj], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') # set motor properties newmotor.phobostype = 'motor' newmotor.name = motor['name'] defname = motor['defname'] # write the custom properties to the motor eUtils.addAnnotation(newmotor, motor['props'], namespace='motor', ignore=['defname']) if 'controller' in defs.definitions['motors'][defname] and addcontrollers: import phobos.model.controllers as controllermodel motorcontroller = defs.definitions['motors'][defname]['controller'] controllerdefs = ioUtils.getDictFromYamlDefs( 'controller', motorcontroller, newmotor.name + '_controller' ) newcontroller = controllermodel.createController( controllerdefs, newmotor, origin=newmotor.matrix_world, annotations='all' ) else: newcontroller = None # select the new motor sUtils.selectObjects( [newmotor] if not newcontroller else [newmotor, newcontroller], clear=True, active=0 ) return newmotor if not newcontroller else [newmotor, newcontroller]
def createController(controller, reference, origin=mathutils.Matrix(), annotations=None): """This function creates a new controller specified by its parameters. If an annotation category or the keyword 'all' is specified, the respective annotations for the controller will be added as objects. Args: controller(dict): phobos representation of the new controller reference(bpy_types.Object): object to add a parent relationship to origin(mathutils.Matrix, optional): new controllers origin (Default value = mathutils.Matrix()) annotations(list(str, optional): list of annotation keys or 'all' to add to as annotation objects (Default value = None) Returns: : bpy.types.Object -- new created controller object """ bUtils.toggleLayer('controller', value=True) # create controller object if controller['shape'].startswith('resource'): newcontroller = bUtils.createPrimitive( controller['name'], 'box', [1, 1, 1], [], plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=controller['material'], phobostype='controller', ) # use resource name provided as: "resource:whatever_name" resource_obj = ioUtils.getResource( ['controller'] + controller['shape'].split('://')[1].split('_')) if resource_obj: log( "Assigned resource mesh and materials to new controller object.", 'DEBUG') newcontroller.data = resource_obj.data newcontroller.scale = (controller['size'], ) * 3 else: log( "Could not use resource mesh for controller. Default cube used instead.", 'WARNING') else: newcontroller = bUtils.createPrimitive( controller['name'], controller['shape'], controller['size'], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=controller['material'], phobostype='controller', ) newcontroller.name = controller['name'] newcontroller['controller/type'] = controller['type'] # write the custom properties to the controller eUtils.addAnnotation(newcontroller, controller['props'], namespace='controller') if controller['annotations'] and annotations: if annotations == 'all': keys = controller['annotations'].keys() elif isinstance(annotations, list): keys = [ key for key in annotations if key in controller['annotations'] ] else: keys = [] for key in keys: eUtils.addAnnotationObject(newcontroller, controller['annotations'][key], namespace='controller/' + key) # assign the parent if available if reference is not None: sUtils.selectObjects([newcontroller, reference], clear=True, active=1) if reference.phobostype == 'link': bpy.ops.object.parent_set(type='BONE_RELATIVE') else: bpy.ops.object.parent_set(type='OBJECT') return newcontroller
def createSensor(sensor, reference, origin=mathutils.Matrix()): """This function creates a new sensor specified by its parameters. The sensor dictionary has to contain these keys: *name*: name of the new sensor *type*: type specifier of the sensor *shape*: a shape specifier for the sensor *props*: custom properties to be written to the sensor object Args: sensor(dict): phobos representation of the new sensor reference(bpy_types.Object): object to add a parent relationship to origin(mathutils.Matrix, optional): new sensors origin (Default value = mathutils.Matrix()) Returns: : The newly created sensor object """ layers = defs.layerTypes['sensor'] bUtils.toggleLayer(layers, value=True) # create sensor object if sensor['shape'].startswith('resource'): newsensor = bUtils.createPrimitive( sensor['name'], 'box', [1, 1, 1], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=sensor['material'], phobostype='sensor', ) # use resource name provided as: "resource:whatever_name" resource_obj = ioUtils.getResource(['sensor'] + sensor['shape'].split('://')[1].split('_')) if resource_obj: log("Assigned resource mesh and materials to new sensor object.", 'DEBUG') newsensor.data = resource_obj.data newsensor.scale = (sensor['size'],) * 3 else: log("Could not use resource mesh for sensor. Default cube used instead.", 'WARNING') else: newsensor = bUtils.createPrimitive( sensor['name'], sensor['shape'], sensor['size'], layers, plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=sensor['material'], phobostype='sensor', ) # assign the parent if available if reference is not None: eUtils.parentObjectsTo(newsensor, reference) # TODO we need to deal with other types of parameters for sensors # TODO cameraRotLock() use or dispose? # contact, force and torque sensors (or unknown sensors) # else: # newsensor = bUtils.createPrimitive( # sensor['name'], 'ico', 0.05, layers, 'phobos_sensor', # origin.to_translation(), protation=origin.to_euler()) # if sensor['name'] == 'Joint6DOF': # # TODO delete me? handle this # #newsensor['sensor/nodes'] = nUtils.getObjectName(reference) # pass # elif 'Node' in sensor['type']: # newsensor['sensor/nodes'] = sorted([nUtils.getObjectName(ref) for ref in reference]) # elif 'Joint' in sensor['type'] or 'Motor' in sensor['type']: # newsensor['sensor/joints'] = sorted([nUtils.getObjectName(ref) for ref in reference]) # elif sensor['type'] in ['Joint6DOF']: # for obj in context.selected_objects: # if obj.phobostype == 'link': # sensor['name'] = "sensor_joint6dof_" + nUtils.getObjectName(obj, phobostype="joint") # sensors.createSensor(sensor, obj, obj.matrix_world) # elif 'Node' in sensor['type']: # sensors.createSensor(sensor, [obj for obj in context.selected_objects if obj.phobostype == 'collision'], # mathutils.Matrix.Translation(context.scene.cursor_location)) # elif 'Motor' in sensor['type'] or 'Joint' in sensor['type']: # sensors.createSensor(sensor, [obj for obj in context.selected_objects if obj.phobostype == 'link'], # mathutils.Matrix.Translation(context.scene.cursor_location)) # set sensor properties newsensor.phobostype = 'sensor' newsensor.name = sensor['name'] newsensor['sensor/name'] = sensor['name'] newsensor['sensor/type'] = sensor['type'] # write the custom properties to the sensor eUtils.addAnnotation(newsensor, sensor['props'], namespace='sensor') # throw warning if type is not known # TODO we need to link this error to the sensor type specifications if sensor['type'] not in [key.lower() for key in defs.def_settings['sensors']]: log( "Sensor " + sensor['name'] + " is of unknown/custom type: " + sensor['type'] + ".", 'WARNING', ) # select the new sensor sUtils.selectObjects([newsensor], clear=True, active=0) return newsensor
def createMotor(motor, parentobj, origin=mathutils.Matrix(), addcontrollers=False): """This function creates a new motor specified by its parameters. If *addcontrollers* is set, a controller object will be created from the controller definition which is specified in the motor dictionary (key *controller*). Args: motor(dict): phobos representation of the new motor. parentobj(bpy_types.Object): object to parent new motor to origin(mathutils.Matrix, optional): new motors origin (Default value = mathutils.Matrix()) addcontrollers(bool, optional): whether to add the defined controller as object (Default value = False) Returns: bpy.types.Object: new motor object or a list of the new motor_object and the new controller object """ bUtils.toggleLayer('motor', value=True) primitive_name = '' # create name if not given by motor dict if not 'name' in motor or len(motor['name']) == 0: motor['name'] = parentobj.name primitive_name = "motor_" + motor['name'] else: primitive_name = motor['name'] primitive_name = '' # create name if not given by motor dict if not 'name' in motor or len(motor['name']) == 0: motor['name'] = parentobj.name primitive_name = "motor_" + motor['name'] else: primitive_name = motor['name'] # create motor object if motor['shape'].startswith('resource'): newmotor = bUtils.createPrimitive( primitive_name, 'box', [1, 1, 1], [], plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=motor['material'], phobostype='motor', ) # use resource name provided as: "resource:whatever_name" resource_obj = ioUtils.getResource( ['motor'] + motor['shape'].split('://')[1].split('_')) if resource_obj: log("Assigned resource mesh and materials to new motor object.", 'DEBUG') newmotor.data = resource_obj.data newmotor.scale = (motor['size'], ) * 3 else: log( "Could not use resource mesh for motor. Default cube used instead.", 'WARNING') else: newmotor = bUtils.createPrimitive( primitive_name, motor['shape'], motor['size'], [], plocation=origin.to_translation(), protation=origin.to_euler(), pmaterial=motor['material'], phobostype='motor', ) # assign the parent if available if parentobj is not None: sUtils.selectObjects([newmotor, parentobj], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') # set motor properties newmotor.phobostype = 'motor' # should not be nessaccary: newmotor.name = motor['name'] defname = motor['defname'] # write the custom properties to the motor eUtils.addAnnotation(newmotor, motor['props'], namespace='motor', ignore=['defname']) # fix motor name since it can differe from object name newmotor['motor/name'] = motor['name'] if 'controller' in defs.definitions['motors'][defname] and addcontrollers: import phobos.model.controllers as controllermodel motorcontroller = defs.definitions['motors'][defname]['controller'] controllerdefs = ioUtils.getDictFromYamlDefs( 'controller', motorcontroller, newmotor.name + '_controller') newcontroller = controllermodel.createController( controllerdefs, newmotor, origin=newmotor.matrix_world, annotations='all') else: newcontroller = None # select the new motor sUtils.selectObjects( [newmotor] if not newcontroller else [newmotor, newcontroller], clear=True, active=0) return newmotor if not newcontroller else [newmotor, newcontroller]
def validateJointType(link, adjust=False): """Validate the joint type of the specified link. If adjust is `True`, the validation errors are fixed on the fly. Args: link(bpy.types.Object): link representing the joint to validate adjust(bool, optional): if True, the validation errors are fixed on the fly. (Default value = False) Returns: : list(ValidateMessage) -- validation errors """ import phobos.model.joints as jointmodel import phobos.utils.io as ioUtils errors = [] if 'joint/type' not in link: errors.append( ValidateMessage( "No joint type specified!", 'WARNING', link, "phobos.define_joint_constraints", {} ) ) # make sure we get no KeyErrors from this if adjust: link['joint/type'] = 'undefined' joint_type, crot = jointmodel.getJointType(link) # warn user if the constraints do not match the specified joint type if 'joint/type' in link and joint_type != link['joint/type']: if not adjust: errors.append( ValidateMessage( "The specified joint type does not match the constraints:", 'WARNING', link, None, { 'log_info': str( "'" + link['joint/type'] + "' should be set to '" + joint_type + "' instead." ) }, ) ) else: # fix joint type and assign new resource object link['joint/type'] = joint_type resource_obj = ioUtils.getResource(('joint', joint_type)) if resource_obj: log("Assigned resource to {}.".format(link.name), 'DEBUG') link.pose.bones[0].custom_shape = resource_obj errors.append( ValidateMessage( "Adjusted joint type to '" + joint_type + "'.", 'INFO', link, None, {} ) ) return errors
def setJointConstraints(joint, jointtype, lower=0.0, upper=0.0, spring=0.0, damping=0.0, maxeffort_approximation=None, maxspeed_approximation=None): """Sets the constraints for a given joint and jointtype. Args: joint(bpy_types.Object): the joint to be edited jointtype(str): joint type lower(float, optional): The constraints' lower limit. (Default value = 0.0) upper(float, optional): The constraints' upper limit. (Default value = 0.0) spring: (Default value = 0.0) damping: (Default value = 0.0) maxeffort_approximation: (Default value = None) maxspeed_approximation: (Default value = None) Returns: """ log(joint.name, 'INFO', end=' ') bpy.ops.object.mode_set(mode='POSE') for c in joint.pose.bones[0].constraints: joint.pose.bones[0].constraints.remove(c) if joint.phobostype == 'link': # add spring & damping if jointtype in ['revolute', 'prismatic'] and (spring or damping): try: bpy.ops.rigidbody.constraint_add(type='GENERIC_SPRING') bpy.context.object.rigid_body_constraint.spring_stiffness_y = spring bpy.context.object.rigid_body_constraint.spring_damping_y = damping except RuntimeError: log( "No Blender Rigid Body World present, only adding custom properties.", "ERROR") # we should make sure that the rigid body constraints gets changed # if the values below are changed manually by the user joint['joint/dynamics/springStiffness'] = spring joint['joint/dynamics/springDamping'] = damping joint[ 'joint/dynamics/spring_const_constraint_axis1'] = spring # FIXME: this is a hack joint[ 'joint/dynamics/damping_const_constraint_axis1'] = damping # FIXME: this is a hack, too # add constraints if jointtype == 'revolute': # fix location bpy.ops.pose.constraint_add(type='LIMIT_LOCATION') cloc = getJointConstraint(joint, 'LIMIT_LOCATION') cloc.use_min_x = True cloc.use_min_y = True cloc.use_min_z = True cloc.use_max_x = True cloc.use_max_y = True cloc.use_max_z = True cloc.owner_space = 'LOCAL' # fix rotation x, z and limit y bpy.ops.pose.constraint_add(type='LIMIT_ROTATION') crot = getJointConstraint(joint, 'LIMIT_ROTATION') crot.use_limit_x = True crot.min_x = 0 crot.max_x = 0 crot.use_limit_y = True crot.min_y = lower crot.max_y = upper crot.use_limit_z = True crot.min_z = 0 crot.max_z = 0 crot.owner_space = 'LOCAL' elif jointtype == 'continuous': # fix location bpy.ops.pose.constraint_add(type='LIMIT_LOCATION') cloc = getJointConstraint(joint, 'LIMIT_LOCATION') cloc.use_min_x = True cloc.use_min_y = True cloc.use_min_z = True cloc.use_max_x = True cloc.use_max_y = True cloc.use_max_z = True cloc.owner_space = 'LOCAL' # fix rotation x, z bpy.ops.pose.constraint_add(type='LIMIT_ROTATION') crot = getJointConstraint(joint, 'LIMIT_ROTATION') crot.use_limit_x = True crot.min_x = 0 crot.max_x = 0 crot.use_limit_z = True crot.min_z = 0 crot.max_z = 0 crot.owner_space = 'LOCAL' elif jointtype == 'prismatic': # fix location except for y axis bpy.ops.pose.constraint_add(type='LIMIT_LOCATION') cloc = getJointConstraint(joint, 'LIMIT_LOCATION') cloc.use_min_x = True cloc.use_min_y = True cloc.use_min_z = True cloc.use_max_x = True cloc.use_max_y = True cloc.use_max_z = True if lower == upper: cloc.use_min_y = False cloc.use_max_y = False else: cloc.min_y = lower cloc.max_y = upper cloc.owner_space = 'LOCAL' # fix rotation bpy.ops.pose.constraint_add(type='LIMIT_ROTATION') crot = getJointConstraint(joint, 'LIMIT_ROTATION') crot.use_limit_x = True crot.min_x = 0 crot.max_x = 0 crot.use_limit_y = True crot.min_y = 0 crot.max_y = 0 crot.use_limit_z = True crot.min_z = 0 crot.max_z = 0 crot.owner_space = 'LOCAL' elif jointtype == 'fixed': # fix location bpy.ops.pose.constraint_add(type='LIMIT_LOCATION') cloc = getJointConstraint(joint, 'LIMIT_LOCATION') cloc.use_min_x = True cloc.use_min_y = True cloc.use_min_z = True cloc.use_max_x = True cloc.use_max_y = True cloc.use_max_z = True cloc.owner_space = 'LOCAL' # fix rotation bpy.ops.pose.constraint_add(type='LIMIT_ROTATION') crot = getJointConstraint(joint, 'LIMIT_ROTATION') crot.use_limit_x = True crot.min_x = 0 crot.max_x = 0 crot.use_limit_y = True crot.min_y = 0 crot.max_y = 0 crot.use_limit_z = True crot.min_z = 0 crot.max_z = 0 crot.owner_space = 'LOCAL' elif jointtype == 'floating': # 6DOF pass elif jointtype == 'planar': # fix location bpy.ops.pose.constraint_add(type='LIMIT_LOCATION') cloc = getJointConstraint(joint, 'LIMIT_LOCATION') cloc.use_min_y = True cloc.use_max_y = True cloc.owner_space = 'LOCAL' # fix rotation bpy.ops.pose.constraint_add(type='LIMIT_ROTATION') crot = getJointConstraint(joint, 'LIMIT_ROTATION') crot.use_limit_x = True crot.min_x = 0 crot.max_x = 0 crot.use_limit_y = True crot.min_y = 0 crot.max_y = 0 crot.use_limit_z = True crot.min_z = 0 crot.max_z = 0 crot.owner_space = 'LOCAL' else: log("Unknown joint type for joint " + joint.name, "WARNING") joint['joint/type'] = jointtype bpy.ops.object.mode_set(mode='OBJECT') # approximation functions for effort and speed if jointtype in ['revolute', 'continuous', 'prismatic']: try: if maxeffort_approximation: joint[ "joint/maxeffort_approximation"] = maxeffort_approximation[ "function"] joint[ "joint/maxeffort_coefficients"] = maxeffort_approximation[ "coefficients"] if maxspeed_approximation: joint[ "joint/maxspeed_approximation"] = maxspeed_approximation[ "function"] joint[ "joint/maxspeed_coefficients"] = maxspeed_approximation[ "coefficients"] except KeyError: log( "Approximation for max effort and/or speed ill-defined in joint object " + joint.name, "ERROR") # set link/joint visualization joint.pose.bones[0].custom_shape = ioUtils.getResource( ('joint', jointtype))
# delete objects designated for deletion sUtils.selectObjects(objects_to_be_deleted) bpy.ops.object.delete(use_global=True) # remove motor limits custom properties for obj in objectlist: if 'motor/limit' in obj: del obj['motor/limit'] elif 'motor/limits' in obj: del obj['motor/limits'] # move modelname to model/name and reassign root for obj in objectlist: if 'modelname' in obj: obj['model/name'] = obj['modelname'] del obj['modelname'] if 'version' in obj: obj['model/version'] = obj['version'] del obj['version'] if sUtils.isRoot(obj): bpy.context.view_layer.objects.active = obj bpy.ops.phobos.set_model_root() # update joint shapes for whole model joints = [obj for obj in objectlist if 'joint/type' in obj] for joint in joints: resource_obj = ioUtils.getResource(['joint', joint['joint/type']]) joint.pose.bones[0].custom_shape = resource_obj
# delete objects designated for deletion sUtils.selectObjects(objects_to_be_deleted) bpy.ops.object.delete(use_global=True) # remove motor limits custom properties for obj in objectlist: if 'motor/limit' in obj: del obj['motor/limit'] elif 'motor/limits' in obj: del obj['motor/limits'] # move modelname to model/name and reassign root for obj in objectlist: if 'modelname' in obj: obj['model/name'] = obj['modelname'] del obj['modelname'] if 'version' in obj: obj['model/version'] = obj['version'] del obj['version'] if sUtils.isRoot(obj): bpy.context.scene.objects.active = obj bpy.ops.phobos.set_model_root() # update joint shapes for whole model joints = [obj for obj in objectlist if 'joint/type' in obj] for joint in joints: resource_obj = ioUtils.getResource(['joint', joint['joint/type']]) joint.pose.bones[0].custom_shape = resource_obj