Browse Source

Add pie menu and skybox rendering

master
C. J. Howard 6 years ago
parent
commit
157abcd052
20 changed files with 586 additions and 41 deletions
  1. +2
    -0
      CMakeLists.txt
  2. +1
    -1
      data
  3. +1
    -1
      lib/emergent
  4. +49
    -5
      src/application.cpp
  5. +15
    -0
      src/application.hpp
  6. +10
    -7
      src/game/ant.cpp
  7. +2
    -1
      src/game/ant.hpp
  8. +3
    -3
      src/game/colony.cpp
  9. +3
    -3
      src/game/habitat.cpp
  10. +1
    -1
      src/game/navmesh.cpp
  11. +17
    -0
      src/game/tool.hpp
  12. +100
    -2
      src/render-passes.cpp
  13. +30
    -0
      src/render-passes.hpp
  14. +46
    -3
      src/states/play-state.cpp
  15. +6
    -0
      src/states/play-state.hpp
  16. +218
    -0
      src/ui/pie-menu.cpp
  17. +65
    -0
      src/ui/pie-menu.hpp
  18. +5
    -2
      src/ui/toolbar.cpp
  19. +6
    -6
      src/ui/ui.cpp
  20. +6
    -6
      src/ui/ui.hpp

+ 2
- 0
CMakeLists.txt View File

