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

游戏

开发平台:

Visual C++

  1. // T3DLIB8.CPP - clipping, terrain, and new lighting
  2. // I N C L U D E S ///////////////////////////////////////////////////////////
  3. #define DEBUG_ON
  4. #define WIN32_LEAN_AND_MEAN  
  5. #include <windows.h>   // include important windows stuff
  6. #include <windowsx.h> 
  7. #include <mmsystem.h>
  8. #include <objbase.h>
  9. #include <iostream.h> // include important C/C++ stuff
  10. #include <conio.h>
  11. #include <stdlib.h>
  12. #include <malloc.h>
  13. #include <memory.h>
  14. #include <string.h>
  15. #include <stdarg.h>
  16. #include <stdio.h>
  17. #include <math.h>
  18. #include <io.h>
  19. #include <fcntl.h>
  20. #include <direct.h>
  21. #include <wchar.h>
  22. #include <limits.h>
  23. #include <float.h>
  24. #include <search.h>
  25. #include <ddraw.h>      // needed for defs in T3DLIB1.H 
  26. #include "T3DLIB1.H"
  27. #include "T3DLIB4.H"
  28. #include "T3DLIB5.H"
  29. #include "T3DLIB6.H"
  30. #include "T3DLIB7.H"
  31. #include "T3DLIB8.H"
  32. // DEFINES //////////////////////////////////////////////////////////////////
  33. // GLOBALS //////////////////////////////////////////////////////////////////
  34. LIGHTV2 lights2[MAX_LIGHTS];  // lights in system
  35. // FUNCTIONS ////////////////////////////////////////////////////////////////
  36. void Clip_Polys_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, CAM4DV1_PTR cam, int clip_flags)
  37. {
  38. // this function clips the polygons in the list against the requested clipping planes
  39. // and sets the clipped flag on the poly, so it's not rendered
  40. // note the function ONLY performs clipping on the near and far clipping plane
  41. // but will perform trivial tests on the top/bottom, left/right clipping planes
  42. // if a polygon is completely out of the viewing frustrum in these cases, it will
  43. // be culled, however, this test isn't as effective on games based on objects since
  44. // in most cases objects that are visible have polygons that are visible, but in the
  45. // case where the polygon list is based on a large object that ALWAYS has some portion
  46. // visible, testing for individual polys is worthwhile..
  47. // the function assumes the polygons have been transformed into camera space
  48. // internal clipping codes
  49. #define CLIP_CODE_GZ   0x0001    // z > z_max
  50. #define CLIP_CODE_LZ   0x0002    // z < z_min
  51. #define CLIP_CODE_IZ   0x0004    // z_min < z < z_max
  52. #define CLIP_CODE_GX   0x0001    // x > x_max
  53. #define CLIP_CODE_LX   0x0002    // x < x_min
  54. #define CLIP_CODE_IX   0x0004    // x_min < x < x_max
  55. #define CLIP_CODE_GY   0x0001    // y > y_max
  56. #define CLIP_CODE_LY   0x0002    // y < y_min
  57. #define CLIP_CODE_IY   0x0004    // y_min < y < y_max
  58. #define CLIP_CODE_NULL 0x0000
  59. int vertex_ccodes[3]; // used to store clipping flags
  60. int num_verts_in;     // number of vertices inside
  61. int v0, v1, v2;       // vertex indices
  62. float z_factor,       // used in clipping computations
  63.       z_test;         // used in clipping computations
  64. float xi, yi, x01i, y01i, x02i, y02i, // vertex intersection points
  65.       t1, t2,                         // parametric t values
  66.       ui, vi, u01i, v01i, u02i, v02i; // texture intersection points
  67. int last_poly_index,            // last valid polygon in polylist
  68.     insert_poly_index;          // the current position new polygons are inserted at
  69. VECTOR4D u,v,n;                 // used in vector calculations
  70. POLYF4DV2 temp_poly;            // used when we need to split a poly into 2 polys
  71. // set last, current insert index to end of polygon list
  72. // we don't want to clip poly's two times
  73. insert_poly_index = last_poly_index = rend_list->num_polys;
  74. // traverse polygon list and clip/cull polygons
  75. for (int poly = 0; poly < last_poly_index; poly++)
  76.     {
  77.     // acquire current polygon
  78.     POLYF4DV2_PTR curr_poly = rend_list->poly_ptrs[poly];
  79.     // is this polygon valid?
  80.     // test this polygon if and only if it's not clipped, not culled,
  81.     // active, and visible and not 2 sided. Note we test for backface in the event that
  82.     // a previous call might have already determined this, so why work
  83.     // harder!
  84.     if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  85.         (curr_poly->state & POLY4DV2_STATE_CLIPPED ) || 
  86.         (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  87.         continue; // move onto next poly
  88.            
  89.        // clip/cull to x-planes       
  90.        if (clip_flags & CLIP_POLY_X_PLANE)
  91.            {
  92.            // clip/cull only based on x clipping planes
  93.            // for each vertice determine if it's in the clipping region or beyond it and
  94.            // set the appropriate clipping code
  95.            // we do NOT clip the final triangles, we are only trying to trivally reject them 
  96.            // we are going to clip polygons in the rasterizer to the screen rectangle
  97.            // but we do want to clip/cull polys that are totally outside the viewfrustrum
  98.            // since we are clipping to the right/left x-planes we need to use the FOV or
  99.            // the plane equations to find the z value that at the current x position would
  100.            // be outside the plane
  101.            z_factor = (0.5)*cam->viewplane_width/cam->view_dist;  
  102.            // vertex 0
  103.            z_test = z_factor*curr_poly->tvlist[0].z;
  104.            if (curr_poly->tvlist[0].x > z_test)
  105.               vertex_ccodes[0] = CLIP_CODE_GX;
  106.            else
  107.            if (curr_poly->tvlist[0].x < -z_test)
  108.               vertex_ccodes[0] = CLIP_CODE_LX;
  109.            else
  110.               vertex_ccodes[0] = CLIP_CODE_IX;
  111.           
  112.            // vertex 1
  113.            z_test = z_factor*curr_poly->tvlist[1].z;         
  114.            if (curr_poly->tvlist[1].x > z_test)
  115.               vertex_ccodes[1] = CLIP_CODE_GX;
  116.            else
  117.            if (curr_poly->tvlist[1].x < -z_test)
  118.               vertex_ccodes[1] = CLIP_CODE_LX;
  119.            else
  120.               vertex_ccodes[1] = CLIP_CODE_IX;
  121.            // vertex 2
  122.            z_test = z_factor*curr_poly->tvlist[2].z;              
  123.            if (curr_poly->tvlist[2].x > z_test)
  124.               vertex_ccodes[2] = CLIP_CODE_GX;
  125.            else
  126.            if (curr_poly->tvlist[2].x < -z_test)
  127.               vertex_ccodes[2] = CLIP_CODE_LX;
  128.            else
  129.               vertex_ccodes[2] = CLIP_CODE_IX;
  130.            
  131.           // test for trivial rejections, polygon completely beyond right or left
  132.           // clipping planes
  133.           if ( ((vertex_ccodes[0] == CLIP_CODE_GX) && 
  134.                 (vertex_ccodes[1] == CLIP_CODE_GX) && 
  135.                 (vertex_ccodes[2] == CLIP_CODE_GX) ) ||
  136.                ((vertex_ccodes[0] == CLIP_CODE_LX) && 
  137.                 (vertex_ccodes[1] == CLIP_CODE_LX) && 
  138.                 (vertex_ccodes[2] == CLIP_CODE_LX) ) )
  139.              {
  140.              // clip the poly completely out of frustrum
  141.              SET_BIT(curr_poly->state, POLY4DV2_STATE_CLIPPED);
  142.              
  143.              // move on to next polygon
  144.              continue;
  145.              } // end if
  146.                            
  147.           } // end if x planes
  148.        // clip/cull to y-planes       
  149.        if (clip_flags & CLIP_POLY_Y_PLANE)
  150.            {
  151.            // clip/cull only based on y clipping planes
  152.            // for each vertice determine if it's in the clipping region or beyond it and
  153.            // set the appropriate clipping code
  154.            // we do NOT clip the final triangles, we are only trying to trivally reject them 
  155.            // we are going to clip polygons in the rasterizer to the screen rectangle
  156.            // but we do want to clip/cull polys that are totally outside the viewfrustrum
  157.            // since we are clipping to the top/bottom y-planes we need to use the FOV or
  158.            // the plane equations to find the z value that at the current y position would
  159.            // be outside the plane
  160.            z_factor = (0.5)*cam->viewplane_width/cam->view_dist;  
  161.            // vertex 0
  162.            z_test = z_factor*curr_poly->tvlist[0].z;
  163.            if (curr_poly->tvlist[0].y > z_test)
  164.               vertex_ccodes[0] = CLIP_CODE_GY;
  165.            else
  166.            if (curr_poly->tvlist[0].y < -z_test)
  167.               vertex_ccodes[0] = CLIP_CODE_LY;
  168.            else
  169.               vertex_ccodes[0] = CLIP_CODE_IY;
  170.           
  171.            // vertex 1
  172.            z_test = z_factor*curr_poly->tvlist[1].z;         
  173.            if (curr_poly->tvlist[1].y > z_test)
  174.               vertex_ccodes[1] = CLIP_CODE_GY;
  175.            else
  176.            if (curr_poly->tvlist[1].y < -z_test)
  177.               vertex_ccodes[1] = CLIP_CODE_LY;
  178.            else
  179.               vertex_ccodes[1] = CLIP_CODE_IY;
  180.            // vertex 2
  181.            z_test = z_factor*curr_poly->tvlist[2].z;              
  182.            if (curr_poly->tvlist[2].y > z_test)
  183.               vertex_ccodes[2] = CLIP_CODE_GY;
  184.            else
  185.            if (curr_poly->tvlist[2].x < -z_test)
  186.               vertex_ccodes[2] = CLIP_CODE_LY;
  187.            else
  188.               vertex_ccodes[2] = CLIP_CODE_IY;
  189.            
  190.           // test for trivial rejections, polygon completely beyond top or bottom
  191.           // clipping planes
  192.           if ( ((vertex_ccodes[0] == CLIP_CODE_GY) && 
  193.                 (vertex_ccodes[1] == CLIP_CODE_GY) && 
  194.                 (vertex_ccodes[2] == CLIP_CODE_GY) ) ||
  195.                ((vertex_ccodes[0] == CLIP_CODE_LY) && 
  196.                 (vertex_ccodes[1] == CLIP_CODE_LY) && 
  197.                 (vertex_ccodes[2] == CLIP_CODE_LY) ) )
  198.              {
  199.              // clip the poly completely out of frustrum
  200.              SET_BIT(curr_poly->state, POLY4DV2_STATE_CLIPPED);
  201.              
  202.              // move on to next polygon
  203.              continue;
  204.              } // end if
  205.                            
  206.           } // end if y planes
  207.         // clip/cull to z planes
  208.         if (clip_flags & CLIP_POLY_Z_PLANE)
  209.            {
  210.            // clip/cull only based on z clipping planes
  211.            // for each vertice determine if it's in the clipping region or beyond it and
  212.            // set the appropriate clipping code
  213.            // then actually clip all polygons to the near clipping plane, this will result
  214.            // in at most 1 additional triangle
  215.            // reset vertex counters, these help in classification
  216.            // of the final triangle 
  217.            num_verts_in = 0;
  218.            // vertex 0
  219.            if (curr_poly->tvlist[0].z > cam->far_clip_z)
  220.               {
  221.               vertex_ccodes[0] = CLIP_CODE_GZ;
  222.               } 
  223.            else
  224.            if (curr_poly->tvlist[0].z < cam->near_clip_z)
  225.               {
  226.               vertex_ccodes[0] = CLIP_CODE_LZ;
  227.               }
  228.            else
  229.               {
  230.               vertex_ccodes[0] = CLIP_CODE_IZ;
  231.               num_verts_in++;
  232.               } 
  233.           
  234.            // vertex 1
  235.            if (curr_poly->tvlist[1].z > cam->far_clip_z)
  236.               {
  237.               vertex_ccodes[1] = CLIP_CODE_GZ;
  238.               } 
  239.            else
  240.            if (curr_poly->tvlist[1].z < cam->near_clip_z)
  241.               {
  242.               vertex_ccodes[1] = CLIP_CODE_LZ;
  243.               }
  244.            else
  245.               {
  246.               vertex_ccodes[1] = CLIP_CODE_IZ;
  247.               num_verts_in++;
  248.               }     
  249.            // vertex 2
  250.            if (curr_poly->tvlist[2].z > cam->far_clip_z)
  251.               {
  252.               vertex_ccodes[2] = CLIP_CODE_GZ;
  253.               } 
  254.            else
  255.            if (curr_poly->tvlist[2].z < cam->near_clip_z)
  256.               {
  257.               vertex_ccodes[2] = CLIP_CODE_LZ;
  258.               }
  259.            else
  260.               {
  261.               vertex_ccodes[2] = CLIP_CODE_IZ;
  262.               num_verts_in++;
  263.               } 
  264.            
  265.           // test for trivial rejections, polygon completely beyond far or near
  266.           // z clipping planes
  267.           if ( ((vertex_ccodes[0] == CLIP_CODE_GZ) && 
  268.                 (vertex_ccodes[1] == CLIP_CODE_GZ) && 
  269.                 (vertex_ccodes[2] == CLIP_CODE_GZ) ) ||
  270.                ((vertex_ccodes[0] == CLIP_CODE_LZ) && 
  271.                 (vertex_ccodes[1] == CLIP_CODE_LZ) && 
  272.                 (vertex_ccodes[2] == CLIP_CODE_LZ) ) )
  273.              {
  274.              // clip the poly completely out of frustrum
  275.              SET_BIT(curr_poly->state, POLY4DV2_STATE_CLIPPED);
  276.              
  277.              // move on to next polygon
  278.              continue;
  279.              } // end if
  280.           // test if any vertex has protruded beyond near clipping plane?
  281.           if ( ( (vertex_ccodes[0] | vertex_ccodes[1] | vertex_ccodes[2]) & CLIP_CODE_LZ) )
  282.           {
  283.           // at this point we are ready to clip the polygon to the near 
  284.           // clipping plane no need to clip to the far plane since it can't 
  285.           // possible cause problems. We have two cases: case 1: the triangle 
  286.           // has 1 vertex interior to the near clipping plane and 2 vertices 
  287.           // exterior, OR case 2: the triangle has two vertices interior of 
  288.           // the near clipping plane and 1 exterior
  289.           // step 1: classify the triangle type based on number of vertices
  290.           // inside/outside
  291.           // case 1: easy case :)
  292.           if (num_verts_in == 1)
  293.              {
  294.              // we need to clip the triangle against the near clipping plane
  295.              // the clipping procedure is done to each edge leading away from
  296.              // the interior vertex, to clip we need to compute the intersection
  297.              // with the near z plane, this is done with a parametric equation of 
  298.              // the edge, once the intersection is computed the old vertex position
  299.              // is overwritten along with re-computing the texture coordinates, if
  300.              // there are any, what's nice about this case, is clipping doesn't 
  301.              // introduce any added vertices, so we can overwrite the old poly
  302.              // the other case below results in 2 polys, so at very least one has
  303.              // to be added to the end of the rendering list -- bummer
  304.  
  305.              // step 1: find vertex index for interior vertex
  306.              if ( vertex_ccodes[0] == CLIP_CODE_IZ)
  307.                 { v0 = 0; v1 = 1; v2 = 2; }
  308.              else 
  309.              if (vertex_ccodes[1] == CLIP_CODE_IZ)
  310.                 { v0 = 1; v1 = 2; v2 = 0; }
  311.              else
  312.                 { v0 = 2; v1 = 0; v2 = 1; }
  313.             // step 2: clip each edge
  314.             // basically we are going to generate the parametric line p = v0 + v01*t
  315.             // then solve for t when the z component is equal to near z, then plug that
  316.             // back into to solve for x,y of the 3D line, we could do this with high
  317.             // level code and parametric lines, but to save time, lets do it manually
  318.             // clip edge v0->v1
  319.             VECTOR4D_Build(&curr_poly->tvlist[v0].v, &curr_poly->tvlist[v1].v, &v);                          
  320.             // the intersection occurs when z = near z, so t = 
  321.             t1 = ( (cam->near_clip_z - curr_poly->tvlist[v0].z) / v.z );
  322.       
  323.             // now plug t back in and find x,y intersection with the plane
  324.             xi = curr_poly->tvlist[v0].x + v.x * t1;
  325.             yi = curr_poly->tvlist[v0].y + v.y * t1;
  326.             // now overwrite vertex with new vertex
  327.             curr_poly->tvlist[v1].x = xi;
  328.             curr_poly->tvlist[v1].y = yi;
  329.             curr_poly->tvlist[v1].z = cam->near_clip_z; 
  330.          
  331.             // clip edge v0->v2
  332.             VECTOR4D_Build(&curr_poly->tvlist[v0].v, &curr_poly->tvlist[v2].v, &v);                          
  333.             // the intersection occurs when z = near z, so t = 
  334.             t2 = ( (cam->near_clip_z - curr_poly->tvlist[v0].z) / v.z );
  335.       
  336.             // now plug t back in and find x,y intersection with the plane
  337.             xi = curr_poly->tvlist[v0].x + v.x * t2;
  338.             yi = curr_poly->tvlist[v0].y + v.y * t2;
  339.             // now overwrite vertex with new vertex
  340.             curr_poly->tvlist[v2].x = xi;
  341.             curr_poly->tvlist[v2].y = yi;
  342.             curr_poly->tvlist[v2].z = cam->near_clip_z; 
  343.             // now that we have both t1, t2, check if the poly is textured, if so clip
  344.             // texture coordinates
  345.             if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_TEXTURE)
  346.                {
  347.                ui = curr_poly->tvlist[v0].u0 + (curr_poly->tvlist[v1].u0 - curr_poly->tvlist[v0].u0)*t1;
  348.                vi = curr_poly->tvlist[v0].v0 + (curr_poly->tvlist[v1].v0 - curr_poly->tvlist[v0].v0)*t1;
  349.                curr_poly->tvlist[v1].u0 = ui;
  350.                curr_poly->tvlist[v1].v0 = vi;
  351.                ui = curr_poly->tvlist[v0].u0 + (curr_poly->tvlist[v2].u0 - curr_poly->tvlist[v0].u0)*t2;
  352.                vi = curr_poly->tvlist[v0].v0 + (curr_poly->tvlist[v2].v0 - curr_poly->tvlist[v0].v0)*t2;
  353.                curr_poly->tvlist[v2].u0 = ui;
  354.                curr_poly->tvlist[v2].v0 = vi;
  355.                } // end if textured
  356.                // finally, we have obliterated our pre-computed normal length
  357.                // it needs to be recomputed!!!!
  358.  
  359.               // build u, v
  360.               VECTOR4D_Build(&curr_poly->tvlist[v0].v, &curr_poly->tvlist[v1].v, &u);
  361.               VECTOR4D_Build(&curr_poly->tvlist[v0].v, &curr_poly->tvlist[v2].v, &v);
  362.               // compute cross product
  363.               VECTOR4D_Cross(&u, &v, &n);
  364.               // compute length of normal accurately and store in poly nlength
  365.               // +- epsilon later to fix over/underflows
  366.               curr_poly->nlength = VECTOR4D_Length_Fast(&n); 
  367.              } // end if
  368.           else
  369.           if (num_verts_in == 2)
  370.              { // num_verts = 2
  371.              // must be the case with num_verts_in = 2 
  372.              // we need to clip the triangle against the near clipping plane
  373.              // the clipping procedure is done to each edge leading away from
  374.              // the interior vertex, to clip we need to compute the intersection
  375.              // with the near z plane, this is done with a parametric equation of 
  376.              // the edge, however unlike case 1 above, the triangle will be split
  377.              // into two triangles, thus during the first clip, we will store the 
  378.              // results into a new triangle at the end of the rendering list, and 
  379.              // then on the last clip we will overwrite the triangle being clipped
  380.              // step 0: copy the polygon
  381.              memcpy(&temp_poly, curr_poly, sizeof(POLYF4DV2) );
  382.              // step 1: find vertex index for exterior vertex
  383.              if ( vertex_ccodes[0] == CLIP_CODE_LZ)
  384.                 { v0 = 0; v1 = 1; v2 = 2; }
  385.              else 
  386.              if (vertex_ccodes[1] == CLIP_CODE_LZ)
  387.                 { v0 = 1; v1 = 2; v2 = 0; }
  388.              else
  389.                 { v0 = 2; v1 = 0; v2 = 1; }
  390.              // step 2: clip each edge
  391.              // basically we are going to generate the parametric line p = v0 + v01*t
  392.              // then solve for t when the z component is equal to near z, then plug that
  393.              // back into to solve for x,y of the 3D line, we could do this with high
  394.              // level code and parametric lines, but to save time, lets do it manually
  395.              // clip edge v0->v1
  396.              VECTOR4D_Build(&curr_poly->tvlist[v0].v, &curr_poly->tvlist[v1].v, &v);                          
  397.              // the intersection occurs when z = near z, so t = 
  398.              t1 = ( (cam->near_clip_z - curr_poly->tvlist[v0].z) / v.z );
  399.       
  400.              // now plug t back in and find x,y intersection with the plane
  401.              x01i = curr_poly->tvlist[v0].x + v.x * t1;
  402.              y01i = curr_poly->tvlist[v0].y + v.y * t1;
  403.          
  404.              // clip edge v0->v2
  405.              VECTOR4D_Build(&curr_poly->tvlist[v0].v, &curr_poly->tvlist[v2].v, &v);                          
  406.              // the intersection occurs when z = near z, so t = 
  407.              t2 = ( (cam->near_clip_z - curr_poly->tvlist[v0].z) / v.z );
  408.       
  409.              // now plug t back in and find x,y intersection with the plane
  410.              x02i = curr_poly->tvlist[v0].x + v.x * t2;
  411.              y02i = curr_poly->tvlist[v0].y + v.y * t2; 
  412.              // now we have both intersection points, we must overwrite the inplace
  413.              // polygon's vertex 0 with the intersection point, this poly 1 of 2 from
  414.              // the split
  415.              // now overwrite vertex with new vertex
  416.              curr_poly->tvlist[v0].x = x01i;
  417.              curr_poly->tvlist[v0].y = y01i;
  418.              curr_poly->tvlist[v0].z = cam->near_clip_z; 
  419.              // now comes the hard part, we have to carefully create a new polygon
  420.              // from the 2 intersection points and v2, this polygon will be inserted
  421.              // at the end of the rendering list, but for now, we are building it up
  422.              // in  temp_poly
  423.              // so leave v2 alone, but overwrite v1 with v01, and overwrite v0 with v02
  424.              temp_poly.tvlist[v1].x = x01i;
  425.              temp_poly.tvlist[v1].y = y01i;
  426.              temp_poly.tvlist[v1].z = cam->near_clip_z;              
  427.              temp_poly.tvlist[v0].x = x02i;
  428.              temp_poly.tvlist[v0].y = y02i;
  429.              temp_poly.tvlist[v0].z = cam->near_clip_z;    
  430.             // now that we have both t1, t2, check if the poly is textured, if so clip
  431.             // texture coordinates
  432.             if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_TEXTURE)
  433.                {
  434.                // compute poly 1 new texture coordinates from split
  435.                u01i = curr_poly->tvlist[v0].u0 + (curr_poly->tvlist[v1].u0 - curr_poly->tvlist[v0].u0)*t1;
  436.                v01i = curr_poly->tvlist[v0].v0 + (curr_poly->tvlist[v1].v0 - curr_poly->tvlist[v0].v0)*t1;
  437.                // compute poly 2 new texture coordinates from split
  438.                u02i = curr_poly->tvlist[v0].u0 + (curr_poly->tvlist[v2].u0 - curr_poly->tvlist[v0].u0)*t2;
  439.                v02i = curr_poly->tvlist[v0].v0 + (curr_poly->tvlist[v2].v0 - curr_poly->tvlist[v0].v0)*t2;
  440.                // write them all at the same time         
  441.                // poly 1
  442.                curr_poly->tvlist[v0].u0 = u01i;
  443.                curr_poly->tvlist[v0].v0 = v01i;
  444.                // poly 2
  445.                temp_poly.tvlist[v0].u0 = u02i;
  446.                temp_poly.tvlist[v0].v0 = v02i;
  447.                temp_poly.tvlist[v1].u0 = u01i;
  448.                temp_poly.tvlist[v1].v0 = v01i;
  449.                } // end if textured
  450.                // finally, we have obliterated our pre-computed normal lengths
  451.                // they need to be recomputed!!!!
  452.  
  453.                // poly 1 first, in place
  454.  
  455.                // build u, v
  456.                VECTOR4D_Build(&curr_poly->tvlist[v0].v, &curr_poly->tvlist[v1].v, &u);
  457.                VECTOR4D_Build(&curr_poly->tvlist[v0].v, &curr_poly->tvlist[v2].v, &v);
  458.                // compute cross product
  459.                VECTOR4D_Cross(&u, &v, &n);
  460.                // compute length of normal accurately and store in poly nlength
  461.                // +- epsilon later to fix over/underflows
  462.                curr_poly->nlength = VECTOR4D_Length_Fast(&n); 
  463.                // now poly 2, temp_poly
  464.                // build u, v
  465.                VECTOR4D_Build(&temp_poly.tvlist[v0].v, &temp_poly.tvlist[v1].v, &u);
  466.                VECTOR4D_Build(&temp_poly.tvlist[v0].v, &temp_poly.tvlist[v2].v, &v);
  467.                // compute cross product
  468.                VECTOR4D_Cross(&u, &v, &n);
  469.                // compute length of normal accurately and store in poly nlength
  470.                // +- epsilon later to fix over/underflows
  471.                temp_poly.nlength = VECTOR4D_Length_Fast(&n); 
  472.              // now we are good to go, insert the polygon into list
  473.              // if the poly won't fit, it won't matter, the function will
  474.              // just return 0
  475.              Insert_POLYF4DV2_RENDERLIST4DV2(rend_list, &temp_poly);
  476.              } // end else
  477.         
  478.            } // end if near_z clipping has occured
  479.              
  480.           } // end if z planes
  481.     } // end for poly
  482. } // end Clip_Polys_RENDERLIST4DV2
  483. ////////////////////////////////////////////////////////////
  484. int Generate_Terrain_OBJECT4DV2(OBJECT4DV2_PTR obj,     // pointer to object
  485.                                 float twidth,            // width in world coords on x-axis
  486.                                 float theight,           // height (length) in world coords on z-axis
  487.                                 float vscale,           // vertical scale of terrain
  488.                                 char *height_map_file,  // filename of height bitmap encoded in 256 colors
  489.                                 char *texture_map_file, // filename of texture map
  490.                                 int rgbcolor,           // color of terrain if no texture        
  491.                                 VECTOR4D_PTR pos,       // initial position
  492.                                 VECTOR4D_PTR rot,       // initial rotations
  493.                                 int poly_attr)          // the shading attributes we would like
  494. {
  495. // this function generates a terrain of width x height in the x-z plane
  496. // the terrain is defined by a height field encoded as color values 
  497. // of a 256 color texture, 0 being ground level 255 being 1.0, this value
  498. // is scaled by vscale for the height of each point in the height field
  499. // the height field generated will be composed of triangles where each vertex
  500. // in the height field is derived from the height map, thus if the height map
  501. // is 256 x 256 points then the final mesh will be (256-1) x (256-1) polygons 
  502. // with a absolute world size of width x height (in the x-z plane)
  503. // also if there is a texture map file then it will be mapped onto the terrain
  504. // and texture coordinates will be generated
  505. char buffer[256];  // working buffer
  506. float col_tstep, row_tstep;
  507. float col_vstep, row_vstep;
  508. int columns, rows;
  509. int rgbwhite;
  510. BITMAP_FILE height_bitmap; // holds the height bitmap
  511. // Step 1: clear out the object and initialize it a bit
  512. memset(obj, 0, sizeof(OBJECT4DV2));
  513. // set state of object to active and visible
  514. obj->state = OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_STATE_VISIBLE;
  515. // set position of object
  516. obj->world_pos.x = pos->x;
  517. obj->world_pos.y = pos->y;
  518. obj->world_pos.z = pos->z;
  519. obj->world_pos.w = pos->w;
  520. // create proper color word based on selected bit depth of terrain
  521. // rgbcolor is always in rgb5.6.5 format, so only need to downconvert for
  522. // 8-bit mesh
  523. if (poly_attr & POLY4DV1_ATTR_8BITCOLOR)
  524.    { 
  525.    rgbcolor = rgblookup[rgbcolor];
  526.    rgbwhite = rgblookup[RGB16Bit(255,255,255)];
  527.    } // end if
  528. else
  529.    {
  530.    rgbwhite = RGB16Bit(255,255,255);
  531.    } // end else
  532. // set number of frames
  533. obj->num_frames = 1;
  534. obj->curr_frame = 0;
  535. obj->attr = OBJECT4DV2_ATTR_SINGLE_FRAME;
  536. // clear the bitmaps out
  537. memset(&height_bitmap, 0, sizeof(BITMAP_FILE));
  538. memset(&bitmap16bit, 0, sizeof(BITMAP_FILE));
  539. // Step 2: load in the height field
  540. Load_Bitmap_File(&height_bitmap, height_map_file);
  541. // compute basic information
  542. columns = height_bitmap.bitmapinfoheader.biWidth;
  543. rows    = height_bitmap.bitmapinfoheader.biHeight;
  544. col_vstep = twidth / (float)(columns - 1);
  545. row_vstep = theight / (float)(rows - 1);
  546. sprintf(obj->name ,"Terrain:%s%s", height_map_file, texture_map_file);
  547. obj->num_vertices = columns * rows;
  548. obj->num_polys    = ((columns - 1) * (rows - 1) ) * 2;
  549. // store some results to help with terrain following
  550. // use the auxialiary variables in the object -- might as well!
  551. obj->ivar1 = columns;
  552. obj->ivar2 = rows;
  553. obj->fvar1 = col_vstep;
  554. obj->fvar2 = row_vstep;
  555. // allocate the memory for the vertices and number of polys
  556. // the call parameters are redundant in this case, but who cares
  557. if (!Init_OBJECT4DV2(obj,   // object to allocate
  558.                      obj->num_vertices, 
  559.                      obj->num_polys, 
  560.                      obj->num_frames))
  561.     {
  562.     Write_Error("nTerrain generator error (can't allocate memory).");
  563.     } // end if
  564. // load texture map if there is one
  565. if ( (poly_attr & POLY4DV2_ATTR_SHADE_MODE_TEXTURE) && texture_map_file)
  566.    {
  567.    // load the texture from disk
  568.    Load_Bitmap_File(&bitmap16bit, texture_map_file);
  569.    // create a proper size and bitdepth bitmap
  570.    obj->texture = (BITMAP_IMAGE_PTR)malloc(sizeof(BITMAP_IMAGE));
  571.    Create_Bitmap(obj->texture,0,0,
  572.                  bitmap16bit.bitmapinfoheader.biWidth,
  573.                  bitmap16bit.bitmapinfoheader.biHeight,
  574.                  bitmap16bit.bitmapinfoheader.biBitCount);
  575.                           
  576.     // load the bitmap image (later make this 8/16 bit)
  577.     if (obj->texture->bpp == 16)
  578.        Load_Image_Bitmap16(obj->texture, &bitmap16bit,0,0,BITMAP_EXTRACT_MODE_ABS);
  579.     else
  580.        {
  581.        Load_Image_Bitmap(obj->texture, &bitmap16bit,0,0,BITMAP_EXTRACT_MODE_ABS);
  582.        } // end else 8 bit
  583.     // compute stepping factors in texture map for texture coordinate computation
  584.     col_tstep = (float)(bitmap16bit.bitmapinfoheader.biWidth-1)/(float)(columns - 1);
  585.     row_tstep = (float)(bitmap16bit.bitmapinfoheader.biHeight-1)/(float)(rows - 1);
  586.     // flag object as having textures
  587.     SET_BIT(obj->attr, OBJECT4DV2_ATTR_TEXTURES);
  588.     // done, so unload the bitmap
  589.     Unload_Bitmap_File(&bitmap16bit);
  590.     } // end if
  591. Write_Error("ncolumns = %d, rows = %d", columns, rows);
  592. Write_Error("ncol_vstep = %f, row_vstep = %f", col_vstep, row_vstep);
  593. Write_Error("ncol_tstep=%f, row_tstep=%f", col_tstep, row_tstep);
  594. Write_Error("nnum_vertices = %d, num_polys = %d", obj->num_vertices, obj->num_polys);
  595. // Step 4: generate the vertex list, and texture coordinate list in row major form
  596. for (int curr_row = 0; curr_row < rows; curr_row++)
  597.     {
  598.     for (int curr_col = 0; curr_col < columns; curr_col++)
  599.         {
  600.         int vertex = (curr_row * columns) + curr_col;
  601.         // compute the vertex
  602.         obj->vlist_local[vertex].x = curr_col * col_vstep - (twidth/2);
  603.         obj->vlist_local[vertex].y = vscale*((float)height_bitmap.buffer[curr_col + (curr_row * columns) ]) / 255;
  604.         obj->vlist_local[vertex].z = curr_row * row_vstep - (theight/2);
  605.         obj->vlist_local[vertex].w = 1;  
  606.         // every vertex has a point at least, set that in the flags attribute
  607.         SET_BIT(obj->vlist_local[vertex].attr, VERTEX4DTV1_ATTR_POINT);
  608.         // need texture coord?
  609.         if ( (poly_attr & POLY4DV2_ATTR_SHADE_MODE_TEXTURE) && texture_map_file)
  610.            {
  611.            // now texture coordinates
  612.            obj->tlist[vertex].x = curr_col * col_tstep;
  613.            obj->tlist[vertex].y = curr_row * row_tstep;
  614.            } // end if
  615.         Write_Error("nVertex %d: V[%f, %f, %f], T[%f, %f]", vertex, obj->vlist_local[vertex].x,
  616.                                                              obj->vlist_local[vertex].y,
  617.                                                              obj->vlist_local[vertex].z,
  618.                                                              obj->tlist[vertex].x,
  619.                                                              obj->tlist[vertex].y);
  620.         } // end for curr_col
  621.      } // end curr_row
  622. // perform rotation transformation?
  623. // compute average and max radius
  624. Compute_OBJECT4DV2_Radius(obj);
  625. Write_Error("nObject average radius = %f, max radius = %f", 
  626.             obj->avg_radius[0], obj->max_radius[0]);
  627. // Step 5: generate the polygon list
  628. for (int poly=0; poly < obj->num_polys/2; poly++)
  629.     {
  630.     // polygons follow a regular pattern of 2 per square, row
  631.     // major form, finding the correct indices is a pain, but 
  632.     // the bottom line is we have an array of vertices mxn and we need
  633.     // a list of polygons that is (m-1) x (n-1), with 2 triangles per
  634.     // square with a consistent winding order... this is one one to arrive
  635.     // at the indices, another way would be to use two loops, etc., 
  636.     int base_poly_index = (poly % (columns-1)) + (columns * (poly / (columns - 1)) );
  637.     // upper left poly
  638.     obj->plist[poly*2].vert[0] = base_poly_index;
  639.     obj->plist[poly*2].vert[1] = base_poly_index+columns;
  640.     obj->plist[poly*2].vert[2] = base_poly_index+columns+1;
  641.     // lower right poly
  642.     obj->plist[poly*2+1].vert[0] = base_poly_index;
  643.     obj->plist[poly*2+1].vert[1] = base_poly_index+columns+1;
  644.     obj->plist[poly*2+1].vert[2] = base_poly_index+1;
  645.  
  646.     // point polygon vertex list to object's vertex list
  647.     // note that this is redundant since the polylist is contained
  648.     // within the object in this case and its up to the user to select
  649.     // whether the local or transformed vertex list is used when building up
  650.     // polygon geometry, might be a better idea to set to NULL in the context
  651.     // of polygons that are part of an object
  652.     obj->plist[poly*2].vlist = obj->vlist_local; 
  653.     obj->plist[poly*2+1].vlist = obj->vlist_local; 
  654.     // set attributes of polygon with sent attributes
  655.     obj->plist[poly*2].attr = poly_attr;
  656.     obj->plist[poly*2+1].attr = poly_attr;
  657.     // now perform some test to make sure any secondary data elements are
  658.     // set properly
  659.     // set color of polygon
  660.     obj->plist[poly*2].color = rgbcolor;
  661.     obj->plist[poly*2+1].color = rgbcolor;
  662.     // check for gouraud of phong shading, if so need normals
  663.     if ( (obj->plist[poly*2].attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) ||  
  664.          (obj->plist[poly*2].attr & POLY4DV2_ATTR_SHADE_MODE_PHONG) )
  665.        {
  666.        // the vertices from this polygon all need normals, set that in the flags attribute
  667.        SET_BIT(obj->vlist_local[ obj->plist[poly*2].vert[0] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  668.        SET_BIT(obj->vlist_local[ obj->plist[poly*2].vert[1] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  669.        SET_BIT(obj->vlist_local[ obj->plist[poly*2].vert[2] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  670.        SET_BIT(obj->vlist_local[ obj->plist[poly*2+1].vert[0] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  671.        SET_BIT(obj->vlist_local[ obj->plist[poly*2+1].vert[1] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  672.        SET_BIT(obj->vlist_local[ obj->plist[poly*2+1].vert[2] ].attr, VERTEX4DTV1_ATTR_NORMAL); 
  673.        } // end if
  674.  
  675.      // if texture in enabled the enable texture coordinates
  676.      if (poly_attr & POLY4DV2_ATTR_SHADE_MODE_TEXTURE)
  677.         {
  678.         // apply texture to this polygon
  679.         obj->plist[poly*2].texture = obj->texture;
  680.         obj->plist[poly*2+1].texture = obj->texture;
  681.         // assign the texture coordinates
  682.         // upper left poly
  683.         obj->plist[poly*2].text[0] = base_poly_index;
  684.         obj->plist[poly*2].text[1] = base_poly_index+columns;
  685.         obj->plist[poly*2].text[2] = base_poly_index+columns+1;
  686.         // lower right poly
  687.         obj->plist[poly*2+1].text[0] = base_poly_index;
  688.         obj->plist[poly*2+1].text[1] = base_poly_index+columns+1;
  689.         obj->plist[poly*2+1].text[2] = base_poly_index+1;
  690.  
  691.         // override base color to make poly more reflective
  692.         obj->plist[poly*2].color = rgbwhite;
  693.         obj->plist[poly*2+1].color = rgbwhite;
  694.         // set texture coordinate attributes
  695.         SET_BIT(obj->vlist_local[ obj->plist[poly*2].vert[0] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  696.         SET_BIT(obj->vlist_local[ obj->plist[poly*2].vert[1] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  697.         SET_BIT(obj->vlist_local[ obj->plist[poly*2].vert[2] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  698.         SET_BIT(obj->vlist_local[ obj->plist[poly*2+1].vert[0] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  699.         SET_BIT(obj->vlist_local[ obj->plist[poly*2+1].vert[1] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  700.         SET_BIT(obj->vlist_local[ obj->plist[poly*2+1].vert[2] ].attr, VERTEX4DTV1_ATTR_TEXTURE); 
  701.         } // end if
  702.     // set the material mode to ver. 1.0 emulation
  703.     SET_BIT(obj->plist[poly*2].attr, POLY4DV2_ATTR_DISABLE_MATERIAL);
  704.     SET_BIT(obj->plist[poly*2+1].attr, POLY4DV2_ATTR_DISABLE_MATERIAL);
  705.     // finally set the polygon to active
  706.     obj->plist[poly*2].state = POLY4DV2_STATE_ACTIVE;    
  707.     obj->plist[poly*2+1].state = POLY4DV2_STATE_ACTIVE;  
  708.     // point polygon vertex list to object's vertex list
  709.     // note that this is redundant since the polylist is contained
  710.     // within the object in this case and its up to the user to select
  711.     // whether the local or transformed vertex list is used when building up
  712.     // polygon geometry, might be a better idea to set to NULL in the context
  713.     // of polygons that are part of an object
  714.     obj->plist[poly*2].vlist = obj->vlist_local; 
  715.     obj->plist[poly*2+1].vlist = obj->vlist_local; 
  716.     // set texture coordinate list, this is needed
  717.     obj->plist[poly*2].tlist = obj->tlist;
  718.     obj->plist[poly*2+1].tlist = obj->tlist;
  719.     } // end for poly
  720. #if 0
  721. for (poly=0; poly < obj->num_polys; poly++)
  722. {
  723. Write_Error("nPoly %d: Vi[%d, %d, %d], Ti[%d, %d, %d]",poly,
  724.                                                         obj->plist[poly].vert[0],
  725.                                                         obj->plist[poly].vert[1],
  726.                                                         obj->plist[poly].vert[2],
  727.                                                         obj->plist[poly].text[0],
  728.                                                         obj->plist[poly].text[1],
  729.                                                         obj->plist[poly].text[2]);
  730. } // end 
  731. #endif
  732. // compute the polygon normal lengths
  733. Compute_OBJECT4DV2_Poly_Normals(obj);
  734. // compute vertex normals for any gouraud shaded polys
  735. Compute_OBJECT4DV2_Vertex_Normals(obj);
  736. // return success
  737. return(1);
  738. } // end Generate_Terrain_OBJECT4DV2
  739. //////////////////////////////////////////////////////////////////////////////
  740. int Init_Light_LIGHTV2(LIGHTV2_PTR   lights,     // light array to work with (new)
  741.                        int           index,      // index of light to create (0..MAX_LIGHTS-1)
  742.                        int          _state,      // state of light
  743.                        int          _attr,       // type of light, and extra qualifiers
  744.                        RGBAV1       _c_ambient,  // ambient light intensity
  745.                        RGBAV1       _c_diffuse,  // diffuse light intensity
  746.                        RGBAV1       _c_specular, // specular light intensity
  747.                        POINT4D_PTR  _pos,        // position of light
  748.                        VECTOR4D_PTR _dir,        // direction of light
  749.                        float        _kc,         // attenuation factors
  750.                        float        _kl, 
  751.                        float        _kq,  
  752.                        float        _spot_inner, // inner angle for spot light
  753.                        float        _spot_outer, // outer angle for spot light
  754.                        float        _pf)         // power factor/falloff for spot lights
  755. {
  756. // this function initializes a light based on the flags sent in _attr, values that
  757. // aren't needed are set to 0 by caller, nearly identical to version 1.0, however has
  758. // new support for transformed light coordinates 
  759. // make sure light is in range 
  760. if (index < 0 || index >= MAX_LIGHTS)
  761.     return(0);
  762. // all good, initialize the light (many fields may be dead)
  763. lights[index].state       = _state;      // state of light
  764. lights[index].id          = index;       // id of light
  765. lights[index].attr        = _attr;       // type of light, and extra qualifiers
  766. lights[index].c_ambient   = _c_ambient;  // ambient light intensity
  767. lights[index].c_diffuse   = _c_diffuse;  // diffuse light intensity
  768. lights[index].c_specular  = _c_specular; // specular light intensity
  769. lights[index].kc          = _kc;         // constant, linear, and quadratic attenuation factors
  770. lights[index].kl          = _kl;   
  771. lights[index].kq          = _kq;   
  772. if (_pos)
  773.    {
  774.    VECTOR4D_COPY(&lights[index].pos, _pos);  // position of light
  775.    VECTOR4D_COPY(&lights[index].tpos, _pos);
  776.    } // end if
  777.    
  778. if (_dir)
  779.    {
  780.    VECTOR4D_COPY(&lights[index].dir, _dir);  // direction of light
  781.    
  782.    // normalize it
  783.    VECTOR4D_Normalize(&lights[index].dir);
  784.    VECTOR4D_COPY(&lights[index].tdir, &lights[index].dir); 
  785.    } // end if
  786. lights[index].spot_inner  = _spot_inner; // inner angle for spot light
  787. lights[index].spot_outer  = _spot_outer; // outer angle for spot light
  788. lights[index].pf          = _pf;         // power factor/falloff for spot lights
  789. // return light index as success
  790. return(index);
  791. } // end Create_Light_LIGHTV2
  792. //////////////////////////////////////////////////////////////////////////////
  793. int Reset_Lights_LIGHTV2(LIGHTV2_PTR lights,       // light array to work with (new)
  794.                          int max_lights)           // number of lights in system
  795. {
  796. // this function simply resets all lights in the system
  797. static int first_time = 1;
  798. memset(lights, 0, max_lights*sizeof(LIGHTV2));
  799. // reset number of lights global
  800. num_lights = 0;
  801. // reset first time
  802. first_time = 0;
  803. // return success
  804. return(1);
  805. } // end Reset_Lights_LIGHTV2
  806. //////////////////////////////////////////////////////////////////////////////
  807. int Light_OBJECT4DV2_World2_16(OBJECT4DV2_PTR obj,  // object to process
  808.                              CAM4DV1_PTR cam,     // camera position
  809.                              LIGHTV2_PTR lights,  // light list (might have more than one)
  810.                              int max_lights)      // maximum lights in list
  811. {
  812. // {andre work in progress }
  813. // 16-bit version of function
  814. // function lights an object based on the sent lights and camera. the function supports
  815. // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
  816. // note that this lighting function is rather brute force and simply follows the math, however
  817. // there are some clever integer operations that are used in scale 256 rather than going to floating
  818. // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
  819. // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
  820. // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
  821. // of the falloff due to attenuation, but they still look like spot lights
  822. // type 2 spot lights are implemented with the intensity having a dot product relationship with the
  823. // angle from the surface point to the light direction just like in the optimized model, but the pf term
  824. // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
  825. unsigned int r_base, g_base,   b_base,  // base color being lit
  826.              r_sum,  g_sum,    b_sum,   // sum of lighting process over all lights
  827.              r_sum0,  g_sum0,  b_sum0,
  828.              r_sum1,  g_sum1,  b_sum1,
  829.              r_sum2,  g_sum2,  b_sum2,
  830.              ri,gi,bi,
  831.              shaded_color;            // final color
  832. float dp,     // dot product 
  833.       dist,   // distance from light to surface
  834.       dists, 
  835.       i,      // general intensities
  836.       nl,     // length of normal
  837.       atten;  // attenuation computations
  838. VECTOR4D u, v, n, l, d, s; // used for cross product and light vector calculations
  839. //Write_Error("nEntering lighting function");
  840. // test if the object is culled
  841. if (!(obj->state & OBJECT4DV2_STATE_ACTIVE) ||
  842.      (obj->state & OBJECT4DV2_STATE_CULLED) ||
  843.      !(obj->state & OBJECT4DV2_STATE_VISIBLE))
  844.    return(0); 
  845. // for each valid poly, light it...
  846. for (int poly=0; poly < obj->num_polys; poly++)
  847.     {
  848.     // acquire polygon
  849.     POLY4DV2_PTR curr_poly = &obj->plist[poly];
  850.     // light this polygon if and only if it's not clipped, not culled,
  851.     // active, and visible
  852.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  853.          (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  854.          (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  855.        continue; // move onto next poly
  856.     // set state of polygon to lit, so we don't light again in renderlist
  857.     // lighting system if it happens to get called
  858.     SET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
  859.     // extract vertex indices into master list, rember the polygons are 
  860.     // NOT self contained, but based on the vertex list stored in the object
  861.     // itself
  862.     int vindex_0 = curr_poly->vert[0];
  863.     int vindex_1 = curr_poly->vert[1];
  864.     int vindex_2 = curr_poly->vert[2];
  865.     
  866.     // we will use the transformed polygon vertex list since the backface removal
  867.     // only makes sense at the world coord stage further of the pipeline 
  868.     //Write_Error("npoly %d",poly);
  869.    
  870.     // we will use the transformed polygon vertex list since the backface removal
  871.     // only makes sense at the world coord stage further of the pipeline 
  872.     // test the lighting mode of the polygon (use flat for flat, gouraud))
  873.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_FLAT)
  874.        {
  875.        //Write_Error("nEntering Flat Shader");
  876.        // step 1: extract the base color out in RGB mode, assume RGB 565
  877.        _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
  878.        // scale to 8 bit 
  879.        r_base <<= 3;
  880.        g_base <<= 2;
  881.        b_base <<= 3;
  882.        //Write_Error("nBase color=%d,%d,%d", r_base, g_base, b_base);
  883.        // initialize color sum
  884.        r_sum  = 0;
  885.        g_sum  = 0;
  886.        b_sum  = 0;
  887.        //Write_Error("nsum color=%d,%d,%d", r_sum, g_sum, b_sum);
  888.        // new optimization:
  889.        // when there are multiple lights in the system we will end up performing numerous
  890.        // redundant calculations to minimize this my strategy is to set key variables to 
  891.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  892.        // the max value, if they are the max value then the first light that needs the math
  893.        // will do it, and then save the information into the variable (causing it to change state
  894.        // from an invalid number) then any other lights that need the math can use the previously
  895.        // computed value
  896.        
  897.        // set surface normal.z to FLT_MAX to flag it as non-computed
  898.        n.z = FLT_MAX;
  899.        // loop thru lights
  900.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  901.            {
  902.            // is this light active
  903.            if (lights[curr_light].state==LIGHTV2_STATE_OFF)
  904.               continue;
  905.            //Write_Error("nprocessing light %d",curr_light);
  906.            // what kind of light are we dealing with
  907.            if (lights[curr_light].attr & LIGHTV2_ATTR_AMBIENT)
  908.               {
  909.               //Write_Error("nEntering ambient light...");
  910.               // simply multiply each channel against the color of the 
  911.               // polygon then divide by 256 to scale back to 0..255
  912.               // use a shift in real life!!! >> 8
  913.               r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
  914.               g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
  915.               b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
  916.               //Write_Error("nambient sum=%d,%d,%d", r_sum, g_sum, b_sum);
  917.               // there better only be one ambient light!
  918.               } // end if
  919.            else
  920.            if (lights[curr_light].attr & LIGHTV2_ATTR_INFINITE) ///////////////////////////////////////////
  921.               {
  922.               //Write_Error("nEntering infinite light...");
  923.               // infinite lighting, we need the surface normal, and the direction
  924.               // of the light source
  925.               // test if we already computed poly normal in previous calculation
  926.               if (n.z==FLT_MAX)       
  927.                  {
  928.                  // we need to compute the normal of this polygon face, and recall
  929.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  930.  
  931.                  // build u, v
  932.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  933.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  934.                  // compute cross product
  935.                  VECTOR4D_Cross(&u, &v, &n);
  936.                  } // end if
  937.               // at this point, we are almost ready, but we have to normalize the normal vector!
  938.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  939.               // normals, so this step can be optimized
  940.               // compute length of normal
  941.               //nl = VECTOR4D_Length_Fast2(&n);
  942.               nl = curr_poly->nlength;  
  943.       
  944.               // ok, recalling the lighting model for infinite lights
  945.               // I(d)dir = I0dir * Cldir
  946.               // and for the diffuse model
  947.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  948.               // so we basically need to multiple it all together
  949.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  950.               // are slower, but the conversion to and from cost cycles
  951.               dp = VECTOR4D_Dot(&n, &lights[curr_light].tdir);
  952.               
  953.               // only add light if dp > 0
  954.               if (dp > 0)
  955.                  { 
  956.                  i = 128*dp/nl; 
  957.                  r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  958.                  g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  959.                  b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  960.                  } // end if
  961.               //Write_Error("ninfinite sum=%d,%d,%d", r_sum, g_sum, b_sum);
  962.               } // end if infinite light
  963.            else
  964.            if (lights[curr_light].attr & LIGHTV2_ATTR_POINT) ///////////////////////////////////////
  965.               {
  966.               //Write_Error("nEntering point light...");
  967.               // perform point light computations
  968.               // light model for point light is once again:
  969.               //              I0point * Clpoint
  970.               //  I(d)point = ___________________
  971.               //              kc +  kl*d + kq*d2              
  972.               //
  973.               //  Where d = |p - s|
  974.               // thus it's almost identical to the infinite light, but attenuates as a function
  975.               // of distance from the point source to the surface point being lit
  976.               // test if we already computed poly normal in previous calculation
  977.               if (n.z==FLT_MAX)       
  978.                  {
  979.                  // we need to compute the normal of this polygon face, and recall
  980.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  981.  
  982.                  // build u, v
  983.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  984.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  985.                  // compute cross product
  986.                  VECTOR4D_Cross(&u, &v, &n);
  987.                  } // end if
  988.               // at this point, we are almost ready, but we have to normalize the normal vector!
  989.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  990.               // normals, so this step can be optimized
  991.               // compute length of normal
  992.               //nl = VECTOR4D_Length_Fast2(&n);
  993.               nl = curr_poly->nlength;  
  994.               // compute vector from surface to light
  995.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].tpos, &l);
  996.               // compute distance and attenuation
  997.               dist = VECTOR4D_Length_Fast2(&l);  
  998.               // and for the diffuse model
  999.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1000.               // so we basically need to multiple it all together
  1001.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1002.               // are slower, but the conversion to and from cost cycles
  1003.               dp = VECTOR4D_Dot(&n, &l);
  1004.               
  1005.               // only add light if dp > 0
  1006.               if (dp > 0)
  1007.                  { 
  1008.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1009.                  i = 128*dp / (nl * dist * atten ); 
  1010.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1011.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1012.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1013.                  } // end if
  1014.               //Write_Error("npoint sum=%d,%d,%d",r_sum,g_sum,b_sum);
  1015.               } // end if point
  1016.            else
  1017.            if (lights[curr_light].attr & LIGHTV2_ATTR_SPOTLIGHT1) ////////////////////////////////////
  1018.               {
  1019.               //Write_Error("nentering spot light1...");
  1020.               // perform spotlight/point computations simplified model that uses
  1021.               // point light WITH a direction to simulate a spotlight
  1022.               // light model for point light is once again:
  1023.               //              I0point * Clpoint
  1024.               //  I(d)point = ___________________
  1025.               //              kc +  kl*d + kq*d2              
  1026.               //
  1027.               //  Where d = |p - s|
  1028.               // thus it's almost identical to the infinite light, but attenuates as a function
  1029.               // of distance from the point source to the surface point being lit
  1030.               // test if we already computed poly normal in previous calculation
  1031.               if (n.z==FLT_MAX)       
  1032.                  {
  1033.                  // we need to compute the normal of this polygon face, and recall
  1034.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1035.  
  1036.                  // build u, v
  1037.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1038.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1039.                  // compute cross product
  1040.                  VECTOR4D_Cross(&u, &v, &n);
  1041.                  } // end if
  1042.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1043.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1044.               // normals, so this step can be optimized
  1045.               // compute length of normal
  1046.               //nl = VECTOR4D_Length_Fast2(&n);
  1047.               nl = curr_poly->nlength;  
  1048.        
  1049.               // compute vector from surface to light
  1050.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].tpos, &l);
  1051.               // compute distance and attenuation
  1052.               dist = VECTOR4D_Length_Fast2(&l);  
  1053.               // and for the diffuse model
  1054.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1055.               // so we basically need to multiple it all together
  1056.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1057.               // are slower, but the conversion to and from cost cycles
  1058.               // note that I use the direction of the light here rather than a the vector to the light
  1059.               // thus we are taking orientation into account which is similar to the spotlight model
  1060.               dp = VECTOR4D_Dot(&n, &lights[curr_light].tdir);
  1061.               
  1062.               // only add light if dp > 0
  1063.               if (dp > 0)
  1064.                  { 
  1065.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1066.                  i = 128*dp / (nl * atten ); 
  1067.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1068.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1069.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1070.                  } // end if
  1071.               //Write_Error("nspotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  1072.               } // end if spotlight1
  1073.            else
  1074.            if (lights[curr_light].attr & LIGHTV2_ATTR_SPOTLIGHT2) // simple version ////////////////////
  1075.               {
  1076.               //Write_Error("nEntering spotlight2 ...");
  1077.  
  1078.               // perform spot light computations
  1079.               // light model for spot light simple version is once again:
  1080.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  1081.               // I(d)spotlight = __________________________________________      
  1082.               //                 kc + kl*d + kq*d2        
  1083.               // Where d = |p - s|, and pf = power factor
  1084.               // thus it's almost identical to the point, but has the extra term in the numerator
  1085.               // relating the angle between the light source and the point on the surface
  1086.               // test if we already computed poly normal in previous calculation
  1087.               if (n.z==FLT_MAX)       
  1088.                  {
  1089.                  // we need to compute the normal of this polygon face, and recall
  1090.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1091.  
  1092.                  // build u, v
  1093.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1094.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1095.                  // compute cross product
  1096.                  VECTOR4D_Cross(&u, &v, &n);
  1097.                  } // end if
  1098.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1099.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1100.               // normals, so this step can be optimized
  1101.               // compute length of normal
  1102.               //nl = VECTOR4D_Length_Fast2(&n);
  1103.               nl = curr_poly->nlength;  
  1104.              
  1105.               // and for the diffuse model
  1106.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1107.               // so we basically need to multiple it all together
  1108.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1109.               // are slower, but the conversion to and from cost cycles
  1110.               dp = VECTOR4D_Dot(&n, &lights[curr_light].tdir);
  1111.               
  1112.               // only add light if dp > 0
  1113.               if (dp > 0)
  1114.                  { 
  1115.                  // compute vector from light to surface (different from l which IS the light dir)
  1116.                  VECTOR4D_Build( &lights[curr_light].tpos, &obj->vlist_trans[ vindex_0].v, &s);
  1117.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1118.                  dists = VECTOR4D_Length_Fast2(&s);  
  1119.                  // compute spot light term (s . l)
  1120.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].tdir)/dists;
  1121.                  // proceed only if term is positive
  1122.                  if (dpsl > 0) 
  1123.                     {
  1124.                     // compute attenuation
  1125.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1126.        
  1127.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1128.                     // must be integral
  1129.                     float dpsl_exp = dpsl;
  1130.  
  1131.                     // exponentiate for positive integral powers
  1132.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1133.                          dpsl_exp*=dpsl;
  1134.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1135.                                                       
  1136.                     i = 128*dp * dpsl_exp / (nl * atten ); 
  1137.                     r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1138.                     g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1139.                     b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1140.   
  1141.                     } // end if
  1142.                  } // end if
  1143.               //Write_Error("nSpotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  1144.      
  1145.               } // end if spot light
  1146.            } // end for light
  1147.   
  1148.        // make sure colors aren't out of range
  1149.        if (r_sum  > 255) r_sum = 255;
  1150.        if (g_sum  > 255) g_sum = 255;
  1151.        if (b_sum  > 255) b_sum = 255;
  1152.        //Write_Error("nWriting final values to polygon %d = %d,%d,%d", poly, r_sum, g_sum, b_sum);
  1153.        // write the color over current color
  1154.        curr_poly->lit_color[0] = RGB16Bit(r_sum, g_sum, b_sum);
  1155.        } // end if
  1156.     else
  1157.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) /////////////////////////////////
  1158.        {
  1159.        // gouraud shade, unfortunetly at this point in the pipeline, we have lost the original
  1160.        // mesh, and only have triangles, thus, many triangles will share the same vertices and
  1161.        // they will get lit 2x since we don't have any way to tell this, alas, performing lighting
  1162.        // at the object level is a better idea when gouraud shading is performed since the 
  1163.        // commonality of vertices is still intact, in any case, lighting here is similar to polygon
  1164.        // flat shaded, but we do it 3 times, once for each vertex, additionally there are lots
  1165.        // of opportunities for optimization, but I am going to lay off them for now, so the code
  1166.        // is intelligible, later we will optimize
  1167.        //Write_Error("nEntering gouraud shader...");
  1168.        // step 1: extract the base color out in RGB mode
  1169.        // assume 565 format
  1170.        _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
  1171.        // scale to 8 bit 
  1172.        r_base <<= 3;
  1173.        g_base <<= 2;
  1174.        b_base <<= 3;
  1175.        //Write_Error("nBase color=%d, %d, %d", r_base, g_base, b_base);
  1176.        // initialize color sum(s) for vertices
  1177.        r_sum0  = 0;
  1178.        g_sum0  = 0;
  1179.        b_sum0  = 0;
  1180.        r_sum1  = 0;
  1181.        g_sum1  = 0;
  1182.        b_sum1  = 0;
  1183.        r_sum2  = 0;
  1184.        g_sum2  = 0;
  1185.        b_sum2  = 0;
  1186.        //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);
  1187.        // new optimization:
  1188.        // when there are multiple lights in the system we will end up performing numerous
  1189.        // redundant calculations to minimize this my strategy is to set key variables to 
  1190.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  1191.        // the max value, if they are the max value then the first light that needs the math
  1192.        // will do it, and then save the information into the variable (causing it to change state
  1193.        // from an invalid number) then any other lights that need the math can use the previously
  1194.        // computed value
  1195.        // loop thru lights
  1196.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  1197.            {
  1198.            // is this light active
  1199.            if (lights[curr_light].state==LIGHTV2_STATE_OFF)
  1200.               continue;
  1201.            //Write_Error("nprocessing light %d", curr_light);
  1202.            // what kind of light are we dealing with
  1203.            if (lights[curr_light].attr & LIGHTV2_ATTR_AMBIENT) ///////////////////////////////
  1204.               {
  1205.               //Write_Error("nEntering ambient light....");
  1206.               // simply multiply each channel against the color of the 
  1207.               // polygon then divide by 256 to scale back to 0..255
  1208.               // use a shift in real life!!! >> 8
  1209.               ri = ((lights[curr_light].c_ambient.r * r_base) / 256);
  1210.               gi = ((lights[curr_light].c_ambient.g * g_base) / 256);
  1211.               bi = ((lights[curr_light].c_ambient.b * b_base) / 256);
  1212.             
  1213.               // ambient light has the same affect on each vertex
  1214.               r_sum0+=ri;
  1215.               g_sum0+=gi;
  1216.               b_sum0+=bi;
  1217.   
  1218.               r_sum1+=ri;
  1219.               g_sum1+=gi;
  1220.               b_sum1+=bi;
  1221.               
  1222.               r_sum2+=ri;
  1223.               g_sum2+=gi;
  1224.               b_sum2+=bi;
  1225.               // there better only be one ambient light!
  1226.               //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);
  1227.               } // end if
  1228.            else
  1229.            if (lights[curr_light].attr & LIGHTV2_ATTR_INFINITE) /////////////////////////////////
  1230.               {
  1231.               //Write_Error("nentering infinite light...");
  1232.               // infinite lighting, we need the surface normal, and the direction
  1233.               // of the light source
  1234.               // no longer need to compute normal or length, we already have the vertex normal
  1235.               // and it's length is 1.0  
  1236.               // ....
  1237.       
  1238.               // ok, recalling the lighting model for infinite lights
  1239.               // I(d)dir = I0dir * Cldir
  1240.               // and for the diffuse model
  1241.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1242.               // so we basically need to multiple it all together
  1243.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1244.               // are slower, but the conversion to and from cost cycles
  1245.               // need to perform lighting for each vertex (lots of redundant math, optimize later!)
  1246.               //Write_Error("nv0=[%f, %f, %f]=%f, v1=[%f, %f, %f]=%f, v2=[%f, %f, %f]=%f",
  1247.                 // 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),
  1248.                 // 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),
  1249.                 // 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) );
  1250.               // vertex 0
  1251.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].tdir); 
  1252.               
  1253.               // only add light if dp > 0
  1254.               if (dp > 0)
  1255.                  { 
  1256.                  i = 128*dp; 
  1257.                  r_sum0+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1258.                  g_sum0+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1259.                  b_sum0+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1260.                  } // end if
  1261.               // vertex 1
  1262.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].tdir);
  1263.               
  1264.               // only add light if dp > 0
  1265.               if (dp > 0)
  1266.                  { 
  1267.                  i = 128*dp; 
  1268.                  r_sum1+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1269.                  g_sum1+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1270.                  b_sum1+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1271.                  } // end if
  1272.               // vertex 2
  1273.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].tdir);
  1274.               
  1275.               // only add light if dp > 0
  1276.               if (dp > 0)
  1277.                  { 
  1278.                  i = 128*dp; 
  1279.                  r_sum2+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1280.                  g_sum2+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1281.                  b_sum2+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1282.                  } // end if
  1283.               //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);
  1284.               } // end if infinite light
  1285.            else
  1286.            if (lights[curr_light].attr & LIGHTV2_ATTR_POINT) //////////////////////////////////////
  1287.               {
  1288.               // perform point light computations
  1289.               // light model for point light is once again:
  1290.               //              I0point * Clpoint
  1291.               //  I(d)point = ___________________
  1292.               //              kc +  kl*d + kq*d2              
  1293.               //
  1294.               //  Where d = |p - s|
  1295.               // thus it's almost identical to the infinite light, but attenuates as a function
  1296.               // of distance from the point source to the surface point being lit
  1297.               // .. normal already in vertex
  1298.               //Write_Error("nEntering point light....");
  1299.               // compute vector from surface to light
  1300.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].tpos, &l);
  1301.               // compute distance and attenuation
  1302.               dist = VECTOR4D_Length_Fast2(&l);  
  1303.               // and for the diffuse model
  1304.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1305.               // so we basically need to multiple it all together
  1306.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1307.               // are slower, but the conversion to and from cost cycles
  1308.               // perform the calculation for all 3 vertices
  1309.               // vertex 0
  1310.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &l);
  1311.               
  1312.               // only add light if dp > 0
  1313.               if (dp > 0)
  1314.                  { 
  1315.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1316.                  i = 128*dp / (dist * atten ); 
  1317.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1318.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1319.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1320.                  } // end if
  1321.               // vertex 1
  1322.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &l);
  1323.               
  1324.               // only add light if dp > 0
  1325.               if (dp > 0)
  1326.                  { 
  1327.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1328.                  i = 128*dp / (dist * atten ); 
  1329.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1330.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1331.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1332.                  } // end if
  1333.               // vertex 2
  1334.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &l);
  1335.               
  1336.               // only add light if dp > 0
  1337.               if (dp > 0)
  1338.                  { 
  1339.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1340.                  i = 128*dp / (dist * atten ); 
  1341.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1342.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1343.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1344.                  } // end if
  1345.               //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);
  1346.               } // end if point
  1347.            else
  1348.            if (lights[curr_light].attr & LIGHTV2_ATTR_SPOTLIGHT1) ///////////////////////////////////////
  1349.               {
  1350.               // perform spotlight/point computations simplified model that uses
  1351.               // point light WITH a direction to simulate a spotlight
  1352.               // light model for point light is once again:
  1353.               //              I0point * Clpoint
  1354.               //  I(d)point = ___________________
  1355.               //              kc +  kl*d + kq*d2              
  1356.               //
  1357.               //  Where d = |p - s|
  1358.               // thus it's almost identical to the infinite light, but attenuates as a function
  1359.               // of distance from the point source to the surface point being lit
  1360.               //Write_Error("nentering spotlight1....");
  1361.               // .. normal is already computed
  1362.        
  1363.               // compute vector from surface to light
  1364.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].tpos, &l);
  1365.               // compute distance and attenuation
  1366.               dist = VECTOR4D_Length_Fast2(&l);  
  1367.               // and for the diffuse model
  1368.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1369.               // so we basically need to multiple it all together
  1370.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1371.               // are slower, but the conversion to and from cost cycles
  1372.               // note that I use the direction of the light here rather than a the vector to the light
  1373.               // thus we are taking orientation into account which is similar to the spotlight model
  1374.               // vertex 0
  1375.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].tdir);
  1376.               
  1377.               // only add light if dp > 0
  1378.               if (dp > 0)
  1379.                  { 
  1380.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1381.                  i = 128*dp / ( atten ); 
  1382.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1383.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1384.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1385.                  } // end if
  1386.               // vertex 1
  1387.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].tdir);
  1388.               
  1389.               // only add light if dp > 0
  1390.               if (dp > 0)
  1391.                  { 
  1392.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1393.                  i = 128*dp / ( atten ); 
  1394.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1395.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1396.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1397.                  } // end i
  1398.               // vertex 2
  1399.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].tdir);
  1400.               
  1401.               // only add light if dp > 0
  1402.               if (dp > 0)
  1403.                  { 
  1404.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1405.                  i = 128*dp / ( atten ); 
  1406.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1407.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1408.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1409.                  } // end i
  1410.               //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);
  1411.               } // end if spotlight1
  1412.            else
  1413.            if (lights[curr_light].attr & LIGHTV2_ATTR_SPOTLIGHT2) // simple version //////////////////////////
  1414.               {
  1415.               // perform spot light computations
  1416.               // light model for spot light simple version is once again:
  1417.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  1418.               // I(d)spotlight = __________________________________________      
  1419.               //                 kc + kl*d + kq*d2        
  1420.               // Where d = |p - s|, and pf = power factor
  1421.               // thus it's almost identical to the point, but has the extra term in the numerator
  1422.               // relating the angle between the light source and the point on the surface
  1423.               // .. already have normals and length are 1.0
  1424.              
  1425.               // and for the diffuse model
  1426.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1427.               // so we basically need to multiple it all together
  1428.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1429.               // are slower, but the conversion to and from cost cycles
  1430.               //Write_Error("nEntering spotlight2...");
  1431.               // tons of redundant math here! lots to optimize later!
  1432.               
  1433.               // vertex 0
  1434.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].tdir);
  1435.               
  1436.               // only add light if dp > 0
  1437.               if (dp > 0)
  1438.                  { 
  1439.                  // compute vector from light to surface (different from l which IS the light dir)
  1440.                  VECTOR4D_Build( &lights[curr_light].tpos, &obj->vlist_trans[ vindex_0].v, &s);
  1441.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1442.                  dists = VECTOR4D_Length_Fast2(&s);  
  1443.                  // compute spot light term (s . l)
  1444.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].tdir)/dists;
  1445.                  // proceed only if term is positive
  1446.                  if (dpsl > 0) 
  1447.                     {
  1448.                     // compute attenuation
  1449.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1450.        
  1451.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1452.                     // must be integral
  1453.                     float dpsl_exp = dpsl;
  1454.  
  1455.                     // exponentiate for positive integral powers
  1456.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1457.                          dpsl_exp*=dpsl;
  1458.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1459.                                                       
  1460.                     i = 128*dp * dpsl_exp / ( atten ); 
  1461.                     r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1462.                     g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1463.                     b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1464.   
  1465.                     } // end if
  1466.                  } // end if
  1467.               // vertex 1
  1468.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].tdir);
  1469.               
  1470.               // only add light if dp > 0
  1471.               if (dp > 0)
  1472.                  { 
  1473.                  // compute vector from light to surface (different from l which IS the light dir)
  1474.                  VECTOR4D_Build( &lights[curr_light].tpos, &obj->vlist_trans[ vindex_1].v, &s);
  1475.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1476.                  dists = VECTOR4D_Length_Fast2(&s);  
  1477.                  // compute spot light term (s . l)
  1478.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].tdir)/dists;
  1479.                  // proceed only if term is positive
  1480.                  if (dpsl > 0) 
  1481.                     {
  1482.                     // compute attenuation
  1483.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1484.        
  1485.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1486.                     // must be integral
  1487.                     float dpsl_exp = dpsl;
  1488.  
  1489.                     // exponentiate for positive integral powers
  1490.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1491.                          dpsl_exp*=dpsl;
  1492.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1493.                                                       
  1494.                     i = 128*dp * dpsl_exp / ( atten ); 
  1495.                     r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1496.                     g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1497.                     b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1498.   
  1499.                     } // end if
  1500.                  } // end if
  1501.               // vertex 2
  1502.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].tdir);
  1503.               
  1504.               // only add light if dp > 0
  1505.               if (dp > 0)
  1506.                  { 
  1507.                  // compute vector from light to surface (different from l which IS the light dir)
  1508.                  VECTOR4D_Build( &lights[curr_light].tpos, &obj->vlist_trans[ vindex_2].v, &s);
  1509.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1510.                  dists = VECTOR4D_Length_Fast2(&s);  
  1511.                  // compute spot light term (s . l)
  1512.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].tdir)/dists;
  1513.                  // proceed only if term is positive
  1514.                  if (dpsl > 0) 
  1515.                     {
  1516.                     // compute attenuation
  1517.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1518.        
  1519.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1520.                     // must be integral
  1521.                     float dpsl_exp = dpsl;
  1522.  
  1523.                     // exponentiate for positive integral powers
  1524.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1525.                          dpsl_exp*=dpsl;
  1526.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1527.                                                       
  1528.                     i = 128*dp * dpsl_exp / ( atten ); 
  1529.                     r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1530.                     g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1531.                     b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1532.                     } // end if
  1533.                  } // end if
  1534.               //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);
  1535.               } // end if spot light
  1536.            } // end for light
  1537.   
  1538.        // make sure colors aren't out of range
  1539.        if (r_sum0  > 255) r_sum0 = 255;
  1540.        if (g_sum0  > 255) g_sum0 = 255;
  1541.        if (b_sum0  > 255) b_sum0 = 255;
  1542.        if (r_sum1  > 255) r_sum1 = 255;
  1543.        if (g_sum1  > 255) g_sum1 = 255;
  1544.        if (b_sum1  > 255) b_sum1 = 255;
  1545.        if (r_sum2  > 255) r_sum2 = 255;
  1546.        if (g_sum2  > 255) g_sum2 = 255;
  1547.        if (b_sum2  > 255) b_sum2 = 255;
  1548.        //Write_Error("nwriting color for poly %d", poly);
  1549.        //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);
  1550.        // write the colors
  1551.        curr_poly->lit_color[0] = RGB16Bit(r_sum0, g_sum0, b_sum0);
  1552.        curr_poly->lit_color[1] = RGB16Bit(r_sum1, g_sum1, b_sum1);
  1553.        curr_poly->lit_color[2] = RGB16Bit(r_sum2, g_sum2, b_sum2);
  1554.        } // end if
  1555.     else // assume POLY4DV2_ATTR_SHADE_MODE_CONSTANT
  1556.        {
  1557.        // emmisive shading only, do nothing
  1558.        // ...
  1559.        curr_poly->lit_color[0] = curr_poly->color;
  1560.        //Write_Error("nentering constant shader, and exiting...");
  1561.        } // end if
  1562.     } // end for poly
  1563. // return success
  1564. return(1);
  1565. } // end Light_OBJECT4DV2_World2_16
  1566. ///////////////////////////////////////////////////////////////////////////////
  1567. int Light_OBJECT4DV2_World2(OBJECT4DV2_PTR obj,  // object to process
  1568.                            CAM4DV1_PTR cam,     // camera position
  1569.                            LIGHTV2_PTR lights,  // light list (might have more than one)
  1570.                            int max_lights)      // maximum lights in list
  1571. {
  1572. // {andre work in progress }
  1573. // 8 bit version
  1574. // function lights an object based on the sent lights and camera. the function supports
  1575. // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
  1576. // note that this lighting function is rather brute force and simply follows the math, however
  1577. // there are some clever integer operations that are used in scale 256 rather than going to floating
  1578. // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
  1579. // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
  1580. // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
  1581. // of the falloff due to attenuation, but they still look like spot lights
  1582. // type 2 spot lights are implemented with the intensity having a dot product relationship with the
  1583. // angle from the surface point to the light direction just like in the optimized model, but the pf term
  1584. // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
  1585. // the function works in 8-bit color space, and uses the rgblookup[] to look colors up from RGB values
  1586. // basically, the function converts the 8-bit color index into an RGB value performs the lighting
  1587. // operations and then back into an 8-bit color index with the table, so we loose a little bit during the incoming and 
  1588. // outgoing transformations, however, the next step for optimization will be to make a purely monochromatic
  1589. // 8-bit system that assumes ALL lights are white, but this function works with color for now
  1590. unsigned int r_base, g_base,   b_base,  // base color being lit
  1591.              r_sum,  g_sum,    b_sum,   // sum of lighting process over all lights
  1592.              r_sum0,  g_sum0,  b_sum0,
  1593.              r_sum1,  g_sum1,  b_sum1,
  1594.              r_sum2,  g_sum2,  b_sum2,
  1595.              ri,gi,bi,
  1596.              shaded_color;            // final color
  1597. float dp,     // dot product 
  1598.       dist,   // distance from light to surface
  1599.       dists, 
  1600.       i,      // general intensities
  1601.       nl,     // length of normal
  1602.       atten;  // attenuation computations
  1603. VECTOR4D u, v, n, l, d, s; // used for cross product and light vector calculations
  1604. //Write_Error("nEntering lighting function");
  1605. // test if the object is culled
  1606. if (!(obj->state & OBJECT4DV2_STATE_ACTIVE) ||
  1607.      (obj->state & OBJECT4DV2_STATE_CULLED) ||
  1608.      !(obj->state & OBJECT4DV2_STATE_VISIBLE))
  1609.    return(0); 
  1610. // for each valid poly, light it...
  1611. for (int poly=0; poly < obj->num_polys; poly++)
  1612.     {
  1613.     // acquire polygon
  1614.     POLY4DV2_PTR curr_poly = &obj->plist[poly];
  1615.     // light this polygon if and only if it's not clipped, not culled,
  1616.     // active, and visible
  1617.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  1618.          (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  1619.          (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  1620.        continue; // move onto next poly
  1621.     // set state of polygon to lit, so we don't light again in renderlist
  1622.     // lighting system if it happens to get called
  1623.     SET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
  1624.     // extract vertex indices into master list, rember the polygons are 
  1625.     // NOT self contained, but based on the vertex list stored in the object
  1626.     // itself
  1627.     int vindex_0 = curr_poly->vert[0];
  1628.     int vindex_1 = curr_poly->vert[1];
  1629.     int vindex_2 = curr_poly->vert[2];
  1630.     
  1631.     // we will use the transformed polygon vertex list since the backface removal
  1632.     // only makes sense at the world coord stage further of the pipeline 
  1633.     //Write_Error("npoly %d",poly);
  1634.    
  1635.     // we will use the transformed polygon vertex list since the backface removal
  1636.     // only makes sense at the world coord stage further of the pipeline 
  1637.     // test the lighting mode of the polygon (use flat for flat, gouraud))
  1638.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_FLAT)
  1639.        {
  1640.        //Write_Error("nEntering Flat Shader");
  1641.        // step 1: extract the base color out in RGB mode (it's already in 8 bits per channel)
  1642.        r_base = palette[curr_poly->color].peRed;
  1643.        g_base = palette[curr_poly->color].peGreen;
  1644.        b_base = palette[curr_poly->color].peBlue;
  1645.        //Write_Error("nBase color=%d,%d,%d", r_base, g_base, b_base);
  1646.        // initialize color sum
  1647.        r_sum  = 0;
  1648.        g_sum  = 0;
  1649.        b_sum  = 0;
  1650.        //Write_Error("nsum color=%d,%d,%d", r_sum, g_sum, b_sum);
  1651.        // new optimization:
  1652.        // when there are multiple lights in the system we will end up performing numerous
  1653.        // redundant calculations to minimize this my strategy is to set key variables to 
  1654.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  1655.        // the max value, if they are the max value then the first light that needs the math
  1656.        // will do it, and then save the information into the variable (causing it to change state
  1657.        // from an invalid number) then any other lights that need the math can use the previously
  1658.        // computed value
  1659.        // set surface normal.z to FLT_MAX to flag it as non-computed
  1660.        n.z = FLT_MAX;
  1661.        // loop thru lights
  1662.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  1663.            {
  1664.            // is this light active
  1665.            if (lights[curr_light].state==LIGHTV2_STATE_OFF)
  1666.               continue;
  1667.            //Write_Error("nprocessing light %d",curr_light);
  1668.            // what kind of light are we dealing with
  1669.            if (lights[curr_light].attr & LIGHTV2_ATTR_AMBIENT)
  1670.               {
  1671.               //Write_Error("nEntering ambient light...");
  1672.               // simply multiply each channel against the color of the 
  1673.               // polygon then divide by 256 to scale back to 0..255
  1674.               // use a shift in real life!!! >> 8
  1675.               r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
  1676.               g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
  1677.               b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
  1678.               //Write_Error("nambient sum=%d,%d,%d", r_sum, g_sum, b_sum);
  1679.               // there better only be one ambient light!
  1680.               } // end if
  1681.            else
  1682.            if (lights[curr_light].attr & LIGHTV2_ATTR_INFINITE) ///////////////////////////////////////////
  1683.               {
  1684.               //Write_Error("nEntering infinite light...");
  1685.               // infinite lighting, we need the surface normal, and the direction
  1686.               // of the light source
  1687.               // test if we already computed poly normal in previous calculation
  1688.               if (n.z==FLT_MAX)       
  1689.                  {
  1690.                  // we need to compute the normal of this polygon face, and recall
  1691.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1692.  
  1693.                  // build u, v
  1694.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1695.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1696.                  // compute cross product
  1697.                  VECTOR4D_Cross(&u, &v, &n);
  1698.                  } // end if
  1699.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1700.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1701.               // normals, so this step can be optimized
  1702.               // compute length of normal
  1703.               //nl = VECTOR4D_Length_Fast2(&n);
  1704.               nl = curr_poly->nlength;  
  1705.       
  1706.               // ok, recalling the lighting model for infinite lights
  1707.               // I(d)dir = I0dir * Cldir
  1708.               // and for the diffuse model
  1709.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1710.               // so we basically need to multiple it all together
  1711.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1712.               // are slower, but the conversion to and from cost cycles
  1713.               dp = VECTOR4D_Dot(&n, &lights[curr_light].tdir);
  1714.               
  1715.               // only add light if dp > 0
  1716.               if (dp > 0)
  1717.                  { 
  1718.                  i = 128*dp/nl; 
  1719.                  r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1720.                  g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1721.                  b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1722.                  } // end if
  1723.               //Write_Error("ninfinite sum=%d,%d,%d", r_sum, g_sum, b_sum);
  1724.               } // end if infinite light
  1725.            else
  1726.            if (lights[curr_light].attr & LIGHTV2_ATTR_POINT) ///////////////////////////////////////
  1727.               {
  1728.               //Write_Error("nEntering point light...");
  1729.               // perform point light computations
  1730.               // light model for point light is once again:
  1731.               //              I0point * Clpoint
  1732.               //  I(d)point = ___________________
  1733.               //              kc +  kl*d + kq*d2              
  1734.               //
  1735.               //  Where d = |p - s|
  1736.               // thus it's almost identical to the infinite light, but attenuates as a function
  1737.               // of distance from the point source to the surface point being lit
  1738.               // test if we already computed poly normal in previous calculation
  1739.               if (n.z==FLT_MAX)       
  1740.                  {
  1741.                  // we need to compute the normal of this polygon face, and recall
  1742.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1743.  
  1744.                  // build u, v
  1745.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1746.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1747.                  // compute cross product
  1748.                  VECTOR4D_Cross(&u, &v, &n);
  1749.                  } // end if
  1750.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1751.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1752.               // normals, so this step can be optimized
  1753.               // compute length of normal
  1754.               //nl = VECTOR4D_Length_Fast2(&n);
  1755.               nl = curr_poly->nlength;  
  1756.               // compute vector from surface to light
  1757.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].tpos, &l);
  1758.               // compute distance and attenuation
  1759.               dist = VECTOR4D_Length_Fast2(&l);  
  1760.               // and for the diffuse model
  1761.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1762.               // so we basically need to multiple it all together
  1763.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1764.               // are slower, but the conversion to and from cost cycles
  1765.               dp = VECTOR4D_Dot(&n, &l);
  1766.               
  1767.               // only add light if dp > 0
  1768.               if (dp > 0)
  1769.                  { 
  1770.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1771.                  i = 128*dp / (nl * dist * atten ); 
  1772.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1773.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1774.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1775.                  } // end if
  1776.               //Write_Error("npoint sum=%d,%d,%d",r_sum,g_sum,b_sum);
  1777.               } // end if point
  1778.            else
  1779.            if (lights[curr_light].attr & LIGHTV2_ATTR_SPOTLIGHT1) ////////////////////////////////////
  1780.               {
  1781.               //Write_Error("nentering spot light1...");
  1782.               // perform spotlight/point computations simplified model that uses
  1783.               // point light WITH a direction to simulate a spotlight
  1784.               // light model for point light is once again:
  1785.               //              I0point * Clpoint
  1786.               //  I(d)point = ___________________
  1787.               //              kc +  kl*d + kq*d2              
  1788.               //
  1789.               //  Where d = |p - s|
  1790.               // thus it's almost identical to the infinite light, but attenuates as a function
  1791.               // of distance from the point source to the surface point being lit
  1792.               // test if we already computed poly normal in previous calculation
  1793.               if (n.z==FLT_MAX)       
  1794.                  {
  1795.                  // we need to compute the normal of this polygon face, and recall
  1796.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1797.  
  1798.                  // build u, v
  1799.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1800.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1801.                  // compute cross product
  1802.                  VECTOR4D_Cross(&u, &v, &n);
  1803.                  } // end if
  1804.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1805.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1806.               // normals, so this step can be optimized
  1807.               // compute length of normal
  1808.               //nl = VECTOR4D_Length_Fast2(&n);
  1809.               nl = curr_poly->nlength;  
  1810.        
  1811.               // compute vector from surface to light
  1812.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].tpos, &l);
  1813.               // compute distance and attenuation
  1814.               dist = VECTOR4D_Length_Fast2(&l);  
  1815.               // and for the diffuse model
  1816.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1817.               // so we basically need to multiple it all together
  1818.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1819.               // are slower, but the conversion to and from cost cycles
  1820.               // note that I use the direction of the light here rather than a the vector to the light
  1821.               // thus we are taking orientation into account which is similar to the spotlight model
  1822.               dp = VECTOR4D_Dot(&n, &lights[curr_light].tdir);
  1823.               
  1824.               // only add light if dp > 0
  1825.               if (dp > 0)
  1826.                  { 
  1827.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1828.                  i = 128*dp / (nl * atten ); 
  1829.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1830.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1831.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1832.                  } // end if
  1833.               //Write_Error("nspotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  1834.               } // end if spotlight1
  1835.            else
  1836.            if (lights[curr_light].attr & LIGHTV2_ATTR_SPOTLIGHT2) // simple version ////////////////////
  1837.               {
  1838.               //Write_Error("nEntering spotlight2 ...");
  1839.  
  1840.               // perform spot light computations
  1841.               // light model for spot light simple version is once again:
  1842.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  1843.               // I(d)spotlight = __________________________________________      
  1844.               //                 kc + kl*d + kq*d2        
  1845.               // Where d = |p - s|, and pf = power factor
  1846.               // thus it's almost identical to the point, but has the extra term in the numerator
  1847.               // relating the angle between the light source and the point on the surface
  1848.               // test if we already computed poly normal in previous calculation
  1849.               if (n.z==FLT_MAX)       
  1850.                  {
  1851.                  // we need to compute the normal of this polygon face, and recall
  1852.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1853.  
  1854.                  // build u, v
  1855.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1856.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1857.                  // compute cross product
  1858.                  VECTOR4D_Cross(&u, &v, &n);
  1859.                  } // end if
  1860.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1861.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1862.               // normals, so this step can be optimized
  1863.               // compute length of normal
  1864.               //nl = VECTOR4D_Length_Fast2(&n);
  1865.               nl = curr_poly->nlength;  
  1866.              
  1867.               // and for the diffuse model
  1868.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1869.               // so we basically need to multiple it all together
  1870.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1871.               // are slower, but the conversion to and from cost cycles
  1872.               dp = VECTOR4D_Dot(&n, &lights[curr_light].tdir);
  1873.               
  1874.               // only add light if dp > 0
  1875.               if (dp > 0)
  1876.                  { 
  1877.                  // compute vector from light to surface (different from l which IS the light dir)
  1878.                  VECTOR4D_Build( &lights[curr_light].tpos, &obj->vlist_trans[ vindex_0].v, &s);
  1879.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1880.                  dists = VECTOR4D_Length_Fast2(&s);  
  1881.                  // compute spot light term (s . l)
  1882.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].tdir)/dists;
  1883.                  // proceed only if term is positive
  1884.                  if (dpsl > 0) 
  1885.                     {
  1886.                     // compute attenuation
  1887.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1888.        
  1889.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1890.                     // must be integral
  1891.                     float dpsl_exp = dpsl;
  1892.  
  1893.                     // exponentiate for positive integral powers
  1894.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1895.                          dpsl_exp*=dpsl;
  1896.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1897.                                                       
  1898.                     i = 128*dp * dpsl_exp / (nl * atten ); 
  1899.                     r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1900.                     g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1901.                     b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1902.   
  1903.                     } // end if
  1904.                  } // end if
  1905.               //Write_Error("nSpotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  1906.      
  1907.               } // end if spot light
  1908.            } // end for light
  1909.   
  1910.        // make sure colors aren't out of range
  1911.        if (r_sum  > 255) r_sum = 255;
  1912.        if (g_sum  > 255) g_sum = 255;
  1913.        if (b_sum  > 255) b_sum = 255;
  1914.        //Write_Error("nWriting final values to polygon %d = %d,%d,%d", poly, r_sum, g_sum, b_sum);
  1915.        // write the color over current color
  1916.        curr_poly->lit_color[0] = rgblookup[RGB16Bit565(r_sum, g_sum, b_sum)];
  1917.        } // end if
  1918.     else
  1919.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) /////////////////////////////////
  1920.        {
  1921.        // gouraud shade, unfortunetly at this point in the pipeline, we have lost the original
  1922.        // mesh, and only have triangles, thus, many triangles will share the same vertices and
  1923.        // they will get lit 2x since we don't have any way to tell this, alas, performing lighting
  1924.        // at the object level is a better idea when gouraud shading is performed since the 
  1925.        // commonality of vertices is still intact, in any case, lighting here is similar to polygon
  1926.        // flat shaded, but we do it 3 times, once for each vertex, additionally there are lots
  1927.        // of opportunities for optimization, but I am going to lay off them for now, so the code
  1928.        // is intelligible, later we will optimize
  1929.        //Write_Error("nEntering gouraud shader...");
  1930.        // step 1: extract the base color out in RGB mode (it's already in 8 bits per channel)
  1931.        r_base = palette[curr_poly->color].peRed;
  1932.        g_base = palette[curr_poly->color].peGreen;
  1933.        b_base = palette[curr_poly->color].peBlue;
  1934.        //Write_Error("nBase color=%d, %d, %d", r_base, g_base, b_base);
  1935.        // initialize color sum(s) for vertices
  1936.        r_sum0  = 0;
  1937.        g_sum0  = 0;
  1938.        b_sum0  = 0;
  1939.        r_sum1  = 0;
  1940.        g_sum1  = 0;
  1941.        b_sum1  = 0;
  1942.        r_sum2  = 0;
  1943.        g_sum2  = 0;
  1944.        b_sum2  = 0;
  1945.        //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);
  1946.        // new optimization:
  1947.        // when there are multiple lights in the system we will end up performing numerous
  1948.        // redundant calculations to minimize this my strategy is to set key variables to 
  1949.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  1950.        // the max value, if they are the max value then the first light that needs the math
  1951.        // will do it, and then save the information into the variable (causing it to change state
  1952.        // from an invalid number) then any other lights that need the math can use the previously
  1953.        // computed value
  1954.        // loop thru lights
  1955.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  1956.            {
  1957.            // is this light active
  1958.            if (lights[curr_light].state==LIGHTV2_STATE_OFF)
  1959.               continue;
  1960.            //Write_Error("nprocessing light %d", curr_light);
  1961.            // what kind of light are we dealing with
  1962.            if (lights[curr_light].attr & LIGHTV2_ATTR_AMBIENT) ///////////////////////////////
  1963.               {
  1964.               //Write_Error("nEntering ambient light....");
  1965.               // simply multiply each channel against the color of the 
  1966.               // polygon then divide by 256 to scale back to 0..255
  1967.               // use a shift in real life!!! >> 8
  1968.               ri = ((lights[curr_light].c_ambient.r * r_base) / 256);
  1969.               gi = ((lights[curr_light].c_ambient.g * g_base) / 256);