/*
 * object.c
 *
 * 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".
 *
 * $Id$
 *
 * $Log$
 */
#include "object.h"
#include "list.h"

Object *
ObjCreate(objptr, methods)
ObjRef objptr;
Methods *methods;
{
	Object *obj;

	if (objptr == (ObjRef)NULL)
		return (Object *)NULL;
		
	obj = (Object *)share_calloc(1, sizeof(Object));
	obj->obj = objptr;
	obj->methods = methods;
#ifdef SHAREDMEM
	/*
	 * If the counter is in shared memory, processes will
	 * be modifying it left-and-right.  So, we cheat and
	 * make counter a pointer to a non-shared location and
	 * store the value there.
	 */
	new->counter = (unsigned long *)Malloc(sizeof(unsigned long));
	*new->counter = 0;
#endif
	return obj;
}

/*
 * Return a copy of the given object.
 * Note that surface, texturing, and transformation information
 * is copied by reference.
 */
Object *
ObjectCopy(obj)
Object *obj;
{
	Object *new;

	new = ObjCreate(obj->obj, obj->methods);
	/* Share texturing, name, #prims, surface info */
	new->name = obj->name;
	new->texture = obj->texture;
	new->surf = obj->surf;
	new->prims = obj->prims;
	new->trans = obj->trans;
	/* copy bounds */
	BoundsCopy(obj->bounds, new->bounds);
	return new;
}

/*
 * Report bounding box and number of primitives in object.
 */
void
AggregatePrintInfo(obj, fp)
Object *obj;
FILE *fp;
{
	if (fp) {
		fprintf(fp,"Object \"%s\":\n", obj->name);
		BoundsPrint(obj->bounds, fp);
		fprintf(fp,"\t%lu primitive%c\n",obj->prims,
			obj->prims == 1 ? ' ' : 's');
	}
}

/*
 * Convert the given object from a linked list of objects to
 * the desired aggregate type.
 */
int
AggregateConvert(obj, objlist)
Object *obj, *objlist;
{
	if (!IsAggregate(obj)) {
		RLerror(RL_ABORT, "A %s isn't an aggregate.\n",
			ObjectName(obj));
		return 0;
	}

	return (*obj->methods->convert)(obj->obj, objlist);
}

/*
 * Set "bounds" of object to be the extent of the primitive.
 */
int
ObjectBounds(obj)
Object *obj;
{
	if (obj && obj->methods->bounds) {
		(*obj->methods->bounds) (obj->obj, obj->bounds);
	} else {
		RLerror(RL_ABORT, "Can't compute bounds of \"%s\".\n",
			ObjectName(obj));
		return FALSE;
	}
	/*
	 * Enlarge by EPSILON in each direction just to
	 * be on the safe side.
	 */
	obj->bounds[LOW][X] -= EPSILON;
	obj->bounds[HIGH][X] += EPSILON;
	obj->bounds[LOW][Y] -= EPSILON;
	obj->bounds[HIGH][Y] += EPSILON;
	obj->bounds[LOW][Z] -= EPSILON;
	obj->bounds[HIGH][Z] += EPSILON;
	return TRUE;
}

char *
ObjectName(obj)
Object *obj;
{
	if (obj->methods->name)
		return (*obj->methods->name)();

	return "unknown";
}

void
ObjectStats(obj, tests, hits)
Object *obj;
unsigned long *tests, *hits;
{
	if (obj && obj->methods->stats)
		(*obj->methods->stats)(tests, hits);
	else {
		*tests = *hits = 0;
	}
}

/*
 * Push an object onto the head of the given stack, returning
 * the new head.
 */
ObjList *
ObjStackPush(obj, list)
Object *obj;
ObjList *list;
{
	ObjList *new;
	/*
	 * Pretty simple.
	 * Make new element point to old head and return new head.
	 */
	new = (ObjList *)Malloc(sizeof(ObjList));
	new->obj = obj;
	new->next = list;
	return new;
}

/*
 * Pop the topmost object off of the given stack, returning the new head.
 * The old head is freed, but the object it points to is not.
 */
ObjList *
ObjStackPop(list)
ObjList *list;
{
	ObjList *ltmp;

	ltmp = list->next;	/* Save new head. */
	free((voidstar)list);	/* Free old head. */
	return ltmp;		/* Return new head. */
}

Methods *
MethodsCreate()
{
	return (Methods *)share_calloc(1, sizeof(Methods));
}

/*
 * Call appropriate routine to compute UV and, if non-null,
 * dpdu and dpdv at given point on the given primitive.  The
 * normal is used to facilitate computation of u, v, and the
 * partial derivatives.
 */
void
PrimUV(prim, pos, norm, uv, dpdu, dpdv)
Object *prim;
Vector *pos, *norm, *dpdu, *dpdv;
Vec2d *uv;
{
	/*
	 * Call appropriate inverse mapping routine
	 */
	if (prim->methods->uv == NULL) {
		uv->u = uv->v = 0.;
		if (dpdu) {
			dpdu->y = dpdu->z = 0.;
			dpdu->x = 1.;
		}
		if (dpdv) {
			dpdv->x = dpdv->z = 0.;
			dpdv->y = 1.;
		}	
	} else
		(*prim->methods->uv)(prim->obj,pos,norm,uv,dpdu,dpdv);
}

int
PrimNormal(prim, pos, norm, gnorm)
Object *prim;
Vector *pos, *norm, *gnorm;
{
	/*
	 * Call appropriate normal routine
	 */
	return (*prim->methods->normal) (prim->obj, pos, norm, gnorm);
}

int
PrimEnter(obj, ray, mind, hitd)
Object *obj;
Ray *ray;
Float mind, hitd;
{
	/*
	 * Call appropriate enter/leave routine
	 */
	if (obj->methods->enter == NULL) {
		Vector pos, nrm, gnrm;
		/*
		 * Sleazy method:  Use hit point, find normal 
		 * and take dot prod with ray 
		 */
		VecAddScaled(ray->pos, hitd, ray->dir, &pos);
		PrimNormal(obj, &pos, &nrm, &gnrm);

		return dotp(&ray->dir, &gnrm) < 0.0;
	}
	else
		return (*obj->methods->enter) (obj->obj, ray, mind, hitd);
}

/*
 * Walk through a linked-list of objects.  If the object is unbounded,
 * unlink it it from the list and add it to the 'unbounded' list.
 * If the object is bounded, enlarge the given bounding box if
 * necessary.  Return pointer to unbounded list.
 */
Object *
BoundsFind(list, bounds, num)
Object **list;
Float bounds[2][3];
int *num;
{
	Object *ltmp, *prev;
	Object *unbounded, *nextobj;

	BoundsInit(bounds);
	prev = unbounded = (Object *)0;

	*num = 0;
	for (ltmp = *list; ltmp; ltmp = nextobj) {
		nextobj = ltmp->next;
		*num += ltmp->prims;
		if (ltmp->bounds[LOW][X] > ltmp->bounds[HIGH][X]) {
			/*
			 * Object is unbounded -- unlink it...
			 */
			if (prev)
				prev->next = ltmp->next;
			else
				*list = ltmp->next;
			/*
			 * And add it to unbounded object list.
			 */
			ltmp->next = unbounded;
			unbounded = ltmp;
		} else {
			/*
			 * Object is bounded.
			 */
			BoundsEnlarge(bounds, ltmp->bounds);
			prev = ltmp;
		}
	}
	return unbounded;
}
