def _dump(f, obj, flip_faces=False, ungroup=False, comments=None, split_normals=False, write_mtl=True): # pylint: disable=redefined-outer-name
    '''
    write_mtl: When True and mesh has a texture, includes a mtllib
      reference in the .obj and writes a .mtl alongside.

    '''
    import os
    import numpy as np
    from baiji import s3

    ff = -1 if flip_faces else 1
    def write_face_to_obj_file(obj, faces, face_index, obj_file):
        vertex_indices = faces[face_index][::ff] + 1

        write_normals = obj.fn is not None or (obj.vn is not None and obj.vn.shape == obj.v.shape)
        write_texture = obj.ft is not None and obj.vt is not None

        if write_normals and obj.fn is not None:
            normal_indices = obj.fn[face_index][::ff] + 1
            assert len(normal_indices) == len(vertex_indices)
        elif write_normals: # unspecified fn but per-vertex normals, assume ordering is same as for v
            normal_indices = faces[face_index][::ff] + 1

        if write_texture:
            texture_indices = obj.ft[face_index][::ff] + 1
            assert len(texture_indices) == len(vertex_indices)

        # Valid obj face lines are: v, v/vt, v//vn, v/vt/vn
        if write_normals and write_texture:
            pattern = '%d/%d/%d'
            value = tuple(np.array([vertex_indices, texture_indices, normal_indices]).T.flatten())
        elif write_normals:
            pattern = '%d//%d'
            value = tuple(np.array([vertex_indices, normal_indices]).T.flatten())
        elif write_texture:
            pattern = '%d/%d'
            value = tuple(np.array([vertex_indices, texture_indices]).T.flatten())
        else:
            pattern = '%d'
            value = tuple(vertex_indices)
        obj_file.write(('f ' + ' '.join([pattern]*len(vertex_indices)) + '\n') % value)

    if comments != None:
        if isinstance(comments, basestring):
            comments = [comments]
        for comment in comments:
            for line in comment.split("\n"):
                f.write("# %s\n" % line)

    if write_mtl and hasattr(obj, 'texture_filepath') and obj.texture_filepath is not None:
        save_to = s3.path.dirname(f.name)
        mtl_name = os.path.splitext(s3.path.basename(f.name))[0]
        mtl_filename = mtl_name + '.mtl'
        f.write('mtllib %s\n' % mtl_filename)
        f.write('usemtl %s\n' % mtl_name)
        texture_filename = mtl_name + os.path.splitext(obj.texture_filepath)[1]
        if not s3.exists(s3.path.join(save_to, texture_filename)):
            s3.cp(obj.texture_filepath, s3.path.join(save_to, texture_filename))
        obj.write_mtl(s3.path.join(save_to, mtl_filename), mtl_name, texture_filename)

    if obj.vc is not None:
        for r, c in zip(obj.v, obj.vc):
            f.write('v %f %f %f %f %f %f\n' % (r[0], r[1], r[2], c[0], c[1], c[2]))
    elif obj.v is not None:
        for r in obj.v:
            f.write('v %f %f %f\n' % (r[0], r[1], r[2]))

    if obj.vn is not None:
        if split_normals:
            for vn_idx in obj.fn:
                r = obj.vn[vn_idx[0]]
                f.write('vn %f %f %f\n' % (r[0], r[1], r[2]))
                r = obj.vn[vn_idx[1]]
                f.write('vn %f %f %f\n' % (r[0], r[1], r[2]))
                r = obj.vn[vn_idx[2]]
                f.write('vn %f %f %f\n' % (r[0], r[1], r[2]))
        else:
            for r in obj.vn:
                f.write('vn %f %f %f\n' % (r[0], r[1], r[2]))

    if obj.ft is not None and obj.vt is not None:
        for r in obj.vt:
            if len(r) == 3:
                f.write('vt %f %f %f\n' % (r[0], r[1], r[2]))
            else:
                f.write('vt %f %f\n' % (r[0], r[1]))
    if obj.f4 is not None:
        faces = obj.f4
    elif obj.f is not None:
        faces = obj.f
    else:
        faces = None
    if obj.segm is not None and not ungroup:
        if faces is not None:
            # An array of strings.
            group_names = np.array(obj.segm.keys())

            # A 2d array of booleans indicating which face is in which group.
            group_mask = np.zeros((len(group_names), len(faces)), dtype=bool)
            for i, segm_faces in enumerate(obj.segm.itervalues()):
                group_mask[i][segm_faces] = True

            # In an OBJ file, "g" changes the current state. This is a slice of
            # group_mask that represents the current state.
            current_group_mask = np.zeros((len(group_names),), dtype=bool)

            for face_index in range(len(faces)):
                # If the group has changed from the previous face, write the
                # group entry.
                this_group_mask = group_mask[:, face_index]
                if any(current_group_mask != this_group_mask):
                    current_group_mask = this_group_mask
                    f.write('g %s\n' % ' '.join(group_names[current_group_mask]))

                write_face_to_obj_file(obj, faces, face_index, f)
    else:
        if faces is not None:
            for face_index in range(len(faces)):
                write_face_to_obj_file(obj, faces, face_index, f)
write_mtl: When True and mesh has a texture, includes a mtllib
      reference in the .obj and writes a .mtl alongside.