/****************************************************************************
 *                  mblur.c
 *
 *  Written By Nathan Kopp - December, 1999
 *
 *  This module implements routines for motion blur.
 *
 * from Persistence of Vision(tm) Ray Tracer version 3.6.
 * Copyright 1991-2003 Persistence of Vision Team
 * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
 *---------------------------------------------------------------------------
 * NOTICE: This source code file is provided so that users may experiment
 * with enhancements to POV-Ray and to port the software to platforms other
 * than those supported by the POV-Ray developers. There are strict rules
 * regarding how you are permitted to use this file. These rules are contained
 * in the distribution and derivative versions licenses which should have been
 * provided with this file.
 *
 * These licences may be found online, linked from the end-user license
 * agreement that is located at http://www.povray.org/povlegal.html
 *---------------------------------------------------------------------------
 * This program is based on the popular DKB raytracer version 2.12.
 * DKBTrace was originally written by David K. Buck.
 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 *---------------------------------------------------------------------------
 *
 *===========================================================================
 * This file is part of MegaPOV, a modified and unofficial version of POV-Ray
 * For more information on MegaPOV visit our website:
 * http://megapov.inetart.net/
 *===========================================================================
 *
 * $RCSfile: mblur.cpp,v $
 * $Revision: 1.7 $
 * $Author: chris $
 *
 *****************************************************************************/
#include <algorithm>

#include "frame.h"
#include "povray.h"
#include "vector.h"
#include "bbox.h"
#include "hfield.h"
#include "matrices.h"
#include "objects.h"
#include "planes.h"
#include "quadrics.h"

#ifdef MOTION_BLUR_PATCH
#include "mblur.h"

BEGIN_POV_NAMESPACE

extern int TimeStamp;


/*****************************************************************************
* Local preprocessor defines
******************************************************************************/




/*****************************************************************************
* Static functions
******************************************************************************/

static int All_Motion_Blur_Intersections (OBJECT *Object, RAY *Ray, ISTACK *Depth_Stack);
static int Inside_Motion_Blur (VECTOR point, OBJECT *Object);
static MOTION_BLUR *Copy_Motion_Blur (OBJECT *Object);
static void Translate_Motion_Blur (OBJECT *Object, VECTOR Vector, TRANSFORM *Trans);
static void Rotate_Motion_Blur (OBJECT *Object, VECTOR Vector, TRANSFORM *Trans);
static void Scale_Motion_Blur (OBJECT *Object, VECTOR Vector, TRANSFORM *Trans);
static void Transform_Motion_Blur (OBJECT *Object, TRANSFORM *Trans);
static void Destroy_Motion_Blur (OBJECT *Object);
static void Invert_Motion_Blur (OBJECT *Object);


/*****************************************************************************
* Local variables
******************************************************************************/

METHODS Motion_Blur_Methods =
{
  All_Motion_Blur_Intersections,
  Inside_Motion_Blur, NULL /*Normal*/, Default_UVCoord /* UVCoord */,
  (COPY_METHOD)Copy_Motion_Blur,
  Translate_Motion_Blur, Rotate_Motion_Blur,
  Scale_Motion_Blur, Transform_Motion_Blur, Invert_Motion_Blur, Destroy_Motion_Blur
};


/*****************************************************************************
*
* FUNCTION
*
*   All_Motion_Blur_Intersections
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*   
* AUTHOR
*
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   Sep 1994 : Added code to count intersection tests. [DB]
*
******************************************************************************/

