// SPDX-License-Identifier: GPL-3.0-or-later // Native steering wheel: indexed-matrix ownership and array matching. The // ownership cases come from heurazy's mario-kart-wii-VR-port test set. #include #include #include "gx/native_wheel.hpp" namespace { // Three positions, 12 bytes each. Position 1 is on the wheel; 0 and 2 belong to // the body/wing. struct OwnershipFixture { std::array original{}; std::array replacement{}; // PN matrix byte, texture matrix byte, big-endian position index. std::array vertices{0, 30, 0, 0, 0, 30, 0, 1, 3, 30, 0, 2}; OwnershipFixture() { replacement[12] = 7; } bool matches(uint16_t mask) const { return aurora::NativeWheelDrawMatches(original, replacement, 12, vertices, 4, 2, 2, mask); } }; TEST(NativeWheelMatch, MixedBodyAndWingDrawAcceptsWheelPositionsOnLocalBody) { OwnershipFixture f; EXPECT_TRUE(f.matches(1)); } TEST(NativeWheelMatch, UnrelatedWingJointCannotAnimateWheel) { OwnershipFixture f; EXPECT_FALSE(f.matches(2)); } TEST(NativeWheelMatch, OpponentWithoutLocalMatrixIsRejected) { OwnershipFixture f; EXPECT_FALSE(f.matches(0)); } TEST(NativeWheelMatch, WheelPositionUnderAnotherMatrixIsRejected) { OwnershipFixture f; f.vertices[4] = 6; EXPECT_FALSE(f.matches(1)); EXPECT_TRUE(f.matches(4)) << "local body can occupy a different palette slot"; } TEST(NativeWheelMatch, MalformedMatrixSelectorRejected) { OwnershipFixture f; f.vertices[4] = 1; EXPECT_FALSE(f.matches(1)); } TEST(NativeWheelMatch, OutOfRangePositionIndexRejected) { OwnershipFixture f; f.vertices[11] = 3; EXPECT_FALSE(f.matches(1)); } TEST(NativeWheelMatch, SelectionTouchingAnotherJointCannotDeformIt) { OwnershipFixture f; f.replacement[24] = 1; EXPECT_FALSE(f.matches(1)); } TEST(NativeWheelMatch, EightBitIndicesAndLayoutValidation) { OwnershipFixture f; std::array indexed8{0, 1, 3, 2}; EXPECT_TRUE(aurora::NativeWheelDrawMatches(f.original, f.replacement, 12, indexed8, 2, 1, 1, 1)); EXPECT_FALSE(aurora::NativeWheelDrawMatches(f.original, f.replacement, 12, indexed8, 2, 2, 1, 1)) << "invalid vertex layout rejected"; EXPECT_FALSE(aurora::NativeWheelDrawMatches(f.original, f.original, 12, indexed8, 2, 1, 1, 1)) << "unchanged positions are not counted as animation"; } // Draw-time matching against the GX position-matrix palette. class NativeWheelArrayTest : public ::testing::Test { protected: void SetUp() override { aurora::gx::g_gxState = {}; aurora::gx::nativeWheelArrays.clear(); aurora::gx::nativeWheelMatches = 0; aurora::gx::NativeWheelArray replacement; replacement.source = source.data(); replacement.bytes.assign(36, 0); replacement.bytes[12] = 1; aurora::gx::nativeWheelArrays.push_back(replacement); array.data = source.data(); array.size = 36; array.stride = 12; } void TearDown() override { aurora::gx::g_gxState = {}; aurora::gx::nativeWheelArrays.clear(); } static float* pos(uint32_t slot) { return reinterpret_cast(&aurora::gx::g_gxState.pnMtx[slot].pos); } std::array source{}; aurora::gx::AttrArray array{}; }; TEST_F(NativeWheelArrayTest, MatchesLocalVehicleMatrixOnly) { EXPECT_NE(aurora::gx::native_wheel_array(array, nullptr, 0, 0, 0), nullptr); EXPECT_EQ(aurora::gx::nativeWheelMatches, 1u); // Same asset drawn by an opponent: a translated matrix. pos(0)[3] = 100; EXPECT_EQ(aurora::gx::native_wheel_array(array, nullptr, 0, 0, 0), nullptr); EXPECT_EQ(aurora::gx::nativeWheelMatches, 1u); } TEST_F(NativeWheelArrayTest, UnboundSourceIsNotAWheelArray) { std::array other{}; aurora::gx::AttrArray unrelated = array; unrelated.data = other.data(); EXPECT_EQ(aurora::gx::native_wheel_array(unrelated, nullptr, 0, 0, 0), nullptr); EXPECT_TRUE(aurora::gx::native_wheel_source(source.data())); EXPECT_FALSE(aurora::gx::native_wheel_source(other.data())); } TEST_F(NativeWheelArrayTest, MultiJointBodyChecksPerPositionOwnership) { // Multi-joint body with an independently animated wing, as in Mario's // mb/mc kart models. Only the wheel position uses the matching body slot. aurora::gx::g_gxState.vtxDesc[GX_VA_PNMTXIDX] = GX_DIRECT; aurora::gx::g_gxState.vtxDesc[GX_VA_POS] = GX_INDEX16; for (uint32_t slot = 0; slot < aurora::gx::MaxPnMtx; ++slot) pos(slot)[3] = 100; pos(2)[3] = 0; uint8_t vertices[]{6, 0, 1, 3, 0, 2}; EXPECT_NE(aurora::gx::native_wheel_array(array, vertices, sizeof(vertices), 3, 1), nullptr); vertices[0] = 0; // opponent wheel while an unrelated palette slot still matches EXPECT_EQ(aurora::gx::native_wheel_array(array, vertices, sizeof(vertices), 3, 1), nullptr); } TEST_F(NativeWheelArrayTest, NonFiniteMatrixNeverMatches) { pos(0)[0] = __builtin_nanf(""); EXPECT_EQ(aurora::gx::native_wheel_array(array, nullptr, 0, 0, 0), nullptr); } } // namespace