1import os 2import sys 3import pkgutil 4import inspect 5import subprocess 6 7try: 8 import bpy # Blender 9except ImportError: 10 print("Could not import bpy, run with Blender\n" 11 "blender -P components_exhibit.py") 12 sys.exit(-1) 13from mathutils import Euler 14 15def ext_exec(cmd, python=None): 16 if not python: 17 python = 'python%i.%i'%(sys.version_info.major, sys.version_info.minor) 18 return subprocess.getoutput('%s -c"%s"' % (python, cmd) ) 19 20def fix_python_path(python=None): 21 pythonpath = ext_exec("import os,sys;print(os.pathsep.join(sys.path))") 22 sys.path.extend(pythonpath.split(os.pathsep)) 23 24fix_python_path() 25try: 26 import morse # MORSE 27except ImportError: 28 raise ImportError("Could not import morse, set your PYTHONPATH\n" 29 'export PYTHONPATH="/usr/local/lib/python3.3/site-packages/:$PYTHONPATH"') 30from morse.builder import * 31 32# 33# helpers 34# 35 36def get_classes_from_module(module_name): 37 __import__(module_name) 38 # Predicate to make sure the classes only come from the module in question 39 def predicate(member): 40 return inspect.isclass(member) and member.__module__.startswith(module_name) 41 # fetch all members of module name matching 'pred' 42 return inspect.getmembers(sys.modules[module_name], predicate) 43 44def get_submodules(module_name): 45 """ Get a list of submodules from a module name. 46 Not recursive, don't return nor look in subpackages """ 47 __import__(module_name) 48 module = sys.modules[module_name] 49 module_path = getattr(module, '__path__') 50 return [name for _, name, ispkg in pkgutil.iter_modules(module_path) if not ispkg] 51 52def get_subclasses(module_name, skip_submodules=[]): 53 subclasses = [] 54 submodules = get_submodules(module_name) 55 for submodule_name in submodules: 56 if submodule_name in skip_submodules: 57 pass 58 submodule = "%s.%s"%(module_name, submodule_name) 59 try: 60 submodule_classes = get_classes_from_module(submodule) 61 for _, klass in submodule_classes: 62 subclasses.append(klass) 63 except Exception: 64 # can not import some resources 65 pass 66 return subclasses 67 68modules = [ 69 "morse.builder.sensors", 70 "morse.builder.actuators", 71 "morse.builder.robots", 72] 73 74specific_camera_pose_per_component = { 75 # Robots 76 'BasePR2': (.36, -2.25, 1.77), 77 'Morsy': (3.84, -3.12, 2.12, 1.32, 0.0, 0.86), 78 'SegwayRMP400': (.36, -2.25, 1.77), 79 'ATRV': (.36, -2.25, 1.77), 80 'Human': (4, 0, 2.8), # 65°,0,90° 81 'Hummer': (.36, -2.25, 1.77), 82 'Jido': (.36, -2.25, 1.77), 83 'B21': (.36, -2.25, 1.77), 84 'RMax': (.36, -2.25, 1.77), 85 'Submarine': (.36, -2.25, 1.77), 86 'Vicitm': (.36, -2.25, 1.77), 87 'NavPR2': (.36, -2.25, 1.77), 88 'QUAD2012': (.36, -1.25, .77), 89 'Quadrotor': (.36, -1.25, .77), 90 'Pioneer3DX': (.36, -1.25, .77), 91 # Sensors 92 'Accelerometer': (.36, -1.25, .77), 93 'Battery': (.36, -1.25, .77), 94 'DepthCamera': (.36, -1.25, .77), 95 'GPS': (.36, -1.25, .77), 96 'Gyroscope': (.36, -1.25, .77), 97 'Hokuyo': (.36, -1.25, .77), 98 'IMU': (.36, -1.25, .77), 99 'Infrared': (.36, -1.25, .77), 100 'Kinect': (.36, -1.25, .77), 101 'Odometry': (.36, -1.25, .77), 102 'Pose': (.36, -1.25, .77), 103 'Proximity': (.36, -1.25, .77), 104 'SearchAndRescue': (.36, -1.25, .77), 105 'SemanticCamera': (.36, -1.25, .77), 106 'Sick': (.36, -1.25, .77), 107 'SickLDMRS': (.36, -1.25, .77), 108 'StereoUnit': (.36, -1.25, .77), 109 'Thermometer': (.36, -1.25, .77), 110 'VideoCamera': (.36, -1.25, .77), 111 'Velodyne': (.36, -1.25, .77), 112 # Actuators 113 'PTU': (.36, -1.25, .77), 114 'PA10': (.36, -2.25, 1.77), 115 'KukaLWR': (.36, -2.25, 1.77), 116 'Gripper': (.36, -1.25, .77), 117} 118 119def pose_camera(location = (.36, -1.25, .77), rotation = (1.1, 0, 0.25)): 120 if len(location) == 6: 121 rotation = location[3:] 122 location = location[:3] 123 scene = bpy.context.scene 124 scene.camera.rotation_euler = Euler(rotation, 'XYZ') 125 scene.camera.location = location 126 127def setup_scene(): 128 scene = bpy.context.scene 129 if 'Cube' in bpy.data.objects: 130 bpymorse.delete('Cube') 131 # Set the scene's camera 132 scene.camera = bpy.data.objects['Camera'] 133 # Set the scene's output file format 134 scene.render.image_settings.file_format = 'PNG' 135 # RGBA, Images are saved with RGB and Alpha data (if supported). 136 scene.render.image_settings.color_mode = 'RGBA' 137 if bpy.app.version < (2, 66, 0): 138 # Premultiplied, Transparent RGB pixels are multiplied by the alpha channel. 139 scene.render.alpha_mode = 'PREMUL' 140 else: 141 # Transparent, World background is transparent with premultiplied alpha. 142 scene.render.alpha_mode = 'TRANSPARENT' 143 # Move the default Lamp 144 bpy.data.objects['Lamp'].location = (-2, 3, 6) 145 if len(bpy.data.lamps) < 3: 146 # Add new lamp point 147 bpy.ops.object.lamp_add(type='POINT', location=(5, 2, 1)) 148 bpy.data.lamps[-1].energy = 0.6 149 bpy.ops.object.lamp_add(type='POINT', location=(-4, 3, 4)) 150 bpy.data.lamps[-1].energy = 0.4 151 # Move the default Camera 152 pose_camera() 153 154def render_component(klass, save_path): 155 scene = bpy.context.scene 156 class_name = klass.__name__ 157 if class_name in specific_camera_pose_per_component: 158 pose_camera(specific_camera_pose_per_component[class_name]) 159 else: 160 print("#### class not in dict: %s"%str(class_name)) 161 pose_camera() 162 origin_objects = [obj.name for obj in bpy.data.objects] 163 try: 164 # load class 165 component = klass() 166 # Refresh the view 167 bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) 168 # Render the scene 169 scene.render.filepath = os.path.join(save_path, klass.__name__ + ".png") 170 bpy.ops.render.render(write_still=True) 171 except Exception as e: 172 print("[ERROR] Could not load %s.\nSet MORSE_ROOT to the prefix " \ 173 "used to install MORSE.\n%s"% (str(klass), str(e) ) ) 174 finally: 175 # Remove the inserted objects 176 bpymorse.delete([obj for obj in bpy.data.objects \ 177 if obj.name not in origin_objects]) 178 179def main(): 180 """ Generate "studio" image of MORSE components 181 182 HOWTO 183 184 export PYTHONPATH="/usr/local/lib/python3.3/site-packages/:$PYTHONPATH" 185 blender -P components_exhibit.py 186 187 Et voila! 188 """ 189 print ("==== PHOTO STUDIO FOR MORSE COMPONENTS ====\n\n") 190 save_path = "//morse_renders" 191 scene = bpy.context.scene 192 print('Using Scene[%s]'%scene.name) 193 setup_scene() 194 195 # browse morse components modules 196 for module in modules: 197 print("browse %s classes"%module) 198 for _, klass in get_classes_from_module(module): 199 print("process %s"%str(klass)) 200 if issubclass(klass, AbstractComponent): 201 try: 202 render_component(klass, save_path) 203 except Exception as e: 204 print("[ERROR] Could not render %s : %s"%(str(klass), str(e))) 205 else: 206 print("[ERROR] Not a Component : %s"%str(klass)) 207 208 print("\n\n") 209 210 print ("DONE!") 211 212 213if __name__ == '__main__': 214 main() 215 216