Main Page Class List Class Members

- Main Page
- User's Guide
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

# sdk/extensions/authoring/source/NvBlastExtAuthoringVSA.h

Go to the documentation of this file.

    00001 // This code contains NVIDIA Confidential Information and is disclosed to you
    00002 // under a form of NVIDIA software license agreement provided separately to you.
    00003 //
    00004 // Notice
    00005 // NVIDIA Corporation and its licensors retain all intellectual property and
    00006 // proprietary rights in and to this software and related documentation and
    00007 // any modifications thereto. Any use, reproduction, disclosure, or
    00008 // distribution of this software and related documentation without an express
    00009 // license agreement from NVIDIA Corporation is strictly prohibited.
    00010 //
    00011 // ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
    00012 // NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
    00013 // THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
    00014 // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
    00015 //
    00016 // Information and code furnished is believed to be accurate and reliable.
    00017 // However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
    00018 // information or for any infringement of patents or other rights of third parties that may
    00019 // result from its use. No license is granted by implication or otherwise under any patent
    00020 // or patent rights of NVIDIA Corporation. Details are subject to change without notice.
    00021 // This code supersedes and replaces all information previously supplied.
    00022 // NVIDIA Corporation products are not authorized for use as critical
    00023 // components in life support devices or systems without express written approval of
    00024 // NVIDIA Corporation.
    00025 //
    00026 // Copyright (c) 2016-2020 NVIDIA Corporation. All rights reserved.
    00027
    00028
    00029 #ifndef NVBLASTEXTAUTHORINGVSA_H
    00030 #define NVBLASTEXTAUTHORINGVSA_H
    00031
    00032 namespace Nv
    00033 {
    00034 namespace Blast
    00035 {
    00036
    00037 /*
    00038     This code copied from APEX GSA
    00039 */
    00040
    00041 namespace VSA
    00042 {
    00043 typedef float real;
    00044
    00045 struct VS3D_Halfspace_Set
    00046 {
    00047     virtual real    farthest_halfspace(real plane[4], const real point[4]) = 0;
    00048 };
    00049
    00050
    00051 // Simple types and operations for internal calculations
    00052 struct Vec3 { real x, y, z; };  // 3-vector
    00053 inline Vec3 vec3(real x, real y, real z) { Vec3 r; r.x = x; r.y = y; r.z = z; return r; }   // vector builder
    00054 inline Vec3 operator +	(const Vec3& a, const Vec3& b) { return vec3(a.x + b.x, a.y + b.y, a.z + b.z); }         // vector addition
    00055 inline Vec3 operator *	(real s, const Vec3& v) { return vec3(s*v.x, s*v.y, s*v.z); }                // scalar multiplication
    00056 inline real operator |	(const Vec3& a, const Vec3& b) { return a.x*b.x + a.y*b.y + a.z*b.z; }               // dot product
    00057 inline Vec3 operator ^	(const Vec3& a, const Vec3& b) { return vec3(a.y*b.z - b.y*a.z, a.z*b.x - b.z*a.x, a.x*b.y - b.x*a.y); } // cross product
    00058
    00059 struct Vec4 { Vec3 v; real w; };    // 4-vector split into 3-vector and scalar parts
    00060 inline Vec4 vec4(const Vec3& v, real w) { Vec4 r; r.v = v; r.w = w; return r; } // vector builder
    00061 inline real operator | (const Vec4& a, const Vec4& b) { return (a.v | b.v) + a.w*b.w; }         // dot product
    00062
    00063 // More accurate perpendicular
    00064 inline Vec3 perp(const Vec3& a, const Vec3& b)
    00065 {
    00066     Vec3 c = a^b;   // Cross-product gives perpendicular
    00067 #if VS3D_HIGH_ACCURACY || REAL_DOUBLE
    00068     const real c2 = c | c;
    00069     if (c2 != 0) c = c + (1 / c2)*((a | c)*(c^b) + (b | c)*(a^c));  // Improvement to (a b)^T(c) = (0)
    00070 #endif
    00071     return c;
    00072 }
    00073
    00074 // Square
    00075 inline real sq(real x) { return x*x; }
    00076
    00077 // Returns index of the extremal element in a three-element set {e0, e1, e2} based upon comparisons c_ij. The extremal index m is such that c_mn is true, or e_m == e_n, for all n.
    00078 inline int  ext_index(int c_10, int c_21, int c_20) { return c_10 << c_21 | (c_21&c_20) << 1; }
    00079
    00080 // Returns index (0, 1, or 2) of minimum argument
    00081 inline int  index_of_min(real x0, real x1, real x2) { return ext_index((int)(x1 < x0), (int)(x2 < x1), (int)(x2 < x0)); }
    00082
    00083 // Compare fractions with positive deominators.  Returns a_num*sqrt(a_rden2) > b_num*sqrt(b_rden2)
    00084 inline bool frac_gt(real a_num, real a_rden2, real b_num, real b_rden2)
    00085 {
    00086     const bool a_num_neg = a_num < 0;
    00087     const bool b_num_neg = b_num < 0;
    00088     return a_num_neg != b_num_neg ? b_num_neg : ((a_num*a_num*a_rden2 > b_num*b_num*b_rden2) != a_num_neg);
    00089 }
    00090
    00091 // Returns index (0, 1, or 2) of maximum fraction with positive deominators
    00092 inline int  index_of_max_frac(real x0_num, real x0_rden2, real x1_num, real x1_rden2, real x2_num, real x2_rden2)
    00093 {
    00094     return ext_index((int)frac_gt(x1_num, x1_rden2, x0_num, x0_rden2), (int)frac_gt(x2_num, x2_rden2, x1_num, x1_rden2), (int)frac_gt(x2_num, x2_rden2, x0_num, x0_rden2));
    00095 }
    00096
    00097 // Compare values given their signs and squares.  Returns a > b.  a2 and b2 may have any constant offset applied to them.
    00098 inline bool sgn_sq_gt(real sgn_a, real a2, real sgn_b, real b2) { return sgn_a*sgn_b < 0 ? (sgn_b < 0) : ((a2 > b2) != (sgn_a < 0)); }
    00099
    00100 // Returns index (0, 1, or 2) of maximum value given their signs and squares.  sq_x0, sq_x1, and sq_x2 may have any constant offset applied to them.
    00101 inline int  index_of_max_sgn_sq(real sgn_x0, real sq_x0, real sgn_x1, real sq_x1, real sgn_x2, real sq_x2)
    00102 {
    00103     return ext_index((int)sgn_sq_gt(sgn_x1, sq_x1, sgn_x0, sq_x0), (int)sgn_sq_gt(sgn_x2, sq_x2, sgn_x1, sq_x1), (int)sgn_sq_gt(sgn_x2, sq_x2, sgn_x0, sq_x0));
    00104 }
    00105
    00106 // Project 2D (homogeneous) vector onto 2D half-space boundary
    00107 inline void project2D(Vec3& r, const Vec3& plane, real delta, real recip_n2, real eps2)
    00108 {
    00109     r = r + (-delta*recip_n2)*vec3(plane.x, plane.y, 0);
    00110     r = r + (-(r | plane)*recip_n2)*vec3(plane.x, plane.y, 0);  // Second projection for increased accuracy
    00111     if ((r | r) > eps2) return;
    00112     r = (-plane.z*recip_n2)*vec3(plane.x, plane.y, 0);
    00113     r.z = 1;
    00114 }
    00115
    00116
    00117 // Update function for vs3d_test
    00118 static bool vs3d_update(Vec4& p, Vec4 S[4], int& plane_count, const Vec4& q, real eps2)
    00119 {
    00120     // h plane is the last plane
    00121     const Vec4& h = S[plane_count - 1];
    00122
    00123     // Handle plane_count == 1 specially (optimization; this could be commented out)
    00124     if (plane_count == 1)
    00125     {
    00126         // Solution is objective projected onto h plane
    00127         p = q;
    00128         p.v = p.v + -(p | h)*h.v;
    00129         if ((p | p) <= eps2) p = vec4(-h.w*h.v, 1); // If p == 0 then q is a direction vector, any point in h is a support point
    00130         return true;
    00131     }
    00132
    00133     // Create basis in the h plane
    00134     const int min_i = index_of_min(h.v.x*h.v.x, h.v.y*h.v.y, h.v.z*h.v.z);
    00135     const Vec3 y = h.v^vec3((real)(min_i == 0), (real)(min_i == 1), (real)(min_i == 2));
    00136     const Vec3 x = y^h.v;
    00137
    00138     // Use reduced vector r instead of p
    00139     Vec3 r = { x | q.v, y | q.v, q.w*(y | y) }; // (x|x) = (y|y) = square of plane basis scale
    00140
    00141     // If r == 0 (within epsilon), then it is a direction vector, and we have a bounded solution
    00142     if ((r | r) <= eps2) r.z = 1;
    00143
    00144     // Create plane equations in the h plane.  These will not be normalized in general.
    00145     int N = 0;          // Plane count in h subspace
    00146     Vec3 R[3];          // Planes in h subspace
    00147     real recip_n2[3];   // Plane normal vector reciprocal lengths squared
    00148     real delta[3];      // Signed distance of objective to the planes
    00149     int index[3];       // Keep track of original plane indices
    00150     for (int i = 0; i < plane_count - 1; ++i)
    00151     {
    00152         const Vec3& vi = S[i].v;
    00153         const real cos_theta = h.v | vi;
    00154         R[N] = vec3(x | vi, y | vi, S[i].w - h.w*cos_theta);
    00155         index[N] = i;
    00156         const real n2 = R[N].x*R[N].x + R[N].y*R[N].y;
    00157         if (n2 >= eps2)
    00158         {
    00159             const real lin_norm = (real)1.5 - (real)0.5*n2; // 1st-order approximation to 1/sqrt(n2) expanded about n2 = 1
    00160             R[N] = lin_norm*R[N];   // We don't need normalized plane equations, but rescaling (even with an approximate normalization) gives better numerical behavior
    00161             recip_n2[N] = 1 / (R[N].x*R[N].x + R[N].y*R[N].y);
    00162             delta[N] = r | R[N];
    00163             ++N;    // Keep this plane
    00164         }
    00165         else if (cos_theta < 0) return false;   // Parallel cases are redundant and rejected, anti-parallel cases are 1D voids
    00166     }
    00167
    00168     // Now work with the N-sized R array of half-spaces in the h plane
    00169     switch (N)
    00170     {
    00171     case 1: one_plane :
    00172         if (delta[0] < 0) N = 0;    // S[0] is redundant, eliminate it
    00173         else project2D(r, R[0], delta[0], recip_n2[0], eps2);
    00174         break;
    00175     case 2: two_planes :
    00176         if (delta[0] < 0 && delta[1] < 0) N = 0;    // S[0] and S[1] are redundant, eliminate them
    00177         else
    00178         {
    00179             const int max_d_index = (int)frac_gt(delta[1], recip_n2[1], delta[0], recip_n2[0]);
    00180             project2D(r, R[max_d_index], delta[max_d_index], recip_n2[max_d_index], eps2);
    00181             const int min_d_index = max_d_index ^ 1;
    00182             const real new_delta_min = r | R[min_d_index];
    00183             if (new_delta_min < 0)
    00184             {
    00185                 index[0] = index[max_d_index];
    00186                 N = 1;  // S[min_d_index] is redundant, eliminate it
    00187             }
    00188             else
    00189             {
    00190                 // Set r to the intersection of R[0] and R[1] and keep both
    00191                 r = perp(R[0], R[1]);
    00192                 if (r.z*r.z*recip_n2[0] * recip_n2[1] < eps2)
    00193                 {
    00194                     if (R[0].x*R[1].x + R[0].y*R[1].y < 0) return false;    // 2D void found
    00195                     goto one_plane;
    00196                 }
    00197                 r = (1 / r.z)*r;    // We could just as well multiply r by sgn(r.z); we just need to ensure r.z > 0
    00198             }
    00199         }
    00200         break;
    00201     case 3:
    00202         if (delta[0] < 0 && delta[1] < 0 && delta[2] < 0) N = 0;    // S[0], S[1], and S[2] are redundant, eliminate them
    00203         else
    00204         {
    00205             const Vec3 row_x = { R[0].x, R[1].x, R[2].x };
    00206             const Vec3 row_y = { R[0].y, R[1].y, R[2].y };
    00207             const Vec3 row_w = { R[0].z, R[1].z, R[2].z };
    00208             const Vec3 cof_w = perp(row_x, row_y);
    00209             const bool detR_pos = (row_w | cof_w) > 0;
    00210             const int nrw_sgn0 = cof_w.x*cof_w.x*recip_n2[1] * recip_n2[2] < eps2 ? 0 : (((int)((cof_w.x > 0) == detR_pos) << 1) - 1);
    00211             const int nrw_sgn1 = cof_w.y*cof_w.y*recip_n2[2] * recip_n2[0] < eps2 ? 0 : (((int)((cof_w.y > 0) == detR_pos) << 1) - 1);
    00212             const int nrw_sgn2 = cof_w.z*cof_w.z*recip_n2[0] * recip_n2[1] < eps2 ? 0 : (((int)((cof_w.z > 0) == detR_pos) << 1) - 1);
    00213
    00214             if ((nrw_sgn0 | nrw_sgn1 | nrw_sgn2) >= 0) return false;    // 3D void found
    00215
    00216             const int positive_width_count = ((nrw_sgn0 >> 1) & 1) + ((nrw_sgn1 >> 1) & 1) + ((nrw_sgn2 >> 1) & 1);
    00217             if (positive_width_count == 1)
    00218             {
    00219                 // A single positive width results from a redundant plane.  Eliminate it and peform N = 2 calculation.
    00220                 const int pos_width_index = ((nrw_sgn1 >> 1) & 1) | (nrw_sgn2 & 2); // Calculates which index corresponds to the positive-width side
    00221                 R[pos_width_index] = R[2];
    00222                 recip_n2[pos_width_index] = recip_n2[2];
    00223                 delta[pos_width_index] = delta[2];
    00224                 index[pos_width_index] = index[2];
    00225                 N = 2;
    00226                 goto two_planes;
    00227             }
    00228
    00229             // Find the max dot product of r and R[i]/|R_normal[i]|.  For numerical accuracy when the angle between r and the i^{th} plane normal is small, we take some care below:
    00230             const int max_d_index = r.z != 0
    00231                 ? index_of_max_frac(delta[0], recip_n2[0], delta[1], recip_n2[1], delta[2], recip_n2[2])    // displacement term resolves small-angle ambiguity, just use dot product
    00232                 : index_of_max_sgn_sq(delta[0], -sq(r.x*R[0].y - r.y*R[0].x)*recip_n2[0], delta[1], -sq(r.x*R[1].y - r.y*R[1].x)*recip_n2[1], delta[2], -sq(r.x*R[2].y - r.y*R[2].x)*recip_n2[2]);  // No displacement term.  Use wedge product to find the sine of the angle.
    00233
    00234             // Project r onto max-d plane
    00235             project2D(r, R[max_d_index], delta[max_d_index], recip_n2[max_d_index], eps2);
    00236             N = 1;  // Unless we use a vertex in the loop below
    00237             const int index_max = index[max_d_index];
    00238
    00239             // The number of finite widths should be >= 2.  If not, it should be 0, but in any case it implies three parallel lines in the plane, which we should not have here.
    00240             // If we do have three parallel lines (# of finite widths < 2), we've picked the line corresponding to the half-plane farthest from r, which is correct.
    00241             const int finite_width_count = (nrw_sgn0 & 1) + (nrw_sgn1 & 1) + (nrw_sgn2 & 1);
    00242             if (finite_width_count >= 2)
    00243             {
    00244                 const int i_remaining[2] = { (1 << max_d_index) & 3, (3 >> max_d_index) ^ 1 };  // = {(max_d_index+1)%3, (max_d_index+2)%3}
    00245                 const int i_select = (int)frac_gt(delta[i_remaining[1]], recip_n2[i_remaining[1]], delta[i_remaining[0]], recip_n2[i_remaining[0]]);    // Select the greater of the remaining dot products
    00246                 for (int i = 0; i < 2; ++i)
    00247                 {
    00248                     const int j = i_remaining[i_select^i];  // i = 0 => the next-greatest, i = 1 => the least
    00249                     if ((r | R[j]) >= 0)
    00250                     {
    00251                         r = perp(R[max_d_index], R[j]);
    00252                         r = (1 / r.z)*r;    // We could just as well multiply r by sgn(r.z); we just need to ensure r.z > 0
    00253                         index[1] = index[j];
    00254                         N = 2;
    00255                         break;
    00256                     }
    00257                 }
    00258             }
    00259
    00260             index[0] = index_max;
    00261         }
    00262         break;
    00263     }
    00264
    00265     // Transform r back to 3D space
    00266     p = vec4(r.x*x + r.y*y + (-r.z*h.w)*h.v, r.z);
    00267
    00268     // Pack S array with kept planes
    00269     if (N < 2 || index[1] != 0) { for (int i = 0; i < N; ++i) S[i] = S[index[i]]; } // Safe to copy columns in order
    00270     else { const Vec4 temp = S[0]; S[0] = S[index[0]]; S[1] = temp; }   // Otherwise use temp storage to avoid overwrite
    00271     S[N] = h;
    00272     plane_count = N + 1;
    00273
    00274     return true;
    00275 }
    00276
    00277
    00278 // Performs the VS algorithm for D = 3
    00279 inline int vs3d_test(VS3D_Halfspace_Set& halfspace_set, real* q = nullptr)
    00280 {
    00281     // Objective = q if it is not NULL, otherwise it is the origin represented in homogeneous coordinates
    00282     const Vec4 objective = q ? (q[3] != 0 ? vec4((1 / q[3])*vec3(q[0], q[1], q[2]), 1) : *(Vec4*)q) : vec4(vec3(0, 0, 0), 1);
    00283
    00284     // Tolerance for 3D void simplex algorithm
    00285     const real eps_f = (real)1 / (sizeof(real) == 4 ? (1L << 23) : (1LL << 52));    // Floating-point epsilon
    00286 #if VS3D_HIGH_ACCURACY || REAL_DOUBLE
    00287     const real eps = 8 * eps_f;
    00288 #else
    00289     const real eps = 80 * eps_f;
    00290 #endif
    00291     const real eps2 = eps*eps;  // Using epsilon squared
    00292
    00293     // Maximum allowed iterations of main loop.  If exceeded, error code is returned
    00294     const int max_iteration_count = 50;
    00295
    00296     // State
    00297     Vec4 S[4];              // Up to 4 planes
    00298     int plane_count = 0;    // Number of valid planes
    00299     Vec4 p = objective;     // Test point, initialized to objective
    00300
    00301     // Default result, changed to valid result if found in loop below
    00302     int result = -1;
    00303
    00304     // Iterate until a stopping condition is met or the maximum number of iterations is reached
    00305     for (int i = 0; result < 0 && i < max_iteration_count; ++i)
    00306     {
    00307         Vec4& plane = S[plane_count++];
    00308         real delta = halfspace_set.farthest_halfspace(&plane.v.x, &p.v.x);
    00309 #if VS3D_UNNORMALIZED_PLANE_HANDLING != 0
    00310         const real recip_norm = vs3d_recip_sqrt(plane.v | plane.v);
    00311         plane = vec4(recip_norm*plane.v, recip_norm*plane.w);
    00312         delta *= recip_norm;
    00313 #endif
    00314         if (delta <= 0 || delta*delta <= eps2*(p | p)) result = 1;  // Intersection found
    00315         else if (!vs3d_update(p, S, plane_count, objective, eps2)) result = 0;  // Void simplex found
    00316     }
    00317
    00318     // If q is given, fill it with the solution (normalize p.w if it is not zero)
    00319     if (q) *(Vec4*)q = (p.w != 0) ? vec4((1 / p.w)*p.v, 1) : p;
    00320
    00321     return result;
    00322 }
    00323
    00324 } // namespace VSA
    00325
    00326 } // namespace Blast
    00327 } // namespace Nv
    00328
    00329
    00330 #endif // ifndef NVBLASTEXTAUTHORINGVSA_H

Copyright © 2015-2017 NVIDIA Corporation, 2701 San Tomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved. www.nvidia.com