/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#define CEIL(x) ((int)((x)-30000.0)+30000)

#ifdef PASS4
#define CAT(a,b) a##b
#endif

/* to avoid overflow on fp->int */
#define MAX (DXD_MAX_INT/2)

/*
 * The following is a kludge relating to to whether or not the
 * z-buffer is updated when a translucent surface is rendered.
 * When we render two transparent objects, we want the closer to
 * overlay the farther, which itself overlays still farther surfaces.
 * A centroid sort is used to approximately prioritize the transparent
 * surfaces.  If it is incorrect at a given pixel, then (in the absense
 * of any other information) they are overlaid in the incorrect order.
 * Which, if the transparency of the front-most (and rendered first)
 * transparent surface is low, is an OK approximation.  If, however, the
 * frontmost surface is relatively opaque, then this approximation
 * becomes less accurate.  In this case it is better to forget about
 * the second transparent surface (which would erroneously be laid over
 * it).  So when the surface is relatively opaque, we update the z-buffer
 * to eliminate subsequent farther surfaces.
 *
 * Note that this appears in line.c also.
 */
#define OPACITY_THRESHOLD 0.5

#ifdef PASS2

#define TRIANGLEX1(pst, pptr, shrgb, sho, col, face, TEST, PIXEL, LABEL, VER) {$\
									$\
    pst *pp, *p;							$\
    Pointer colors;							$\
    int cstcolors;							$\
    RGBColor cbuf1, cbuf2, cbuf3;					$\
    char cbyte;								$\
    char obyte = xf->obyte;						$\
									$\
    /* offsets */							$\
    x1+=ox, y1+=oy;							$\
    x2+=ox, y2+=oy;							$\
    x3+=ox, y3+=oy;							$\
									$\
    /* avoid overflow - XXX check performance impact */			$\
    if (x1<-MAX || x1>MAX || y1<-MAX || y1>MAX ||			$\
      x2<-MAX || x2>MAX || y2<-MAX || y2>MAX ||				$\
      x3<-MAX || x3>MAX || y3<-MAX || y3>MAX)				$\
	DXErrorReturn(ERROR_BAD_PARAMETER,				$\
		"camera causes numerical overflow");			$\
									$\
    /* x delta setup; sign of d also indicates parity */		$\
    d1 = y3-y2,  d2 = y1-y3,  d3 = y2-y1;				$\
    d = x1*d1 + x2*d2 + x3*d3;						$\
    Qx = d? (float)1.0 / (float)d : 0.0;				$\
    d1 *= Qx,  d2 *= Qx,  d3 *= Qx;					$\
									$\
    /* colors */							$\
    if (col) {								$\
	if (d < 0)							$\
	    cbyte = fbyte, colors = fcolors, cstcolors = xf->fcst;	$\
	else								$\
	    cbyte = bbyte, colors = bcolors, cstcolors = xf->bcst;	$\
	if (!colors) goto CAT(LABEL,VER);				$\
    }									$\
    if (shrgb) {							$\
	if (shademe) {							$\
	    c1 = ((RGBColor *)colors) + i1;				$\
	    c2 = ((RGBColor *)colors) + i2;				$\
	    c3 = ((RGBColor *)colors) + i3;				$\
	} else {							$\
	    if (cmap)							$\
		c1= &MAPCOLS(v1), c2= &MAPCOLS(v2), c3= &MAPCOLS(v3);	$\
	    else {							$\
		COLS(v1, cbuf1); COLS(v2, cbuf2); COLS(v3, cbuf3);	$\
		c1 = &cbuf1; c2 = &cbuf2; c3 = &cbuf3;			$\
	    }								$\
	}								$\
	r1=c1->r, g1=c1->g, b1=c1->b;					$\
	r2=c2->r, g2=c2->g, b2=c2->b;					$\
	r3=c3->r, g3=c3->g, b3=c3->b;					$\
    } else if (col) {							$\
	if (cmap){							$\
	   r=MAPCOLS(index).r; g=MAPCOLS(index).g; b=MAPCOLS(index).b;	$\
	} else {							$\
	   COLS(index, cbuf1);						$\
	   r = cbuf1.r; g = cbuf1.g; b = cbuf1.b;			$\
	}								$\
    }									$\
    if (sho) {								$\
	if (omap) o1=MAPOPS(v1), o2=MAPOPS(v2), o3=MAPOPS(v3);		$\
	else {								$\
	    OPS(v1, o1); OPS(v2, o2); OPS(v3, o3);			$\
	}								$\
    } else if (opacities||omap) {					$\
	if (omap) o=MAPOPS(index);				    	$\
	else      OPS(index, o);					$\
	obar = 1.0-o;							$\
    } else								$\
	o = 1.0, obar = 0.0;						$\
									$\
    /* x deltas */							$\
    if (shrgb) {							$\
	dxr = (r1 == r2 && r1 == r3) ? 0.0 : d1*r1 + d2*r2 + d3*r3;	$\
	dxg = (g1 == g2 && g1 == g3) ? 0.0 : d1*g1 + d2*g2 + d3*g3;	$\
	dxb = (b1 == b2 && b1 == b3) ? 0.0 : d1*b1 + d2*b2 + d3*b3;	$\
    }									$\
    if (sho) dxo = (o1 == o2 && o1 == o3) ? 0.0 : d1*o1 + d2*o2 + d3*o3;$\
    dxz = (z1 == z2 && z1 == z3) ? 0.0 : d1*z1 + d2*z2 + d3*z3;		$\
									$\
    /* sort pts so 1 <= 2 <= 3, so 1 to 3 is the long edge */		$\
    XCHNG(y1,y2,x1,x2,r1,r2,g1,g2,b1,b2,o1,o2,z1,z2,shrgb,sho)		$\
    XCHNG(y1,y3,x1,x3,r1,r3,g1,g3,b1,b3,o1,o3,z1,z3,shrgb,sho)		$\
    XCHNG(y2,y3,x2,x3,r2,r3,g2,g3,b2,b3,o2,o3,z2,z3,shrgb,sho)		$\
									$\
    /* object bounds for faces */					$\
    /* XXX - phooey, reusing A and B here to avoid compiler problems */	$\
    if (face) {								$\
	A = B = x1;							$\
	if (x2<A) A = x2; else B = x2;					$\
        if (x3<A) A = x3; else if (x3>B) B = x3;			$\
	if (A<buf->fmin.x) buf->fmin.x = A;				$\
	if (B>buf->fmax.x) buf->fmax.x = B;				$\
	if (y1<buf->fmin.y) buf->fmin.y = y1;				$\
	if (y3>buf->fmax.y) buf->fmax.y = y3;				$\
    }									$\
									$\
    /* round up to get iy1, iy2, iy3*/					$\
    iy1 = CEIL(y1), iy2 = CEIL(y2), iy3 = CEIL(y3);			$\
    if (iy1>iy2 || iy2>iy3) {						$\
	DXSetError(ERROR_INVALID_DATA,					$\
	    "position number %d, %d, or %d is invalid", v1, v2, v3);	$\
	return NULL;							$\
    }									$\
    if (iy1==iy3)					/* TIME */	$\
	goto CAT(LABEL,VER);



