diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/module/clothing/include/ModuleClothingHelpers.h | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'APEX_1.4/module/clothing/include/ModuleClothingHelpers.h')
| -rw-r--r-- | APEX_1.4/module/clothing/include/ModuleClothingHelpers.h | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/APEX_1.4/module/clothing/include/ModuleClothingHelpers.h b/APEX_1.4/module/clothing/include/ModuleClothingHelpers.h new file mode 100644 index 00000000..f2f43c1c --- /dev/null +++ b/APEX_1.4/module/clothing/include/ModuleClothingHelpers.h @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA CORPORATION and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA CORPORATION is strictly prohibited. + */ + + +#ifndef MODULE_CLOTHING_HELPERS_H +#define MODULE_CLOTHING_HELPERS_H + +#include "RenderMeshAssetIntl.h" +#include "RenderDebugInterface.h" +#include "PsUserAllocated.h" +#include "ApexSharedUtils.h" + +namespace nvidia +{ +namespace clothing +{ + +class ClothingCookedParam; + +// used for mesh skinning map creation +struct TriangleWithNormals +{ + void init() + { + // bestDot and worstDot store the difference of the vertex normals from the face normal + PxVec3 faceNormal = (vertices[1] - vertices[0]).cross(vertices[2] - vertices[0]); + faceNormal.normalize(); + float bestDot = -1.0f; + float worstDot = 1.0f; + for (uint32_t j = 0; j < 3; j++) + { + const float dot = faceNormal.dot(normals[j]); + bestDot = PxMax(bestDot, dot); + worstDot = PxMin(worstDot, dot); + } + + doNotUse = worstDot < 0.0f || bestDot < 0.5f; // bestDot has more than 60 degrees difference from faceNormal + + // init the rest of the triangle members + timestamp = -1; + valid = 0; + + bounds.minimum = bounds.maximum = vertices[0]; + bounds.include(vertices[1]); + bounds.include(vertices[2]); + PX_ASSERT(!bounds.isEmpty()); + } + + PxVec3 vertices[3]; + PxVec3 normals[3]; + uint32_t faceIndex0; + int32_t timestamp : 30; + uint32_t valid : 2; + + PxBounds3 bounds; + + PxVec3 tempBaryVertex; + PxVec3 tempBaryNormal; + PxVec3 tempBaryTangent; + + uint32_t master; + + bool doNotUse; +}; + + + +class ModuleClothingHelpers +{ +public: + /** + \brief A fast inverse square root. Mainly used to normalize vectors more quickly (and less accurately) + */ + inline static float invSqrt(float input) + { + const float halfInput = 0.5f * input; + int32_t i = *(int*)&input; + + i = 0x5f375a86 - (i >> 1); + input = *(float*) & i; + input = input * (1.5f - halfInput * input * input); + return input; + } + + + static float triangleArea(const PxVec3& v1, const PxVec3& v2, const PxVec3& v3) + { + const PxVec3 a = v2 - v1; + const PxVec3 b = v3 - v1; + const PxVec3 c = a.cross(b); + //PxVec3 normal = c; + //normal.normalize(); + //const float area = normal.dot(c) * 0.5f; + //return area; + return c.magnitude() * 0.5f; + } + +#pragma warning(push) +#pragma warning(disable:4127) // (conditional expression is constant) + template<bool method1> + static PxVec3 computeTriBarycentricCoords(const PxVec3& a, const PxVec3& b, const PxVec3& c, const PxVec3& g) + { + PxVec3 bary(0, 0, 0); + if (method1) + { + const float abc = triangleArea(a, b, c); + + // Convert our position from local space into barycentric coordinates + offset from surface. + if (abc > 0.0f) + { + PxVec3 Edge0 = b - a; + PxVec3 Edge1 = c - a; + + PxVec3 planeNormal = Edge0.cross(Edge1); + planeNormal.normalize(); + + // See comments in set() for computation of d + float planeD = -a.dot(planeNormal); + + // Valid for plane equation a*x + b*y + c*z + d = 0 + const float distance = g.dot(planeNormal) + planeD; + + // project on plane + const PxVec3 p = g - planeNormal * distance; + + bary.x = triangleArea(p, b, c) / abc; + bary.y = triangleArea(p, c, a) / abc; + bary.z = distance; + } + } + else + { + // triangle's normal + PxVec3 faceNormal = (b - a).cross(c - a); + faceNormal.normalize(); + + // distance to plane + float h = (g - a).dot(faceNormal); + + // project g onto plane + PxVec3 pg = g - (h * faceNormal); + + // compute barycentric coordinates of g in <a,b,c> + float s(0), t(0); + generateBarycentricCoordinatesTri(a, b, c, pg, s, t); + + // PH: s and t are not the barycentric coordinate of a and b, but of b and c + bary.x = 1 - s - t; + bary.y = s; + bary.z = h; + } + return bary; + } +#pragma warning(pop) + + static float TetraVolume(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& p3) + { + return (p1 - p0).cross(p2 - p0).dot(p3 - p0) * (1.0f / 6.0f); + } + + + + static PxVec3 computeTetraBarycentricCoords(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& p3, const PxVec3& p) + { + float tetraVol = TetraVolume(p0, p1, p2, p3); + PX_ASSERT(tetraVol != 0.0f); + PxVec3 bary; + bary.x = TetraVolume(p, p1, p2, p3) / tetraVol; + bary.y = TetraVolume(p, p2, p0, p3) / tetraVol; + bary.z = TetraVolume(p, p3, p0, p1) / tetraVol; + /* + bary.x = PxAbs(bary.x); + bary.y = PxAbs(bary.y); + bary.z = PxAbs(bary.z); + */ + + return bary; + } + + + + static float barycentricDist(const PxVec3& barycentricCoords) + { + float dist = 0.0f; + if (-barycentricCoords.x > dist) + { + dist = -barycentricCoords.x; + } + if (-barycentricCoords.y > dist) + { + dist = -barycentricCoords.y; + } + if (-barycentricCoords.z > dist) + { + dist = -barycentricCoords.z; + } + float sum = barycentricCoords.x + barycentricCoords.y + barycentricCoords.z - 1.0f; + if (sum > dist) + { + dist = sum; + } + return dist; + } + + + static bool pointInTetra(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& p3, const PxVec3& p, const float epsilon) + { + PxVec3 n = (p1 - p0).cross(p2 - p0); + float s0 = n.dot(p - p0); + + n = (p2 - p0).cross(p3 - p0); + float s1 = n.dot(p - p0); + + n = (p3 - p0).cross(p1 - p0); + float s2 = n.dot(p - p0); + + n = (p3 - p1).cross(p2 - p1); + float s3 = n.dot(p - p1); + + return ((s0 >= -epsilon) && (s1 >= -epsilon) && (s2 >= -epsilon) && (s3 >= -epsilon)) || + ((s0 <= epsilon) && (s1 <= epsilon) && (s2 <= epsilon) && (s3 <= epsilon)); + } + + + + struct Ellipsoid + { + PxVec3 center; + PxVec3 xAxis; // contains scale + PxVec3 yAxis; // contains scale + PxVec3 zAxis; // contains scale + }; + + + + float GetWeight(float value, float maxValue) + { + float x = PxAbs(value) / PxAbs(maxValue); + if (value > 1.0) + { + value = 1.0; + } + if (value < 0.0) + { + value = 0.0; + } + //return cos(HalfPi*x)*cos(HalfPi*x); + return PxExp(-x * x * 4); + } + + + + // Returns a value 0 and 1 if inside, > 1 if outside + float GetEllipsoidDistance(const Ellipsoid& e, const PxVec3& p) + { +#define SQR(_x) ((_x)*(_x)) + + PxVec3 cp = p - e.center; + float xProj = cp.dot(e.xAxis); + float yProj = cp.dot(e.yAxis); + float zProj = cp.dot(e.zAxis); + + return sqrtf(SQR(xProj) + SQR(yProj) + SQR(zProj)); + } + + + + float GetEllipsoidWeight(const Ellipsoid& e, const PxVec3& p) + { + float d = GetEllipsoidDistance(e, p); + return GetWeight(d, 1.0f); + } + + + + static PxVec3 projectOnPlane(const PxVec3& planeNormal, float planeD, const PxVec3& point, const PxVec3& projectionDirection) + { + // from http://en.wikipedia.org/wiki/Line-plane_intersection + + //t={-d-\mathbf{l}_a\cdot\mathbf{n} \over (\mathbf{l}_b-\mathbf{l}_a)\cdot\mathbf{n}}. + + const float nominator = -planeD - point.dot(planeNormal); + const float denominator = projectionDirection.dot(planeNormal); + + if (denominator != 0.0f) + { + const float t = nominator / denominator; + + return point + t * projectionDirection; + } + + return point; + } + + + /** + Given target point p and triangle with points p1, p2, p3 and normals n1, n2, n3. + Find barycentric coordinates (x, y, z) and height h, such that z = 1-x-y and + x*p1+y*p2+z*p3 + (x*n1+y*p2+y*p3)*h + + This means finding a point on the triangle where the interpolated normal goes through the target point. + FinalBary will contain (x, y, h). + */ + static bool iterateOnTriangle(uint32_t maxIterations, float convergenceThreshold, const TriangleWithNormals& triangle, const PxVec3& position, + PxVec3& finalBary) + { + uint32_t iteration = 0; + + PxVec3 planeNormal = (triangle.vertices[1] - triangle.vertices[0]).cross(triangle.vertices[2] - triangle.vertices[0]); + planeNormal.normalize(); + const float planeD = -triangle.vertices[0].dot(planeNormal); + + // start with normal from the middle of the triangle + PxVec3 trustedBary(1.0f / 3.0f); + //PxVec3 tempNormal = planeNormal; + PxVec3 tempNormal = trustedBary.x * triangle.normals[0] + trustedBary.y * triangle.normals[1] + trustedBary.z * triangle.normals[2]; + + float baryConvergence = PX_MAX_F32; + + float trust = 1.0f; + while (iteration++ < maxIterations) + { + PxVec3 tempPosOnTriangle = projectOnPlane(planeNormal, planeD, position, tempNormal); + PxVec3 tempBary = ModuleClothingHelpers::computeTriBarycentricCoords<false>(triangle.vertices[0], triangle.vertices[1], triangle.vertices[2], tempPosOnTriangle); + + // projectOnPlane should lead to position on plane + if (PxAbs(tempBary.z) > 0.001f) + { + return false; + } + + // compute real bary coordinate + tempBary.z = 1.0f - tempBary.x - tempBary.y; + + const PxVec3 errorVec = tempBary - trustedBary; + baryConvergence = errorVec.dot(errorVec); + + // average bary coordinate, graded sum of new values + trustedBary *= (1.0f - trust); + trustedBary += trust * tempBary; + + // do we still maintain the barycentric invariant? + if (PxAbs(1.0f - (trustedBary.x + trustedBary.y + trustedBary.z)) > 0.001f) + { + return false; + } + + // find new normal + tempNormal = trustedBary.x * triangle.normals[0] + trustedBary.y * triangle.normals[1] + trustedBary.z * triangle.normals[2]; + + if (baryConvergence < convergenceThreshold) + { + break; + } + + trust *= 0.8f; + } + + if (baryConvergence < convergenceThreshold) + { + const PxVec3 posOnTriangle = trustedBary.x * triangle.vertices[0] + trustedBary.y * triangle.vertices[1] + trustedBary.z * triangle.vertices[2]; + + const float length = tempNormal.normalize(); + float avgHeight = tempNormal.dot(position - posOnTriangle) / length; + + finalBary = trustedBary; + finalBary.z = avgHeight; + + return true; + } + + return false; + } + + + + static void computeTriangleBarys(TriangleWithNormals& triangle, const PxVec3& position, const PxVec3& normal, const PxVec3& tangent, float thickness, int32_t timeStamp, bool onlyTangent) + { + // todo PH: assert is finite! + + if (triangle.timestamp < timeStamp) + { + triangle.valid = 0; + + // This is the value that is used when nothing is hit + triangle.tempBaryVertex = PxVec3(PX_MAX_F32); + triangle.tempBaryNormal = PxVec3(PX_MAX_F32); + triangle.tempBaryTangent = PxVec3(PX_MAX_F32); + + PxVec3 positionBary(PX_MAX_F32); + PxVec3 normalBary(PX_MAX_F32); + PxVec3 tangentBary(PX_MAX_F32); + + if (onlyTangent || iterateOnTriangle(100, 0.0001f, triangle, position, positionBary)) + { + triangle.valid = 1; + + triangle.tempBaryVertex = positionBary; + PX_ASSERT(triangle.tempBaryVertex.isFinite()); + + if (!normal.isZero()) + { + if (iterateOnTriangle(100, 0.0001f, triangle, normal, normalBary)) + { + triangle.valid = 2; + + triangle.tempBaryNormal = normalBary; + PX_ASSERT(triangle.tempBaryNormal.isFinite()); + } + else + { + PxVec3 newNormal = normal; + for (uint32_t iter = 0; iter < 10; iter++) + { + // changing normal size might help + newNormal = (position + newNormal) / 2.0f; + if (iterateOnTriangle(10, 0.0001f, triangle, newNormal, normalBary)) + { + triangle.valid = 2; + + triangle.tempBaryNormal = normalBary; + PX_ASSERT(triangle.tempBaryNormal.isFinite()); + + break; + } + } + if (triangle.valid != 2) + { + // now this is a really low approximation, but still better than nothing + triangle.tempBaryNormal = triangle.tempBaryVertex; + triangle.tempBaryNormal.z += thickness; + } + } + } + + if (!tangent.isZero()) + { + if(iterateOnTriangle(1, PX_MAX_F32, triangle, tangent, tangentBary)) + { + triangle.tempBaryTangent = tangentBary; + PX_ASSERT(triangle.tempBaryTangent.isFinite()); + } + } + } + + triangle.timestamp = timeStamp; + } + } +}; + +} +} // namespace nvidia + +#endif // MODULE_CLOTHING_HELPERS_H |