RAVE CommonCode/RAVE Utilities.cp

/*
    File:       RAVE Utilities.cp
 
    Contains:   RAVE Utilities defines a number of useful functions for working with RAVE,
                including functions to find the deepest GDevice, loading textures from a PICT
                resource, and so on. Essentially, it defines a number of pieces of common code
                that were useful to many of my projects.
 
    Written by: Timothy Carroll 
 
    Copyright:  Copyright ©1996-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/15/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
#include <fp.h>
#include "Rave Utilities.h"
#include "common stuff.h"
#include <Memory.h>
#include <Resources.h>
 
 
 
/*****************************************************************************
LOOKUP TABLES
 
I use SIN and COS in a number of places, and we don't need perfect precision,
so we generate a set of lookup tables on 0.5 degree increments.  Most of the
3D math routines use these lookups to generate matrices.
*****************************************************************************/
 
float gSinArray[721]; // [720] = [0]
float gCosArray[721]; // [720] = [0]
 
OSStatus InitializeLookups(void)
{
    int loop;
    
    for (loop = 0; loop < 720; loop++)
    {
        double angle = (loop * 0.5)/57.296;
        
        gSinArray[loop] = sin (angle);
        gCosArray[loop] = cos (angle);
    }
    
    gSinArray[720] = gSinArray[0];
    gCosArray[720] = gCosArray[0];
    
    return noErr;
}
 
 
 
/*****************************************************************************
RAVE ENGINE FUNCTIONS
    
FindTextureMappingEngine walks the list of available engines and returns
the first engine capable of Texture Mapping that will run on the device.
 
GetListOfEngines creates a handle filled with references to all engines
available to the application that run on the GDevice passed in the routine.
Pass in NULL to find all engines.
*****************************************************************************/
 
