/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkSmartVolumeMapper.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/**
 * @class   vtkSmartVolumeMapper
 * @brief   Adaptive volume mapper
 *
 * vtkSmartVolumeMapper is a volume mapper that will delegate to a specific
 * volume mapper based on rendering parameters and available hardware. Use the
 * SetRequestedRenderMode() method to control the behavior of the selection.
 * The following options are available:
 *
 * @par vtkSmartVolumeMapper::DefaultRenderMode:
 *          Allow the vtkSmartVolumeMapper to select the best mapper based on
 *          rendering parameters and hardware support. If GPU ray casting is
 *          supported, this mapper will be used for all rendering. If not,
 *          then if 3D texture mapping is supported, it will be used for
 *          interactive rendering and the vtkFixedPointRayCastMapper will be
 *          used for still rendering. If 3D texture mapping is not supported,
 *          then the vtkFixedPointRayCastMapper will be used exclusively.
 *          This is the default requested render mode, and is generally the
 *          best option. When you use this option, your volume will always
 *          be rendered, but the method used to render it may vary based
 *          on parameters and platform.
 *
 * @par vtkSmartVolumeMapper::RayCastAndTextureRenderMode:
 *          Use the vtkVolumeTextureMapper3D for interactive rendering,
 *          and the vtkFixedPointVolumeRayCastMapper for still renders.
 *          If 3D texture mapping is not supported, then the ray
 *          caster will be used exclusively. When you use this option your
 *          volume will always be rendered, but the method used for
 *          interactive rendering will vary based on parameters and
 *          platform. The decision on whether a particular render is
 *          interactive or still is based on the adjustable parameter
 *          InteractiveUpdateRate. If the DesiredUpdateRate found in the
 *          vtkRenderWindow that initiated the Render is at or above
 *          the InteractiveUpdateRate value, then the render is considered
 *          interactive, otherwise it is considered a still render.
 *
 * @par vtkSmartVolumeMapper::RayCastRenderMode:
 *          Use the vtkFixedPointVolumeRayCastMapper for both interactive and
 *          still rendering. When you use this option your volume will always
 *          be rendered with the vtkFixedPointVolumeRayCastMapper.
 *
 * @par vtkSmartVolumeMapper::TextureRenderMode:
 *          Use the vtkVolumeTextureMapper3D, if supported, for both
 *          interactive and still rendering. If 3D texture mapping is not
 *          supported (either by the hardware, or due to the rendering
 *          parameters) then no image will be rendered. Use this option only
 *          if you have already checked for support based on the current
 *          hardware, number of scalar components, and rendering parameters
 *          in the vtkVolumeProperty. Also note that the
 *          vtkVolumeTextureMapper3D does not support window / level
 *          operations on the final image, so FinalColorWindow must be at
 *          the default value of 1.0 and FinalColorLevel must be at the
 *          default value of 0.5.
 *
 * @par vtkSmartVolumeMapper::GPURenderMode:
 *          Use the vtkGPUVolumeRayCastMapper, if supported, for both
 *          interactive and still rendering. If the GPU ray caster is not
 *          supported (due to hardware limitations or rendering parameters)
 *          then no image will be rendered. Use this option only if you have
 *          already checked for supported based on the current hardware,
 *          number of scalar components, and rendering parameters in the
 *          vtkVolumeProperty.
 *
 * @par vtkSmartVolumeMapper::GPURenderMode:
 *  You can adjust the contrast and brightness in the rendered image using the
 *  FinalColorWindow and FinalColorLevel ivars. By default the
 *  FinalColorWindow is set to 1.0, and the FinalColorLevel is set to 0.5,
 *  which applies no correction to the computed image. To apply the window /
 *  level operation to the computer image color, first a Scale and Bias
 *  value are computed:
 *  <pre>
 *  scale = 1.0 / this->FinalColorWindow
 *  bias  = 0.5 - this->FinalColorLevel / this->FinalColorWindow
 *  </pre>
 *  To compute a new color (R', G', B', A') from an existing color (R,G,B,A)
 *  for a pixel, the following equation is used:
 *  <pre>
 *  R' = R*scale + bias*A
 *  G' = G*scale + bias*A
 *  B' = B*scale + bias*A
 *  A' = A
 *  </pre>
 * Note that bias is multiplied by the alpha component before adding because
 * the red, green, and blue component of the color are already pre-multiplied
 * by alpha. Also note that the window / level operation leaves the alpha
 * component unchanged - it only adjusts the RGB values.
*/

#ifndef vtkSmartVolumeMapper_h
#define vtkSmartVolumeMapper_h

#include "vtkRenderingVolumeOpenGLModule.h" // For export macro
#include "vtkVolumeMapper.h"
#include "vtkImageReslice.h" // for VTK_RESLICE_NEAREST, VTK_RESLICE_CUBIC

