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

游戏

开发平台:

Visual C++

  1. {
  2. // 16-bit version of function
  3. // function lights the entire rendering list based on the sent lights and camera. the function supports
  4. // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
  5. // note that this lighting function is rather brute force and simply follows the math, however
  6. // there are some clever integer operations that are used in scale 256 rather than going to floating
  7. // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
  8. // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
  9. // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
  10. // of the falloff due to attenuation, but they still look like spot lights
  11. // type 2 spot lights are implemented with the intensity having a dot product relationship with the
  12. // angle from the surface point to the light direction just like in the optimized model, but the pf term
  13. // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
  14. // this function now performs emissive, flat, and gouraud lighting, results are stored in the 
  15. // lit_color[] array of each polygon
  16. unsigned int r_base, g_base,   b_base,  // base color being lit
  17.              r_sum,  g_sum,    b_sum,   // sum of lighting process over all lights
  18.              r_sum0,  g_sum0,  b_sum0,
  19.              r_sum1,  g_sum1,  b_sum1,
  20.              r_sum2,  g_sum2,  b_sum2,
  21.              ri,gi,bi,
  22.              shaded_color;            // final color
  23. float dp,     // dot product 
  24.       dist,   // distance from light to surface
  25.       dists, 
  26.       i,      // general intensities
  27.       nl,     // length of normal
  28.       atten;  // attenuation computations
  29. VECTOR4D u, v, n, l, d, s; // used for cross product and light vector calculations
  30. //Write_Error("nEntering lighting function");
  31. // for each valid poly, light it...
  32. for (int poly=0; poly < rend_list->num_polys; poly++)
  33.     {
  34.     // acquire polygon
  35.     POLYF4DV2_PTR curr_poly = rend_list->poly_ptrs[poly];
  36.     // light this polygon if and only if it's not clipped, not culled,
  37.     // active, and visible
  38.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  39.          (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  40.          (curr_poly->state & POLY4DV2_STATE_BACKFACE) ||
  41.          (curr_poly->state & POLY4DV2_STATE_LIT) )
  42.        continue; // move onto next poly
  43.     //Write_Error("npoly %d",poly);
  44. #ifdef DEBUG_ON
  45. // track rendering stats
  46.     debug_polys_lit_per_frame++;
  47. #endif
  48.    
  49.     // set state of polygon to lit
  50.     SET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
  51.     // we will use the transformed polygon vertex list since the backface removal
  52.     // only makes sense at the world coord stage further of the pipeline 
  53.     // test the lighting mode of the polygon (use flat for flat, gouraud))
  54.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_FLAT)
  55.        {
  56.        //Write_Error("nEntering Flat Shader");
  57.        // step 1: extract the base color out in RGB mode
  58.        // assume 565 format
  59.        _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
  60.        // scale to 8 bit 
  61.        r_base <<= 3;
  62.        g_base <<= 2;
  63.        b_base <<= 3;
  64.        //Write_Error("nBase color=%d,%d,%d", r_base, g_base, b_base);
  65.        // initialize color sum
  66.        r_sum  = 0;
  67.        g_sum  = 0;
  68.        b_sum  = 0;
  69.        //Write_Error("nsum color=%d,%d,%d", r_sum, g_sum, b_sum);
  70.        // new optimization:
  71.        // when there are multiple lights in the system we will end up performing numerous
  72.        // redundant calculations to minimize this my strategy is to set key variables to 
  73.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  74.        // the max value, if they are the max value then the first light that needs the math
  75.        // will do it, and then save the information into the variable (causing it to change state
  76.        // from an invalid number) then any other lights that need the math can use the previously
  77.        // computed value
  78.        
  79.        // set surface normal.z to FLT_MAX to flag it as non-computed
  80.        n.z = FLT_MAX;
  81.        // loop thru lights
  82.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  83.            {
  84.            // is this light active
  85.            if (lights[curr_light].state==LIGHTV1_STATE_OFF)
  86.               continue;
  87.            //Write_Error("nprocessing light %d",curr_light);
  88.            // what kind of light are we dealing with
  89.            if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
  90.               {
  91.               //Write_Error("nEntering ambient light...");
  92.               // simply multiply each channel against the color of the 
  93.               // polygon then divide by 256 to scale back to 0..255
  94.               // use a shift in real life!!! >> 8
  95.               r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
  96.               g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
  97.               b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
  98.               //Write_Error("nambient sum=%d,%d,%d", r_sum, g_sum, b_sum);
  99.               // there better only be one ambient light!
  100.               } // end if
  101.            else
  102.            if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) ///////////////////////////////////////////
  103.               {
  104.               //Write_Error("nEntering infinite light...");
  105.               // infinite lighting, we need the surface normal, and the direction
  106.               // of the light source
  107.               // test if we already computed poly normal in previous calculation
  108.               if (n.z==FLT_MAX)       
  109.                  {
  110.                  // we need to compute the normal of this polygon face, and recall
  111.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  112.  
  113.                  // build u, v
  114.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  115.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  116.                  // compute cross product
  117.                  VECTOR4D_Cross(&u, &v, &n);
  118.                  } // end if
  119.               // at this point, we are almost ready, but we have to normalize the normal vector!
  120.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  121.               // normals, so this step can be optimized
  122.               // compute length of normal
  123.               //nl = VECTOR4D_Length_Fast2(&n);
  124.               nl = curr_poly->nlength;  
  125.       
  126.               // ok, recalling the lighting model for infinite lights
  127.               // I(d)dir = I0dir * Cldir
  128.               // and for the diffuse model
  129.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  130.               // so we basically need to multiple it all together
  131.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  132.               // are slower, but the conversion to and from cost cycles
  133.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  134.               
  135.               // only add light if dp > 0
  136.               if (dp > 0)
  137.                  { 
  138.                  i = 128*dp/nl; 
  139.                  r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  140.                  g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  141.                  b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  142.                  } // end if
  143.               //Write_Error("ninfinite sum=%d,%d,%d", r_sum, g_sum, b_sum);
  144.               } // end if infinite light
  145.            else
  146.            if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) ///////////////////////////////////////
  147.               {
  148.               //Write_Error("nEntering point light...");
  149.               // perform point light computations
  150.               // light model for point light is once again:
  151.               //              I0point * Clpoint
  152.               //  I(d)point = ___________________
  153.               //              kc +  kl*d + kq*d2              
  154.               //
  155.               //  Where d = |p - s|
  156.               // thus it's almost identical to the infinite light, but attenuates as a function
  157.               // of distance from the point source to the surface point being lit
  158.               // test if we already computed poly normal in previous calculation
  159.               if (n.z==FLT_MAX)       
  160.                  {
  161.                  // we need to compute the normal of this polygon face, and recall
  162.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  163.  
  164.                  // build u, v
  165.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  166.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  167.                  // compute cross product
  168.                  VECTOR4D_Cross(&u, &v, &n);
  169.                  } // end if
  170.               // at this point, we are almost ready, but we have to normalize the normal vector!
  171.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  172.               // normals, so this step can be optimized
  173.               // compute length of normal
  174.               //nl = VECTOR4D_Length_Fast2(&n);
  175.               nl = curr_poly->nlength;  
  176.               // compute vector from surface to light
  177.               VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
  178.               // compute distance and attenuation
  179.               dist = VECTOR4D_Length_Fast2(&l);  
  180.               // and for the diffuse model
  181.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  182.               // so we basically need to multiple it all together
  183.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  184.               // are slower, but the conversion to and from cost cycles
  185.               dp = VECTOR4D_Dot(&n, &l);
  186.               
  187.               // only add light if dp > 0
  188.               if (dp > 0)
  189.                  { 
  190.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  191.                  i = 128*dp / (nl * dist * atten ); 
  192.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  193.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  194.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  195.                  } // end if
  196.               //Write_Error("npoint sum=%d,%d,%d",r_sum,g_sum,b_sum);
  197.               } // end if point
  198.            else
  199.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ////////////////////////////////////
  200.               {
  201.               //Write_Error("nentering spot light1...");
  202.               // perform spotlight/point computations simplified model that uses
  203.               // point light WITH a direction to simulate a spotlight
  204.               // light model for point light is once again:
  205.               //              I0point * Clpoint
  206.               //  I(d)point = ___________________
  207.               //              kc +  kl*d + kq*d2              
  208.               //
  209.               //  Where d = |p - s|
  210.               // thus it's almost identical to the infinite light, but attenuates as a function
  211.               // of distance from the point source to the surface point being lit
  212.               // test if we already computed poly normal in previous calculation
  213.               if (n.z==FLT_MAX)       
  214.                  {
  215.                  // we need to compute the normal of this polygon face, and recall
  216.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  217.  
  218.                  // build u, v
  219.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  220.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  221.                  // compute cross product
  222.                  VECTOR4D_Cross(&u, &v, &n);
  223.                  } // end if
  224.               // at this point, we are almost ready, but we have to normalize the normal vector!
  225.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  226.               // normals, so this step can be optimized
  227.               // compute length of normal
  228.               //nl = VECTOR4D_Length_Fast2(&n);
  229.               nl = curr_poly->nlength;  
  230.        
  231.               // compute vector from surface to light
  232.               VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
  233.               // compute distance and attenuation
  234.               dist = VECTOR4D_Length_Fast2(&l);  
  235.               // and for the diffuse model
  236.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  237.               // so we basically need to multiple it all together
  238.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  239.               // are slower, but the conversion to and from cost cycles
  240.               // note that I use the direction of the light here rather than a the vector to the light
  241.               // thus we are taking orientation into account which is similar to the spotlight model
  242.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  243.               
  244.               // only add light if dp > 0
  245.               if (dp > 0)
  246.                  { 
  247.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  248.                  i = 128*dp / (nl * atten ); 
  249.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  250.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  251.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  252.                  } // end if
  253.               //Write_Error("nspotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  254.               } // end if spotlight1
  255.            else
  256.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version ////////////////////
  257.               {
  258.               //Write_Error("nEntering spotlight2 ...");
  259.  
  260.               // perform spot light computations
  261.               // light model for spot light simple version is once again:
  262.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  263.               // I(d)spotlight = __________________________________________      
  264.               //                 kc + kl*d + kq*d2        
  265.               // Where d = |p - s|, and pf = power factor
  266.               // thus it's almost identical to the point, but has the extra term in the numerator
  267.               // relating the angle between the light source and the point on the surface
  268.               // test if we already computed poly normal in previous calculation
  269.               if (n.z==FLT_MAX)       
  270.                  {
  271.                  // we need to compute the normal of this polygon face, and recall
  272.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  273.  
  274.                  // build u, v
  275.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  276.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  277.                  // compute cross product
  278.                  VECTOR4D_Cross(&u, &v, &n);
  279.                  } // end if
  280.               // at this point, we are almost ready, but we have to normalize the normal vector!
  281.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  282.               // normals, so this step can be optimized
  283.               // compute length of normal
  284.               //nl = VECTOR4D_Length_Fast2(&n);
  285.               nl = curr_poly->nlength;  
  286.              
  287.               // and for the diffuse model
  288.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  289.               // so we basically need to multiple it all together
  290.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  291.               // are slower, but the conversion to and from cost cycles
  292.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  293.               
  294.               // only add light if dp > 0
  295.               if (dp > 0)
  296.                  { 
  297.                  // compute vector from light to surface (different from l which IS the light dir)
  298.                  VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[0].v, &s);
  299.                  // compute length of s (distance to light source) to normalize s for lighting calc
  300.                  dists = VECTOR4D_Length_Fast2(&s);  
  301.                  // compute spot light term (s . l)
  302.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  303.                  // proceed only if term is positive
  304.                  if (dpsl > 0) 
  305.                     {
  306.                     // compute attenuation
  307.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  308.        
  309.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  310.                     // must be integral
  311.                     float dpsl_exp = dpsl;
  312.  
  313.                     // exponentiate for positive integral powers
  314.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  315.                          dpsl_exp*=dpsl;
  316.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  317.                                                       
  318.                     i = 128*dp * dpsl_exp / (nl * atten ); 
  319.                     r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  320.                     g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  321.                     b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  322.   
  323.                     } // end if
  324.                  } // end if
  325.               //Write_Error("nSpotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  326.      
  327.               } // end if spot light
  328.            } // end for light
  329.   
  330.        // make sure colors aren't out of range
  331.        if (r_sum  > 255) r_sum = 255;
  332.        if (g_sum  > 255) g_sum = 255;
  333.        if (b_sum  > 255) b_sum = 255;
  334.        //Write_Error("nWriting final values to polygon %d = %d,%d,%d", poly, r_sum, g_sum, b_sum);
  335.        // write the color over current color
  336.        curr_poly->lit_color[0] = RGB16Bit(r_sum, g_sum, b_sum);
  337.        } // end if
  338.     else
  339.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) /////////////////////////////////
  340.        {
  341.        // gouraud shade, unfortunetly at this point in the pipeline, we have lost the original
  342.        // mesh, and only have triangles, thus, many triangles will share the same vertices and
  343.        // they will get lit 2x since we don't have any way to tell this, alas, performing lighting
  344.        // at the object level is a better idea when gouraud shading is performed since the 
  345.        // commonality of vertices is still intact, in any case, lighting here is similar to polygon
  346.        // flat shaded, but we do it 3 times, once for each vertex, additionally there are lots
  347.        // of opportunities for optimization, but I am going to lay off them for now, so the code
  348.        // is intelligible, later we will optimize
  349.        //Write_Error("nEntering gouraud shader...");
  350.        // step 1: extract the base color out in RGB mode
  351.        // assume 565 format
  352.        _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
  353.        // scale to 8 bit 
  354.        r_base <<= 3;
  355.        g_base <<= 2;
  356.        b_base <<= 3;
  357.        //Write_Error("nBase color=%d, %d, %d", r_base, g_base, b_base);
  358.        // initialize color sum(s) for vertices
  359.        r_sum0  = 0;
  360.        g_sum0  = 0;
  361.        b_sum0  = 0;
  362.        r_sum1  = 0;
  363.        g_sum1  = 0;
  364.        b_sum1  = 0;
  365.        r_sum2  = 0;
  366.        g_sum2  = 0;
  367.        b_sum2  = 0;
  368.        //Write_Error("nColor sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,   r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
  369.        // new optimization:
  370.        // when there are multiple lights in the system we will end up performing numerous
  371.        // redundant calculations to minimize this my strategy is to set key variables to 
  372.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  373.        // the max value, if they are the max value then the first light that needs the math
  374.        // will do it, and then save the information into the variable (causing it to change state
  375.        // from an invalid number) then any other lights that need the math can use the previously
  376.        // computed value, however, since we already have the normals, not much here to cache on
  377.        // a large scale, but small scale stuff is there, however, we will optimize those later
  378.        // loop thru lights
  379.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  380.            {
  381.            // is this light active
  382.            if (lights[curr_light].state==LIGHTV1_STATE_OFF)
  383.               continue;
  384.            //Write_Error("nprocessing light %d", curr_light);
  385.            // what kind of light are we dealing with
  386.            if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT) ///////////////////////////////
  387.               {
  388.               //Write_Error("nEntering ambient light....");
  389.               // simply multiply each channel against the color of the 
  390.               // polygon then divide by 256 to scale back to 0..255
  391.               // use a shift in real life!!! >> 8
  392.               ri = ((lights[curr_light].c_ambient.r * r_base) / 256);
  393.               gi = ((lights[curr_light].c_ambient.g * g_base) / 256);
  394.               bi = ((lights[curr_light].c_ambient.b * b_base) / 256);
  395.             
  396.               // ambient light has the same affect on each vertex
  397.               r_sum0+=ri;
  398.               g_sum0+=gi;
  399.               b_sum0+=bi;
  400.   
  401.               r_sum1+=ri;
  402.               g_sum1+=gi;
  403.               b_sum1+=bi;
  404.               
  405.               r_sum2+=ri;
  406.               g_sum2+=gi;
  407.               b_sum2+=bi;
  408.               // there better only be one ambient light!
  409.               //Write_Error("nexiting ambient ,sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1,  r_sum2, g_sum2, b_sum2);
  410.               } // end if
  411.            else
  412.            if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) /////////////////////////////////
  413.               {
  414.               //Write_Error("nentering infinite light...");
  415.               // infinite lighting, we need the surface normal, and the direction
  416.               // of the light source
  417.               // no longer need to compute normal or length, we already have the vertex normal
  418.               // and it's length is 1.0  
  419.               // ....
  420.       
  421.               // ok, recalling the lighting model for infinite lights
  422.               // I(d)dir = I0dir * Cldir
  423.               // and for the diffuse model
  424.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  425.               // so we basically need to multiple it all together
  426.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  427.               // are slower, but the conversion to and from cost cycles
  428.               // need to perform lighting for each vertex (lots of redundant math, optimize later!)
  429.               //Write_Error("nv0=[%f, %f, %f]=%f, v1=[%f, %f, %f]=%f, v2=[%f, %f, %f]=%f",
  430.                 // curr_poly->tvlist[0].n.x, curr_poly->tvlist[0].n.y,curr_poly->tvlist[0].n.z, VECTOR4D_Length(&curr_poly->tvlist[0].n),
  431.                 // curr_poly->tvlist[1].n.x, curr_poly->tvlist[1].n.y,curr_poly->tvlist[1].n.z, VECTOR4D_Length(&curr_poly->tvlist[1].n),
  432.                 // curr_poly->tvlist[2].n.x, curr_poly->tvlist[2].n.y,curr_poly->tvlist[2].n.z, VECTOR4D_Length(&curr_poly->tvlist[2].n) );
  433.               // vertex 0
  434.               dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &lights[curr_light].dir); 
  435.               
  436.               // only add light if dp > 0
  437.               if (dp > 0)
  438.                  { 
  439.                  i = 128*dp; 
  440.                  r_sum0+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  441.                  g_sum0+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  442.                  b_sum0+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  443.                  } // end if
  444.               // vertex 1
  445.               dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &lights[curr_light].dir);
  446.               
  447.               // only add light if dp > 0
  448.               if (dp > 0)
  449.                  { 
  450.                  i = 128*dp; 
  451.                  r_sum1+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  452.                  g_sum1+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  453.                  b_sum1+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  454.                  } // end if
  455.               // vertex 2
  456.               dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &lights[curr_light].dir);
  457.               
  458.               // only add light if dp > 0
  459.               if (dp > 0)
  460.                  { 
  461.                  i = 128*dp; 
  462.                  r_sum2+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  463.                  g_sum2+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  464.                  b_sum2+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  465.                  } // end if
  466.               //Write_Error("nexiting infinite, color sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1,  r_sum2, g_sum2, b_sum2);
  467.               } // end if infinite light
  468.            else
  469.            if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) //////////////////////////////////////
  470.               {
  471.               // perform point light computations
  472.               // light model for point light is once again:
  473.               //              I0point * Clpoint
  474.               //  I(d)point = ___________________
  475.               //              kc +  kl*d + kq*d2              
  476.               //
  477.               //  Where d = |p - s|
  478.               // thus it's almost identical to the infinite light, but attenuates as a function
  479.               // of distance from the point source to the surface point being lit
  480.               // .. normal already in vertex
  481.               //Write_Error("nEntering point light....");
  482.               // compute vector from surface to light
  483.               VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
  484.               // compute distance and attenuation
  485.               dist = VECTOR4D_Length_Fast2(&l);  
  486.               // and for the diffuse model
  487.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  488.               // so we basically need to multiple it all together
  489.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  490.               // are slower, but the conversion to and from cost cycles
  491.               // perform the calculation for all 3 vertices
  492.               // vertex 0
  493.               dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &l);
  494.               
  495.               // only add light if dp > 0
  496.               if (dp > 0)
  497.                  { 
  498.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  499.                  i = 128*dp / (dist * atten ); 
  500.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  501.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  502.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  503.                  } // end if
  504.               // vertex 1
  505.               dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &l);
  506.               
  507.               // only add light if dp > 0
  508.               if (dp > 0)
  509.                  { 
  510.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  511.                  i = 128*dp / (dist * atten ); 
  512.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  513.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  514.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  515.                  } // end if
  516.               // vertex 2
  517.               dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &l);
  518.               
  519.               // only add light if dp > 0
  520.               if (dp > 0)
  521.                  { 
  522.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  523.                  i = 128*dp / (dist * atten ); 
  524.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  525.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  526.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  527.                  } // end if
  528.               //Write_Error("nexiting point light, rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
  529.               } // end if point
  530.            else
  531.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ///////////////////////////////////////
  532.               {
  533.               // perform spotlight/point computations simplified model that uses
  534.               // point light WITH a direction to simulate a spotlight
  535.               // light model for point light is once again:
  536.               //              I0point * Clpoint
  537.               //  I(d)point = ___________________
  538.               //              kc +  kl*d + kq*d2              
  539.               //
  540.               //  Where d = |p - s|
  541.               // thus it's almost identical to the infinite light, but attenuates as a function
  542.               // of distance from the point source to the surface point being lit
  543.               //Write_Error("nentering spotlight1....");
  544.               // .. normal is already computed
  545.        
  546.               // compute vector from surface to light
  547.               VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
  548.               // compute distance and attenuation
  549.               dist = VECTOR4D_Length_Fast2(&l);  
  550.               // and for the diffuse model
  551.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  552.               // so we basically need to multiple it all together
  553.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  554.               // are slower, but the conversion to and from cost cycles
  555.               // note that I use the direction of the light here rather than a the vector to the light
  556.               // thus we are taking orientation into account which is similar to the spotlight model
  557.               // vertex 0
  558.               dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &lights[curr_light].dir);
  559.               
  560.               // only add light if dp > 0
  561.               if (dp > 0)
  562.                  { 
  563.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  564.                  i = 128*dp / ( atten ); 
  565.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  566.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  567.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  568.                  } // end if
  569.               // vertex 1
  570.               dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &lights[curr_light].dir);
  571.               
  572.               // only add light if dp > 0
  573.               if (dp > 0)
  574.                  { 
  575.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  576.                  i = 128*dp / ( atten ); 
  577.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  578.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  579.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  580.                  } // end i
  581.               // vertex 2
  582.               dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &lights[curr_light].dir);
  583.               
  584.               // only add light if dp > 0
  585.               if (dp > 0)
  586.                  { 
  587.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  588.                  i = 128*dp / ( atten ); 
  589.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  590.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  591.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  592.                  } // end i
  593.               //Write_Error("nexiting spotlight1, sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1,  r_sum2, g_sum2, b_sum2);
  594.               } // end if spotlight1
  595.            else
  596.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version //////////////////////////
  597.               {
  598.               // perform spot light computations
  599.               // light model for spot light simple version is once again:
  600.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  601.               // I(d)spotlight = __________________________________________      
  602.               //                 kc + kl*d + kq*d2        
  603.               // Where d = |p - s|, and pf = power factor
  604.               // thus it's almost identical to the point, but has the extra term in the numerator
  605.               // relating the angle between the light source and the point on the surface
  606.               // .. already have normals and length are 1.0
  607.              
  608.               // and for the diffuse model
  609.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  610.               // so we basically need to multiple it all together
  611.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  612.               // are slower, but the conversion to and from cost cycles
  613.               //Write_Error("nEntering spotlight2...");
  614.               // tons of redundant math here! lots to optimize later!
  615.               
  616.               // vertex 0
  617.               dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &lights[curr_light].dir);
  618.               
  619.               // only add light if dp > 0
  620.               if (dp > 0)
  621.                  { 
  622.                  // compute vector from light to surface (different from l which IS the light dir)
  623.                  VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[0].v, &s);
  624.                  // compute length of s (distance to light source) to normalize s for lighting calc
  625.                  dists = VECTOR4D_Length_Fast2(&s);  
  626.                  // compute spot light term (s . l)
  627.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  628.                  // proceed only if term is positive
  629.                  if (dpsl > 0) 
  630.                     {
  631.                     // compute attenuation
  632.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  633.        
  634.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  635.                     // must be integral
  636.                     float dpsl_exp = dpsl;
  637.  
  638.                     // exponentiate for positive integral powers
  639.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  640.                          dpsl_exp*=dpsl;
  641.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  642.                                                       
  643.                     i = 128*dp * dpsl_exp / ( atten ); 
  644.                     r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  645.                     g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  646.                     b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  647.   
  648.                     } // end if
  649.                  } // end if
  650.               // vertex 1
  651.               dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &lights[curr_light].dir);
  652.               
  653.               // only add light if dp > 0
  654.               if (dp > 0)
  655.                  { 
  656.                  // compute vector from light to surface (different from l which IS the light dir)
  657.                  VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[1].v, &s);
  658.                  // compute length of s (distance to light source) to normalize s for lighting calc
  659.                  dists = VECTOR4D_Length_Fast2(&s);  
  660.                  // compute spot light term (s . l)
  661.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  662.                  // proceed only if term is positive
  663.                  if (dpsl > 0) 
  664.                     {
  665.                     // compute attenuation
  666.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  667.        
  668.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  669.                     // must be integral
  670.                     float dpsl_exp = dpsl;
  671.  
  672.                     // exponentiate for positive integral powers
  673.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  674.                          dpsl_exp*=dpsl;
  675.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  676.                                                       
  677.                     i = 128*dp * dpsl_exp / ( atten ); 
  678.                     r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  679.                     g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  680.                     b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  681.   
  682.                     } // end if
  683.                  } // end if
  684.               // vertex 2
  685.               dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &lights[curr_light].dir);
  686.               
  687.               // only add light if dp > 0
  688.               if (dp > 0)
  689.                  { 
  690.                  // compute vector from light to surface (different from l which IS the light dir)
  691.                  VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[2].v, &s);
  692.                  // compute length of s (distance to light source) to normalize s for lighting calc
  693.                  dists = VECTOR4D_Length_Fast2(&s);  
  694.                  // compute spot light term (s . l)
  695.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  696.                  // proceed only if term is positive
  697.                  if (dpsl > 0) 
  698.                     {
  699.                     // compute attenuation
  700.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  701.        
  702.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  703.                     // must be integral
  704.                     float dpsl_exp = dpsl;
  705.  
  706.                     // exponentiate for positive integral powers
  707.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  708.                          dpsl_exp*=dpsl;
  709.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  710.                                                       
  711.                     i = 128*dp * dpsl_exp / ( atten ); 
  712.                     r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  713.                     g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  714.                     b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  715.                     } // end if
  716.                  } // end if
  717.               //Write_Error("nexiting spotlight2, sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,   r_sum1, g_sum1, b_sum1,  r_sum2, g_sum2, b_sum2);
  718.               } // end if spot light
  719.            } // end for light
  720.   
  721.        // make sure colors aren't out of range
  722.        if (r_sum0  > 255) r_sum0 = 255;
  723.        if (g_sum0  > 255) g_sum0 = 255;
  724.        if (b_sum0  > 255) b_sum0 = 255;
  725.        if (r_sum1  > 255) r_sum1 = 255;
  726.        if (g_sum1  > 255) g_sum1 = 255;
  727.        if (b_sum1  > 255) b_sum1 = 255;
  728.        if (r_sum2  > 255) r_sum2 = 255;
  729.        if (g_sum2  > 255) g_sum2 = 255;
  730.        if (b_sum2  > 255) b_sum2 = 255;
  731.        //Write_Error("nwriting color for poly %d", poly);
  732.        //Write_Error("n******** final sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
  733.        // write the colors
  734.        curr_poly->lit_color[0] = RGB16Bit(r_sum0, g_sum0, b_sum0);
  735.        curr_poly->lit_color[1] = RGB16Bit(r_sum1, g_sum1, b_sum1);
  736.        curr_poly->lit_color[2] = RGB16Bit(r_sum2, g_sum2, b_sum2);
  737.        } // end if
  738.     else // assume POLY4DV2_ATTR_SHADE_MODE_CONSTANT
  739.        {
  740.        // emmisive shading only, do nothing
  741.        // ...
  742.        curr_poly->lit_color[0] = curr_poly->color;
  743.        //Write_Error("nentering constant shader, and exiting...");
  744.        } // end if
  745.     } // end for poly
  746. // return success
  747. return(1);
  748. } // end Light_RENDERLIST4DV2_World16
  749. //////////////////////////////////////////////////////////////////////////////
  750. int Light_RENDERLIST4DV2_World(RENDERLIST4DV2_PTR rend_list,  // list to process
  751.                                    CAM4DV1_PTR cam,     // camera position
  752.                                    LIGHTV1_PTR lights,  // light list (might have more than one)
  753.                                    int max_lights)      // maximum lights in list
  754. {
  755. // {andre work in progress }
  756. // 8-bit version of function
  757. // function lights the entire rendering list based on the sent lights and camera. the function supports
  758. // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
  759. // note that this lighting function is rather brute force and simply follows the math, however
  760. // there are some clever integer operations that are used in scale 256 rather than going to floating
  761. // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
  762. // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
  763. // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
  764. // of the falloff due to attenuation, but they still look like spot lights
  765. // type 2 spot lights are implemented with the intensity having a dot product relationship with the
  766. // angle from the surface point to the light direction just like in the optimized model, but the pf term
  767. // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
  768. // also note since we are dealing with a rendering list and not object, the final lit color is
  769. // immediately written over the real color
  770. unsigned int r_base, g_base,   b_base,  // base color being lit
  771.              r_sum,  g_sum,    b_sum,   // sum of lighting process over all lights
  772.              r_sum0,  g_sum0,  b_sum0,
  773.              r_sum1,  g_sum1,  b_sum1,
  774.              r_sum2,  g_sum2,  b_sum2,
  775.              ri,gi,bi,
  776.              shaded_color;            // final color
  777. float dp,     // dot product 
  778.       dist,   // distance from light to surface
  779.       dists, 
  780.       i,      // general intensities
  781.       nl,     // length of normal
  782.       atten;  // attenuation computations
  783. VECTOR4D u, v, n, l, d, s; // used for cross product and light vector calculations
  784. //Write_Error("nEntering lighting function");
  785. // for each valid poly, light it...
  786. for (int poly=0; poly < rend_list->num_polys; poly++)
  787.     {
  788.     // acquire polygon
  789.     POLYF4DV2_PTR curr_poly = rend_list->poly_ptrs[poly];
  790.     // light this polygon if and only if it's not clipped, not culled,
  791.     // active, and visible
  792.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  793.          (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  794.          (curr_poly->state & POLY4DV2_STATE_BACKFACE) ||
  795.          (curr_poly->state & POLY4DV2_STATE_LIT) )
  796.        continue; // move onto next poly
  797.     //Write_Error("npoly %d",poly);
  798. #ifdef DEBUG_ON
  799. // track rendering stats
  800.     debug_polys_lit_per_frame++;
  801. #endif
  802.     // set state of polygon to lit
  803.     SET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
  804.    
  805.     // we will use the transformed polygon vertex list since the backface removal
  806.     // only makes sense at the world coord stage further of the pipeline 
  807.     // test the lighting mode of the polygon (use flat for flat, gouraud))
  808.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_FLAT)
  809.        {
  810.        //Write_Error("nEntering Flat Shader");
  811.        r_base = palette[curr_poly->color].peRed;
  812.        g_base = palette[curr_poly->color].peGreen;
  813.        b_base = palette[curr_poly->color].peBlue;
  814.        //Write_Error("nBase color=%d,%d,%d", r_base, g_base, b_base);
  815.        // initialize color sum
  816.        r_sum  = 0;
  817.        g_sum  = 0;
  818.        b_sum  = 0;
  819.        //Write_Error("nsum color=%d,%d,%d", r_sum, g_sum, b_sum);
  820.        // new optimization:
  821.        // when there are multiple lights in the system we will end up performing numerous
  822.        // redundant calculations to minimize this my strategy is to set key variables to 
  823.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  824.        // the max value, if they are the max value then the first light that needs the math
  825.        // will do it, and then save the information into the variable (causing it to change state
  826.        // from an invalid number) then any other lights that need the math can use the previously
  827.        // computed value
  828.        
  829.        // set surface normal.z to FLT_MAX to flag it as non-computed
  830.        n.z = FLT_MAX;
  831.        // loop thru lights
  832.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  833.            {
  834.            // is this light active
  835.            if (lights[curr_light].state==LIGHTV1_STATE_OFF)
  836.               continue;
  837.            //Write_Error("nprocessing light %d",curr_light);
  838.            // what kind of light are we dealing with
  839.            if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
  840.               {
  841.               //Write_Error("nEntering ambient light...");
  842.               // simply multiply each channel against the color of the 
  843.               // polygon then divide by 256 to scale back to 0..255
  844.               // use a shift in real life!!! >> 8
  845.               r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
  846.               g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
  847.               b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
  848.               //Write_Error("nambient sum=%d,%d,%d", r_sum, g_sum, b_sum);
  849.               // there better only be one ambient light!
  850.               } // end if
  851.            else
  852.            if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) ///////////////////////////////////////////
  853.               {
  854.               //Write_Error("nEntering infinite light...");
  855.               // infinite lighting, we need the surface normal, and the direction
  856.               // of the light source
  857.               // test if we already computed poly normal in previous calculation
  858.               if (n.z==FLT_MAX)       
  859.                  {
  860.                  // we need to compute the normal of this polygon face, and recall
  861.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  862.  
  863.                  // build u, v
  864.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  865.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  866.                  // compute cross product
  867.                  VECTOR4D_Cross(&u, &v, &n);
  868.                  } // end if
  869.               // at this point, we are almost ready, but we have to normalize the normal vector!
  870.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  871.               // normals, so this step can be optimized
  872.               // compute length of normal
  873.               //nl = VECTOR4D_Length_Fast2(&n);
  874.               nl = curr_poly->nlength;  
  875.       
  876.               // ok, recalling the lighting model for infinite lights
  877.               // I(d)dir = I0dir * Cldir
  878.               // and for the diffuse model
  879.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  880.               // so we basically need to multiple it all together
  881.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  882.               // are slower, but the conversion to and from cost cycles
  883.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  884.               
  885.               // only add light if dp > 0
  886.               if (dp > 0)
  887.                  { 
  888.                  i = 128*dp/nl; 
  889.                  r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  890.                  g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  891.                  b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  892.                  } // end if
  893.               //Write_Error("ninfinite sum=%d,%d,%d", r_sum, g_sum, b_sum);
  894.               } // end if infinite light
  895.            else
  896.            if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) ///////////////////////////////////////
  897.               {
  898.               //Write_Error("nEntering point light...");
  899.               // perform point light computations
  900.               // light model for point light is once again:
  901.               //              I0point * Clpoint
  902.               //  I(d)point = ___________________
  903.               //              kc +  kl*d + kq*d2              
  904.               //
  905.               //  Where d = |p - s|
  906.               // thus it's almost identical to the infinite light, but attenuates as a function
  907.               // of distance from the point source to the surface point being lit
  908.               // test if we already computed poly normal in previous calculation
  909.               if (n.z==FLT_MAX)       
  910.                  {
  911.                  // we need to compute the normal of this polygon face, and recall
  912.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  913.  
  914.                  // build u, v
  915.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  916.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  917.                  // compute cross product
  918.                  VECTOR4D_Cross(&u, &v, &n);
  919.                  } // end if
  920.               // at this point, we are almost ready, but we have to normalize the normal vector!
  921.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  922.               // normals, so this step can be optimized
  923.               // compute length of normal
  924.               //nl = VECTOR4D_Length_Fast2(&n);
  925.               nl = curr_poly->nlength;  
  926.               // compute vector from surface to light
  927.               VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
  928.               // compute distance and attenuation
  929.               dist = VECTOR4D_Length_Fast2(&l);  
  930.               // and for the diffuse model
  931.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  932.               // so we basically need to multiple it all together
  933.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  934.               // are slower, but the conversion to and from cost cycles
  935.               dp = VECTOR4D_Dot(&n, &l);
  936.               
  937.               // only add light if dp > 0
  938.               if (dp > 0)
  939.                  { 
  940.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  941.                  i = 128*dp / (nl * dist * atten ); 
  942.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  943.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  944.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  945.                  } // end if
  946.               //Write_Error("npoint sum=%d,%d,%d",r_sum,g_sum,b_sum);
  947.               } // end if point
  948.            else
  949.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ////////////////////////////////////
  950.               {
  951.               //Write_Error("nentering spot light1...");
  952.               // perform spotlight/point computations simplified model that uses
  953.               // point light WITH a direction to simulate a spotlight
  954.               // light model for point light is once again:
  955.               //              I0point * Clpoint
  956.               //  I(d)point = ___________________
  957.               //              kc +  kl*d + kq*d2              
  958.               //
  959.               //  Where d = |p - s|
  960.               // thus it's almost identical to the infinite light, but attenuates as a function
  961.               // of distance from the point source to the surface point being lit
  962.               // test if we already computed poly normal in previous calculation
  963.               if (n.z==FLT_MAX)       
  964.                  {
  965.                  // we need to compute the normal of this polygon face, and recall
  966.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  967.  
  968.                  // build u, v
  969.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  970.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  971.                  // compute cross product
  972.                  VECTOR4D_Cross(&u, &v, &n);
  973.                  } // end if
  974.               // at this point, we are almost ready, but we have to normalize the normal vector!
  975.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  976.               // normals, so this step can be optimized
  977.               // compute length of normal
  978.               //nl = VECTOR4D_Length_Fast2(&n);
  979.               nl = curr_poly->nlength;  
  980.        
  981.               // compute vector from surface to light
  982.               VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
  983.               // compute distance and attenuation
  984.               dist = VECTOR4D_Length_Fast2(&l);  
  985.               // and for the diffuse model
  986.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  987.               // so we basically need to multiple it all together
  988.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  989.               // are slower, but the conversion to and from cost cycles
  990.               // note that I use the direction of the light here rather than a the vector to the light
  991.               // thus we are taking orientation into account which is similar to the spotlight model
  992.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  993.               
  994.               // only add light if dp > 0
  995.               if (dp > 0)
  996.                  { 
  997.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  998.                  i = 128*dp / (nl * atten ); 
  999.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1000.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1001.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1002.                  } // end if
  1003.               //Write_Error("nspotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  1004.               } // end if spotlight1
  1005.            else
  1006.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version ////////////////////
  1007.               {
  1008.               //Write_Error("nEntering spotlight2 ...");
  1009.  
  1010.               // perform spot light computations
  1011.               // light model for spot light simple version is once again:
  1012.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  1013.               // I(d)spotlight = __________________________________________      
  1014.               //                 kc + kl*d + kq*d2        
  1015.               // Where d = |p - s|, and pf = power factor
  1016.               // thus it's almost identical to the point, but has the extra term in the numerator
  1017.               // relating the angle between the light source and the point on the surface
  1018.               // test if we already computed poly normal in previous calculation
  1019.               if (n.z==FLT_MAX)       
  1020.                  {
  1021.                  // we need to compute the normal of this polygon face, and recall
  1022.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1023.  
  1024.                  // build u, v
  1025.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  1026.                  VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  1027.                  // compute cross product
  1028.                  VECTOR4D_Cross(&u, &v, &n);
  1029.                  } // end if
  1030.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1031.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1032.               // normals, so this step can be optimized
  1033.               // compute length of normal
  1034.               //nl = VECTOR4D_Length_Fast2(&n);
  1035.               nl = curr_poly->nlength;  
  1036.              
  1037.               // and for the diffuse model
  1038.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1039.               // so we basically need to multiple it all together
  1040.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1041.               // are slower, but the conversion to and from cost cycles
  1042.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  1043.               
  1044.               // only add light if dp > 0
  1045.               if (dp > 0)
  1046.                  { 
  1047.                  // compute vector from light to surface (different from l which IS the light dir)
  1048.                  VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[0].v, &s);
  1049.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1050.                  dists = VECTOR4D_Length_Fast2(&s);  
  1051.                  // compute spot light term (s . l)
  1052.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  1053.                  // proceed only if term is positive
  1054.                  if (dpsl > 0) 
  1055.                     {
  1056.                     // compute attenuation
  1057.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1058.        
  1059.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1060.                     // must be integral
  1061.                     float dpsl_exp = dpsl;
  1062.  
  1063.                     // exponentiate for positive integral powers
  1064.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1065.                          dpsl_exp*=dpsl;
  1066.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1067.                                                       
  1068.                     i = 128*dp * dpsl_exp / (nl * atten ); 
  1069.                     r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1070.                     g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1071.                     b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1072.   
  1073.                     } // end if
  1074.                  } // end if
  1075.               //Write_Error("nSpotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  1076.      
  1077.               } // end if spot light
  1078.            } // end for light
  1079.   
  1080.        // make sure colors aren't out of range
  1081.        if (r_sum  > 255) r_sum = 255;
  1082.        if (g_sum  > 255) g_sum = 255;
  1083.        if (b_sum  > 255) b_sum = 255;
  1084.        //Write_Error("nWriting final values to polygon %d = %d,%d,%d", poly, r_sum, g_sum, b_sum);
  1085.        // write the color over current color       
  1086.        curr_poly->lit_color[0] = rgblookup[RGB16Bit565(r_sum, g_sum, b_sum)];
  1087.        } // end if
  1088.     else
  1089.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) /////////////////////////////////
  1090.        {
  1091.        // gouraud shade, unfortunetly at this point in the pipeline, we have lost the original
  1092.        // mesh, and only have triangles, thus, many triangles will share the same vertices and
  1093.        // they will get lit 2x since we don't have any way to tell this, alas, performing lighting
  1094.        // at the object level is a better idea when gouraud shading is performed since the 
  1095.        // commonality of vertices is still intact, in any case, lighting here is similar to polygon
  1096.        // flat shaded, but we do it 3 times, once for each vertex, additionally there are lots
  1097.        // of opportunities for optimization, but I am going to lay off them for now, so the code
  1098.        // is intelligible, later we will optimize
  1099.        //Write_Error("nEntering gouraud shader...");
  1100.        // step 1: extract the base color out in RGB mode
  1101.        r_base = palette[curr_poly->color].peRed;
  1102.        g_base = palette[curr_poly->color].peGreen;
  1103.        b_base = palette[curr_poly->color].peBlue;
  1104.        //Write_Error("nBase color=%d, %d, %d", r_base, g_base, b_base);
  1105.        // initialize color sum(s) for vertices
  1106.        r_sum0  = 0;
  1107.        g_sum0  = 0;
  1108.        b_sum0  = 0;
  1109.        r_sum1  = 0;
  1110.        g_sum1  = 0;
  1111.        b_sum1  = 0;
  1112.        r_sum2  = 0;
  1113.        g_sum2  = 0;
  1114.        b_sum2  = 0;
  1115.        //Write_Error("nColor sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,   r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
  1116.        // new optimization:
  1117.        // when there are multiple lights in the system we will end up performing numerous
  1118.        // redundant calculations to minimize this my strategy is to set key variables to 
  1119.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  1120.        // the max value, if they are the max value then the first light that needs the math
  1121.        // will do it, and then save the information into the variable (causing it to change state
  1122.        // from an invalid number) then any other lights that need the math can use the previously
  1123.        // computed value       
  1124.        // loop thru lights
  1125.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  1126.            {
  1127.            // is this light active
  1128.            if (lights[curr_light].state==LIGHTV1_STATE_OFF)
  1129.               continue;
  1130.            //Write_Error("nprocessing light %d", curr_light);
  1131.            // what kind of light are we dealing with
  1132.            if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT) ///////////////////////////////
  1133.               {
  1134.               //Write_Error("nEntering ambient light....");
  1135.               // simply multiply each channel against the color of the 
  1136.               // polygon then divide by 256 to scale back to 0..255
  1137.               // use a shift in real life!!! >> 8
  1138.               ri = ((lights[curr_light].c_ambient.r * r_base) / 256);
  1139.               gi = ((lights[curr_light].c_ambient.g * g_base) / 256);
  1140.               bi = ((lights[curr_light].c_ambient.b * b_base) / 256);
  1141.             
  1142.               // ambient light has the same affect on each vertex
  1143.               r_sum0+=ri;
  1144.               g_sum0+=gi;
  1145.               b_sum0+=bi;
  1146.   
  1147.               r_sum1+=ri;
  1148.               g_sum1+=gi;
  1149.               b_sum1+=bi;
  1150.               
  1151.               r_sum2+=ri;
  1152.               g_sum2+=gi;
  1153.               b_sum2+=bi;
  1154.               // there better only be one ambient light!
  1155.               //Write_Error("nexiting ambient ,sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1,  r_sum2, g_sum2, b_sum2);
  1156.               } // end if
  1157.            else
  1158.            if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) /////////////////////////////////
  1159.               {
  1160.               //Write_Error("nentering infinite light...");
  1161.               // infinite lighting, we need the surface normal, and the direction
  1162.               // of the light source
  1163.               // no longer need to compute normal or length, we already have the vertex normal
  1164.               // and it's length is 1.0  
  1165.               // ....
  1166.       
  1167.               // ok, recalling the lighting model for infinite lights
  1168.               // I(d)dir = I0dir * Cldir
  1169.               // and for the diffuse model
  1170.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1171.               // so we basically need to multiple it all together
  1172.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1173.               // are slower, but the conversion to and from cost cycles
  1174.               // need to perform lighting for each vertex (lots of redundant math, optimize later!)
  1175.               //Write_Error("nv0=[%f, %f, %f]=%f, v1=[%f, %f, %f]=%f, v2=[%f, %f, %f]=%f",
  1176.                 // curr_poly->tvlist[0].n.x, curr_poly->tvlist[0].n.y,curr_poly->tvlist[0].n.z, VECTOR4D_Length(&curr_poly->tvlist[0].n),
  1177.                 // curr_poly->tvlist[1].n.x, curr_poly->tvlist[1].n.y,curr_poly->tvlist[1].n.z, VECTOR4D_Length(&curr_poly->tvlist[1].n),
  1178.                 // curr_poly->tvlist[2].n.x, curr_poly->tvlist[2].n.y,curr_poly->tvlist[2].n.z, VECTOR4D_Length(&curr_poly->tvlist[2].n) );
  1179.               // vertex 0
  1180.               dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &lights[curr_light].dir); 
  1181.               
  1182.               // only add light if dp > 0
  1183.               if (dp > 0)
  1184.                  { 
  1185.                  i = 128*dp; 
  1186.                  r_sum0+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1187.                  g_sum0+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1188.                  b_sum0+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1189.                  } // end if
  1190.               // vertex 1
  1191.               dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &lights[curr_light].dir);
  1192.               
  1193.               // only add light if dp > 0
  1194.               if (dp > 0)
  1195.                  { 
  1196.                  i = 128*dp; 
  1197.                  r_sum1+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1198.                  g_sum1+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1199.                  b_sum1+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1200.                  } // end if
  1201.               // vertex 2
  1202.               dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &lights[curr_light].dir);
  1203.               
  1204.               // only add light if dp > 0
  1205.               if (dp > 0)
  1206.                  { 
  1207.                  i = 128*dp; 
  1208.                  r_sum2+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1209.                  g_sum2+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1210.                  b_sum2+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1211.                  } // end if
  1212.               //Write_Error("nexiting infinite, color sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1,  r_sum2, g_sum2, b_sum2);
  1213.               } // end if infinite light
  1214.            else
  1215.            if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) //////////////////////////////////////
  1216.               {
  1217.               // perform point light computations
  1218.               // light model for point light is once again:
  1219.               //              I0point * Clpoint
  1220.               //  I(d)point = ___________________
  1221.               //              kc +  kl*d + kq*d2              
  1222.               //
  1223.               //  Where d = |p - s|
  1224.               // thus it's almost identical to the infinite light, but attenuates as a function
  1225.               // of distance from the point source to the surface point being lit
  1226.               // .. normal already in vertex
  1227.               //Write_Error("nEntering point light....");
  1228.               // compute vector from surface to light
  1229.               VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
  1230.               // compute distance and attenuation
  1231.               dist = VECTOR4D_Length_Fast2(&l);  
  1232.               // and for the diffuse model
  1233.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1234.               // so we basically need to multiple it all together
  1235.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1236.               // are slower, but the conversion to and from cost cycles
  1237.               // perform the calculation for all 3 vertices
  1238.               // vertex 0
  1239.               dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &l);
  1240.               
  1241.               // only add light if dp > 0
  1242.               if (dp > 0)
  1243.                  { 
  1244.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1245.                  i = 128*dp / (dist * atten ); 
  1246.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1247.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1248.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1249.                  } // end if
  1250.               // vertex 1
  1251.               dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &l);
  1252.               
  1253.               // only add light if dp > 0
  1254.               if (dp > 0)
  1255.                  { 
  1256.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1257.                  i = 128*dp / (dist * atten ); 
  1258.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1259.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1260.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1261.                  } // end if
  1262.               // vertex 2
  1263.               dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &l);
  1264.               
  1265.               // only add light if dp > 0
  1266.               if (dp > 0)
  1267.                  { 
  1268.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1269.                  i = 128*dp / (dist * atten ); 
  1270.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1271.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1272.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1273.                  } // end if
  1274.               //Write_Error("nexiting point light, rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
  1275.               } // end if point
  1276.            else
  1277.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ///////////////////////////////////////
  1278.               {
  1279.               // perform spotlight/point computations simplified model that uses
  1280.               // point light WITH a direction to simulate a spotlight
  1281.               // light model for point light is once again:
  1282.               //              I0point * Clpoint
  1283.               //  I(d)point = ___________________
  1284.               //              kc +  kl*d + kq*d2              
  1285.               //
  1286.               //  Where d = |p - s|
  1287.               // thus it's almost identical to the infinite light, but attenuates as a function
  1288.               // of distance from the point source to the surface point being lit
  1289.               //Write_Error("nentering spotlight1....");
  1290.               // .. normal is already computed
  1291.        
  1292.               // compute vector from surface to light
  1293.               VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
  1294.               // compute distance and attenuation
  1295.               dist = VECTOR4D_Length_Fast2(&l);  
  1296.               // and for the diffuse model
  1297.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1298.               // so we basically need to multiple it all together
  1299.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1300.               // are slower, but the conversion to and from cost cycles
  1301.               // note that I use the direction of the light here rather than a the vector to the light
  1302.               // thus we are taking orientation into account which is similar to the spotlight model
  1303.               // vertex 0
  1304.               dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &lights[curr_light].dir);
  1305.               
  1306.               // only add light if dp > 0
  1307.               if (dp > 0)
  1308.                  { 
  1309.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1310.                  i = 128*dp / ( atten ); 
  1311.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1312.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1313.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1314.                  } // end if
  1315.               // vertex 1
  1316.               dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &lights[curr_light].dir);
  1317.               
  1318.               // only add light if dp > 0
  1319.               if (dp > 0)
  1320.                  { 
  1321.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1322.                  i = 128*dp / ( atten ); 
  1323.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1324.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1325.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1326.                  } // end i
  1327.               // vertex 2
  1328.               dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &lights[curr_light].dir);
  1329.               
  1330.               // only add light if dp > 0
  1331.               if (dp > 0)
  1332.                  { 
  1333.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1334.                  i = 128*dp / ( atten ); 
  1335.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1336.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1337.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1338.                  } // end i
  1339.               //Write_Error("nexiting spotlight1, sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1,  r_sum2, g_sum2, b_sum2);
  1340.               } // end if spotlight1
  1341.            else
  1342.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version //////////////////////////
  1343.               {
  1344.               // perform spot light computations
  1345.               // light model for spot light simple version is once again:
  1346.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  1347.               // I(d)spotlight = __________________________________________      
  1348.               //                 kc + kl*d + kq*d2        
  1349.               // Where d = |p - s|, and pf = power factor
  1350.               // thus it's almost identical to the point, but has the extra term in the numerator
  1351.               // relating the angle between the light source and the point on the surface
  1352.               // .. already have normals and length are 1.0
  1353.              
  1354.               // and for the diffuse model
  1355.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1356.               // so we basically need to multiple it all together
  1357.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1358.               // are slower, but the conversion to and from cost cycles
  1359.               //Write_Error("nEntering spotlight2...");
  1360.               // tons of redundant math here! lots to optimize later!
  1361.               
  1362.               // vertex 0
  1363.               dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &lights[curr_light].dir);
  1364.               
  1365.               // only add light if dp > 0
  1366.               if (dp > 0)
  1367.                  { 
  1368.                  // compute vector from light to surface (different from l which IS the light dir)
  1369.                  VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[0].v, &s);
  1370.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1371.                  dists = VECTOR4D_Length_Fast2(&s);  
  1372.                  // compute spot light term (s . l)
  1373.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  1374.                  // proceed only if term is positive
  1375.                  if (dpsl > 0) 
  1376.                     {
  1377.                     // compute attenuation
  1378.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1379.        
  1380.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1381.                     // must be integral
  1382.                     float dpsl_exp = dpsl;
  1383.  
  1384.                     // exponentiate for positive integral powers
  1385.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1386.                          dpsl_exp*=dpsl;
  1387.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1388.                                                       
  1389.                     i = 128*dp * dpsl_exp / ( atten ); 
  1390.                     r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1391.                     g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1392.                     b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1393.   
  1394.                     } // end if
  1395.                  } // end if
  1396.               // vertex 1
  1397.               dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &lights[curr_light].dir);
  1398.               
  1399.               // only add light if dp > 0
  1400.               if (dp > 0)
  1401.                  { 
  1402.                  // compute vector from light to surface (different from l which IS the light dir)
  1403.                  VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[1].v, &s);
  1404.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1405.                  dists = VECTOR4D_Length_Fast2(&s);  
  1406.                  // compute spot light term (s . l)
  1407.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  1408.                  // proceed only if term is positive
  1409.                  if (dpsl > 0) 
  1410.                     {
  1411.                     // compute attenuation
  1412.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1413.        
  1414.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1415.                     // must be integral
  1416.                     float dpsl_exp = dpsl;
  1417.  
  1418.                     // exponentiate for positive integral powers
  1419.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1420.                          dpsl_exp*=dpsl;
  1421.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1422.                                                       
  1423.                     i = 128*dp * dpsl_exp / ( atten ); 
  1424.                     r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1425.                     g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1426.                     b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1427.   
  1428.                     } // end if
  1429.                  } // end if
  1430.               // vertex 2
  1431.               dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &lights[curr_light].dir);
  1432.               
  1433.               // only add light if dp > 0
  1434.               if (dp > 0)
  1435.                  { 
  1436.                  // compute vector from light to surface (different from l which IS the light dir)
  1437.                  VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[2].v, &s);
  1438.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1439.                  dists = VECTOR4D_Length_Fast2(&s);  
  1440.                  // compute spot light term (s . l)
  1441.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  1442.                  // proceed only if term is positive
  1443.                  if (dpsl > 0) 
  1444.                     {
  1445.                     // compute attenuation
  1446.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1447.        
  1448.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1449.                     // must be integral
  1450.                     float dpsl_exp = dpsl;
  1451.  
  1452.                     // exponentiate for positive integral powers
  1453.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1454.                          dpsl_exp*=dpsl;
  1455.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1456.                                                       
  1457.                     i = 128*dp * dpsl_exp / ( atten ); 
  1458.                     r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1459.                     g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1460.                     b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1461.                     } // end if
  1462.                  } // end if
  1463.               //Write_Error("nexiting spotlight2, sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,   r_sum1, g_sum1, b_sum1,  r_sum2, g_sum2, b_sum2);
  1464.               } // end if spot light
  1465.            } // end for light
  1466.   
  1467.        // make sure colors aren't out of range
  1468.        if (r_sum0  > 255) r_sum0 = 255;
  1469.        if (g_sum0  > 255) g_sum0 = 255;
  1470.        if (b_sum0  > 255) b_sum0 = 255;
  1471.        if (r_sum1  > 255) r_sum1 = 255;
  1472.        if (g_sum1  > 255) g_sum1 = 255;
  1473.        if (b_sum1  > 255) b_sum1 = 255;
  1474.        if (r_sum2  > 255) r_sum2 = 255;
  1475.        if (g_sum2  > 255) g_sum2 = 255;
  1476.        if (b_sum2  > 255) b_sum2 = 255;
  1477.        //Write_Error("nwriting color for poly %d", poly);
  1478.        //Write_Error("n******** final sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0,  r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
  1479.        // write all colors in RGB form since we need to interpolate at the rasterizer
  1480.        curr_poly->lit_color[0] = RGB16Bit(r_sum0, g_sum0, b_sum0);
  1481.        curr_poly->lit_color[1] = RGB16Bit(r_sum1, g_sum1, b_sum1);
  1482.        curr_poly->lit_color[2] = RGB16Bit(r_sum2, g_sum2, b_sum2);
  1483.        } // end if
  1484.     else // assume POLY4DV2_ATTR_SHADE_MODE_CONSTANT
  1485.        {
  1486.        // emmisive shading only, do nothing
  1487.        // ...
  1488.        curr_poly->lit_color[0] = curr_poly->color;
  1489.        //Write_Error("nentering constant shader, and exiting...");
  1490.        } // end if
  1491.     } // end for poly
  1492. // return success
  1493. return(1);
  1494. } // end Light_RENDERLIST4DV2_World
  1495. /////////////////////////////////////////////////////////////////////////
  1496. float Compute_OBJECT4DV2_Radius(OBJECT4DV2_PTR obj)
  1497. {
  1498. // this function computes the average and maximum radius for 
  1499. // sent object and opdates the object data for the "current frame"
  1500. // it's up to the caller to make sure Set_Frame() for this object
  1501. // has been called to set the object up properly
  1502. // reset incase there's any residue
  1503. obj->avg_radius[obj->curr_frame] = 0;
  1504. obj->max_radius[obj->curr_frame] = 0;
  1505. // loop thru and compute radius
  1506. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1507.     {
  1508.     // update the average and maximum radius
  1509.     float dist_to_vertex = 
  1510.           sqrt(obj->vlist_local[vertex].x*obj->vlist_local[vertex].x +
  1511.                obj->vlist_local[vertex].y*obj->vlist_local[vertex].y +
  1512.                obj->vlist_local[vertex].z*obj->vlist_local[vertex].z);
  1513.     
  1514.     // accumulate total radius
  1515.     obj->avg_radius[obj->curr_frame]+=dist_to_vertex;
  1516.     // update maximum radius   
  1517.     if (dist_to_vertex > obj->max_radius[obj->curr_frame])
  1518.        obj->max_radius[obj->curr_frame] = dist_to_vertex; 
  1519.  
  1520.     } // end for vertex
  1521. // finallize average radius computation
  1522. obj->avg_radius[obj->curr_frame]/=obj->num_vertices;
  1523. // return max radius of frame 0
  1524. return(obj->max_radius[0]);
  1525. } // end Compute_OBJECT4DV2_Radius
  1526. //////////////////////////////////////////////////////////////////////////////
  1527. char *Extract_Filename_From_Path(char *filepath, char *filename)
  1528. {
  1529. // this function extracts the filename from a complete path and file
  1530. // "../folder/.../filname.ext"
  1531. // the function operates by scanning backward and looking for the first 
  1532. // occurance of "" or "/" then copies the filename from there to the end
  1533. // test of filepath is valid
  1534. if (!filepath || strlen(filepath)==0) 
  1535.     return(NULL);
  1536. int index_end = strlen(filepath)-1;
  1537. // find filename
  1538. while( (filepath[index_end]!='\') && 
  1539.        (filepath[index_end]!='/') && 
  1540.        (filepath[index_end] > 0) ) 
  1541.      index_end--; 
  1542.         
  1543. // copy file name out into filename var
  1544. memcpy(filename, &filepath[index_end+1], strlen(filepath) - index_end);
  1545. // return result
  1546. return(filename);
  1547.       
  1548. } // end Extract_Filename_From_Path
  1549. /////////////////////////////////////////////////////////////////////////////
  1550. int Load_OBJECT4DV2_COB(OBJECT4DV2_PTR obj,   // pointer to object
  1551.                         char *filename,       // filename of Caligari COB file
  1552.                         VECTOR4D_PTR scale,   // initial scaling factors
  1553.                         VECTOR4D_PTR pos,     // initial position
  1554.                         VECTOR4D_PTR rot,     // initial rotations
  1555.                         int vertex_flags)     // flags to re-order vertices 
  1556.                                               // and perform transforms
  1557. {
  1558. // this function loads a Caligari TrueSpace .COB file object in off disk, additionally
  1559. // it allows the caller to scale, position, and rotate the object
  1560. // to save extra calls later for non-dynamic objects, note that this function 
  1561. // works with a OBJECT4DV2 which has support for textures, but not materials, etc, 
  1562. // however we will still parse out the material stuff and get them ready for the 
  1563. // next incarnation objects, so we can re-use this code to support those features
  1564. // also, since this version IS going to read in the texture map and texture coordinates
  1565. // we have a couple issues to think about, first COB format like absolute texture paths
  1566. // we can't have that, so we will simple extract out ONLY the texture map bitmap name
  1567. // and use the global texture path variable to build a real file path, also texture
  1568. // coordinates are in 0..1 0..1 form, I still haven't decided if I want to use absolute
  1569. // coordinates or 0..1 0..1, but right now the affine texture mapper uses 
  1570. // create a parser object
  1571. CPARSERV1 parser; 
  1572. char seps[16];          // seperators for token scanning
  1573. char token_buffer[256]; // used as working buffer for token
  1574. char *token;            // pointer to next token
  1575. int r,g,b;              // working colors
  1576. // cache for texture vertices
  1577. VERTEX2DF texture_vertices[OBJECT4DV2_MAX_VERTICES];
  1578. int num_texture_vertices = 0;
  1579. MATRIX4X4 mat_local,  // storage for local transform if user requests it in cob format
  1580.           mat_world;  // "   " for local to world " "
  1581. // initialize matrices
  1582. MAT_IDENTITY_4X4(&mat_local);
  1583. MAT_IDENTITY_4X4(&mat_world);
  1584. // Step 1: clear out the object and initialize it a bit
  1585. memset(obj, 0, sizeof(OBJECT4DV2));
  1586. // set state of object to active and visible
  1587. obj->state = OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_STATE_VISIBLE;
  1588. // set number of frames
  1589. obj->num_frames = 1;
  1590. obj->curr_frame = 0;
  1591. obj->attr = OBJECT4DV2_ATTR_SINGLE_FRAME;
  1592. // set position of object is caller requested position
  1593. if (pos)
  1594.    {
  1595.    // set position of object
  1596.    obj->world_pos.x = pos->x;
  1597.    obj->world_pos.y = pos->y;
  1598.    obj->world_pos.z = pos->z;
  1599.    obj->world_pos.w = pos->w;
  1600.    } // end 
  1601. else
  1602.    {
  1603.    // set it to (0,0,0,1)
  1604.    obj->world_pos.x = 0;
  1605.    obj->world_pos.y = 0;
  1606.    obj->world_pos.z = 0;
  1607.    obj->world_pos.w = 1;
  1608.    } // end else
  1609. // Step 2: open the file for reading using the parser
  1610. if (!parser.Open(filename))
  1611.    {
  1612.    Write_Error("Couldn't open .COB file %s.", filename);
  1613.    return(0);
  1614.    } // end if
  1615. // Step 3: 
  1616. // lets find the name of the object first 
  1617. while(1)
  1618.      {
  1619.      // get the next line, we are looking for "Name"
  1620.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1621.         {
  1622.         Write_Error("Image 'name' not found in .COB file %s.", filename);
  1623.         return(0);
  1624.         } // end if
  1625.     
  1626.      // check for pattern?  
  1627.      if ( parser.Pattern_Match(parser.buffer, "['Name'] [s>0]") )
  1628.         {
  1629.         // name should be in second string variable, index 1
  1630.         strcpy(obj->name, parser.pstrings[1]);          
  1631.         Write_Error("nCOB Reader Object Name: %s", obj->name);
  1632.         break;    
  1633.         } // end if
  1634.      } // end while
  1635. // step 4: get local and world transforms and store them
  1636. // center 0 0 0
  1637. // x axis 1 0 0
  1638. // y axis 0 1 0
  1639. // z axis 0 0 1
  1640. while(1)
  1641.      {
  1642.      // get the next line, we are looking for "center"
  1643.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1644.         {
  1645.         Write_Error("Center not found in .COB file %s.", filename);
  1646.         return(0);
  1647.         } // end if
  1648.     
  1649.      // check for pattern?  
  1650.      if ( parser.Pattern_Match(parser.buffer, "['center'] [f] [f] [f]") )
  1651.         {
  1652.         // the "center" holds the translation factors, so place in
  1653.         // last row of homogeneous matrix, note that these are row vectors
  1654.         // that we need to drop in each column of matrix
  1655.         mat_local.M[3][0] = -parser.pfloats[0]; // center x
  1656.         mat_local.M[3][1] = -parser.pfloats[1]; // center y
  1657.         mat_local.M[3][2] = -parser.pfloats[2]; // center z
  1658.         // ok now, the next 3 lines should be the x,y,z transform vectors
  1659.         // so build up   
  1660.         // "x axis" 
  1661.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1662.         parser.Pattern_Match(parser.buffer, "['x'] ['axis'] [f] [f] [f]");
  1663.       
  1664.         // place row in x column of transform matrix
  1665.         mat_local.M[0][0] = parser.pfloats[0]; // rxx
  1666.         mat_local.M[1][0] = parser.pfloats[1]; // rxy
  1667.         mat_local.M[2][0] = parser.pfloats[2]; // rxz
  1668.         // "y axis" 
  1669.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1670.         parser.Pattern_Match(parser.buffer, "['y'] ['axis'] [f] [f] [f]");
  1671.       
  1672.         // place row in y column of transform matrix
  1673.         mat_local.M[0][1] = parser.pfloats[0]; // ryx
  1674.         mat_local.M[1][1] = parser.pfloats[1]; // ryy
  1675.         mat_local.M[2][1] = parser.pfloats[2]; // ryz
  1676.         // "z axis" 
  1677.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1678.         parser.Pattern_Match(parser.buffer, "['z'] ['axis'] [f] [f] [f]");
  1679.       
  1680.         // place row in z column of transform matrix
  1681.         mat_local.M[0][2] = parser.pfloats[0]; // rzx
  1682.         mat_local.M[1][2] = parser.pfloats[1]; // rzy
  1683.         mat_local.M[2][2] = parser.pfloats[2]; // rzz
  1684.         Print_Mat_4X4(&mat_local, "Local COB Matrix:");
  1685.         break;    
  1686.         } // end if
  1687.      } // end while
  1688. // now "Transform"
  1689. while(1)
  1690.      {
  1691.      // get the next line, we are looking for "Transform"
  1692.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1693.         {
  1694.         Write_Error("Transform not found in .COB file %s.", filename);
  1695.         return(0);
  1696.         } // end if
  1697.     
  1698.      // check for pattern?  
  1699.      if ( parser.Pattern_Match(parser.buffer, "['Transform']") )
  1700.         {
  1701.         // "x axis" 
  1702.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1703.         parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
  1704.       
  1705.         // place row in x column of transform matrix
  1706.         mat_world.M[0][0] = parser.pfloats[0]; // rxx
  1707.         mat_world.M[1][0] = parser.pfloats[1]; // rxy
  1708.         mat_world.M[2][0] = parser.pfloats[2]; // rxz
  1709.         // "y axis" 
  1710.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1711.         parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
  1712.       
  1713.         // place row in y column of transform matrix
  1714.         mat_world.M[0][1] = parser.pfloats[0]; // ryx
  1715.         mat_world.M[1][1] = parser.pfloats[1]; // ryy
  1716.         mat_world.M[2][1] = parser.pfloats[2]; // ryz
  1717.         // "z axis" 
  1718.         parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
  1719.         parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
  1720.       
  1721.         // place row in z column of transform matrix
  1722.         mat_world.M[0][2] = parser.pfloats[0]; // rzx
  1723.         mat_world.M[1][2] = parser.pfloats[1]; // rzy
  1724.         mat_world.M[2][2] = parser.pfloats[2]; // rzz
  1725.         Print_Mat_4X4(&mat_world, "World COB Matrix:");
  1726.         // no need to read in last row, since it's always 0,0,0,1 and we don't use it anyway
  1727.         break;    
  1728.         } // end if
  1729.      } // end while
  1730. // step 6: get number of vertices and polys in object
  1731. while(1)
  1732.      {
  1733.      // get the next line, we are looking for "World Vertices" 
  1734.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1735.         {
  1736.         Write_Error("'World Vertices' line not found in .COB file %s.", filename);
  1737.         return(0);
  1738.         } // end if
  1739.     
  1740.      // check for pattern?  
  1741.      if (parser.Pattern_Match(parser.buffer, "['World'] ['Vertices'] [i]") )
  1742.         {
  1743.         // simply extract the number of vertices from the pattern matching 
  1744.         // output arrays
  1745.         obj->num_vertices = parser.pints[0];
  1746.         Write_Error("nCOB Reader Num Vertices: %d", obj->num_vertices);
  1747.         break;    
  1748.  
  1749.         } // end if
  1750.      } // end while
  1751. // allocate the memory for the vertices and number of polys (unknown, so use 3*num_vertices)
  1752. // the call parameters are redundant in this case, but who cares
  1753. if (!Init_OBJECT4DV2(obj,   // object to allocate
  1754.                 obj->num_vertices, 
  1755.                 obj->num_vertices*3,  
  1756.                 obj->num_frames))
  1757.     {
  1758.     Write_Error("nASC file error with file %s (can't allocate memory).",filename);
  1759.     } // end if
  1760. // Step 7: load the vertex list
  1761. // now read in vertex list, format:
  1762. // "d.d d.d d.d"
  1763.  for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1764.      {
  1765.      // hunt for vertex
  1766.      while(1)
  1767.      {
  1768.      // get the next vertex
  1769.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1770.         {
  1771.         Write_Error("nVertex list ended abruptly! in .COB file %s.", filename);
  1772.         return(0);
  1773.         } // end if
  1774.     
  1775.      // check for pattern?  
  1776.      if (parser.Pattern_Match(parser.buffer, "[f] [f] [f]"))
  1777.         {
  1778.         // at this point we have the x,y,z in the the pfloats array locations 0,1,2
  1779.         obj->vlist_local[vertex].x = parser.pfloats[0];
  1780.         obj->vlist_local[vertex].y = parser.pfloats[1];
  1781.         obj->vlist_local[vertex].z = parser.pfloats[2];
  1782.         obj->vlist_local[vertex].w = 1;
  1783.         // do vertex swapping right here, allow muliple swaps, why not!
  1784.         // defines for vertex re-ordering flags
  1785.         //#define VERTEX_FLAGS_INVERT_X   1    // inverts the Z-coordinates
  1786.         //#define VERTEX_FLAGS_INVERT_Y   2    // inverts the Z-coordinates
  1787.         //#define VERTEX_FLAGS_INVERT_Z   4    // inverts the Z-coordinates
  1788.         //#define VERTEX_FLAGS_SWAP_YZ    8    // transforms a RHS model to a LHS model
  1789.         //#define VERTEX_FLAGS_SWAP_XZ    16   // ???
  1790.         //#define VERTEX_FLAGS_SWAP_XY    32
  1791.         //#define VERTEX_FLAGS_INVERT_WINDING_ORDER 64  // invert winding order from cw to ccw or ccw to cc
  1792.         //#define VERTEX_FLAGS_TRANSFORM_LOCAL         512   // if file format has local transform then do it!
  1793.         //#define VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD  1024  // if file format has local to world then do it!
  1794.         VECTOR4D temp_vector; // temp for calculations
  1795.         // now apply local and world transformations encoded in COB format
  1796.         if (vertex_flags & VERTEX_FLAGS_TRANSFORM_LOCAL )
  1797.            {
  1798.            Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].v, &mat_local, &temp_vector);
  1799.            VECTOR4D_COPY(&obj->vlist_local[vertex].v, &temp_vector); 
  1800.            } // end if 
  1801.         if (vertex_flags & VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD )
  1802.            {
  1803.            Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].v, &mat_world, &temp_vector);
  1804.            VECTOR4D_COPY(&obj->vlist_local[vertex].v, &temp_vector); 
  1805.            } // end if 
  1806.         float temp_f; // used for swapping
  1807.         // invert signs?
  1808.         if (vertex_flags & VERTEX_FLAGS_INVERT_X)
  1809.            obj->vlist_local[vertex].x=-obj->vlist_local[vertex].x;
  1810.         if (vertex_flags & VERTEX_FLAGS_INVERT_Y)
  1811.            obj->vlist_local[vertex].y=-obj->vlist_local[vertex].y;
  1812.         if (vertex_flags & VERTEX_FLAGS_INVERT_Z)
  1813.            obj->vlist_local[vertex].z=-obj->vlist_local[vertex].z;
  1814.         // swap any axes?
  1815.         if (vertex_flags & VERTEX_FLAGS_SWAP_YZ)
  1816.            SWAP(obj->vlist_local[vertex].y, obj->vlist_local[vertex].z, temp_f);
  1817.         
  1818.         if (vertex_flags & VERTEX_FLAGS_SWAP_XZ)
  1819.            SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].z, temp_f);
  1820.         if (vertex_flags & VERTEX_FLAGS_SWAP_XY)
  1821.            SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].y, temp_f);
  1822.         // scale vertices
  1823.         if (scale)
  1824.            {
  1825.            obj->vlist_local[vertex].x*=scale->x;
  1826.            obj->vlist_local[vertex].y*=scale->y;
  1827.            obj->vlist_local[vertex].z*=scale->z;
  1828.            } // end if
  1829.           Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
  1830.                                            obj->vlist_local[vertex].x, 
  1831.                                            obj->vlist_local[vertex].y, 
  1832.                                            obj->vlist_local[vertex].z,
  1833.                                            obj->vlist_local[vertex].w);
  1834.          // set point field in this vertex, we need that at least 
  1835.          SET_BIT(obj->vlist_local[vertex].attr, VERTEX4DTV1_ATTR_POINT);
  1836.         // found vertex, break out of while for next pass
  1837.         break;
  1838.         } // end if
  1839.     } // end while
  1840.     } // end for vertex
  1841. // compute average and max radius
  1842. Compute_OBJECT4DV2_Radius(obj);
  1843. Write_Error("nObject average radius = %f, max radius = %f", 
  1844.             obj->avg_radius, obj->max_radius);
  1845. // step 8: get number of texture vertices
  1846. while(1)
  1847.      {
  1848.      // get the next line, we are looking for "Texture Vertices ddd" 
  1849.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1850.         {
  1851.         Write_Error("'Texture Vertices' line not found in .COB file %s.", filename);
  1852.         return(0);
  1853.         } // end if
  1854.     
  1855.      // check for pattern?  
  1856.      if (parser.Pattern_Match(parser.buffer, "['Texture'] ['Vertices'] [i]") )
  1857.         {
  1858.         // simply extract the number of texture vertices from the pattern matching 
  1859.         // output arrays
  1860.         num_texture_vertices = parser.pints[0];
  1861.         Write_Error("nCOB Reader Texture Vertices: %d", num_texture_vertices);
  1862.         break;    
  1863.  
  1864.         } // end if
  1865.      } // end while
  1866. // Step 9: load the texture vertex list in format "U V"
  1867. // "d.d d.d"
  1868.  for (int tvertex = 0; tvertex < num_texture_vertices; tvertex++)
  1869.      {
  1870.      // hunt for texture
  1871.      while(1)
  1872.      {
  1873.      // get the next vertex
  1874.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1875.         {
  1876.         Write_Error("nTexture Vertex list ended abruptly! in .COB file %s.", filename);
  1877.         return(0);
  1878.         } // end if
  1879.     
  1880.      // check for pattern?  
  1881.      if (parser.Pattern_Match(parser.buffer, "[f] [f]"))
  1882.         {
  1883.         // at this point we have the U V in the the pfloats array locations 0,1 for this 
  1884.         // texture vertex, store in texture coordinate list
  1885.         // note texture coords are in 0..1 format, and must be scaled to texture size
  1886.         // after we load the texture
  1887.         obj->tlist[tvertex].x = parser.pfloats[0]; 
  1888.         obj->tlist[tvertex].y = parser.pfloats[1];
  1889.         Write_Error("nTexture Vertex %d: U=%f, V=%f", tvertex,
  1890.                                           obj->tlist[tvertex].x, 
  1891.                                           obj->tlist[tvertex].y );
  1892.         // found vertex, break out of while for next pass
  1893.         break;
  1894.         } // end if
  1895.        } // end while
  1896.    } // end for
  1897. // when we load in the polygons then we will copy the texture vertices into the polygon
  1898. // vertices assuming that each vertex has a SINGLE texture coordinate, this means that
  1899. // you must NOT use multiple textures on an object! in other words think "skin" this is
  1900. // inline with Quake II md2 format, in 99% of the cases a single object can be textured
  1901. // with a single skin and the texture coordinates can be unique for each vertex and 1:1
  1902. int poly_material[OBJECT4DV2_MAX_POLYS]; // this holds the material index for each polygon
  1903.                                          // we need these indices since when reading the file
  1904.                                          // we read the polygons BEFORE the materials, so we need
  1905.                                          // this data, so we can go back later and extract the material
  1906.                                          // that each poly WAS assigned and get the colors out, since
  1907.                                          // objects and polygons do not currently support materials
  1908. int material_index_referenced[MAX_MATERIALS];   // used to track if an index has been used yet as a material 
  1909.                                                 // reference. since we don't know how many materials, we need
  1910.                                                 // a way to count them up, but if we have seen a material reference
  1911.                                                 // more than once then we don't increment the total number of materials
  1912.                                                 // this array is for this
  1913. // clear out reference array
  1914. memset(material_index_referenced,0, sizeof(material_index_referenced));
  1915. // step 10: load in the polygons
  1916. // poly list starts off with:
  1917. // "Faces ddd:"
  1918. while(1)
  1919.      {
  1920.      // get next line
  1921.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1922.         {
  1923.         Write_Error("n'Faces' line not found in .COB file %s.", filename);
  1924.         return(0);
  1925.         } // end if
  1926.     
  1927.      // check for pattern?  
  1928.      if (parser.Pattern_Match(parser.buffer, "['Faces'] [i]"))
  1929.         {
  1930.         Write_Error("nCOB Reader found face list in .COB file %s.", filename);
  1931.         // finally set number of polys
  1932.         obj->num_polys = parser.pints[0];
  1933.         break;
  1934.         } // end if
  1935.      } // end while
  1936. // now read each face in format:
  1937. // Face verts nn flags ff mat mm
  1938. // the nn is the number of vertices, always 3
  1939. // the ff is the flags, unused for now, has to do with holes
  1940. // the mm is the material index number 
  1941. int poly_surface_desc    = 0; // ASC surface descriptor/material in this case
  1942. int poly_num_verts       = 0; // number of vertices for current poly (always 3)
  1943. int num_materials_object = 0; // number of materials for this object
  1944. for (int poly=0; poly < obj->num_polys; poly++)
  1945.     {
  1946.     Write_Error("nPolygon %d:", poly);
  1947.     // hunt until next face is found
  1948.     while(1)
  1949.          {
  1950.          // get the next polygon face
  1951.          if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1952.             {
  1953.             Write_Error("nface list ended abruptly! in .COB file %s.", filename);
  1954.             return(0);
  1955.             } // end if
  1956.      
  1957.          // check for pattern?  
  1958.          if (parser.Pattern_Match(parser.buffer, "['Face'] ['verts'] [i] ['flags'] [i] ['mat'] [i]"))
  1959.             {
  1960.             // at this point we have the number of vertices for the polygon, the flags, and it's material index
  1961.             // in the integer output array locations 0,1,2
  1962.             // store the material index for this polygon for retrieval later, but make sure adjust the 
  1963.             // the index to take into consideration that the data in parser.pints[2] is 0 based, and we need
  1964.             // an index relative to the entire library, so we simply need to add num_materials to offset the 
  1965.             // index properly, but we will leave this reference zero based for now... and fix up later
  1966.             poly_material[poly] = parser.pints[2];
  1967.             // update the reference array
  1968.             if (material_index_referenced[ poly_material[poly] ] == 0)
  1969.                {
  1970.                // mark as referenced
  1971.                material_index_referenced[ poly_material[poly] ] = 1;
  1972.                // increment total number of materials for this object
  1973.                num_materials_object++;
  1974.                } // end if        
  1975.             // test if number of vertices is 3
  1976.             if (parser.pints[0]!=3)
  1977.                {
  1978.                Write_Error("nface not a triangle! in .COB file %s.", filename);
  1979.                return(0);
  1980.                } // end if
  1981.            // now read out the vertex indices and texture indices format:
  1982.            // <vindex0, tindex0>  <vindex1, tindex1> <vindex1, tindex1> 
  1983.            if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  1984.               {
  1985.               Write_Error("nface list ended abruptly! in .COB file %s.", filename);
  1986.               return(0);
  1987.               } // end if
  1988.            // lets replace ",<>" with ' ' to make extraction easy
  1989.            ReplaceChars(parser.buffer, parser.buffer, ",<>",' ');      
  1990.            parser.Pattern_Match(parser.buffer, "[i] [i] [i] [i] [i] [i]");
  1991.  
  1992.            // 0,2,4 holds vertex indices
  1993.            // 1,3,5 holds texture indices
  1994.            
  1995.           // insert polygon, check for winding order invert
  1996.           if (vertex_flags & VERTEX_FLAGS_INVERT_WINDING_ORDER)
  1997.              {     
  1998.              poly_num_verts           = 3;
  1999.              obj->plist[poly].vert[0] = parser.pints[4];
  2000.              obj->plist[poly].vert[1] = parser.pints[2];
  2001.              obj->plist[poly].vert[2] = parser.pints[0];
  2002.              // now copy the texture coordinates into the vertices, this
  2003.              // may not be needed if the polygon doesn't have texture mapping
  2004.              // enabled, etc., 
  2005.              // so here's the deal the texture coordinates that 
  2006.              // map to vertex 0,1,2 have indices stored in the odd
  2007.              // numbered pints[] locations, so we simply need to copy
  2008.              // the right texture coordinate into the right vertex
  2009.              obj->plist[poly].text[0] = parser.pints[5];
  2010.              obj->plist[poly].text[1] = parser.pints[3];
  2011.              obj->plist[poly].text[2] = parser.pints[1];
  2012.              
  2013.              Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
  2014.                                                                    parser.pints[5],
  2015.                                                                    obj->tlist[ parser.pints[5] ].x, 
  2016.                                                                    obj->tlist[ parser.pints[5] ].y,
  2017.                                                                    obj->plist[poly].vert[0] );
  2018.              Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
  2019.                                                                    parser.pints[3],
  2020.                                                                    obj->tlist[ parser.pints[3] ].x, 
  2021.                                                                    obj->tlist[ parser.pints[3] ].y,
  2022.                                                                    obj->plist[poly].vert[1] );
  2023.              Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
  2024.                                                                    parser.pints[1],
  2025.                                                                    obj->tlist[ parser.pints[1] ].x, 
  2026.                                                                    obj->tlist[ parser.pints[1] ].y,
  2027.                                                                    obj->plist[poly].vert[2] );
  2028.    
  2029.              } // end if
  2030.           else
  2031.              { // leave winding order alone
  2032.              poly_num_verts           = 3;
  2033.              obj->plist[poly].vert[0] = parser.pints[0];
  2034.              obj->plist[poly].vert[1] = parser.pints[2];
  2035.              obj->plist[poly].vert[2] = parser.pints[4];
  2036.              // now copy the texture coordinates into the vertices, this
  2037.              // may not be needed if the polygon doesn't have texture mapping
  2038.              // enabled, etc., 
  2039.              // so here's the deal the texture coordinates that 
  2040.              // map to vertex 0,1,2 have indices stored in the odd
  2041.              // numbered pints[] locations, so we simply need to copy
  2042.              // the right texture coordinate into the right vertex
  2043.              obj->plist[poly].text[0] = parser.pints[1];
  2044.              obj->plist[poly].text[1] = parser.pints[3];
  2045.              obj->plist[poly].text[2] = parser.pints[5];
  2046.              
  2047.              Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
  2048.                                                                    parser.pints[1],
  2049.                                                                    obj->tlist[ parser.pints[1] ].x, 
  2050.                                                                    obj->tlist[ parser.pints[1] ].y,
  2051.                                                                    obj->plist[poly].vert[0] );
  2052.              Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
  2053.                                                                    parser.pints[3],
  2054.                                                                    obj->tlist[ parser.pints[3] ].x, 
  2055.                                                                    obj->tlist[ parser.pints[3] ].y,
  2056.                                                                    obj->plist[poly].vert[1] );
  2057.              Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
  2058.                                                                    parser.pints[5],
  2059.                                                                    obj->tlist[ parser.pints[5] ].x, 
  2060.                                                                    obj->tlist[ parser.pints[5] ].y,
  2061.                                                                    obj->plist[poly].vert[2] );
  2062.              } // end else
  2063.           // point polygon vertex list to object's vertex list
  2064.           // note that this is redundant since the polylist is contained
  2065.           // within the object in this case and its up to the user to select
  2066.           // whether the local or transformed vertex list is used when building up
  2067.           // polygon geometry, might be a better idea to set to NULL in the context
  2068.           // of polygons that are part of an object
  2069.           obj->plist[poly].vlist = obj->vlist_local; 
  2070.           // set texture coordinate list, this is needed
  2071.           obj->plist[poly].tlist = obj->tlist;
  2072.           // set polygon to active
  2073.           obj->plist[poly].state = POLY4DV2_STATE_ACTIVE;    
  2074.           // found the face, break out of while for another pass
  2075.           break;
  2076.           } // end if
  2077.  
  2078.        } // end while      
  2079.        Write_Error("nLocal material Index=%d, total materials for object = %d, vert_indices [%d, %d, %d]", 
  2080.                                                                                 poly_material[poly],
  2081.                                                                                 num_materials_object,
  2082.                                                                                 obj->plist[poly].vert[0],
  2083.                                                                                 obj->plist[poly].vert[1],
  2084.                                                                                 obj->plist[poly].vert[2]);       
  2085.     } // end for poly
  2086. // now find materials!!! and we are out of here!
  2087. for (int curr_material = 0; curr_material < num_materials_object; curr_material++)
  2088.     {
  2089.     // hunt for the material header "mat# ddd"
  2090.     while(1)
  2091.     {
  2092.     // get the next polygon material 
  2093.     if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2094.        {
  2095.        Write_Error("nmaterial list ended abruptly! in .COB file %s.", filename);
  2096.        return(0);
  2097.        } // end if
  2098.      
  2099.     // check for pattern?  
  2100.     if (parser.Pattern_Match(parser.buffer, "['mat#'] [i]") )
  2101.        {
  2102.        // extract the material that is being defined 
  2103.        int material_index = parser.pints[0];
  2104.        // get color of polygon, although it might be irrelevant for a textured surface
  2105.        while(1)
  2106.             {
  2107.             // get the next line
  2108.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2109.                {
  2110.                Write_Error("nRGB color ended abruptly! in .COB file %s.", filename);
  2111.                return(0);
  2112.                } // end if
  2113.                // replace the , comma's if there are any with spaces
  2114.                ReplaceChars(parser.buffer, parser.buffer, ",", ' ', 1);
  2115.                // look for "rgb float,float,float"
  2116.                if (parser.Pattern_Match(parser.buffer, "['rgb'] [f] [f] [f]") )
  2117.                   {
  2118.                   // extract data and store color in material libary
  2119.                   // pfloats[] 0,1,2,3, has data
  2120.                   materials[material_index + num_materials].color.r = (int)(parser.pfloats[0]*255 + 0.5);
  2121.                   materials[material_index + num_materials].color.g = (int)(parser.pfloats[1]*255 + 0.5);
  2122.                   materials[material_index + num_materials].color.b = (int)(parser.pfloats[2]*255 + 0.5);
  2123.                   break; // while looking for rgb
  2124.                   } // end if
  2125.              } // end while    
  2126.        // extract out lighting constants for the heck of it, they are on a line like this:
  2127.        // "alpha float ka float ks float exp float ior float"
  2128.        // alpha is transparency           0 - 1
  2129.        // ka is ambient coefficient       0 - 1
  2130.        // ks is specular coefficient      0 - 1
  2131.        // exp is highlight power exponent 0 - 1
  2132.        // ior is index of refraction (unused)
  2133.        // although our engine will have minimal support for these, we might as well get them
  2134.        while(1)
  2135.             {
  2136.             // get the next line
  2137.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2138.                {
  2139.                Write_Error("nmaterial properties ended abruptly! in .COB file %s.", filename);
  2140.                return(0);
  2141.                } // end if
  2142.             // look for "alpha float ka float ks float exp float ior float"
  2143.             if (parser.Pattern_Match(parser.buffer, "['alpha'] [f] ['ka'] [f] ['ks'] [f] ['exp'] [f]") )
  2144.                {
  2145.                // extract data and store in material libary
  2146.                // pfloats[] 0,1,2,3, has data
  2147.                materials[material_index + num_materials].color.a  = (UCHAR)(parser.pfloats[0]*255 + 0.5);
  2148.                materials[material_index + num_materials].ka       = parser.pfloats[1];
  2149.                materials[material_index + num_materials].kd       = 1; // hard code for now
  2150.                materials[material_index + num_materials].ks       = parser.pfloats[2];
  2151.                materials[material_index + num_materials].power    = parser.pfloats[3];
  2152.  
  2153.                // compute material reflectivities in pre-multiplied format to help engine
  2154.                for (int rgb_index=0; rgb_index < 3; rgb_index++)
  2155.                     {
  2156.                     // ambient reflectivity
  2157.                     materials[material_index + num_materials].ra.rgba_M[rgb_index] = 
  2158.                               ( (UCHAR)(materials[material_index + num_materials].ka * 
  2159.                                 (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
  2160.   
  2161.                     // diffuse reflectivity
  2162.                     materials[material_index + num_materials].rd.rgba_M[rgb_index] = 
  2163.                               ( (UCHAR)(materials[material_index + num_materials].kd * 
  2164.                                 (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
  2165.   
  2166.                     // specular reflectivity
  2167.                     materials[material_index + num_materials].rs.rgba_M[rgb_index] = 
  2168.                               ( (UCHAR)(materials[material_index + num_materials].ks * 
  2169.                                 (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
  2170.                      } // end for rgb_index
  2171.                break;
  2172.                } // end if
  2173.              } // end while    
  2174.        // now we need to know the shading model, it's a bit tricky, we need to look for the lines
  2175.        // "Shader class: color" first, then after this line is:
  2176.        // "Shader name: "xxxxxx" (xxxxxx) "
  2177.        // where the xxxxx part will be "plain color" and "plain" for colored polys 
  2178.        // or "texture map" and "caligari texture"  for textures
  2179.        // THEN based on that we hunt for "Shader class: reflectance" which is where the type
  2180.        // of shading is encoded, we look for the "Shader name: "xxxxxx" (xxxxxx) " again, 
  2181.        // and based on it's value we map it to our shading system as follows:
  2182.        // "constant" -> MATV1_ATTR_SHADE_MODE_CONSTANT 
  2183.        // "matte"    -> MATV1_ATTR_SHADE_MODE_FLAT
  2184.        // "plastic"  -> MATV1_ATTR_SHADE_MODE_GOURAUD
  2185.        // "phong"    -> MATV1_ATTR_SHADE_MODE_FASTPHONG 
  2186.        // and in the case that in the "color" class, we found a "texture map" then the "shading mode" is
  2187.        // "texture map" -> MATV1_ATTR_SHADE_MODE_TEXTURE 
  2188.        // which must be logically or'ed with the other previous modes
  2189.  
  2190.        //  look for the "shader class: color"
  2191.        while(1)
  2192.             {
  2193.             // get the next line
  2194.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2195.                {
  2196.                Write_Error("nshader class ended abruptly! in .COB file %s.", filename);
  2197.                return(0);
  2198.                } // end if
  2199.        
  2200.             if (parser.Pattern_Match(parser.buffer, "['Shader'] ['class:'] ['color']") )
  2201.                {
  2202.                break;
  2203.                } // end if
  2204.              } // end while
  2205.           
  2206.        // now look for the shader name for this class
  2207.        // Shader name: "plain color" or Shader name: "texture map"
  2208.        while(1)
  2209.             {
  2210.             // get the next line
  2211.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2212.                {
  2213.                Write_Error("nshader name ended abruptly! in .COB file %s.", filename);
  2214.                return(0);
  2215.                } // end if
  2216.             // replace the " with spaces
  2217.             ReplaceChars(parser.buffer, parser.buffer, """, ' ', 1);
  2218.             // is this a "plain color" poly?
  2219.             if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] ['plain'] ['color']") )
  2220.                {
  2221.                // not much to do this is default, we need to wait for the reflectance type
  2222.                // to tell us the shading mode
  2223.                break;
  2224.                } // end if
  2225.             // is this a "texture map" poly?
  2226.             if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] ['texture'] ['map']") )
  2227.                {
  2228.                // set the texture mapping flag in material
  2229.                SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_TEXTURE);
  2230.       
  2231.                // almost done, we need the file name of the darn texture map, its in this format:
  2232.                // file name: string "D:Source..modelstextureswall01.bmp"
  2233.           
  2234.                // of course the filename in the quotes will change
  2235.                // so lets hunt until we find it...
  2236.                while(1)
  2237.                     {
  2238.                     // get the next line
  2239.                     if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2240.                        {
  2241.                        Write_Error("ncouldnt find texture name! in .COB file %s.", filename);
  2242.                        return(0);
  2243.                        } // end if
  2244.                     // replace the " with spaces
  2245.                     ReplaceChars(parser.buffer, parser.buffer, """, ' ', 1);
  2246.                     // is this the file name?
  2247.                     if (parser.Pattern_Match(parser.buffer, "['file'] ['name:'] ['string']") )
  2248.                        {
  2249.                        // and save the FULL filename (useless though since its the path from the 
  2250.                        // machine that created it, but later we might want some of the info).
  2251.                        // filename and path starts at char position 19, 0 indexed
  2252.                        memcpy(materials[material_index + num_materials].texture_file, &parser.buffer[18], strlen(parser.buffer) - 18 + 2 );
  2253.                        // the OBJECT4DV2 is only allowed a single texture, although we are loading in all
  2254.                        // the materials, if this is the first texture map, load it, and set a flag disallowing
  2255.                        // any more texture loads for the object
  2256.                        if (!obj->texture)
  2257.                           {
  2258.                           // step 1: allocate memory for bitmap
  2259.                           obj->texture = (BITMAP_IMAGE_PTR)malloc(sizeof(BITMAP_IMAGE));
  2260.                           // load the texture, just use the final file name and the absolute global 
  2261.                           // texture path
  2262.                           char filename[80];
  2263.                           char path_filename[80];
  2264.                           // get the filename                     
  2265.                           Extract_Filename_From_Path(materials[material_index + num_materials].texture_file, filename);
  2266.                           // build the filename with root path
  2267.                           strcpy(path_filename, texture_path);
  2268.                           strcat(path_filename, filename);
  2269.                           // buffer now holds final texture path and file name
  2270.                           // load the bitmap(8/16 bit)
  2271.                           Load_Bitmap_File(&bitmap16bit, path_filename);
  2272.                           // create a proper size and bitdepth bitmap
  2273.                           Create_Bitmap(obj->texture,0,0,
  2274.                                         bitmap16bit.bitmapinfoheader.biWidth,
  2275.                                         bitmap16bit.bitmapinfoheader.biHeight,
  2276.                                         bitmap16bit.bitmapinfoheader.biBitCount);
  2277.                           
  2278.                           // load the bitmap image (later make this 8/16 bit)
  2279.                           if (obj->texture->bpp == 16)
  2280.                              Load_Image_Bitmap16(obj->texture, &bitmap16bit,0,0,BITMAP_EXTRACT_MODE_ABS);
  2281.                           else
  2282.                              {
  2283.                              Load_Image_Bitmap(obj->texture, &bitmap16bit,0,0,BITMAP_EXTRACT_MODE_ABS);
  2284.                              } // end else 8 bit
  2285.                           // done, so unload the bitmap
  2286.                           Unload_Bitmap_File(&bitmap16bit);
  2287.                           // flag object as having textures
  2288.                           SET_BIT(obj->attr, OBJECT4DV2_ATTR_TEXTURES);
  2289.                           } // end if
  2290.                        break;
  2291.                        } // end if
  2292.                     } // end while
  2293.                 break;
  2294.                 } // end if
  2295.             } // end while 
  2296.        // alright, finally! Now we need to know what the actual shader type, now in the COB format
  2297.        // I have decided that in the "reflectance" class that's where we will look at what kind
  2298.        // of shader is supposed to be used on the polygon
  2299.        //  look for the "Shader class: reflectance"
  2300.        while(1)
  2301.             {
  2302.             // get the next line
  2303.             if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2304.                {
  2305.                Write_Error("nshader reflectance class not found in .COB file %s.", filename);
  2306.                return(0);
  2307.                } // end if
  2308.             // look for "Shader class: reflectance"
  2309.             if (parser.Pattern_Match(parser.buffer, "['Shader'] ['class:'] ['reflectance']") )
  2310.                {
  2311.                // now we know the next "shader name" is what we are looking for so, break
  2312.                break;
  2313.                } // end if
  2314.              } // end while    
  2315.         // looking for "Shader name: "xxxxxx" (xxxxxx) " again, 
  2316.         // and based on it's value we map it to our shading system as follows:
  2317.         // "constant" -> MATV1_ATTR_SHADE_MODE_CONSTANT 
  2318.         // "matte"    -> MATV1_ATTR_SHADE_MODE_FLAT
  2319.         // "plastic"  -> MATV1_ATTR_SHADE_MODE_GOURAUD
  2320.         // "phong"    -> MATV1_ATTR_SHADE_MODE_FASTPHONG 
  2321.         // and in the case that in the "color" class, we found a "texture map" then the "shading mode" is
  2322.         // "texture map" -> MATV1_ATTR_SHADE_MODE_TEXTURE 
  2323.         // which must be logically or'ed with the other previous modes
  2324.         while(1)
  2325.              {
  2326.              // get the next line
  2327.              if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2328.                 {
  2329.                 Write_Error("nshader name ended abruptly! in .COB file %s.", filename);
  2330.                 return(0);
  2331.                 } // end if
  2332.          
  2333.              // get rid of those quotes
  2334.              ReplaceChars(parser.buffer, parser.buffer, """,' ',1);
  2335.              // did we find the name?
  2336.             if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] [s>0]" ) )
  2337.                {
  2338.                // figure out which shader to use
  2339.                if (strcmp(parser.pstrings[2], "constant") == 0)
  2340.                   {
  2341.                   // set the shading mode flag in material
  2342.                   SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_CONSTANT);
  2343.                   } // end if
  2344.                else
  2345.                if (strcmp(parser.pstrings[2], "matte") == 0)
  2346.                   {
  2347.                   // set the shading mode flag in material
  2348.                   SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FLAT);
  2349.                   } // end if
  2350.                else
  2351.                if (strcmp(parser.pstrings[2], "plastic") == 0)
  2352.                   {
  2353.                   // set the shading mode flag in material
  2354.                   SET_BIT(materials[curr_material + num_materials].attr, MATV1_ATTR_SHADE_MODE_GOURAUD);
  2355.                   } // end if
  2356.                else
  2357.                if (strcmp(parser.pstrings[2], "phong") == 0)
  2358.                   {
  2359.                   // set the shading mode flag in material
  2360.                   SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FASTPHONG);
  2361.                   } // end if
  2362.                else
  2363.                   {
  2364.                   // set the shading mode flag in material
  2365.                   SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FLAT);
  2366.                   } // end else
  2367.             break;
  2368.             } // end if
  2369.          } // end while
  2370.           
  2371.        // found the material, break out of while for another pass
  2372.        break;
  2373.        } // end if found material
  2374.     } // end while looking for mat#1
  2375.     } // end for curr_material
  2376. // at this point poly_material[] holds all the indices for the polygon materials (zero based, so they need fix up)
  2377. // and we must access the materials array to fill in each polygon with the polygon color, etc.
  2378. // now that we finally have the material libary loaded
  2379. for (int curr_poly = 0; curr_poly < obj->num_polys; curr_poly++)
  2380.     {
  2381.     Write_Error("nfixing poly material %d from index %d to index %d", curr_poly, 
  2382.                                                                        poly_material[curr_poly],
  2383.                                                                        poly_material[curr_poly] + num_materials  );
  2384.     // fix up offset
  2385.     poly_material[curr_poly]  = poly_material[curr_poly] + num_materials;
  2386.     // we need to know what color depth we are dealing with, so check
  2387.     // the bits per pixel, this assumes that the system has already
  2388.     // made the call to DDraw_Init() or set the bit depth
  2389.     if (screen_bpp == 16)
  2390.        {
  2391.        // cool, 16 bit mode
  2392.        SET_BIT(obj->plist[curr_poly].attr,POLY4DV1_ATTR_RGB16);
  2393.        // test if this is a textured poly, if so override the color to WHITE,
  2394.        // so we get maximum reflection in lighting stage
  2395.        if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_TEXTURE)
  2396.            obj->plist[curr_poly].color = RGB16Bit(255,255,255);
  2397.        else
  2398.            obj->plist[curr_poly].color = RGB16Bit(materials[ poly_material[curr_poly] ].color.r, 
  2399.                                                   materials[ poly_material[curr_poly] ].color.g, 
  2400.                                                   materials[ poly_material[curr_poly] ].color.b);
  2401.        Write_Error("nPolygon 16-bit");
  2402.        } // end
  2403.     else
  2404.        {
  2405.        // 8 bit mode
  2406.        SET_BIT(obj->plist[curr_poly].attr,POLY4DV1_ATTR_8BITCOLOR);
  2407.        // test if this is a textured poly, if so override the color to WHITE,
  2408.        // so we get maximum reflection in lighting stage
  2409.        if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_TEXTURE)
  2410.           obj->plist[curr_poly].color = RGBto8BitIndex(255, 255, 255, palette, 0);
  2411.        else
  2412.           obj->plist[curr_poly].color = RGBto8BitIndex(materials[ poly_material[curr_poly] ].color.r,
  2413.                                                        materials[ poly_material[curr_poly] ].color.g,
  2414.                                                        materials[ poly_material[curr_poly] ].color.b,
  2415.                                                        palette, 0);
  2416.        Write_Error("nPolygon 8-bit, index=%d", obj->plist[curr_poly].color);
  2417.        } // end else
  2418.      // now set all the shading flags
  2419.      // figure out which shader to use
  2420.      if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_CONSTANT)
  2421.         {
  2422.         // set shading mode
  2423.         SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_SHADE_MODE_CONSTANT);
  2424.         } // end if
  2425.      else
  2426.      if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_FLAT)
  2427.         {
  2428.         // set shading mode
  2429.         SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_SHADE_MODE_FLAT);
  2430.         } // end if
  2431.      else
  2432.      if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_GOURAUD)
  2433.         {
  2434.         // set shading mode
  2435.         SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_SHADE_MODE_GOURAUD);
  2436.         // going to need vertex normals!
  2437.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[0] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2438.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[1] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2439.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[2] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2440.         } // end if
  2441.      else
  2442.      if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_FASTPHONG)
  2443.         {
  2444.         // set shading mode
  2445.         SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_SHADE_MODE_FASTPHONG);
  2446.         // going to need vertex normals!
  2447.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[0] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2448.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[1] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2449.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[2] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2450.         } // end if
  2451.      else
  2452.         {
  2453.         // set shading mode to default flat
  2454.         SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_SHADE_MODE_FLAT);
  2455.         } // end if
  2456.      if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_TEXTURE)
  2457.         {
  2458.         // set shading mode
  2459.         SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_SHADE_MODE_TEXTURE);
  2460.         // apply texture to this polygon
  2461.         obj->plist[curr_poly].texture = obj->texture;
  2462.         // set texture coordinate attributes
  2463.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[0] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  2464.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[1] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  2465.         SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[2] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  2466.         } // end if
  2467.       // set the material mode to ver. 1.0 emulation (for now only!!!)
  2468.       SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_DISABLE_MATERIAL);
  2469.     } // end for curr_poly
  2470. // local object materials have been added to database, update total materials in system
  2471. num_materials+=num_materials_object;
  2472. // now fix up all texture coordinates
  2473. if (obj->texture)
  2474.    {
  2475.     for (tvertex = 0; tvertex < num_texture_vertices; tvertex++)
  2476.         {
  2477.         // step 1: scale the texture coordinates by the texture size
  2478.         int texture_size = obj->texture->width; 
  2479.         // scale 0..1 to 0..texture_size-1
  2480.         obj->tlist[tvertex].x *= (texture_size-1); 
  2481.         obj->tlist[tvertex].y *= (texture_size-1);
  2482.         // now test for vertex transformation flags
  2483.         if (vertex_flags & VERTEX_FLAGS_INVERT_TEXTURE_U)  
  2484.            {
  2485.            obj->tlist[tvertex].x = (texture_size-1) - obj->tlist[tvertex].x;
  2486.            } // end if
  2487.         if (vertex_flags & VERTEX_FLAGS_INVERT_TEXTURE_V)  
  2488.            {
  2489.            obj->tlist[tvertex].y = (texture_size-1) - obj->tlist[tvertex].y;
  2490.            } // end if
  2491.         if (vertex_flags & VERTEX_FLAGS_INVERT_SWAP_UV)  
  2492.            {
  2493.            float temp;
  2494.            SWAP(obj->tlist[tvertex].x, obj->tlist[tvertex].y, temp);
  2495.            } // end if
  2496.         } // end for
  2497.     } // end if there was a texture loaded for this object
  2498. #ifdef DEBUG_ON
  2499. for (curr_material = 0; curr_material < num_materials; curr_material++)
  2500.     {
  2501.     Write_Error("nMaterial %d", curr_material);
  2502.     Write_Error("nint  state    = %d", materials[curr_material].state);
  2503.     Write_Error("nint  id       = %d", materials[curr_material].id);
  2504.     Write_Error("nchar name[64] = %s", materials[curr_material].name);
  2505.     Write_Error("nint  attr     = %d", materials[curr_material].attr); 
  2506.     Write_Error("nint r         = %d", materials[curr_material].color.r); 
  2507.     Write_Error("nint g         = %d", materials[curr_material].color.g); 
  2508.     Write_Error("nint b         = %d", materials[curr_material].color.b); 
  2509.     Write_Error("nint alpha     = %d", materials[curr_material].color.a);
  2510.     Write_Error("nint color     = %d", materials[curr_material].attr); 
  2511.     Write_Error("nfloat ka      = %f", materials[curr_material].ka); 
  2512.     Write_Error("nkd            = %f", materials[curr_material].kd); 
  2513.     Write_Error("nks            = %f", materials[curr_material].ks); 
  2514.     Write_Error("npower         = %f", materials[curr_material].power);
  2515.     Write_Error("nchar texture_file = %sn", materials[curr_material].texture_file);
  2516.     } // end for curr_material
  2517. #endif
  2518. // now that we know the correct number of polygons, we must allocate memory for them
  2519. // and fix up the object, this is a hack, but the file formats are so stupid by not
  2520. // all starting with NUM_VERTICES, NUM_POLYGONS -- that would make everyone's life
  2521. // easier!
  2522. #if 0
  2523. // step 1: allocate memory for the polygons
  2524. POLY4DV2_PTR plist_temp = NULL;
  2525. // allocate memory for polygon list, the correct number of polys was overwritten
  2526. // into the object during parsing, so we can use the num_polys field
  2527. if (!(plist_temp = (POLY4DV2_PTR)malloc(sizeof(POLY4DV2)*obj->num_polys)))
  2528.    return(0);
  2529. // step 2:  now copy the polygons into the correct list
  2530. memcpy((void *)plist_temp, (void *)obj->plist, sizeof(POLY4DV2));
  2531. // step 3: now free the old memory and fix the pointer
  2532. free(obj->plist);
  2533. // now fix the pointer
  2534. obj->plist = plist_temp;
  2535. #endif
  2536. // compute the polygon normal lengths
  2537. Compute_OBJECT4DV2_Poly_Normals(obj);
  2538. // compute vertex normals for any gouraud shaded polys
  2539. Compute_OBJECT4DV2_Vertex_Normals(obj);
  2540. // return success
  2541. return(1);
  2542. } // end Load_OBJECT4DV2_COB
  2543. ////////////////////////////////////////////////////////////////////////////////
  2544. int Load_OBJECT4DV2_3DSASC(OBJECT4DV2_PTR obj,   // pointer to object
  2545.                            char *filename,       // filename of ASC file
  2546.                            VECTOR4D_PTR scale,   // initial scaling factors
  2547.                            VECTOR4D_PTR pos,     // initial position
  2548.                            VECTOR4D_PTR rot,     // initial rotations
  2549.                            int vertex_flags)     // flags to re-order vertices a
  2550.                                                  // and shading overrides
  2551. {
  2552. // this function loads a 3D Studio .ASC file object in off disk, additionally
  2553. // it allows the caller to scale, position, and rotate the object
  2554. // to save extra calls later for non-dynamic objects, also new functionality
  2555. // is supported in the vertex_flags, they allow the specification of shading 
  2556. // overrides since .ASC doesn't support anything other than polygon colors,
  2557. // so with these overrides you can force the entire object to be emmisive, flat,
  2558. // gouraud, etc., and the function will set up the vertices, and normals, for 
  2559. // you based on these overrides.
  2560. // create a parser object
  2561. CPARSERV1 parser; 
  2562. char seps[16];          // seperators for token scanning
  2563. char token_buffer[256]; // used as working buffer for token
  2564. char *token;            // pointer to next token
  2565. int r,g,b;              // working colors
  2566. // Step 1: clear out the object and initialize it a bit
  2567. memset(obj, 0, sizeof(OBJECT4DV2));
  2568. // set state of object to active and visible
  2569. obj->state = OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_STATE_VISIBLE;
  2570. // set number of frames
  2571. obj->num_frames = 1;
  2572. obj->curr_frame = 0;
  2573. obj->attr = OBJECT4DV2_ATTR_SINGLE_FRAME;
  2574. // set position of object is caller requested position
  2575. if (pos)
  2576.    {
  2577.    // set position of object
  2578.    obj->world_pos.x = pos->x;
  2579.    obj->world_pos.y = pos->y;
  2580.    obj->world_pos.z = pos->z;
  2581.    obj->world_pos.w = pos->w;
  2582.    } // end 
  2583. else
  2584.    {
  2585.    // set it to (0,0,0,1)
  2586.    obj->world_pos.x = 0;
  2587.    obj->world_pos.y = 0;
  2588.    obj->world_pos.z = 0;
  2589.    obj->world_pos.w = 1;
  2590.    } // end else
  2591. // Step 2: open the file for reading using the parser
  2592. if (!parser.Open(filename))
  2593.    {
  2594.    Write_Error("Couldn't open .ASC file %s.", filename);
  2595.    return(0);
  2596.    } // end if
  2597. // Step 3: 
  2598. // lets find the name of the object first 
  2599. while(1)
  2600.      {
  2601.      // get the next line, we are looking for "Named object:"
  2602.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2603.         {
  2604.         Write_Error("Image 'name' not found in .ASC file %s.", filename);
  2605.         return(0);
  2606.         } // end if
  2607.     
  2608.      // check for pattern?  
  2609.      if (parser.Pattern_Match(parser.buffer, "['Named'] ['object:']"))
  2610.         {
  2611.         // at this point we have the string with the name in it, parse it out by finding 
  2612.         // name between quotes "name...."
  2613.         strcpy(token_buffer, parser.buffer);
  2614.         strcpy(seps, """);        
  2615.         strtok( token_buffer, seps );
  2616.         
  2617.         // this will be the token between the quotes
  2618.         token = strtok(NULL, seps);
  2619.       
  2620.         // copy name into structure
  2621.         strcpy(obj->name, token);          
  2622.         Write_Error("nASC Reader Object Name: %s", obj->name);
  2623.         break;    
  2624.         } // end if
  2625.      } // end while
  2626. // step 4: get number of vertices and polys in object
  2627. while(1)
  2628.      {
  2629.      // get the next line, we are looking for "Tri-mesh, Vertices:" 
  2630.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2631.         {
  2632.         Write_Error("'Tri-mesh' line not found in .ASC file %s.", filename);
  2633.         return(0);
  2634.         } // end if
  2635.     
  2636.      // check for pattern?  
  2637.      if (parser.Pattern_Match(parser.buffer, "['Tri-mesh,'] ['Vertices:'] [i] ['Faces:'] [i]"))
  2638.         {
  2639.         // simply extract the number of vertices and polygons from the pattern matching 
  2640.         // output arrays
  2641.         obj->num_vertices = parser.pints[0];
  2642.         obj->num_polys    = parser.pints[1];
  2643.         Write_Error("nASC Reader Num Vertices: %d, Num Polys: %d", 
  2644.                     obj->num_vertices, obj->num_polys);
  2645.         break;    
  2646.  
  2647.         } // end if
  2648.      } // end while
  2649. // allocate the memory for the vertices and number of polys
  2650. // the call parameters are redundant in this case, but who cares
  2651. if (!Init_OBJECT4DV2(obj,   // object to allocate
  2652.                 obj->num_vertices, 
  2653.                 obj->num_polys,  
  2654.                 obj->num_frames))
  2655.     {
  2656.     Write_Error("nASC file error with file %s (can't allocate memory).",filename);
  2657.     } // end if
  2658. // Step 5: load the vertex list
  2659. // advance parser to vertex list denoted by:
  2660. // "Vertex list:"
  2661. while(1)
  2662.      {
  2663.      // get next line
  2664.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2665.         {
  2666.         Write_Error("n'Vertex list:' line not found in .ASC file %s.", filename);
  2667.         return(0);
  2668.         } // end if
  2669.     
  2670.      // check for pattern?  
  2671.      if (parser.Pattern_Match(parser.buffer, "['Vertex'] ['list:']"))
  2672.         {
  2673.         Write_Error("nASC Reader found vertex list in .ASC file %s.", filename);
  2674.         break;
  2675.         } // end if
  2676.      } // end while
  2677. // now read in vertex list, format:
  2678. // "Vertex: d  X:d.d Y:d.d  Z:d.d"
  2679.  for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  2680.      {
  2681.      // hunt for vertex
  2682.      while(1)
  2683.      {
  2684.      // get the next vertex
  2685.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2686.         {
  2687.         Write_Error("nVertex list ended abruptly! in .ASC file %s.", filename);
  2688.         return(0);
  2689.         } // end if
  2690.     
  2691.      // strip all ":XYZ", make this easier, note use of input and output as same var, this is legal
  2692.      // since the output is guaranteed to be the same length or shorter as the input :)
  2693.      StripChars(parser.buffer, parser.buffer, ":XYZ");
  2694.      
  2695.      // check for pattern?  
  2696.      if (parser.Pattern_Match(parser.buffer, "['Vertex'] [i] [f] [f] [f]"))
  2697.         {
  2698.         // at this point we have the x,y,z in the the pfloats array locations 0,1,2
  2699.         obj->vlist_local[vertex].x = parser.pfloats[0];
  2700.         obj->vlist_local[vertex].y = parser.pfloats[1];
  2701.         obj->vlist_local[vertex].z = parser.pfloats[2];
  2702.         obj->vlist_local[vertex].w = 1;
  2703.         // do vertex swapping right here, allow muliple swaps, why not!
  2704.         // defines for vertex re-ordering flags
  2705.         //#define VERTEX_FLAGS_INVERT_X   1    // inverts the Z-coordinates
  2706.         //#define VERTEX_FLAGS_INVERT_Y   2    // inverts the Z-coordinates
  2707.         //#define VERTEX_FLAGS_INVERT_Z   4    // inverts the Z-coordinates
  2708.         //#define VERTEX_FLAGS_SWAP_YZ    8    // transforms a RHS model to a LHS model
  2709.         //#define VERTEX_FLAGS_SWAP_XZ    16   // ???
  2710.         //#define VERTEX_FLAGS_SWAP_XY    32
  2711.         //#define VERTEX_FLAGS_INVERT_WINDING_ORDER 64  // invert winding order from cw to ccw or ccw to cc
  2712.         float temp_f; // used for swapping
  2713.         // invert signs?
  2714.         if (vertex_flags & VERTEX_FLAGS_INVERT_X)
  2715.            obj->vlist_local[vertex].x=-obj->vlist_local[vertex].x;
  2716.         if (vertex_flags & VERTEX_FLAGS_INVERT_Y)
  2717.            obj->vlist_local[vertex].y=-obj->vlist_local[vertex].y;
  2718.         if (vertex_flags & VERTEX_FLAGS_INVERT_Z)
  2719.            obj->vlist_local[vertex].z=-obj->vlist_local[vertex].z;
  2720.         // swap any axes?
  2721.         if (vertex_flags & VERTEX_FLAGS_SWAP_YZ)
  2722.            SWAP(obj->vlist_local[vertex].y, obj->vlist_local[vertex].z, temp_f);
  2723.         
  2724.         if (vertex_flags & VERTEX_FLAGS_SWAP_XZ)
  2725.            SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].z, temp_f);
  2726.         if (vertex_flags & VERTEX_FLAGS_SWAP_XY)
  2727.            SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].y, temp_f);
  2728.         Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
  2729.                                            obj->vlist_local[vertex].x, 
  2730.                                            obj->vlist_local[vertex].y, 
  2731.                                            obj->vlist_local[vertex].z,
  2732.                                            obj->vlist_local[vertex].w);
  2733.         // scale vertices
  2734.         if (scale)
  2735.            {
  2736.            obj->vlist_local[vertex].x*=scale->x;
  2737.            obj->vlist_local[vertex].y*=scale->y;
  2738.            obj->vlist_local[vertex].z*=scale->z;
  2739.            } // end if
  2740.          // set point field in this vertex, we need that at least 
  2741.          SET_BIT(obj->vlist_local[vertex].attr, VERTEX4DTV1_ATTR_POINT);
  2742.         // found vertex, break out of while for next pass
  2743.         break;
  2744.         } // end if
  2745.     } // end while
  2746.     } // end for vertex
  2747. // compute average and max radius
  2748. Compute_OBJECT4DV2_Radius(obj);
  2749. Write_Error("nObject average radius = %f, max radius = %f", 
  2750.             obj->avg_radius, obj->max_radius);
  2751. // step 6: load in the polygons
  2752. // poly list starts off with:
  2753. // "Face list:"
  2754. while(1)
  2755.      {
  2756.      // get next line
  2757.      if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2758.         {
  2759.         Write_Error("n'Face list:' line not found in .ASC file %s.", filename);
  2760.         return(0);
  2761.         } // end if
  2762.     
  2763.      // check for pattern?  
  2764.      if (parser.Pattern_Match(parser.buffer, "['Face'] ['list:']"))
  2765.         {
  2766.         Write_Error("nASC Reader found face list in .ASC file %s.", filename);
  2767.         break;
  2768.         } // end if
  2769.      } // end while
  2770. // now read each face in format:
  2771. // Face ddd:    A:ddd B:ddd C:ddd AB:1|0 BC:1|0 CA:1|
  2772. // Material:"rdddgdddbddda0"
  2773. // the A, B, C part is vertex 0,1,2 but the AB, BC, CA part
  2774. // has to do with the edges and the vertex ordering
  2775. // the material indicates the color, and has an 'a0' tacked on the end???
  2776. int  poly_surface_desc = 0; // ASC surface descriptor/material in this case
  2777. int  poly_num_verts    = 0; // number of vertices for current poly (always 3)
  2778. char tmp_string[8];         // temp string to hold surface descriptor in and
  2779.                             // test if it need to be converted from hex
  2780. for (int poly=0; poly < obj->num_polys; poly++)
  2781.     {
  2782.     // hunt until next face is found
  2783.     while(1)
  2784.     {
  2785.     // get the next polygon face
  2786.     if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2787.        {
  2788.        Write_Error("nface list ended abruptly! in .ASC file %s.", filename);
  2789.        return(0);
  2790.        } // end if
  2791.     
  2792.     // strip all ":ABC", make this easier, note use of input and output as same var, this is legal
  2793.     // since the output is guaranteed to be the same length or shorter as the input :)
  2794.     StripChars(parser.buffer, parser.buffer, ":ABC");
  2795.      
  2796.     // check for pattern?  
  2797.     if (parser.Pattern_Match(parser.buffer, "['Face'] [i] [i] [i] [i]"))
  2798.        {
  2799.        // at this point we have the vertex indices in the the pints array locations 1,2,3, 
  2800.        // 0 contains the face number
  2801.        // insert polygon, check for winding order invert
  2802.        if (vertex_flags & VERTEX_FLAGS_INVERT_WINDING_ORDER)
  2803.           {     
  2804.           poly_num_verts           = 3;
  2805.           obj->plist[poly].vert[0] = parser.pints[3];
  2806.           obj->plist[poly].vert[1] = parser.pints[2];
  2807.           obj->plist[poly].vert[2] = parser.pints[1];
  2808.           } // end if
  2809.        else
  2810.           { // leave winding order alone
  2811.           poly_num_verts           = 3;
  2812.           obj->plist[poly].vert[0] = parser.pints[1];
  2813.           obj->plist[poly].vert[1] = parser.pints[2];
  2814.           obj->plist[poly].vert[2] = parser.pints[3];
  2815.           } // end else
  2816.        // point polygon vertex list to object's vertex list
  2817.        // note that this is redundant since the polylist is contained
  2818.        // within the object in this case and its up to the user to select
  2819.        // whether the local or transformed vertex list is used when building up
  2820.        // polygon geometry, might be a better idea to set to NULL in the context
  2821.        // of polygons that are part of an object
  2822.        obj->plist[poly].vlist = obj->vlist_local; 
  2823.        // set texture coordinate list, this is needed
  2824.        obj->plist[poly].tlist = obj->tlist;
  2825.   
  2826.        // found the face, break out of while for another pass
  2827.        break;
  2828.        } // end if
  2829.  
  2830.      } // end while      
  2831.     // hunt until next material for face is found
  2832.     while(1)
  2833.     {
  2834.     // get the next polygon material (the "page xxx" breaks mess everything up!!!)
  2835.     if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
  2836.        {
  2837.        Write_Error("nmaterial list ended abruptly! in .ASC file %s.", filename);
  2838.        return(0);
  2839.        } // end if
  2840.     
  2841.     // Material:"rdddgdddbddda0"
  2842.     // replace all ':"rgba', make this easier, note use of input and output as same var, this is legal
  2843.     // since the output is guaranteed to be the same length or shorter as the input :)
  2844.     // the result will look like:
  2845.     // "M t ri l   ddd ddd ddd 0" 
  2846.     // which we can parse!
  2847.     ReplaceChars(parser.buffer, parser.buffer, ":"rgba", ' ');
  2848.      
  2849.     // check for pattern?  
  2850.     if (parser.Pattern_Match(parser.buffer, "[i] [i] [i]"))
  2851.        {
  2852.        // at this point we have the red, green, and blue components in the the pints array locations 0,1,2, 
  2853.        r = parser.pints[0];
  2854.        g = parser.pints[1];
  2855.        b = parser.pints[2];
  2856.        // set all the attributes of polygon as best we can with this format
  2857.        // SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_2SIDED);
  2858.     
  2859.        // we need to know what color depth we are dealing with, so check
  2860.        // the bits per pixel, this assumes that the system has already
  2861.        // made the call to DDraw_Init() or set the bit depth
  2862.        if (screen_bpp==16)
  2863.           {
  2864.           // cool, 16 bit mode
  2865.           SET_BIT(obj->plist[poly].attr,POLY4DV2_ATTR_RGB16);
  2866.           obj->plist[poly].color = RGB16Bit(r, g, b);
  2867.           Write_Error("nPolygon 16-bit");
  2868.           } // end if 
  2869.        else
  2870.           {
  2871.           // 8 bit mode
  2872.           SET_BIT(obj->plist[poly].attr,POLY4DV2_ATTR_8BITCOLOR);
  2873.           obj->plist[poly].color = RGBto8BitIndex(r,g,b, palette, 0);
  2874.           Write_Error("nPolygon 8-bit, index=%d", obj->plist[poly].color);
  2875.           } // end else
  2876.          // we have added the ability to manually override the shading mode
  2877.          // of the object, not a face by face basis, but at least on an
  2878.          // object wide basis, normally all ASC files are loaded with flat shading as
  2879.          // the default, by adding flags to the vertex flags this can be overridden
  2880.          //#define VERTEX_FLAGS_OVERRIDE_MASK          0xf000 // this masks these bits to extract them
  2881.          //#define VERTEX_FLAGS_OVERRIDE_CONSTANT      0x1000
  2882.          //#define VERTEX_FLAGS_OVERRIDE_EMISSIVE      0x1000 (alias)
  2883.          //#define VERTEX_FLAGS_OVERRIDE_FLAT          0x2000
  2884.          //#define VERTEX_FLAGS_OVERRIDE_GOURAUD       0x4000
  2885.          //#define VERTEX_FLAGS_OVERRIDE_TEXTURE       0x8000
  2886.          
  2887.          // first test to see if there is an override at all 
  2888.          int vertex_overrides = (vertex_flags & VERTEX_FLAGS_OVERRIDE_MASK);        
  2889.          
  2890.          if (vertex_overrides)
  2891.             {
  2892.             // which override?
  2893.             if (vertex_overrides & VERTEX_FLAGS_OVERRIDE_PURE)
  2894.                 SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_PURE);
  2895.             
  2896.             if (vertex_overrides & VERTEX_FLAGS_OVERRIDE_FLAT)
  2897.                 SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_FLAT);
  2898.             
  2899.             if (vertex_overrides & VERTEX_FLAGS_OVERRIDE_GOURAUD)
  2900.                {
  2901.                SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_GOURAUD);
  2902.                // going to need vertex normals!
  2903.                SET_BIT(obj->vlist_local[ obj->plist[poly].vert[0] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2904.                SET_BIT(obj->vlist_local[ obj->plist[poly].vert[1] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2905.                SET_BIT(obj->vlist_local[ obj->plist[poly].vert[2] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  2906.                } // end if
  2907.             if (vertex_overrides & VERTEX_FLAGS_OVERRIDE_TEXTURE)
  2908.                 SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_TEXTURE);
  2909.             } // end if
  2910.         else
  2911.            {
  2912.            // for now manually set shading mode to flat
  2913.            //SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_PURE);
  2914.            //SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_GOURAUD);
  2915.            //SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_PHONG);
  2916.            SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_FLAT);
  2917.            } // end else 
  2918.        // set the material mode to ver. 1.0 emulation
  2919.        SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_DISABLE_MATERIAL);
  2920.        // set polygon to active
  2921.        obj->plist[poly].state = POLY4DV2_STATE_ACTIVE;    
  2922.        
  2923.        // found the material, break out of while for another pass
  2924.        break;
  2925.        } // end if
  2926.  
  2927.      } // end while      
  2928.      Write_Error("nPolygon %d:", poly);
  2929.      Write_Error("nSurface Desc = [RGB]=[%d, %d, %d], vert_indices [%d, %d, %d]", 
  2930.                                                          r,g,b,
  2931.                                                          obj->plist[poly].vert[0],
  2932.                                                          obj->plist[poly].vert[1],
  2933.                                                          obj->plist[poly].vert[2]);
  2934.     } // end for poly
  2935. // compute the polygon normal lengths
  2936. Compute_OBJECT4DV2_Poly_Normals(obj);
  2937. // compute vertex normals for any gouraud shaded polys
  2938. Compute_OBJECT4DV2_Vertex_Normals(obj);
  2939. // return success
  2940. return(1);
  2941. } // end Load_OBJECT4DV2_3DASC
  2942. ///////////////////////////////////////////////////////////////////////////////
  2943. int Load_OBJECT4DV2_PLG(OBJECT4DV2_PTR obj, // pointer to object
  2944.                     char *filename,         // filename of plg file
  2945.                     VECTOR4D_PTR scale,     // initial scaling factors
  2946.                     VECTOR4D_PTR pos,       // initial position
  2947.                     VECTOR4D_PTR rot,       // initial rotations
  2948.                     int vertex_flags)       // vertex flags, used to override
  2949. {
  2950. // this function loads a plg object in off disk, additionally
  2951. // it allows the caller to scale, position, and rotate the object
  2952. // to save extra calls later for non-dynamic objects
  2953. // there is only one frame, so load the object and set the fields
  2954. // appropriately for a single frame OBJECT4DV2
  2955. FILE *fp;          // file pointer
  2956. char buffer[256];  // working buffer
  2957. char *token_string;  // pointer to actual token text, ready for parsing
  2958. // file format review, note types at end of each description
  2959. // # this is a comment
  2960. // # object descriptor
  2961. // object_name_string num_verts_int num_polys_int
  2962. // # vertex list
  2963. // x0_float y0_float z0_float
  2964. // x1_float y1_float z1_float
  2965. // x2_float y2_float z2_float
  2966. // .
  2967. // .
  2968. // xn_float yn_float zn_float
  2969. //
  2970. // # polygon list
  2971. // surface_description_ushort num_verts_int v0_index_int v1_index_int ..  vn_index_int
  2972. // .
  2973. // .
  2974. // surface_description_ushort num_verts_int v0_index_int v1_index_int ..  vn_index_int
  2975. // lets keep it simple and assume one element per line
  2976. // hence we have to find the object descriptor, read it in, then the
  2977. // vertex list and read it in, and finally the polygon list -- simple :)
  2978. // Step 1: clear out the object and initialize it a bit
  2979. memset(obj, 0, sizeof(OBJECT4DV2));
  2980. // set state of object to active and visible
  2981. obj->state = OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_STATE_VISIBLE;
  2982. // set position of object
  2983. obj->world_pos.x = pos->x;
  2984. obj->world_pos.y = pos->y;
  2985. obj->world_pos.z = pos->z;
  2986. obj->world_pos.w = pos->w;
  2987. // set number of frames
  2988. obj->num_frames = 1;
  2989. obj->curr_frame = 0;
  2990. obj->attr = OBJECT4DV2_ATTR_SINGLE_FRAME;
  2991. // Step 2: open the file for reading
  2992. if (!(fp = fopen(filename, "r")))
  2993.    {
  2994.    Write_Error("Couldn't open PLG file %s.", filename);
  2995.    return(0);
  2996.    } // end if
  2997. // Step 3: get the first token string which should be the object descriptor
  2998. if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
  2999.    {
  3000.    Write_Error("PLG file error with file %s (object descriptor invalid).",filename);
  3001.    return(0);
  3002.    } // end if
  3003. Write_Error("Object Descriptor: %s", token_string);
  3004. // parse out the info object
  3005. sscanf(token_string, "%s %d %d", obj->name, &obj->num_vertices, &obj->num_polys);
  3006. // allocate the memory for the vertices and number of polys
  3007. // the call parameters are redundant in this case, but who cares
  3008. if (!Init_OBJECT4DV2(obj,   // object to allocate
  3009.                 obj->num_vertices, 
  3010.                 obj->num_polys, 
  3011.                 obj->num_frames))
  3012.     {
  3013.     Write_Error("nPLG file error with file %s (can't allocate memory).",filename);
  3014.     } // end if
  3015. // Step 4: load the vertex list
  3016. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  3017.     {
  3018.     // get the next vertex
  3019.     if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
  3020.           {
  3021.           Write_Error("PLG file error with file %s (vertex list invalid).",filename);
  3022.           return(0);
  3023.           } // end if
  3024.     // parse out vertex
  3025.     sscanf(token_string, "%f %f %f", &obj->vlist_local[vertex].x, 
  3026.                                      &obj->vlist_local[vertex].y, 
  3027.                                      &obj->vlist_local[vertex].z);
  3028.     obj->vlist_local[vertex].w = 1;    
  3029.     // scale vertices
  3030.     obj->vlist_local[vertex].x*=scale->x;
  3031.     obj->vlist_local[vertex].y*=scale->y;
  3032.     obj->vlist_local[vertex].z*=scale->z;
  3033.     Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
  3034.                                            obj->vlist_local[vertex].x, 
  3035.                                            obj->vlist_local[vertex].y, 
  3036.                                            obj->vlist_local[vertex].z,
  3037.                                            obj->vlist_local[vertex].w);
  3038.     // every vertex has a point at least, set that in the flags attribute
  3039.     SET_BIT(obj->vlist_local[vertex].attr, VERTEX4DTV1_ATTR_POINT);
  3040.     } // end for vertex
  3041. // compute average and max radius
  3042. Compute_OBJECT4DV2_Radius(obj);
  3043. Write_Error("nObject average radius = %f, max radius = %f", 
  3044.             obj->avg_radius, obj->max_radius);
  3045. int poly_surface_desc = 0; // PLG/PLX surface descriptor
  3046. int poly_num_verts    = 0; // number of vertices for current poly (always 3)
  3047. char tmp_string[8];        // temp string to hold surface descriptor in and
  3048.                            // test if it need to be converted from hex
  3049. // Step 5: load the polygon list
  3050. for (int poly=0; poly < obj->num_polys; poly++)
  3051.     {
  3052.     // get the next polygon descriptor
  3053.     if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
  3054.         {
  3055.         Write_Error("PLG file error with file %s (polygon descriptor invalid).",filename);
  3056.         return(0);
  3057.         } // end if
  3058.    
  3059.     Write_Error("nPolygon %d:", poly);
  3060.     // each vertex list MUST have 3 vertices since we made this a rule that all models
  3061.     // must be constructed of triangles
  3062.     // read in surface descriptor, number of vertices, and vertex list
  3063.     sscanf(token_string, "%s %d %d %d %d", tmp_string,
  3064.                                            &poly_num_verts, // should always be 3 
  3065.                                            &obj->plist[poly].vert[0],
  3066.                                            &obj->plist[poly].vert[1],
  3067.                                            &obj->plist[poly].vert[2]);
  3068.     
  3069.     // since we are allowing the surface descriptor to be in hex format
  3070.     // with a leading "0x" we need to test for it
  3071.     if (tmp_string[0] == '0' && toupper(tmp_string[1]) == 'X')
  3072.        sscanf(tmp_string,"%x", &poly_surface_desc);
  3073.     else
  3074.        poly_surface_desc = atoi(tmp_string);
  3075.     
  3076.     // point polygon vertex list to object's vertex list
  3077.     // note that this is redundant since the polylist is contained
  3078.     // within the object in this case and its up to the user to select
  3079.     // whether the local or transformed vertex list is used when building up
  3080.     // polygon geometry, might be a better idea to set to NULL in the context
  3081.     // of polygons that are part of an object
  3082.     obj->plist[poly].vlist = obj->vlist_local;