1#--- ### Header 2bl_info = { 3 "name": "MORSE scene as Python API (.py)", 4 "author": "Gilberto Echeverria", 5 "version": (1, 0, 0), 6 "blender": (2, 5, 9), 7 "api": 36147, 8 "location": "File>Import-Export", 9 "category": "Import-Export", 10 "description": "Save a MORSE scene as a Python description", 11 "warning": "", 12 "wiki_url": "", 13 "tracker_url": "https://softs.laas.fr/bugzilla/" 14} 15 16import os 17import bpy 18import json 19import re 20from morse.builder.data import * 21from bpy.utils import register_module, unregister_module 22 23""" 24Morse API to save scene files 25 26To test this module you can open this file inside a Text panel in Blender, 27then run the script. 28This will generate a python file in the same directory where Blender was first executed. 29""" 30 31morse_types = { 32 "robots": "Robot", 33 "sensors": "Sensor", 34 "actuators": "Actuator", 35 "middleware": "Middleware", 36 "modifiers": "Modifier", 37} 38 39def save_translation(obj, obj_name): 40 # Set its position 41 position_string = '' 42 text_buffer = '' 43 component_position = obj.location 44 if component_position[0] != 0: 45 position_string += 'x=%.4f' % component_position[0] 46 if component_position[1] != 0: 47 if position_string != '': 48 position_string += ', ' 49 position_string += 'y=%.4f' % component_position[1] 50 if component_position[2] != 0: 51 if position_string != '': 52 position_string += ', ' 53 position_string += 'z=%.4f' % component_position[2] 54 # Register a translation only if necessary 55 if position_string != '': 56 text_buffer += "%s.translate(%s)\n" % (obj_name, position_string) 57 58 return (text_buffer) 59 60 61def save_rotation(obj, obj_name): 62 # Set its rotation 63 rotation_string = '' 64 text_buffer = '' 65 component_rotation = obj.rotation_euler 66 if component_rotation[0] != 0: 67 rotation_string += 'x=%.4f' % component_rotation[0] 68 if component_rotation[1] != 0: 69 if rotation_string != '': 70 rotation_string += ', ' 71 rotation_string += 'y=%.4f' % component_rotation[1] 72 if component_rotation[2] != 0: 73 if rotation_string != '': 74 rotation_string += ', ' 75 rotation_string += 'z=%.4f' % component_rotation[2] 76 # Register a translation only if necessary 77 if rotation_string != '': 78 text_buffer += "%s.rotate(%s)\n" % (obj_name, rotation_string) 79 80 return (text_buffer) 81 82 83def save_properties(obj, obj_name): 84 text_buffer = '' 85 # Store the properties of the component 86 for key,prop in obj.game.properties.items(): 87 if key not in ['Robot_Tag', 'Component_Tag', 'Middleware_Tag', 'Modifier_Tag', 'Class', 'Path']: 88 if prop.value != '': 89 if prop.type == 'STRING': 90 text_buffer += "%s.properties(%s = '%s')\n" % (obj_name, key, prop.value) 91 elif prop.type == 'FLOAT' or prop.type == 'TIMER': 92 text_buffer += "%s.properties(%s = %.4f)\n" % (obj_name, key, prop.value) 93 else: 94 text_buffer += "%s.properties(%s = %s)\n" % (obj_name, key, prop.value) 95 96 return (text_buffer) 97 98 99def scan_scene (file_out): 100 """ Read all the MORSE components from a Blender file 101 102 Create lists of robots and components to save them as a text file 103 """ 104 file_out.write("from morse.builder import *\n\n") 105 106 robot_text = '' 107 component_text = '' 108 109 for obj in bpy.data.objects: 110 try: 111 component_path = obj.game.properties['Path'].value 112 # Exit if the object is not a MORSE component 113 except KeyError as detail: 114 continue 115 116 # Ignore middleware and modifier empties. 117 # These will be added dinamically by the builder 118 if 'middleware' in component_path or 'modifiers' in component_path: 119 continue 120 121 # Read what type of component this is, 122 # from the source of its python file 123 path_elements = component_path.split('/') 124 component_type = path_elements[-2] 125 component_name = path_elements[-1] 126 127 builder_type = morse_types[component_type] 128 129 # Swap dots for underscores in object names 130 obj_name = re.sub('\.', '_', obj.name) 131 # Create the object instance 132 if component_type == 'robots': 133 robot_text += "%s = %s('%s')\n" % (obj_name, builder_type, component_name) 134 robot_text += save_translation(obj, obj_name) 135 robot_text += save_rotation(obj, obj_name) 136 robot_text += save_properties(obj, obj_name) 137 robot_text += "\n" 138 139 # Assign component to the parent 140 if component_type == 'sensors' or component_type == 'actuators': 141 component_text += "%s = %s('%s')\n" % (obj_name, builder_type, component_name) 142 component_text += save_translation(obj, obj_name) 143 component_text += save_rotation(obj, obj_name) 144 parent_name = re.sub('\.', '_', obj.parent.name) 145 component_text += "%s.append(%s)\n" % (parent_name, obj_name) 146 component_text += save_properties(obj, obj_name) 147 component_text += "\n" 148 149 # Write the buffers to the text file 150 file_out.write("# Robots\n") 151 file_out.write(robot_text) 152 file_out.write("# Components\n") 153 file_out.write(component_text) 154 155 156def scan_config(file_out): 157 """ Parse the contents of 'component_config.py' 158 159 Produce a configuration file that 'morsebuilder' can use to 160 configure the robot/middleware bindings in a scene. 161 """ 162 import component_config 163 file_out.write("# Scene configuration\n") 164 for key,value in component_config.component_datastream.items(): 165 component = re.sub('\.', '_', key) 166 # If the 'value' variable contains only strings, use that string 167 # as the name of the middleware. 168 # This is done for backwards compatibility with the previous 169 # syntax that allowed only one middleware per component 170 if isinstance (value[0], str): 171 mw = value[0] 172 mw = mw.lower() 173 file_out.write("%s.add_stream('%s', %s)\n" % (component, mw, value)) 174 # If using the new syntax that allows more than one middleware 175 # per component 176 else: 177 for item in value: 178 mw = item[0] 179 mw = mw.lower() 180 file_out.write("%s.add_stream('%s', %s)\n" % (component, mw, item)) 181 182 try: 183 component_config.component_service 184 file_out.write("\n") 185 for key,value in component_config.component_service.items(): 186 component = re.sub('\.', '_', key) 187 mw = re.search('(\w+)_request_manager', value[0]) 188 file_out.write("%s.add_service('%s')\n" % (component, mw.group(1))) 189 except AttributeError as detail: 190 print ("\tNo services configured") 191 192 try: 193 component_config.component_modifier 194 file_out.write("\n") 195 for key,value in component_config.component_modifier.items(): 196 component = re.sub('\.', '_', key) 197 mod = value[0] 198 file_out.write("%s.alter(%s)\n" % (component, mod)) 199 except AttributeError as detail: 200 print ("\tNo modifiers configured") 201 202def get_environment(): 203 try: 204 ssh = bpy.data.objects['Scene_Script_Holder'] 205 environment_file = ssh.game.properties['environment_file'].value 206 except KeyError as detail: 207 environment_file = 'indoors-1/indoor-1' 208 print ("No environment file specified in 'Scene_Script_Holder'\nUsing '%s' as default" % environment_file) 209 210 return environment_file 211 212 213def save_scene(): 214 print ("\nRunning from %s" % bpy.data.filepath) 215 filename = bpy.path.display_name_from_filepath(bpy.data.filepath) + ".py" 216 file_out = open(filename, "w") 217 print ("Saving scene robot configuration to file '%s'" % filename) 218 scan_scene(file_out) 219 scan_config(file_out) 220 env_name = get_environment() 221 file_out.write("\nenv = Environment('%s')" % env_name) 222 file_out.write("\nenv.create()") 223 file_out.close() 224 print ("Configuration saved") 225 226 227#--- ### Operator 228class MorseExporter(bpy.types.Operator): 229 ''' Convert a MORSE scene configuration to a python script ''' 230 bl_idname = "export_scene.morse" 231 bl_label = "Save MORSE scene" 232 bl_description = "Convert a MORSE scene configuration to a python script" 233 234 #--- Blender interface methods 235 #@classmethod 236 #def poll(cls,context): 237 #return (context.mode == 'OBJECT') 238 239 def execute(self,context): 240 save_scene() 241 return ('FINISHED') 242 243 244def menu_draw(self, context): 245 self.layout.operator_context = 'INVOKE_REGION_WIN' 246 self.layout.operator(MorseExporter.bl_idname, "Save MORSE scene (.py)") 247 248#--- ### Register 249def register(): 250 register_module(__name__) 251 bpy.types.INFO_MT_file_export.prepend(menu_draw) 252def unregister(): 253 bpy.types.INFO_MT_file_export.remove(menu_draw) 254 unregister_module(__name__) 255 256#--- ### Main code 257if __name__ == '__main__': 258 register() 259 #save_scene() 260