summaryrefslogtreecommitdiff
path: root/Runtime/Dynamics/PhysXRaycast.cpp
blob: 8e16d02ed1cf4614b665cf41fe89e4de1a034760 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include "UnityPrefix.h"
#if ENABLE_PHYSICS
#include "PhysXRaycast.h"

#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
#include "Runtime/BaseClasses/GameObject.h"
#include "Runtime/Dynamics/Collider.h"
#include "Runtime/Dynamics/PhysicsManager.h"
#include "Runtime/Dynamics/RigidBody.h"
#include "Runtime/Geometry/Intersection.h"
#include "Runtime/Geometry/AABB.h"
#include "Runtime/Geometry/Plane.h"
#include <limits>

// a small value to expand the shape AABBs with in order to make sure there are not boundary issues when broadphase culling the raycasts
static const float kAABBEpsilon	= 0.00001f;
static const float kEpsilon		= 0.000001f;

void PhysXRaycast::InitializeClass ()
{
	SetRaycastInterface (new PhysXRaycast ());
}

void PhysXRaycast::CleanupClass ()
{
	PhysXRaycast* raycast = reinterpret_cast<PhysXRaycast*> (GetRaycastInterface ());
	delete raycast;
	SetRaycastInterface (NULL);
}

static inline int GetAttachedRigidbodyOrColliderInstanceID (Collider& collider)
{
	Rigidbody* body = collider.GetRigidbody ();
	if (body)
		return body->GetInstanceID();
	else
		return collider.GetInstanceID();
}

bool PhysXRaycast::Raycast (const Ray& ray, float distance, int mask, HitInfo& output)
{
	RaycastHit hit;
	bool result = GetPhysicsManager().Raycast(ray,distance,hit,mask);
    if (!result)
        return false;
    
	output.intersection = hit.point;
	output.normal = hit.normal;
	output.colliderInstanceID = hit.collider->GetInstanceID();
	output.rigidBodyOrColliderInstanceID = GetAttachedRigidbodyOrColliderInstanceID (*hit.collider);

	return true;
}


// Cast a ray against a list of shapes shapes
bool PhysXRaycast::Intersect(const Ray& ray, float maxDist, NxShape** shapes, AABB* shapeBounds, size_t shapeCount, HitInfo& hit) const
{
	Vector3f halfDir = ray.GetDirection() * maxDist * 0.5;
	AABB rayBounds(ray.GetOrigin() + halfDir, Abs(halfDir));
	rayBounds.Expand(kAABBEpsilon);
	NxRay physicsRay ((const NxVec3&)ray.GetOrigin(), (const NxVec3&)ray.GetDirection());

	NxRaycastHit nxhit; 
	NxU32 flags = NX_RAYCAST_IMPACT | NX_RAYCAST_NORMAL;

	Vector3f collisionPos;
	Vector3f collisionNormal;
	float collisionSqrDist = std::numeric_limits<float>::infinity ();

	for (size_t s = 0; s < shapeCount; ++s)
	{
		if (!IntersectAABBAABBInclusive(rayBounds, shapeBounds[s]))
			continue;

		NxShape* shape = shapes[s];
		if (!shape) continue;

#if ENABLE_MULTITHREADED_CODE
		// We shouldn't need to do this. But it turns out that sometimes raycast will return a duff intersection normal when
		// called from multiple threads intersection the same object at the same time, this causes graphical functional test
		// 267 to fail for instance. Jesper has contacted NVidia about this.
		SimpleLock::AutoLock lock(m_Lock);
#endif

		if (shape->raycast (physicsRay, maxDist, flags, nxhit, false))
		{
			Vector3f intersectionPoint = (const Vector3f&)nxhit.worldImpact;
			float sqrDistance = SqrMagnitude(ray.GetOrigin() - intersectionPoint);

			if (sqrDistance < collisionSqrDist)
			{
				Collider* collider = (Collider*)shape->userData;
				if (!collider || collider->GetIsTrigger ())
					continue;
				hit.colliderInstanceID = collider->GetInstanceID ();
				hit.rigidBodyOrColliderInstanceID = GetAttachedRigidbodyOrColliderInstanceID (*collider);
				collisionSqrDist = sqrDistance;
				hit.intersection = (const Vector3f&)nxhit.worldImpact;
				hit.normal = (const Vector3f&)nxhit.worldNormal;												
			}
		}
	}

	return collisionSqrDist < std::numeric_limits<float>::infinity ();
}