@ -114,6 +114,8 @@ set(EXECUTABLE_SOURCES
${EXECUTABLE_SOURCE_DIR}/ui/tween.cpp
${EXECUTABLE_SOURCE_DIR}/ui/toolbar.hpp
${EXECUTABLE_SOURCE_DIR}/ui/toolbar.cpp
${EXECUTABLE_SOURCE_DIR}/ui/pie-menu.hpp
${EXECUTABLE_SOURCE_DIR}/ui/pie-menu.cpp
${EXECUTABLE_SOURCE_DIR}/render-passes.cpp
${EXECUTABLE_SOURCE_DIR}/game/ant.hpp
${EXECUTABLE_SOURCE_DIR}/game/ant.cpp

+ 1
- 1
data

@ -1 +1 @@
Subproject commit e100b6f66b464e97f3896153a9bb26e586d81271
Subproject commit 38fc778db84b0fd8c176c828a54fbf7145b948e1

+ 1
- 1
lib/emergent

@ -1 +1 @@
Subproject commit 0de6664dde3dfa888b1d6997ce73618c4981f25c
Subproject commit fbeb27d70b08fc64543ccf6cfcd0c7c67399e358

+ 49
- 5
src/application.cpp View File

@ -28,6 +28,7 @@
#include "states/play-state.hpp"
#include "game/colony.hpp"
#include "ui/toolbar.hpp"
#include "ui/pie-menu.hpp"
#include "debug.hpp"
#include "camera-controller.hpp"
#include <cstdlib>
@ -632,6 +633,10 @@ bool Application::loadScene()
bgCamera.setCompositor(&bgCompositor);
bgCamera.setCompositeIndex(0);
// Setup skybox pass
skyboxPass.setRenderTarget(&defaultRenderTarget);
defaultCompositor.addPass(&skyboxPass);
// Setup soil pass
soilPass.setRenderTarget(&defaultRenderTarget);
defaultCompositor.addPass(&soilPass);
@ -704,6 +709,7 @@ bool Application::loadUI()
pauseButtonTexture = textureLoader->load("data/textures/pause-button.png");
playButtonTexture = textureLoader->load("data/textures/play-button.png");
rectangularPaletteTexture = textureLoader->load("data/textures/rectangular-palette.png");
foodIndicatorTexture = textureLoader->load("data/textures/food-indicator.png");
toolBrushTexture = textureLoader->load("data/textures/tool-brush.png");
toolLensTexture = textureLoader->load("data/textures/tool-lens.png");
toolForcepsTexture = textureLoader->load("data/textures/tool-forceps.png");
@ -715,6 +721,14 @@ bool Application::loadUI()
toolbarButtonRaisedTexture = textureLoader->load("data/textures/toolbar-button-raised.png");
toolbarButtonDepressedTexture = textureLoader->load("data/textures/toolbar-button-depressed.png");
arcNorthTexture = textureLoader->load("data/textures/pie-menu-arc-north.png");
arcEastTexture = textureLoader->load("data/textures/pie-menu-arc-east.png");
arcSouthTexture = textureLoader->load("data/textures/pie-menu-arc-south.png");
arcWestTexture = textureLoader->load("data/textures/pie-menu-arc-west.png");
mouseLeftTexture = textureLoader->load("data/textures/mouse-left.png");
mouseRightTexture = textureLoader->load("data/textures/mouse-right.png");
// Get strings
std::string pressAnyKeyString;
std::string copyrightString;
@ -755,6 +769,9 @@ bool Application::loadUI()
selectedColor = Vector4(1.0f, 1.0f, 1.0f, 1.0f);
deselectedColor = Vector4(1.0f, 1.0f, 1.0f, 0.35f);
// Create tweener
tweener = new Tweener();
// Setup root UI element
uiRootElement = new UIContainer();
uiRootElement->setDimensions(Vector2(width, height));
@ -1018,14 +1035,28 @@ bool Application::loadUI()
uiRootElement->addChild(playButtonImage);
rectangularPaletteImage = new UIImage();
rectangularPaletteImage->setAnchor(Vector2(0.5f, 1.0f));
rectangularPaletteImage->setAnchor(Vector2(0.0f, 1.0f));
rectangularPaletteImage->setDimensions(Vector2(rectangularPaletteTexture->getWidth(), rectangularPaletteTexture->getHeight()));
rectangularPaletteImage->setTranslation(Vector2(0.0f, -16.0f));
rectangularPaletteImage->setTranslation(Vector2(16.0f, -16.0f));
rectangularPaletteImage->setTexture(rectangularPaletteTexture);
rectangularPaletteImage->setVisible(false);
rectangularPaletteImage->setActive(false);
uiRootElement->addChild(rectangularPaletteImage);
contextButtonImage0 = new UIImage();
contextButtonImage0->setAnchor(Vector2(0.5f, 1.0f));
contextButtonImage0->setDimensions(Vector2(mouseLeftTexture->getWidth(), mouseLeftTexture->getHeight()));
contextButtonImage0->setTranslation(Vector2(0.0f, -16.0f));
contextButtonImage0->setTexture(mouseLeftTexture);
uiRootElement->addChild(contextButtonImage0);
foodIndicatorImage = new UIImage();
foodIndicatorImage->setAnchor(Vector2(1.0f, 0.0f));
foodIndicatorImage->setDimensions(Vector2(foodIndicatorTexture->getWidth(), foodIndicatorTexture->getHeight()));
foodIndicatorImage->setTranslation(Vector2(-16.0f, 16.0f));
foodIndicatorImage->setTexture(foodIndicatorTexture);
uiRootElement->addChild(foodIndicatorImage);
// Create toolbar
toolbar = new Toolbar();
toolbar->setToolbarTopTexture(toolbarTopTexture);
@ -1038,12 +1069,22 @@ bool Application::loadUI()
toolbar->addButton(toolForcepsTexture, std::bind(SceneObject::setActive, &forcepsModelInstance, true), std::bind(SceneObject::setActive, &forcepsModelInstance, false));
toolbar->addButton(toolTrowelTexture, std::bind(std::printf, "3\n"), std::bind(std::printf, "3\n"));
toolbar->resize();
uiRootElement->addChild(toolbar->getContainer());
//uiRootElement->addChild(toolbar->getContainer());
toolbar->getContainer()->setVisible(false);
toolbar->getContainer()->setActive(false);
// Create tweener
tweener = new Tweener();
// Create pie menu
pieMenu = new PieMenu(tweener);
pieMenu->addOption(arcNorthTexture, toolLensTexture, std::bind(std::printf, "0 on\n"), std::bind(std::printf, "0 off\n"));
pieMenu->addOption(arcEastTexture, toolForcepsTexture, std::bind(std::printf, "1 on\n"), std::bind(std::printf, "1 off\n"));
pieMenu->addOption(arcSouthTexture, toolTrowelTexture, std::bind(std::printf, "2 on\n"), std::bind(std::printf, "2 off\n"));
pieMenu->addOption(arcWestTexture, toolBrushTexture, std::bind(std::printf, "3 on\n"), std::bind(std::printf, "3 off\n"));
uiRootElement->addChild(pieMenu->getContainer());
pieMenu->resize();
pieMenu->getContainer()->setVisible(false);
pieMenu->getContainer()->setActive(true);
// Setup screen fade in/fade out tween
fadeInTween = new Tween<Vector4>(EaseFunction::IN_CUBIC, 0.0f, 1.5f, Vector4(0.0f, 0.0f, 0.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, -1.0f));
@ -1523,6 +1564,9 @@ void Application::loadLevel()
std::string heightmap = std::string("data/textures/") + level->heightmap;
terrain.load(heightmap);
// Set skybox
skyboxPass.setCubemap(biome->specularCubemap);
changeState(playState);
}

