DZDisplay.c

/*
 *  File:       DZDisplay.c
 *
 *  Contents:   Handles the window and its contents.
 *
 *  Copyright © 1996 Apple Computer, Inc.
 */
 
#ifndef USE_DRAW_SPROCKET
    #define USE_DRAW_SPROCKET       1
#endif
 
#if USE_DRAW_SPROCKET
    #define USE_PIXMAP_DRAW_CONTEXT 1
#endif
 
#include <assert.h>
 
#include <Fonts.h>
#include <Quickdraw.h>
#include <SegLoad.h>
#include <Windows.h>
 
#include <QD3D.h>
#include <QD3DCamera.h>
#include <QD3DDrawContext.h>
#include <QD3DGroup.h>
#include <QD3DLight.h>
#include <QD3DMath.h>
#include <QD3DRenderer.h>
#include <QD3DShader.h>
#include <QD3DView.h>
 
#include "DZDisplay.h"
#include "DZGame.h"
#include "DZInput.h"
#include "DZResource.h"
 
#if USE_DRAW_SPROCKET
    #include "DrawSprocket.h"
#endif
 
 
#if USE_DRAW_SPROCKET
    static DSpContextReference  gDisplayContext         = NULL;
#else
    static WindowPtr        gDisplayWindow              = NULL;
#endif
 
static Boolean              gDisplayActive              = false;
 
static PicHandle            gDisplayStartScreen         = NULL;
 
static TQ3ViewObject        gDisplayView                = NULL;
static TQ3DrawContextObject gDisplayDrawContext         = NULL;
static TQ3RendererObject    gDisplayRenderer            = NULL;
static TQ3CameraObject      gDisplayCamera              = NULL;
static TQ3GroupObject       gDisplayLightGroup          = NULL;
static TQ3LightObject       gDisplayAmbientLight        = NULL;
static TQ3LightObject       gDisplayDirectionalLight    = NULL;
static TQ3LightObject       gDisplayPointLight          = NULL;
static TQ3ShaderObject      gDisplayIllumination        = NULL;
 
//¥ To get around an Interactive Renderer bug wherein Q3Renderer_Sync
//¥ crashes when called on a renderer that has not rendered.
static Boolean              gDisplayHasRendered         = false;
 
 
static void Display_RenderImage(
    void);
    
static void Display_BeginQD(
    void);
    
static void Display_EndQD(
    void);
    
static void Display_GetImageArea(
    TQ3Area*                outArea);
 
static float Display_GetImageAspectRatio(
    void);
 
 
/* =============================================================================
 *      Display_Init (external)
 *
 *  Creates the display window, initializes QD3D view etc. to draw in it.
 * ========================================================================== */
