def createLink(scale, position=None, orientation=None, name=''): """Creates an empty link (bone) at the current 3D cursor position. :param scale: This is the scale you want to apply to the new link. :type scale: Float array with 3 elements. :param position: This specifies the position of the newly created link. When not given its (0.0,0.0,0.0) :type position: Float array with 3 elements. :param orientation: This specifies the rotation of the newly created link. When not given its (0.0,0.0,0.0) :type orientation:Float array with 3 elements. :param name: This sets the name for the new link. When not given the link is nameless. :type name: str :return: bpy_types.Object """ blenderUtils.toggleLayer(defs.layerTypes['link'], True) if position is None and orientation is None: bpy.ops.object.armature_add(layers=blenderUtils.defLayers([0])) elif position is None: bpy.ops.object.armature_add(rotation=orientation, layers=blenderUtils.defLayers([0])) elif orientation is None: bpy.ops.object.armature_add(location=position, layers=blenderUtils.defLayers([0])) else: bpy.ops.object.armature_add(location=position, rotation=orientation, layers=blenderUtils.defLayers([0])) link = bpy.context.active_object link.scale = [scale, scale, scale] bpy.ops.object.transform_apply(scale=True) if name: link.name = name link.phobostype = 'link' return link
def createLink(scale, position=None, orientation=None, name=''): """Creates an empty link (bone) at the current 3D cursor position. :param scale: This is the scale you want to apply to the new link. :type scale: Float array with 3 elements. :param position: This specifies the position of the newly created link. When not given its (0.0,0.0,0.0) :type position: Float array with 3 elements. :param orientation: This specifies the rotation of the newly created link. When not given its (0.0,0.0,0.0) :type orientation:Float array with 3 elements. :param name: This sets the name for the new link. When not given the link is nameless. :type name: string. :return: blender object """ blenderUtils.toggleLayer(defs.layerTypes['link'], True) if position is None and orientation is None: bpy.ops.object.armature_add(layers=blenderUtils.defLayers([0])) elif position is None: bpy.ops.object.armature_add(rotation=orientation, layers=blenderUtils.defLayers([0])) elif orientation is None: bpy.ops.object.armature_add(location=position, layers=blenderUtils.defLayers([0])) else: bpy.ops.object.armature_add(location=position, rotation=orientation, layers=blenderUtils.defLayers([0])) link = bpy.context.active_object link.scale = [scale, scale, scale] bpy.ops.object.transform_apply(scale=True) if name: link.name = name link.phobostype = 'link' return link
def setLinkTransformations(model, parent): """Assigns the transformations recursively for a model parent link according to the model. This needs access to the **object** key of a link entry in the specified model. The transformations for each link object are extracted from the specified model and applied to the Blender object. Args: parent(dict): parent link you want to set the children for. model(dict): model dictionary containing the **object** key for each link Returns: """ bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes['link']) for chi in parent['children']: child = model['links'][chi] # apply transform as saved in model location = mathutils.Matrix.Translation(child['pose']['translation']) rotation = ( mathutils.Euler(tuple(child['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() ) log("Transforming link {0}.".format(child['name']), 'DEBUG') transform_matrix = location * rotation child['object'].matrix_local = transform_matrix # traverse the tree setLinkTransformations(model, child)
def placeLinkSubelements(link): """Finds all subelements for a given link and sets the appropriate relations. In this case subelements are interials, visuals and collisions. :param link: The parent link you want to set the subelements for :type link: dict """ elements = getGeometricElements(link) + ([link['inertial']] if 'inertial' in link else []) bpy.context.scene.layers = bUtils.defLayers([defs.layerTypes[t] for t in defs.layerTypes]) parentlink = bpy.data.objects[link['name']] for element in elements: if 'pose' in element: location = mathutils.Matrix.Translation(element['pose']['translation']) rotation = mathutils.Euler(tuple(element['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() else: location = mathutils.Matrix.Identity(4) rotation = mathutils.Matrix.Identity(4) try: obj = bpy.data.objects[element['name']] except KeyError: log('Missing link element for placement: ' + element['name'], 'ERROR', 'placeLinkSubelements') continue sUtils.selectObjects([obj, parentlink], True, 1) bpy.ops.object.parent_set(type='BONE_RELATIVE') obj.matrix_local = location * rotation try: obj.scale = mathutils.Vector(element['geometry']['scale']) except KeyError: pass
def attachSensor(self, sensor): """This function attaches a given sensor to its parent link. :param sensor: The sensor you want to attach to its parent link. :type sensor: dict """ bpy.context.scene.layers = bUtils.defLayers([defs.layerTypes[t] for t in defs.layerTypes]) #try: if 'pose' in sensor: urdf_geom_loc = mathutils.Matrix.Translation(sensor['pose']['translation']) urdf_geom_rot = mathutils.Euler(tuple(sensor['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() else: urdf_geom_loc = mathutils.Matrix.Identity(4) urdf_geom_rot = mathutils.Matrix.Identity(4) sensorobj = bpy.data.objects[sensor['name']] if 'link' in sensor: parentLink = sUtils.getObjectByNameAndType(sensor['link'], 'link') #parentLink = bpy.data.objects['link_' + sensor['link']] sUtils.selectObjects([sensorobj, parentLink], True, 1) bpy.ops.object.parent_set(type='BONE_RELATIVE') else: #TODO: what? pass sensorobj.matrix_local = urdf_geom_loc * urdf_geom_rot
def createLink(link): """Creates the blender representation of a given link and its parent joint. Args: link(dict): The link you want to create a representation of. Returns: bpy_types.Object -- the newly created blender link object. """ # create armature/bone bUtils.toggleLayer(defs.layerTypes['link'], True) bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.armature_add( layers=bUtils.defLayers([defs.layerTypes['link']])) newlink = bpy.context.active_object # Move bone when adding at selected objects location if 'matrix' in link: newlink.matrix_world = link['matrix'] newlink.phobostype = 'link' if link['name'] in bpy.data.objects.keys(): log('Object with name of new link already exists: ' + link['name'], 'WARNING') nUtils.safelyName(newlink, link['name']) # set the size of the link visuals, collisions = getGeometricElements(link) if visuals or collisions: scale = max((geometrymodel.getLargestDimension(e['geometry']) for e in visuals + collisions)) else: scale = 0.2 # use scaling factor provided by user if 'scale' in link: scale *= link['scale'] newlink.scale = (scale, scale, scale) bpy.ops.object.transform_apply(scale=True) # add custom properties for prop in link: if prop.startswith('$'): for tag in link[prop]: newlink['link/' + prop[1:] + '/' + tag] = link[prop][tag] # create inertial if 'inertial' in link: inertia.createInertial(link['name'], link['inertial'], newlink) # create geometric elements log( "Creating visual and collision objects for link '{0}': {1}".format( link['name'], ', '.join([elem['name'] for elem in visuals + collisions])), 'DEBUG') for v in visuals: geometrymodel.createGeometry(v, 'visual', newlink) for c in collisions: geometrymodel.createGeometry(c, 'collision', newlink) return newlink
def createLink(link): """Creates the blender representation of a given link and its parent joint. :param link: The link you want to create a representation of. :type link: dict :return: bpy_types.Object -- the newly created blender link object. """ # create armature/bone bUtils.toggleLayer(defs.layerTypes['link'], True) bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.armature_add( layers=bUtils.defLayers([defs.layerTypes['link']])) newlink = bpy.context.active_object # Move bone when adding at selected objects location if 'matrix' in link: newlink.matrix_world = link['matrix'] newlink.phobostype = 'link' newlink.name = link['name'] # FIXME: This is a hack and should be checked before creation! # this is a backup in case an object with the link's name already exists newlink["link/name"] = link['name'] # FIXME geometric dimensions are not intiallized properly, thus scale always 0.2! # set the size of the link elements = getGeometricElements(link) scale = max((geometrymodel.getLargestDimension(element['geometry']) for element in elements)) if elements else 0.2 # use scaling factor provided by user #FIXME where would this *scale* come from? if 'scale' in link: scale *= link['scale'] newlink.scale = (scale, scale, scale) bpy.ops.object.transform_apply(scale=True) # add custom properties for prop in link: if prop.startswith('$'): for tag in link[prop]: newlink['link/' + prop[1:] + '/' + tag] = link[prop][tag] # create inertial if 'inertial' in link: inertiamodel.createInertialFromDictionary(link['name'], link['inertial']) # create visual elements if 'visual' in link: for v in link['visual']: visual = link['visual'][v] geometrymodel.createGeometry(visual, 'visual') # create collision elements if 'collision' in link: for c in link['collision']: collision = link['collision'][c] geometrymodel.createGeometry(collision, 'collision') return newlink
def createSensor(sensor, reference, origin=mathutils.Matrix()): blenderUtils.toggleLayer(defs.layerTypes['sensor'], value=True) # create sensor object if 'Camera' in sensor['type']: bpy.context.scene.layers[defs.layerTypes['sensor']] = True bpy.ops.object.add(type='CAMERA', location=origin.to_translation(), rotation=origin.to_euler(), layers=blenderUtils.defLayers([defs.layerTypes['sensor']])) newsensor = bpy.context.active_object if reference is not None: selectionUtils.selectObjects([newsensor, bpy.data.objects[reference]], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') elif sensor['type'] in ['RaySensor', 'RotatingRaySensor', 'ScanningSonar', 'MultiLevelLaserRangeFinder']: # TODO: create a proper ray sensor scanning layer disc here newsensor = blenderUtils.createPrimitive(sensor['name'], 'disc', (0.5, 36), defs.layerTypes['sensor'], 'phobos_laserscanner', origin.to_translation(), protation=origin.to_euler()) if reference is not None and reference != []: if type(reference) == str: key = reference else: key = reference[0] selectionUtils.selectObjects([newsensor, bpy.data.objects[key]], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') else: # contact, force and torque sensors (or unknown sensors) newsensor = blenderUtils.createPrimitive(sensor['name'], 'sphere', 0.05, defs.layerTypes['sensor'], 'phobos_sensor', origin.to_translation(), protation=origin.to_euler()) if 'Node' in sensor['type']: newsensor['sensor/nodes'] = sorted(reference) elif 'Joint' in sensor['type'] or 'Motor' in sensor['type']: newsensor['sensor/joints'] = sorted(reference) if reference is not None and reference != []: selectionUtils.selectObjects([newsensor, selectionUtils.getRoot(bpy.data.objects[0])], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') # set sensor properties newsensor.phobostype = 'sensor' newsensor.name = sensor['name'] newsensor['sensor/type'] = sensor['type'] for prop in sensor['props']: newsensor['sensor/'+prop] = sensor['props'][prop] # add custom properties #for prop in sensor: # if prop.startswith('$'): # for tag in sensor[prop]: # newsensor[prop[1:]+'/'+tag] = sensor[prop][tag] # throw warning if type is not known if sensor['type'] not in defs.sensortypes: print("### Warning: sensor", sensor['name'], "is of unknown/custom type.") selectionUtils.selectObjects([newsensor], clear=False, active=0) return newsensor
def placeChildLinks(model, parent): """Creates parent-child-relationship for a given parent and all existing children in Blender. :param parent: This is the parent link you want to set the children for. :type: dict """ bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes['link']) children = [] for l in model['links']: if 'parent' in model['links'][l] and model['links'][l][ 'parent'] == parent['name']: children.append(model['links'][l]) for child in children: # 1: set parent relationship (this makes the parent inverse the inverse of the parents world transform) parentLink = bpy.data.objects[parent['name']] childLink = bpy.data.objects[child['name']] sUtils.selectObjects([childLink, parentLink], True, 1) bpy.ops.object.parent_set(type='BONE_RELATIVE') # 2: move to parents origin by setting the world matrix to the parents world matrix # removing this line does not seem to make a difference (TODO delete me?) childLink.matrix_world = parentLink.matrix_world # TODO delete me? # #bpy.context.scene.objects.active = childLink # if 'pivot' in child: # pivot = child['pivot'] # cursor_location = bpy.context.scene.cursor_location # bpy.context.scene.cursor_location = mathutils.Vector((-pivot[0]*0.3, -pivot[1]*0.3, -pivot[2]*0.3)) # bpy.ops.object.origin_set(type='ORIGIN_CURSOR') # bpy.context.scene.cursor_location = cursor_location # 3: apply local transform as saved in model (changes matrix_local) location = mathutils.Matrix.Translation(child['pose']['translation']) rotation = mathutils.Euler(tuple(child['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() transform_matrix = location * rotation childLink.matrix_local = transform_matrix # 4: be happy, as world and basis are now the same and local is the transform to be exported to urdf # 5: take care of the rest of the tree placeChildLinks(model, child)
def placeLinkSubelements(link): """Places visual and collision objects for a given link. :param link: The parent link you want to set the subelements for :type link: dict """ elements = getGeometricElements(link) bpy.context.scene.layers = bUtils.defLayers( [defs.layerTypes[t] for t in defs.layerTypes]) parentlink = bpy.data.objects[link['name']] log( 'Placing subelements for link: ' + link['name'] + ': ' + ', '.join([elem['name'] for elem in elements]), 'DEBUG') for element in elements: if 'pose' in element: log('Pose detected for element: ' + element['name'], 'DEBUG') location = mathutils.Matrix.Translation( element['pose']['translation']) rotation = mathutils.Euler( tuple(element['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() else: log('No pose in element: ' + element['name'], 'DEBUG') location = mathutils.Matrix.Identity(4) rotation = mathutils.Matrix.Identity(4) try: obj = bpy.data.objects[element['name']] except KeyError: log('Missing link element for placement: ' + element['name'], 'ERROR') continue sUtils.selectObjects([obj, parentlink], True, 1) bpy.ops.object.parent_set(type='BONE_RELATIVE') obj.matrix_local = location * rotation try: obj.scale = mathutils.Vector(element['geometry']['scale']) except KeyError: log('No scale defined for element ' + element['name'], 'DEBUG')
def placeChildLinks(model, parent): """Creates parent-child-relationship for a given parent and all existing children in Blender. Args: parent(dict): parent link you want to set the children for. model(dict): Returns: """ bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes['link']) for c in parent['children']: child = model['links'][c] # apply transform as saved in model location = mathutils.Matrix.Translation(child['pose']['translation']) rotation = mathutils.Euler(tuple(child['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() log('Placing link {0}'.format(child['name']), 'DEBUG') transform_matrix = location * rotation log("Joint transform: {0}".format(transform_matrix), 'DEBUG') child['object'].matrix_local = transform_matrix # traverse the tree placeChildLinks(model, child)
def createGeometry(viscol, geomsrc, linkobj=None): """Creates Blender object for visual or collision objects. Returns reference to new object or None if creation failed. Args: viscol(dict): visual/collision dictionary element geomsrc(str): new object's phobostype linkobj(bpy.types.Object): link object Returns: bpy.types.Object or None """ if 'geometry' not in viscol or viscol['geometry'] is {}: return None bpy.ops.object.select_all(action='DESELECT') geom = viscol['geometry'] # create the Blender object if geom['type'] == 'mesh': bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes[geomsrc]) meshname = "".join(os.path.basename(geom["filename"]).split(".")[:-1]) if not os.path.isfile(geom['filename']): log(geom['filename'] + " is no file. Object " + viscol['name'] + " will have empty mesh!", "ERROR") #bpy.data.meshes.new(meshname) bpy.ops.object.add(type='MESH') newgeom = bpy.context.active_object nUtils.safelyName(newgeom, viscol['name'], phobostype=geomsrc) else: if meshname in bpy.data.meshes: log('Assigning copy of existing mesh ' + meshname + ' to ' + viscol['name'], 'INFO') bpy.ops.object.add(type='MESH') newgeom = bpy.context.object newgeom.data = bpy.data.meshes[meshname] else: log("Importing mesh for {0} element: '{1}".format(geomsrc, viscol['name']), 'INFO') filetype = geom['filename'].split('.')[-1].lower() newgeom = meshes.importMesh(geom['filename'], filetype) newgeom.data.name = meshname if not newgeom: log('Failed to import mesh file ' + geom['filename'], 'ERROR') return # scale imported object if 'scale' in geom: newgeom.scale = geom['scale'] else: if geom['type'] == 'box': dimensions = geom['size'] elif geom['type'] == 'cylinder': dimensions = (geom['radius'], geom['length']) elif geom['type'] == 'sphere': dimensions = geom['radius'] else: log("Unknown geometry type of " + geomsrc + viscol['name'] + '. Placing empty coordinate system.', "ERROR") bpy.ops.object.empty_add(type='PLAIN_AXES', radius=0.2) obj = bpy.context.object obj.phobostype = geomsrc nUtils.safelyName(bpy.context.active_object, viscol['name'], phobostype=geomsrc) return None log('Creating primtive for {0}: {1}'.format(geomsrc, viscol['name']), 'INFO') newgeom = bUtils.createPrimitive(viscol['name'], geom['type'], dimensions, phobostype=geomsrc) newgeom.select = True bpy.ops.object.transform_apply(scale=True) # from here it's the same for both meshes and primitives newgeom['geometry/type'] = geom['type'] if geomsrc == 'visual': try: assignMaterial(newgeom, viscol['material']) except KeyError: log('No material for visual ' + viscol['name'], 'DEBUG') for prop in viscol: if prop.startswith('$'): for tag in viscol[prop]: newgeom[prop[1:]+'/'+tag] = viscol[prop][tag] nUtils.safelyName(newgeom, viscol['name']) newgeom[geomsrc+"/name"] = viscol['name'] newgeom.phobostype = geomsrc # place geometric object relative to its parent link if linkobj: if 'pose' in viscol: log('Setting transformation of element: ' + viscol['name'], 'DEBUG') location = mathutils.Matrix.Translation(viscol['pose']['translation']) rotation = mathutils.Euler(tuple(viscol['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() else: log('No pose in element: ' + viscol['name'], 'DEBUG') location = mathutils.Matrix.Identity(4) rotation = mathutils.Matrix.Identity(4) sUtils.selectObjects([newgeom, linkobj], True, 1) bpy.ops.object.parent_set(type='BONE_RELATIVE') newgeom.matrix_local = location * rotation if 'scale' in viscol['geometry']: newgeom.scale = mathutils.Vector(viscol['geometry']['scale']) return newgeom
def defineSubmodel(submodelname, submodeltype, version='', objects=None): """Defines a new submodule group with the specified name and type. The group will be named like so: 'submodeltype:submodelname/version' Objects with the phobostype 'interface' (if present) are handled separately and put into a respective submodel group (which features the 'interface' submodeltype). If the version is omitted, the respective part of the name is dropped, too. If no object list is provided the objects are derived from selection. The submodeltype is also added as dict entry to the group in Blender. The selected objects are moved to the respective layer for submodels or interfaces. Args: submodelname: descriptive name of the submodel submodeltype: type of the submodel (e.g. 'fmu', 'mechanics') version: a version string (e.g. '1.0', 'dangerous') (Default value = '') objects: the objects which belong to the submodel (None will derive objects from the selection) (Default value = None) Returns: : a tuple of the submodelgroup and interfacegroup/None """ if not objects: objects = bpy.context.selected_objects # split interface from physical objects interfaces = [i for i in objects if i.phobostype == 'interface'] physical_objects = [p for p in objects if p.phobostype != 'interface'] # make the physical group sUtils.selectObjects(physical_objects, True, 0) submodelgroupname = submodeltype + ':' + submodelname if version != '': submodelgroupname += '/' + version if submodelgroupname in bpy.data.groups.keys(): log('submodelgroupname ' + 'already exists', 'WARNING') bpy.ops.group.create(name=submodelgroupname) submodelgroup = bpy.data.groups[submodelgroupname] submodelgroup['submodeltype'] = submodeltype submodelgroup['version'] = version modeldefs = defs.definitions['submodeltypes'][submodeltype] # copy the definition parameters to the group properties for key in modeldefs['definitions']: submodelgroup[key] = modeldefs['definitions'][key] # move objects to submodel layer for obj in physical_objects: obj.layers = bUtils.defLayers(defs.layerTypes['submodel']) log('Created submodel group ' + submodelname + ' of type "' + submodeltype + '".', 'DEBUG') interfacegroup = None # make the interface group if interfaces: sUtils.selectObjects(interfaces, True, 0) interfacegroupname = 'interfaces:' + submodelname if version != '': interfacegroupname += '/' + version # TODO what about overwriting groups with same names? bpy.ops.group.create(name=interfacegroupname) interfacegroup = bpy.data.groups[interfacegroupname] interfacegroup['submodeltype'] = 'interfaces' # copy interface definitions from submodel definitions for key in modeldefs['interfaces']: interfacegroup[key] = modeldefs['interfaces'][key] # move objects to interface layer for obj in interfaces: obj.layers = bUtils.defLayers(defs.layerTypes['interface']) log('Created interface group for submodel ' + submodelname + '.', 'DEBUG') else: log('No interfaces for this submodel.', 'DEBUG') for i in interfaces: i.show_name = True return (submodelgroup, interfacegroup)
def createGeometry(viscol, geomsrc, linkobj=None): """Creates Blender object for visual or collision objects. If the creation fails, nothing is returned. These entries in the dictionary are mandatory: | **geometry**: | **type**: type of geometry (mesh, box, cylinder, sphere) Depending on the geometry type other values are required: `size`, `radius`, `length` These entries are optional: | **geometry**: | **scale**: scale for the new geometry | **material**: material name to assign to the visual | **pose**: specifies the placement of the new object relative to the optional linkobj | **translation**: position vector for the new object | **rotation_euler**: rotation for the new object Furthermore any generic properties, prepended by a ``$`` will be added as custom properties to the visual/collision object. E.g. ``$test/etc`` would be put to visual/test/etc for a visual object. However, these properties are extracted only in the first layer of hierarchy. Args: viscol(dict): visual/collision model dictionary representation geomsrc(str): phobostype of the new object linkobj(bpy.types.Object, optional): link object to attach the visual/collision object to (Default value = None) Returns: bpy.types.Object or None: the new geometry object or nothing """ if 'geometry' not in viscol or viscol['geometry'] is {}: log("Could not create {}. Geometry information not defined!".format(geomsrc), 'ERROR') return None bpy.ops.object.select_all(action='DESELECT') geom = viscol['geometry'] # create the Blender object if geom['type'] == 'mesh': bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes[geomsrc]) meshname = "".join(os.path.basename(geom["filename"]).split(".")[:-1]) if not os.path.isfile(geom['filename']): log( "This path " + geom['filename'] + " is no file. Object " + viscol['name'] + " will have empty mesh!", 'ERROR', ) bpy.ops.object.add(type='MESH') newgeom = bpy.context.active_object nUtils.safelyName(newgeom, viscol['name'], phobostype=geomsrc) else: if meshname in bpy.data.meshes: log('Assigning copy of existing mesh ' + meshname + ' to ' + viscol['name'], 'INFO') bpy.ops.object.add(type='MESH') newgeom = bpy.context.object newgeom.data = bpy.data.meshes[meshname] else: log("Importing mesh for {0} element: '{1}".format(geomsrc, viscol['name']), 'INFO') filetype = geom['filename'].split('.')[-1].lower() newgeom = meshes.importMesh(geom['filename'], filetype) # bpy.data.meshes[newgeom].name = meshname if not newgeom: log('Failed to import mesh file ' + geom['filename'], 'ERROR') return else: if geom['type'] == 'box': dimensions = geom['size'] elif geom['type'] == 'cylinder': dimensions = (geom['radius'], geom['length']) elif geom['type'] == 'sphere': dimensions = geom['radius'] # TODO add support for heightmap, image, plane and polyline geometries (see sdf!) else: log( "Unknown geometry type of " + geomsrc + viscol['name'] + '. Placing empty coordinate system.', "ERROR", ) bpy.ops.object.empty_add(type='PLAIN_AXES', radius=0.2) obj = bpy.context.object obj.phobostype = geomsrc nUtils.safelyName(bpy.context.active_object, viscol['name'], phobostype=geomsrc) return None log("Creating primtive for {0}: {1}".format(geomsrc, viscol['name']), 'INFO') newgeom = bUtils.createPrimitive( viscol['name'], geom['type'], dimensions, phobostype=geomsrc ) newgeom.select = True bpy.ops.object.transform_apply(scale=True) # from here it's the same for both meshes and primitives newgeom['geometry/type'] = geom['type'] if geomsrc == 'visual': if 'material' in viscol: assignMaterial(newgeom, viscol['material']) else: log('No material for visual {}.'.format(viscol['name']), 'WARNING') # write generic custom properties for prop in viscol: if prop.startswith('$'): for tag in viscol[prop]: newgeom[prop[1:] + '/' + tag] = viscol[prop][tag] nUtils.safelyName(newgeom, viscol['name']) newgeom[geomsrc + '/name'] = viscol['name'] newgeom.phobostype = geomsrc # place geometric object relative to its parent link if linkobj: if 'pose' in viscol: log("Setting transformation of element: " + viscol['name'], 'DEBUG') location = mathutils.Matrix.Translation(viscol['pose']['translation']) rotation = ( mathutils.Euler(tuple(viscol['pose']['rotation_euler']), 'XYZ').to_matrix().to_4x4() ) else: log("No pose in element: " + viscol['name'], 'DEBUG') location = mathutils.Matrix.Identity(4) rotation = mathutils.Matrix.Identity(4) eUtils.parentObjectsTo(newgeom, linkobj) newgeom.matrix_local = location * rotation # scale imported object if 'scale' in geom: newgeom.scale = geom['scale'] # make object smooth eUtils.smoothen_surface(newgeom) return newgeom
def createSensor(sensor, reference, origin=mathutils.Matrix()): """This function creates a new sensor specified by its parameters. :param sensor: The phobos representation of the new sensor. :type sensor: dict :param reference: This is an object to add a parent relationship to. :type reference: bpy_types.Object :param origin: The new sensors origin. :type origin: mathutils.Matrix :return: The newly created sensor object """ blenderUtils.toggleLayer(defs.layerTypes['sensor'], value=True) # create sensor object if 'Camera' in sensor['type']: bpy.context.scene.layers[defs.layerTypes['sensor']] = True bpy.ops.object.add(type='CAMERA', location=origin.to_translation(), rotation=origin.to_euler(), layers=blenderUtils.defLayers([defs.layerTypes['sensor']])) newsensor = bpy.context.active_object if reference is not None: selectionUtils.selectObjects([newsensor, bpy.data.objects[reference]], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') elif sensor['type'] in ['RaySensor', 'RotatingRaySensor', 'ScanningSonar', 'MultiLevelLaserRangeFinder']: # TODO: create a proper ray sensor scanning layer disc here newsensor = blenderUtils.createPrimitive(sensor['name'], 'disc', (0.5, 36), defs.layerTypes['sensor'], 'phobos_laserscanner', origin.to_translation(), protation=origin.to_euler()) if reference is not None and reference != []: if type(reference) == str: key = reference else: key = reference[0] selectionUtils.selectObjects([newsensor, bpy.data.objects[key]], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') else: # contact, force and torque sensors (or unknown sensors) newsensor = blenderUtils.createPrimitive(sensor['name'], 'sphere', 0.05, defs.layerTypes['sensor'], 'phobos_sensor', origin.to_translation(), protation=origin.to_euler()) if 'Node' in sensor['type']: newsensor['sensor/nodes'] = sorted(reference) elif 'Joint' in sensor['type'] or 'Motor' in sensor['type']: newsensor['sensor/joints'] = sorted(reference) # set sensor properties newsensor.phobostype = 'sensor' newsensor.name = sensor['name'] newsensor['sensor/type'] = sensor['type'] #for prop in ['link', 'joint', 'links', 'joints', 'motors']: # if prop in sensor: # newsensor['sensor/'+prop] = sensor[prop] # add custom properties #for prop in sensor: # if prop.startswith('$'): # for tag in sensor[prop]: # newsensor[prop[1:]+'/'+tag] = sensor[prop][tag] # throw warning if type is not known if sensor['type'] not in defs.sensortypes: print("### Warning: sensor", sensor['name'], "is of unknown/custom type.") selectionUtils.selectObjects([newsensor], clear=False, active=0) return newsensor
def createGeometry(viscol, geomsrc): """Creates geometrical Blender object for visual or collision objects. :param viscol: The visual/collision dictionary element you want to create the geometry for. :type viscol: dict :param geomsrc: The new viscols phobostype. :type geomsrc: str """ if 'geometry' not in viscol or viscol['geometry'] is {}: return None newgeom = None bpy.ops.object.select_all(action='DESELECT') geom = viscol['geometry'] geomtype = geom['type'] # create the Blender object if geomtype == 'mesh': # if hasattr(self, 'zipped') and self.zipped: # if not os.path.isdir(os.path.join(self.tmp_path, tmp_dir_name)): # os.mkdir(os.path.join(self.tmp_path, tmp_dir_name)) # archive = zipfile.ZipFile(self.filepath) # archive.extract(geom['filename'], path=os.path.join(self.tmp_path, tmp_dir_name)) # geom_path = os.path.join(os.path.abspath(os.path.join(self.tmp_path, tmp_dir_name)), geom['filename']) # else: if 'sourcefilepath' in geom: geom_path = os.path.normpath( os.path.join(os.path.dirname(geom['sourcefilepath']), geom['filename'])) log('sourcefilepath: ' + geom_path, 'DEBUG', 'createGeometry') else: geom_path = geom['filename'] # Remove 'urdf/package://{package_name}' to workaround the lack # of rospack here. This supposes that the urdf file is in the # urdf folder and that the meshes are in the meshes folder at # the same level as the urdf folder. if 'package://' in geom_path: geom_path = re.sub(r'(.*)urdf/package://([^/]+)/(.*)', '\\1\\3', geom_path) bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes[geomsrc]) meshname = "".join(os.path.basename(geom["filename"]).split(".")[:-1]) if not os.path.isfile(geom_path): log( geom_path + " is no file. Object " + viscol['name'] + " will have empty mesh!", "ERROR", "createGeometry") bpy.data.meshes.new(meshname) if meshname in bpy.data.meshes: log( 'Assigning copy of existing mesh ' + meshname + ' to ' + viscol['name'], 'INFO', 'createGeometry') bpy.ops.object.add(type='MESH') newgeom = bpy.context.object newgeom.data = bpy.data.meshes[meshname] else: log('Importing mesh for link element ' + viscol['name'], 'INFO', 'createGeometry') filetype = geom['filename'].split('.')[-1].lower() newgeom = meshes.importMesh(geom_path, filetype) newgeom.data.name = meshname if not newgeom: log('Failed to import mesh file ' + geom['filename'], 'ERROR', 'createGeometry') return # scale imported object if 'scale' in geom: sUtils.selectObjects((newgeom, ), clear=True) newgeom.scale = geom['scale'] else: if geomtype == 'box': dimensions = geom['size'] elif geomtype == 'cylinder': dimensions = (geom['radius'], geom['length']) elif geomtype == 'sphere': dimensions = geom['radius'] else: log( "Could not determine geometry type of " + geomsrc + viscol['name'] + '. Placing empty coordinate system.', "ERROR") bpy.ops.object.empty_add(type='PLAIN_AXES', radius=0.2) bpy.context.active_object.name = viscol['name'] return None log('Creating primitve for obj ' + viscol['name'], 'INFO', 'createGeometry') newgeom = bUtils.createPrimitive(viscol['name'], geomtype, dimensions, player=geomsrc) newgeom.select = True bpy.ops.object.transform_apply(scale=True) # from here it's the same for both meshes and primitives newgeom.phobostype = geomsrc newgeom['geometry/type'] = geomtype if geomsrc == 'visual': try: if 'name' in viscol['material']: assignMaterial(newgeom, viscol['material']['name']) else: assignMaterial(newgeom, viscol['material']) except KeyError: log('No material for obj', viscol['name'], 'DEBUG', 'createGeometry') #FIXME: place empty coordinate system and return...what? Error handling of file import! for prop in viscol: if prop.startswith('$'): for tag in viscol[prop]: newgeom[prop[1:] + '/' + tag] = viscol[prop][tag] newgeom.name = viscol['name'] newgeom[geomsrc + "/name"] = viscol['name'] return newgeom
def createSensor(sensor, reference, origin=mathutils.Matrix()): """This function creates a new sensor specified by its parameters. :param sensor: The phobos representation of the new sensor. :type sensor: dict :param reference: This is an object to add a parent relationship to. :type reference: bpy_types.Object :param origin: The new sensors origin. :type origin: mathutils.Matrix :return: The newly created sensor object """ blenderUtils.toggleLayer(defs.layerTypes['sensor'], value=True) # create sensor object if 'Camera' in sensor['type']: bpy.ops.object.add(type='CAMERA', location=origin.to_translation(), rotation=origin.to_euler(), layers=blenderUtils.defLayers([defs.layerTypes['sensor']])) newsensor = bpy.context.active_object if reference is not None: selectionUtils.selectObjects([newsensor, reference], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') elif sensor['type'] in ['RaySensor', 'RotatingRaySensor', 'ScanningSonar', 'MultiLevelLaserRangeFinder']: # TODO: create a proper ray sensor scanning layer disc here newsensor = blenderUtils.createPrimitive(sensor['name'], 'disc', (0.5, 36), defs.layerTypes['sensor'], 'phobos_laserscanner', origin.to_translation(), protation=origin.to_euler()) if reference is not None: selectionUtils.selectObjects([newsensor, reference], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') else: # contact, force and torque sensors (or unknown sensors) newsensor = blenderUtils.createPrimitive(sensor['name'], 'sphere', 0.05, defs.layerTypes['sensor'], 'phobos_sensor', origin.to_translation(), protation=origin.to_euler()) if sensor['type'] == 'Joint6DOF': pass #newsensor['sensor/nodes'] = nameUtils.getObjectName(reference) elif 'Node' in sensor['type']: newsensor['sensor/nodes'] = sorted([nameUtils.getObjectName(ref) for ref in reference]) elif 'Joint' in sensor['type'] or 'Motor' in sensor['type']: newsensor['sensor/joints'] = sorted([nameUtils.getObjectName(ref) for ref in reference]) if reference is not None: selectionUtils.selectObjects([newsensor, reference], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') # set sensor properties newsensor.phobostype = 'sensor' newsensor.name = sensor['name'] newsensor['sensor/type'] = sensor['type'] #for prop in ['link', 'joint', 'links', 'joints', 'motors']: # if prop in sensor: # newsensor['sensor/'+prop] = sensor[prop] # add custom properties #for prop in sensor: # if prop.startswith('$'): # for tag in sensor[prop]: # newsensor[prop[1:]+'/'+tag] = sensor[prop][tag] # throw warning if type is not known if sensor['type'] not in defs.sensortypes: print("### Warning: sensor", sensor['name'], "is of unknown/custom type.") selectionUtils.selectObjects([newsensor], clear=False, active=0) return newsensor
def createSensor(sensor, reference, origin=mathutils.Matrix()): """This function creates a new sensor specified by its parameters. Args: sensor(dict): The phobos representation of the new sensor. reference(bpy_types.Object): This is an object to add a parent relationship to. origin(mathutils.Matrix, optional): The new sensors origin. (Default value = mathutils.Matrix() Returns: The newly created sensor object """ bUtils.toggleLayer(defs.layerTypes['sensor'], value=True) # create sensor object if 'Camera' in sensor['type']: bpy.ops.object.add(type='CAMERA', location=origin.to_translation(), rotation=origin.to_euler(), layers=bUtils.defLayers([defs.layerTypes['sensor'] ])) newsensor = bpy.context.active_object if reference is not None: sUtils.selectObjects([newsensor, reference], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') elif sensor['type'] in [ 'RaySensor', 'RotatingRaySensor', 'ScanningSonar', 'MultiLevelLaserRangeFinder' ]: # TODO: create a proper ray sensor scanning layer disc here newsensor = bUtils.createPrimitive(sensor['name'], 'disc', (0.5, 36), defs.layerTypes['sensor'], 'phobos_laserscanner', origin.to_translation(), protation=origin.to_euler()) if reference is not None: sUtils.selectObjects([newsensor, reference], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') # contact, force and torque sensors (or unknown sensors) else: newsensor = bUtils.createPrimitive(sensor['name'], 'sphere', 0.05, defs.layerTypes['sensor'], 'phobos_sensor', origin.to_translation(), protation=origin.to_euler()) if sensor['type'] == '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]) if reference is not None: sUtils.selectObjects([newsensor, reference], clear=True, active=1) bpy.ops.object.parent_set(type='BONE_RELATIVE') # set sensor properties newsensor.phobostype = 'sensor' newsensor.name = sensor['name'] newsensor['sensor/type'] = sensor['type'] # TODO delete me? #for prop in ['link', 'joint', 'links', 'joints', 'motors']: # if prop in sensor: # newsensor['sensor/'+prop] = sensor[prop] # add custom properties #for prop in sensor: # if prop.startswith('$'): # for tag in sensor[prop]: # newsensor[prop[1:]+'/'+tag] = sensor[prop][tag] # throw warning if type is not known if sensor['type'] not in defs.sensortypes: print("### Warning: sensor", sensor['name'], "is of unknown/custom type.") sUtils.selectObjects([newsensor], clear=False, active=0) return newsensor
def createGeometry(viscol, geomsrc): """Creates geometrical Blender object for visual or collision objects. :param viscol: The visual/collision dictionary element you want to create the geometry for. :type viscol: dict :param geomsrc: The new viscols phobostype. :type geomsrc: str """ if 'geometry' not in viscol or viscol['geometry'] is {}: return None bpy.ops.object.select_all(action='DESELECT') geom = viscol['geometry'] geomtype = geom['type'] # create the Blender object if geomtype == 'mesh': bpy.context.scene.layers = bUtils.defLayers(defs.layerTypes[geomsrc]) meshname = "".join(os.path.basename(geom["filename"]).split(".")[:-1]) if not os.path.isfile(geom['filename']): log(geom['filename'] + " is no file. Object " + viscol['name'] + " will have empty mesh!", "ERROR") bpy.data.meshes.new(meshname) if meshname in bpy.data.meshes: log('Assigning copy of existing mesh ' + meshname + ' to ' + viscol['name'], 'INFO') bpy.ops.object.add(type='MESH') newgeom = bpy.context.object newgeom.data = bpy.data.meshes[meshname] else: log('Importing mesh for link element ' + viscol['name'], 'INFO') filetype = geom['filename'].split('.')[-1].lower() newgeom = meshes.importMesh(geom['filename'], filetype) newgeom.data.name = meshname if not newgeom: log('Failed to import mesh file ' + geom['filename'], 'ERROR') return # scale imported object if 'scale' in geom: sUtils.selectObjects((newgeom,), clear=True) newgeom.scale = geom['scale'] else: if geomtype == 'box': dimensions = geom['size'] elif geomtype == 'cylinder': dimensions = (geom['radius'], geom['length']) elif geomtype == 'sphere': dimensions = geom['radius'] else: log("Could not determine geometry type of " + geomsrc + viscol['name'] + '. Placing empty coordinate system.', "ERROR") bpy.ops.object.empty_add(type='PLAIN_AXES', radius=0.2) bpy.context.active_object.name = viscol['name'] return None log('Creating primtive for obj ' + viscol['name'], 'INFO') newgeom = bUtils.createPrimitive(viscol['name'], geomtype, dimensions, player=geomsrc) newgeom.select = True bpy.ops.object.transform_apply(scale=True) # from here it's the same for both meshes and primitives newgeom.phobostype = geomsrc newgeom['geometry/type'] = geomtype if geomsrc == 'visual': try: if 'name' in viscol['material']: assignMaterial(newgeom, viscol['material']['name']) else: assignMaterial(newgeom, viscol['material']) except KeyError: log('No material for obj ' + viscol['name'], 'DEBUG') # FIXME: place empty coordinate system and return...what? Error handling of file import! for prop in viscol: if prop.startswith('$'): for tag in viscol[prop]: newgeom[prop[1:]+'/'+tag] = viscol[prop][tag] newgeom.name = viscol['name'] newgeom[geomsrc+"/name"] = viscol['name'] return newgeom
def createLink(link): """Creates the blender representation of a given link and its parent joint. The link is added to the link layer. These entries in the dictionary are mandatory: *name*: name for the link The specified dictionary may contain these entries: *matrix*: world matrix for the new link transformation *scale*: scale for the new link (single float) *visual*: list of visual dictionaries *collision*: list of collision dictionaries *inertial*: inertial dictionary (an inertial object will be created on the fly) Furthermore any generic properties, prepended by a `$` will be added as custom properties to the link. E.g. $test/etc would be put to link/test/etc. However, these properties are extracted only in the first layer of hierarchy. Args: link(dict): The link you want to create a representation of. Returns: : bpy_types.Object -- the newly created blender link object. """ log("Creating link object '{}'...".format(link['name']), 'DEBUG', prefix='\n') # create armature/bone bUtils.toggleLayer(defs.layerTypes['link'], True) bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.armature_add(layers=bUtils.defLayers([defs.layerTypes['link']])) newlink = bpy.context.active_object # Move bone when adding at selected objects location if 'matrix' in link: newlink.matrix_world = link['matrix'] # give it a proper name newlink.phobostype = 'link' if link['name'] in bpy.data.objects.keys(): log('Object with name of new link already exists: ' + link['name'], 'WARNING') nUtils.safelyName(newlink, link['name']) # set the size of the link visuals, collisions = getGeometricElements(link) if visuals or collisions: scale = max( (geometrymodel.getLargestDimension(e['geometry']) for e in visuals + collisions) ) else: scale = 0.2 # use scaling factor provided by user if 'scale' in link: scale *= link['scale'] newlink.scale = (scale, scale, scale) bpy.ops.object.transform_apply(scale=True) # add custom properties for prop in link: if prop.startswith('$'): for tag in link[prop]: newlink['link/' + prop[1:] + '/' + tag] = link[prop][tag] # create inertial if 'inertial' in link: inertia.createInertial(link['inertial'], newlink) # create geometric elements log( "Creating visual and collision objects for link '{0}':\n{1}".format( link['name'], ' \n'.join([elem['name'] for elem in visuals + collisions]) ), 'DEBUG', ) for vis in visuals: geometrymodel.createGeometry(vis, 'visual', newlink) for col in collisions: geometrymodel.createGeometry(col, 'collision', newlink) return newlink