/*
 * This file contains functions to perform decal mapping, which is especially
 * useful for putting texture maps onto surfaces consisting of many small
 * polygons.
 *
 * Major function is decalmap, which calls the mapping functions:
 * cylinmap, planemap, sphermap, automap (and bilerp).
 *
 */

/*----------------------------------------------------------------------
 *
 * bilerp does a bilinear interpolation of one number between the
 * four given corners in texture space to the given point in parameter
 * space.
 *
 * uu,vv - point in parameter space
 * x1,x2,x3,x4 - corners in texture space
 *
 *--------------------------------------------------------------------*/

float
bilerp(float uu,float vv,float x1,float x2,float x3,float x4)
{
    float left, right;

    left = x1 + uu * (x2 - x1);
    right = x3+ uu * (x4 - x3);
    return(left + vv * (right - left));
}

/*----------------------------------------------------------------------
 *
 * cylinmap performs a cylindrical projection to create texture coordinates
 *	uu and vv
 * 
 * p - point on surface to be projected
 * uu,vv - new projected coordinates
 * maporigin - the bottom point of the center axis of the cylindrical proj.
 * xaxis, yaxis, zaxis - 3 points defining the coordinate system of
 *        the cylinder.  The central axis is along zaxis-maporigin.
 *	  The point on the equator where (uu,vv)==(0,0) is mapped is at xaxis.
 *--------------------------------------------------------------------*/

void cylinmap(point p,
	output float uu, output float vv,
	point maporigin, point xaxis, point yaxis, point zaxis)
{
    vector	V,Vn,
			XX, YY, ZZ;
    float	xx, yy;


    V = p - maporigin;
    Vn = normalize(V);

    XX = normalize(xaxis - maporigin);
    YY = normalize(yaxis - maporigin);
    ZZ = normalize(zaxis - maporigin);
    xx = dot(Vn,XX);
    yy = dot(Vn,YY);

    vv = dot(V,ZZ);

    uu = atan(yy/xx) / (2*M_PI);  /* -.5 -> .5 */
    uu = uu + step(0,-uu); /* remaps to 0 -> 1 */
}



/*---------------------------------------------------------------------
 * planemap performs a planar projection to create texture coordinates
 *	uu and vv
 * 
 * p - point on surface to be projected
 * uu,vv - new projected coordinates
 * maporigin, xaxis, yaxis - The projection is performed as if the texture
 *	  map were placed with (uu,vv)==(0,0) at 'maporigin', uu==0 along
 *        yaxis-maporigin and vv==0 along xaxis-maporigin.  The map is then
 *        projected parallel to itself onto the surface.
 *--------------------------------------------------------------------*/

void planemap(point p,
	output float uu, output float vv,
	point maporigin, point xaxis, point yaxis)
{
    vector	V,
			XX, YY;
    float	xx, yy;


    V = p - maporigin;

    XX = normalize(xaxis - maporigin);
    YY = normalize(yaxis - maporigin);
    uu = dot(V,XX);
    vv = dot(V,YY);
}




/*--------------------------------------------------------------------
 *
 * sphermap performs a spherical projection to create texture coordinates
 *	uu and vv
 * 
 * p - point on surface to be projected
 * uu,vv - new projected coordinates
 * maporigin - the center of the spherical projection.
 * xaxis, yaxis, zaxis - 3 points defining the coordinate system of
 *        the sphere.  The north pole is at zaxis.
 *	  The point on the equator where uu==0 is mapped is at xaxis.
 *--------------------------------------------------------------------*/

void sphermap(point p,
	output float uu, output float vv,
	point maporigin, point xaxis, point yaxis, point zaxis)
{
    vector	V,
			XX, YY, ZZ;
    float	xx, yy, zz;


    V = normalize(p - maporigin);

    XX = normalize(xaxis - maporigin);
    YY = normalize(yaxis - maporigin);
    ZZ = normalize(zaxis - maporigin);
    xx = dot(V,XX);
    yy = dot(V,YY);
    zz = dot(V,ZZ);

    uu = atan(yy/xx) / (2*M_PI);  /* -.5 -> .5 */
    uu = uu + step(0,-uu); /* remaps to 0 -> 1 */
    vv = acos(-zz) / M_PI;

}


