t3dlib6.cpp
上传用户:husern
上传日期:2018-01-20
资源大小:42486k
文件大小:181k
源码类别:

游戏

开发平台:

Visual C++

  1. // T3DLIB6.CPP - low level line and triangle rendering code
  2. // I N C L U D E S ///////////////////////////////////////////////////////////
  3. #define DEBUG_ON
  4. #define WIN32_LEAN_AND_MEAN  
  5. #include <windows.h>   // include important windows stuff
  6. #include <windowsx.h> 
  7. #include <mmsystem.h>
  8. #include <objbase.h>
  9. #include <iostream.h> // include important C/C++ stuff
  10. #include <conio.h>
  11. #include <stdlib.h>
  12. #include <malloc.h>
  13. #include <memory.h>
  14. #include <string.h>
  15. #include <stdarg.h>
  16. #include <stdio.h>
  17. #include <math.h>
  18. #include <io.h>
  19. #include <fcntl.h>
  20. #include <direct.h>
  21. #include <wchar.h>
  22. #include <limits.h>
  23. #include <float.h>
  24. #include <search.h>
  25. #include <ddraw.h>      // needed for defs in T3DLIB1.H 
  26. #include "T3DLIB1.H"
  27. #include "T3DLIB4.H"
  28. #include "T3DLIB5.H"
  29. #include "T3DLIB6.H"
  30. // GLOBALS //////////////////////////////////////////////////////////////////
  31. MATV1 materials[MAX_MATERIALS]; // materials in system
  32. int num_materials;              // current number of materials
  33. LIGHTV1 lights[MAX_LIGHTS];  // lights in system
  34. int num_lights;              // current number of lights
  35. // these look up tables are used by the 8-bit lighting engine
  36. // the first one holds a color translation table in the form of each
  37. // row is a color 0..255, and each row consists of 256 shades of that color
  38. // the data in each row is the color/intensity indices and the resulting value
  39. // is an 8-bit index into the real color lookup that should be used as the color
  40. // the second table works by each index being a compressed 16bit RGB value
  41. // the data indexed by that RGB value IS the index 0..255 of the real
  42. // color lookup that matches the desired color the closest
  43. UCHAR rgbilookup[256][256];         // intensity RGB 8-bit lookup storage
  44. UCHAR rgblookup[65536];             // RGB 8-bit color lookup
  45. // FUNCTIONS ////////////////////////////////////////////////////////////////
  46. void Draw_OBJECT4DV1_Solid(OBJECT4DV1_PTR obj, 
  47.                           UCHAR *video_buffer, int lpitch)
  48.                      
  49. {
  50. // this function renders an object to the screen in solid, 
  51. // 8 bit mode, it has no regard at all about hidden surface removal, 
  52. // etc. the function only exists as an easy way to render an object 
  53. // without converting it into polygons, the function assumes all 
  54. // coordinates are screen coordinates, but will perform 2D clipping
  55. // iterate thru the poly list of the object and simply draw
  56. // each polygon
  57. for (int poly=0; poly < obj->num_polys; poly++)
  58.     {
  59.     // render this polygon if and only if it's not clipped, not culled,
  60.     // active, and visible, note however the concecpt of "backface" is 
  61.     // irrelevant in a wire frame engine though
  62.     if (!(obj->plist[poly].state & POLY4DV1_STATE_ACTIVE) ||
  63.          (obj->plist[poly].state & POLY4DV1_STATE_CLIPPED ) ||
  64.          (obj->plist[poly].state & POLY4DV1_STATE_BACKFACE) )
  65.        continue; // move onto next poly
  66.     
  67.     // extract vertex indices into master list, rember the polygons are 
  68.     // NOT self contained, but based on the vertex list stored in the object
  69.     // itself
  70.     int vindex_0 = obj->plist[poly].vert[0];
  71.     int vindex_1 = obj->plist[poly].vert[1];
  72.     int vindex_2 = obj->plist[poly].vert[2];
  73.     // draw the triangle
  74.     Draw_Triangle_2D(obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y,
  75.              obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y,
  76.              obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y,
  77.              obj->plist[poly].color, video_buffer, lpitch);
  78.     } // end for poly
  79. } // end Draw_OBJECT4DV1_Solid
  80. ///////////////////////////////////////////////////////////////
  81. void Draw_RENDERLIST4DV1_Solid(RENDERLIST4DV1_PTR rend_list, 
  82.                               UCHAR *video_buffer, int lpitch)
  83. {
  84. // this function "executes" the render list or in other words
  85. // draws all the faces in the list in wire frame 8bit mode
  86. // note there is no need to sort wire frame polygons, but 
  87. // later we will need to, so hidden surfaces stay hidden
  88. // also, we leave it to the function to determine the bitdepth
  89. // and call the correct rasterizer
  90. // at this point, all we have is a list of polygons and it's time
  91. // to draw them
  92. for (int poly=0; poly < rend_list->num_polys; poly++)
  93.     {
  94.     // render this polygon if and only if it's not clipped, not culled,
  95.     // active, and visible, note however the concecpt of "backface" is 
  96.     // irrelevant in a wire frame engine though
  97.     if (!(rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_ACTIVE) ||
  98.          (rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_CLIPPED ) ||
  99.          (rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_BACKFACE) )
  100.        continue; // move onto next poly
  101.     // draw the triangle
  102.     Draw_Triangle_2D(rend_list->poly_ptrs[poly]->tvlist[0].x, rend_list->poly_ptrs[poly]->tvlist[0].y,
  103.              rend_list->poly_ptrs[poly]->tvlist[1].x, rend_list->poly_ptrs[poly]->tvlist[1].y,
  104.          rend_list->poly_ptrs[poly]->tvlist[2].x, rend_list->poly_ptrs[poly]->tvlist[2].y,
  105.              rend_list->poly_ptrs[poly]->color, video_buffer, lpitch);
  106.     } // end for poly
  107. } // end Draw_RENDERLIST4DV1_Solid
  108. /////////////////////////////////////////////////////////////
  109. void Draw_OBJECT4DV1_Solid16(OBJECT4DV1_PTR obj, 
  110.                             UCHAR *video_buffer, int lpitch)
  111.                      
  112. {
  113. // this function renders an object to the screen in wireframe, 
  114. // 16 bit mode, it has no regard at all about hidden surface removal, 
  115. // etc. the function only exists as an easy way to render an object 
  116. // without converting it into polygons, the function assumes all 
  117. // coordinates are screen coordinates, but will perform 2D clipping
  118. // iterate thru the poly list of the object and simply draw
  119. // each polygon
  120. for (int poly=0; poly < obj->num_polys; poly++)
  121.     {
  122.     // render this polygon if and only if it's not clipped, not culled,
  123.     // active, and visible, note however the concecpt of "backface" is 
  124.     // irrelevant in a wire frame engine though
  125.     if (!(obj->plist[poly].state & POLY4DV1_STATE_ACTIVE) ||
  126.          (obj->plist[poly].state & POLY4DV1_STATE_CLIPPED ) ||
  127.          (obj->plist[poly].state & POLY4DV1_STATE_BACKFACE) )
  128.        continue; // move onto next poly
  129.     
  130.     // extract vertex indices into master list, rember the polygons are 
  131.     // NOT self contained, but based on the vertex list stored in the object
  132.     // itself
  133.     int vindex_0 = obj->plist[poly].vert[0];
  134.     int vindex_1 = obj->plist[poly].vert[1];
  135.     int vindex_2 = obj->plist[poly].vert[2];
  136.     
  137.     // draw the triangle
  138.     Draw_Triangle_2D16(obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y,
  139.                obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y,
  140.                obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y,
  141.                obj->plist[poly].color, video_buffer, lpitch);
  142.     } // end for poly
  143. } // end Draw_OBJECT4DV1_Solid16
  144. ///////////////////////////////////////////////////////////////
  145. void Draw_RENDERLIST4DV1_Solid16(RENDERLIST4DV1_PTR rend_list, 
  146.                                 UCHAR *video_buffer, int lpitch)
  147. {
  148. // this function "executes" the render list or in other words
  149. // draws all the faces in the list in wire frame 16bit mode
  150. // note there is no need to sort wire frame polygons, but 
  151. // later we will need to, so hidden surfaces stay hidden
  152. // also, we leave it to the function to determine the bitdepth
  153. // and call the correct rasterizer
  154. // at this point, all we have is a list of polygons and it's time
  155. // to draw them
  156. for (int poly=0; poly < rend_list->num_polys; poly++)
  157.     {
  158.     // render this polygon if and only if it's not clipped, not culled,
  159.     // active, and visible, note however the concecpt of "backface" is 
  160.     // irrelevant in a wire frame engine though
  161.     if (!(rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_ACTIVE) ||
  162.          (rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_CLIPPED ) ||
  163.          (rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_BACKFACE) )
  164.        continue; // move onto next poly
  165.     // draw the triangle
  166.     Draw_Triangle_2D16(rend_list->poly_ptrs[poly]->tvlist[0].x, rend_list->poly_ptrs[poly]->tvlist[0].y,
  167.              rend_list->poly_ptrs[poly]->tvlist[1].x, rend_list->poly_ptrs[poly]->tvlist[1].y,
  168.          rend_list->poly_ptrs[poly]->tvlist[2].x, rend_list->poly_ptrs[poly]->tvlist[2].y,
  169.              rend_list->poly_ptrs[poly]->color, video_buffer, lpitch);
  170.     } // end for poly
  171. } // end Draw_RENDERLIST4DV1_Solid16
  172. // CLASS CPARSERV1 IMPLEMENTATION /////////////////////////////////////////
  173. // constructor /////////////////////////////////////////////////
  174. CPARSERV1::CPARSERV1()
  175. {
  176. #ifdef PARSER_DEBUG_ON
  177. printf("nEntering CPARSERV1() constructor.");
  178. #endif
  179. // reset file system
  180. fstream = NULL;
  181. Reset();
  182. } // end constructor
  183. // destructor ///////////////////////////////////////////////////
  184. CPARSERV1::~CPARSERV1() 
  185. #ifdef PARSER_DEBUG_ON
  186. printf("nEntering ~CPARSERV1() destructor.");
  187. #endif
  188. Reset();
  189. } // end destructor
  190. // reset file system ////////////////////////////////////////////
  191. int CPARSERV1::Reset()
  192. {
  193. #ifdef PARSER_DEBUG_ON
  194. printf("nEntering Reset().");
  195. #endif
  196. // reset file buffer
  197. if (fstream)
  198.    fclose(fstream);
  199. fstream = NULL;
  200. // clear and reset buffer
  201. memset(buffer, 0, sizeof(buffer));
  202. length    = 0;
  203. num_lines = 0;
  204. // set comment
  205. strcpy(comment, PARSER_DEFAULT_COMMENT);
  206. return(1);
  207. } // end Reset
  208. // open file /////////////////////////////////////////////////////
  209. int CPARSERV1::Open(char *filename)
  210. {
  211. #ifdef PARSER_DEBUG_ON
  212. printf("nEntering Open().");
  213. #endif
  214. // reset file system
  215. Reset();
  216. // opens a file
  217. if ((fstream = fopen(filename, "r"))!=NULL) 
  218.    {
  219. #ifdef PARSER_DEBUG_ON
  220. printf("nOpening file: %s", filename);
  221. #endif   
  222.    return(1);
  223.    } // end if
  224. else
  225.     {
  226. #ifdef PARSER_DEBUG_ON
  227. printf("nCouldn't open file: %s", filename);
  228. #endif
  229.     return(0);
  230.     } // end else
  231. } // end Open
  232. // close file ////////////////////////////////////////////////////
  233. int CPARSERV1::Close()
  234. {
  235. return(Reset());
  236. } // end Close
  237. // get line //////////////////////////////////////////////////////
  238. char *CPARSERV1::Getline(int mode)
  239. {
  240. #ifdef PARSER_DEBUG_ON
  241. printf("nEntering Getline().");
  242. #endif
  243. char *string;
  244. // gets a single line from the stream
  245. if (fstream)
  246.    {
  247.    // check translation mode
  248.    if (mode & PARSER_STRIP_EMPTY_LINES)
  249.        {
  250.        // get lines until we get a real one with data on it
  251.        while(1)
  252.             {      
  253.             // have we went to the end of the file without getting anything?
  254.             if ((string = fgets(buffer,PARSER_BUFFER_SIZE, fstream))==NULL)
  255.                break;
  256.             
  257.             // we have something, strip ws from it
  258.             int slength = strlen(string);
  259.             int sindex = 0;
  260.             // eat up space
  261.             while(isspace(string[sindex]))
  262.                  sindex++;
  263.             // is there anything left?
  264.             if ((slength - sindex) > 0)
  265.                {
  266.                // copy the string into place
  267.                memmove((void *)buffer, (void *)&string[sindex],  (slength - sindex)+1 );
  268.                string = buffer;
  269.                slength = strlen(string); 
  270.                 
  271.                // strip comments also?
  272.                if (mode & PARSER_STRIP_COMMENTS)
  273.                    {
  274.                    // does this begin with a comment or end with a comment?
  275.                    char *comment_string = strstr(string, comment);
  276.                    
  277.                    // 3 cases, no comment, comment at beginning, comment at end
  278.                    if (comment_string == NULL)
  279.                       break; // line is valid exit with line
  280.                    // compute index into string from beginning where comment begins
  281.                    int cindex = (int)(comment_string - string);
  282.                    // comment at beginning then continue
  283.                    
  284.                    if (cindex == 0)
  285.                       continue; // this line is a comment, ignore completely, get another
  286.                    else
  287.                       {
  288.                       // comment at end, strip it, insert null where it begins
  289.                       comment_string[0] = 0;
  290.                       break;
  291.                       } // end else
  292.                    } // end if 
  293.                // exit loop, we have something :)
  294.                break;
  295.                } // end if
  296.             } // end while
  297.        } // end if strip mode
  298.     else
  299.        { 
  300.        // just get the next line, don't worry about stripping anything
  301.        string = fgets(buffer,PARSER_BUFFER_SIZE, fstream);
  302.        } // end else 
  303.   
  304.    // was the line valid?
  305.    if (string)
  306.       {
  307.       // increment line count
  308.       num_lines++;
  309.       // final stripping of whitspace
  310.       if (mode & PARSER_STRIP_WS_ENDS)
  311.          {
  312.          StringLtrim(buffer);
  313.          StringRtrim(buffer);
  314.          } // end if         
  315.       // compute line length
  316.       length = strlen(buffer);
  317. #ifdef PARSER_DEBUG_ON
  318. printf("nString[%d]:%s", length, string);
  319. #endif
  320.       // return the pointer, copy of data already in buffer
  321.       return(string);     
  322.         
  323.       } // end if
  324.    else
  325.       {
  326. #ifdef PARSER_DEBUG_ON
  327. printf("nEOF");
  328. #endif
  329.       return(NULL);
  330.       }
  331.    
  332.    } // end if
  333. else
  334.    {
  335. #ifdef PARSER_DEBUG_ON
  336. printf("nFstream NULL.");
  337. #endif
  338.    return(NULL);
  339.    } // end else
  340. } // end Getline
  341. // sets the comment string ///////////////////////////////////////
  342. int CPARSERV1::SetComment(char *string)
  343. {
  344. // sets the comment string
  345. if (strlen(string) < PARSER_MAX_COMMENT)
  346.    {
  347.    strcpy(comment, string);
  348.    return(1);
  349.    } // end if
  350. else
  351.    return(0);
  352. } // end SetComment
  353. // find pattern in line //////////////////////////////////////////
  354. int CPARSERV1::Pattern_Match(char *string, char *pattern, ...)
  355. {
  356. // this function tries to match the pattern sent in pattern with
  357. // the sent string, the results are sent back to the sender in the
  358. // variable arguments as well as stored in the parameter passing area
  359. // string literal                        = ['string']
  360. // floating point number                 = [f]
  361. // integer number                        = [i]
  362. // match a string exactly ddd chars      = [s=ddd] 
  363. // match a string less than ddd chars    = [s<ddd] 
  364. // match a string greater than ddd chars = [s>ddd]
  365. // for example to match "vertex: 34.234 56.34 12.4
  366. // ['vertex'] [f] [f] [f]
  367. char token_type[PATTERN_MAX_ARGS];         // type of token, f,i,s,l
  368. char token_string[PATTERN_MAX_ARGS][PATTERN_BUFFER_SIZE];   // for literal strings this holds them
  369. char token_operator[PATTERN_MAX_ARGS];     // holds and operators for the token, >, <, =, etc.
  370. int  token_numeric[PATTERN_MAX_ARGS];      // holds any numeric data to qualify the token
  371. char buffer[PARSER_BUFFER_SIZE]; // working buffer
  372. // a little error testing
  373. if ( (!string || strlen(string)==0) || (!pattern || strlen(pattern)==0) )
  374.    return(0);
  375. // copy line into working area
  376. strcpy(buffer, string);
  377. int tok_start  = 0, 
  378.     tok_end    = 0,
  379.     tok_restart = 0,
  380.     tok_first_pass = 0,
  381.     num_tokens = 0;
  382. // step 1: extract token list
  383. while(1)
  384.     {
  385.     // eat whitepace
  386.     while(isspace(pattern[tok_start]) )
  387.           tok_start++;
  388.     
  389.     // end of line?
  390.     if (tok_start >= strlen(pattern))
  391.        break;    
  392.     // look for beginning of token '['
  393.     if (pattern[tok_start] == '[')
  394.        {
  395.        // now look for token code
  396.        switch(pattern[tok_start+1])
  397.              {
  398.              case PATTERN_TOKEN_FLOAT:  // float
  399.              {
  400.              // make sure token is well formed
  401.              if (pattern[tok_start+2]!=']')
  402.                 return(0); // error
  403.              // advance token scanner
  404.              tok_start+=3;
  405.              // insert a float into pattern
  406.              token_type[num_tokens] = PATTERN_TOKEN_FLOAT;  // type of token, f,i,s,l
  407.              strcpy(token_string[num_tokens],"");           // for literal strings this holds them
  408.              token_operator[num_tokens] = 0;                // holds and operators for the token, >, <, =, etc.
  409.              token_numeric[num_tokens]  = 0; 
  410.              // increment number of tokens
  411.              num_tokens++;
  412. #ifdef PARSER_DEBUG_ON
  413. printf("nFound Float token");
  414. #endif
  415.              } break;
  416.              case PATTERN_TOKEN_INT:  // integer
  417.              {
  418.              // make sure token is well formed
  419.              if (pattern[tok_start+2]!=']')
  420.                 return(0); // error
  421.              // advance token scanner
  422.              tok_start+=3;
  423.              // insert a int into pattern
  424.              token_type[num_tokens] = PATTERN_TOKEN_INT;  // type of token, f,i,s,l
  425.              strcpy(token_string[num_tokens],"");         // for literal strings this holds them
  426.              token_operator[num_tokens] = 0;              // holds and operators for the token, >, <, =, etc.
  427.              token_numeric[num_tokens]  = 0; 
  428.              // increment number of tokens
  429.              num_tokens++;
  430. #ifdef PARSER_DEBUG_ON
  431. printf("nFound Int token");
  432. #endif
  433.              } break;
  434.              case PATTERN_TOKEN_LITERAL: // literal string
  435.              {
  436.              // advance token scanner to begining literal string
  437.              tok_start+=2;
  438.              tok_end = tok_start;
  439.               
  440.              // eat up string
  441.              while(pattern[tok_end]!=PATTERN_TOKEN_LITERAL)
  442.                   tok_end++;
  443.              // make sure string is well formed
  444.              if (pattern[tok_end+1]!=']')
  445.                 return(0);
  446.              // insert a string into pattern              
  447.  
  448.              // literal string lies from (tok_start - (tok_end-1)
  449.              memcpy(token_string[num_tokens], &pattern[tok_start], (tok_end - tok_start) );
  450.              token_string[num_tokens][(tok_end - tok_start)] = 0; // null terminate
  451.              
  452.              token_type[num_tokens] = PATTERN_TOKEN_LITERAL;  // type of token, f,i,s,'
  453.              token_operator[num_tokens] = 0;                 // holds and operators for the token, >, <, =, etc.
  454.              token_numeric[num_tokens]  = 0; 
  455. #ifdef PARSER_DEBUG_ON
  456. printf("nFound Literal token = %s",token_string[num_tokens]);
  457. #endif
  458.              // advance token scanner
  459.              tok_start = tok_end+2;
  460.              // increment number of tokens
  461.              num_tokens++;
  462.              } break;
  463.              case PATTERN_TOKEN_STRING: // ascii string varying length
  464.              {
  465.              // look for comparator
  466.              if (pattern[tok_start+2] == '=' || 
  467.                  pattern[tok_start+2] == '>' ||
  468.                  pattern[tok_start+2] == '<' )
  469.                 {
  470.                 // extract the number
  471.                 tok_end = tok_start+3;
  472.                 while( isdigit(pattern[tok_end] ) )
  473.                       tok_end++;
  474.                 // check for well formed
  475.                 if (pattern[tok_end]!=']')
  476.                    return(0);
  477.                 // copy number in ascii to string and convert to real number
  478.                 memcpy(buffer, &pattern[tok_start+3], (tok_end - tok_start) );
  479.                 buffer[tok_end - tok_start] = 0;
  480.                  
  481.                 // insert a string into pattern
  482.                 token_type[num_tokens] = PATTERN_TOKEN_STRING;     // type of token, f,i,s,l
  483.                 strcpy(token_string[num_tokens],"");               // for literal strings this holds them
  484.                 token_operator[num_tokens] = pattern[tok_start+2]; // holds and operators for the token, >, <, =, etc.
  485.                 token_numeric[num_tokens]  = atoi(buffer);
  486.                 } // end if
  487.              else
  488.                  return(0); // not well formed
  489. #ifdef PARSER_DEBUG_ON
  490. printf("nFound String token, comparator: %c, characters: %d", token_operator[num_tokens], token_numeric[num_tokens]);
  491. #endif
  492.              // advance token scanner
  493.              tok_start = tok_end+1; 
  494.              // increment number of tokens
  495.              num_tokens++;
  496.              } break;             
  497.              default: break;
  498.              } // end switch
  499.        } // end if
  500.     
  501.     // end of line?
  502.     if (tok_start >= strlen(pattern))
  503.        break;    
  504.     } // end while
  505. #ifdef PARSER_DEBUG_ON
  506. printf("nstring to parse: %s", string);
  507. printf("nPattern to scan for: %s", pattern);
  508. printf("nnumber of tokens found %d", num_tokens);
  509. #endif
  510. // at this point we have the pattern we need to look for, so look for it
  511. int pattern_state = PATTERN_STATE_INIT; // initial state for pattern recognizer
  512. int curr_tok = 0;                 // test for num_tokens
  513. char token[PATTERN_BUFFER_SIZE];  // token under consideration
  514. // enter scan state machine
  515. while(1)
  516.       {
  517.       switch(pattern_state)
  518.       {
  519.       case PATTERN_STATE_INIT:
  520.            {
  521.            // initial state for pattern
  522.            strcpy(buffer, string);
  523.            tok_start      = 0; 
  524.            tok_end        = 0;
  525.            tok_restart    = 0;
  526.            tok_first_pass = 1;
  527.            curr_tok       = 0;
  528.            // reset output arrays
  529.            num_pints = num_pfloats = num_pstrings = 0;
  530.            // transition to restart         
  531.            pattern_state = PATTERN_STATE_RESTART;
  532.            } break;
  533.  
  534.       case PATTERN_STATE_RESTART:
  535.            {
  536.            // pattern may still be here?
  537.            curr_tok       = 0;
  538.            tok_first_pass = 1;
  539.            // error detection
  540.            if (tok_end >= strlen(buffer))
  541.               return(0);
  542.            // restart scanner after first token from last pass
  543.            tok_start = tok_end = tok_restart;
  544.            // start validating tokens
  545.            pattern_state = PATTERN_STATE_NEXT;
  546.            } break;
  547.  
  548.       case PATTERN_STATE_NEXT:
  549.            {
  550.            // have we matched pattern yet?
  551.            if (curr_tok >= num_tokens)
  552.            {   
  553.            pattern_state = PATTERN_STATE_MATCH;       
  554.            }
  555.            else
  556.            {
  557.            // get next token
  558.            if (tok_end >= strlen(buffer))
  559.               return(0);
  560.            tok_start = tok_end;
  561.            while(isspace(buffer[tok_start])) tok_start++;
  562.            tok_end = tok_start;
  563.            while(!isspace(buffer[tok_end]) && tok_end < strlen(buffer)) tok_end++;
  564.               
  565.            // copy token
  566.            memcpy(token, &buffer[tok_start], tok_end - tok_start);
  567.            token[tok_end - tok_start] = 0;
  568.            // check for error
  569.            if (strlen(token)==0) 
  570.               return(0);
  571.            // remember position of first token, so we can restart after it on next pass
  572.            // if need
  573.            if (tok_first_pass)
  574.                {
  575.                tok_first_pass = 0; 
  576.                tok_restart = tok_end;
  577.                } // end if
  578.            // we have the token, set state to check for that token
  579.            switch(token_type[curr_tok])
  580.                  {
  581.                  case PATTERN_TOKEN_FLOAT:
  582.                       {
  583.                       pattern_state = PATTERN_STATE_FLOAT;
  584.                       } break;
  585.                  case PATTERN_TOKEN_INT:    
  586.                       {
  587.                       pattern_state = PATTERN_STATE_INT;
  588.                       } break;
  589.                  case PATTERN_TOKEN_STRING: 
  590.                       {
  591.                       pattern_state = PATTERN_STATE_STRING;
  592.                       } break;
  593.                  case PATTERN_TOKEN_LITERAL:
  594.                       {
  595.                       pattern_state = PATTERN_STATE_LITERAL;
  596.                       } break;
  597.                  default: break;
  598.                  } // end switch
  599.            
  600.            } // end else
  601.            } break;
  602.  
  603.       case PATTERN_STATE_FLOAT:
  604.            {
  605.            // simply validate this token as a float
  606.            float f = IsFloat(token);
  607.            if (f!=FLT_MIN)
  608.               {
  609.               pfloats[num_pfloats++] = f;
  610.               // get next token
  611.               curr_tok++;
  612.               pattern_state = PATTERN_STATE_NEXT;
  613.               } // end if                
  614.            else
  615.               {
  616.               // error pattern doesn't match, restart
  617.               pattern_state = PATTERN_STATE_RESTART;
  618.               } // end else
  619.            } break;
  620.  
  621.       case PATTERN_STATE_INT:
  622.            {
  623.            // simply validate this token as a int
  624.            int i = IsInt(token);
  625.            if (i!=INT_MIN)
  626.               {
  627.               pints[num_pints++] = i;
  628.               // get next token
  629.               curr_tok++;
  630.               pattern_state = PATTERN_STATE_NEXT;
  631.               } // end if                
  632.            else
  633.               {
  634.               // error pattern doesn't match, restart
  635.               pattern_state = PATTERN_STATE_RESTART;
  636.               } // end else
  637.            } break;
  638.  
  639.       case PATTERN_STATE_LITERAL:
  640.            {
  641.            // simply validate this token by comparing to data in table
  642.            if (strcmp(token, token_string[curr_tok]) == 0)
  643.               {
  644.               // increment number of pstrings found and insert into table
  645.               strcpy(pstrings[num_pstrings++], token);
  646.               // get next token
  647.               curr_tok++;
  648.               pattern_state = PATTERN_STATE_NEXT;
  649.               } // end if                
  650.            else
  651.               {
  652.               // error pattern doesn't match, restart
  653.               pattern_state = PATTERN_STATE_RESTART;
  654.               } // end else
  655.            } break;
  656.   
  657.       case PATTERN_STATE_STRING:
  658.            {
  659.            // need to test for non-space chars 
  660.            // get comparator
  661.            switch(token_operator[curr_tok])
  662.                  {
  663.                  case '=':
  664.                  {
  665.                  // we need exactly
  666.                  if (strlen(token) == token_numeric[curr_tok])
  667.                     {
  668.                     // put this string into table
  669.                     strcpy(pstrings[num_pstrings++], token);
  670.                     // get next token
  671.                     curr_tok++;
  672.                     pattern_state = PATTERN_STATE_NEXT;
  673.                     } // end if    
  674.                  else
  675.                     {
  676.                     // error pattern doesn't match, restart
  677.                     pattern_state = PATTERN_STATE_RESTART;
  678.                     } // end else
  679.                  } break; 
  680.                  case '>':
  681.                  {
  682.                  // we need greater than
  683.                  if (strlen(token) > token_numeric[curr_tok])
  684.                     {
  685.                     // put this string into table
  686.                     strcpy(pstrings[num_pstrings++], token);
  687.                     // get next token
  688.                     curr_tok++;
  689.                     pattern_state = PATTERN_STATE_NEXT;
  690.                     } // end if  
  691.                  else
  692.                     {
  693.                     // error pattern doesn't match, restart
  694.                     pattern_state = PATTERN_STATE_RESTART;
  695.                     } // end else
  696.   
  697.                  } break; 
  698.                  case '<':
  699.                  {
  700.                  // we need less than
  701.                  if (strlen(token) < token_numeric[curr_tok])
  702.                     {
  703.                     // put this string into table
  704.                     strcpy(pstrings[num_pstrings++], token);
  705.                     // get next token
  706.                     curr_tok++;
  707.                     pattern_state = PATTERN_STATE_NEXT;
  708.                     } // end if    
  709.                  else
  710.                     {
  711.                     // error pattern doesn't match, restart
  712.                     pattern_state = PATTERN_STATE_RESTART;
  713.                     } // end else
  714.                  } break; 
  715.                  default: break;
  716.                  } // end switch
  717.            } break;
  718.    
  719.       case PATTERN_STATE_MATCH:
  720.            {
  721.            // we have matched the string, output vars into variable arg list
  722. #ifdef PARSER_DEBUG_ON
  723. printf("nPattern: %s matched!", pattern);
  724. #endif
  725.        
  726.            return(1);
  727.            } break;
  728.     
  729.       case PATTERN_STATE_END: { } break;
  730.       default: break;
  731.       } // end switch            
  732.       } // end while
  733. } // end Pattern_Match
  734. // END IMPLEMENTATION OF CPARSERV1 CLASS ///////////////////////////////////
  735. int ReplaceChars(char *string_in, char *string_out, char *replace_chars, char rep_char, int case_on)
  736. {
  737. // this function simply replaces the characters from the input string that
  738. // are listed in replace with the replace char, the results are stored in 
  739. // string_out, string_in and isn't touched, the number of replacments is 
  740. // returned. if case_on = 1 then case is checked, other it's case insensitive
  741. int num_replacements = 0,  // tracks number of characters replaced
  742.     index_in     = 0,      // curr index into input
  743.     index_out    = 0,      // curr index into output
  744.     sindex,                // loop var into strip array
  745.     slength = strlen(replace_chars); // length of strip string
  746. // do some error checking
  747. if (!string_in || !string_out || strlen(string_in) == 0)
  748.    return(0);
  749. // nothing to replace
  750. if (!replace_chars || strlen(replace_chars)==0)
  751.    {
  752.    strcpy(string_out, string_in);
  753.    return(0);
  754.    } // end if
  755. // determine if case is important
  756. if (case_on==1)
  757. {
  758. // perform char by char copy
  759. while(string_in[index_in])
  760.      {
  761.      for (sindex = 0; sindex < slength; sindex++)
  762.          if (string_in[index_in] == replace_chars[sindex])
  763.             {
  764.             // replace it
  765.             string_out[index_out++] = rep_char;
  766.             index_in++;
  767.             num_replacements++;
  768.             break;
  769.             } // end if
  770.      
  771.      // was a replacement performed?, no just copy then
  772.      if (sindex >= slength)
  773.         string_out[index_out++] = string_in[index_in++];
  774.      } // end while
  775. } // end if case_on
  776. else
  777. {
  778. // perform char by char copy with case insensitivity
  779. while(string_in[index_in])
  780.      {
  781.      for (sindex = 0; sindex < slength; sindex++)
  782.          if (toupper(string_in[index_in]) == toupper(replace_chars[sindex]))
  783.             {
  784.             // replace it
  785.             string_out[index_out++] = rep_char;
  786.             index_in++;
  787.             num_replacements++;
  788.             break;
  789.             } // end if
  790.      
  791.      // was a strip char found?
  792.      if (sindex >= slength)
  793.         string_out[index_out++] = string_in[index_in++];
  794.      } // end while
  795. } // end if case_off
  796. // terminate output string
  797. string_out[index_out] = 0;
  798. // return extracts
  799. return(num_replacements);
  800. } // end ReplaceChars
  801. //////////////////////////////////////////////////////////////////////////////////
  802. int StripChars(char *string_in, char *string_out, char *strip_chars, int case_on)
  803. {
  804. // this function simply strips/extracts the characters from the input string that
  805. // are listed in strip, the results are stored in string_out, string_in
  806. // isn't touched, the number of extractions or returned
  807. // if case_on = 1 then case is checked, other it's case insensitive
  808. int num_extracts = 0,  // tracks number of characters extracted
  809.     index_in     = 0,  // curr index into input
  810.     index_out    = 0,  // curr index into output
  811.     sindex,            // loop var into strip array
  812.     slength = strlen(strip_chars); // length of strip string
  813. // do some error checking
  814. if (!string_in || !string_out || strlen(string_in) == 0)
  815.    return(0);
  816. // nothing to replace
  817. if (!strip_chars || strlen(strip_chars)==0)
  818.    {
  819.    strcpy(string_out, string_in);
  820.    return(0);
  821.    } // end if
  822. // determine if case is importants
  823. if (case_on==1)
  824. {
  825. // perform char by char copy
  826. while(string_in[index_in])
  827.      {
  828.      for (sindex = 0; sindex < slength; sindex++)
  829.          if (string_in[index_in] == strip_chars[sindex])
  830.             {
  831.             // jump over input char, it's stripped
  832.             index_in++;
  833.             num_extracts++;
  834.             break;
  835.             } // end if
  836.      
  837.      // was a strip char found?
  838.      if (sindex >= slength)
  839.         string_out[index_out++] = string_in[index_in++];
  840.      } // end while
  841. } // end if case_on
  842. else
  843. {
  844. // perform char by char copy with case insensitivity
  845. while(string_in[index_in])
  846.      {
  847.      for (sindex = 0; sindex < slength; sindex++)
  848.          if (toupper(string_in[index_in]) == toupper(strip_chars[sindex]))
  849.             {
  850.             // jump over input char, it's stripped
  851.             index_in++;
  852.             num_extracts++;
  853.             break;
  854.             } // end if
  855.      
  856.      // was a strip char found?
  857.      if (sindex >= slength)
  858.         string_out[index_out++] = string_in[index_in++];
  859.      } // end while
  860. } // end if case_off
  861. // terminate output string
  862. string_out[index_out] = 0;
  863. // return extracts
  864. return(num_extracts);
  865. } // end StripChars
  866. ////////////////////////////////////////////////////////////////////////////
  867. int IsInt(char *istring)
  868. {
  869. // validates the sent string as a int and converts it, if it's not valid
  870. // the function sends back INT_MIN, the chances of this being the number
  871. // validated is slim
  872. // [whitespace] [sign]digits
  873. char *string = istring;
  874. // must be of the form
  875. // [whitespace] 
  876. while(isspace(*string)) string++;
  877. // [sign] 
  878. if (*string=='+' || *string=='-') string++;
  879. // [digits] 
  880. while(isdigit(*string)) string++;
  881. // the string better be the same size as the other one
  882. if (strlen(istring) == (int)(string - istring))
  883.    return(atoi(istring));
  884. else
  885.    return(INT_MIN);
  886. } // end IsInt
  887. //////////////////////////////////////////////////////////////////////////////
  888. float IsFloat(char *fstring)
  889. {
  890. // validates the sent string as a float and converts it, if it's not valid
  891. // the function sends back FLT_MIN, the chances of this being the number
  892. // validated is slim
  893. // [whitespace] [sign] [digits] [.digits] [ {d | D | e | E }[sign]digits]
  894. char *string = fstring;
  895. // must be of the form
  896. // [whitespace] 
  897. while(isspace(*string)) string++;
  898. // [sign] 
  899. if (*string=='+' || *string=='-') string++;
  900. // [digits] 
  901. while(isdigit(*string)) string++;
  902. // [.digits] 
  903. if (*string =='.') 
  904.    {
  905.    string++;
  906.    while(isdigit(*string)) string++;
  907.    }
  908. // [ {d | D | e | E }[sign]digits]
  909. if (*string =='e' || *string == 'E' || *string =='d' || *string == 'D')
  910.    {
  911.    string++;
  912.    // [sign] 
  913.    if (*string=='+' || *string=='-') string++;
  914.    // [digits] 
  915.    while(isdigit(*string)) string++;
  916.    } 
  917. // the string better be the same size as the other one
  918. if (strlen(fstring) == (int)(string - fstring))
  919.    return(atof(fstring));
  920. else
  921.    return(FLT_MIN);
  922. } // end IsFloat
  923. ////////////////////////////////////////////////////////////////////////////
  924. char *StringRtrim(char *string)
  925. {
  926. // trims whitespace from right side, note is destructive
  927. int sindex = 0;
  928. int slength = strlen(string);
  929. if (!string || slength == 0) return(string);
  930. // index to end of string
  931. sindex = slength - 1;
  932. // trim whitespace by overwriting nulls
  933. while( isspace(string[sindex]) && sindex >= 0)
  934.      string[sindex--] = 0;
  935. // string doens't need to be moved, so simply return pointer
  936. return(string);
  937. } // end StringRtrim
  938. ////////////////////////////////////////////////////////////////////////////
  939. char *StringLtrim(char *string)
  940. {
  941. // trims whitespace from left side, note is destructive
  942. int sindex = 0;
  943. int slength = strlen(string);
  944. if (!string || slength == 0) return(string);
  945. // trim whitespace by advancing pointer
  946. while(isspace(string[sindex]) && sindex < slength)
  947.      string[sindex++] = 0; // not needed actually
  948. // copy string to left
  949. memmove((void *)string, (void *)&string[sindex], (slength - sindex)+1);
  950. // now return pointer
  951. return(string);
  952. } // end StringLtrim
  953. ////////////////////////////////////////////////////////////////////////////
  954. int Convert_Bitmap_8_16(BITMAP_FILE_PTR bitmap)
  955. {
  956. // function converts a bitmap from 8 bit to 16 bit based on the palette
  957. // and the current bitformat
  958. // is this a valid bitmap file?
  959. if (!bitmap || !bitmap->buffer)
  960.    return(0);
  961. // cache vars
  962. int bwidth = bitmap->bitmapinfoheader.biWidth;
  963. int bheight = bitmap->bitmapinfoheader.biHeight;
  964. // allocate temporary buffer
  965. USHORT *buffer16 = (USHORT *)malloc(bwidth * bheight * 2);
  966. // for each pixel in the bitmap convert it to a 16 bit value
  967. for (int pixel = 0; pixel < bwidth*bheight; pixel++)
  968.     {
  969.     // get 8-bit color index
  970.     int cindex = bitmap->buffer[pixel];
  971.     // build 16-bit pixel with extractd RGB values for the pixel in palette
  972.     buffer16[pixel] = RGB16Bit(bitmap->palette[cindex].peRed, 
  973.                                bitmap->palette[cindex].peGreen,
  974.                                bitmap->palette[cindex].peBlue);
  975.     } // end for
  976. // now change file information, actually not much has changed, but what the heck
  977. bitmap->bitmapinfoheader.biBitCount     = 16; 
  978. bitmap->bitmapinfoheader.biSizeImage    = bwidth*bheight*2;
  979. bitmap->bitmapinfoheader.biWidth        = bwidth;
  980. bitmap->bitmapinfoheader.biHeight       = bheight;
  981. bitmap->bitmapinfoheader.biClrUsed      = 0;
  982. bitmap->bitmapinfoheader.biClrImportant = 0;
  983. // finally copy temp buffer
  984. free( bitmap->buffer );
  985. // link new buffer
  986. bitmap->buffer = (UCHAR *)buffer16;
  987. #if 1
  988. // write the file info out 
  989. printf("nsize=%d nwidth=%d nheight=%d nbitsperpixel=%d ncolors=%d nimpcolors=%d",
  990.         bitmap->bitmapinfoheader.biSizeImage,
  991.         bitmap->bitmapinfoheader.biWidth,
  992.         bitmap->bitmapinfoheader.biHeight,
  993. bitmap->bitmapinfoheader.biBitCount,
  994.         bitmap->bitmapinfoheader.biClrUsed,
  995.         bitmap->bitmapinfoheader.biClrImportant);
  996. #endif
  997. // return success
  998. return(1);
  999. } // end Convert_Bitmap_8_16
  1000. ///////////////////////////////////////////////////////////////////////////////
  1001. int Load_OBJECT4DV1_3DSASC(OBJECT4DV1_PTR obj,   // pointer to object
  1002.                            char *filename,       // filename of ASC file
  1003.                            VECTOR4D_PTR scale,   // initial scaling factors
  1004.                            VECTOR4D_PTR pos,     // initial position
  1005.                            VECTOR4D_PTR rot,     // initial rotations
  1006.                            int vertex_flags)     // flags to re-order vertices
  1007. {
  1008. // this function loads a 3D Studi .ASC file object in off disk, additionally
  1009. // it allows the caller to scale, position, and rotate the object
  1010. // to save extra calls later for non-dynamic objects
  1011. // create a parser object
  1012. CPARSERV1 parser; 
  1013. char seps[16];          // seperators for token scanning
  1014. char token_buffer[256]; // used as working buffer for token
  1015. char *token;            // pointer to next token
  1016. int r,g,b;              // working colors
  1017. // Step 1: clear out the object and initialize it a bit
  1018. memset(obj, 0, sizeof(OBJECT4DV1));
  1019. // set state of object to active and visible
  1020. obj->state = OBJECT4DV1_STATE_ACTIVE | OBJECT4DV1_STATE_VISIBLE;
  1021. // set position of object is caller requested position
  1022. if (pos)
  1023.    {
  1024.    // set position of object
  1025.    obj->world_pos.x = pos->x;
  1026.    obj->world_pos.y = pos->y;
  1027.    obj->world_pos.z = pos->z;
  1028.    obj->world_pos.w = pos->w;
  1029.    } // end 
  1030. else
  1031.    {
  1032.    // set it to (0,0,0,1)
  1033.    obj->world_pos.x = 0;
  1034.    obj->world_pos.y = 0;
  1035.    obj->world_pos.z = 0;
  1036.    obj->world_pos.w = 1;
  1037.    } // end else
  1038. // Step 2: open the file for reading using the parser
  1039. if (!parser.Open(filename))
  1040.    {
  1041.    Write_Error("Couldn't open .ASC file %s.", filename);
  1042.    return(0);
  1043.    } // end if
  1044. // Step 3: 
  1045. // lets find the name of the object first 
  1046. while(1)
  1047.      {
  1048.      // get the next line, we are looking for "Named object:"
  1049.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1050.         {
  1051.         Write_Error("Image 'name' not found in .ASC file %s.", filename);
  1052.         return(0);
  1053.         } // end if
  1054.     
  1055.      // check for pattern?  
  1056.      if (parser.Pattern_Match(parser.buffer, "['Named'] ['object:']"))
  1057.         {
  1058.         // at this point we have the string with the name in it, parse it out by finding 
  1059.         // name between quotes "name...."
  1060.         strcpy(token_buffer, parser.buffer);
  1061.         strcpy(seps, """);        
  1062.         strtok( token_buffer, seps );
  1063.         
  1064.         // this will be the token between the quotes
  1065.         token = strtok(NULL, seps);
  1066.       
  1067.         // copy name into structure
  1068.         strcpy(obj->name, token);          
  1069.         Write_Error("nASC Reader Object Name: %s", obj->name);
  1070.         break;    
  1071.         } // end if
  1072.      } // end while
  1073. // step 4: get number of vertices and polys in object
  1074. while(1)
  1075.      {
  1076.      // get the next line, we are looking for "Tri-mesh, Vertices:" 
  1077.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1078.         {
  1079.         Write_Error("'Tri-mesh' line not found in .ASC file %s.", filename);
  1080.         return(0);
  1081.         } // end if
  1082.     
  1083.      // check for pattern?  
  1084.      if (parser.Pattern_Match(parser.buffer, "['Tri-mesh,'] ['Vertices:'] [i] ['Faces:'] [i]"))
  1085.         {
  1086.         // simply extract the number of vertices and polygons from the pattern matching 
  1087.         // output arrays
  1088.         obj->num_vertices = parser.pints[0];
  1089.         obj->num_polys    = parser.pints[1];
  1090.         Write_Error("nASC Reader Num Vertices: %d, Num Polys: %d", 
  1091.                     obj->num_vertices, obj->num_polys);
  1092.         break;    
  1093.  
  1094.         } // end if
  1095.      } // end while
  1096. // Step 5: load the vertex list
  1097. // advance parser to vertex list denoted by:
  1098. // "Vertex list:"
  1099. while(1)
  1100.      {
  1101.      // get next line
  1102.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1103.         {
  1104.         Write_Error("n'Vertex list:' line not found in .ASC file %s.", filename);
  1105.         return(0);
  1106.         } // end if
  1107.     
  1108.      // check for pattern?  
  1109.      if (parser.Pattern_Match(parser.buffer, "['Vertex'] ['list:']"))
  1110.         {
  1111.         Write_Error("nASC Reader found vertex list in .ASC file %s.", filename);
  1112.         break;
  1113.         } // end if
  1114.      } // end while
  1115. // now read in vertex list, format:
  1116. // "Vertex: d  X:d.d Y:d.d  Z:d.d"
  1117.  for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1118.      {
  1119.      // hunt for vertex
  1120.      while(1)
  1121.      {
  1122.      // get the next vertex
  1123.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1124.         {
  1125.         Write_Error("nVertex list ended abruptly! in .ASC file %s.", filename);
  1126.         return(0);
  1127.         } // end if
  1128.     
  1129.      // strip all ":XYZ", make this easier, note use of input and output as same var, this is legal
  1130.      // since the output is guaranteed to be the same length or shorter as the input :)
  1131.      StripChars(parser.buffer, parser.buffer, ":XYZ");
  1132.      
  1133.      // check for pattern?  
  1134.      if (parser.Pattern_Match(parser.buffer, "['Vertex'] [i] [f] [f] [f]"))
  1135.         {
  1136.         // at this point we have the x,y,z in the the pfloats array locations 0,1,2
  1137.         obj->vlist_local[vertex].x = parser.pfloats[0];
  1138.         obj->vlist_local[vertex].y = parser.pfloats[1];
  1139.         obj->vlist_local[vertex].z = parser.pfloats[2];
  1140.         obj->vlist_local[vertex].w = 1;
  1141.         // do vertex swapping right here, allow muliple swaps, why not!
  1142.         // defines for vertex re-ordering flags
  1143.         //#define VERTEX_FLAGS_INVERT_X   1    // inverts the Z-coordinates
  1144.         //#define VERTEX_FLAGS_INVERT_Y   2    // inverts the Z-coordinates
  1145.         //#define VERTEX_FLAGS_INVERT_Z   4    // inverts the Z-coordinates
  1146.         //#define VERTEX_FLAGS_SWAP_YZ    8    // transforms a RHS model to a LHS model
  1147.         //#define VERTEX_FLAGS_SWAP_XZ    16   // ???
  1148.         //#define VERTEX_FLAGS_SWAP_XY    32
  1149.         //#define VERTEX_FLAGS_INVERT_WINDING_ORDER 64  // invert winding order from cw to ccw or ccw to cc
  1150.         float temp_f; // used for swapping
  1151.         // invert signs?
  1152.         if (vertex_flags & VERTEX_FLAGS_INVERT_X)
  1153.            obj->vlist_local[vertex].x=-obj->vlist_local[vertex].x;
  1154.         if (vertex_flags & VERTEX_FLAGS_INVERT_Y)
  1155.            obj->vlist_local[vertex].y=-obj->vlist_local[vertex].y;
  1156.         if (vertex_flags & VERTEX_FLAGS_INVERT_Z)
  1157.            obj->vlist_local[vertex].z=-obj->vlist_local[vertex].z;
  1158.         // swap any axes?
  1159.         if (vertex_flags & VERTEX_FLAGS_SWAP_YZ)
  1160.            SWAP(obj->vlist_local[vertex].y, obj->vlist_local[vertex].z, temp_f);
  1161.         
  1162.         if (vertex_flags & VERTEX_FLAGS_SWAP_XZ)
  1163.            SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].z, temp_f);
  1164.         if (vertex_flags & VERTEX_FLAGS_SWAP_XY)
  1165.            SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].y, temp_f);
  1166.         Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
  1167.                                            obj->vlist_local[vertex].x, 
  1168.                                            obj->vlist_local[vertex].y, 
  1169.                                            obj->vlist_local[vertex].z,
  1170.                                            obj->vlist_local[vertex].w);
  1171.         // scale vertices
  1172.         if (scale)
  1173.            {
  1174.            obj->vlist_local[vertex].x*=scale->x;
  1175.            obj->vlist_local[vertex].y*=scale->y;
  1176.            obj->vlist_local[vertex].z*=scale->z;
  1177.            } // end if
  1178.         // found vertex, break out of while for next pass
  1179.         break;
  1180.         } // end if
  1181.     } // end while
  1182.     } // end for vertex
  1183. // compute average and max radius
  1184. Compute_OBJECT4DV1_Radius(obj);
  1185. Write_Error("nObject average radius = %f, max radius = %f", 
  1186.             obj->avg_radius, obj->max_radius);
  1187. // step 6: load in the polygons
  1188. // poly list starts off with:
  1189. // "Face list:"
  1190. while(1)
  1191.      {
  1192.      // get next line
  1193.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1194.         {
  1195.         Write_Error("n'Face list:' line not found in .ASC file %s.", filename);
  1196.         return(0);
  1197.         } // end if
  1198.     
  1199.      // check for pattern?  
  1200.      if (parser.Pattern_Match(parser.buffer, "['Face'] ['list:']"))
  1201.         {
  1202.         Write_Error("nASC Reader found face list in .ASC file %s.", filename);
  1203.         break;
  1204.         } // end if
  1205.      } // end while
  1206. // now read each face in format:
  1207. // Face ddd:    A:ddd B:ddd C:ddd AB:1|0 BC:1|0 CA:1|
  1208. // Material:"rdddgdddbddda0"
  1209. // the A, B, C part is vertex 0,1,2 but the AB, BC, CA part
  1210. // has to do with the edges and the vertex ordering
  1211. // the material indicates the color, and has an 'a0' tacked on the end???
  1212. int  poly_surface_desc = 0; // ASC surface descriptor/material in this case
  1213. int  poly_num_verts    = 0; // number of vertices for current poly (always 3)
  1214. char tmp_string[8];         // temp string to hold surface descriptor in and
  1215.                             // test if it need to be converted from hex
  1216. for (int poly=0; poly < obj->num_polys; poly++)
  1217.     {
  1218.     // hunt until next face is found
  1219.     while(1)
  1220.     {
  1221.     // get the next polygon face
  1222.     if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1223.        {
  1224.        Write_Error("nface list ended abruptly! in .ASC file %s.", filename);
  1225.        return(0);
  1226.        } // end if
  1227.     
  1228.     // strip all ":ABC", make this easier, note use of input and output as same var, this is legal
  1229.     // since the output is guaranteed to be the same length or shorter as the input :)
  1230.     StripChars(parser.buffer, parser.buffer, ":ABC");
  1231.      
  1232.     // check for pattern?  
  1233.     if (parser.Pattern_Match(parser.buffer, "['Face'] [i] [i] [i] [i]"))
  1234.        {
  1235.        // at this point we have the vertex indices in the the pints array locations 1,2,3, 
  1236.        // 0 contains the face number
  1237.        // insert polygon, check for winding order invert
  1238.        if (vertex_flags & VERTEX_FLAGS_INVERT_WINDING_ORDER)
  1239.           {     
  1240.           poly_num_verts           = 3;
  1241.           obj->plist[poly].vert[0] = parser.pints[3];
  1242.           obj->plist[poly].vert[1] = parser.pints[2];
  1243.           obj->plist[poly].vert[2] = parser.pints[1];
  1244.           } // end if
  1245.        else
  1246.           { // leave winding order alone
  1247.           poly_num_verts           = 3;
  1248.           obj->plist[poly].vert[0] = parser.pints[1];
  1249.           obj->plist[poly].vert[1] = parser.pints[2];
  1250.           obj->plist[poly].vert[2] = parser.pints[3];
  1251.           } // end else
  1252.        // point polygon vertex list to object's vertex list
  1253.        // note that this is redundant since the polylist is contained
  1254.        // within the object in this case and its up to the user to select
  1255.        // whether the local or transformed vertex list is used when building up
  1256.        // polygon geometry, might be a better idea to set to NULL in the context
  1257.        // of polygons that are part of an object
  1258.        obj->plist[poly].vlist = obj->vlist_local; 
  1259.   
  1260.        // found the face, break out of while for another pass
  1261.        break;
  1262.        } // end if
  1263.  
  1264.      } // end while      
  1265.     // hunt until next material for face is found
  1266.     while(1)
  1267.     {
  1268.     // get the next polygon material (the "page xxx" breaks mess everything up!!!)
  1269.     if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1270.        {
  1271.        Write_Error("nmaterial list ended abruptly! in .ASC file %s.", filename);
  1272.        return(0);
  1273.        } // end if
  1274.     
  1275.     // Material:"rdddgdddbddda0"
  1276.     // replace all ':"rgba', make this easier, note use of input and output as same var, this is legal
  1277.     // since the output is guaranteed to be the same length or shorter as the input :)
  1278.     // the result will look like:
  1279.     // "M t ri l   ddd ddd ddd 0" 
  1280.     // which we can parse!
  1281.     ReplaceChars(parser.buffer, parser.buffer, ":"rgba", ' ');
  1282.      
  1283.     // check for pattern?  
  1284.     if (parser.Pattern_Match(parser.buffer, "[i] [i] [i]"))
  1285.        {
  1286.        // at this point we have the red, green, and blue components in the the pints array locations 0,1,2, 
  1287.        r = parser.pints[0];
  1288.        g = parser.pints[1];
  1289.        b = parser.pints[2];
  1290.        // set all the attributes of polygon as best we can with this format
  1291.        // SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_2SIDED);
  1292.     
  1293.        // we need to know what color depth we are dealing with, so check
  1294.        // the bits per pixel, this assumes that the system has already
  1295.        // made the call to DDraw_Init() or set the bit depth
  1296.        if (screen_bpp==16)
  1297.           {
  1298.           // cool, 16 bit mode
  1299.           SET_BIT(obj->plist[poly].attr,POLY4DV1_ATTR_RGB16);
  1300.           obj->plist[poly].color = RGB16Bit(r, g, b);
  1301.           Write_Error("nPolygon 16-bit");
  1302.           } // end if 
  1303.        else
  1304.           {
  1305.           // 8 bit mode
  1306.           SET_BIT(obj->plist[poly].attr,POLY4DV1_ATTR_8BITCOLOR);
  1307.           obj->plist[poly].color = RGBto8BitIndex(r,g,b, palette, 0);
  1308.           Write_Error("nPolygon 8-bit, index=%d", obj->plist[poly].color);
  1309.           } // end else
  1310.        // for now manually set shading mode
  1311.        //SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_PURE);
  1312.        //SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_GOURAUD);
  1313.        //SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_PHONG);
  1314.        SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_FLAT);
  1315.        // set polygon to active
  1316.        obj->plist[poly].state = POLY4DV1_STATE_ACTIVE;    
  1317.        
  1318.        // found the material, break out of while for another pass
  1319.        break;
  1320.        } // end if
  1321.  
  1322.      } // end while      
  1323.      Write_Error("nPolygon %d:", poly);
  1324.      Write_Error("nSurface Desc = [RGB]=[%d, %d, %d], vert_indices [%d, %d, %d]", 
  1325.                                                          r,g,b,
  1326.                                                          obj->plist[poly].vert[0],
  1327.                                                          obj->plist[poly].vert[1],
  1328.                                                          obj->plist[poly].vert[2]);
  1329.     } // end for poly
  1330. // return success
  1331. return(1);
  1332. } // end Load_OBJECT4DV1_3DASC
  1333. //////////////////////////////////////////////////////////////////////////////
  1334. int Load_OBJECT4DV1_COB(OBJECT4DV1_PTR obj,   // pointer to object
  1335.                         char *filename,       // filename of Caligari COB file
  1336.                         VECTOR4D_PTR scale,   // initial scaling factors
  1337.                         VECTOR4D_PTR pos,     // initial position
  1338.                         VECTOR4D_PTR rot,     // initial rotations
  1339.                         int vertex_flags)     // flags to re-order vertices 
  1340.                                               // and perform transforms
  1341. {
  1342. // this function loads a Caligari TrueSpace .COB file object in off disk, additionally
  1343. // it allows the caller to scale, position, and rotate the object
  1344. // to save extra calls later for non-dynamic objects, note that this function 
  1345. // works with a OBJECT4DV1 which has no support for textures, or materials, etc, however we will
  1346. // still parse them and get them ready for the next incarnation objects, so we can
  1347. // re-use this code to support those features
  1348. // create a parser object
  1349. CPARSERV1 parser; 
  1350. char seps[16];          // seperators for token scanning
  1351. char token_buffer[256]; // used as working buffer for token
  1352. char *token;            // pointer to next token
  1353. int r,g,b;              // working colors
  1354. // cache for texture vertices
  1355. VERTEX2DF texture_vertices[1024];
  1356. int num_texture_vertices = 0;
  1357. MATRIX4X4 mat_local,  // storage for local transform if user requests it in cob format
  1358.           mat_world;  // "   " for local to world " "
  1359. // initialize matrices
  1360. MAT_IDENTITY_4X4(&mat_local);
  1361. MAT_IDENTITY_4X4(&mat_world);
  1362. // Step 1: clear out the object and initialize it a bit
  1363. memset(obj, 0, sizeof(OBJECT4DV1));
  1364. // set state of object to active and visible
  1365. obj->state = OBJECT4DV1_STATE_ACTIVE | OBJECT4DV1_STATE_VISIBLE;
  1366. // set position of object is caller requested position
  1367. if (pos)
  1368.    {
  1369.    // set position of object
  1370.    obj->world_pos.x = pos->x;
  1371.    obj->world_pos.y = pos->y;
  1372.    obj->world_pos.z = pos->z;
  1373.    obj->world_pos.w = pos->w;
  1374.    } // end 
  1375. else
  1376.    {
  1377.    // set it to (0,0,0,1)
  1378.    obj->world_pos.x = 0;
  1379.    obj->world_pos.y = 0;
  1380.    obj->world_pos.z = 0;
  1381.    obj->world_pos.w = 1;
  1382.    } // end else
  1383. // Step 2: open the file for reading using the parser
  1384. if (!parser.Open(filename))
  1385.    {
  1386.    Write_Error("Couldn't open .COB file %s.", filename);
  1387.    return(0);
  1388.    } // end if
  1389. // Step 3: 
  1390. // lets find the name of the object first 
  1391. while(1)
  1392.      {
  1393.      // get the next line, we are looking for "Name"
  1394.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1395.         {
  1396.         Write_Error("Image 'name' not found in .COB file %s.", filename);
  1397.         return(0);
  1398.         } // end if
  1399.     
  1400.      // check for pattern?  
  1401.      if ( parser.Pattern_Match(parser.buffer, "['Name'] [s>0]") )
  1402.         {
  1403.         // name should be in second string variable, index 1
  1404.         strcpy(obj->name, parser.pstrings[1]);          
  1405.         Write_Error("nCOB Reader Object Name: %s", obj->name);
  1406.         break;    
  1407.         } // end if
  1408.      } // end while
  1409. // step 4: get local and world transforms and store them
  1410. // center 0 0 0
  1411. // x axis 1 0 0
  1412. // y axis 0 1 0
  1413. // z axis 0 0 1
  1414. while(1)
  1415.      {
  1416.      // get the next line, we are looking for "center"
  1417.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1418.         {
  1419.         Write_Error("Center not found in .COB file %s.", filename);
  1420.         return(0);
  1421.         } // end if
  1422.     
  1423.      // check for pattern?  
  1424.      if ( parser.Pattern_Match(parser.buffer, "['center'] [f] [f] [f]") )
  1425.         {
  1426.         // the "center" holds the translation factors, so place in
  1427.         // last row of homogeneous matrix, note that these are row vectors
  1428.         // that we need to drop in each column of matrix
  1429.         mat_local.M[3][0] = -parser.pfloats[0]; // center x
  1430.         mat_local.M[3][1] = -parser.pfloats[1]; // center y
  1431.         mat_local.M[3][2] = -parser.pfloats[2]; // center z
  1432.         // ok now, the next 3 lines should be the x,y,z transform vectors
  1433.         // so build up   
  1434.         // "x axis" 
  1435.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1436.         parser.Pattern_Match(parser.buffer, "['x'] ['axis'] [f] [f] [f]");
  1437.       
  1438.         // place row in x column of transform matrix
  1439.         mat_local.M[0][0] = parser.pfloats[0]; // rxx
  1440.         mat_local.M[1][0] = parser.pfloats[1]; // rxy
  1441.         mat_local.M[2][0] = parser.pfloats[2]; // rxz
  1442.         // "y axis" 
  1443.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1444.         parser.Pattern_Match(parser.buffer, "['y'] ['axis'] [f] [f] [f]");
  1445.       
  1446.         // place row in y column of transform matrix
  1447.         mat_local.M[0][1] = parser.pfloats[0]; // ryx
  1448.         mat_local.M[1][1] = parser.pfloats[1]; // ryy
  1449.         mat_local.M[2][1] = parser.pfloats[2]; // ryz
  1450.         // "z axis" 
  1451.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1452.         parser.Pattern_Match(parser.buffer, "['z'] ['axis'] [f] [f] [f]");
  1453.       
  1454.         // place row in z column of transform matrix
  1455.         mat_local.M[0][2] = parser.pfloats[0]; // rzx
  1456.         mat_local.M[1][2] = parser.pfloats[1]; // rzy
  1457.         mat_local.M[2][2] = parser.pfloats[2]; // rzz
  1458.         Print_Mat_4X4(&mat_local, "Local COB Matrix:");
  1459.         break;    
  1460.         } // end if
  1461.      } // end while
  1462. // now "Transform"
  1463. while(1)
  1464.      {
  1465.      // get the next line, we are looking for "Transform"
  1466.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1467.         {
  1468.         Write_Error("Transform not found in .COB file %s.", filename);
  1469.         return(0);
  1470.         } // end if
  1471.     
  1472.      // check for pattern?  
  1473.      if ( parser.Pattern_Match(parser.buffer, "['Transform']") )
  1474.         {
  1475.         // "x axis" 
  1476.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1477.         parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
  1478.       
  1479.         // place row in x column of transform matrix
  1480.         mat_world.M[0][0] = parser.pfloats[0]; // rxx
  1481.         mat_world.M[1][0] = parser.pfloats[1]; // rxy
  1482.         mat_world.M[2][0] = parser.pfloats[2]; // rxz
  1483.         // "y axis" 
  1484.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1485.         parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
  1486.       
  1487.         // place row in y column of transform matrix
  1488.         mat_world.M[0][1] = parser.pfloats[0]; // ryx
  1489.         mat_world.M[1][1] = parser.pfloats[1]; // ryy
  1490.         mat_world.M[2][1] = parser.pfloats[2]; // ryz
  1491.         // "z axis" 
  1492.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1493.         parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
  1494.       
  1495.         // place row in z column of transform matrix
  1496.         mat_world.M[0][2] = parser.pfloats[0]; // rzx
  1497.         mat_world.M[1][2] = parser.pfloats[1]; // rzy
  1498.         mat_world.M[2][2] = parser.pfloats[2]; // rzz
  1499.         Print_Mat_4X4(&mat_world, "World COB Matrix:");
  1500.         // no need to read in last row, since it's always 0,0,0,1 and we don't use it anyway
  1501.         break;    
  1502.         } // end if
  1503.      } // end while
  1504. // step 6: get number of vertices and polys in object
  1505. while(1)
  1506.      {
  1507.      // get the next line, we are looking for "World Vertices" 
  1508.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1509.         {
  1510.         Write_Error("'World Vertices' line not found in .COB file %s.", filename);
  1511.         return(0);
  1512.         } // end if
  1513.     
  1514.      // check for pattern?  
  1515.      if (parser.Pattern_Match(parser.buffer, "['World'] ['Vertices'] [i]") )
  1516.         {
  1517.         // simply extract the number of vertices from the pattern matching 
  1518.         // output arrays
  1519.         obj->num_vertices = parser.pints[0];
  1520.         Write_Error("nCOB Reader Num Vertices: %d", obj->num_vertices);
  1521.         break;    
  1522.  
  1523.         } // end if
  1524.      } // end while
  1525. // Step 7: load the vertex list
  1526. // now read in vertex list, format:
  1527. // "d.d d.d d.d"
  1528.  for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1529.      {
  1530.      // hunt for vertex
  1531.      while(1)
  1532.      {
  1533.      // get the next vertex
  1534.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1535.         {
  1536.         Write_Error("nVertex list ended abruptly! in .COB file %s.", filename);
  1537.         return(0);
  1538.         } // end if
  1539.     
  1540.      // check for pattern?  
  1541.      if (parser.Pattern_Match(parser.buffer, "[f] [f] [f]"))
  1542.         {
  1543.         // at this point we have the x,y,z in the the pfloats array locations 0,1,2
  1544.         obj->vlist_local[vertex].x = parser.pfloats[0];
  1545.         obj->vlist_local[vertex].y = parser.pfloats[1];
  1546.         obj->vlist_local[vertex].z = parser.pfloats[2];
  1547.         obj->vlist_local[vertex].w = 1;
  1548.         // do vertex swapping right here, allow muliple swaps, why not!
  1549.         // defines for vertex re-ordering flags
  1550.         //#define VERTEX_FLAGS_INVERT_X   1    // inverts the Z-coordinates
  1551.         //#define VERTEX_FLAGS_INVERT_Y   2    // inverts the Z-coordinates
  1552.         //#define VERTEX_FLAGS_INVERT_Z   4    // inverts the Z-coordinates
  1553.         //#define VERTEX_FLAGS_SWAP_YZ    8    // transforms a RHS model to a LHS model
  1554.         //#define VERTEX_FLAGS_SWAP_XZ    16   // ???
  1555.         //#define VERTEX_FLAGS_SWAP_XY    32
  1556.         //#define VERTEX_FLAGS_INVERT_WINDING_ORDER 64  // invert winding order from cw to ccw or ccw to cc
  1557.         //#define VERTEX_FLAGS_TRANSFORM_LOCAL         512   // if file format has local transform then do it!
  1558.         //#define VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD  1024  // if file format has local to world then do it!
  1559.         VECTOR4D temp_vector; // temp for calculations
  1560.         // now apply local and world transformations encoded in COB format
  1561.         if (vertex_flags & VERTEX_FLAGS_TRANSFORM_LOCAL )
  1562.            {
  1563.            Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex], &mat_local, &temp_vector);
  1564.            VECTOR4D_COPY(&obj->vlist_local[vertex], &temp_vector); 
  1565.            } // end if 
  1566.         if (vertex_flags & VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD )
  1567.            {
  1568.            Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex], &mat_world, &temp_vector);
  1569.            VECTOR4D_COPY(&obj->vlist_local[vertex], &temp_vector); 
  1570.            } // end if 
  1571.         float temp_f; // used for swapping
  1572.         // invert signs?
  1573.         if (vertex_flags & VERTEX_FLAGS_INVERT_X)
  1574.            obj->vlist_local[vertex].x=-obj->vlist_local[vertex].x;
  1575.         if (vertex_flags & VERTEX_FLAGS_INVERT_Y)
  1576.            obj->vlist_local[vertex].y=-obj->vlist_local[vertex].y;
  1577.         if (vertex_flags & VERTEX_FLAGS_INVERT_Z)
  1578.            obj->vlist_local[vertex].z=-obj->vlist_local[vertex].z;
  1579.         // swap any axes?
  1580.         if (vertex_flags & VERTEX_FLAGS_SWAP_YZ)
  1581.            SWAP(obj->vlist_local[vertex].y, obj->vlist_local[vertex].z, temp_f);
  1582.         
  1583.         if (vertex_flags & VERTEX_FLAGS_SWAP_XZ)
  1584.            SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].z, temp_f);
  1585.         if (vertex_flags & VERTEX_FLAGS_SWAP_XY)
  1586.            SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].y, temp_f);
  1587.         // scale vertices
  1588.         if (scale)
  1589.            {
  1590.            obj->vlist_local[vertex].x*=scale->x;
  1591.            obj->vlist_local[vertex].y*=scale->y;
  1592.            obj->vlist_local[vertex].z*=scale->z;
  1593.            } // end if
  1594.           Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
  1595.                                            obj->vlist_local[vertex].x, 
  1596.                                            obj->vlist_local[vertex].y, 
  1597.                                            obj->vlist_local[vertex].z,
  1598.                                            obj->vlist_local[vertex].w);
  1599.         // found vertex, break out of while for next pass
  1600.         break;
  1601.         } // end if
  1602.     } // end while
  1603.     } // end for vertex
  1604. // compute average and max radius
  1605. Compute_OBJECT4DV1_Radius(obj);
  1606. Write_Error("nObject average radius = %f, max radius = %f", 
  1607.             obj->avg_radius, obj->max_radius);
  1608. // step 8: get number of texture vertices
  1609. while(1)
  1610.      {
  1611.      // get the next line, we are looking for "Texture Vertices ddd" 
  1612.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1613.         {
  1614.         Write_Error("'Texture Vertices' line not found in .COB file %s.", filename);
  1615.         return(0);
  1616.         } // end if
  1617.     
  1618.      // check for pattern?  
  1619.      if (parser.Pattern_Match(parser.buffer, "['Texture'] ['Vertices'] [i]") )
  1620.         {
  1621.         // simply extract the number of texture vertices from the pattern matching 
  1622.         // output arrays
  1623.         num_texture_vertices = parser.pints[0];
  1624.         Write_Error("nCOB Reader Texture Vertices: %d", num_texture_vertices);
  1625.         break;    
  1626.  
  1627.         } // end if
  1628.      } // end while
  1629. // Step 9: load the texture vertex list in format "U V"
  1630. // "d.d d.d"
  1631.  for (int tvertex = 0; tvertex < num_texture_vertices; tvertex++)
  1632.      {
  1633.      // hunt for texture
  1634.      while(1)
  1635.      {
  1636.      // get the next vertex
  1637.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1638.         {
  1639.         Write_Error("nTexture Vertex list ended abruptly! in .COB file %s.", filename);
  1640.         return(0);
  1641.         } // end if
  1642.     
  1643.      // check for pattern?  
  1644.      if (parser.Pattern_Match(parser.buffer, "[f] [f]"))
  1645.         {
  1646.         // at this point we have the U V in the the pfloats array locations 0,1 for this 
  1647.         // texture vertex, although we do nothing with them at this point with this parser
  1648.         texture_vertices[tvertex].x = parser.pfloats[0];
  1649.         texture_vertices[tvertex].y = parser.pfloats[1];
  1650.         Write_Error("nTexture Vertex %d: U=%f, V=%f", tvertex,
  1651.                                           texture_vertices[tvertex].x, 
  1652.                                           texture_vertices[tvertex].y );
  1653.         // found vertex, break out of while for next pass
  1654.         break;
  1655.         } // end if
  1656.        } // end while
  1657.    } // end for
  1658. int poly_material[OBJECT4DV1_MAX_POLYS]; // this holds the material index for each polygon
  1659.                                          // we need these indices since when reading the file
  1660.                                          // we read the polygons BEFORE the materials, so we need
  1661.                                          // this data, so we can go back later and extract the material
  1662.                                          // that each poly WAS assigned and get the colors out, since
  1663.                                          // objects and polygons do not currenlty support materials
  1664. int material_index_referenced[MAX_MATERIALS];   // used to track if an index has been used yet as a material 
  1665.                                                 // reference. since we don't know how many materials, we need
  1666.                                                 // a way to count them up, but if we have seen a material reference
  1667.                                                 // more than once then we don't increment the total number of materials
  1668.                                                 // this array is for this
  1669. // clear out reference array
  1670. memset(material_index_referenced,0, sizeof(material_index_referenced));
  1671. // step 10: load in the polygons
  1672. // poly list starts off with:
  1673. // "Faces ddd:"
  1674. while(1)
  1675.      {
  1676.      // get next line
  1677.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1678.         {
  1679.         Write_Error("n'Faces' line not found in .ASC file %s.", filename);
  1680.         return(0);
  1681.         } // end if
  1682.     
  1683.      // check for pattern?  
  1684.      if (parser.Pattern_Match(parser.buffer, "['Faces'] [i]"))
  1685.         {
  1686.         Write_Error("nCOB Reader found face list in .COB file %s.", filename);
  1687.         // finally set number of polys
  1688.         obj->num_polys = parser.pints[0];
  1689.         break;
  1690.         } // end if
  1691.      } // end while
  1692. // now read each face in format:
  1693. // Face verts nn flags ff mat mm
  1694. // the nn is the number of vertices, always 3
  1695. // the ff is the flags, unused for now, has to do with holes
  1696. // the mm is the material index number 
  1697. int poly_surface_desc    = 0; // ASC surface descriptor/material in this case
  1698. int poly_num_verts       = 0; // number of vertices for current poly (always 3)
  1699. int num_materials_object = 0; // number of materials for this object
  1700. for (int poly=0; poly < obj->num_polys; poly++)
  1701.     {
  1702.     // hunt until next face is found
  1703.     while(1)
  1704.          {
  1705.          // get the next polygon face
  1706.          if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1707.             {
  1708.             Write_Error("nface list ended abruptly! in .COB file %s.", filename);
  1709.             return(0);
  1710.             } // end if
  1711.      
  1712.          // check for pattern?  
  1713.          if (parser.Pattern_Match(parser.buffer, "['Face'] ['verts'] [i] ['flags'] [i] ['mat'] [i]"))
  1714.             {
  1715.             // at this point we have the number of vertices for the polygon, the flags, and it's material index
  1716.             // in the integer output array locations 0,1,2
  1717.             // store the material index for this polygon for retrieval later, but make sure adjust the 
  1718.             // the index to take into consideration that the data in parser.pints[2] is 0 based, and we need
  1719.             // an index relative to the entire library, so we simply need to add num_materials to offset the 
  1720.             // index properly, but we will leave this reference zero based for now... and fix up later
  1721.             poly_material[poly] = parser.pints[2];
  1722.             // update the reference array
  1723.             if (material_index_referenced[ poly_material[poly] ] == 0)
  1724.                {
  1725.                // mark as referenced
  1726.                material_index_referenced[ poly_material[poly] ] = 1;
  1727.                // increment total number of materials for this object
  1728.                num_materials_object++;
  1729.                } // end if        
  1730.             // test if number of vertices is 3
  1731.             if (parser.pints[0]!=3)
  1732.                {
  1733.                Write_Error("nface not a triangle! in .COB file %s.", filename);
  1734.                return(0);
  1735.                } // end if
  1736.            // now read out the vertex indices and texture indices format:
  1737.            // <vindex0, tindex0>  <vindex1, tindex1> <vindex1, tindex1> 
  1738.            if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1739.               {
  1740.               Write_Error("nface list ended abruptly! in .COB file %s.", filename);
  1741.               return(0);
  1742.               } // end if
  1743.            // lets replace ",<>" with ' ' to make extraction easy
  1744.            ReplaceChars(parser.buffer, parser.buffer, ",<>",' ');      
  1745.            parser.Pattern_Match(parser.buffer, "[i] [i] [i] [i] [i] [i]");
  1746.  
  1747.            // 0,2,4 holds vertex indices
  1748.            // 1,3,5 holds texture indices -- unused for now, no place to put them!
  1749.           // insert polygon, check for winding order invert
  1750.           if (vertex_flags & VERTEX_FLAGS_INVERT_WINDING_ORDER)
  1751.              {     
  1752.              poly_num_verts           = 3;
  1753.              obj->plist[poly].vert[0] = parser.pints[4];
  1754.              obj->plist[poly].vert[1] = parser.pints[2];
  1755.              obj->plist[poly].vert[2] = parser.pints[0];
  1756.              } // end if
  1757.           else
  1758.              { // leave winding order alone
  1759.              poly_num_verts           = 3;
  1760.              obj->plist[poly].vert[0] = parser.pints[0];
  1761.              obj->plist[poly].vert[1] = parser.pints[2];
  1762.              obj->plist[poly].vert[2] = parser.pints[4];
  1763.              } // end else
  1764.           // point polygon vertex list to object's vertex list
  1765.           // note that this is redundant since the polylist is contained
  1766.           // within the object in this case and its up to the user to select
  1767.           // whether the local or transformed vertex list is used when building up
  1768.           // polygon geometry, might be a better idea to set to NULL in the context
  1769.           // of polygons that are part of an object
  1770.           obj->plist[poly].vlist = obj->vlist_local; 
  1771.           // set polygon to active
  1772.           obj->plist[poly].state = POLY4DV1_STATE_ACTIVE;    
  1773.  
  1774.           // found the face, break out of while for another pass
  1775.           break;
  1776.           } // end if
  1777.  
  1778.        } // end while      
  1779.        Write_Error("nPolygon %d:", poly);
  1780.        Write_Error("nLocal material Index=%d, total materials for object = %d, vert_indices [%d, %d, %d]", 
  1781.                                                                                 poly_material[poly],
  1782.                                                                                 num_materials_object,
  1783.                                                                                 obj->plist[poly].vert[0],
  1784.                                                                                 obj->plist[poly].vert[1],
  1785.                                                                                 obj->plist[poly].vert[2]);       
  1786.     } // end for poly
  1787. // now find materials!!! and we are out of here!
  1788. for (int curr_material = 0; curr_material < num_materials_object; curr_material++)
  1789.     {
  1790.     // hunt for the material header "mat# ddd"
  1791.     while(1)
  1792.     {
  1793.     // get the next polygon material 
  1794.     if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1795.        {
  1796.        Write_Error("nmaterial list ended abruptly! in .COB file %s.", filename);
  1797.        return(0);
  1798.        } // end if
  1799.      
  1800.     // check for pattern?  
  1801.     if (parser.Pattern_Match(parser.buffer, "['mat#'] [i]") )
  1802.        {
  1803.        // extract the material that is being defined 
  1804.        int material_index = parser.pints[0];
  1805.        // get color of polygon, although it might be irrelevant for a textured surface
  1806.        while(1)
  1807.             {
  1808.             // get the next line
  1809.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1810.                {
  1811.                Write_Error("nRGB color ended abruptly! in .COB file %s.", filename);
  1812.                return(0);
  1813.                } // end if
  1814.                // replace the , comma's if there are any with spaces
  1815.                ReplaceChars(parser.buffer, parser.buffer, ",", ' ', 1);
  1816.                // look for "rgb float,float,float"
  1817.                if (parser.Pattern_Match(parser.buffer, "['rgb'] [f] [f] [f]") )
  1818.                   {
  1819.                   // extract data and store color in material libary
  1820.                   // pfloats[] 0,1,2,3, has data
  1821.                   materials[material_index + num_materials].color.r = (int)(parser.pfloats[0]*255 + 0.5);
  1822.                   materials[material_index + num_materials].color.g = (int)(parser.pfloats[1]*255 + 0.5);
  1823.                   materials[material_index + num_materials].color.b = (int)(parser.pfloats[2]*255 + 0.5);
  1824.                   break; // while looking for rgb
  1825.                   } // end if
  1826.              } // end while    
  1827.        // extract out lighting constants for the heck of it, they are on a line like this:
  1828.        // "alpha float ka float ks float exp float ior float"
  1829.        // alpha is transparency           0 - 1
  1830.        // ka is ambient coefficient       0 - 1
  1831.        // ks is specular coefficient      0 - 1
  1832.        // exp is highlight power exponent 0 - 1
  1833.        // ior is index of refraction (unused)
  1834.        // although our engine will have minimal support for these, we might as well get them
  1835.        while(1)
  1836.             {
  1837.             // get the next line
  1838.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1839.                {
  1840.                Write_Error("nmaterial properties ended abruptly! in .COB file %s.", filename);
  1841.                return(0);
  1842.                } // end if
  1843.             // look for "alpha float ka float ks float exp float ior float"
  1844.             if (parser.Pattern_Match(parser.buffer, "['alpha'] [f] ['ka'] [f] ['ks'] [f] ['exp'] [f]") )
  1845.                {
  1846.                // extract data and store in material libary
  1847.                // pfloats[] 0,1,2,3, has data
  1848.                materials[material_index + num_materials].color.a  = (UCHAR)(parser.pfloats[0]*255 + 0.5);
  1849.                materials[material_index + num_materials].ka       = parser.pfloats[1];
  1850.                materials[material_index + num_materials].kd       = 1; // hard code for now
  1851.                materials[material_index + num_materials].ks       = parser.pfloats[2];
  1852.                materials[material_index + num_materials].power    = parser.pfloats[3];
  1853.  
  1854.                // compute material reflectivities in pre-multiplied format to help engine
  1855.                for (int rgb_index=0; rgb_index < 3; rgb_index++)
  1856.                     {
  1857.                     // ambient reflectivity
  1858.                     materials[material_index + num_materials].ra.rgba_M[rgb_index] = 
  1859.                               ( (UCHAR)(materials[material_index + num_materials].ka * 
  1860.                                 (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
  1861.   
  1862.                     // diffuse reflectivity
  1863.                     materials[material_index + num_materials].rd.rgba_M[rgb_index] = 
  1864.                               ( (UCHAR)(materials[material_index + num_materials].kd * 
  1865.                                 (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
  1866.   
  1867.                     // specular reflectivity
  1868.                     materials[material_index + num_materials].rs.rgba_M[rgb_index] = 
  1869.                               ( (UCHAR)(materials[material_index + num_materials].ks * 
  1870.                                 (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
  1871.                      } // end for rgb_index
  1872.                break;
  1873.                } // end if
  1874.              } // end while    
  1875.        // now we need to know the shading model, it's a bit tricky, we need to look for the lines
  1876.        // "Shader class: color" first, then after this line is:
  1877.        // "Shader name: "xxxxxx" (xxxxxx) "
  1878.        // where the xxxxx part will be "plain color" and "plain" for colored polys 
  1879.        // or "texture map" and "caligari texture"  for textures
  1880.        // THEN based on that we hunt for "Shader class: reflectance" which is where the type
  1881.        // of shading is encoded, we look for the "Shader name: "xxxxxx" (xxxxxx) " again, 
  1882.        // and based on it's value we map it to our shading system as follows:
  1883.        // "constant" -> MATV1_ATTR_SHADE_MODE_CONSTANT 
  1884.        // "matte"    -> MATV1_ATTR_SHADE_MODE_FLAT
  1885.        // "plastic"  -> MATV1_ATTR_SHADE_MODE_GOURAUD
  1886.        // "phong"    -> MATV1_ATTR_SHADE_MODE_FASTPHONG 
  1887.        // and in the case that in the "color" class, we found a "texture map" then the "shading mode" is
  1888.        // "texture map" -> MATV1_ATTR_SHADE_MODE_TEXTURE 
  1889.        // which must be logically or'ed with the other previous modes
  1890.  
  1891.        //  look for the "shader class: color"
  1892.        while(1)
  1893.             {
  1894.             // get the next line
  1895.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1896.                {
  1897.                Write_Error("nshader class ended abruptly! in .COB file %s.", filename);
  1898.                return(0);
  1899.                } // end if
  1900.        
  1901.             if (parser.Pattern_Match(parser.buffer, "['Shader'] ['class:'] ['color']") )
  1902.                {
  1903.                break;
  1904.                } // end if
  1905.              } // end while
  1906.           
  1907.        // now look for the shader name for this class
  1908.        // Shader name: "plain color" or Shader name: "texture map"
  1909.        while(1)
  1910.             {
  1911.             // get the next line
  1912.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1913.                {
  1914.                Write_Error("nshader name ended abruptly! in .COB file %s.", filename);
  1915.                return(0);
  1916.                } // end if
  1917.             // replace the " with spaces
  1918.             ReplaceChars(parser.buffer, parser.buffer, """, ' ', 1);
  1919.             // is this a "plain color" poly?
  1920.             if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] ['plain'] ['color']") )
  1921.                {
  1922.                // not much to do this is default, we need to wait for the reflectance type
  1923.                // to tell us the shading mode
  1924.                break;
  1925.                } // end if
  1926.             // is this a "texture map" poly?
  1927.             if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] ['texture'] ['map']") )
  1928.                {
  1929.                // set the texture mapping flag in material
  1930.                SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_TEXTURE);
  1931.       
  1932.                // almost done, we need the file name of the darn texture map, its in this format: