Skip to content

Commit

Permalink
Change picking to consider all room results first (#1198)
Browse files Browse the repository at this point in the history
Change picking to take results from all rooms and then proceed until it finds a wall, at which point it will give up. Items will be the priority up until that point.
Closes #1197
  • Loading branch information
chreden authored Nov 30, 2023
1 parent 685607d commit d05367b
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 48 deletions.
4 changes: 2 additions & 2 deletions trview.app.tests/Elements/LevelTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ TEST(Level, OcbAdjustmentsPerformedWhenNeeded)
auto room = mock_shared<MockRoom>();
PickResult result{};
result.hit = true;
EXPECT_CALL(*room, pick).WillRepeatedly(Return(result));
EXPECT_CALL(*room, pick).WillRepeatedly(Return(std::vector<PickResult> { result }));

auto level = register_test_module().with_level(std::move(mock_level_ptr))
.with_room_source(
Expand Down Expand Up @@ -215,7 +215,7 @@ TEST(Level, OcbAdjustmentsNotPerformedWhenNotNeeded)
auto room = mock_shared<MockRoom>();
PickResult result{};
result.hit = true;
EXPECT_CALL(*room, pick).WillRepeatedly(Return(result));
EXPECT_CALL(*room, pick).WillRepeatedly(Return(std::vector<PickResult> { result }));
return room;
})
.with_entity_source(
Expand Down
16 changes: 12 additions & 4 deletions trview.app.tests/Elements/RoomTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,9 @@ TEST(Room, PickTestsEntities)
EXPECT_CALL(*entity, pick).Times(1).WillOnce(Return(PickResult{ true, 0, {}, {}, PickResult::Type::Entity, 10 }));
room->add_entity(entity);

auto result = room->pick(Vector3(0, 0, -2), Vector3(0, 0, 1), PickFilter::Entities);
auto results = room->pick(Vector3(0, 0, -2), Vector3(0, 0, 1), PickFilter::Entities);
ASSERT_EQ(results.size(), 1);
auto result = results.front();
ASSERT_EQ(result.hit, true);
ASSERT_EQ(result.type, PickResult::Type::Entity);
ASSERT_EQ(result.index, 10);
Expand All @@ -365,7 +367,9 @@ TEST(Room, PickTestsTriggers)
EXPECT_CALL(*trigger, pick).Times(1).WillOnce(Return(PickResult{ true, 0, {}, {}, PickResult::Type::Trigger, 10 }));
room->add_trigger(trigger);

auto result = room->pick(Vector3(0, 0, -2), Vector3(0, 0, 1), PickFilter::Triggers);
auto results = room->pick(Vector3(0, 0, -2), Vector3(0, 0, 1), PickFilter::Triggers);
ASSERT_EQ(results.size(), 1);
auto result = results.front();
ASSERT_EQ(result.hit, true);
ASSERT_EQ(result.type, PickResult::Type::Trigger);
ASSERT_EQ(result.index, 10);
Expand All @@ -390,7 +394,9 @@ TEST(Room, PickChoosesClosest)
EXPECT_CALL(*entity2, pick).Times(1).WillOnce(Return(PickResult{ true, 1.0f, {}, {}, PickResult::Type::Entity, 10 }));
room->add_entity(entity2);

auto result = room->pick(Vector3(0, 0, -2), Vector3(0, 0, 1), PickFilter::Entities | PickFilter::Triggers);
auto results = room->pick(Vector3(0, 0, -2), Vector3(0, 0, 1), PickFilter::Entities | PickFilter::Triggers);
ASSERT_EQ(results.size(), 2);
auto result = results.front();
ASSERT_EQ(result.hit, true);
ASSERT_EQ(result.type, PickResult::Type::Entity);
ASSERT_EQ(result.index, 5);
Expand All @@ -415,7 +421,9 @@ TEST(Room, PickChoosesEntityOverTrigger)
EXPECT_CALL(*trigger, pick).Times(0);
room->add_trigger(trigger);

auto result = room->pick(Vector3(0, 0, -2), Vector3(0, 0, 1), PickFilter::Entities | PickFilter::Triggers);
auto results = room->pick(Vector3(0, 0, -2), Vector3(0, 0, 1), PickFilter::Entities | PickFilter::Triggers);
ASSERT_EQ(results.size(), 1);
auto result = results.front();
ASSERT_EQ(result.hit, true);
ASSERT_EQ(result.type, PickResult::Type::Entity);
ASSERT_EQ(result.index, 5);
Expand Down
2 changes: 1 addition & 1 deletion trview.app/Elements/IRoom.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ namespace trview
/// <param name="direction">The direction of the ray.</param>
/// <param name="filters">The types of objects to include the picking operation.</param>
/// <returns>The <see cref="PickResult"/>.</returns>
virtual PickResult pick(const DirectX::SimpleMath::Vector3& position, const DirectX::SimpleMath::Vector3& direction, PickFilter filters = PickFilter::Default) const = 0;
virtual std::vector<PickResult> pick(const DirectX::SimpleMath::Vector3& position, const DirectX::SimpleMath::Vector3& direction, PickFilter filters = PickFilter::Default) const = 0;
/// <summary>
/// Gets whether the room is a quicksand room based on the room flags. This can only be true if the game is TR3 or later.
/// </summary>
Expand Down
70 changes: 43 additions & 27 deletions trview.app/Elements/Level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,41 +642,53 @@ namespace trview
// is also specified.
PickResult Level::pick(const ICamera& camera, const Vector3& position, const Vector3& direction) const
{
PickResult final_result;

auto choose = [&](PickResult result)
std::vector<PickResult> results;
for (auto& room : get_rooms_to_render(camera))
{
// Choose the nearest pick - but if the previous closest was trigger an entity should take priority over it.
if (result.hit && (result.distance < final_result.distance || (result.type == PickResult::Type::Entity && final_result.type == PickResult::Type::Trigger)))
{
final_result.hit = true;
final_result.distance = result.distance;
final_result.position = result.position;
final_result.centroid = result.centroid;
final_result.index = result.index;
final_result.type = result.type;
final_result.triangle = result.triangle;
}
};

auto rooms = get_rooms_to_render(camera);
for (auto& room : rooms)
{
choose(room.room.pick(position, direction,
for (const auto& result : room.room.pick(position, direction,
filter_flag(PickFilter::Geometry, has_flag(_render_filters, RenderFilter::Rooms)) |
filter_flag(PickFilter::Entities, has_flag(_render_filters, RenderFilter::Entities)) |
filter_flag(PickFilter::StaticMeshes, has_flag(_render_filters, RenderFilter::Rooms)) |
filter_flag(PickFilter::AllGeometry, has_flag(_render_filters, RenderFilter::AllGeometry)) |
filter_flag(PickFilter::Triggers, has_flag(_render_filters, RenderFilter::Triggers)) |
filter_flag(PickFilter::Lights, has_flag(_render_filters, RenderFilter::Lights)) |
filter_flag(PickFilter::CameraSinks, has_flag(_render_filters, RenderFilter::CameraSinks))));
filter_flag(PickFilter::CameraSinks, has_flag(_render_filters, RenderFilter::CameraSinks))))
{
results.push_back(result);
}

if (!is_alternate_mismatch(room.room) && room.room.alternate_mode() == IRoom::AlternateMode::IsAlternate)
{
auto& original_room = _rooms[room.room.alternate_room()];
choose(original_room->pick(position, direction, PickFilter::Entities));
const auto& original_room = _rooms[room.room.alternate_room()];
for (const auto& result : original_room->pick(position, direction, PickFilter::Entities))
{
results.push_back(result);
}
}
}
return final_result;

std::sort(results.begin(), results.end(), [](const auto& l, const auto& r) { return l.distance < r.distance; });

std::optional<PickResult> actual_result;
if (!results.empty())
{
actual_result = results.front();
}

for (const auto& result : results)
{
if (result.type == PickResult::Type::Room)
{
return actual_result.value_or(result);
}

if (result.type == PickResult::Type::Entity)
{
actual_result = result;
}
}

return actual_result.value_or(PickResult {});
}

// Determines whether the room is currently being rendered.
Expand Down Expand Up @@ -962,10 +974,14 @@ namespace trview
{
const auto entity_pos = entity->bounding_box().Center;
const auto result = room->pick(Vector3(entity_pos.x, entity_pos.y, entity_pos.z), Vector3(0, 1, 0), PickFilter::Geometry | PickFilter::StaticMeshes);
if (result.hit)
if (!result.empty())
{
const auto new_height = result.position.y - entity->bounding_box().Extents.y;
entity->adjust_y(new_height - entity_pos.y);
const auto hit = result.front();
if (hit.hit)
{
const auto new_height = hit.position.y - entity->bounding_box().Extents.y;
entity->adjust_y(new_height - entity_pos.y);
}
}
}
}
Expand Down
18 changes: 6 additions & 12 deletions trview.app/Elements/Room.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,15 @@ namespace trview
return _neighbours;
}