/*---------------------------------------------------------------------
 * automap performs a planar projection using the normal to the surface
 *	as the normal to the plane to create texture coordinates
 *	uu and vv
 * NOTE: this will only work very well on faceted (polygonal or bilinear)
 *	surfaces.
 * 
 * p - point on surface to be projected
 * uu,vv - new projected coordinates
 * maporigin, yaxis - The projection is performed as if the texture
 *	  map were placed with (uu,vv)==(0,0) at 'maporigin', uu==0 along
 *        yaxis-maporigin and vv==0 along the vector perpendicular to
 *	  both yaxis-maporigin and the normal to the surface.  The map is
 *	  then projected parallel to itself onto the surface.  This has
 *	  the effect of giving each facet of a surface a separate planar
 *	  projection.  This will work better for periodic textures than
 *	  for non-periodic textures.  For surfaces that are perpendicular
 *	  to the yaxis-maporigin vector, a reasonable guess at a good
 *	  "up" vector is attempted.
 * n - surface normal
 *--------------------------------------------------------------------*/

void automap(point p,
	output float uu, output float vv,
	point maporigin, point yaxis, point n)
{
    vector	V,
			XX, YY, ZZ;
    float	xx, yy,
			_dot;
    vector	yvector, zvector;


    /*
     * The yvector and zvector are the axes of shader space, but expressed
     * in shader space coordinates, not in current coordinates, so no
     * space conversion is required.  Everything else has already been
     * put into shader space coordinates by the callers of this routine.
     */

    yvector = point (0,1,0);
    zvector = point (0,0,1);

    V = p - maporigin;

    ZZ = normalize(n);
    YY = normalize(yaxis - maporigin);
    _dot = dot(ZZ,YY);
    /* if "up" (YY) vector parallel to "at" (ZZ) vector, find new "up" */
    if ((_dot > (1-0.01))||(_dot < (-1+0.01))) {
	_dot = dot(ZZ,yvector);
	if ((_dot > (1-0.01))||(_dot < (-1+0.01))) YY = zvector;
	else YY = yvector;
    }
    /* get orthogonal coordinate system */
    XX = cross(YY,ZZ);
    YY = cross(ZZ,XX);

    uu = dot(V,XX);
    vv = dot(V,YY);
}




/*--------------------------------------------------------------------
 *
 * decalmap - perform decal mapping. Calls one of the above mapping functions.
 *
 * p - point on surface
 * s, t - unmapped texture coordinates
 * ss, tt - returned mapped texture coordinates
 * maptype - the type of mapping (0 for planar, 1 for cylinder, 2 for sphere,
 *	3 for "standard" mapping using textcoords, 4 for "auto-planar"
 *	mapping - planar perpendicular to surface normal)
 * maporigin - the center of the projection.
 * xaxis, yaxis, zaxis - a local coordinate system that defines the
 *        orientation of the projection.
 * s1,t1,... - corner points for parameter mapping as in textcoords
 * n - normal to the surface
 *--------------------------------------------------------------------*/

void decalmap(point p,
	float s, float t,
	output float ss, output float tt,
	float maptype,
	point maporigin, point xaxis, point yaxis, point zaxis,
	float s1, float t1, float s2, float t2, float s3, float t3, float s4, float t4,
	point n)
{
	float uu, vv;

	if (maptype == 0) {
		planemap(p, uu, vv, maporigin, xaxis, yaxis);
	} else if (maptype == 1) {
		cylinmap(p, uu, vv, maporigin, xaxis, yaxis, zaxis);
	} else if (maptype == 2) {
		sphermap(p, uu, vv, maporigin, xaxis, yaxis, zaxis);
	} else if (maptype == 4) {
		automap(p, uu, vv, maporigin, yaxis, n);
	}
	if (maptype != 3) {
	   /* bilinear interpolation to get texture parameters */
	    ss=bilerp(uu,vv,s1,s2,s3,s4);
	    tt=bilerp(uu,vv,t1,t2,t3,t4);
	} else {
		ss = s;
		tt = t;
	}
}