+ 15
- 0
src/application.hpp View File

@ -50,6 +50,7 @@ class LineBatcher;
class ModelLoader;
class MaterialLoader;
class Toolbar;
class PieMenu;
/**
* Encapsulates the state of the application.
@ -173,6 +174,7 @@ public:
BillboardBatch bgBatch;
Compositor bgCompositor;
VignetteRenderPass vignettePass;
SkyboxRenderPass skyboxPass;
TextureLoader* textureLoader;
MaterialLoader* materialLoader;
ModelLoader* modelLoader;
@ -230,6 +232,7 @@ public:
Texture* pauseButtonTexture;
Texture* playButtonTexture;
Texture* rectangularPaletteTexture;
Texture* foodIndicatorTexture;
Texture* toolBrushTexture;
Texture* toolLensTexture;
Texture* toolForcepsTexture;
@ -241,6 +244,13 @@ public:
Texture* toolbarButtonRaisedTexture;
Texture* toolbarButtonDepressedTexture;
Texture* arcNorthTexture;
Texture* arcEastTexture;
Texture* arcSouthTexture;
Texture* arcWestTexture;
Texture* mouseLeftTexture;
Texture* mouseRightTexture;
// UI elements
Vector4 selectedColor;
Vector4 deselectedColor;
@ -287,7 +297,12 @@ public:
UIImage* playButtonImage;
UIImage* rectangularPaletteImage;
UIImage* foodIndicatorImage;
UIImage* contextButtonImage0;
UIImage* contextButtonImage1;
Toolbar* toolbar;
PieMenu* pieMenu;
// Animation
Tweener* tweener;

+ 10
- 7
src/game/ant.cpp View File

@ -90,7 +90,7 @@ void Ant::update(float dt)
AABB neighborhoodAABB(getPosition() - Vector3(neighborhoodSize * 0.5f), getPosition() + Vector3(neighborhoodSize * 0.5f));
std::list<Agent*> neighbors;
colony->queryAnts(neighborhoodAABB, &neighbors);
// Calculate separation force
Vector3 separationForce = separation(neighbors);
@ -107,7 +107,7 @@ void Ant::update(float dt)
// Move ant
move(velocity);
}
else
else if (state == Ant::State::IDLE)
{
Vector3 leftProbe = getForward() * probeForwardOffset - getRight() * probeLateralOffset;
Vector3 rightProbe = getForward() * probeForwardOffset + getRight() * probeLateralOffset;
@ -137,11 +137,14 @@ void Ant::update(float dt)
*/
// Update transform
transform.translation = getPosition();
transform.rotation = getRotation();
// Update model instance
modelInstance.setTransform(transform);
if (state != Ant::State::DEAD)
{
transform.translation = getPosition();
transform.rotation = getRotation();
// Update model instance
modelInstance.setTransform(transform);
}
}
Vector3 Ant::forage(const Vector3& leftReceptor, const Vector3& rightReceptor)

+ 2
- 1
src/game/ant.hpp View File

