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

Program:   Visualization Toolkit
Module:    vtkOpenGLHAVSVolumeMapper.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.

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

/* Copyright 2005, 2006 by University of Utah. */

/**
 * @class   vtkOpenGLHAVSVolumeMapper
 * @brief   Hardware-Assisted
 * Visibility Sorting unstructured grid mapper, OpenGL implementation
 *
 *
 *
 * vtkHAVSVolumeMapper is a class that renders polygonal data
 * (represented as an unstructured grid) using the Hardware-Assisted
 * Visibility Sorting (HAVS) algorithm.  First the unique triangles are sorted
 * in object space, then they are sorted in image space using a fixed size
 * A-buffer implemented on the GPU called the k-buffer.  The HAVS algorithm
 * excels at rendering large datasets quickly.  The trade-off is that the
 * algorithm may produce some rendering artifacts due to an insufficient k
 * size (currently 2 or 6 is supported) or read/write race conditions.
 *
 * A built in level-of-detail (LOD) approach samples the geometry using one of
 * two heuristics (field or area).  If LOD is enabled, the amount of geometry
 * that is sampled and rendered changes dynamically to stay within the target
 * frame rate.  The field sampling method generally works best for datasets
 * with cell sizes that don't vary much in size.  On the contrary, the area
 * sampling approach gives better approximations when the volume has a lot of
 * variation in cell size.
 *
 * The HAVS algorithm uses several advanced features on graphics hardware.
 * The k-buffer sorting network is implemented using framebuffer objects
 * (FBOs) with multiple render targets (MRTs).  Therefore, only cards that
 * support these features can run the algorithm (at least an ATI 9500 or an
 * NVidia NV40 (6600)).
 *
 *
 * @attention
 * Several issues had to be addressed to get the HAVS algorithm working within
 * the vtk framework.  These additions forced the code to forsake speed for
 * the sake of compliance and robustness.
 *
 * @attention
 * The HAVS algorithm operates on the triangles that compose the mesh.
 * Therefore, before rendering, the cells are decomposed into unique triangles
 * and stored on the GPU for efficient rendering.  The use of GPU data
 * structures is only recommended if the entire geometry can fit in graphics
 * memory.  Otherwise this feature should be disabled.
 *
 * @attention
 * Another new feature is the handling of mixed data types (eg., polygonal
 * data with volume data).  This is handled by reading the z-buffer from the
 * current window and copying it into the framebuffer object for off-screen
 * rendering.  The depth test is then enabled so that the volume only appears
 * over the opaque geometry.  Finally, the results of the off-screen rendering
 * are blended into the framebuffer as a transparent, view-aligned texture.
 *
 * @attention
 * Instead of using a preintegrated 3D lookup table for storing the ray
 * integral, this implementation uses partial pre-integration.  This improves
 * the performance of dynamic transfer function updates by avoiding a costly
 * preprocess of the table.
 *
 * @attention
 * A final change to the original algorithm is the handling of non-convexities
 * in the mesh.  Due to read/write hazards that may create undesired artifacts
 * with non-convexities when using a inside/outside toggle in the fragment
 * program, another approach was employed.  To handle non-convexities, the
 * fragment shader determines if a ray-gap is larger than the max cell size
 * and kill the fragment if so.  This approximation performs rather well in
 * practice but may miss small non-convexities.
 *
 * @attention
 * For more information on the HAVS algorithm see:
 *
 * @attention
 *  "Hardware-Assisted Visibility Sorting for Unstructured Volume
 * Rendering" by S. P. Callahan, M. Ikits, J. L. D. Comba, and C. T. Silva,
 * IEEE Transactions of Visualization and Computer Graphics; May/June 2005.
 *
 * @attention
 * For more information on the Level-of-Detail algorithm, see:
 *
 * @attention
 * "Interactive Rendering of Large Unstructured Grids Using Dynamic
 * Level-of-Detail" by S. P. Callahan, J. L. D. Comba, P. Shirley, and
 * C. T. Silva, Proceedings of IEEE Visualization '05, Oct. 2005.
 *
 *
 * @par Acknowledgments:
 * This code was developed by Steven P. Callahan under the supervision
 * of Prof. Claudio T. Silva. The code also contains contributions
 * from Milan Ikits, Linh Ha, Huy T. Vo, Carlos E. Scheidegger, and
 * Joao L. D. Comba.
 *
 * @par Acknowledgments:
 * The work was supported by grants, contracts, and gifts from the
 * National Science Foundation, the Department of Energy, the Army
 * Research Office, and IBM.
 *
 * @par Acknowledgments:
 * The port of HAVS to VTK and ParaView has been primarily supported
 * by Sandia National Labs.
 *
*/