void Display_Init(
    void)
{
    OSStatus                        err = noErr;
    TQ3ViewAngleAspectCameraData    viewAngleCameraData;
    TQ3PointLightData               pointLightData;
    TQ3ColorRGB                     white = {1.0, 1.0, 1.0};
    
    // Grab the start screen
    gDisplayStartScreen = GetPicture(kPictID_StartScreen);
    
    // Create the view
    gDisplayView = Q3View_New();
    
    // Set up where to draw
    #if USE_DRAW_SPROCKET
    {
        DSpContextAttributes attributes;
        DSpContextAttributes actual;
        
        DSpStartup();
        
        // Create the display
        attributes.frequency                = 0;
        attributes.displayWidth             = 512;
        attributes.displayHeight            = 384;
        attributes.reserved1                = 0;
        attributes.reserved2                = 0;
        attributes.colorNeeds               = kDSpColorNeeds_Request;
        attributes.colorTable               = NULL;
        attributes.contextOptions           = 0; //¥ kDSpContextOption_QD3DAccel
        attributes.backBufferDepthMask      = kDSpDepthMask_32 | kDSpDepthMask_16;
        attributes.displayDepthMask         = kDSpDepthMask_32 | kDSpDepthMask_16;
        attributes.backBufferBestDepth      = 16;
        attributes.displayBestDepth         = 16;
        attributes.pageCount                = 1;
        attributes.gameMustConfirmSwitch    = false;
        attributes.reserved3[0]             = 0;
        attributes.reserved3[1]             = 0;
        attributes.reserved3[2]             = 0;
        attributes.reserved3[3]             = 0;
        
        err = DSpFindBestContext(&attributes, &gDisplayContext);
        if (err != noErr || gDisplayContext == NULL)
        {
            //¥ PUT UP AN ALERT INDICATING THAT THERE ISN'T A
            //¥ DEVICE GOOD ENOUGH TO RUN ON.
            ExitToShell();
        }
        
        DSpContext_Reserve(gDisplayContext, &attributes);
        DSpContext_FadeGammaOut(NULL, NULL);
        DSpContext_SetState(gDisplayContext, kDSpContextState_Active);
        
        Display_DrawContents();
        
        DSpContext_FadeGammaIn(NULL, NULL);
        DSpContext_GetAttributes(gDisplayContext, &actual);
        
        #if USE_PIXMAP_DRAW_CONTEXT
        {
            GWorldPtr                       theGWorld;
            PixMapHandle                    pixMapHandle;
            TQ3PixmapDrawContextData        pixmapDrawContextData;
            
            // Create the pixmap draw context with the image pointing to our back buffer
            DSpContext_GetBackBuffer(gDisplayContext, kDSpBufferKind_Normal, &theGWorld);
            pixMapHandle = GetGWorldPixMap(theGWorld);
            
            pixmapDrawContextData.drawContextData.clearImageMethod  = kQ3ClearMethodWithColor;
            pixmapDrawContextData.drawContextData.clearImageColor.a = 1.0;
            pixmapDrawContextData.drawContextData.clearImageColor.r = 0.0;
            pixmapDrawContextData.drawContextData.clearImageColor.g = 0.0;
            pixmapDrawContextData.drawContextData.clearImageColor.b = 0.0;
            pixmapDrawContextData.drawContextData.paneState         = kQ3False;
            pixmapDrawContextData.drawContextData.maskState         = kQ3False;
            pixmapDrawContextData.drawContextData.doubleBufferState = kQ3True;
            
            pixmapDrawContextData.pixmap.image      = GetPixBaseAddr(pixMapHandle);
            pixmapDrawContextData.pixmap.width      = attributes.displayWidth;
            pixmapDrawContextData.pixmap.height     = attributes.displayHeight;
            pixmapDrawContextData.pixmap.rowBytes   = (*pixMapHandle)->rowBytes & 0x00003FFF;
            pixmapDrawContextData.pixmap.pixelSize  = actual.displayBestDepth;
            pixmapDrawContextData.pixmap.bitOrder   = kQ3EndianBig;
            pixmapDrawContextData.pixmap.byteOrder  = kQ3EndianBig;
            
            switch (pixmapDrawContextData.pixmap.pixelSize)
            {
                case 16:
                    pixmapDrawContextData.pixmap.pixelType = kQ3PixelTypeRGB16;
                break;
                
                case 32:
                    pixmapDrawContextData.pixmap.pixelType = kQ3PixelTypeRGB32;
                break;
                
                default:
                    assert(0);
            }
            
            gDisplayDrawContext = Q3PixmapDrawContext_New(&pixmapDrawContextData);
        }
        #else
        {
            TQ3MacDrawContextData           macDrawContextData;
            
            // Create the mac draw context with the port pointing to our back buffer
            macDrawContextData.drawContextData.clearImageMethod     = kQ3ClearMethodWithColor;
            macDrawContextData.drawContextData.clearImageColor.a    = 1.0;
            macDrawContextData.drawContextData.clearImageColor.r    = 0.0;
            macDrawContextData.drawContextData.clearImageColor.g    = 0.0;
            macDrawContextData.drawContextData.clearImageColor.b    = 0.0;
            macDrawContextData.drawContextData.maskState            = kQ3False;
            macDrawContextData.drawContextData.paneState            = kQ3False;
            macDrawContextData.drawContextData.doubleBufferState    = kQ3True;
            macDrawContextData.library                              = kQ3Mac2DLibraryNone;
            macDrawContextData.viewPort                             = NULL;
            macDrawContextData.grafPort                             = NULL;
            
            GetDisplayBackBuffer(gDisplayContext, &macDrawContextData.window);
        
            gDisplayDrawContext = Q3MacDrawContext_New(&macDrawContextData);
        }
        #endif
    }
    #else
    {
        TQ3MacDrawContextData           macDrawContextData;
        
        // Create the window
        gDisplayWindow = GetNewCWindow(kWindID_Display, NULL, (WindowPtr)(-1));
        
        // Create the draw context
        macDrawContextData.drawContextData.clearImageMethod     = kQ3ClearMethodWithColor;
        macDrawContextData.drawContextData.clearImageColor.a    = 1.0;
        macDrawContextData.drawContextData.clearImageColor.r    = 0.0;
        macDrawContextData.drawContextData.clearImageColor.g    = 0.0;
        macDrawContextData.drawContextData.clearImageColor.b    = 0.0;
        macDrawContextData.drawContextData.maskState            = kQ3False;
        macDrawContextData.library                              = kQ3Mac2DLibraryNone;
        macDrawContextData.drawContextData.paneState            = kQ3True;
        macDrawContextData.drawContextData.doubleBufferState    = kQ3True;
        macDrawContextData.window                               = (CGrafPtr) gDisplayWindow;
        
        Display_GetImageArea(&macDrawContextData.drawContextData.pane);
    
        gDisplayDrawContext = Q3MacDrawContext_New(&macDrawContextData);
    }
    #endif
    
    Q3View_SetDrawContext(gDisplayView, gDisplayDrawContext);
    
    // Create the renderer
    #if 1
        gDisplayRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive);
        Q3InteractiveRenderer_SetDoubleBufferBypass(gDisplayRenderer, kQ3True);
    #else
        gDisplayRenderer = Q3Renderer_NewFromType(kQ3RendererTypeWireframe);
    #endif
    
    Q3View_SetRenderer(gDisplayView, gDisplayRenderer);
    
    // Create the camera
    viewAngleCameraData.cameraData.placement.cameraLocation.x   = 0.0;
    viewAngleCameraData.cameraData.placement.cameraLocation.y   = 0.0;
    viewAngleCameraData.cameraData.placement.cameraLocation.z   = 0.0;
    viewAngleCameraData.cameraData.placement.pointOfInterest.x  = 1.0;
    viewAngleCameraData.cameraData.placement.pointOfInterest.y  = 0.0;
    viewAngleCameraData.cameraData.placement.pointOfInterest.z  = 0.0;
    viewAngleCameraData.cameraData.placement.upVector.x         = 0.0;
    viewAngleCameraData.cameraData.placement.upVector.y         = 1.0;
    viewAngleCameraData.cameraData.placement.upVector.z         = 0.0;
    viewAngleCameraData.cameraData.range.hither                 = 0.2;
    viewAngleCameraData.cameraData.range.yon                    = 200.0;
    viewAngleCameraData.cameraData.viewPort.origin.x            = -1.0;
    viewAngleCameraData.cameraData.viewPort.origin.y            = 1.0;
    viewAngleCameraData.cameraData.viewPort.width               = 2.0;
    viewAngleCameraData.cameraData.viewPort.height              = 2.0;
    viewAngleCameraData.fov                                     = 1.2;
    viewAngleCameraData.aspectRatioXToY                         = Display_GetImageAspectRatio();
 
    gDisplayCamera = Q3ViewAngleAspectCamera_New(&viewAngleCameraData);
    
    Q3View_SetCamera(gDisplayView, gDisplayCamera);
    
    // Create the light group
    gDisplayLightGroup = Q3LightGroup_New();
    Q3View_SetLightGroup(gDisplayView, gDisplayLightGroup);
    
    // Create the point light
    pointLightData.lightData.isOn       = kQ3True;
    pointLightData.lightData.brightness = 5.0;
    pointLightData.lightData.color      = white;
    pointLightData.castsShadows         = kQ3True;
    pointLightData.attenuation          = kQ3AttenuationTypeInverseDistance;
    pointLightData.location.x           = 0.0;
    pointLightData.location.y           = 0.0;
    pointLightData.location.z           = 0.0;
    
    gDisplayPointLight = Q3PointLight_New(&pointLightData);
    
    Q3Group_AddObject(gDisplayLightGroup, gDisplayPointLight);
    
    // Create the illumination shader
    #if 1
        gDisplayIllumination = Q3PhongIllumination_New();
    #else
        gDisplayIllumination = Q3LambertIllumination_New();
    #endif
}
 
 
/* =============================================================================
 *      Display_Exit (external)
 *
 *  Takes down the window, disposes of the QD3D stuff.
 * ========================================================================== */
