Example #1
0
    def create_ik_targets(self, bones):

        # Bug with iTaSC! cf http://developer.blender.org/T37894
        if bpymorse.version() < (2, 70, 0):
            if self._bpy_object.pose.ik_solver == 'ITASC':
                logger.warn("Due to a bug in Blender (T37894), only the standard " \
                            "IK solver can be used with IK targets. Switching " \
                            "from iTaSC to standard IK solver.")
                self._bpy_object.pose.ik_solver = 'LEGACY'

        for target in bones:
            posebone = self.get_posebone(target)
            bpymorse.add_morse_empty("ARROWS")
            empty = bpymorse.get_first_selected_object()
            empty.scale = [0.01, 0.01, 0.01]

            empty.matrix_local = posebone.bone.matrix_local
            empty.location = posebone.bone.tail_local

            ik = posebone.constraints.new("IK")
            ik.use_rotation = True
            ik.use_tail = True
            ik.target = empty

            self.ik_targets.append((empty, target))
Example #2
0
    def __init__(self, cname, category, filename=''):
        """ ComponentCreator constructor

        This class allow to create Python component for MORSE. It consists of an
        Empty object, to which you can then add meshs of your choice. It adds
        automaticaly the logic (Always sensor link to a Python controller). And
        set the default physics_type to 'NO_COLLISION'.

        :param cname: (string) component name (Empty object in Blender scene)
        :param category: (string) in ['actuators', 'sensors', 'robots']
        :param filename: (string, optional) used for the datastream configuration
            name of the Blender file in MORSE_COMPONENTS/category/filename.blend
            see morse.builder.data.MORSE_DATASTREAM_DICT (default: None)
        :return: a new AbstractComponent instance.
        """
        AbstractComponent.__init__(self, filename=filename, category=category)
        bpymorse.deselect_all()
        bpymorse.add_morse_empty()
        obj = bpymorse.get_first_selected_object()
        if cname:
            obj.name = cname
            self.basename = cname
        # no collision by default for components
        obj.game.physics_type = 'NO_COLLISION'
        self.set_blender_object(obj)
        # Add MORSE logic
        self.morseable()
Example #3
0
    def __init__(self, cname, category, filename=''):
        """ ComponentCreator constructor

        This class allow to create Python component for MORSE. It consists of an
        Empty object, to which you can then add meshs of your choice. It adds
        automaticaly the logic (Always sensor link to a Python controller). And
        set the default physics_type to 'NO_COLLISION'.

        :param cname: (string) component name (Empty object in Blender scene)
        :param category: (string) in ['actuators', 'sensors', 'robots']
        :param filename: (string, optional) used for the datastream configuration
            name of the Blender file in MORSE_COMPONENTS/category/filename.blend
            see morse.builder.data.MORSE_DATASTREAM_DICT (default: None)
        :return: a new AbstractComponent instance.
        """
        AbstractComponent.__init__(self, filename=filename, category=category)
        bpymorse.deselect_all()
        bpymorse.add_morse_empty()
        obj = bpymorse.get_first_selected_object()
        if cname:
            obj.name = cname
            self.basename = cname
        # no collision by default for components
        obj.game.physics_type = 'NO_COLLISION'
        self.set_blender_object(obj)
        # Add MORSE logic
        self.morseable()
Example #4
0
    def create_ik_targets(self, bones):

        # Bug with iTaSC! cf http://developer.blender.org/T37894
        if bpymorse.version() < (2, 70, 0):
            if self._bpy_object.pose.ik_solver == 'ITASC':
                logger.warn("Due to a bug in Blender (T37894), only the standard " \
                            "IK solver can be used with IK targets. Switching " \
                            "from iTaSC to standard IK solver.")
                self._bpy_object.pose.ik_solver = 'LEGACY'

        for target in bones:
            posebone = self._get_posebone(target)
            bpymorse.add_morse_empty("ARROWS")
            empty = bpymorse.get_first_selected_object()
            empty.scale = [0.01, 0.01, 0.01]

            empty.matrix_local = posebone.bone.matrix_local
            empty.location = posebone.bone.tail_local

            existing_ik = [c for c in posebone.constraints if c.type == 'IK']
            if len(existing_ik) == 1:
                ik_constraint = existing_ik[0]
            elif existing_ik:
                raise MorseBuilderError("Bone %s has several IK constraints." \
                        "MORSE supports only one IK constraint per bone. Please " \
                        "remove other ones.")
            else:
                ik_constraint = posebone.constraints.new("IK")

            ik_constraint.ik_type = "DISTANCE"
            ik_constraint.use_rotation = True
            ik_constraint.use_tail = True
            ik_constraint.target = empty

            self.ik_targets.append((empty, target))