@ -57,7 +57,8 @@ public:
enum class State
{
IDLE,
WANDER
WANDER,
DEAD
};
/**

+ 3
- 3
src/game/colony.cpp View File

@ -24,8 +24,8 @@
Colony::Colony():
antModel(nullptr)
{
antOctree = new Octree<Agent*>(AABB(Vector3(-8.0f), Vector3(8.0f)), 5);
pheromoneOctree = new Octree<Pheromone*>(AABB(Vector3(-8.0f), Vector3(8.0f)), 5);
antOctree = new Octree<Agent*>(5, AABB(Vector3(-26.0f), Vector3(26.0f)));
pheromoneOctree = new Octree<Pheromone*>(5, AABB(Vector3(-26.0f), Vector3(26.0f)));
}
Colony::~Colony()
@ -53,7 +53,7 @@ void Colony::update(float dt)
antOctree->clear();
for (Ant* ant: ants)
{
antOctree->insert(AABB(ant->getPosition(), ant->getPosition()), ant);
antOctree->insert(ant->getModelInstance()->getBounds(), ant);
}
// Update ants

+ 3
- 3
src/game/habitat.cpp View File

@ -21,9 +21,9 @@
Habitat::Habitat(const AABB& bounds, int maxOctreeDepth)
{
obstacleOctree = new Octree<Navmesh*>(bounds, maxOctreeDepth);
pheromoneOctree = new Octree<Pheromone*>(bounds, maxOctreeDepth);
agentOctree = new Octree<Agent*>(bounds, maxOctreeDepth);
obstacleOctree = new Octree<Navmesh*>(maxOctreeDepth, bounds);
pheromoneOctree = new Octree<Pheromone*>(maxOctreeDepth, bounds);
agentOctree = new Octree<Agent*>(maxOctreeDepth, bounds);
}
Habitat::~Habitat()

+ 1
- 1
src/game/navmesh.cpp View File

@ -670,7 +670,7 @@ std::tuple intersects(const Ray& r
Octree<Navmesh::Triangle*>* Navmesh::createOctree(std::size_t maxDepth)
{
Octree<Navmesh::Triangle*>* result = new Octree<Navmesh::Triangle*>(bounds, maxDepth);
Octree<Navmesh::Triangle*>* result = new Octree<Navmesh::Triangle*>(maxDepth, bounds);
for (Navmesh::Triangle* triangle: triangles)
{

+ 17
- 0
src/game/tool.hpp View File

@ -0,0 +1,17 @@
enum class ToolContext
{
BRUSH_PAINT,
LENS_FOCUS,
FORCEPS_PINCH,
FORCEPS_RELEASE,
FORCEPS_PICK_UP,
FORCEPS_PUT_DOWN
};
class Tool
{
public:
ToolContext gatherContext();
};

+ 100
- 2
src/render-passes.cpp View File

@ -334,7 +334,7 @@ bool LightingRenderPass::load(const RenderContext* renderContext)
// Load cubemap
textureLoader.setCubemap(true);
textureLoader.setMipmapChain(false);
diffuseCubemap = textureLoader.load("data/textures/galileo-diffuse.png");
diffuseCubemap = textureLoader.load("data/textures/campus-diffuse.png");
if (!diffuseCubemap)
{
std::cerr << "Failed to load cubemap" << std::endl;
@ -342,7 +342,7 @@ bool LightingRenderPass::load(const RenderContext* renderContext)
textureLoader.setCubemap(true);
textureLoader.setMipmapChain(true);
specularCubemap = textureLoader.load("data/textures/galileo-specular_m%02d.png");
specularCubemap = textureLoader.load("data/textures/campus-specular_m%02d.png");
if (!specularCubemap)
{
std::cerr << "Failed to load cubemap" << std::endl;
@ -1260,3 +1260,101 @@ void VignetteRenderPass::render(const RenderContext* renderContext)
glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset);
}
}
SkyboxRenderPass::SkyboxRenderPass():
shader(nullptr),
cubemap(nullptr)
{
matrixParam = parameterSet.addParameter("matrix", ShaderParameter::Type::MATRIX_4, 1);
cubemapParam = parameterSet.addParameter("cubemap", ShaderParameter::Type::INT, 1);
}
bool SkyboxRenderPass::load(const RenderContext* renderContext)
{
shaderLoader.undefine();
shaderLoader.define("VERTEX_POSITION", EMERGENT_VERTEX_POSITION);
shader = shaderLoader.load("data/shaders/skybox.glsl", &parameterSet);
if (!shader)
{
return false;
}
const float quadVertexData[] =
{
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f
};
const std::uint32_t quadIndexData[] =
{
0, 1, 3,
3, 1, 2
};
quadVertexCount = 4;
quadIndexCount = 6;
// Create AABB geometry
glGenVertexArrays(1, &quadVAO);
glBindVertexArray(quadVAO);
glGenBuffers(1, &quadVBO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * quadVertexCount, quadVertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(EMERGENT_VERTEX_POSITION);
glVertexAttribPointer(EMERGENT_VERTEX_POSITION, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (char*)0 + 0*sizeof(float));
glGenBuffers(1, &quadIBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::uint32_t) * quadIndexCount, quadIndexData, GL_STATIC_DRAW);
return true;
}
void SkyboxRenderPass::unload()
{
delete shader;
shader = nullptr;
glDeleteBuffers(1, &quadIBO);
glDeleteBuffers(1, &quadVBO);
glDeleteVertexArrays(1, &quadVAO);
}
void SkyboxRenderPass::render(const RenderContext* renderContext)
{
if (!cubemap)
{
return;
}
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
//glDisable(GL_CULL_FACE);
//glCullFace(GL_BACK);
// Bind shader
shader->bind();
// Bind cubemap texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap->getTextureID());
// Pass texture unit to shader
shader->setParameter(cubemapParam, 0);
// Calculate matrix
const Camera& camera = *(renderContext->camera);
Matrix4 modelView = Matrix4(Matrix3(camera.getView()));
Matrix4 matrix = glm::inverse(modelView) * glm::inverse(camera.getProjection());
// Pass matrix to shader
shader->setParameter(matrixParam, matrix);
// Render quad
glBindVertexArray(quadVAO);
glDrawElementsBaseVertex(GL_TRIANGLES, quadIndexCount, GL_UNSIGNED_INT, (void*)0, 0);
}

+ 30
- 0
src/render-passes.hpp View File

@ -233,5 +233,35 @@ private:
GLuint bayerTextureID;
};
/**
* Renders a skybox
*/
class SkyboxRenderPass: public RenderPass
{
public:
SkyboxRenderPass();
inline void setCubemap(Texture* cubemap) { this->cubemap = cubemap; }
virtual bool load(const RenderContext* renderContext);
virtual void unload();
virtual void render(const RenderContext* renderContext);
private:
ShaderParameterSet parameterSet;
const ShaderParameter* matrixParam;
const ShaderParameter* cubemapParam;
ShaderLoader shaderLoader;
Shader* shader;
Texture* cubemap;
int quadVertexCount;
int quadIndexCount;
GLuint quadVAO;
GLuint quadVBO;
GLuint quadIBO;
};
#endif // RENDER_PASSES_HPP