class vtkFixedPointVolumeRayCastMapper;
class vtkGPUVolumeRayCastMapper;
class vtkImageResample;
class vtkOSPRayVolumeInterface;
class vtkRenderWindow;
class vtkVolume;
class vtkVolumeProperty;
#if !defined(VTK_LEGACY_REMOVE)
class vtkVolumeTextureMapper3D;
#endif // VTK_LEGACY_REMOVE

class VTKRENDERINGVOLUMEOPENGL_EXPORT vtkSmartVolumeMapper : public vtkVolumeMapper
{
public:
  static vtkSmartVolumeMapper *New();
  vtkTypeMacro(vtkSmartVolumeMapper,vtkVolumeMapper);
  void PrintSelf( ostream& os, vtkIndent indent ) VTK_OVERRIDE;

  //@{
  /**
   * Set the final color window. This controls the contrast of
   * the image. The default value is 1.0. The Window can be
   * negative (this causes a "negative" effect on the image)
   * Although Window can be set to 0.0, any value less than
   * 0.00001 and greater than or equal to 0.0 will be set to
   * 0.00001, and any value greater than -0.00001 but less
   * than or equal to 0.0 will be set to -0.00001.
   * Initial value is 1.0.
   */
  vtkSetMacro( FinalColorWindow, float );
  //@}

  //@{
  /**
   * Get the final color window. Initial value is 1.0.
   */
  vtkGetMacro( FinalColorWindow, float );
  //@}

  //@{
  /**
   * Set the final color level. The level controls the
   * brightness of the image. The final color window will
   * be centered at the final color level, and together
   * represent a linear remapping of color values. The
   * default value for the level is 0.5.
   */
  vtkSetMacro( FinalColorLevel,  float );
  //@}

  //@{
  /**
   * Get the final color level.
   */
  vtkGetMacro( FinalColorLevel,  float );
  //@}

// The possible values for the default and current render mode ivars
  enum
  {
    DefaultRenderMode=0,
    RayCastAndTextureRenderMode,
    RayCastRenderMode,
    TextureRenderMode,
    GPURenderMode,
    OSPRayRenderMode,
    UndefinedRenderMode,
    InvalidRenderMode
  };

  /**
   * Set the requested render mode. The default is
   * vtkSmartVolumeMapper::DefaultRenderMode.
   */
  void SetRequestedRenderMode(int mode);

  /**
   * Set the requested render mode to vtkSmartVolumeMapper::DefaultRenderMode.
   * This is the best option for an application that must adapt to different
   * data types, hardware, and rendering parameters.
   */
  void SetRequestedRenderModeToDefault();

  /**
   * Set the requested render mode to vtkSmartVolumeMapper::GPURenderMode.
   * This option will use hardware accelerated rendering exclusively. This is a
   * good option if you know there is hardware acceleration.
   */
  void SetRequestedRenderModeToGPU();

  /**
   * Set the requested render mode to
   * vtkSmartVolumeMapper::TextureRenderMode.
   */
#if !defined(VTK_LEGACY_REMOVE)
  void SetRequestedRenderModeToTexture();
#endif // VTK_LEGACY_REMOVE

  /**
   * Set the requested render mode to
   * vtkSmartVolumeMapper::RayCastAndTextureRenderMode.
   * This is a good option if you want to avoid using advanced OpenGL
   * functionality, but would still like to used 3D texture mapping, if
   * available, for interactive rendering.
   */
#if !defined(VTK_LEGACY_REMOVE)
  void SetRequestedRenderModeToRayCastAndTexture();
#endif // VTK_LEGACY_REMOVE

  /**
   * Set the requested render mode to vtkSmartVolumeMapper::RayCastRenderMode.
   * This option will use software rendering exclusively. This is a good option
   * if you know there is no hardware acceleration.
   */
  void SetRequestedRenderModeToRayCast();

  /**
   * Set the requested render mode to vtkSmartVolumeMapper::OSPRayRenderMode.
   * This option will use intel OSPRay to do software rendering exclusively.
   */
  void SetRequestedRenderModeToOSPRay();

  //@{
  /**
   * Get the requested render mode.
   */
  vtkGetMacro( RequestedRenderMode, int );
  //@}

  //@{
  /**
   * Set the rate at or above this render will be considered interactive.
   * If the DesiredUpdateRate of the vtkRenderWindow that caused the Render
   * falls at or above this rate, the render is considered interactive and
   * the mapper may be adjusted (depending on the render mode).
   * Initial value is 1.0.
   */
  vtkSetClampMacro( InteractiveUpdateRate, double, 1.0e-10, 1.0e10 );
  //@}

  //@{
  /**
   * Get the update rate at or above which this is considered an
   * interactive render.
   * Initial value is 1.0.
   */
  vtkGetMacro( InteractiveUpdateRate, double );
  //@}

  /**
   * This will return the render mode used during the previous call to
   * Render().
   */
  int GetLastUsedRenderMode();

