/*
 * Title:
 *	displaymap.c
 *
 * Authors:
 * 	Michael P. Schenck
 *
 * Purpose:
 * 	This module impliments an object hierarchy similar to the 
 *	one used in phigs.  It allows subobjects and lists of nodes 
 *	which act as a series of command matricies.  
 *	A root node is always allocated first for an object.  This
 *	is followed by a list of other nodes whos matricies can 
 *	either be appended to the local matrix or can set a new
 *	local matrix to thier own matrix.  All of these nodes can
 *	have models linked to them that will be transformed into
 * 	world coordinate (object parts) by using the concatenation
 *	of the matrix from the previous level (if a subobject) and
 *	and the current local matrix.  A subobject is made by linking
 *	another node to the subject link of a node in the previous
 *	level.  An action block (node) can be inserted after any
 *	node already in the objects display map.
 *	There is no current way to remove an individual action block,
 *	only the removal of the entire object.  If either of the 
 *	action block creation functions return NULL, then the nodes
 *	could not be created.  The values returned are pointers to
 *	the blocks allowing access to the matrix, the model, and 
 *	its effect in the display map from a central point. For more
 *	details on the way this hierarchy functions Refer to
 *	Section 7.6 in Computer Graphics, Principles and Practice, 
 *	Foley, VanDam, ect.
 *
 * 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 "/include/types.h"
#include "/include/matrix.h"
#include "/include/database.h"
#include "/include/displaymap.h"

static ULONG curnumobjects;   
static struct Action *roots[MAXROOTOBJECTS];
       
extern struct Model *models[MAXNUMMODELS];
extern struct Object *objects[MAXNUMOBJECTS];

void traverseobject(struct Action *,MATRIX);
void traverseactionblock(struct Action *,MATRIX,MATRIX);
void evaluatemodel(ULONG,ULONG,MATRIX);

	/* Clears the entire display map. */

void cleardisplaymap()

{
   ULONG i;
        
   curnumobjects=0;
   for(i=0;i<MAXROOTOBJECTS;i++) {
      if(roots[i] != NULL)
         removeroot(roots[i]);  
      roots[i]=NULL;
   }     
}    

	/* Creates a root action for a new object. */

struct Action *createrootaction(MATRIX transmatrix,ULONG model)

{
   struct Action *new;
   ULONG i;
   
   if (((new=(struct Action *)malloc(sizeof(struct Action))) == NULL)&&(curnumobjects < MAXROOTOBJECTS))
      return(NULL);
   if((new->lm=allocatematrix())==NULL) {
      free((void *)new);
      return(NULL);
   }   
   if((new->cmtm=allocatematrix())==NULL) {
      freematrix(new->lm);
      free((void *)new);
      return(NULL);
   }	
   if(model!=NOMODEL) {
      if((new->object=allocateobject(model))==NOOBJECT) {
	 freematrix(new->lm);
	 freematrix(new->cmtm);
	 free((void *)new);
	 return(NULL);
      }
   }   
   else
      new->object = NOOBJECT;
   new->transtype = SET;
   new->changed = TRUE;
   new->transmatrix = transmatrix;
   new->model = model;
   new->previous = NULL;
   new->next = NULL;
   new->subobject = NULL;
   i=0;
   while(roots[i]!=NULL) 
      i++;
   roots[i]=new;   
   curnumobjects++;
   return(new);
}           

	/* Inserts a new action block before the one specified in curaction. 
	   this block can be a subobject or an object on the same level. */

struct Action *createaction(struct Action *curaction,MATRIX transmatrix,UBYTE transtype,ULONG model,UBYTE type)

{
   struct Action *new;
     