+ 46
- 3
src/states/play-state.cpp View File

@ -100,6 +100,8 @@ void PlayState::enter()
application->simulationPaused = false;
application->mouse->addMouseButtonObserver(this);
pickAnt = nullptr;
}
void PlayState::execute()
@ -172,10 +174,8 @@ void PlayState::execute()
Vector3 mouseNear = application->camera.unproject(Vector3(mousePosition.x, mousePosition.y, 0.0f), viewport);
Vector3 mouseFar = application->camera.unproject(Vector3(mousePosition.x, mousePosition.y, 1.0f), viewport);
Ray pickingRay;
pickingRay.origin = mouseNear;
pickingRay.direction = glm::normalize(mouseFar - mouseNear);
Vector3 pick;
std::list<Navmesh::Triangle*> triangles;
application->terrain.getSurfaceOctree()->query(pickingRay, &triangles);
@ -186,7 +186,7 @@ void PlayState::execute()
pick = pickingRay.extrapolate(std::get<1>(result));
std::size_t triangleIndex = std::get<3>(result);
const Navmesh::Triangle* triangle = (*application->terrain.getSurfaceNavmesh()->getTriangles())[triangleIndex];
pickTriangle = (*application->terrain.getSurfaceNavmesh()->getTriangles())[triangleIndex];
float forcepsDistance = (application->forcepsClosed) ? 0.0f : 0.5f;
@ -201,6 +201,11 @@ void PlayState::execute()
application->forcepsModelInstance.setRotation(rotation);
}
if (pickAnt != nullptr)
{
pickAnt->getModelInstance()->setTranslation(pick);
}
// Update colony
if (!application->simulationPaused)
{
@ -235,6 +240,31 @@ void PlayState::mouseButtonPressed(int button, int x, int y)
if (button == 1)
{
application->forcepsClosed = true;
Sphere forcepsSphere = Sphere(pick, 0.35f);
std::list<Agent*> ants;
pickAnt = nullptr;
float closestDistance = std::numeric_limits<float>::infinity();
application->colony->queryAnts(forcepsSphere, &ants);
for (Agent* agent: ants)
{
Ant* ant = static_cast<Ant*>(agent);
Vector3 difference = ant->getPosition() - pick;
float distanceSquared = glm::dot(difference, difference);
if (distanceSquared < closestDistance)
{
closestDistance = distanceSquared;
pickAnt = ant;
}
}
if (pickAnt != nullptr)
{
pickAnt->setState(Ant::State::DEAD);
}
}
}
@ -243,5 +273,18 @@ void PlayState::mouseButtonReleased(int button, int x, int y)
if (button == 1)
{
application->forcepsClosed = false;
if (pickAnt != nullptr)
{
auto result = intersects(pickingRay, pickTriangle);
if (std::get<0>(result))
{
Vector3 barycentricPosition = Vector3(std::get<2>(result), std::get<3>(result), 1.0f - std::get<2>(result) - std::get<3>(result));
pickAnt->setPosition(pickTriangle, barycentricPosition);
}
pickAnt->setState(Ant::State::WANDER);
pickAnt = nullptr;
}
}
}

+ 6
- 0
src/states/play-state.hpp View File

@ -22,6 +22,8 @@
#include "../application-state.hpp"
#include "../input.hpp"
#include "../game/ant.hpp"
#include "../game/navmesh.hpp"
#include <emergent/emergent.hpp>
using namespace Emergent;
@ -42,6 +44,10 @@ public:
private:
ModelInstance terrainSurface;
ModelInstance terrainSubsurface;
Vector3 pick;
Ray pickingRay;
Navmesh::Triangle* pickTriangle;
Ant* pickAnt;
};
#endif // PLAY_STATE_HPP

+ 218
- 0
src/ui/pie-menu.cpp View File

@ -0,0 +1,218 @@
#include "pie-menu.hpp"
#include <cmath>
PieMenu::PieMenu(Tweener* tweener):
tweener(tweener),
scaleUpTween(nullptr),
scaleDownTween(nullptr),
scale(1.0f),
selectionIndex(0),
dragging(false),
dragStart(0.0f)
{
// Setup fullscreen container
fullscreenContainer.addChild(&croppedContainer);
fullscreenContainer.setMouseMovedCallback(std::bind(PieMenu::mouseMoved, this, std::placeholders::_1, std::placeholders::_2));
fullscreenContainer.setMousePressedCallback(std::bind(PieMenu::mouseButtonPressed, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
fullscreenContainer.setMouseReleasedCallback(std::bind(PieMenu::mouseButtonReleased, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
// Setup cropped container
croppedContainer.addChild(&scalingContainer);
// Setup scaling container
scalingContainer.setAnchor(Vector2(0.5f));
// Create tweens
scaleUpTween = new Tween<float>(EaseFunction::OUT_SINE, 0.0f, 0.1f, 0.0f, 1.0f);
scaleUpTween->setUpdateCallback(std::bind(PieMenu::setScale, this, std::placeholders::_1));
scaleDownTween = new Tween<float>(EaseFunction::IN_SINE, 0.0f, 0.1f, 1.0f, -1.0f);
scaleDownTween->setUpdateCallback(std::bind(PieMenu::setScale, this, std::placeholders::_1));
// Add tweens
tweener->addTween(scaleUpTween);
tweener->addTween(scaleDownTween);
}
void PieMenu::resize()
{
float iconDistance = 0.0f;
if (fullscreenContainer.getParent() == nullptr)
{
return;
}
// Resize fullscreen container
fullscreenContainer.setDimensions(fullscreenContainer.getParent()->getDimensions());
// Resize cropped container
croppedContainer.setDimensions(Vector2(options[0]->getTexture()->getWidth(), options[0]->getTexture()->getHeight()));
// Place options
for (std::size_t i = 0; i < options.size(); ++i)
{
float angle = glm::radians(360.0f) / static_cast<float>(i + 1) / static_cast<float>(options.size());
options[i]->setAnchor(Vector2(0.5f, 0.5f));
options[i]->setTranslation(Vector2(0.0f, 0.0f));
icons[i]->setAnchor(Vector2(0.5f, 0.5f));
icons[i]->setTranslation(Vector2(0.0f, 0.0f));
}
}
void PieMenu::setScale(float scale)
{
for (std::size_t i = 0; i < options.size(); ++i)
{
options[i]->setDimensions(Vector2(options[i]->getTexture()->getWidth(), options[i]->getTexture()->getHeight()) * scale);
icons[i]->setDimensions(Vector2(icons[i]->getTexture()->getWidth(), icons[i]->getTexture()->getHeight()) * scale);
}
}
void PieMenu::addOption(Texture* backgroundTexture, Texture* iconTexture, std::function<void()> selectedCallback, std::function<void()> deselectedCallback)
{
// Allocate new option
UIImage* option = new UIImage();
option->setTexture(backgroundTexture);
option->setDimensions(Vector2(backgroundTexture->getWidth(), backgroundTexture->getHeight()));
option->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.50f));
options.push_back(option);
UIImage* icon = new UIImage();
icon->setTexture(iconTexture);
icon->setDimensions(Vector2(iconTexture->getWidth(), iconTexture->getHeight()));
icon->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.50f));
icons.push_back(icon);
// Add icon to option
scalingContainer.addChild(icon);
// Add option to menu
scalingContainer.addChild(option);
// Setup callbacks
selectedCallbacks.push_back(selectedCallback);
deselectedCallbacks.push_back(deselectedCallback);
}
void PieMenu::select(std::size_t index)
{
if (index != selectionIndex && selectionIndex < options.size())
{
deselect(selectionIndex);
}
selectionIndex = index;
selectedCallbacks[index]();
}
void PieMenu::deselect(std::size_t index)
{
deselectedCallbacks[index]();
}
void PieMenu::highlight(std::size_t index)
{
options[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
icons[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
}
void PieMenu::unhighlight(std::size_t index)
{
options[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.50f));
icons[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.50f));
}
void PieMenu::mouseMoved(int x, int y)
{
if (dragging)
{
Vector2 direction = Vector2(x, y) - dragStart;
if (direction.x != 0.0f || direction.y != 0.0f)
{
direction = glm::normalize(direction);
// Calculate arc length
float arcLength = glm::radians(360.0f) / static_cast<float>(options.size());
// Calculate angle between cursor and pie menu
float angle = std::atan2(direction.y, direction.x) + glm::radians(90.0f) + arcLength * 0.5f;
while (angle < 0.0f) angle += glm::radians(360.0f);
// Determine option index from angle
std::size_t index = static_cast<std::size_t>(angle / arcLength);
if (index != highlightedIndex)
{
if (highlightedIndex < options.size())
{
unhighlight(highlightedIndex);
}
highlight(index);
highlightedIndex = index;
}
}
}
}
void PieMenu::mouseButtonPressed(int button, int x, int y)
{
if (button == 3)
{
// Start dragging
dragging = true;
dragStart.x = x;
dragStart.y = y;
// Set pie menu position
Vector2 halfDimensions = croppedContainer.getDimensions() * 0.5f;
croppedContainer.setTranslation(Vector2(x - halfDimensions.x, y - halfDimensions.y));
// Clear highlights
for (std::size_t i = 0; i < options.size(); ++i)
{
unhighlight(i);
}
// Reset highlighted index
highlightedIndex = options.size();
// Show pie menu
fullscreenContainer.setVisible(true);
// Start scale-up tween
scaleDownTween->stop();
scaleUpTween->start();
}
}
void PieMenu::mouseButtonReleased(int button, int x, int y)
{
if (button == 3)
{
// Stop dragging
dragging = false;
// Select highlighted index
if (highlightedIndex != selectionIndex && highlightedIndex < options.size())
{
select(highlightedIndex);
}
// Clear highlights
for (std::size_t i = 0; i < options.size(); ++i)
{
unhighlight(i);
}
// Reset highlighted index
highlightedIndex = options.size();
// Start scale-down tween
scaleUpTween->stop();
scaleDownTween->start();
//fullscreenContainer.setVisible(false);
}
}