Example #5
0
    def __init__(self, 
                 name, 
                 category, 
                 action = APPEND_EMPTY, 
                 blendfile = "", 
                 blendobject = None,
                 make_morseable = True):
        """ ComponentCreator constructor

        This class allow to create simulation components from MORSE builder
        scripts. It initially consists in an Empty object, to which you can
        then add meshs of your choice. It adds automaticaly the logic (Always
        sensor link to a Python controller). And set the default physics_type
        to 'NO_COLLISION'.

        :param name: (string) component name (used as Blender object name)
        :param category: (string) one of ['actuators', 'sensors', 'robots']
        :param action: indicate what to do with the `blendfile` and
        `blendobject` parameters. Must be one of [APPEND_EMPTY, USE_BLEND,
        LINK_EXISTING_OBJECT]. 
            - If APPEND_EMPTY (default), a new Blender `Empty` is created and
            `blendfile` and `blendobject` are ignored.
            - If USE_BLEND, `blendfile` is treated as the path to a Blender file,
            and if `blendobject` is also specified, the given object is
            selected (otherwise, the last object selected in the Blender file
            is returned).
            - If LINK_EXISTING_OBJECT, `blendfile` is ignored and `blendobject`
            is treated as the name of a Blender object which is already present
            in the scene.
        :param blendfile: (string, default:"") path to a Blender file (.blend)
        containing meshes for the component. Must be in MORSE_RESOURCE_PATH.
        :param blendobject: (string, default:None) Name of the Blender object
        to use (cf above for details).
        :param make_morseable: (boolean) Add Morse logic. Make it false
            if you add some blend file which already contains the
            necessary logic (default: True).
        """
        AbstractComponent.__init__(self, filename=blendfile, category=category)
        bpymorse.deselect_all()
        if action == ComponentCreator.APPEND_EMPTY:
            bpymorse.add_morse_empty()
        elif action == ComponentCreator.USE_BLEND:
            self.append_meshes()
            if blendobject:
                bpymorse.select_only(bpymorse.get_object(blendobject))
        elif action == ComponentCreator.LINK_EXISTING_OBJECT:
            bpymorse.select_only(bpymorse.get_object(blendobject))

        obj = bpymorse.get_first_selected_object()
        if name:
            obj.name = name
            self.basename = name
        # no collision by default for components
        obj.game.physics_type = 'NO_COLLISION'
        self.set_blender_object(obj)
        # Add MORSE logic
        if make_morseable:
            self.morseable()

        self.properties(Component_Tag = True, classpath = self.__class__._classpath)
Example #6
0
    def create_ik_targets(self, bones):

        # Bug with iTaSC! cf http://developer.blender.org/T37894
        if bpymorse.version() < (2, 70, 0):
            if self._bpy_object.pose.ik_solver == 'ITASC':
                logger.warn("Due to a bug in Blender (T37894), only the standard " \
                            "IK solver can be used with IK targets. Switching " \
                            "from iTaSC to standard IK solver.")
                self._bpy_object.pose.ik_solver = 'LEGACY'

        for target in bones:
            posebone = self._get_posebone(target)
            bpymorse.add_morse_empty("ARROWS")
            empty = bpymorse.get_first_selected_object()
            empty.scale = [0.01, 0.01, 0.01]

            empty.matrix_local = posebone.bone.matrix_local
            empty.location = posebone.bone.tail_local

            existing_ik = [c for c in posebone.constraints if c.type == 'IK']
            if len(existing_ik) == 1:
                ik_constraint = existing_ik[0]
            elif existing_ik:
                raise MorseBuilderError("Bone %s has several IK constraints." \
                        "MORSE supports only one IK constraint per bone. Please " \
                        "remove other ones.")
            else:
                ik_constraint = posebone.constraints.new("IK")

            ik_constraint.ik_type = "DISTANCE"
            ik_constraint.use_rotation = True
            ik_constraint.use_tail = True
            ik_constraint.target = empty

            self.ik_targets.append((empty, target))
