208 lines
6.4 KiB
C++
208 lines
6.4 KiB
C++
// Copyright (c) 2022 Dominic Masters
|
|
//
|
|
// This software is released under the MIT License.
|
|
// https://opensource.org/licenses/MIT
|
|
|
|
#pragma once
|
|
#include "util/Math.hpp"
|
|
#include "input/InputBinds.hpp"
|
|
|
|
namespace Dawn {
|
|
class DawnGame;
|
|
|
|
template<typename T>
|
|
class IInputManager {
|
|
protected:
|
|
std::unordered_map<enum InputBind, std::vector<T>> binds;
|
|
std::unordered_map<enum InputBind, float_t> valuesLeft;
|
|
std::unordered_map<enum InputBind, float_t> valuesRight;
|
|
bool_t currentIsLeft = true;
|
|
|
|
/**
|
|
* Method to be overwritten by the host, reads a RAW input value from
|
|
* the pad/input device directly.
|
|
*
|
|
* @param axis Axis to get the value of.
|
|
* @return The current input value (between 0 and 1).
|
|
*/
|
|
virtual float_t getInputValue(const T axis) = 0;
|
|
|
|
public:
|
|
/**
|
|
* Binds an axis to a bind.
|
|
*
|
|
* @param bind Bind to bind the axis to.
|
|
* @param axis Axis to use for this bind.
|
|
*/
|
|
void bind(const enum InputBind bind, const T axis) {
|
|
this->binds[bind].push_back(axis);
|
|
}
|
|
|
|
/**
|
|
* Unbind a previously bound axis from a bind.
|
|
*
|
|
* @param bind Bind to remove all binds from.
|
|
*/
|
|
void unbind(const enum InputBind bind) {
|
|
this->binds[bind].clear();
|
|
}
|
|
|
|
/**
|
|
* Unbind all values, all of them.
|
|
*/
|
|
void unbindAll() {
|
|
this->binds.clear();
|
|
this->values.clear();
|
|
}
|
|
|
|
/**
|
|
* Return the current bind value.
|
|
*
|
|
* @param bind Bind to get the value of.
|
|
* @return The current input state (between 0 and 1).
|
|
*/
|
|
float_t getValue(const enum InputBind bind) {
|
|
if(this->currentIsLeft) {
|
|
auto exist = this->valuesLeft.find(bind);
|
|
return exist == this->valuesLeft.end() ? 0.0f : exist->second;
|
|
} else {
|
|
auto exist = this->valuesRight.find(bind);
|
|
return exist == this->valuesRight.end() ? 0.0f : exist->second;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the bind value from the previous frame.
|
|
*
|
|
* @param bind Bind to get the value of.
|
|
* @return The value of the bind, last frame.
|
|
*/
|
|
float_t getValueLastUpdate(const enum InputBind bind) {
|
|
if(this->currentIsLeft) {
|
|
auto exist = this->valuesRight.find(bind);
|
|
return exist == this->valuesRight.end() ? 0.0f : exist->second;
|
|
} else {
|
|
auto exist = this->valuesLeft.find(bind);
|
|
return exist == this->valuesLeft.end() ? 0.0f : exist->second;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns an axis input for a given negative and positive bind. This will
|
|
* combine and clamp the values to be between -1 and 1. For example, if
|
|
* the negative bind is 100% actuated and positive is not at all actuated,
|
|
* then the value will be -1. If instead positive is 50% actuated, then
|
|
* the value will be -0.5. If both are equally actuacted, then the value
|
|
* will be 0.
|
|
*
|
|
* @param negative Bind to use for the negative axis.
|
|
* @param positive Bind to use for the positive axis.
|
|
* @return A value between -1 and 1.
|
|
*/
|
|
float_t getAxis(const enum InputBind negative, const enum InputBind positive) {
|
|
return -getValue(negative) + getValue(positive);
|
|
}
|
|
|
|
glm::vec2 getAxis2D(
|
|
const enum InputBind negativeX,
|
|
const enum InputBind positiveX,
|
|
const enum InputBind negativeY,
|
|
const enum InputBind positiveY
|
|
) {
|
|
return glm::vec2(
|
|
getAxis(negativeX, positiveX),
|
|
getAxis(negativeY, positiveY)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns the 2D Axis for the given binds.
|
|
*
|
|
* @param x X Axis bind.
|
|
* @param y Y Axis bind.
|
|
* @return 2D vector of the two given input binds.
|
|
*/
|
|
glm::vec2 getAxis2D(const enum InputBind x, const enum InputBind y) {
|
|
return glm::vec2(getValue(x), getValue(y));
|
|
}
|
|
|
|
/**
|
|
* Returns true if the given bind is currently being pressed (a non-zero
|
|
* value).
|
|
*
|
|
* @param bind Bind to check if pressed.
|
|
* @return True if value is non-zero, or false for zero.
|
|
*/
|
|
bool_t isDown(const enum InputBind bind) {
|
|
return this->getValue(bind) != 0.0f;
|
|
}
|
|
|
|
/**
|
|
* Returns true on the first frame an input was pressed (when the state
|
|
* had changed from 0 to non-zero).
|
|
*
|
|
* @param bind Bind to check if pressed.
|
|
* @return True if down this frame and not down last frame.
|
|
*/
|
|
bool_t isPressed(const enum InputBind bind) {
|
|
return this->getValue(bind) != 0 && this->getValueLastUpdate(bind) == 0;
|
|
}
|
|
|
|
/**
|
|
* Returns true on the first frame an input was released (when the state
|
|
* had changed from non-zero to 0).
|
|
*
|
|
* @param bind Bind to check if released.
|
|
* @return True if up this frame, and down last frame.
|
|
*/
|
|
bool_t wasReleased(const enum InputBind bind) {
|
|
return this->getValue(bind) == 0 && this->getValueLastUpdate(bind) != 0;
|
|
}
|
|
|
|
/**
|
|
* Internal method to update the input state, checks current input raws
|
|
* and decides what values are set for the inputs.
|
|
*/
|
|
void update() {
|
|
auto it = this->binds.begin();
|
|
this->currentIsLeft = !this->currentIsLeft;
|
|
|
|
// For each bind...
|
|
while(it != this->binds.end()) {
|
|
float_t value = 0.0f, valCurrent;
|
|
|
|
// For each input axis...
|
|
auto bindIt = it->second.begin();
|
|
while(bindIt != it->second.end()) {
|
|
// Get value and make the new max.
|
|
float_t inputValue = this->getInputValue(*bindIt);
|
|
value = Math::max<float_t>(value, inputValue);
|
|
++bindIt;
|
|
}
|
|
|
|
// Set into current values
|
|
if(this->currentIsLeft) {
|
|
valCurrent = this->valuesRight[it->first];
|
|
this->valuesLeft[it->first] = value;
|
|
} else {
|
|
valCurrent = this->valuesLeft[it->first];
|
|
this->valuesRight[it->first] = value;
|
|
}
|
|
|
|
// Fire events when necessary.
|
|
if(value == 0 && valCurrent == 1) {
|
|
// eventBindReleased.invoke(it->first);
|
|
} else if(valCurrent == 0 && value == 1) {
|
|
// eventBindPressed.invoke(it->first);
|
|
}
|
|
|
|
++it;
|
|
}
|
|
|
|
// TODO: trigger events
|
|
}
|
|
|
|
virtual ~IInputManager() {
|
|
}
|
|
};
|
|
} |