mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-22 02:12:45 +01:00
HitTest class for use in area scanning
This commit is contained in:
parent
465f0ab056
commit
97609fcb5e
@ -49,6 +49,8 @@ set(RWENGINE_SOURCES
|
||||
|
||||
src/dynamics/CollisionInstance.cpp
|
||||
src/dynamics/CollisionInstance.hpp
|
||||
src/dynamics/HitTest.cpp
|
||||
src/dynamics/HitTest.hpp
|
||||
src/dynamics/RaycastCallbacks.hpp
|
||||
|
||||
src/engine/Animator.cpp
|
||||
|
64
rwengine/src/dynamics/HitTest.cpp
Normal file
64
rwengine/src/dynamics/HitTest.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "HitTest.hpp"
|
||||
#include <objects/GameObject.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4305 5033)
|
||||
#endif
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default : 4305 5033)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
HitTest::TestResult HitTestWorld(btDiscreteDynamicsWorld& world, btPairCachingGhostObject& tester)
|
||||
{
|
||||
world.addCollisionObject(&tester);
|
||||
|
||||
HitTest::TestResult result;
|
||||
result.reserve(static_cast<unsigned long>(tester.getNumOverlappingObjects()));
|
||||
|
||||
for (auto i = 0; i < tester.getNumOverlappingObjects(); ++i)
|
||||
{
|
||||
auto overlapping = tester.getOverlappingObject(i);
|
||||
|
||||
HitTest::Hit hit{};
|
||||
hit.body = overlapping;
|
||||
|
||||
if (auto object = static_cast<GameObject*>(overlapping->getUserPointer()))
|
||||
{
|
||||
hit.object = object;
|
||||
}
|
||||
|
||||
result.push_back(hit);
|
||||
}
|
||||
|
||||
world.removeCollisionObject(&tester);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
HitTest::TestResult HitTestWithShape(btDiscreteDynamicsWorld& world, btCollisionShape& shape,
|
||||
const glm::vec3& center, const glm::quat& rotation) {
|
||||
btPairCachingGhostObject ghost{};
|
||||
btTransform xform{};
|
||||
xform.setOrigin({center.x, center.y, center.z});
|
||||
xform.setRotation({rotation.x, rotation.y, rotation.z, rotation.w});
|
||||
ghost.setWorldTransform(xform);
|
||||
ghost.setCollisionShape(&shape);
|
||||
return HitTestWorld(world, ghost);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HitTest::TestResult HitTest::sphereTest(const glm::vec3& center, float radius) {
|
||||
btSphereShape sphere {radius};
|
||||
return HitTestWithShape(_world, sphere, center, {});
|
||||
}
|
||||
|
||||
HitTest::TestResult HitTest::boxTest(const glm::vec3 ¢er, const glm::vec3 &size, const glm::quat& rotation) {
|
||||
btBoxShape box {{size.x, size.y, size.z}};
|
||||
return HitTestWithShape(_world, box, center, rotation);
|
||||
}
|
39
rwengine/src/dynamics/HitTest.hpp
Normal file
39
rwengine/src/dynamics/HitTest.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef _RWENGINE_HITTEST_HPP_
|
||||
#define _RWENGINE_HITTEST_HPP_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class btDiscreteDynamicsWorld;
|
||||
class btCollisionObject;
|
||||
class GameObject;
|
||||
|
||||
/**
|
||||
* Utility for performing collision tests against the world.
|
||||
*/
|
||||
class HitTest {
|
||||
public:
|
||||
struct Hit {
|
||||
btCollisionObject* body;
|
||||
GameObject* object;
|
||||
};
|
||||
using TestResult = std::vector<Hit>;
|
||||
|
||||
explicit HitTest(btDiscreteDynamicsWorld& world)
|
||||
: _world(world)
|
||||
{}
|
||||
|
||||
~HitTest() = default;
|
||||
|
||||
TestResult sphereTest(const glm::vec3& center, float radius);
|
||||
TestResult boxTest(const glm::vec3& center, const glm::vec3& size, const glm::quat& rotation = {1.f, 0.f, 0.f, 0.f});
|
||||
|
||||
private:
|
||||
btDiscreteDynamicsWorld& _world;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -11,6 +11,7 @@ set(TESTS
|
||||
GameData
|
||||
GameWorld
|
||||
Garage
|
||||
HitTest
|
||||
Input
|
||||
Items
|
||||
Lifetime
|
||||
|
91
tests/test_HitTest.cpp
Normal file
91
tests/test_HitTest.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_Globals.hpp"
|
||||
#include <dynamics/HitTest.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
#include <dynamics/CollisionInstance.hpp>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4305 5033)
|
||||
#endif
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default : 4305 5033)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct HitTestFixture {
|
||||
btDefaultCollisionConfiguration collisionConfig;
|
||||
btCollisionDispatcher collisionDispatcher;
|
||||
btDbvtBroadphase broadphase;
|
||||
btGhostPairCallback overlappingPairCallback;
|
||||
btSequentialImpulseConstraintSolver solver;
|
||||
btDiscreteDynamicsWorld dynamicsWorld;
|
||||
HitTest hitTest;
|
||||
|
||||
HitTestFixture()
|
||||
: collisionDispatcher{&collisionConfig}
|
||||
, dynamicsWorld{&collisionDispatcher, &broadphase, &solver, &collisionConfig}
|
||||
, hitTest{dynamicsWorld}
|
||||
{
|
||||
broadphase.getOverlappingPairCache()->setInternalGhostPairCallback(
|
||||
&overlappingPairCallback);
|
||||
}
|
||||
};
|
||||
|
||||
struct WithSphere : public HitTestFixture {
|
||||
btSphereShape shape {0.5f};
|
||||
std::unique_ptr<btRigidBody> target;
|
||||
GameObject* object {reinterpret_cast<GameObject*>(0xDEADBEEF)};
|
||||
|
||||
WithSphere()
|
||||
{
|
||||
btDefaultMotionState ms;
|
||||
btRigidBody::btRigidBodyConstructionInfo info {1.f, &ms, &shape};
|
||||
target = std::make_unique<btRigidBody>(info);
|
||||
target->setUserPointer(object);
|
||||
dynamicsWorld.addRigidBody(target.get());
|
||||
}
|
||||
|
||||
~WithSphere() {
|
||||
dynamicsWorld.removeRigidBody(target.get());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(HitTestTests)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(test_creation, HitTestFixture) {
|
||||
HitTest test {dynamicsWorld};
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(sphereTest_returns_result, WithSphere) {
|
||||
const auto result = hitTest.sphereTest({0.f, 0.f, 0.f}, 1.f);
|
||||
BOOST_CHECK_EQUAL(result.size(), 1);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(boxTest_returns_result, WithSphere) {
|
||||
auto sphereBoundingBoxEdge = glm::vec3{ 0.f, 0.f, shape.getRadius() };
|
||||
const auto result = hitTest.boxTest(sphereBoundingBoxEdge, {0.01f, 0.01f, 0.01f});
|
||||
BOOST_CHECK_EQUAL(result.size(), 1);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(non_overlapping_test_returns_nothing, WithSphere) {
|
||||
auto sphereBoundingBoxEdge = glm::vec3{ shape.getRadius() };
|
||||
const auto result = hitTest.boxTest(sphereBoundingBoxEdge * 2.f, {0.01f, 0.01f, 0.01f});
|
||||
BOOST_CHECK(result.empty());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(result_contains_body, WithSphere) {
|
||||
const auto result = hitTest.sphereTest({0.f, 0.f, 0.f}, 1.f);
|
||||
BOOST_ASSERT(result.size() == 1);
|
||||
BOOST_CHECK_EQUAL(result[0].body, target.get());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(test_result_contains_object, WithSphere) {
|
||||
const auto result = hitTest.sphereTest({0.f, 0.f, 0.f}, 1.f);
|
||||
BOOST_ASSERT(result.size() == 1);
|
||||
BOOST_CHECK_EQUAL(result[0].object, object);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue
Block a user