Example #7
0
    def append_meshes(self, objects=None, component=None, prefix=None):
        """ Append the objects to the scene

        The ``objects`` are located either in:
        MORSE_COMPONENTS/``self._category``/``component``.blend/Object/
        or in: MORSE_RESOURCE_PATH/``component``/Object/

        If `component` is not set (neither as argument of `append_meshes` nor
        through the :py:class:`AbstractComponent` constructor), a Blender
        `Empty` is created instead.

        :param objects: list of the objects names to append
        :param component: component in which the objects are located
        :param prefix: filter the objects names to append (used by PassiveObject)
        :return: list of the imported (selected) Blender objects
        """

        component = component or self._blender_filename

        if not component:  # no Blender resource: simply create an empty
            bpymorse.deselect_all()
            bpymorse.add_morse_empty()
            return [bpymorse.get_first_selected_object()]

        filepath = self._compute_filepath(component)

        if not objects:  # append all objects from blend file
            objects = bpymorse.get_objects_in_blend(filepath)

        if prefix:  # filter (used by PassiveObject)
            objects = [obj for obj in objects if obj.startswith(prefix)]

        # Format the objects list to append
        objlist = [{"name": obj} for obj in objects]

        bpymorse.deselect_all()
        # Append the objects to the scene, and (auto)select them
        if bpymorse.version() >= (2, 71, 6):
            bpymorse.append(directory=filepath + "/Object/", autoselect=True, files=objlist)
        else:
            bpymorse.link_append(directory=filepath + "/Object/", link=False, autoselect=True, files=objlist)

        return bpymorse.get_selected_objects()
Example #8
0
    def append_meshes(self, objects=None, component=None, prefix=None):
        """ Append the component's Blender objects to the scene

        The ``objects`` are located either in:
        MORSE_COMPONENTS/``self._category``/``component``.blend/Object/
        or in: MORSE_RESOURCE_PATH/``component``/Object/

        If `component` is not set (neither as argument of `append_meshes` nor
        through the :py:class:`AbstractComponent` constructor), a Blender
        `Empty` is created instead.

        .. note::

            By default, all the objects present in the component's blend file are
            imported. If you need to exclude some (like lights you may have in your
            blend file), prefix the name of this objects with ``_`` so that MORSE
            ignores them.

        :param objects: list of the objects names to append
        :param component: component in which the objects are located
        :param prefix: filter the objects names to append (used by PassiveObject)
        :return: list of the imported (selected) Blender objects
        """

        component = component or self._blender_filename

        if not component:  # no Blender resource: simply create an empty
            bpymorse.deselect_all()
            bpymorse.add_morse_empty()
            return [
                bpymorse.get_first_selected_object(),
            ]

        filepath = self._compute_filepath(component)

        if not objects:  # append all objects from blend file
            objects = bpymorse.get_objects_in_blend(filepath)

        filtered_objects = objects

        # ignore objects starting with '_'
        filtered_objects = [obj for obj in objects if not obj.startswith('_')]

        if prefix:  # filter (used by PassiveObject)
            filtered_objects = [
                obj for obj in filtered_objects if obj.startswith(prefix)
            ]

        logger.info("Importing objects from %s: %s" % (filepath, str(objects)))
        excluded = set(objects) - set(filtered_objects)
        if excluded:
            logger.info("(excluding %s)" % str(list(excluded)))

        # Format the objects list to append
        objlist = [{'name': obj} for obj in filtered_objects]

        bpymorse.deselect_all()
        # Append the objects to the scene, and (auto)select them
        if bpymorse.version() >= (2, 71, 6):
            bpymorse.append(directory=filepath + '/Object/',
                            autoselect=True,
                            files=objlist)
        else:
            bpymorse.link_append(directory=filepath + '/Object/',
                                 link=False,
                                 autoselect=True,
                                 files=objlist)

        return bpymorse.get_selected_objects()