+ 65
- 0
src/ui/pie-menu.hpp View File

@ -0,0 +1,65 @@
#ifndef PIE_MENU_HPP
#define PIE_MENU_HPP
#include "ui.hpp"
#include "tween.hpp"
#include <functional>
#include <vector>
#include <emergent/emergent.hpp>
using namespace Emergent;
class PieMenu
{
public:
PieMenu(Tweener* tweener);
void resize();
void addOption(Texture* backgroundTexture, Texture* iconTexture, std::function<void()> selectedCallback, std::function<void()> deselectedCallback);
void select(std::size_t index);
void deselect(std::size_t index);
const UIContainer* getContainer() const;
UIContainer* getContainer();
void mouseMoved(int x, int y);
void mouseButtonPressed(int button, int x, int y);
void mouseButtonReleased(int button, int x, int y);
void setScale(float scale);
private:
void highlight(std::size_t index);
void unhighlight(std::size_t index);
Tweener* tweener;
Tween<float>* scaleUpTween;
Tween<float>* scaleDownTween;
float scale;
UIContainer fullscreenContainer;
UIContainer croppedContainer;
UIContainer scalingContainer;
std::vector<UIImage*> options;
std::vector<UIImage*> icons;
std::vector<std::function<void()>> selectedCallbacks;
std::vector<std::function<void()>> deselectedCallbacks;
std::size_t selectionIndex;
bool dragging;
Vector2 dragStart;
std::size_t highlightedIndex;
};
inline const UIContainer* PieMenu::getContainer() const
{
return &fullscreenContainer;
}
inline UIContainer* PieMenu::getContainer()
{
return &fullscreenContainer;
}
#endif // PIE_MENU_HPP