void Display_Exit(
    void)
{
    if (gDisplayHasRendered)
    {
//      Q3Renderer_Sync(gDisplayRenderer, gDisplayView);
        Q3View_Sync(gDisplayView);
    }
    
    #if USE_DRAW_SPROCKET
        if(gDisplayContext != NULL)
        {
            DSpContext_FadeGammaOut(NULL, NULL);
            DSpContext_Release(gDisplayContext);
            DSpContext_SetState(gDisplayContext, kDSpContextState_Active);
            DSpContext_FadeGammaIn(NULL, NULL);
            
            gDisplayContext = NULL;
        }
        
        DSpShutdown();
    #else
        if (gDisplayWindow != NULL)
        {
            DisposeWindow(gDisplayWindow);
            gDisplayWindow = NULL;
        }
    #endif
    
    if (gDisplayView != NULL)
    {
        Q3Object_Dispose(gDisplayView);
        gDisplayView = NULL;
    }
    
    if (gDisplayDrawContext != NULL)
    {
        Q3Object_Dispose(gDisplayDrawContext);
        gDisplayDrawContext = NULL;
    }
    
    if (gDisplayRenderer != NULL)
    {
        Q3Object_Dispose(gDisplayRenderer);
        gDisplayRenderer = NULL;
    }
    
    if (gDisplayCamera != NULL)
    {
        Q3Object_Dispose(gDisplayCamera);
        gDisplayCamera = NULL;
    }
    
    if (gDisplayLightGroup != NULL)
    {
        Q3Object_Dispose(gDisplayLightGroup);
        gDisplayLightGroup = NULL;
    }
    
    if (gDisplayAmbientLight != NULL)
    {
        Q3Object_Dispose(gDisplayAmbientLight);
        gDisplayAmbientLight = NULL;
    }
    
    if (gDisplayDirectionalLight != NULL)
    {
        Q3Object_Dispose(gDisplayDirectionalLight);
        gDisplayDirectionalLight = NULL;
    }
    
    if (gDisplayPointLight != NULL)
    {
        Q3Object_Dispose(gDisplayPointLight);
        gDisplayPointLight = NULL;
    }
    
    if (gDisplayIllumination != NULL)
    {
        Q3Object_Dispose(gDisplayIllumination);
        gDisplayIllumination = NULL;
    }
}
 
 
/* =============================================================================
 *      Display_Activate (external)
 *
 *  Handles activation and deactivation.
 * ========================================================================== */
void Display_Activate(
    Boolean         inActivate)
{
    if (gDisplayActive != inActivate)
    {
        gDisplayActive = inActivate;
        
        Input_Activate(inActivate);
        
        if (!gDisplayActive)
        {
            Game_Silence();
        }
        
        if (gDisplayHasRendered)
        {
//          Q3Renderer_Sync(gDisplayRenderer, gDisplayView);
            Q3View_Sync(gDisplayView);
        }
        
        Display_DrawGrow();
    }
}
 
 
/* =============================================================================
 *      Display_IsActive (external)
 *
 *  Returns true if the game is active.
 * ========================================================================== */
Boolean Display_IsActive(
    void)
{
    #if USE_DRAW_SPROCKET
        return true;
    #else
        return gDisplayActive;
    #endif
}
 
 
/* =============================================================================
 *      Display_DrawGrow (external)
 *
 *  Draws the grow box.
 * ========================================================================== */
void Display_DrawGrow(
    void)
{
#if !USE_DRAW_SPROCKET
    Rect                bounds;
    
    SetPort(gDisplayWindow);
    
    // Erase the horizontal part
    bounds = gDisplayWindow->portRect;
    bounds.top = bounds.bottom-15;
    
    EraseRect(&bounds);
    
    // Draw the grow box, but only the horizontal scroll stuff
    ClipRect(&bounds);
    DrawGrowIcon(gDisplayWindow);
    ClipRect(&gDisplayWindow->portRect);
#endif
}
 
 
/* =============================================================================
 *      Display_DrawContents (external)
 *
 *  Draws the contents of the window.
 * ========================================================================== */
