diff --git a/lib/SDL b/lib/SDL index c9aec268..87a83787 160000 --- a/lib/SDL +++ b/lib/SDL @@ -1 +1 @@ -Subproject commit c9aec268fa7f892e183219683160599a4a2b86db +Subproject commit 87a83787a3a0a9922b02b35ba809d9da86930fc8 diff --git a/lib/openal-soft b/lib/openal-soft index fde74453..d66107e9 160000 --- a/lib/openal-soft +++ b/lib/openal-soft @@ -1 +1 @@ -Subproject commit fde74453a62a1ce4b5efaac0ec1835b9f5731e25 +Subproject commit d66107e9f008770b48f0df4fce041ee3e501e1e8 diff --git a/src/dawn/physics/3d/Ray3D.cpp b/src/dawn/physics/3d/Ray3D.cpp index 75f7111e..4db0ffb7 100644 --- a/src/dawn/physics/3d/Ray3D.cpp +++ b/src/dawn/physics/3d/Ray3D.cpp @@ -1,165 +1,203 @@ -// Copyright (c) 2023 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#include "Ray3D.hpp" - -using namespace Dawn; - -bool_t Dawn::raytestSphere( - struct Ray3D ray, - struct PhysicsSphere sphere, - glm::vec3 *hit, - glm::vec3 *normal -) { - assertNotNull(hit); - assertNotNull(normal); - - glm::vec3 h, n; - auto result = glm::intersectRaySphere( - ray.origin, ray.direction, - sphere.center, sphere.radius, - h, n - ); - - *hit = h; - *normal = n; - - return result; -} - -bool_t Dawn::raytestTriangle( - struct Ray3D ray, - struct PhysicsTriangle triangle, - glm::vec3 *hitPoint, - glm::vec3 *hitNormal, - float_t *hitDistance -) { - assertNotNull(hitPoint); - assertNotNull(hitNormal); - assertNotNull(hitDistance); - - // Calculate the normal of the triangle - glm::vec3 e0 = triangle.v1 - triangle.v0; - glm::vec3 e1 = triangle.v2 - triangle.v0; - glm::vec3 normal = glm::normalize(glm::cross(e0, e1)); - - // Calculate the denominator of the ray-triangle intersection formula - float_t denominator = glm::dot(normal, ray.direction); - - // If the denominator is zero, the ray and triangle are parallel and there is no intersection - if(denominator == 0) return -1; - - // Calculate the distance from the ray origin to the plane of the triangle - float_t d = glm::dot(triangle.v0 - ray.origin, normal) / denominator; - - // If the distance is negative, the intersection point is behind the ray origin and there is no intersection - if(d < 0) return -1; - - // Calculate the intersection point - glm::vec3 intersectionPoint = ray.origin + d * ray.direction; - - // Check if the intersection point is inside the triangle - glm::vec3 edge0 = triangle.v1 - triangle.v0; - glm::vec3 edge1 = triangle.v2 - triangle.v1; - glm::vec3 edge2 = triangle.v0 - triangle.v2; - glm::vec3 c0 = intersectionPoint - triangle.v0; - glm::vec3 c1 = intersectionPoint - triangle.v1; - glm::vec3 c2 = intersectionPoint - triangle.v2; - glm::vec3 n0 = glm::cross(edge0, c0); - glm::vec3 n1 = glm::cross(edge1, c1); - glm::vec3 n2 = glm::cross(edge2, c2); - if(glm::dot(n0, normal) >= 0 && glm::dot(n1, normal) >= 0 && glm::dot(n2, normal) >= 0) { - // If the intersection point is inside the triangle, set the hit point, normal and distance - *hitPoint = intersectionPoint; - *hitNormal = normal; - *hitDistance = d; - return true; - } - - // If the intersection point is outside the triangle, there is no intersection - return false; -} - -bool_t Dawn::raytestAABB( - struct Ray3D ray, - struct AABB3D box, - glm::vec3 *point, - glm::vec3 *normal, - float_t *distance -) { - assertNotNull(point); - assertNotNull(normal); - assertNotNull(distance); - - // Compute the inverse direction of the ray, for numerical stability - glm::vec3 invDir(1.0f / ray.direction.x, 1.0f / ray.direction.y, 1.0f / ray.direction.z); - - // Compute the t-values for the two intersection candidates - glm::vec3 tMin = (box.min - ray.origin) * invDir; - glm::vec3 tMax = (box.max - ray.origin) * invDir; - - // Make sure tMin is less than or equal to tMax for all components - glm::vec3 t1 = glm::min(tMin, tMax); - glm::vec3 t2 = glm::max(tMin, tMax); - float tNear = glm::compMax(t1); - float tFar = glm::compMin(t2); - - // If tNear is greater than or equal to tFar, there is no intersection - if(tNear >= tFar) return false; - - // If tFar is negative, the ray is pointing away from the box - if(tFar < 0.0f) return false; - - // Compute the hit point and normal - glm::vec3 hitPoint = ray.origin + tNear * ray.direction; - - *point = hitPoint; - *distance = tNear; - - // Small value to account for floating point imprecision - const float epsilon = 0.001f; - if(std::abs(hitPoint.x - box.min.x) < epsilon) { - *normal = glm::vec3(-1, 0, 0); - } else if(std::abs(hitPoint.x - box.max.x) < epsilon) { - *normal = glm::vec3(1, 0, 0); - } else if(std::abs(hitPoint.y - box.min.y) < epsilon) { - *normal = glm::vec3(0, -1, 0); - } else if(std::abs(hitPoint.y - box.max.y) < epsilon) { - *normal = glm::vec3(0, 1, 0); - } else if(std::abs(hitPoint.z - box.min.z) < epsilon) { - *normal = glm::vec3(0, 0, -1); - } else if(std::abs(hitPoint.z - box.max.z) < epsilon) { - *normal = glm::vec3(0, 0, 1); - } - - return true; -} - -bool_t Dawn::raytestCube( - struct Ray3D ray, - struct AABB3D box, - glm::mat4 transform, - glm::vec3 *point, - glm::vec3 *normal, - float_t *distance -) { - // Compute the inverse transformation matrix - glm::mat4 inverseTransform = glm::inverse(transform); - - // Transform the ray into model space - struct Ray3D localRay; - localRay.origin = glm::vec3(inverseTransform * glm::vec4(ray.origin, 1.0f)); - localRay.direction = glm::normalize(glm::vec3(inverseTransform * glm::vec4(ray.direction, 0.0f))); - - // Call raytestAABB with the transformed ray and cube - bool_t hit = raytestAABB(localRay, box, point, normal, distance); - if(!hit) return false; - - // Transform the hit point and normal back into world space - *point = glm::vec3(transform * glm::vec4(*point, 1.0f)); - *normal = glm::normalize(glm::vec3(glm::transpose(inverseTransform) * glm::vec4(*normal, 0.0f))); - - return true; +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "Ray3D.hpp" + +using namespace Dawn; + +bool_t Dawn::raytestSphere( + struct Ray3D ray, + struct PhysicsSphere sphere, + glm::vec3 *hit, + glm::vec3 *normal +) { + assertNotNull(hit); + assertNotNull(normal); + + glm::vec3 h, n; + auto result = glm::intersectRaySphere( + ray.origin, ray.direction, + sphere.center, sphere.radius, + h, n + ); + + *hit = h; + *normal = n; + + return result; +} + +bool_t Dawn::raytestTriangle( + struct Ray3D ray, + struct PhysicsTriangle triangle, + glm::vec3 *hitPoint, + glm::vec3 *hitNormal, + float_t *hitDistance +) { + assertNotNull(hitPoint); + assertNotNull(hitNormal); + assertNotNull(hitDistance); + + // Calculate the normal of the triangle + glm::vec3 e0 = triangle.v1 - triangle.v0; + glm::vec3 e1 = triangle.v2 - triangle.v0; + glm::vec3 normal = glm::normalize(glm::cross(e0, e1)); + + // Calculate the denominator of the ray-triangle intersection formula + float_t denominator = glm::dot(normal, ray.direction); + + // If the denominator is zero, the ray and triangle are parallel and there is no intersection + if(denominator == 0) return -1; + + // Calculate the distance from the ray origin to the plane of the triangle + float_t d = glm::dot(triangle.v0 - ray.origin, normal) / denominator; + + // If the distance is negative, the intersection point is behind the ray origin and there is no intersection + if(d < 0) return -1; + + // Calculate the intersection point + glm::vec3 intersectionPoint = ray.origin + d * ray.direction; + + // Check if the intersection point is inside the triangle + glm::vec3 edge0 = triangle.v1 - triangle.v0; + glm::vec3 edge1 = triangle.v2 - triangle.v1; + glm::vec3 edge2 = triangle.v0 - triangle.v2; + glm::vec3 c0 = intersectionPoint - triangle.v0; + glm::vec3 c1 = intersectionPoint - triangle.v1; + glm::vec3 c2 = intersectionPoint - triangle.v2; + glm::vec3 n0 = glm::cross(edge0, c0); + glm::vec3 n1 = glm::cross(edge1, c1); + glm::vec3 n2 = glm::cross(edge2, c2); + if(glm::dot(n0, normal) >= 0 && glm::dot(n1, normal) >= 0 && glm::dot(n2, normal) >= 0) { + // If the intersection point is inside the triangle, set the hit point, normal and distance + *hitPoint = intersectionPoint; + *hitNormal = normal; + *hitDistance = d; + return true; + } + + // If the intersection point is outside the triangle, there is no intersection + return false; +} + +bool_t Dawn::raytestAABB( + struct Ray3D ray, + struct AABB3D box, + glm::vec3 *point, + glm::vec3 *normal, + float_t *distance +) { + assertNotNull(point); + assertNotNull(normal); + assertNotNull(distance); + + // Compute the inverse direction of the ray, for numerical stability + glm::vec3 invDir(1.0f / ray.direction.x, 1.0f / ray.direction.y, 1.0f / ray.direction.z); + + // Compute the t-values for the two intersection candidates + glm::vec3 tMin = (box.min - ray.origin) * invDir; + glm::vec3 tMax = (box.max - ray.origin) * invDir; + + // Make sure tMin is less than or equal to tMax for all components + glm::vec3 t1 = glm::min(tMin, tMax); + glm::vec3 t2 = glm::max(tMin, tMax); + float tNear = glm::compMax(t1); + float tFar = glm::compMin(t2); + + // If tNear is greater than or equal to tFar, there is no intersection + if(tNear >= tFar) return false; + + // If tFar is negative, the ray is pointing away from the box + if(tFar < 0.0f) return false; + + // Compute the hit point and normal + glm::vec3 hitPoint = ray.origin + tNear * ray.direction; + + *point = hitPoint; + *distance = tNear; + + // Small value to account for floating point imprecision + const float epsilon = 0.001f; + if(std::abs(hitPoint.x - box.min.x) < epsilon) { + *normal = glm::vec3(-1, 0, 0); + } else if(std::abs(hitPoint.x - box.max.x) < epsilon) { + *normal = glm::vec3(1, 0, 0); + } else if(std::abs(hitPoint.y - box.min.y) < epsilon) { + *normal = glm::vec3(0, -1, 0); + } else if(std::abs(hitPoint.y - box.max.y) < epsilon) { + *normal = glm::vec3(0, 1, 0); + } else if(std::abs(hitPoint.z - box.min.z) < epsilon) { + *normal = glm::vec3(0, 0, -1); + } else if(std::abs(hitPoint.z - box.max.z) < epsilon) { + *normal = glm::vec3(0, 0, 1); + } + + return true; +} + +bool_t Dawn::raytestCube( + struct Ray3D ray, + struct AABB3D box, + glm::mat4 transform, + glm::vec3 *point, + glm::vec3 *normal, + float_t *distance +) { + // Compute the inverse transformation matrix + glm::mat4 inverseTransform = glm::inverse(transform); + + // Transform the ray into model space + struct Ray3D localRay; + localRay.origin = glm::vec3(inverseTransform * glm::vec4(ray.origin, 1.0f)); + localRay.direction = glm::normalize(glm::vec3(inverseTransform * glm::vec4(ray.direction, 0.0f))); + + // Call raytestAABB with the transformed ray and cube + bool_t hit = raytestAABB(localRay, box, point, normal, distance); + if(!hit) return false; + + // Transform the hit point and normal back into world space + *point = glm::vec3(transform * glm::vec4(*point, 1.0f)); + *normal = glm::normalize(glm::vec3(glm::transpose(inverseTransform) * glm::vec4(*normal, 0.0f))); + + 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 } \ No newline at end of file diff --git a/src/dawn/physics/3d/Ray3D.hpp b/src/dawn/physics/3d/Ray3D.hpp index b86ed017..d9560a05 100644 --- a/src/dawn/physics/3d/Ray3D.hpp +++ b/src/dawn/physics/3d/Ray3D.hpp @@ -1,50 +1,60 @@ -// Copyright (c) 2023 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#pragma once -#include "dawnlibs.hpp" -#include "assert/assert.hpp" -#include "PhysicsTriangle.hpp" -#include "PhysicsSphere.hpp" -#include "AABB3D.hpp" - -namespace Dawn { - struct Ray3D { - glm::vec3 origin; - glm::vec3 direction; - }; - - bool_t raytestSphere( - struct Ray3D ray, - struct PhysicsSphere sphere, - glm::vec3 *hit, - glm::vec3 *normal - ); - - bool_t raytestTriangle( - struct Ray3D ray, - struct PhysicsTriangle triangle, - glm::vec3 *hitPoint, - glm::vec3 *hitNormal, - float_t *hitDistance - ); - - bool_t raytestAABB( - struct Ray3D ray, - struct AABB3D box, - glm::vec3 *point, - glm::vec3 *normal, - float_t *distance - ); - - bool_t raytestCube( - struct Ray3D ray, - struct AABB3D box, - glm::mat4 transform, - glm::vec3 *point, - glm::vec3 *normal, - float_t *distance - ); -} +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "dawnlibs.hpp" +#include "assert/assert.hpp" +#include "PhysicsTriangle.hpp" +#include "PhysicsSphere.hpp" +#include "AABB3D.hpp" + +namespace Dawn { + struct Ray3D { + glm::vec3 origin; + glm::vec3 direction; + }; + + bool_t raytestSphere( + struct Ray3D ray, + struct PhysicsSphere sphere, + glm::vec3 *hit, + glm::vec3 *normal + ); + + bool_t raytestTriangle( + struct Ray3D ray, + struct PhysicsTriangle triangle, + glm::vec3 *hitPoint, + glm::vec3 *hitNormal, + float_t *hitDistance + ); + + bool_t raytestAABB( + struct Ray3D ray, + struct AABB3D box, + glm::vec3 *point, + glm::vec3 *normal, + float_t *distance + ); + + bool_t raytestCube( + struct Ray3D ray, + struct AABB3D box, + glm::mat4 transform, + glm::vec3 *point, + 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 + ); +} diff --git a/src/dawn/physics/ScenePhysicsManager.hpp b/src/dawn/physics/ScenePhysicsManager.hpp index 39989e90..51952821 100644 --- a/src/dawn/physics/ScenePhysicsManager.hpp +++ b/src/dawn/physics/ScenePhysicsManager.hpp @@ -1,34 +1,34 @@ -// Copyright (c) 2023 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#pragma once -#include "dawnlibs.hpp" -#include "physics/3d/Ray3D.hpp" - -typedef int64_t scenechunk_t; - -#define SCENE_CHUNK_SIZE_2D 512 - -namespace Dawn { - class Scene; - class Collider3D; - struct Collider3DRayResult; - - class ScenePhysicsManager { - protected: - Scene *scene; - // std::map> chunkItems; - // std::map> itemChunks; - - public: - ScenePhysicsManager(Scene *scene); - - void update(); - - std::vector raycast3DAll(struct Ray3D ray); - - friend class Scene; - }; +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "dawnlibs.hpp" +#include "physics/3d/Ray3D.hpp" + +typedef int64_t scenechunk_t; + +#define SCENE_CHUNK_SIZE_2D 512 + +namespace Dawn { + class Scene; + class Collider3D; + struct Collider3DRayResult; + + class ScenePhysicsManager { + protected: + Scene *scene; + // std::map> chunkItems; + // std::map> itemChunks; + + public: + ScenePhysicsManager(Scene *scene); + + void update(); + + std::vector raycast3DAll(struct Ray3D ray); + + friend class Scene; + }; } \ No newline at end of file diff --git a/src/dawn/scene/components/ui/CMakeLists.txt b/src/dawn/scene/components/ui/CMakeLists.txt index 324f1446..baaae6f9 100644 --- a/src/dawn/scene/components/ui/CMakeLists.txt +++ b/src/dawn/scene/components/ui/CMakeLists.txt @@ -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) \ No newline at end of file +add_subdirectory(menu) \ No newline at end of file diff --git a/src/dawn/scene/components/ui/UICanvas.hpp b/src/dawn/scene/components/ui/UICanvas.hpp index bbba0ac5..5b3fb25e 100644 --- a/src/dawn/scene/components/ui/UICanvas.hpp +++ b/src/dawn/scene/components/ui/UICanvas.hpp @@ -64,7 +64,7 @@ namespace Dawn { //======================================================================// StateProperty 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. diff --git a/src/dawn/scene/components/ui/UIMenuController.cpp b/src/dawn/scene/components/ui/UIMenuController.cpp deleted file mode 100644 index fd121a6c..00000000 --- a/src/dawn/scene/components/ui/UIMenuController.cpp +++ /dev/null @@ -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)(); -} \ No newline at end of file diff --git a/src/dawn/scene/components/ui/menu/CMakeLists.txt b/src/dawn/scene/components/ui/menu/CMakeLists.txt new file mode 100644 index 00000000..59bc6305 --- /dev/null +++ b/src/dawn/scene/components/ui/menu/CMakeLists.txt @@ -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 +) \ No newline at end of file diff --git a/src/dawn/scene/components/ui/menu/UIMenuController.cpp b/src/dawn/scene/components/ui/menu/UIMenuController.cpp new file mode 100644 index 00000000..7020fbb7 --- /dev/null +++ b/src/dawn/scene/components/ui/menu/UIMenuController.cpp @@ -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(menuX, 0, rows); + }, columns); + + useEffect([&]{ + menuY = mathClamp(menuY, 0, rows); + }, rows); + + return activeTeardown = useEvent([&](inputbind_t bind) { + switch(bind) { + case INPUT_BIND_POSITIVE_X: + menuX = mathClamp(menuX+1, 0, columns); + break; + case INPUT_BIND_POSITIVE_Y: + menuY = mathClamp(menuY+1, 0, rows); + break; + case INPUT_BIND_NEGATIVE_X: + menuX = mathClamp(menuX-1, 0, columns); + break; + case INPUT_BIND_NEGATIVE_Y: + menuY = mathClamp(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)(); +} \ No newline at end of file diff --git a/src/dawn/scene/components/ui/UIMenuController.hpp b/src/dawn/scene/components/ui/menu/UIMenuController.hpp similarity index 89% rename from src/dawn/scene/components/ui/UIMenuController.hpp rename to src/dawn/scene/components/ui/menu/UIMenuController.hpp index d5eefdc6..2bf759d3 100644 --- a/src/dawn/scene/components/ui/UIMenuController.hpp +++ b/src/dawn/scene/components/ui/menu/UIMenuController.hpp @@ -20,6 +20,9 @@ namespace Dawn { StateEvent eventItemChange; StateEvent eventItemSelected; + StateEvent<> eventMenuCancel; + + void moveRelative(int32_t x, int32_t y); UIMenuController(SceneItem *item); diff --git a/src/dawn/scene/components/ui/menu/UISimpleMenu.cpp b/src/dawn/scene/components/ui/menu/UISimpleMenu.cpp new file mode 100644 index 00000000..6acaf14f --- /dev/null +++ b/src/dawn/scene/components/ui/menu/UISimpleMenu.cpp @@ -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 UISimpleMenu::getDependencies() { + return { + (this->menu = this->item->getComponent()), + (this->canvas == nullptr ? (this->canvas = this->item->getComponent()) : nullptr) + }; +} + +void UISimpleMenu::onStart() { + if(canvas == nullptr) canvas = getScene()->findComponent(); + + assertNotNull(this->menu); + assertNotNull(this->canvas); + menuItems = this->item->findChildren(); + + 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); +} \ No newline at end of file diff --git a/src/dawn/scene/components/ui/menu/UISimpleMenu.hpp b/src/dawn/scene/components/ui/menu/UISimpleMenu.hpp new file mode 100644 index 00000000..ff96bb43 --- /dev/null +++ b/src/dawn/scene/components/ui/menu/UISimpleMenu.hpp @@ -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 menuItems; + + public: + UISimpleMenu(SceneItem *item); + + std::vector getDependencies() override; + void onStart() override; + }; +} \ No newline at end of file diff --git a/src/dawn/scene/components/ui/menu/UISimpleMenuItem.cpp b/src/dawn/scene/components/ui/menu/UISimpleMenuItem.cpp new file mode 100644 index 00000000..067b1980 --- /dev/null +++ b/src/dawn/scene/components/ui/menu/UISimpleMenuItem.cpp @@ -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(); + return uiComponent; +} \ No newline at end of file diff --git a/src/dawn/scene/components/ui/menu/UISimpleMenuItem.hpp b/src/dawn/scene/components/ui/menu/UISimpleMenuItem.hpp new file mode 100644 index 00000000..df456fa3 --- /dev/null +++ b/src/dawn/scene/components/ui/menu/UISimpleMenuItem.hpp @@ -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(); + }; +} \ No newline at end of file diff --git a/src/dawn/state/State.hpp b/src/dawn/state/State.hpp index a57f8a4f..f21c68d1 100644 --- a/src/dawn/state/State.hpp +++ b/src/dawn/state/State.hpp @@ -1,243 +1,245 @@ -// Copyright (c) 2023 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#pragma once -#include "StateEvent.hpp" -#include "StateProperty.hpp" - -namespace Dawn { - template - class StateOwnerEventLegacy : public IStateOwnerEventLegacy { - public: - IStateOwner *owner; - Event *event; - std::function fn; - - /** - * Function used to simply remove the event listener from the legacy - * event, does not deal with the state owner. - */ - void removeListener() override { - event->removeListener(this, &StateOwnerEventLegacy::callback); - } - - /** - * Function that can be used to tear down this legacy event. - */ - void teardown() override { - this->removeListener(); - owner->_stateLegacyEventDisposed(this); - } - - /** - * Callbaack method that is invoked by the legacy event. - * @param args Arguments received by legacy event. - */ - void callback(A... args) { - this->fn(args...); - } - }; - - class StateOwner : public IStateOwner { - private: - std::vector eventsSubscribed; - std::vector eventLegacyBridge; - - public: - /** - * Called by the state event when it is disposing (before the StateOwner - * itself is). - * - * @param evt Event that is being disposed. - */ - void _stateEventDisposed(IStateEvent *evt) override { - auto it = eventsSubscribed.begin(); - while(it != eventsSubscribed.end()) { - if(*it == evt) { - it = eventsSubscribed.erase(it); - } else { - ++it; - } - } - } - - /** - * Called by legacy events when they are being disposed in a way that was - * not called by this state owner disposing. - * - * @param evt Event that is being disposed. - */ - void _stateLegacyEventDisposed(IStateOwnerEventLegacy *evt) override { - auto it = this->eventLegacyBridge.begin(); - while(it != this->eventLegacyBridge.end()) { - if(*it == evt) { - this->eventLegacyBridge.erase(it); - break; - } else { - ++it; - } - } - } - - /** - * Listen for changes to a state property and invoke the provided func - * when the value is changed. - * - * @param fn The callback to be invoked when the state value changes. - * @param property Property to listen for affect changees to. - * @return Returns callback that invokes the provided FN immediately. - */ - std::function useEffect( - const std::function &fn, - IStateProperty &property - ) { - if(property.owner == nullptr) { - property.owner = this; - } else { - // TODO: This actually isn't needed, but because I need to teardown - // all the listeners on StateProperty<> that belong to this StateOwner - // I need to keep track of what events I've subbed to, consuming more - // memory, and I don't know if I actually need to do this or not. - assertTrue(property.owner == this); - } - property._effectListners.push_back(fn); - return fn; - } - - /** - * Listen for changes to a set of state properties and invoke the provided - * func when any of their values are changed. - * - * @param fn The callback to be invoked when the state value changes. - * @param property Vector list of properties to listen for changes to. - * @return Returns callback that invokes the provided FN immediately. - */ - std::function useEffect( - const std::function &fn, - std::vector props - ) { - auto itProp = props.begin(); - while(itProp != props.end()) { - auto property = *itProp; - if(property->owner == nullptr) { property->owner = this; } else { assertTrue(property->owner == this); } - property->_effectListners.push_back(fn); - ++itProp; - } - return fn; - } - - /** - * Listen for changes to a state property and invoke the provided callback - * also, when state is changed this will run the returned teardown - * callback. - * - * @param fn The callback to be invoked when the state value changes. - * @param property Vector list of properties to listen for changes to. - * @return Returns callback that invokes the provided FN immediately. - */ - std::function useEffectWithTeardown( - const std::function()> &fn, - IStateProperty &property - ) { - if(property.owner == nullptr) { property.owner = this; } else { assertTrue(property.owner == this); } - property._effectListnersWithTeardown.push_back(fn); - - return std::bind([&]( - std::function()> &callback, - IStateProperty *prop - ) { - auto teardown = callback(); - prop->_effectTeardowns.push_back(teardown); - }, fn, &property); - } - - /** - * Listen for when an event is invoked by a state event. This is intended - * to allow for cross-state-owner communication in a simple and effective - * way. - * - * @tparam F The type of the callback function. - * @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. - */ - template - std::function useEvent(F fn, StateEvent &event) { - // Create a listener structure - struct StateEventListener listener; - listener.listener = fn; - listener.owner = this; - listener.event = &event; - listener.unsubWithParams = [&](struct StateEventListener listener) { - auto itFound = listener.event->_eventListeners.begin(); - while(itFound != listener.event->_eventListeners.end()) { - if(itFound->id == listener.id) { - listener.event->_eventListeners.erase(itFound); - break; - } - ++itFound++; - } - }; - listener.id = event.stateEventId++; - listener.unsub = std::bind(listener.unsubWithParams, listener); - - // Put that listener structure on to the event stack - event._eventListeners.push_back(listener); - this->eventsSubscribed.push_back(&event); - - return listener.unsub; - } - - /** - * Listen for callback of a legacy styled event. This will be removed in - * the future in favour of everything using State Events. This uses a lot - * more memory than the new state event listener. - * - * @deprecated In favour of StateEvent<> - * @tparam F The type of the callback function. - * @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. - */ - template - std::function useEventLegacy( - F fn, - Event &event - ) { - // This is a legacy feature to make upgrading to the new useEffect a bit - // easier for me. For the time being I am just bodging this together to - // do what I need here. - auto bridge = new StateOwnerEventLegacy(); - bridge->owner = this; - bridge->event = &event; - bridge->fn = fn; - event.addListener(bridge, &StateOwnerEventLegacy::callback); - eventLegacyBridge.push_back(bridge); - - return std::bind([&](IStateOwnerEventLegacy *evt){ - evt->teardown(); - }, bridge); - } - - /** - * State Owner teardown function. Mostly just used to remove any lingering - * useEffects or useEvents. - */ - virtual ~StateOwner() { - auto it = this->eventsSubscribed.begin(); - while(it != this->eventsSubscribed.end()) { - (*it)->_stateOwnerDestroyed(this); - ++it; - } - - auto itBridge = this->eventLegacyBridge.begin(); - while(itBridge != this->eventLegacyBridge.end()) { - (*itBridge)->removeListener(); - delete *itBridge; - ++itBridge; - } - } - }; +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "StateEvent.hpp" +#include "StateProperty.hpp" + +namespace Dawn { + template + class StateOwnerEventLegacy : public IStateOwnerEventLegacy { + public: + IStateOwner *owner; + Event *event; + std::function fn; + + /** + * Function used to simply remove the event listener from the legacy + * event, does not deal with the state owner. + */ + void removeListener() override { + event->removeListener(this, &StateOwnerEventLegacy::callback); + } + + /** + * Function that can be used to tear down this legacy event. + */ + void teardown() override { + this->removeListener(); + owner->_stateLegacyEventDisposed(this); + } + + /** + * Callbaack method that is invoked by the legacy event. + * @param args Arguments received by legacy event. + */ + void callback(A... args) { + this->fn(args...); + } + }; + + class StateOwner : public IStateOwner { + private: + std::vector eventsSubscribed; + std::vector eventLegacyBridge; + + public: + /** + * Called by the state event when it is disposing (before the StateOwner + * itself is). + * + * @param evt Event that is being disposed. + */ + void _stateEventDisposed(IStateEvent *evt) override { + auto it = eventsSubscribed.begin(); + while(it != eventsSubscribed.end()) { + if(*it == evt) { + it = eventsSubscribed.erase(it); + } else { + ++it; + } + } + } + + /** + * Called by legacy events when they are being disposed in a way that was + * not called by this state owner disposing. + * + * @param evt Event that is being disposed. + */ + void _stateLegacyEventDisposed(IStateOwnerEventLegacy *evt) override { + auto it = this->eventLegacyBridge.begin(); + while(it != this->eventLegacyBridge.end()) { + if(*it == evt) { + this->eventLegacyBridge.erase(it); + break; + } else { + ++it; + } + } + } + + /** + * Listen for changes to a state property and invoke the provided func + * when the value is changed. + * + * @param fn The callback to be invoked when the state value changes. + * @param property Property to listen for affect changees to. + * @return Returns callback that invokes the provided FN immediately. + */ + std::function useEffect( + const std::function &fn, + IStateProperty &property + ) { + if(property.owner == nullptr) { + property.owner = this; + } else { + // TODO: This actually isn't needed, but because I need to teardown + // all the listeners on StateProperty<> that belong to this StateOwner + // I need to keep track of what events I've subbed to, consuming more + // memory, and I don't know if I actually need to do this or not. + assertTrue(property.owner == this); + } + property._effectListners.push_back(fn); + return fn; + } + + /** + * Listen for changes to a set of state properties and invoke the provided + * func when any of their values are changed. + * + * @param fn The callback to be invoked when the state value changes. + * @param property Vector list of properties to listen for changes to. + * @return Returns callback that invokes the provided FN immediately. + */ + std::function useEffect( + const std::function &fn, + std::vector props + ) { + auto itProp = props.begin(); + while(itProp != props.end()) { + auto property = *itProp; + if(property->owner == nullptr) { property->owner = this; } else { assertTrue(property->owner == this); } + property->_effectListners.push_back(fn); + ++itProp; + } + return fn; + } + + /** + * Listen for changes to a state property and invoke the provided callback + * also, when state is changed this will run the returned teardown + * callback. + * + * @param fn The callback to be invoked when the state value changes. + * @param property Vector list of properties to listen for changes to. + * @return Returns callback that invokes the provided FN immediately. + */ + std::function useEffectWithTeardown( + const std::function()> &fn, + IStateProperty &property + ) { + if(property.owner == nullptr) { property.owner = this; } else { assertTrue(property.owner == this); } + property._effectListnersWithTeardown.push_back(fn); + + return std::bind([&]( + std::function()> &callback, + IStateProperty *prop + ) { + auto teardown = callback(); + prop->_effectTeardowns.push_back(teardown); + }, fn, &property); + } + + /** + * Listen for when an event is invoked by a state event. This is intended + * to allow for cross-state-owner communication in a simple and effective + * way. + * + * @tparam F The type of the callback function. + * @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 + std::function useEvent(F fn, StateEvent &event) { + // Create a listener structure + struct StateEventListener listener; + listener.listener = fn; + listener.owner = this; + listener.event = &event; + listener.unsubWithParams = [&](struct StateEventListener listener) { + auto itFound = listener.event->_eventListeners.begin(); + while(itFound != listener.event->_eventListeners.end()) { + if(itFound->id == listener.id) { + listener.event->_eventListeners.erase(itFound); + break; + } + ++itFound++; + } + }; + listener.id = event.stateEventId++; + listener.unsub = std::bind(listener.unsubWithParams, listener); + + // Put that listener structure on to the event stack + event._eventListeners.push_back(listener); + this->eventsSubscribed.push_back(&event); + + return listener.unsub; + } + + /** + * Listen for callback of a legacy styled event. This will be removed in + * the future in favour of everything using State Events. This uses a lot + * more memory than the new state event listener. + * + * @deprecated In favour of StateEvent<> + * @tparam F The type of the callback function. + * @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 + std::function useEventLegacy( + F fn, + Event &event + ) { + // This is a legacy feature to make upgrading to the new useEffect a bit + // easier for me. For the time being I am just bodging this together to + // do what I need here. + auto bridge = new StateOwnerEventLegacy(); + bridge->owner = this; + bridge->event = &event; + bridge->fn = fn; + event.addListener(bridge, &StateOwnerEventLegacy::callback); + eventLegacyBridge.push_back(bridge); + + return std::bind([&](IStateOwnerEventLegacy *evt){ + evt->teardown(); + }, bridge); + } + + /** + * State Owner teardown function. Mostly just used to remove any lingering + * useEffects or useEvents. + */ + virtual ~StateOwner() { + auto it = this->eventsSubscribed.begin(); + while(it != this->eventsSubscribed.end()) { + (*it)->_stateOwnerDestroyed(this); + ++it; + } + + auto itBridge = this->eventLegacyBridge.begin(); + while(itBridge != this->eventLegacyBridge.end()) { + (*itBridge)->removeListener(); + delete *itBridge; + ++itBridge; + } + } + }; } \ No newline at end of file diff --git a/src/dawn/state/StateProperty.hpp b/src/dawn/state/StateProperty.hpp index 29c84750..b0dd1abf 100644 --- a/src/dawn/state/StateProperty.hpp +++ b/src/dawn/state/StateProperty.hpp @@ -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; } diff --git a/src/dawnglfw/host/DawnGLFWHost.cpp b/src/dawnglfw/host/DawnGLFWHost.cpp index f3e56122..648e2338 100644 --- a/src/dawnglfw/host/DawnGLFWHost.cpp +++ b/src/dawnglfw/host/DawnGLFWHost.cpp @@ -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); diff --git a/src/dawntictactoe/input/InputBinds.hpp b/src/dawntictactoe/input/InputBinds.hpp index 5a6dbe40..32fc1016 100644 --- a/src/dawntictactoe/input/InputBinds.hpp +++ b/src/dawntictactoe/input/InputBinds.hpp @@ -15,4 +15,5 @@ #define INPUT_BIND_POSITIVE_Y INPUT_BIND(5) #define INPUT_BIND_MOUSE_X INPUT_BIND(6) #define INPUT_BIND_MOUSE_Y INPUT_BIND(7) -#define INPUT_BIND_MOUSE_CLICK INPUT_BIND(8) \ No newline at end of file +#define INPUT_BIND_MOUSE_CLICK INPUT_BIND(8) +#define INPUT_BIND_CANCEL INPUT_BIND(9) \ No newline at end of file diff --git a/src/dawntictactoe/scenes/TicTacToeScene.hpp b/src/dawntictactoe/scenes/TicTacToeScene.hpp index 1a972b72..855fb3bc 100644 --- a/src/dawntictactoe/scenes/TicTacToeScene.hpp +++ b/src/dawntictactoe/scenes/TicTacToeScene.hpp @@ -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 evtUnsub; - UILabel *label; - StateProperty 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(); + auto menu = canvasItem->addComponent(); + auto simpleMenu = canvasItem->addComponent(); - auto labelItem = this->createSceneItem(); - label = labelItem->addComponent(); - label->font = &this->game->assetManager.get("truetype_bizudp")->font; - label->text = "Hello World"; - label->fontSize = 36.0f; - labelItem->transform.setParent(canvas->transform); + for(int32_t x = 0; x < 2; x++) { + for(int32_t y = 0; y < 2; y++) { + auto labelItem = this->createSceneItem(); + auto label = labelItem->addComponent(); + auto labelMenuItem = labelItem->addComponent(); - label->alignX = UI_COMPONENT_ALIGN_MIDDLE; - label->alignY = UI_COMPONENT_ALIGN_MIDDLE; - label->alignment = glm::vec4(0, 0, 350, 100); - label->maxWidth = 0; + label->font = &this->game->assetManager.get("truetype_bizudp")->font; + label->text = "Item " + std::to_string(x) + ", " + std::to_string(y); + // label->fontSize = 36.0f; + label->fontSize = 1.0f; + labelItem->transform.setParent(canvas->transform); - useEffect([&]{ - label->text = "Hello " + std::to_string(test) + " world"; - }, test); + label->alignment = glm::vec4(label->fontSize * 8 * x, label->fontSize * 1.5f * y, 350, 100); + label->maxWidth = 0; + + 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 getRequiredAssets() override { @@ -82,6 +95,6 @@ namespace Dawn { } public: - TicTacToeScene(DawnGame *game) : Scene(game), test(0) {} + TicTacToeScene(DawnGame *game) : Scene(game) {} }; } \ No newline at end of file