Source/SR_LinePipe.c

/******************************************************************************
 **                                                                          **
 **     Module:     SR_LinePipe.c                                            **
 **                                                                          **
 **                                                                          **
 **     Purpose:    Sample Renderer polygon edge pipeline code               **
 **                                                                          **
 **                                                                          **
 **                                                                          **
 **     Copyright (C) 1996 Apple Computer, Inc.  All rights reserved.        **
 **                                                                          **
 **                                                                          **
 *****************************************************************************/
#include <stdlib.h>
#include <assert.h>
 
#include "QD3D.h"
#include "QD3DErrors.h"
#include "QD3DMath.h"
 
#include "SR_Math.h"
#include "SR.h"
#include "SR_ClipUtilities.h"
 
 
/*===========================================================================*\
 *
 *  Routine:    SR_LinePipe()
 *
 *  Comments:   Handles lines as well as triangles.
 *
\*===========================================================================*/
 
TQ3Status SR_LinePipe(
    TSRPrivate          *srPrivate, 
    TQ3Point3D          *localVertices,
    long                numVertices,
    long                sizeOfLocalVertices,
    TQ3ColorRGB         *color,
    TQ3Vector3D         *normal,
    long                mode)
{
    TQ3Status           status;
    unsigned long       i;
    TQ3RationalPoint4D  *deviceVertices         = NULL;
    TQ3RationalPoint4D  *clippedVertices        = NULL;
    TQ3RationalPoint4D  *renderVertices         = NULL;
    long                clipFound, allOut;
    unsigned long       *clipFlags              = NULL;
    long                *clippedVerticesFlags   = NULL;
    LineFunction2D      lineFunction;
        
    /*
     *  Get the appropriate rasterization function for the lines
     *  (depth, whether window is clipped or not, etc.
     */
    lineFunction = ((TSRRasterFunctions *)
                        (srPrivate->currentRasterFunctions))->lineFunction;
    
    /*
     *  If we're in "DO_POLYGON" mode, then we have to consider whether
     *  we need to cull or not.
     */
    if (mode == DO_POLYGON) {
        assert(normal != NULL);
        
        /*
         *  We will attempt to do culling if the rank of the upper-left 3x3 
         *  submatrix of the local-to-world matrix is at least 2.  A rank of 3 
         *  means that volumes are transformed into volumes.  A rank of 2 means 
         *  that volumes are transformed onto a plane.  For a rank of 1 (line) or 
         *  0 (point), we bypass the culling stage and just render the line or 
         *  point.
         */
        if (srPrivate->backfacingStyle == kQ3BackfacingStyleRemove &&
            (srPrivate->normalLocalToWorldRank >= 2)) {
                        
            if (normal->x * normal->x + 
                normal->y * normal->y + 
                normal->z * normal->z < 1.e-10) {
                /* 
                 *  Normal vector's length is < 1.e-10 meaning the polygon is 
                 *  degenerate 
                 */
                return (kQ3Success);
            }
            
            /*
             *  If the local-to-world matrix has a rank of 2, then the eye
             *  in local coordinates is a vector even if the view is a 
             *  perspective transformation.
             */
            if ((srPrivate->cameraType == kQ3CameraTypeOrthographic) ||
                (srPrivate->normalLocalToWorldRank == 2)) {
 
                /* Orthographic projection */
                if ((normal->x * srPrivate->eyeVectorInLocalCoords.x + 
                     normal->y * srPrivate->eyeVectorInLocalCoords.y + 
                     normal->z * srPrivate->eyeVectorInLocalCoords.z < 0.0) ^
                    ((srPrivate->orientationStyle == 
                        kQ3OrientationStyleClockwise))) {
                    /* 
                     *  Backfacing, so cull it 
                     */
                    return (kQ3Success);
                }
            } else {
                TQ3Point3D              *point;
                TQ3Vector3D             eyeVector;
                
                /* Perspective projection */
                point = localVertices;
                eyeVector.x = srPrivate->eyePointInLocalCoords.x - point->x;
                eyeVector.y = srPrivate->eyePointInLocalCoords.y - point->y;
                eyeVector.z = srPrivate->eyePointInLocalCoords.z - point->z;
                if ((normal->x * eyeVector.x + 
                     normal->y * eyeVector.y + 
                     normal->z * eyeVector.z < 0.0) ^
                    ((srPrivate->orientationStyle == 
                        kQ3OrientationStyleClockwise))) {
                    /* 
                     *  Backfacing, so cull it 
                     */
                    return (kQ3Success);
                }
            }
        }
    }
 
    /*
     *  Status set to success - if a subsequent memory allocation fails, or a 
     *  called function fails, set to failure and bail out.
     */
    status = kQ3Success;
 
    /*
     *  Allocate device-coordinate vertices
     */
    deviceVertices = malloc(numVertices * sizeof(TQ3RationalPoint4D));
    if (deviceVertices == NULL) {
        status = kQ3Failure;
        goto bail;
    }
 
    /*
     *  Transform local-space line vertices to device space
     */
    Q3Point3D_To4DTransformArray(
        localVertices,
        &srPrivate->transforms.localToDC,  
        deviceVertices,
        numVertices,
        sizeOfLocalVertices, 
        sizeof(TQ3RationalPoint4D));
        
    /*
     *  renderVertices are those that will be rendered. If there is a clip,
     *  then we'll set this to the clipped vertices instead.
     */
    renderVertices = deviceVertices;
    
    /*
     *  Allocate a clip flag for each vertex. Bail if allocation fails.
     */
    clipFlags = malloc(numVertices * sizeof(unsigned long));
    if (clipFlags == NULL) {
        status = kQ3Failure;
        goto bail;
    }
 
    /*
     *  See if we have a clip
     */
    SRPointList_ClipTestVertices(
        deviceVertices, 
        clipFlags, 
        numVertices, 
        &srPrivate->clipPlanesInDC[0], 
        &clipFound, 
        &allOut, 
        sizeof(TQ3RationalPoint4D));
 
    if (allOut) {
        /*
         *  Return now, as nothing need be drawn
         */
        goto bail;
    }
    
    /*
     *  We've found a clip, so generate the clipped vertext list
     */
    if (clipFound) {
        long    numberOfClippedVertices;
 
        /*
         *  Allocate space for clipped vertices. There will be at most
         *  twice as many of these as there are in the original geometric
         *  primitive. Bail if allocation fails.
         */
        clippedVertices = 
            malloc((numVertices << 1) * sizeof(TQ3RationalPoint4D));
        if (clippedVertices == NULL) {
            status = kQ3Failure;
            goto bail;
        }
        
        /*
         *  Allocate space for clip flags for vertices. Bail if allocation
         *  fails.
         */
        clippedVerticesFlags = malloc((numVertices << 1) * sizeof(long));
        if (clippedVerticesFlags == NULL) {
            status = kQ3Failure;
            goto bail;
        }
 
        /*
         *  Generate the clipped vertex list
         */
        SRPointList_ClipVertices(
            deviceVertices, 
            sizeof(TQ3RationalPoint4D),
            clippedVertices, 
            sizeof(TQ3RationalPoint4D),
            clipFlags, 
            clippedVerticesFlags,
            numVertices, 
            &numberOfClippedVertices,
            &srPrivate->clipPlanesInDC[0],
            mode);
 
        if (numberOfClippedVertices == 0) {
            goto bail;
        }
        
        renderVertices = clippedVertices;
        numVertices = numberOfClippedVertices;
    }
 
    /*
     *  Divide by homogeneous coordinate, but only if
     *  we've NOT found a clip - in that case, the w divide has
     *  already been done...
     */
    if (!clipFound) {
        SRPointList_WDivide(
            renderVertices, 
            numVertices, 
            sizeof(TQ3RationalPoint4D));
    }       
 
#if DEBUGGING
    if (FLOAT_ROUND_TO_LONG_POSITIVE(renderVertices[0].x) < srPrivate->clipPlanesInDC[0]) {
        goto bail;      
    }
    if (FLOAT_ROUND_TO_LONG_POSITIVE(renderVertices[0].x) > srPrivate->clipPlanesInDC[1]) {
        goto bail;      
    }
    if (FLOAT_ROUND_TO_LONG_POSITIVE(renderVertices[0].y) < srPrivate->clipPlanesInDC[2]) {
        goto bail;      
    }
    if (FLOAT_ROUND_TO_LONG_POSITIVE(renderVertices[0].y) > srPrivate->clipPlanesInDC[3]) {
        goto bail;      
    }
    if (FLOAT_ROUND_TO_LONG_POSITIVE(renderVertices[1].x) < srPrivate->clipPlanesInDC[0]) {
        goto bail;      
    }
    if (FLOAT_ROUND_TO_LONG_POSITIVE(renderVertices[1].x) > srPrivate->clipPlanesInDC[1]) {
        goto bail;      
    }
    if (FLOAT_ROUND_TO_LONG_POSITIVE(renderVertices[1].y) < srPrivate->clipPlanesInDC[2]) {
        goto bail;      
    }
    if (FLOAT_ROUND_TO_LONG_POSITIVE(renderVertices[1].y) > srPrivate->clipPlanesInDC[3]) {
        goto bail;      
    }
#endif  /*  DEBUGGING  */
 
 
    /*
     *  Draw line segments between each pair of vertices, taking into
     *  account the clipping information where appropriate.
     */
    for (i = 0; i < numVertices - 1; i++) {
        if (!clipFound              || 
            (clippedVerticesFlags   && 
             clippedVerticesFlags[i] & SR_DRAW_NEXT)) {
            if ((*lineFunction)(
                    srPrivate,
                    (TQ3Point3D *)&renderVertices[i], 
                    (TQ3Point3D *)&renderVertices[i + 1], 
                    color) == kQ3Failure) {
                status = kQ3Failure;
                goto bail;
            }
        }
    }
 
    /*
     *  If we're in "polygon" mode, then we have to connect the last
     *  vertex with the first.
     */
    if (mode == DO_POLYGON) {
        if (!clipFound              || 
            (clippedVerticesFlags   && 
             clippedVerticesFlags[numVertices - 1] & SR_DRAW_NEXT)) {
            if ((*lineFunction)(
                    srPrivate,
                    (TQ3Point3D *)&renderVertices[numVertices - 1], 
                    (TQ3Point3D *)&renderVertices[0], 
                    color) == kQ3Failure) {
                status = kQ3Failure;
                goto bail;
            }
        }
    }
 
bail:
    /*
     *  Free up any allocated memory
     */
    if (deviceVertices != NULL) {
        free(deviceVertices);
    }
    
    if (clipFlags != NULL) {
        free(clipFlags);
    }
    
    if (clippedVertices != NULL) {
        free(clippedVertices);
    }
    
    if (clippedVerticesFlags != NULL) {
        free(clippedVerticesFlags);
    }
    
    /*
     *  Status may be success or failure, the latter being the case if 
     *  an allocation failed or if the line rasterization function returns
     *  failure.
     */
    return (status);
}