TQAEngine   *FindTextureMappingEngine (TQADevice *device)
{
    TQAEngine       *engine = NULL;
    TQAError        theQAErr = kQANoErr;
    unsigned long   features;
    
    engine = QADeviceGetFirstEngine (device);
    
    while (engine != NULL)
    {
        theQAErr = QAEngineGestalt (engine,
                                    kQAGestalt_OptionalFeatures, 
                                    &features);
                                    
        if ((theQAErr == kQANoErr) && (features & kQAOptional_Texture))
            return engine;
            
        engine = QADeviceGetNextEngine (device, engine);
    }
    
    // Failed to find an engine that supports texture mapping
    return NULL;
}
 
 
OSStatus    GetListOfEngines (GDHandle screen, Handle *list, long *number)
{
    OSStatus    theErr = noErr;
    TQAError    theQAErr = kQANoErr;
    
    long        loop, tempCount;
    Handle      tempList;
    
    // We need a device structure to build the list of engines.
    // We only need to set it up if we were passed a GDevice.
    TQADevice   device, *devicePtr = NULL;
    TQAEngine   *engine;
    
    if (screen)
    {
        device.deviceType = kQADeviceGDevice;
        device.device.gDevice = screen;
        devicePtr = &device;
    }
    
// We walk the list of engines twice.  First, we count the number
// of engines, and allocate a handle large enough to hold the
// engine references.  Second, we walk the list a second time and
// stuff the handle.
 
// The software engine should always be available, so I consider
// a count of 0 to be an error.
 
    tempCount = 0;
    engine = QADeviceGetFirstEngine (devicePtr);
    while (engine != NULL)
    {
        tempCount++;
        engine = QADeviceGetNextEngine (devicePtr, engine);
    }
    
    if (tempCount == 0)
        SIGNAL_ERROR ("\pERROR: No Engines available for this GDevice")
     
// Allocate a handle for the list.
    tempList = NewHandle (tempCount * sizeof (TQAEngine *));
    theErr = MemError();
    FAIL_OSERR (theErr, "\pError:Unable to allocate a handle for the engine list")
    FAIL_NIL(tempList, "\pError:Unable to allocate a handle for the engine list")
 
// Walk the list again and stuff the references into the handle
    engine = QADeviceGetFirstEngine (devicePtr);
    
    for (loop = 0; loop < tempCount; loop++)
    {
        (*(TQAEngine ***)tempList)[loop] = engine;
        engine = QADeviceGetNextEngine (devicePtr, engine);
    }
    
// Everything went successfully, fill in the list and count and return no error;
    *list = tempList;
    *number = tempCount;
    
    goto cleanup;
 
error:
// If an error occurred, clear the list and return the error.
    if (theErr == noErr)
        theErr = paramErr;
        
    if (tempList)
        DisposeHandle (tempList);   
        
    *list = NULL;
    *number = 0;
 
cleanup:
    
    return theErr;
}
 
 
TQATexture  *LoadTextureFromPictResource (TQAEngine *engine, short pictID)
{
    TQAError        theQAErr = kQANoErr;
    OSStatus        theErr = noErr;
 
    TQAImage        image;
    TQATexture      *texture= NULL;
    
    PicHandle       picture = NULL;
    Rect            pictRect;
    GWorldPtr       pictWorld = NULL;
    PixMapHandle    pictPix = NULL;
    
    CGrafPtr        savePort = NULL;
    GDHandle        saveDevice = NULL;
    
// Load the texture from the resource;
    
    picture = GetPicture (pictID);
    theErr = ResError();
    FAIL_NIL (picture, "\pERROR: Failed to load the texture resource")
    FAIL_OSERR (theErr,"\pERROR: Failed to load the texture resource")
    
// for now, we assume that the PICT is a valid size -- that is,
// it should be a power of 2 in both dimensions.
    
// Create a 32-bit GWorld to hold the pix map data.
    pictRect = (**picture).picFrame;
    OffsetRect (&pictRect, -pictRect.left, -pictRect.top);
    
    theErr = NewGWorld(&pictWorld, 32, &pictRect, NULL, NULL, NULL);
    FAIL_OSERR (theErr, "\pERROR:Couldn't create the texture gworld")
    FAIL_NIL (pictWorld,"\pERROR:Couldn't create the texture gworld")
    
    pictPix = GetGWorldPixMap (pictWorld);
    FAIL_NIL (pictPix, "\pERROR:Couldn't retrieve the texture PixMap")
    FAIL_FALSE (LockPixels (pictPix),"\pERROR:Couldn't lock the texture PixMap")
    
// Draw the picture into the GWorld
    GetGWorld (&savePort, &saveDevice);
    SetGWorld (pictWorld, NULL);
    DrawPicture(picture, &pictRect);
    SetGWorld (savePort, saveDevice);
 
// Fill in the image structure to describe our GWorld and create the texture
    image.width = pictRect.right;
    image.height = pictRect.bottom;
    image.rowBytes = (**pictPix).rowBytes & 0x3FFF;
    image.pixmap = GetPixBaseAddr (pictPix);
    
// Create the texture and detach it so that the engine is responsible for it
    theQAErr = QATextureNew (engine,kQATexture_None ,kQAPixel_RGB32,&image, &texture);
    if (theQAErr != kQANoErr) SIGNAL_ERROR ("\pError: RAVE failed to allocate the texture")
    
    theQAErr = QATextureDetach (engine, texture); 
    if (theQAErr != kQANoErr) SIGNAL_ERROR ("\pError: RAVE failed to detach the texture")
    
// We're done -- jump to the cleanup code and return.
    goto cleanup;
    
error:
// We hit an error, so we need to make sure we cleanup 
// everything and return a NULL texture
    if (texture != NULL)
        QATextureDelete (engine, texture);
    texture = NULL;
    
cleanup:
    if (picture != NULL)
        ReleaseResource ((Handle) picture);
    if (pictWorld != NULL)
        DisposeGWorld (pictWorld);
    
    return texture;
}