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

游戏

开发平台:

Visual C++

  1. // T3DLIB5.CPP
  2. #define STANDALONE 0
  3. // INCLUDES ///////////////////////////////////////////////////
  4. #define DEBUG_ON
  5. #define WIN32_LEAN_AND_MEAN  
  6. #if (STANDALONE==1)
  7. #define INITGUID       // you need this or DXGUID.LIB
  8. #endif
  9. #include <windows.h>   // include important windows stuff
  10. #include <windowsx.h> 
  11. #include <mmsystem.h>
  12. #include <objbase.h>
  13. #include <iostream.h> // include important C/C++ stuff
  14. #include <conio.h>
  15. #include <stdlib.h>
  16. #include <malloc.h>
  17. #include <memory.h>
  18. #include <string.h>
  19. #include <stdarg.h>
  20. #include <stdio.h>
  21. #include <math.h>
  22. #include <io.h>
  23. #include <fcntl.h>
  24. #include <direct.h>
  25. #include <wchar.h>
  26. #include <ddraw.h>      // needed for defs in T3DLIB1.H 
  27. #include "T3DLIB1.H"    // T3DLIB4 is based on some defs in this 
  28. #include "T3DLIB4.H"
  29. #include "T3DLIB5.H"
  30. // CLASSES ////////////////////////////////////////////////////
  31. // PROTOTYPES /////////////////////////////////////////////////
  32. // GLOBALS ////////////////////////////////////////////////////
  33. #if (STANDALONE==1)
  34. HWND main_window_handle           = NULL; // save the window handle
  35. HINSTANCE main_instance           = NULL; // save the instance
  36. char buffer[256];                         // used to print text
  37. #endif
  38. // FUNCTIONS //////////////////////////////////////////////////
  39. char *Get_Line_PLG(char *buffer, int maxlength, FILE *fp)
  40. {
  41. // this little helper function simply read past comments 
  42. // and blank lines in a PLG file and always returns full 
  43. // lines with something on them on NULL if the file is empty
  44. int index = 0;  // general index
  45. int length = 0; // general length
  46. // enter into parsing loop
  47. while(1)
  48.      {
  49.      // read the next line
  50.      if (!fgets(buffer, maxlength, fp))
  51.      return(NULL);
  52.     // kill the whitespace
  53.     for (length = strlen(buffer), index = 0; isspace(buffer[index]); index++);
  54.  
  55.     // test if this was a blank line or a comment
  56.     if (index >= length || buffer[index]=='#') 
  57.        continue;
  58.    
  59.     // at this point we have a good line
  60.     return(&buffer[index]);
  61.     } // end while
  62. } // end Get_Line_PLG
  63. ///////////////////////////////////////////////////////////////
  64. float Compute_OBJECT4DV1_Radius(OBJECT4DV1_PTR obj)
  65. {
  66. // this function computes the average and maximum radius for 
  67. // sent object and opdates the object data
  68. // reset incase there's any residue
  69. obj->avg_radius = 0;
  70. obj->max_radius = 0;
  71. // loop thru and compute radius
  72. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  73.     {
  74.     // update the average and maximum radius
  75.     float dist_to_vertex = 
  76.           sqrt(obj->vlist_local[vertex].x*obj->vlist_local[vertex].x +
  77.                obj->vlist_local[vertex].y*obj->vlist_local[vertex].y +
  78.                obj->vlist_local[vertex].z*obj->vlist_local[vertex].z);
  79.     
  80.     // accumulate total radius
  81.     obj->avg_radius+=dist_to_vertex;
  82.     // update maximum radius   
  83.     if (dist_to_vertex > obj->max_radius)
  84.        obj->max_radius = dist_to_vertex; 
  85.  
  86.     } // end for vertex
  87. // finallize average radius computation
  88. obj->avg_radius/=obj->num_vertices;
  89. // return max radius
  90. return(obj->max_radius);
  91. } // end Compute_OBJECT4DV1_Radius
  92. ///////////////////////////////////////////////////////////////
  93. int Load_OBJECT4DV1_PLG(OBJECT4DV1_PTR obj, // pointer to object
  94.                     char *filename,     // filename of plg file
  95.                     VECTOR4D_PTR scale,     // initial scaling factors
  96.                     VECTOR4D_PTR pos,       // initial position
  97.                     VECTOR4D_PTR rot)       // initial rotations
  98. {
  99. // this function loads a plg object in off disk, additionally
  100. // it allows the caller to scale, position, and rotate the object
  101. // to save extra calls later for non-dynamic objects
  102. FILE *fp;          // file pointer
  103. char buffer[256];  // working buffer
  104. char *token_string;  // pointer to actual token text, ready for parsing
  105. // file format review, note types at end of each description
  106. // # this is a comment
  107. // # object descriptor
  108. // object_name_string num_verts_int num_polys_int
  109. // # vertex list
  110. // x0_float y0_float z0_float
  111. // x1_float y1_float z1_float
  112. // x2_float y2_float z2_float
  113. // .
  114. // .
  115. // xn_float yn_float zn_float
  116. //
  117. // # polygon list
  118. // surface_description_ushort num_verts_int v0_index_int v1_index_int ..  vn_index_int
  119. // .
  120. // .
  121. // surface_description_ushort num_verts_int v0_index_int v1_index_int ..  vn_index_int
  122. // lets keep it simple and assume one element per line
  123. // hence we have to find the object descriptor, read it in, then the
  124. // vertex list and read it in, and finally the polygon list -- simple :)
  125. // Step 1: clear out the object and initialize it a bit
  126. memset(obj, 0, sizeof(OBJECT4DV1));
  127. // set state of object to active and visible
  128. obj->state = OBJECT4DV1_STATE_ACTIVE | OBJECT4DV1_STATE_VISIBLE;
  129. // set position of object
  130. obj->world_pos.x = pos->x;
  131. obj->world_pos.y = pos->y;
  132. obj->world_pos.z = pos->z;
  133. obj->world_pos.w = pos->w;
  134. // Step 2: open the file for reading
  135. if (!(fp = fopen(filename, "r")))
  136.    {
  137.    Write_Error("Couldn't open PLG file %s.", filename);
  138.    return(0);
  139.    } // end if
  140. // Step 3: get the first token string which should be the object descriptor
  141. if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
  142.    {
  143.    Write_Error("PLG file error with file %s (object descriptor invalid).",filename);
  144.    return(0);
  145.    } // end if
  146. Write_Error("Object Descriptor: %s", token_string);
  147. // parse out the info object
  148. sscanf(token_string, "%s %d %d", obj->name, &obj->num_vertices, &obj->num_polys);
  149. // Step 4: load the vertex list
  150. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  151.     {
  152.     // get the next vertex
  153.     if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
  154.           {
  155.           Write_Error("PLG file error with file %s (vertex list invalid).",filename);
  156.           return(0);
  157.           } // end if
  158.     // parse out vertex
  159.     sscanf(token_string, "%f %f %f", &obj->vlist_local[vertex].x, 
  160.                                      &obj->vlist_local[vertex].y, 
  161.                                      &obj->vlist_local[vertex].z);
  162.     obj->vlist_local[vertex].w = 1;    
  163.     // scale vertices
  164.     obj->vlist_local[vertex].x*=scale->x;
  165.     obj->vlist_local[vertex].y*=scale->y;
  166.     obj->vlist_local[vertex].z*=scale->z;
  167.     Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
  168.                                            obj->vlist_local[vertex].x, 
  169.                                            obj->vlist_local[vertex].y, 
  170.                                            obj->vlist_local[vertex].z,
  171.                                            obj->vlist_local[vertex].w);
  172.     } // end for vertex
  173. // compute average and max radius
  174. Compute_OBJECT4DV1_Radius(obj);
  175. Write_Error("nObject average radius = %f, max radius = %f", 
  176.             obj->avg_radius, obj->max_radius);
  177. int poly_surface_desc = 0; // PLG/PLX surface descriptor
  178. int poly_num_verts    = 0; // number of vertices for current poly (always 3)
  179. char tmp_string[8];        // temp string to hold surface descriptor in and
  180.                            // test if it need to be converted from hex
  181. // Step 5: load the polygon list
  182. for (int poly=0; poly < obj->num_polys; poly++)
  183.     {
  184.     // get the next polygon descriptor
  185.     if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
  186.         {
  187.         Write_Error("PLG file error with file %s (polygon descriptor invalid).",filename);
  188.         return(0);
  189.         } // end if
  190.    
  191.     Write_Error("nPolygon %d:", poly);
  192.     // each vertex list MUST have 3 vertices since we made this a rule that all models
  193.     // must be constructed of triangles
  194.     // read in surface descriptor, number of vertices, and vertex list
  195.     sscanf(token_string, "%s %d %d %d %d", tmp_string,
  196.                                            &poly_num_verts, // should always be 3 
  197.                                            &obj->plist[poly].vert[0],
  198.                                            &obj->plist[poly].vert[1],
  199.                                            &obj->plist[poly].vert[2]);
  200.     
  201.     // since we are allowing the surface descriptor to be in hex format
  202.     // with a leading "0x" we need to test for it
  203.     if (tmp_string[0] == '0' && toupper(tmp_string[1]) == 'X')
  204.        sscanf(tmp_string,"%x", &poly_surface_desc);
  205.     else
  206.        poly_surface_desc = atoi(tmp_string);
  207.     
  208.     // point polygon vertex list to object's vertex list
  209.     // note that this is redundant since the polylist is contained
  210.     // within the object in this case and its up to the user to select
  211.     // whether the local or transformed vertex list is used when building up
  212.     // polygon geometry, might be a better idea to set to NULL in the context
  213.     // of polygons that are part of an object
  214.     obj->plist[poly].vlist = obj->vlist_local; 
  215.     Write_Error("nSurface Desc = 0x%.4x, num_verts = %d, vert_indices [%d, %d, %d]", 
  216.                                                          poly_surface_desc, 
  217.                                                          poly_num_verts, 
  218.                                                          obj->plist[poly].vert[0],
  219.                                                          obj->plist[poly].vert[1],
  220.                                                          obj->plist[poly].vert[2]);
  221.     // now we that we have the vertex list and we have entered the polygon
  222.     // vertex index data into the polygon itself, now let's analyze the surface
  223.     // descriptor and set the fields for the polygon based on the description
  224.     // extract out each field of data from the surface descriptor
  225.     // first let's get the single/double sided stuff out of the way
  226.     if ((poly_surface_desc & PLX_2SIDED_FLAG))
  227.        {
  228.        SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_2SIDED);
  229.        Write_Error("n2 sided.");
  230.        } // end if
  231.     else
  232.        {
  233.        // one sided
  234.        Write_Error("n1 sided.");
  235.        } // end else
  236.     // now let's set the color type and color
  237.     if ((poly_surface_desc & PLX_COLOR_MODE_RGB_FLAG)) 
  238.        {
  239.        // this is an RGB 4.4.4 surface
  240.        SET_BIT(obj->plist[poly].attr,POLY4DV1_ATTR_RGB16);
  241.  
  242.        // now extract color and copy into polygon color
  243.        // field in proper 16-bit format 
  244.        // 0x0RGB is the format, 4 bits per pixel 
  245.        int red   = ((poly_surface_desc & 0x0f00) >> 8);
  246.        int green = ((poly_surface_desc & 0x00f0) >> 4);
  247.        int blue  = (poly_surface_desc & 0x000f);
  248.        // although the data is always in 4.4.4 format, the graphics card
  249.        // is either 5.5.5 or 5.6.5, but our virtual color system translates
  250.        // 8.8.8 into 5.5.5 or 5.6.5 for us, but we have to first scale all
  251.        // these 4.4.4 values into 8.8.8
  252.        obj->plist[poly].color = RGB16Bit(red*16, green*16, blue*16);
  253.        Write_Error("nRGB color = [%d, %d, %d]", red, green, blue);
  254.        } // end if
  255.     else
  256.        {
  257.        // this is an 8-bit color indexed surface
  258.        SET_BIT(obj->plist[poly].attr,POLY4DV1_ATTR_8BITCOLOR);
  259.        // and simple extract the last 8 bits and that's the color index
  260.        obj->plist[poly].color = (poly_surface_desc & 0x00ff);
  261.        
  262.        Write_Error("n8-bit color index = %d", obj->plist[poly].color);
  263.        } // end else
  264.    // handle shading mode
  265.    int shade_mode = (poly_surface_desc & PLX_SHADE_MODE_MASK);
  266.    // set polygon shading mode
  267.    switch(shade_mode)
  268.          {
  269.          case PLX_SHADE_MODE_PURE_FLAG: {
  270.          SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_PURE);
  271.          Write_Error("nShade mode = pure");
  272.          } break;
  273.          case PLX_SHADE_MODE_FLAT_FLAG: {
  274.          SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_FLAT);
  275.          Write_Error("nShade mode = flat");
  276.          } break;
  277.          case PLX_SHADE_MODE_GOURAUD_FLAG: {
  278.          SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_GOURAUD);
  279.          Write_Error("nShade mode = gouraud");
  280.          } break;
  281.          case PLX_SHADE_MODE_PHONG_FLAG: {
  282.          SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_SHADE_MODE_PHONG);
  283.          Write_Error("nShade mode = phong");
  284.          } break;
  285.          default: break;
  286.          } // end switch
  287.     // finally set the polygon to active
  288.     obj->plist[poly].state = POLY4DV1_STATE_ACTIVE;    
  289.     } // end for poly
  290. // close the file
  291. fclose(fp);
  292. // return success
  293. return(1);
  294. } // end Load_OBJECT4DV1_PLG
  295. //////////////////////////////////////////////////////////////
  296. void Translate_OBJECT4DV1(OBJECT4DV1_PTR obj, VECTOR4D_PTR vt) 
  297. {
  298. // NOTE: Not matrix based
  299. // this function translates an object without matrices,
  300. // simply updates the world_pos
  301. VECTOR4D_Add(&obj->world_pos, vt, &obj->world_pos);
  302. } // end Translate_OBJECT4DV1
  303. /////////////////////////////////////////////////////////////
  304. void Scale_OBJECT4DV1(OBJECT4DV1_PTR obj, VECTOR4D_PTR vs)
  305. {
  306. // NOTE: Not matrix based
  307. // this function scales and object without matrices 
  308. // modifies the object's local vertex list 
  309. // additionally the radii is updated for the object
  310. // for each vertex in the mesh scale the local coordinates by
  311. // vs on a componentwise basis, that is, sx, sy, sz
  312. for (int vertex=0; vertex < obj->num_vertices; vertex++)
  313.     {
  314.     obj->vlist_local[vertex].x*=vs->x;
  315.     obj->vlist_local[vertex].y*=vs->y;
  316.     obj->vlist_local[vertex].z*=vs->z;
  317.     // leave w unchanged, always equal to 1
  318.     } // end for vertex
  319. // now since the object is scaled we have to do something with 
  320. // the radii calculation, but we don't know how the scaling
  321. // factors relate to the original major axis of the object,
  322. // therefore for scaling factors all ==1 we will simple multiply
  323. // which is correct, but for scaling factors not equal to 1, we
  324. // must take the largest scaling factor and use it to scale the
  325. // radii with since it's the worst case scenario of the new max and
  326. // average radii
  327. // find max scaling factor
  328. float scale = MAX(vs->x, vs->y);
  329. scale = MAX(scale, vs->z);
  330. // now scale
  331. obj->max_radius*=scale;
  332. obj->avg_radius*=scale;
  333. } // end Scale_OBJECT4DV1
  334. /////////////////////////////////////////////////////////////
  335. void Build_XYZ_Rotation_MATRIX4X4(float theta_x, // euler angles
  336.                                   float theta_y, 
  337.                                   float theta_z,
  338.                                   MATRIX4X4_PTR mrot) // output 
  339. {
  340. // this helper function takes a set if euler angles and computes
  341. // a rotation matrix from them, usefull for object and camera
  342. // work, also  we will do a little testing in the function to determine
  343. // the rotations that need to be performed, since there's no
  344. // reason to perform extra matrix multiplies if the angles are
  345. // zero!
  346. MATRIX4X4 mx, my, mz, mtmp;       // working matrices
  347. float sin_theta=0, cos_theta=0;   // used to initialize matrices
  348. int rot_seq = 0;                  // 1 for x, 2 for y, 4 for z
  349. // step 0: fill in with identity matrix
  350. MAT_IDENTITY_4X4(mrot);
  351. // step 1: based on zero and non-zero rotation angles, determine
  352. // rotation sequence
  353. if (fabs(theta_x) > EPSILON_E5) // x
  354.    rot_seq = rot_seq | 1;
  355. if (fabs(theta_y) > EPSILON_E5) // y
  356.    rot_seq = rot_seq | 2;
  357. if (fabs(theta_z) > EPSILON_E5) // z
  358.    rot_seq = rot_seq | 4;
  359. // now case on sequence
  360. switch(rot_seq)
  361.       {
  362.       case 0: // no rotation
  363.       {
  364.       // what a waste!
  365.       return;
  366.       } break;
  367.       case 1: // x rotation
  368.       {
  369.       // compute the sine and cosine of the angle
  370.       cos_theta = Fast_Cos(theta_x);
  371.       sin_theta = Fast_Sin(theta_x);
  372.       // set the matrix up 
  373.       Mat_Init_4X4(&mx, 1,    0,          0,         0,
  374.                         0,    cos_theta,  sin_theta, 0,
  375.                         0,   -sin_theta, cos_theta, 0,
  376.                         0,    0,          0,         1);
  377.       // that's it, copy to output matrix
  378.       MAT_COPY_4X4(&mx, mrot);
  379.       return;
  380.       } break;
  381.       case 2: // y rotation
  382.       {
  383.       // compute the sine and cosine of the angle
  384.       cos_theta = Fast_Cos(theta_y);
  385.       sin_theta = Fast_Sin(theta_y);
  386.       // set the matrix up 
  387.       Mat_Init_4X4(&my,cos_theta, 0, -sin_theta, 0,  
  388.                        0,         1,  0,         0,
  389.                        sin_theta, 0, cos_theta,  0,
  390.                        0,         0, 0,          1);
  391.       // that's it, copy to output matrix
  392.       MAT_COPY_4X4(&my, mrot);
  393.       return;
  394.       } break;
  395.       case 3: // xy rotation
  396.       {
  397.       // compute the sine and cosine of the angle for x
  398.       cos_theta = Fast_Cos(theta_x);
  399.       sin_theta = Fast_Sin(theta_x);
  400.       // set the matrix up 
  401.       Mat_Init_4X4(&mx, 1,    0,          0,         0,
  402.                         0,    cos_theta,  sin_theta, 0,
  403.                         0,   -sin_theta, cos_theta, 0,
  404.                         0,    0,          0,         1);
  405.       // compute the sine and cosine of the angle for y
  406.       cos_theta = Fast_Cos(theta_y);
  407.       sin_theta = Fast_Sin(theta_y);
  408.       // set the matrix up 
  409.       Mat_Init_4X4(&my,cos_theta, 0, -sin_theta, 0,  
  410.                        0,         1,  0,         0,
  411.                        sin_theta, 0, cos_theta,  0,
  412.                        0,         0, 0,          1);
  413.       // concatenate matrices 
  414.       Mat_Mul_4X4(&mx, &my, mrot);
  415.       return;
  416.       } break;
  417.       case 4: // z rotation
  418.       {
  419.       // compute the sine and cosine of the angle
  420.       cos_theta = Fast_Cos(theta_z);
  421.       sin_theta = Fast_Sin(theta_z);
  422.       // set the matrix up 
  423.       Mat_Init_4X4(&mz, cos_theta, sin_theta, 0, 0,  
  424.                        -sin_theta, cos_theta, 0, 0,
  425.                         0,         0,         1, 0,
  426.                         0,         0,         0, 1);
  427.       // that's it, copy to output matrix
  428.       MAT_COPY_4X4(&mz, mrot);
  429.       return;
  430.       } break;
  431.       case 5: // xz rotation
  432.       {
  433.       // compute the sine and cosine of the angle x
  434.       cos_theta = Fast_Cos(theta_x);
  435.       sin_theta = Fast_Sin(theta_x);
  436.       // set the matrix up 
  437.       Mat_Init_4X4(&mx, 1,    0,          0,         0,
  438.                         0,    cos_theta,  sin_theta, 0,
  439.                         0,   -sin_theta, cos_theta, 0,
  440.                         0,    0,          0,         1);
  441.       // compute the sine and cosine of the angle z
  442.       cos_theta = Fast_Cos(theta_z);
  443.       sin_theta = Fast_Sin(theta_z);
  444.       // set the matrix up 
  445.       Mat_Init_4X4(&mz, cos_theta, sin_theta, 0, 0,  
  446.                        -sin_theta, cos_theta, 0, 0,
  447.                         0,         0,         1, 0,
  448.                         0,         0,         0, 1);
  449.       // concatenate matrices 
  450.       Mat_Mul_4X4(&mx, &mz, mrot);
  451.       return;
  452.       } break;
  453.       case 6: // yz rotation
  454.       {
  455.       // compute the sine and cosine of the angle y
  456.       cos_theta = Fast_Cos(theta_y);
  457.       sin_theta = Fast_Sin(theta_y);
  458.       // set the matrix up 
  459.       Mat_Init_4X4(&my,cos_theta, 0, -sin_theta, 0,  
  460.                        0,         1,  0,         0,
  461.                        sin_theta, 0, cos_theta,  0,
  462.                        0,         0, 0,          1);
  463.       // compute the sine and cosine of the angle z
  464.       cos_theta = Fast_Cos(theta_z);
  465.       sin_theta = Fast_Sin(theta_z);
  466.       // set the matrix up 
  467.       Mat_Init_4X4(&mz, cos_theta, sin_theta, 0, 0,  
  468.                        -sin_theta, cos_theta, 0, 0,
  469.                         0,         0,         1, 0,
  470.                         0,         0,         0, 1);
  471.       // concatenate matrices 
  472.       Mat_Mul_4X4(&my, &mz, mrot);
  473.       return;
  474.       } break;
  475.       case 7: // xyz rotation
  476.       {
  477.       // compute the sine and cosine of the angle x
  478.       cos_theta = Fast_Cos(theta_x);
  479.       sin_theta = Fast_Sin(theta_x);
  480.       // set the matrix up 
  481.       Mat_Init_4X4(&mx, 1,    0,         0,         0,
  482.                         0,    cos_theta, sin_theta, 0,
  483.                         0,   -sin_theta, cos_theta, 0,
  484.                         0,    0,         0,         1);
  485.       // compute the sine and cosine of the angle y
  486.       cos_theta = Fast_Cos(theta_y);
  487.       sin_theta = Fast_Sin(theta_y);
  488.       // set the matrix up 
  489.       Mat_Init_4X4(&my,cos_theta, 0, -sin_theta, 0,  
  490.                        0,         1,  0,         0,
  491.                        sin_theta, 0,  cos_theta,  0,
  492.                        0,         0,  0,          1);
  493.       // compute the sine and cosine of the angle z
  494.       cos_theta = Fast_Cos(theta_z);
  495.       sin_theta = Fast_Sin(theta_z);
  496.       // set the matrix up 
  497.       Mat_Init_4X4(&mz, cos_theta, sin_theta, 0, 0,  
  498.                        -sin_theta, cos_theta, 0, 0,
  499.                         0,         0,         1, 0,
  500.                         0,         0,         0, 1);
  501.       // concatenate matrices, watch order!
  502.       Mat_Mul_4X4(&mx, &my, &mtmp);
  503.       Mat_Mul_4X4(&mtmp, &mz, mrot);
  504.       } break;
  505.       default: break;
  506.       
  507.       } // end switch
  508. } // end Build_XYZ_Rotation_MATRIX4X4                                    
  509.  
  510. ///////////////////////////////////////////////////////////
  511. void Build_Model_To_World_MATRIX4X4(VECTOR4D_PTR vpos, MATRIX4X4_PTR m)
  512. {
  513. // this function builds up a general local to world 
  514. // transformation matrix that is really nothing more than a translation
  515. // of the origin by the amount specified in vpos
  516. Mat_Init_4X4(m, 1,       0,       0,       0, 
  517.                 0,       1,       0,       0, 
  518.                 0,       0,       1,       0,
  519.                 vpos->x, vpos->y, vpos->z, 1 );
  520. } // end Build_Model_To_World_MATRIX4X4
  521. //////////////////////////////////////////////////////////
  522. void Build_Camera_To_Perspective_MATRIX4X4(CAM4DV1_PTR cam, MATRIX4X4_PTR m)
  523. {
  524. // this function builds up a camera to perspective transformation
  525. // matrix, in most cases the camera would have a 2x2 normalized
  526. // view plane with a 90 degree FOV, since the point of the having
  527. // this matrix must be to also have a perspective to screen (viewport)
  528. // matrix that scales the normalized coordinates, also the matrix
  529. // assumes that you are working in 4D homogenous coordinates and at 
  530. // some point there will be a 4D->3D conversion, it might be immediately
  531. // after this transform is applied to vertices, or after the perspective
  532. // to screen transform
  533. Mat_Init_4X4(m, cam->view_dist, 0,                                0, 0, 
  534.                 0,              cam->view_dist*cam->aspect_ratio, 0, 0, 
  535.                 0,              0,                                1, 1,
  536.                 0,              0,                                0, 0);
  537. } // end Build_Camera_To_Perspective_MATRIX4X4
  538. ///////////////////////////////////////////////////////////
  539. void Build_Perspective_To_Screen_4D_MATRIX4X4(CAM4DV1_PTR cam, MATRIX4X4_PTR m)
  540. {
  541. // this function builds up a perspective to screen transformation
  542. // matrix, the function assumes that you want to perform the
  543. // transform in homogeneous coordinates and at raster time there will be 
  544. // a 4D->3D homogenous conversion and of course only the x,y points
  545. // will be considered for the 2D rendering, thus you would use this
  546. // function's matrix is your perspective coordinates were still 
  547. // in homgeneous form whene this matrix was applied, additionally
  548. // the point of this matrix to to scale and translate the perspective
  549. // coordinates to screen coordinates, thus the matrix is built up
  550. // assuming that the perspective coordinates are in normalized form for
  551. // a (2x2)/aspect_ratio viewplane, that is, x: -1 to 1, y:-1/aspect_ratio to 1/aspect_ratio
  552. float alpha = (0.5*cam->viewport_width-0.5);
  553. float beta  = (0.5*cam->viewport_height-0.5);
  554. Mat_Init_4X4(m, alpha,   0,     0,    0, 
  555.                 0,      -beta,  0,    0, 
  556.                 alpha,   beta,  1,    0,
  557.                 0,       0,     0,    1);
  558. } // end Build_Perspective_To_Screen_4D_MATRIX4X4()
  559. //////////////////////////////////////////////////////////
  560. void Build_Perspective_To_Screen_MATRIX4X4(CAM4DV1_PTR cam, MATRIX4X4_PTR m)
  561. {
  562. // this function builds up a perspective to screen transformation
  563. // matrix, the function assumes that you want to perform the
  564. // transform in 2D/3D coordinates, that is, you have already converted
  565. // the perspective coordinates from homogenous 4D to 3D before applying
  566. // this matrix, additionally
  567. // the point of this matrix to to scale and translate the perspective
  568. // coordinates to screen coordinates, thus the matrix is built up
  569. // assuming that the perspective coordinates are in normalized form for
  570. // a 2x2 viewplane, that is, x: -1 to 1, y:-1 to 1 
  571. // the only difference between this function and the version that
  572. // assumes the coordinates are still in homogenous format is the
  573. // last column doesn't force w=z, in fact the z, and w results
  574. // are irrelevent since we assume that BEFORE this matrix is applied
  575. // all points are already converted from 4D->3D
  576. float alpha = (0.5*cam->viewport_width-0.5);
  577. float beta  = (0.5*cam->viewport_height-0.5);
  578. Mat_Init_4X4(m, alpha,   0,     0,    0, 
  579.                 0,      -beta,  0,    0, 
  580.                 alpha,   beta,  1,    0,
  581.                 0,       0,     0,    1);
  582. } // end Build_Perspective_To_Screen_MATRIX4X4()
  583. ///////////////////////////////////////////////////////////
  584. void Build_Camera_To_Screen_MATRIX4X4(CAM4DV1_PTR cam, MATRIX4X4_PTR m)
  585. {
  586. // this function creates a single matrix that performs the
  587. // entire camera->perspective->screen transform, the only
  588. // important thing is that the camera must be created with
  589. // a viewplane specified to be the size of the viewport
  590. // furthermore, after this transform is applied the the vertex
  591. // must be converted from 4D homogeneous to 3D, technically
  592. // the z is irrelevant since the data would be used for the
  593. // screen, but still the division by w is needed no matter
  594. // what
  595. float alpha = (0.5*cam->viewport_width-0.5);
  596. float beta  = (0.5*cam->viewport_height-0.5);
  597. Mat_Init_4X4(m, cam->view_dist,    0,               0,    0, 
  598.                 0,                -cam->view_dist,  0,    0, 
  599.                 alpha,             beta,            1,    1,
  600.                 0,                 0,               0,    0);
  601. } // end Build_Camera_To_Screen_MATRIX4X4()
  602. ///////////////////////////////////////////////////////////
  603. void Transform_OBJECT4DV1(OBJECT4DV1_PTR obj, // object to transform
  604.                           MATRIX4X4_PTR mt,   // transformation matrix
  605.                           int coord_select,   // selects coords to transform
  606.                           int transform_basis) // flags if vector orientation
  607.                                                // should be transformed too
  608. {
  609. // this function simply transforms all of the vertices in the local or trans
  610. // array by the sent matrix
  611. // what coordinates should be transformed?
  612. switch(coord_select)
  613.       {
  614.       case TRANSFORM_LOCAL_ONLY:
  615.       {
  616.       // transform each local/model vertex of the object mesh in place
  617.       for (int vertex=0; vertex < obj->num_vertices; vertex++)
  618.           {
  619.           POINT4D presult; // hold result of each transformation
  620.           // transform point
  621.           Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex], mt, &presult);
  622.           // store result back
  623.           VECTOR4D_COPY(&obj->vlist_local[vertex], &presult); 
  624.           } // end for index
  625.       } break;
  626.  
  627.       case TRANSFORM_TRANS_ONLY:
  628.       {
  629.       // transform each "transformed" vertex of the object mesh in place
  630.       // remember, the idea of the vlist_trans[] array is to accumulate
  631.       // transformations
  632.       for (int vertex=0; vertex < obj->num_vertices; vertex++)
  633.           {
  634.           POINT4D presult; // hold result of each transformation
  635.           // transform point
  636.           Mat_Mul_VECTOR4D_4X4(&obj->vlist_trans[vertex], mt, &presult);
  637.           // store result back
  638.           VECTOR4D_COPY(&obj->vlist_trans[vertex], &presult); 
  639.           } // end for index
  640.       } break;
  641.       case TRANSFORM_LOCAL_TO_TRANS:
  642.       {
  643.       // transform each local/model vertex of the object mesh and store result
  644.       // in "transformed" vertex list
  645.       for (int vertex=0; vertex < obj->num_vertices; vertex++)
  646.           {
  647.           POINT4D presult; // hold result of each transformation
  648.           // transform point
  649.           Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex], mt, &obj->vlist_trans[vertex]);
  650.           } // end for index
  651.       } break;
  652.       default: break;
  653. } // end switch
  654. // finally, test if transform should be applied to orientation basis
  655. // hopefully this is a rotation, otherwise the basis will get corrupted
  656. if (transform_basis)
  657.    {
  658.    // now rotate orientation basis for object
  659.    VECTOR4D vresult; // use to rotate each orientation vector axis
  660.    // rotate ux of basis
  661.    Mat_Mul_VECTOR4D_4X4(&obj->ux, mt, &vresult);
  662.    VECTOR4D_COPY(&obj->ux, &vresult); 
  663.    // rotate uy of basis
  664.    Mat_Mul_VECTOR4D_4X4(&obj->uy, mt, &vresult);
  665.    VECTOR4D_COPY(&obj->uy, &vresult); 
  666.    // rotate uz of basis
  667.    Mat_Mul_VECTOR4D_4X4(&obj->uz, mt, &vresult);
  668.    VECTOR4D_COPY(&obj->uz, &vresult); 
  669.    } // end if
  670. } // end Transform_OBJECT4DV1
  671.                                    
  672. ///////////////////////////////////////////////////////////
  673. void Rotate_XYZ_OBJECT4DV1(OBJECT4DV1_PTR obj, // object to rotate
  674.                           float theta_x,      // euler angles
  675.                           float theta_y, 
  676.                           float theta_z)
  677. {
  678. // this function rotates and object parallel to the
  679. // XYZ axes in that order or a subset thereof, without
  680. // matrices (at least externally sent)
  681. // modifies the object's local vertex list 
  682. // additionally it rotates the unit directional vectors
  683. // that track the objects orientation, also note that each
  684. // time this function is called it calls the rotation generation
  685. // function, this is wastefull if a number of object are being rotated
  686. // by the same matrix, therefore, if that's the case, then generate the
  687. // rotation matrix, store it, and call the general Transform_OBJECT4DV1()
  688. // with the matrix
  689.  
  690. MATRIX4X4 mrot; // used to store generated rotation matrix
  691. // generate rotation matrix, no way to avoid rotation with a matrix
  692. // too much math to do manually!
  693. Build_XYZ_Rotation_MATRIX4X4(theta_x, theta_y, theta_z, &mrot);
  694. // now simply rotate each point of the mesh in local/model coordinates
  695. for (int vertex=0; vertex < obj->num_vertices; vertex++)
  696.     {
  697.     POINT4D presult; // hold result of each transformation
  698.     // transform point
  699.     Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex], &mrot, &presult);
  700.     // store result back
  701.     VECTOR4D_COPY(&obj->vlist_local[vertex], &presult); 
  702.     } // end for index
  703. // now rotate orientation basis for object
  704. VECTOR4D vresult; // use to rotate each orientation vector axis
  705. // rotate ux of basis
  706. Mat_Mul_VECTOR4D_4X4(&obj->ux, &mrot, &vresult);
  707. VECTOR4D_COPY(&obj->ux, &vresult); 
  708. // rotate uy of basis
  709. Mat_Mul_VECTOR4D_4X4(&obj->uy, &mrot, &vresult);
  710. VECTOR4D_COPY(&obj->uy, &vresult); 
  711. // rotate uz of basis
  712. Mat_Mul_VECTOR4D_4X4(&obj->uz, &mrot, &vresult);
  713. VECTOR4D_COPY(&obj->uz, &vresult); 
  714. } // end Rotate_XYZ_OBJECT4DV1
  715. ////////////////////////////////////////////////////////////
  716. void Model_To_World_OBJECT4DV1(OBJECT4DV1_PTR obj, int coord_select)
  717. {
  718. // NOTE: Not matrix based
  719. // this function converts the local model coordinates of the
  720. // sent object into world coordinates, the results are stored
  721. // in the transformed vertex list (vlist_trans) within the object
  722. // interate thru vertex list and transform all the model/local 
  723. // coords to world coords by translating the vertex list by
  724. // the amount world_pos and storing the results in vlist_trans[]
  725. if (coord_select == TRANSFORM_LOCAL_TO_TRANS)
  726. {
  727. for (int vertex=0; vertex < obj->num_vertices; vertex++)
  728.     {
  729.     // translate vertex
  730.     VECTOR4D_Add(&obj->vlist_local[vertex], &obj->world_pos, &obj->vlist_trans[vertex]);
  731.     } // end for vertex
  732. } // end if local
  733. else
  734. { // TRANSFORM_TRANS_ONLY
  735. for (int vertex=0; vertex < obj->num_vertices; vertex++)
  736.     {
  737.     // translate vertex
  738.     VECTOR4D_Add(&obj->vlist_trans[vertex], &obj->world_pos, &obj->vlist_trans[vertex]);
  739.     } // end for vertex
  740. } // end else trans
  741. } // end Model_To_World_OBJECT4DV1
  742. ////////////////////////////////////////////////////////////
  743. int Cull_OBJECT4DV1(OBJECT4DV1_PTR obj,  // object to cull
  744.                     CAM4DV1_PTR cam,     // camera to cull relative to
  745.                      int cull_flags)     // clipping planes to consider
  746. {
  747. // NOTE: is matrix based
  748. // this function culls an entire object from the viewing
  749. // frustrum by using the sent camera information and object
  750. // the cull_flags determine what axes culling should take place
  751. // x, y, z or all which is controlled by ORing the flags
  752. // together
  753. // if the object is culled its state is modified thats all
  754. // this function assumes that both the camera and the object
  755. // are valid!
  756. // step 1: transform the center of the object's bounding
  757. // sphere into camera space
  758. POINT4D sphere_pos; // hold result of transforming center of bounding sphere
  759. // transform point
  760. Mat_Mul_VECTOR4D_4X4(&obj->world_pos, &cam->mcam, &sphere_pos);
  761. // step 2:  based on culling flags remove the object
  762. if (cull_flags & CULL_OBJECT_Z_PLANE)
  763. {
  764. // cull only based on z clipping planes
  765. // test far plane
  766. if ( ((sphere_pos.z - obj->max_radius) > cam->far_clip_z) ||
  767.      ((sphere_pos.z + obj->max_radius) < cam->near_clip_z) )
  768.    { 
  769.    SET_BIT(obj->state, OBJECT4DV1_STATE_CULLED);
  770.    return(1);
  771.    } // end if
  772. } // end if
  773. if (cull_flags & CULL_OBJECT_X_PLANE)
  774. {
  775. // cull only based on x clipping planes
  776. // we could use plane equations, but simple similar triangles
  777. // is easier since this is really a 2D problem
  778. // if the view volume is 90 degrees the the problem is trivial
  779. // buts lets assume its not
  780. // test the the right and left clipping planes against the leftmost and rightmost
  781. // points of the bounding sphere
  782. float z_test = (0.5)*cam->viewplane_width*sphere_pos.z/cam->view_dist;
  783. if ( ((sphere_pos.x-obj->max_radius) > z_test)  || // right side
  784.      ((sphere_pos.x+obj->max_radius) < -z_test) )  // left side, note sign change
  785.    { 
  786.    SET_BIT(obj->state, OBJECT4DV1_STATE_CULLED);
  787.    return(1);
  788.    } // end if
  789. } // end if
  790. if (cull_flags & CULL_OBJECT_Y_PLANE)
  791. {
  792. // cull only based on y clipping planes
  793. // we could use plane equations, but simple similar triangles
  794. // is easier since this is really a 2D problem
  795. // if the view volume is 90 degrees the the problem is trivial
  796. // buts lets assume its not
  797. // test the the top and bottom clipping planes against the bottommost and topmost
  798. // points of the bounding sphere
  799. float z_test = (0.5)*cam->viewplane_height*sphere_pos.z/cam->view_dist;
  800. if ( ((sphere_pos.y-obj->max_radius) > z_test)  || // top side
  801.      ((sphere_pos.y+obj->max_radius) < -z_test) )  // bottom side, note sign change
  802.    { 
  803.    SET_BIT(obj->state, OBJECT4DV1_STATE_CULLED);
  804.    return(1);
  805.    } // end if
  806. } // end if
  807. // return failure to cull
  808. return(0);
  809. } // end Cull_OBJECT4DV1
  810. ////////////////////////////////////////////////////////////
  811. void Remove_Backfaces_OBJECT4DV1(OBJECT4DV1_PTR obj, CAM4DV1_PTR cam)
  812. {
  813. // NOTE: this is not a matrix based function
  814. // this function removes the backfaces from an object's
  815. // polygon mesh, the function does this based on the vertex
  816. // data in vlist_trans along with the camera position (only)
  817. // note that only the backface state is set in each polygon
  818. // test if the object is culled
  819. if (obj->state & OBJECT4DV1_STATE_CULLED)
  820.    return;
  821. // process each poly in mesh
  822. for (int poly=0; poly < obj->num_polys; poly++)
  823.     {
  824.     // acquire polygon
  825.     POLY4DV1_PTR curr_poly = &obj->plist[poly];
  826.     // is this polygon valid?
  827.     // test this polygon if and only if it's not clipped, not culled,
  828.     // active, and visible and not 2 sided. Note we test for backface in the event that
  829.     // a previous call might have already determined this, so why work
  830.     // harder!
  831.     if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  832.          (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  833.          (curr_poly->attr  & POLY4DV1_ATTR_2SIDED)    ||
  834.          (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  835.        continue; // move onto next poly
  836.     
  837.     // extract vertex indices into master list, rember the polygons are 
  838.     // NOT self contained, but based on the vertex list stored in the object
  839.     // itself
  840.     int vindex_0 = curr_poly->vert[0];
  841.     int vindex_1 = curr_poly->vert[1];
  842.     int vindex_2 = curr_poly->vert[2];
  843.     
  844.     // we will use the transformed polygon vertex list since the backface removal
  845.     // only makes sense at the world coord stage further of the pipeline 
  846.     // we need to compute the normal of this polygon face, and recall
  847.     // that the vertices are in cw order, u = p0->p1, v=p0->p2, n=uxv
  848.     VECTOR4D u, v, n;
  849.  
  850.     // build u, v
  851.     VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
  852.     VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
  853.     // compute cross product
  854.     VECTOR4D_Cross(&u, &v, &n);
  855.     // now create eye vector to viewpoint
  856.     VECTOR4D view;
  857.     VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &cam->pos, &view); 
  858.     // and finally, compute the dot product
  859.     float dp = VECTOR4D_Dot(&n, &view);
  860.     // if the sign is > 0 then visible, 0 = scathing, < 0 invisible
  861.     if (dp <= 0.0 )
  862.        SET_BIT(curr_poly->state, POLY4DV1_STATE_BACKFACE);
  863.     } // end for poly
  864. } // end Remove_Backfaces_OBJECT4DV1
  865. ////////////////////////////////////////////////////////////
  866. void Remove_Backfaces_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, CAM4DV1_PTR cam)
  867. {
  868. // NOTE: this is not a matrix based function
  869. // this function removes the backfaces from polygon list
  870. // the function does this based on the polygon list data
  871. // tvlist along with the camera position (only)
  872. // note that only the backface state is set in each polygon
  873. for (int poly = 0; poly < rend_list->num_polys; poly++)
  874.     {
  875.     // acquire current polygon
  876.     POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  877.     // is this polygon valid?
  878.     // test this polygon if and only if it's not clipped, not culled,
  879.     // active, and visible and not 2 sided. Note we test for backface in the event that
  880.     // a previous call might have already determined this, so why work
  881.     // harder!
  882.     if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  883.         (curr_poly->state & POLY4DV1_STATE_CLIPPED ) || 
  884.         (curr_poly->attr  & POLY4DV1_ATTR_2SIDED)    ||
  885.         (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  886.         continue; // move onto next poly
  887.     
  888.         // we need to compute the normal of this polygon face, and recall
  889.         // that the vertices are in cw order, u = p0->p1, v=p0->p2, n=uxv
  890.         VECTOR4D u, v, n;
  891.  
  892.         // build u, v
  893.         VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
  894.         VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
  895.         // compute cross product
  896.         VECTOR4D_Cross(&u, &v, &n);
  897.         // now create eye vector to viewpoint
  898.         VECTOR4D view;
  899.         VECTOR4D_Build(&curr_poly->tvlist[0], &cam->pos, &view); 
  900.         // and finally, compute the dot product
  901.         float dp = VECTOR4D_Dot(&n, &view);
  902.         // if the sign is > 0 then visible, 0 = scathing, < 0 invisible
  903.         if (dp <= 0.0 )
  904.             SET_BIT(curr_poly->state, POLY4DV1_STATE_BACKFACE);
  905.          } // end for poly
  906. } // end Remove_Backfaces_RENDERLIST4DV1
  907. ////////////////////////////////////////////////////////////
  908. void World_To_Camera_OBJECT4DV1(OBJECT4DV1_PTR obj, CAM4DV1_PTR cam)
  909. {
  910. // NOTE: this is a matrix based function
  911. // this function transforms the world coordinates of an object
  912. // into camera coordinates, based on the sent camera matrix
  913. // but it totally disregards the polygons themselves,
  914. // it only works on the vertices in the vlist_trans[] list
  915. // this is one way to do it, you might instead transform
  916. // the global list of polygons in the render list since you 
  917. // are guaranteed that those polys represent geometry that 
  918. // has passed thru backfaces culling (if any)
  919. // transform each vertex in the object to camera coordinates
  920. // assumes the object has already been transformed to world
  921. // coordinates and the result is in vlist_trans[]
  922. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  923.     {
  924.     // transform the vertex by the mcam matrix within the camera
  925.     // it better be valid!
  926.     POINT4D presult; // hold result of each transformation
  927.     // transform point
  928.     Mat_Mul_VECTOR4D_4X4(&obj->vlist_trans[vertex], &cam->mcam, &presult);
  929.     // store result back
  930.     VECTOR4D_COPY(&obj->vlist_trans[vertex], &presult); 
  931.     } // end for vertex
  932. } // end World_To_Camera_OBJECT4DV1
  933. ////////////////////////////////////////////////////////////
  934. void Camera_To_Perspective_OBJECT4DV1(OBJECT4DV1_PTR obj, CAM4DV1_PTR cam)
  935. {
  936. // NOTE: this is not a matrix based function
  937. // this function transforms the camera coordinates of an object
  938. // into perspective coordinates, based on the 
  939. // sent camera object, but it totally disregards the polygons themselves,
  940. // it only works on the vertices in the vlist_trans[] list
  941. // this is one way to do it, you might instead transform
  942. // the global list of polygons in the render list since you 
  943. // are guaranteed that those polys represent geometry that 
  944. // has passed thru backfaces culling (if any)
  945. // finally this function is really for experimental reasons only
  946. // you would probably never let an object stay intact this far down
  947. // the pipeline, since it's probably that there's only a single polygon
  948. // that is visible! But this function has to transform the whole mesh!
  949. // transform each vertex in the object to perspective coordinates
  950. // assumes the object has already been transformed to camera
  951. // coordinates and the result is in vlist_trans[]
  952. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  953.     {
  954.     float z = obj->vlist_trans[vertex].z;
  955.     // transform the vertex by the view parameters in the camera
  956.     obj->vlist_trans[vertex].x = cam->view_dist*obj->vlist_trans[vertex].x/z;
  957.     obj->vlist_trans[vertex].y = cam->view_dist*obj->vlist_trans[vertex].y*cam->aspect_ratio/z;
  958.     // z = z, so no change
  959.     // not that we are NOT dividing by the homogenous w coordinate since
  960.     // we are not using a matrix operation for this version of the function 
  961.   
  962.     } // end for vertex
  963. } // end Camera_To_Perspective_OBJECT4DV1
  964. //////////////////////////////////////////////////////////////
  965. void Camera_To_Perspective_Screen_OBJECT4DV1(OBJECT4DV1_PTR obj, CAM4DV1_PTR cam)
  966. {
  967. // NOTE: this is not a matrix based function
  968. // this function transforms the camera coordinates of an object
  969. // into Screen scaled perspective coordinates, based on the 
  970. // sent camera object, that is, view_dist_h and view_dist_v 
  971. // should be set to cause the desired (width X height)
  972. // projection of the vertices, but the function totally 
  973. // disregards the polygons themselves,
  974. // it only works on the vertices in the vlist_trans[] list
  975. // this is one way to do it, you might instead transform
  976. // the global list of polygons in the render list since you 
  977. // are guaranteed that those polys represent geometry that 
  978. // has passed thru backfaces culling (if any)
  979. // finally this function is really for experimental reasons only
  980. // you would probably never let an object stay intact this far down
  981. // the pipeline, since it's probably that there's only a single polygon
  982. // that is visible! But this function has to transform the whole mesh!
  983. // finally, the function also inverts the y axis, so the coordinates
  984. // generated from this function ARE screen coordinates and ready for
  985. // rendering
  986. float alpha = (0.5*cam->viewport_width-0.5);
  987. float beta  = (0.5*cam->viewport_height-0.5);
  988. // transform each vertex in the object to perspective screen coordinates
  989. // assumes the object has already been transformed to camera
  990. // coordinates and the result is in vlist_trans[]
  991. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  992.     {
  993.     float z = obj->vlist_trans[vertex].z;
  994.     // transform the vertex by the view parameters in the camera
  995.     obj->vlist_trans[vertex].x = cam->view_dist*obj->vlist_trans[vertex].x/z;
  996.     obj->vlist_trans[vertex].y = cam->view_dist*obj->vlist_trans[vertex].y/z;
  997.     // z = z, so no change
  998.     // not that we are NOT dividing by the homogenous w coordinate since
  999.     // we are not using a matrix operation for this version of the function 
  1000.     // now the coordinates are in the range x:(-viewport_width/2 to viewport_width/2)
  1001.     // and y:(-viewport_height/2 to viewport_height/2), thus we need a translation and
  1002.     // since the y-axis is inverted, we need to invert y to complete the screen 
  1003.     // transform:
  1004.     obj->vlist_trans[vertex].x =  obj->vlist_trans[vertex].x + alpha;
  1005.     obj->vlist_trans[vertex].y = -obj->vlist_trans[vertex].y + beta;
  1006.     } // end for vertex
  1007. } // end Camera_To_Perspective_Screen_OBJECT4DV1
  1008. //////////////////////////////////////////////////////////////
  1009. void Perspective_To_Screen_OBJECT4DV1(OBJECT4DV1_PTR obj, CAM4DV1_PTR cam)
  1010. {
  1011. // NOTE: this is not a matrix based function
  1012. // this function transforms the perspective coordinates of an object
  1013. // into screen coordinates, based on the sent viewport info
  1014. // but it totally disregards the polygons themselves,
  1015. // it only works on the vertices in the vlist_trans[] list
  1016. // this is one way to do it, you might instead transform
  1017. // the global list of polygons in the render list since you 
  1018. // are guaranteed that those polys represent geometry that 
  1019. // has passed thru backfaces culling (if any)
  1020. // finally this function is really for experimental reasons only
  1021. // you would probably never let an object stay intact this far down
  1022. // the pipeline, since it's probably that there's only a single polygon
  1023. // that is visible! But this function has to transform the whole mesh!
  1024. // this function would be called after a perspective
  1025. // projection was performed on the object
  1026. // transform each vertex in the object to screen coordinates
  1027. // assumes the object has already been transformed to perspective
  1028. // coordinates and the result is in vlist_trans[]
  1029. float alpha = (0.5*cam->viewport_width-0.5);
  1030. float beta  = (0.5*cam->viewport_height-0.5);
  1031. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1032.     {
  1033.     // assumes the vertex is in perspective normalized coords from -1 to 1
  1034.     // on each axis, simple scale them to viewport and invert y axis and project
  1035.     // to screen
  1036.     // transform the vertex by the view parameters in the camera
  1037.     obj->vlist_trans[vertex].x = alpha + alpha*obj->vlist_trans[vertex].x;
  1038.     obj->vlist_trans[vertex].y = beta  - beta *obj->vlist_trans[vertex].y;
  1039.   
  1040.     } // end for vertex
  1041. } // end Perspective_To_Screen_OBJECT4DV1
  1042. /////////////////////////////////////////////////////////////
  1043. void Convert_From_Homogeneous4D_OBJECT4DV1(OBJECT4DV1_PTR obj)
  1044. {
  1045. // this function convertes all vertices in the transformed
  1046. // vertex list from 4D homogeneous coordinates to normal 3D coordinates
  1047. // by dividing each x,y,z component by w
  1048. for (int vertex = 0; vertex < obj->num_vertices; vertex++)
  1049.     {
  1050.     // convert to non-homogenous coords
  1051.     VECTOR4D_DIV_BY_W(&obj->vlist_trans[vertex]);     
  1052.     } // end for vertex
  1053. } // end Convert_From_Homogeneous4D_OBJECT4DV1
  1054. /////////////////////////////////////////////////////////////
  1055. void Transform_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, // render list to transform
  1056.                               MATRIX4X4_PTR mt,   // transformation matrix
  1057.                               int coord_select)   // selects coords to transform
  1058. {
  1059. // this function simply transforms all of the polygons vertices in the local or trans
  1060. // array of the render list by the sent matrix
  1061. // what coordinates should be transformed?
  1062. switch(coord_select)
  1063.       {
  1064.       case TRANSFORM_LOCAL_ONLY:
  1065.       {
  1066.       for (int poly = 0; poly < rend_list->num_polys; poly++)
  1067.           {
  1068.           // acquire current polygon
  1069.           POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1070.           // is this polygon valid?
  1071.           // transform this polygon if and only if it's not clipped, not culled,
  1072.           // active, and visible, note however the concept of "backface" is 
  1073.           // irrelevant in a wire frame engine though
  1074.           if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1075.               (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1076.               (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1077.              continue; // move onto next poly
  1078.           // all good, let's transform 
  1079.           for (int vertex = 0; vertex < 3; vertex++)
  1080.               {
  1081.               // transform the vertex by mt
  1082.               POINT4D presult; // hold result of each transformation
  1083.               // transform point
  1084.               Mat_Mul_VECTOR4D_4X4(&curr_poly->vlist[vertex], mt, &presult);
  1085.               // store result back
  1086.               VECTOR4D_COPY(&curr_poly->vlist[vertex], &presult); 
  1087.               } // end for vertex
  1088.  
  1089.           } // end for poly
  1090.       } break;
  1091.  
  1092.       case TRANSFORM_TRANS_ONLY:
  1093.       {
  1094.       // transform each "transformed" vertex of the render list
  1095.       // remember, the idea of the tvlist[] array is to accumulate
  1096.       // transformations
  1097.       for (int poly = 0; poly < rend_list->num_polys; poly++)
  1098.           {
  1099.           // acquire current polygon
  1100.           POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1101.           // is this polygon valid?
  1102.           // transform this polygon if and only if it's not clipped, not culled,
  1103.           // active, and visible, note however the concept of "backface" is 
  1104.           // irrelevant in a wire frame engine though
  1105.           if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1106.               (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1107.               (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1108.              continue; // move onto next poly
  1109.           // all good, let's transform 
  1110.           for (int vertex = 0; vertex < 3; vertex++)
  1111.               {
  1112.               // transform the vertex by mt
  1113.               POINT4D presult; // hold result of each transformation
  1114.               // transform point
  1115.               Mat_Mul_VECTOR4D_4X4(&curr_poly->tvlist[vertex], mt, &presult);
  1116.               // store result back
  1117.               VECTOR4D_COPY(&curr_poly->tvlist[vertex], &presult); 
  1118.               } // end for vertex
  1119.  
  1120.           } // end for poly
  1121.       } break;
  1122.       case TRANSFORM_LOCAL_TO_TRANS:
  1123.       {
  1124.       // transform each local/model vertex of the render list and store result
  1125.       // in "transformed" vertex list
  1126.       for (int poly = 0; poly < rend_list->num_polys; poly++)
  1127.           {
  1128.           // acquire current polygon
  1129.           POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1130.           // is this polygon valid?
  1131.           // transform this polygon if and only if it's not clipped, not culled,
  1132.           // active, and visible, note however the concept of "backface" is 
  1133.           // irrelevant in a wire frame engine though
  1134.           if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1135.               (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1136.               (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1137.              continue; // move onto next poly
  1138.           // all good, let's transform 
  1139.           for (int vertex = 0; vertex < 3; vertex++)
  1140.               {
  1141.               // transform the vertex by mt
  1142.               Mat_Mul_VECTOR4D_4X4(&curr_poly->vlist[vertex], mt, &curr_poly->tvlist[vertex]);
  1143.               } // end for vertex
  1144.  
  1145.           } // end for poly
  1146.       } break;
  1147.       default: break;
  1148. } // end switch
  1149. } // end Transform_RENDERLIST4DV1
  1150. /////////////////////////////////////////////////////////////////////////
  1151. void Model_To_World_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, 
  1152.                                   POINT4D_PTR world_pos, 
  1153.                                   int coord_select)
  1154. {
  1155. // NOTE: Not matrix based
  1156. // this function converts the local model coordinates of the
  1157. // sent render list into world coordinates, the results are stored
  1158. // in the transformed vertex list (tvlist) within the renderlist
  1159. // interate thru vertex list and transform all the model/local 
  1160. // coords to world coords by translating the vertex list by
  1161. // the amount world_pos and storing the results in tvlist[]
  1162. // is this polygon valid?
  1163. if (coord_select == TRANSFORM_LOCAL_TO_TRANS)
  1164.    {
  1165.    for (int poly = 0; poly < rend_list->num_polys; poly++)
  1166.        {
  1167.        // acquire current polygon
  1168.        POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1169.        // transform this polygon if and only if it's not clipped, not culled,
  1170.        // active, and visible, note however the concept of "backface" is 
  1171.        // irrelevant in a wire frame engine though
  1172.        if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1173.            (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1174.            (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1175.           continue; // move onto next poly
  1176.    // all good, let's transform 
  1177.    for (int vertex = 0; vertex < 3; vertex++)
  1178.        {
  1179.        // translate vertex
  1180.        VECTOR4D_Add(&curr_poly->vlist[vertex], world_pos, &curr_poly->tvlist[vertex]);
  1181.        } // end for vertex
  1182.    } // end for poly
  1183. } // end if local
  1184. else // TRANSFORM_TRANS_ONLY
  1185. {
  1186. for (int poly = 0; poly < rend_list->num_polys; poly++)
  1187.     {
  1188.     // acquire current polygon
  1189.     POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1190.    // transform this polygon if and only if it's not clipped, not culled,
  1191.    // active, and visible, note however the concept of "backface" is 
  1192.    // irrelevant in a wire frame engine though
  1193.    if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1194.        (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1195.        (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1196.         continue; // move onto next poly
  1197.    for (int vertex = 0; vertex < 3; vertex++)
  1198.        {
  1199.        // translate vertex
  1200.        VECTOR4D_Add(&curr_poly->tvlist[vertex], world_pos, &curr_poly->tvlist[vertex]);
  1201.        } // end for vertex
  1202.     } // end for poly
  1203. } // end else
  1204. } // end Model_To_World_RENDERLIST4DV1
  1205. ////////////////////////////////////////////////////////////
  1206. void Convert_From_Homogeneous4D_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list)
  1207. {
  1208. // this function convertes all valid polygons vertices in the transformed
  1209. // vertex list from 4D homogeneous coordinates to normal 3D coordinates
  1210. // by dividing each x,y,z component by w
  1211. for (int poly = 0; poly < rend_list->num_polys; poly++)
  1212. {
  1213. // acquire current polygon
  1214. POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1215. // is this polygon valid?
  1216. // transform this polygon if and only if it's not clipped, not culled,
  1217. // active, and visible, note however the concept of "backface" is 
  1218. // irrelevant in a wire frame engine though
  1219. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1220.      (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1221.      (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1222.        continue; // move onto next poly
  1223. // all good, let's transform 
  1224. for (int vertex = 0; vertex < 3; vertex++)
  1225.     {
  1226.     // convert to non-homogenous coords
  1227.     VECTOR4D_DIV_BY_W(&curr_poly->tvlist[vertex]); 
  1228.     } // end for vertex
  1229. } // end for poly
  1230. } // end Convert_From_Homogeneous4D_RENDERLIST4DV1
  1231. /////////////////////////////////////////////////////////////////////////
  1232. void World_To_Camera_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, 
  1233.                                  CAM4DV1_PTR cam)
  1234. {
  1235. // NOTE: this is a matrix based function
  1236. // this function transforms each polygon in the global render list
  1237. // to camera coordinates based on the sent camera transform matrix
  1238. // you would use this function instead of the object based function
  1239. // if you decided earlier in the pipeline to turn each object into 
  1240. // a list of polygons and then add them to the global render list
  1241. // the conversion of an object into polygons probably would have
  1242. // happened after object culling, local transforms, local to world
  1243. // and backface culling, so the minimum number of polygons from
  1244. // each object are in the list, note that the function assumes
  1245. // that at LEAST the local to world transform has been called
  1246. // and the polygon data is in the transformed list tvlist of
  1247. // the POLYF4DV1 object
  1248. // transform each polygon in the render list into camera coordinates
  1249. // assumes the render list has already been transformed to world
  1250. // coordinates and the result is in tvlist[] of each polygon object
  1251. for (int poly = 0; poly < rend_list->num_polys; poly++)
  1252. {
  1253. // acquire current polygon
  1254. POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1255. // is this polygon valid?
  1256. // transform this polygon if and only if it's not clipped, not culled,
  1257. // active, and visible, note however the concept of "backface" is 
  1258. // irrelevant in a wire frame engine though
  1259. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1260.      (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1261.      (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1262.        continue; // move onto next poly
  1263. // all good, let's transform 
  1264. for (int vertex = 0; vertex < 3; vertex++)
  1265.     {
  1266.     // transform the vertex by the mcam matrix within the camera
  1267.     // it better be valid!
  1268.     POINT4D presult; // hold result of each transformation
  1269.     // transform point
  1270.     Mat_Mul_VECTOR4D_4X4(&curr_poly->tvlist[vertex], &cam->mcam, &presult);
  1271.     // store result back
  1272.     VECTOR4D_COPY(&curr_poly->tvlist[vertex], &presult); 
  1273.     } // end for vertex
  1274. } // end for poly
  1275. } // end World_To_Camera_RENDERLIST4DV1
  1276. ///////////////////////////////////////////////////////////////
  1277. void Camera_To_Perspective_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, 
  1278.                                                CAM4DV1_PTR cam)
  1279. {
  1280. // NOTE: this is not a matrix based function
  1281. // this function transforms each polygon in the global render list
  1282. // into perspective coordinates, based on the 
  1283. // sent camera object, 
  1284. // you would use this function instead of the object based function
  1285. // if you decided earlier in the pipeline to turn each object into 
  1286. // a list of polygons and then add them to the global render list
  1287. // transform each polygon in the render list into camera coordinates
  1288. // assumes the render list has already been transformed to world
  1289. // coordinates and the result is in tvlist[] of each polygon object
  1290. for (int poly = 0; poly < rend_list->num_polys; poly++)
  1291. {
  1292. // acquire current polygon
  1293. POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1294. // is this polygon valid?
  1295. // transform this polygon if and only if it's not clipped, not culled,
  1296. // active, and visible, note however the concept of "backface" is 
  1297. // irrelevant in a wire frame engine though
  1298. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1299.      (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1300.      (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1301.        continue; // move onto next poly
  1302. // all good, let's transform 
  1303. for (int vertex = 0; vertex < 3; vertex++)
  1304.     {
  1305.     float z = curr_poly->tvlist[vertex].z;
  1306.     // transform the vertex by the view parameters in the camera
  1307.     curr_poly->tvlist[vertex].x = cam->view_dist*curr_poly->tvlist[vertex].x/z;
  1308.     curr_poly->tvlist[vertex].y = cam->view_dist*curr_poly->tvlist[vertex].y*cam->aspect_ratio/z;
  1309.     // z = z, so no change
  1310.     // not that we are NOT dividing by the homogenous w coordinate since
  1311.     // we are not using a matrix operation for this version of the function 
  1312.     } // end for vertex
  1313. } // end for poly
  1314. } // end Camera_To_Perspective_RENDERLIST4DV1
  1315. ////////////////////////////////////////////////////////////////
  1316. void Camera_To_Perspective_Screen_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, 
  1317.                                                  CAM4DV1_PTR cam)
  1318. {
  1319. // NOTE: this is not a matrix based function
  1320. // this function transforms the camera coordinates of an object
  1321. // into Screen scaled perspective coordinates, based on the 
  1322. // sent camera object, that is, view_dist_h and view_dist_v 
  1323. // should be set to cause the desired (viewport_width X viewport_height)
  1324. // it only works on the vertices in the tvlist[] list
  1325. // finally, the function also inverts the y axis, so the coordinates
  1326. // generated from this function ARE screen coordinates and ready for
  1327. // rendering
  1328. // transform each polygon in the render list to perspective screen 
  1329. // coordinates assumes the render list has already been transformed 
  1330. // to camera coordinates and the result is in tvlist[]
  1331. for (int poly = 0; poly < rend_list->num_polys; poly++)
  1332. {
  1333. // acquire current polygon
  1334. POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1335. // is this polygon valid?
  1336. // transform this polygon if and only if it's not clipped, not culled,
  1337. // active, and visible, note however the concept of "backface" is 
  1338. // irrelevant in a wire frame engine though
  1339. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1340.      (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1341.      (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1342.        continue; // move onto next poly
  1343. float alpha = (0.5*cam->viewport_width-0.5);
  1344. float beta  = (0.5*cam->viewport_height-0.5);
  1345. // all good, let's transform 
  1346. for (int vertex = 0; vertex < 3; vertex++)
  1347.     {
  1348.     float z = curr_poly->tvlist[vertex].z;
  1349.     // transform the vertex by the view parameters in the camera
  1350.     curr_poly->tvlist[vertex].x = cam->view_dist*curr_poly->tvlist[vertex].x/z;
  1351.     curr_poly->tvlist[vertex].y = cam->view_dist*curr_poly->tvlist[vertex].y/z;
  1352.     // z = z, so no change
  1353.     // not that we are NOT dividing by the homogenous w coordinate since
  1354.     // we are not using a matrix operation for this version of the function 
  1355.     // now the coordinates are in the range x:(-viewport_width/2 to viewport_width/2)
  1356.     // and y:(-viewport_height/2 to viewport_height/2), thus we need a translation and
  1357.     // since the y-axis is inverted, we need to invert y to complete the screen 
  1358.     // transform:
  1359.     curr_poly->tvlist[vertex].x =  curr_poly->tvlist[vertex].x + alpha; 
  1360.     curr_poly->tvlist[vertex].y = -curr_poly->tvlist[vertex].y + beta;
  1361.     } // end for vertex
  1362. } // end for poly
  1363. } // end Camera_To_Perspective_Screen_RENDERLIST4DV1
  1364. //////////////////////////////////////////////////////////////
  1365. void Perspective_To_Screen_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, 
  1366.                                                CAM4DV1_PTR cam)
  1367. {
  1368. // NOTE: this is not a matrix based function
  1369. // this function transforms the perspective coordinates of the render
  1370. // list into screen coordinates, based on the sent viewport in the camera
  1371. // assuming that the viewplane coordinates were normalized
  1372. // you would use this function instead of the object based function
  1373. // if you decided earlier in the pipeline to turn each object into 
  1374. // a list of polygons and then add them to the global render list
  1375. // you would only call this function if you previously performed
  1376. // a normalized perspective transform
  1377. // transform each polygon in the render list from perspective to screen 
  1378. // coordinates assumes the render list has already been transformed 
  1379. // to normalized perspective coordinates and the result is in tvlist[]
  1380. for (int poly = 0; poly < rend_list->num_polys; poly++)
  1381. {
  1382. // acquire current polygon
  1383. POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
  1384. // is this polygon valid?
  1385. // transform this polygon if and only if it's not clipped, not culled,
  1386. // active, and visible, note however the concept of "backface" is 
  1387. // irrelevant in a wire frame engine though
  1388. if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1389.      (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1390.      (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1391.        continue; // move onto next poly
  1392. float alpha = (0.5*cam->viewport_width-0.5);
  1393. float beta  = (0.5*cam->viewport_height-0.5);
  1394. // all good, let's transform 
  1395. for (int vertex = 0; vertex < 3; vertex++)
  1396.     {
  1397.     // the vertex is in perspective normalized coords from -1 to 1
  1398.     // on each axis, simple scale them and invert y axis and project
  1399.     // to screen
  1400.     // transform the vertex by the view parameters in the camera
  1401.     curr_poly->tvlist[vertex].x = alpha + alpha*curr_poly->tvlist[vertex].x;
  1402.     curr_poly->tvlist[vertex].y = beta  - beta *curr_poly->tvlist[vertex].y;
  1403.     } // end for vertex
  1404. } // end for poly
  1405. } // end Perspective_To_Screen_RENDERLIST4DV1
  1406. ///////////////////////////////////////////////////////////////
  1407. void Reset_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list)
  1408. {
  1409. // this function intializes and resets the sent render list and
  1410. // redies it for polygons/faces to be inserted into it
  1411. // note that the render list in this version is composed
  1412. // of an array FACE4DV1 pointer objects, you call this
  1413. // function each frame
  1414. // since we are tracking the number of polys in the
  1415. // list via num_polys we can set it to 0
  1416. // but later we will want a more robust scheme if
  1417. // we generalize the linked list more and disconnect
  1418. // it from the polygon pointer list
  1419. rend_list->num_polys = 0; // that was hard!
  1420. }  // Reset_RENDERLIST4DV1
  1421. ////////////////////////////////////////////////////////////////
  1422. void Reset_OBJECT4DV1(OBJECT4DV1_PTR obj)
  1423. {
  1424. // this function resets the sent object and redies it for 
  1425. // transformations, basically just resets the culled, clipped and
  1426. // backface flags, but here's where you would add stuff
  1427. // to ready any object for the pipeline
  1428. // the object is valid, let's rip it apart polygon by polygon
  1429. // reset object's culled flag
  1430. RESET_BIT(obj->state, OBJECT4DV1_STATE_CULLED);
  1431. // now the clipped and backface flags for the polygons 
  1432. for (int poly = 0; poly < obj->num_polys; poly++)
  1433.     {
  1434.     // acquire polygon
  1435.     POLY4DV1_PTR curr_poly = &obj->plist[poly];
  1436.     
  1437.     // first is this polygon even visible?
  1438.     if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE))
  1439.        continue; // move onto next poly
  1440.     // reset clipped and backface flags
  1441.     RESET_BIT(curr_poly->state, POLY4DV1_STATE_CLIPPED);
  1442.     RESET_BIT(curr_poly->state, POLY4DV1_STATE_BACKFACE);
  1443.     } // end for poly
  1444. } // end Reset_OBJECT4DV1
  1445. ///////////////////////////////////////////////////////////////
  1446. int Insert_POLY4DV1_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, 
  1447.                                    POLY4DV1_PTR poly)
  1448. {
  1449. // converts the sent POLY4DV1 into a FACE4DV1 and inserts it
  1450. // into the render list
  1451. // step 0: are we full?
  1452. if (rend_list->num_polys >= RENDERLIST4DV1_MAX_POLYS)
  1453.    return(0);
  1454. // step 1: copy polygon into next opening in polygon render list
  1455. // point pointer to polygon structure
  1456. rend_list->poly_ptrs[rend_list->num_polys] = &rend_list->poly_data[rend_list->num_polys];
  1457. // copy fields
  1458. rend_list->poly_data[rend_list->num_polys].state = poly->state;
  1459. rend_list->poly_data[rend_list->num_polys].attr  = poly->attr;
  1460. rend_list->poly_data[rend_list->num_polys].color = poly->color;
  1461. // now copy vertices, be careful! later put a loop, but for now
  1462. // know there are 3 vertices always!
  1463. VECTOR4D_COPY(&rend_list->poly_data[rend_list->num_polys].tvlist[0],
  1464.               &poly->vlist[poly->vert[0]]);
  1465. VECTOR4D_COPY(&rend_list->poly_data[rend_list->num_polys].tvlist[1],
  1466.               &poly->vlist[poly->vert[1]]);
  1467. VECTOR4D_COPY(&rend_list->poly_data[rend_list->num_polys].tvlist[2],
  1468.               &poly->vlist[poly->vert[2]]);
  1469. // and copy into local vertices too
  1470. VECTOR4D_COPY(&rend_list->poly_data[rend_list->num_polys].vlist[0],
  1471.               &poly->vlist[poly->vert[0]]);
  1472. VECTOR4D_COPY(&rend_list->poly_data[rend_list->num_polys].vlist[1],
  1473.               &poly->vlist[poly->vert[1]]);
  1474. VECTOR4D_COPY(&rend_list->poly_data[rend_list->num_polys].vlist[2],
  1475.               &poly->vlist[poly->vert[2]]);
  1476. // now the polygon is loaded into the next free array position, but
  1477. // we need to fix up the links
  1478. // test if this is the first entry
  1479. if (rend_list->num_polys == 0)
  1480.    {
  1481.    // set pointers to null, could loop them around though to self
  1482.    rend_list->poly_data[0].next = NULL;
  1483.    rend_list->poly_data[0].prev = NULL;
  1484.    } // end if
  1485. else
  1486.    {
  1487.    // first set this node to point to previous node and next node (null)
  1488.    rend_list->poly_data[rend_list->num_polys].next = NULL;
  1489.    rend_list->poly_data[rend_list->num_polys].prev = 
  1490.          &rend_list->poly_data[rend_list->num_polys-1];
  1491.    // now set previous node to point to this node
  1492.    rend_list->poly_data[rend_list->num_polys-1].next = 
  1493.           &rend_list->poly_data[rend_list->num_polys];
  1494.    } // end else
  1495. // increment number of polys in list
  1496. rend_list->num_polys++;
  1497. // return successful insertion
  1498. return(1);
  1499. } // end Insert_POLY4DV1_RENDERLIST4DV1
  1500. //////////////////////////////////////////////////////////////
  1501. int Insert_POLYF4DV1_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, 
  1502.                                      POLYF4DV1_PTR poly)
  1503. {
  1504. // inserts the sent polyface POLYF4DV1 into the render list
  1505. // step 0: are we full?
  1506. if (rend_list->num_polys >= RENDERLIST4DV1_MAX_POLYS)
  1507.    return(0);
  1508. // step 1: copy polygon into next opening in polygon render list
  1509. // point pointer to polygon structure
  1510. rend_list->poly_ptrs[rend_list->num_polys] = &rend_list->poly_data[rend_list->num_polys];
  1511. // copy face right into array, thats it
  1512. memcpy((void *)&rend_list->poly_data[rend_list->num_polys],(void *)poly, sizeof(POLYF4DV1));
  1513. // now the polygon is loaded into the next free array position, but
  1514. // we need to fix up the links
  1515. // test if this is the first entry
  1516. if (rend_list->num_polys == 0)
  1517.    {
  1518.    // set pointers to null, could loop them around though to self
  1519.    rend_list->poly_data[0].next = NULL;
  1520.    rend_list->poly_data[0].prev = NULL;
  1521.    } // end if
  1522. else
  1523.    {
  1524.    // first set this node to point to previous node and next node (null)
  1525.    rend_list->poly_data[rend_list->num_polys].next = NULL;
  1526.    rend_list->poly_data[rend_list->num_polys].prev = 
  1527.          &rend_list->poly_data[rend_list->num_polys-1];
  1528.    // now set previous node to point to this node
  1529.    rend_list->poly_data[rend_list->num_polys-1].next = 
  1530.           &rend_list->poly_data[rend_list->num_polys];
  1531.    } // end else
  1532. // increment number of polys in list
  1533. rend_list->num_polys++;
  1534. // return successful insertion
  1535. return(1);
  1536. } // end Insert_POLYF4DV1_RENDERLIST4DV1
  1537. //////////////////////////////////////////////////////////////
  1538. int Insert_OBJECT4DV1_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, 
  1539.                                       OBJECT4DV1_PTR obj,
  1540.                                       int insert_local)
  1541. {
  1542. // converts the entire object into a face list and then inserts
  1543. // the visible, active, non-clipped, non-culled polygons into
  1544. // the render list, also note the flag insert_local control 
  1545. // whether or not the vlist_local or vlist_trans vertex list
  1546. // is used, thus you can insert an object "raw" totally untranformed
  1547. // if you set insert_local to 1, default is 0, that is you would
  1548. // only insert an object after at least the local to world transform
  1549. // is this objective inactive or culled or invisible?
  1550. if (!(obj->state & OBJECT4DV1_STATE_ACTIVE) ||
  1551.      (obj->state & OBJECT4DV1_STATE_CULLED) ||
  1552.      !(obj->state & OBJECT4DV1_STATE_VISIBLE))
  1553.    return(0); 
  1554. // the object is valid, let's rip it apart polygon by polygon
  1555. for (int poly = 0; poly < obj->num_polys; poly++)
  1556.     {
  1557.     // acquire polygon
  1558.     POLY4DV1_PTR curr_poly = &obj->plist[poly];
  1559.     // first is this polygon even visible?
  1560.     if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
  1561.          (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
  1562.          (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
  1563.     continue; // move onto next poly
  1564.     // override vertex list polygon refers to
  1565.     // the case that you want the local coords used
  1566.     // first save old pointer
  1567.     POINT4D_PTR vlist_old = curr_poly->vlist;
  1568.     if (insert_local)
  1569.        curr_poly->vlist = obj->vlist_local;
  1570.     else
  1571.        curr_poly->vlist = obj->vlist_trans;
  1572.     // now insert this polygon
  1573.     if (!Insert_POLY4DV1_RENDERLIST4DV1(rend_list, curr_poly))
  1574.        {
  1575.        // fix vertex list pointer
  1576.        curr_poly->vlist = vlist_old;
  1577.    
  1578.        // the whole object didn't fit!
  1579.        return(0);
  1580.        } // end if
  1581.     // fix vertex list pointer
  1582.     curr_poly->vlist = vlist_old;
  1583.     } // end for
  1584. // return success
  1585. return(1);
  1586. } // end Insert_OBJECT4DV1_RENDERLIST4DV1
  1587. //////////////////////////////////////////////////////////////
  1588. void Draw_OBJECT4DV1_Wire(OBJECT4DV1_PTR obj, 
  1589.                           UCHAR *video_buffer, int lpitch)
  1590.                      
  1591. {
  1592. // this function renders an object to the screen in wireframe, 
  1593. // 8 bit mode, it has no regard at all about hidden surface removal, 
  1594. // etc. the function only exists as an easy way to render an object 
  1595. // without converting it into polygons, the function assumes all 
  1596. // coordinates are screen coordinates, but will perform 2D clipping
  1597. // iterate thru the poly list of the object and simply draw
  1598. // each polygon
  1599. for (int poly=0; poly < obj->num_polys; poly++)
  1600.     {
  1601.     // render this polygon if and only if it's not clipped, not culled,
  1602.     // active, and visible, note however the concecpt of "backface" is 
  1603.     // irrelevant in a wire frame engine though
  1604.     if (!(obj->plist[poly].state & POLY4DV1_STATE_ACTIVE) ||
  1605.          (obj->plist[poly].state & POLY4DV1_STATE_CLIPPED ) ||
  1606.          (obj->plist[poly].state & POLY4DV1_STATE_BACKFACE) )
  1607.        continue; // move onto next poly
  1608.     
  1609.     // extract vertex indices into master list, rember the polygons are 
  1610.     // NOT self contained, but based on the vertex list stored in the object
  1611.     // itself
  1612.     int vindex_0 = obj->plist[poly].vert[0];
  1613.     int vindex_1 = obj->plist[poly].vert[1];
  1614.     int vindex_2 = obj->plist[poly].vert[2];
  1615.     
  1616.     // draw the lines now
  1617.     Draw_Clip_Line(obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y, 
  1618.                 obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y, 
  1619.                 obj->plist[poly].color,
  1620.                 video_buffer, lpitch);
  1621.     Draw_Clip_Line(obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y, 
  1622.                 obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y, 
  1623.                 obj->plist[poly].color,
  1624.                 video_buffer, lpitch);
  1625.     Draw_Clip_Line(obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y, 
  1626.                 obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y, 
  1627.                 obj->plist[poly].color,
  1628.                 video_buffer, lpitch);
  1629. // track rendering stats
  1630. #ifdef DEBUG_ON
  1631. debug_polys_rendered_per_frame++;
  1632. #endif
  1633.     } // end for poly
  1634. } // end Draw_OBJECT4DV1_Wire
  1635. ///////////////////////////////////////////////////////////////
  1636. void Draw_RENDERLIST4DV1_Wire(RENDERLIST4DV1_PTR rend_list, 
  1637.                               UCHAR *video_buffer, int lpitch)
  1638. {
  1639. // this function "executes" the render list or in other words
  1640. // draws all the faces in the list in wire frame 8bit mode
  1641. // note there is no need to sort wire frame polygons, but 
  1642. // later we will need to, so hidden surfaces stay hidden
  1643. // also, we leave it to the function to determine the bitdepth
  1644. // and call the correct rasterizer
  1645. // at this point, all we have is a list of polygons and it's time
  1646. // to draw them
  1647. for (int poly=0; poly < rend_list->num_polys; poly++)
  1648.     {
  1649.     // render this polygon if and only if it's not clipped, not culled,
  1650.     // active, and visible, note however the concecpt of "backface" is 
  1651.     // irrelevant in a wire frame engine though
  1652.     if (!(rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_ACTIVE) ||
  1653.          (rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_CLIPPED ) ||
  1654.          (rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_BACKFACE) )
  1655.        continue; // move onto next poly
  1656.     // draw the triangle edge one, note that clipping was already set up
  1657.     // by 2D initialization, so line clipper will clip all polys out
  1658.     // of the 2D screen/window boundary
  1659.     Draw_Clip_Line(rend_list->poly_ptrs[poly]->tvlist[0].x, 
  1660.                 rend_list->poly_ptrs[poly]->tvlist[0].y,
  1661.                 rend_list->poly_ptrs[poly]->tvlist[1].x, 
  1662.                 rend_list->poly_ptrs[poly]->tvlist[1].y,
  1663.                 rend_list->poly_ptrs[poly]->color,
  1664.                 video_buffer, lpitch);
  1665.     Draw_Clip_Line(rend_list->poly_ptrs[poly]->tvlist[1].x, 
  1666.                 rend_list->poly_ptrs[poly]->tvlist[1].y,
  1667.                 rend_list->poly_ptrs[poly]->tvlist[2].x, 
  1668.                 rend_list->poly_ptrs[poly]->tvlist[2].y,
  1669.                 rend_list->poly_ptrs[poly]->color,
  1670.                 video_buffer, lpitch);
  1671.     Draw_Clip_Line(rend_list->poly_ptrs[poly]->tvlist[2].x, 
  1672.                 rend_list->poly_ptrs[poly]->tvlist[2].y,
  1673.                 rend_list->poly_ptrs[poly]->tvlist[0].x, 
  1674.                 rend_list->poly_ptrs[poly]->tvlist[0].y,
  1675.                 rend_list->poly_ptrs[poly]->color,
  1676.                 video_buffer, lpitch);
  1677. // track rendering stats
  1678. #ifdef DEBUG_ON
  1679. debug_polys_rendered_per_frame++;
  1680. #endif
  1681.     } // end for poly
  1682. } // end Draw_RENDERLIST4DV1_Wire
  1683. /////////////////////////////////////////////////////////////
  1684. void Draw_OBJECT4DV1_Wire16(OBJECT4DV1_PTR obj, 
  1685.                             UCHAR *video_buffer, int lpitch)
  1686.                      
  1687. {
  1688. // this function renders an object to the screen in wireframe, 
  1689. // 16 bit mode, it has no regard at all about hidden surface removal, 
  1690. // etc. the function only exists as an easy way to render an object 
  1691. // without converting it into polygons, the function assumes all 
  1692. // coordinates are screen coordinates, but will perform 2D clipping
  1693. // iterate thru the poly list of the object and simply draw
  1694. // each polygon
  1695. for (int poly=0; poly < obj->num_polys; poly++)
  1696.     {
  1697.     // render this polygon if and only if it's not clipped, not culled,
  1698.     // active, and visible, note however the concecpt of "backface" is 
  1699.     // irrelevant in a wire frame engine though
  1700.     if (!(obj->plist[poly].state & POLY4DV1_STATE_ACTIVE) ||
  1701.          (obj->plist[poly].state & POLY4DV1_STATE_CLIPPED ) ||
  1702.          (obj->plist[poly].state & POLY4DV1_STATE_BACKFACE) )
  1703.        continue; // move onto next poly
  1704.     
  1705.     // extract vertex indices into master list, rember the polygons are 
  1706.     // NOT self contained, but based on the vertex list stored in the object
  1707.     // itself
  1708.     int vindex_0 = obj->plist[poly].vert[0];
  1709.     int vindex_1 = obj->plist[poly].vert[1];
  1710.     int vindex_2 = obj->plist[poly].vert[2];
  1711.     
  1712.     // draw the lines now
  1713.     Draw_Clip_Line16(obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y, 
  1714.                 obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y, 
  1715.                 obj->plist[poly].color,
  1716.                 video_buffer, lpitch);
  1717.     Draw_Clip_Line16(obj->vlist_trans[ vindex_1 ].x, obj->vlist_trans[ vindex_1 ].y, 
  1718.                 obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y, 
  1719.                 obj->plist[poly].color,
  1720.                 video_buffer, lpitch);
  1721.     Draw_Clip_Line16(obj->vlist_trans[ vindex_2 ].x, obj->vlist_trans[ vindex_2 ].y, 
  1722.                 obj->vlist_trans[ vindex_0 ].x, obj->vlist_trans[ vindex_0 ].y, 
  1723.                 obj->plist[poly].color,
  1724.                 video_buffer, lpitch);
  1725. // track rendering stats
  1726. #ifdef DEBUG_ON
  1727. debug_polys_rendered_per_frame++;
  1728. #endif
  1729.     } // end for poly
  1730. } // end Draw_OBJECT4DV1_Wire16
  1731. ///////////////////////////////////////////////////////////////
  1732. void Draw_RENDERLIST4DV1_Wire16(RENDERLIST4DV1_PTR rend_list, 
  1733.                                 UCHAR *video_buffer, int lpitch)
  1734. {
  1735. // this function "executes" the render list or in other words
  1736. // draws all the faces in the list in wire frame 16bit mode
  1737. // note there is no need to sort wire frame polygons, but 
  1738. // later we will need to, so hidden surfaces stay hidden
  1739. // also, we leave it to the function to determine the bitdepth
  1740. // and call the correct rasterizer
  1741. // at this point, all we have is a list of polygons and it's time
  1742. // to draw them
  1743. for (int poly=0; poly < rend_list->num_polys; poly++)
  1744.     {
  1745.     // render this polygon if and only if it's not clipped, not culled,
  1746.     // active, and visible, note however the concecpt of "backface" is 
  1747.     // irrelevant in a wire frame engine though
  1748.     if (!(rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_ACTIVE) ||
  1749.          (rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_CLIPPED ) ||
  1750.          (rend_list->poly_ptrs[poly]->state & POLY4DV1_STATE_BACKFACE) )
  1751.        continue; // move onto next poly
  1752.     // draw the triangle edge one, note that clipping was already set up
  1753.     // by 2D initialization, so line clipper will clip all polys out
  1754.     // of the 2D screen/window boundary
  1755.     Draw_Clip_Line16(rend_list->poly_ptrs[poly]->tvlist[0].x, 
  1756.                 rend_list->poly_ptrs[poly]->tvlist[0].y,
  1757.                 rend_list->poly_ptrs[poly]->tvlist[1].x, 
  1758.                 rend_list->poly_ptrs[poly]->tvlist[1].y,
  1759.                 rend_list->poly_ptrs[poly]->color,
  1760.                 video_buffer, lpitch);
  1761.     Draw_Clip_Line16(rend_list->poly_ptrs[poly]->tvlist[1].x, 
  1762.                 rend_list->poly_ptrs[poly]->tvlist[1].y,
  1763.                 rend_list->poly_ptrs[poly]->tvlist[2].x, 
  1764.                 rend_list->poly_ptrs[poly]->tvlist[2].y,
  1765.                 rend_list->poly_ptrs[poly]->color,
  1766.                 video_buffer, lpitch);
  1767.     Draw_Clip_Line16(rend_list->poly_ptrs[poly]->tvlist[2].x, 
  1768.                 rend_list->poly_ptrs[poly]->tvlist[2].y,
  1769.                 rend_list->poly_ptrs[poly]->tvlist[0].x, 
  1770.                 rend_list->poly_ptrs[poly]->tvlist[0].y,
  1771.                 rend_list->poly_ptrs[poly]->color,
  1772.                 video_buffer, lpitch);
  1773. // track rendering stats
  1774. #ifdef DEBUG_ON
  1775. debug_polys_rendered_per_frame++;
  1776. #endif
  1777.     } // end for poly
  1778. } // end Draw_RENDERLIST4DV1_Wire
  1779. /////////////////////////////////////////////////////////////
  1780. void Build_CAM4DV1_Matrix_Euler(CAM4DV1_PTR cam, int cam_rot_seq)
  1781. {
  1782. // this creates a camera matrix based on Euler angles 
  1783. // and stores it in the sent camera object
  1784. // if you recall from chapter 6 to create the camera matrix
  1785. // we need to create a transformation matrix that looks like:
  1786. // Mcam = mt(-1) * my(-1) * mx(-1) * mz(-1)
  1787. // that is the inverse of the camera translation matrix mutilplied
  1788. // by the inverses of yxz, in that order, however, the order of
  1789. // the rotation matrices is really up to you, so we aren't going
  1790. // to force any order, thus its programmable based on the value
  1791. // of cam_rot_seq which can be any value CAM_ROT_SEQ_XYZ where 
  1792. // XYZ can be in any order, YXZ, ZXY, etc.
  1793. MATRIX4X4 mt_inv,  // inverse camera translation matrix
  1794.           mx_inv,  // inverse camera x axis rotation matrix
  1795.           my_inv,  // inverse camera y axis rotation matrix
  1796.           mz_inv,  // inverse camera z axis rotation matrix
  1797.           mrot,    // concatenated inverse rotation matrices
  1798.           mtmp;    // temporary working matrix
  1799. // step 1: create the inverse translation matrix for the camera
  1800. // position
  1801. Mat_Init_4X4(&mt_inv, 1,    0,     0,     0,
  1802.                       0,    1,     0,     0,
  1803.                       0,    0,     1,     0,
  1804.                       -cam->pos.x, -cam->pos.y, -cam->pos.z, 1);
  1805. // step 2: create the inverse rotation sequence for the camera
  1806. // rember either the transpose of the normal rotation matrix or
  1807. // plugging negative values into each of the rotations will result
  1808. // in an inverse matrix
  1809. // first compute all 3 rotation matrices
  1810. // extract out euler angles
  1811. float theta_x = cam->dir.x;
  1812. float theta_y = cam->dir.y;
  1813. float theta_z = cam->dir.z;
  1814. // compute the sine and cosine of the angle x
  1815. float cos_theta = Fast_Cos(theta_x);  // no change since cos(-x) = cos(x)
  1816. float sin_theta = -Fast_Sin(theta_x); // sin(-x) = -sin(x)
  1817. // set the matrix up 
  1818. Mat_Init_4X4(&mx_inv, 1,    0,         0,         0,
  1819.                       0,    cos_theta, sin_theta, 0,
  1820.                       0,   -sin_theta, cos_theta, 0,
  1821.                       0,    0,         0,         1);
  1822. // compute the sine and cosine of the angle y
  1823. cos_theta = Fast_Cos(theta_y);  // no change since cos(-x) = cos(x)
  1824. sin_theta = -Fast_Sin(theta_y); // sin(-x) = -sin(x)
  1825. // set the matrix up 
  1826. Mat_Init_4X4(&my_inv,cos_theta, 0, -sin_theta, 0,  
  1827.                      0,         1,  0,         0,
  1828.                      sin_theta, 0,  cos_theta,  0,
  1829.                      0,         0,  0,          1);
  1830. // compute the sine and cosine of the angle z
  1831. cos_theta = Fast_Cos(theta_z);  // no change since cos(-x) = cos(x)
  1832. sin_theta = -Fast_Sin(theta_z); // sin(-x) = -sin(x)
  1833. // set the matrix up 
  1834. Mat_Init_4X4(&mz_inv, cos_theta, sin_theta, 0, 0,  
  1835.                      -sin_theta, cos_theta, 0, 0,
  1836.                       0,         0,         1, 0,
  1837.                       0,         0,         0, 1);
  1838. // now compute inverse camera rotation sequence
  1839. switch(cam_rot_seq)
  1840.       {
  1841.       case CAM_ROT_SEQ_XYZ:
  1842.       {
  1843.       Mat_Mul_4X4(&mx_inv, &my_inv, &mtmp);
  1844.       Mat_Mul_4X4(&mtmp, &mz_inv, &mrot);
  1845.       } break;
  1846.       case CAM_ROT_SEQ_YXZ:
  1847.       {
  1848.       Mat_Mul_4X4(&my_inv, &mx_inv, &mtmp);
  1849.       Mat_Mul_4X4(&mtmp, &mz_inv, &mrot);
  1850.       } break;
  1851.       case CAM_ROT_SEQ_XZY:
  1852.       {
  1853.       Mat_Mul_4X4(&mx_inv, &mz_inv, &mtmp);
  1854.       Mat_Mul_4X4(&mtmp, &my_inv, &mrot);
  1855.       } break;
  1856.       case CAM_ROT_SEQ_YZX:
  1857.       {
  1858.       Mat_Mul_4X4(&my_inv, &mz_inv, &mtmp);
  1859.       Mat_Mul_4X4(&mtmp, &mx_inv, &mrot);
  1860.       } break;
  1861.       case CAM_ROT_SEQ_ZYX:
  1862.       {
  1863.       Mat_Mul_4X4(&mz_inv, &my_inv, &mtmp);
  1864.       Mat_Mul_4X4(&mtmp, &mx_inv, &mrot);
  1865.       } break;
  1866.       case CAM_ROT_SEQ_ZXY:
  1867.       {
  1868.       Mat_Mul_4X4(&mz_inv, &mx_inv, &mtmp);
  1869.       Mat_Mul_4X4(&mtmp, &my_inv, &mrot);
  1870.       } break;
  1871.       default: break;
  1872.       } // end switch
  1873. // now mrot holds the concatenated product of inverse rotation matrices
  1874. // multiply the inverse translation matrix against it and store in the 
  1875. // camera objects' camera transform matrix we are done!
  1876. Mat_Mul_4X4(&mt_inv, &mrot, &cam->mcam);
  1877. } // end Build_CAM4DV1_Matrix_Euler
  1878. /////////////////////////////////////////////////////////////
  1879. void Build_CAM4DV1_Matrix_UVN(CAM4DV1_PTR cam, int mode)
  1880. {
  1881. // this creates a camera matrix based on a look at vector n,
  1882. // look up vector v, and a look right (or left) u
  1883. // and stores it in the sent camera object, all values are
  1884. // extracted out of the camera object itself
  1885. // mode selects how uvn is computed
  1886. // UVN_MODE_SIMPLE - low level simple model, use the target and view reference point
  1887. // UVN_MODE_SPHERICAL - spherical mode, the x,y components will be used as the
  1888. //     elevation and heading of the view vector respectively
  1889. //     along with the view reference point as the position
  1890. //     as usual
  1891. MATRIX4X4 mt_inv,  // inverse camera translation matrix
  1892.           mt_uvn,  // the final uvn matrix
  1893.           mtmp;    // temporary working matrix
  1894. // step 1: create the inverse translation matrix for the camera
  1895. // position
  1896. Mat_Init_4X4(&mt_inv, 1,    0,     0,     0,
  1897.                       0,    1,     0,     0,
  1898.                       0,    0,     1,     0,
  1899.                       -cam->pos.x, -cam->pos.y, -cam->pos.z, 1);
  1900. // step 2: determine how the target point will be computed
  1901. if (mode == UVN_MODE_SPHERICAL)
  1902.    {
  1903.    // use spherical construction
  1904.    // target needs to be recomputed
  1905.    // extract elevation and heading 
  1906.    float phi   = cam->dir.x; // elevation
  1907.    float theta = cam->dir.y; // heading
  1908.    // compute trig functions once
  1909.    float sin_phi = Fast_Sin(phi);
  1910.    float cos_phi = Fast_Cos(phi);
  1911.    float sin_theta = Fast_Sin(theta);
  1912.    float cos_theta = Fast_Cos(theta);
  1913.    // now compute the target point on a unit sphere x,y,z
  1914.    cam->target.x = -1*sin_phi*sin_theta;
  1915.    cam->target.y =  1*cos_phi;
  1916.    cam->target.z =  1*sin_phi*cos_theta;
  1917.    } // end else
  1918. // at this point, we have the view reference point, the target and that's
  1919. // all we need to recompute u,v,n
  1920. // Step 1: n = <target position - view reference point>
  1921. VECTOR4D_Build(&cam->pos, &cam->target, &cam->n);
  1922. // Step 2: Let v = <0,1,0>
  1923. VECTOR4D_INITXYZ(&cam->v,0,1,0);
  1924. // Step 3: u = (v x n)
  1925. VECTOR4D_Cross(&cam->v,&cam->n,&cam->u);
  1926. // Step 4: v = (n x u)
  1927. VECTOR4D_Cross(&cam->n,&cam->u,&cam->v);
  1928. // Step 5: normalize all vectors
  1929. VECTOR4D_Normalize(&cam->u);
  1930. VECTOR4D_Normalize(&cam->v);
  1931. VECTOR4D_Normalize(&cam->n);
  1932. // build the UVN matrix by placing u,v,n as the columns of the matrix
  1933. Mat_Init_4X4(&mt_uvn, cam->u.x,    cam->v.x,     cam->n.x,     0,
  1934.                       cam->u.y,    cam->v.y,     cam->n.y,     0,
  1935.                       cam->u.z,    cam->v.z,     cam->n.z,     0,
  1936.                       0,           0,            0,            1);
  1937. // now multiply the translation matrix and the uvn matrix and store in the 
  1938. // final camera matrix mcam
  1939. Mat_Mul_4X4(&mt_inv, &mt_uvn, &cam->mcam);
  1940. } // end Build_CAM4DV1_Matrix_UVN
  1941. /////////////////////////////////////////////////////////////
  1942. void Init_CAM4DV1(CAM4DV1_PTR cam,       // the camera object
  1943.                   int cam_attr,          // attributes
  1944.                   POINT4D_PTR cam_pos,   // initial camera position
  1945.                   VECTOR4D_PTR cam_dir,  // initial camera angles
  1946.                   POINT4D_PTR cam_target, // UVN target
  1947.                   float near_clip_z,     // near and far clipping planes
  1948.                   float far_clip_z,
  1949.                   float fov,             // field of view in degrees
  1950.                   float viewport_width,  // size of final screen viewport
  1951.                   float viewport_height)
  1952. {
  1953. // this function initializes the camera object cam, the function
  1954. // doesn't do a lot of error checking or sanity checking since 
  1955. // I want to allow you to create projections as you wish, also 
  1956. // I tried to minimize the number of parameters the functions needs
  1957. // first set up parms that are no brainers
  1958. cam->attr = cam_attr;              // camera attributes
  1959. VECTOR4D_COPY(&cam->pos, cam_pos); // positions
  1960. VECTOR4D_COPY(&cam->dir, cam_dir); // direction vector or angles for
  1961.                                    // euler camera
  1962. // for UVN camera
  1963. VECTOR4D_INITXYZ(&cam->u, 1,0,0);  // set to +x
  1964. VECTOR4D_INITXYZ(&cam->v, 0,1,0);  // set to +y
  1965. VECTOR4D_INITXYZ(&cam->n, 0,0,1);  // set to +z        
  1966. if (cam_target!=NULL)
  1967.    VECTOR4D_COPY(&cam->target, cam_target); // UVN target
  1968. else
  1969.    VECTOR4D_ZERO(&cam->target);
  1970. cam->near_clip_z = near_clip_z;     // near z=constant clipping plane
  1971. cam->far_clip_z  = far_clip_z;      // far z=constant clipping plane
  1972. cam->viewport_width  = viewport_width;   // dimensions of viewport
  1973. cam->viewport_height = viewport_height;
  1974. cam->viewport_center_x = (viewport_width-1)/2; // center of viewport
  1975. cam->viewport_center_y = (viewport_height-1)/2;
  1976. cam->aspect_ratio = (float)viewport_width/(float)viewport_height;
  1977. // set all camera matrices to identity matrix
  1978. MAT_IDENTITY_4X4(&cam->mcam);
  1979. MAT_IDENTITY_4X4(&cam->mper);
  1980. MAT_IDENTITY_4X4(&cam->mscr);
  1981. // set independent vars
  1982. cam->fov              = fov;
  1983. // set the viewplane dimensions up, they will be 2 x (2/ar)
  1984. cam->viewplane_width  = 2.0;
  1985. cam->viewplane_height = 2.0/cam->aspect_ratio;
  1986. // now we know fov and we know the viewplane dimensions plug into formula and
  1987. // solve for view distance parameters
  1988. float tan_fov_div2 = tan(DEG_TO_RAD(fov/2));
  1989. cam->view_dist = (0.5)*(cam->viewplane_width)*tan_fov_div2;
  1990. // test for 90 fov first since it's easy :)
  1991. if (fov == 90.0)
  1992.     {
  1993.       // set up the clipping planes -- easy for 90 degrees!
  1994.       POINT3D pt_origin; // point on the plane
  1995.       VECTOR3D_INITXYZ(&pt_origin,0,0,0);
  1996.       VECTOR3D vn; // normal to plane
  1997.       // right clipping plane 
  1998.       VECTOR3D_INITXYZ(&vn,1,0,-1); // x=z plane
  1999.       PLANE3D_Init(&cam->rt_clip_plane, &pt_origin,  &vn, 1);
  2000.       // left clipping plane
  2001.       VECTOR3D_INITXYZ(&vn,-1,0,-1); // -x=z plane
  2002.       PLANE3D_Init(&cam->lt_clip_plane, &pt_origin,  &vn, 1);
  2003.       // top clipping plane
  2004.       VECTOR3D_INITXYZ(&vn,0,1,-1); // y=z plane
  2005.       PLANE3D_Init(&cam->tp_clip_plane, &pt_origin,  &vn, 1);
  2006.       // bottom clipping plane
  2007.       VECTOR3D_INITXYZ(&vn,0,-1,-1); // -y=z plane
  2008.       PLANE3D_Init(&cam->bt_clip_plane, &pt_origin,  &vn, 1);
  2009.     } // end if d=1
  2010. else 
  2011.     {
  2012.       // now compute clipping planes yuck!
  2013.       POINT3D pt_origin; // point on the plane
  2014.       VECTOR3D_INITXYZ(&pt_origin,0,0,0);
  2015.       VECTOR3D vn; // normal to plane
  2016.       // since we don't have a 90 fov, computing the normals
  2017.       // are a bit tricky, there are a number of geometric constructions
  2018.       // that solve the problem, but I'm going to solve for the
  2019.       // vectors that represent the 2D projections of the frustrum planes
  2020.       // on the x-z and y-z planes and then find perpendiculars to them
  2021.  
  2022.       // right clipping plane, check the math on graph paper 
  2023.       VECTOR3D_INITXYZ(&vn,cam->view_dist,0,-cam->viewplane_width/2.0); 
  2024.       PLANE3D_Init(&cam->rt_clip_plane, &pt_origin,  &vn, 1);
  2025.       // left clipping plane, we can simply reflect the right normal about
  2026.       // the z axis since the planes are symetric about the z axis
  2027.       // thus invert x only
  2028.       VECTOR3D_INITXYZ(&vn,-cam->view_dist,0,-cam->viewplane_width/2.0); 
  2029.       PLANE3D_Init(&cam->lt_clip_plane, &pt_origin,  &vn, 1);
  2030.       // top clipping plane, same construction
  2031.       VECTOR3D_INITXYZ(&vn,0,cam->view_dist,-cam->viewplane_width/2.0); 
  2032.       PLANE3D_Init(&cam->tp_clip_plane, &pt_origin,  &vn, 1);
  2033.       // bottom clipping plane, same inversion
  2034.       VECTOR3D_INITXYZ(&vn,0,-cam->view_dist,-cam->viewplane_width/2.0); 
  2035.       PLANE3D_Init(&cam->bt_clip_plane, &pt_origin,  &vn, 1);
  2036.     } // end else
  2037. } // end Init_CAM4DV1
  2038. // MAIN //////////////////////////////////////////////////////
  2039. #if (STANDALONE==1)
  2040. void main()
  2041. {
  2042. static VECTOR4D vscale={1,1,1,1}, vpos = {0,0,0,1}, vrot = {0,0,0,1};
  2043. static OBJECT4DV1 obj;
  2044. static CAM4DV1 cam;
  2045. static RENDERLIST4DV1 rend_list;
  2046. static POLYF4DV1 poly1;
  2047. Open_Error_File("ERROR.TXT", stdout);
  2048. // set function pointer to something
  2049. RGB16Bit = RGB16Bit565;
  2050. // initialize math engine
  2051. Build_Sin_Cos_Tables();
  2052. // initialize the renderlist
  2053. Init_RENDERLIST4DV1(&rend_list);
  2054. // initialize single polygon
  2055. poly1.state  = POLY4DV1_STATE_ACTIVE;
  2056. poly1.attr   =  0; 
  2057. poly1.color = RGB16Bit(255,255,255);
  2058.   
  2059. poly1.vlist[0].x = 0;
  2060. poly1.vlist[0].y = 50;
  2061. poly1.vlist[0].z = 100;
  2062. poly1.vlist[0].w = 1;
  2063. poly1.vlist[1].x = 50;
  2064. poly1.vlist[1].y = 0;
  2065. poly1.vlist[1].z = 100;
  2066. poly1.vlist[1].w = 1;
  2067. poly1.vlist[2].x = -50;
  2068. poly1.vlist[2].y = 50;
  2069. poly1.vlist[2].z = 100;
  2070. poly1.vlist[2].w = 1;
  2071. poly1.tvlist[0].x = 0;
  2072. poly1.tvlist[0].y = 50;
  2073. poly1.tvlist[0].z = 100;
  2074. poly1.tvlist[0].w = 1;
  2075. poly1.tvlist[1].x = 50;
  2076. poly1.tvlist[1].y = 0;
  2077. poly1.tvlist[1].z = 100;
  2078. poly1.tvlist[1].w = 1;
  2079. poly1.tvlist[2].x = -50;
  2080. poly1.tvlist[2].y = 50;
  2081. poly1.tvlist[2].z = 100;
  2082. poly1.tvlist[2].w = 1;
  2083. poly1.next = poly1.prev = NULL;
  2084. // insert the polygon in the renderlist
  2085. Insert_POLYF4DV1_RENDERLIST4DV1(&rend_list, &poly1);
  2086. Write_Error("nPolygon Initial");
  2087. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[0], "v0");
  2088. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[1], "v1");
  2089. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[2], "v2");
  2090. // initialize camera 
  2091. static POINT4D cam_pos = {0,0,-500};
  2092. static VECTOR4D cam_dir = {0,0,0};
  2093. Init_CAM4DV1(&cam,      // the camera object
  2094.              &cam_pos,  // initial camera position
  2095.              &cam_dir,  // initial camera angles
  2096.              10.0,      // near and far clipping planes
  2097.              1000.0,
  2098.              90.0,      // field of view in degrees
  2099.              1.0,       // viewing distance
  2100.              0.0,       // size of viewplane
  2101.              0.0,
  2102.              640,   // size of final screen viewport
  2103.              480,
  2104.              ((float)640/(float)480));
  2105. // generate camera matrix
  2106. Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_XYZ);
  2107. // apply world to camera transformation
  2108. Camera_To_Perspective_Norm_RENDERLIST4DV1(&rend_list, &cam);
  2109. Write_Error("nPolygon Perspective");
  2110. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[0], "v0");
  2111. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[1], "v1");
  2112. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[2], "v2");
  2113. // apply screen transform
  2114. Perspective_Norm_To_Screen_RENDERLIST4DV1(&rend_list, &cam);
  2115. Write_Error("nPolygon Screen");
  2116. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[0], "v0");
  2117. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[1], "v1");
  2118. VECTOR4D_Print(&rend_list.poly_ptrs[0]->tvlist[2], "v2");
  2119. //Load_OBJECT4DV1_PLG(&obj, "test.plg",vscale, vpos, vrot);
  2120. Close_Error_File();
  2121.   
  2122. } // end main
  2123. #endif