#define TRIANGLEX2(pst, pptr, shrgb, sho, col, face, TEST, PIXEL, LABEL, VER)$\
    /* y deltas */							$\
    d = y3 - y1;							$\
    Qy = d? (float)1.0 / (float)d : 0.0;				$\
    if (shrgb) dyr=(r3-r1)*Qy, dyg=(g3-g1)*Qy, dyb=(b3-b1)*Qy;		$\
    if (sho) dyo = (float)(o3-o1) * Qy;					$\
    dyz = (z3-z1) * Qy;							$\
    dyA = (float)(x3-x1) * Qy; 						$\
									$\
    /* initial values */						$\
    A = x1;								$\
    pp = buf->pptr + iy1*width;					   	$\
									$\
    /* y fractional pixel adjustment and/or y clip */			$\
    d = iy1 - y1;							$\
    if (iy1<0)								$\
	d-=iy1, pp=buf->pptr;					   	$\
    A += dyA*d;								$\
    if (shrgb) r1+=dyr*d, g1+=dyg*d, b1+=dyb*d;				$\
    if (sho) o1+=dyo*d;							$\
    z1 += dyz*d;							$\
									$\
    /* do the two halves of the triangle */				$\
    nn = 0;								$\
    HALF(iy1, iy2, x1, x2, y1, y2, shrgb, sho, TEST, PIXEL)		$\
    HALF(iy2, iy3, x2, x3, y2, y3, shrgb, sho, TEST, PIXEL)		$\
    buf->pixels += nn;							$\
}

