Procházet zdrojové kódy

Ray cast issues (#47)

- Added documentation for ray casts
- Added RayCast::GetPointOnRay
Jorrit Rouwe před 3 roky
rodič
revize
3612150b1b

+ 5 - 2
Jolt/Physics/Collision/NarrowPhaseQuery.h

@@ -24,10 +24,13 @@ public:
 								NarrowPhaseQuery() = default;
 								NarrowPhaseQuery(BodyLockInterface &inBodyLockInterface, BroadPhase &inBroadPhase) : mBodyLockInterface(&inBodyLockInterface), mBroadPhase(&inBroadPhase) { }
 
-	/// Cast a ray, returns true if it finds a hit closer than ioHit.mFraction and updates ioHit in that case.
+	/// Cast a ray and find the closest hit. Returns true if it finds a hit. Hits further than ioHit.mFraction will not be considered and in this case ioHit will remain unmodified (and the function will return false).
+	/// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits against triangles are returned.
+	/// If you want the surface normal of the hit use Body::GetWorldSpaceSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)) on body with ID ioHit.mBodyID.
 	bool						CastRay(const RayCast &inRay, RayCastResult &ioHit, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }) const;
 
-	/// Cast a ray, allows collecting multiple hits
+	/// Cast a ray, allows collecting multiple hits. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit.
+	/// If you want the surface normal of the hit use Body::GetWorldSpaceSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected fraction)) on body with collected body ID.
 	void						CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter = { }, const ObjectLayerFilter &inObjectLayerFilter = { }, const BodyFilter &inBodyFilter = { }) const;
 
 	/// Check if inPoint is inside any shapes. For this tests all shapes are treated as if they were solid. 

+ 6 - 0
Jolt/Physics/Collision/RayCast.h

@@ -18,6 +18,12 @@ struct RayCast
 		return { ray_origin, ray_direction };
 	}
 
+	/// Get point with fraction inFraction on ray (0 = start of ray, 1 = end of ray)
+	inline Vec3					GetPointOnRay(float inFraction) const
+	{
+		return mOrigin + inFraction * mDirection;
+	}
+
 	Vec3						mOrigin;					///< Origin of the ray
 	Vec3						mDirection;					///< Direction and length of the ray (anything beyond this length will not be reported as a hit)
 };

+ 4 - 2
Jolt/Physics/Collision/Shape/Shape.h

@@ -236,10 +236,12 @@ public:
 
 	/// Cast a ray against this shape, returns true if it finds a hit closer than ioHit.mFraction and updates that fraction. Otherwise ioHit is left untouched and the function returns false.
 	/// Note that the ray should be relative to the center of mass of this shape (i.e. subtract Shape::GetCenterOfMass() from RayCast::mOrigin if you want to cast against the shape in the space it was created).
-	/// If this is a solid shape and the ray starts inside the object, the returned fraction will be 0.
+	/// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits against triangles are returned.
+	/// If you want the surface normal of the hit use GetSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)).
 	virtual bool					CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const = 0;
 
-	/// Cast a ray against this shape. Allows returning multiple hits through ioCollector.
+	/// Cast a ray against this shape. Allows returning multiple hits through ioCollector. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit.
+	/// If you want the surface normal of the hit use GetSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected faction)).
 	virtual void					CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector) const = 0;
 
 	/// Check if inPoint is inside this shape. For this tests all shapes are treated as if they were solid. 

+ 5 - 2
Jolt/Physics/Collision/TransformedShape.h

@@ -27,10 +27,13 @@ public:
 								TransformedShape() = default;
 								TransformedShape(Vec3Arg inPositionCOM, QuatArg inRotation, const Shape *inShape, const BodyID &inBodyID, const SubShapeIDCreator &inSubShapeIDCreator = SubShapeIDCreator()) : mShapePositionCOM(inPositionCOM), mShapeRotation(inRotation), mShape(inShape), mBodyID(inBodyID), mSubShapeIDCreator(inSubShapeIDCreator) { }
 
