Source/Objects.c

/*********************************/
/*    OBJECT MANAGER             */
/* By Brian Greenstone           */
/*********************************/
 
 
/***************/
/* EXTERNALS   */
/***************/
#include <QD3D.h>
#include <QD3DGeometry.h>
#include <QD3DGroup.h>
#include <QD3DMath.h>
#include <QD3DTransform.h>
 
#include "myglobals.h"
#include "objects.h"
#include "misc.h"
#include "qd3d_support.h"
 
/****************************/
/*    CONSTANTS             */
/****************************/
 
#define INVALID_NODE_FLAG   0xffffffffL         // put into CType when node is deleted
 
 
/**********************/
/*     VARIABLES      */
/**********************/
 
                                            // OBJECT LIST
ObjNode     *gFirstNodePtr;
                    
ObjNode     *gCurrentNode,*gMostRecentlyAddedNode;
                                        
NewObjectDefinitionType gNewObjectDefinition;
 
TQ3Point3D  gCoord;
 
 
/************************ INIT OBJECT MANAGER **********************/
 
void InitObjectManager(void)
{
    gCurrentNode = nil;
    gFirstNodePtr = nil;                                    // no node yet
    
}
 
 
/*********************** MAKE NEW OBJECT ******************/
//
// MAKE NEW OBJECT & RETURN PTR TO IT
//
// The linked list is sorted from LARGEST z to smallest!
//
 
ObjNode *MakeNewObject(NewObjectDefinitionType *newObjDef)
{
ObjNode *newNodePtr,*scanNodePtr,*reNodePtr;
long    slot;
    
    slot = newObjDef->slot;
    
                /* INITIALIZE NEW NODE */
    
    newNodePtr = (ObjNode *)AllocPtr(sizeof(ObjNode));
    if (newNodePtr == nil)
        DoFatalAlert("\pMakeNewObject: Alloc Ptr failed!");
 
    newNodePtr->SortSlot = slot;
 
    newNodePtr->Type = newObjDef->type;
    newNodePtr->MoveCall = newObjDef->moveCall;     // save move routine
    newNodePtr->Genre = newObjDef->genre;
    newNodePtr->Coord = newObjDef->coord;
    newNodePtr->ModeFlags = newObjDef->flags|OBJ_MODE_MOVE;
    newNodePtr->RotX =
    newNodePtr->RotY = 
    newNodePtr->RotZ = 0;
    newNodePtr->ScaleX =
    newNodePtr->ScaleY = 
    newNodePtr->ScaleZ = 1.0;
        
    newNodePtr->BaseGroup = nil;
        
    
        
                    /* FIND INSERTION PLACE FOR NODE */
                    
    if (gFirstNodePtr == nil)                           // special case only entry
    {
        gFirstNodePtr = newNodePtr;
        newNodePtr->PrevNode = nil;
        newNodePtr->NextNode = nil;
    }
    else
    if (slot < gFirstNodePtr->SortSlot)             // INSERT AS FIRST NODE
    {
        newNodePtr->PrevNode = nil;                 // no prev
        newNodePtr->NextNode = gFirstNodePtr;       // next pts to old 1st
        gFirstNodePtr->PrevNode = newNodePtr;       // old pts to new 1st
        gFirstNodePtr = newNodePtr;
    }
    else
    {
        reNodePtr = gFirstNodePtr;
        scanNodePtr = gFirstNodePtr;
            
        while (scanNodePtr != nil)
        {
            if (slot < scanNodePtr->SortSlot)           // INSERT IN MIDDLE HERE
            {
                newNodePtr->NextNode = scanNodePtr;
                newNodePtr->PrevNode = reNodePtr;
                reNodePtr->NextNode = newNodePtr;
                scanNodePtr->PrevNode = newNodePtr;         
                goto out;
            }
            reNodePtr = scanNodePtr;
            scanNodePtr=(ObjNode *)scanNodePtr->NextNode;   // try next node
        } 
    
        newNodePtr->NextNode = nil;                         // TAG TO END
        newNodePtr->PrevNode = reNodePtr;
        reNodePtr->NextNode = newNodePtr;       
    }
 
out:
    gMostRecentlyAddedNode = newNodePtr;                    // remember this
    return(newNodePtr);
}
 
 
/*******************************  MOVE OBJECTS **************************/
 