static int All_Motion_Blur_Intersections (OBJECT *Object, RAY *Ray, ISTACK *Depth_Stack)
{
  int Found;
  OBJECT *Current_Sib, *Clip;
  ISTACK *Local_Stack;
  INTERSECTION *Sibling_Intersection;

  Increase_Counter(stats[Ray_Motion_Blur_Tests]);

  Found = false;

  /* Use shortcut if no clip. */

  if ((Clip = Object->Clip) == NULL)
  {
    for (Current_Sib = ((MOTION_BLUR *)Object)->Children; Current_Sib != NULL; Current_Sib = Current_Sib->Sibling)
    {
      if (!TimeStamp || 
          Current_Sib->TimeStamp==TimeStamp)
      if (Ray_In_Bound (Ray, Current_Sib->Bound))
      {
        if (All_Intersections (Current_Sib, Ray, Depth_Stack))
        {
          Found = true;
        }
      }
    }
  }
  else
  {
    Local_Stack = open_istack();

    for (Current_Sib = ((MOTION_BLUR *)Object)->Children; Current_Sib != NULL; Current_Sib = Current_Sib->Sibling)
    {
      if (!TimeStamp || 
          Current_Sib->TimeStamp==TimeStamp)
      if (Ray_In_Bound (Ray, Current_Sib->Bound))
      {
        if (All_Intersections (Current_Sib, Ray, Local_Stack))
        {
          while ((Sibling_Intersection = pop_entry(Local_Stack)) != NULL)
          {
            if (Point_In_Clip (Sibling_Intersection->IPoint, Clip))
            {
              push_copy (Depth_Stack, Sibling_Intersection);

              Found = true;
            }
          }
        }
      }
    }

    close_istack (Local_Stack);
  }

  if (Found)
  {
    Increase_Counter(stats[Ray_Motion_Blur_Tests_Succeeded]);
  }

  return (Found);
}



/*****************************************************************************
*
* FUNCTION
*
*   Inside_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static int Inside_Motion_Blur (VECTOR IPoint, OBJECT *Object)
{
  OBJECT *Current_Sib;

  for (Current_Sib = ((MOTION_BLUR *)Object)->Children; Current_Sib != NULL; Current_Sib = Current_Sib->Sibling)
  {
    if (!TimeStamp || 
        Current_Sib->TimeStamp==TimeStamp)
    if (Inside_Object (IPoint, Current_Sib))
    {
      return (true);
    }
  }

  return (false);
}



/*****************************************************************************
*
* FUNCTION
*
*   Translate_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Translate_Motion_Blur (OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)
{
  OBJECT *Sib;

  for (Sib = ((MOTION_BLUR *) Object)->Children; Sib != NULL; Sib = Sib->Sibling)
  {
    Translate_Object(Sib, Vector, Trans);
  }

  Recompute_BBox(&Object->BBox, Trans);
}



/*****************************************************************************
*
* FUNCTION
*
*   Rotate_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Rotate_Motion_Blur (OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)
{
  OBJECT *Sib;

  for (Sib = ((MOTION_BLUR *) Object)->Children; Sib != NULL; Sib = Sib->Sibling)
  {
    Rotate_Object(Sib, Vector, Trans);
  }

  Recompute_BBox(&Object->BBox, Trans);
}



/*****************************************************************************
*
* FUNCTION
*
*   Scale_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Scale_Motion_Blur (OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)
{
  OBJECT *Sib;

  for (Sib = ((MOTION_BLUR *) Object)->Children; Sib != NULL; Sib = Sib->Sibling)
  {
    Scale_Object(Sib, Vector, Trans);
  }

  Recompute_BBox(&Object->BBox, Trans);
}



/*****************************************************************************
*
* FUNCTION
*
*   Transform_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Transform_Motion_Blur (OBJECT *Object, TRANSFORM *Trans)
{
  OBJECT *Sib;

  for (Sib = ((MOTION_BLUR *) Object)->Children; Sib != NULL; Sib = Sib->Sibling)
  {
    Transform_Object (Sib, Trans);
  }

  Recompute_BBox(&Object->BBox, Trans);
}



/*****************************************************************************
*
* FUNCTION
*
*   Invert_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Invert_Motion_Blur (OBJECT *Object)
{
  OBJECT *Sib;

  for (Sib = ((MOTION_BLUR *)Object)->Children; Sib != NULL; Sib = Sib->Sibling)
  {
    Invert_Object (Sib);
  }

  Invert_Flag(Object, INVERTED_FLAG);
}





/*****************************************************************************
*
* FUNCTION
*
*   Create_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

MOTION_BLUR *Create_Motion_Blur ()
{
  MOTION_BLUR *New;

  New = (MOTION_BLUR *)POV_MALLOC(sizeof (MOTION_BLUR), "motion blur");

  INIT_OBJECT_FIELDS(New, MOTION_BLUR_OBJECT+IS_COMPOUND_OBJECT, &Motion_Blur_Methods)

  New->Children = NULL;

  return (New);
}



/*****************************************************************************
*
* FUNCTION
*
*   Copy_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static MOTION_BLUR *Copy_Motion_Blur (OBJECT *Object)
{
  MOTION_BLUR *New;
  OBJECT *Old_Sib, *New_Sib, *Prev_Sib;

  New = (MOTION_BLUR *)POV_MALLOC(sizeof (MOTION_BLUR), "motion blur");

  *New = *(MOTION_BLUR *)Object;

  New->Children = Prev_Sib = NULL;

  for (Old_Sib = ((MOTION_BLUR *)Object)->Children; Old_Sib != NULL; Old_Sib = Old_Sib->Sibling)
  {
    New_Sib = Copy_Object (Old_Sib);

    if (New->Children == NULL)
    {
      New->Children = New_Sib;
    }

    if (Prev_Sib != NULL)
    {
      Prev_Sib->Sibling = New_Sib;
    }

    Prev_Sib = New_Sib;
  }

  return (New);
}



/*****************************************************************************
*
* FUNCTION
*
*   Destroy_Motion_Blur
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Destroy_Motion_Blur (OBJECT *Object)
{
  Destroy_Object (((MOTION_BLUR *) Object)->Children);

  POV_FREE (Object);
}



/*****************************************************************************
*
* FUNCTION
*
*   Compute_Motion_Blur_BBox
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
*   POV-Ray Team
*
* DESCRIPTION
*
*   -
*
* CHANGES
*
*
******************************************************************************/

