Skip to content

Commit

Permalink
Add mouse support and refactor.
Browse files Browse the repository at this point in the history
* rename addEventListener to subscribe
* rename dispatch to send
  • Loading branch information
Brad Phelan committed Mar 1, 2024
1 parent 4797df2 commit 2b906e4
Show file tree
Hide file tree
Showing 27 changed files with 432 additions and 615 deletions.
83 changes: 38 additions & 45 deletions examples/misc/mouse_key_listener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,35 @@ using namespace threepp;

namespace {

struct MyMouseListener: MouseListener {
void onMouseDown(MouseButtonEvent & e, double t) {
std::cout << "onMouseDown, button= " << e.button << ", pos=" << e.pos << " at t=" << t << std::endl;
}

float& t;
void onMouseUp(MouseButtonEvent & e, double t) {
std::cout << "onMouseUp, button= " << e.button << ", pos=" << e.pos << " at t=" << t << std::endl;
}

explicit MyMouseListener(float& t): t(t) {}
void onMouseMove(MouseMoveEvent &e, double t) {
std::cout << "onMouseMove, "
<< "pos=" << e.pos << " at t=" << t << std::endl;
}

void onMouseDown(int button, const Vector2& pos) override {
std::cout << "onMouseDown, button= " << button << ", pos=" << pos << " at t=" << t << std::endl;
}

void onMouseUp(int button, const Vector2& pos) override {
std::cout << "onMouseUp, button= " << button << ", pos=" << pos << " at t=" << t << std::endl;
}

void onMouseMove(const Vector2& pos) override {
std::cout << "onMouseMove, "
<< "pos=" << pos << " at t=" << t << std::endl;
}

void onMouseWheel(const Vector2& delta) override {
std::cout << "onMouseWheel, "
<< "delta=" << delta << " at t=" << t << std::endl;
}
};
void onMouseWheel(MouseWheelEvent &e, double t) {
std::cout << "onMouseWheel, "
<< "delta=" << e.offset << " at t=" << t << std::endl;
}

struct MyKeyListener: KeyListener {
void onKeyPressed(KeyEvent evt, double t) {
std::cout << "onKeyPressed at t=" << t << std::endl;
}

float& t;
void onKeyReleased(KeyEvent evt, double t) {
std::cout << "onKeyReleased at t=" << t << std::endl;
}

explicit MyKeyListener(float& t): t(t) {}

void onKeyPressed(KeyEvent evt) override {
std::cout << "onKeyPressed at t=" << t << std::endl;
}

void onKeyReleased(KeyEvent evt) override {
std::cout << "onKeyReleased at t=" << t << std::endl;
}

void onKeyRepeat(KeyEvent evt) override {
std::cout << "onKeyRepeat at t=" << t << std::endl;
}
};
void onKeyRepeat(KeyEvent evt, double t) {
std::cout << "onKeyRepeat at t=" << t << std::endl;
}

}// namespace

Expand All @@ -58,22 +44,29 @@ int main() {
Canvas canvas("Mouse and Key Listeners Demo");
Clock clock;

MyMouseListener ml{clock.elapsedTime};
MyKeyListener kl{clock.elapsedTime};
canvas.addMouseListener(ml);
canvas.addKeyListener(kl);
Subscriptions subs_;
subs_ << canvas.mouse.OnMouseDown.subscribe([&](auto& e) { onMouseDown(e, clock.elapsedTime); });
subs_ << canvas.mouse.OnMouseUp.subscribe([&](auto& e) { onMouseUp(e, clock.elapsedTime); });
subs_ << canvas.mouse.OnMouseMove.subscribe([&](auto& e) { onMouseMove(e, clock.elapsedTime); });
subs_ << canvas.mouse.OnMouseWheel.subscribe([&](auto& e) { onMouseWheel(e, clock.elapsedTime); });

Subscriptions key_subs_;
auto subscribe_keys = [&]() {
key_subs_ << canvas.keys.OnKeyPressed.subscribe([&](auto& e) {onKeyPressed(e, clock.elapsedTime); });
key_subs_ << canvas.keys.OnKeyReleased.subscribe([&](auto& e) {onKeyReleased(e, clock.elapsedTime); });
key_subs_ << canvas.keys.OnKeyRepeat.subscribe([&](auto& e) {onKeyRepeat(e, clock.elapsedTime); });
};

bool finish = false;
canvas.animate([&]() {
clock.getElapsedTime();

if (clock.elapsedTime > 2 && clock.elapsedTime < 4) {
if (canvas.removeKeyListener(kl)) {
std::cout << "removed key listener" << std::endl;
}
key_subs_.clear();
std::cout << "removed key listener" << std::endl;
} else if (!finish && clock.elapsedTime > 5) {
subscribe_keys();
std::cout << "re-added key listener" << std::endl;
canvas.addKeyListener(kl);
finish = true;
}
});
Expand Down
7 changes: 3 additions & 4 deletions examples/misc/raycast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@ int main() {
});

