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 }