// ============================================================================
// $Id: ray.h,v 1.8 2003/08/19 14:06:11 havran Exp $
//
// ray.h
//	  CRay class - ray - core of the ray-tracer design.
//
// Class: CRay
//
// Licence and usage conditions in November 2003:
// This file can be used and modified only with written permission of
// Vlastimil Havran, Pohodli 27, 57001 Litomysl, the Czech Republic.
// This library is granted to be used by Polina Kondratieva within
// her MPI HIWI job working on BRDFLIB library. The usage condition
// may change in the next version of the library. At the moment,
// the library development is supported by Max-Planck-Institut for
// Informatik, Stuhlsatzenhausweg 85, 66123 Saarbruecken Germany
// See WWW: http://www.mpi-sb.mpg.de
// REPLACEMENT_STRING
//
// Initial coding by Nicholas Wilt, originally placed in oort.h,
// Redisigned and rewritten by Vlastimil Havran, 1998.
//
// Given to BRDFLIB project for the Polina Kondratieva's
// HIWI JOB assignment in November 2003.
// ============================================================================

#ifndef __RAY_H__
#define __RAY_H__

// BRDFLIB headers
#include "basic.h"
#include "matrix4.h"
#include "vector3.h"


__BEGIN_BRDFLIB_HEADER

// forward declarations

// type of the ray
enum ERayType {
  PRIMARY_RAY,
  SHADOW_RAY,
  REFLECTED_RAY,
  REFRACTED_RAY, // also called transmitted ray
  LIGHT_RAY,
  DUMMY_RAY
};

// -------------------------------------------------------------------
// CRay class.  A ray is defined by a location and a direction.
// The direction is always normalized (length == 1).
// -------------------------------------------------------------------

class CRay
{
public:
  // constructors
  CRay(const CVector3D &wherefrom, const CVector3D &whichdir,
       const ERayType _type) {
    loc = wherefrom;
    dir = Normalize(whichdir);
    type = _type;
    depth = 0;
    Init();
  }
  // dummy constructor
  CRay() {}

  // Inititalize the ray again when already constructed
  void Init(const CVector3D &wherefrom, const CVector3D &whichdir,
	    const ERayType _type, bool dirNormalized = false) {
    loc = wherefrom;
    dir = (dirNormalized) ? whichdir: Normalize(whichdir) ;
    type = _type;
    depth = 0;
    Init();
  }

  // --------------------------------------------------------
  // Extrapolate ray given a signed distance, returns a point
  // --------------------------------------------------------
  CVector3D Extrap(float t) const {
    return loc + dir * t;
  }

  // -----------------------------------
  // Return parameter given point on ray
  // -----------------------------------
  float Interp(CVector3D &x) const {
    for (int i = 0; i < 3; i++)
      if (Abs(dir[i]) > CLimits::Small)
	return (x[i] - loc[i]) / dir[i];
    return 0;
  }

  // -----------------------------------
  // Reflects direction of reflection for the ray, 
  // given the normal to the surface.
  // -----------------------------------
  CVector3D ReflectRay(const CVector3D &N) const {
    return N * 2.0 * DotProd(N, -dir) + dir;
  }
  void ReflectRay(CVector3D &result, const CVector3D &N) const {
    result =  N * 2.0 * DotProd(N, -dir) + dir;
  }

  // Computes the inverted direction of the ray, used optionally by
  // a ray traversal algorithm.
  void ComputeInvertedDir() const;

  // Given the matrix 4x4, transforms the ray to another space
  void ApplyTransform(const CMatrix4 &tform) {
    loc = tform * loc;
    dir = RotateOnly(tform, dir);
    // note that normalization to the unit size of the direction
    // is NOT computed -- this is what we want.
    Precompute();
  }

  // returns ID of this ray (use for mailboxes)
  int GetID() const { return ID; }

  // returns the transfrom ID of the ray (use for ray transformations)
  int GetTransformID() const { return transfID; }

  // copy the transform ID from an input ray
  void CopyTransformID(const CRay &ray) { transfID = ray.transfID; }
  
  // set unique ID for a given ray - always avoid setting to zero
  void SetID() {
    if ((ID = ++genID) == 0)
      ID = ++genID;
    transfID = ID;
  }
  // set ID to explicit value - it can be even 0 for rays transformed
  // to the canonical object space to supress the mailbox failure.
  void SetID(int newID) {
    ID = newID;
    // note that transfID is not changed!
  }

  void SetLoc(const CVector3D &l);
  CVector3D& GetLoc() { return loc; }
  CVector3D  GetLoc() const { return loc; }

  void SetDir(const CVector3D &ndir) { dir = ndir;} 
  CVector3D& GetDir() { return dir; }
  CVector3D  GetDir() const { return dir; }

  ERayType GetType() const { return type; }
  
  // the depth of the ray - primary rays are in the depth 0
  int GetDepth() const { return depth;}
  void SetDepth(int newDepth) { depth = newDepth;}
  
private:
  CVector3D loc, dir;		// Describes ray origin and vector
  // The inverted direction of the ray components. It is computed optionally
  // by the ray traversal algorithm using function ComputeInvertedDir();
  mutable CVector3D invDir;

  // Type of the ray: primary, shadow, dummy etc., see ERayType above
  ERayType type;

  // unique ID of a ray for the use in the mailboxes
  int ID;
  // unique ID of a ray for the use with a transformations - this one
  // never can be changed that allows the nesting of transformations
  // and caching the transformed rays correctly
  int transfID;
  
  // the ID generator fo each ray instantiated
  static int genID;  

  // When ray shot from the source(camera/light), this number is equal
  // to the number of bounces of the ray, also called the depth of the
  // ray (primary ray has its depth zero)
  int      depth;
  
  void Init();

  // precompute some values that are necessary
  void Precompute();
};


__END_BRDFLIB_HEADER

#endif // __RAY_H__