#define TRIANGLEV1(pst, pptr, shrgb, sho, col, face, TEST, PIXEL, LABEL, VER) {$\
									$\
    pst *pp, *p;							$\
    Pointer colors;							$\
    int cstcolors;							$\
    RGBColor cbuf1, cbuf2, cbuf3;					$\
    char cbyte;								$\
    char obyte = xf->obyte;						$\
									$\
    /* offsets */							$\
    x1+=ox, y1+=oy;							$\
    x2+=ox, y2+=oy;							$\
    x3+=ox, y3+=oy;							$\
									$\
    /* avoid overflow - XXX check performance impact */			$\
    if (x1<-MAX || x1>MAX || y1<-MAX || y1>MAX ||			$\
      x2<-MAX || x2>MAX || y2<-MAX || y2>MAX ||				$\
      x3<-MAX || x3>MAX || y3<-MAX || y3>MAX)				$\
	DXErrorReturn(ERROR_BAD_PARAMETER,				$\
		"camera causes numerical overflow");			$\
									$\
    /* x delta setup; sign of d also indicates parity */		$\
    d1 = y3-y2,  d2 = y1-y3,  d3 = y2-y1;				$\
    d = x1*d1 + x2*d2 + x3*d3;						$\
    Qx = d? (float)1.0 / (float)d : 0.0;				$\
    d1 *= Qx,  d2 *= Qx,  d3 *= Qx;					$\
									$\
    /* colors ONLY if valid */						$\
    if (valid) {							$\
	if (col) {							$\
	    if (d < 0)							$\
		cbyte = fbyte, colors = fcolors, cstcolors = xf->fcst;	$\
	    else							$\
		cbyte = bbyte, colors = bcolors, cstcolors = xf->bcst;	$\
	    if (!colors) goto CAT(LABEL,VER);				$\
	}								$\
	if (shrgb) {							$\
	    if (cmap) c1= &MAPCOLS(v1), c2= &MAPCOLS(v2), c3= &MAPCOLS(v3);$\
	    else {							$\
		COLS(v1, cbuf1); COLS(v2, cbuf2); COLS(v3, cbuf3);	$\
		c1 = &cbuf1; c2 = &cbuf2; c3 = &cbuf3;			$\
	    }								$\
	    r1=c1->r, g1=c1->g, b1=c1->b;				$\
	    r2=c2->r, g2=c2->g, b2=c2->b;				$\
	    r3=c3->r, g3=c3->g, b3=c3->b;				$\
	} else if (col) {						$\
	    if (cmap) r=MAPCOLS(i).r, g=MAPCOLS(i).g, b=MAPCOLS(i).b;	$\
	    else {						 	$\
		COLS(i, cbuf1);						$\
		r = cbuf1.r; g = cbuf1.g; b = cbuf1.b;			$\
	    }								$\
	}								$\
	if (sho) {							$\
	    if (omap) o1=MAPOPS(v1), o2=MAPOPS(v2), o3=MAPOPS(v3);	$\
	    else {							$\
		OPS(v1, o1); OPS(v2, o2); OPS(v3, o3);			$\
	    }								$\
	} else if (opacities||omap) {					$\
	    if (omap) o=MAPOPS(i);					$\
	    else      OPS(i, o);					$\
	    obar = 1.0-o;						$\
	} else								$\
	    o = 1.0, obar = 0.0;					$\
									$\
	if (shrgb) dxr = d1*r1 + d2*r2 + d3*r3;				$\
	if (shrgb) dxg = d1*g1 + d2*g2 + d3*g3;				$\
	if (shrgb) dxb = d1*b1 + d2*b2 + d3*b3;				$\
	if (sho)   dxo = d1*o1 + d2*o2 + d3*o3;				$\
    } else								$\
	r1 = r2 = r3 = g1 = g2 = g3 = b1 = b2 = b3 = o1 = o2 = o3 = 0.0;$\
									$\
    /* x deltas */							$\
    dxz = d1*z1 + d2*z2 + d3*z3;					$\
									$\
    /* sort pts so 1 <= 2 <= 3, so 1 to 3 is the long edge */		$\
    XCHNG(y1,y2,x1,x2,r1,r2,g1,g2,b1,b2,o1,o2,z1,z2,shrgb,sho)		$\
    XCHNG(y1,y3,x1,x3,r1,r3,g1,g3,b1,b3,o1,o3,z1,z3,shrgb,sho)		$\
    XCHNG(y2,y3,x2,x3,r2,r3,g2,g3,b2,b3,o2,o3,z2,z3,shrgb,sho)		$\
									$\
    /* object bounds for faces */					$\
    /* XXX - phooey, reusing A and B here to avoid compiler problems */	$\
    if (face) {								$\
	A = B = x1;							$\
	if (x2<A) A = x2; else B = x2;					$\
        if (x3<A) A = x3; else if (x3>B) B = x3;			$\
	if (A<buf->fmin.x) buf->fmin.x = A;				$\
	if (B>buf->fmax.x) buf->fmax.x = B;				$\
	if (y1<buf->fmin.y) buf->fmin.y = y1;				$\
	if (y3>buf->fmax.y) buf->fmax.y = y3;				$\
    }									$\
									$\
    /* round up to get iy1, iy2, iy3*/					$\
    iy1 = CEIL(y1), iy2 = CEIL(y2), iy3 = CEIL(y3);			$\
    if (iy1>iy2 || iy2>iy3) {						$\
	DXSetError(ERROR_INVALID_DATA,					$\
	    "position number %d, %d, or %d is invalid", v1, v2, v3);	$\
	return NULL;							$\
    }									$\
    if (iy1==iy3)				/* TIME */		$\
	goto CAT(LABEL,VER);						$\
									$\
    /* y deltas */							$\
    d = y3 - y1;							$\
    Qy = d? (float)1.0 / (float)d : 0.0;				$\
    if (valid) {							$\
	if (shrgb) dyr=(r3-r1)*Qy, dyg=(g3-g1)*Qy, dyb=(b3-b1)*Qy;	$\
	if (sho) dyo = (float)(o3-o1) * Qy;				$\
    }									$\
    dyz = (z3-z1) * Qy;							$\
    dyA = (float)(x3-x1) * Qy; 						$\
									$\
    /* initial values */						$\
    A = x1;								$\
    pp = buf->pptr + iy1*width;					    	$\
									$\
    /* y fractional pixel adjustment and/or y clip */			$\
    d = iy1 - y1;							$\
    if (iy1<0)								$\
	d-=iy1, pp=buf->pptr;					    	$\
    A += dyA*d;								    