Example #9
0
    def append_meshes(self, objects=None, component=None, prefix=None):
        """ Append the objects to the scene

        The ``objects`` are located either in:
        MORSE_COMPONENTS/``self._category``/``component``.blend/Object/
        or in: MORSE_RESOURCE_PATH/``component``/Object/

        If `component` is not set (neither as argument of `append_meshes` nor
        through the :py:class:`AbstractComponent` constructor), a Blender
        `Empty` is created instead.

        :param objects: list of the objects names to append
        :param component: component in which the objects are located
        :param prefix: filter the objects names to append (used by PassiveObject)
        :return: list of the imported (selected) Blender objects
        """


        component = component or self._blender_filename

        if not component: # no Blender resource: simply create an empty
            bpymorse.deselect_all()
            bpymorse.add_morse_empty()
            return [bpymorse.get_first_selected_object(),]


        if component.endswith('.blend'):
            filepath = os.path.abspath(component) # external blend file
        else:
            filepath = os.path.join(MORSE_COMPONENTS, self._category,
                                    component + '.blend')

        looked_dirs = [filepath]

        if not os.path.exists(filepath):
            # Search for some blend file in different paths
            filepath = None
            resource_path = MORSE_RESOURCE_PATH.split(os.pathsep)
            for path in resource_path:
                tmp = os.path.join(path, component)
                looked_dirs.append(tmp)
                if os.path.exists(tmp):
                    filepath = tmp
                    break
            # Check if we got a match

            if not filepath:
                logger.error("Error while trying to load '%s': model not found.\n"
                             "I was looking for one of these files: \n%s\n"
                             "Either provide an absolute path, or a path relative \n"
                             "to MORSE assets directories ($MORSE_RESOURCE_PATH \n"
                             "or default path, typically $PREFIX/share/morse/data)."% (component, looked_dirs))
                raise FileNotFoundError("%s '%s' not found"%(self.__class__.__name__, component))

        if not objects: # append all objects from blend file
            objects = bpymorse.get_objects_in_blend(filepath)

        if prefix: # filter (used by PassiveObject)
            objects = [obj for obj in objects if obj.startswith(prefix)]

        # Format the objects list to append
        objlist = [{'name':obj} for obj in objects]

        bpymorse.deselect_all()
        # Append the objects to the scene, and (auto)select them
        if bpymorse.version() >= (2, 71, 6):
            bpymorse.append(directory=filepath + '/Object/',
                            autoselect=True, files=objlist)
        else:
            bpymorse.link_append(directory=filepath + '/Object/', link=False,
                                 autoselect=True, files=objlist)

        return bpymorse.get_selected_objects()
Example #10
0
    def __init__(self,
                 name,
                 category,
                 action=APPEND_EMPTY,
                 blendfile="",
                 blendobject=None,
                 make_morseable=True):
        """ ComponentCreator constructor

        This class allow to create simulation components from MORSE builder
        scripts. It initially consists in an Empty object, to which you can
        then add meshs of your choice. It adds automaticaly the logic (Always
        sensor link to a Python controller). And set the default physics_type
        to 'NO_COLLISION'.

        :param name: (string) component name (used as Blender object name)
        :param category: (string) one of ['actuators', 'sensors', 'robots']
        :param action: indicate what to do with the `blendfile` and
        `blendobject` parameters. Must be one of [APPEND_EMPTY, USE_BLEND,
        LINK_EXISTING_OBJECT]. 
            - If APPEND_EMPTY (default), a new Blender `Empty` is created and
            `blendfile` and `blendobject` are ignored.
            - If USE_BLEND, `blendfile` is treated as the path to a Blender file,
            and if `blendobject` is also specified, the given object is
            selected (otherwise, the last object selected in the Blender file
            is returned).
            - If LINK_EXISTING_OBJECT, `blendfile` is ignored and `blendobject`
            is treated as the name of a Blender object which is already present
            in the scene.
        :param blendfile: (string, default:"") path to a Blender file (.blend)
        containing meshes for the component. Must be in MORSE_RESOURCE_PATH.
        :param blendobject: (string, default:None) Name of the Blender object
        to use (cf above for details).
        :param make_morseable: (boolean) Add Morse logic. Make it false
            if you add some blend file which already contains the
            necessary logic (default: True).
        """
        AbstractComponent.__init__(self, filename=blendfile, category=category)
        bpymorse.deselect_all()
        if action == ComponentCreator.APPEND_EMPTY:
            bpymorse.add_morse_empty()
        elif action == ComponentCreator.USE_BLEND:
            self.append_meshes()
            if blendobject:
                bpymorse.select_only(bpymorse.get_object(blendobject))
        elif action == ComponentCreator.LINK_EXISTING_OBJECT:
            bpymorse.select_only(bpymorse.get_object(blendobject))

        obj = bpymorse.get_first_selected_object()
        if name:
            obj.name = name
            self.basename = name
        # no collision by default for components
        obj.game.physics_type = 'NO_COLLISION'
        self.set_blender_object(obj)
        # Add MORSE logic
        if make_morseable:
            self.morseable()

        self.properties(Component_Tag=True,
                        classpath=self.__class__._classpath)
    def append_meshes(self, objects=None, component=None, prefix=None):
        """ Append the component's Blender objects to the scene

        The ``objects`` are located either in:
        MORSE_COMPONENTS/``self._category``/``component``.blend/Object/
        or in: MORSE_RESOURCE_PATH/``component``/Object/

        If `component` is not set (neither as argument of `append_meshes` nor
        through the :py:class:`AbstractComponent` constructor), a Blender
        `Empty` is created instead.

        .. note::

            By default, all the objects present in the component's blend file are
            imported. If you need to exclude some (like lights you may have in your
            blend file), prefix the name of this objects with ``_`` so that MORSE
            ignores them.

        :param objects: list of the objects names to append
        :param component: component in which the objects are located
        :param prefix: filter the objects names to append (used by PassiveObject)
        :return: list of the imported (selected) Blender objects
        """

        component = component or self._blender_filename

        if not component: # no Blender resource: simply create an empty
            bpymorse.deselect_all()
            bpymorse.add_morse_empty()
            return [bpymorse.get_first_selected_object(),]

        filepath = self._compute_filepath(component)

        if not objects: # append all objects from blend file
            objects = bpymorse.get_objects_in_blend(filepath)

        filtered_objects = objects

        # ignore objects starting with '_'
        filtered_objects = [obj for obj in objects if not obj.startswith('_')]

        if prefix: # filter (used by PassiveObject)
            filtered_objects = [obj for obj in filtered_objects if obj.startswith(prefix)]

        logger.info("Importing objects from %s: %s" % (filepath,
                                    str(objects)))
        excluded = set(objects) - set(filtered_objects)
        if excluded:
            logger.info("(excluding %s)" % str(list(excluded)))

        # Format the objects list to append
        objlist = [{'name':obj} for obj in filtered_objects]

        bpymorse.deselect_all()
        # Append the objects to the scene, and (auto)select them
        if bpymorse.version() >= (2, 71, 6):
            bpymorse.append(directory=filepath + '/Object/',
                            autoselect=True, files=objlist)
        else:
            bpymorse.link_append(directory=filepath + '/Object/', link=False,
                                 autoselect=True, files=objlist)

        return bpymorse.get_selected_objects()