void Display_DrawContents(
    void)
{
    TQ3Area         area;
    FontInfo        fontInfo;
    Rect            bounds;
    Rect            bounds1;
    
    static Str255 pausedString = "\pPaused";  //¥ SHOULD COME FROM RESOURCE
    
    switch (Game_GetState())
    {
        case kGameState_Playing:
            Display_RenderImage();
        break;
        
        case kGameState_Paused:
            Display_RenderImage();
            Display_BeginQD();
            Display_GetImageArea(&area);
            
            TextFont(kFontIDGeneva);
            TextFace(bold);
            TextSize(72);
            
            GetFontInfo(&fontInfo);
            
            MoveTo(
                area.min.x + 0.5*(area.min.x + area.max.x - StringWidth(pausedString)),
                area.min.y + 0.5*(area.min.y + area.max.y + fontInfo.ascent));
            
            ForeColor(redColor);
            DrawString(pausedString);
            ForeColor(blackColor);
            
            Display_EndQD();
        break;
        
        case kGameState_Stopped:
            Display_BeginQD();
            
            Display_GetImageArea(&area);
            
            bounds.top    = area.min.y;
            bounds.left   = area.min.x;
            bounds.bottom = area.max.y;
            bounds.right  = area.max.x;
            
            PaintRect(&bounds);
            
            bounds1 = (*gDisplayStartScreen)->picFrame;
            OffsetRect(
                    &bounds1,
                    ((bounds.right-bounds.left) - (bounds1.right-bounds1.left) >> 1) - bounds1.left,
                    ((bounds.bottom-bounds.top) - (bounds1.bottom-bounds1.top) >> 1) - bounds1.top);
            
            DrawPicture(gDisplayStartScreen, &bounds1);
            
            Display_EndQD();
        break;
    }
}
 
 
/* =============================================================================
 *      Display_RenderImage (internal)
 *
 *  Draws the 3D part of the window.
 * ========================================================================== */
void Display_RenderImage(
    void)
{
    Q3View_StartRendering(gDisplayView);
    do
    {
        // Set up the rendering state
        Q3InterpolationStyle_Submit(kQ3InterpolationStyleVertex, gDisplayView);
        Q3BackfacingStyle_Submit(kQ3BackfacingStyleFlip, gDisplayView);
        Q3FillStyle_Submit(kQ3FillStyleFilled, gDisplayView);
        Q3Object_Submit(gDisplayIllumination, gDisplayView);
        
        // Draw the game contents
        Game_Submit(gDisplayView);
    }
    while (Q3View_EndRendering(gDisplayView) == kQ3ViewStatusRetraverse);
    
    gDisplayHasRendered = true;
    
#if USE_DRAW_SPROCKET
    // we don't need to swap buffers because we're single buffered
#endif
}
 
 
/* =============================================================================
 *      Display_Resize (external)
 *
 *  Called after the window has changed size.
 * ========================================================================== */
void Display_Resize(
    void)
{
#if !USE_DRAW_SPROCKET
    TQ3Area                 imageArea;
    
    // Reset the clip
    ClipRect(&gDisplayWindow->portRect);
    
    // Change the draw context
    Display_GetImageArea(&imageArea);
    Q3DrawContext_SetPane(gDisplayDrawContext, &imageArea);
    
    // Change the camera aspect ratio
    Q3ViewAngleAspectCamera_SetAspectRatio(gDisplayCamera, Display_GetImageAspectRatio());
#endif
}
 
 
/* =============================================================================
 *      Display_GetWindow (external)
 *
 *  Returns our window pointer.
 * ========================================================================== */
