/* plane.c,v 1.1.1.1 1995/02/27 07:38:35 explorer Exp */

/*
 * Copyright (C) 1989, 1991, Craig E. Kolb
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

#include "geom.h"
#include "plane.h"

static Methods *iPlaneMethods = NULL;
static char planeName[] = "plane";

unsigned long PlaneTests, PlaneHits;

/*
 * create plane primitive
 */
GeomRef
PlaneCreate(pos, norm)
     Vector *pos, *norm;
{
  Plane *plane;
  Vector tmpnrm;
  
  tmpnrm = *norm;
  if (VecNormalize(&tmpnrm) == 0.) {
    RLerror(RL_WARN, "Degenerate plane normal.\n");
    return (GeomRef)NULL;
  }
  plane = (Plane *)share_malloc(sizeof(Plane));
  plane->norm = tmpnrm;
  plane->pos = *pos;
  plane->d = dotp(&plane->norm, pos);
  
  return (GeomRef)plane;
}

Methods *
PlaneMethods()
{
  if (iPlaneMethods == (Methods *)NULL) {
    iPlaneMethods = MethodsCreate();
    iPlaneMethods->name = PlaneName;
#if 0
    iPlaneMethods->create = (GeomCreateFunc *)PlaneCreate;
#endif
    iPlaneMethods->methods = PlaneMethods;
    iPlaneMethods->intersect = PlaneIntersect;
    iPlaneMethods->normal = PlaneNormal;
    iPlaneMethods->uv = PlaneUV;
    iPlaneMethods->bounds = PlaneBounds;
    iPlaneMethods->stats = PlaneStats;
    iPlaneMethods->checkbounds = FALSE;
    iPlaneMethods->closed = FALSE;
  }
  return iPlaneMethods;
}

int
PlaneIntersect(ref, ray, hl, mindist, maxdist)
     GeomRef ref;
     Ray *ray;
     HitList *hl;  /* unused here */
     Float mindist, *maxdist;
{
  Plane *plane = (Plane *)ref;
  Float d;
  
  PlaneTests++;
  
  d = dotp(&plane->norm, &ray->dir);
  if (fabs(d) < EPSILON)
    return FALSE;
  d = (plane->d - dotp(&plane->norm, &ray->pos)) / d;
  
  if (d > mindist && d < *maxdist) {
    *maxdist = d;
    PlaneHits++;
    return TRUE;
  }
  return FALSE;
}

/*ARGSUSED*/
int
PlaneNormal(ref, pos, nrm, gnrm)
     GeomRef ref;
     Vector *pos, *nrm, *gnrm;
{
  Plane *plane = (Plane *)ref;

  *gnrm = *nrm = plane->norm;
  return FALSE;
}

void
PlaneUV(ref, pos, norm, uv, dpdu, dpdv)
     GeomRef ref;
     Vector *pos, *norm, *dpdu, *dpdv;
     Vec2d *uv;
{
  Plane *plane = (Plane *)ref;
  Vector vec, du, dv;
  
  VecCoordSys(norm, &du, &dv);
  VecSub(*pos, plane->pos, &vec);
  
  uv->u = dotp(&vec, &du);
  uv->v = dotp(&vec, &dv);
  
  if (dpdu)
    *dpdu = du;
  if (dpdv)
    *dpdv = dv;
}
	
/*ARGSUSED*/
void
PlaneBounds(ref, bounds)
     GeomRef ref;
     Float bounds[2][3];
{
  /*
   * Planes are unbounded by nature.  minx > maxx signifies
   * this.
   */
  bounds[LOW][X] = 1.0;
  bounds[HIGH][X] = -1.0;
}

char *
PlaneName()
{
  return planeName;
}

void
PlaneStats(tests, hits)
     unsigned long *tests, *hits;
{
  *tests = PlaneTests;
  *hits = PlaneHits;
}

void
PlaneMethodRegister(meth)
     UserMethodType meth;
{
  if (iPlaneMethods)
    iPlaneMethods->user = meth;
}
