// Copyright Epic Games, Inc. All Rights Reserved. #include "HeadlessChaosTestSweep.h" #include "HeadlessChaos.h" #include "Chaos/Capsule.h" #include "Chaos/Convex.h" #include "Chaos/TriangleMeshImplicitObject.h" #include "Chaos/ImplicitObjectScaled.h" #include "Chaos/GeometryQueries.h" namespace ChaosTest { using namespace Chaos; void CapsuleSweepAgainstTriMeshReal() { // Trimesh is from SM_Cattus_POI_Rib, this was a real world failure that is now fixed. using namespace Chaos; FTriangleMeshImplicitObject::ParticlesType TrimeshParticles( { {29.0593967, -5.21321106, -10.2669592}, {34.5006638, -3.16600156, -14.5092020}, {28.8770218, -5.00888205, -10.4567394}, {38.9350929, -1.47836626, -17.6896191}, {39.1246262, -1.58615386, -17.4255066}, {42.4614334, -0.114946000, -19.8563728}, {38.7996712, -1.91535223, -16.3864536}, {29.4634132, -5.15883255, -9.64184284}, {29.6582336, -5.05185938, -9.37638092}, {45.0206337, 0.641714215, -21.0300026}, {46.2034111, 1.19252074, -22.0121441}, {44.3153610, 0.398101240, -19.9310055}, {38.2805557, -1.87191427, -15.7972298}, {48.8065224, 1.82796133, -22.7662964}, {49.7862473, 2.21876359, -23.4238491}, {49.1621628, 1.89441979, -22.1913338}, {52.2055054, 2.59018326, -23.6808910}, {55.6262436, 3.71029496, -24.4840908}, {52.2546806, 2.55433965, -22.8908424}, {47.6990814, 1.76376379, -21.3171616}, {55.9418335, 3.34045410, -24.2242432}, {56.7527275, 4.26803923, -24.7314072}, {56.6030273, 3.37918210, -23.5389385}, {57.1201477, 3.92609882, -24.5670509}, {57.5926590, 5.32376480, -24.8299332}, {57.3900146, 3.86687994, -24.3628445}, {58.3100967, 5.59623432, -24.5884380}, {57.9720459, 6.88918400, -24.9411621}, {58.3736610, 5.22753286, -23.6481304}, {58.3795776, 9.20724392, -24.4941502}, {57.6161652, 7.89536858, -25.0186253}, {57.7064209, 8.63112640, -24.8542480}, {57.4299011, 9.53679848, -24.7764454}, {58.6315613, 9.11761856, -24.1844330}, {58.5211067, 7.24213171, -23.5957718}, {57.5271606, 9.99959183, -24.4184647}, {58.2185135, 9.55542183, -23.2548218}, {57.3097725, 10.2978697, -23.5637169}, {56.3722000, 9.18606091, -24.9406128}, {58.2657166, 8.36739063, -23.1939850}, {56.3347893, 9.67211342, -24.4351349}, {57.3641586, 9.99938393, -23.0135994}, {58.1764908, 5.82101727, -23.1902943}, {57.3481255, 8.60126781, -22.9416485}, {56.1609459, 9.56552029, -23.1539116}, {56.2483864, 9.85045052, -23.4782410}, {55.7174797, 8.29436588, -23.0638103}, {54.7229729, 8.73790932, -23.3331184}, {54.0776367, 8.77919769, -23.7519341}, {53.9943695, 8.17912292, -24.5668526}, {51.3485641, 7.47948122, -23.0380058}, {51.2222366, 7.41138554, -23.6724663}, {53.9260139, 7.74872541, -24.6529694}, {52.2321625, 7.36892176, -22.8332882}, {50.7700424, 6.47493553, -23.9456253}, {56.6027756, 8.31463623, -25.0338459}, {57.2853279, 6.51692963, -25.0099850}, {50.7739677, 5.00270128, -24.0342751}, {47.1190453, 4.83312464, -22.8379707}, {49.7170258, 2.62024260, -23.6686172}, {43.0110207, 0.426635355, -20.4862480}, {42.7733459, 3.24669766, -20.7472992}, {34.1604767, -2.98149395, -14.5161381}, {28.7377853, -4.72821426, -10.5358887}, {28.2660236, -3.50151801, -10.7232647}, {34.4872894, 0.0475072451, -15.1762705}, {27.9901352, -2.42618680, -10.5657129}, {27.6759071, 0.205285028, -10.2063637}, {36.6210670, 2.21063828, -16.3613548}, {27.6877193, 0.690624714, -10.0054235}, {42.8507233, 3.80447602, -20.6911526}, {39.7815170, 3.57913852, -17.9840279}, {42.7781487, 4.36921978, -19.9032001}, {27.8298340, 0.836181641, -9.67990112}, {28.1982155, 0.777253330, -9.15566635}, {46.0600357, 5.31439161, -22.0218697}, {36.9135399, 2.88628912, -15.1429548}, {47.6548805, 5.92783451, -22.1377392}, {44.6417084, 4.89560652, -20.4432411}, {47.3978271, 5.70256472, -21.4681816}, {42.0819168, 3.81306863, -18.2309093}, {34.6343575, 1.72932339, -12.9041548}, {28.5061035, 0.184601068, -8.87326050}, {28.7382660, -0.523662448, -8.71943665}, {47.9805756, 5.56415987, -21.3477497}, {35.0854912, 1.10745859, -13.0555801}, {29.5277596, -4.36109161, -9.26433849}, {44.2550583, 3.73801875, -19.3567085}, {48.6162643, 5.16706753, -21.4052410}, {33.8666039, -3.11327124, -12.2873840}, {29.5907612, -4.66688442, -9.28112030}, {37.8056297, -1.60099864, -15.0567427}, {43.0328369, 1.30700612, -18.5348587}, {45.9731026, 1.34582365, -20.3940430}, {48.3442001, 2.43499756, -21.2875118}, {53.0610924, 3.33885503, -22.4557037}, {54.3709755, 5.39051533, -22.8000984}, {55.9649162, 3.45125723, -23.1164742}, {56.6591415, 4.77854252, -22.8085098}, {57.1962891, 4.12890625, -23.1115723}, {25.7303848, -5.68249369, -8.51585770}, {26.5309143, -4.24291611, -9.48308945}, {24.8292999, -5.12226152, -8.47907925}, {20.3731937, -6.52481079, -5.81806421}, {26.4056702, -3.28238487, -9.07682896}, {23.9174252, -5.14487743, -7.70250511}, {21.3869915, -5.92491102, -6.74390554}, {18.1200867, -6.53198242, -4.76391602}, {21.6681881, -5.50328684, -6.94691038}, {18.0004005, -5.80428123, -5.25099564}, {15.9813662, -6.10711002, -4.21955252}, {18.8343563, -5.61828232, -5.61174917}, {20.2135773, -4.88624334, -5.77969599}, {15.4357758, -5.21927977, -4.48837900}, {11.1496544, -2.60679626, -4.38681793}, {10.0865431, -3.16139269, -3.85664177}, {8.63047791, -2.38198924, -3.79941773}, {14.6292696, -4.35020351, -4.44057178}, {7.90794277, -1.09154844, -4.05677366}, {6.95699024, -1.20261848, -3.97343445}, {8.23572540, -0.744559944, -4.00086451}, {3.10794330, 0.166724160, -4.27361870}, {3.13311577, -0.346626908, -4.11541176}, {1.48072076, -0.108915798, -4.42642593}, {0.311369270, -0.0673256740, -4.49766636}, {-1.59366548, 0.725093842, -4.37339926}, {-3.45725179, -0.749191761, -4.34381390}, {-6.95951319, 0.0120848129, -3.59336853}, {-6.83396530, -1.23874116, -3.47961354}, {-7.89957952, -1.66941118, -2.82847047}, {0.703817546, 0.936612248, -4.19160843}, {-5.88422966, 0.830549538, -3.74061680}, {-8.81333447, -0.651533544, -2.60129619}, {3.10681534, 0.612948775, -3.77665353}, {-4.67421389, 1.29445243, -3.36058092}, {-0.481138408, 1.18588996, -3.53271747}, {7.11869955, -0.267135799, -3.69777322}, {-7.27621984, 0.737751126, -2.76471734}, {2.97556901, 1.46023333, -1.62857735}, {-8.61850739, -0.257534623, -2.48780465}, {-7.48897791, 0.836947024, -2.17553926}, {-6.28466797, 1.53784180, -1.55349731}, {-9.28733826, -2.43306065, -1.33127916}, {-5.33093262, 1.85986328, -1.26861572}, {-8.90132236, -2.71124601, -1.38725543}, {-9.17060471, -2.78321695, -0.819307029}, {-7.84990644, -2.40683627, -2.12729597}, {-7.91611624, -3.38985372, -0.952804804}, {-7.87829638, -3.03356981, -1.37831473}, {-2.43495178, 1.76181090, -1.93872476}, {-5.54367924, 1.81969070, -0.745879471}, {-1.47680271, 1.64132023, -1.37136936}, {-7.19018078, 0.901845515, -0.857508898}, {-2.50409842, 1.67723644, -1.04755175}, {-5.39768934, 1.59197235, -0.603915393}, {-8.16507626, 0.222914696, -1.11252105}, {-8.16396332, -0.777114809, -0.289943308}, {-9.22219181, -2.11877251, -0.794639707}, {-8.59923363, -0.829777956, -0.520817995}, {-8.82921314, -1.96039867, -0.298297912}, {-7.40556860, -1.55998039, -0.250481576}, {-8.14031315, -2.90432310, -0.262226462}, {-6.85163212, -3.75249863, -0.688983977}, {-7.04014444, -3.11551261, -0.126083776}, {-6.32870483, -3.58578491, -0.493814230}, {-6.28338480, -3.61894345, -0.929058373}, {-2.62741208, -3.09605527, -1.33044124}, {-1.79794896, -2.99078751, -1.21258605}, {-2.96854925, -2.36802864, -0.369529188}, {-4.30677605, -1.66542077, -0.303167671}, {-4.98426914, 0.696137369, -0.565792441}, {-2.85455132, -0.0173058268, -0.637033641}, {-2.70905447, 1.40332532, -0.955382884}, {-2.02012229, 1.18688262, -0.943184257}, {-2.42252111, 0.0620364472, -0.474374950}, {-1.56289959, 1.40382648, -0.681109011}, {-2.39032364, -1.25889599, 0.0907319933}, {-2.19775391, 0.174560547, 0.274658203}, {-0.306748420, 1.84567726, -1.35950983}, {0.250524580, 2.27636194, -0.823237658}, {-1.23819447, 1.49723995, 0.237386853}, {-2.13792968, -0.616698205, 0.937073231}, {-0.347599745, 1.71002686, 0.470531255}, {-1.66023159, -2.55999684, -0.452154934}, {-1.58301616, -2.24312091, 0.468077749}, {-1.67812920, 0.0274793506, 0.998333335}, {-1.36489558, -1.84878838, 0.930600643}, {0.255698651, -2.16175246, 0.109909050}, {-0.787146091, -1.07865918, 0.862207949}, {-0.293874621, -2.31530595, 0.560731053}, {0.162645504, 1.18506360, 0.511415780}, {0.535789251, -1.17199028, 0.772845566}, {1.08959603, -1.89062190, 0.519282222}, {-0.329488188, -2.73174953, -0.953910708}, {3.05201745, -1.60201991, -0.293554634}, {1.98701501, -1.01330709, 0.433782279}, {4.13116980, -1.43739676, -0.0357451625}, {2.42845988, 0.716189802, 0.176060840}, {5.01461887, -0.924357474, -0.0785766020}, {2.14807916, 1.47517538, 0.204042286}, {8.51385689, -0.236161187, -0.669285059}, {1.14428675, 2.08710790, -0.255287379}, {6.16604090, 0.561538637, -0.544156194}, {6.23778296, 0.760041595, -0.706411839}, {4.30203772, 1.36588299, -1.17367637}, {8.15555477, 0.428962231, -1.73520589}, {12.0248108, -0.490237832, -1.43530607}, {11.9705954, -0.198881984, -1.88936043}, {10.0048714, -0.699601173, -3.28313756}, {11.2095804, -0.147232190, -2.45466590}, {16.6165028, -0.696181834, -3.11862493}, {11.7848577, -1.81683028, -3.73632550}, {11.8666601, -1.22696412, -3.14981008}, {15.7040558, -0.663861752, -3.65228081}, {14.0951519, -1.07241631, -3.61164665}, {13.9181585, -2.03203130, -3.63557220}, {14.9848728, -1.53837466, -3.98249745}, {15.0845509, -3.46569848, -3.95276761}, {16.0242348, -2.97810125, -4.17965460}, {17.6243019, -4.46606350, -4.79625034}, {17.4456787, -3.35461426, -5.32739258}, {18.7262955, -3.89710188, -5.70676517}, {16.9230785, -2.57194042, -5.40591431}, {22.7467842, -3.61460471, -6.97978115}, {18.4024391, -1.30696046, -6.12640095}, {24.8947697, -0.243050858, -8.32624245}, {20.7879944, -1.08460391, -6.77528811}, {21.7145271, -0.556654394, -6.90972757}, {17.4861660, -1.25891256, -5.46996546}, {24.8378277, 0.196830407, -7.98421001}, {18.6861229, -0.568430483, -5.20232296}, {23.9673500, -0.0726047829, -6.57525635}, {20.4216003, -0.520928204, -4.92496729}, {23.2731094, -0.885929525, -5.62632990}, {17.6102352, -1.26811695, -3.06619239}, {18.1110725, -2.89222097, -2.71354604}, {15.2493439, -1.51500702, -1.99904561}, {23.4795017, -5.85999155, -5.31351185}, {23.3095360, -6.35326576, -5.25977182}, {17.5398540, -4.66228580, -2.38013840}, {23.1760674, -6.62616539, -5.57958126}, {19.5313797, -6.43315840, -3.60291767}, {15.4807367, -3.66881418, -1.85560131}, {14.3018312, -4.70082331, -1.46323049}, {12.8267784, -4.37319517, -1.09716678}, {15.7109251, -6.05440664, -2.37732816}, {12.3741608, -1.41286337, -1.26915634}, {13.8603306, -5.49541807, -1.90867615}, {17.6683712, -6.81585407, -3.37876749}, {12.2477732, -4.72466660, -1.29786050}, {11.1379576, -3.74680114, -0.753773510}, {9.36099339, -2.47074175, -0.534102321}, {10.0452557, -3.30079317, -0.685146451}, {6.68738413, -2.30865097, -0.461158574}, {7.36793947, -3.03040981, -1.47094512}, {11.8176231, -4.84084797, -1.82727480}, {4.59196663, -2.28209496, -1.41259789}, {7.10861683, -2.85069633, -2.06923938}, {3.39536858, -2.24798036, -1.65714610}, {0.172218561, -2.71166372, -1.40673709}, {2.89844537, -1.88502884, -2.27524662}, {-2.25631189, -2.96263766, -1.67407131}, {4.27257490, -1.76102304, -2.35111189}, {-1.88132656, -2.04832077, -2.39434218}, {-6.50054932, -3.11684728, -1.45549214}, {-6.90812969, -1.50060546, -3.38022733}, {-2.73349977, -0.821462870, -4.29114628}, {1.80500829, -1.06030691, -3.12149286}, {4.53264713, -1.14842916, -3.26621342}, {7.26582527, -2.38612676, -2.95009947}, {11.0593281, -3.88886213, -3.55232120}, {13.7664843, -5.74423409, -2.92658687}, {17.2798290, -6.74407482, -4.16304970}, {22.1492062, -6.65115118, -6.29792738}, { 26.2703266, -5.87738276, -8.47085190 } }); TArray> Indices( { {1, 0, 2}, {3, 0, 1}, {0, 3, 4}, {5, 4, 3}, {4, 6, 0}, {7, 0, 6}, {7, 6, 8}, {4, 5, 9}, {5, 10, 9}, {4, 11, 6}, {4, 9, 11}, {12, 8, 6}, {6, 11, 12}, {9, 10, 13}, {10, 14, 13}, {9, 15, 11}, {9, 13, 15}, {13, 14, 16}, {17, 16, 14}, {13, 18, 15}, {18, 13, 16}, {19, 11, 15}, {19, 15, 18}, {11, 19, 12}, {16, 17, 20}, {21, 20, 17}, {16, 22, 18}, {22, 16, 20}, {23, 20, 21}, {24, 23, 21}, {23, 25, 20}, {20, 25, 22}, {26, 23, 24}, {26, 25, 23}, {27, 26, 24}, {25, 28, 22}, {28, 25, 26}, {26, 27, 29}, {29, 27, 30}, {31, 29, 30}, {31, 32, 29}, {33, 26, 29}, {28, 26, 34}, {33, 34, 26}, {35, 33, 29}, {35, 29, 32}, {34, 33, 36}, {37, 33, 35}, {33, 37, 36}, {38, 35, 32}, {34, 36, 39}, {35, 38, 40}, {40, 37, 35}, {36, 41, 39}, {37, 41, 36}, {42, 34, 39}, {34, 42, 28}, {43, 39, 41}, {43, 42, 39}, {44, 41, 37}, {44, 43, 41}, {37, 45, 44}, {37, 40, 45}, {44, 46, 43}, {42, 43, 46}, {45, 47, 44}, {46, 44, 47}, {48, 45, 40}, {47, 45, 48}, {48, 40, 49}, {38, 49, 40}, {48, 50, 47}, {51, 48, 49}, {50, 48, 51}, {49, 38, 52}, {50, 53, 47}, {53, 46, 47}, {52, 54, 49}, {49, 54, 51}, {38, 55, 52}, {32, 55, 38}, {55, 32, 31}, {55, 31, 30}, {30, 56, 55}, {52, 55, 56}, {30, 27, 56}, {24, 56, 27}, {56, 24, 21}, {56, 57, 52}, {52, 57, 54}, {14, 21, 17}, {57, 58, 54}, {51, 54, 58}, {21, 14, 59}, {59, 56, 21}, {59, 57, 56}, {59, 58, 57}, {10, 59, 14}, {59, 10, 60}, {5, 60, 10}, {3, 60, 5}, {59, 61, 58}, {60, 61, 59}, {60, 3, 62}, {1, 62, 3}, {2, 62, 1}, {62, 2, 63}, {62, 63, 64}, {60, 62, 65}, {62, 64, 65}, {60, 65, 61}, {66, 65, 64}, {66, 67, 65}, {67, 68, 65}, {61, 65, 68}, {68, 67, 69}, {61, 68, 70}, {58, 61, 70}, {69, 71, 68}, {70, 68, 71}, {69, 72, 71}, {70, 71, 72}, {72, 69, 73}, {74, 72, 73}, {58, 70, 75}, {70, 72, 75}, {58, 75, 51}, {74, 76, 72}, {75, 77, 51}, {50, 51, 77}, {72, 78, 75}, {78, 77, 75}, {78, 72, 76}, {79, 50, 77}, {79, 77, 78}, {79, 53, 50}, {80, 78, 76}, {80, 79, 78}, {76, 74, 81}, {81, 80, 76}, {82, 81, 74}, {82, 83, 81}, {80, 84, 79}, {84, 53, 79}, {83, 85, 81}, {85, 80, 81}, {83, 86, 85}, {87, 84, 80}, {80, 85, 87}, {88, 53, 84}, {87, 88, 84}, {89, 85, 86}, {89, 86, 90}, {90, 8, 89}, {12, 89, 8}, {91, 85, 89}, {89, 12, 91}, {85, 92, 87}, {92, 85, 91}, {91, 12, 93}, {93, 92, 91}, {12, 19, 93}, {87, 92, 94}, {92, 93, 94}, {93, 19, 94}, {87, 94, 88}, {94, 19, 95}, {19, 18, 95}, {88, 94, 96}, {96, 94, 95}, {53, 88, 96}, {18, 97, 95}, {97, 18, 22}, {95, 98, 96}, {98, 53, 96}, {95, 97, 98}, {53, 98, 46}, {46, 98, 42}, {97, 22, 99}, {98, 97, 99}, {98, 99, 42}, {28, 99, 22}, {99, 28, 42}, {100, 63, 2}, {100, 64, 63}, {64, 100, 101}, {102, 101, 100}, {103, 102, 100}, {104, 64, 101}, {64, 104, 66}, {101, 102, 105}, {105, 104, 101}, {102, 103, 106}, {107, 106, 103}, {102, 108, 105}, {108, 102, 106}, {109, 106, 107}, {110, 109, 107}, {111, 108, 106}, {109, 111, 106}, {112, 105, 108}, {111, 112, 108}, {113, 109, 110}, {113, 111, 109}, {114, 113, 110}, {115, 114, 110}, {115, 116, 114}, {117, 111, 113}, {114, 117, 113}, {116, 118, 114}, {119, 118, 116}, {114, 118, 120}, {117, 114, 120}, {121, 118, 119}, {118, 121, 120}, {119, 122, 121}, {123, 121, 122}, {124, 121, 123}, {121, 124, 125}, {126, 125, 124}, {127, 125, 126}, {128, 127, 126}, {128, 129, 127}, {130, 121, 125}, {125, 127, 131}, {130, 125, 131}, {132, 127, 129}, {127, 132, 131}, {121, 130, 133}, {120, 121, 133}, {130, 131, 134}, {130, 135, 133}, {134, 135, 130}, {133, 136, 120}, {134, 131, 137}, {132, 137, 131}, {133, 138, 136}, {135, 138, 133}, {139, 137, 132}, {139, 140, 137}, {137, 140, 141}, {137, 141, 134}, {132, 142, 139}, {141, 143, 134}, {142, 132, 144}, {132, 129, 144}, {142, 144, 145}, {129, 146, 144}, {144, 147, 145}, {148, 144, 146}, {147, 144, 148}, {149, 134, 143}, {149, 135, 134}, {143, 150, 149}, {143, 141, 150}, {135, 149, 151}, {141, 152, 150}, {149, 150, 153}, {151, 149, 153}, {150, 152, 154}, {153, 150, 154}, {152, 141, 155}, {155, 141, 140}, {139, 155, 140}, {156, 152, 155}, {154, 152, 156}, {157, 155, 139}, {157, 139, 142}, {157, 142, 145}, {157, 158, 155}, {158, 156, 155}, {158, 157, 159}, {158, 159, 156}, {157, 145, 159}, {159, 160, 156}, {156, 160, 154}, {161, 159, 145}, {147, 161, 145}, {162, 161, 147}, {163, 159, 161}, {163, 161, 162}, {159, 163, 160}, {163, 162, 164}, {165, 164, 162}, {166, 164, 165}, {164, 166, 167}, {163, 164, 168}, {168, 164, 167}, {160, 163, 169}, {163, 168, 169}, {160, 169, 170}, {154, 160, 170}, {168, 171, 169}, {169, 171, 170}, {154, 170, 172}, {170, 171, 172}, {154, 172, 153}, {173, 153, 172}, {172, 171, 173}, {173, 151, 153}, {173, 171, 174}, {151, 173, 175}, {174, 175, 173}, {171, 176, 174}, {171, 168, 176}, {175, 174, 177}, {177, 174, 176}, {178, 151, 175}, {135, 151, 178}, {138, 135, 178}, {175, 179, 178}, {179, 138, 178}, {175, 177, 180}, {180, 179, 175}, {177, 181, 180}, {177, 176, 181}, {182, 179, 180}, {168, 183, 176}, {168, 167, 183}, {184, 181, 176}, {183, 184, 176}, {180, 181, 185}, {180, 185, 182}, {186, 181, 184}, {186, 185, 181}, {184, 183, 187}, {185, 186, 188}, {186, 184, 189}, {189, 184, 187}, {190, 185, 188}, {182, 185, 190}, {186, 191, 188}, {191, 186, 189}, {188, 191, 190}, {187, 192, 189}, {191, 189, 192}, {187, 183, 193}, {183, 167, 193}, {194, 192, 187}, {193, 194, 187}, {195, 191, 192}, {196, 192, 194}, {192, 196, 195}, {197, 191, 195}, {190, 191, 197}, {196, 198, 195}, {198, 197, 195}, {199, 190, 197}, {190, 199, 182}, {197, 198, 200}, {201, 182, 199}, {182, 201, 179}, {197, 202, 199}, {202, 197, 200}, {203, 199, 202}, {199, 203, 201}, {202, 200, 203}, {204, 179, 201}, {201, 203, 204}, {179, 204, 138}, {205, 138, 204}, {204, 203, 205}, {138, 205, 136}, {200, 206, 203}, {203, 207, 205}, {206, 207, 203}, {136, 205, 208}, {205, 207, 209}, {205, 209, 208}, {206, 210, 207}, {211, 136, 208}, {120, 136, 211}, {120, 211, 117}, {208, 209, 212}, {211, 208, 212}, {207, 213, 209}, {213, 207, 210}, {214, 212, 209}, {214, 209, 213}, {211, 212, 215}, {212, 214, 216}, {216, 215, 212}, {211, 215, 217}, {117, 211, 217}, {216, 218, 215}, {215, 218, 217}, {117, 217, 219}, {218, 219, 217}, {219, 111, 117}, {111, 219, 112}, {219, 218, 220}, {219, 221, 112}, {220, 221, 219}, {218, 222, 220}, {221, 220, 222}, {222, 218, 216}, {112, 221, 223}, {223, 105, 112}, {223, 104, 105}, {224, 221, 222}, {223, 225, 104}, {104, 225, 66}, {226, 223, 221}, {226, 225, 223}, {226, 221, 224}, {66, 225, 67}, {226, 224, 227}, {227, 225, 226}, {222, 228, 224}, {224, 228, 227}, {216, 228, 222}, {216, 214, 228}, {214, 213, 228}, {67, 225, 229}, {229, 225, 227}, {229, 69, 67}, {229, 73, 69}, {74, 73, 229}, {227, 228, 230}, {229, 227, 230}, {213, 230, 228}, {231, 74, 229}, {230, 231, 229}, {232, 230, 213}, {230, 232, 231}, {210, 232, 213}, {74, 231, 233}, {233, 231, 232}, {233, 82, 74}, {233, 83, 82}, {234, 232, 210}, {232, 234, 233}, {235, 83, 233}, {235, 233, 234}, {234, 210, 236}, {236, 235, 234}, {210, 206, 236}, {206, 200, 236}, {235, 237, 83}, {86, 83, 237}, {237, 90, 86}, {90, 237, 238}, {238, 8, 90}, {237, 235, 239}, {8, 238, 240}, {240, 7, 8}, {241, 238, 237}, {240, 238, 241}, {237, 239, 241}, {239, 235, 242}, {235, 236, 242}, {239, 242, 243}, {242, 236, 243}, {241, 239, 243}, {236, 244, 243}, {241, 243, 245}, {244, 236, 246}, {246, 236, 200}, {245, 243, 247}, {247, 243, 244}, {245, 248, 241}, {245, 247, 248}, {240, 241, 248}, {247, 244, 249}, {246, 250, 244}, {244, 250, 249}, {200, 251, 246}, {250, 246, 251}, {251, 200, 198}, {251, 198, 196}, {252, 250, 251}, {252, 249, 250}, {196, 253, 251}, {251, 253, 252}, {249, 252, 253}, {253, 196, 194}, {254, 249, 253}, {253, 194, 254}, {249, 255, 247}, {249, 254, 255}, {255, 248, 247}, {256, 254, 194}, {194, 193, 256}, {255, 254, 257}, {256, 257, 254}, {256, 193, 258}, {257, 256, 258}, {258, 193, 259}, {167, 259, 193}, {260, 258, 259}, {259, 167, 261}, {260, 259, 261}, {167, 166, 261}, {166, 165, 261}, {262, 258, 260}, {262, 257, 258}, {260, 261, 263}, {261, 165, 264}, {261, 264, 263}, {264, 165, 162}, {147, 264, 162}, {148, 264, 147}, {146, 264, 148}, {264, 146, 265}, {146, 129, 265}, {129, 128, 265}, {126, 265, 128}, {266, 264, 265}, {265, 126, 266}, {266, 263, 264}, {126, 124, 123}, {123, 266, 126}, {266, 267, 263}, {267, 266, 123}, {263, 267, 260}, {267, 123, 122}, {267, 262, 260}, {122, 268, 267}, {262, 267, 268}, {268, 122, 119}, {268, 119, 116}, {268, 116, 115}, {268, 269, 262}, {115, 269, 268}, {257, 262, 269}, {270, 269, 115}, {257, 269, 270}, {270, 115, 110}, {270, 271, 257}, {270, 110, 271}, {271, 255, 257}, {271, 248, 255}, {110, 272, 271}, {248, 271, 272}, {272, 110, 107}, {107, 103, 272}, {272, 273, 248}, {273, 272, 103}, {240, 248, 273}, {100, 273, 103}, {273, 274, 240}, {273, 100, 274}, {240, 274, 7}, {2, 274, 100}, {0, 7, 274}, {274, 2, 0} }); TArray Materials; for (int32 i = 0; i < Indices.Num(); ++i) { Materials.Emplace(0); } FTriangleMeshImplicitObjectPtr TriangleMesh( new FTriangleMeshImplicitObject(MoveTemp(TrimeshParticles), MoveTemp(Indices), MoveTemp(Materials))); TImplicitObjectScaled ScaledTriangleMesh = TImplicitObjectScaled(TriangleMesh, FVec3(50,50,50)); const FVec3 X1 = { 0,0,-19.45 }; const FVec3 X2 = X1 + FVec3(0, 0, 38.9); const FReal Radius = 25.895; const FCapsule Capsule = FCapsule(X1, X2, Radius); const FVec3 CapsuleToTrimeshTranslation = { 1818.55884, 27.8377075, -630.160645 }; const FRigidTransform3 CapsuleToTrimesh(CapsuleToTrimeshTranslation, FQuat::Identity); const FVec3 TrimeshTranslation = { -1040.00000, 700.000000, 992.000000 }; const FRigidTransform3 TrimeshTransform(TrimeshTranslation, FQuat::Identity); const FVec3 Dir(0, 0, -1); const FReal Length = 159.100098; FReal OutTime = -1; FVec3 Normal(0.0); FVec3 Position(0.0); int32 FaceIndex = -1; FVec3 FaceNormal(0.0); bool bResult = ScaledTriangleMesh.LowLevelSweepGeom(Capsule, CapsuleToTrimesh, Dir, Length, OutTime, Position, Normal, FaceIndex, FaceNormal, 0.0f, true); FVec3 WorldPosition = TrimeshTransform.TransformPositionNoScale(Position); EXPECT_EQ(bResult, true); EXPECT_EQ(FaceIndex, 415); EXPECT_NEAR(WorldPosition.X, 763.85413, KINDA_SMALL_NUMBER); EXPECT_NEAR(WorldPosition.Y, 728.80212, KINDA_SMALL_NUMBER); EXPECT_NEAR(WorldPosition.Z, 303.77856, KINDA_SMALL_NUMBER); } struct FSphereSweepFixture : public testing::Test { struct FSweepResultData { bool bResult; FReal TOI; FVec3 Position, Normal, FaceNormal; int32 FaceId; }; FSweepResultData SweepQuerySphere(const FImplicitObject& TestObject, const FRigidTransform3& TestObjectTransform, const FVec3& SweepStart, const FVec3& SweepDirection, const FReal SweepLength = 100) { const Chaos::FSphere Sphere(FVec3::ZeroVector, SphereRadius); const FRigidTransform3 StartTM(SweepStart, TRotation::Identity); const FVec3 NormalizedSweepDirection = SweepDirection.GetSafeNormal(); FSweepResultData Result; Result.bResult = SweepQuery(TestObject, TestObjectTransform, Sphere, StartTM, NormalizedSweepDirection, SweepLength, Result.TOI, Result.Position, Result.Normal, Result.FaceId, Result.FaceNormal, 0.0, bComputeMTD); return Result; } FSweepResultData SweepQuerySphere(const FImplicitObject& TestObject, const FVec3& SweepStart, const FVec3& SweepDirection, const FReal SweepLength = 100) { return SweepQuerySphere(TestObject, FRigidTransform3(), SweepStart, SweepDirection, SweepLength); } FReal SphereRadius = 25; bool bComputeMTD = true; }; struct FTriangleMeshSweepFixture : public FSphereSweepFixture { struct FMeshInitData { FTriangleMeshImplicitObject::ParticlesType TrimeshParticles; TArray> Indices; TArray Materials; void AutoBuildMaterials() { Materials.Empty(); for (int32 i = 0; i < Indices.Num(); ++i) { Materials.Emplace(0); } } }; static FMeshInitData CreateSimpleTriInitData() { FMeshInitData Result; Result.TrimeshParticles = FTriangleMeshImplicitObject::ParticlesType( { {0, 0, 0}, {100, 0, 0}, {100, 100, 0}, }); Result.Indices = { {0, 1, 2}, }; Result.AutoBuildMaterials(); return Result; } static FMeshInitData CreateSimpleQuadInitData() { FMeshInitData Result; Result.TrimeshParticles = FTriangleMeshImplicitObject::ParticlesType( { {0, 0, 0}, {100, 0, 0}, {100, 100, 0}, {0, 100, 0}, }); Result.Indices = { {0, 1, 2}, {0, 2, 3}, }; Result.AutoBuildMaterials(); return Result; } FSweepResultData SweepSphere(const FTriangleMeshImplicitObject& TriangleMesh, const FVec3& SweepStart, const FVec3& SweepDirection, const FReal SweepLength = 100) { const Chaos::FSphere Sphere(FVec3::ZeroVector, SphereRadius); const FRigidTransform3 StartTM(SweepStart, TRotation::Identity); const FVec3 NormalizedSweepDirection = SweepDirection.GetSafeNormal(); FSweepResultData Result; Result.bResult = TriangleMesh.SweepGeom(Sphere, StartTM, NormalizedSweepDirection, SweepLength, Result.TOI, Result.Position, Result.Normal, Result.FaceId, Result.FaceNormal, 0.0, bComputeMTD); return Result; } FSweepResultData SweepSphere(FMeshInitData& InitData, const FVec3& SweepStart, const FVec3& SweepDirection, const FReal SweepLength = 100) { FTriangleMeshImplicitObject TriangleMesh(MoveTemp(InitData.TrimeshParticles), MoveTemp(InitData.Indices), MoveTemp(InitData.Materials), nullptr, nullptr, true); return SweepSphere(TriangleMesh, SweepStart, SweepDirection, SweepLength); } }; TEST_F(FTriangleMeshSweepFixture, SphereWithInitialIntersectionOfTwoTriangles_SweepAgainstTriMesh_CorrectTriangleIsHit) { // Test a straight down sweep where the sphere has an initial intersection with both triangles. FMeshInitData InitData = CreateSimpleQuadInitData(); FTriangleMeshImplicitObject TriangleMesh(MoveTemp(InitData.TrimeshParticles), MoveTemp(InitData.Indices), MoveTemp(InitData.Materials), nullptr, nullptr, true); // Test where tri0 has the first TOI. FSweepResultData Result = SweepSphere(TriangleMesh, FVec3(60, 50, 10), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, -15, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(60, 50, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); EXPECT_EQ(Result.FaceId, 0); // Test where tri1 has the first TOI. Result = SweepSphere(TriangleMesh, FVec3(40, 50, 10), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, -15, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(40, 50, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); EXPECT_EQ(Result.FaceId, 1); } TEST_F(FTriangleMeshSweepFixture, SphereWithInitialIntersectionOfOneTriangle_SweepAgainstTriMesh_CorrectTriangleIsHit) { // Test a diagonal sweep where one triangle has an initial intersection and the other has a positive TOI. FMeshInitData InitData = CreateSimpleQuadInitData(); FTriangleMeshImplicitObject TriangleMesh(MoveTemp(InitData.TrimeshParticles), MoveTemp(InitData.Indices), MoveTemp(InitData.Materials), nullptr, nullptr, true); // Test where tri0 has the initial intersection. FSweepResultData Result = SweepSphere(TriangleMesh, FVec3(90, 50, 10), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, -15, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(90, 50, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); EXPECT_EQ(Result.FaceId, 0); // Test where tri1 has the initial intersection. Result = SweepSphere(TriangleMesh, FVec3(10, 50, 10), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, -15, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(10, 50, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); EXPECT_EQ(Result.FaceId, 1); } TEST_F(FTriangleMeshSweepFixture, SphereWithZeroLengthSweep_TestAllAxes_HitIsExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FTriangleMeshImplicitObject TriangleMesh(MoveTemp(InitData.TrimeshParticles), MoveTemp(InitData.Indices), MoveTemp(InitData.Materials), nullptr, nullptr, true); // Test each cardinal axis FSweepResultData Result = SweepSphere(TriangleMesh, FVec3(20, 20, 0), FVec3(-1, 0, 0), 0); EXPECT_EQ(Result.bResult, true); Result = SweepSphere(TriangleMesh, FVec3(20, 20, 0), FVec3(1, 0, 0), 0); EXPECT_EQ(Result.bResult, true); Result = SweepSphere(TriangleMesh, FVec3(20, 20, 0), FVec3(0, -1, 0), 0); EXPECT_EQ(Result.bResult, true); Result = SweepSphere(TriangleMesh, FVec3(20, 20, 0), FVec3(0, 1, 0), 0); EXPECT_EQ(Result.bResult, true); Result = SweepSphere(TriangleMesh, FVec3(20, 20, 0), FVec3(0, 0, -1), 0); EXPECT_EQ(Result.bResult, true); Result = SweepSphere(TriangleMesh, FVec3(20, 20, 0), FVec3(0, 0, 1), 0); EXPECT_EQ(Result.bResult, true); // Test the zero vector direction Result = SweepSphere(TriangleMesh, FVec3(20, 20, 0), FVec3(0, 0, 0), 0); EXPECT_EQ(Result.bResult, true); Result = SweepSphere(TriangleMesh, FVec3(20, 20, 0), FVec3(0, 0, 0), 1); EXPECT_EQ(Result.bResult, true); } // All front-face hits (sweeping opposed to the normal) should generate valid hits. TEST_F(FTriangleMeshSweepFixture, SphereInFront_SweepOpposedToNormal_HitIsExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); const FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 25, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); EXPECT_EQ(Result.FaceId, 0); } TEST_F(FTriangleMeshSweepFixture, SphereInFrontWithInitialOverlap_SweepOpposedToNormal_HitIsExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 10), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, -15, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); EXPECT_EQ(Result.FaceId, 0); } TEST_F(FTriangleMeshSweepFixture, SphereBehindWithInitialOverlap_SweepOpposedToNormal_HitIsExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, -10), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, -15, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, -1), KINDA_SMALL_NUMBER); EXPECT_EQ(Result.FaceId, 0); } struct FTriangleMeshBackfaceSweepFixture : public FTriangleMeshSweepFixture { static constexpr FReal ValueWithinParallelEpsilon = UE_SMALL_NUMBER; static constexpr FReal ValueOutsideParallelEpsilon = 4 * UE_KINDA_SMALL_NUMBER; }; TEST_F(FTriangleMeshBackfaceSweepFixture, SphereBehind_SweepOpposedToNormal_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, -50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, false); } // All back-face hits (sweeping along the normal) should not generate a valid hit. TEST_F(FTriangleMeshBackfaceSweepFixture, SphereBehind_SweepAlongNormal_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, -50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereBehindWithInitialOverlap_SweepAlongNormal_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, -10), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereInFrontWithInitialOverlap_SweepAlongNormal_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 10), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereInFront_SweepAlongNormal_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, false); } // Parallel checks are more interesting. If there is an initial overlap then no hit should be generated. // This parallel checks uses an epsilon to determine the boundaries. TEST_F(FTriangleMeshBackfaceSweepFixture, SphereWithInitialOverlap_SweepExactlyParallel_HitIsExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 10), FVec3(1, 0, 0)); EXPECT_EQ(Result.bResult, true); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereWithInitialOverlap_SweepOpposedToNormalWithinParallelEpsilon_HitIsExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 10), FVec3(1, 0, ValueWithinParallelEpsilon)); EXPECT_EQ(Result.bResult, true); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereWithInitialOverlap_SweepOpposedToNormalOutsideParallelEpsilon_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 10), FVec3(1, 0, ValueOutsideParallelEpsilon)); EXPECT_EQ(Result.bResult, false); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereWithInitialOverlap_SweepAlongNormalWithinParallelEpsilon_HitIsExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 10), FVec3(1, 0, -ValueWithinParallelEpsilon)); EXPECT_EQ(Result.bResult, true); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereWithInitialOverlap_SweepAlongNormalOutsideParallelEpsilon_HitIsExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(20, 20, 10), FVec3(1, 0, 4 * -ValueOutsideParallelEpsilon)); EXPECT_EQ(Result.bResult, true); } // Need a few extra tests for parallel to validate that no initial overlap always returns false. TEST_F(FTriangleMeshBackfaceSweepFixture, SphereInAabbWithNoInitialOverlap_SweepExactlyParallel_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(0, 100, 0), FVec3(1, -1, 0)); EXPECT_EQ(Result.bResult, false); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereInAabbWithNoInitialOverlap_AlongNormalWithinPositiveEpsilon_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(0, 100, 0), FVec3(1, -1, ValueWithinParallelEpsilon)); EXPECT_EQ(Result.bResult, false); } TEST_F(FTriangleMeshBackfaceSweepFixture, SphereInAabbWithNoInitialOverlap_AlongNormalWithinNegativeEpsilon_HitIsNotExpected) { FMeshInitData InitData = CreateSimpleTriInitData(); FSweepResultData Result = SweepSphere(InitData, FVec3(0, 100, 0), FVec3(1, -1, -ValueWithinParallelEpsilon)); EXPECT_EQ(Result.bResult, false); } // Some extra tests for negative scales struct FScaledTriMeshSweepFixture : public FTriangleMeshBackfaceSweepFixture { static FMeshInitData CreateSimpleTriInitData() { FMeshInitData Result; // Use a slightly different triangle mesh than above. This allows easy negation of the mesh without having to move the sweep's location. Result.TrimeshParticles = FTriangleMeshImplicitObject::ParticlesType( { {-100, -100, 0}, {100, -100, 0}, {0, 100, 0}, }); Result.Indices = { {0, 1, 2}, }; Result.AutoBuildMaterials(); return Result; } FSweepResultData SweepSphere(const TImplicitObjectScaled& TriangleMesh, const FVec3& SweepStart, const FVec3& SweepDirection, const FReal SweepLength = 100) { const Chaos::FSphere Sphere(FVec3::ZeroVector, SphereRadius); const FRigidTransform3 StartTM(SweepStart, TRotation::Identity); const FVec3 NormalizedSweepDirection = SweepDirection.GetSafeNormal(); FSweepResultData Result; Result.bResult = SweepQuery(TriangleMesh, FRigidTransform3(), Sphere, StartTM, NormalizedSweepDirection, SweepLength, Result.TOI, Result.Position, Result.Normal, Result.FaceId, Result.FaceNormal, 0.0, true); return Result; } FSweepResultData SweepSphere(FMeshInitData& InitData, const FVec3& SweepStart, const FVec3& SweepDirection, const FReal SweepLength = 100) { FTriangleMeshImplicitObject TriangleMesh(MoveTemp(InitData.TrimeshParticles), MoveTemp(InitData.Indices), MoveTemp(InitData.Materials), nullptr, nullptr, true); FTriangleMeshBackfaceSweepFixture::SweepSphere(TriangleMesh, SweepStart, SweepDirection, SweepLength); // Unfortunately, it is not possible to currently call FTriangleMeshImplicitObject::SweepGeom directly. // There's code inside of TImplicitObjectScaled that negates some inputs values (the direction) that is necessary for the tests to work (follows the editor path). TImplicitObjectScaled ScaledMesh(&TriangleMesh, TriMeshScale); return SweepSphere(ScaledMesh, SweepStart, SweepDirection, SweepLength); } FSweepResultData SweepSphereVsSimpleTri(const FVec3& SweepStart, const FVec3& SweepDirection, const FReal SweepLength = 100) { FMeshInitData InitData = CreateSimpleTriInitData(); return SweepSphere(InitData, SweepStart, SweepDirection, SweepLength); } FVec3 TriMeshScale = FVec3(1, 1, 1); }; // Test sweeps along the original front face TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXAxis_SweepInFrontOpposedToNormal_HitIsExpected) { // Negating the x axis should flip the winding order, but up is still the front face TriMeshScale = FVec3(-1, 1, 1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, 50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 25, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeYAxis_SweepInFrontOpposedToNormal_HitIsExpected) { // Negating the y axis should flip the winding order, but up is still the front face TriMeshScale = FVec3(1, -1, 1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, 50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 25, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeZAxis_SweepInBackAlongToNormal_HitIsNotExpected) { // Negating the z axis should cause down to be the front face TriMeshScale = FVec3(1, 1, -1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, 50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXYAxis_SweepInFrontOpposedToNormal_HitIsExpected) { TriMeshScale = FVec3(-1, -1, 1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, 50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 25, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, 1), KINDA_SMALL_NUMBER); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXZAxis_SweepInFrontOpposedToNormal_HitIsNotExpected) { TriMeshScale = FVec3(-1, 1, -1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, 50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeYZAxis_SweepInFrontOpposedToNormal_HitIsNotExpected) { TriMeshScale = FVec3(1, -1, -1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, 50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXYZAxis_SweepInFrontOpposedToNormal_HitIsNotExpected) { TriMeshScale = FVec3(-1, -1, -1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, 50), FVec3(0, 0, -1)); EXPECT_EQ(Result.bResult, false); } // Test sweeps along the original back face TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXAxis_SweepOnBackAlongNormal_HitIsNotExpected) { // Negating the x axis should flip the winding order, but up is still the front face TriMeshScale = FVec3(-1, 1, 1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, -50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeYAxis_SweepOnBackAlongNormal_HitIsNotExpected) { // Negating the y axis should flip the winding order, but up is still the front face TriMeshScale = FVec3(1, -1, 1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, -50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeZAxis_SweepOnBackAlongNormal_HitIsExpected) { // Negating the z axis should cause down to be the front face TriMeshScale = FVec3(1, 1, -1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, -50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 25, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, -1), KINDA_SMALL_NUMBER); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXYAxis_SweepOnBackAlongNormal_HitIsNotExpected) { TriMeshScale = FVec3(-1, -1, 1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, -50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, false); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXZAxis_SweepOnBackAlongNormal_HitIsExpected) { TriMeshScale = FVec3(-1, 1, -1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, -50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 25, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, -1), KINDA_SMALL_NUMBER); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeYZAxis_SweepOnBackAlongNormal_HitIsExpected) { TriMeshScale = FVec3(1, -1, -1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, -50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 25, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, -1), KINDA_SMALL_NUMBER); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXYZAxis_SweepOnBackAlongNormal_HitIsExpected) { TriMeshScale = FVec3(-1, -1, -1); const FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(20, 20, -50), FVec3(0, 0, 1)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 25, KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Position, FVec3(20, 20, 0), KINDA_SMALL_NUMBER); EXPECT_VECTOR_NEAR(Result.Normal, FVec3(0, 0, -1), KINDA_SMALL_NUMBER); } // Test parallel sweeps TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXAxis_SphereWithInitialOverlap_SweepOpposedToNormalWithinParallelEpsilon_HitIsExpected) { TriMeshScale = FVec3(-1, 1, 1); FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(0, 0, 10), FVec3(1, 0, ValueWithinParallelEpsilon)); EXPECT_EQ(Result.bResult, true); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeZAxis_SphereWithInitialOverlap_SweepOpposedToNormalWithinParallelEpsilon_HitIsExpected) { TriMeshScale = FVec3(1, 1, -1); FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(0, 0, 10), FVec3(1, 0, ValueWithinParallelEpsilon)); EXPECT_EQ(Result.bResult, true); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeXAxis_SphereWithInitialOverlap_SweepAlongNormalWithinParallelEpsilon_HitIsExpected) { TriMeshScale = FVec3(-1, 1, 1); FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(0, 0, 10), FVec3(1, 0, -ValueWithinParallelEpsilon)); EXPECT_EQ(Result.bResult, true); } TEST_F(FScaledTriMeshSweepFixture, TriMeshWithNegativeZAxis_SphereWithInitialOverlap_SweepAlongNormalWithinParallelEpsilon_HitIsExpected) { TriMeshScale = FVec3(1, 1, -1); FSweepResultData Result = SweepSphereVsSimpleTri(FVec3(0, 0, 10), FVec3(1, 0, -ValueWithinParallelEpsilon)); EXPECT_EQ(Result.bResult, true); } TEST_F(FSphereSweepFixture, TransformedUnionContainingNonUniformScaledObject_SweepSphere_ResultsAreExpected) { // This test is to catch a bug where a Transformed union with a scaled object would // ensure because it attempted to go down the sweep as raycast path when there was a non-uniform scale. bComputeMTD = false; SphereRadius = 1; // Create a union that has a scaled and non-scaled object FImplicitObjectPtr SphereObjPtr = new FImplicitSphere3(FVec3(0, 0, 5), 1); FImplicitObjectPtr ScaledBoxObjPtr = new TImplicitObjectScaled(new FImplicitBox3(FVec3(-1), FVec3(1)), FVec3(2, 1, 1)); TArray Objects{ ScaledBoxObjPtr, SphereObjPtr }; FImplicitObjectPtr UnionObjectPtr = new FImplicitObjectUnion(MoveTemp(Objects)); // Build a transform around the union (this is necessary to produce the bug) FRigidTransform3 ObjTransform(FVec3::Zero(), FRotation3::Identity, FVec3(1)); TImplicitObjectTransformed TestObject(UnionObjectPtr, ObjTransform); // Test a few configurations to make sure we hit and don't hit where expected FSweepResultData Result; // To the left of the box Result = SweepQuerySphere(TestObject, FVec3(-3.1, 20, 0), FVec3(0, -1, 0)); EXPECT_EQ(Result.bResult, false); // Should hit left edge of the box Result = SweepQuerySphere(TestObject, FVec3(-2, 20, 0), FVec3(0, -1, 0)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 18, KINDA_SMALL_NUMBER); // Should hit right edge of the box Result = SweepQuerySphere(TestObject, FVec3(2, 20, 0), FVec3(0, -1, 0)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 18, KINDA_SMALL_NUMBER); // To the right of the box Result = SweepQuerySphere(TestObject, FVec3(3.1, 20, 0), FVec3(0, -1, 0)); EXPECT_EQ(Result.bResult, false); // Center of the sphere Result = SweepQuerySphere(TestObject, FVec3(0, 20, 5), FVec3(0, -1, 0)); EXPECT_EQ(Result.bResult, true); EXPECT_NEAR(Result.TOI, 18, KINDA_SMALL_NUMBER); // To the left of the sphere Result = SweepQuerySphere(TestObject, FVec3(-2.1, 20, 5), FVec3(0, -1, 0)); EXPECT_EQ(Result.bResult, false); // To the right of the sphere Result = SweepQuerySphere(TestObject, FVec3(2.1, 20, 5), FVec3(0, -1, 0)); EXPECT_EQ(Result.bResult, false); } }