#ifndef vtkOpenGLHAVSVolumeMapper_h
#define vtkOpenGLHAVSVolumeMapper_h

#include "vtkRenderingVolumeOpenGLModule.h" // For export macro
#include "vtkHAVSVolumeMapper.h"

#include "vtkWeakPointer.h" // to cache the vtkRenderWindow
class vtkRenderer;
class vtkRenderWindow;


class VTKRENDERINGVOLUMEOPENGL_EXPORT vtkOpenGLHAVSVolumeMapper
  : public vtkHAVSVolumeMapper
{
public:
  static vtkOpenGLHAVSVolumeMapper *New();
  vtkTypeMacro(vtkOpenGLHAVSVolumeMapper,
                       vtkHAVSVolumeMapper);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Render the volume
   */
  void Render(vtkRenderer *ren, vtkVolume *vol) override;

  /**
   * Release any graphics resources that are being consumed by this volume
   * renderer.
   */
  void ReleaseGraphicsResources(vtkWindow *) override;

  /**
   * Set/get whether or not the data structures should be stored on the GPU
   * for better peformance.
   */
  void SetGPUDataStructures(bool) override;

  /**
   * Check hardware support for the HAVS algorithm.  Necessary
   * features include off-screen rendering, 32-bit fp textures, multiple
   * render targets, and framebuffer objects.
   * Subclasses must override this method to indicate if supported by Hardware.
   */
  bool SupportedByHardware(vtkRenderer *r) override;
protected:

  vtkOpenGLHAVSVolumeMapper();
  ~vtkOpenGLHAVSVolumeMapper() override;
  int FillInputPortInformation(int port, vtkInformation* info) override;

  void Initialize(vtkRenderer *ren, vtkVolume *vol) override;
  virtual void InitializeLookupTables(vtkVolume *vol);
  void InitializeGPUDataStructures();
  void InitializeShaders();
  void DeleteShaders();
  void InitializeFramebufferObject();

  void RenderHAVS(vtkRenderer *ren);
  void SetupFBOZBuffer(int screenWidth, int screenHeight, float depthNear, float depthFar,
                       float *zbuffer);
  void SetupFBOMRT();
  void DrawFBOInit(int screenWidth, int screenHeight, float depthNear, float depthFar);
  void DrawFBOGeometry();
  void DrawFBOFlush(int screenWidth, int screenHeight, float depthNear, float depthFar);
  void DrawBlend(int screenWidth, int screenHeight, float depthNear, float depthFar);

  void CheckOpenGLError(const char *str);

  // GPU
  unsigned int VBOVertexName;
  unsigned int VBOTexCoordName;
  unsigned int VBOVertexIndexName;
  unsigned int VertexProgram;
  unsigned int FragmentProgramBegin;
  unsigned int FragmentProgram;
  unsigned int FragmentProgramEnd;
  unsigned int FramebufferObject;
  int FramebufferObjectSize;
  unsigned int FramebufferTextures[4];
  unsigned int DepthTexture;

  // Lookup Tables
  unsigned int PsiTableTexture;
  unsigned int TransferFunctionTexture;

  vtkWeakPointer<vtkRenderWindow> RenderWindow;

private:
  vtkOpenGLHAVSVolumeMapper(const vtkOpenGLHAVSVolumeMapper&) VTK_DELETE_FUNCTION;
  void operator=(const vtkOpenGLHAVSVolumeMapper&) VTK_DELETE_FUNCTION;
};

#endif