-	/// Cast a ray, returns true if it finds a hit closer than ioHit.mFraction and updates ioHit in that case.
+	/// Cast a ray and find the closest hit. Returns true if it finds a hit. Hits further than ioHit.mFraction will not be considered and in this case ioHit will remain unmodified (and the function will return false).
+	/// Convex objects will be treated as solid (meaning if the ray starts inside, you'll get a hit fraction of 0) and back face hits are returned.
+	/// If you want the surface normal of the hit use GetWorldSpaceSurfaceNormal(ioHit.mSubShapeID2, inRay.GetPointOnRay(ioHit.mFraction)) on this object.
 	bool						CastRay(const RayCast &inRay, RayCastResult &ioHit) const;
 
-	/// Cast a ray, allows collecting multiple hits
+	/// Cast a ray, allows collecting multiple hits. Note that this version is more flexible but also slightly slower than the CastRay function that returns only a single hit.
+	/// If you want the surface normal of the hit use GetWorldSpaceSurfaceNormal(collected sub shape ID, inRay.GetPointOnRay(collected fraction)) on this object.
 	void						CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector) const;
 
 	/// Check if inPoint is inside any shapes. For this tests all shapes are treated as if they were solid. 

+ 1 - 1
Jolt/Physics/Vehicle/VehicleCollisionTester.cpp

@@ -43,7 +43,7 @@ bool VehicleCollisionTesterRay::Collide(PhysicsSystem &inPhysicsSystem, uint inW
 				const Body *body = &lock.GetBody();
 
 				// Test that we're not hitting a vertical wall
-				Vec3 contact_pos = mRay.mOrigin + inResult.mFraction * mRay.mDirection;
+				Vec3 contact_pos = mRay.GetPointOnRay(inResult.mFraction);
 				Vec3 normal = body->GetWorldSpaceSurfaceNormal(inResult.mSubShapeID2, contact_pos);
 				if (normal.Dot(mUpDirection) > mCosMaxSlopeAngle)
 				{

+ 2 - 2
Samples/SamplesApp.cpp

@@ -853,7 +853,7 @@ bool SamplesApp::CastProbe(float inProbeLength, float &outFraction, Vec3 &outPos
 			had_hit = mPhysicsSystem->GetNarrowPhaseQuery().CastRay(ray, hit);
 
 			// Fill in results
-			outPosition = start + hit.mFraction * direction;
+			outPosition = ray.GetPointOnRay(hit.mFraction);
 			outFraction = hit.mFraction;
 			outID = hit.mBodyID;
 
@@ -943,7 +943,7 @@ bool SamplesApp::CastProbe(float inProbeLength, float &outFraction, Vec3 &outPos
 				for (const RayCastResult &hit : hits)
 				{
 					// Draw line
-					Vec3 position = start + hit.mFraction * direction;
+					Vec3 position = ray.GetPointOnRay(hit.mFraction);
 					mDebugRenderer->DrawLine(prev_position, position, c? Color::sGrey : Color::sWhite);
 					c = !c;
 					prev_position = position;

+ 1 - 1
UnitTests/Physics/TransformedShapeTests.cpp

@@ -89,7 +89,7 @@ TEST_SUITE("TransformedShapeTests")
 		CHECK_APPROX_EQUAL(hit.mFraction, 0.5f);
 		CHECK(hit.mBodyID == body.GetID());
 		CHECK(ts.GetMaterial(hit.mSubShapeID2) == material);
-		Vec3 world_space_normal = ts.GetWorldSpaceSurfaceNormal(hit.mSubShapeID2, ray_start_world + hit.mFraction * ray_direction_world);
+		Vec3 world_space_normal = ts.GetWorldSpaceSurfaceNormal(hit.mSubShapeID2, ray_in_world.GetPointOnRay(hit.mFraction));
 		Vec3 expected_normal = (calc_transform.GetDirectionPreservingMatrix() * normal_on_box).Normalized();
 		CHECK_APPROX_EQUAL(world_space_normal, expected_normal);