void MoveObjects(void)
{
ObjNode     *thisNodePtr;
    
            
    if (gFirstNodePtr == nil)                               // see if there are any objects
        return;
 
    thisNodePtr = gFirstNodePtr;
    
    do
    {
                    /* NEXT TRY TO MOVE IT */
                    
        if ((thisNodePtr->ModeFlags & OBJ_MODE_MOVE) && (thisNodePtr->MoveCall != nil))
        {
            gCurrentNode = thisNodePtr;                     // set current object node
            thisNodePtr->MoveCall();                        // call object's move routine
        }
            
        thisNodePtr = (ObjNode *)thisNodePtr->NextNode;     // next node
    }
    while (thisNodePtr != nil);
 
}
 
/**************************** DRAW OBJECTS ***************************/
 
void DrawObjects(QD3DSetupOutputType *viewInfo)
{
ObjNode     *theNode;
TQ3Status   myStatus;
 
    if (gFirstNodePtr == nil)               // see if there are any objects
        return;
 
    theNode = gFirstNodePtr;
 
    
                    /* MAIN NODE TASK LOOP */
            
    do
    {
        if (theNode->BaseGroup)             // see if group exists
        {               
            myStatus = Q3Object_Submit(theNode->BaseGroup,viewInfo->viewObject);
            if ( myStatus == kQ3Failure )
                DoFatalAlert("\p Drawing Q3Object_Submit Failed!");
        }
        theNode = (ObjNode *)theNode->NextNode;
    }while (theNode != nil);
}
 
 
/******************** DELETE ALL OBJECTS ********************/
 
void DeleteAllObjects(void)
{
    while (gFirstNodePtr != nil)
        DeleteObject(gFirstNodePtr);
}
 
 
/************************ DELETE OBJECT ****************/
 
void DeleteObject(ObjNode   *theNode)
{
ObjNode *tempNode;
 
    if (theNode == nil)                             // see if passed a bogus node
        return;
 
            /* PURGE SPECIAL DATA */
            
    if(theNode->BaseGroup)
    {
        Q3Object_Dispose(theNode->BaseGroup);
        theNode->BaseGroup = nil;   
    }
            
                    /* DO NODE SWITCHING */
 
    if (theNode->PrevNode == nil)                   // special case 1st node
    {
        gFirstNodePtr = (ObjNode *)theNode->NextNode;       
        tempNode = (ObjNode *)theNode->NextNode;
        tempNode->PrevNode = nil;
    }
    else if (theNode->NextNode == nil)              // special case last node
    {
        tempNode = (ObjNode *)theNode->PrevNode;
        tempNode->NextNode = nil;
    }
    else                                            // generic middle deletion
    {
        tempNode = (ObjNode *)theNode->PrevNode;
        tempNode->NextNode = theNode->NextNode;
        tempNode = (ObjNode *)theNode->NextNode;
        tempNode->PrevNode = theNode->PrevNode;
    }
    
    DisposePtr((Ptr)theNode);       
}
 
 
/********************** GET OBJECT INFO *********************/
 
void GetObjectInfo(ObjNode *theNode)
{
    gCoord = theNode->Coord;
}
 
/********************** UPDATE OBJECT *********************/
 
void UpdateObject(ObjNode *theNode)
{
    theNode->Coord = gCoord;
}
 
 
/************* MAKE NEW DISPLAY GROUP OBJECT *************/
 
ObjNode *MakeNewDisplayGroupObject(NewObjectDefinitionType *newObjDef)
{
ObjNode *newObj;
 
    newObjDef->genre = DISPLAY_GROUP_GENRE;
 
    newObj = MakeNewObject(newObjDef);      
    if (newObj == nil)
        return(nil);
 
    CreateBaseGroup(newObj);
    return(newObj);
}
 
 
/************************* ADD GEOMETRY TO DISPLAY GROUP OBJECT ***********************/
//
// Attaches a geometry object to the BaseGroup object. MakeNewDisplayGroupObject must have already been
// called which made the group & transforms.
//
 
void AttachGeometryToDisplayGroupObject(ObjNode *theNode, TQ3GeometryObject geometry)
{
TQ3GroupPosition    groupPosition;
 
    groupPosition = Q3Group_AddObject(theNode->BaseGroup,geometry);
    if (groupPosition == nil)
        DoFatalAlert("\pError: AttachGeometryToDisplayGroupObject");
}
 
 
/***************** CREATE BASE GROUP **********************/
//
// the base group contains the base translation/rotation transforms
// plus the link to the heirarchial chain of joint objects if its a skeleton object.
//
// NOTE: this is not only used for skeleton objects, but also for display group objects!!!
//
 
