docs: 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)