PickResult Room::pick(const Vector3& position, const Vector3& direction, PickFilter filters) const
std::vector<PickResult> Room::pick(const Vector3& position, const Vector3& direction, PickFilter filters) const
{
using namespace DirectX::TriangleTests;

// Test against bounding box for the room first, to avoid more expensive mesh-ray intersection
float box_distance = 0;
if (!_bounding_box.Intersects(position, direction, box_distance))
{
return PickResult();
return {};
}

std::vector<PickResult> pick_results;
Expand All @@ -126,7 +126,7 @@ namespace trview
}
}

if (has_flag(filters, PickFilter::Lights) && pick_results.empty())
if (has_flag(filters, PickFilter::Lights))
{
for (const auto& light : _lights)
{
Expand All @@ -144,7 +144,7 @@ namespace trview
}
}

if (has_flag(filters, PickFilter::CameraSinks) && pick_results.empty())
if (has_flag(filters, PickFilter::CameraSinks))
{
for (const auto& camera_sink : _camera_sinks)
{
Expand All @@ -162,7 +162,7 @@ namespace trview
}
}

if (has_flag(filters, PickFilter::Triggers) && pick_results.empty())
if (has_flag(filters, PickFilter::Triggers))
{
for (const auto& trigger_pair : _triggers)
{
Expand Down Expand Up @@ -219,15 +219,9 @@ namespace trview
}
}

