/*
 * Copyright (c) 1993-1995 Argonaut Technologies Limited. All rights reserved.
 *
 * $Id: onscreen.c 1.1 1997/12/10 16:52:24 jon Exp $
 * $Locker: $
 *
 */
#include "drv.h"
#include "shortcut.h"

BR_RCS_ID("$Id: onscreen.c 1.1 1997/12/10 16:52:24 jon Exp $")

/*
 * Check a bounding box against the view volume and return
 * one of:
 *
 *	BRT_REJECT	- Model is completely off screen
 *	BRT_PARTIAL	- Model is partially on screen
 *	BRT_ACCEPT  - Model is completely on screen
 *
 * Works by transforming the 6 planes that bound the view volume into
 * model space. We have the model_to_screen transfrom. To transform a
 * plane (screen to model), use the inverse of the transpose, ie: transpose
 * of model_to_screen. Since the view plane equations are just zero's
 * and ones, the transformed plane equation can be generated by adds/subs
 * of specific rows of the transform.
 *
 * To test the (axis aligned) box against the view plane, only two vertices
 * need testing - those that lie along the diagonal corresponding to the
 * direction of the plane's normal.
 *
 * The components of these test vertices are selected from min/max
 * by looking at the sign of the X,Y,Z components of the normal
 *
 * XXX Could probably write a mean version of this is assembler
 */

#define MAKE_EQN(sign,row) {\
	eqn.v[X] =  model_to_screen->m[0][3] sign model_to_screen->m[0][row];\
	eqn.v[Y] =  model_to_screen->m[1][3] sign model_to_screen->m[1][row];\
	eqn.v[Z] =  model_to_screen->m[2][3] sign model_to_screen->m[2][row];\
	eqn.v[W] =-(model_to_screen->m[3][3] sign model_to_screen->m[3][row]);\
	}

#define TEST_NOT_IN\
		(BR_MAC3(\
			eqn.v[X],((eqn.v[X]>0)?(bounds->min.v[X]):(bounds->max.v[X])),\
			eqn.v[Y],((eqn.v[Y]>0)?(bounds->min.v[Y]):(bounds->max.v[Y])),\
			eqn.v[Z],((eqn.v[Z]>0)?(bounds->min.v[Z]):(bounds->max.v[Z]))) <\
			eqn.v[W])
	
#define TEST_OUT\
		(BR_MAC3(\
			eqn.v[X],((eqn.v[X]>0)?(bounds->max.v[X]):(bounds->min.v[X])),\
			eqn.v[Y],((eqn.v[Y]>0)?(bounds->max.v[Y]):(bounds->min.v[Y])),\
			eqn.v[Z],((eqn.v[Z]>0)?(bounds->max.v[Z]):(bounds->min.v[Z]))) <\
			eqn.v[W])


br_token OnScreenCheck(br_renderer *self, br_matrix4 *model_to_screen, br_bounds3 *bounds)
{
	int accept = 1;
	br_vector4 eqn;
	int c;

	/*
	 * Left	  - screen space plane eqn. = ( 1, 0, 0, 1)
	 */
	MAKE_EQN(+,0);

	if(TEST_OUT)
		return BRT_REJECT;

	if(TEST_NOT_IN)
		accept = 0;

	/*
	 * Right  - screen space plane eqn. = (-1, 0, 0, 1)
	 */
	MAKE_EQN(-,0);

	if(TEST_OUT)
		return BRT_REJECT;

	if(accept && TEST_NOT_IN)
		accept = 0;

	/*
	 * Bottom - screen space plane eqn. = ( 0, 1, 0, 1)
	 */
	MAKE_EQN(+,1);

	if(TEST_OUT)
		return BRT_REJECT;

	if(accept && TEST_NOT_IN)
		accept = 0;

	/*
	 * Top    - screen space plane eqn. = ( 0,-1, 0, 1)
	 */
	MAKE_EQN(-,1);

	if(TEST_OUT)
		return BRT_REJECT;

	if(accept && TEST_NOT_IN)
		accept = 0;

	/*
	 * Yon    - screen space plane eqn. = ( 0, 0, 1, 1)
	 */
	MAKE_EQN(+,2);

	if(TEST_OUT)
		return BRT_REJECT;

	if(accept && TEST_NOT_IN)
		accept = 0;

	/*
	 * Hither - screen space plane eqn. = ( 0, 0,-1, 1)
	 */
	MAKE_EQN(-,2);

	if(TEST_OUT)
		return BRT_REJECT;

	if(accept && TEST_NOT_IN)
		accept = 0;

	/*
	 * Any user clip planes
	 *
	 * XXX - user_clip_active needs checking - getting
	 * astale version here
	 */
//	if(scache.user_clip_active) {
		for(c = 0; c < MAX_STATE_CLIP_PLANES; c++) {

			if(self->state.clip[c].type != BRT_PLANE)
				continue;

			BrMatrix4TApply(&eqn,
					 &self->state.clip[c].plane,
					 model_to_screen);
			eqn.v[W] = -eqn.v[W];

			if(TEST_OUT)
				return BRT_REJECT;

			if(accept && TEST_NOT_IN)
				accept = 0;
		}
//	}

	return accept?BRT_ACCEPT:BRT_PARTIAL;
}

