/*
 * texture.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 "texture.h"

/*
 * Transformation structures used to map from texture space to
 * model/primitive/world space.
 */
Trans prim2model, model2text, prim2text, world2text;

#define ApplyMapping(m,o,p,n,c,u,v)	(*m->method)(m, o, p, n, c, u, v)

Texture *
TextCreate(data, meth)
TextRef data;
void (*meth)();
{
	Texture *res;

	res = (Texture *)share_calloc(1, sizeof(Texture));
	res->data = data;
	res->method = meth;
	res->trans = (Trans *)NULL; 
	res->next = (Texture *)NULL;
	return res;
}

/*
 * Apply appropriate textures to a surface.
 */
void
TextApply(tlist, prim, ray, pos, norm, gnorm, surf, p2model, world2model)
Texture *tlist;				/* Textures */
Object *prim;
Ray *ray;
Vector *pos, *norm, *gnorm;		/* pos, shading norm, geo. norm */
Surface *surf;
Trans *p2model, *world2model;
{
	Vector ptmp;
	Texture *ttmp;

	prim2model = *p2model;
	/*
	 * Walk down texture list, applying each in turn.
	 */
	for (ttmp = tlist; ttmp; ttmp = ttmp->next) {
		/*
		 * Make copies of pos & ray to pass to the texturing function.
		 */
		ptmp = *pos;
		if (ttmp->trans) {
			/*
			 * We compose ttmp->trans, which maps from model to
			 * texture space, with prim2model and world2model
			 * to get prim2text and world2text.
			 */
			model2text = *ttmp->trans;
			TransCompose(&model2text, &prim2model, &prim2text);
			TransCompose(&model2text, world2model, &world2text);
			/*
			 * Transform intersection point to texture space.
			 * Ray and normal are passed in model space.
			 */
			ModelPointToText(&ptmp);
		} else {
			/*
		 	 * By default, texture and model space are identical.
		 	 */
			TransInit(&model2text);
			TransCopy(&prim2model, &prim2text);
			TransCopy(world2model, &world2text);
		}

		/*
		 * Call texture function.
		 */
		(*ttmp->method) (ttmp->data,prim,ray,&ptmp,norm,gnorm,surf);
	}
}

/*
 * Compute UV at 'pos' on given primitive.
 */
TextToUV(mapping, prim, pos, norm, u, v, dpdu, dpdv)
Mapping *mapping;
Object *prim;
Vector *pos, *norm, *dpdu, *dpdv;
Float *u, *v;
{
	Vec2d uv;
	Vector ptmp;
	Matrix *t;

	ptmp = *pos;

	if (mapping->flags & PRIMSPACE) {
		/*
	 	 * Convert point and normal to primitive space.
	 	 */
		TextPointToPrim(&ptmp);
	} else {
		/*
		 * Convert point and normal to object space.
		 */
		TextPointToModel(&ptmp);
	}

	ApplyMapping(mapping, prim, &ptmp, norm, &uv, dpdu, dpdv);

	/*
	 * Transform UV by model2text.  We set X = u and Y = v,
	 * while Z = 0.
	 * Although the UV coordinates may be in prim space,
	 * we treat them as if they are model-space coords.
	 * This is due to the fact that we want the texture
	 * to be applied in model space.
	 */
	ptmp.x = uv.u;
	ptmp.y = uv.v;
	ptmp.z = 0.;
	PointTransform(&ptmp, &model2text.trans);
	*u = ptmp.x;
	*v = ptmp.y;
	if (dpdu == (Vector *)NULL || dpdv == (Vector *)NULL)
		return;
	/*
	 * Here's the ugly part.
	 * Build initial UVN-->XYZ matrix...
	 */
	t = ArbitraryMatrix(dpdu->x, dpdu->y, dpdu->z,
			 dpdv->x, dpdv->y, dpdv->z,
			 norm->x, norm->y, norm->z, 0., 0., 0.);
	/*
	 * ...transform to model space...
	 */
	MatrixMult(t, &prim2model.trans, t);
	/*
	 * ... apply model2text in UVN space.
	 */
	MatrixMult(&model2text.itrans, t, t);
	dpdu->x = t->matrix[0][0];
	dpdu->y = t->matrix[0][1];
	dpdu->z = t->matrix[0][2];
	dpdv->x = t->matrix[1][0];
	dpdv->y = t->matrix[1][1];
	dpdv->z = t->matrix[1][2];
	(void)VecNormalize(dpdu);
	(void)VecNormalize(dpdv);
	free((voidstar)t);
}