Vector2 mouse{-Infinity<float>, -Infinity<float>};
MouseMoveListener l([&](Vector2 pos) {
auto sub = canvas.mouse.OnMouseMove.subscribe([&](MouseMoveEvent& e) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components

auto size = canvas.size();
mouse.x = (pos.x / static_cast<float>(size.width)) * 2 - 1;
mouse.y = -(pos.y / static_cast<float>(size.height)) * 2 + 1;
mouse.x = (e.pos.x / static_cast<float>(size.width)) * 2 - 1;
mouse.y = -(e.pos.y / static_cast<float>(size.height)) * 2 + 1;
});
canvas.addMouseListener(l);

Raycaster raycaster;
raycaster.params.lineThreshold = 0.1f;
Expand Down
7 changes: 3 additions & 4 deletions examples/objects/instancing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,11 @@ int main() {
});

Vector2 mouse{-Infinity<float>, -Infinity<float>};
MouseMoveListener l([&](auto& pos) {
auto sub = canvas.mouse.OnMouseMove.subscribe([&](auto& e) {
auto size = canvas.size();
mouse.x = (pos.x / static_cast<float>(size.width)) * 2 - 1;
mouse.y = -(pos.y / static_cast<float>(size.height)) * 2 + 1;
mouse.x = (e.pos.x / static_cast<float>(size.width)) * 2 - 1;
mouse.y = -(e.pos.y / static_cast<float>(size.height)) * 2 + 1;
});
canvas.addMouseListener(l);


Clock clock;
Expand Down
7 changes: 3 additions & 4 deletions examples/objects/sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,11 @@ int main() {
});

Vector2 mouse{-Infinity<float>, -Infinity<float>};
MouseMoveListener l([&](auto& pos) {
auto sub = canvas.mouse.OnMouseMove.subscribe([&](MouseMoveEvent& e) {
auto size = canvas.size();
mouse.x = (pos.x / static_cast<float>(size.width)) * 2 - 1;
mouse.y = -(pos.y / static_cast<float>(size.height)) * 2 + 1;
mouse.x = (e.pos.x / static_cast<float>(size.width)) * 2 - 1;
mouse.y = -(e.pos.y / static_cast<float>(size.height)) * 2 + 1;
});
canvas.addMouseListener(l);

Clock clock;
Raycaster raycaster;
Expand Down
11 changes: 6 additions & 5 deletions examples/projects/Pathfinding/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ int main() {
Raycaster raycaster;
raycaster.layers.set(1); // ignore grid
Vector2 mouse{-Infinity<float>, -Infinity<float>};
MouseUpListener mouseListener([&](int button, Vector2 pos) {
TEventListener<MouseButtonEvent> mouseListener = [&](MouseButtonEvent & e) {
if (start && target) return;

const auto s = canvas.size();
mouse.x = (pos.x / static_cast<float>(s.width)) * 2 - 1;
mouse.y = -(pos.y / static_cast<float>(s.height)) * 2 + 1;
mouse.x = (e.pos.x / static_cast<float>(s.width)) * 2 - 1;
mouse.y = -(e.pos.y / static_cast<float>(s.height)) * 2 + 1;

raycaster.setFromCamera(mouse, camera);
auto intersects = raycaster.intersectObjects(scene.children);
Expand Down Expand Up @@ -139,8 +139,9 @@ int main() {
}
}
}
});
canvas.addMouseListener(mouseListener);
};

auto sub = canvas.mouse.OnMouseUp.subscribe(mouseListener);

canvas.onWindowResize([&](WindowSize s) {
camera.left = -size * s.aspect() / 2;
Expand Down
4 changes: 2 additions & 2 deletions examples/projects/Snake/SnakeScene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using namespace threepp;


class SnakeScene: public Scene, public KeyListener {
class SnakeScene: public Scene {

public:
explicit SnakeScene(SnakeGame& game): game_(game) {
Expand All @@ -34,7 +34,7 @@ class SnakeScene: public Scene, public KeyListener {
add(snake_.back());
}

void onKeyPressed(KeyEvent evt) override {
void onKeyPressed(KeyEvent evt) {

if (game_.isRunning()) {

Expand Down
2 changes: 1 addition & 1 deletion examples/projects/Snake/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ int main() {
renderer.autoClear = false;

auto scene = SnakeScene(game);
canvas.addKeyListener(scene);
auto sub = canvas.keys.OnKeyPressed.subscribe([&](auto& e) {scene.onKeyPressed(e); });

auto camera = OrthographicCamera::create(0, game.gridSize(), 0, game.gridSize());
camera->position.z = 1;
Expand Down
72 changes: 57 additions & 15 deletions include/threepp/core/EventDispatcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,77 @@

namespace threepp {


namespace concepts {
#if defined(__cpp_concepts) && (__cpp_concepts >= 201907L)
template<typename TEvent>
concept Event = requires(TEvent e) {
{ e.target } -> std::convertible_to<void*>;
{ e.unsubscribe } -> std::convertible_to<bool>;
};
#endif
}

/// An event listener is just a function that takes an argument of type TEvent
template<typename TEvent>
using TEventListener = std::function<void(TEvent&)>;

/// A single subscription for an event
using Subscription = std::shared_ptr<void>;

/// For holding a large number of subscriptions to events
using Subscriptions= std::vector<Subscription>;

/// Allows one to use the << to push subscriptions onto the vector
inline
void operator<<(std::vector<Subscription>& subs, Subscription const & sub) {
subs.push_back(sub);
}

/// Generic event dispatch class
template <typename TEvent>
#if defined(__cpp_concepts) && (__cpp_concepts >= 201907L)
requires concepts::Event<TEvent>
#endif
// C++20 (and later) code
class TEventDispatcher {
public:
using EventListener = TEventListener<TEvent>;

/// Adds an event listener and returns a subscription
[[nodiscard]] Subscription addEventListener(EventListener listener) {
[[nodiscard]] Subscription subscribe(EventListener listener) {
size_t current_id = id_.load();
listeners_.insert({current_id, listener});
listeners_.insert({ current_id, listener });
Subscription disposer((void*) nullptr, [this, current_id](void*) { listeners_.erase(current_id); });
id_ = id_ + 1;
return disposer;
}

/// Adds an event listener and assumes event.unsubscribe = true will be
/// used to unsubscribe. Useful for one shot events
void addEventListenerOwned( EventListener listener) {
size_t current_id = id_.load();
listeners_.insert({current_id, listener});
id_ = id_ + 1;
}
/// Adds an event listener and never automatically unsubscribes. You
/// can set event.unsubscribe = true and the subscription will be
/// cancelled. Not recommended to be used directly. Build other
/// tools on this.
void subscribeForever(EventListener listener) {
size_t current_id = id_.load();
listeners_.insert({ current_id, listener });
id_ = id_ + 1;
}

/// Adds an event listener and unsubscribes after a single shot
void addEventListenerOneShot( EventListener listener) {
this->addEventListenerOwned([l = std::move(listener)](TEvent& e) { l(e); e.unsubscribe = true; });
}
/// Adds an event listener that unsubscribes after a single shot
void subscribeOnce(EventListener listener) {
this->subscribeForever([l = std::move(listener)](TEvent& e) { l(e); e.unsubscribe = true; });
}

/// Hold onto the other subscription until the second source fires then
/// dispose the subscription.
template <typename T>
void subscribeUntil(TEventDispatcher<T>& s, EventListener listener) {
auto sub = subscribe(listener);
s.subscribeOnce([sub](auto&) {});
}

void dispatchEvent(TEvent & e){

/// Send an event to all listeners.
void send(TEvent & e){
std::vector<size_t> toUnsubscribe;
for (auto const& item : listeners_) {
item.second(e);
Expand All @@ -60,6 +97,11 @@ namespace threepp {
listeners_.erase(id);
}

/// Handle r-value versions of send
void send(TEvent && e) {
send(e);
}

virtual ~TEventDispatcher() = default;

private:
Expand Down
48 changes: 6 additions & 42 deletions include/threepp/input/KeyListener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

#include <functional>
#include <utility>
#include <threepp/core/EventDispatcher.hpp>

namespace threepp {

enum class Key;

struct KeyEvent {
struct KeyEvent : Event {

const Key key;
const int scancode;
Expand All @@ -19,50 +20,13 @@ namespace threepp {
: key(key), scancode(scancode), mods(mods) {}
};

struct KeyListener {

virtual void onKeyPressed(KeyEvent evt) {}

virtual void onKeyReleased(KeyEvent evt) {}

virtual void onKeyRepeat(KeyEvent evt) {}

virtual ~KeyListener() = default;
struct Keys {
TEventDispatcher<KeyEvent> OnKeyPressed;
TEventDispatcher<KeyEvent> OnKeyReleased;
TEventDispatcher<KeyEvent> OnKeyRepeat;
};

struct KeyAdapter: KeyListener {

enum Mode {
KEY_PRESSED = 1,
KEY_RELEASED = 2,
KEY_REPEAT = 4
};

KeyAdapter(const Mode& mode, std::function<void(KeyEvent)> f)
: mode_(mode), f_(std::move(f)) {}

void onKeyPressed(KeyEvent evt) override {
if (mode_ == 1 || mode_ == 3 || mode_ == 5) f_(evt);
}

void onKeyReleased(KeyEvent evt) override {
if (mode_ == 2 || mode_ == 3 || mode_ == 6) f_(evt);
}

void onKeyRepeat(KeyEvent evt) override {
if (mode_ == 4 || mode_ == 5 || mode_ == 6) f_(evt);
}

private:
Mode mode_;
std::function<void(KeyEvent)> f_;
};


inline KeyAdapter::Mode operator|(KeyAdapter::Mode a, KeyAdapter::Mode b) {
return static_cast<KeyAdapter::Mode>(static_cast<int>(a) | static_cast<int>(b));
}

enum class Key {

UNKNOWN,
Expand Down
Loading

0 comments on commit 2b906e4

Please sign in to comment.