code: def __init__(self, vtype, utype=None, itype=None):
      # Vertices and type (mandatory)
      self._vertices_list = None
      self._vertices_buffer = None
  
      # Vertex indices and type (optional)
      self._indices_list = None
      self._indices_buffer = None
  
      # Uniforms and type (optional)
      self._uniforms_list = None
      self._uniforms_texture = None
  
      # Make sure types are np.dtype (or None)
      vtype = np.dtype(vtype) if vtype is not None else None
      itype = np.dtype(itype) if itype is not None else None
      utype = np.dtype(utype) if utype is not None else None
  
      # Vertices type (mandatory)
      # -------------------------
      if vtype.names is None:
          raise ValueError("vtype must be a structured dtype")
  
      # Indices type (optional)
      # -----------------------
      if itype is not None:
          if itype not in [np.uint8, np.uint16, np.uint32]:
              raise ValueError("itype must be unsigned integer or None")
          self._indices_list = ArrayList(dtype=itype)
  
      # No program yet
      self._programs = []
  
      # Need to update buffers & texture
      self._need_update = True
  
      # Uniforms type (optional)
      # -------------------------
      if utype is not None:
          if utype.names is None:
              raise ValueError("utype must be a structured dtype")
  
          # Convert types to lists (in case they were already dtypes) such
          # that we can append new fields
          vtype = eval(str(np.dtype(vtype)))
          # We add a uniform index to access uniform data
          vtype.append(("collection_index", np.float32))
          vtype = np.dtype(vtype)
  
          # Check utype is made of float32 only
          utype = eval(str(np.dtype(utype)))
          r_utype = dtype_reduce(utype)
          if not isinstance(r_utype[0], str) or r_utype[2] != "float32":
              raise RuntimeError(
                  "utype cannot be reduced to float32 only"
              )
  
          # Make utype divisible by 4
          # count = ((r_utype[1]-1)//4+1)*4
  
          # Make utype a power of two
          count = next_power_of_2(r_utype[1])
          if (count - r_utype[1]) > 0:
              utype.append(
                  ("__unused__", np.float32, count - r_utype[1])
              )
  
          self._uniforms_list = ArrayList(dtype=utype)
          self._uniforms_float_count = count
  
          # Reserve some space in texture such that we have
          # at least one full line
          shape = self._compute_texture_shape(1)
          self._uniforms_list.reserve(shape[1] / (count / 4))
  
      # Last since utype may add a new field in vtype (collecion_index)
      self._vertices_list = ArrayList(dtype=vtype)
  
      # Record all types
      self._vtype = np.dtype(vtype)
      self._itype = np.dtype(itype) if itype is not None else None
      self._utype = np.dtype(utype) if utype is not None else None
  
imports: from __future__ import division
  import math
  import numpy
  from ...gloo import Texture2D, VertexBuffer, IndexBuffer
  from .util import dtype_reduce
  from .array_list import ArrayList
label_desc: Use image node_id 1 to create a new BaseCollection object from inherited base classes: object with example: obj = BaseCollection(vtype, utype, itype)