Пример #1
0
def loadPose(modelname, posename):
    """
    Load and apply a robot's stored pose.

    :param modelname: The model's name.
    :type modelname: str.
    :param posename: The name the pose is stored under.
    :type posename: str.
    :return Nothing.
    """
    load_file = bUtils.readTextFile(modelname + '::poses')
    if load_file == '':
        log('No poses stored.', 'ERROR', 'loadPose')
        return
    poses = yaml.load(load_file)
    try:
        pose = poses[posename]
        prev_mode = bpy.context.mode
        bpy.ops.object.mode_set(mode='POSE')
        for obj in sUtils.getObjectsByPhobostypes(['link']):
            if nUtils.getObjectName(obj, 'joint') in pose['joints']:
                obj.pose.bones['Bone'].rotation_mode = 'XYZ'
                obj.pose.bones['Bone'].rotation_euler.y = pose['joints'][nUtils.getObjectName(obj, 'joint')]
        bpy.ops.object.mode_set(mode=prev_mode)
    except KeyError:
        log('No pose with name ' + posename + ' stored for model ' + modelname, 'ERROR', "loadPose")
Пример #2
0
def storePose(modelname, posename):
    """
    Stores the current pose of all of a robot's selected joints.
    Existing poses of the same name will be overwritten.

    :param modelname: The robot the pose belongs to.
    :type modelname: str.
    :param posename: The name the pose will be stored under.
    :type posename: str.
    :return: Nothing.
    """
    rootlink = None
    for root in sUtils.getRoots():
        if root['modelname'] == modelname:
            rootlink = root
    if rootlink:
        filename = modelname + '::poses'
        posedict = yaml.load(bUtils.readTextFile(filename))
        if not posedict:
            posedict = {posename: {'name': posename, 'joints': {}}}
        else:
            posedict[posename] = {'name': posename, 'joints': {}}
        bpy.ops.object.mode_set(mode='POSE')
        links = sUtils.getChildren(rootlink, ('link',), True, False)
        for link in (link for link in links if 'joint/type' in link
                     and link['joint/type'] not in ['fixed', 'floating']):
            link.pose.bones['Bone'].rotation_mode = 'XYZ'
            posedict[posename]['joints'][nUtils.getObjectName(link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y
        bUtils.updateTextFile(filename, yaml.dump(posedict, default_flow_style=False))
    else:
        log("No model root could be found to store the pose for", "ERROR", "storePose")
Пример #3
0
def storePose(robot_name, pose_name):
    """
    Store the current pose of all of a robot's selected links.
    Existing poses of the same name will be overwritten.

    :param robot_name: The robot the pose belongs to.
    :type robot_name: str.
    :param pose_name: The name the pose will be stored under.
    :type pose_name: str.
    :return: Nothing.
    """
    file_name = 'robot_poses_' + robot_name
    load_file = blenderUtils.readTextFile(file_name)
    if load_file == '':
        poses = {}
    else:
        poses = yaml.load(load_file)
    new_pose = {}
    prev_mode = bpy.context.mode
    bpy.ops.object.mode_set(mode='POSE')
    for root in selectionUtils.getRoots():
        if root['modelname'] == robot_name:
            links = selectionUtils.getChildren(root)
            for link in links:
                if link.select and link.phobostype == 'link':
                    link.pose.bones['Bone'].rotation_mode = 'XYZ'
                    new_pose[namingUtils.getObjectName(link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y
    bpy.ops.object.mode_set(mode=prev_mode)
    poses[pose_name] = new_pose
    blenderUtils.updateTextFile(file_name, yaml.dump(poses))
Пример #4
0
def storePose(modelname, posename):
    """
    Stores the current pose of all of a robot's selected joints.
    Existing poses of the same name will be overwritten.

    :param modelname: The robot the pose belongs to.
    :type modelname: str.
    :param posename: The name the pose will be stored under.
    :type posename: str.
    :return: Nothing.
    """
    rootlink = None
    for root in sUtils.getRoots():
        if root['modelname'] == modelname:
            rootlink = root
    if rootlink:
        filename = modelname + '::poses'
        posedict = yaml.load(bUtils.readTextFile(filename))
        if not posedict:
            posedict = {posename: {'name': posename, 'joints': {}}}
        else:
            posedict[posename] = {'name': posename, 'joints': {}}
        bpy.ops.object.mode_set(mode='POSE')
        links = sUtils.getChildren(rootlink, ('link', ), True, False)
        for link in (link for link in links if 'joint/type' in link
                     and link['joint/type'] not in ['fixed', 'floating']):
            link.pose.bones['Bone'].rotation_mode = 'XYZ'
            posedict[posename]['joints'][nUtils.getObjectName(
                link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y
        bUtils.updateTextFile(filename,
                              yaml.dump(posedict, default_flow_style=False))
    else:
        log("No model root could be found to store the pose for", "ERROR")
Пример #5
0
def storePose(robot_name, pose_name):
    """
    Store the current pose of all of a robot's selected links.
    Existing poses of the same name will be overwritten.

    :param robot_name: The robot the pose belongs to.
    :type robot_name: str.
    :param pose_name: The name the pose will be stored under.
    :type pose_name: str.
    :return: Nothing.
    """
    file_name = 'robot_poses_' + robot_name
    load_file = blenderUtils.readTextFile(file_name)
    if load_file == '':
        poses = {}
    else:
        poses = yaml.load(load_file)
    new_pose = {}
    prev_mode = bpy.context.mode
    bpy.ops.object.mode_set(mode='POSE')
    for root in selectionUtils.getRoots():
        if root['modelname'] == robot_name:
            links = selectionUtils.getChildren(root)
            for link in links:
                if link.select and link.phobostype == 'link':
                    link.pose.bones['Bone'].rotation_mode = 'XYZ'
                    new_pose[namingUtils.getObjectName(
                        link,
                        'joint')] = link.pose.bones['Bone'].rotation_euler.y
    bpy.ops.object.mode_set(mode=prev_mode)
    poses[pose_name] = new_pose
    blenderUtils.updateTextFile(file_name, yaml.dump(poses))
Пример #6
0
def loadPose(modelname, posename):
    """
    Load and apply a robot's stored pose.

    :param modelname: The model's name.
    :type modelname: str.
    :param posename: The name the pose is stored under.
    :type posename: str.
    :return Nothing.
    """
    load_file = bUtils.readTextFile(modelname + '::poses')
    if load_file == '':
        log('No poses stored.', 'ERROR')
        return
    poses = yaml.load(load_file)
    try:
        pose = poses[posename]
        prev_mode = bpy.context.mode
        bpy.ops.object.mode_set(mode='POSE')
        for obj in sUtils.getObjectsByPhobostypes(['link']):
            if nUtils.getObjectName(obj, 'joint') in pose['joints']:
                obj.pose.bones['Bone'].rotation_mode = 'XYZ'
                obj.pose.bones['Bone'].rotation_euler.y = float(
                    pose['joints'][nUtils.getObjectName(obj, 'joint')])
        bpy.ops.object.mode_set(mode=prev_mode)
    except KeyError:
        log('No pose with name ' + posename + ' stored for model ' + modelname,
            'ERROR')
Пример #7
0
def storePose(root, posename):
    """Stores the current pose of all of a model's selected joints.
    
    Existing poses of the same name will be overwritten.

    Args:
      root(bpy_types.Object): root of the model the pose belongs to
      posename(str): name the pose will be stored under

    Returns:
      : Nothing.

    """
    if root:
        filename = nUtils.getModelName(root) + '::poses'
        posedict = json.loads(bUtils.readTextFile(filename))
        if not posedict:
            posedict = {posename: {'name': posename, 'joints': {}}}
        else:
            posedict[posename] = {'name': posename, 'joints': {}}
        links = sUtils.getChildren(root, ('link', ), True, False)
        sUtils.selectObjects([root] + links, clear=True, active=0)
        bpy.ops.object.mode_set(mode='POSE')
        for link in (link for link in links if 'joint/type' in link
                     and link['joint/type'] not in ['fixed', 'floating']):
            link.pose.bones['Bone'].rotation_mode = 'XYZ'
            posedict[posename]['joints'][nUtils.getObjectName(
                link, 'joint')] = link.pose.bones['Bone'].rotation_euler.y
        bpy.ops.object.mode_set(mode='OBJECT')
        posedict = gUtils.roundFloatsInDict(
            posedict,
            ioUtils.getExpSettings().decimalPlaces)
        bUtils.updateTextFile(filename, json.dumps(posedict))
    else:
        log("No model root provided to store the pose for", "ERROR")
Пример #8
0
def loadPose(modelname, posename):
    """Load and apply a robot's stored pose.

    :param modelname: the model's name
    :type modelname: str
    :param posename: the name the pose is stored under
    :type posename: str
    """

    load_file = bUtils.readTextFile(modelname + '::poses')
    if load_file == '':
        log('No poses stored.', 'ERROR')
        return

    loadedposes = yaml.load(load_file)
    if posename not in loadedposes:
        log('No pose with name ' + posename + ' stored for model ' + modelname,
            'ERROR')
        return
    prev_mode = bpy.context.mode
    pose = loadedposes[posename]

    # apply rotations to all joints defined by the pose
    try:
        bpy.ops.object.mode_set(mode='POSE')
        for obj in sUtils.getObjectsByPhobostypes(['link']):
            if nUtils.getObjectName(obj, 'joint') in pose['joints']:
                obj.pose.bones['Bone'].rotation_mode = 'XYZ'
                obj.pose.bones['Bone'].rotation_euler.y = float(
                    pose['joints'][nUtils.getObjectName(obj, 'joint')])
    except KeyError as error:
        log("Could not apply the pose: " + str(error), 'ERROR')
    finally:
        # restore previous mode
        bpy.ops.object.mode_set(mode=prev_mode)
Пример #9
0
def deriveTextData(modelname):
    """
    Collect additional data stored for a specific model.

    :param modelname: Name of the model for which data should be derived.
    :return: A dictionary containing additional data.
    """
    datadict = {}
    datatextfiles = [
        text for text in bpy.data.texts
        if text.name.startswith(modelname + '::')
    ]
    for text in datatextfiles:
        try:
            dataname = text.name.split('::')[-1]
        except IndexError:
            log("Possibly invalidly named model data text file: " + modelname,
                "WARNING")
        try:
            data = yaml.load(bUtils.readTextFile(text.name))
        except yaml.scanner.ScannerError:
            log("Invalid formatting of data file: " + dataname, "ERROR")
        if data:
            datadict[dataname] = data
    return datadict
Пример #10
0
def getPoses(modelname):
    """
    Get the names of the poses that have been stored for a robot.

    :param modelname: The model's name.
    :return: A list containing the poses' names.
    """
    load_file = bUtils.readTextFile(modelname + '::poses')
    if load_file == '':
        return []
    poses = yaml.load(load_file)
    return poses.keys()
Пример #11
0
def get_poses(robot_name):
    """
    Get the names of the poses that have been stored for a robot.

    :param robot_name: The robot's name.
    :return: A list containing the poses' names.
    """
    load_file = blenderUtils.readTextFile('robot_poses_' + robot_name)
    if load_file == '':
        return []
    poses = yaml.load(load_file)
    return poses.keys()
Пример #12
0
def get_poses(robot_name):
    """
    Get the names of the poses that have been stored for a robot.

    :param robot_name: The robot's name.
    :return: A list containing the poses' names.
    """
    load_file = blenderUtils.readTextFile('robot_poses_' + robot_name)
    if load_file == '':
        return []
    poses = yaml.load(load_file)
    return poses.keys()
Пример #13
0
def getPoses(modelname):
    """
    Get the names of the poses that have been stored for a robot.

    :param modelname: The model's name.
    :return: A list containing the poses' names.
    """
    load_file = bUtils.readTextFile(modelname + '::poses')
    if load_file == '':
        return []
    poses = yaml.load(load_file)
    return poses.keys()
Пример #14
0
def deriveStoredPoses():
    """
    """
    poses_file = blenderUtils.readTextFile('robot_poses')
    if poses_file == '':
        return {}
    poses = yaml.load(poses_file)
    pose_dict = {}
    for pose in poses:
        new_pose = {}
        new_pose['name'] = pose
        new_pose['joints'] = poses[pose]
        pose_dict[pose] = new_pose
    return pose_dict
Пример #15
0
def getPoses(modelname):
    """Get the names of the poses that have been stored for a robot.

    Args:
      modelname: The model's name.

    Returns:
      : A list containing the poses' names.

    """
    load_file = bUtils.readTextFile(modelname + '::poses')
    if load_file == '':
        return []
    poses = json.loads(load_file)
    return poses.keys()
Пример #16
0
def storePose(pose_name):
    load_file = blenderUtils.readTextFile('robot_poses')
    if load_file == '':
        poses = {}
    else:
        poses = yaml.load(load_file)
    new_pose = {}
    prev_mode = bpy.context.mode
    bpy.ops.object.mode_set(mode='POSE')
    for obj in selectionUtils.returnObjectList('link'):
        obj.pose.bones['Bone'].rotation_mode = 'XYZ'
        new_pose[namingUtils.getObjectName(obj, 'joint')] = obj.pose.bones['Bone'].rotation_euler.y
    bpy.ops.object.mode_set(mode=prev_mode)
    poses[pose_name] = new_pose
    blenderUtils.updateTextFile('robot_poses', yaml.dump(poses))
Пример #17
0
def loadPose(pose_name):
    load_file = blenderUtils.readTextFile('robot_poses')
    if load_file == '':
        log('No poses stored.', 'ERROR')
        return
    poses = yaml.load(load_file)
    if pose_name in poses:
        prev_mode = bpy.context.mode
        bpy.ops.object.mode_set(mode='POSE')
        for obj in selectionUtils.returnObjectList('link'):
            if namingUtils.getObjectName(obj, 'joint') in poses[pose_name]:
                obj.pose.bones['Bone'].rotation_mode = 'XYZ'
                obj.pose.bones['Bone'].rotation_euler.y = poses[pose_name][namingUtils.getObjectName(obj, 'joint')]
        bpy.ops.object.mode_set(mode=prev_mode)
    else:
        log('No pose with name ' + pose_name + ' stored.', 'ERROR')
Пример #18
0
def deriveTextData(modelname):
    """
    Collect additional data stored for a specific model.

    :param modelname: Name of the model for which data should be derived.
    :return: A dictionary containing additional data.
    """
    datadict = {}
    datatextfiles = [text for text in bpy.data.texts if text.name.startswith(modelname+'::')]
    for text in datatextfiles:
        try:
            dataname = text.name.split('::')[-1]
        except IndexError:
            log("Possibly invalidly named model data text file: " + modelname, "WARNING", "deriveTextData")
        try:
            data = yaml.load(bUtils.readTextFile(text.name))
        except yaml.scanner.ScannerError:
            log("Invalid formatting of data file: " + dataname, "ERROR", "deriveTextData")
        if data:
            datadict[dataname] = data
    return datadict
Пример #19
0
def loadPose(robot_name, pose_name):
    """
    Load and apply a robot's stored pose.

    :param robot_name: The robot's name.
    :type robot_name: str.
    :param pose_name: The name the pose is stored under.
    :type pose_name: str.
    :return Nothing.
    """
    load_file = blenderUtils.readTextFile('robot_poses_' + robot_name)
    if load_file == '':
        log('No poses stored.', 'ERROR')
        return
    poses = yaml.load(load_file)
    if pose_name in poses:
        prev_mode = bpy.context.mode
        bpy.ops.object.mode_set(mode='POSE')
        for obj in selectionUtils.returnObjectList('link'):
            if namingUtils.getObjectName(obj, 'joint') in poses[pose_name]:
                obj.pose.bones['Bone'].rotation_mode = 'XYZ'
                obj.pose.bones['Bone'].rotation_euler.y = poses[pose_name][namingUtils.getObjectName(obj, 'joint')]
        bpy.ops.object.mode_set(mode=prev_mode)
Пример #20
0
def storePose(root, posename):
    """Stores the current pose of all of a model's selected joints.
    
    Existing poses of the same name will be overwritten.

    Args:
      root(bpy_types.Object): root of the model the pose belongs to
      posename(str): name the pose will be stored under

    Returns:
      : Nothing.

    """
    if root:
        filename = nUtils.getModelName(root) + '::poses'
        posedict = yaml.load(bUtils.readTextFile(filename))
        if not posedict:
            posedict = {posename: {'name': posename, 'joints': {}}}
        else:
            posedict[posename] = {'name': posename, 'joints': {}}
        links = sUtils.getChildren(root, ('link',), True, False)
        sUtils.selectObjects([root] + links, clear=True, active=0)
        bpy.ops.object.mode_set(mode='POSE')
        for link in (
            link
            for link in links
            if 'joint/type' in link and link['joint/type'] not in ['fixed', 'floating']
        ):
            link.pose.bones['Bone'].rotation_mode = 'XYZ'
            posedict[posename]['joints'][nUtils.getObjectName(link, 'joint')] = link.pose.bones[
                'Bone'
            ].rotation_euler.y
        bpy.ops.object.mode_set(mode='OBJECT')
        posedict = gUtils.roundFloatsInDict(posedict, ioUtils.getExpSettings().decimalPlaces)
        bUtils.updateTextFile(filename, yaml.dump(posedict, default_flow_style=False))
    else:
        log("No model root provided to store the pose for", "ERROR")
Пример #21
0
def loadPose(modelname, posename):
    """Load and apply a robot's stored pose.

    Args:
      modelname(str): the model's name
      posename(str): the name the pose is stored under

    Returns:

    """

    load_file = bUtils.readTextFile(modelname + '::poses')
    if load_file == '':
        log('No poses stored.', 'ERROR')
        return

    loadedposes = yaml.load(load_file)
    if posename not in loadedposes:
        log('No pose with name ' + posename + ' stored for model ' + modelname, 'ERROR')
        return
    prev_mode = bpy.context.mode
    pose = loadedposes[posename]

    # apply rotations to all joints defined by the pose
    try:
        bpy.ops.object.mode_set(mode='POSE')
        for obj in sUtils.getObjectsByPhobostypes(['link']):
            if nUtils.getObjectName(obj, 'joint') in pose['joints']:
                obj.pose.bones['Bone'].rotation_mode = 'XYZ'
                obj.pose.bones['Bone'].rotation_euler.y = float(
                    pose['joints'][nUtils.getObjectName(obj, 'joint')]
                )
    except KeyError as error:
        log("Could not apply the pose: " + str(error), 'ERROR')
    finally:
        # restore previous mode
        bpy.ops.object.mode_set(mode=prev_mode)
Пример #22
0
def deriveStoredPoses():
    """
    Collect the poses that have been stored for the scene's robots.

    :return: A dictionary containing the poses.
    """
    poses_dict = {}
    for text in bpy.data.texts:
        file_name = text.name
        if file_name.startswith('robot_poses_'):
            robot_name = file_name[len('robot_poses_'):]
            poses_file = blenderUtils.readTextFile(file_name)
            if poses_file == '':
                poses_dict[robot_name] = {}
                break
            poses = yaml.load(poses_file)
            pose_dict = {}
            for pose in poses:
                new_pose = {}
                new_pose['name'] = pose
                new_pose['joints'] = poses[pose]
                pose_dict[pose] = new_pose
            poses_dict[robot_name] = pose_dict
    return poses_dict
Пример #23
0
def loadPose(robot_name, pose_name):
    """
    Load and apply a robot's stored pose.

    :param robot_name: The robot's name.
    :type robot_name: str.
    :param pose_name: The name the pose is stored under.
    :type pose_name: str.
    :return Nothing.
    """
    load_file = blenderUtils.readTextFile('robot_poses_' + robot_name)
    if load_file == '':
        log('No poses stored.', 'ERROR')
        return
    poses = yaml.load(load_file)
    if pose_name in poses:
        prev_mode = bpy.context.mode
        bpy.ops.object.mode_set(mode='POSE')
        for obj in selectionUtils.returnObjectList('link'):
            if namingUtils.getObjectName(obj, 'joint') in poses[pose_name]:
                obj.pose.bones['Bone'].rotation_mode = 'XYZ'
                obj.pose.bones['Bone'].rotation_euler.y = poses[pose_name][
                    namingUtils.getObjectName(obj, 'joint')]
        bpy.ops.object.mode_set(mode=prev_mode)
Пример #24
0
def deriveStoredPoses():
    """
    Collect the poses that have been stored for the scene's robots.

    :return: A dictionary containing the poses.
    """
    poses_dict = {}
    for text in bpy.data.texts:
        file_name = text.name
        if file_name.startswith('robot_poses_'):
            robot_name = file_name[len('robot_poses_'):]
            poses_file = blenderUtils.readTextFile(file_name)
            if poses_file == '':
                poses_dict[robot_name] = {}
                break
            poses = yaml.load(poses_file)
            pose_dict = {}
            for pose in poses:
                new_pose = {}
                new_pose['name'] = pose
                new_pose['joints'] = poses[pose]
                pose_dict[pose] = new_pose
            poses_dict[robot_name] = pose_dict
    return poses_dict
Пример #25
0
def deriveModelDictionary(root, name='', objectlist=[]):
    """Returns a dictionary representation of a Phobos model.
    
    If name is not specified, it overrides the modelname in the root. If the modelname is not
    defined at all, 'unnamed' will be used instead.

    Args:
      root(bpy_types.Object): root object of the model
      name(str, optional): name for the derived model (Default value = '')
      objectlist(list: bpy_types.Object): objects to derive the model from
      objectlist: (Default value = [])

    Returns:

    """
    if root.phobostype not in ['link', 'submodel']:
        log(root.name + " is no valid 'link' or 'submodel' object.", "ERROR")
        return None

    # define model name
    if name:
        modelname = name
    elif 'model/name' in root:
        modelname = root['model/name']
    else:
        modelname = 'unnamed'

    # define model version
    if 'model/version' in root:
        modelversion = root['model/version']
    else:
        modelversion = 'undefined'

    modeldescription = bUtils.readTextFile('README.md')

    model = {
        'links': {},
        'joints': {},
        'sensors': {},
        'motors': {},
        'controllers': {},
        'materials': {},
        'meshes': {},
        'lights': {},
        'groups': {},
        'chains': {},
        'date': datetime.now().strftime("%Y%m%d_%H:%M"),
        'name': modelname,
        'version': modelversion,
        'description': modeldescription,
    }

    log(
        "Creating dictionary for model '" + modelname + "' with root '" + root.name + "'.",
        'INFO',
        prefix="\n",
    )

    # create tuples of objects belonging to model
    if not objectlist:
        objectlist = sUtils.getChildren(
            root, selected_only=ioUtils.getExpSettings().selectedOnly, include_hidden=False
        )
    linklist = [link for link in objectlist if link.phobostype == 'link']

    # digest all the links to derive link and joint information
    log("Parsing links, joints and motors... " + (str(len(linklist))) + " total.", "INFO")
    for link in linklist:
        # parse link information (including inertia)
        model['links'][nUtils.getObjectName(link, 'link')] = deriveLink(
            link, logging=True, objectlist=objectlist
        )

        # parse joint and motor information
        if sUtils.getEffectiveParent(link):
            # joint may be None if link is a root
            # to prevent confusion links are always defining also joints
            jointdict = deriveJoint(link, logging=True, adjust=True)
            log("  Setting joint type '{}' for link.".format(jointdict['type']), 'DEBUG')
            # first check if we have motor information in the joint properties
            # if so they can be extended/overwritten by motor objects later on
            if '$motor' in jointdict:
                motordict = jointdict['$motor']
                # at least we need a type property
                if 'type' in motordict:
                    # if no name is given derive it from the joint
                    if not 'name' in motordict:
                        motordict["name"] = jointdict['name']
                    model['motors'][motordict['name']] = motordict
                    # link the joint by name:
                    motordict['joint'] = jointdict['name']
                del jointdict['$motor']

            model['joints'][jointdict['name']] = jointdict

            for mot in [child for child in link.children if child.phobostype == 'motor']:
                motordict = motormodel.deriveMotor(mot, jointdict)
                # motor may be None if no motor is attached
                if motordict:
                    log("  Added motor {} to link.".format(motordict['name']), 'DEBUG')
                    if motordict['name'] in model["motors"]:
                        model['motors'][motordict['name']].update(motordict)
                    else:
                        model['motors'][motordict['name']] = motordict

    # parse sensors and controllers
    sencons = [obj for obj in objectlist if obj.phobostype in ['sensor', 'controller']]
    log("Parsing sensors and controllers... {} total.".format(len(sencons)), 'INFO')
    for obj in sencons:
        props = deriveDictEntry(obj, names=True, objectlist=objectlist)
        model[obj.phobostype + 's'][nUtils.getObjectName(obj)] = props

    # parse materials
    log("Parsing materials...", 'INFO')
    model['materials'] = collectMaterials(objectlist)
    for obj in objectlist:
        if obj.phobostype == 'visual':
            mat = obj.active_material
            if mat:
                if mat.name not in model['materials']:
                    model['materials'][mat.name] = deriveMaterial(mat)
                    linkname = nUtils.getObjectName(
                        sUtils.getEffectiveParent(obj, ignore_selection=bool(objectlist))
                    )
                    model['links'][linkname]['visual'][nUtils.getObjectName(obj)][
                        'material'
                    ] = mat.name

    # identify unique meshes
    log("Parsing meshes...", "INFO")
    for obj in objectlist:
        try:
            if (
                (obj.phobostype == 'visual' or obj.phobostype == 'collision')
                and (obj['geometry/type'] == 'mesh')
                and (obj.data.name not in model['meshes'])
            ):
                model['meshes'][obj.data.name] = obj
                #todo2.9: for lod in obj.lod_levels:
                #     if lod.object.data.name not in model['meshes']:
                #         model['meshes'][lod.object.data.name] = lod.object
        except KeyError:
            log("Undefined geometry type in object " + obj.name, "ERROR")

    # gather information on groups of objects
    log("Parsing groups...", 'INFO')
    #todo2.9: TODO: get rid of the "data" part and check for relation to robot
    # for group in bpy.data.groups:
    #     # skip empty groups
    #     if not group.objects:
    #         continue

    #     # handle submodel groups separately from other groups
    #     if 'submodeltype' in group.keys():
    #         continue
    #         # TODO create code to derive Submodels
    #         # model['submodels'] = deriveSubmodel(group)
    #     elif nUtils.getObjectName(group, 'group') != "RigidBodyWorld":
    #         model['groups'][nUtils.getObjectName(group, 'group')] = deriveGroupEntry(group)

    # gather information on chains of objects
    log("Parsing chains...", "INFO")
    chains = []
    for obj in objectlist:
        if obj.phobostype == 'link' and 'endChain' in obj:
            chains.extend(deriveChainEntry(obj))
    for chain in chains:
        model['chains'][chain['name']] = chain

    # gather information on lights
    log("Parsing lights...", "INFO")
    for obj in objectlist:
        if obj.phobostype == 'light':
            model['lights'][nUtils.getObjectName(obj)] = deriveLight(obj)

    # gather submechanism information from links
    log("Parsing submechanisms...", "INFO")

    def getSubmechanisms(link):
        """

        Args:
          link: 

        Returns:

        """
        if 'submechanism/name' in link.keys():
            submech = {
                'type': link['submechanism/type'],
                'contextual_name': link['submechanism/name'],
                'name': link['submechanism/subtype']
                if 'submechanism/subtype' in link
                else link['submechanism/type'],
                'jointnames_independent': [
                    nUtils.getObjectName(j, 'joint') for j in link['submechanism/independent']
                ],
                'jointnames_spanningtree': [
                    nUtils.getObjectName(j, 'joint') for j in link['submechanism/spanningtree']
                ],
                'jointnames_active': [
                    nUtils.getObjectName(j, 'joint') for j in link['submechanism/active']
                ],
                # TODO: this should work in almost all cases, still a bit of a hack:
                'file_path': '../submechanisms/urdf/' + link['submechanism/name'] + '.urdf',
            }
            log('    ' + submech['contextual_name'], 'DEBUG')
        else:
            submech = None
        mechanisms = [submech] if submech else []
        for c in link.children:
            if c.phobostype in ['link', 'interface'] and c in objectlist:
                mechanisms.extend(getSubmechanisms(c))
        return mechanisms

    model['submechanisms'] = getSubmechanisms(root)

    # add additional data to model
    model.update(deriveTextData(model['name']))

    # shorten numbers in dictionary to n decimalPlaces and return it
    log("Rounding numbers to {} digits.".format(ioUtils.getExpSettings().decimalPlaces), 'INFO')
    model = roundFloatsInDict(model, ioUtils.getExpSettings().decimalPlaces)
    log("Sorting objects.", 'DEBUG')
    model = sortListsInDict(model)

    return model