UI menu now accepts mouse input from absolutes.
This commit is contained in:
2
lib/SDL
2
lib/SDL
Submodule lib/SDL updated: c9aec268fa...87a83787a3
Submodule lib/openal-soft updated: fde74453a6...d66107e9f0
@ -163,3 +163,41 @@ bool_t Dawn::raytestCube(
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool_t Dawn::raytestQuad(
|
||||
struct Ray3D ray,
|
||||
glm::vec2 min,
|
||||
glm::vec2 max,
|
||||
glm::mat4 transform,
|
||||
glm::vec3 *point,
|
||||
glm::vec3 *normal,
|
||||
float_t *distance
|
||||
) {
|
||||
assertNotNull(point);
|
||||
assertNotNull(normal);
|
||||
assertNotNull(distance);
|
||||
|
||||
// transform ray into local space of the quad
|
||||
glm::mat4 inverseTransform = glm::inverse(transform);
|
||||
glm::vec3 localRayOrigin = glm::vec3(inverseTransform * glm::vec4(ray.origin, 1.0f));
|
||||
glm::vec3 localRayDirection = glm::vec3(inverseTransform * glm::vec4(ray.direction, 0.0f));
|
||||
|
||||
// perform ray-quad intersection test
|
||||
float_t t = -localRayOrigin.z / localRayDirection.z; // intersection distance along ray
|
||||
if (t < 0) return false; // intersection is behind the ray origin
|
||||
glm::vec2 intersectionPoint = glm::vec2(localRayOrigin) + t * glm::vec2(localRayDirection);
|
||||
if (
|
||||
glm::any(glm::lessThan(intersectionPoint, min)) ||
|
||||
glm::any(glm::greaterThan(intersectionPoint, max))
|
||||
) {
|
||||
return false; // intersection is outside the quad
|
||||
}
|
||||
*distance = t;
|
||||
|
||||
// compute point and normal of intersection in world space
|
||||
glm::vec3 localIntersectionPoint = glm::vec3(intersectionPoint, 0.0f);
|
||||
*point = glm::vec3(transform * glm::vec4(localIntersectionPoint, 1.0f));
|
||||
*normal = glm::normalize(glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)));
|
||||
|
||||
return true; // intersection found
|
||||
}
|
@ -47,4 +47,14 @@ namespace Dawn {
|
||||
glm::vec3 *normal,
|
||||
float_t *distance
|
||||
);
|
||||
|
||||
bool_t raytestQuad(
|
||||
struct Ray3D ray,
|
||||
glm::vec2 min,
|
||||
glm::vec2 max,
|
||||
glm::mat4 transform,
|
||||
glm::vec3 *point,
|
||||
glm::vec3 *normal,
|
||||
float_t *distance
|
||||
);
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ target_sources(${DAWN_TARGET_NAME}
|
||||
UICanvas.cpp
|
||||
UIComponent.cpp
|
||||
UILabel.cpp
|
||||
UIMenuController.cpp
|
||||
)
|
||||
|
||||
tool_scenecomponent(UICanvas scene/components/ui/UICanvas.hpp)
|
||||
add_subdirectory(menu)
|
@ -64,7 +64,7 @@ namespace Dawn {
|
||||
|
||||
//======================================================================//
|
||||
StateProperty<Camera*> camera;
|
||||
enum UIDrawType drawType = UI_DRAW_TYPE_WORLD_CAMERA_RELATIVE;
|
||||
enum UIDrawType drawType = UI_DRAW_TYPE_WORLD_ABSOLUTE;
|
||||
|
||||
/**
|
||||
* Constructs the UI Canvas Scene Item Component.
|
||||
|
@ -1,30 +0,0 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "UIMenuController.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
UIMenuController::UIMenuController(SceneItem *item) :
|
||||
SceneItemComponent(item),
|
||||
active(false),
|
||||
menuX(0),
|
||||
menuY(0),
|
||||
columns(4),
|
||||
rows(4)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UIMenuController::onStart() {
|
||||
useEffectWithTeardown([&]{
|
||||
if(!active) return activeTeardown = [&]{ };
|
||||
|
||||
return activeTeardown = [&]{
|
||||
|
||||
};
|
||||
}, active)();
|
||||
}
|
12
src/dawn/scene/components/ui/menu/CMakeLists.txt
Normal file
12
src/dawn/scene/components/ui/menu/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
# Copyright (c) 2022 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DAWN_TARGET_NAME}
|
||||
PRIVATE
|
||||
UIMenuController.cpp
|
||||
UISimpleMenu.cpp
|
||||
UISimpleMenuItem.cpp
|
||||
)
|
67
src/dawn/scene/components/ui/menu/UIMenuController.cpp
Normal file
67
src/dawn/scene/components/ui/menu/UIMenuController.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "UIMenuController.hpp"
|
||||
#include "game/DawnGame.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
UIMenuController::UIMenuController(SceneItem *item) :
|
||||
SceneItemComponent(item),
|
||||
active(true),
|
||||
menuX(0),
|
||||
menuY(0),
|
||||
columns(4),
|
||||
rows(4)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UIMenuController::onStart() {
|
||||
useEffectWithTeardown([&]{
|
||||
if(!active) return activeTeardown = [&]{ };
|
||||
|
||||
useEffect([&]{
|
||||
eventItemChange.invoke(menuX, menuY);
|
||||
}, menuX);
|
||||
|
||||
useEffect([&]{
|
||||
eventItemChange.invoke(menuX, menuY);
|
||||
}, menuY);
|
||||
|
||||
useEffect([&]{
|
||||
menuX = mathClamp<int32_t>(menuX, 0, rows);
|
||||
}, columns);
|
||||
|
||||
useEffect([&]{
|
||||
menuY = mathClamp<int32_t>(menuY, 0, rows);
|
||||
}, rows);
|
||||
|
||||
return activeTeardown = useEvent([&](inputbind_t bind) {
|
||||
switch(bind) {
|
||||
case INPUT_BIND_POSITIVE_X:
|
||||
menuX = mathClamp<int32_t>(menuX+1, 0, columns);
|
||||
break;
|
||||
case INPUT_BIND_POSITIVE_Y:
|
||||
menuY = mathClamp<int32_t>(menuY+1, 0, rows);
|
||||
break;
|
||||
case INPUT_BIND_NEGATIVE_X:
|
||||
menuX = mathClamp<int32_t>(menuX-1, 0, columns);
|
||||
break;
|
||||
case INPUT_BIND_NEGATIVE_Y:
|
||||
menuY = mathClamp<int32_t>(menuY-1, 0, rows);
|
||||
break;
|
||||
case INPUT_BIND_ACCEPT:
|
||||
eventItemSelected.invoke(menuX, menuY);
|
||||
break;
|
||||
case INPUT_BIND_CANCEL:
|
||||
eventMenuCancel.invoke();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}, this->getGame()->inputManager.eventBindPressed);
|
||||
}, active)();
|
||||
}
|
@ -20,6 +20,9 @@ namespace Dawn {
|
||||
|
||||
StateEvent<int32_t, int32_t> eventItemChange;
|
||||
StateEvent<int32_t, int32_t> eventItemSelected;
|
||||
StateEvent<> eventMenuCancel;
|
||||
|
||||
void moveRelative(int32_t x, int32_t y);
|
||||
|
||||
UIMenuController(SceneItem *item);
|
||||
|
110
src/dawn/scene/components/ui/menu/UISimpleMenu.cpp
Normal file
110
src/dawn/scene/components/ui/menu/UISimpleMenu.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "UISimpleMenu.hpp"
|
||||
#include "game/DawnGame.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
UISimpleMenu::UISimpleMenu(SceneItem *item) :
|
||||
SceneItemComponent(item)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::vector<SceneItemComponent*> UISimpleMenu::getDependencies() {
|
||||
return {
|
||||
(this->menu = this->item->getComponent<UIMenuController>()),
|
||||
(this->canvas == nullptr ? (this->canvas = this->item->getComponent<UICanvas>()) : nullptr)
|
||||
};
|
||||
}
|
||||
|
||||
void UISimpleMenu::onStart() {
|
||||
if(canvas == nullptr) canvas = getScene()->findComponent<UICanvas>();
|
||||
|
||||
assertNotNull(this->menu);
|
||||
assertNotNull(this->canvas);
|
||||
menuItems = this->item->findChildren<UISimpleMenuItem>();
|
||||
|
||||
auto updateSimpleMenuPos = [&](int32_t x, int32_t y) {
|
||||
if(currentlySelected != nullptr) {
|
||||
currentlySelected->eventHoveredOff.invoke();
|
||||
currentlySelected = nullptr;
|
||||
}
|
||||
|
||||
// Find item
|
||||
auto itItem = menuItems.begin();
|
||||
while(itItem != menuItems.end()) {
|
||||
auto itm = *itItem;
|
||||
if(itm->menuX == x && itm->menuY == y) {
|
||||
currentlySelected = itm;
|
||||
break;
|
||||
}
|
||||
++itItem;
|
||||
}
|
||||
|
||||
// Was anything found?
|
||||
if(currentlySelected == nullptr) return;
|
||||
currentlySelected->eventHoveredOn.invoke();
|
||||
};
|
||||
|
||||
useEvent(updateSimpleMenuPos, menu->eventItemChange);
|
||||
updateSimpleMenuPos(menu->menuX, menu->menuY);
|
||||
|
||||
useEvent([&](int32_t x, int32_t y){
|
||||
if(currentlySelected == nullptr) return;
|
||||
currentlySelected->eventSelected.invoke();
|
||||
}, menu->eventItemSelected);
|
||||
|
||||
useEvent([&](float_t d){
|
||||
assertNotNull(canvas->camera);
|
||||
if(!this->menu->active) return;
|
||||
|
||||
auto mouse = getGame()->inputManager.getAxis2D(INPUT_BIND_MOUSE_X, INPUT_BIND_MOUSE_Y);
|
||||
mouse *= 2.0f;
|
||||
mouse -= glm::vec2(1, 1);
|
||||
|
||||
struct Ray3D ray;
|
||||
ray.origin = canvas->camera->transform->getWorldPosition();
|
||||
ray.direction = canvas->camera->getRayDirectionFromScreenSpace(mouse);
|
||||
|
||||
switch(canvas->drawType) {
|
||||
case UI_DRAW_TYPE_WORLD_CAMERA_RELATIVE:
|
||||
break;
|
||||
|
||||
case UI_DRAW_TYPE_WORLD_ABSOLUTE:
|
||||
auto itItems = menuItems.begin();
|
||||
while(itItems != menuItems.end()) {
|
||||
auto item = *itItems;
|
||||
++itItems;
|
||||
auto highlight = item->getComponentForHighlighting();
|
||||
if(highlight == nullptr) continue;
|
||||
|
||||
glm::vec2 size(
|
||||
highlight->getContentWidth(),
|
||||
highlight->getContentHeight()
|
||||
);
|
||||
glm::vec3 point;
|
||||
glm::vec3 normal;
|
||||
float_t distance;
|
||||
|
||||
// TODO: Include Z axis.
|
||||
if(!raytestQuad(
|
||||
ray,
|
||||
glm::vec2(0, 0), size,
|
||||
highlight->transform->getWorldTransform(),
|
||||
&point,
|
||||
&normal,
|
||||
&distance
|
||||
)) continue;
|
||||
|
||||
this->menu->menuX = item->menuX;
|
||||
this->menu->menuY = item->menuY;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}, getScene()->eventSceneUnpausedUpdate);
|
||||
}
|
24
src/dawn/scene/components/ui/menu/UISimpleMenu.hpp
Normal file
24
src/dawn/scene/components/ui/menu/UISimpleMenu.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "UIMenuController.hpp"
|
||||
#include "UISimpleMenuItem.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class UISimpleMenu : public SceneItemComponent {
|
||||
protected:
|
||||
UIMenuController *menu = nullptr;
|
||||
UICanvas *canvas = nullptr;
|
||||
UISimpleMenuItem *currentlySelected = nullptr;
|
||||
std::vector<UISimpleMenuItem*> menuItems;
|
||||
|
||||
public:
|
||||
UISimpleMenu(SceneItem *item);
|
||||
|
||||
std::vector<SceneItemComponent*> getDependencies() override;
|
||||
void onStart() override;
|
||||
};
|
||||
}
|
17
src/dawn/scene/components/ui/menu/UISimpleMenuItem.cpp
Normal file
17
src/dawn/scene/components/ui/menu/UISimpleMenuItem.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "UISimpleMenuItem.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
UISimpleMenuItem::UISimpleMenuItem(SceneItem *item) : SceneItemComponent(item) {
|
||||
|
||||
}
|
||||
|
||||
UIComponent * UISimpleMenuItem::getComponentForHighlighting() {
|
||||
if(uiComponent == nullptr) uiComponent = item->getComponent<UIComponent>();
|
||||
return uiComponent;
|
||||
}
|
25
src/dawn/scene/components/ui/menu/UISimpleMenuItem.hpp
Normal file
25
src/dawn/scene/components/ui/menu/UISimpleMenuItem.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "scene/components/ui/UIComponent.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class UISimpleMenuItem : public SceneItemComponent {
|
||||
public:
|
||||
StateEvent<> eventHoveredOn;
|
||||
StateEvent<> eventHoveredOff;
|
||||
StateEvent<> eventSelected;
|
||||
|
||||
// May make these into either state events or make a new event.
|
||||
int32_t menuX;
|
||||
int32_t menuY;
|
||||
|
||||
UIComponent *uiComponent = nullptr;
|
||||
|
||||
UISimpleMenuItem(SceneItem *item);
|
||||
virtual UIComponent * getComponentForHighlighting();
|
||||
};
|
||||
}
|
@ -162,6 +162,7 @@ namespace Dawn {
|
||||
* @tparam A The arguments from the state event that are calledback.
|
||||
* @param fn The function to be inokved on event trigger.
|
||||
* @param event The event that is being subscribed to.
|
||||
* @return A method that, when invoked, will unsubscribe from the event.
|
||||
*/
|
||||
template<typename F, typename... A>
|
||||
std::function<void()> useEvent(F fn, StateEvent<A...> &event) {
|
||||
@ -200,6 +201,7 @@ namespace Dawn {
|
||||
* @tparam A Argument types for the event.
|
||||
* @param fn Callback function to be invoked when the event is triggered.
|
||||
* @param event Event that will be listened to.
|
||||
* @return A method that, when invoked, will unsubscribe from the event.
|
||||
*/
|
||||
template<typename F, typename... A>
|
||||
std::function<void()> useEventLegacy(
|
||||
|
@ -80,6 +80,28 @@ namespace Dawn {
|
||||
return *this;
|
||||
}
|
||||
|
||||
StateProperty& operator++() {
|
||||
this->setInternal(_realValue + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
V operator++(int) {
|
||||
V temp = _realValue;
|
||||
this->setInternal(_realValue + 1);
|
||||
return temp;
|
||||
}
|
||||
|
||||
StateProperty& operator--() {
|
||||
this->setInternal(_realValue - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
V operator--(int) {
|
||||
V temp = _realValue;
|
||||
this->setInternal(_realValue - 1);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const V operator->() const {
|
||||
return this->_realValue;
|
||||
}
|
||||
|
@ -61,10 +61,12 @@ int32_t DawnHost::init(DawnGame *game) {
|
||||
game->inputManager.bind(INPUT_BIND_ACCEPT, GLFW_KEY_ENTER);
|
||||
game->inputManager.bind(INPUT_BIND_ACCEPT, GLFW_KEY_E);
|
||||
game->inputManager.bind(INPUT_BIND_ACCEPT, GLFW_KEY_SPACE);
|
||||
game->inputManager.bind(INPUT_BIND_NEGATIVE_X, GLFW_KEY_A);
|
||||
game->inputManager.bind(INPUT_BIND_POSITIVE_X, GLFW_KEY_D);
|
||||
game->inputManager.bind(INPUT_BIND_NEGATIVE_Y, GLFW_KEY_S);
|
||||
game->inputManager.bind(INPUT_BIND_POSITIVE_Y, GLFW_KEY_W);
|
||||
game->inputManager.bind(INPUT_BIND_NEGATIVE_X, GLFW_KEY_A);
|
||||
game->inputManager.bind(INPUT_BIND_POSITIVE_Y, GLFW_KEY_S);
|
||||
game->inputManager.bind(INPUT_BIND_NEGATIVE_Y, GLFW_KEY_W);
|
||||
game->inputManager.bind(INPUT_BIND_CANCEL, GLFW_KEY_ESCAPE);
|
||||
game->inputManager.bind(INPUT_BIND_CANCEL, GLFW_KEY_Q);
|
||||
|
||||
game->inputManager.bind(INPUT_BIND_MOUSE_CLICK, INPUT_MANAGER_AXIS_MOUSE_0);
|
||||
game->inputManager.bind(INPUT_BIND_MOUSE_X, INPUT_MANAGER_AXIS_MOUSE_X);
|
||||
|
@ -16,3 +16,4 @@
|
||||
#define INPUT_BIND_MOUSE_X INPUT_BIND(6)
|
||||
#define INPUT_BIND_MOUSE_Y INPUT_BIND(7)
|
||||
#define INPUT_BIND_MOUSE_CLICK INPUT_BIND(8)
|
||||
#define INPUT_BIND_CANCEL INPUT_BIND(9)
|
@ -10,6 +10,7 @@
|
||||
#include "display/mesh/TriangleMesh.hpp"
|
||||
#include "components/TicTacToeGame.hpp"
|
||||
#include "scene/components/ui/UILabel.hpp"
|
||||
#include "scene/components/ui/menu/UISimpleMenu.hpp"
|
||||
|
||||
#include "state/State.hpp"
|
||||
|
||||
@ -20,13 +21,10 @@ namespace Dawn {
|
||||
Camera *camera;
|
||||
std::function<void()> evtUnsub;
|
||||
|
||||
UILabel *label;
|
||||
StateProperty<int32_t> test;
|
||||
|
||||
void stage() override {
|
||||
camera = Camera::create(this);
|
||||
// camera->transform->lookAt(glm::vec3(0, 0, 8), glm::vec3(0, 0, 0));
|
||||
camera->transform->lookAt(glm::vec3(32, 32, 32), glm::vec3(0, 0, 0));
|
||||
camera->transform->lookAt(glm::vec3(3, 3, 3), glm::vec3(0, 0, 0));
|
||||
|
||||
float_t s = 2.0f;
|
||||
camera->orthoTop = s;
|
||||
@ -50,26 +48,41 @@ namespace Dawn {
|
||||
|
||||
auto canvasItem = this->createSceneItem();
|
||||
auto canvas = canvasItem->addComponent<UICanvas>();
|
||||
auto menu = canvasItem->addComponent<UIMenuController>();
|
||||
auto simpleMenu = canvasItem->addComponent<UISimpleMenu>();
|
||||
|
||||
for(int32_t x = 0; x < 2; x++) {
|
||||
for(int32_t y = 0; y < 2; y++) {
|
||||
auto labelItem = this->createSceneItem();
|
||||
label = labelItem->addComponent<UILabel>();
|
||||
auto label = labelItem->addComponent<UILabel>();
|
||||
auto labelMenuItem = labelItem->addComponent<UISimpleMenuItem>();
|
||||
|
||||
label->font = &this->game->assetManager.get<TrueTypeAsset>("truetype_bizudp")->font;
|
||||
label->text = "Hello World";
|
||||
label->fontSize = 36.0f;
|
||||
label->text = "Item " + std::to_string(x) + ", " + std::to_string(y);
|
||||
// label->fontSize = 36.0f;
|
||||
label->fontSize = 1.0f;
|
||||
labelItem->transform.setParent(canvas->transform);
|
||||
|
||||
label->alignX = UI_COMPONENT_ALIGN_MIDDLE;
|
||||
label->alignY = UI_COMPONENT_ALIGN_MIDDLE;
|
||||
label->alignment = glm::vec4(0, 0, 350, 100);
|
||||
label->alignment = glm::vec4(label->fontSize * 8 * x, label->fontSize * 1.5f * y, 350, 100);
|
||||
label->maxWidth = 0;
|
||||
|
||||
useEffect([&]{
|
||||
label->text = "Hello " + std::to_string(test) + " world";
|
||||
}, test);
|
||||
labelMenuItem->menuX = x;
|
||||
labelMenuItem->menuY = y;
|
||||
|
||||
useEvent([&](float_t delta){
|
||||
test = test + 1;
|
||||
}, this->eventSceneUpdate);
|
||||
|
||||
useEvent(std::bind([&](UISimpleMenuItem *itm){
|
||||
std::cout << "Hover on " << std::to_string(itm->menuX) << ", " << std::to_string(itm->menuY) << std::endl;
|
||||
}, labelMenuItem), labelMenuItem->eventHoveredOn);
|
||||
|
||||
useEvent(std::bind([&](UISimpleMenuItem *itm){
|
||||
std::cout << "Hover off " << std::to_string(itm->menuX) << ", " << std::to_string(itm->menuY) << std::endl;
|
||||
}, labelMenuItem), labelMenuItem->eventHoveredOff);
|
||||
|
||||
useEvent([&]{
|
||||
std::cout << "Selected" << std::endl;
|
||||
}, labelMenuItem->eventSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Asset*> getRequiredAssets() override {
|
||||
@ -82,6 +95,6 @@ namespace Dawn {
|
||||
}
|
||||
|
||||
public:
|
||||
TicTacToeScene(DawnGame *game) : Scene(game), test(0) {}
|
||||
TicTacToeScene(DawnGame *game) : Scene(game) {}
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user