home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Archive / Graphics / QD3D / rollercoaster / Sources / Track.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  30.9 KB  |  1,003 lines  |  [TEXT/dosa]

  1. /*
  2.     File:        Track.c
  3.     
  4.     Contains:    Contains code to create our rollercoaster track
  5.     
  6.     Written by:    Scott Kuechle, based on original Gerbils code by Brian Greenstone
  7.  
  8.     Copyright:    © 1998 by Apple Computer, Inc. All rights reserved
  9.     
  10.     Change History (most recent first)
  11.     
  12.         <2>        9/28/98        rtm        made changes for Metrowerks compiler
  13.         <1>        9/01/98        srk        first file
  14.  
  15.  
  16. */
  17.  
  18. /************************************************************
  19. *                                                           *
  20. *    INCLUDE FILES                                          *
  21. *                                                           *
  22. *************************************************************/
  23.  
  24. #include "Track.h"
  25.  
  26.  
  27. /************************************************************
  28. *                                                           *
  29. *    FUNCTION PROTOTYPES                                    *
  30. *                                                           *
  31. *************************************************************/
  32.  
  33. static float Track_AmountToRotateNubs(TrackSectionType    *trackSectionList,
  34.                                     long             numTrackSections,
  35.                                     NubEntryType     *coordPtr,
  36.                                     long             numNubsInPart,
  37.                                     long             sectionNum,
  38.                                     float             *scale);
  39. static void Track_PutCameraOnTrack(TQ3CameraObject     camera,
  40.                                 NubEntryType         *splinePtArray,
  41.                                 long                 numSplinePoints,
  42.                                 long                 curTrackLocation);
  43.  
  44. #if TARGET_OS_WIN32
  45.  
  46.     HANDLE WinIO_CreateFile();
  47.     BOOL WinIO_WriteToFile(HANDLE fileHndl, PartType *thisPart);
  48.  
  49. #endif
  50.  
  51.  
  52. /************************************************************
  53. *                                                           *
  54. *    FUNCTION:  Track_MakeRandomTrack                       *
  55. *                                                           *
  56. *    PURPOSE:   Generates a random series of track sections *
  57. *                                                           *
  58. *************************************************************/
  59.  
  60.  
  61. void Track_MakeRandomTrack(TrackSectionType    *trackSectionList,
  62.                             long            numTrackSections)
  63. {
  64.     long    i,r;
  65.         
  66.         for (i = 0; i < numTrackSections; i++)
  67.         {
  68.             do
  69.             {
  70.                 r = Utils_MyRandomLong() & numTrackSections;
  71.             } while(r==7);
  72.             
  73.             trackSectionList[i].partNum = i;    
  74.             trackSectionList[i].nubCoord.y = 0;
  75.             trackSectionList[i].nubCoord.x = sin(6.24F/numTrackSections*(float)i)*(LAZY_SUSAN_RADIUS - 6.0F);
  76.             trackSectionList[i].nubCoord.z = cos(6.24F/numTrackSections*(float)i)*(LAZY_SUSAN_RADIUS - 6.0F);
  77.         }
  78.                 
  79. }
  80.  
  81. /************************************************************
  82. *                                                           *
  83. *    FUNCTION:  Track_CreateMasterNubList                   *
  84. *                                                           *
  85. *    PURPOSE:   creates a master list of spline nubs which  *
  86. *               contains all of the nubs within all         *
  87. *               sections of a track                         *
  88. *                                                           *
  89. *************************************************************/
  90.  
  91. void Track_CreateMasterNubList(TrackSectionType *trackSectionList,
  92.                                 unsigned long        numTrackSections,
  93.                                 PartType             *partsList,
  94.                                 NubEntryType        *nubList,
  95.                                 long                *nubTotal)
  96. {
  97.     unsigned long    sectionNum,partNum,i,numNubsInPart;
  98.     NubEntryType    *coordPtr;
  99.     TQ3Point3D    sectionStartCoords,basePt,upPt;
  100.     float        rotation,scale;
  101.  
  102.         *nubTotal = 0;
  103.  
  104.         for (sectionNum=0; sectionNum < numTrackSections; sectionNum++)
  105.         {
  106.             partNum = trackSectionList[sectionNum].partNum;    /* get part # to add */
  107.  
  108.                         /* GET INFO FOR PART */
  109.         
  110.             sectionStartCoords = trackSectionList[sectionNum].nubCoord;    /* get coords where track section should start */
  111.             numNubsInPart = partsList[partNum].numNubs;        /* get # nubs in part */
  112.             coordPtr = partsList[partNum].coordsPtr;            /* get ptr to coord list */
  113.  
  114.             rotation = Track_AmountToRotateNubs(trackSectionList,
  115.                                                 numTrackSections,
  116.                                                 coordPtr,
  117.                                                 numNubsInPart,
  118.                                                 sectionNum,
  119.                                                 &scale);
  120.  
  121.                     /* COPY & ADJUST PART NUBS */
  122.             
  123.             for (i=1; i < (numNubsInPart-1); i++)                /* skip nub 0 & last nub */
  124.             {
  125.  
  126.                 basePt = coordPtr->basePt;                        /* get coords from part data */
  127.                 upPt = coordPtr->upPt;
  128.                 
  129.                 Utils_RotatePoint(&basePt,rotation);                    /* rotate the points into position */
  130.                 Utils_RotatePoint(&upPt,rotation);
  131.                 
  132.                 basePt.x *= scale;    basePt.y *= scale;    basePt.z *= scale;    /* scale it */
  133.                 upPt.x *= scale;    upPt.y *= scale;    upPt.z *= scale;
  134.                 
  135.                 nubList[*nubTotal].basePt.x = basePt.x + sectionStartCoords.x;    /* tag nubs to end of previous part */
  136.                 nubList[*nubTotal].basePt.y = basePt.y + sectionStartCoords.y;
  137.                 nubList[*nubTotal].basePt.z = basePt.z + sectionStartCoords.z;
  138.  
  139.  
  140.                 nubList[*nubTotal].upPt.x = upPt.x + sectionStartCoords.x;
  141.                 nubList[*nubTotal].upPt.y = upPt.y + sectionStartCoords.y;
  142.                 nubList[*nubTotal].upPt.z = upPt.z + sectionStartCoords.z;
  143.  
  144.  
  145.                 nubList[*nubTotal].sectionNum = sectionNum;            /* remember which section this belongs to */
  146.  
  147.  
  148.                 coordPtr++;
  149.                 (*nubTotal)++;
  150.             }        
  151.         }
  152.         
  153.             /* CREATE 3 FINAL NUBS WHICH WRAP BACK TO BEGINNING TO CLOSE THE LOOP */
  154.  
  155.         partNum = trackSectionList[0].partNum;                                /* get part # */
  156.         coordPtr = partsList[partNum].coordsPtr;                            /* point back to beginning */
  157.         sectionStartCoords = trackSectionList[0].nubCoord;                    /* get coords where track section should start */
  158.         numNubsInPart = partsList[partNum].numNubs;                        /* get # nubs in part */
  159.  
  160.         rotation = Track_AmountToRotateNubs(trackSectionList,
  161.                                             numTrackSections,
  162.                                             coordPtr,
  163.                                             numNubsInPart,
  164.                                             0,
  165.                                             &scale);
  166.  
  167.         for (i=0; i < 3; i++)
  168.         {
  169.             basePt = coordPtr->basePt;                        /* get coords from part data */
  170.             upPt = coordPtr->upPt;
  171.  
  172.             Utils_RotatePoint(&basePt,rotation);                /* rotate the points into position */
  173.             Utils_RotatePoint(&upPt,rotation);
  174.  
  175.             basePt.x *= scale;    basePt.y *= scale;    basePt.z *= scale;    /* scale it */
  176.             upPt.x *= scale;    upPt.y *= scale;    upPt.z *= scale;
  177.             
  178.             nubList[*nubTotal].basePt.x = basePt.x + sectionStartCoords.x;    /* tag nubs to end of previous part */
  179.             nubList[*nubTotal].basePt.y = basePt.y + sectionStartCoords.y;
  180.             nubList[*nubTotal].basePt.z = basePt.z + sectionStartCoords.z;
  181.  
  182.  
  183.             nubList[*nubTotal].upPt.x = upPt.x + sectionStartCoords.x;
  184.             nubList[*nubTotal].upPt.y = upPt.y + sectionStartCoords.y;
  185.             nubList[*nubTotal].upPt.z = upPt.z + sectionStartCoords.z;
  186.  
  187.  
  188.             (*nubTotal)++;
  189.             coordPtr++;
  190.         }
  191.  
  192. }
  193.  
  194. /************************************************************
  195. *                                                           *
  196. *    FUNCTION:  Track_AmountToRotateNubs                    *
  197. *                                                           *
  198. *    PURPOSE:   Calculates the amount to rotate nubs on the *
  199. *               y axis so that the track section will go    *
  200. *               from point A to point B. It also returns a  *
  201. *               scaling value used to scale the nub based   *
  202. *               on the change in length.                    *
  203. *                                                           *
  204. *************************************************************/
  205.  
  206. static float Track_AmountToRotateNubs(TrackSectionType    *trackSectionList,
  207.                                         long             numTrackSections,
  208.                                         NubEntryType     *coordPtr,
  209.                                         long             numNubsInPart,
  210.                                         long             sectionNum,
  211.                                         float             *scale)
  212. {
  213.     TQ3Vector3D    startVec,endVec,originalVec,desiredVec;
  214.     long    i;
  215.     float    rotation,originalSize,desiredSize;
  216.     TQ3Point3D    originalPt,desiredPt,zeroPt = {0,0,0};
  217.  
  218.  
  219.                     /* CALC ORIGINAL VECTOR */
  220.  
  221.  
  222.         startVec.x = coordPtr[1].basePt.x;                    /* use 2nd nub as start coord (remember that we skip the 1st nub in a part) */
  223.         startVec.y = coordPtr[1].basePt.y;
  224.         startVec.z = coordPtr[1].basePt.z;
  225.         endVec.x = coordPtr[numNubsInPart-1].basePt.x;        /* use last nub as end coord of original data */
  226.         endVec.y = coordPtr[numNubsInPart-1].basePt.y;    
  227.         endVec.z = coordPtr[numNubsInPart-1].basePt.z;    
  228.         Q3Vector3D_Subtract(&endVec,&startVec,&originalVec); /* calc vector from start to end */
  229.  
  230.                     /* CALC DESIRED VECTOR */
  231.  
  232.         i = sectionNum+1;
  233.         if (i >= numTrackSections)
  234.         {
  235.             i = 0;
  236.         }
  237.         startVec.x = trackSectionList[sectionNum].nubCoord.x;    /* get vector of where we want it to endup */
  238.         startVec.y = trackSectionList[sectionNum].nubCoord.y;                
  239.         startVec.z = trackSectionList[sectionNum].nubCoord.z;                
  240.         endVec.x = trackSectionList[i].nubCoord.x;                /* get vector of where we want it to endup */
  241.         endVec.y = trackSectionList[i].nubCoord.y;                
  242.         endVec.z = trackSectionList[i].nubCoord.z;                
  243.         Q3Vector3D_Subtract(&endVec,&startVec,&desiredVec); /* calc vector from start to desired end */
  244.         
  245.                     /* CALC ANGLE */
  246.  
  247.         rotation = Utils_AngleBetweenVectors(originalVec,desiredVec);    /* calc amount we need to rotate all nubs in part */
  248.         if (desiredVec.z > originalVec.z)                    /* see if need to negate (since we only have absolute angle between vecs) */
  249.         {
  250.             rotation = -rotation;
  251.         }
  252.  
  253.             /* CALC SCALING TO ADJUST FOR DISTANCE CHANGE */
  254.  
  255.         originalPt.x = originalVec.x;                    /* calc size of original segment */
  256.         originalPt.y = originalVec.y;
  257.         originalPt.z = originalVec.z;
  258.         originalSize = Q3Point3D_Distance(&originalPt,&zeroPt);
  259.         
  260.         desiredPt.x = desiredVec.x;                        /* calc size of desired seg */
  261.         desiredPt.y = desiredVec.y;
  262.         desiredPt.z = desiredVec.z;
  263.         desiredSize = Q3Point3D_Distance(&desiredPt,&zeroPt);
  264.             
  265.         *scale = desiredSize/originalSize;                /* return scaling value */
  266.             
  267.         return(rotation);
  268. }
  269.  
  270.  
  271. #if TARGET_OS_WIN32
  272.  
  273. /************************************************************
  274. *                                                           *
  275. *    FUNCTION:  Track_LoadPartsFromFile                     *
  276. *                                                           *
  277. *    PURPOSE:   Loads our pre-generated track parts         *
  278. *               from a file                                 *
  279. *                                                           *
  280. *************************************************************/
  281.  
  282. void Track_LoadPartsFromFile(PartType *partsList, short *partCount)
  283.  
  284. {
  285.     HANDLE        fileHndl;
  286.     DWORD        err;
  287.     char        partFilePath[MAX_PATH];
  288.  
  289.  
  290.         err = Utils_Win32_BuildCurDirPath((Ptr)&partFilePath, kPartDataFileName);
  291.         fileHndl = CreateFile((char *)&partFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
  292.         if ((fileHndl == NULL) || (fileHndl == INVALID_HANDLE_VALUE))
  293.  
  294.         {
  295.  
  296.             Utils_DisplayFatalErrorMsg("Failure loading track part file TrackPartData.dat!");
  297.  
  298.         }
  299.  
  300.         else
  301.  
  302.         {
  303.             Ptr        currentIndex;
  304.             HLOCAL    localMem;
  305.             DWORD    numBytesRead, fileSize;
  306.             BOOL    successfull = FALSE;
  307.  
  308.  
  309.                 fileSize = GetFileSize(fileHndl, NULL);
  310.                 localMem = LocalAlloc(LMEM_FIXED, fileSize);
  311.                 if (localMem != NULL)
  312.                 {
  313.                     currentIndex = localMem;
  314.                     *partCount = 0;
  315.                     successfull = FALSE;
  316.  
  317.  
  318.                     do
  319.                     {
  320.                             /* get count of nubs for this part */
  321.  
  322.                         successfull = ReadFile(fileHndl, &partsList[*partCount].numNubs, sizeof(partsList->numNubs), &numBytesRead, NULL);
  323.                         if ((successfull == TRUE) && (numBytesRead != 0))
  324.                         {
  325.                             numBytesRead = 0;
  326.                             successfull = ReadFile(fileHndl, currentIndex, sizeof(NubEntryType) * partsList[*partCount].numNubs, &numBytesRead, NULL);
  327.                             if ((successfull == TRUE) && (numBytesRead != 0))
  328.                             {
  329.                                     /* SAVE PART RECORD */
  330.                                 partsList[*partCount].coordsPtr = (NubEntryType *)currentIndex;
  331.                                     /* move pointer to point to memory for next part */
  332.                                 currentIndex = currentIndex + sizeof(NubEntryType) * partsList[*partCount].numNubs;
  333.                                 ++ (*partCount);
  334.                             }
  335.                             else
  336.                             {
  337.                                 Utils_DisplayFatalErrorMsg("Error reading track part file TrackPartData.dat!");
  338.  
  339.                             }
  340.                         }
  341.                     }
  342.                     while ((successfull == TRUE) && (numBytesRead != 0));
  343.                 }
  344.                 else
  345.                 {
  346.                     Utils_DisplayFatalErrorMsg("Memory allocation failure!");
  347.  
  348.                 }
  349.         }
  350. }
  351.  
  352.  
  353. #endif
  354.  
  355.  
  356.  
  357.  
  358. /************************************************************
  359. *                                                           *
  360. *    FUNCTION:  Track_LoadPartsFromRez                      *
  361. *                                                           *
  362. *    PURPOSE:   Loads our pre-generated track parts         *
  363. *               from a resource file                        *
  364. *                                                           *
  365. *************************************************************/
  366.  
  367.  
  368. #if TARGET_OS_MAC
  369.  
  370.  
  371. void Track_LoadPartsFromRez(PartType *partsList, short *partCount)
  372. {
  373. OSErr        err;
  374. short        i;
  375. Handle        partHandle;
  376. PartType    *thisPart;
  377. Byte        numNubs;
  378. Ptr            nubCoordsPtr;
  379.  
  380.  
  381.     *partCount = 0;
  382.  
  383.         /* get count of part resources in our resource file */
  384.     *partCount = Count1Resources(kPartType);
  385.     err = ResError();
  386.     if (err != noErr)
  387.     {
  388.         Utils_DisplayFatalErrorMsg("Error Getting Part Resource");
  389.     }
  390.     else
  391.     {
  392.         for (i = 1; i <= *partCount; i++)
  393.         {
  394.                 /* get next part resource in our file */
  395.             partHandle = GetIndResource(kPartType,i);
  396.             if (partHandle == nil)
  397.             {
  398.                 Utils_DisplayFatalErrorMsg("Error Getting Part Resource");
  399.             }
  400.             else
  401.             {
  402.                 DetachResource(partHandle);            /* give it to me */
  403.                 HLockHi(partHandle);
  404.  
  405.                 thisPart = (PartType *)(*partHandle);
  406.  
  407.                     /* get number of points in this part from the numNubs field of
  408.                         the PartType structure */
  409.                         
  410.                 numNubs = thisPart->numNubs;
  411.  
  412.                     /* allocate enough NubEntryType structures to hold all of the points
  413.                         in this part */
  414.  
  415.                 nubCoordsPtr = NewPtr(sizeof(NubEntryType) * numNubs);// alloc memory for nub coords
  416.                 if (nubCoordsPtr == nil)
  417.                 {
  418.                     Utils_DisplayFatalErrorMsg("Not enough memory to load Part");
  419.                 }
  420.                 else
  421.                 {
  422.                         /* copy NubEntryType data to our memory block */
  423.                     //BlockMove((Ptr)&thisPart->coordsPtr,nubCoordsPtr,sizeof(NubEntryType) * numNubs);
  424.                     BlockMove((Ptr)thisPart + sizeof(thisPart->numNubs),nubCoordsPtr,sizeof(NubEntryType) * numNubs);
  425.                     DisposeHandle(partHandle);
  426.                     
  427.                         /* save part record as a PartType structure */
  428.                     partsList[i-1].numNubs = numNubs;
  429.                     partsList[i-1].coordsPtr = (NubEntryType *)nubCoordsPtr;
  430.  
  431.                 }
  432.             }
  433.         }
  434.     }
  435. }
  436.  
  437.  
  438. #endif
  439.  
  440.  
  441. /************************************************************
  442. *                                                           *
  443. *    FUNCTION:  Track_PutCameraOnTrack                      *
  444. *                                                           *
  445. *    PURPOSE:   Calculates placement of the camera on the   *
  446. *               track                                       *
  447. *                                                           *
  448. *                                                           *
  449. *                                                           *
  450. *************************************************************/
  451.  
  452. void Track_PutCameraOnTrack(TQ3CameraObject     camera,
  453.                             NubEntryType         *splinePtArray,
  454.                             long                 numSplinePoints,
  455.                             long                 curTrackLocation
  456.                             )
  457. {
  458.     TQ3Point3D            cameraFrom,cameraTo;
  459.     TQ3Vector3D            cameraUp,cameraFromVect;
  460.     long                desti;
  461.     TQ3CameraPlacement    placement;
  462.         
  463.             
  464.         if (numSplinePoints == 0)
  465.             return;
  466.  
  467.                         
  468.                     /* CALC UP VECTOR */
  469.                     
  470.         Q3Point3D_Subtract(&splinePtArray[curTrackLocation].upPt,        /* calc vector from base to up point */
  471.                             &splinePtArray[curTrackLocation].basePt,
  472.                             &cameraUp);
  473.         Q3Vector3D_Normalize(&cameraUp,&cameraUp);
  474.         
  475.  
  476.             /* USE UP VECTOR TO CALC "FROM" POSITION */
  477.             
  478.         Q3Vector3D_Scale(&cameraUp,DISTANCE_FROM_TRACK_TO_CAMERA,&cameraFromVect);    /* calc how far above track to put camera */
  479.         cameraFrom.x = (splinePtArray[curTrackLocation].basePt.x +
  480.                          cameraFromVect.x);
  481.         cameraFrom.y = (splinePtArray[curTrackLocation].basePt.y +
  482.                          cameraFromVect.y);
  483.         cameraFrom.z = (splinePtArray[curTrackLocation].basePt.z +
  484.                          cameraFromVect.z);
  485.         
  486.                     /* SET "TO" PT */
  487.                                         
  488.         desti = curTrackLocation + kSkipAheadPoints;
  489.         if (desti >= (numSplinePoints-1))
  490.         {
  491.             desti -= numSplinePoints;        /* loop back around */
  492.         }
  493.  
  494.         cameraTo = splinePtArray[desti].basePt;            /* look at base spline point in distance */
  495.                 
  496.                             
  497.                     /* UPDATE CAMERA PLACEMENT */
  498.                     
  499.         placement.cameraLocation = cameraFrom;                /* set placement data */
  500.         placement.pointOfInterest = cameraTo;
  501.         placement.upVector = cameraUp;
  502.         Q3Camera_SetPlacement(camera,&placement);
  503.     
  504. }
  505.  
  506.  
  507. /************************************************************
  508. *                                                           *
  509. *    FUNCTION:  Track_BuildCoasterGeometry_Mesh_Textured    *
  510. *                                                           *
  511. *    PURPOSE:   Build a textured track using mesh objects   *
  512. *                                                           *
  513. *                                                           *
  514. *************************************************************/
  515.  
  516. void Track_BuildCoasterGeometry_Mesh(long             skipValue,
  517.                                     TQ3GroupObject     theGroup,
  518.                                     long             numSplinePoints,
  519.                                     NubEntryType     *splinePointsPtr)
  520. {
  521. TQ3GeometryObject    myMesh = NULL;
  522. TQ3GroupPosition    myGroupPosition;
  523. TQ3ColorRGB            whiteColor = {1,1,1};
  524. long                i,faceCount,colorTick = 0;
  525. float                x2,y2,z2;
  526. TQ3Vector3D            tangentVector;    
  527. TQ3MeshVertex        meshVertexList[4];
  528. TQ3Vertex3D            vertexList[4],firstLeft,firstRight;
  529. TQ3AttributeSet        generalAttribs = NULL,vAttrib[4] = {NULL, NULL, NULL, NULL};
  530. TQ3MeshFace            face;
  531. float                ambient = 0.8F;
  532. float                spec = 0.0F;
  533. TQ3Param2D            corner1 = {0,1};    /* uv's for texture mapping */
  534. TQ3Param2D            corner2 = {1,1};
  535. TQ3Param2D            corner3 = {1,0};
  536. TQ3Param2D            corner4 = {0,0};
  537.  
  538.     
  539.  
  540.                 /* CREATE NEW MESH OBJECT */
  541.     
  542.     myMesh = Q3Mesh_New();
  543.     
  544.     if( myMesh == NULL )
  545.     {
  546.         Utils_DisplayErrorMsg("Group_AddObject failed!");
  547.         goto memoryError;
  548.     }
  549.                                 
  550.     Q3Mesh_DelayUpdates(myMesh);
  551.  
  552.  
  553.                 /* SETUP GENERAL ATTRIBUTES */
  554.  
  555.     generalAttribs = Q3AttributeSet_New();    
  556.     if( generalAttribs == NULL )
  557.     {
  558.         Utils_DisplayErrorMsg("Q3AttributeSet_New failed!");
  559.         goto memoryError;
  560.     }
  561.     Q3AttributeSet_Add(generalAttribs, kQ3AttributeTypeAmbientCoefficient, &ambient);
  562.     Q3AttributeSet_Add(generalAttribs, kQ3AttributeTypeSpecularControl, &spec);    
  563.  
  564.  
  565.             /* CREATE UV ATTRIBUTES FOR THE VERTICES */
  566.  
  567.     vAttrib[0] = Q3AttributeSet_New();    
  568.     if( vAttrib[0] == NULL )
  569.     {
  570.         Utils_DisplayErrorMsg("Q3AttributeSet_New failed!");
  571.         goto memoryError;
  572.     }
  573.     Q3AttributeSet_Add(vAttrib[0], kQ3AttributeTypeShadingUV, &corner1);    
  574.  
  575.     vAttrib[1] = Q3AttributeSet_New();    
  576.     if( vAttrib[1] == NULL )
  577.     {
  578.         Utils_DisplayErrorMsg("Q3AttributeSet_New failed!");
  579.         goto memoryError;
  580.     }
  581.     Q3AttributeSet_Add(vAttrib[1], kQ3AttributeTypeShadingUV, &corner2);    
  582.  
  583.     vAttrib[2] = Q3AttributeSet_New();    
  584.     if( vAttrib[2] == NULL )
  585.     {
  586.         Utils_DisplayErrorMsg("Q3AttributeSet_New failed!");
  587.         goto memoryError;
  588.     }
  589.     Q3AttributeSet_Add(vAttrib[2], kQ3AttributeTypeShadingUV, &corner3);    
  590.  
  591.     vAttrib[3] = Q3AttributeSet_New();    
  592.     if( vAttrib[3] == NULL )
  593.     {
  594.         Utils_DisplayErrorMsg("Q3AttributeSet_New failed!");
  595.         goto memoryError;
  596.     }
  597.     Q3AttributeSet_Add(vAttrib[3], kQ3AttributeTypeShadingUV, &corner4);    
  598.  
  599.  
  600.     vertexList[0].attributeSet = nil;
  601.     vertexList[1].attributeSet = nil;
  602.     vertexList[2].attributeSet = nil;
  603.     vertexList[3].attributeSet = nil;
  604.  
  605.     faceCount = 0;
  606.  
  607.  
  608.     for (i = 0; i < (numSplinePoints - 1 - skipValue); i += skipValue)
  609.     {        
  610.                 /* GET SPLINE POINTS */
  611.                 
  612.         x2 = splinePointsPtr[i+skipValue].basePt.x;        /* get coords of end pt #2 (far point) */
  613.         y2 = splinePointsPtr[i+skipValue].basePt.y;
  614.         z2 = splinePointsPtr[i+skipValue].basePt.z;
  615.  
  616.  
  617.                 /* CALC TANGENT VECTOR */
  618.  
  619.         Q3Point3D_CrossProductTri(&splinePointsPtr[i].basePt,
  620.                                 &splinePointsPtr[i+skipValue].basePt,
  621.                                 &splinePointsPtr[i].upPt,&tangentVector);
  622.  
  623.         Q3Vector3D_Normalize(&tangentVector,&tangentVector);
  624.         Q3Vector3D_Scale(&tangentVector,kTrackWidth,&tangentVector);
  625.  
  626.  
  627.                 /* CALC NEW "UPPER" COORDS OF FACE */
  628.  
  629.         Q3Point3D_Set(&vertexList[0].point,            /* upper left */
  630.                         x2-tangentVector.x,
  631.                         y2-tangentVector.y,
  632.                         z2-tangentVector.z);
  633.  
  634.         Q3Point3D_Set(&vertexList[1].point,            /* upper right */
  635.                         x2+tangentVector.x,
  636.                         y2+tangentVector.y,
  637.                         z2+tangentVector.z);
  638.  
  639.  
  640.         meshVertexList[0] = Q3Mesh_VertexNew(myMesh, &vertexList[0]);        /* get new vertex for "upper/far" */
  641.         meshVertexList[1] = Q3Mesh_VertexNew(myMesh, &vertexList[1]);
  642.  
  643.  
  644.                     /* FOR 1ST FACE, MUST RIG "BOTTOM" COORDS */
  645.                     
  646.         if (i == 0)
  647.         {
  648.             Q3Point3D_Set(&vertexList[2].point,            /* lower right */
  649.                             splinePointsPtr[0].basePt.x+tangentVector.x,
  650.                             splinePointsPtr[0].basePt.y+tangentVector.y,
  651.                             splinePointsPtr[0].basePt.z+tangentVector.z);
  652.  
  653.             Q3Point3D_Set(&vertexList[3].point,            /* lower left */
  654.                             splinePointsPtr[0].basePt.x-tangentVector.x,
  655.                             splinePointsPtr[0].basePt.y-tangentVector.y,
  656.                             splinePointsPtr[0].basePt.z-tangentVector.z);
  657.  
  658.             firstRight.point = vertexList[2].point;            /* remember these coords for wrap-back later */
  659.             firstLeft.point = vertexList[3].point;
  660.             
  661.             meshVertexList[2] = Q3Mesh_VertexNew(myMesh, &vertexList[2]);    /* set vertex */
  662.             meshVertexList[3] = Q3Mesh_VertexNew(myMesh, &vertexList[3]);
  663.         }
  664.  
  665.         
  666.                     /* CREATE FACE */
  667.                     
  668.         face = Q3Mesh_FaceNew(myMesh,4,&meshVertexList[0],generalAttribs);
  669.  
  670.         if( face == NULL )
  671.         {
  672.             Utils_DisplayErrorMsg("Q3AttributeSet_New failed!");
  673.             goto memoryError;
  674.         }
  675.  
  676.                     /* APPLY UV COORD ATTRIBS */
  677.                     
  678.         Q3Mesh_SetCornerAttributeSet(myMesh, meshVertexList[0], face, vAttrib[0]);
  679.         Q3Mesh_SetCornerAttributeSet(myMesh, meshVertexList[1], face, vAttrib[1]);
  680.         Q3Mesh_SetCornerAttributeSet(myMesh, meshVertexList[2], face, vAttrib[2]);
  681.         Q3Mesh_SetCornerAttributeSet(myMesh, meshVertexList[3], face, vAttrib[3]);
  682.  
  683.         colorTick++;        
  684.         faceCount++;
  685.         
  686.             /* SEE IF MESH OBJECT IS LARGE ENOUGH TO USE NOW */
  687.         
  688.         if (faceCount > kMaxFacesInMesh)
  689.         {
  690.             Q3Mesh_ResumeUpdates(myMesh);
  691.  
  692.             myGroupPosition = Q3Group_AddObject(theGroup, myMesh);    /* add mesh to group */
  693.             Q3Object_Dispose(myMesh);                                    /* make another mesh object */
  694.             if ( myGroupPosition == nil )
  695.             {
  696.                 Utils_DisplayErrorMsg("Q3Group_AddObject failed!");
  697.                 goto memoryError;
  698.             }
  699.             
  700.             myMesh = Q3Mesh_New();
  701.             if ( myMesh == nil )
  702.             {
  703.                 Utils_DisplayErrorMsg("Q3Mesh_New failed!");
  704.                 goto memoryError;
  705.             }
  706.             Q3Mesh_DelayUpdates(myMesh);
  707.             meshVertexList[0] = Q3Mesh_VertexNew(myMesh, &vertexList[0]);    /* reset these to the new mesh */
  708.             meshVertexList[1] = Q3Mesh_VertexNew(myMesh, &vertexList[1]);
  709.             
  710.             faceCount = 0;        
  711.         }    
  712.     
  713.                 /* UPPERS WILL BE LOWERS ON NEXT POLY */
  714.     
  715.         vertexList[2] = vertexList[1];                /* this is so next poly's bottom will match last poly's top */
  716.         vertexList[3] = vertexList[0];
  717.         meshVertexList[2] = meshVertexList[1];
  718.         meshVertexList[3] = meshVertexList[0];        
  719.  
  720.         if (colorTick & 1)
  721.         {
  722.             vertexList[0].attributeSet = vAttrib[0];            /* apply uv attribs to the vertices */
  723.             vertexList[1].attributeSet = vAttrib[1];
  724.             vertexList[2].attributeSet = vAttrib[2];
  725.             vertexList[3].attributeSet = vAttrib[3];
  726.         }
  727.         else
  728.         {
  729.             vertexList[0].attributeSet = vAttrib[3];            /* apply uv attribs to the vertices */
  730.             vertexList[1].attributeSet = vAttrib[2];
  731.             vertexList[2].attributeSet = vAttrib[1];
  732.             vertexList[3].attributeSet = vAttrib[0];
  733.         }
  734.     }
  735.  
  736.                 /* CREATE 1 FINAL FACE TO LINK BACK TO BEGINNING */
  737.  
  738.     vertexList[0].point = firstLeft.point;
  739.     vertexList[1].point = firstRight.point;
  740.     meshVertexList[0] = Q3Mesh_VertexNew(myMesh, &vertexList[0]);        /* create vertex based on 1st */
  741.     meshVertexList[1] = Q3Mesh_VertexNew(myMesh, &vertexList[1]);
  742.     
  743.     if (faceCount & 1)
  744.     {
  745.         face = Q3Mesh_FaceNew(myMesh,4,&meshVertexList[0],nil);
  746.     }
  747.     else
  748.     {
  749.         face = Q3Mesh_FaceNew(myMesh,4,&meshVertexList[0],nil);
  750.     }
  751.     faceCount++;
  752.  
  753.                     /* APPLY ANY REMAINING MESH */
  754.                     
  755.     Q3Mesh_ResumeUpdates(myMesh);
  756.  
  757.     if (faceCount > 0)
  758.     {
  759.         myGroupPosition = Q3Group_AddObject(theGroup, myMesh);
  760.         if ( myGroupPosition == nil )
  761.         {
  762.             Utils_DisplayErrorMsg("Q3Group_AddObject failed!");
  763.             goto memoryError;
  764.         }
  765.     }
  766.  
  767.     Q3Object_Dispose(myMesh);                            /* kill mesh object */
  768.     Q3Object_Dispose(generalAttribs);    
  769.     Q3Object_Dispose(vAttrib[0]);    
  770.     Q3Object_Dispose(vAttrib[1]);    
  771.     Q3Object_Dispose(vAttrib[2]);    
  772.     Q3Object_Dispose(vAttrib[3]);
  773.     
  774.     return;
  775.     
  776. memoryError:
  777.     if( myMesh )
  778.     {
  779.         Q3Object_Dispose(myMesh);
  780.     }
  781.  
  782.     if( generalAttribs )
  783.     {
  784.         Q3Object_Dispose(generalAttribs);
  785.     }
  786.  
  787.     if( vAttrib[0] )
  788.     {
  789.         Q3Object_Dispose(vAttrib[0]);
  790.     }
  791.  
  792.     if( vAttrib[1] )
  793.     {
  794.         Q3Object_Dispose(vAttrib[1]);
  795.     }
  796.  
  797.     if( vAttrib[2] )
  798.     {
  799.         Q3Object_Dispose(vAttrib[2]);
  800.     }
  801.  
  802.     if( vAttrib[3] )
  803.     {
  804.         Q3Object_Dispose(vAttrib[3]);
  805.     }
  806.     
  807. }
  808.  
  809.  
  810. /************************************************************
  811. *                                                           *
  812. *    FUNCTION:  Track_CalcSplineCurve                       *
  813. *                                                           *
  814. *    PURPOSE:   Use our control points to construct a       *
  815. *               spline curve for each section of the        *
  816. *               track                                       *
  817. *                                                           *
  818. *                                                           *
  819. *************************************************************/
  820.  
  821. void Track_CalcSplineCurve(NubEntryType     **splinePoints,
  822.                             long            maxSplinePoints,
  823.                             NubEntryType    *nubPoints,
  824.                             long            numSplineNubs,
  825.                             long            *numSplinePoints,
  826.                             float             subDivFactor)
  827. {
  828.     float        t,tSquared,tCubed,a,b,c,d,incVal;
  829.     long        subCount,nubNum,numNubs,numSubDivs;
  830.     TQ3Point3D    highestPoint = {0,0,0};
  831.  
  832.                 /* ALLOC MEMORY FOR SPLINE DATA */
  833.  
  834.         *splinePoints = (NubEntryType *)NewPtr( sizeof(NubEntryType) * maxSplinePoints );
  835.         if (*splinePoints == nil)
  836.         {
  837.             Utils_DisplayFatalErrorMsg("Sorry, but there doesnt seem to be enough memory to allocate the track spline curve");
  838.         }
  839.         else
  840.         {
  841.  
  842.             numNubs = numSplineNubs;    /* # nubs */
  843.             *numSplinePoints = 0;        /* # points generated */
  844.             
  845.                         /* SCAN THRU NUBS */
  846.  
  847.                 /* Note: skips 1st & last nubs (sorta) */
  848.             for (nubNum=1; nubNum < (numNubs-2); nubNum++)
  849.             {                
  850.                 numSubDivs = Q3Point3D_Distance(&nubPoints[nubNum].basePt,
  851.                                                 &nubPoints[nubNum+1].basePt)*subDivFactor + 0.5F;    /* # segments between next nubs */
  852.                 if (numSubDivs < 1)
  853.                     numSubDivs = 1;
  854.                 
  855.                 incVal = 1.0F/numSubDivs;                /* increment value */
  856.  
  857.                 for (t=0, subCount=0; subCount < numSubDivs; t+=incVal,subCount++)
  858.                 {
  859.                     tSquared = t*t;
  860.                     tCubed = tSquared*t;
  861.                     a = (-0.166F * tCubed)+(0.5F * tSquared)-(0.5F * t) + 0.166F;
  862.                     b = (0.5F * tCubed) - tSquared + 0.666F;
  863.                     c = (-0.5F * tCubed) + (0.5F * tSquared) + (0.5F * t + 0.166F);
  864.                     d = 0.166F * tCubed;
  865.  
  866.                     if (*numSplinePoints < maxSplinePoints)
  867.                     {    
  868.                                         /* CALC SEG OF BASE */
  869.                                         
  870.                         (*splinePoints)[*numSplinePoints].basePt.x =
  871.                                             (a * nubPoints[nubNum-1].basePt.x) +
  872.                                             (b * nubPoints[nubNum].basePt.x) +
  873.                                             (c * nubPoints[nubNum+1].basePt.x) +
  874.                                             (d * nubPoints[nubNum+2].basePt.x);
  875.                                         
  876.                         (*splinePoints)[*numSplinePoints].basePt.y =
  877.                                             (a * nubPoints[nubNum-1].basePt.y) + 
  878.                                             (b * nubPoints[nubNum].basePt.y) +
  879.                                             (c * nubPoints[nubNum+1].basePt.y) +
  880.                                             (d * nubPoints[nubNum+2].basePt.y);
  881.  
  882.                         (*splinePoints)[*numSplinePoints].basePt.z =
  883.                                             (a * nubPoints[nubNum-1].basePt.z) +
  884.                                             (b * nubPoints[nubNum].basePt.z) +
  885.                                             (c * nubPoints[nubNum+1].basePt.z) +
  886.                                             (d * nubPoints[nubNum+2].basePt.z);
  887.  
  888.                                         /* CALC SEG OF UP */
  889.                                         
  890.                         (*splinePoints)[*numSplinePoints].upPt.x =
  891.                                             (a * nubPoints[nubNum-1].upPt.x) +
  892.                                             (b * nubPoints[nubNum].upPt.x) +
  893.                                             (c * nubPoints[nubNum+1].upPt.x) +
  894.                                             (d * nubPoints[nubNum+2].upPt.x);
  895.                                         
  896.                         (*splinePoints)[*numSplinePoints].upPt.y =
  897.                                             (a * nubPoints[nubNum-1].upPt.y) + 
  898.                                             (b * nubPoints[nubNum].upPt.y) +
  899.                                             (c * nubPoints[nubNum+1].upPt.y) +
  900.                                             (d * nubPoints[nubNum+2].upPt.y);
  901.  
  902.                         (*splinePoints)[*numSplinePoints].upPt.z =
  903.                                             (a * nubPoints[nubNum-1].upPt.z) +
  904.                                             (b * nubPoints[nubNum].upPt.z) +
  905.                                             (c * nubPoints[nubNum+1].upPt.z) +
  906.                                             (d * nubPoints[nubNum+2].upPt.z);
  907.  
  908.                                         /* REMEMBER WHAT TYPE IT IS */
  909.                                         
  910.                         (*splinePoints)[*numSplinePoints].sectionNum = nubPoints[nubNum].sectionNum;
  911.  
  912.                         (*numSplinePoints)++;
  913.                     }
  914.                     else
  915.                     {
  916.                         Utils_DisplayFatalErrorMsg("Too many spline points!  Overflowed array!");
  917.                     }
  918.                 }
  919.             }
  920.         }
  921. }
  922.  
  923. /************************************************************
  924. *                                                           *
  925. *    FUNCTION:  Track_MoveCamera                            *
  926. *                                                           *
  927. *    PURPOSE:   Move the camera to the next location on the *
  928. *               track                                       *
  929. *                                                           *
  930. *                                                           *
  931. *************************************************************/
  932.  
  933. void Track_MoveCamera(TQ3CameraObject     camera,
  934.                     NubEntryType         *splinePtArray,
  935.                     long                 numSplinePoints,
  936.                     long                 *curTrackLocation)
  937. {
  938.     Track_PutCameraOnTrack(camera,
  939.                             splinePtArray,
  940.                             numSplinePoints,
  941.                             *curTrackLocation);
  942.         /* have we reached the end of the track? */
  943.     if ( ((*curTrackLocation) + 1) >= numSplinePoints)
  944.     {
  945.             /* end-of-track, so wrap back to the beginning */
  946.         (*curTrackLocation) = 0;
  947.     }
  948.     else
  949.     {
  950.             /* move to next location on track */
  951.         ++(*curTrackLocation);
  952.     }
  953.  
  954. }
  955.  
  956.  
  957. /************************************************************
  958. *                                                           *
  959. *    FUNCTION:  Track_GetForwardVector                      *
  960. *                                                           *
  961. *    PURPOSE:   Returns the vector representing forward at  *
  962. *               trackIndex                                  *
  963. *                                                           *
  964. *                                                           *
  965. *************************************************************/
  966.  
  967. void Track_GetForwardVector(long trackIndex, NubEntryType *splinePointsPtr, long numSplinePoints, TQ3Vector3D *theVector)
  968. {
  969. TQ3Vector3D    forward;
  970.  
  971.     if ((trackIndex+1) == numSplinePoints)                    /* see if +1 will wrap it */
  972.         Q3Point3D_Subtract(&splinePointsPtr[0].basePt,        /* calc vector from here to one in front */
  973.                         &splinePointsPtr[trackIndex].basePt,
  974.                         &forward);
  975.     else
  976.         Q3Point3D_Subtract(&splinePointsPtr[trackIndex+1].basePt,        /* calc vector from here to one in front */
  977.                         &splinePointsPtr[trackIndex].basePt,
  978.                         &forward);
  979.     Q3Vector3D_Normalize(&forward,theVector);                    /* normalize & return it */
  980. }
  981.  
  982.  
  983. /************************************************************
  984. *                                                           *
  985. *    FUNCTION:  Track_GetNormalVector                       *
  986. *                                                           *
  987. *    PURPOSE:   Returns the vector to the normal of the     *
  988. *               surface of the track at trackIndex          *
  989. *                                                           *
  990. *                                                           *
  991. *************************************************************/
  992.  
  993. void Track_GetNormalVector(NubEntryType *splinePointsPtr, long trackIndex, TQ3Vector3D *theVector)
  994. {
  995. TQ3Vector3D    cameraUp;
  996.  
  997.     Q3Point3D_Subtract(&splinePointsPtr[trackIndex].upPt,        /* calc vector from base to up point */
  998.                         &splinePointsPtr[trackIndex].basePt,
  999.                         &cameraUp);
  1000.     Q3Vector3D_Normalize(&cameraUp,theVector);                    /* normalize & return it */
  1001. }
  1002.  
  1003.