public LoadedTextureInfo(int width, int height, int depth, TextureData data) {
   this.width = width;
   this.height = height;
   this.depth = depth;
   this.wrapMode = data.getWrapMode();
   this.filterMode = data.getFilterMode();
   this.textureData = data;
 }
  @Override
  protected void doReload(TextureData data) {
    switch (data.getType()) {
        // TODO: reconsider how 3D textures handled (probably separate asset implementation with
        // common interface?
      case TEXTURE3D:
        if (data.getWidth() % data.getHeight() != 0
            || data.getWidth() / data.getHeight() != data.getHeight()) {
          throw new RuntimeException(
              "3D texture must be cubic (height^3) - width must thus be a multiple of height");
        }
        int size = data.getHeight();

        final int byteLength = 4 * 16 * 16 * 16;
        final int strideX = 16 * 4;
        final int strideY = 16 * 16 * 4;
        final int strideZ = 4;

        ByteBuffer alignedBuffer = ByteBuffer.allocateDirect(byteLength);
        for (int x = 0; x < size; x++) {
          for (int y = 0; y < size; y++) {
            for (int z = 0; z < size; z++) {
              final int index = x * strideX + z * strideZ + strideY * y;

              alignedBuffer.put(data.getBuffers()[0].get(index));
              alignedBuffer.put(data.getBuffers()[0].get(index + 1));
              alignedBuffer.put(data.getBuffers()[0].get(index + 2));
              alignedBuffer.put(data.getBuffers()[0].get(index + 3));
            }
          }
        }
        alignedBuffer.flip();

        resources.loadedTextureInfo = new LoadedTextureInfo(size, size, size, data);

        if (resources.id == 0) {
          resources.graphicsManager.createTexture3D(
              alignedBuffer,
              getWrapMode(),
              getFilterMode(),
              size,
              (newId) -> {
                synchronized (this) {
                  if (resources.id != 0) {
                    resources.graphicsManager.disposeTexture(resources.id);
                  }
                  if (isDisposed()) {
                    resources.graphicsManager.disposeTexture(newId);
                  } else {
                    resources.id = newId;
                    logger.debug("Bound texture '{}' - {}", getUrn(), resources.id);
                  }
                }
              });
        } else {
          resources.graphicsManager.reloadTexture3D(
              resources.id, alignedBuffer, getWrapMode(), getFilterMode(), size);
        }
        break;
      default:
        int width = data.getWidth();
        int height = data.getHeight();
        resources.loadedTextureInfo = new LoadedTextureInfo(width, height, 1, data);
        if (resources.id == 0) {
          resources.graphicsManager.createTexture2D(
              data.getBuffers(),
              getWrapMode(),
              getFilterMode(),
              width,
              height,
              (newId) -> {
                synchronized (this) {
                  if (resources.id != 0) {
                    resources.graphicsManager.disposeTexture(resources.id);
                  }
                  if (isDisposed()) {
                    resources.graphicsManager.disposeTexture(newId);
                  } else {
                    resources.id = newId;
                    logger.debug("Bound texture '{}' - {}", getUrn(), resources.id);
                  }
                }
              });
        } else {
          resources.graphicsManager.reloadTexture2D(
              resources.id, data.getBuffers(), getWrapMode(), getFilterMode(), width, height);
        }
        break;
    }
  }