+ 5
- 2
src/ui/toolbar.cpp View File

@ -94,6 +94,7 @@ void Toolbar::addButton(Texture* iconTexture, std::function pressCallbac
UIImage* icon = new UIImage();
icon->setTexture(iconTexture);
icon->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.30f));
icons.push_back(icon);
// Add button to toolbar
@ -124,7 +125,8 @@ void Toolbar::pressButton(std::size_t index)
{
depressedButtonIndex = index;
buttons[index]->setTexture(buttonDepressedTexture);
icons[index]->setTranslation(Vector2(2.0f, 2.0f));
//icons[index]->setTranslation(Vector2(2.0f, 2.0f));
icons[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
pressCallbacks[index]();
}
@ -135,7 +137,8 @@ void Toolbar::releaseButton(std::size_t index)
if (index < buttons.size())
{
buttons[index]->setTexture(buttonRaisedTexture);
icons[index]->setTranslation(Vector2(0.0f, 0.0f));
//icons[index]->setTranslation(Vector2(0.0f, 0.0f));
icons[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.30f));
releaseCallbacks[index]();
}

+ 6
- 6
src/ui/ui.cpp View File

@ -113,17 +113,17 @@ void UIElement::setMouseOutCallback(std::function callback)
mouseOutCallback = callback;
}
void UIElement::setMouseMovedCallback(std::function<void()> callback)
void UIElement::setMouseMovedCallback(std::function<void(int, int)> callback)
{
mouseMovedCallback = callback;
}
void UIElement::setMousePressedCallback(std::function<void(int)> callback)
void UIElement::setMousePressedCallback(std::function<void(int, int, int)> callback)
{
mousePressedCallback = callback;
}
void UIElement::setMouseReleasedCallback(std::function<void(int)> callback)
void UIElement::setMouseReleasedCallback(std::function<void(int, int, int)> callback)
{
mouseReleasedCallback = callback;
}
@ -148,7 +148,7 @@ void UIElement::mouseMoved(int x, int y)
if (mouseMovedCallback)
{
mouseMovedCallback();
mouseMovedCallback(x, y);
}
}
else if (mouseOver)
@ -177,7 +177,7 @@ void UIElement::mouseButtonPressed(int button, int x, int y)
{
if (mousePressedCallback)
{
mousePressedCallback(button);
mousePressedCallback(button, x, y);
}
for (UIElement* child: children)
@ -198,7 +198,7 @@ void UIElement::mouseButtonReleased(int button, int x, int y)
{
if (mouseReleasedCallback)
{
mouseReleasedCallback(button);
mouseReleasedCallback(button, x , y);
}
for (UIElement* child: children)

+ 6
- 6
src/ui/ui.hpp View File

@ -151,9 +151,9 @@ public:
void setMouseOverCallback(std::function<void()> callback);
void setMouseOutCallback(std::function<void()> callback);
void setMouseMovedCallback(std::function<void()> callback);
void setMousePressedCallback(std::function<void(int)> callback);
void setMouseReleasedCallback(std::function<void(int)> callback);
void setMouseMovedCallback(std::function<void(int, int)> callback);
void setMousePressedCallback(std::function<void(int, int, int)> callback);
void setMouseReleasedCallback(std::function<void(int, int, int)> callback);
void mouseMoved(int x, int y);
void mouseButtonPressed(int button, int x, int y);
@ -181,9 +181,9 @@ private:
bool mouseOver;
std::function<void()> mouseOverCallback;
std::function<void()> mouseOutCallback;
std::function<void()> mouseMovedCallback;
std::function<void(int)> mousePressedCallback;
std::function<void(int)> mouseReleasedCallback;
std::function<void(int, int)> mouseMovedCallback;
std::function<void(int, int, int)> mousePressedCallback;
std::function<void(int, int, int)> mouseReleasedCallback;
};
inline void UIElement::setAnchor(const Vector2& anchor)

Loading…
Cancel
Save