00001 /*
00002 File: RT_Prim.cc
00003
00004 Function: Implements various ray-trace primitives
00005
00006 Author: Andrew Willmott
00007
00008 Notes:
00009 */
00010
00011 #include "RT_Prim.h"
00012 #include "gcl/VecUtil.h"
00013
00014 // --- Triangle methods -------------------------------------------------------
00015
00016
00017 Int RT_Prim::gStamp = 1;
00018 Int RT_Object::gStamp = 1;
00019
00020 Void RT_Tri::Init(Int v1, Int v2, Int v3, RT_Object *obj, Int triID)
00021 {
00022 v[0] = v1;
00023 v[1] = v2;
00024 v[2] = v3;
00025 flags = 0;
00026 stamp = 0;
00027 primType = rt_triangle;
00028 object = obj;
00029 id = triID;
00030 }
00031
00032 #define CROSS_X_3(a, b, c) ((a[1] - b[1]) * (c[2] - b[2]) \
00033 - (a[2] - b[2]) * (c[1] - b[1]))
00034 // returns (a - b) x (c - a) . (1, 0, 0)
00035 #define CROSS_Y_3(a, b, c) ((a[2] - b[2]) * (c[0] - b[0]) \
00036 - (a[0] - b[0]) * (c[2] - b[2]))
00037 // returns (a - b) x (c - a) . (0, 1, 0)
00038 #define CROSS_Z_3(a, b, c) ((a[0] - b[0]) * (c[1] - b[1]) \
00039 - (a[1] - b[1]) * (c[0] - b[0]))
00040 // returns (a - b) x (c - a) . (0, 0, 1)
00041
00042 static const GCLReal kEdgeFuzz = 0.0;
00043 static const GCLReal kNegEdgeFuzz = -kEdgeFuzz;
00044
00045 Bool RT_Tri::PointIsInside(Point& point)
00046 {
00047 Point* points = object->points;
00048 Int i0 = v[0], i1 = v[1], i2 = v[2];
00049 GCLReal temp, a, b;
00050
00051 #ifdef RT_TRI_CACHE
00052 // Time stamp object so we can reuse this intersection test if necessary.
00053
00054 Stamp();
00055 #endif
00056
00057 flags &= ~TRI_HIT;
00058
00059 // Given a point lying in the plane of the triangle, we return false
00060 // if it lies outside the triangle, and its barycentric coordinates if it
00061 // lies inside.
00062
00063 temp = normal[normMajorAxis];
00064
00065 switch(normMajorAxis)
00066 {
00067 case 0:
00068 a = CROSS_X_3(points[i2], points[i1], point) / temp;
00069 if (a < kEdgeFuzz) return(false);
00070
00071 b = CROSS_X_3(points[i0], points[i2], point) / temp;
00072 if (b < kEdgeFuzz) return(false);
00073
00074 a = 1.0 - a - b;
00075 if (a < kEdgeFuzz) return(false);
00076
00077 flags |= TRI_HIT;
00078 return(true);
00079
00080 case 1:
00081 a = CROSS_Y_3(points[i2], points[i1], point) / temp;
00082 if (a < kEdgeFuzz) return(false);
00083
00084 b = CROSS_Y_3(points[i0], points[i2], point) / temp;
00085 if (b < kEdgeFuzz) return(false);
00086
00087 a = 1.0 - a - b;
00088 if (a < kEdgeFuzz) return(false);
00089
00090 flags |= TRI_HIT;
00091 return(true);
00092
00093 case 2:
00094 a = CROSS_Z_3(points[i2], points[i1], point) / temp;
00095 if (a < kEdgeFuzz) return(false);
00096
00097 b = CROSS_Z_3(points[i0], points[i2], point) / temp;
00098 if (b < kEdgeFuzz) return(false);
00099
00100 a = 1.0 - a - b;
00101 if (a < kEdgeFuzz) return(false);
00102
00103 flags |= TRI_HIT;
00104 return(true);
00105 }
00106
00107 return(true); // to keep the compiler happy.
00108 }
00109
00110 Void RT_Tri::FindBaryCoords(Point& point, Vector &coords)
00111 {
00112 Point* points = object->points;
00113 Int i0 = v[0], i1 = v[1], i2 = v[2];
00114 GCLReal temp;
00115
00116 #ifdef RT_TRI_CACHE
00117 // Time stamp object so we can reuse this intersection test if necessary.
00118
00119 Stamp();
00120 #endif
00121
00122 flags &= ~TRI_HIT;
00123
00124 // Given a point lying in the plane of the triangle, we return false
00125 // if it lies outside the triangle, and its barycentric coordinates if it
00126 // lies inside.
00127
00128 temp = normal[normMajorAxis];
00129
00130 switch(normMajorAxis)
00131 {
00132 case 0:
00133 coords[0] = CROSS_X_3(points[i2], points[i1], point) / temp;
00134 coords[1] = CROSS_X_3(points[i0], points[i2], point) / temp;
00135 coords[2] = 1.0 - coords[0] - coords[1];
00136 return;
00137
00138 case 1:
00139 coords[0] = CROSS_Y_3(points[i2], points[i1], point) / temp;
00140 coords[1] = CROSS_Y_3(points[i0], points[i2], point) / temp;
00141 coords[2] = 1.0 - coords[0] - coords[1];
00142 return;
00143
00144 case 2:
00145 coords[0] = CROSS_Z_3(points[i2], points[i1], point) / temp;
00146 coords[1] = CROSS_Z_3(points[i0], points[i2], point) / temp;
00147 coords[2] = 1.0 - coords[0] - coords[1];
00148 return;
00149 }
00150 }
00151
00152
00153 Void RT_Tri::UpdateBounds(Point& min, Point& max)
00154 // Use the tri's vertices to update the given min & max bounds.
00155 {
00156 Int i;
00157 Point *points = object->points;
00158
00159 for (i = 0; i < 3; i++)
00160 ::UpdateBounds(points[v[i]], min, max);
00161 }
00162
00163 Void RT_Tri::MakeNormal()
00164 {
00165 GCLReal xf, yf, zf;
00166 Point* points = object->points;
00167
00168 // Calculate face normal & other info
00169
00170 CalcTriAreaNormal(points[v[0]], points[v[1]], points[v[2]], normal);
00171
00172 d = -dot(normal, points[v[2]]);
00173
00174 xf = abs(normal[0]);
00175 yf = abs(normal[1]);
00176 zf = abs(normal[2]);
00177
00178 if (xf > yf)
00179 {
00180 if (xf > zf)
00181 normMajorAxis = 0;
00182 else
00183 normMajorAxis = 2;
00184 }
00185 else
00186 {
00187 if (yf > zf)
00188 normMajorAxis = 1;
00189 else
00190 normMajorAxis = 2;
00191 }
00192
00193 area = 0.5 * len(normal);
00194 }
00195
00196
00197 // --- RT_Sphere methods ------------------------------------------------------
00198
00199 Void RT_Sphere::Init(const Point &c, GCLReal r, RT_Object *obj, Int genID)
00200 {
00201 centre = c;
00202 radius = r;
00203 sqrRad = sqr(r);
00204 area = vl_pi * sqrRad;
00205 primType = rt_gen;
00206 object = obj;
00207 id = genID;
00208 }
00209
00210
00211 Bool RT_Sphere::Intersect(Point &start, Vector &dir, GCLReal tMin, GCLReal tMax)
00212 // Assumes dir is normalised.
00213 {
00214 Vector v = centre - start;
00215 GCLReal b = dot(v, dir);
00216 GCLReal disc = sqr(b) - sqrlen(v) + sqrRad;
00217 GCLReal t0;
00218
00219 flags &= ~TRI_HIT;
00220
00221 // Root must be smaller than current value of t...
00222 // and larger than tMin
00223
00224 if (disc <= 0.0)
00225 return(false); // no intersection
00226
00227 disc = sqrt(disc);
00228 t0 = b - disc; // 1st root
00229
00230 if (t0 >= tMin && t0 < tMax)
00231 {
00232 hitT = t0;
00233 flags |= TRI_HIT;
00234 return(true);
00235 }
00236
00237 t0 = b + disc; // 2nd root
00238
00239 if (t0 >= tMin && t0 < tMax)
00240 {
00241 hitT = t0;
00242 flags |= TRI_HIT;
00243 return(true);
00244 }
00245
00246 return(false); // intersection is behind
00247 }
00248
00249 Void RT_Sphere::UpdateBounds(Point &min, Point &max)
00250 {
00251 Point sMin, sMax;
00252
00253 FindBounds(sMin, sMax);
00254 FindMinElts(sMin, min, min);
00255 FindMaxElts(sMax, max, max);
00256 }
00257
00258 Void RT_Sphere::FindBounds(Point &min, Point &max)
00259 {
00260 max.MakeBlock(radius);
00261 min = centre - max;
00262 max += centre;
00263 }
00264
00265
00266 // --- RT_Object methods -------------------------------------------------------
00267
00268
00269 Void RT_Object::Init(Int numTris, Int numGens)
00270 {
00271 SELF.numTris = numTris;
00272 SELF.numGens = numGens;
00273 if (numTris == 0)
00274 tris = 0;
00275 else
00276 tris = new RT_Tri[numTris];
00277 if (numGens == 0)
00278 gens = 0;
00279 else
00280 gens = new RT_GenPtr[numGens];
00281
00282 points = 0;
00283 numPoints = 0;
00284 normals = 0;
00285 numNormals = 0;
00286
00287 tag = 1;
00288 id = 0;
00289 }
00290
00291 Void RT_Object::Setup()
00292 {
00293 Int i;
00294
00295 for (i = 0; i < numTris; i++)
00296 {
00297 tris[i].object = this;
00298 tris[i].MakeNormal();
00299 }
00300 }
00301
00302 Void RT_Object::Free()
00303 {
00304 Int i;
00305
00306 for (i = 0; i < numGens; i++)
00307 delete gens[i];
00308
00309 delete[] tris;
00310 delete[] gens;
00311 delete[] points;
00312 delete[] normals;
00313 }