// Should probably be a param
enum { kMaxParticleCollisionShapes = 256 };

// Determines which shapes overlap the particle system and caches the bounding boxes of those shapes
size_t PhysXRaycast::BroadPhaseCulling(dynamic_array<NxShape*>& nxShapes, dynamic_array<AABB>& aabbs, const MinMaxAABB& particleSystemAABB, const int filter, bool staticOnly) const
{
	size_t shapeCount = GetShapes(particleSystemAABB, kMaxParticleCollisionShapes, nxShapes.data(), filter, staticOnly);
	if (shapeCount == 0)
		return 0;

	// Cache bounds for all shapes
	for (int i = 0; i < shapeCount; ++i)
	{
		GetAABB( aabbs[i], *nxShapes[i] );
	}
	return shapeCount;
}

size_t PhysXRaycast::BatchIntersect( const dynamic_array<BatchedRaycast>& raycasts, dynamic_array<BatchedRaycastResult>& results, const UInt32 filter, bool staticOnly ) const
{
	// Get new aabb for particle system
	MinMaxAABB aabb = ComputeBatchAABB(raycasts);
	aabb.Expand(kAABBEpsilon);
	
	// Get all shapes within bounds (aabb)
	dynamic_array<NxShape*> nxShapes(kMaxParticleCollisionShapes, kMemTempAlloc);
	dynamic_array<AABB> aabbs(kMaxParticleCollisionShapes, kMemTempAlloc);
	
	size_t shapeCount = BroadPhaseCulling( nxShapes, aabbs, aabb, filter, staticOnly );

	if (shapeCount == 0)
		return 0;
	
	// trace rays against the shapes
	size_t numIntersections = 0;
	for (size_t r = 0; r < raycasts.size(); r++)
	{
		const BatchedRaycast pointsRay = raycasts[r];

		// attempt to cull based on ray extent
		if ( !IntersectAny( pointsRay, aabbs.data(), shapeCount ) ) continue;

		// build actual ray - this is relatively expensive due to Magnitude that has a sqrt and a dot product
		// TODO: possibly do 4 rays at a time in SIMD
		const Vector3f displacement = pointsRay.to-pointsRay.from;
		float distance = Magnitude(displacement);
		if (distance <= kEpsilon)
			continue;
		const Vector3f direction = displacement / distance;
		const Ray ray(pointsRay.from, direction);

		// intersect the shapes with the ray
		HitInfo hit;
		const bool isHit = Intersect(ray, distance, nxShapes.data(), aabbs.data(), shapeCount, hit);
		if ( isHit )
		{
			results[numIntersections] = BatchedRaycastResult(r,hit);
			numIntersections++;
		}
	}
	
	return numIntersections;
}

// Perform PhysX broad phase culling
size_t PhysXRaycast::GetShapes( const AABB& bounds, int maxResult, NxShape** outShapes, UInt32 groupMask, bool staticOnly ) const
{
	Vector3f min = bounds.GetMin();
	Vector3f max = bounds.GetMax();

	NxBounds3 physicsBounds;
	physicsBounds.set((const NxVec3&)min, (const NxVec3&)max);

	return GetDynamicsScene().overlapAABBShapes (physicsBounds, ( staticOnly ? NX_STATIC_SHAPES : NX_ALL_SHAPES ), maxResult, outShapes, NULL, groupMask, NULL, false);
}

void PhysXRaycast::GetAABB( AABB& bounds, const NxShape& shape ) const
{
	NxBounds3 physicsBounds;
	shape.getWorldBounds(physicsBounds);
	NxVec3 center, extents;
	physicsBounds.getCenter(center);
	physicsBounds.getExtents(extents);
	bounds = AABB((const Vector3f&)center, (const Vector3f&)extents);
	bounds.Expand(kAABBEpsilon);
}

#endif //ENABLE_PHYSICS