t3dlib6.cpp
资源名称:Source.rar [点击查看]
上传用户:husern
上传日期:2018-01-20
资源大小:42486k
文件大小:181k
源码类别:
游戏
开发平台:
Visual C++
- // file name: string "D:Sourcemodelstextureswall01.bmp"
- // of course the filename in the quotes will change
- // so lets hunt until we find it...
- while(1)
- {
- // get the next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("ncouldnt find texture name! in .COB file %s.", filename);
- return(0);
- } // end if
- // replace the " with spaces
- ReplaceChars(parser.buffer, parser.buffer, """, ' ', 1);
- // is this the file name?
- if (parser.Pattern_Match(parser.buffer, "['file'] ['name:'] ['string'] [s>0]") )
- {
- // ok, simply convert to a real file name by changing the slashes
- ReplaceChars(parser.pstrings[3], parser.pstrings[3], "\", '/',1);
- // and save the filename
- strcpy(materials[material_index + num_materials].texture_file, parser.pstrings[3]);
- break;
- } // end if
- } // end while
- break;
- } // end if
- } // end while
- // alright, finally! Now we need to know what the actual shader type, now in the COB format
- // I have decided that in the "reflectance" class that's where we will look at what kind
- // of shader is supposed to be used on the polygon
- // look for the "Shader class: reflectance"
- while(1)
- {
- // get the next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nshader reflectance class not found in .COB file %s.", filename);
- return(0);
- } // end if
- // look for "Shader class: reflectance"
- if (parser.Pattern_Match(parser.buffer, "['Shader'] ['class:'] ['reflectance']") )
- {
- // now we know the next "shader name" is what we are looking for so, break
- break;
- } // end if
- } // end while
- // looking for "Shader name: "xxxxxx" (xxxxxx) " again,
- // and based on it's value we map it to our shading system as follows:
- // "constant" -> MATV1_ATTR_SHADE_MODE_CONSTANT
- // "matte" -> MATV1_ATTR_SHADE_MODE_FLAT
- // "plastic" -> MATV1_ATTR_SHADE_MODE_GOURAUD
- // "phong" -> MATV1_ATTR_SHADE_MODE_FASTPHONG
- // and in the case that in the "color" class, we found a "texture map" then the "shading mode" is
- // "texture map" -> MATV1_ATTR_SHADE_MODE_TEXTURE
- // which must be logically or'ed with the other previous modes
- while(1)
- {
- // get the next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nshader name ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- // get rid of those quotes
- ReplaceChars(parser.buffer, parser.buffer, """,' ',1);
- // did we find the name?
- if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] [s>0]" ) )
- {
- // figure out which shader to use
- if (strcmp(parser.pstrings[2], "constant") == 0)
- {
- // set the shading mode flag in material
- SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_CONSTANT);
- } // end if
- else
- if (strcmp(parser.pstrings[2], "matte") == 0)
- {
- // set the shading mode flag in material
- SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FLAT);
- } // end if
- else
- if (strcmp(parser.pstrings[2], "plastic") == 0)
- {
- // set the shading mode flag in material
- SET_BIT(materials[curr_material + num_materials].attr, MATV1_ATTR_SHADE_MODE_GOURAUD);
- } // end if
- else
- if (strcmp(parser.pstrings[2], "phong") == 0)
- {
- // set the shading mode flag in material
- SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FASTPHONG);
- } // end if
- else
- {
- // set the shading mode flag in material
- SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_FLAT);
- } // end else
- break;
- } // end if
- } // end while
- // found the material, break out of while for another pass
- break;
- } // end if found material
- } // end while looking for mat#1
- } // end for curr_material
- // at this point poly_material[] holds all the indices for the polygon materials (zero based, so they need fix up)
- // and we must access the materials array to fill in each polygon with the polygon color, etc.
- // now that we finally have the material libary loaded
- for (int curr_poly = 0; curr_poly < obj->num_polys; curr_poly++)
- {
- Write_Error("nfixing poly material %d from index %d to index %d", curr_poly,
- poly_material[curr_poly],
- poly_material[curr_poly] + num_materials );
- // fix up offset
- poly_material[curr_poly] = poly_material[curr_poly] + num_materials;
- // we need to know what color depth we are dealing with, so check
- // the bits per pixel, this assumes that the system has already
- // made the call to DDraw_Init() or set the bit depth
- if (screen_bpp == 16)
- {
- // cool, 16 bit mode
- SET_BIT(obj->plist[curr_poly].attr,POLY4DV1_ATTR_RGB16);
- obj->plist[curr_poly].color = RGB16Bit(materials[ poly_material[curr_poly] ].color.r,
- materials[ poly_material[curr_poly] ].color.g,
- materials[ poly_material[curr_poly] ].color.b);
- Write_Error("nPolygon 16-bit");
- } // end
- else
- {
- // 8 bit mode
- SET_BIT(obj->plist[curr_poly].attr,POLY4DV1_ATTR_8BITCOLOR);
- obj->plist[curr_poly].color = RGBto8BitIndex(materials[ poly_material[curr_poly] ].color.r,
- materials[ poly_material[curr_poly] ].color.g,
- materials[ poly_material[curr_poly] ].color.b,
- palette, 0);
- Write_Error("nPolygon 8-bit, index=%d", obj->plist[curr_poly].color);
- } // end else
- // now set all the shading flags
- // figure out which shader to use
- if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_CONSTANT)
- {
- // set shading mode
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_CONSTANT);
- } // end if
- else
- if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_FLAT)
- {
- // set shading mode
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_FLAT);
- } // end if
- else
- if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_GOURAUD)
- {
- // set shading mode
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_GOURAUD);
- } // end if
- else
- if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_FASTPHONG)
- {
- // set shading mode
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_FASTPHONG);
- } // end if
- else
- {
- // set shading mode
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_GOURAUD);
- } // end if
- if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_TEXTURE)
- {
- // set shading mode
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV1_ATTR_SHADE_MODE_TEXTURE);
- } // end if
- } // end for curr_poly
- // local object materials have been added to database, update total materials in system
- num_materials+=num_materials_object;
- #ifdef DEBUG_ON
- for (curr_material = 0; curr_material < num_materials; curr_material++)
- {
- Write_Error("nMaterial %d", curr_material);
- Write_Error("nint state = %d", materials[curr_material].state);
- Write_Error("nint id = %d", materials[curr_material].id);
- Write_Error("nchar name[64] = %s", materials[curr_material].name);
- Write_Error("nint attr = %d", materials[curr_material].attr);
- Write_Error("nint r = %d", materials[curr_material].color.r);
- Write_Error("nint g = %d", materials[curr_material].color.g);
- Write_Error("nint b = %d", materials[curr_material].color.b);
- Write_Error("nint alpha = %d", materials[curr_material].color.a);
- Write_Error("nint color = %d", materials[curr_material].attr);
- Write_Error("nfloat ka = %f", materials[curr_material].ka);
- Write_Error("nkd = %f", materials[curr_material].kd);
- Write_Error("nks = %f", materials[curr_material].ks);
- Write_Error("npower = %f", materials[curr_material].power);
- Write_Error("nchar texture_file = %sn", materials[curr_material].texture_file);
- } // end for curr_material
- #endif
- // return success
- return(1);
- } // end Load_OBJECT4DV1_COB
- ///////////////////////////////////////////////////////////////////////////////
- int RGBto8BitIndex(UCHAR r, UCHAR g, UCHAR b, LPPALETTEENTRY palette, int flush_cache=0)
- {
- // this function hunts thru the loaded 8-bit palette and tries to find the
- // best match to the sent rgb color, the 8-bit index is returned. The algorithm
- // performings a least squares match on the values in the CLUT, also to speed up
- // the process, the last few translated colored are stored in a stack in the format
- // rgbi, so when a new rgb comes in, it is compared against the rgb entries in the
- // table, if found then that index is used, else, the rgb is translated and added to
- // the table, and the table is shifted one slot, so the last element is thrown away,
- // hence the table is FIFO in as much as the first discarded value will be the first
- // this way the system keeps previously translated colors cached, so the fairly long
- // least squared scan doesn't take forever!
- // also note the compression of the RGBI data, a compare is performed on the upper 24 bits only
- // also, if flush_cache = 1 then the local cache is flushed, for example a new palette is loaded
- #define COLOR_CACHE_SIZE 16 // 16 entries should do for now
- typedef struct
- {
- UCHAR r,g,b; // the rgb value of this translated color
- UCHAR index; // the color index that matched is most closely
- } RGBINDEX, *RGBINDEX_PTR;
- static RGBINDEX color_cache[COLOR_CACHE_SIZE]; // the color cache
- static int cache_entries=0; // number of entries in the cache
- // test for flush cache command, new palette coming in...
- if (flush_cache==1)
- cache_entries = 0;
- // test if the color is in the cache
- for (int cache_index=0; cache_index < cache_entries; cache_index++)
- {
- // is this a match?
- if (r==color_cache[cache_index].r &&
- g==color_cache[cache_index].g &&
- b==color_cache[cache_index].b )
- return(color_cache[cache_index].index);
- } // end for
- // if we get here then we had no luck, so least sqaures scan for best match
- // and make sure to add results to cache
- int curr_index = -1; // current color index of best match
- long curr_error = INT_MAX; // distance in color space to nearest match or "error"
- for (int color_index = 0; color_index < 256; color_index++)
- {
- // compute distance to color from target
- long delta_red = abs(palette[color_index].peRed - r);
- long delta_green = abs(palette[color_index].peGreen - g);
- long delta_blue = abs(palette[color_index].peBlue - b);
- long error = (delta_red*delta_red) + (delta_green*delta_green) + (delta_blue*delta_blue);
- // is this color a better match?
- if (error < curr_error)
- {
- curr_index = color_index;
- curr_error = error;
- } // end if
- } // end for color_index
- // at this point we have the new color, insert it into cache
- // shift cache over one entry, copy elements [0 - (n-1)] -> [1 - n]
- memmove((void *)&color_cache[1], (void *)&color_cache[0], COLOR_CACHE_SIZE*sizeof(RGBINDEX) - sizeof(RGBINDEX) );
- // now insert the new element
- color_cache[0].r = r;
- color_cache[0].b = b;
- color_cache[0].g = g;
- color_cache[0].index = curr_index;
- // increment number of elements in the cache until saturation
- if (++cache_entries > COLOR_CACHE_SIZE)
- cache_entries = COLOR_CACHE_SIZE;
- // return results
- return(curr_index);
- } // end RGBto8BitIndex
- //////////////////////////////////////////////////////////////////////////////
- int Init_Light_LIGHTV1(int index, // index of light to create (0..MAX_LIGHTS-1)
- int _state, // state of light
- int _attr, // type of light, and extra qualifiers
- RGBAV1 _c_ambient, // ambient light intensity
- RGBAV1 _c_diffuse, // diffuse light intensity
- RGBAV1 _c_specular, // specular light intensity
- POINT4D_PTR _pos, // position of light
- VECTOR4D_PTR _dir, // direction of light
- float _kc, // attenuation factors
- float _kl,
- float _kq,
- float _spot_inner, // inner angle for spot light
- float _spot_outer, // outer angle for spot light
- float _pf) // power factor/falloff for spot lights
- {
- // this function initializes a light based on the flags sent in _attr, values that
- // aren't needed are set to 0 by caller
- // make sure light is in range
- if (index < 0 || index >= MAX_LIGHTS)
- return(0);
- // all good, initialize the light (many fields may be dead)
- lights[index].state = _state; // state of light
- lights[index].id = index; // id of light
- lights[index].attr = _attr; // type of light, and extra qualifiers
- lights[index].c_ambient = _c_ambient; // ambient light intensity
- lights[index].c_diffuse = _c_diffuse; // diffuse light intensity
- lights[index].c_specular = _c_specular; // specular light intensity
- lights[index].kc = _kc; // constant, linear, and quadratic attenuation factors
- lights[index].kl = _kl;
- lights[index].kq = _kq;
- if (_pos)
- VECTOR4D_COPY(&lights[index].pos, _pos); // position of light
- if (_dir)
- {
- VECTOR4D_COPY(&lights[index].dir, _dir); // direction of light
- // normalize it
- VECTOR4D_Normalize(&lights[index].dir);
- } // end if
- lights[index].spot_inner = _spot_inner; // inner angle for spot light
- lights[index].spot_outer = _spot_outer; // outer angle for spot light
- lights[index].pf = _pf; // power factor/falloff for spot lights
- // return light index as success
- return(index);
- } // end Create_Light_LIGHTV1
- //////////////////////////////////////////////////////////////////////////////
- int Reset_Lights_LIGHTV1(void)
- {
- // this function simply resets all lights in the system
- static int first_time = 1;
- memset(lights, 0, MAX_LIGHTS*sizeof(LIGHTV1));
- // reset number of lights
- num_lights = 0;
- // reset first time
- first_time = 0;
- // return success
- return(1);
- } // end Reset_Lights_LIGHTV1
- //////////////////////////////////////////////////////////////////////////////
- int Reset_Materials_MATV1(void)
- {
- // this function resets all the materials
- static int first_time = 1;
- // if this is the first time then zero EVERYTHING out
- if (first_time)
- {
- memset(materials, 0, MAX_MATERIALS*sizeof(MATV1));
- first_time = 0;
- } // end if
- // scan thru materials and release all textures, if any?
- for (int curr_matt = 0; curr_matt < MAX_MATERIALS; curr_matt++)
- {
- // regardless if the material is active check to see if there is a
- // dangling texture map
- Destroy_Bitmap(&materials[curr_matt].texture);
- // now it's safe to zero memory out
- memset(&materials[curr_matt], 0, sizeof(MATV1));
- } // end if
- return(1);
- } // end Reset_Materials_MATV1
- //////////////////////////////////////////////////////////////////////////////
- int RGB_16_8_IndexedRGB_Table_Builder(int rgb_format, // format we want to build table for
- // 99/100 565
- LPPALETTEENTRY src_palette, // source palette
- UCHAR *rgblookup) // lookup table
- {
- // this function takes as input the rgb format that it should generate the lookup table
- // for; dd_pixel_format = DD_PIXEL_FORMAT565, or DD_PIXEL_FORMAT555
- // notice in 5.5.5 format the input has only 32K possible colors and the high most
- // bit will be disregarded, thus the look up table will only need to be 32K
- // in either case, it's up to the caller to send in the rgblookup table pre-allocated
- // the function doesn't allocate memory for the caller
- // the function uses a simple least squares scan for all possible RGB colors in the
- // 16-bit color space that map to the discrete RGB space in the 8-bit palette
- // first check the pointers
- if (!src_palette || !rgblookup)
- return(-1);
- // what is the color depth we are building a table for?
- if (rgb_format==DD_PIXEL_FORMAT565)
- {
- // there are a total of 64k entries, perform a loop and look them up, do the least
- // amount of work, even with a pentium, there are 65536*256 interations here!
- for (int rgbindex = 0; rgbindex < 65536; rgbindex++)
- {
- int curr_index = -1; // current color index of best match
- long curr_error = INT_MAX; // distance in color space to nearest match or "error"
- for (int color_index = 0; color_index < 256; color_index++)
- {
- // extract r,g,b from rgbindex, assuming an encoding of 5.6.5, then scale to 8.8.8 since
- // palette is in that format always
- int r = (rgbindex >> 11) << 3;;
- int g = ((rgbindex >> 5) & 0x3f) << 2;
- int b = (rgbindex & 0x1f) << 3;
- // compute distance to color from target
- long delta_red = abs(src_palette[color_index].peRed - r);
- long delta_green = abs(src_palette[color_index].peGreen - g);
- long delta_blue = abs(src_palette[color_index].peBlue - b);
- long error = (delta_red*delta_red) + (delta_green*delta_green) + (delta_blue*delta_blue);
- // is this color a better match?
- if (error < curr_error)
- {
- curr_index = color_index;
- curr_error = error;
- } // end if
- } // end for color_index
- // best match has been found, enter it into table
- rgblookup[rgbindex] = curr_index;
- } // end for rgbindex
- } // end if
- else
- if (rgb_format==DD_PIXEL_FORMAT555)
- {
- // there are a total of 32k entries, perform a loop and look them up, do the least
- // amount of work, even with a pentium, there are 32768*256 interations here!
- for (int rgbindex = 0; rgbindex < 32768; rgbindex++)
- {
- int curr_index = -1; // current color index of best match
- long curr_error = INT_MAX; // distance in color space to nearest match or "error"
- for (int color_index = 0; color_index < 256; color_index++)
- {
- // extract r,g,b from rgbindex, assuming an encoding of 5.6.5, then scale to 8.8.8 since
- // palette is in that format always
- int r = (rgbindex >> 10) << 3;;
- int g = ((rgbindex >> 5) & 0x1f) << 3;
- int b = (rgbindex & 0x1f) << 3;
- // compute distance to color from target
- long delta_red = abs(src_palette[color_index].peRed - r);
- long delta_green = abs(src_palette[color_index].peGreen - g);
- long delta_blue = abs(src_palette[color_index].peBlue - b);
- long error = (delta_red*delta_red) + (delta_green*delta_green) + (delta_blue*delta_blue);
- // is this color a better match?
- if (error < curr_error)
- {
- curr_index = color_index;
- curr_error = error;
- } // end if
- } // end for color_index
- // best match has been found, enter it into table
- rgblookup[rgbindex] = curr_index;
- } // end for rgbindex
- } // end if
- else
- return(-1); // serious problem! unsupported format, what are you doing to me!!!!
- // return success
- return(1);
- } // end RGB_16_8_IndexedRGB_Table_Builder
- //////////////////////////////////////////////////////////////////////////////
- int RGB_16_8_Indexed_Intensity_Table_Builder(LPPALETTEENTRY src_palette, // source palette
- UCHAR rgbilookup[256][256], // lookup table
- int intensity_normalization)
- {
- // this function takes the source palette to compute the intensity shading table with
- // the table will be formatted such that each row is a color index, and each column
- // is the shade 0..255 desired, the output is a single byte index
- // in either case, it's up to the caller to send in the rgbilookup table pre-allocated
- // 64k buffer byte [256][256]the function doesn't allocate memory for the caller
- // the function builds the table by looping thru each color in the color palette and then
- // for each color, it scales the color to maximum intensity without overflow the RGB channels
- // and then uses this as the 100% intensity value of the color, then the algorithm computes
- // the 256 shades of the color, and then uses the standard least squares scan the find the
- // colors in the palette and stores them in the row of the current color under intensity
- // translation, sounds diabolical huh? Note: if you set intensity normalization to 0
- // the the maximization step isn't performed.
- int ri,gi,bi; // initial color
- int rw,gw,bw; // current working color
- float ratio; // scaling ratio
- float dl,dr,db,dg; // intensity gradients for 256 shades
- // first check the pointers
- if (!src_palette || !rgbilookup)
- return(-1);
- // for each color in the palette, compute maximum intensity value then scan
- // for 256 shades of it
- for (int col_index = 0; col_index < 256; col_index++)
- {
- // extract color from palette
- ri = src_palette[col_index].peRed;
- gi = src_palette[col_index].peGreen;
- bi = src_palette[col_index].peBlue;
- // find largest channel then max it out and scale other
- // channels based on ratio
- if (intensity_normalization==1)
- {
- // red largest?
- if (ri >= gi && ri >= bi)
- {
- // compute scaling ratio
- ratio = (float)255/(float)ri;
- // max colors out
- ri = 255;
- gi = (int)((float)gi * ratio + 0.5);
- bi = (int)((float)bi * ratio + 0.5);
- } // end if
- else // green largest?
- if (gi >= ri && gi >= bi)
- {
- // compute scaling ratio
- ratio = (float)255/(float)gi;
- // max colors out
- gi = 255;
- ri = (int)((float)ri * ratio + 0.5);
- bi = (int)((float)bi * ratio + 0.5);
- } // end if
- else // blue is largest
- {
- // compute scaling ratio
- ratio = (float)255/(float)bi;
- // max colors out
- bi = 255;
- ri = (int)((float)ri * ratio + 0.5);
- gi = (int)((float)gi * ratio + 0.5);
- } // end if
- } // end if
- // at this point, we need to compute the intensity gradients for this color,
- // so we can compute the RGB values for 256 shades of the current color
- dl = sqrt(ri*ri + gi*gi + bi*bi)/(float)256;
- dr = ri/dl,
- db = gi/dl,
- dg = bi/dl;
- // initialize working color
- rw = 0;
- gw = 0;
- bw = 0;
- // at this point rw,gw,bw, is the color that we need to compute the 256 intensities for to
- // enter into the col_index (th) row of the table
- for (int intensity_index = 0; intensity_index < 256; intensity_index++)
- {
- int curr_index = -1; // current color index of best match
- long curr_error = INT_MAX; // distance in color space to nearest match or "error"
- for (int color_index = 0; color_index < 256; color_index++)
- {
- // compute distance to color from target
- long delta_red = abs(src_palette[color_index].peRed - rw);
- long delta_green = abs(src_palette[color_index].peGreen - gw);
- long delta_blue = abs(src_palette[color_index].peBlue - bw);
- long error = (delta_red*delta_red) + (delta_green*delta_green) + (delta_blue*delta_blue);
- // is this color a better match?
- if (error < curr_error)
- {
- curr_index = color_index;
- curr_error = error;
- } // end if
- } // end for color_index
- // best match has been found, enter it into table
- rgbilookup[col_index][intensity_index] = curr_index;
- // compute next intensity level (test for overflow, shouldn't happen, but never know)
- if (rw+=dr > 255) rw=255;
- if (gw+=dg > 255) gw=255;
- if (bw+=db > 255) bw=255;
- } // end for intensity_index
- } // end for c_index
- // return success
- return(1);
- } // end RGB_16_8_Indexed_Intensity_Table_Builder
- ///////////////////////////////////////////////////////////////////////////////
- int Insert_OBJECT4DV1_RENDERLIST4DV12(RENDERLIST4DV1_PTR rend_list,
- OBJECT4DV1_PTR obj,
- int insert_local,
- int lighting_on)
- {
- // converts the entire object into a face list and then inserts
- // the visible, active, non-clipped, non-culled polygons into
- // the render list, also note the flag insert_local control
- // whether or not the vlist_local or vlist_trans vertex list
- // is used, thus you can insert an object "raw" totally untranformed
- // if you set insert_local to 1, default is 0, that is you would
- // only insert an object after at least the local to world transform
- // the last parameter is used to control if their has been
- // a lighting step that has generated a light value stored
- // in the upper 16-bits of color, if lighting_on = 1 then
- // this value is used to overwrite the base color of the
- // polygon when its sent to the rendering list
- unsigned int base_color; // save base color of polygon
- // is this objective inactive or culled or invisible?
- if (!(obj->state & OBJECT4DV1_STATE_ACTIVE) ||
- (obj->state & OBJECT4DV1_STATE_CULLED) ||
- !(obj->state & OBJECT4DV1_STATE_VISIBLE))
- return(0);
- // the object is valid, let's rip it apart polygon by polygon
- for (int poly = 0; poly < obj->num_polys; poly++)
- {
- // acquire polygon
- POLY4DV1_PTR curr_poly = &obj->plist[poly];
- // first is this polygon even visible?
- if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
- (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
- (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
- continue; // move onto next poly
- // override vertex list polygon refers to
- // the case that you want the local coords used
- // first save old pointer
- POINT4D_PTR vlist_old = curr_poly->vlist;
- if (insert_local)
- curr_poly->vlist = obj->vlist_local;
- else
- curr_poly->vlist = obj->vlist_trans;
- // test if we should overwrite color with upper 16-bits
- if (lighting_on==1)
- {
- // save color for a sec
- base_color = (unsigned int)(curr_poly->color);
- curr_poly->color = (int)(base_color >> 16);
- } // end if
- // now insert this polygon
- if (!Insert_POLY4DV1_RENDERLIST4DV1(rend_list, curr_poly))
- {
- // fix vertex list pointer
- curr_poly->vlist = vlist_old;
- // the whole object didn't fit!
- return(0);
- } // end if
- // test if we should overwrite color with upper 16-bits
- if (lighting_on==1)
- {
- // fix color upc
- curr_poly->color = (int)(base_color & 0xffff);
- } // end if
- // fix vertex list pointer
- curr_poly->vlist = vlist_old;
- } // end for
- // return success
- return(1);
- } // end Insert_OBJECT4DV1_RENDERLIST4DV12
- ///////////////////////////////////////////////////////////////////////////////
- int Light_OBJECT4DV1_World16(OBJECT4DV1_PTR obj, // object to process
- CAM4DV1_PTR cam, // camera position
- LIGHTV1_PTR lights, // light list (might have more than one)
- int max_lights) // maximum lights in list
- {
- // 16-bit version of function
- // function lights an object based on the sent lights and camera. the function supports
- // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
- // note that this lighting function is rather brute force and simply follows the math, however
- // there are some clever integer operations that are used in scale 256 rather than going to floating
- // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
- // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
- // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
- // of the falloff due to attenuation, but they still look like spot lights
- // type 2 spot lights are implemented with the intensity having a dot product relationship with the
- // angle from the surface point to the light direction just like in the optimized model, but the pf term
- // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
- unsigned int r_base, g_base, b_base, // base color being lit
- r_sum, g_sum, b_sum, // sum of lighting process over all lights
- shaded_color; // final color
- float dp, // dot product
- dist, // distance from light to surface
- i, // general intensities
- nl, // length of normal
- atten; // attenuation computations
- // test if the object is culled
- if (!(obj->state & OBJECT4DV1_STATE_ACTIVE) ||
- (obj->state & OBJECT4DV1_STATE_CULLED) ||
- !(obj->state & OBJECT4DV1_STATE_VISIBLE))
- return(0);
- // process each poly in mesh
- for (int poly=0; poly < obj->num_polys; poly++)
- {
- // acquire polygon
- POLY4DV1_PTR curr_poly = &obj->plist[poly];
- // is this polygon valid?
- // test this polygon if and only if it's not clipped, not culled,
- // active, and visible. Note we test for backface in the event that
- // a previous call might have already determined this, so why work
- // harder!
- if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
- (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
- (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
- continue; // move onto next poly
- // extract vertex indices into master list, rember the polygons are
- // NOT self contained, but based on the vertex list stored in the object
- // itself
- int vindex_0 = curr_poly->vert[0];
- int vindex_1 = curr_poly->vert[1];
- int vindex_2 = curr_poly->vert[2];
- // we will use the transformed polygon vertex list since the backface removal
- // only makes sense at the world coord stage further of the pipeline
- // test the lighting mode of the polygon (use flat for flat, gouraud))
- if (curr_poly->attr & POLY4DV1_ATTR_SHADE_MODE_FLAT || curr_poly->attr & POLY4DV1_ATTR_SHADE_MODE_GOURAUD)
- {
- // step 1: extract the base color out in RGB mode
- if (dd_pixel_format == DD_PIXEL_FORMAT565)
- {
- _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
- // scale to 8 bit
- r_base <<= 3;
- g_base <<= 2;
- b_base <<= 3;
- } // end if
- else
- {
- _RGB555FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
- // scale to 8 bit
- r_base <<= 3;
- g_base <<= 3;
- b_base <<= 3;
- } // end if
- // initialize color sum
- r_sum = 0;
- g_sum = 0;
- b_sum = 0;
- // loop thru lights
- for (int curr_light = 0; curr_light < max_lights; curr_light++)
- {
- // is this light active
- if (lights[curr_light].state==LIGHTV1_STATE_OFF)
- continue;
- // what kind of light are we dealing with
- if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
- {
- // simply multiply each channel against the color of the
- // polygon then divide by 256 to scale back to 0..255
- // use a shift in real life!!! >> 8
- r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
- g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
- b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
- // there better only be one ambient light!
- } // end if
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE)
- {
- // infinite lighting, we need the surface normal, and the direction
- // of the light source
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n;
- // build u, v
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // ok, recalling the lighting model for infinite lights
- // I(d)dir = I0dir * Cldir
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp/nl;
- r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if infinite light
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_POINT)
- {
- // perform point light computations
- // light model for point light is once again:
- // I0point * Clpoint
- // I(d)point = ___________________
- // kc + kl*d + kq*d2
- //
- // Where d = |p - s|
- // thus it's almost identical to the infinite light, but attenuates as a function
- // of distance from the point source to the surface point being lit
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, l;
- // build u, v
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // compute vector from surface to light
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast(&l);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &l);
- // only add light if dp > 0
- if (dp > 0)
- {
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- i = 128*dp / (nl * dist * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if point
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1)
- {
- // perform spotlight/point computations simplified model that uses
- // point light WITH a direction to simulate a spotlight
- // light model for point light is once again:
- // I0point * Clpoint
- // I(d)point = ___________________
- // kc + kl*d + kq*d2
- //
- // Where d = |p - s|
- // thus it's almost identical to the infinite light, but attenuates as a function
- // of distance from the point source to the surface point being lit
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, l;
- // build u, v
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
- // compute cross product (we need -n, so do vxu)
- VECTOR4D_Cross(&v, &u, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // compute vector from surface to light
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast(&l);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- // note that I use the direction of the light here rather than a the vector to the light
- // thus we are taking orientation into account which is similar to the spotlight model
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- i = 128*dp / (nl * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if spotlight1
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version
- {
- // perform spot light computations
- // light model for spot light simple version is once again:
- // I0spotlight * Clspotlight * MAX( (l . s), 0)^pf
- // I(d)spotlight = __________________________________________
- // kc + kl*d + kq*d2
- // Where d = |p - s|, and pf = power factor
- // thus it's almost identical to the point, but has the extra term in the numerator
- // relating the angle between the light source and the point on the surface
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, d, s;
- // build u, v
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
- // compute cross product (v x u, to invert n)
- VECTOR4D_Cross(&v, &u, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- // compute vector from light to surface (different from l which IS the light dir)
- VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_0 ], &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dist = VECTOR4D_Length_Fast(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dist;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
- // must be integral
- float dpsl_exp = dpsl;
- // exponentiate for positive integral powers
- for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
- dpsl_exp*=dpsl;
- // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf
- i = 128*dp * dpsl_exp / (nl * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- } // end if spot light
- } // end for light
- // make sure colors aren't out of range
- if (r_sum > 255) r_sum = 255;
- if (g_sum > 255) g_sum = 255;
- if (b_sum > 255) b_sum = 255;
- // write the color
- shaded_color = RGB16Bit(r_sum, g_sum, b_sum);
- curr_poly->color = (int)((shaded_color << 16) | curr_poly->color);
- } // end if
- else // assume POLY4DV1_ATTR_SHADE_MODE_CONSTANT
- {
- // emmisive shading only, copy base color into upper 16-bits
- // without any change
- curr_poly->color = (int)((curr_poly->color << 16) | curr_poly->color);
- } // end if
- } // end for poly
- // return success
- return(1);
- } // end Light_OBJECT4DV1_World16
- ///////////////////////////////////////////////////////////////////////////////
- int Light_OBJECT4DV1_World(OBJECT4DV1_PTR obj, // object to process
- CAM4DV1_PTR cam, // camera position
- LIGHTV1_PTR lights, // light list (might have more than one)
- int max_lights) // maximum lights in list
- {
- // 8 bit version
- // function lights an object based on the sent lights and camera. the function supports
- // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
- // note that this lighting function is rather brute force and simply follows the math, however
- // there are some clever integer operations that are used in scale 256 rather than going to floating
- // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
- // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
- // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
- // of the falloff due to attenuation, but they still look like spot lights
- // type 2 spot lights are implemented with the intensity having a dot product relationship with the
- // angle from the surface point to the light direction just like in the optimized model, but the pf term
- // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
- // the function works in 8-bit color space, and uses the rgblookup[] to look colors up from RGB values
- // basically, the function converts the 8-bit color index into an RGB value performs the lighting
- // operations and then back into an 8-bit color index with the table, so we loose a little bit during the incoming and
- // outgoing transformations, however, the next step for optimization will be to make a purely monochromatic
- // 8-bit system that assumes ALL lights are white, but this function works with color for now
- unsigned int r_base, g_base, b_base, // base color being lit
- r_sum, g_sum, b_sum, // sum of lighting process over all lights
- shaded_color; // final color
- float dp, // dot product
- dist, // distance from light to surface
- i, // general intensities
- nl, // length of normal
- atten; // attenuation computations
- // test if the object is culled
- if (!(obj->state & OBJECT4DV1_STATE_ACTIVE) ||
- (obj->state & OBJECT4DV1_STATE_CULLED) ||
- !(obj->state & OBJECT4DV1_STATE_VISIBLE))
- return(0);
- // process each poly in mesh
- for (int poly=0; poly < obj->num_polys; poly++)
- {
- // acquire polygon
- POLY4DV1_PTR curr_poly = &obj->plist[poly];
- // is this polygon valid?
- // test this polygon if and only if it's not clipped, not culled,
- // active, and visible. Note we test for backface in the event that
- // a previous call might have already determined this, so why work
- // harder!
- if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
- (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
- (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
- continue; // move onto next poly
- // extract vertex indices into master list, rember the polygons are
- // NOT self contained, but based on the vertex list stored in the object
- // itself
- int vindex_0 = curr_poly->vert[0];
- int vindex_1 = curr_poly->vert[1];
- int vindex_2 = curr_poly->vert[2];
- // we will use the transformed polygon vertex list since the backface removal
- // only makes sense at the world coord stage further of the pipeline
- // test the lighting mode of the polygon (use flat for flat, gouraud))
- if (curr_poly->attr & POLY4DV1_ATTR_SHADE_MODE_FLAT || curr_poly->attr & POLY4DV1_ATTR_SHADE_MODE_GOURAUD)
- {
- // step 1: extract the base color out in RGB mode (it's already in 8 bits per channel)
- r_base = palette[curr_poly->color].peRed;
- g_base = palette[curr_poly->color].peGreen;
- b_base = palette[curr_poly->color].peBlue;
- // initialize color sum
- r_sum = 0;
- g_sum = 0;
- b_sum = 0;
- // loop thru lights
- for (int curr_light = 0; curr_light < max_lights; curr_light++)
- {
- // is this light active
- if (lights[curr_light].state==LIGHTV1_STATE_OFF)
- continue;
- // what kind of light are we dealing with
- if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
- {
- // simply multiply each channel against the color of the
- // polygon then divide by 256 to scale back to 0..255
- // use a shift in real life!!! >> 8
- r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
- g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
- b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
- // there better only be one ambient light!
- } // end if
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE)
- {
- // infinite lighting, we need the surface normal, and the direction
- // of the light source
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n;
- // build u, v
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // ok, recalling the lighting model for infinite lights
- // I(d)dir = I0dir * Cldir
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp/nl;
- r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if infinite light
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_POINT)
- {
- // perform point light computations
- // light model for point light is once again:
- // I0point * Clpoint
- // I(d)point = ___________________
- // kc + kl*d + kq*d2
- //
- // Where d = |p - s|
- // thus it's almost identical to the infinite light, but attenuates as a function
- // of distance from the point source to the surface point being lit
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, l;
- // build u, v
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // compute vector from surface to light
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast(&l);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &l);
- // only add light if dp > 0
- if (dp > 0)
- {
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- i = 128*dp / (nl * dist * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if point
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1)
- {
- // perform spotlight/point computations simplified model that uses
- // point light WITH a direction to simulate a spotlight
- // light model for point light is once again:
- // I0point * Clpoint
- // I(d)point = ___________________
- // kc + kl*d + kq*d2
- //
- // Where d = |p - s|
- // thus it's almost identical to the infinite light, but attenuates as a function
- // of distance from the point source to the surface point being lit
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, l;
- // build u, v
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
- // compute cross product (we need -n, so do vxu)
- VECTOR4D_Cross(&v, &u, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // compute vector from surface to light
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast(&l);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- // note that I use the direction of the light here rather than a the vector to the light
- // thus we are taking orientation into account which is similar to the spotlight model
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- i = 128*dp / (nl * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if spotlight1
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version
- {
- // perform spot light computations
- // light model for spot light simple version is once again:
- // I0spotlight * Clspotlight * MAX( (l . s), 0)^pf
- // I(d)spotlight = __________________________________________
- // kc + kl*d + kq*d2
- // Where d = |p - s|, and pf = power factor
- // thus it's almost identical to the point, but has the extra term in the numerator
- // relating the angle between the light source and the point on the surface
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, d, s;
- // build u, v
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_1 ], &u);
- VECTOR4D_Build(&obj->vlist_trans[ vindex_0 ], &obj->vlist_trans[ vindex_2 ], &v);
- // compute cross product (v x u, to invert n)
- VECTOR4D_Cross(&v, &u, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- // compute vector from light to surface (different from l which IS the light dir)
- VECTOR4D_Build( &lights[curr_light].pos, &obj->vlist_trans[ vindex_0 ], &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dist = VECTOR4D_Length_Fast(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dist;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
- // must be integral
- float dpsl_exp = dpsl;
- // exponentiate for positive integral powers
- for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
- dpsl_exp*=dpsl;
- // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf
- i = 128*dp * dpsl_exp / (nl * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- } // end if spot light
- } // end for light
- // make sure colors aren't out of range
- if (r_sum > 255) r_sum = 255;
- if (g_sum > 255) g_sum = 255;
- if (b_sum > 255) b_sum = 255;
- // use straght rgb look up, assume 565 format, more color space
- int rgbindex = RGB16Bit565(r_sum, g_sum, b_sum);
- shaded_color = rgblookup[rgbindex];
- // and now insert color index into upper 16 bits
- curr_poly->color = (int)((shaded_color << 16) | curr_poly->color);
- } // end if
- else // assume POLY4DV1_ATTR_SHADE_MODE_CONSTANT
- {
- // emmisive shading only, copy base color into upper 16-bits
- // without any change
- curr_poly->color = (int)((curr_poly->color << 16) | curr_poly->color);
- } // end if
- } // end for poly
- // return success
- return(1);
- } // end Light_OBJECT4DV1_World
- //////////////////////////////////////////////////////////////////////////////
- int Light_RENDERLIST4DV1_World16(RENDERLIST4DV1_PTR rend_list, // list to process
- CAM4DV1_PTR cam, // camera position
- LIGHTV1_PTR lights, // light list (might have more than one)
- int max_lights) // maximum lights in list
- {
- // 16-bit version of function
- // function lights the enture rendering list based on the sent lights and camera. the function supports
- // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
- // note that this lighting function is rather brute force and simply follows the math, however
- // there are some clever integer operations that are used in scale 256 rather than going to floating
- // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
- // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
- // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
- // of the falloff due to attenuation, but they still look like spot lights
- // type 2 spot lights are implemented with the intensity having a dot product relationship with the
- // angle from the surface point to the light direction just like in the optimized model, but the pf term
- // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
- // also note since we are dealing with a rendering list and not object, the final lit color is
- // immediately written over the real color
- unsigned int r_base, g_base, b_base, // base color being lit
- r_sum, g_sum, b_sum, // sum of lighting process over all lights
- shaded_color; // final color
- float dp, // dot product
- dist, // distance from light to surface
- i, // general intensities
- nl, // length of normal
- atten; // attenuation computations
- // for each valid poly, light it...
- for (int poly=0; poly < rend_list->num_polys; poly++)
- {
- // acquire polygon
- POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
- // light this polygon if and only if it's not clipped, not culled,
- // active, and visible
- if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
- (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
- (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
- continue; // move onto next poly
- // we will use the transformed polygon vertex list since the backface removal
- // only makes sense at the world coord stage further of the pipeline
- // test the lighting mode of the polygon (use flat for flat, gouraud))
- if (curr_poly->attr & POLY4DV1_ATTR_SHADE_MODE_FLAT || curr_poly->attr & POLY4DV1_ATTR_SHADE_MODE_GOURAUD)
- {
- // step 1: extract the base color out in RGB mode
- if (dd_pixel_format == DD_PIXEL_FORMAT565)
- {
- _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
- // scale to 8 bit
- r_base <<= 3;
- g_base <<= 2;
- b_base <<= 3;
- } // end if
- else
- {
- _RGB555FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
- // scale to 8 bit
- r_base <<= 3;
- g_base <<= 3;
- b_base <<= 3;
- } // end if
- // initialize color sum
- r_sum = 0;
- g_sum = 0;
- b_sum = 0;
- // loop thru lights
- for (int curr_light = 0; curr_light < max_lights; curr_light++)
- {
- // is this light active
- if (lights[curr_light].state==LIGHTV1_STATE_OFF)
- continue;
- // what kind of light are we dealing with
- if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
- {
- // simply multiply each channel against the color of the
- // polygon then divide by 256 to scale back to 0..255
- // use a shift in real life!!! >> 8
- r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
- g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
- b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
- // there better only be one ambient light!
- } // end if
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE)
- {
- // infinite lighting, we need the surface normal, and the direction
- // of the light source
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n;
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // ok, recalling the lighting model for infinite lights
- // I(d)dir = I0dir * Cldir
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp/nl;
- r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if infinite light
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_POINT)
- {
- // perform point light computations
- // light model for point light is once again:
- // I0point * Clpoint
- // I(d)point = ___________________
- // kc + kl*d + kq*d2
- //
- // Where d = |p - s|
- // thus it's almost identical to the infinite light, but attenuates as a function
- // of distance from the point source to the surface point being lit
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, l;
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0], &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast(&l);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &l);
- // only add light if dp > 0
- if (dp > 0)
- {
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- i = 128*dp / (nl * dist * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if point
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1)
- {
- // perform spotlight/point computations simplified model that uses
- // point light WITH a direction to simulate a spotlight
- // light model for point light is once again:
- // I0point * Clpoint
- // I(d)point = ___________________
- // kc + kl*d + kq*d2
- //
- // Where d = |p - s|
- // thus it's almost identical to the infinite light, but attenuates as a function
- // of distance from the point source to the surface point being lit
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, l;
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
- // compute cross product (we need -n, so do vxu)
- VECTOR4D_Cross(&v, &u, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0], &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast(&l);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- // note that I use the direction of the light here rather than a the vector to the light
- // thus we are taking orientation into account which is similar to the spotlight model
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- i = 128*dp / (nl * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if spotlight1
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version
- {
- // perform spot light computations
- // light model for spot light simple version is once again:
- // I0spotlight * Clspotlight * MAX( (l . s), 0)^pf
- // I(d)spotlight = __________________________________________
- // kc + kl*d + kq*d2
- // Where d = |p - s|, and pf = power factor
- // thus it's almost identical to the point, but has the extra term in the numerator
- // relating the angle between the light source and the point on the surface
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, d, s;
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
- // compute cross product (v x u, to invert n)
- VECTOR4D_Cross(&v, &u, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- // compute vector from light to surface (different from l which IS the light dir)
- VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[0], &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dist = VECTOR4D_Length_Fast(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dist;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
- // must be integral
- float dpsl_exp = dpsl;
- // exponentiate for positive integral powers
- for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
- dpsl_exp*=dpsl;
- // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf
- i = 128*dp * dpsl_exp / (nl * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- } // end if spot light
- } // end for light
- // make sure colors aren't out of range
- if (r_sum > 255) r_sum = 255;
- if (g_sum > 255) g_sum = 255;
- if (b_sum > 255) b_sum = 255;
- // write the color over current color
- curr_poly->color = RGB16Bit(r_sum, g_sum, b_sum);
- } // end if
- else // assume POLY4DV1_ATTR_SHADE_MODE_CONSTANT
- {
- // emmisive shading only, do nothing
- // ...
- } // end if
- } // end for poly
- // return success
- return(1);
- } // end Light_RENDERLIST4DV1_World16
- //////////////////////////////////////////////////////////////////////////////
- int Light_RENDERLIST4DV1_World(RENDERLIST4DV1_PTR rend_list, // list to process
- CAM4DV1_PTR cam, // camera position
- LIGHTV1_PTR lights, // light list (might have more than one)
- int max_lights) // maximum lights in list
- {
- // 8-bit version of function
- // function lights the enture rendering list based on the sent lights and camera. the function supports
- // constant/pure shading (emmisive), flat shading with ambient, infinite, point lights, and spot lights
- // note that this lighting function is rather brute force and simply follows the math, however
- // there are some clever integer operations that are used in scale 256 rather than going to floating
- // point, but why? floating point and ints are the same speed, HOWEVER, the conversion to and from floating
- // point can be cycle intensive, so if you can keep your calcs in ints then you can gain some speed
- // also note, type 1 spot lights are simply point lights with direction, the "cone" is more of a function
- // of the falloff due to attenuation, but they still look like spot lights
- // type 2 spot lights are implemented with the intensity having a dot product relationship with the
- // angle from the surface point to the light direction just like in the optimized model, but the pf term
- // that is used for a concentration control must be 1,2,3,.... integral and non-fractional
- // the function works in 8-bit color space, and uses the rgblookup[] to look colors up from RGB values
- // basically, the function converts the 8-bit color index into an RGB value performs the lighting
- // operations and then back into an 8-bit color index with the table, so we loose a little bit during the incoming and
- // outgoing transformations, however, the next step for optimization will be to make a purely monochromatic
- // 8-bit system that assumes ALL lights are white, but this function works with color for now
- // also note since we are dealing with a rendering list and not object, the final lit color is
- // immediately written over the real color
- unsigned int r_base, g_base, b_base, // base color being lit
- r_sum, g_sum, b_sum, // sum of lighting process over all lights
- shaded_color; // final color
- float dp, // dot product
- dist, // distance from light to surface
- i, // general intensities
- nl, // length of normal
- atten; // attenuation computations
- // for each valid poly, light it...
- for (int poly=0; poly < rend_list->num_polys; poly++)
- {
- // acquire polygon
- POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];
- // light this polygon if and only if it's not clipped, not culled,
- // active, and visible
- if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
- (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
- (curr_poly->state & POLY4DV1_STATE_BACKFACE) )
- continue; // move onto next poly
- // we will use the transformed polygon vertex list since the backface removal
- // only makes sense at the world coord stage further of the pipeline
- // test the lighting mode of the polygon (use flat for flat, gouraud))
- if (curr_poly->attr & POLY4DV1_ATTR_SHADE_MODE_FLAT || curr_poly->attr & POLY4DV1_ATTR_SHADE_MODE_GOURAUD)
- {
- // step 1: extract the base color out in RGB mode (it's already in 8 bits per channel)
- r_base = palette[curr_poly->color].peRed;
- g_base = palette[curr_poly->color].peGreen;
- b_base = palette[curr_poly->color].peBlue;
- // initialize color sum
- r_sum = 0;
- g_sum = 0;
- b_sum = 0;
- // loop thru lights
- for (int curr_light = 0; curr_light < max_lights; curr_light++)
- {
- // is this light active
- if (lights[curr_light].state==LIGHTV1_STATE_OFF)
- continue;
- // what kind of light are we dealing with
- if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
- {
- // simply multiply each channel against the color of the
- // polygon then divide by 256 to scale back to 0..255
- // use a shift in real life!!! >> 8
- r_sum+= ((lights[curr_light].c_ambient.r * r_base) / 256);
- g_sum+= ((lights[curr_light].c_ambient.g * g_base) / 256);
- b_sum+= ((lights[curr_light].c_ambient.b * b_base) / 256);
- // there better only be one ambient light!
- } // end if
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE)
- {
- // infinite lighting, we need the surface normal, and the direction
- // of the light source
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n;
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // ok, recalling the lighting model for infinite lights
- // I(d)dir = I0dir * Cldir
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp/nl;
- r_sum+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if infinite light
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_POINT)
- {
- // perform point light computations
- // light model for point light is once again:
- // I0point * Clpoint
- // I(d)point = ___________________
- // kc + kl*d + kq*d2
- //
- // Where d = |p - s|
- // thus it's almost identical to the infinite light, but attenuates as a function
- // of distance from the point source to the surface point being lit
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, l;
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0], &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast(&l);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &l);
- // only add light if dp > 0
- if (dp > 0)
- {
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- i = 128*dp / (nl * dist * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if point
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1)
- {
- // perform spotlight/point computations simplified model that uses
- // point light WITH a direction to simulate a spotlight
- // light model for point light is once again:
- // I0point * Clpoint
- // I(d)point = ___________________
- // kc + kl*d + kq*d2
- //
- // Where d = |p - s|
- // thus it's almost identical to the infinite light, but attenuates as a function
- // of distance from the point source to the surface point being lit
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, l;
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
- // compute cross product (we need -n, so do vxu)
- VECTOR4D_Cross(&v, &u, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0], &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast(&l);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- // note that I use the direction of the light here rather than a the vector to the light
- // thus we are taking orientation into account which is similar to the spotlight model
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- i = 128*dp / (nl * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if spotlight1
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version
- {
- // perform spot light computations
- // light model for spot light simple version is once again:
- // I0spotlight * Clspotlight * MAX( (l . s), 0)^pf
- // I(d)spotlight = __________________________________________
- // kc + kl*d + kq*d2
- // Where d = |p - s|, and pf = power factor
- // thus it's almost identical to the point, but has the extra term in the numerator
- // relating the angle between the light source and the point on the surface
- // we need to compute the normal of this polygon face, and recall
- // that the vertices are in cw order, u=p0->p1, v=p0->p2, n=uxv
- VECTOR4D u, v, n, d, s;
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[1], &u);
- VECTOR4D_Build(&curr_poly->tvlist[0], &curr_poly->tvlist[2], &v);
- // compute cross product (v x u, to invert n)
- VECTOR4D_Cross(&v, &u, &n);
- // at this point, we are almost ready, but we have to normalize the normal vector!
- // this is a key optimization we can make later, we can pre-compute the length of all polygon
- // normals, so this step can be optimized
- // compute length of normal
- nl = VECTOR4D_Length_Fast(&n);
- // and for the diffuse model
- // Itotald = Rsdiffuse*Idiffuse * (n . l)
- // so we basically need to multiple it all together
- // notice the scaling by 128, I want to avoid floating point calculations, not because they
- // are slower, but the conversion to and from cost cycles
- dp = VECTOR4D_Dot(&n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- // compute vector from light to surface (different from l which IS the light dir)
- VECTOR4D_Build( &lights[curr_light].pos, &curr_poly->tvlist[0], &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dist = VECTOR4D_Length_Fast(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dist;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dist + lights[curr_light].kq*dist*dist);
- // for speed reasons, pf exponents that are less that 1.0 are out of the question, and exponents
- // must be integral
- float dpsl_exp = dpsl;
- // exponentiate for positive integral powers
- for (int e_index = 1; e_index < (int)lights[curr_light].pf; e_index++)
- dpsl_exp*=dpsl;
- // now dpsl_exp holds (dpsl)^pf power which is of course (s . l)^pf
- i = 128*dp * dpsl_exp / (nl * atten );
- r_sum += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- } // end if spot light
- } // end for light
- // make sure colors aren't out of range
- if (r_sum > 255) r_sum = 255;
- if (g_sum > 255) g_sum = 255;
- if (b_sum > 255) b_sum = 255;
- // look color up
- int rgbindex = RGB16Bit565(r_sum, g_sum, b_sum);
- // write the color over current color
- curr_poly->color = rgblookup[rgbindex];
- } // end if
- else // assume POLY4DV1_ATTR_SHADE_MODE_CONSTANT
- {
- // emmisive shading only, do nothing
- // ...
- } // end if
- } // end for poly
- // return success
- return(1);
- } // end Light_RENDERLIST4DV1_World
- ///////////////////////////////////////////////////////////////////////////////
- void Sort_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, int sort_method)
- {
- // this function sorts the rendering list based on the polygon z-values
- // the specific sorting method is controlled by sending in control flags
- // #define SORT_POLYLIST_AVGZ 0 - sorts on average of all vertices
- // #define SORT_POLYLIST_NEARZ 1 - sorts on closest z vertex of each poly
- // #define SORT_POLYLIST_FARZ 2 - sorts on farthest z vertex of each poly
- switch(sort_method)
- {
- case SORT_POLYLIST_AVGZ: // - sorts on average of all vertices
- {
- qsort((void *)rend_list->poly_ptrs, rend_list->num_polys, sizeof(POLYF4DV1_PTR), Compare_AvgZ_POLYF4DV1);
- } break;
- case SORT_POLYLIST_NEARZ: // - sorts on closest z vertex of each poly
- {
- qsort((void *)rend_list->poly_ptrs, rend_list->num_polys, sizeof(POLYF4DV1_PTR), Compare_NearZ_POLYF4DV1);
- } break;
- case SORT_POLYLIST_FARZ: // - sorts on farthest z vertex of each poly
- {
- qsort((void *)rend_list->poly_ptrs, rend_list->num_polys, sizeof(POLYF4DV1_PTR), Compare_FarZ_POLYF4DV1);
- } break;
- default: break;
- } // end switch
- } // end Sort_RENDERLIST4DV1
- ////////////////////////////////////////////////////////////////////////////////
- int Compare_AvgZ_POLYF4DV1(const void *arg1, const void *arg2)
- {
- // this function comapares the average z's of two polygons and is used by the
- // depth sort surface ordering algorithm
- float z1, z2;
- POLYF4DV1_PTR poly_1, poly_2;
- // dereference the poly pointers
- poly_1 = *((POLYF4DV1_PTR *)(arg1));
- poly_2 = *((POLYF4DV1_PTR *)(arg2));
- // compute z average of each polygon
- z1 = (float)0.33333*(poly_1->tvlist[0].z + poly_1->tvlist[1].z + poly_1->tvlist[2].z);
- // now polygon 2
- z2 = (float)0.33333*(poly_2->tvlist[0].z + poly_2->tvlist[1].z + poly_2->tvlist[2].z);
- // compare z1 and z2, such that polys' will be sorted in descending Z order
- if (z1 > z2)
- return(-1);
- else
- if (z1 < z2)
- return(1);
- else
- return(0);
- } // end Compare_AvgZ_POLYF4DV1
- ////////////////////////////////////////////////////////////////////////////////
- int Compare_NearZ_POLYF4DV1(const void *arg1, const void *arg2)
- {
- // this function comapares the closest z's of two polygons and is used by the
- // depth sort surface ordering algorithm
- float z1, z2;
- POLYF4DV1_PTR poly_1, poly_2;
- // dereference the poly pointers
- poly_1 = *((POLYF4DV1_PTR *)(arg1));
- poly_2 = *((POLYF4DV1_PTR *)(arg2));
- // compute the near z of each polygon
- z1 = MIN(poly_1->tvlist[0].z, poly_1->tvlist[1].z);
- z1 = MIN(z1, poly_1->tvlist[2].z);
- z2 = MIN(poly_2->tvlist[0].z, poly_2->tvlist[1].z);
- z2 = MIN(z2, poly_2->tvlist[2].z);
- // compare z1 and z2, such that polys' will be sorted in descending Z order
- if (z1 > z2)
- return(-1);
- else
- if (z1 < z2)
- return(1);
- else
- return(0);
- } // end Compare_NearZ_POLYF4DV1
- ////////////////////////////////////////////////////////////////////////////////
- int Compare_FarZ_POLYF4DV1(const void *arg1, const void *arg2)
- {
- // this function comapares the farthest z's of two polygons and is used by the
- // depth sort surface ordering algorithm
- float z1, z2;
- POLYF4DV1_PTR poly_1, poly_2;
- // dereference the poly pointers
- poly_1 = *((POLYF4DV1_PTR *)(arg1));
- poly_2 = *((POLYF4DV1_PTR *)(arg2));
- // compute the near z of each polygon
- z1 = MAX(poly_1->tvlist[0].z, poly_1->tvlist[1].z);
- z1 = MAX(z1, poly_1->tvlist[2].z);
- z2 = MAX(poly_2->tvlist[0].z, poly_2->tvlist[1].z);
- z2 = MAX(z2, poly_2->tvlist[2].z);
- // compare z1 and z2, such that polys' will be sorted in descending Z order
- if (z1 > z2)
- return(-1);
- else
- if (z1 < z2)
- return(1);
- else
- return(0);
- } // end Compare_FarZ_POLYF4DV1