Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gamedev/fggl
  • onuralpsezer/fggl
2 results
Show changes
Showing
with 891 additions and 160 deletions
......@@ -13,65 +13,52 @@
*/
//
// Created by webpigeon on 20/11/2021.
// Created by webpigeon on 04/03/23.
//
// quick and dirty way to disable the class
#define FGGL_UTIL_SERVICE_HPP
#ifndef FGGL_GUI_MODEL_PARSER_HPP
#define FGGL_GUI_MODEL_PARSER_HPP
#ifndef FGGL_UTIL_SERVICE_HPP
#define FGGL_UTIL_SERVICE_HPP
#include <fggl/gui/model/structure.hpp>
#include <fggl/gui/fonts.hpp>
#include <fggl/modules/module.hpp>
#include <memory>
#include <typeindex>
#include <unordered_map>
#include <stdexcept>
#include <iostream>
namespace fggl::gui::model {
namespace fggl::util {
class ServiceLocator {
inline static ServiceLocator *s_instance;
std::unordered_map<std::type_index, std::shared_ptr<void>> m_services;
ServiceLocator() = default;
constexpr auto WIDGET_FACTORY_SERVICE = modules::make_service("gui::WidgetService");
class WidgetFactory {
public:
// don't allow copying or moving
ServiceLocator(ServiceLocator &other) = delete;
ServiceLocator(ServiceLocator &&other) = delete;
constexpr static auto service = WIDGET_FACTORY_SERVICE;
inline WidgetFactory(FontLibrary* lib) : m_fontLibrary(lib) {}
static ServiceLocator &instance() {
if (s_instance == nullptr) {
s_instance = new ServiceLocator();
}
return *s_instance;
inline Widget* build(std::string templateName) {
return new Widget(m_templates.at(templateName).get());
}
template<typename T>
std::shared_ptr<T> supply(std::shared_ptr<T> ptr) {
auto info = std::type_index(typeid(T));
m_services[info] = ptr;
return ptr;
inline Widget* buildEmpty() {
return new Widget(m_fontLibrary);
}
template<typename T>
std::shared_ptr<T> get() {
try {
auto info = std::type_index(typeid(T));
return std::static_pointer_cast<T>(m_services.at(info));
} catch ( std::out_of_range& e ){
std::cerr << "someone requested a service that doesn't exist!" << std::endl;
return nullptr;
}
inline void push(std::string name, const Widget&& definition) {
m_templates.emplace(name, std::make_unique<Widget>(definition));
}
template<typename T>
std::shared_ptr<T> providePtr() {
return get<T>();
Widget* getTemplate(const std::string& name) {
auto itr = m_templates.find(name);
if ( itr == m_templates.end() ){
return nullptr;
}
return itr->second.get();
}
private:
std::map<std::string, std::unique_ptr<Widget>> m_templates;
FontLibrary* m_fontLibrary;
};
}
Widget* parseFile(WidgetFactory& factory, const std::string& path);
} // namespace fggl::gui::model
#endif //FGGL_UTIL_SERVICE_HPP
#endif //FGGL_GUI_MODEL_PARSER_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 04/03/23.
//
#ifndef FGGL_GUI_MODEL_STRUCTURE_HPP
#define FGGL_GUI_MODEL_STRUCTURE_HPP
#include <map>
#include <variant>
#include <vector>
#include <string>
#include <optional>
#include <fggl/math/types.hpp>
#include <fggl/gui/fonts.hpp>
namespace fggl::gui::model {
using AttrKey = std::string;
using AttrValue = std::variant<bool, int, float, std::string, math::vec2, math::vec3>;
constexpr float UNDEFINED = INFINITY;
constexpr math::vec2 UNDEFINED_SIZE{INFINITY, INFINITY};
class Widget {
public:
using ChildItr = std::vector<Widget>::iterator;
using ChildItrConst = std::vector<Widget>::const_iterator;
inline Widget(FontLibrary* library) :
m_parent(nullptr),
m_children(),
m_fontLibrary(library),
m_attrs(),
m_cachedSize(),
m_dirty(true) {
}
explicit inline Widget(Widget* parent) :
m_parent(parent),
m_children(),
m_fontLibrary(parent->m_fontLibrary),
m_attrs(),
m_cachedSize(),
m_dirty(true) {
}
inline bool isRoot() const {
return m_parent == nullptr;
}
inline bool isLeaf() const {
return m_children.empty();
}
inline bool hasAttr(const AttrKey& key) const {
return m_attrs.contains(key);
}
inline std::map<AttrKey, AttrValue> attrs() {
return m_attrs;
}
template<typename type>
inline type get(const AttrKey& key) const {
return std::get<type>(m_attrs.at(key));
}
template<typename type>
type get_or_default(const AttrKey& key) const {
auto itr = m_attrs.find(key);
if ( itr == m_attrs.end() ) {
return type();
} else {
return std::get<type>(itr->second);
}
}
template<typename type>
inline void set(const AttrKey& key, type value) {
m_attrs[key] = value;
}
inline void addChild(const Widget& element) {
auto childItr = m_children.insert( m_children.cend(), element );
childItr->m_parent = this;
}
inline ChildItr begin() {
return m_children.begin();
}
inline ChildItrConst begin() const noexcept {
return m_children.begin();
}
inline ChildItrConst cbegin() const noexcept {
return m_children.cbegin();
}
inline ChildItr end() {
return m_children.end();
}
inline ChildItrConst end() const {
return m_children.end();
}
inline ChildItrConst cend() const {
return m_children.cend();
}
inline std::optional<math::vec2> preferredSize() {
calcPrefSize(m_fontLibrary->getDefaultFont());
return m_cachedSize;
}
private:
Widget *m_parent;
std::vector<Widget> m_children;
FontLibrary* m_fontLibrary;
std::map< AttrKey, AttrValue > m_attrs;
math::vec2 m_cachedSize;
bool m_dirty;
void calcPrefSize(std::shared_ptr<FontFace> face);
};
void attr_box_set(Widget& widget, const AttrKey& key, auto top, auto right, auto bottom, auto left) {
widget.set(key + "::top", top);
widget.set(key + "::right", right);
widget.set(key + "::bottom", bottom);
widget.set(key + "::left", left);
}
inline void attr_box_set(Widget& widget, const AttrKey& key, auto vert, auto horz) {
attr_box_set(widget, key, vert, horz, vert, horz);
}
inline void attr_box_set(Widget& widget, const AttrKey& key, auto value) {
attr_box_set(widget, key, value, value, value, value);
}
} // namespace fggl::gui::model
#endif //FGGL_GUI_MODEL_STRUCTURE_HPP
......@@ -20,30 +20,55 @@
#define FGGL_GUI_MODULE_HPP
#include "fggl/gui/fonts.hpp"
#include "fggl/gui/model/parser.hpp"
#include "fggl/data/module.hpp"
#include "fggl/assets/packed/module.hpp"
namespace fggl::gui {
constexpr auto MIME_TTF = assets::from_mime("font/ttf");
constexpr auto ASSET_FONT_TTF = assets::make_asset_type("font/ttf");
static assets::AssetTypeID is_font(std::filesystem::path path) {
if ( path.extension() == ".ttf" ){
return ASSET_FONT_TTF;
}
return assets::INVALID_ASSET_TYPE;
}
struct FreeType {
constexpr static const char* name = "fggl::gui::FreeType";
constexpr static const std::array<modules::ModuleService, 1> provides = {
FontLibrary::service
constexpr static const char *name = "fggl::gui::FreeType";
constexpr static const std::array<modules::ServiceName, 2> provides = {
FontLibrary::service,
model::WidgetFactory::service
};
constexpr static const std::array<modules::ModuleService, 1> depends = {
data::Storage::service
constexpr static const std::array<modules::ServiceName, 2> depends = {
data::Storage::service,
assets::CheckinAdapted::service
};
static const modules::ServiceFactory factory;
static bool factory(modules::ServiceName name, modules::Services &serviceManager);
};
bool freetype_factory(modules::ModuleService service, modules::Services& services) {
if ( service == FontLibrary::service ) {
bool FreeType::factory(modules::ServiceName service, modules::Services &services) {
if (service == FontLibrary::service) {
auto storage = services.get<data::Storage>();
services.create< FontLibrary >(storage);
services.create<FontLibrary>(storage);
auto* checkin = services.get<assets::CheckinAdapted>();
checkin->setLoader( MIME_TTF, assets::NEEDS_CHECKIN, is_font );
return true;
}
if ( service == model::WidgetFactory::service ) {
auto fonts = services.get<FontLibrary>();
services.create<model::WidgetFactory>(fonts);
return true;
}
return false;
}
const modules::ServiceFactory FreeType::factory = freetype_factory;
}
#endif //FGGL_GUI_MODULE_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 04/03/23.
//
#ifndef FGGL_GUI_RENDERER_RENDERER_HPP
#define FGGL_GUI_RENDERER_RENDERER_HPP
#include <fggl/gui/model/structure.hpp>
#include <fggl/gfx/paint.hpp>
namespace fggl::gui::renderer {
struct Box {
float top;
float right;
float bottom;
float left;
inline Box trim(Box border) const {
return {
top + border.top,
right - border.right,
bottom - border.bottom,
left + border.left
};
}
inline Box expand(Box border) const {
return {
top - border.top,
right + border.right,
bottom + border.bottom,
left - border.left
};
}
inline float width() const {
return right - left;
}
inline float height() const {
return bottom - top;
}
};
inline math::vec2 get_vec2(const model::Widget& root, const std::string& name) {
if ( !root.hasAttr(name) ) {
return {};
}
return root.get<math::vec2>(name);
}
inline math::vec3 get_vec3_rgb(const model::Widget& root, const std::string& name) {
if ( !root.hasAttr(name) ) {
return {};
}
return root.get<math::vec3>(name);
}
inline Box get_box(const model::Widget& root, const std::string& name) {
return {
root.get_or_default<float>(name + "::top"),
root.get_or_default<float>(name + "::right"),
root.get_or_default<float>(name + "::bottom"),
root.get_or_default<float>(name + "::left")
};
}
inline Box getBounds(math::vec2 pos, math::vec2 size) {
return {
pos.y,
pos.x + size.x,
pos.y + size.y,
pos.x
};
}
void draw_box(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 bottomRight);
void draw_border_patch(gfx::Paint& paint, Box& bounds, Box& size, math::vec3 colour);
void draw_border_solid(gfx::Paint& paint, Box& bounds, Box& size, math::vec3 colour);
void draw_background_solid(gfx::Paint& paint, Box& bounds, math::vec3 colour);
void layout(model::Widget& root);
void visit(const model::Widget& root, gfx::Paint& paint, Box offset = {0.0F, 1024.0F, 768.0F, 0.0F});
} // namespace fggl::gui::renderer
#endif //FGGL_GUI_RENDERER_RENDERER_HPP
......@@ -22,8 +22,17 @@
namespace fggl::gui {
constexpr float RGB_MAX_VAL = 255.0F;
constexpr math::vec3 rgbToNormal(char red, char green, char blue) {
constexpr uint8_t RGB_MAX_VAL = 255;
/**
* Convert an RGB value using 0-255 to a 0-1 value.
*
* @param red red component
* @param green green component
* @param blue blue component
* @return the normalized RGB value
*/
constexpr math::vec3 rgbToNormal(uint8_t red, uint8_t green, uint8_t blue) {
return {red / RGB_MAX_VAL, green / RGB_MAX_VAL, blue / RGB_MAX_VAL};
}
......@@ -36,53 +45,55 @@ namespace fggl::gui {
* @return RGB, in the range 0,1 for each component
*/
inline math::vec3 HSVtoNormal(float hue, float saturation, float value) {
assert( 0 < hue && hue <= 360);
assert( 0 < saturation && saturation <= 1);
assert( 0 < value && value <= 1);
assert(0 < hue && hue <= 360);
assert(0 < saturation && saturation <= 1);
assert(0 < value && value <= 1);
const float chroma = value * saturation;
const float x = chroma * (1 - fabs( fmod(hue / 60.0F, 2) - 1) );
math::vec3 tmp{0,0,0};
if ( 0 <= hue && hue < 60 ) {
tmp = {chroma,x,0};
} else if ( 60 <= hue && hue < 120 ) {
const float x = chroma * (1 - std::fabs(std::fmod(hue / 60.0F, 2.0F) - 1.0F));
math::vec3 tmp{0, 0, 0};
if (0 <= hue && hue < 60) {
tmp = {chroma, x, 0};
} else if (60 <= hue && hue < 120) {
tmp = {x, chroma, 0};
} else if (120 <= hue && hue < 180 ) {
} else if (120 <= hue && hue < 180) {
tmp = {0, chroma, x};
} else if ( 180 <= hue && hue < 240 ) {
} else if (180 <= hue && hue < 240) {
tmp = {0, x, chroma};
} else if ( 240 <= hue && hue < 300) {
} else if (240 <= hue && hue < 300) {
tmp = {x, 0, chroma};
} else if ( 300 <= hue && hue < 360) {
} else if (300 <= hue && hue < 360) {
tmp = {chroma, 0, x};
}
return tmp + (value - chroma);
}
void draw_box( gfx::Path2D& path, math::vec2 topLeft, math::vec2 bottomRight);
void draw_progress( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, float value);
void draw_slider( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, float value);
void draw_button( gfx::Path2D& path, math::vec2 topLeft, math::vec2 size, bool active, bool pressed);
void draw_box(gfx::Path2D &path, math::vec2 topLeft, math::vec2 bottomRight);
void draw_progress(gfx::Path2D &path, math::vec2 topLeft, math::vec2 size, float value);
void draw_slider(gfx::Path2D &path, math::vec2 topLeft, math::vec2 size, float value);
void draw_button(gfx::Path2D &path, math::vec2 topLeft, math::vec2 size, bool active, bool pressed);
struct Bounds2D {
math::vec2 topLeft;
math::vec2 size;
Bounds2D() = default;
inline Bounds2D(math::vec2 pos, math::vec2 a_size) : topLeft(pos), size(a_size) {}
inline bool contains(math::vec2 point) {
return ! ( (point.x > topLeft.x + size.x) ||
(point.x < topLeft.x ) ||
(point.y > topLeft.y + size.y) ||
(point.y < topLeft.y)
);
return !((point.x > topLeft.x + size.x) ||
(point.x < topLeft.x) ||
(point.y > topLeft.y + size.y) ||
(point.y < topLeft.y)
);
}
};
class Widget {
public:
Widget() = default;
inline Widget(math::vec2 pos, math::vec2 size) : m_bounds(pos, size) {}
virtual ~Widget() = default;
......@@ -104,28 +115,43 @@ namespace fggl::gui {
return m_bounds.size;
}
virtual inline bool contains(const math::vec2 &point){
virtual inline bool contains(const math::vec2 &point) {
return m_bounds.contains(point);
};
virtual inline Widget *getChildAt(const math::vec2 &point) {
if ( !contains(point) ) {
if (!contains(point)) {
return nullptr;
}
return this;
}
virtual void update(float deltaTime) = 0;
virtual void render(gfx::Paint &paint) = 0;
inline virtual void layout() {};
inline virtual void activate() {};
inline virtual void onEnter() {}
inline virtual void onExit() {}
inline virtual void onMouseOver(math::vec2 pos) {
if ( !m_hover ) {
onEnter(pos);
}
}
inline virtual void onEnter(math::vec2i /*pos*/) {
m_hover = true;
}
inline virtual void onExit(math::vec2i /*pos*/) {
m_hover = false;
}
protected:
bool m_hover = false;
private:
Bounds2D m_bounds;
};
}; //namespace fggl::gui
#endif
......@@ -35,8 +35,8 @@ namespace fggl::gui {
void render(gfx::Paint &paint) override {
auto pos = topLeft();
auto size2 = size();
math::vec2 baseLine{pos.x + 10, pos.y + size2.y/2 + 5};
paint.text(m_value,baseLine);
math::vec2 baseLine{pos.x + 10, pos.y + size2.y / 2 + 5};
paint.text(m_value, baseLine);
}
inline void font(std::shared_ptr<FontFace> font) {
......@@ -44,7 +44,7 @@ namespace fggl::gui {
m_needsLayout = true;
}
inline void text(const std::string& value) {
inline void text(const std::string &value) {
m_value = value;
m_needsLayout = true;
}
......@@ -54,13 +54,14 @@ namespace fggl::gui {
}
math::vec2 naturalSize() {
if ( m_needsLayout ) {
if (m_needsLayout) {
layout();
}
return m_naturalSize;
}
void layout();
void layout() override;
inline void update(float /*deltaTime*/) override {}
private:
std::shared_ptr<gui::FontFace> m_font;
......@@ -77,24 +78,23 @@ namespace fggl::gui {
void render(gfx::Paint &paint) override;
void activate() override;
void onEnter() override;
void onExit() override;
void label(const std::string& value);
void label(const std::string &value);
[[nodiscard]]
std::string label() const;
inline void update(float /*deltaTime*/) override {}
void addCallback(Callback callback);
void layout() override;
private:
Label m_label;
std::string m_value;
std::vector<Callback> m_callbacks;
bool m_hover;
bool m_active;
};
class Toggle : public Button {
public:
Toggle() = default;
......
......@@ -22,10 +22,10 @@
namespace fggl::input {
enum class InputType {
BUTTON, // 0 || 1
UNIDIRECTIONAL_AXIS, // [0,1], absolute
BIDIRECTIONAL_AXIS, // [-1, 1], absolute
RELATIVE_AXIS // [-1, 1], relative
BUTTON, // 0 || 1
UNIDIRECTIONAL_AXIS, // [0,1], absolute
BIDIRECTIONAL_AXIS, // [-1, 1], absolute
RELATIVE_AXIS // [-1, 1], relative
};
} // namespace fggl::input
......
......@@ -19,8 +19,8 @@
#ifndef FGGL_INPUT_CAMERA_INPUT_HPP
#define FGGL_INPUT_CAMERA_INPUT_HPP
#include <fggl/ecs3/ecs.hpp>
#include <fggl/math/types.hpp>
#include "fggl/entity/entity.hpp"
#include "fggl/math/types.hpp"
namespace fggl::input {
......@@ -38,7 +38,11 @@ namespace fggl::input {
scancode_t rotate_ccw;
};
void process_scroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam, float minZoom = 10.0F, float maxZoom = 50.0F);
void process_scroll(entity::EntityManager &ecs,
const Input &input,
entity::EntityID cam,
float minZoom = 10.0F,
float maxZoom = 50.0F);
/**
* Process the camera based on rotation around a fixed point.
......@@ -47,7 +51,7 @@ namespace fggl::input {
* @param input the input module to read the mouse location from
* @param cam the ID of the camera entity
*/
void process_arcball(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam);
void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam);
/**
* Process free (floating) camera movement.
......@@ -56,7 +60,7 @@ namespace fggl::input {
* @param input the input module to read the mouse location from
* @param cam the ID of the camera entity
*/
void process_freecam(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam);
void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam);
/**
* Input processing for moving the camera when the mouse is close to the edge of the screen.
......@@ -69,7 +73,7 @@ namespace fggl::input {
* @param input the input module to read the mouse location from
* @param cam the ID of the camera entity
*/
void process_edgescroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam);
void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam);
}
......
......@@ -192,7 +192,7 @@ namespace fggl::input {
return m_current[id].buttons[(int) btn] != m_previous[id].buttons[(int) btn];
}
inline void frame(float dt) {
inline void frame(float /*dt*/) {
m_previous = m_current;
}
......
......@@ -24,11 +24,11 @@
namespace fggl::input {
constexpr const modules::ModuleService SERVICE_INPUT = modules::make_service("fggl::input::Input");
constexpr const auto SERVICE_INPUT = modules::make_service("fggl::input::Input");
class Input {
public:
constexpr static const modules::ModuleService service = SERVICE_INPUT;
constexpr static const auto service = SERVICE_INPUT;
Input() = default;
void frame(float dt);
......
......@@ -52,7 +52,7 @@ namespace fggl::input {
class KeyboardInput {
public:
inline void frame(float dt) {
inline void frame(float /*dt*/) {
m_prev = m_curr;
}
......
......@@ -26,22 +26,21 @@
namespace fggl::input {
struct Generic {
constexpr static const char* name = "fggl::input::Generic";
constexpr static const std::array<modules::ModuleService, 1> provides = {
constexpr static const char *name = "fggl::input::Generic";
constexpr static const std::array<modules::ServiceName, 1> provides = {
SERVICE_INPUT
};
constexpr static const std::array<modules::ModuleService, 0> depends = {};
static const modules::ServiceFactory factory;
constexpr static const std::array<modules::ServiceName, 0> depends = {};
static bool factory(modules::ServiceName service, modules::Services &services);
};
bool input_factory(modules::ModuleService service, modules::Services& services) {
bool Generic::factory(modules::ServiceName service, modules::Services &services) {
if (service == SERVICE_INPUT) {
services.create<input::Input>();
return true;
}
return false;
}
const modules::ServiceFactory Generic::factory = input_factory;
}
#endif //FGGL_INPUT_MODULE_HPP
......@@ -19,17 +19,19 @@
#include <array>
#include <bitset>
#include "fggl/math/types.hpp"
namespace fggl::input {
enum class MouseButton {
LEFT,
MIDDLE,
RIGHT,
EXTRA_1,
EXTRA_2,
EXTRA_3,
EXTRA_4,
EXTRA_5
LEFT = 0,
MIDDLE = 2,
RIGHT = 1,
EXTRA_1 = 3,
EXTRA_2 = 4,
EXTRA_3 = 5,
EXTRA_4 = 6,
EXTRA_5 = 7
};
enum class MouseAxis {
......@@ -74,13 +76,14 @@ namespace fggl::input {
void operator=(const MouseState &rhs);
};
class MouseInput {
public:
MouseInput() = default;
MouseInput(const MouseInput &rhs) = delete;
void operator=(const MouseInput &rhs) = delete;
inline void frame(float dt) {
inline void frame(float /*dt*/) {
m_prev = m_curr;
}
......@@ -121,6 +124,15 @@ namespace fggl::input {
MouseState m_prev;
};
// helpers
inline math::vec2 mouse_axis(const MouseInput& input) {
return { input.axis(MouseAxis::X), input.axis(MouseAxis::Y) };
}
inline math::vec2 mouse_axis_delta(const MouseInput& input) {
return { input.axisDelta(MouseAxis::X), input.axisDelta(MouseAxis::Y) };
}
};
#endif
......@@ -37,7 +37,7 @@ namespace fggl::math {
//
using transformF = std::function<float(float)>;
inline float scaleFilter(float in, float inMin, float inMax, float outMin, float outMax, const transformF& filter) {
inline float scaleFilter(float in, float inMin, float inMax, float outMin, float outMax, const transformF &filter) {
float out = in - inMin;
out /= (inMax - inMin);
out = filter(out);
......@@ -45,7 +45,7 @@ namespace fggl::math {
return out + outMin;
}
inline float mix(const transformF& funcA, const transformF& funcB, float weightB, float t) {
inline float mix(const transformF &funcA, const transformF &funcB, float weightB, float t) {
return ((1 - weightB) * funcA(t)) + (weightB * funcB(t));
}
......
......@@ -16,28 +16,90 @@
#define FGGL_MATH_FMATH_HPP
#include <cmath>
#include "fggl/math/vector.hpp"
namespace fggl::math {
// wrap value in range [0, max)
/**
* A 4D floating-point vector.
*/
using vec4f = glm::vec4;
/**
* A 3D floating-point vector.
*/
using vec3f = glm::vec3;
/**
* A 2D floating-point vector.
*/
using vec2f = glm::vec2;
constexpr static const math::vec2f VEC2_ZERO{0.0F, 0.0F};
constexpr static const math::vec3f VEC3_ZERO{0.0F, 0.0F, 0.0F};
constexpr static const math::vec3f VEC3_ONES{1.0F, 1.0F, 1.0F};
/**
* return the remainder (modulo) of value / maximum.
*
* This will return a value in the range [0, maximum), the result will always be positive, even if passed a negative
* input.
*
* @param value the value to wrap.
* @param max the maximum value that it can take.
* @return the wrapped value
*/
inline float wrap(float value, float max) {
return fmod(max + fmod(value, max), max);
return fmodf(max + fmodf(value, max), max);
}
// wrap value in range [min, max)
/**
* wrap value in range [min, max)
*
* @param value the value to be tested.
* @param min the minimum allowable value
* @param max the maximum allowable value
*/
inline float wrap(float value, float min, float max) {
if ( min > max ){ std::swap(min, max); };
if (min > max) {
std::swap(min, max);
};
value -= min;
float rangeSize = max - min;
return value - (rangeSize* std::floor(value/rangeSize)) + min;
return value - (rangeSize * std::floor(value / rangeSize)) + min;
}
/**
* Ensure that value is wrapped in the range [min, max].
* if the value is larger than max, return max.
* if the value is smaller than min, return min.
*
* @param value the value to be tested.
* @param min the minimum allowable value
* @param max the maximum allowable value
* @return value, if it is in the range [min, max], otherwise the bound that it is outside of.
*/
constexpr float clamp(float value, float min, float max) {
const float valueOrMin = value < min ? min : value;
return valueOrMin > max ? max : valueOrMin;
}
// if the value is out of bounds, return that bound
inline float clamp(float value, float min, float max) {
const float t = value < min ? min : value;
return t > max ? max : t;
constexpr float lerpImprecise(float start, float end, float t) {
return start + t * (end - start);
}
constexpr float lerp(float start, float end, float t) {
return (1 - t) * start + t * end;
}
template<typename T>
[[nodiscard]]
constexpr T smooth_add(T first, T second, const float weight) {
const float other = 1 - weight;
return (first * other) + (second * weight);
}
} // namespace fggl::math
#endif //FGGL_MATH_FMATH_HPP
......@@ -19,37 +19,69 @@
#ifndef FGGL_MATH_IMATH_HPP
#define FGGL_MATH_IMATH_HPP
#include <cstdint>
namespace fggl::math {
// wrap value in range [0, max)
inline int wrap(int value, int max) {
if ( value < 0 ) return (n-1)-(-1-value) % n;
if ( value >= max ) return value % max;
constexpr int wrap(int value, int max) {
if (value < 0)
return (value - 1) - (-1 - value) % value;
if (value >= max)
return value % max;
return value;
}
// wrap value in range [min, max)
inline int wrap(int value, int const min, int const max) {
constexpr int wrap(int value, int const min, int const max) {
int range = max - min + 1;
if ( value < min) {
value += range * ( (min - value) / range + 1 );
if (value < min) {
value += range * ((min - value) / range + 1);
}
return min + (value - min) % range;
}
inline
int clamp(int value, int min, int max) {
constexpr int clamp(int value, int min, int max) {
const int i = value > max ? max : value;
return i < min ? min : value;
}
template<typename T>
inline
T clampT(T value, T min, T max) {
constexpr T clampT(T value, T min, T max) {
const T i = value > max ? max : value;
return i < min ? min : value;
}
/**
* Calculate the sum of the first N positive integers.
*
* @param n the number to stop at
* @return sum(0 ... N)
*/
constexpr uint64_t calcSum(uint64_t n) {
return (n * (n + 1)) / 2;
}
/**
* Calculate the squared sum of the first N positive integers.
*
* @param n the number to stop at
* @return sum(0 ... N) * sum(0 ... N)
*/
constexpr uint64_t calcSumSquared(uint64_t n) {
return (n * (n + 1) * (2*n + 1)) / 6;
}
/**
* Calculate the cubed sum of the first N positive integers.
*
* @param n the number to stop at
* @return sum(0 ... N) * sum(0 ... N) * sum(0 ... N)
*/
constexpr uint64_t calcSumCubed(uint64_t n) {
return ((n * n) * ((n + 1) * (n + 1))) / 4;
}
} // namespace fggl::math
#endif //FGGL_MATH_IMATH_HPP
......@@ -57,9 +57,9 @@ namespace fggl::math::phs3d {
math::vec3 min;
math::vec3 max;
void add(const math::vec3& p);
void add(const math::vec3 &p);
void emtpy();
void set(const AABB& other, const math::mat4& m);
void set(const AABB &other, const math::mat4 &m);
inline math::vec3 center() const {
return (min + max) / 2.0F;
......@@ -73,23 +73,23 @@ namespace fggl::math::phs3d {
return max - center();
}
static AABB fromPoints(const std::vector<math::vec3>& points);
static AABB fromPoints(const std::vector<math::vec3> &points);
};
struct Plane {
glm::vec3 normal;
float d; // distance to origin
inline bool contains(const math::vec3& point) {
inline bool contains(const math::vec3 &point) {
return glm::dot(point, normal) == d;
}
inline float distance(const math::vec3& q) {
inline float distance(const math::vec3 &q) {
return glm::dot(q, normal) - d;
}
static Plane fromPoints(const math::vec3, const math::vec3, const math::vec3);
static Plane bestFit(const std::vector<math::vec3>& points);
static Plane bestFit(const std::vector<math::vec3> &points);
};
struct Barycentric {
......@@ -98,8 +98,8 @@ namespace fggl::math::phs3d {
bool inTriangle() {
return 0 <= b[0] && b[0] <= 1 &&
0 <= b[1] && b[1] <= 1 &&
0 <= b[2] && b[2] <= 1;
0 <= b[1] && b[1] <= 1 &&
0 <= b[2] && b[2] <= 1;
}
inline bool isLegal() const {
......@@ -107,8 +107,8 @@ namespace fggl::math::phs3d {
}
};
constexpr float THIRD = 1.0F/3.0F;
const Barycentric cGrav = { {THIRD, THIRD, THIRD}};
constexpr float THIRD = 1.0F / 3.0F;
const Barycentric cGrav = {{THIRD, THIRD, THIRD}};
struct Triangle {
std::array<math::vec3, 3> v;
......@@ -130,11 +130,11 @@ namespace fggl::math::phs3d {
}
inline float length(int a, int b) const {
return glm::length( edge(a, b));
return glm::length(edge(a, b));
}
inline float perimeter() const {
return length(3, 2) + length(1,3) + length(2, 1);
return length(3, 2) + length(1, 3) + length(2, 1);
}
inline float area() const {
......@@ -190,13 +190,13 @@ namespace fggl::math::phs3d {
return 0.0F;
}
bool CartToBarycentric(const math::vec3& cart, Barycentric& outVal);
bool CartToBarycentric2(const math::vec3& cart, Barycentric& outVal);
bool CartToBarycentric(const math::vec3 &cart, Barycentric &outVal);
bool CartToBarycentric2(const math::vec3 &cart, Barycentric &outVal);
math::vec3 BarycentricToCart(const Barycentric& p) const {
math::vec3 BarycentricToCart(const Barycentric &p) const {
return v[0] * p.b[0] +
v[1] * p.b[1] +
v[2] * p.b[2];
v[1] * p.b[1] +
v[2] * p.b[2];
}
};
......
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 27/11/22.
//
#ifndef FGGL_MATH_STATS_HPP
#define FGGL_MATH_STATS_HPP
#include <cstdint>
#include <cmath>
namespace fggl::math {
class CumulativeAverage {
public:
inline void add(float item) {
m_current = ( item + (m_count * m_current) ) / (m_count + 1);
m_count++;
}
inline float average() {
return m_current;
}
private:
float m_current;
std::size_t m_count;
};
/**
* Useful Statistics class.
* ported to C++ from Piers' Java implementation.
*
* @tparam T
*/
template<typename T>
class Statistics {
public:
void add(T observation) {
m_count++;
m_sum += observation;
m_sumSquared += (observation * observation);
m_min = std::min(m_min, observation);
m_max = std::max(m_max, observation);
m_dirty = true;
}
inline T average() {
compute();
return m_average;
}
inline T average() const {
if ( m_dirty ) {
return m_sum / m_count;
}
return m_average;
}
inline T standardDeviation() {
compute();
return m_standardDiv;
}
inline T standardDeviation() const {
if ( !m_dirty ) {
return m_standardDiv;
}
T avg = average();
T num = m_sumSquared - (m_count * avg * avg);
return num < 0 ? 0 : sqrt( num / (m_count-1));
}
inline T standardError() {
compute();
return m_standardDiv / sqrt(m_count);
}
inline T standardError() const {
if ( !m_dirty ) {
return m_standardDiv / sqrt(m_count);
}
return standardDeviation() / sqrt(m_count);
}
inline T squareSum () const {
return m_sumSquared;
}
inline std::size_t count() const {
return m_count;
}
inline T sum() const {
return m_sum;
}
inline T min() const {
return m_min;
}
inline T max() const {
return m_max;
}
private:
T m_average{0};
T m_sum{0};
T m_sumSquared{0};
T m_standardDiv{0};
T m_min{0};
T m_max{0};
std::size_t m_count{0};
bool m_dirty{false};
void compute() {
if ( !m_dirty ) {
return;
}
m_average = m_sum / m_count;
T num = m_sumSquared - (m_count * m_average * m_average);
if ( num < 0 ) {
num = 0;
}
m_standardDiv = sqrt( num / (m_count-1) );
m_dirty = false;
}
};
using StatisticsF = Statistics<float>;
using StatisticsD = Statistics<double>;
} // namespace fggl::math
#endif //FGGL_MATH_STATS_HPP
......@@ -127,13 +127,13 @@ namespace fggl::math {
}
static data::Vertex2D pointToVertex(const math::vec2 &point) {
return data::Vertex2D{ point, {1.0f, 1.0f, 1.0f} };
return data::Vertex2D{point, {1.0F, 1.0F, 1.0F}, {0.0F, 0.0F}};
}
/**
* Fast Triangulation for convex polygons.
*/
void fan_triangulation(const PolygonVertex& polygon, data::Mesh2D &mesh);
void fan_triangulation(const PolygonVertex &polygon, data::Mesh2D &mesh);
} // namespace fggl::util
......
......@@ -18,19 +18,23 @@
#include <tuple>
#include <iostream>
#include <glm/glm.hpp>
#include "fggl/math/vector.hpp"
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/quaternion.hpp>
#include "fggl/math/fmath.hpp"
#include "fggl/util/guid.hpp"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#define M_PI 3.14159265358979323846
#endif
#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923
#define M_PI_2 1.57079632679489661923
#endif
namespace fggl::math {
......@@ -42,36 +46,99 @@ namespace fggl::math {
using uint8 = std::uint8_t;
// math types (aliased for ease of use)
/**
* A 4D floating-point vector.
*/
using vec4 = glm::vec4;
using vec4f = glm::vec4;
/**
* A 4D signed integer vector.
*/
using vec4i = glm::ivec4;
using vec4ui = glm::ivec4;
/**
* A 4D unsigned integer vector.
*/
using vec4ui = glm::uvec4;
/**
* A 3D floating-point vector.
*/
using vec3 = glm::vec3;
/**
* A 3D integer vector.
*/
using vec3i = glm::ivec3;
using vec3f = glm::vec3;
using vec3ui = glm::ivec3;
/**
* A 3D unsigned integer vector.
*/
using vec3ui = glm::uvec3;
/**
* A 2D floating-point vector
*/
using vec2 = glm::vec2;
using vec2f = glm::vec2;
/**
* A 2D integer vector
*/
using vec2i = glm::ivec2;
using vec2ui = glm::ivec2;
/**
* a 2D unsigned integer vector
*/
using vec2ui = glm::uvec2;
using vec2b = glm::bvec2;
using vec3b = glm::bvec3;
using vec4b = glm::bvec4;
/**
* A 2x2 floating-point matrix.
*/
using mat2 = glm::mat2;
/**
* A 3x3 floating-point matrix.
*/
using mat3 = glm::mat3;
/**
* A 4x4 floating-point matrix.
*/
using mat4 = glm::mat4;
/**
* A quaternion.
*/
using quat = glm::quat;
constexpr static const math::vec2 VEC2_ZERO {0.0F, 0.0F};
constexpr static const math::vec3 VEC3_ZERO {0.0F, 0.0F, 0.0F};
constexpr static const math::vec3 VEC3_ONES {1.0F, 1.0F, 1.0F};
constexpr static const math::mat4 IDENTITY_M4{1.0F};
constexpr static const math::quat IDENTITY_Q{1.0F, 0.0, 0.0, 0.0};
constexpr static const math::mat4 IDENTITY_M4 {1.0F};
constexpr static const math::quat IDENTITY_Q {1.0F, 0.0, 0.0, 0.0};
constexpr static const math::vec3 AXIS_X{1.0F, 0.0F, 0.0F};
constexpr static const math::vec3 AXIS_Y{0.0F, 1.0F, 0.0F};
constexpr static const math::vec3 AXIS_Z{0.0F, 0.0F, 1.0F};
constexpr static const math::vec3 AXIS_X { 1.0F, 0.0F, 0.0F };
constexpr static const math::vec3 AXIS_Y { 0.0F, 1.0F, 0.0F };
constexpr static const math::vec3 AXIS_Z { 0.0F, 0.0F, 1.0F };
constexpr auto minElm(vec3 a, vec3 b) -> vec3{
return {
a.x < b.x ? a.x : b.x,
a.y < b.y ? a.y : b.y,
a.z < b.z ? a.z : b.z
};
}
constexpr auto maxElm(vec3 a, vec3 b) -> vec3 {
return {
a.x > b.x ? a.x : b.x,
a.y > b.y ? a.y : b.y,
a.z > b.z ? a.z : b.z
};
}
// fastFloor from OpenSimplex2
inline int fastFloor(double x) {
......@@ -79,27 +146,60 @@ namespace fggl::math {
return x < xi ? xi - 1 : xi;
}
inline float rescale_norm(float value, float min, float max) {
/**
* Rescale a value between [min, max] into [0, 1].
*
* @param value the value to rescale
* @param min the minimum value of the original range
* @param max the maximum value of the original range
* @return the rescaled value, [0, 1]
*/
constexpr float rescale_norm(float value, float min, float max) {
return (value - min) / (max - min);
}
inline float rescale_norm(float value, float min, float max, float newMin, float newMax) {
/**
* Rescale a value between [min, max] into [newMin, newMax].
*
* @param value the value to rescale
* @param min the minimum value of the original range
* @param max the maximum value of the original range
* @param newMin the new minimum value
* @param newMax the new maximum value
* @return the rescaled value, [newMin, newMax]
*/
constexpr float rescale_norm(float value, float min, float max, float newMin, float newMax) {
return newMin + ((value - min) * (newMax - newMin)) / (max - min);
}
inline float rescale_ndc(float value, float newMin, float newMax){
/**
* Rescale a normalised device-coordinate value [-1, 1] into another range.
*
* @param value the value to rescale, [-1.0, 1.0]
* @param newMin the new minimum value
* @param newMax the new maximum value
* @return the rescaled value, [newMin, newMax]
*/
constexpr float rescale_ndc(float value, float newMin, float newMax) {
return rescale_norm(value, -1, 1, newMin, newMax);
}
inline float rescale_01(float value, float newMin, float newMax){
/**
* Rescale a normalised value [0, 1] into another range.
*
* @param value the value to rescale
* @param newMin the new minimum value
* @param newMax the new maximum value
* @return the rescaled value, [newMin, newMax]
*/
constexpr float rescale_01(float value, float newMin, float newMax) {
return rescale_norm(value, 0, 1, newMin, newMax);
}
inline float recale_mean(float value, float avg, float max, float min) {
constexpr float recale_mean(float value, float avg, float max, float min) {
return (value - avg) / (max - min);
}
// reference vectors
constexpr vec3f UP{0.0f, 1.0f, 0.0f};
constexpr vec3f FORWARD{1.0f, 0.0f, 0.0f};
......@@ -113,13 +213,14 @@ namespace fggl::math {
return modelMatrix(offset, glm::quat(eulerAngles));
}
// FIXME: we have multiple definitions of rays in the codebase!
struct Ray {
vec3 origin;
vec3 direction;
};
struct Transform {
constexpr static const char name[] = "Transform";
constexpr static const util::GUID guid = util::make_guid("Transform");
Transform() :
m_model(IDENTITY_M4),
......@@ -187,14 +288,14 @@ namespace fggl::math {
[[nodiscard]]
inline mat4 local() const {
const glm::mat4 transformX = glm::rotate( math::IDENTITY_M4,glm::radians(m_euler.x), AXIS_X );
const glm::mat4 transformY = glm::rotate( math::IDENTITY_M4, glm::radians(m_euler.y), AXIS_Y );
const glm::mat4 transformZ = glm::rotate( math::IDENTITY_M4, glm::radians(m_euler.z), AXIS_Z );
const glm::mat4 transformX = glm::rotate(math::IDENTITY_M4, glm::radians(m_euler.x), AXIS_X);
const glm::mat4 transformY = glm::rotate(math::IDENTITY_M4, glm::radians(m_euler.y), AXIS_Y);
const glm::mat4 transformZ = glm::rotate(math::IDENTITY_M4, glm::radians(m_euler.z), AXIS_Z);
const auto rotation = transformY * transformX * transformZ;
return glm::translate(math::IDENTITY_M4, m_origin)
* rotation
* glm::scale( math::IDENTITY_M4, m_scale );
* glm::scale(math::IDENTITY_M4, m_scale);
}
[[nodiscard]]
......@@ -202,7 +303,7 @@ namespace fggl::math {
return local();
}
inline void update(const math::mat4& parent) {
inline void update(const math::mat4 &parent) {
m_model = parent * local();
}
......@@ -222,23 +323,14 @@ namespace fggl::math {
vec3 m_scale;
};
inline math::mat4 calc_view_matrix(const Transform* transform) {
return glm::lookAt(transform->origin(), transform->origin() + transform->forward(), transform->up());
inline math::mat4 calc_view_matrix(const Transform &transform) {
return glm::lookAt(transform.origin(), transform.origin() + transform.forward(), transform.up());
}
inline math::mat4 calc_view_matrix(const Transform* transform, vec3 target) {
return glm::lookAt(transform->origin(), target, transform->up());
inline math::mat4 calc_view_matrix(const Transform &transform, vec3 target) {
return glm::lookAt(transform.origin(), target, transform.up());
}
}
// feels a bit strange to be doing this...
namespace glm {
inline bool operator<(const vec3 &lhs, const vec3 &rhs) {
return std::tie(lhs.x, lhs.y, lhs.z)
< std::tie(rhs.x, rhs.y, rhs.z);
}
}
#endif