Beispiel #1
0
    def execute(self, context):
        obj = context.active_object

        # rename only if necessary
        if self.newname != '' and self.newname != nUtils.getObjectName(obj):
            log("Renaming " + obj.phobostype + " '" + nUtils.getObjectName(obj) + "' to '" +
                self.newname + "'.", 'INFO')
            nUtils.safelyName(obj, self.newname)
        elif self.newname == '':
            log("Removing custom name from " + obj.phobostype + " '" + obj.name + "'.", 'INFO')
            if obj.phobostype + '/name' in obj:
                del obj[obj.phobostype + '/name']

        # only links have joint names
        if obj.phobostype == 'link':
            if self.jointname != '':
                # only change/add joint/name if it was changed
                if 'joint/name' not in obj or (
                        'joint/name' in obj and self.jointname != obj['joint/name']):
                    log("Renaming joint of " + obj.phobostype + " '" + nUtils.getObjectName(obj) +
                        "' to '" + self.jointname + "'.", 'INFO')
                    obj['joint/name'] = self.jointname
            # remove joint/name when empty
            elif self.jointname == '':
                if 'joint/name' in obj:
                    log("Removing joint name from " + obj.phobostype + " '" + obj.name + "'.",
                        'INFO')
                    del obj['joint/name']

        return {'FINISHED'}
Beispiel #2
0
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
Beispiel #3
0
    def execute(self, context):
        """

        Args:
          context: 

        Returns:

        """
        obj = context.active_object

        # rename only if necessary
        if self.newname != '' and self.newname != nUtils.getObjectName(obj):
            log(
                "Renaming "
                + obj.phobostype
                + " '"
                + nUtils.getObjectName(obj)
                + "' to '"
                + self.newname
                + "'.",
                'INFO',
            )
            nUtils.safelyName(obj, self.newname)
        elif self.newname == '':
            log("Removing custom name from " + obj.phobostype + " '" + obj.name + "'.", 'INFO')
            if obj.phobostype + '/name' in obj:
                del obj[obj.phobostype + '/name']

        # only links have joint names
        if obj.phobostype == 'link':
            if self.jointname != '':
                # only change/add joint/name if it was changed
                if 'joint/name' not in obj or (
                    'joint/name' in obj and self.jointname != obj['joint/name']
                ):
                    log(
                        "Renaming joint of "
                        + obj.phobostype
                        + " '"
                        + nUtils.getObjectName(obj)
                        + "' to '"
                        + self.jointname
                        + "'.",
                        'INFO',
                    )
                    obj['joint/name'] = self.jointname
            # remove joint/name when empty
            elif self.jointname == '':
                if 'joint/name' in obj:
                    log(
                        "Removing joint name from " + obj.phobostype + " '" + obj.name + "'.",
                        'INFO',
                    )
                    del obj['joint/name']

        return {'FINISHED'}
Beispiel #4
0
def createInterface(ifdict, parent=None):
    """Create an interface object and optionally parent to existing object.
    
    ifdict is expected as:
    
    | **type**: str
    | **direction**: str
    | **model**: str
    | **name**: str
    | **parent**: bpy.types.Object (optional)
    | **scale**: float (optional)

    Args:
      ifdict(dict): interface data
      parent(bpy.types.Object, optional): designated parent object (Default value = None)

    Returns:
      bpy.data.Object: newly created interface object

    """
    if not parent:
        try:
            parent = ifdict['parent']
            assert isinstance(parent, bpy.types.Object)
        except (AttributeError, AssertionError, KeyError):
            parent = None
    location = parent.matrix_world.translation if parent else mathutils.Vector(
    )
    rotation = parent.matrix_world.to_euler() if parent else mathutils.Euler()

    model = ifdict['model'] if 'model' in ifdict else 'default'
    templateobj = ioUtils.getResource(
        ('interface', model, ifdict['direction']))
    scale = ifdict['scale'] if 'scale' in ifdict else 1.0
    ifobj = bUtils.createPrimitive(
        ifdict['name'],
        'box',
        (1.0, 1.0, 1.0),
        defs.layerTypes['interface'],
        plocation=location,
        protation=rotation,
        phobostype='interface',
    )
    nUtils.safelyName(ifobj, ifdict['name'], 'interface')
    ifobj.data = templateobj.data
    ifobj.scale = (scale, ) * 3
    ifobj['interface/type'] = ifdict['type']
    ifobj['interface/direction'] = ifdict['direction']
    if parent is not None:
        ifobj['interface/parent'] = parent.name
        parentObjectsTo(ifobj, parent)
    bpy.ops.object.make_single_user(object=True, obdata=True)
Beispiel #5
0
def createInterface(ifdict, parent=None):
    """Create an interface object and optionally parent to existing object.
    
    ifdict is expected as:
    
    | **type**: str
    | **direction**: str
    | **model**: str
    | **name**: str
    | **parent**: bpy.types.Object (optional)
    | **scale**: float (optional)

    Args:
      ifdict(dict): interface data
      parent(bpy.types.Object, optional): designated parent object (Default value = None)

    Returns:
      bpy.data.Object: newly created interface object

    """
    if not parent:
        try:
            parent = ifdict['parent']
            assert isinstance(parent, bpy.types.Object)
        except (AttributeError, AssertionError, KeyError):
            parent = None
    location = parent.matrix_world.translation if parent else mathutils.Vector()
    rotation = parent.matrix_world.to_euler() if parent else mathutils.Euler()

    model = ifdict['model'] if 'model' in ifdict else 'default'
    templateobj = ioUtils.getResource(('interface', model, ifdict['direction']))
    scale = ifdict['scale'] if 'scale' in ifdict else 1.0
    ifobj = bUtils.createPrimitive(
        ifdict['name'],
        'box',
        (1.0, 1.0, 1.0),
        defs.layerTypes['interface'],
        plocation=location,
        protation=rotation,
        phobostype='interface',
    )
    nUtils.safelyName(ifobj, ifdict['name'], 'interface')
    ifobj.data = templateobj.data
    ifobj.scale = (scale,) * 3
    ifobj['interface/type'] = ifdict['type']
    ifobj['interface/direction'] = ifdict['direction']
    bpy.ops.object.make_single_user(object=True, obdata=True)
Beispiel #6
0
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('link', True)
    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.object.armature_add()
    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(location=False,
                                   rotation=False,
                                   scale=True,
                                   properties=False)

    # 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)
    bUtils.sortObjectToCollection(newlink, 'link')
    return newlink
Beispiel #7
0
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
Beispiel #8
0
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
Beispiel #9
0
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
Beispiel #10
0
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