   if((new=(struct Action *)malloc(sizeof(struct Action))) == NULL)
      return(NULL);
   if((new->lm=allocatematrix())==NULL) {
      free((void *)new);
      return(NULL);
   }   
   if((new->cmtm=allocatematrix())==NULL) {
      freematrix(new->lm);
      free((void *)new);
      return(NULL);
   }   
   if(model!=NOMODEL) {
      if((new->object=allocateobject(model))==NOOBJECT) {
	 freematrix(new->lm);
	 freematrix(new->cmtm);
	 free((void *)new);
	 return(NULL);
      }
   }   
   else
      new->object = NOOBJECT;
   new->transtype = transtype;
   new->changed = TRUE;
   new->transmatrix = transmatrix;
   new->model = model;
   new->previous = curaction;
   if(type==CUROBJECT) {
      new->next = curaction->next;
      curaction->next = new;
      new->subobject = NULL;
   }
   else {
      new->subobject = curaction->subobject;
      curaction->subobject = new;
      new->next = NULL;
   }
   return(new);          
}                   
    
	/* Removes an entire object. */ 
    
void removeroot(struct Action *root)

{        
   deallocateobject(root->object);     
   freematrix(root->lm);
   freematrix(root->cmtm);
   if(root->subobject != NULL)
      removeaction(root->subobject);
   if(root->next != NULL)
      removeaction(root->next);
   free((void *)root);      
}            
        
	/* Removes an action block and any action blocks below it. */
	
void removeaction(struct Action *curaction)

{	
   deallocateobject(curaction->object);
   freematrix(curaction->lm);
   freematrix(curaction->cmtm);
   if(curaction->subobject != NULL)
      removeaction(curaction->subobject);
   if(curaction->next != NULL)
      removeaction(curaction->next);
   free((void *)curaction);     
}

	/* Begins traversal of the entire display tree. */

void traversedisplaymap()

{
   ULONG i;
   MATRIX gm;     
        
   gm=allocatematrix();     
   for(i=0;i<MAXROOTOBJECTS;i++) {
      if(roots[i] != NULL) {
         identitymatrix(gm);
         traverseobject(roots[i],gm);
      }
   }
   freematrix(gm);   
}            

	/* Starts traversal of a new object or subobject. */

void traverseobject(struct Action *cur,MATRIX gm)

{  
   if(cur->changed==TRUE) {
      if((cur->transmatrix!=NULL)&&((cur->transtype==SET)||(cur->transtype==APPEND)))
         copymatrix(cur->transmatrix,cur->lm);
      else 
         identitymatrix(cur->lm);
      multmatrix(cur->lm,gm,cur->cmtm);
      if(cur->model!=NOMODEL)
         evaluatemodel(cur->model,cur->object,cur->cmtm);
      if(cur->next!=NULL)
         cur->next->changed=TRUE;
      if(cur->subobject!=NULL)
         cur->subobject->changed=TRUE;
      cur->changed=FALSE;   
   } 
   if(cur->subobject!=NULL)
      traverseobject(cur->subobject,cur->cmtm);        
   if(cur->next!=NULL)
      traverseactionblock(cur->next,cur->lm,gm);  
}

	/* Traverses a single action block. */

void traverseactionblock(struct Action *cur,MATRIX lm,MATRIX gm)

{
   if(cur->changed==TRUE) {
      switch(cur->transtype) {
         case SET    : copymatrix(cur->transmatrix,cur->lm);
                       break;
         case APPEND : multmatrix(cur->transmatrix,lm,cur->lm);
                       break;
         case NONE   : identitymatrix(cur->lm);
                       break;
      }  
      multmatrix(cur->lm,gm,cur->cmtm);
      if(cur->model!=NOMODEL)
         evaluatemodel(cur->model,cur->object,cur->cmtm);
      if(cur->next!=NULL)
         cur->next->changed=TRUE;
      if(cur->subobject!=NULL)
         cur->subobject->changed=TRUE;
      cur->changed=FALSE;   
   } 
   if(cur->subobject!=NULL)
      traverseobject(cur->subobject,cur->cmtm);        
   if(cur->next!=NULL)
      traverseactionblock(cur->next,cur->lm,gm);  
}  

	/* Transform model verticies to object verticies (world coords). */

void evaluatemodel(ULONG model,ULONG object,MATRIX transmatrix)

{
   ULONG i;
   	
   for(i=0;i<(models[model]->numvert*4);i+=4)
      multvecandmat(models[model]->verticies+i,transmatrix,objects[object]->verticies+i);     	
}              