void Compute_Motion_Blur_BBox (OBJECT *Object)
{
  DBL Old_Volume, New_Volume;
  VECTOR NewMin, NewMax, TmpMin, TmpMax;
  OBJECT *Sib;

    Make_Vector(NewMin,  BOUND_HUGE,  BOUND_HUGE,  BOUND_HUGE);
    Make_Vector(NewMax, -BOUND_HUGE, -BOUND_HUGE, -BOUND_HUGE);

    for (Sib = ((MOTION_BLUR *)Object)->Children; Sib != NULL; Sib = Sib->Sibling)
    {
      Make_min_max_from_BBox(TmpMin, TmpMax, Sib->BBox);

      NewMin[X] = min(NewMin[X], TmpMin[X]);
      NewMin[Y] = min(NewMin[Y], TmpMin[Y]);
      NewMin[Z] = min(NewMin[Z], TmpMin[Z]);
      NewMax[X] = max(NewMax[X], TmpMax[X]);
      NewMax[Y] = max(NewMax[Y], TmpMax[Y]);
      NewMax[Z] = max(NewMax[Z], TmpMax[Z]);
    }

  if ((NewMin[X] > NewMax[X]) || (NewMin[Y] > NewMax[Y]) || (NewMin[Z] > NewMax[Z]))
  {
    Warning(0, "Degenerate motion blur bounding box (not used!).\n");
  }
  else
  {
    New_Volume = (NewMax[X] - NewMin[X]) * (NewMax[Y] - NewMin[Y]) * (NewMax[Z] - NewMin[Z]);

    BOUNDS_VOLUME(Old_Volume, Object->BBox);

    if (New_Volume < Old_Volume)
    {
      Make_BBox_from_min_max(Object->BBox, NewMin, NewMax);

      /* Beware of bounding boxes to large. */

      if ((Object->BBox.Lengths[X] > CRITICAL_LENGTH) ||
          (Object->BBox.Lengths[Y] > CRITICAL_LENGTH) ||
          (Object->BBox.Lengths[Z] > CRITICAL_LENGTH))
      {
        Make_BBox(Object->BBox, -BOUND_HUGE/2, -BOUND_HUGE/2, -BOUND_HUGE/2,
          BOUND_HUGE, BOUND_HUGE, BOUND_HUGE);
      }
    }
  }
}
END_POV_NAMESPACE
#endif