WindowPtr Display_GetWindow(
    void)
{
#if USE_DRAW_SPROCKET
    return NULL;
#else
    return gDisplayWindow;
#endif
}
 
 
/* =============================================================================
 *      Display_SetViewerPosition (external)
 *
 *  Moves the camera and point light based on the given info.
 * ========================================================================== */
void Display_SetViewerPosition(
    const TQ3Point3D*       inPosition,
    const TQ3Vector3D*      inDirection,
    const TQ3Vector3D*      inUp)
{
    TQ3CameraPlacement      placement;
    
    assert(inPosition != NULL);
    assert(inDirection != NULL);
    assert(inUp != NULL);
    
    // Move the camera
    placement.cameraLocation    = *inPosition;
    placement.upVector          = *inUp;
    
    Q3Point3D_Vector3D_Add(inPosition, inDirection, &placement.pointOfInterest);
    
    Q3Camera_SetPlacement(gDisplayCamera, &placement);
    
    // Move the point light
    Q3PointLight_SetLocation(gDisplayPointLight, inPosition);
}
 
 
/* =============================================================================
 *      Display_GetViewerPosition (external)
 *
 *  Returns the current camera position.
 * ========================================================================== */
void Display_GetViewerPosition(
    TQ3Point3D*             outPosition,
    TQ3Vector3D*            outDirection,
    TQ3Vector3D*            outUp)
{
    TQ3CameraPlacement      placement;
    
    assert(outPosition != NULL);
    assert(outDirection != NULL);
    assert(outUp != NULL);
    
    Q3Camera_GetPlacement(gDisplayCamera, &placement);
    
    *outPosition = placement.cameraLocation;
    *outUp = placement.upVector;
    
    Q3Point3D_Subtract(
            &placement.pointOfInterest,
            &placement.cameraLocation,
            outDirection);
}
 
 
/* =============================================================================
 *      Display_GetImageArea (internal)
 *
 *  Sets outArea to the area occupied by the 3D image part of the window.
 * ========================================================================== */
void Display_GetImageArea(
    TQ3Area*                outArea)
{
    assert(outArea != NULL);
 
#if USE_DRAW_SPROCKET
    {
        DSpContextAttributes attributes;
        
        DSpContext_GetAttributes(gDisplayContext, &attributes);
        
        outArea->min.x = 0;
        outArea->max.x = attributes.displayWidth;
        outArea->min.y = 0;
        outArea->max.y = attributes.displayHeight;
    }
#else   
    outArea->min.x = gDisplayWindow->portRect.left;
    outArea->max.x = gDisplayWindow->portRect.right;
    outArea->min.y = gDisplayWindow->portRect.top;
    outArea->max.y = gDisplayWindow->portRect.bottom-15;
#endif
}
 
 
/* =============================================================================
 *      Display_GetImageAspectRatio (internal)
 *
 *  Returns the aspect ratio of the area occupied by the 3D image part of the
 *  window.
 * ========================================================================== */
float Display_GetImageAspectRatio(
    void)
{
    TQ3Area                 imageArea;
    
    Display_GetImageArea(&imageArea);
    
    return (imageArea.max.x-imageArea.min.x) / (imageArea.max.y-imageArea.min.y);
}
 
 
/* =============================================================================
 *      Display_BeginQD (internal)
 *
 *  Sets up for doing Quickdraw drawing.
 * ========================================================================== */
void Display_BeginQD(
    void)
{
    if (gDisplayHasRendered)
    {
//      Q3Renderer_Sync(gDisplayRenderer, gDisplayView);
        Q3View_Sync(gDisplayView);
    }
    
#if USE_DRAW_SPROCKET
    {
        CGrafPtr port;
        
        DSpContext_GetBackBuffer(gDisplayContext, kDSpBufferKind_Normal, &port);
        SetPort((GrafPtr) port);
    }
#else   
    SetPort(gDisplayWindow);
#endif
}
 
 
/* =============================================================================
 *      Display_EndQD (internal)
 *
 *  Undoes what Display_BeginQD did.
 * ========================================================================== */
void Display_EndQD(
    void)
{
#if USE_DRAW_SPROCKET
    // do nothing
#else   
    // do nothing
#endif
}