t3dlib7.cpp
资源名称:Source.rar [点击查看]
上传用户:husern
上传日期:2018-01-20
资源大小:42486k
文件大小:512k
源码类别:
游戏
开发平台:
Visual C++
- {
- // 16-bit version of function
- // function lights the entire 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
- // this function now performs emissive, flat, and gouraud lighting, results are stored in the
- // lit_color[] array of each polygon
- 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
- r_sum0, g_sum0, b_sum0,
- r_sum1, g_sum1, b_sum1,
- r_sum2, g_sum2, b_sum2,
- ri,gi,bi,
- shaded_color; // final color
- float dp, // dot product
- dist, // distance from light to surface
- dists,
- i, // general intensities
- nl, // length of normal
- atten; // attenuation computations
- VECTOR4D u, v, n, l, d, s; // used for cross product and light vector calculations
- //Write_Error("nEntering lighting function");
- // for each valid poly, light it...
- for (int poly=0; poly < rend_list->num_polys; poly++)
- {
- // acquire polygon
- POLYF4DV2_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 & POLY4DV2_STATE_ACTIVE) ||
- (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
- (curr_poly->state & POLY4DV2_STATE_BACKFACE) ||
- (curr_poly->state & POLY4DV2_STATE_LIT) )
- continue; // move onto next poly
- //Write_Error("npoly %d",poly);
- #ifdef DEBUG_ON
- // track rendering stats
- debug_polys_lit_per_frame++;
- #endif
- // set state of polygon to lit
- SET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
- // 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 & POLY4DV2_ATTR_SHADE_MODE_FLAT)
- {
- //Write_Error("nEntering Flat Shader");
- // step 1: extract the base color out in RGB mode
- // assume 565 format
- _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
- // scale to 8 bit
- r_base <<= 3;
- g_base <<= 2;
- b_base <<= 3;
- //Write_Error("nBase color=%d,%d,%d", r_base, g_base, b_base);
- // initialize color sum
- r_sum = 0;
- g_sum = 0;
- b_sum = 0;
- //Write_Error("nsum color=%d,%d,%d", r_sum, g_sum, b_sum);
- // new optimization:
- // when there are multiple lights in the system we will end up performing numerous
- // redundant calculations to minimize this my strategy is to set key variables to
- // to MAX values on each loop, then during the lighting calcs to test the vars for
- // the max value, if they are the max value then the first light that needs the math
- // will do it, and then save the information into the variable (causing it to change state
- // from an invalid number) then any other lights that need the math can use the previously
- // computed value
- // set surface normal.z to FLT_MAX to flag it as non-computed
- n.z = FLT_MAX;
- // 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;
- //Write_Error("nprocessing light %d",curr_light);
- // what kind of light are we dealing with
- if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
- {
- //Write_Error("nEntering ambient light...");
- // 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);
- //Write_Error("nambient sum=%d,%d,%d", r_sum, g_sum, b_sum);
- // there better only be one ambient light!
- } // end if
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) ///////////////////////////////////////////
- {
- //Write_Error("nEntering infinite light...");
- // infinite lighting, we need the surface normal, and the direction
- // of the light source
- // test if we already computed poly normal in previous calculation
- if (n.z==FLT_MAX)
- {
- // 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
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- } // end if
- // 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_Fast2(&n);
- nl = curr_poly->nlength;
- // 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
- //Write_Error("ninfinite sum=%d,%d,%d", r_sum, g_sum, b_sum);
- } // end if infinite light
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) ///////////////////////////////////////
- {
- //Write_Error("nEntering point light...");
- // 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
- // test if we already computed poly normal in previous calculation
- if (n.z==FLT_MAX)
- {
- // 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
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- } // end if
- // 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_Fast2(&n);
- nl = curr_poly->nlength;
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast2(&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
- //Write_Error("npoint sum=%d,%d,%d",r_sum,g_sum,b_sum);
- } // end if point
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ////////////////////////////////////
- {
- //Write_Error("nentering spot light1...");
- // 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
- // test if we already computed poly normal in previous calculation
- if (n.z==FLT_MAX)
- {
- // 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
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- } // end if
- // 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_Fast2(&n);
- nl = curr_poly->nlength;
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast2(&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
- //Write_Error("nspotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
- } // end if spotlight1
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version ////////////////////
- {
- //Write_Error("nEntering spotlight2 ...");
- // 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
- // test if we already computed poly normal in previous calculation
- if (n.z==FLT_MAX)
- {
- // 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
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- } // end if
- // 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_Fast2(&n);
- nl = curr_poly->nlength;
- // 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].v, &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dists = VECTOR4D_Length_Fast2(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);
- // 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
- //Write_Error("nSpotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
- } // 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_Error("nWriting final values to polygon %d = %d,%d,%d", poly, r_sum, g_sum, b_sum);
- // write the color over current color
- curr_poly->lit_color[0] = RGB16Bit(r_sum, g_sum, b_sum);
- } // end if
- else
- if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) /////////////////////////////////
- {
- // gouraud shade, unfortunetly at this point in the pipeline, we have lost the original
- // mesh, and only have triangles, thus, many triangles will share the same vertices and
- // they will get lit 2x since we don't have any way to tell this, alas, performing lighting
- // at the object level is a better idea when gouraud shading is performed since the
- // commonality of vertices is still intact, in any case, lighting here is similar to polygon
- // flat shaded, but we do it 3 times, once for each vertex, additionally there are lots
- // of opportunities for optimization, but I am going to lay off them for now, so the code
- // is intelligible, later we will optimize
- //Write_Error("nEntering gouraud shader...");
- // step 1: extract the base color out in RGB mode
- // assume 565 format
- _RGB565FROM16BIT(curr_poly->color, &r_base, &g_base, &b_base);
- // scale to 8 bit
- r_base <<= 3;
- g_base <<= 2;
- b_base <<= 3;
- //Write_Error("nBase color=%d, %d, %d", r_base, g_base, b_base);
- // initialize color sum(s) for vertices
- r_sum0 = 0;
- g_sum0 = 0;
- b_sum0 = 0;
- r_sum1 = 0;
- g_sum1 = 0;
- b_sum1 = 0;
- r_sum2 = 0;
- g_sum2 = 0;
- b_sum2 = 0;
- //Write_Error("nColor sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- // new optimization:
- // when there are multiple lights in the system we will end up performing numerous
- // redundant calculations to minimize this my strategy is to set key variables to
- // to MAX values on each loop, then during the lighting calcs to test the vars for
- // the max value, if they are the max value then the first light that needs the math
- // will do it, and then save the information into the variable (causing it to change state
- // from an invalid number) then any other lights that need the math can use the previously
- // computed value, however, since we already have the normals, not much here to cache on
- // a large scale, but small scale stuff is there, however, we will optimize those later
- // 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;
- //Write_Error("nprocessing light %d", curr_light);
- // what kind of light are we dealing with
- if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT) ///////////////////////////////
- {
- //Write_Error("nEntering ambient light....");
- // 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
- ri = ((lights[curr_light].c_ambient.r * r_base) / 256);
- gi = ((lights[curr_light].c_ambient.g * g_base) / 256);
- bi = ((lights[curr_light].c_ambient.b * b_base) / 256);
- // ambient light has the same affect on each vertex
- r_sum0+=ri;
- g_sum0+=gi;
- b_sum0+=bi;
- r_sum1+=ri;
- g_sum1+=gi;
- b_sum1+=bi;
- r_sum2+=ri;
- g_sum2+=gi;
- b_sum2+=bi;
- // there better only be one ambient light!
- //Write_Error("nexiting ambient ,sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // end if
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) /////////////////////////////////
- {
- //Write_Error("nentering infinite light...");
- // infinite lighting, we need the surface normal, and the direction
- // of the light source
- // no longer need to compute normal or length, we already have the vertex normal
- // and it's length is 1.0
- // ....
- // 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
- // need to perform lighting for each vertex (lots of redundant math, optimize later!)
- //Write_Error("nv0=[%f, %f, %f]=%f, v1=[%f, %f, %f]=%f, v2=[%f, %f, %f]=%f",
- // curr_poly->tvlist[0].n.x, curr_poly->tvlist[0].n.y,curr_poly->tvlist[0].n.z, VECTOR4D_Length(&curr_poly->tvlist[0].n),
- // curr_poly->tvlist[1].n.x, curr_poly->tvlist[1].n.y,curr_poly->tvlist[1].n.z, VECTOR4D_Length(&curr_poly->tvlist[1].n),
- // curr_poly->tvlist[2].n.x, curr_poly->tvlist[2].n.y,curr_poly->tvlist[2].n.z, VECTOR4D_Length(&curr_poly->tvlist[2].n) );
- // vertex 0
- dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp;
- r_sum0+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum0+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum0+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 1
- dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp;
- r_sum1+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum1+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum1+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 2
- dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp;
- r_sum2+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum2+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum2+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- //Write_Error("nexiting infinite, color sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // 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
- // .. normal already in vertex
- //Write_Error("nEntering point light....");
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast2(&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
- // perform the calculation for all 3 vertices
- // vertex 0
- dp = VECTOR4D_Dot(&curr_poly->tvlist[0].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 / (dist * atten );
- r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 1
- dp = VECTOR4D_Dot(&curr_poly->tvlist[1].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 / (dist * atten );
- r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 2
- dp = VECTOR4D_Dot(&curr_poly->tvlist[2].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 / (dist * atten );
- r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- //Write_Error("nexiting point light, rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // 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
- //Write_Error("nentering spotlight1....");
- // .. normal is already computed
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast2(&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
- // vertex 0
- dp = VECTOR4D_Dot(&curr_poly->tvlist[0].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 / ( atten );
- r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 1
- dp = VECTOR4D_Dot(&curr_poly->tvlist[1].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 / ( atten );
- r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end i
- // vertex 2
- dp = VECTOR4D_Dot(&curr_poly->tvlist[2].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 / ( atten );
- r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end i
- //Write_Error("nexiting spotlight1, sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // 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
- // .. already have normals and length are 1.0
- // 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
- //Write_Error("nEntering spotlight2...");
- // tons of redundant math here! lots to optimize later!
- // vertex 0
- dp = VECTOR4D_Dot(&curr_poly->tvlist[0].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].v, &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dists = VECTOR4D_Length_Fast2(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);
- // 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 / ( atten );
- r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- // vertex 1
- dp = VECTOR4D_Dot(&curr_poly->tvlist[1].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[1].v, &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dists = VECTOR4D_Length_Fast2(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);
- // 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 / ( atten );
- r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- // vertex 2
- dp = VECTOR4D_Dot(&curr_poly->tvlist[2].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[2].v, &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dists = VECTOR4D_Length_Fast2(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);
- // 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 / ( atten );
- r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- //Write_Error("nexiting spotlight2, sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // end if spot light
- } // end for light
- // make sure colors aren't out of range
- if (r_sum0 > 255) r_sum0 = 255;
- if (g_sum0 > 255) g_sum0 = 255;
- if (b_sum0 > 255) b_sum0 = 255;
- if (r_sum1 > 255) r_sum1 = 255;
- if (g_sum1 > 255) g_sum1 = 255;
- if (b_sum1 > 255) b_sum1 = 255;
- if (r_sum2 > 255) r_sum2 = 255;
- if (g_sum2 > 255) g_sum2 = 255;
- if (b_sum2 > 255) b_sum2 = 255;
- //Write_Error("nwriting color for poly %d", poly);
- //Write_Error("n******** final sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- // write the colors
- curr_poly->lit_color[0] = RGB16Bit(r_sum0, g_sum0, b_sum0);
- curr_poly->lit_color[1] = RGB16Bit(r_sum1, g_sum1, b_sum1);
- curr_poly->lit_color[2] = RGB16Bit(r_sum2, g_sum2, b_sum2);
- } // end if
- else // assume POLY4DV2_ATTR_SHADE_MODE_CONSTANT
- {
- // emmisive shading only, do nothing
- // ...
- curr_poly->lit_color[0] = curr_poly->color;
- //Write_Error("nentering constant shader, and exiting...");
- } // end if
- } // end for poly
- // return success
- return(1);
- } // end Light_RENDERLIST4DV2_World16
- //////////////////////////////////////////////////////////////////////////////
- int Light_RENDERLIST4DV2_World(RENDERLIST4DV2_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
- {
- // {andre work in progress }
- // 8-bit version of function
- // function lights the entire 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
- r_sum0, g_sum0, b_sum0,
- r_sum1, g_sum1, b_sum1,
- r_sum2, g_sum2, b_sum2,
- ri,gi,bi,
- shaded_color; // final color
- float dp, // dot product
- dist, // distance from light to surface
- dists,
- i, // general intensities
- nl, // length of normal
- atten; // attenuation computations
- VECTOR4D u, v, n, l, d, s; // used for cross product and light vector calculations
- //Write_Error("nEntering lighting function");
- // for each valid poly, light it...
- for (int poly=0; poly < rend_list->num_polys; poly++)
- {
- // acquire polygon
- POLYF4DV2_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 & POLY4DV2_STATE_ACTIVE) ||
- (curr_poly->state & POLY4DV2_STATE_CLIPPED ) ||
- (curr_poly->state & POLY4DV2_STATE_BACKFACE) ||
- (curr_poly->state & POLY4DV2_STATE_LIT) )
- continue; // move onto next poly
- //Write_Error("npoly %d",poly);
- #ifdef DEBUG_ON
- // track rendering stats
- debug_polys_lit_per_frame++;
- #endif
- // set state of polygon to lit
- SET_BIT(curr_poly->state, POLY4DV2_STATE_LIT);
- // 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 & POLY4DV2_ATTR_SHADE_MODE_FLAT)
- {
- //Write_Error("nEntering Flat Shader");
- r_base = palette[curr_poly->color].peRed;
- g_base = palette[curr_poly->color].peGreen;
- b_base = palette[curr_poly->color].peBlue;
- //Write_Error("nBase color=%d,%d,%d", r_base, g_base, b_base);
- // initialize color sum
- r_sum = 0;
- g_sum = 0;
- b_sum = 0;
- //Write_Error("nsum color=%d,%d,%d", r_sum, g_sum, b_sum);
- // new optimization:
- // when there are multiple lights in the system we will end up performing numerous
- // redundant calculations to minimize this my strategy is to set key variables to
- // to MAX values on each loop, then during the lighting calcs to test the vars for
- // the max value, if they are the max value then the first light that needs the math
- // will do it, and then save the information into the variable (causing it to change state
- // from an invalid number) then any other lights that need the math can use the previously
- // computed value
- // set surface normal.z to FLT_MAX to flag it as non-computed
- n.z = FLT_MAX;
- // 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;
- //Write_Error("nprocessing light %d",curr_light);
- // what kind of light are we dealing with
- if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT)
- {
- //Write_Error("nEntering ambient light...");
- // 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);
- //Write_Error("nambient sum=%d,%d,%d", r_sum, g_sum, b_sum);
- // there better only be one ambient light!
- } // end if
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) ///////////////////////////////////////////
- {
- //Write_Error("nEntering infinite light...");
- // infinite lighting, we need the surface normal, and the direction
- // of the light source
- // test if we already computed poly normal in previous calculation
- if (n.z==FLT_MAX)
- {
- // 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
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- } // end if
- // 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_Fast2(&n);
- nl = curr_poly->nlength;
- // 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
- //Write_Error("ninfinite sum=%d,%d,%d", r_sum, g_sum, b_sum);
- } // end if infinite light
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_POINT) ///////////////////////////////////////
- {
- //Write_Error("nEntering point light...");
- // 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
- // test if we already computed poly normal in previous calculation
- if (n.z==FLT_MAX)
- {
- // 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
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- } // end if
- // 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_Fast2(&n);
- nl = curr_poly->nlength;
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast2(&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
- //Write_Error("npoint sum=%d,%d,%d",r_sum,g_sum,b_sum);
- } // end if point
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT1) ////////////////////////////////////
- {
- //Write_Error("nentering spot light1...");
- // 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
- // test if we already computed poly normal in previous calculation
- if (n.z==FLT_MAX)
- {
- // 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
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- } // end if
- // 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_Fast2(&n);
- nl = curr_poly->nlength;
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast2(&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
- //Write_Error("nspotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
- } // end if spotlight1
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_SPOTLIGHT2) // simple version ////////////////////
- {
- //Write_Error("nEntering spotlight2 ...");
- // 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
- // test if we already computed poly normal in previous calculation
- if (n.z==FLT_MAX)
- {
- // 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
- // build u, v
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[1].v, &u);
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &curr_poly->tvlist[2].v, &v);
- // compute cross product
- VECTOR4D_Cross(&u, &v, &n);
- } // end if
- // 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_Fast2(&n);
- nl = curr_poly->nlength;
- // 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].v, &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dists = VECTOR4D_Length_Fast2(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);
- // 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
- //Write_Error("nSpotlight sum=%d,%d,%d",r_sum, g_sum, b_sum);
- } // 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_Error("nWriting final values to polygon %d = %d,%d,%d", poly, r_sum, g_sum, b_sum);
- // write the color over current color
- curr_poly->lit_color[0] = rgblookup[RGB16Bit565(r_sum, g_sum, b_sum)];
- } // end if
- else
- if (curr_poly->attr & POLY4DV2_ATTR_SHADE_MODE_GOURAUD) /////////////////////////////////
- {
- // gouraud shade, unfortunetly at this point in the pipeline, we have lost the original
- // mesh, and only have triangles, thus, many triangles will share the same vertices and
- // they will get lit 2x since we don't have any way to tell this, alas, performing lighting
- // at the object level is a better idea when gouraud shading is performed since the
- // commonality of vertices is still intact, in any case, lighting here is similar to polygon
- // flat shaded, but we do it 3 times, once for each vertex, additionally there are lots
- // of opportunities for optimization, but I am going to lay off them for now, so the code
- // is intelligible, later we will optimize
- //Write_Error("nEntering gouraud shader...");
- // step 1: extract the base color out in RGB mode
- r_base = palette[curr_poly->color].peRed;
- g_base = palette[curr_poly->color].peGreen;
- b_base = palette[curr_poly->color].peBlue;
- //Write_Error("nBase color=%d, %d, %d", r_base, g_base, b_base);
- // initialize color sum(s) for vertices
- r_sum0 = 0;
- g_sum0 = 0;
- b_sum0 = 0;
- r_sum1 = 0;
- g_sum1 = 0;
- b_sum1 = 0;
- r_sum2 = 0;
- g_sum2 = 0;
- b_sum2 = 0;
- //Write_Error("nColor sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- // new optimization:
- // when there are multiple lights in the system we will end up performing numerous
- // redundant calculations to minimize this my strategy is to set key variables to
- // to MAX values on each loop, then during the lighting calcs to test the vars for
- // the max value, if they are the max value then the first light that needs the math
- // will do it, and then save the information into the variable (causing it to change state
- // from an invalid number) then any other lights that need the math can use the previously
- // computed value
- // 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;
- //Write_Error("nprocessing light %d", curr_light);
- // what kind of light are we dealing with
- if (lights[curr_light].attr & LIGHTV1_ATTR_AMBIENT) ///////////////////////////////
- {
- //Write_Error("nEntering ambient light....");
- // 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
- ri = ((lights[curr_light].c_ambient.r * r_base) / 256);
- gi = ((lights[curr_light].c_ambient.g * g_base) / 256);
- bi = ((lights[curr_light].c_ambient.b * b_base) / 256);
- // ambient light has the same affect on each vertex
- r_sum0+=ri;
- g_sum0+=gi;
- b_sum0+=bi;
- r_sum1+=ri;
- g_sum1+=gi;
- b_sum1+=bi;
- r_sum2+=ri;
- g_sum2+=gi;
- b_sum2+=bi;
- // there better only be one ambient light!
- //Write_Error("nexiting ambient ,sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // end if
- else
- if (lights[curr_light].attr & LIGHTV1_ATTR_INFINITE) /////////////////////////////////
- {
- //Write_Error("nentering infinite light...");
- // infinite lighting, we need the surface normal, and the direction
- // of the light source
- // no longer need to compute normal or length, we already have the vertex normal
- // and it's length is 1.0
- // ....
- // 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
- // need to perform lighting for each vertex (lots of redundant math, optimize later!)
- //Write_Error("nv0=[%f, %f, %f]=%f, v1=[%f, %f, %f]=%f, v2=[%f, %f, %f]=%f",
- // curr_poly->tvlist[0].n.x, curr_poly->tvlist[0].n.y,curr_poly->tvlist[0].n.z, VECTOR4D_Length(&curr_poly->tvlist[0].n),
- // curr_poly->tvlist[1].n.x, curr_poly->tvlist[1].n.y,curr_poly->tvlist[1].n.z, VECTOR4D_Length(&curr_poly->tvlist[1].n),
- // curr_poly->tvlist[2].n.x, curr_poly->tvlist[2].n.y,curr_poly->tvlist[2].n.z, VECTOR4D_Length(&curr_poly->tvlist[2].n) );
- // vertex 0
- dp = VECTOR4D_Dot(&curr_poly->tvlist[0].n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp;
- r_sum0+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum0+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum0+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 1
- dp = VECTOR4D_Dot(&curr_poly->tvlist[1].n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp;
- r_sum1+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum1+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum1+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 2
- dp = VECTOR4D_Dot(&curr_poly->tvlist[2].n, &lights[curr_light].dir);
- // only add light if dp > 0
- if (dp > 0)
- {
- i = 128*dp;
- r_sum2+= (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum2+= (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum2+= (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- //Write_Error("nexiting infinite, color sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // 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
- // .. normal already in vertex
- //Write_Error("nEntering point light....");
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast2(&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
- // perform the calculation for all 3 vertices
- // vertex 0
- dp = VECTOR4D_Dot(&curr_poly->tvlist[0].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 / (dist * atten );
- r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 1
- dp = VECTOR4D_Dot(&curr_poly->tvlist[1].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 / (dist * atten );
- r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 2
- dp = VECTOR4D_Dot(&curr_poly->tvlist[2].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 / (dist * atten );
- r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- //Write_Error("nexiting point light, rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // 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
- //Write_Error("nentering spotlight1....");
- // .. normal is already computed
- // compute vector from surface to light
- VECTOR4D_Build(&curr_poly->tvlist[0].v, &lights[curr_light].pos, &l);
- // compute distance and attenuation
- dist = VECTOR4D_Length_Fast2(&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
- // vertex 0
- dp = VECTOR4D_Dot(&curr_poly->tvlist[0].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 / ( atten );
- r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- // vertex 1
- dp = VECTOR4D_Dot(&curr_poly->tvlist[1].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 / ( atten );
- r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end i
- // vertex 2
- dp = VECTOR4D_Dot(&curr_poly->tvlist[2].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 / ( atten );
- r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end i
- //Write_Error("nexiting spotlight1, sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // 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
- // .. already have normals and length are 1.0
- // 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
- //Write_Error("nEntering spotlight2...");
- // tons of redundant math here! lots to optimize later!
- // vertex 0
- dp = VECTOR4D_Dot(&curr_poly->tvlist[0].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].v, &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dists = VECTOR4D_Length_Fast2(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);
- // 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 / ( atten );
- r_sum0 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum0 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum0 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- // vertex 1
- dp = VECTOR4D_Dot(&curr_poly->tvlist[1].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[1].v, &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dists = VECTOR4D_Length_Fast2(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);
- // 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 / ( atten );
- r_sum1 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum1 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum1 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- // vertex 2
- dp = VECTOR4D_Dot(&curr_poly->tvlist[2].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[2].v, &s);
- // compute length of s (distance to light source) to normalize s for lighting calc
- dists = VECTOR4D_Length_Fast2(&s);
- // compute spot light term (s . l)
- float dpsl = VECTOR4D_Dot(&s, &lights[curr_light].dir)/dists;
- // proceed only if term is positive
- if (dpsl > 0)
- {
- // compute attenuation
- atten = (lights[curr_light].kc + lights[curr_light].kl*dists + lights[curr_light].kq*dists*dists);
- // 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 / ( atten );
- r_sum2 += (lights[curr_light].c_diffuse.r * r_base * i) / (256*128);
- g_sum2 += (lights[curr_light].c_diffuse.g * g_base * i) / (256*128);
- b_sum2 += (lights[curr_light].c_diffuse.b * b_base * i) / (256*128);
- } // end if
- } // end if
- //Write_Error("nexiting spotlight2, sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- } // end if spot light
- } // end for light
- // make sure colors aren't out of range
- if (r_sum0 > 255) r_sum0 = 255;
- if (g_sum0 > 255) g_sum0 = 255;
- if (b_sum0 > 255) b_sum0 = 255;
- if (r_sum1 > 255) r_sum1 = 255;
- if (g_sum1 > 255) g_sum1 = 255;
- if (b_sum1 > 255) b_sum1 = 255;
- if (r_sum2 > 255) r_sum2 = 255;
- if (g_sum2 > 255) g_sum2 = 255;
- if (b_sum2 > 255) b_sum2 = 255;
- //Write_Error("nwriting color for poly %d", poly);
- //Write_Error("n******** final sums rgb0[%d, %d, %d], rgb1[%d,%d,%d], rgb2[%d,%d,%d]", r_sum0, g_sum0, b_sum0, r_sum1, g_sum1, b_sum1, r_sum2, g_sum2, b_sum2);
- // write all colors in RGB form since we need to interpolate at the rasterizer
- curr_poly->lit_color[0] = RGB16Bit(r_sum0, g_sum0, b_sum0);
- curr_poly->lit_color[1] = RGB16Bit(r_sum1, g_sum1, b_sum1);
- curr_poly->lit_color[2] = RGB16Bit(r_sum2, g_sum2, b_sum2);
- } // end if
- else // assume POLY4DV2_ATTR_SHADE_MODE_CONSTANT
- {
- // emmisive shading only, do nothing
- // ...
- curr_poly->lit_color[0] = curr_poly->color;
- //Write_Error("nentering constant shader, and exiting...");
- } // end if
- } // end for poly
- // return success
- return(1);
- } // end Light_RENDERLIST4DV2_World
- /////////////////////////////////////////////////////////////////////////
- float Compute_OBJECT4DV2_Radius(OBJECT4DV2_PTR obj)
- {
- // this function computes the average and maximum radius for
- // sent object and opdates the object data for the "current frame"
- // it's up to the caller to make sure Set_Frame() for this object
- // has been called to set the object up properly
- // reset incase there's any residue
- obj->avg_radius[obj->curr_frame] = 0;
- obj->max_radius[obj->curr_frame] = 0;
- // loop thru and compute radius
- for (int vertex = 0; vertex < obj->num_vertices; vertex++)
- {
- // update the average and maximum radius
- float dist_to_vertex =
- sqrt(obj->vlist_local[vertex].x*obj->vlist_local[vertex].x +
- obj->vlist_local[vertex].y*obj->vlist_local[vertex].y +
- obj->vlist_local[vertex].z*obj->vlist_local[vertex].z);
- // accumulate total radius
- obj->avg_radius[obj->curr_frame]+=dist_to_vertex;
- // update maximum radius
- if (dist_to_vertex > obj->max_radius[obj->curr_frame])
- obj->max_radius[obj->curr_frame] = dist_to_vertex;
- } // end for vertex
- // finallize average radius computation
- obj->avg_radius[obj->curr_frame]/=obj->num_vertices;
- // return max radius of frame 0
- return(obj->max_radius[0]);
- } // end Compute_OBJECT4DV2_Radius
- //////////////////////////////////////////////////////////////////////////////
- char *Extract_Filename_From_Path(char *filepath, char *filename)
- {
- // this function extracts the filename from a complete path and file
- // "../folder/.../filname.ext"
- // the function operates by scanning backward and looking for the first
- // occurance of "" or "/" then copies the filename from there to the end
- // test of filepath is valid
- if (!filepath || strlen(filepath)==0)
- return(NULL);
- int index_end = strlen(filepath)-1;
- // find filename
- while( (filepath[index_end]!='\') &&
- (filepath[index_end]!='/') &&
- (filepath[index_end] > 0) )
- index_end--;
- // copy file name out into filename var
- memcpy(filename, &filepath[index_end+1], strlen(filepath) - index_end);
- // return result
- return(filename);
- } // end Extract_Filename_From_Path
- /////////////////////////////////////////////////////////////////////////////
- int Load_OBJECT4DV2_COB(OBJECT4DV2_PTR obj, // pointer to object
- char *filename, // filename of Caligari COB file
- VECTOR4D_PTR scale, // initial scaling factors
- VECTOR4D_PTR pos, // initial position
- VECTOR4D_PTR rot, // initial rotations
- int vertex_flags) // flags to re-order vertices
- // and perform transforms
- {
- // this function loads a Caligari TrueSpace .COB file object in off disk, additionally
- // it allows the caller to scale, position, and rotate the object
- // to save extra calls later for non-dynamic objects, note that this function
- // works with a OBJECT4DV2 which has support for textures, but not materials, etc,
- // however we will still parse out the material stuff and get them ready for the
- // next incarnation objects, so we can re-use this code to support those features
- // also, since this version IS going to read in the texture map and texture coordinates
- // we have a couple issues to think about, first COB format like absolute texture paths
- // we can't have that, so we will simple extract out ONLY the texture map bitmap name
- // and use the global texture path variable to build a real file path, also texture
- // coordinates are in 0..1 0..1 form, I still haven't decided if I want to use absolute
- // coordinates or 0..1 0..1, but right now the affine texture mapper uses
- // create a parser object
- CPARSERV1 parser;
- char seps[16]; // seperators for token scanning
- char token_buffer[256]; // used as working buffer for token
- char *token; // pointer to next token
- int r,g,b; // working colors
- // cache for texture vertices
- VERTEX2DF texture_vertices[OBJECT4DV2_MAX_VERTICES];
- int num_texture_vertices = 0;
- MATRIX4X4 mat_local, // storage for local transform if user requests it in cob format
- mat_world; // " " for local to world " "
- // initialize matrices
- MAT_IDENTITY_4X4(&mat_local);
- MAT_IDENTITY_4X4(&mat_world);
- // Step 1: clear out the object and initialize it a bit
- memset(obj, 0, sizeof(OBJECT4DV2));
- // set state of object to active and visible
- obj->state = OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_STATE_VISIBLE;
- // set number of frames
- obj->num_frames = 1;
- obj->curr_frame = 0;
- obj->attr = OBJECT4DV2_ATTR_SINGLE_FRAME;
- // set position of object is caller requested position
- if (pos)
- {
- // set position of object
- obj->world_pos.x = pos->x;
- obj->world_pos.y = pos->y;
- obj->world_pos.z = pos->z;
- obj->world_pos.w = pos->w;
- } // end
- else
- {
- // set it to (0,0,0,1)
- obj->world_pos.x = 0;
- obj->world_pos.y = 0;
- obj->world_pos.z = 0;
- obj->world_pos.w = 1;
- } // end else
- // Step 2: open the file for reading using the parser
- if (!parser.Open(filename))
- {
- Write_Error("Couldn't open .COB file %s.", filename);
- return(0);
- } // end if
- // Step 3:
- // lets find the name of the object first
- while(1)
- {
- // get the next line, we are looking for "Name"
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("Image 'name' not found in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if ( parser.Pattern_Match(parser.buffer, "['Name'] [s>0]") )
- {
- // name should be in second string variable, index 1
- strcpy(obj->name, parser.pstrings[1]);
- Write_Error("nCOB Reader Object Name: %s", obj->name);
- break;
- } // end if
- } // end while
- // step 4: get local and world transforms and store them
- // center 0 0 0
- // x axis 1 0 0
- // y axis 0 1 0
- // z axis 0 0 1
- while(1)
- {
- // get the next line, we are looking for "center"
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("Center not found in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if ( parser.Pattern_Match(parser.buffer, "['center'] [f] [f] [f]") )
- {
- // the "center" holds the translation factors, so place in
- // last row of homogeneous matrix, note that these are row vectors
- // that we need to drop in each column of matrix
- mat_local.M[3][0] = -parser.pfloats[0]; // center x
- mat_local.M[3][1] = -parser.pfloats[1]; // center y
- mat_local.M[3][2] = -parser.pfloats[2]; // center z
- // ok now, the next 3 lines should be the x,y,z transform vectors
- // so build up
- // "x axis"
- parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
- parser.Pattern_Match(parser.buffer, "['x'] ['axis'] [f] [f] [f]");
- // place row in x column of transform matrix
- mat_local.M[0][0] = parser.pfloats[0]; // rxx
- mat_local.M[1][0] = parser.pfloats[1]; // rxy
- mat_local.M[2][0] = parser.pfloats[2]; // rxz
- // "y axis"
- parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
- parser.Pattern_Match(parser.buffer, "['y'] ['axis'] [f] [f] [f]");
- // place row in y column of transform matrix
- mat_local.M[0][1] = parser.pfloats[0]; // ryx
- mat_local.M[1][1] = parser.pfloats[1]; // ryy
- mat_local.M[2][1] = parser.pfloats[2]; // ryz
- // "z axis"
- parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
- parser.Pattern_Match(parser.buffer, "['z'] ['axis'] [f] [f] [f]");
- // place row in z column of transform matrix
- mat_local.M[0][2] = parser.pfloats[0]; // rzx
- mat_local.M[1][2] = parser.pfloats[1]; // rzy
- mat_local.M[2][2] = parser.pfloats[2]; // rzz
- Print_Mat_4X4(&mat_local, "Local COB Matrix:");
- break;
- } // end if
- } // end while
- // now "Transform"
- while(1)
- {
- // get the next line, we are looking for "Transform"
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("Transform not found in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if ( parser.Pattern_Match(parser.buffer, "['Transform']") )
- {
- // "x axis"
- parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
- parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
- // place row in x column of transform matrix
- mat_world.M[0][0] = parser.pfloats[0]; // rxx
- mat_world.M[1][0] = parser.pfloats[1]; // rxy
- mat_world.M[2][0] = parser.pfloats[2]; // rxz
- // "y axis"
- parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
- parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
- // place row in y column of transform matrix
- mat_world.M[0][1] = parser.pfloats[0]; // ryx
- mat_world.M[1][1] = parser.pfloats[1]; // ryy
- mat_world.M[2][1] = parser.pfloats[2]; // ryz
- // "z axis"
- parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS);
- parser.Pattern_Match(parser.buffer, "[f] [f] [f]");
- // place row in z column of transform matrix
- mat_world.M[0][2] = parser.pfloats[0]; // rzx
- mat_world.M[1][2] = parser.pfloats[1]; // rzy
- mat_world.M[2][2] = parser.pfloats[2]; // rzz
- Print_Mat_4X4(&mat_world, "World COB Matrix:");
- // no need to read in last row, since it's always 0,0,0,1 and we don't use it anyway
- break;
- } // end if
- } // end while
- // step 6: get number of vertices and polys in object
- while(1)
- {
- // get the next line, we are looking for "World Vertices"
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("'World Vertices' line not found in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['World'] ['Vertices'] [i]") )
- {
- // simply extract the number of vertices from the pattern matching
- // output arrays
- obj->num_vertices = parser.pints[0];
- Write_Error("nCOB Reader Num Vertices: %d", obj->num_vertices);
- break;
- } // end if
- } // end while
- // allocate the memory for the vertices and number of polys (unknown, so use 3*num_vertices)
- // the call parameters are redundant in this case, but who cares
- if (!Init_OBJECT4DV2(obj, // object to allocate
- obj->num_vertices,
- obj->num_vertices*3,
- obj->num_frames))
- {
- Write_Error("nASC file error with file %s (can't allocate memory).",filename);
- } // end if
- // Step 7: load the vertex list
- // now read in vertex list, format:
- // "d.d d.d d.d"
- for (int vertex = 0; vertex < obj->num_vertices; vertex++)
- {
- // hunt for vertex
- while(1)
- {
- // get the next vertex
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nVertex list ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "[f] [f] [f]"))
- {
- // at this point we have the x,y,z in the the pfloats array locations 0,1,2
- obj->vlist_local[vertex].x = parser.pfloats[0];
- obj->vlist_local[vertex].y = parser.pfloats[1];
- obj->vlist_local[vertex].z = parser.pfloats[2];
- obj->vlist_local[vertex].w = 1;
- // do vertex swapping right here, allow muliple swaps, why not!
- // defines for vertex re-ordering flags
- //#define VERTEX_FLAGS_INVERT_X 1 // inverts the Z-coordinates
- //#define VERTEX_FLAGS_INVERT_Y 2 // inverts the Z-coordinates
- //#define VERTEX_FLAGS_INVERT_Z 4 // inverts the Z-coordinates
- //#define VERTEX_FLAGS_SWAP_YZ 8 // transforms a RHS model to a LHS model
- //#define VERTEX_FLAGS_SWAP_XZ 16 // ???
- //#define VERTEX_FLAGS_SWAP_XY 32
- //#define VERTEX_FLAGS_INVERT_WINDING_ORDER 64 // invert winding order from cw to ccw or ccw to cc
- //#define VERTEX_FLAGS_TRANSFORM_LOCAL 512 // if file format has local transform then do it!
- //#define VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD 1024 // if file format has local to world then do it!
- VECTOR4D temp_vector; // temp for calculations
- // now apply local and world transformations encoded in COB format
- if (vertex_flags & VERTEX_FLAGS_TRANSFORM_LOCAL )
- {
- Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].v, &mat_local, &temp_vector);
- VECTOR4D_COPY(&obj->vlist_local[vertex].v, &temp_vector);
- } // end if
- if (vertex_flags & VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD )
- {
- Mat_Mul_VECTOR4D_4X4(&obj->vlist_local[vertex].v, &mat_world, &temp_vector);
- VECTOR4D_COPY(&obj->vlist_local[vertex].v, &temp_vector);
- } // end if
- float temp_f; // used for swapping
- // invert signs?
- if (vertex_flags & VERTEX_FLAGS_INVERT_X)
- obj->vlist_local[vertex].x=-obj->vlist_local[vertex].x;
- if (vertex_flags & VERTEX_FLAGS_INVERT_Y)
- obj->vlist_local[vertex].y=-obj->vlist_local[vertex].y;
- if (vertex_flags & VERTEX_FLAGS_INVERT_Z)
- obj->vlist_local[vertex].z=-obj->vlist_local[vertex].z;
- // swap any axes?
- if (vertex_flags & VERTEX_FLAGS_SWAP_YZ)
- SWAP(obj->vlist_local[vertex].y, obj->vlist_local[vertex].z, temp_f);
- if (vertex_flags & VERTEX_FLAGS_SWAP_XZ)
- SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].z, temp_f);
- if (vertex_flags & VERTEX_FLAGS_SWAP_XY)
- SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].y, temp_f);
- // scale vertices
- if (scale)
- {
- obj->vlist_local[vertex].x*=scale->x;
- obj->vlist_local[vertex].y*=scale->y;
- obj->vlist_local[vertex].z*=scale->z;
- } // end if
- Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
- obj->vlist_local[vertex].x,
- obj->vlist_local[vertex].y,
- obj->vlist_local[vertex].z,
- obj->vlist_local[vertex].w);
- // set point field in this vertex, we need that at least
- SET_BIT(obj->vlist_local[vertex].attr, VERTEX4DTV1_ATTR_POINT);
- // found vertex, break out of while for next pass
- break;
- } // end if
- } // end while
- } // end for vertex
- // compute average and max radius
- Compute_OBJECT4DV2_Radius(obj);
- Write_Error("nObject average radius = %f, max radius = %f",
- obj->avg_radius, obj->max_radius);
- // step 8: get number of texture vertices
- while(1)
- {
- // get the next line, we are looking for "Texture Vertices ddd"
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("'Texture Vertices' line not found in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Texture'] ['Vertices'] [i]") )
- {
- // simply extract the number of texture vertices from the pattern matching
- // output arrays
- num_texture_vertices = parser.pints[0];
- Write_Error("nCOB Reader Texture Vertices: %d", num_texture_vertices);
- break;
- } // end if
- } // end while
- // Step 9: load the texture vertex list in format "U V"
- // "d.d d.d"
- for (int tvertex = 0; tvertex < num_texture_vertices; tvertex++)
- {
- // hunt for texture
- while(1)
- {
- // get the next vertex
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nTexture Vertex list ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "[f] [f]"))
- {
- // at this point we have the U V in the the pfloats array locations 0,1 for this
- // texture vertex, store in texture coordinate list
- // note texture coords are in 0..1 format, and must be scaled to texture size
- // after we load the texture
- obj->tlist[tvertex].x = parser.pfloats[0];
- obj->tlist[tvertex].y = parser.pfloats[1];
- Write_Error("nTexture Vertex %d: U=%f, V=%f", tvertex,
- obj->tlist[tvertex].x,
- obj->tlist[tvertex].y );
- // found vertex, break out of while for next pass
- break;
- } // end if
- } // end while
- } // end for
- // when we load in the polygons then we will copy the texture vertices into the polygon
- // vertices assuming that each vertex has a SINGLE texture coordinate, this means that
- // you must NOT use multiple textures on an object! in other words think "skin" this is
- // inline with Quake II md2 format, in 99% of the cases a single object can be textured
- // with a single skin and the texture coordinates can be unique for each vertex and 1:1
- int poly_material[OBJECT4DV2_MAX_POLYS]; // this holds the material index for each polygon
- // we need these indices since when reading the file
- // we read the polygons BEFORE the materials, so we need
- // this data, so we can go back later and extract the material
- // that each poly WAS assigned and get the colors out, since
- // objects and polygons do not currently support materials
- int material_index_referenced[MAX_MATERIALS]; // used to track if an index has been used yet as a material
- // reference. since we don't know how many materials, we need
- // a way to count them up, but if we have seen a material reference
- // more than once then we don't increment the total number of materials
- // this array is for this
- // clear out reference array
- memset(material_index_referenced,0, sizeof(material_index_referenced));
- // step 10: load in the polygons
- // poly list starts off with:
- // "Faces ddd:"
- while(1)
- {
- // get next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("n'Faces' line not found in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Faces'] [i]"))
- {
- Write_Error("nCOB Reader found face list in .COB file %s.", filename);
- // finally set number of polys
- obj->num_polys = parser.pints[0];
- break;
- } // end if
- } // end while
- // now read each face in format:
- // Face verts nn flags ff mat mm
- // the nn is the number of vertices, always 3
- // the ff is the flags, unused for now, has to do with holes
- // the mm is the material index number
- int poly_surface_desc = 0; // ASC surface descriptor/material in this case
- int poly_num_verts = 0; // number of vertices for current poly (always 3)
- int num_materials_object = 0; // number of materials for this object
- for (int poly=0; poly < obj->num_polys; poly++)
- {
- Write_Error("nPolygon %d:", poly);
- // hunt until next face is found
- while(1)
- {
- // get the next polygon face
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nface list ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Face'] ['verts'] [i] ['flags'] [i] ['mat'] [i]"))
- {
- // at this point we have the number of vertices for the polygon, the flags, and it's material index
- // in the integer output array locations 0,1,2
- // store the material index for this polygon for retrieval later, but make sure adjust the
- // the index to take into consideration that the data in parser.pints[2] is 0 based, and we need
- // an index relative to the entire library, so we simply need to add num_materials to offset the
- // index properly, but we will leave this reference zero based for now... and fix up later
- poly_material[poly] = parser.pints[2];
- // update the reference array
- if (material_index_referenced[ poly_material[poly] ] == 0)
- {
- // mark as referenced
- material_index_referenced[ poly_material[poly] ] = 1;
- // increment total number of materials for this object
- num_materials_object++;
- } // end if
- // test if number of vertices is 3
- if (parser.pints[0]!=3)
- {
- Write_Error("nface not a triangle! in .COB file %s.", filename);
- return(0);
- } // end if
- // now read out the vertex indices and texture indices format:
- // <vindex0, tindex0> <vindex1, tindex1> <vindex1, tindex1>
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nface list ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- // lets replace ",<>" with ' ' to make extraction easy
- ReplaceChars(parser.buffer, parser.buffer, ",<>",' ');
- parser.Pattern_Match(parser.buffer, "[i] [i] [i] [i] [i] [i]");
- // 0,2,4 holds vertex indices
- // 1,3,5 holds texture indices
- // insert polygon, check for winding order invert
- if (vertex_flags & VERTEX_FLAGS_INVERT_WINDING_ORDER)
- {
- poly_num_verts = 3;
- obj->plist[poly].vert[0] = parser.pints[4];
- obj->plist[poly].vert[1] = parser.pints[2];
- obj->plist[poly].vert[2] = parser.pints[0];
- // now copy the texture coordinates into the vertices, this
- // may not be needed if the polygon doesn't have texture mapping
- // enabled, etc.,
- // so here's the deal the texture coordinates that
- // map to vertex 0,1,2 have indices stored in the odd
- // numbered pints[] locations, so we simply need to copy
- // the right texture coordinate into the right vertex
- obj->plist[poly].text[0] = parser.pints[5];
- obj->plist[poly].text[1] = parser.pints[3];
- obj->plist[poly].text[2] = parser.pints[1];
- Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
- parser.pints[5],
- obj->tlist[ parser.pints[5] ].x,
- obj->tlist[ parser.pints[5] ].y,
- obj->plist[poly].vert[0] );
- Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
- parser.pints[3],
- obj->tlist[ parser.pints[3] ].x,
- obj->tlist[ parser.pints[3] ].y,
- obj->plist[poly].vert[1] );
- Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
- parser.pints[1],
- obj->tlist[ parser.pints[1] ].x,
- obj->tlist[ parser.pints[1] ].y,
- obj->plist[poly].vert[2] );
- } // end if
- else
- { // leave winding order alone
- poly_num_verts = 3;
- obj->plist[poly].vert[0] = parser.pints[0];
- obj->plist[poly].vert[1] = parser.pints[2];
- obj->plist[poly].vert[2] = parser.pints[4];
- // now copy the texture coordinates into the vertices, this
- // may not be needed if the polygon doesn't have texture mapping
- // enabled, etc.,
- // so here's the deal the texture coordinates that
- // map to vertex 0,1,2 have indices stored in the odd
- // numbered pints[] locations, so we simply need to copy
- // the right texture coordinate into the right vertex
- obj->plist[poly].text[0] = parser.pints[1];
- obj->plist[poly].text[1] = parser.pints[3];
- obj->plist[poly].text[2] = parser.pints[5];
- Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
- parser.pints[1],
- obj->tlist[ parser.pints[1] ].x,
- obj->tlist[ parser.pints[1] ].y,
- obj->plist[poly].vert[0] );
- Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
- parser.pints[3],
- obj->tlist[ parser.pints[3] ].x,
- obj->tlist[ parser.pints[3] ].y,
- obj->plist[poly].vert[1] );
- Write_Error("nAssigning texture vertex index %d [%f, %f] to mesh vertex %d",
- parser.pints[5],
- obj->tlist[ parser.pints[5] ].x,
- obj->tlist[ parser.pints[5] ].y,
- obj->plist[poly].vert[2] );
- } // end else
- // point polygon vertex list to object's vertex list
- // note that this is redundant since the polylist is contained
- // within the object in this case and its up to the user to select
- // whether the local or transformed vertex list is used when building up
- // polygon geometry, might be a better idea to set to NULL in the context
- // of polygons that are part of an object
- obj->plist[poly].vlist = obj->vlist_local;
- // set texture coordinate list, this is needed
- obj->plist[poly].tlist = obj->tlist;
- // set polygon to active
- obj->plist[poly].state = POLY4DV2_STATE_ACTIVE;
- // found the face, break out of while for another pass
- break;
- } // end if
- } // end while
- Write_Error("nLocal material Index=%d, total materials for object = %d, vert_indices [%d, %d, %d]",
- poly_material[poly],
- num_materials_object,
- obj->plist[poly].vert[0],
- obj->plist[poly].vert[1],
- obj->plist[poly].vert[2]);
- } // end for poly
- // now find materials!!! and we are out of here!
- for (int curr_material = 0; curr_material < num_materials_object; curr_material++)
- {
- // hunt for the material header "mat# ddd"
- while(1)
- {
- // get the next polygon material
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nmaterial list ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['mat#'] [i]") )
- {
- // extract the material that is being defined
- int material_index = parser.pints[0];
- // get color of polygon, although it might be irrelevant for a textured surface
- while(1)
- {
- // get the next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nRGB color ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- // replace the , comma's if there are any with spaces
- ReplaceChars(parser.buffer, parser.buffer, ",", ' ', 1);
- // look for "rgb float,float,float"
- if (parser.Pattern_Match(parser.buffer, "['rgb'] [f] [f] [f]") )
- {
- // extract data and store color in material libary
- // pfloats[] 0,1,2,3, has data
- materials[material_index + num_materials].color.r = (int)(parser.pfloats[0]*255 + 0.5);
- materials[material_index + num_materials].color.g = (int)(parser.pfloats[1]*255 + 0.5);
- materials[material_index + num_materials].color.b = (int)(parser.pfloats[2]*255 + 0.5);
- break; // while looking for rgb
- } // end if
- } // end while
- // extract out lighting constants for the heck of it, they are on a line like this:
- // "alpha float ka float ks float exp float ior float"
- // alpha is transparency 0 - 1
- // ka is ambient coefficient 0 - 1
- // ks is specular coefficient 0 - 1
- // exp is highlight power exponent 0 - 1
- // ior is index of refraction (unused)
- // although our engine will have minimal support for these, we might as well get them
- while(1)
- {
- // get the next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nmaterial properties ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- // look for "alpha float ka float ks float exp float ior float"
- if (parser.Pattern_Match(parser.buffer, "['alpha'] [f] ['ka'] [f] ['ks'] [f] ['exp'] [f]") )
- {
- // extract data and store in material libary
- // pfloats[] 0,1,2,3, has data
- materials[material_index + num_materials].color.a = (UCHAR)(parser.pfloats[0]*255 + 0.5);
- materials[material_index + num_materials].ka = parser.pfloats[1];
- materials[material_index + num_materials].kd = 1; // hard code for now
- materials[material_index + num_materials].ks = parser.pfloats[2];
- materials[material_index + num_materials].power = parser.pfloats[3];
- // compute material reflectivities in pre-multiplied format to help engine
- for (int rgb_index=0; rgb_index < 3; rgb_index++)
- {
- // ambient reflectivity
- materials[material_index + num_materials].ra.rgba_M[rgb_index] =
- ( (UCHAR)(materials[material_index + num_materials].ka *
- (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
- // diffuse reflectivity
- materials[material_index + num_materials].rd.rgba_M[rgb_index] =
- ( (UCHAR)(materials[material_index + num_materials].kd *
- (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
- // specular reflectivity
- materials[material_index + num_materials].rs.rgba_M[rgb_index] =
- ( (UCHAR)(materials[material_index + num_materials].ks *
- (float)materials[material_index + num_materials].color.rgba_M[rgb_index] + 0.5) );
- } // end for rgb_index
- break;
- } // end if
- } // end while
- // now we need to know the shading model, it's a bit tricky, we need to look for the lines
- // "Shader class: color" first, then after this line is:
- // "Shader name: "xxxxxx" (xxxxxx) "
- // where the xxxxx part will be "plain color" and "plain" for colored polys
- // or "texture map" and "caligari texture" for textures
- // THEN based on that we hunt for "Shader class: reflectance" which is where the type
- // of shading is encoded, we look for the "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
- // look for the "shader class: color"
- while(1)
- {
- // get the next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nshader class ended abruptly! in .COB file %s.", filename);
- return(0);
- } // end if
- if (parser.Pattern_Match(parser.buffer, "['Shader'] ['class:'] ['color']") )
- {
- break;
- } // end if
- } // end while
- // now look for the shader name for this class
- // Shader name: "plain color" or Shader name: "texture map"
- 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
- // replace the " with spaces
- ReplaceChars(parser.buffer, parser.buffer, """, ' ', 1);
- // is this a "plain color" poly?
- if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] ['plain'] ['color']") )
- {
- // not much to do this is default, we need to wait for the reflectance type
- // to tell us the shading mode
- break;
- } // end if
- // is this a "texture map" poly?
- if (parser.Pattern_Match(parser.buffer, "['Shader'] ['name:'] ['texture'] ['map']") )
- {
- // set the texture mapping flag in material
- SET_BIT(materials[material_index + num_materials].attr, MATV1_ATTR_SHADE_MODE_TEXTURE);
- // almost done, we need the file name of the darn texture map, its in this format:
- // file name: string "D:Source..modelstextureswall01.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']") )
- {
- // and save the FULL filename (useless though since its the path from the
- // machine that created it, but later we might want some of the info).
- // filename and path starts at char position 19, 0 indexed
- memcpy(materials[material_index + num_materials].texture_file, &parser.buffer[18], strlen(parser.buffer) - 18 + 2 );
- // the OBJECT4DV2 is only allowed a single texture, although we are loading in all
- // the materials, if this is the first texture map, load it, and set a flag disallowing
- // any more texture loads for the object
- if (!obj->texture)
- {
- // step 1: allocate memory for bitmap
- obj->texture = (BITMAP_IMAGE_PTR)malloc(sizeof(BITMAP_IMAGE));
- // load the texture, just use the final file name and the absolute global
- // texture path
- char filename[80];
- char path_filename[80];
- // get the filename
- Extract_Filename_From_Path(materials[material_index + num_materials].texture_file, filename);
- // build the filename with root path
- strcpy(path_filename, texture_path);
- strcat(path_filename, filename);
- // buffer now holds final texture path and file name
- // load the bitmap(8/16 bit)
- Load_Bitmap_File(&bitmap16bit, path_filename);
- // create a proper size and bitdepth bitmap
- Create_Bitmap(obj->texture,0,0,
- bitmap16bit.bitmapinfoheader.biWidth,
- bitmap16bit.bitmapinfoheader.biHeight,
- bitmap16bit.bitmapinfoheader.biBitCount);
- // load the bitmap image (later make this 8/16 bit)
- if (obj->texture->bpp == 16)
- Load_Image_Bitmap16(obj->texture, &bitmap16bit,0,0,BITMAP_EXTRACT_MODE_ABS);
- else
- {
- Load_Image_Bitmap(obj->texture, &bitmap16bit,0,0,BITMAP_EXTRACT_MODE_ABS);
- } // end else 8 bit
- // done, so unload the bitmap
- Unload_Bitmap_File(&bitmap16bit);
- // flag object as having textures
- SET_BIT(obj->attr, OBJECT4DV2_ATTR_TEXTURES);
- } // end if
- 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);
- // test if this is a textured poly, if so override the color to WHITE,
- // so we get maximum reflection in lighting stage
- if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_TEXTURE)
- obj->plist[curr_poly].color = RGB16Bit(255,255,255);
- else
- 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);
- // test if this is a textured poly, if so override the color to WHITE,
- // so we get maximum reflection in lighting stage
- if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_TEXTURE)
- obj->plist[curr_poly].color = RGBto8BitIndex(255, 255, 255, palette, 0);
- else
- 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, POLY4DV2_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, POLY4DV2_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, POLY4DV2_ATTR_SHADE_MODE_GOURAUD);
- // going to need vertex normals!
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[0] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[1] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[2] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- } // 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, POLY4DV2_ATTR_SHADE_MODE_FASTPHONG);
- // going to need vertex normals!
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[0] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[1] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[2] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- } // end if
- else
- {
- // set shading mode to default flat
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_SHADE_MODE_FLAT);
- } // end if
- if (materials[ poly_material[curr_poly] ].attr & MATV1_ATTR_SHADE_MODE_TEXTURE)
- {
- // set shading mode
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_SHADE_MODE_TEXTURE);
- // apply texture to this polygon
- obj->plist[curr_poly].texture = obj->texture;
- // set texture coordinate attributes
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[0] ].attr, VERTEX4DTV1_ATTR_TEXTURE);
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[1] ].attr, VERTEX4DTV1_ATTR_TEXTURE);
- SET_BIT(obj->vlist_local[ obj->plist[curr_poly].vert[2] ].attr, VERTEX4DTV1_ATTR_TEXTURE);
- } // end if
- // set the material mode to ver. 1.0 emulation (for now only!!!)
- SET_BIT(obj->plist[curr_poly].attr, POLY4DV2_ATTR_DISABLE_MATERIAL);
- } // end for curr_poly
- // local object materials have been added to database, update total materials in system
- num_materials+=num_materials_object;
- // now fix up all texture coordinates
- if (obj->texture)
- {
- for (tvertex = 0; tvertex < num_texture_vertices; tvertex++)
- {
- // step 1: scale the texture coordinates by the texture size
- int texture_size = obj->texture->width;
- // scale 0..1 to 0..texture_size-1
- obj->tlist[tvertex].x *= (texture_size-1);
- obj->tlist[tvertex].y *= (texture_size-1);
- // now test for vertex transformation flags
- if (vertex_flags & VERTEX_FLAGS_INVERT_TEXTURE_U)
- {
- obj->tlist[tvertex].x = (texture_size-1) - obj->tlist[tvertex].x;
- } // end if
- if (vertex_flags & VERTEX_FLAGS_INVERT_TEXTURE_V)
- {
- obj->tlist[tvertex].y = (texture_size-1) - obj->tlist[tvertex].y;
- } // end if
- if (vertex_flags & VERTEX_FLAGS_INVERT_SWAP_UV)
- {
- float temp;
- SWAP(obj->tlist[tvertex].x, obj->tlist[tvertex].y, temp);
- } // end if
- } // end for
- } // end if there was a texture loaded for this 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
- // now that we know the correct number of polygons, we must allocate memory for them
- // and fix up the object, this is a hack, but the file formats are so stupid by not
- // all starting with NUM_VERTICES, NUM_POLYGONS -- that would make everyone's life
- // easier!
- #if 0
- // step 1: allocate memory for the polygons
- POLY4DV2_PTR plist_temp = NULL;
- // allocate memory for polygon list, the correct number of polys was overwritten
- // into the object during parsing, so we can use the num_polys field
- if (!(plist_temp = (POLY4DV2_PTR)malloc(sizeof(POLY4DV2)*obj->num_polys)))
- return(0);
- // step 2: now copy the polygons into the correct list
- memcpy((void *)plist_temp, (void *)obj->plist, sizeof(POLY4DV2));
- // step 3: now free the old memory and fix the pointer
- free(obj->plist);
- // now fix the pointer
- obj->plist = plist_temp;
- #endif
- // compute the polygon normal lengths
- Compute_OBJECT4DV2_Poly_Normals(obj);
- // compute vertex normals for any gouraud shaded polys
- Compute_OBJECT4DV2_Vertex_Normals(obj);
- // return success
- return(1);
- } // end Load_OBJECT4DV2_COB
- ////////////////////////////////////////////////////////////////////////////////
- int Load_OBJECT4DV2_3DSASC(OBJECT4DV2_PTR obj, // pointer to object
- char *filename, // filename of ASC file
- VECTOR4D_PTR scale, // initial scaling factors
- VECTOR4D_PTR pos, // initial position
- VECTOR4D_PTR rot, // initial rotations
- int vertex_flags) // flags to re-order vertices a
- // and shading overrides
- {
- // this function loads a 3D Studio .ASC file object in off disk, additionally
- // it allows the caller to scale, position, and rotate the object
- // to save extra calls later for non-dynamic objects, also new functionality
- // is supported in the vertex_flags, they allow the specification of shading
- // overrides since .ASC doesn't support anything other than polygon colors,
- // so with these overrides you can force the entire object to be emmisive, flat,
- // gouraud, etc., and the function will set up the vertices, and normals, for
- // you based on these overrides.
- // create a parser object
- CPARSERV1 parser;
- char seps[16]; // seperators for token scanning
- char token_buffer[256]; // used as working buffer for token
- char *token; // pointer to next token
- int r,g,b; // working colors
- // Step 1: clear out the object and initialize it a bit
- memset(obj, 0, sizeof(OBJECT4DV2));
- // set state of object to active and visible
- obj->state = OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_STATE_VISIBLE;
- // set number of frames
- obj->num_frames = 1;
- obj->curr_frame = 0;
- obj->attr = OBJECT4DV2_ATTR_SINGLE_FRAME;
- // set position of object is caller requested position
- if (pos)
- {
- // set position of object
- obj->world_pos.x = pos->x;
- obj->world_pos.y = pos->y;
- obj->world_pos.z = pos->z;
- obj->world_pos.w = pos->w;
- } // end
- else
- {
- // set it to (0,0,0,1)
- obj->world_pos.x = 0;
- obj->world_pos.y = 0;
- obj->world_pos.z = 0;
- obj->world_pos.w = 1;
- } // end else
- // Step 2: open the file for reading using the parser
- if (!parser.Open(filename))
- {
- Write_Error("Couldn't open .ASC file %s.", filename);
- return(0);
- } // end if
- // Step 3:
- // lets find the name of the object first
- while(1)
- {
- // get the next line, we are looking for "Named object:"
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("Image 'name' not found in .ASC file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Named'] ['object:']"))
- {
- // at this point we have the string with the name in it, parse it out by finding
- // name between quotes "name...."
- strcpy(token_buffer, parser.buffer);
- strcpy(seps, """);
- strtok( token_buffer, seps );
- // this will be the token between the quotes
- token = strtok(NULL, seps);
- // copy name into structure
- strcpy(obj->name, token);
- Write_Error("nASC Reader Object Name: %s", obj->name);
- break;
- } // end if
- } // end while
- // step 4: get number of vertices and polys in object
- while(1)
- {
- // get the next line, we are looking for "Tri-mesh, Vertices:"
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("'Tri-mesh' line not found in .ASC file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Tri-mesh,'] ['Vertices:'] [i] ['Faces:'] [i]"))
- {
- // simply extract the number of vertices and polygons from the pattern matching
- // output arrays
- obj->num_vertices = parser.pints[0];
- obj->num_polys = parser.pints[1];
- Write_Error("nASC Reader Num Vertices: %d, Num Polys: %d",
- obj->num_vertices, obj->num_polys);
- break;
- } // end if
- } // end while
- // allocate the memory for the vertices and number of polys
- // the call parameters are redundant in this case, but who cares
- if (!Init_OBJECT4DV2(obj, // object to allocate
- obj->num_vertices,
- obj->num_polys,
- obj->num_frames))
- {
- Write_Error("nASC file error with file %s (can't allocate memory).",filename);
- } // end if
- // Step 5: load the vertex list
- // advance parser to vertex list denoted by:
- // "Vertex list:"
- while(1)
- {
- // get next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("n'Vertex list:' line not found in .ASC file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Vertex'] ['list:']"))
- {
- Write_Error("nASC Reader found vertex list in .ASC file %s.", filename);
- break;
- } // end if
- } // end while
- // now read in vertex list, format:
- // "Vertex: d X:d.d Y:d.d Z:d.d"
- for (int vertex = 0; vertex < obj->num_vertices; vertex++)
- {
- // hunt for vertex
- while(1)
- {
- // get the next vertex
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nVertex list ended abruptly! in .ASC file %s.", filename);
- return(0);
- } // end if
- // strip all ":XYZ", make this easier, note use of input and output as same var, this is legal
- // since the output is guaranteed to be the same length or shorter as the input :)
- StripChars(parser.buffer, parser.buffer, ":XYZ");
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Vertex'] [i] [f] [f] [f]"))
- {
- // at this point we have the x,y,z in the the pfloats array locations 0,1,2
- obj->vlist_local[vertex].x = parser.pfloats[0];
- obj->vlist_local[vertex].y = parser.pfloats[1];
- obj->vlist_local[vertex].z = parser.pfloats[2];
- obj->vlist_local[vertex].w = 1;
- // do vertex swapping right here, allow muliple swaps, why not!
- // defines for vertex re-ordering flags
- //#define VERTEX_FLAGS_INVERT_X 1 // inverts the Z-coordinates
- //#define VERTEX_FLAGS_INVERT_Y 2 // inverts the Z-coordinates
- //#define VERTEX_FLAGS_INVERT_Z 4 // inverts the Z-coordinates
- //#define VERTEX_FLAGS_SWAP_YZ 8 // transforms a RHS model to a LHS model
- //#define VERTEX_FLAGS_SWAP_XZ 16 // ???
- //#define VERTEX_FLAGS_SWAP_XY 32
- //#define VERTEX_FLAGS_INVERT_WINDING_ORDER 64 // invert winding order from cw to ccw or ccw to cc
- float temp_f; // used for swapping
- // invert signs?
- if (vertex_flags & VERTEX_FLAGS_INVERT_X)
- obj->vlist_local[vertex].x=-obj->vlist_local[vertex].x;
- if (vertex_flags & VERTEX_FLAGS_INVERT_Y)
- obj->vlist_local[vertex].y=-obj->vlist_local[vertex].y;
- if (vertex_flags & VERTEX_FLAGS_INVERT_Z)
- obj->vlist_local[vertex].z=-obj->vlist_local[vertex].z;
- // swap any axes?
- if (vertex_flags & VERTEX_FLAGS_SWAP_YZ)
- SWAP(obj->vlist_local[vertex].y, obj->vlist_local[vertex].z, temp_f);
- if (vertex_flags & VERTEX_FLAGS_SWAP_XZ)
- SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].z, temp_f);
- if (vertex_flags & VERTEX_FLAGS_SWAP_XY)
- SWAP(obj->vlist_local[vertex].x, obj->vlist_local[vertex].y, temp_f);
- Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
- obj->vlist_local[vertex].x,
- obj->vlist_local[vertex].y,
- obj->vlist_local[vertex].z,
- obj->vlist_local[vertex].w);
- // scale vertices
- if (scale)
- {
- obj->vlist_local[vertex].x*=scale->x;
- obj->vlist_local[vertex].y*=scale->y;
- obj->vlist_local[vertex].z*=scale->z;
- } // end if
- // set point field in this vertex, we need that at least
- SET_BIT(obj->vlist_local[vertex].attr, VERTEX4DTV1_ATTR_POINT);
- // found vertex, break out of while for next pass
- break;
- } // end if
- } // end while
- } // end for vertex
- // compute average and max radius
- Compute_OBJECT4DV2_Radius(obj);
- Write_Error("nObject average radius = %f, max radius = %f",
- obj->avg_radius, obj->max_radius);
- // step 6: load in the polygons
- // poly list starts off with:
- // "Face list:"
- while(1)
- {
- // get next line
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("n'Face list:' line not found in .ASC file %s.", filename);
- return(0);
- } // end if
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Face'] ['list:']"))
- {
- Write_Error("nASC Reader found face list in .ASC file %s.", filename);
- break;
- } // end if
- } // end while
- // now read each face in format:
- // Face ddd: A:ddd B:ddd C:ddd AB:1|0 BC:1|0 CA:1|
- // Material:"rdddgdddbddda0"
- // the A, B, C part is vertex 0,1,2 but the AB, BC, CA part
- // has to do with the edges and the vertex ordering
- // the material indicates the color, and has an 'a0' tacked on the end???
- int poly_surface_desc = 0; // ASC surface descriptor/material in this case
- int poly_num_verts = 0; // number of vertices for current poly (always 3)
- char tmp_string[8]; // temp string to hold surface descriptor in and
- // test if it need to be converted from hex
- for (int poly=0; poly < obj->num_polys; poly++)
- {
- // hunt until next face is found
- while(1)
- {
- // get the next polygon face
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nface list ended abruptly! in .ASC file %s.", filename);
- return(0);
- } // end if
- // strip all ":ABC", make this easier, note use of input and output as same var, this is legal
- // since the output is guaranteed to be the same length or shorter as the input :)
- StripChars(parser.buffer, parser.buffer, ":ABC");
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "['Face'] [i] [i] [i] [i]"))
- {
- // at this point we have the vertex indices in the the pints array locations 1,2,3,
- // 0 contains the face number
- // insert polygon, check for winding order invert
- if (vertex_flags & VERTEX_FLAGS_INVERT_WINDING_ORDER)
- {
- poly_num_verts = 3;
- obj->plist[poly].vert[0] = parser.pints[3];
- obj->plist[poly].vert[1] = parser.pints[2];
- obj->plist[poly].vert[2] = parser.pints[1];
- } // end if
- else
- { // leave winding order alone
- poly_num_verts = 3;
- obj->plist[poly].vert[0] = parser.pints[1];
- obj->plist[poly].vert[1] = parser.pints[2];
- obj->plist[poly].vert[2] = parser.pints[3];
- } // end else
- // point polygon vertex list to object's vertex list
- // note that this is redundant since the polylist is contained
- // within the object in this case and its up to the user to select
- // whether the local or transformed vertex list is used when building up
- // polygon geometry, might be a better idea to set to NULL in the context
- // of polygons that are part of an object
- obj->plist[poly].vlist = obj->vlist_local;
- // set texture coordinate list, this is needed
- obj->plist[poly].tlist = obj->tlist;
- // found the face, break out of while for another pass
- break;
- } // end if
- } // end while
- // hunt until next material for face is found
- while(1)
- {
- // get the next polygon material (the "page xxx" breaks mess everything up!!!)
- if (!parser.Getline(PARSER_STRIP_EMPTY_LINES | PARSER_STRIP_WS_ENDS))
- {
- Write_Error("nmaterial list ended abruptly! in .ASC file %s.", filename);
- return(0);
- } // end if
- // Material:"rdddgdddbddda0"
- // replace all ':"rgba', make this easier, note use of input and output as same var, this is legal
- // since the output is guaranteed to be the same length or shorter as the input :)
- // the result will look like:
- // "M t ri l ddd ddd ddd 0"
- // which we can parse!
- ReplaceChars(parser.buffer, parser.buffer, ":"rgba", ' ');
- // check for pattern?
- if (parser.Pattern_Match(parser.buffer, "[i] [i] [i]"))
- {
- // at this point we have the red, green, and blue components in the the pints array locations 0,1,2,
- r = parser.pints[0];
- g = parser.pints[1];
- b = parser.pints[2];
- // set all the attributes of polygon as best we can with this format
- // SET_BIT(obj->plist[poly].attr, POLY4DV1_ATTR_2SIDED);
- // 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[poly].attr,POLY4DV2_ATTR_RGB16);
- obj->plist[poly].color = RGB16Bit(r, g, b);
- Write_Error("nPolygon 16-bit");
- } // end if
- else
- {
- // 8 bit mode
- SET_BIT(obj->plist[poly].attr,POLY4DV2_ATTR_8BITCOLOR);
- obj->plist[poly].color = RGBto8BitIndex(r,g,b, palette, 0);
- Write_Error("nPolygon 8-bit, index=%d", obj->plist[poly].color);
- } // end else
- // we have added the ability to manually override the shading mode
- // of the object, not a face by face basis, but at least on an
- // object wide basis, normally all ASC files are loaded with flat shading as
- // the default, by adding flags to the vertex flags this can be overridden
- //#define VERTEX_FLAGS_OVERRIDE_MASK 0xf000 // this masks these bits to extract them
- //#define VERTEX_FLAGS_OVERRIDE_CONSTANT 0x1000
- //#define VERTEX_FLAGS_OVERRIDE_EMISSIVE 0x1000 (alias)
- //#define VERTEX_FLAGS_OVERRIDE_FLAT 0x2000
- //#define VERTEX_FLAGS_OVERRIDE_GOURAUD 0x4000
- //#define VERTEX_FLAGS_OVERRIDE_TEXTURE 0x8000
- // first test to see if there is an override at all
- int vertex_overrides = (vertex_flags & VERTEX_FLAGS_OVERRIDE_MASK);
- if (vertex_overrides)
- {
- // which override?
- if (vertex_overrides & VERTEX_FLAGS_OVERRIDE_PURE)
- SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_PURE);
- if (vertex_overrides & VERTEX_FLAGS_OVERRIDE_FLAT)
- SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_FLAT);
- if (vertex_overrides & VERTEX_FLAGS_OVERRIDE_GOURAUD)
- {
- SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_GOURAUD);
- // going to need vertex normals!
- SET_BIT(obj->vlist_local[ obj->plist[poly].vert[0] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- SET_BIT(obj->vlist_local[ obj->plist[poly].vert[1] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- SET_BIT(obj->vlist_local[ obj->plist[poly].vert[2] ].attr, VERTEX4DTV1_ATTR_NORMAL);
- } // end if
- if (vertex_overrides & VERTEX_FLAGS_OVERRIDE_TEXTURE)
- SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_TEXTURE);
- } // end if
- else
- {
- // for now manually set shading mode to flat
- //SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_PURE);
- //SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_GOURAUD);
- //SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_PHONG);
- SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_SHADE_MODE_FLAT);
- } // end else
- // set the material mode to ver. 1.0 emulation
- SET_BIT(obj->plist[poly].attr, POLY4DV2_ATTR_DISABLE_MATERIAL);
- // set polygon to active
- obj->plist[poly].state = POLY4DV2_STATE_ACTIVE;
- // found the material, break out of while for another pass
- break;
- } // end if
- } // end while
- Write_Error("nPolygon %d:", poly);
- Write_Error("nSurface Desc = [RGB]=[%d, %d, %d], vert_indices [%d, %d, %d]",
- r,g,b,
- obj->plist[poly].vert[0],
- obj->plist[poly].vert[1],
- obj->plist[poly].vert[2]);
- } // end for poly
- // compute the polygon normal lengths
- Compute_OBJECT4DV2_Poly_Normals(obj);
- // compute vertex normals for any gouraud shaded polys
- Compute_OBJECT4DV2_Vertex_Normals(obj);
- // return success
- return(1);
- } // end Load_OBJECT4DV2_3DASC
- ///////////////////////////////////////////////////////////////////////////////
- int Load_OBJECT4DV2_PLG(OBJECT4DV2_PTR obj, // pointer to object
- char *filename, // filename of plg file
- VECTOR4D_PTR scale, // initial scaling factors
- VECTOR4D_PTR pos, // initial position
- VECTOR4D_PTR rot, // initial rotations
- int vertex_flags) // vertex flags, used to override
- {
- // this function loads a plg object in off disk, additionally
- // it allows the caller to scale, position, and rotate the object
- // to save extra calls later for non-dynamic objects
- // there is only one frame, so load the object and set the fields
- // appropriately for a single frame OBJECT4DV2
- FILE *fp; // file pointer
- char buffer[256]; // working buffer
- char *token_string; // pointer to actual token text, ready for parsing
- // file format review, note types at end of each description
- // # this is a comment
- // # object descriptor
- // object_name_string num_verts_int num_polys_int
- // # vertex list
- // x0_float y0_float z0_float
- // x1_float y1_float z1_float
- // x2_float y2_float z2_float
- // .
- // .
- // xn_float yn_float zn_float
- //
- // # polygon list
- // surface_description_ushort num_verts_int v0_index_int v1_index_int .. vn_index_int
- // .
- // .
- // surface_description_ushort num_verts_int v0_index_int v1_index_int .. vn_index_int
- // lets keep it simple and assume one element per line
- // hence we have to find the object descriptor, read it in, then the
- // vertex list and read it in, and finally the polygon list -- simple :)
- // Step 1: clear out the object and initialize it a bit
- memset(obj, 0, sizeof(OBJECT4DV2));
- // set state of object to active and visible
- obj->state = OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_STATE_VISIBLE;
- // set position of object
- obj->world_pos.x = pos->x;
- obj->world_pos.y = pos->y;
- obj->world_pos.z = pos->z;
- obj->world_pos.w = pos->w;
- // set number of frames
- obj->num_frames = 1;
- obj->curr_frame = 0;
- obj->attr = OBJECT4DV2_ATTR_SINGLE_FRAME;
- // Step 2: open the file for reading
- if (!(fp = fopen(filename, "r")))
- {
- Write_Error("Couldn't open PLG file %s.", filename);
- return(0);
- } // end if
- // Step 3: get the first token string which should be the object descriptor
- if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
- {
- Write_Error("PLG file error with file %s (object descriptor invalid).",filename);
- return(0);
- } // end if
- Write_Error("Object Descriptor: %s", token_string);
- // parse out the info object
- sscanf(token_string, "%s %d %d", obj->name, &obj->num_vertices, &obj->num_polys);
- // allocate the memory for the vertices and number of polys
- // the call parameters are redundant in this case, but who cares
- if (!Init_OBJECT4DV2(obj, // object to allocate
- obj->num_vertices,
- obj->num_polys,
- obj->num_frames))
- {
- Write_Error("nPLG file error with file %s (can't allocate memory).",filename);
- } // end if
- // Step 4: load the vertex list
- for (int vertex = 0; vertex < obj->num_vertices; vertex++)
- {
- // get the next vertex
- if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
- {
- Write_Error("PLG file error with file %s (vertex list invalid).",filename);
- return(0);
- } // end if
- // parse out vertex
- sscanf(token_string, "%f %f %f", &obj->vlist_local[vertex].x,
- &obj->vlist_local[vertex].y,
- &obj->vlist_local[vertex].z);
- obj->vlist_local[vertex].w = 1;
- // scale vertices
- obj->vlist_local[vertex].x*=scale->x;
- obj->vlist_local[vertex].y*=scale->y;
- obj->vlist_local[vertex].z*=scale->z;
- Write_Error("nVertex %d = %f, %f, %f, %f", vertex,
- obj->vlist_local[vertex].x,
- obj->vlist_local[vertex].y,
- obj->vlist_local[vertex].z,
- obj->vlist_local[vertex].w);
- // every vertex has a point at least, set that in the flags attribute
- SET_BIT(obj->vlist_local[vertex].attr, VERTEX4DTV1_ATTR_POINT);
- } // end for vertex
- // compute average and max radius
- Compute_OBJECT4DV2_Radius(obj);
- Write_Error("nObject average radius = %f, max radius = %f",
- obj->avg_radius, obj->max_radius);
- int poly_surface_desc = 0; // PLG/PLX surface descriptor
- int poly_num_verts = 0; // number of vertices for current poly (always 3)
- char tmp_string[8]; // temp string to hold surface descriptor in and
- // test if it need to be converted from hex
- // Step 5: load the polygon list
- for (int poly=0; poly < obj->num_polys; poly++)
- {
- // get the next polygon descriptor
- if (!(token_string = Get_Line_PLG(buffer, 255, fp)))
- {
- Write_Error("PLG file error with file %s (polygon descriptor invalid).",filename);
- return(0);
- } // end if
- Write_Error("nPolygon %d:", poly);
- // each vertex list MUST have 3 vertices since we made this a rule that all models
- // must be constructed of triangles
- // read in surface descriptor, number of vertices, and vertex list
- sscanf(token_string, "%s %d %d %d %d", tmp_string,
- &poly_num_verts, // should always be 3
- &obj->plist[poly].vert[0],
- &obj->plist[poly].vert[1],
- &obj->plist[poly].vert[2]);
- // since we are allowing the surface descriptor to be in hex format
- // with a leading "0x" we need to test for it
- if (tmp_string[0] == '0' && toupper(tmp_string[1]) == 'X')
- sscanf(tmp_string,"%x", &poly_surface_desc);
- else
- poly_surface_desc = atoi(tmp_string);
- // point polygon vertex list to object's vertex list
- // note that this is redundant since the polylist is contained
- // within the object in this case and its up to the user to select
- // whether the local or transformed vertex list is used when building up
- // polygon geometry, might be a better idea to set to NULL in the context
- // of polygons that are part of an object
- obj->plist[poly].vlist = obj->vlist_local;