/*
 * Title:
 *	viewperstrans.c
 *
 * Authors:
 *	Michael P. Schenck
 *
 * Purpose:
 *	This module provides the view structure used to describe the 
 *	position of the viepoint and the structure of the view
 *	volume.  This view volume is used for clipping everything 
 *	outside of it out of the raster list.  A view structure can
 * 	be allocated and then configured to be used as the current 
 *	view.  After this, if displaygraphics() is called, it will
 *	use this new view point.  The view is controlled by specifying
 * 	a point you are looking at and then spherical coordinates to
 *	the point you are looking from.  D and f are the parameters
 *	that control the far and near clipping plane. 
 *	The clipping implimented clips entire polygons if any of thier
 *	verticies are outside the view volume.  This can create some
 * 	bizzare visual artifacts if you have large polygons and are up
 *	close to them.  They could randomly appear and dissapear.
 *	If a view structure could not be allocated, a NULL pointer is
 *	returned.  	
 *
 * Copyright Info:
 *	Copyright (C) 1993, 1994 -- by Michael P. Schenck, 
 *	(mps4466@ultb.isc.rit.edu)
 *	
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published
 *	by the Free Software Foundation; either version 2 of the License,
 *	or (at your option) any later version.
 *
 *	This software is distributed in the hope that it will be useful, but
 *	WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *      For a copy of the GNU General Public License
 *	write to the Free Software Foundation, 675 Mass Ave,
 *	Cambridge, MA  02139, USA.
 *
 */

#include <stdlib.h>
#include <math.h>
#include "/include/types.h"
#include "/include/errors.h"
#include "/include/matrix.h"
#include "/include/database.h"
#include "/include/viewperstrans.h"

ULONG objectviewlist[MAXPOLYINVIEW],
      polyviewlist[MAXPOLYINVIEW],
      viewlistindex;

extern struct Object *objects[MAXNUMOBJECTS];
extern UWORD height,x_center,y_center;	     
	   
static FLOAT sintheta,costheta,sinphi,cosphi,scale,*v,*p,*rm;

	/* Open and allocate required matricies. */

UBYTE openviewperstrans()

{
   if((v = allocatematrix())==NULL)
   	return(MEM_ALLOC_FAILURE);
   if((p = allocatematrix())==NULL) {
	freematrix(v);
	return(MEM_ALLOC_FAILURE);
   }
   if((rm = allocatematrix())==NULL) {
	freematrix(v);
	freematrix(p);
	return(MEM_ALLOC_FAILURE);
   }
   return(SUCCESS);
}

	/* Get a raw view structure. */

struct View *allocateview()

{
   return((struct View *)malloc(sizeof(struct View)));
}

	/* Configure the view by calculating new viewing and perspective 
	   transformation. */

void configureview(struct View *view)

{	   
   scale = (float)height;	/* change to a constant smaller than the height and you will
   				   see the clipping occur for the entire view volume */
   sintheta = sin(view->theta);
   costheta = cos(view->theta); 
   sinphi = sin(view->phi);
   cosphi = cos(view->phi);
  
  	/* Construct viewing matrix. */
   
   *(v) = -sintheta;
   *(v+1) = -cosphi * costheta;
   *(v+2) = -sinphi * costheta;
   *(v+3) = 0.0;
   *(v+4) = costheta;
   *(v+5) = -cosphi * sintheta;
   *(v+6) = -sinphi * sintheta;
   *(v+7) = 0.0;
   *(v+8) = 0.0;
   *(v+9) = sinphi;
   *(v+10) = -cosphi;
   *(v+11) = 0.0;
   *(v+12) = view->x*sintheta-view->y*costheta;
   *(v+13) = view->x*cosphi*costheta+view->y*cosphi*sintheta-view->z*sinphi;
   *(v+14) = view->ro+view->x*sinphi*costheta+view->y*sinphi*sintheta+view->z*cosphi;
   *(v+15) = 1.0;

	/* Construct and concatenate perspective transformation matrix. */

   identitymatrix(p);

   *(p) = scale;
   *(p+5) = scale;
   *(p+10) = view->f/(view->f-view->d);
   *(p+11) = 1.0;
   *(p+14) = -(view->d*view->f/(view->f-view->d));
   *(p+15) = 0.0;

   multmatrix(v,p,rm);
}

	/* Free up a view structure. */

void releaseview(struct View *view) 

{
   free((void *)view);
}

	/* Transform all the verticies in the view list, clip the polys,
	   and build a raster list. */ 

void transformvert()
{	
   register ULONG i,j,k,l;
   UBYTE *vertflag;
   ULONG *polygons,inside,curvert;	
   FLOAT *verticies,*transvert,w,*rv;
   
   viewlistindex = 0;
   for(i=0;i<MAXNUMOBJECTS;i++) {
      if(objects[i] != NULL) {
	
	 vertflag = objects[i]->vertflag;
	 polygons = objects[i]->polygons;
	 transvert = objects[i]->transvert;
	 verticies = objects[i]->verticies;
	
	 for(j=0;j<(objects[i]->numvert);j++)
	          *(vertflag+j) = 0;
      	
	 for(j=0;j<objects[i]->numpoly*MAXPOLYVERT;j+=MAXPOLYVERT) {
	    inside = TRUE;
	    for(k=0;k<MAXPOLYVERT;k++) {
	       if((curvert = *(polygons+j+k))==NOVERT)
	          k=MAXPOLYVERT;
	       else {
	          if(!(*(vertflag+curvert))) {
	             rv = transvert+curvert*4;
		     
		     /* Viewing transformation. */
		     
	             multvecandmat(verticies+curvert*4,rm,rv);
 
 	             /* Add clipping buffer to reduce visual artifacs inherent
		        in this method of clipping (removing entire polygons!). */

		     w = scale * (*(rv+3) + *(rv+3)*0.2); 
		     
		     /* 3D clipping in homogeneous coords. */ 
 		     
		     if((*rv>w)||(*rv<-w)||(*(rv+1)>w)||(*(rv+1)<-w)||(*(rv+2)<0.0)||(*(rv+2)>*(rv+3))) {
		     	*(vertflag+curvert) += 1;
			inside = FALSE;
			k = MAXPOLYVERT;
		     }
		     else {
      	                for(l=0;l<3;l++)
	                   *(rv+l) = (*(rv+l)) / (*(rv+3));
                        *rv = *rv + x_center;
	                *(rv+1) = *(rv+1) + y_center;
		     }
	             *(vertflag+curvert) += 1; 
	          }
		  else {
		     if(*(vertflag+curvert)==2){
		  	inside = FALSE;
			k = MAXPOLYVERT;
		     }
		  }
	       } 
            }
	    if(inside) {
	       objectviewlist[viewlistindex] = i;
	       polyviewlist[viewlistindex] = j / MAXPOLYVERT;
 	       viewlistindex++;
	       if(viewlistindex == MAXPOLYINVIEW)
	          return;
	    }
         }
      }
   }   
}   

	/* Close module. */

void closeviewperstrans()

{
   freematrix(v);
   freematrix(p);
   freematrix(rm);
   
}
