diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d4d782..c6b9984 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ $,-Og,-fno-delete-null-pointer-checks -fno-strict-overflow -f add_executable(UnitTest src/test.cpp) target_link_libraries(UnitTest SC_SeparatingAxisCollisionDetection) target_include_directories(UnitTest PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") +target_compile_features(UnitTest PUBLIC cxx_std_20) target_compile_options(UnitTest PUBLIC $,-Og,-fno-delete-null-pointer-checks -fno-strict-overflow -fno-strict-aliasing -ftrivial-auto-var-init=zero> diff --git a/src/sd_sacd.cpp b/src/sd_sacd.cpp index ebafe61..90e8e9d 100644 --- a/src/sd_sacd.cpp +++ b/src/sd_sacd.cpp @@ -2,6 +2,126 @@ // Standard library includes. #include +#include +#include + +// ============================================================================= +// Private Helpers BEGIN +// ============================================================================= + +SC_SACD_Vec3 operator+(const SC_SACD_Vec3 &a, const SC_SACD_Vec3 &b) { + return SC_SACD_Vec3{a.x + b.x, a.y + b.y, a.z + b.z}; +} + +std::vector SC_SACD_Get_Box_Normals( + const SC_SACD_Generic_Box *box) { + std::vector normals; + + normals.emplace_back(SC_SACD_Vec3{1.0F, 0.0F, 0.0F}); + normals.back() = SC_SACD_Vec3_Rotate(normals.back(), box->x_radians, + box->y_radians, box->z_radians); + + normals.emplace_back(SC_SACD_Vec3{0.0F, 1.0F, 0.0F}); + normals.back() = SC_SACD_Vec3_Rotate(normals.back(), box->x_radians, + box->y_radians, box->z_radians); + + normals.emplace_back(SC_SACD_Vec3{0.0F, 0.0F, 1.0F}); + normals.back() = SC_SACD_Vec3_Rotate(normals.back(), box->x_radians, + box->y_radians, box->z_radians); + + // Not normalizing the normals on purpose for optimization. (No unit vectors.) + return normals; +} + +std::vector SC_SACD_Get_Box_Corners( + const SC_SACD_Generic_Box *box) { + std::vector corners; + + SC_SACD_Vec3 pos{box->x, box->y, box->z}; + + corners.push_back( + SC_SACD_Vec3_Rotate(SC_SACD_Vec3{-box->width / 2.0F, -box->height / 2.0F, + -box->depth / 2.0F}, + box->x_radians, box->y_radians, box->z_radians) + + pos); + + corners.push_back( + SC_SACD_Vec3_Rotate(SC_SACD_Vec3{box->width / 2.0F, -box->height / 2.0F, + -box->depth / 2.0F}, + box->x_radians, box->y_radians, box->z_radians) + + pos); + + corners.push_back( + SC_SACD_Vec3_Rotate(SC_SACD_Vec3{-box->width / 2.0F, box->height / 2.0F, + -box->depth / 2.0F}, + box->x_radians, box->y_radians, box->z_radians) + + pos); + + corners.push_back( + SC_SACD_Vec3_Rotate(SC_SACD_Vec3{box->width / 2.0F, box->height / 2.0F, + -box->depth / 2.0F}, + box->x_radians, box->y_radians, box->z_radians) + + pos); + + corners.push_back( + SC_SACD_Vec3_Rotate(SC_SACD_Vec3{-box->width / 2.0F, -box->height / 2.0F, + box->depth / 2.0F}, + box->x_radians, box->y_radians, box->z_radians) + + pos); + + corners.push_back( + SC_SACD_Vec3_Rotate(SC_SACD_Vec3{box->width / 2.0F, -box->height / 2.0F, + box->depth / 2.0F}, + box->x_radians, box->y_radians, box->z_radians) + + pos); + + corners.push_back( + SC_SACD_Vec3_Rotate(SC_SACD_Vec3{-box->width / 2.0F, box->height / 2.0F, + box->depth / 2.0F}, + box->x_radians, box->y_radians, box->z_radians) + + pos); + + corners.push_back( + SC_SACD_Vec3_Rotate(SC_SACD_Vec3{box->width / 2.0F, box->height / 2.0F, + box->depth / 2.0F}, + box->x_radians, box->y_radians, box->z_radians) + + pos); + + return corners; +} + +struct SC_SACD_MinMax { + float min, max; +}; + +std::vector SC_SACD_Get_Box_MinMax( + const SC_SACD_Generic_Box *box, const std::vector &normals) { + std::vector minmaxes; + + std::vector corners = SC_SACD_Get_Box_Corners(box); + + // Assuming normals are not normalized, and will not normalize anyway. + // MinMax count should be same as normals count. + for (const auto &normal : normals) { + SC_SACD_MinMax minmax{INFINITY, -INFINITY}; + for (const auto &corner : corners) { + float projected = SC_SACD_Dot_Product(corner, normal); + if (projected > minmax.max) { + minmax.max = projected; + } + if (projected < minmax.min) { + minmax.min = projected; + } + } + minmaxes.push_back(minmax); + } + + return minmaxes; +} + +// ============================================================================= +// Private Helpers END +// ============================================================================= int SC_SACD_AABB_Box_Collision(const SC_SACD_AABB_Box *a, const SC_SACD_AABB_Box *b) { @@ -27,8 +147,25 @@ int SC_SACD_AABB_Box_Collision(const SC_SACD_AABB_Box *a, int SC_SACD_Generic_Box_Collision(const SC_SACD_Generic_Box *a, const SC_SACD_Generic_Box *b) { - // TODO - return 0; + // Get all normals. + std::vector normals = SC_SACD_Get_Box_Normals(a); + for (const auto &normal : SC_SACD_Get_Box_Normals(b)) { + normals.push_back(normal); + } + + // Get all minmaxes. + std::vector minmaxes_a = SC_SACD_Get_Box_MinMax(a, normals); + std::vector minmaxes_b = SC_SACD_Get_Box_MinMax(b, normals); + + // Check minmaxes. + for (unsigned int i = 0; i < normals.size(); ++i) { + if (minmaxes_a[i].max < minmaxes_b[i].min || + minmaxes_b[i].max < minmaxes_a[i].min) { + return 0; + } + } + + return 1; } int SC_SACD_AABB_Generic_Box_Collision(const SC_SACD_AABB_Box *a, diff --git a/src/test.cpp b/src/test.cpp index 1ddcd28..124a5a6 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1,4 +1,5 @@ #include +#include static int checks_checked = 0; static int checks_passed = 0; @@ -28,57 +29,107 @@ static int checks_passed = 0; int main() { // Test 2D AABB. - SC_SACD_AABB_Box a{0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F}; - SC_SACD_AABB_Box b{2.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F}; + { + SC_SACD_AABB_Box a{0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F}; + SC_SACD_AABB_Box b{2.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F}; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.x = -2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.x = -2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.x = 0.5F; - CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.x = -0.5F; - CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.x = 0.5F; + CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.x = -0.5F; + CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.x = 0.0F; - b.y = 2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.y = -2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.x = 0.0F; + b.y = 2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.y = -2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.y = 0.5F; - CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.y = -0.5F; - CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.y = 0.5F; + CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.y = -0.5F; + CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.y = 0.0F; - b.z = 2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.y = 0.0F; + b.z = 2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.z = -2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.z = -2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.z = 0.5F; - CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.z = 0.5F; + CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.z = -0.5F; - CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.z = -0.5F; + CHECK_TRUE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.z = 0.0F; - b.x = 0.5F; - b.y = 2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.z = 0.0F; + b.x = 0.5F; + b.y = 2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.y = -2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.y = -2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.x = -0.5F; - b.y = 2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.x = -0.5F; + b.y = 2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); - b.y = -2.0F; - CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + b.y = -2.0F; + CHECK_FALSE(SC_SACD_AABB_Box_Collision(&a, &b)); + } + + // Test Separating_Axis_Collision check. + { + SC_SACD_Generic_Box a{0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F}; + SC_SACD_Generic_Box b{0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F}; + + CHECK_TRUE(SC_SACD_Generic_Box_Collision(&a, &b)); + + b.x = 1.1F; + CHECK_FALSE(SC_SACD_Generic_Box_Collision(&a, &b)); + b.x = -1.1F; + CHECK_FALSE(SC_SACD_Generic_Box_Collision(&a, &b)); + + a.z_radians = std::numbers::pi_v / 4.0F; + b.z_radians = a.z_radians; + CHECK_TRUE(SC_SACD_Generic_Box_Collision(&a, &b)); + b.x = 1.1F; + CHECK_TRUE(SC_SACD_Generic_Box_Collision(&a, &b)); + + b.x = 0.0F; + b.y = 1.1F; + a.z_radians = 0.0F; + b.z_radians = 0.0F; + CHECK_FALSE(SC_SACD_Generic_Box_Collision(&a, &b)); + b.y = -1.1F; + CHECK_FALSE(SC_SACD_Generic_Box_Collision(&a, &b)); + + a.z_radians = std::numbers::pi_v / 4.0F; + b.z_radians = a.z_radians; + CHECK_TRUE(SC_SACD_Generic_Box_Collision(&a, &b)); + b.y = 1.1F; + CHECK_TRUE(SC_SACD_Generic_Box_Collision(&a, &b)); + + b.y = 0.0F; + a.z_radians = 0.0F; + b.z_radians = 0.0F; + + b.z = 1.1F; + CHECK_FALSE(SC_SACD_Generic_Box_Collision(&a, &b)); + b.z = -1.1F; + CHECK_FALSE(SC_SACD_Generic_Box_Collision(&a, &b)); + + a.y_radians = std::numbers::pi_v / 4.0F; + b.y_radians = a.y_radians; + CHECK_TRUE(SC_SACD_Generic_Box_Collision(&a, &b)); + b.z = 1.1F; + CHECK_TRUE(SC_SACD_Generic_Box_Collision(&a, &b)); + } std::cout << "Checks checked: " << checks_checked << '\n' << "Checks passed: " << checks_passed << '\n';