Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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)
Example #5
0
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)
Example #6
0
    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'}
Example #7
0
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
Example #8
0
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
Example #9
0
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
Example #10
0
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
Example #11
0
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]
Example #12
0
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
Example #13
0
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
Example #14
0
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]
Example #15
0
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
Example #16
0
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