void CreateBaseGroup(ObjNode *theNode)
{
TQ3GroupPosition        myGroupPosition;
TQ3Matrix4x4            transMatrix,scaleMatrix;
TQ3TransformObject      transObject;
 
 
    if (theNode->BaseGroup)                     // nuke old one
        Q3Object_Dispose(theNode->BaseGroup);
        
 
                /* CREATE THE GROUP OBJECT */
                
    theNode->BaseGroup = Q3DisplayGroup_New();
    if (theNode->BaseGroup == nil)
        DoFatalAlert("\p Couldnt allocate BaseGroup!");
 
 
                    /* SETUP BASE MATRIX */
            
    Q3Matrix4x4_SetRotate_XYZ(&theNode->BaseTransformMatrix,        // init rotation matrix
                                 theNode->RotX, theNode->RotY,
                                 theNode->RotZ);
 
    Q3Matrix4x4_SetTranslate(&transMatrix, theNode->Coord.x, theNode->Coord.y,  // make translate matrix
                             theNode->Coord.z);
 
    Q3Matrix4x4_Multiply(&theNode->BaseTransformMatrix,             // mult rot & trans matrices
                         &transMatrix,
                         &theNode->BaseTransformMatrix);
 
    Q3Matrix4x4_SetScale(&scaleMatrix, theNode->ScaleX,
                         theNode->ScaleY,
                         theNode->ScaleZ);
 
    Q3Matrix4x4_Multiply(&theNode->BaseTransformMatrix,             // mult rot & trans matrices
                         &scaleMatrix,
                         &theNode->BaseTransformMatrix);
 
 
                    /* CREATE A MATRIX XFORM */
 
    transObject = Q3MatrixTransform_New(&theNode->BaseTransformMatrix);
    myGroupPosition = Q3Group_AddObject(theNode->BaseGroup, transObject);   // add to base group
    if (myGroupPosition == 0)
        DoFatalAlert("\pQ3Group_AddObject Failed!");
    theNode->BaseTransformObject = transObject;                             // keep illegal ref
    Q3Object_Dispose(transObject);
 
 
}
 
/****************** UPDATE OBJECT TRANSFORMS *****************/
//
// This updates the skeleton object's base translate & rotate transforms
//
 
void UpdateObjectTransforms(ObjNode *theNode)
{
TQ3Matrix4x4    scaleMatrix;
 
 
                    /* RESET BASE MATRIX */
            
    Q3Matrix4x4_SetRotate_XYZ(&theNode->BaseTransformMatrix,        // init rotation matrix
                                 theNode->RotX, theNode->RotY,
                                 theNode->RotZ);
 
    TranslateObjectBaseMatrixByXYZ(theNode);
                            
                            
    Q3Matrix4x4_SetScale(&scaleMatrix, theNode->ScaleX,     // do scale
                         theNode->ScaleY,
                         theNode->ScaleZ);
    TransformObjectBaseMatrix(theNode,&scaleMatrix);
                            
    SetObjectTransformMatrix(theNode);
}
 
 
/***************** SET OBJECT TRANSFORM MATRIX *******************/
//
// This call simply resets the base transform object so that it uses the latest
// base transform matrix
//
 
void SetObjectTransformMatrix(ObjNode *theNode)
{
TQ3Status               error;
 
    error = Q3MatrixTransform_Set(theNode->BaseTransformObject,&theNode->BaseTransformMatrix);
    if (error != kQ3Success)
        DoFatalAlert("\pQ3MatrixTransform_Set Failed!");
}
 
 
/******************** TRANSLATE OBJECT BASE MATRIX BY X/Y/Z  *******************/
 
void TranslateObjectBaseMatrixByXYZ(ObjNode *theNode)
{
TQ3Matrix4x4            transMatrix;
 
    Q3Matrix4x4_SetTranslate(&transMatrix, theNode->Coord.x, theNode->Coord.y,  // make translate matrix
                             theNode->Coord.z);
                             
    TransformObjectBaseMatrix(theNode,&transMatrix);
}
 
 
 
/***************** TRANSFORM OBJECT BASE MATRIX ******************/
//
// This multiplies the current BaseTransformMatrix by the input transform matrix.
//
 
void TransformObjectBaseMatrix(ObjNode *theNode, TQ3Matrix4x4 *inMatrix)
{
    Q3Matrix4x4_Multiply(&theNode->BaseTransformMatrix, 
                         inMatrix,
                         &theNode->BaseTransformMatrix);
}