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

游戏

开发平台:

Visual C++

  1. // T3DLIB7.CPP - low level line and triangle rendering code
  2. // I N C L U D E S ///////////////////////////////////////////////////////////
  3. #define DEBUG_ON
  4. #define WIN32_LEAN_AND_MEAN  
  5. #include <windows.h>   // include important windows stuff
  6. #include <windowsx.h> 
  7. #include <mmsystem.h>
  8. #include <objbase.h>
  9. #include <iostream.h> // include important C/C++ stuff
  10. #include <conio.h>
  11. #include <stdlib.h>
  12. #include <malloc.h>
  13. #include <memory.h>
  14. #include <string.h>
  15. #include <stdarg.h>
  16. #include <stdio.h>
  17. #include <math.h>
  18. #include <io.h>
  19. #include <fcntl.h>
  20. #include <direct.h>
  21. #include <wchar.h>
  22. #include <limits.h>
  23. #include <float.h>
  24. #include <search.h>
  25. #include <ddraw.h>      // needed for defs in T3DLIB1.H 
  26. #include "T3DLIB1.H"
  27. #include "T3DLIB4.H"
  28. #include "T3DLIB5.H"
  29. #include "T3DLIB6.H"
  30. #include "T3DLIB7.H"
  31. // DEFINES //////////////////////////////////////////////////////////////////
  32. // GLOBALS //////////////////////////////////////////////////////////////////
  33. // this array is used to quickly compute the nearest power of 2 for a number from 0-256
  34. // and always rounds down
  35. // basically it's the log (b2) of n, but returns an integer
  36. // logbase2ofx[x] = (int)log2 x, [x:0-512]
  37. UCHAR logbase2ofx[513] = 
  38. {
  39. 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 
  40. 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 
  41. 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 
  42. 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 
  43. 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 
  44. 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 
  45. 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 
  46. 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  47. 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  48. 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  49. 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  50. 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  51. 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  52. 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  53. 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  54. 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  55. }; 
  56. char texture_path[80] = "./"; // root path to ALL textures, make current directory for now
  57. UCHAR rgblightlookup[4096][256]; // rgb 8.12 lighting table lookup
  58. // FUNCTIONS ////////////////////////////////////////////////////////////////
  59. // setting the frame is so important that it should be a member function
  60. int OBJECT4DV2::Set_Frame(int frame)
  61. {
  62. // this functions sets the current frame in a multi frame object
  63. // if the object is not multiframed then the function has no effect
  64. // test if object is valid?
  65. if (!this)
  66.    return(0);
  67. // test if its multiframe?
  68. if (!(this->attr & OBJECT4DV2_ATTR_MULTI_FRAME))
  69.    return(0);
  70. // we have a valid object and its multiframe, vector pointers to frame data
  71. // check bounds on frame
  72. if (frame < 0 )
  73.     frame = 0;
  74. else
  75. if (frame >= this->num_frames)
  76.     frame = this->num_frames - 1;
  77. // set frame
  78. this->curr_frame = frame;
  79. // update pointers to point to "block" of vertices that represent this frame
  80. // the vertices from each frame are 1-1 and onto relative to the polygons that
  81. // make up the object, they are simply shifted, this we need to re-vector the 
  82. // pointers based on the head pointers
  83. this->vlist_local = &(this->head_vlist_local[frame*this->num_vertices]);
  84. this->vlist_trans = &(this->head_vlist_trans[frame*this->num_vertices]);
  85. // return success
  86. return(1);
  87. } // end Set_Frame
  88. ///////////////////////////////////////////////////////////////////////////////
  89. int Set_OBJECT4DV2_Frame(OBJECT4DV2_PTR obj, int frame)
  90. {
  91. // this functions sets the current frame in a multi frame object
  92. // if the object is not multiframed then the function has no effect
  93. // totally external function
  94. // test if object is valid?
  95. if (!obj)
  96.    return(0);
  97. // test if its multiframe?
  98. if (!(obj->attr & OBJECT4DV2_ATTR_MULTI_FRAME))
  99.    return(0);
  100. // we have a valid object and its multiframe, vector pointers to frame data
  101. // check bounds on frame
  102. if (frame < 0 )
  103.     frame = 0;
  104. else
  105. if (frame >= obj->num_frames)
  106.     frame = obj->num_frames - 1;
  107. // set frame
  108. obj->curr_frame = frame;
  109. // update pointers to point to "block" of vertices that represent this frame
  110. // the vertices from each frame are 1-1 and onto relative to the polygons that
  111. // make up the object, they are simply shifted, this we need to re-vector the 
  112. // pointers based on the head pointers
  113. obj->vlist_local = &(obj->head_vlist_local[frame*obj->num_vertices]);
  114. obj->vlist_trans = &(obj->head_vlist_trans[frame*obj->num_vertices]);
  115. // return success
  116. return(1);
  117. } // end Set_Frame
  118. ////////////////////////////////////////////////////////////////////////////////
  119. int Destroy_OBJECT4DV2(OBJECT4DV2_PTR obj)   // object to destroy
  120. {
  121. // this function destroys the sent object, basically frees the memory
  122. // if any that has been allocated
  123. // local vertex list
  124. if (obj->head_vlist_local)
  125.    free(obj->head_vlist_local);
  126. // transformed vertex list
  127. if (obj->head_vlist_trans)
  128.    free(obj->head_vlist_trans);
  129. // texture coordinate list
  130. if (obj->tlist)
  131.    free(obj->tlist);
  132. // polygon list
  133. if (obj->plist)
  134.    free(obj->plist);
  135. // object radii arrays
  136. if (obj->avg_radius)
  137.     free(obj->avg_radius);
  138. if (obj->max_radius)
  139.     free(obj->max_radius);
  140. // now clear out object completely
  141. memset((void *)obj, 0, sizeof(OBJECT4DV2));
  142. // return success
  143. return(1);
  144. } // end Destroy_OBJECT4DV2
  145. ////////////////////////////////////////////////////////////////////////////////
  146. int Init_OBJECT4DV2(OBJECT4DV2_PTR obj,   // object to allocate
  147.                      int _num_vertices, 
  148.                      int _num_polys, 
  149.                      int _num_frames,
  150.                      int destroy)
  151. {
  152. // this function does nothing more than allocate the memory for an OBJECT4DV2
  153. // based on the sent data, later we may want to create more robust initializers
  154. // but the problem is that we don't want to tie the initializer to anthing yet
  155. // in 99% of cases this all will be done by the call to load object
  156. // we just might need this function if we manually want to build an object???
  157. // first destroy the object if it exists
  158. if (destroy)
  159.    Destroy_OBJECT4DV2(obj);
  160. // allocate memory for vertex lists
  161. if (!(obj->vlist_local = (VERTEX4DTV1_PTR)malloc(sizeof(VERTEX4DTV1)*_num_vertices*_num_frames)))
  162.    return(0);
  163. // clear data
  164. memset((void *)obj->vlist_local,0,sizeof(VERTEX4DTV1)*_num_vertices*_num_frames);
  165. if (!(obj->vlist_trans = (VERTEX4DTV1_PTR)malloc(sizeof(VERTEX4DTV1)*_num_vertices*_num_frames)))
  166.    return(0);
  167. // clear data
  168. memset((void *)obj->vlist_trans,0,sizeof(VERTEX4DTV1)*_num_vertices*_num_frames);
  169. // number of texture coordinates always 3*number of polys
  170. if (!(obj->tlist = (POINT2D_PTR)malloc(sizeof(POINT2D)*_num_polys*3)))
  171.    return(0);
  172. // clear data
  173. memset((void *)obj->tlist,0,sizeof(POINT2D)*_num_polys*3);
  174. // allocate memory for radii arrays
  175. if (!(obj->avg_radius = (float *)malloc(sizeof(float)*_num_frames)))
  176.    return(0);
  177. // clear data
  178. memset((void *)obj->avg_radius,0,sizeof(float)*_num_frames);
  179. if (!(obj->max_radius = (float *)malloc(sizeof(float)*_num_frames)))
  180.    return(0);
  181. // clear data
  182. memset((void *)obj->max_radius,0,sizeof(float)*_num_frames);
  183. // allocate memory for polygon list
  184. if (!(obj->plist = (POLY4DV2_PTR)malloc(sizeof(POLY4DV2)*_num_polys)))
  185.    return(0);
  186. // clear data
  187. memset((void *)obj->plist,0,sizeof(POLY4DV2)*_num_polys);
  188. // alias head pointers
  189. obj->head_vlist_local = obj->vlist_local;
  190. obj->head_vlist_trans = obj->vlist_trans;
  191. // set some internal variables
  192. obj->num_frames     = _num_frames;
  193. obj->num_polys      = _num_polys;
  194. obj->num_vertices   = _num_vertices;
  195. obj->total_vertices = _num_vertices*_num_frames;
  196. // return success
  197. return(1);
  198. } // end Init_OBJECT4DV2
  199. //////////////////////////////////////////////////////////////
  200. void Translate_OBJECT4DV2(OBJECT4DV2_PTR obj, VECTOR4D_PTR vt) 
  201. {
  202. // NOTE: Not matrix based
  203. // this function translates an object without matrices,
  204. // simply updates the world_pos
  205. VECTOR4D_Add(&obj->world_pos, vt, &obj->world_pos);
  206. } // end Translate_OBJECT4DV2
  207. /////////////////////////////////////////////////////////////
  208. void Scale_OBJECT4DV2(OBJECT4DV2_PTR obj, VECTOR4D_PTR vs, int all_frames)
  209. {
  210. // NOTE: Not matrix based
  211. // this function scales and object without matrices 
  212. // modifies the object's local vertex list 
  213. // additionally the radii is updated for the object
  214. // for each vertex in the mesh scale the local coordinates by
  215. // vs on a componentwise basis, that is, sx, sy, sz
  216. // also, since the object may hold multiple frames this update
  217. // will ONLY scale the currently selected frame in default mode
  218. // unless all_frames = 1, in essence the function emulates version 1
  219. // for single frame objects
  220. // also, normal lengths will change if the object is scaled!
  221. // so we must scale those too, we simple need to scale the length of
  222. // each normal n*(sx*sy*sz) 
  223. if (!all_frames)
  224.    {
  225.     // perform transform on selected frame only
  226.     for (int vertex=0; vertex < obj->num_vertices; vertex++)
  227.         {
  228.         obj->vlist_local[vertex].x*=vs->x;
  229.         obj->vlist_local[vertex].y*=vs->y;
  230.         obj->vlist_local[vertex].z*=vs->z;
  231.         // leave w unchanged, always equal to 1
  232.         } // end for vertex
  233.         // now since the object is scaled we have to do something with 
  234.         // the radii calculation, but we don't know how the scaling
  235.         // factors relate to the original major axis of the object,
  236.         // therefore for scaling factors all ==1 we will simple multiply
  237.         // which is correct, but for scaling factors not equal to 1, we
  238.         // must take the largest scaling factor and use it to scale the
  239.         // radii with since it's the worst case scenario of the new max and
  240.         // average radii, the ONLY reason we do this is to keep the function
  241.         // fast, you may want to recompute the radii if you need the accuracy
  242.         // find max scaling factor
  243.         float scale = MAX(vs->x, vs->y);
  244.         scale = MAX(scale, vs->z);
  245.         // now scale current frame
  246.         obj->max_radius[obj->curr_frame]*=scale;
  247.         obj->avg_radius[obj->curr_frame]*=scale;
  248.    } // end if
  249. else  // 
  250.    {
  251.     // perform transform
  252.     for (int vertex=0; vertex < obj->total_vertices; vertex++)
  253.         {
  254.         obj->head_vlist_local[vertex].x*=vs->x;
  255.         obj->head_vlist_local[vertex].y*=vs->y;
  256.         obj->head_vlist_local[vertex].z*=vs->z;
  257.         // leave w unchanged, always equal to 1
  258.         } // end for vertex
  259.         // now since the object is scaled we have to do something with 
  260.         // the radii calculation, but we don't know how the scaling
  261.         // factors relate to the original major axis of the object,
  262.         // therefore for scaling factors all ==1 we will simple multiply
  263.         // which is correct, but for scaling factors not equal to 1, we
  264.         // must take the largest scaling factor and use it to scale the
  265.         // radii with since it's the worst case scenario of the new max and
  266.         // average radii,  the ONLY reason we do this is to keep the function
  267.         // fast, you may want to recompute the radii if you need the accuracy
  268.     for (int curr_frame = 0; curr_frame < obj->num_frames; curr_frame++)
  269.         {
  270.         // find max scaling factor
  271.         float scale = MAX(vs->x, vs->y);
  272.         scale = MAX(scale, vs->z);
  273.         // now scale
  274.         obj->max_radius[curr_frame]*=scale;
  275.         obj->avg_radius[curr_frame]*=scale;
  276.         } // for
  277.    } // end else
  278.                       
  279. // now scale the polygon normals
  280. for (int poly=0; poly < obj->num_polys; poly++)
  281.     {
  282.     obj->plist[poly].nlength*=(vs->x * vs->y * vs->z);
  283.     } // end for poly
  284. } // end Scale_OBJECT4DV2
  285. ////////////////////////////////////////////////////////////////////////
  286. void Transform_OBJECT4DV2(OBJECT4DV2_PTR obj,  // object to transform
  287.                           MATRIX4X4_PTR mt,    // transformation matrix
  288.                           int coord_select,    // selects coords to transform
  289.                           int transform_basis, // flags if vector orientation
  290.                                                // should be transformed too
  291.                           int all_frames)      // should all frames be transformed
  292. {
  293. // this function simply transforms all of the vertices in the local or trans
  294. // array by the sent matrix, since the object may have multiple frames, it
  295. // takes that into consideration
  296. // also vertex normals are rotated, however, if there is a translation factor
  297. // in the sent matrix that will corrupt the normals, later we might want to
  298. // null out the last row of the matrix before transforming the normals?
  299. // future optimization: set flag in object attributes, and objects without 
  300. // vertex normals can be rotated without the test in line
  301. // single frame or all frames?
  302. if (!all_frames)
  303. {
  304. // what coordinates should be transformed?
  305. switch(coord_select)
  306.       {
  307.       case TRANSFORM_LOCAL_ONLY:
  308.       {
  309.       // transform each local/model vertex of the object mesh in place
  310.       for (int vertex=0; vertex < obj->num_vertices; vertex++)
  311.           {
  312.           POINT4D presult; // hold result of each transformation
  313.           // transform point
  314.           Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].v, mt, &presult);
  315.           // store result back
  316.           VECTOR4D_COPY(&obj->vlist_local[vertex].v, &presult); 
  317.  
  318.           // transform vertex normal if needed
  319.           if (obj->vlist_local[vertex].attr & VERTEX4DTV1_ATTR_NORMAL)
  320.              {
  321.              // transform normal
  322.              Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].n, mt, &presult);
  323.              // store result back
  324.              VECTOR4D_COPY(&obj->vlist_local[vertex].n, &presult); 
  325.              } // end if
  326.           } // end for index
  327.       } break;
  328.  
  329.       case TRANSFORM_TRANS_ONLY:
  330.       {
  331.       // transform each "transformed" vertex of the object mesh in place
  332.       // remember, the idea of the vlist_trans[] array is to accumulate
  333.       // transformations
  334.       for (int vertex=0; vertex < obj->num_vertices; vertex++)
  335.           {
  336.           POINT4D presult; // hold result of each transformation
  337.           // transform point
  338.           Mat_Mul_VECTOR4D_4X4(&obj->vlist_trans[vertex].v, mt, &presult);
  339.           // store result back
  340.           VECTOR4D_COPY(&obj->vlist_trans[vertex].v, &presult); 
  341.           // transform vertex normal if needed
  342.           if (obj->vlist_trans[vertex].attr & VERTEX4DTV1_ATTR_NORMAL)
  343.              {
  344.              // transform normal
  345.              Mat_Mul_VECTOR4D_4X4(&obj->vlist_trans[vertex].n, mt, &presult);
  346.              // store result back
  347.              VECTOR4D_COPY(&obj->vlist_trans[vertex].n, &presult); 
  348.              } // end if
  349.           } // end for index
  350.       } break;
  351.       case TRANSFORM_LOCAL_TO_TRANS:
  352.       {
  353.       // transform each local/model vertex of the object mesh and store result
  354.       // in "transformed" vertex list
  355.       for (int vertex=0; vertex < obj->num_vertices; vertex++)
  356.           {
  357.           POINT4D presult; // hold result of each transformation
  358.           // transform point
  359.           Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].v, mt, &obj->vlist_trans[vertex].v);
  360.           // transform vertex normal if needed
  361.           if (obj->vlist_local[vertex].attr & VERTEX4DTV1_ATTR_NORMAL)
  362.              {
  363.              // transform point
  364.              Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].n, mt, &obj->vlist_trans[vertex].n);
  365.              } // end if
  366.           } // end for index
  367.       } break;
  368.       default: break;
  369.       } // end switch
  370. } // end if single frame
  371. else // transform all frames
  372. {
  373. // what coordinates should be transformed?
  374. switch(coord_select)
  375.       {
  376.       case TRANSFORM_LOCAL_ONLY:
  377.       {
  378.       // transform each local/model vertex of the object mesh in place
  379.       for (int vertex=0; vertex < obj->total_vertices; vertex++)
  380.           {
  381.           POINT4D presult; // hold result of each transformation
  382.           // transform point
  383.           Mat_Mul_VECTOR4D_4X4(&obj->head_vlist_local[vertex].v, mt, &presult);
  384.           // store result back
  385.           VECTOR4D_COPY(&obj->head_vlist_local[vertex].v, &presult); 
  386.           // transform vertex normal if needed
  387.           if (obj->head_vlist_local[vertex].attr & VERTEX4DTV1_ATTR_NORMAL)
  388.              {
  389.              // transform normal
  390.              Mat_Mul_VECTOR4D_4X4(&obj->head_vlist_local[vertex].n, mt, &presult);
  391.              // store result back
  392.              VECTOR4D_COPY(&obj->head_vlist_local[vertex].n, &presult); 
  393.              } // end if
  394.           } // end for index
  395.       } break;
  396.  
  397.       case TRANSFORM_TRANS_ONLY:
  398.       {
  399.       // transform each "transformed" vertex of the object mesh in place
  400.       // remember, the idea of the vlist_trans[] array is to accumulate
  401.       // transformations
  402.       for (int vertex=0; vertex < obj->total_vertices; vertex++)
  403.           {
  404.           POINT4D presult; // hold result of each transformation
  405.           // transform point
  406.           Mat_Mul_VECTOR4D_4X4(&obj->head_vlist_trans[vertex].v, mt, &presult);
  407.           // store result back
  408.           VECTOR4D_COPY(&obj->head_vlist_trans[vertex].v, &presult); 
  409.           // transform vertex normal if needed
  410.           if (obj->head_vlist_trans[vertex].attr & VERTEX4DTV1_ATTR_NORMAL)
  411.              {
  412.              // transform normal
  413.              Mat_Mul_VECTOR4D_4X4(&obj->head_vlist_trans[vertex].n, mt, &presult);
  414.              // store result back
  415.              VECTOR4D_COPY(&obj->head_vlist_trans[vertex].n, &presult); 
  416.              } // end if
  417.           } // end for index
  418.       } break;
  419.       case TRANSFORM_LOCAL_TO_TRANS:
  420.       {
  421.       // transform each local/model vertex of the object mesh and store result
  422.       // in "transformed" vertex list
  423.       for (int vertex=0; vertex < obj->total_vertices; vertex++)
  424.           {
  425.           POINT4D presult; // hold result of each transformation
  426.           // transform point
  427.           Mat_Mul_VECTOR4D_4X4(&obj->head_vlist_local[vertex].v, mt, &obj->head_vlist_trans[vertex].v);
  428.           // transform vertex normal if needed
  429.           if (obj->head_vlist_local[vertex].attr & VERTEX4DTV1_ATTR_NORMAL)
  430.              {
  431.              // transform point
  432.              Mat_Mul_VECTOR4D_4X4(&obj->head_vlist_local[vertex].n, mt, &obj->head_vlist_trans[vertex].n);
  433.              } // end if
  434.           } // end for index
  435.       } break;
  436.       default: break;
  437.       } // end switch
  438. } // end else multiple frames
  439. // finally, test if transform should be applied to orientation basis
  440. // hopefully this is a rotation, otherwise the basis will get corrupted
  441. if (transform_basis)
  442.    {
  443.    // now rotate orientation basis for object
  444.    VECTOR4D vresult; // use to rotate each orientation vector axis
  445.    // rotate ux of basis
  446.    Mat_Mul_VECTOR4D_4X4(&obj->ux, mt, &vresult);
  447.    VECTOR4D_COPY(&obj->ux, &vresult); 
  448.    // rotate uy of basis
  449.    Mat_Mul_VECTOR4D_4X4(&obj->uy, mt, &vresult);
  450.    VECTOR4D_COPY(&obj->uy, &vresult); 
  451.    // rotate uz of basis
  452.    Mat_Mul_VECTOR4D_4X4(&obj->uz, mt, &vresult);
  453.    VECTOR4D_COPY(&obj->uz, &vresult); 
  454.    } // end if
  455. } // end Transform_OBJECT4DV2
  456. /////////////////////////////////////////////////////////////////////////////////////////
  457. void Rotate_XYZ_OBJECT4DV2(OBJECT4DV2_PTR obj, // object to rotate
  458.                           float theta_x,       // euler angles
  459.                           float theta_y, 
  460.                           float theta_z,
  461.                           int all_frames) // process all frames
  462. {
  463. // this function rotates and object parallel to the
  464. // XYZ axes in that order or a subset thereof, without
  465. // matrices (at least externally sent)
  466. // modifies the object's local vertex list 
  467. // additionally it rotates the unit directional vectors
  468. // that track the objects orientation, also note that each
  469. // time this function is called it calls the rotation generation
  470. // function, this is wastefull if a number of object are being rotated
  471. // by the same matrix, therefore, if that's the case, then generate the
  472. // rotation matrix, store it, and call the general Transform_OBJECT4DV2()
  473. // with the matrix
  474. // also vertex normals are rotated by the matrix
  475. // future optimization: set flag in object attributes, and objects without 
  476. // vertex normals can be rotated without the test in line
  477.  
  478. MATRIX4X4 mrot; // used to store generated rotation matrix
  479. // generate rotation matrix, no way to avoid rotation with a matrix
  480. // too much math to do manually!
  481. Build_XYZ_Rotation_MATRIX4X4(theta_x, theta_y, theta_z, &mrot);
  482. // single or multi frames
  483. if (!all_frames)
  484. {
  485. // now simply rotate each point of the mesh in local/model coordinates
  486. for (int vertex=0; vertex < obj->num_vertices; vertex++)
  487.     {
  488.     POINT4D presult; // hold result of each transformation
  489.     // transform point
  490.     Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].v, &mrot, &presult);
  491.     // store result back
  492.     VECTOR4D_COPY(&obj->vlist_local[vertex].v, &presult); 
  493.     // test for vertex normal
  494.     if (obj->vlist_local[vertex].attr & VERTEX4DTV1_ATTR_NORMAL)
  495.        {
  496.        // transform point
  497.        Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].n, &mrot, &presult);
  498.        // store result back
  499.        VECTOR4D_COPY(&obj->vlist_local[vertex].n, &presult); 
  500.        } // end if
  501.     } // end for index
  502. } // end if single frame
  503. else
  504. { // process all frames
  505. // now simply rotate each point of the mesh in local/model coordinates
  506. for (int vertex=0; vertex < obj->total_vertices; vertex++)
  507.     {
  508.     POINT4D presult; // hold result of each transformation
  509.     // transform point
  510.     Mat_Mul_VECTOR4D_4X4(&obj->head_vlist_local[vertex].v, &mrot, &presult);
  511.     // store result back
  512.     VECTOR4D_COPY(&obj->head_vlist_local[vertex].v, &presult); 
  513.     // test for vertex normal
  514.     if (obj->head_vlist_local[vertex].attr & VERTEX4DTV1_ATTR_NORMAL)
  515.        {
  516.        // transform point
  517.        Mat_Mul_VECTOR4D_4X4(&obj->head_vlist_local[vertex].n, &mrot, &presult);
  518.        // store result back
  519.        VECTOR4D_COPY(&obj->head_vlist_local[vertex].n, &presult); 
  520.        } // end if
  521.     } // end for index
  522. } // end else all frames
  523. // now rotate orientation basis for object
  524. VECTOR4D vresult; // use to rotate each orientation vector axis
  525. // rotate ux of basis
  526. Mat_Mul_VECTOR4D_4X4(&obj->ux, &mrot, &vresult);
  527. VECTOR4D_COPY(&obj->ux, &vresult); 
  528. // rotate uy of basis
  529. Mat_Mul_VECTOR4D_4X4(&obj->uy, &mrot, &vresult);
  530. VECTOR4D_COPY(&obj->uy, &vresult); 
  531. // rotate uz of basis
  532. Mat_Mul_VECTOR4D_4X4(&obj->uz, &mrot, &vresult);
  533. VECTOR4D_COPY(&obj->uz, &vresult); 
  534. } // end Rotate_XYZ_OBJECT4DV2
  535. ////////////////////////////////////////////////////////////
  536. void Model_To_World_OBJECT4DV2(OBJECT4DV2_PTR obj, 
  537.                                int coord_select, 
  538.                                int all_frames)
  539. {
  540. // NOTE: Not matrix based
  541. // this function converts the local model coordinates of the
  542. // sent object into world coordinates, the results are stored
  543. // in the transformed vertex list (vlist_trans) within the object
  544. // interate thru vertex list and transform all the model/local 
  545. // coords to world coords by translating the vertex list by
  546. // the amount world_pos and storing the results in vlist_trans[]
  547. // no need to transform vertex normals, they are invariant of position
  548. if (!all_frames)
  549.    {
  550.    if (coord_select == TRANSFORM_LOCAL_TO_TRANS)
  551.       {
  552.       for (int vertex=0; vertex < obj->num_vertices; vertex++)
  553.           {
  554.           // translate vertex
  555.           VECTOR4D_Add(&obj->vlist_local[vertex].v, &obj->world_pos, &obj->vlist_trans[vertex].v);
  556.           // copy normal
  557.           VECTOR4D_COPY(&obj->vlist_trans[vertex].n, &obj->vlist_local[vertex].n);
  558.           } // end for vertex
  559.       } // end if local
  560.    else
  561.       { // TRANSFORM_TRANS_ONLY
  562.       for (int vertex=0; vertex < obj->num_vertices; vertex++)
  563.           {
  564.           // translate vertex
  565.           VECTOR4D_Add(&obj->vlist_trans[vertex].v, &obj->world_pos, &obj->vlist_trans[vertex].v);
  566.           } // end for vertex
  567.       } // end else trans
  568.     } // end if single frame
  569. else // all frames
  570.    {
  571.    if (coord_select == TRANSFORM_LOCAL_TO_TRANS)
  572.       {
  573.       for (int vertex=0; vertex < obj->total_vertices; vertex++)
  574.           {
  575.           // translate vertex
  576.           VECTOR4D_Add(&obj->head_vlist_local[vertex].v, &obj->world_pos, &obj->head_vlist_trans[vertex].v);
  577.           // copy normal
  578.           VECTOR4D_COPY(&obj->head_vlist_trans[vertex].n, &obj->head_vlist_local[vertex].n);
  579.           } // end for vertex
  580.       } // end if local
  581.    else
  582.       { // TRANSFORM_TRANS_ONLY
  583.       for (int vertex=0; vertex < obj->total_vertices; vertex++)
  584.           {
  585.           // translate vertex
  586.           VECTOR4D_Add(&obj->head_vlist_trans[vertex].v, &obj->world_pos, &obj->head_vlist_trans[vertex].v);
  587.           } // end for vertex
  588.       } // end else trans
  589.     } // end if all frames
  590. } // end Model_To_World_OBJECT4DV2
  591. //////////////////////////////////////////////////////////////////////
  592. int Cull_OBJECT4DV2(OBJECT4DV2_PTR obj,  // object to cull
  593.                     CAM4DV1_PTR cam,     // camera to cull relative to
  594.                      int cull_flags)     // clipping planes to consider
  595. {
  596. // NOTE: is matrix based
  597. // this function culls an entire object from the viewing
  598. // frustrum by using the sent camera information and object
  599. // the cull_flags determine what axes culling should take place
  600. // x, y, z or all which is controlled by ORing the flags
  601. // together
  602. // if the object is culled its state is modified thats all
  603. // this function assumes that both the camera and the object
  604. // are valid!
  605. // also for OBJECT4DV2, only the current frame matters for culling
  606. // step 1: transform the center of the object's bounding
  607. // sphere into camera space
  608. POINT4D sphere_pos; // hold result of transforming center of bounding sphere
  609. // transform point
  610. Mat_Mul_VECTOR4D_4X4(&obj->world_pos, &cam->mcam, &sphere_pos);
  611. // step 2:  based on culling flags remove the object
  612. if (cull_flags & CULL_OBJECT_Z_PLANE)
  613. {
  614. // cull only based on z clipping planes
  615. // test far plane
  616. if ( ((sphere_pos.z - obj->max_radius[obj->curr_frame]) > cam->far_clip_z) ||
  617.      ((sphere_pos.z + obj->max_radius[obj->curr_frame]) < cam->near_clip_z) )
  618.    { 
  619.    SET_BIT(obj->state, OBJECT4DV2_STATE_CULLED);
  620.    return(1);
  621.    } // end if
  622. } // end if
  623. if (cull_flags & CULL_OBJECT_X_PLANE)
  624. {
  625. // cull only based on x clipping planes
  626. // we could use plane equations, but simple similar triangles
  627. // is easier since this is really a 2D problem
  628. // if the view volume is 90 degrees the the problem is trivial
  629. // buts lets assume its not
  630. // test the the right and left clipping planes against the leftmost and rightmost
  631. // points of the bounding sphere
  632. float z_test = (0.5)*cam->viewplane_width*sphere_pos.z/cam->view_dist;
  633. if ( ((sphere_pos.x-obj->max_radius[obj->curr_frame]) > z_test)  || // right side
  634.      ((sphere_pos.x+obj->max_radius[obj->curr_frame]) < -z_test) )  // left side, note sign change
  635.    { 
  636.    SET_BIT(obj->state, OBJECT4DV2_STATE_CULLED);
  637.    return(1);
  638.    } // end if
  639. } // end if
  640. if (cull_flags & CULL_OBJECT_Y_PLANE)
  641. {
  642. // cull only based on y clipping planes
  643. // we could use plane equations, but simple similar triangles
  644. // is easier since this is really a 2D problem
  645. // if the view volume is 90 degrees the the problem is trivial
  646. // buts lets assume its not
  647. // test the the top and bottom clipping planes against the bottommost and topmost
  648. // points of the bounding sphere
  649. float z_test = (0.5)*cam->viewplane_height*sphere_pos.z/cam->view_dist;
  650. if ( ((sphere_pos.y-obj->max_radius[obj->curr_frame]) > z_test)  || // top side
  651.      ((sphere_pos.y+obj->max_radius[obj->curr_frame]) < -z_test) )  // bottom side, note sign change
  652.    { 
  653.    SET_BIT(obj->state, OBJECT4DV2_STATE_CULLED);
  654.    return(1);
  655.    } // end if
  656. } // end if
  657. // return failure to cull
  658. return(0);
  659. } // end Cull_OBJECT4DV2
  660. /////////////////////////////////////////////////////////////////////////////
  661. void Remove_Backfaces_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, CAM4DV1_PTR cam)
  662. {
  663. // NOTE: this is not a matrix based function
  664. // this function removes the backfaces from polygon list
  665. // the function does this based on the polygon list data
  666. // tvlist along with the camera position (only)
  667. // note that only the backface state is set in each polygon
  668. for (int poly = 0; poly < rend_list->num_polys; poly++)
  669.     {
  670.     // acquire current polygon
  671.     POLYF4DV2_PTR curr_poly = rend_list->poly_ptrs[poly];
  672.     // is this polygon valid?
  673.     // test this polygon if and only if it's not clipped, not culled,
  674.     // active, and visible and not 2 sided. Note we test for backface in the event that
  675.     // a previous call might have already determined this, so why work
  676.     // harder!
  677.     if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  678.         (curr_poly->state & POLY4DV2_STATE_CLIPPED ) || 
  679.         (curr_poly->attr  & POLY4DV2_ATTR_2SIDED)    ||
  680.         (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  681.         continue; // move onto next poly
  682.     
  683.         // we need to compute the normal of this polygon face, and recall
  684.         // that the vertices are in cw order, u = p0->p1, v=p0->p2, n=uxv
  685.         VECTOR4D u, v, n;
  686.  
  687.         // build u, v
  688.         VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
  689.         VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
  690.         // compute cross product
  691.         VECTOR4D_Cross(&u, &v, &n);
  692.         // now create eye vector to viewpoint
  693.         VECTOR4D view;
  694.         VECTOR4D_Build(&curr_poly->tvlist[0].v, &cam->pos, &view); 
  695.         // and finally, compute the dot product
  696.         float dp = VECTOR4D_Dot(&n, &view);
  697.         // if the sign is > 0 then visible, 0 = scathing, < 0 invisible
  698.         if (dp <= 0.0 )
  699.             SET_BIT(curr_poly->state, POLY4DV2_STATE_BACKFACE);
  700.          } // end for poly
  701. } // end Remove_Backfaces_RENDERLIST4DV2
  702. ////////////////////////////////////////////////////////////
  703. void Remove_Backfaces_OBJECT4DV2(OBJECT4DV2_PTR obj, CAM4DV1_PTR cam)
  704. {
  705. // NOTE: this is not a matrix based function
  706. // this function removes the backfaces from an object's
  707. // polygon mesh, the function does this based on the vertex
  708. // data in vlist_trans along with the camera position (only)
  709. // note that only the backface state is set in each polygon
  710. // also since this works on polygons the current frame is the frame
  711. // that's vertices are used by the backface cull
  712. // note: only operates on the current frame
  713. // test if the object is culled
  714. if (obj->state & OBJECT4DV2_STATE_CULLED)
  715.    return;
  716. // process each poly in mesh
  717. for (int poly=0; poly < obj->num_polys; poly++)
  718.     {
  719.     // acquire polygon
  720.     POLY4DV2_PTR curr_poly = &obj->plist[poly];
  721.     // is this polygon valid?
  722.     // test this polygon if and only if it's not clipped, not culled,
  723.     // active, and visible and not 2 sided. Note we test for backface in the event that
  724.     // a previous call might have already determined this, so why work
  725.     // harder!
  726.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  727.          (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  728.          (curr_poly->attr  & POLY4DV2_ATTR_2SIDED)    ||
  729.          (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  730.        continue; // move onto next poly
  731.     
  732.     // extract vertex indices into master list, rember the polygons are 
  733.     // NOT self contained, but based on the vertex list stored in the object
  734.     // itself
  735.     int vindex_0 = curr_poly->vert[0];
  736.     int vindex_1 = curr_poly->vert[1];
  737.     int vindex_2 = curr_poly->vert[2];
  738.     
  739.     // we will use the transformed polygon vertex list since the backface removal
  740.     // only makes sense at the world coord stage further of the pipeline 
  741.     // we need to compute the normal of this polygon face, and recall
  742.     // that the vertices are in cw order, u = p0->p1, v=p0->p2, n=uxv
  743.     VECTOR4D u, v, n;
  744.  
  745.     // build u, v
  746.     VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ].v, &obj->vlist_trans[ vindex_1 ].v, &u);
  747.     VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ].v, &obj->vlist_trans[ vindex_2 ].v, &v);
  748.     // compute cross product
  749.     VECTOR4D_Cross(&u, &v, &n);
  750.     // now create eye vector to viewpoint
  751.     VECTOR4D view;
  752.     VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ].v, &cam->pos, &view); 
  753.     // and finally, compute the dot product
  754.     float dp = VECTOR4D_Dot(&n, &view);
  755.     // if the sign is > 0 then visible, 0 = scathing, < 0 invisible
  756.     if (dp <= 0.0 )
  757.        SET_BIT(curr_poly->state, POLY4DV2_STATE_BACKFACE);
  758.     } // end for poly
  759. } // end Remove_Backfaces_OBJECT4DV2
  760. ////////////////////////////////////////////////////////////
  761. void World_To_Camera_OBJECT4DV2(OBJECT4DV2_PTR obj, CAM4DV1_PTR cam)
  762. {
  763. // NOTE: this is a matrix based function
  764. // this function transforms the world coordinates of an object
  765. // into camera coordinates, based on the sent camera matrix
  766. // but it totally disregards the polygons themselves,
  767. // it only works on the vertices in the vlist_trans[] list
  768. // this is one way to do it, you might instead transform
  769. // the global list of polygons in the render list since you 
  770. // are guaranteed that those polys represent geometry that 
  771. // has passed thru backfaces culling (if any)
  772. // note: only operates on the current frame
  773. // transform each vertex in the object to camera coordinates
  774. // assumes the object has already been transformed to world
  775. // coordinates and the result is in vlist_trans[]
  776. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  777.     {
  778.     // transform the vertex by the mcam matrix within the camera
  779.     // it better be valid!
  780.     POINT4D presult; // hold result of each transformation
  781.     // transform point
  782.     Mat_Mul_VECTOR4D_4X4(&obj->vlist_trans[vertex].v, &cam->mcam, &presult);
  783.     // store result back
  784.     VECTOR4D_COPY(&obj->vlist_trans[vertex].v, &presult); 
  785.     } // end for vertex
  786. } // end World_To_Camera_OBJECT4DV2
  787. ////////////////////////////////////////////////////////
  788. void Camera_To_Perspective_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, 
  789.                                           CAM4DV1_PTR cam)
  790. {
  791. // NOTE: this is not a matrix based function
  792. // this function transforms each polygon in the global render list
  793. // into perspective coordinates, based on the 
  794. // sent camera object, 
  795. // you would use this function instead of the object based function
  796. // if you decided earlier in the pipeline to turn each object into 
  797. // a list of polygons and then add them to the global render list
  798. // transform each polygon in the render list into camera coordinates
  799. // assumes the render list has already been transformed to world
  800. // coordinates and the result is in tvlist[] of each polygon object
  801. for (int poly = 0; poly < rend_list->num_polys; poly++)
  802. {
  803. // acquire current polygon
  804. POLYF4DV2_PTR curr_poly = rend_list->poly_ptrs[poly];
  805. // is this polygon valid?
  806. // transform this polygon if and only if it's not clipped, not culled,
  807. // active, and visible, note however the concept of "backface" is 
  808. // irrelevant in a wire frame engine though
  809. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  810.      (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  811.      (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  812.        continue; // move onto next poly
  813. // all good, let's transform 
  814. for (int vertex = 0; vertex < 3; vertex++)
  815.     {
  816.     float z = curr_poly->tvlist[vertex].z;
  817.     // transform the vertex by the view parameters in the camera
  818.     curr_poly->tvlist[vertex].x = cam->view_dist*curr_poly->tvlist[vertex].x/z;
  819.     curr_poly->tvlist[vertex].y = cam->view_dist*curr_poly->tvlist[vertex].y*cam->aspect_ratio/z;
  820.     // z = z, so no change
  821.     // not that we are NOT dividing by the homogenous w coordinate since
  822.     // we are not using a matrix operation for this version of the function 
  823.     } // end for vertex
  824. } // end for poly
  825. } // end Camera_To_Perspective_RENDERLIST4DV2
  826. ////////////////////////////////////////////////////////////////
  827. void Camera_To_Perspective_Screen_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, 
  828.                                                  CAM4DV1_PTR cam)
  829. {
  830. // NOTE: this is not a matrix based function
  831. // this function transforms the camera coordinates of an object
  832. // into Screen scaled perspective coordinates, based on the 
  833. // sent camera object, that is, view_dist_h and view_dist_v 
  834. // should be set to cause the desired (viewport_width X viewport_height)
  835. // it only works on the vertices in the tvlist[] list
  836. // finally, the function also inverts the y axis, so the coordinates
  837. // generated from this function ARE screen coordinates and ready for
  838. // rendering
  839. // transform each polygon in the render list to perspective screen 
  840. // coordinates assumes the render list has already been transformed 
  841. // to camera coordinates and the result is in tvlist[]
  842. for (int poly = 0; poly < rend_list->num_polys; poly++)
  843. {
  844. // acquire current polygon
  845. POLYF4DV2_PTR curr_poly = rend_list->poly_ptrs[poly];
  846. // is this polygon valid?
  847. // transform this polygon if and only if it's not clipped, not culled,
  848. // active, and visible, note however the concept of "backface" is 
  849. // irrelevant in a wire frame engine though
  850. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  851.      (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  852.      (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  853.        continue; // move onto next poly
  854. float alpha = (0.5*cam->viewport_width-0.5);
  855. float beta  = (0.5*cam->viewport_height-0.5);
  856. // all good, let's transform 
  857. for (int vertex = 0; vertex < 3; vertex++)
  858.     {
  859.     float z = curr_poly->tvlist[vertex].z;
  860.     // transform the vertex by the view parameters in the camera
  861.     curr_poly->tvlist[vertex].x = cam->view_dist*curr_poly->tvlist[vertex].x/z;
  862.     curr_poly->tvlist[vertex].y = cam->view_dist*curr_poly->tvlist[vertex].y/z;
  863.     // z = z, so no change
  864.     // not that we are NOT dividing by the homogenous w coordinate since
  865.     // we are not using a matrix operation for this version of the function 
  866.     // now the coordinates are in the range x:(-viewport_width/2 to viewport_width/2)
  867.     // and y:(-viewport_height/2 to viewport_height/2), thus we need a translation and
  868.     // since the y-axis is inverted, we need to invert y to complete the screen 
  869.     // transform:
  870.     curr_poly->tvlist[vertex].x =  curr_poly->tvlist[vertex].x + alpha; 
  871.     curr_poly->tvlist[vertex].y = -curr_poly->tvlist[vertex].y + beta;
  872.     } // end for vertex
  873. } // end for poly
  874. } // end Camera_To_Perspective_Screen_RENDERLIST4DV2
  875. //////////////////////////////////////////////////////////////
  876. void Perspective_To_Screen_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, 
  877.                                           CAM4DV1_PTR cam)
  878. {
  879. // NOTE: this is not a matrix based function
  880. // this function transforms the perspective coordinates of the render
  881. // list into screen coordinates, based on the sent viewport in the camera
  882. // assuming that the viewplane coordinates were normalized
  883. // you would use this function instead of the object based function
  884. // if you decided earlier in the pipeline to turn each object into 
  885. // a list of polygons and then add them to the global render list
  886. // you would only call this function if you previously performed
  887. // a normalized perspective transform
  888. // transform each polygon in the render list from perspective to screen 
  889. // coordinates assumes the render list has already been transformed 
  890. // to normalized perspective coordinates and the result is in tvlist[]
  891. for (int poly = 0; poly < rend_list->num_polys; poly++)
  892. {
  893. // acquire current polygon
  894. POLYF4DV2_PTR curr_poly = rend_list->poly_ptrs[poly];
  895. // is this polygon valid?
  896. // transform this polygon if and only if it's not clipped, not culled,
  897. // active, and visible, note however the concept of "backface" is 
  898. // irrelevant in a wire frame engine though
  899. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  900.      (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  901.      (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  902.        continue; // move onto next poly
  903. float alpha = (0.5*cam->viewport_width-0.5);
  904. float beta  = (0.5*cam->viewport_height-0.5);
  905. // all good, let's transform 
  906. for (int vertex = 0; vertex < 3; vertex++)
  907.     {
  908.     // the vertex is in perspective normalized coords from -1 to 1
  909.     // on each axis, simple scale them and invert y axis and project
  910.     // to screen
  911.     // transform the vertex by the view parameters in the camera
  912.     curr_poly->tvlist[vertex].x = alpha + alpha*curr_poly->tvlist[vertex].x;
  913.     curr_poly->tvlist[vertex].y = beta  - beta *curr_poly->tvlist[vertex].y;
  914.     } // end for vertex
  915. } // end for poly
  916. } // end Perspective_To_Screen_RENDERLIST4DV2
  917. ///////////////////////////////////////////////////////////////
  918. void World_To_Camera_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, 
  919.                                    CAM4DV1_PTR cam)
  920. {
  921. // NOTE: this is a matrix based function
  922. // this function transforms each polygon in the global render list
  923. // to camera coordinates based on the sent camera transform matrix
  924. // you would use this function instead of the object based function
  925. // if you decided earlier in the pipeline to turn each object into 
  926. // a list of polygons and then add them to the global render list
  927. // the conversion of an object into polygons probably would have
  928. // happened after object culling, local transforms, local to world
  929. // and backface culling, so the minimum number of polygons from
  930. // each object are in the list, note that the function assumes
  931. // that at LEAST the local to world transform has been called
  932. // and the polygon data is in the transformed list tvlist of
  933. // the POLYF4DV1 object
  934. // transform each polygon in the render list into camera coordinates
  935. // assumes the render list has already been transformed to world
  936. // coordinates and the result is in tvlist[] of each polygon object
  937. for (int poly = 0; poly < rend_list->num_polys; poly++)
  938. {
  939. // acquire current polygon
  940. POLYF4DV2_PTR curr_poly = rend_list->poly_ptrs[poly];
  941. // is this polygon valid?
  942. // transform this polygon if and only if it's not clipped, not culled,
  943. // active, and visible, note however the concept of "backface" is 
  944. // irrelevant in a wire frame engine though
  945. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  946.      (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  947.      (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  948.        continue; // move onto next poly
  949. // all good, let's transform 
  950. for (int vertex = 0; vertex < 3; vertex++)
  951.     {
  952.     // transform the vertex by the mcam matrix within the camera
  953.     // it better be valid!
  954.     POINT4D presult; // hold result of each transformation
  955.     // transform point
  956.     Mat_Mul_VECTOR4D_4X4(&curr_poly->tvlist[vertex].v, &cam->mcam, &presult);
  957.     // store result back
  958.     VECTOR4D_COPY(&curr_poly->tvlist[vertex].v, &presult); 
  959.     } // end for vertex
  960. } // end for poly
  961. } // end World_To_Camera_RENDERLIST4DV2
  962. ////////////////////////////////////////////////////////////
  963. void Camera_To_Perspective_OBJECT4DV2(OBJECT4DV2_PTR obj, CAM4DV1_PTR cam)
  964. {
  965. // NOTE: this is not a matrix based function
  966. // this function transforms the camera coordinates of an object
  967. // into perspective coordinates, based on the 
  968. // sent camera object, but it totally disregards the polygons themselves,
  969. // it only works on the vertices in the vlist_trans[] list
  970. // this is one way to do it, you might instead transform
  971. // the global list of polygons in the render list since you 
  972. // are guaranteed that those polys represent geometry that 
  973. // has passed thru backfaces culling (if any)
  974. // finally this function is really for experimental reasons only
  975. // you would probably never let an object stay intact this far down
  976. // the pipeline, since it's probably that there's only a single polygon
  977. // that is visible! But this function has to transform the whole mesh!
  978. // note: only operates on the current frame
  979. // transform each vertex in the object to perspective coordinates
  980. // assumes the object has already been transformed to camera
  981. // coordinates and the result is in vlist_trans[]
  982. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  983.     {
  984.     float z = obj->vlist_trans[vertex].z;
  985.     // transform the vertex by the view parameters in the camera
  986.     obj->vlist_trans[vertex].x = cam->view_dist*obj->vlist_trans[vertex].x/z;
  987.     obj->vlist_trans[vertex].y = cam->view_dist*obj->vlist_trans[vertex].y*cam->aspect_ratio/z;
  988.     // z = z, so no change
  989.     // not that we are NOT dividing by the homogenous w coordinate since
  990.     // we are not using a matrix operation for this version of the function 
  991.   
  992.     } // end for vertex
  993. } // end Camera_To_Perspective_OBJECT4DV2
  994. //////////////////////////////////////////////////////////////
  995. void Camera_To_Perspective_Screen_OBJECT4DV2(OBJECT4DV2_PTR obj, CAM4DV1_PTR cam)
  996. {
  997. // NOTE: this is not a matrix based function
  998. // this function transforms the camera coordinates of an object
  999. // into Screen scaled perspective coordinates, based on the 
  1000. // sent camera object, that is, view_dist_h and view_dist_v 
  1001. // should be set to cause the desired (width X height)
  1002. // projection of the vertices, but the function totally 
  1003. // disregards the polygons themselves,
  1004. // it only works on the vertices in the vlist_trans[] list
  1005. // this is one way to do it, you might instead transform
  1006. // the global list of polygons in the render list since you 
  1007. // are guaranteed that those polys represent geometry that 
  1008. // has passed thru backfaces culling (if any)
  1009. // finally this function is really for experimental reasons only
  1010. // you would probably never let an object stay intact this far down
  1011. // the pipeline, since it's probably that there's only a single polygon
  1012. // that is visible! But this function has to transform the whole mesh!
  1013. // finally, the function also inverts the y axis, so the coordinates
  1014. // generated from this function ARE screen coordinates and ready for
  1015. // rendering
  1016. // note: only operates on the current frame
  1017. float alpha = (0.5*cam->viewport_width-0.5);
  1018. float beta  = (0.5*cam->viewport_height-0.5);
  1019. // transform each vertex in the object to perspective screen coordinates
  1020. // assumes the object has already been transformed to camera
  1021. // coordinates and the result is in vlist_trans[]
  1022. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1023.     {
  1024.     float z = obj->vlist_trans[vertex].z;
  1025.     // transform the vertex by the view parameters in the camera
  1026.     obj->vlist_trans[vertex].x = cam->view_dist*obj->vlist_trans[vertex].x/z;
  1027.     obj->vlist_trans[vertex].y = cam->view_dist*obj->vlist_trans[vertex].y/z;
  1028.     // z = z, so no change
  1029.     // not that we are NOT dividing by the homogenous w coordinate since
  1030.     // we are not using a matrix operation for this version of the function 
  1031.     // now the coordinates are in the range x:(-viewport_width/2 to viewport_width/2)
  1032.     // and y:(-viewport_height/2 to viewport_height/2), thus we need a translation and
  1033.     // since the y-axis is inverted, we need to invert y to complete the screen 
  1034.     // transform:
  1035.     obj->vlist_trans[vertex].x =  obj->vlist_trans[vertex].x + alpha;
  1036.     obj->vlist_trans[vertex].y = -obj->vlist_trans[vertex].y + beta;
  1037.     } // end for vertex
  1038. } // end Camera_To_Perspective_Screen_OBJECT4DV2
  1039. //////////////////////////////////////////////////////////////
  1040. void Perspective_To_Screen_OBJECT4DV2(OBJECT4DV2_PTR obj, CAM4DV1_PTR cam)
  1041. {
  1042. // NOTE: this is not a matrix based function
  1043. // this function transforms the perspective coordinates of an object
  1044. // into screen coordinates, based on the sent viewport info
  1045. // but it totally disregards the polygons themselves,
  1046. // it only works on the vertices in the vlist_trans[] list
  1047. // this is one way to do it, you might instead transform
  1048. // the global list of polygons in the render list since you 
  1049. // are guaranteed that those polys represent geometry that 
  1050. // has passed thru backfaces culling (if any)
  1051. // finally this function is really for experimental reasons only
  1052. // you would probably never let an object stay intact this far down
  1053. // the pipeline, since it's probably that there's only a single polygon
  1054. // that is visible! But this function has to transform the whole mesh!
  1055. // this function would be called after a perspective
  1056. // projection was performed on the object
  1057. // transform each vertex in the object to screen coordinates
  1058. // assumes the object has already been transformed to perspective
  1059. // coordinates and the result is in vlist_trans[]
  1060. // note: only operates on the current frame
  1061. float alpha = (0.5*cam->viewport_width-0.5);
  1062. float beta  = (0.5*cam->viewport_height-0.5);
  1063. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1064.     {
  1065.     // assumes the vertex is in perspective normalized coords from -1 to 1
  1066.     // on each axis, simple scale them to viewport and invert y axis and project
  1067.     // to screen
  1068.     // transform the vertex by the view parameters in the camera
  1069.     obj->vlist_trans[vertex].x = alpha + alpha*obj->vlist_trans[vertex].x;
  1070.     obj->vlist_trans[vertex].y = beta  - beta *obj->vlist_trans[vertex].y;
  1071.   
  1072.     } // end for vertex
  1073. } // end Perspective_To_Screen_OBJECT4DV2
  1074. /////////////////////////////////////////////////////////////
  1075. void Convert_From_Homogeneous4D_OBJECT4DV2(OBJECT4DV2_PTR obj)
  1076. {
  1077. // this function convertes all vertices in the transformed
  1078. // vertex list from 4D homogeneous coordinates to normal 3D coordinates
  1079. // by dividing each x,y,z component by w
  1080. // note: only operates on the current frame
  1081. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1082.     {
  1083.     // convert to non-homogenous coords
  1084.     VECTOR4D_DIV_BY_W(&obj->vlist_trans[vertex].v);     
  1085.     } // end for vertex
  1086. } // end Convert_From_Homogeneous4D_OBJECT4DV2
  1087. //////////////////////////////////////////////////////////////////
  1088. int Insert_POLY4DV2_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, 
  1089.                                    POLY4DV2_PTR poly)
  1090. {
  1091. // converts the sent POLY4DV2 into a POLYF4DV2 and inserts it
  1092. // into the render list, this function needs optmizing
  1093. // step 0: are we full?
  1094. if (rend_list->num_polys >= RENDERLIST4DV2_MAX_POLYS)
  1095.    return(0);
  1096. // step 1: copy polygon into next opening in polygon render list
  1097. // point pointer to polygon structure
  1098. rend_list->poly_ptrs[rend_list->num_polys] = &rend_list->poly_data[rend_list->num_polys];
  1099. // copy fields { ??????????? make sure ALL fields are copied, normals, textures, etc!!!  }
  1100. rend_list->poly_data[rend_list->num_polys].state   = poly->state;
  1101. rend_list->poly_data[rend_list->num_polys].attr    = poly->attr;
  1102. rend_list->poly_data[rend_list->num_polys].color   = poly->color;
  1103. rend_list->poly_data[rend_list->num_polys].nlength = poly->nlength;
  1104. rend_list->poly_data[rend_list->num_polys].texture = poly->texture;
  1105. // poly could be lit, so copy these too...
  1106. rend_list->poly_data[rend_list->num_polys].lit_color[0] = poly->lit_color[0];
  1107. rend_list->poly_data[rend_list->num_polys].lit_color[1] = poly->lit_color[1];
  1108. rend_list->poly_data[rend_list->num_polys].lit_color[2] = poly->lit_color[2];
  1109. // now copy vertices, be careful! later put a loop, but for now
  1110. // know there are 3 vertices always!
  1111. VERTEX4DTV1_COPY(&rend_list->poly_data[rend_list->num_polys].tvlist[0],
  1112.               &poly->vlist[poly->vert[0]]);
  1113. VERTEX4DTV1_COPY(&rend_list->poly_data[rend_list->num_polys].tvlist[1],
  1114.               &poly->vlist[poly->vert[1]]);
  1115. VERTEX4DTV1_COPY(&rend_list->poly_data[rend_list->num_polys].tvlist[2],
  1116.               &poly->vlist[poly->vert[2]]);
  1117. // and copy into local vertices too
  1118. VERTEX4DTV1_COPY(&rend_list->poly_data[rend_list->num_polys].vlist[0],
  1119.               &poly->vlist[poly->vert[0]]);
  1120. VERTEX4DTV1_COPY(&rend_list->poly_data[rend_list->num_polys].vlist[1],
  1121.               &poly->vlist[poly->vert[1]]);
  1122. VERTEX4DTV1_COPY(&rend_list->poly_data[rend_list->num_polys].vlist[2],
  1123.               &poly->vlist[poly->vert[2]]);
  1124. // finally the texture coordinates, this has to be performed manually
  1125. // since at this point in the pipeline the vertices do NOT have texture
  1126. // coordinate, the polygons DO, however, now, there are 3 vertices for 
  1127. // EVERY polygon, rather than vertex sharing, so we can copy the texture
  1128. // coordinates out of the indexed arrays into the VERTEX4DTV1 structures
  1129. rend_list->poly_data[rend_list->num_polys].tvlist[0].t = poly->tlist[ poly->text[0] ];
  1130. rend_list->poly_data[rend_list->num_polys].tvlist[1].t = poly->tlist[ poly->text[1] ];
  1131. rend_list->poly_data[rend_list->num_polys].tvlist[2].t = poly->tlist[ poly->text[2] ];
  1132. rend_list->poly_data[rend_list->num_polys].vlist[0].t = poly->tlist[ poly->text[0] ];
  1133. rend_list->poly_data[rend_list->num_polys].vlist[1].t = poly->tlist[ poly->text[1] ];
  1134. rend_list->poly_data[rend_list->num_polys].vlist[2].t = poly->tlist[ poly->text[2] ];
  1135. // now the polygon is loaded into the next free array position, but
  1136. // we need to fix up the links
  1137. // test if this is the first entry
  1138. if (rend_list->num_polys == 0)
  1139.    {
  1140.    // set pointers to null, could loop them around though to self
  1141.    rend_list->poly_data[0].next = NULL;
  1142.    rend_list->poly_data[0].prev = NULL;
  1143.    } // end if
  1144. else
  1145.    {
  1146.    // first set this node to point to previous node and next node (null)
  1147.    rend_list->poly_data[rend_list->num_polys].next = NULL;
  1148.    rend_list->poly_data[rend_list->num_polys].prev = 
  1149.          &rend_list->poly_data[rend_list->num_polys-1];
  1150.    // now set previous node to point to this node
  1151.    rend_list->poly_data[rend_list->num_polys-1].next = 
  1152.           &rend_list->poly_data[rend_list->num_polys];
  1153.    } // end else
  1154. // increment number of polys in list
  1155. rend_list->num_polys++;
  1156. // return successful insertion
  1157. return(1);
  1158. } // end Insert_POLY4DV2_RENDERLIST4DV2
  1159. //////////////////////////////////////////////////////////////
  1160. int Insert_POLYF4DV2_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, 
  1161.                                      POLYF4DV2_PTR poly)
  1162. {
  1163. // inserts the sent polyface POLYF4DV1 into the render list
  1164. // step 0: are we full?
  1165. if (rend_list->num_polys >= RENDERLIST4DV2_MAX_POLYS)
  1166.    return(0);
  1167. // step 1: copy polygon into next opening in polygon render list
  1168. // point pointer to polygon structure
  1169. rend_list->poly_ptrs[rend_list->num_polys] = &rend_list->poly_data[rend_list->num_polys];
  1170. // copy face right into array, thats it!
  1171. memcpy((void *)&rend_list->poly_data[rend_list->num_polys],(void *)poly, sizeof(POLYF4DV2));
  1172. // now the polygon is loaded into the next free array position, but
  1173. // we need to fix up the links
  1174. // test if this is the first entry
  1175. if (rend_list->num_polys == 0)
  1176.    {
  1177.    // set pointers to null, could loop them around though to self
  1178.    rend_list->poly_data[0].next = NULL;
  1179.    rend_list->poly_data[0].prev = NULL;
  1180.    } // end if
  1181. else
  1182.    {
  1183.    // first set this node to point to previous node and next node (null)
  1184.    rend_list->poly_data[rend_list->num_polys].next = NULL;
  1185.    rend_list->poly_data[rend_list->num_polys].prev = 
  1186.          &rend_list->poly_data[rend_list->num_polys-1];
  1187.    // now set previous node to point to this node
  1188.    rend_list->poly_data[rend_list->num_polys-1].next = 
  1189.           &rend_list->poly_data[rend_list->num_polys];
  1190.    } // end else
  1191. // increment number of polys in list
  1192. rend_list->num_polys++;
  1193. // return successful insertion
  1194. return(1);
  1195. } // end Insert_POLYF4DV2_RENDERLIST4DV2
  1196. //////////////////////////////////////////////////////////////
  1197. int Insert_OBJECT4DV2_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, 
  1198.                                       OBJECT4DV2_PTR obj,
  1199.                                       int insert_local=0)
  1200.                                       
  1201. {
  1202. // { andre work in progress, rewrite with materials...}
  1203. // converts the entire object into a face list and then inserts
  1204. // the visible, active, non-clipped, non-culled polygons into
  1205. // the render list, also note the flag insert_local control 
  1206. // whether or not the vlist_local or vlist_trans vertex list
  1207. // is used, thus you can insert an object "raw" totally untranformed
  1208. // if you set insert_local to 1, default is 0, that is you would
  1209. // only insert an object after at least the local to world transform
  1210. // the last parameter is used to control if their has been
  1211. // a lighting step that has generated a light value stored
  1212. // in the upper 16-bits of color, if lighting_on = 1 then
  1213. // this value is used to overwrite the base color of the 
  1214. // polygon when its sent to the rendering list
  1215. unsigned int base_color; // save base color of polygon
  1216. // is this objective inactive or culled or invisible?
  1217. if (!(obj->state & OBJECT4DV2_STATE_ACTIVE) ||
  1218.      (obj->state & OBJECT4DV2_STATE_CULLED) ||
  1219.      !(obj->state & OBJECT4DV2_STATE_VISIBLE))
  1220.    return(0); 
  1221. // the object is valid, let's rip it apart polygon by polygon
  1222. for (int poly = 0; poly < obj->num_polys; poly++)
  1223.     {
  1224.     // acquire polygon
  1225.     POLY4DV2_PTR curr_poly = &obj->plist[poly];
  1226.     // first is this polygon even visible?
  1227.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  1228.          (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  1229.          (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  1230.     continue; // move onto next poly
  1231.     // override vertex list polygon refers to
  1232.     // the case that you want the local coords used
  1233.     // first save old pointer
  1234.     VERTEX4DTV1_PTR vlist_old = curr_poly->vlist;
  1235.     if (insert_local)
  1236.        curr_poly->vlist = obj->vlist_local;
  1237.     else
  1238.        curr_poly->vlist = obj->vlist_trans;
  1239.     // now insert this polygon
  1240.     if (!Insert_POLY4DV2_RENDERLIST4DV2(rend_list, curr_poly))
  1241.        {
  1242.        // fix vertex list pointer
  1243.        curr_poly->vlist = vlist_old;
  1244.               
  1245.        // the whole object didn't fit!
  1246.        return(0);
  1247.        } // end if
  1248.     // fix vertex list pointer
  1249.     curr_poly->vlist = vlist_old;
  1250.     } // end for
  1251. // return success
  1252. return(1);
  1253. } // end Insert_OBJECT4DV2_RENDERLIST4DV2
  1254. /////////////////////////////////////////////////////////////////////
  1255. void Reset_OBJECT4DV2(OBJECT4DV2_PTR obj)
  1256. {
  1257. // this function resets the sent object and redies it for 
  1258. // transformations, basically just resets the culled, clipped and
  1259. // backface flags, but here's where you would add stuff
  1260. // to ready any object for the pipeline
  1261. // the object is valid, let's rip it apart polygon by polygon
  1262. // note: works on the entire object, all frames
  1263. // reset object's culled flag
  1264. RESET_BIT(obj->state, OBJECT4DV2_STATE_CULLED);
  1265. // now the clipped and backface flags for the polygons 
  1266. for (int poly = 0; poly < obj->num_polys; poly++)
  1267.     {
  1268.     // acquire polygon
  1269.     POLY4DV2_PTR curr_poly = &obj->plist[poly];
  1270.     
  1271.     // first is this polygon even visible?
  1272.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE))
  1273.        continue; // move onto next poly
  1274.     // reset clipped and backface flags
  1275.     RESET_BIT(curr_poly->state, POLY4DV2_STATE_CLIPPED);
  1276.     RESET_BIT(curr_poly->state, POLY4DV2_STATE_BACKFACE);
  1277.     RESET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
  1278.     } // end for poly
  1279. } // end Reset_OBJECT4DV2
  1280. //////////////////////////////////////////////////////////////
  1281. void Draw_OBJECT4DV2_Wire(OBJECT4DV2_PTR obj, 
  1282.                           UCHAR *video_buffer, int lpitch)
  1283.                      
  1284. {
  1285. // this function renders an object to the screen in wireframe, 
  1286. // 8 bit mode, it has no regard at all about hidden surface removal, 
  1287. // etc. the function only exists as an easy way to render an object 
  1288. // without converting it into polygons, the function assumes all 
  1289. // coordinates are screen coordinates, but will perform 2D clipping
  1290. // note: only operates on the current frame
  1291. // iterate thru the poly list of the object and simply draw
  1292. // each polygon
  1293. for (int poly=0; poly < obj->num_polys; poly++)
  1294.     {
  1295.     // render this polygon if and only if it's not clipped, not culled,
  1296.     // active, and visible, note however the concecpt of "backface" is 
  1297.     // irrelevant in a wire frame engine though
  1298.     if (!(obj->plist[poly].state & POLY4DV2_STATE_ACTIVE) ||
  1299.          (obj->plist[poly].state & POLY4DV2_STATE_CLIPPED ) ||
  1300.          (obj->plist[poly].state & POLY4DV2_STATE_BACKFACE) )
  1301.        continue; // move onto next poly
  1302.     
  1303.     // extract vertex indices into master list, rember the polygons are 
  1304.     // NOT self contained, but based on the vertex list stored in the object
  1305.     // itself
  1306.     int vindex_0 = obj->plist[poly].vert[0];
  1307.     int vindex_1 = obj->plist[poly].vert[1];
  1308.     int vindex_2 = obj->plist[poly].vert[2];
  1309.     
  1310.     // {andre need material stuff here!!!! }
  1311.     // draw the lines now
  1312.     Draw_Clip_Line(obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y, 
  1313.                 obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y, 
  1314.                 obj->plist[poly].lit_color[0],
  1315.                 video_buffer, lpitch);
  1316.     Draw_Clip_Line(obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y, 
  1317.                 obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y, 
  1318.                 obj->plist[poly].lit_color[0],
  1319.                 video_buffer, lpitch);
  1320.     Draw_Clip_Line(obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y, 
  1321.                 obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y, 
  1322.                 obj->plist[poly].lit_color[0],
  1323.                 video_buffer, lpitch);
  1324. // track rendering stats
  1325. #ifdef DEBUG_ON
  1326. debug_polys_rendered_per_frame++;
  1327. #endif
  1328.     } // end for poly
  1329. } // end Draw_OBJECT4DV2_Wire
  1330. ///////////////////////////////////////////////////////////////
  1331. void Draw_OBJECT4DV2_Wire16(OBJECT4DV2_PTR obj, 
  1332.                             UCHAR *video_buffer, int lpitch)
  1333.                      
  1334. {
  1335. // this function renders an object to the screen in wireframe, 
  1336. // 16 bit mode, it has no regard at all about hidden surface removal, 
  1337. // etc. the function only exists as an easy way to render an object 
  1338. // without converting it into polygons, the function assumes all 
  1339. // coordinates are screen coordinates, but will perform 2D clipping
  1340. // note: only operates on the current frame
  1341. // iterate thru the poly list of the object and simply draw
  1342. // each polygon
  1343. for (int poly=0; poly < obj->num_polys; poly++)
  1344.     {
  1345.     // render this polygon if and only if it's not clipped, not culled,
  1346.     // active, and visible, note however the concecpt of "backface" is 
  1347.     // irrelevant in a wire frame engine though
  1348.     if (!(obj->plist[poly].state & POLY4DV2_STATE_ACTIVE) ||
  1349.          (obj->plist[poly].state & POLY4DV2_STATE_CLIPPED ) ||
  1350.          (obj->plist[poly].state & POLY4DV2_STATE_BACKFACE) )
  1351.        continue; // move onto next poly
  1352.     
  1353.     // extract vertex indices into master list, rember the polygons are 
  1354.     // NOT self contained, but based on the vertex list stored in the object
  1355.     // itself
  1356.     int vindex_0 = obj->plist[poly].vert[0];
  1357.     int vindex_1 = obj->plist[poly].vert[1];
  1358.     int vindex_2 = obj->plist[poly].vert[2];
  1359.     
  1360.     // {andre need material stuff here!!!! }
  1361.     // draw the lines now
  1362.     Draw_Clip_Line16(obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y, 
  1363.                 obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y, 
  1364.                 obj->plist[poly].lit_color[0],
  1365.                 video_buffer, lpitch);
  1366.     Draw_Clip_Line16(obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y, 
  1367.                 obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y, 
  1368.                 obj->plist[poly].lit_color[0],
  1369.                 video_buffer, lpitch);
  1370.     Draw_Clip_Line16(obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y, 
  1371.                 obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y, 
  1372.                 obj->plist[poly].lit_color[0],
  1373.                 video_buffer, lpitch);
  1374. // track rendering stats
  1375. #ifdef DEBUG_ON
  1376. debug_polys_rendered_per_frame++;
  1377. #endif
  1378.     } // end for poly
  1379. } // end Draw_OBJECT4DV2_Wire16
  1380. ///////////////////////////////////////////////////////////////////////////////
  1381. void Sort_RENDERLIST4DV2(RENDERLIST4DV2_PTR rend_list, int sort_method = SORT_POLYLIST_AVGZ)
  1382. {
  1383. // this function sorts the rendering list based on the polygon z-values 
  1384. // the specific sorting method is controlled by sending in control flags
  1385. // #define SORT_POLYLIST_AVGZ  0 - sorts on average of all vertices
  1386. // #define SORT_POLYLIST_NEARZ 1 - sorts on closest z vertex of each poly
  1387. // #define SORT_POLYLIST_FARZ  2 - sorts on farthest z vertex of each poly
  1388. switch(sort_method)
  1389.       {
  1390.       case SORT_POLYLIST_AVGZ:  //  - sorts on average of all vertices
  1391.            {
  1392.            qsort((void *)rend_list->poly_ptrs, rend_list->num_polys, sizeof(POLYF4DV2_PTR), Compare_AvgZ_POLYF4DV2);
  1393.            } break;
  1394.       case SORT_POLYLIST_NEARZ: // - sorts on closest z vertex of each poly
  1395.            {
  1396.            qsort((void *)rend_list->poly_ptrs, rend_list->num_polys, sizeof(POLYF4DV2_PTR), Compare_NearZ_POLYF4DV2);
  1397.            } break;
  1398.       case SORT_POLYLIST_FARZ:  //  - sorts on farthest z vertex of each poly
  1399.            {
  1400.            qsort((void *)rend_list->poly_ptrs, rend_list->num_polys, sizeof(POLYF4DV2_PTR), Compare_FarZ_POLYF4DV2);
  1401.            } break;
  1402.        default: break;
  1403.        } // end switch
  1404. } // end Sort_RENDERLIST4DV2
  1405. ////////////////////////////////////////////////////////////////////////////////
  1406. int Compare_AvgZ_POLYF4DV2(const void *arg1, const void *arg2)
  1407. {
  1408. // this function comapares the average z's of two polygons and is used by the
  1409. // depth sort surface ordering algorithm
  1410. float z1, z2;
  1411. POLYF4DV2_PTR poly_1, poly_2;
  1412. // dereference the poly pointers
  1413. poly_1 = *((POLYF4DV2_PTR *)(arg1));
  1414. poly_2 = *((POLYF4DV2_PTR *)(arg2));
  1415. // compute z average of each polygon
  1416. z1 = (float)0.33333*(poly_1->tvlist[0].z + poly_1->tvlist[1].z + poly_1->tvlist[2].z);
  1417. // now polygon 2
  1418. z2 = (float)0.33333*(poly_2->tvlist[0].z + poly_2->tvlist[1].z + poly_2->tvlist[2].z);
  1419. // compare z1 and z2, such that polys' will be sorted in descending Z order
  1420. if (z1 > z2)
  1421.    return(-1);
  1422. else
  1423. if (z1 < z2)
  1424.    return(1);
  1425. else
  1426.    return(0);
  1427. } // end Compare_AvgZ_POLYF4DV2
  1428. ////////////////////////////////////////////////////////////////////////////////
  1429. int Compare_NearZ_POLYF4DV2(const void *arg1, const void *arg2)
  1430. {
  1431. // this function comapares the closest z's of two polygons and is used by the
  1432. // depth sort surface ordering algorithm
  1433. float z1, z2;
  1434. POLYF4DV2_PTR poly_1, poly_2;
  1435. // dereference the poly pointers
  1436. poly_1 = *((POLYF4DV2_PTR *)(arg1));
  1437. poly_2 = *((POLYF4DV2_PTR *)(arg2));
  1438. // compute the near z of each polygon
  1439. z1 = MIN(poly_1->tvlist[0].z, poly_1->tvlist[1].z);
  1440. z1 = MIN(z1, poly_1->tvlist[2].z);
  1441. z2 = MIN(poly_2->tvlist[0].z, poly_2->tvlist[1].z);
  1442. z2 = MIN(z2, poly_2->tvlist[2].z);
  1443. // compare z1 and z2, such that polys' will be sorted in descending Z order
  1444. if (z1 > z2)
  1445.    return(-1);
  1446. else
  1447. if (z1 < z2)
  1448.    return(1);
  1449. else
  1450.    return(0);
  1451. } // end Compare_NearZ_POLYF4DV2
  1452. ////////////////////////////////////////////////////////////////////////////////
  1453. int Compare_FarZ_POLYF4DV2(const void *arg1, const void *arg2)
  1454. {
  1455. // this function comapares the farthest z's of two polygons and is used by the
  1456. // depth sort surface ordering algorithm
  1457. float z1, z2;
  1458. POLYF4DV2_PTR poly_1, poly_2;
  1459. // dereference the poly pointers
  1460. poly_1 = *((POLYF4DV2_PTR *)(arg1));
  1461. poly_2 = *((POLYF4DV2_PTR *)(arg2));
  1462. // compute the near z of each polygon
  1463. z1 = MAX(poly_1->tvlist[0].z, poly_1->tvlist[1].z);
  1464. z1 = MAX(z1, poly_1->tvlist[2].z);
  1465. z2 = MAX(poly_2->tvlist[0].z, poly_2->tvlist[1].z);
  1466. z2 = MAX(z2, poly_2->tvlist[2].z);
  1467. // compare z1 and z2, such that polys' will be sorted in descending Z order
  1468. if (z1 > z2)
  1469.    return(-1);
  1470. else
  1471. if (z1 < z2)
  1472.    return(1);
  1473. else
  1474.    return(0);
  1475. } // end Compare_FarZ_POLYF4DV2
  1476. ///////////////////////////////////////////////////////////////////////////////
  1477. int Light_OBJECT4DV2_World16(OBJECT4DV2_PTR obj,  // object to process
  1478.                              CAM4DV1_PTR cam,     // camera position
  1479.                              LIGHTV1_PTR lights,  // light list (might have more than one)
  1480.                              int max_lights)      // maximum lights in list
  1481. {
  1482. // {andre work in progress }
  1483. // 16-bit version of function
  1484. // function lights an object based on the sent lights and camera. the function supports
  1485. // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
  1486. // note that this lighting function is rather brute force and simply follows the math, however
  1487. // there are some clever integer operations that are used in scale 256 rather than going to floating
  1488. // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
  1489. // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
  1490. // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
  1491. // of the falloff due to attenuation, but they still look like spot lights
  1492. // type 2 spot lights are implemented with the intensity having a dot product relationship with the
  1493. // angle from the surface point to the light direction just like in the optimized model, but the pf term
  1494. // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
  1495. unsigned int r_base, g_base,   b_base,  // base color being lit
  1496.              r_sum,  g_sum,    b_sum,   // sum of lighting process over all lights
  1497.              r_sum0,  g_sum0,  b_sum0,
  1498.              r_sum1,  g_sum1,  b_sum1,
  1499.              r_sum2,  g_sum2,  b_sum2,
  1500.              ri,gi,bi,
  1501.              shaded_color;            // final color
  1502. float dp,     // dot product 
  1503.       dist,   // distance from light to surface
  1504.       dists, 
  1505.       i,      // general intensities
  1506.       nl,     // length of normal
  1507.       atten;  // attenuation computations
  1508. VECTOR4D u, v, n, l, d, s; // used for cross product and light vector calculations
  1509. //Write_Error("nEntering lighting function");
  1510. // test if the object is culled
  1511. if (!(obj->state & OBJECT4DV2_STATE_ACTIVE) ||
  1512.      (obj->state & OBJECT4DV2_STATE_CULLED) ||
  1513.      !(obj->state & OBJECT4DV2_STATE_VISIBLE))
  1514.    return(0); 
  1515. // for each valid poly, light it...
  1516. for (int poly=0; poly < obj->num_polys; poly++)
  1517.     {
  1518.     // acquire polygon
  1519.     POLY4DV2_PTR curr_poly = &obj->plist[poly];
  1520.     // light this polygon if and only if it's not clipped, not culled,
  1521.     // active, and visible
  1522.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  1523.          (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  1524.          (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  1525.        continue; // move onto next poly
  1526.     // set state of polygon to lit, so we don't light again in renderlist
  1527.     // lighting system if it happens to get called
  1528.     SET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
  1529.     // extract vertex indices into master list, rember the polygons are 
  1530.     // NOT self contained, but based on the vertex list stored in the object
  1531.     // itself
  1532.     int vindex_0 = curr_poly->vert[0];
  1533.     int vindex_1 = curr_poly->vert[1];
  1534.     int vindex_2 = curr_poly->vert[2];
  1535.     
  1536.     // we will use the transformed polygon vertex list since the backface removal
  1537.     // only makes sense at the world coord stage further of the pipeline 
  1538.     //Write_Error("npoly %d",poly);
  1539.    
  1540.     // we will use the transformed polygon vertex list since the backface removal
  1541.     // only makes sense at the world coord stage further of the pipeline 
  1542.     // test the lighting mode of the polygon (use flat for flat, gouraud))
  1543.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_FLAT)
  1544.        {
  1545.        //Write_Error("nEntering Flat Shader");
  1546.        // step 1: extract the base color out in RGB mode, assume RGB 565
  1547.        _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
  1548.        // scale to 8 bit 
  1549.        r_base <<= 3;
  1550.        g_base <<= 2;
  1551.        b_base <<= 3;
  1552.        //Write_Error("nBase color=%d,%d,%d", r_base, g_base, b_base);
  1553.        // initialize color sum
  1554.        r_sum  = 0;
  1555.        g_sum  = 0;
  1556.        b_sum  = 0;
  1557.        //Write_Error("nsum color=%d,%d,%d", r_sum, g_sum, b_sum);
  1558.        // new optimization:
  1559.        // when there are multiple lights in the system we will end up performing numerous
  1560.        // redundant calculations to minimize this my strategy is to set key variables to 
  1561.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  1562.        // the max value, if they are the max value then the first light that needs the math
  1563.        // will do it, and then save the information into the variable (causing it to change state
  1564.        // from an invalid number) then any other lights that need the math can use the previously
  1565.        // computed value
  1566.        
  1567.        // set surface normal.z to FLT_MAX to flag it as non-computed
  1568.        n.z = FLT_MAX;
  1569.        // loop thru lights
  1570.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  1571.            {
  1572.            // is this light active
  1573.            if (lights[curr_light].state==LIGHTV1_STATE_OFF)
  1574.               continue;
  1575.            //Write_Error("nprocessing light %d",curr_light);
  1576.            // what kind of light are we dealing with
  1577.            if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
  1578.               {
  1579.               //Write_Error("nEntering ambient light...");
  1580.               // simply multiply each channel against the color of the 
  1581.               // polygon then divide by 256 to scale back to 0..255
  1582.               // use a shift in real life!!! >> 8
  1583.               r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
  1584.               g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
  1585.               b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
  1586.               //Write_Error("nambient sum=%d,%d,%d", r_sum, g_sum, b_sum);
  1587.               // there better only be one ambient light!
  1588.               } // end if
  1589.            else
  1590.            if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) ///////////////////////////////////////////
  1591.               {
  1592.               //Write_Error("nEntering infinite light...");
  1593.               // infinite lighting, we need the surface normal, and the direction
  1594.               // of the light source
  1595.               // test if we already computed poly normal in previous calculation
  1596.               if (n.z==FLT_MAX)       
  1597.                  {
  1598.                  // we need to compute the normal of this polygon face, and recall
  1599.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1600.  
  1601.                  // build u, v
  1602.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1603.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1604.                  // compute cross product
  1605.                  VECTOR4D_Cross(&u, &v, &n);
  1606.                  } // end if
  1607.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1608.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1609.               // normals, so this step can be optimized
  1610.               // compute length of normal
  1611.               //nl = VECTOR4D_Length_Fast2(&n);
  1612.               nl = curr_poly->nlength;  
  1613.       
  1614.               // ok, recalling the lighting model for infinite lights
  1615.               // I(d)dir = I0dir * Cldir
  1616.               // and for the diffuse model
  1617.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1618.               // so we basically need to multiple it all together
  1619.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1620.               // are slower, but the conversion to and from cost cycles
  1621.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  1622.               
  1623.               // only add light if dp > 0
  1624.               if (dp > 0)
  1625.                  { 
  1626.                  i = 128*dp/nl; 
  1627.                  r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1628.                  g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1629.                  b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1630.                  } // end if
  1631.               //Write_Error("ninfinite sum=%d,%d,%d", r_sum, g_sum, b_sum);
  1632.               } // end if infinite light
  1633.            else
  1634.            if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) ///////////////////////////////////////
  1635.               {
  1636.               //Write_Error("nEntering point light...");
  1637.               // perform point light computations
  1638.               // light model for point light is once again:
  1639.               //              I0point * Clpoint
  1640.               //  I(d)point = ___________________
  1641.               //              kc +  kl*d + kq*d2              
  1642.               //
  1643.               //  Where d = |p - s|
  1644.               // thus it's almost identical to the infinite light, but attenuates as a function
  1645.               // of distance from the point source to the surface point being lit
  1646.               // test if we already computed poly normal in previous calculation
  1647.               if (n.z==FLT_MAX)       
  1648.                  {
  1649.                  // we need to compute the normal of this polygon face, and recall
  1650.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1651.  
  1652.                  // build u, v
  1653.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1654.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1655.                  // compute cross product
  1656.                  VECTOR4D_Cross(&u, &v, &n);
  1657.                  } // end if
  1658.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1659.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1660.               // normals, so this step can be optimized
  1661.               // compute length of normal
  1662.               //nl = VECTOR4D_Length_Fast2(&n);
  1663.               nl = curr_poly->nlength;  
  1664.               // compute vector from surface to light
  1665.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].pos, &l);
  1666.               // compute distance and attenuation
  1667.               dist = VECTOR4D_Length_Fast2(&l);  
  1668.               // and for the diffuse model
  1669.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1670.               // so we basically need to multiple it all together
  1671.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1672.               // are slower, but the conversion to and from cost cycles
  1673.               dp = VECTOR4D_Dot(&n, &l);
  1674.               
  1675.               // only add light if dp > 0
  1676.               if (dp > 0)
  1677.                  { 
  1678.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1679.                  i = 128*dp / (nl * dist * atten ); 
  1680.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1681.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1682.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1683.                  } // end if
  1684.               //Write_Error("npoint sum=%d,%d,%d",r_sum,g_sum,b_sum);
  1685.               } // end if point
  1686.            else
  1687.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ////////////////////////////////////
  1688.               {
  1689.               //Write_Error("nentering spot light1...");
  1690.               // perform spotlight/point computations simplified model that uses
  1691.               // point light WITH a direction to simulate a spotlight
  1692.               // light model for point light is once again:
  1693.               //              I0point * Clpoint
  1694.               //  I(d)point = ___________________
  1695.               //              kc +  kl*d + kq*d2              
  1696.               //
  1697.               //  Where d = |p - s|
  1698.               // thus it's almost identical to the infinite light, but attenuates as a function
  1699.               // of distance from the point source to the surface point being lit
  1700.               // test if we already computed poly normal in previous calculation
  1701.               if (n.z==FLT_MAX)       
  1702.                  {
  1703.                  // we need to compute the normal of this polygon face, and recall
  1704.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1705.  
  1706.                  // build u, v
  1707.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1708.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1709.                  // compute cross product
  1710.                  VECTOR4D_Cross(&u, &v, &n);
  1711.                  } // end if
  1712.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1713.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1714.               // normals, so this step can be optimized
  1715.               // compute length of normal
  1716.               //nl = VECTOR4D_Length_Fast2(&n);
  1717.               nl = curr_poly->nlength;  
  1718.        
  1719.               // compute vector from surface to light
  1720.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].pos, &l);
  1721.               // compute distance and attenuation
  1722.               dist = VECTOR4D_Length_Fast2(&l);  
  1723.               // and for the diffuse model
  1724.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1725.               // so we basically need to multiple it all together
  1726.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1727.               // are slower, but the conversion to and from cost cycles
  1728.               // note that I use the direction of the light here rather than a the vector to the light
  1729.               // thus we are taking orientation into account which is similar to the spotlight model
  1730.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  1731.               
  1732.               // only add light if dp > 0
  1733.               if (dp > 0)
  1734.                  { 
  1735.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1736.                  i = 128*dp / (nl * atten ); 
  1737.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1738.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1739.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1740.                  } // end if
  1741.               //Write_Error("nspotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  1742.               } // end if spotlight1
  1743.            else
  1744.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version ////////////////////
  1745.               {
  1746.               //Write_Error("nEntering spotlight2 ...");
  1747.  
  1748.               // perform spot light computations
  1749.               // light model for spot light simple version is once again:
  1750.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  1751.               // I(d)spotlight = __________________________________________      
  1752.               //                 kc + kl*d + kq*d2        
  1753.               // Where d = |p - s|, and pf = power factor
  1754.               // thus it's almost identical to the point, but has the extra term in the numerator
  1755.               // relating the angle between the light source and the point on the surface
  1756.               // test if we already computed poly normal in previous calculation
  1757.               if (n.z==FLT_MAX)       
  1758.                  {
  1759.                  // we need to compute the normal of this polygon face, and recall
  1760.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  1761.  
  1762.                  // build u, v
  1763.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  1764.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  1765.                  // compute cross product
  1766.                  VECTOR4D_Cross(&u, &v, &n);
  1767.                  } // end if
  1768.               // at this point, we are almost ready, but we have to normalize the normal vector!
  1769.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  1770.               // normals, so this step can be optimized
  1771.               // compute length of normal
  1772.               //nl = VECTOR4D_Length_Fast2(&n);
  1773.               nl = curr_poly->nlength;  
  1774.              
  1775.               // and for the diffuse model
  1776.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1777.               // so we basically need to multiple it all together
  1778.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1779.               // are slower, but the conversion to and from cost cycles
  1780.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  1781.               
  1782.               // only add light if dp > 0
  1783.               if (dp > 0)
  1784.                  { 
  1785.                  // compute vector from light to surface (different from l which IS the light dir)
  1786.                  VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_0].v, &s);
  1787.                  // compute length of s (distance to light source) to normalize s for lighting calc
  1788.                  dists = VECTOR4D_Length_Fast2(&s);  
  1789.                  // compute spot light term (s . l)
  1790.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  1791.                  // proceed only if term is positive
  1792.                  if (dpsl > 0) 
  1793.                     {
  1794.                     // compute attenuation
  1795.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  1796.        
  1797.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  1798.                     // must be integral
  1799.                     float dpsl_exp = dpsl;
  1800.  
  1801.                     // exponentiate for positive integral powers
  1802.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  1803.                          dpsl_exp*=dpsl;
  1804.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  1805.                                                       
  1806.                     i = 128*dp * dpsl_exp / (nl * atten ); 
  1807.                     r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1808.                     g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1809.                     b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1810.   
  1811.                     } // end if
  1812.                  } // end if
  1813.               //Write_Error("nSpotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  1814.      
  1815.               } // end if spot light
  1816.            } // end for light
  1817.   
  1818.        // make sure colors aren't out of range
  1819.        if (r_sum  > 255) r_sum = 255;
  1820.        if (g_sum  > 255) g_sum = 255;
  1821.        if (b_sum  > 255) b_sum = 255;
  1822.        //Write_Error("nWriting final values to polygon %d = %d,%d,%d", poly, r_sum, g_sum, b_sum);
  1823.        // write the color over current color
  1824.        curr_poly->lit_color[0] = RGB16Bit(r_sum, g_sum, b_sum);
  1825.        } // end if
  1826.     else
  1827.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) /////////////////////////////////
  1828.        {
  1829.        // gouraud shade, unfortunetly at this point in the pipeline, we have lost the original
  1830.        // mesh, and only have triangles, thus, many triangles will share the same vertices and
  1831.        // they will get lit 2x since we don't have any way to tell this, alas, performing lighting
  1832.        // at the object level is a better idea when gouraud shading is performed since the 
  1833.        // commonality of vertices is still intact, in any case, lighting here is similar to polygon
  1834.        // flat shaded, but we do it 3 times, once for each vertex, additionally there are lots
  1835.        // of opportunities for optimization, but I am going to lay off them for now, so the code
  1836.        // is intelligible, later we will optimize
  1837.        //Write_Error("nEntering gouraud shader...");
  1838.        // step 1: extract the base color out in RGB mode
  1839.        // assume 565 format
  1840.        _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
  1841.        // scale to 8 bit 
  1842.        r_base <<= 3;
  1843.        g_base <<= 2;
  1844.        b_base <<= 3;
  1845.        //Write_Error("nBase color=%d, %d, %d", r_base, g_base, b_base);
  1846.        // initialize color sum(s) for vertices
  1847.        r_sum0  = 0;
  1848.        g_sum0  = 0;
  1849.        b_sum0  = 0;
  1850.        r_sum1  = 0;
  1851.        g_sum1  = 0;
  1852.        b_sum1  = 0;
  1853.        r_sum2  = 0;
  1854.        g_sum2  = 0;
  1855.        b_sum2  = 0;
  1856.        //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);
  1857.        // new optimization:
  1858.        // when there are multiple lights in the system we will end up performing numerous
  1859.        // redundant calculations to minimize this my strategy is to set key variables to 
  1860.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  1861.        // the max value, if they are the max value then the first light that needs the math
  1862.        // will do it, and then save the information into the variable (causing it to change state
  1863.        // from an invalid number) then any other lights that need the math can use the previously
  1864.        // computed value
  1865.        // loop thru lights
  1866.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  1867.            {
  1868.            // is this light active
  1869.            if (lights[curr_light].state==LIGHTV1_STATE_OFF)
  1870.               continue;
  1871.            //Write_Error("nprocessing light %d", curr_light);
  1872.            // what kind of light are we dealing with
  1873.            if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT) ///////////////////////////////
  1874.               {
  1875.               //Write_Error("nEntering ambient light....");
  1876.               // simply multiply each channel against the color of the 
  1877.               // polygon then divide by 256 to scale back to 0..255
  1878.               // use a shift in real life!!! >> 8
  1879.               ri = ((lights[curr_light].c_ambient.r * r_base) / 256);
  1880.               gi = ((lights[curr_light].c_ambient.g * g_base) / 256);
  1881.               bi = ((lights[curr_light].c_ambient.b * b_base) / 256);
  1882.             
  1883.               // ambient light has the same affect on each vertex
  1884.               r_sum0+=ri;
  1885.               g_sum0+=gi;
  1886.               b_sum0+=bi;
  1887.   
  1888.               r_sum1+=ri;
  1889.               g_sum1+=gi;
  1890.               b_sum1+=bi;
  1891.               
  1892.               r_sum2+=ri;
  1893.               g_sum2+=gi;
  1894.               b_sum2+=bi;
  1895.               // there better only be one ambient light!
  1896.               //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);
  1897.               } // end if
  1898.            else
  1899.            if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) /////////////////////////////////
  1900.               {
  1901.               //Write_Error("nentering infinite light...");
  1902.               // infinite lighting, we need the surface normal, and the direction
  1903.               // of the light source
  1904.               // no longer need to compute normal or length, we already have the vertex normal
  1905.               // and it's length is 1.0  
  1906.               // ....
  1907.       
  1908.               // ok, recalling the lighting model for infinite lights
  1909.               // I(d)dir = I0dir * Cldir
  1910.               // and for the diffuse model
  1911.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1912.               // so we basically need to multiple it all together
  1913.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1914.               // are slower, but the conversion to and from cost cycles
  1915.               // need to perform lighting for each vertex (lots of redundant math, optimize later!)
  1916.               //Write_Error("nv0=[%f, %f, %f]=%f, v1=[%f, %f, %f]=%f, v2=[%f, %f, %f]=%f",
  1917.                 // 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),
  1918.                 // 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),
  1919.                 // 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) );
  1920.               // vertex 0
  1921.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].dir); 
  1922.               
  1923.               // only add light if dp > 0
  1924.               if (dp > 0)
  1925.                  { 
  1926.                  i = 128*dp; 
  1927.                  r_sum0+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1928.                  g_sum0+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1929.                  b_sum0+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1930.                  } // end if
  1931.               // vertex 1
  1932.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].dir);
  1933.               
  1934.               // only add light if dp > 0
  1935.               if (dp > 0)
  1936.                  { 
  1937.                  i = 128*dp; 
  1938.                  r_sum1+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1939.                  g_sum1+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1940.                  b_sum1+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1941.                  } // end if
  1942.               // vertex 2
  1943.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].dir);
  1944.               
  1945.               // only add light if dp > 0
  1946.               if (dp > 0)
  1947.                  { 
  1948.                  i = 128*dp; 
  1949.                  r_sum2+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1950.                  g_sum2+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1951.                  b_sum2+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1952.                  } // end if
  1953.               //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);
  1954.               } // end if infinite light
  1955.            else
  1956.            if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) //////////////////////////////////////
  1957.               {
  1958.               // perform point light computations
  1959.               // light model for point light is once again:
  1960.               //              I0point * Clpoint
  1961.               //  I(d)point = ___________________
  1962.               //              kc +  kl*d + kq*d2              
  1963.               //
  1964.               //  Where d = |p - s|
  1965.               // thus it's almost identical to the infinite light, but attenuates as a function
  1966.               // of distance from the point source to the surface point being lit
  1967.               // .. normal already in vertex
  1968.               //Write_Error("nEntering point light....");
  1969.               // compute vector from surface to light
  1970.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].pos, &l);
  1971.               // compute distance and attenuation
  1972.               dist = VECTOR4D_Length_Fast2(&l);  
  1973.               // and for the diffuse model
  1974.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  1975.               // so we basically need to multiple it all together
  1976.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  1977.               // are slower, but the conversion to and from cost cycles
  1978.               // perform the calculation for all 3 vertices
  1979.               // vertex 0
  1980.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &l);
  1981.               
  1982.               // only add light if dp > 0
  1983.               if (dp > 0)
  1984.                  { 
  1985.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1986.                  i = 128*dp / (dist * atten ); 
  1987.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  1988.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  1989.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  1990.                  } // end if
  1991.               // vertex 1
  1992.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &l);
  1993.               
  1994.               // only add light if dp > 0
  1995.               if (dp > 0)
  1996.                  { 
  1997.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  1998.                  i = 128*dp / (dist * atten ); 
  1999.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2000.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2001.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2002.                  } // end if
  2003.               // vertex 2
  2004.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &l);
  2005.               
  2006.               // only add light if dp > 0
  2007.               if (dp > 0)
  2008.                  { 
  2009.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2010.                  i = 128*dp / (dist * atten ); 
  2011.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2012.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2013.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2014.                  } // end if
  2015.               //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);
  2016.               } // end if point
  2017.            else
  2018.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ///////////////////////////////////////
  2019.               {
  2020.               // perform spotlight/point computations simplified model that uses
  2021.               // point light WITH a direction to simulate a spotlight
  2022.               // light model for point light is once again:
  2023.               //              I0point * Clpoint
  2024.               //  I(d)point = ___________________
  2025.               //              kc +  kl*d + kq*d2              
  2026.               //
  2027.               //  Where d = |p - s|
  2028.               // thus it's almost identical to the infinite light, but attenuates as a function
  2029.               // of distance from the point source to the surface point being lit
  2030.               //Write_Error("nentering spotlight1....");
  2031.               // .. normal is already computed
  2032.        
  2033.               // compute vector from surface to light
  2034.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].pos, &l);
  2035.               // compute distance and attenuation
  2036.               dist = VECTOR4D_Length_Fast2(&l);  
  2037.               // and for the diffuse model
  2038.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2039.               // so we basically need to multiple it all together
  2040.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2041.               // are slower, but the conversion to and from cost cycles
  2042.               // note that I use the direction of the light here rather than a the vector to the light
  2043.               // thus we are taking orientation into account which is similar to the spotlight model
  2044.               // vertex 0
  2045.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].dir);
  2046.               
  2047.               // only add light if dp > 0
  2048.               if (dp > 0)
  2049.                  { 
  2050.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2051.                  i = 128*dp / ( atten ); 
  2052.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2053.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2054.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2055.                  } // end if
  2056.               // vertex 1
  2057.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].dir);
  2058.               
  2059.               // only add light if dp > 0
  2060.               if (dp > 0)
  2061.                  { 
  2062.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2063.                  i = 128*dp / ( atten ); 
  2064.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2065.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2066.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2067.                  } // end i
  2068.               // vertex 2
  2069.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].dir);
  2070.               
  2071.               // only add light if dp > 0
  2072.               if (dp > 0)
  2073.                  { 
  2074.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2075.                  i = 128*dp / ( atten ); 
  2076.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2077.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2078.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2079.                  } // end i
  2080.               //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);
  2081.               } // end if spotlight1
  2082.            else
  2083.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version //////////////////////////
  2084.               {
  2085.               // perform spot light computations
  2086.               // light model for spot light simple version is once again:
  2087.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  2088.               // I(d)spotlight = __________________________________________      
  2089.               //                 kc + kl*d + kq*d2        
  2090.               // Where d = |p - s|, and pf = power factor
  2091.               // thus it's almost identical to the point, but has the extra term in the numerator
  2092.               // relating the angle between the light source and the point on the surface
  2093.               // .. already have normals and length are 1.0
  2094.              
  2095.               // and for the diffuse model
  2096.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2097.               // so we basically need to multiple it all together
  2098.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2099.               // are slower, but the conversion to and from cost cycles
  2100.               //Write_Error("nEntering spotlight2...");
  2101.               // tons of redundant math here! lots to optimize later!
  2102.               
  2103.               // vertex 0
  2104.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].dir);
  2105.               
  2106.               // only add light if dp > 0
  2107.               if (dp > 0)
  2108.                  { 
  2109.                  // compute vector from light to surface (different from l which IS the light dir)
  2110.                  VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_0].v, &s);
  2111.                  // compute length of s (distance to light source) to normalize s for lighting calc
  2112.                  dists = VECTOR4D_Length_Fast2(&s);  
  2113.                  // compute spot light term (s . l)
  2114.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  2115.                  // proceed only if term is positive
  2116.                  if (dpsl > 0) 
  2117.                     {
  2118.                     // compute attenuation
  2119.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  2120.        
  2121.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  2122.                     // must be integral
  2123.                     float dpsl_exp = dpsl;
  2124.  
  2125.                     // exponentiate for positive integral powers
  2126.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  2127.                          dpsl_exp*=dpsl;
  2128.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  2129.                                                       
  2130.                     i = 128*dp * dpsl_exp / ( atten ); 
  2131.                     r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2132.                     g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2133.                     b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2134.   
  2135.                     } // end if
  2136.                  } // end if
  2137.               // vertex 1
  2138.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].dir);
  2139.               
  2140.               // only add light if dp > 0
  2141.               if (dp > 0)
  2142.                  { 
  2143.                  // compute vector from light to surface (different from l which IS the light dir)
  2144.                  VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_1].v, &s);
  2145.                  // compute length of s (distance to light source) to normalize s for lighting calc
  2146.                  dists = VECTOR4D_Length_Fast2(&s);  
  2147.                  // compute spot light term (s . l)
  2148.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  2149.                  // proceed only if term is positive
  2150.                  if (dpsl > 0) 
  2151.                     {
  2152.                     // compute attenuation
  2153.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  2154.        
  2155.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  2156.                     // must be integral
  2157.                     float dpsl_exp = dpsl;
  2158.  
  2159.                     // exponentiate for positive integral powers
  2160.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  2161.                          dpsl_exp*=dpsl;
  2162.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  2163.                                                       
  2164.                     i = 128*dp * dpsl_exp / ( atten ); 
  2165.                     r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2166.                     g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2167.                     b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2168.   
  2169.                     } // end if
  2170.                  } // end if
  2171.               // vertex 2
  2172.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].dir);
  2173.               
  2174.               // only add light if dp > 0
  2175.               if (dp > 0)
  2176.                  { 
  2177.                  // compute vector from light to surface (different from l which IS the light dir)
  2178.                  VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_2].v, &s);
  2179.                  // compute length of s (distance to light source) to normalize s for lighting calc
  2180.                  dists = VECTOR4D_Length_Fast2(&s);  
  2181.                  // compute spot light term (s . l)
  2182.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  2183.                  // proceed only if term is positive
  2184.                  if (dpsl > 0) 
  2185.                     {
  2186.                     // compute attenuation
  2187.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  2188.        
  2189.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  2190.                     // must be integral
  2191.                     float dpsl_exp = dpsl;
  2192.  
  2193.                     // exponentiate for positive integral powers
  2194.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  2195.                          dpsl_exp*=dpsl;
  2196.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  2197.                                                       
  2198.                     i = 128*dp * dpsl_exp / ( atten ); 
  2199.                     r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2200.                     g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2201.                     b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2202.                     } // end if
  2203.                  } // end if
  2204.               //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);
  2205.               } // end if spot light
  2206.            } // end for light
  2207.   
  2208.        // make sure colors aren't out of range
  2209.        if (r_sum0  > 255) r_sum0 = 255;
  2210.        if (g_sum0  > 255) g_sum0 = 255;
  2211.        if (b_sum0  > 255) b_sum0 = 255;
  2212.        if (r_sum1  > 255) r_sum1 = 255;
  2213.        if (g_sum1  > 255) g_sum1 = 255;
  2214.        if (b_sum1  > 255) b_sum1 = 255;
  2215.        if (r_sum2  > 255) r_sum2 = 255;
  2216.        if (g_sum2  > 255) g_sum2 = 255;
  2217.        if (b_sum2  > 255) b_sum2 = 255;
  2218.        //Write_Error("nwriting color for poly %d", poly);
  2219.        //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);
  2220.        // write the colors
  2221.        curr_poly->lit_color[0] = RGB16Bit(r_sum0, g_sum0, b_sum0);
  2222.        curr_poly->lit_color[1] = RGB16Bit(r_sum1, g_sum1, b_sum1);
  2223.        curr_poly->lit_color[2] = RGB16Bit(r_sum2, g_sum2, b_sum2);
  2224.        } // end if
  2225.     else // assume POLY4DV2_ATTR_SHADE_MODE_CONSTANT
  2226.        {
  2227.        // emmisive shading only, do nothing
  2228.        // ...
  2229.        curr_poly->lit_color[0] = curr_poly->color;
  2230.        //Write_Error("nentering constant shader, and exiting...");
  2231.        } // end if
  2232.     } // end for poly
  2233. // return success
  2234. return(1);
  2235. } // end Light_OBJECT4DV2_World16
  2236. ///////////////////////////////////////////////////////////////////////////////
  2237. int Light_OBJECT4DV2_World(OBJECT4DV2_PTR obj,  // object to process
  2238.                            CAM4DV1_PTR cam,     // camera position
  2239.                            LIGHTV1_PTR lights,  // light list (might have more than one)
  2240.                            int max_lights)      // maximum lights in list
  2241. {
  2242. // {andre work in progress }
  2243. // 8 bit version
  2244. // function lights an object based on the sent lights and camera. the function supports
  2245. // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
  2246. // note that this lighting function is rather brute force and simply follows the math, however
  2247. // there are some clever integer operations that are used in scale 256 rather than going to floating
  2248. // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
  2249. // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
  2250. // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
  2251. // of the falloff due to attenuation, but they still look like spot lights
  2252. // type 2 spot lights are implemented with the intensity having a dot product relationship with the
  2253. // angle from the surface point to the light direction just like in the optimized model, but the pf term
  2254. // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
  2255. // the function works in 8-bit color space, and uses the rgblookup[] to look colors up from RGB values
  2256. // basically, the function converts the 8-bit color index into an RGB value performs the lighting
  2257. // operations and then back into an 8-bit color index with the table, so we loose a little bit during the incoming and 
  2258. // outgoing transformations, however, the next step for optimization will be to make a purely monochromatic
  2259. // 8-bit system that assumes ALL lights are white, but this function works with color for now
  2260. unsigned int r_base, g_base,   b_base,  // base color being lit
  2261.              r_sum,  g_sum,    b_sum,   // sum of lighting process over all lights
  2262.              r_sum0,  g_sum0,  b_sum0,
  2263.              r_sum1,  g_sum1,  b_sum1,
  2264.              r_sum2,  g_sum2,  b_sum2,
  2265.              ri,gi,bi,
  2266.              shaded_color;            // final color
  2267. float dp,     // dot product 
  2268.       dist,   // distance from light to surface
  2269.       dists, 
  2270.       i,      // general intensities
  2271.       nl,     // length of normal
  2272.       atten;  // attenuation computations
  2273. VECTOR4D u, v, n, l, d, s; // used for cross product and light vector calculations
  2274. //Write_Error("nEntering lighting function");
  2275. // test if the object is culled
  2276. if (!(obj->state & OBJECT4DV2_STATE_ACTIVE) ||
  2277.      (obj->state & OBJECT4DV2_STATE_CULLED) ||
  2278.      !(obj->state & OBJECT4DV2_STATE_VISIBLE))
  2279.    return(0); 
  2280. // for each valid poly, light it...
  2281. for (int poly=0; poly < obj->num_polys; poly++)
  2282.     {
  2283.     // acquire polygon
  2284.     POLY4DV2_PTR curr_poly = &obj->plist[poly];
  2285.     // light this polygon if and only if it's not clipped, not culled,
  2286.     // active, and visible
  2287.     if (!(curr_poly->state & POLY4DV2_STATE_ACTIVE) ||
  2288.          (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
  2289.          (curr_poly->state & POLY4DV2_STATE_BACKFACE) )
  2290.        continue; // move onto next poly
  2291.     // set state of polygon to lit, so we don't light again in renderlist
  2292.     // lighting system if it happens to get called
  2293.     SET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
  2294.     // extract vertex indices into master list, rember the polygons are 
  2295.     // NOT self contained, but based on the vertex list stored in the object
  2296.     // itself
  2297.     int vindex_0 = curr_poly->vert[0];
  2298.     int vindex_1 = curr_poly->vert[1];
  2299.     int vindex_2 = curr_poly->vert[2];
  2300.     
  2301.     // we will use the transformed polygon vertex list since the backface removal
  2302.     // only makes sense at the world coord stage further of the pipeline 
  2303.     //Write_Error("npoly %d",poly);
  2304.    
  2305.     // we will use the transformed polygon vertex list since the backface removal
  2306.     // only makes sense at the world coord stage further of the pipeline 
  2307.     // test the lighting mode of the polygon (use flat for flat, gouraud))
  2308.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_FLAT)
  2309.        {
  2310.        //Write_Error("nEntering Flat Shader");
  2311.        // step 1: extract the base color out in RGB mode (it's already in 8 bits per channel)
  2312.        r_base = palette[curr_poly->color].peRed;
  2313.        g_base = palette[curr_poly->color].peGreen;
  2314.        b_base = palette[curr_poly->color].peBlue;
  2315.        //Write_Error("nBase color=%d,%d,%d", r_base, g_base, b_base);
  2316.        // initialize color sum
  2317.        r_sum  = 0;
  2318.        g_sum  = 0;
  2319.        b_sum  = 0;
  2320.        //Write_Error("nsum color=%d,%d,%d", r_sum, g_sum, b_sum);
  2321.        // new optimization:
  2322.        // when there are multiple lights in the system we will end up performing numerous
  2323.        // redundant calculations to minimize this my strategy is to set key variables to 
  2324.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  2325.        // the max value, if they are the max value then the first light that needs the math
  2326.        // will do it, and then save the information into the variable (causing it to change state
  2327.        // from an invalid number) then any other lights that need the math can use the previously
  2328.        // computed value
  2329.        // set surface normal.z to FLT_MAX to flag it as non-computed
  2330.        n.z = FLT_MAX;
  2331.        // loop thru lights
  2332.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  2333.            {
  2334.            // is this light active
  2335.            if (lights[curr_light].state==LIGHTV1_STATE_OFF)
  2336.               continue;
  2337.            //Write_Error("nprocessing light %d",curr_light);
  2338.            // what kind of light are we dealing with
  2339.            if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
  2340.               {
  2341.               //Write_Error("nEntering ambient light...");
  2342.               // simply multiply each channel against the color of the 
  2343.               // polygon then divide by 256 to scale back to 0..255
  2344.               // use a shift in real life!!! >> 8
  2345.               r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
  2346.               g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
  2347.               b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
  2348.               //Write_Error("nambient sum=%d,%d,%d", r_sum, g_sum, b_sum);
  2349.               // there better only be one ambient light!
  2350.               } // end if
  2351.            else
  2352.            if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) ///////////////////////////////////////////
  2353.               {
  2354.               //Write_Error("nEntering infinite light...");
  2355.               // infinite lighting, we need the surface normal, and the direction
  2356.               // of the light source
  2357.               // test if we already computed poly normal in previous calculation
  2358.               if (n.z==FLT_MAX)       
  2359.                  {
  2360.                  // we need to compute the normal of this polygon face, and recall
  2361.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  2362.  
  2363.                  // build u, v
  2364.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  2365.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  2366.                  // compute cross product
  2367.                  VECTOR4D_Cross(&u, &v, &n);
  2368.                  } // end if
  2369.               // at this point, we are almost ready, but we have to normalize the normal vector!
  2370.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  2371.               // normals, so this step can be optimized
  2372.               // compute length of normal
  2373.               //nl = VECTOR4D_Length_Fast2(&n);
  2374.               nl = curr_poly->nlength;  
  2375.       
  2376.               // ok, recalling the lighting model for infinite lights
  2377.               // I(d)dir = I0dir * Cldir
  2378.               // and for the diffuse model
  2379.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2380.               // so we basically need to multiple it all together
  2381.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2382.               // are slower, but the conversion to and from cost cycles
  2383.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  2384.               
  2385.               // only add light if dp > 0
  2386.               if (dp > 0)
  2387.                  { 
  2388.                  i = 128*dp/nl; 
  2389.                  r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2390.                  g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2391.                  b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2392.                  } // end if
  2393.               //Write_Error("ninfinite sum=%d,%d,%d", r_sum, g_sum, b_sum);
  2394.               } // end if infinite light
  2395.            else
  2396.            if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) ///////////////////////////////////////
  2397.               {
  2398.               //Write_Error("nEntering point light...");
  2399.               // perform point light computations
  2400.               // light model for point light is once again:
  2401.               //              I0point * Clpoint
  2402.               //  I(d)point = ___________________
  2403.               //              kc +  kl*d + kq*d2              
  2404.               //
  2405.               //  Where d = |p - s|
  2406.               // thus it's almost identical to the infinite light, but attenuates as a function
  2407.               // of distance from the point source to the surface point being lit
  2408.               // test if we already computed poly normal in previous calculation
  2409.               if (n.z==FLT_MAX)       
  2410.                  {
  2411.                  // we need to compute the normal of this polygon face, and recall
  2412.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  2413.  
  2414.                  // build u, v
  2415.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  2416.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  2417.                  // compute cross product
  2418.                  VECTOR4D_Cross(&u, &v, &n);
  2419.                  } // end if
  2420.               // at this point, we are almost ready, but we have to normalize the normal vector!
  2421.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  2422.               // normals, so this step can be optimized
  2423.               // compute length of normal
  2424.               //nl = VECTOR4D_Length_Fast2(&n);
  2425.               nl = curr_poly->nlength;  
  2426.               // compute vector from surface to light
  2427.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].pos, &l);
  2428.               // compute distance and attenuation
  2429.               dist = VECTOR4D_Length_Fast2(&l);  
  2430.               // and for the diffuse model
  2431.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2432.               // so we basically need to multiple it all together
  2433.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2434.               // are slower, but the conversion to and from cost cycles
  2435.               dp = VECTOR4D_Dot(&n, &l);
  2436.               
  2437.               // only add light if dp > 0
  2438.               if (dp > 0)
  2439.                  { 
  2440.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2441.                  i = 128*dp / (nl * dist * atten ); 
  2442.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2443.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2444.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2445.                  } // end if
  2446.               //Write_Error("npoint sum=%d,%d,%d",r_sum,g_sum,b_sum);
  2447.               } // end if point
  2448.            else
  2449.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ////////////////////////////////////
  2450.               {
  2451.               //Write_Error("nentering spot light1...");
  2452.               // perform spotlight/point computations simplified model that uses
  2453.               // point light WITH a direction to simulate a spotlight
  2454.               // light model for point light is once again:
  2455.               //              I0point * Clpoint
  2456.               //  I(d)point = ___________________
  2457.               //              kc +  kl*d + kq*d2              
  2458.               //
  2459.               //  Where d = |p - s|
  2460.               // thus it's almost identical to the infinite light, but attenuates as a function
  2461.               // of distance from the point source to the surface point being lit
  2462.               // test if we already computed poly normal in previous calculation
  2463.               if (n.z==FLT_MAX)       
  2464.                  {
  2465.                  // we need to compute the normal of this polygon face, and recall
  2466.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  2467.  
  2468.                  // build u, v
  2469.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  2470.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  2471.                  // compute cross product
  2472.                  VECTOR4D_Cross(&u, &v, &n);
  2473.                  } // end if
  2474.               // at this point, we are almost ready, but we have to normalize the normal vector!
  2475.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  2476.               // normals, so this step can be optimized
  2477.               // compute length of normal
  2478.               //nl = VECTOR4D_Length_Fast2(&n);
  2479.               nl = curr_poly->nlength;  
  2480.        
  2481.               // compute vector from surface to light
  2482.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].pos, &l);
  2483.               // compute distance and attenuation
  2484.               dist = VECTOR4D_Length_Fast2(&l);  
  2485.               // and for the diffuse model
  2486.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2487.               // so we basically need to multiple it all together
  2488.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2489.               // are slower, but the conversion to and from cost cycles
  2490.               // note that I use the direction of the light here rather than a the vector to the light
  2491.               // thus we are taking orientation into account which is similar to the spotlight model
  2492.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  2493.               
  2494.               // only add light if dp > 0
  2495.               if (dp > 0)
  2496.                  { 
  2497.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2498.                  i = 128*dp / (nl * atten ); 
  2499.                  r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2500.                  g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2501.                  b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2502.                  } // end if
  2503.               //Write_Error("nspotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  2504.               } // end if spotlight1
  2505.            else
  2506.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version ////////////////////
  2507.               {
  2508.               //Write_Error("nEntering spotlight2 ...");
  2509.  
  2510.               // perform spot light computations
  2511.               // light model for spot light simple version is once again:
  2512.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  2513.               // I(d)spotlight = __________________________________________      
  2514.               //                 kc + kl*d + kq*d2        
  2515.               // Where d = |p - s|, and pf = power factor
  2516.               // thus it's almost identical to the point, but has the extra term in the numerator
  2517.               // relating the angle between the light source and the point on the surface
  2518.               // test if we already computed poly normal in previous calculation
  2519.               if (n.z==FLT_MAX)       
  2520.                  {
  2521.                  // we need to compute the normal of this polygon face, and recall
  2522.                  // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
  2523.  
  2524.                  // build u, v
  2525.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_1].v, &u);
  2526.                  VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &obj->vlist_trans[ vindex_2].v, &v);
  2527.                  // compute cross product
  2528.                  VECTOR4D_Cross(&u, &v, &n);
  2529.                  } // end if
  2530.               // at this point, we are almost ready, but we have to normalize the normal vector!
  2531.               // this is a key optimization we can make later, we can pre-compute the length of all polygon
  2532.               // normals, so this step can be optimized
  2533.               // compute length of normal
  2534.               //nl = VECTOR4D_Length_Fast2(&n);
  2535.               nl = curr_poly->nlength;  
  2536.              
  2537.               // and for the diffuse model
  2538.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2539.               // so we basically need to multiple it all together
  2540.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2541.               // are slower, but the conversion to and from cost cycles
  2542.               dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
  2543.               
  2544.               // only add light if dp > 0
  2545.               if (dp > 0)
  2546.                  { 
  2547.                  // compute vector from light to surface (different from l which IS the light dir)
  2548.                  VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_0].v, &s);
  2549.                  // compute length of s (distance to light source) to normalize s for lighting calc
  2550.                  dists = VECTOR4D_Length_Fast2(&s);  
  2551.                  // compute spot light term (s . l)
  2552.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  2553.                  // proceed only if term is positive
  2554.                  if (dpsl > 0) 
  2555.                     {
  2556.                     // compute attenuation
  2557.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  2558.        
  2559.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  2560.                     // must be integral
  2561.                     float dpsl_exp = dpsl;
  2562.  
  2563.                     // exponentiate for positive integral powers
  2564.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  2565.                          dpsl_exp*=dpsl;
  2566.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  2567.                                                       
  2568.                     i = 128*dp * dpsl_exp / (nl * atten ); 
  2569.                     r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2570.                     g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2571.                     b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2572.   
  2573.                     } // end if
  2574.                  } // end if
  2575.               //Write_Error("nSpotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
  2576.      
  2577.               } // end if spot light
  2578.            } // end for light
  2579.   
  2580.        // make sure colors aren't out of range
  2581.        if (r_sum  > 255) r_sum = 255;
  2582.        if (g_sum  > 255) g_sum = 255;
  2583.        if (b_sum  > 255) b_sum = 255;
  2584.        //Write_Error("nWriting final values to polygon %d = %d,%d,%d", poly, r_sum, g_sum, b_sum);
  2585.        // write the color over current color
  2586.        curr_poly->lit_color[0] = rgblookup[RGB16Bit565(r_sum, g_sum, b_sum)];
  2587.        } // end if
  2588.     else
  2589.     if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) /////////////////////////////////
  2590.        {
  2591.        // gouraud shade, unfortunetly at this point in the pipeline, we have lost the original
  2592.        // mesh, and only have triangles, thus, many triangles will share the same vertices and
  2593.        // they will get lit 2x since we don't have any way to tell this, alas, performing lighting
  2594.        // at the object level is a better idea when gouraud shading is performed since the 
  2595.        // commonality of vertices is still intact, in any case, lighting here is similar to polygon
  2596.        // flat shaded, but we do it 3 times, once for each vertex, additionally there are lots
  2597.        // of opportunities for optimization, but I am going to lay off them for now, so the code
  2598.        // is intelligible, later we will optimize
  2599.        //Write_Error("nEntering gouraud shader...");
  2600.        // step 1: extract the base color out in RGB mode (it's already in 8 bits per channel)
  2601.        r_base = palette[curr_poly->color].peRed;
  2602.        g_base = palette[curr_poly->color].peGreen;
  2603.        b_base = palette[curr_poly->color].peBlue;
  2604.        //Write_Error("nBase color=%d, %d, %d", r_base, g_base, b_base);
  2605.        // initialize color sum(s) for vertices
  2606.        r_sum0  = 0;
  2607.        g_sum0  = 0;
  2608.        b_sum0  = 0;
  2609.        r_sum1  = 0;
  2610.        g_sum1  = 0;
  2611.        b_sum1  = 0;
  2612.        r_sum2  = 0;
  2613.        g_sum2  = 0;
  2614.        b_sum2  = 0;
  2615.        //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);
  2616.        // new optimization:
  2617.        // when there are multiple lights in the system we will end up performing numerous
  2618.        // redundant calculations to minimize this my strategy is to set key variables to 
  2619.        // to MAX values on each loop, then during the lighting calcs to test the vars for
  2620.        // the max value, if they are the max value then the first light that needs the math
  2621.        // will do it, and then save the information into the variable (causing it to change state
  2622.        // from an invalid number) then any other lights that need the math can use the previously
  2623.        // computed value
  2624.        // loop thru lights
  2625.        for (int curr_light = 0; curr_light < max_lights; curr_light++)
  2626.            {
  2627.            // is this light active
  2628.            if (lights[curr_light].state==LIGHTV1_STATE_OFF)
  2629.               continue;
  2630.            //Write_Error("nprocessing light %d", curr_light);
  2631.            // what kind of light are we dealing with
  2632.            if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT) ///////////////////////////////
  2633.               {
  2634.               //Write_Error("nEntering ambient light....");
  2635.               // simply multiply each channel against the color of the 
  2636.               // polygon then divide by 256 to scale back to 0..255
  2637.               // use a shift in real life!!! >> 8
  2638.               ri = ((lights[curr_light].c_ambient.r * r_base) / 256);
  2639.               gi = ((lights[curr_light].c_ambient.g * g_base) / 256);
  2640.               bi = ((lights[curr_light].c_ambient.b * b_base) / 256);
  2641.             
  2642.               // ambient light has the same affect on each vertex
  2643.               r_sum0+=ri;
  2644.               g_sum0+=gi;
  2645.               b_sum0+=bi;
  2646.   
  2647.               r_sum1+=ri;
  2648.               g_sum1+=gi;
  2649.               b_sum1+=bi;
  2650.               
  2651.               r_sum2+=ri;
  2652.               g_sum2+=gi;
  2653.               b_sum2+=bi;
  2654.               // there better only be one ambient light!
  2655.               //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);
  2656.               } // end if
  2657.            else
  2658.            if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) /////////////////////////////////
  2659.               {
  2660.               //Write_Error("nentering infinite light...");
  2661.               // infinite lighting, we need the surface normal, and the direction
  2662.               // of the light source
  2663.               // no longer need to compute normal or length, we already have the vertex normal
  2664.               // and it's length is 1.0  
  2665.               // ....
  2666.       
  2667.               // ok, recalling the lighting model for infinite lights
  2668.               // I(d)dir = I0dir * Cldir
  2669.               // and for the diffuse model
  2670.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2671.               // so we basically need to multiple it all together
  2672.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2673.               // are slower, but the conversion to and from cost cycles
  2674.               // need to perform lighting for each vertex (lots of redundant math, optimize later!)
  2675.               //Write_Error("nv0=[%f, %f, %f]=%f, v1=[%f, %f, %f]=%f, v2=[%f, %f, %f]=%f",
  2676.                 // 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),
  2677.                 // 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),
  2678.                 // 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) );
  2679.               // vertex 0
  2680.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].dir); 
  2681.               
  2682.               // only add light if dp > 0
  2683.               if (dp > 0)
  2684.                  { 
  2685.                  i = 128*dp; 
  2686.                  r_sum0+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2687.                  g_sum0+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2688.                  b_sum0+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2689.                  } // end if
  2690.               // vertex 1
  2691.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].dir);
  2692.               
  2693.               // only add light if dp > 0
  2694.               if (dp > 0)
  2695.                  { 
  2696.                  i = 128*dp; 
  2697.                  r_sum1+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2698.                  g_sum1+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2699.                  b_sum1+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2700.                  } // end if
  2701.               // vertex 2
  2702.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].dir);
  2703.               
  2704.               // only add light if dp > 0
  2705.               if (dp > 0)
  2706.                  { 
  2707.                  i = 128*dp; 
  2708.                  r_sum2+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2709.                  g_sum2+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2710.                  b_sum2+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2711.                  } // end if
  2712.               //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);
  2713.               } // end if infinite light
  2714.            else
  2715.            if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) //////////////////////////////////////
  2716.               {
  2717.               // perform point light computations
  2718.               // light model for point light is once again:
  2719.               //              I0point * Clpoint
  2720.               //  I(d)point = ___________________
  2721.               //              kc +  kl*d + kq*d2              
  2722.               //
  2723.               //  Where d = |p - s|
  2724.               // thus it's almost identical to the infinite light, but attenuates as a function
  2725.               // of distance from the point source to the surface point being lit
  2726.               // .. normal already in vertex
  2727.               //Write_Error("nEntering point light....");
  2728.               // compute vector from surface to light
  2729.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].pos, &l);
  2730.               // compute distance and attenuation
  2731.               dist = VECTOR4D_Length_Fast2(&l);  
  2732.               // and for the diffuse model
  2733.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2734.               // so we basically need to multiple it all together
  2735.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2736.               // are slower, but the conversion to and from cost cycles
  2737.               // perform the calculation for all 3 vertices
  2738.               // vertex 0
  2739.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &l);
  2740.               
  2741.               // only add light if dp > 0
  2742.               if (dp > 0)
  2743.                  { 
  2744.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2745.                  i = 128*dp / (dist * atten ); 
  2746.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2747.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2748.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2749.                  } // end if
  2750.               // vertex 1
  2751.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &l);
  2752.               
  2753.               // only add light if dp > 0
  2754.               if (dp > 0)
  2755.                  { 
  2756.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2757.                  i = 128*dp / (dist * atten ); 
  2758.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2759.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2760.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2761.                  } // end if
  2762.               // vertex 2
  2763.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &l);
  2764.               
  2765.               // only add light if dp > 0
  2766.               if (dp > 0)
  2767.                  { 
  2768.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2769.                  i = 128*dp / (dist * atten ); 
  2770.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2771.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2772.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2773.                  } // end if
  2774.               //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);
  2775.               } // end if point
  2776.            else
  2777.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ///////////////////////////////////////
  2778.               {
  2779.               // perform spotlight/point computations simplified model that uses
  2780.               // point light WITH a direction to simulate a spotlight
  2781.               // light model for point light is once again:
  2782.               //              I0point * Clpoint
  2783.               //  I(d)point = ___________________
  2784.               //              kc +  kl*d + kq*d2              
  2785.               //
  2786.               //  Where d = |p - s|
  2787.               // thus it's almost identical to the infinite light, but attenuates as a function
  2788.               // of distance from the point source to the surface point being lit
  2789.               //Write_Error("nentering spotlight1....");
  2790.               // .. normal is already computed
  2791.        
  2792.               // compute vector from surface to light
  2793.               VECTOR4D_Build(&obj->vlist_trans[ vindex_0].v, &lights[curr_light].pos, &l);
  2794.               // compute distance and attenuation
  2795.               dist = VECTOR4D_Length_Fast2(&l);  
  2796.               // and for the diffuse model
  2797.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2798.               // so we basically need to multiple it all together
  2799.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2800.               // are slower, but the conversion to and from cost cycles
  2801.               // note that I use the direction of the light here rather than a the vector to the light
  2802.               // thus we are taking orientation into account which is similar to the spotlight model
  2803.               // vertex 0
  2804.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].dir);
  2805.               
  2806.               // only add light if dp > 0
  2807.               if (dp > 0)
  2808.                  { 
  2809.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2810.                  i = 128*dp / ( atten ); 
  2811.                  r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2812.                  g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2813.                  b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2814.                  } // end if
  2815.               // vertex 1
  2816.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].dir);
  2817.               
  2818.               // only add light if dp > 0
  2819.               if (dp > 0)
  2820.                  { 
  2821.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2822.                  i = 128*dp / ( atten ); 
  2823.                  r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2824.                  g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2825.                  b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2826.                  } // end i
  2827.               // vertex 2
  2828.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].dir);
  2829.               
  2830.               // only add light if dp > 0
  2831.               if (dp > 0)
  2832.                  { 
  2833.                  atten =  (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);    
  2834.                  i = 128*dp / ( atten ); 
  2835.                  r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2836.                  g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2837.                  b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2838.                  } // end i
  2839.               //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);
  2840.               } // end if spotlight1
  2841.            else
  2842.            if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version //////////////////////////
  2843.               {
  2844.               // perform spot light computations
  2845.               // light model for spot light simple version is once again:
  2846.               //               I0spotlight * Clspotlight * MAX( (l . s), 0)^pf                     
  2847.               // I(d)spotlight = __________________________________________      
  2848.               //                 kc + kl*d + kq*d2        
  2849.               // Where d = |p - s|, and pf = power factor
  2850.               // thus it's almost identical to the point, but has the extra term in the numerator
  2851.               // relating the angle between the light source and the point on the surface
  2852.               // .. already have normals and length are 1.0
  2853.              
  2854.               // and for the diffuse model
  2855.               // Itotald =   Rsdiffuse*Idiffuse * (n . l)
  2856.               // so we basically need to multiple it all together
  2857.               // notice the scaling by 128, I want to avoid floating point calculations, not because they 
  2858.               // are slower, but the conversion to and from cost cycles
  2859.               //Write_Error("nEntering spotlight2...");
  2860.               // tons of redundant math here! lots to optimize later!
  2861.               
  2862.               // vertex 0
  2863.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_0].n, &lights[curr_light].dir);
  2864.               
  2865.               // only add light if dp > 0
  2866.               if (dp > 0)
  2867.                  { 
  2868.                  // compute vector from light to surface (different from l which IS the light dir)
  2869.                  VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_0].v, &s);
  2870.                  // compute length of s (distance to light source) to normalize s for lighting calc
  2871.                  dists = VECTOR4D_Length_Fast2(&s);  
  2872.                  // compute spot light term (s . l)
  2873.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  2874.                  // proceed only if term is positive
  2875.                  if (dpsl > 0) 
  2876.                     {
  2877.                     // compute attenuation
  2878.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  2879.        
  2880.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  2881.                     // must be integral
  2882.                     float dpsl_exp = dpsl;
  2883.  
  2884.                     // exponentiate for positive integral powers
  2885.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  2886.                          dpsl_exp*=dpsl;
  2887.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  2888.                                                       
  2889.                     i = 128*dp * dpsl_exp / ( atten ); 
  2890.                     r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2891.                     g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2892.                     b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2893.   
  2894.                     } // end if
  2895.                  } // end if
  2896.               // vertex 1
  2897.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_1].n, &lights[curr_light].dir);
  2898.               
  2899.               // only add light if dp > 0
  2900.               if (dp > 0)
  2901.                  { 
  2902.                  // compute vector from light to surface (different from l which IS the light dir)
  2903.                  VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_1].v, &s);
  2904.                  // compute length of s (distance to light source) to normalize s for lighting calc
  2905.                  dists = VECTOR4D_Length_Fast2(&s);  
  2906.                  // compute spot light term (s . l)
  2907.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  2908.                  // proceed only if term is positive
  2909.                  if (dpsl > 0) 
  2910.                     {
  2911.                     // compute attenuation
  2912.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  2913.        
  2914.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  2915.                     // must be integral
  2916.                     float dpsl_exp = dpsl;
  2917.  
  2918.                     // exponentiate for positive integral powers
  2919.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  2920.                          dpsl_exp*=dpsl;
  2921.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  2922.                                                       
  2923.                     i = 128*dp * dpsl_exp / ( atten ); 
  2924.                     r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2925.                     g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2926.                     b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2927.   
  2928.                     } // end if
  2929.                  } // end if
  2930.               // vertex 2
  2931.               dp = VECTOR4D_Dot(&obj->vlist_trans[ vindex_2].n, &lights[curr_light].dir);
  2932.               
  2933.               // only add light if dp > 0
  2934.               if (dp > 0)
  2935.                  { 
  2936.                  // compute vector from light to surface (different from l which IS the light dir)
  2937.                  VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_2].v, &s);
  2938.                  // compute length of s (distance to light source) to normalize s for lighting calc
  2939.                  dists = VECTOR4D_Length_Fast2(&s);  
  2940.                  // compute spot light term (s . l)
  2941.                  float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
  2942.                  // proceed only if term is positive
  2943.                  if (dpsl > 0) 
  2944.                     {
  2945.                     // compute attenuation
  2946.                     atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);    
  2947.        
  2948.                     // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
  2949.                     // must be integral
  2950.                     float dpsl_exp = dpsl;
  2951.  
  2952.                     // exponentiate for positive integral powers
  2953.                     for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
  2954.                          dpsl_exp*=dpsl;
  2955.                     // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf 
  2956.                                                       
  2957.                     i = 128*dp * dpsl_exp / ( atten ); 
  2958.                     r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
  2959.                     g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
  2960.                     b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
  2961.                     } // end if
  2962.                  } // end if
  2963.               //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);
  2964.               } // end if spot light
  2965.            } // end for light
  2966.   
  2967.        // make sure colors aren't out of range
  2968.        if (r_sum0  > 255) r_sum0 = 255;
  2969.        if (g_sum0  > 255) g_sum0 = 255;
  2970.        if (b_sum0  > 255) b_sum0 = 255;
  2971.        if (r_sum1  > 255) r_sum1 = 255;
  2972.        if (g_sum1  > 255) g_sum1 = 255;
  2973.        if (b_sum1  > 255) b_sum1 = 255;
  2974.        if (r_sum2  > 255) r_sum2 = 255;
  2975.        if (g_sum2  > 255) g_sum2 = 255;
  2976.        if (b_sum2  > 255) b_sum2 = 255;
  2977.        //Write_Error("nwriting color for poly %d", poly);
  2978.        //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);
  2979.        // write the colors, leave in 5.6.5 format, so we have more color range
  2980.        // since the rasterizer will scale down to 8-bit
  2981.        curr_poly->lit_color[0] = RGB16Bit(r_sum0, g_sum0, b_sum0);
  2982.        curr_poly->lit_color[1] = RGB16Bit(r_sum1, g_sum1, b_sum1);
  2983.        curr_poly->lit_color[2] = RGB16Bit(r_sum2, g_sum2, b_sum2);
  2984.        } // end if
  2985.     else // assume POLY4DV2_ATTR_SHADE_MODE_CONSTANT
  2986.        {
  2987.        // emmisive shading only, do nothing
  2988.        // ...
  2989.        curr_poly->lit_color[0] = curr_poly->color;
  2990.        //Write_Error("nentering constant shader, and exiting...");
  2991.        } // end if
  2992.     } // end for poly
  2993. // return success
  2994. return(1);
  2995. } // end Light_OBJECT4DV2_World
  2996. //////////////////////////////////////////////////////////////////////////////
  2997. int Light_RENDERLIST4DV2_World16(RENDERLIST4DV2_PTR rend_list,  // list to process
  2998.                                    CAM4DV1_PTR cam,     // camera position
  2999.                                    LIGHTV1_PTR lights,  // light list (might have more than one)
  3000.                                    int max_lights)      // maximum lights in list