1import logging; logger = logging.getLogger("morsebuilder." + __name__)
2from morse.builder import AbstractComponent, bpymorse
3from morse.core.exceptions import *
4
5class ComponentCreator(AbstractComponent):
6    _classpath = None
7    _blendname = None
8
9    APPEND_EMPTY = 0
10    USE_BLEND = 1
11    LINK_EXISTING_OBJECT = 2
12
13    def __init__(self,
14                 name,
15                 category,
16                 action = APPEND_EMPTY,
17                 blendfile = "",
18                 blendobject = None,
19                 make_morseable = True):
20        """ ComponentCreator constructor
21
22        This class allow to create simulation components from MORSE builder
23        scripts. It initially consists in an Empty object, to which you can
24        then add meshs of your choice. It adds automaticaly the logic (Always
25        sensor link to a Python controller). And set the default physics_type
26        to 'NO_COLLISION'.
27
28        :param name: (string) component name (used as Blender object name)
29        :param category: (string) one of ['actuators', 'sensors', 'robots']
30        :param action: indicate what to do with the `blendfile` and
31        `blendobject` parameters. Must be one of [APPEND_EMPTY, USE_BLEND,
32        LINK_EXISTING_OBJECT].
33            - If APPEND_EMPTY (default), a new Blender `Empty` is created and
34            `blendfile` and `blendobject` are ignored.
35            - If USE_BLEND, `blendfile` is treated as the path to a Blender file,
36            and if `blendobject` is also specified, the given object is
37            selected (otherwise, the last object selected in the Blender file
38            is returned).
39            - If LINK_EXISTING_OBJECT, `blendfile` is ignored and `blendobject`
40            is treated as the name of a Blender object which is already present
41            in the scene.
42        :param blendfile: (string, default:"") path to a Blender file (.blend)
43        containing meshes for the component. Must be in MORSE_RESOURCE_PATH.
44        :param blendobject: (string, default:None) Name of the Blender object
45        to use (cf above for details).
46        :param make_morseable: (boolean) Add Morse logic. Make it false
47            if you add some blend file which already contains the
48            necessary logic (default: True).
49        """
50        AbstractComponent.__init__(self, filename=blendfile, category=category)
51        bpymorse.deselect_all()
52        if action == ComponentCreator.APPEND_EMPTY:
53            bpymorse.add_morse_empty()
54        elif action == ComponentCreator.USE_BLEND:
55            self.append_meshes()
56            if blendobject:
57                bpymorse.select_only(bpymorse.get_object(blendobject))
58        elif action == ComponentCreator.LINK_EXISTING_OBJECT:
59            bpymorse.select_only(bpymorse.get_object(blendobject))
60
61        obj = bpymorse.get_first_selected_object()
62        if name:
63            obj.name = name
64            self.basename = name
65        # no collision by default for components
66        obj.game.physics_type = 'NO_COLLISION'
67        self.set_blender_object(obj)
68        # Add MORSE logic
69        if make_morseable:
70            self.morseable()
71
72        self.properties(Component_Tag = True, classpath = self.__class__._classpath)
73        self.frequency(60)
74
75    def parent_root(self, objects):
76        # Parent the root objects with this Component
77        for child in objects:
78            if not child.parent:
79                child.matrix_parent_inverse.identity()
80                child.parent = self._bpy_object
81
82    def append_meshes(self, objects=None, component=None, prefix=None):
83        """ Append the objects to this component
84
85        Overloads :py:meth:`morse.builder.abstractcomponent.AbstractComponent.append_meshes`
86
87        :param objects: list of the objects names to append
88        :param component: component in which the objects are located
89        :return: list of the imported Blender objects
90        """
91        imported_objects = AbstractComponent.append_meshes(self, objects,
92                                                           component, prefix)
93        self.parent_root(imported_objects)
94
95        return imported_objects
96
97    def append_collada(self, component=None):
98        """ Append Collada objects to this component
99
100        Overloads :py:meth:`morse.builder.abstractcomponent.AbstractComponent.append_collada`
101
102        :param component: component in which the objects are located
103        :return: list of the imported Blender objects
104        """
105        imported_objects = AbstractComponent.append_collada(self, component)
106        self.parent_root(imported_objects)
107
108        return imported_objects
109
110class SensorCreator(ComponentCreator):
111    def __init__(self, name = None,
112                       action = ComponentCreator.APPEND_EMPTY,
113                       make_morseable = True):
114
115        ComponentCreator.__init__(self,
116                                  name,
117                                  'sensors',
118                                  action,
119                                  blendfile = self.__class__._blendname,
120                                  make_morseable = make_morseable)
121
122
123class ActuatorCreator(ComponentCreator):
124
125    def __init__(self, name = None,
126                       action = ComponentCreator.APPEND_EMPTY,
127                       blendfile = None,
128                       blendobject = None,
129                       make_morseable = True):
130
131        if not blendfile:
132            blendfile = self.__class__._blendname
133
134        ComponentCreator.__init__(self,
135                                name,
136                                'actuators',
137                                action,
138                                blendfile,
139                                blendobject,
140                                make_morseable)
141