RavEngine
Loading...
Searching...
No Matches
CmConeLimitHelper.h
1// Redistribution and use in source and binary forms, with or without
2// modification, are permitted provided that the following conditions
3// are met:
4// * Redistributions of source code must retain the above copyright
5// notice, this list of conditions and the following disclaimer.
6// * Redistributions in binary form must reproduce the above copyright
7// notice, this list of conditions and the following disclaimer in the
8// documentation and/or other materials provided with the distribution.
9// * Neither the name of NVIDIA CORPORATION nor the names of its
10// contributors may be used to endorse or promote products derived
11// from this software without specific prior written permission.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
14// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24//
25// Copyright (c) 2008-2022 NVIDIA Corporation. All rights reserved.
26// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
27// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
28
29#ifndef CM_CONE_LIMIT_HELPER_H
30#define CM_CONE_LIMIT_HELPER_H
31
32// This class contains methods for supporting the tan-quarter swing limit - that
33// is the, ellipse defined by tanQ(theta)^2/tanQ(thetaMax)^2 + tanQ(phi)^2/tanQ(phiMax)^2 = 1
34//
35// Angles are passed as an PxVec3 swing vector with x = 0 and y and z the swing angles
36// around the y and z axes
37
38#include "foundation/PxMathUtils.h"
39
40namespace physx
41{
42namespace Cm
43{
44 PX_FORCE_INLINE PxReal tanAdd(PxReal tan1, PxReal tan2)
45 {
46 PX_ASSERT(PxAbs(1-tan1*tan2)>1e-6f);
47 return (tan1+tan2)/(1-tan1*tan2);
48 }
49
50 PX_FORCE_INLINE float computeAxisAndError(const PxVec3& r, const PxVec3& d, const PxVec3& twistAxis, PxVec3& axis)
51 {
52 // the point on the cone defined by the tanQ swing vector r
53 // this code is equal to quatFromTanQVector(r).rotate(PxVec3(1.0f, 0.0f, 0.0f);
54 PxVec3 p(1.f,0,0);
55 PxReal r2 = r.dot(r), a = 1-r2, b = 1/(1+r2), b2 = b*b;
56 PxReal v1 = 2*a*b2;
57 PxVec3 v2(a, 2*r.z, -2*r.y); // a*p + 2*r.cross(p);
58 PxVec3 coneLine = v1 * v2 - p; // already normalized
59
60 // the derivative of coneLine in the direction d
61 PxReal rd = r.dot(d);
62 PxReal dv1 = -4*rd*(3-r2)*b2*b;
63 PxVec3 dv2(-2*rd, 2*d.z, -2*d.y);
64
65 PxVec3 coneNormal = v1 * dv2 + dv1 * v2;
66
67 axis = coneLine.cross(coneNormal)/coneNormal.magnitude();
68 return coneLine.cross(axis).dot(twistAxis);
69 }
70
71 // this is here because it's used in both LL and Extensions. However, it
72 // should STAY IN THE SDK CODE BASE because it's SDK-specific
73
75 {
76 public:
77 ConeLimitHelper(PxReal tanQSwingY, PxReal tanQSwingZ, PxReal tanQPadding)
78 : mTanQYMax(tanQSwingY), mTanQZMax(tanQSwingZ), mTanQPadding(tanQPadding) {}
79
80 // whether the point is inside the (inwardly) padded cone - if it is, there's no limit
81 // constraint
82
83 PX_FORCE_INLINE bool contains(const PxVec3& tanQSwing) const
84 {
85 PxReal tanQSwingYPadded = tanAdd(PxAbs(tanQSwing.y),mTanQPadding);
86 PxReal tanQSwingZPadded = tanAdd(PxAbs(tanQSwing.z),mTanQPadding);
87 return PxSqr(tanQSwingYPadded/mTanQYMax)+PxSqr(tanQSwingZPadded/mTanQZMax) <= 1;
88 }
89
90 PX_FORCE_INLINE PxVec3 clamp(const PxVec3& tanQSwing, PxVec3& normal) const
91 {
92 PxVec3 p = PxEllipseClamp(tanQSwing, PxVec3(0,mTanQYMax,mTanQZMax));
93 normal = PxVec3(0, p.y/PxSqr(mTanQYMax), p.z/PxSqr(mTanQZMax));
94#ifdef PX_PARANOIA_ELLIPSE_CHECK
95 PxReal err = PxAbs(PxSqr(p.y/mTanQYMax) + PxSqr(p.z/mTanQZMax) - 1);
96 PX_ASSERT(err<1e-3);
97#endif
98 return p;
99 }
100
101 // input is a swing quat, such that swing.x = twist.y = twist.z = 0, q = swing * twist
102 // The routine is agnostic to the sign of q.w (i.e. we don't need the minimal-rotation swing)
103
104 // output is an axis such that positive rotation increases the angle outward from the
105 // limit (i.e. the image of the x axis), the error is the sine of the angular difference,
106 // positive if the twist axis is inside the cone
107
108 bool getLimit(const PxQuat& swing, PxVec3& axis, PxReal& error) const
109 {
110 PX_ASSERT(swing.w>0);
111 PxVec3 twistAxis = swing.getBasisVector0();
112 PxVec3 tanQSwing = PxVec3(0, PxTanHalf(swing.z,swing.w), -PxTanHalf(swing.y,swing.w));
113 if(contains(tanQSwing))
114 return false;
115
116 PxVec3 normal, clamped = clamp(tanQSwing, normal);
117
118 // rotation vector and ellipse normal
119 PxVec3 r(0,-clamped.z,clamped.y), d(0, -normal.z, normal.y);
120
121 error = computeAxisAndError(r, d, twistAxis, axis);
122
123 PX_ASSERT(PxAbs(axis.magnitude()-1)<1e-5f);
124
125#ifdef PX_PARANOIA_ELLIPSE_CHECK
126 bool inside = PxSqr(tanQSwing.y/mTanQYMax) + PxSqr(tanQSwing.z/mTanQZMax) <= 1;
127 PX_ASSERT(inside && error>-1e-4f || !inside && error<1e-4f);
128#endif
129 return true;
130 }
131
132 private:
133
134
135 PxReal mTanQYMax, mTanQZMax, mTanQPadding;
136 };
137
139 {
140 public:
141 ConeLimitHelperTanLess(PxReal swingY, PxReal swingZ, PxReal padding)
142 : mYMax(swingY), mZMax(swingZ), mPadding(padding) {}
143
144 // whether the point is inside the (inwardly) padded cone - if it is, there's no limit
145 // constraint
146 PX_FORCE_INLINE bool contains(const PxVec3& swing) const
147 {
148 // padded current swing angles
149 PxReal swingYPadded = PxAbs(swing.y) + mPadding;
150 PxReal swingZPadded = PxAbs(swing.z) + mPadding;
151 // if angle is within ellipse defined by mYMax/mZMax
152 return PxSqr(swingYPadded/mYMax)+PxSqr(swingZPadded/mZMax) <= 1;
153 }
154
155 PX_FORCE_INLINE PxVec3 clamp(const PxVec3& swing, PxVec3& normal) const
156 {
157 // finds the closest point on the ellipse to a given point
158 PxVec3 p = PxEllipseClamp(swing, PxVec3(0,mYMax,mZMax));
159 // normal to the point on ellipse
160 normal = PxVec3(0, p.y/PxSqr(mYMax), p.z/PxSqr(mZMax));
161#ifdef PX_PARANOIA_ELLIPSE_CHECK
162 PxReal err = PxAbs(PxSqr(p.y/mYMax) + PxSqr(p.z/mZMax) - 1);
163 PX_ASSERT(err<1e-3);
164#endif
165 return p;
166 }
167
168 // input is a swing quat, such that swing.x = twist.y = twist.z = 0, q = swing * twist
169 // The routine is agnostic to the sign of q.w (i.e. we don't need the minimal-rotation swing)
170
171 // output is an axis such that positive rotation increases the angle outward from the
172 // limit (i.e. the image of the x axis), the error is the sine of the angular difference,
173 // positive if the twist axis is inside the cone
174
175 bool getLimit(const PxQuat& swing, PxVec3& axis, PxReal& error) const
176 {
177 PX_ASSERT(swing.w>0);
178 PxVec3 twistAxis = swing.getBasisVector0();
179 // get the angles from the swing quaternion
180 PxVec3 swingAngle(0.0f, 4 * PxAtan2(swing.y, 1 + swing.w), 4 * PxAtan2(swing.z, 1 + swing.w));
181 if(contains(swingAngle))
182 return false;
183
184 PxVec3 normal, clamped = clamp(swingAngle, normal);
185
186 // rotation vector and ellipse normal
187 PxVec3 r(0,PxTan(clamped.y/4),PxTan(clamped.z/4)), d(0, normal.y, normal.z);
188
189 error = computeAxisAndError(r, d, twistAxis, axis);
190
191 PX_ASSERT(PxAbs(axis.magnitude()-1)<1e-5f);
192
193 return true;
194 }
195
196 private:
197 PxReal mYMax, mZMax, mPadding;
198 };
199
200} // namespace Cm
201
202}
203
204#endif
Definition CmConeLimitHelper.h:139
Definition CmConeLimitHelper.h:75
This is a quaternion class. For more information on quaternion mathematics consult a mathematics sour...
Definition PxQuat.h:50
PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getBasisVector0() const
Definition PxQuat.h:261
3 Element vector class.
Definition PxVec3.h:50
#define PX_FORCE_INLINE
Definition PxPreprocessor.h:335
Sorts an array of objects in ascending order, assuming that the predicate implements the < operator:
Definition PxBoxController.h:39
PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAtan2(float x, float y)
Arctangent of (x/y) with correct sign. Returns angle between -PI and PI in radians Unit: Radians.
Definition PxMath.h:302
PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal PxTanHalf(PxReal sin, PxReal cos)
Compute tan(theta/2) given sin(theta) and cos(theta) as inputs.
Definition PxMathUtils.h:160
PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAbs(float a)
abs returns the absolute value of its argument.
Definition PxMath.h:109
PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 PxSqr(const PxF32 a)
square of the argument
Definition PxMath.h:170
PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxEllipseClamp(const PxVec3 &point, const PxVec3 &radii)
Compute the closest point on an 2d ellipse to a given 2d point.
Definition PxMathUtils.h:178
PX_CUDA_CALLABLE PX_FORCE_INLINE float PxTan(float a)
Tangent of an angle. Unit: Radians.
Definition PxMath.h:223