#define TRIANGLEV2(pst, pptr, shrgb, sho, col, face, TEST, PIXEL, LABEL, VER)$\
									$\
    if (valid) {							$\
	if (shrgb) r1+=dyr*d, g1+=dyg*d, b1+=dyb*d;			$\
	if (sho) o1+=dyo*d;						$\
    }									$\
    z1 += dyz*d;							$\
									$\
    /* do the two halves of the triangle */				$\
    nn = 0;								$\
    if (valid) {							$\
	HALF(iy1, iy2, x1, x2, y1, y2, shrgb, sho, TEST, PIXEL) 	$\
	HALF(iy2, iy3, x2, x3, y2, y3, shrgb, sho, TEST, PIXEL)	    	$\
    } else {								$\
	HALF(iy1, iy2, x1, x2, y1, y2, 0, 0, TEST, PIXEL)		$\
	HALF(iy2, iy3, x2, x3, y2, y3, 0, 0, TEST, PIXEL)		$\
    }									$\
    buf->pixels += nn;							$\
}

#endif

#ifdef PASS3

#define X(a,b) (d=a, a=b, b=d)

#define XCHNG(y1,y2,x1,x2,r1,r2,g1,g2,b1,b2,o1,o2,z1,z2,shrgb,sho) {	$\
    if (y2 < y1) {							$\
	X(y1,y2), X(x1,x2);						$\
	if (shrgb) X(r1,r2), X(g1,g2), X(b1,b2);			$\
	if (sho) X(o1,o2);						$\
	X(z1,z2);							$\
    }									$\
}