if (pick_results.empty())
{
return PickResult();
}

// Choose the closest pick out of all results.
std::sort(pick_results.begin(), pick_results.end(),
[](const auto& l, const auto& r) { return l.distance < r.distance; });
return pick_results.front();
return pick_results;
}

void Room::render(const ICamera& camera, SelectionMode selected, RenderFilter render_filter, const std::unordered_set<uint32_t>& visible_rooms)
Expand Down
2 changes: 1 addition & 1 deletion trview.app/Elements/Room.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace trview
Room& operator=(const Room&) = delete;
virtual RoomInfo info() const override;
virtual std::set<uint16_t> neighbours() const override;
virtual PickResult pick(const DirectX::SimpleMath::Vector3& position, const DirectX::SimpleMath::Vector3& direction, PickFilter filters = PickFilter::Default) const override;
virtual std::vector<PickResult> pick(const DirectX::SimpleMath::Vector3& position, const DirectX::SimpleMath::Vector3& direction, PickFilter filters = PickFilter::Default) const override;
virtual void render(const ICamera& camera, SelectionMode selected, RenderFilter render_filter, const std::unordered_set<uint32_t>& visible_rooms) override;
virtual void render_bounding_boxes(const ICamera& camera) override;
virtual void render_lights(const ICamera& camera, const std::weak_ptr<ILight>& selected_light) override;
Expand Down
2 changes: 1 addition & 1 deletion trview.app/Mocks/Elements/IRoom.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace trview
MOCK_METHOD(uint16_t, num_z_sectors, (), (const, override));
MOCK_METHOD(uint32_t, number, (), (const, override));
MOCK_METHOD(bool, outside, (), (const, override));
MOCK_METHOD(PickResult, pick, (const DirectX::SimpleMath::Vector3&, const DirectX::SimpleMath::Vector3&, PickFilter), (const, override));
MOCK_METHOD(std::vector<PickResult>, pick, (const DirectX::SimpleMath::Vector3&, const DirectX::SimpleMath::Vector3&, PickFilter), (const, override));
MOCK_METHOD(bool, quicksand, (), (const, override));
MOCK_METHOD(void, render, (const ICamera&, SelectionMode, RenderFilter, const std::unordered_set<uint32_t>&), (override));
MOCK_METHOD(void, render_bounding_boxes, (const ICamera&), (override));
Expand Down

0 comments on commit d05367b

Please sign in to comment.