Browse Source

Added PhysicsWorld::RaycastSingleSegmented(). It's much more efficient for big scenes with many bodies in them.

Enhex 10 years ago
parent
commit
e57cc88020

+ 9 - 1
Source/Urho3D/AngelScript/PhysicsAPI.cpp

@@ -242,6 +242,13 @@ static PhysicsRaycastResult PhysicsWorldRaycastSingle(const Ray& ray, float maxD
     return result;
 }
 
+static PhysicsRaycastResult PhysicsWorldRaycastSingleSegmented(const Ray& ray, float maxDistance, float segmentDistance, unsigned collisionMask, PhysicsWorld* ptr)
+{
+	PhysicsRaycastResult result;
+	ptr->RaycastSingleSegmented(result, ray, maxDistance, segmentDistance, collisionMask);
+	return result;
+}
+
 static PhysicsRaycastResult PhysicsWorldSphereCast(const Ray& ray, float radius, float maxDistance, unsigned collisionMask, PhysicsWorld* ptr)
 {
     PhysicsRaycastResult result;
@@ -302,7 +309,8 @@ static void RegisterPhysicsWorld(asIScriptEngine* engine)
     engine->RegisterObjectMethod("PhysicsWorld", "void Update(float)", asMETHOD(PhysicsWorld, Update), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void UpdateCollisions()", asMETHOD(PhysicsWorld, UpdateCollisions), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "Array<PhysicsRaycastResult>@ Raycast(const Ray&in, float, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycast), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult RaycastSingle(const Ray&in, float, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycastSingle), asCALL_CDECL_OBJLAST);
+	engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult RaycastSingle(const Ray&in, float, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycastSingle), asCALL_CDECL_OBJLAST);
+	engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult RaycastSingleSegmented(const Ray&in, float, float, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycastSingleSegmented), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult SphereCast(const Ray&in, float, float, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldSphereCast), asCALL_CDECL_OBJLAST);
     // There seems to be a bug in AngelScript resulting in a crash if we use an auto handle with this function.
     // Work around by manually releasing the CollisionShape handle

+ 47 - 0
Source/Urho3D/Physics/PhysicsWorld.cpp

@@ -406,6 +406,53 @@ void PhysicsWorld::RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, f
     }
 }
 
+void PhysicsWorld::RaycastSingleSegmented(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, float segmentDistance, unsigned collisionMask)
+{
+	URHO3D_PROFILE(PhysicsRaycastSingleSegmented);
+
+	if (maxDistance >= M_INFINITY)
+		URHO3D_LOGWARNING("Infinite maxDistance in physics raycast is not supported");
+
+	btVector3 start = ToBtVector3(ray.origin_);
+	btVector3 end;
+	btVector3 direction = ToBtVector3(ray.direction_);
+	float distance;
+
+	for (float remainingDistance = maxDistance; remainingDistance > 0; remainingDistance -= segmentDistance)
+	{
+		distance = Min(remainingDistance, segmentDistance);
+
+		end = start + distance * direction;
+
+		btCollisionWorld::ClosestRayResultCallback
+			rayCallback(start, end);
+		rayCallback.m_collisionFilterGroup = (short)0xffff;
+		rayCallback.m_collisionFilterMask = (short)collisionMask;
+
+		world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);
+
+		if (rayCallback.hasHit())
+		{
+			result.position_ = ToVector3(rayCallback.m_hitPointWorld);
+			result.normal_ = ToVector3(rayCallback.m_hitNormalWorld);
+			result.distance_ = (result.position_ - ray.origin_).Length();
+			result.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObject->getUserPointer());
+
+			// No need to cast the rest of the segments
+			return;
+		}
+
+		// Use the end position as the new start position
+		start = end;
+	}
+
+	// Didn't hit anything
+	result.position_ = Vector3::ZERO;
+	result.normal_ = Vector3::ZERO;
+	result.distance_ = M_INFINITY;
+	result.body_ = 0;
+}
+
 void PhysicsWorld::SphereCast(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask)
 {
     URHO3D_PROFILE(PhysicsSphereCast);

+ 2 - 0
Source/Urho3D/Physics/PhysicsWorld.h

@@ -160,6 +160,8 @@ public:
         (PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
     /// Perform a physics world raycast and return the closest hit.
     void RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
+	/// Perform a physics world segmented raycast and return the closest hit. Useful for big scenes with many bodies.
+	void RaycastSingleSegmented(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, float segmentDistance, unsigned collisionMask = M_MAX_UNSIGNED);
     /// Perform a physics world swept sphere test and return the closest hit.
     void SphereCast
         (PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);