/* direct or mapped colors */
#define COLS(i, dst)							$\
{									$\
    if (cbyte)								$\
    {									$\
	RGBByteColor *ptr=((RGBByteColor *)colors)+(cstcolors ? 0 : i);	$\
	dst.r = _dxd_ubyteToFloat[ptr->r];				$\
	dst.g = _dxd_ubyteToFloat[ptr->g];				$\
	dst.b = _dxd_ubyteToFloat[ptr->b];				$\
    } else								$\
	dst = (((RGBColor *)colors)[cstcolors ? 0 : i]);		$\
}

#define OPS(i, dst) 							$\
{									$\
    if (obyte)								$\
      dst = _dxd_ubyteToFloat[((ubyte *)opacities)[xf->ocst ? 0 : i]];  $\
    else								$\
      dst = (((float *)opacities)[xf->ocst ? 0 : i]);			$\
}

#define MAPCOLS(i) (cmap[((unsigned char *)colors)[cstcolors ? 0 : i]])
#define MAPOPS(i) (omap[((unsigned char *)opacities)[xf->ocst ? 0 : i]])


/*
 * The various per-pixel routines.  We start with the opaque
 * cases: opaque triangles, and triangles that are part of
 * the faces/loops/edges stuff.  We define two comparisons,
 * for case of render-time CSG (CLIPZ) or no render-time CSG
 * (NOCLIPZ).  We also define a comparison for interference checking
 * (IFZ) that also flips the IF_OBJECT bit as appropriate.
 */

/*
 * The IFZ macro here is a conditional containing an outer condition
 * controlling an XOR assignment, followed by a continuation of the condition.
 * To get around a preprocessor limitation (commas in macro arguments) we
 * (Nancy and Greg) changed the inner XOR assignment to an additional	
 * term in the condition that always evaluates TRUE.
 */

#define CLIPZ   (z>p->z && z<p->front && z>p->back)
#define NOCLIPZ (z>p->z)
#define IFZ     ((z<p->front)             &&	\
		 (1 | (p->in^=IF_OBJECT)) &&	\
		 (z>p->z)                 &&	\
		 (z>p->back))

#define OPAQUE {				\
	p->c.r=r; p->c.g=g; p->c.b=b; p->z=z;   \
}

#define FACE {					\
    /* reuse co.o for z here; XXX-use union? */	\
    p->co.r=r; p->co.g=g; p->co.b=b; p->co.o=z;	\
    p->in ^= IN_FACE;				\
}


/*
 * Clipping triangles just affect the front/back buffers
 */

#define CLIPPINGPIXEL {							\
    if (z > p->front)	/* find frontmost surface */			\
	p->front = z;	/* XXX - could use orientation of face */	\
    if (z < p->back)	/* find backmost surface */			\
	p->back = z;							\
}


/*
 * For translucent triangles, we can assume that ->back has
 * already been merged into ->z; furthermore, we reuse ->back
 * in volume rendering, so it is incorrect to compare against
 * ->back here because we may be mixed in with volume faces
 * This also occurs in line.c.
 */

#define TRANSCLIPZ (z>p->z && z<p->front)

#define TRANSLUCENT {							\
	p->c.r=o*r+obar*p->c.r;						\
	p->c.g=o*g+obar*p->c.g;						\
	p->c.b=o*b+obar*p->c.b;						\
	if (o > OPACITY_THRESHOLD)					\
	    p->z=z;							\
}

#define COMPOSITE {							\
	p->c.r=r+obar*p->c.r;						\
	p->c.g=g+obar*p->c.g;						\
	p->c.b=b+obar*p->c.b;						\
}


/*
 * We use ->back instead of ->z to keep track of the distance to
 * the last volume face rendered; this allows us to leave ->z alone,
 * which makes it possible to mix translucent surfaces clipped by
 * ->z with volumes.  In order for this to work, we have to have
 * merged ->back into ->z before doing translucent surfaces and
 * volumes.
 */