  //@{
  /**
   * Value passed to the GPU mapper. Ignored by other mappers.
   * Maximum size of the 3D texture in GPU memory.
   * Will default to the size computed from the graphics
   * card. Can be adjusted by the user.
   * Useful if the automatic detection is defective or missing.
   */
  vtkSetMacro( MaxMemoryInBytes, vtkIdType );
  vtkGetMacro( MaxMemoryInBytes, vtkIdType );
  //@}

  //@{
  /**
   * Value passed to the GPU mapper. Ignored by other mappers.
   * Maximum fraction of the MaxMemoryInBytes that should
   * be used to hold the texture. Valid values are 0.1 to
   * 1.0.
   */
  vtkSetClampMacro( MaxMemoryFraction, float, 0.1f, 1.0f );
  vtkGetMacro( MaxMemoryFraction, float );
  //@}

  //@{
  /**
   * Set interpolation mode for downsampling (lowres GPU)
   * (initial value: cubic).
   */
  vtkSetClampMacro(InterpolationMode, int,
                   VTK_RESLICE_NEAREST, VTK_RESLICE_CUBIC);
  vtkGetMacro(InterpolationMode, int);
  void SetInterpolationModeToNearestNeighbor();
  void SetInterpolationModeToLinear();
  void SetInterpolationModeToCubic();
  //@}

  /**
   * This method can be used to render a representative view of the input data
   * into the supplied image given the supplied blending mode, view direction,
   * and view up vector.
   */
  void CreateCanonicalView( vtkRenderer *ren,
                            vtkVolume *volume,
                            vtkVolume *volume2,
                            vtkImageData *image,
                            int blend_mode,
                            double viewDirection[3],
                            double viewUp[3] );

  /**
   * WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE
   * Initialize rendering for this volume.
   */
  void Render( vtkRenderer *, vtkVolume * ) VTK_OVERRIDE;

  /**
   * WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE
   * Release any graphics resources that are being consumed by this mapper.
   * The parameter window could be used to determine which graphic
   * resources to release.
   */
  void ReleaseGraphicsResources(vtkWindow *) VTK_OVERRIDE;

protected:
  vtkSmartVolumeMapper();
  ~vtkSmartVolumeMapper() VTK_OVERRIDE;

  /**
   * Connect input of the vtkSmartVolumeMapper to the input of the
   * internal volume mapper by doing a shallow to avoid memory leaks.
   * \pre m_exists: m!=0
   */
  void ConnectMapperInput(vtkVolumeMapper *m);

  /**
   * Connect input of the vtkSmartVolumeMapper to the input of the
   * internal resample filter by doing a shallow to avoid memory leaks.
   * \pre m_exists: f!=0
   */
  void ConnectFilterInput(vtkImageResample *f);

  // Window / level ivars
  float   FinalColorWindow;
  float   FinalColorLevel;

  // GPU mapper-specific memory ivars.
  vtkIdType MaxMemoryInBytes;
  float MaxMemoryFraction;

  // Used for downsampling.
  int InterpolationMode;

  // The requested render mode is used to compute the current render mode. Note
  // that the current render mode can be invalid if the requested mode is not
  // supported.
  int     RequestedRenderMode;
  int     CurrentRenderMode;

  // Initialization variables.
  int          Initialized;
  vtkTimeStamp SupportStatusCheckTime;
  int          TextureSupported;
  int          GPUSupported;
  int          RayCastSupported;
  int          LowResGPUNecessary;

  // This is the resample filter that may be used if we need to
  // create a low resolution version of the volume for GPU rendering
  vtkImageResample *GPUResampleFilter;

  // If the DesiredUpdateRate of the vtkRenderWindow causing the Render is at
  // or above this value, the render is considered interactive. Otherwise it is
  // considered still.
  double InteractiveUpdateRate;

  // The initialize method. Called from ComputeRenderMode whenever something
  // relevant has changed.
  void  Initialize(vtkRenderer *ren,
                   vtkVolume *vol);

  // The method that computes the render mode from the requested render mode
  // based on the support status for each render method.
  void  ComputeRenderMode(vtkRenderer *ren,
                          vtkVolume *vol);

  // The three potential mappers
  vtkGPUVolumeRayCastMapper      *GPULowResMapper;
  vtkGPUVolumeRayCastMapper      *GPUMapper;
  vtkFixedPointVolumeRayCastMapper  *RayCastMapper;
#if !defined(VTK_LEGACY_REMOVE)
  vtkVolumeTextureMapper3D          *TextureMapper;
#endif // VTK_LEGACY_REMOVE

  // We need to keep track of the blend mode we had when we initialized
  // because we need to reinitialize (and recheck hardware support) if
  // it changes
  int  InitializedBlendMode;

private:
  vtkSmartVolumeMapper(const vtkSmartVolumeMapper&) VTK_DELETE_FUNCTION;
  void operator=(const vtkSmartVolumeMapper&) VTK_DELETE_FUNCTION;

  vtkOSPRayVolumeInterface *OSPRayMapper;
};

#endif