Example #12
0
    def append_meshes(self, objects=None, component=None, prefix=None):
        """ Append the objects to the scene

        The ``objects`` are located either in:
        MORSE_COMPONENTS/``self._category``/``component``.blend/Object/
        or in: MORSE_RESOURCE_PATH/``component``/Object/

        If `component` is not set (neither as argument of `append_meshes` nor
        through the :py:class:`AbstractComponent` constructor), a Blender
        `Empty` is created instead.

        :param objects: list of the objects names to append
        :param component: component in which the objects are located
        :param prefix: filter the objects names to append (used by PassiveObject)
        :return: list of the imported (selected) Blender objects
        """

        component = component or self._blender_filename

        if not component:  # no Blender resource: simply create an empty
            bpymorse.deselect_all()
            bpymorse.add_morse_empty()
            return [
                bpymorse.get_first_selected_object(),
            ]

        if component.endswith('.blend'):
            filepath = os.path.abspath(component)  # external blend file
        else:
            filepath = os.path.join(MORSE_COMPONENTS, self._category,
                                    component + '.blend')

        looked_dirs = [filepath]

        if not os.path.exists(filepath):
            # Search for some blend file in different paths
            filepath = None
            resource_path = MORSE_RESOURCE_PATH.split(os.pathsep)
            for path in resource_path:
                tmp = os.path.join(path, component)
                looked_dirs.append(tmp)
                if os.path.exists(tmp):
                    filepath = tmp
                    break
            # Check if we got a match

            if not filepath:
                logger.error(
                    "Error while trying to load '%s': model not found.\n"
                    "I was looking for one of these files: \n%s\n"
                    "Either provide an absolute path, or a path relative \n"
                    "to MORSE assets directories ($MORSE_RESOURCE_PATH \n"
                    "or default path, typically $PREFIX/share/morse/data)." %
                    (component, looked_dirs))
                raise FileNotFoundError("%s '%s' not found" %
                                        (self.__class__.__name__, component))

        if not objects:  # append all objects from blend file
            objects = bpymorse.get_objects_in_blend(filepath)

        if prefix:  # filter (used by PassiveObject)
            objects = [obj for obj in objects if obj.startswith(prefix)]

        # Format the objects list to append
        objlist = [{'name': obj} for obj in objects]

        bpymorse.deselect_all()
        # Append the objects to the scene, and (auto)select them
        if bpymorse.version() >= (2, 71, 6):
            bpymorse.append(directory=filepath + '/Object/',
                            autoselect=True,
                            files=objlist)
        else:
            bpymorse.link_append(directory=filepath + '/Object/',
                                 link=False,
                                 autoselect=True,
                                 files=objlist)

        return bpymorse.get_selected_objects()