#define VOLUME {							\
    if (p->in<=0) {							\
	if (z < p->front) {						\
	    if (surface) {						\
		p->in++;						\
		if (z > p->back)					\
		    p->back = z;					\
		if (valid && !xf->tile.flat_z)				\
		    { p->co.r=r; p->co.g=g; p->co.b=b; }		\
	    }								\
	}								\
    } else {								\
	if (surface)							\
	    p->in = 0;							\
	if (z >= p->front) {						\
	    p->in = -DXD_MAX_INT;					\
	    z = p->front;						\
	}								\
	if (z > p->back) {						\
	    float ar; float ag; float ab; float ao;			\
	    if (valid) { /* don't affect color if not valid, just Z */	\
		if (/*xf->tile.flat_z*/ 1) {				\
		    d = (z - p->back) * cmul;				\
		    ar = d * r;						\
		    ag = d * g;						\
		    ab = d * b;						\
		    ao = d * o * omul;					\
		} else {						\
		    d = 0.5 * (z - p->back) * cmul;			\
		    ar = d * (p->co.r+r);				\
		    ag = d * (p->co.g+g);				\
		    ab = d * (p->co.b+b);				\
		    ao = d * (p->co.o+r) * omul;			\
		    p->co.r = r;					\
		    p->co.g = g;					\
		    p->co.b = b;					\
		    p->co.o = o;					\
		}							\
		if (/*xf->tile.fast_exp*/ 1) {				\
		    if ((obar=1-ao) < 0.0)				\
			obar = 0.0;					\
		} else {						\
		    if ((obar=1-0.125*ao) < 0.0)			\
			obar = 0.0;					\
		    obar = obar*obar; obar = obar*obar; obar = obar*obar;\
		}							\
		p->c.r = p->c.r * obar + ar;				\
		p->c.g = p->c.g * obar + ag;				\
		p->c.b = p->c.b * obar + ab;				\
	    }								\
	    p->back = z;						\
	}								\
    }									\
}


/*
 * shade/don't shade colors/opacities
 * colored/uncolored surfaces
 */

#define NOSHRGB 0
#define SHRGB 1

#define NOSHO 0
#define SHO 1

#define UNCOL 0
#define COL 1


#define CEIL(x) ((int)((x)-30000.0)+30000)

#define HALF(iy1, iy2, x1, x2, y1, y2, shrgb, sho, TEST, PIXEL) {	$\
									$\
    if (iy2>height)							$\
	iy2 = height;							$\
    if (iy1<iy2 && iy2>0 && iy1<=height) {				$\
	B = x1;								$\
	d = y2 - y1;							$\
	d = d? (float)1.0 / (float)d : 0.0;				$\
	dyB = (float)(x2-x1) * d;					$\
	if (iy1<0) iy1=0;						$\
	d = iy1 - y1;							$\
	B += d*dyB;							$\
	for (iy=iy1; iy<iy2; iy++) {					$\
	    iA = CEIL(A);						$\
	    iB = CEIL(B);						$\
	    if (iB>iA) left=iA, right=iB;				$\
	    else left=iB, right=iA;					$\
	    if (left<0) left=0;						$\
	    if (right>width) right=width;				$\
	    n = right - left;						$\
	    if (n>0) {							$\
		nn += n;						$\
		p = pp+left;						$\
		d = left - A;						$\
		if (shrgb) r=r1+d*dxr, g=g1+d*dxg, b=b1+d*dxb;		$\
		if (sho) o=o1+d*dxo, obar=1.0-o;			$\
		z=z1+d*dxz;						$\
		while (--n>=0) {					$\
		    if (TEST) {						$\
			PIXEL;						$\
			if (shrgb) r+=dxr, g+=dxg, b+=dxb;		$\
			if (sho) o+=dxo, obar=1.0-o;			$\
			z+=dxz;						$\
			p++;						$\
		    } else {						$\
			if (shrgb) r+=dxr, g+=dxg, b+=dxb;		$\
			if (sho) o+=dxo, obar=1.0-o;			$\
			z+=dxz;						$\
			p++;						$\
		    }							$\
		}							$\
	    }								$\
	    if (shrgb) r1+=dyr, g1+=dyg, b1+=dyb;			$\
	    if (sho) o1+=dyo;						$\
	    z1 += dyz;							$\
	    A+=dyA, B+=dyB;						$\
	    pp+=width;							$\
	}								$\
    }									$\
}

#endif
