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 1484 additions and 169 deletions
/*
* 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 24/07/22.
//
#include "fggl/gfx/ogl4/setup.hpp"
namespace fggl::gfx::ogl4 {
auto WindowGraphics::create(display::Window &window) -> Graphics * {
return new OpenGL4Backend(m_storage, m_fonts, m_assets, (GlFunctionLoader)glfwGetProcAddress);
}
}
\ No newline at end of file
#ifndef FGGL_GFX_RENDERING_H
#define FGGL_GFX_RENDERING_H
/**
* Ensure Graphics libraries are in the right order.
*/
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#endif
/*
* 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/>.
*/
#include "window.hpp"
#include <utility>
#include "fggl/display/glfw/window.hpp"
#include "fggl/display/glfw/window_input.hpp"
#include "fggl/debug/logging.hpp"
#include <iostream>
#include <string>
#include <stdexcept>
#include <GLFW/glfw3.h>
using namespace fggl::gfx;
namespace fggl::display::glfw {
static void glfw_error(int code, const char *description) {
debug::log(debug::Level::error, "[GLFW] {}: {}", code, description);
}
static void framebuffer_resize(GLFWwindow *window, int width, int height) {
auto *fgglWindow = static_cast<Window *>(glfwGetWindowUserPointer(window));
fgglWindow->framesize(width, height);
}
static void fggl_input_cursor(GLFWwindow *window, double xPos, double yPos) {
auto &input = GlfwInputManager::instance();
auto *fgglWin = static_cast<Window *>(glfwGetWindowUserPointer(window));
#ifndef FGGL_INPUT_SCREEN_COORDS
// convert to nice ranges...
xPos = (xPos / fgglWin->width() * 2) - 1.0; // [-1, 1]
yPos = (yPos / fgglWin->height() * 2) - 1.0; // [-1, 1]
#endif
// inform the input system
input.onMouseMove(xPos, yPos);
}
static void fggl_input_scroll(GLFWwindow */*window*/, double xPos, double yPos) {
auto &input = GlfwInputManager::instance();
input.onMouseScroll(xPos, yPos);
}
static void fggl_input_mouse_btn(GLFWwindow */*window*/, int btn, int action, int /*mods*/) {
auto &input = GlfwInputManager::instance();
input.onMouseButton(btn, action == GLFW_PRESS);
}
static void fggl_input_keyboard(GLFWwindow */*window*/, int /*key*/, int scancode, int action, int /*mods*/) {
auto &input = GlfwInputManager::instance();
input.onKeyEvent(scancode, action == GLFW_PRESS || action == GLFW_REPEAT);
}
static void fggl_update_joystick(fggl::input::GamepadInput &input, int jid) {
bool isGamepad = (glfwJoystickIsGamepad(jid) == GLFW_TRUE);
if (isGamepad) {
if (!input.present(jid)) {
std::string name = glfwGetJoystickName(jid);
input.setActive(jid, true);
input.name(jid, name);
}
GLFWgamepadstate state;
glfwGetGamepadState(jid, &state);
fggl::input::GamepadState gamepadState;
for (int i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) {
gamepadState.buttons[i] = state.buttons[i];
}
static void glfw_error(int code, const char* description) {
std::cerr << "[GLFW] " << code << " " << description << std::endl;
}
for (int i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) {
gamepadState.axes[i] = state.axes[i];
}
Context::Context() {
int state = glfwInit();
if ( state == GLFW_FALSE ) {
const char** error;
int code = glfwGetError(error);
throw std::runtime_error( *error );
input.update(jid, gamepadState);
} else {
input.setActive(jid, false);
}
}
}
Context::~Context() {
glfwTerminate();
}
static void fggl_joystick_poll() {
auto &input = GlfwInputManager::instance();
if (!input.alive()) {
return;
}
void Context::pollEvents() {
glfwPollEvents();
}
auto &gamepadCtl = input.gamepads();
for (int jid = 0; jid < GLFW_JOYSTICK_LAST; jid++) {
if (glfwJoystickPresent(jid) == GLFW_TRUE) {
fggl_update_joystick(gamepadCtl, jid);
} else {
gamepadCtl.setActive(jid, false);
}
}
}
Window::Window() : m_window(nullptr), m_input(nullptr) {
m_window = glfwCreateWindow(1920, 1080, "main", NULL, NULL);
glfwSetWindowUserPointer(m_window, this);
}
static void fggl_joystick(int jid, int state) {
auto &input = GlfwInputManager::instance();
if (!input.alive()) {
return;
}
Window::~Window() {
if ( m_window != nullptr ) {
glfwDestroyWindow(m_window);
m_window = nullptr;
auto &gamepadCtl = input.gamepads();
if (state == GLFW_CONNECTED) {
fggl_update_joystick(gamepadCtl, jid);
} else {
gamepadCtl.setActive(jid, false);
}
}
}
void Window::activate() {
assert( m_window != nullptr );
glfwMakeContextCurrent(m_window);
}
GlfwContext::GlfwContext(fggl::input::Input *input) {
debug::log(debug::Level::info, "[GLFW] Initializing...");
auto &glfwCallbacks = GlfwInputManager::instance();
glfwCallbacks.setup(input);
glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE);
glfwSetErrorCallback(glfw_error);
int state = glfwInit();
if (state == GLFW_FALSE) {
const char **error = nullptr;
glfwGetError(error);
throw std::runtime_error(*error);
}
void Window::swap() {
assert( m_window != nullptr );
glfwSwapBuffers(m_window);
}
// joysticks are global
fggl_joystick_poll();
glfwSetJoystickCallback(fggl_joystick);
debug::log(debug::Level::info, "[GLFW] Initialization complete.");
}
GlfwContext::~GlfwContext() {
glfwTerminate();
debug::trace("[glfw] context terminated");
}
void GlfwContext::pollEvents() {
auto &glfwCallbacks = GlfwInputManager::instance();
glfwCallbacks.frame();
glfwPollEvents();
fggl_joystick_poll();
}
Window::Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics *graphics)
: m_context(std::move(context)), m_window(nullptr), m_framesize() {
// don't iconify when focus is lost.
glfwWindowHint( GLFW_AUTO_ICONIFY, GLFW_FALSE );
// FIXME - this ties the graphics API before window creation
auto graphicsConfig = graphics->config();
if (graphicsConfig.api == gfx::GraphicsAPI::OpenGL) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, graphicsConfig.majorVersion);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, graphicsConfig.minorVersion);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, graphicsConfig.debug ? GLFW_TRUE : GLFW_FALSE);
}
m_window = glfwCreateWindow(1920, 1080, "main", nullptr, nullptr);
if (m_window == nullptr) {
return;
}
glfwSetWindowUserPointer(m_window, this);
// setup callback wrappers (will invoke the methods via the user ptr above)
glfwSetFramebufferSizeCallback(m_window, framebuffer_resize);
glfwSetScrollCallback(m_window, fggl_input_scroll);
glfwSetCursorPosCallback(m_window, fggl_input_cursor);
glfwSetMouseButtonCallback(m_window, fggl_input_mouse_btn);
glfwSetKeyCallback(m_window, fggl_input_keyboard);
// bind the graphics API
glfwMakeContextCurrent(m_window);
m_graphics = graphics->createMain(*this);
}
Window::~Window() {
if ( m_graphics != nullptr ) {
delete m_graphics;
m_graphics = nullptr;
}
if (m_window != nullptr) {
// prevent dangling pointers
glfwSetWindowUserPointer(m_window, nullptr);
glfwDestroyWindow(m_window);
m_window = nullptr;
}
}
void Window::frameStart() {
assert(m_window != nullptr);
assert(m_graphics != nullptr);
glfwMakeContextCurrent(m_window);
m_graphics->clear();
}
void Window::frameEnd() {
glfwSwapBuffers(m_window);
}
void Window::activate() const {
assert(m_window != nullptr);
glfwMakeContextCurrent(m_window);
}
auto Window::frameSize() const -> fggl::math::vec2i {
assert(m_window != nullptr);
math::vec2i size;
glfwGetFramebufferSize(m_window, &size.x, &size.y);
return size;
}
auto Window::wantClose() const -> bool {
assert(m_window != nullptr);
return glfwWindowShouldClose(m_window) == GLFW_TRUE;
}
}
\ No newline at end of file
#ifndef FGGL_GFX_H
#define FGGL_GFX_H
#include <cassert>
#include <string>
#include <fggl/gfx/rendering.hpp>
namespace fggl::gfx {
class Context {
public:
Context();
~Context();
void pollEvents();
};
class Input {
private:
};
enum MutWindowHint {
Decorated = GLFW_DECORATED,
Resizable = GLFW_RESIZABLE,
Floating = GLFW_FLOATING,
AutoIconify = GLFW_AUTO_ICONIFY,
FocusOnShow = GLFW_FOCUS_ON_SHOW
};
enum WindowHint {
Focused = GLFW_FOCUSED,
Iconified = GLFW_ICONIFIED,
Maximised = GLFW_MAXIMIZED,
Hovered = GLFW_HOVERED,
Visible = GLFW_VISIBLE,
Transparent = GLFW_TRANSPARENT_FRAMEBUFFER,
GlForwardCompat = GLFW_OPENGL_FORWARD_COMPAT,
GlDebugContext = GLFW_OPENGL_DEBUG_CONTEXT,
NoError = GLFW_CONTEXT_NO_ERROR
};
class Window {
public:
Window();
~Window();
// window <-> opengl stuff
void activate();
void swap();
// window manager stuff
inline bool closeRequested() const {
assert( m_window != nullptr );
return glfwWindowShouldClose(m_window);
}
inline void title(const std::string& title) {
assert( m_window != nullptr );
glfwSetWindowTitle( m_window, title.c_str() );
}
inline bool fullscreen() const {
assert( m_window != nullptr );
return glfwGetWindowMonitor( m_window ) != NULL;
}
inline void fullscreen(bool state) {
assert( m_window != nullptr );
if ( state ) {
auto monitor = glfwGetPrimaryMonitor();
const auto mode = glfwGetVideoMode( monitor );
glfwSetWindowMonitor( m_window, monitor,
0, 0,
mode->width, mode->height,
mode->refreshRate);
} else {
glfwSetWindowMonitor( m_window, NULL,
0, 0,
800, 600, 0);
}
}
inline bool checkHint(WindowHint hint) const {
return check_hint(hint);
}
inline bool checkHint(MutWindowHint hint) const {
return check_hint(hint);
}
inline void setHint(MutWindowHint hint, bool state) {
set_hint(hint, state);
}
inline void visible(bool state) {
assert( m_window != nullptr );
if ( state ) {
glfwShowWindow( m_window );
} else {
glfwHideWindow( m_window );
}
}
private:
GLFWwindow* m_window;
Input* m_input;
inline void set_hint(int hint, bool state) const {
assert( m_window != nullptr );
glfwSetWindowAttrib( m_window, hint, state );
}
inline bool check_hint(int hint) const {
assert( m_window != nullptr );
return glfwGetWindowAttrib( m_window, hint) == GLFW_TRUE;
}
};
}
#endif
target_sources( fggl
PRIVATE
hexagon.cpp
)
\ No newline at end of file
/*
* 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 10/12/22.
//
#include "fggl/grid/hexagon.hpp"
namespace fggl::grid {
std::vector<IntHex> lineTo(const IntHex& start, const IntHex& end) {
const int distance = start.distance(end);
std::vector<IntHex> line;
for (auto i=0; i < distance; ++i) {
line.push_back( round2(hexLerp(start, end, 1.0F/distance * i)) );
}
return line;
}
} // namespace fggl:grid
target_sources( ${PROJECT_NAME}
PRIVATE
widget.cpp
widgets.cpp
containers.cpp
fonts.cpp
model/parser.cpp
model/structure.cpp
renderer/renderer.cpp
)
find_package(Freetype)
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype)
/*
* 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/>.
*/
#include <fggl/gui/containers.hpp>
#include <spdlog/spdlog.h>
namespace fggl::gui {
auto Container::getChildAt(const math::vec2 &point) -> Widget * {
for (auto &child : m_children) {
if (child->contains(point)) {
return child->getChildAt(point);
}
}
return nullptr;
}
auto Container::contains(const math::vec2 &point) -> bool {
return true;
}
void Container::render(gfx::Paint &paint) {
for (auto &child : m_children) {
child->render(paint);
}
}
void Container::add(std::unique_ptr<Widget> widget) {
m_children.push_back(std::move(widget));
m_dirty = true;
}
void Container::onMouseOver(math::vec2 pos) {
Widget::onMouseOver(pos);
auto* childHover = getChildAt(pos);
for ( auto& child : m_children) {
if ( child.get() != childHover ) {
child->onExit(pos);
}
}
if ( childHover != nullptr ) {
childHover->onMouseOver(pos);
}
}
void Container::onEnter(math::vec2i pos) {
Widget::onEnter(pos);
for ( auto& child : m_children ) {
if ( child->contains(pos)) {
child->onEnter(pos);
}
}
}
void Container::onExit(math::vec2i pos) {
Widget::onExit(pos);
for ( auto& child : m_children) {
child->onExit(pos);
}
}
GridBox::GridBox(uint32_t rows, uint32_t cols, uint32_t padx, uint32_t pady) : m_rows(rows), m_cols(cols), m_padding(padx, pady) {}
void GridBox::layout() {
assert( m_rows != 0 || m_cols != 0 );
if ( m_rows == 0 ) {
int rows = m_children.size() / m_cols;
// figure out the width and heights
auto* widths = new float[m_cols]{0.0F};
auto* heights = new float[rows]{0.0F};
for ( auto idx = 0U; idx < m_children.size(); ++idx) {
auto& child = m_children[idx];
int col = idx % m_cols;
int row = idx / m_cols;
widths[col] = std::max( child->size().x, widths[col] );
heights[row] = std::max( child->size().y, heights[row] );
}
// populate the grid
fggl::math::vec2i pos{0, 0};
unsigned int row = 0;
unsigned int col = 0;
for ( auto& child : m_children ) {
fggl::math::vec2i size{ widths[col], heights[row] };
child->size(pos, size);
child->layout();
// next iter
pos.x += size.x + m_padding.x;
col++;
if ( col == m_cols ) {
col = 0;
row++;
pos.x = 0;
pos.y += size.y + m_padding.y;
}
}
// cleanup variables
delete[] widths;
delete[] heights;
}
}
/*
Box::Box( LayoutAxis axis ) : m_axis( axis ) {}
void Box::layout() {
math::vec2 baseSize{};
const int mainAxis = 0;
const int crossAxis = 1;
// ask for preferred sizes
for ( auto& child : m_children ) {
auto childBaseSize = child.baseSize();
baseSize[mainAxis] += childBaseSize[mainAxis];
if ( childBaseSize[crossAxis] > baseSize[crossAxis] ) {
baseSize[crossAxis] = childBaseSize[crossAxis];
}
}
if ( lineAxis == 0 )
setSize(lineSum, biggestCross);
else
setSize(biggestCross, lineSum);
}*/
void Panel::render(gfx::Paint &paint) {
// background painting time
gfx::Path2D background(topLeft());
background.colour(math::vec3(32.0F / 255.0F, 74.0F / 255.0F, 135.0F / 255.0F));
draw_box(background, topLeft(), bottomRight());
paint.fill(background);
// call parent's render method for child objects
Container::render(paint);
}
};
/*
* 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/>.
*/
#include "fggl/gui/fonts.hpp"
namespace fggl::gui {
FontFace::FontFace(FT_Face face) : m_face(face) {
}
FontFace::~FontFace() {
FT_Done_Face(m_face);
}
FontLibrary::FontLibrary(data::Storage *storage) : m_context(nullptr), m_storage(storage) {
FT_Init_FreeType(&m_context);
m_defaultFont = getFont(DEFAULT_FONT_NAME);
}
FontLibrary::~FontLibrary() {
// free all fonts
m_defaultFont = nullptr;
m_cache.clear();
// shut the library down
FT_Done_FreeType(m_context);
}
auto FontFace::populateMetrics(char letter) -> GlyphMetrics & {
if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER)) {
// something bad happened
return m_metrics['?'];
}
GlyphMetrics metrics{
{m_face->glyph->bitmap.width, m_face->glyph->bitmap.rows},
{m_face->glyph->bitmap_left, m_face->glyph->bitmap_top},
m_face->glyph->advance.x
};
// return the new metrics data
auto it = m_metrics.emplace(letter, metrics);
return it.first->second;
}
void FontFace::texture(char letter, int &width, int &height, unsigned char **buff) {
if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER)) {
// something bad happened
return;
}
width = m_face->glyph->bitmap.width;
height = m_face->glyph->bitmap.rows;
*buff = m_face->glyph->bitmap.buffer;
}
} // namespace fggl::fonts
\ No newline at end of file
/*
* 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 11/03/23.
//
#include "fggl/gui/model/parser.hpp"
#include <yaml-cpp/yaml.h>
namespace fggl::gui::model {
Widget* YamlToWidgetTree(WidgetFactory& factory, const YAML::Node& config) {
Widget* root;
if ( config["template"] ) {
root = factory.build( config["template"].as<std::string>());
} else {
root = factory.buildEmpty();
}
// deal with attrs
for ( auto attr : config["attrs"] ) {
root->set(attr.first.as<std::string>(), attr.second.as<std::string>());
}
// deal with child nodes
for ( auto child : config["children"] ) {
Widget* childWidget = YamlToWidgetTree(factory, child);
root->addChild(*childWidget);
}
// are we a template definition?
if ( config["define"] ) {
factory.push( config["define"].as<std::string>(), std::move(*root) );
return factory.getTemplate( config["define"].as<std::string>() );
}
return root;
}
inline Widget* parseFile(WidgetFactory& factory, const std::string& path) {
YAML::Node root = YAML::LoadFile(path);
if ( !root ){
return nullptr;
}
return YamlToWidgetTree(factory, root);
}
}
\ No newline at end of file
/*
* 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 11/03/23.
//
#include "fggl/gui/model/structure.hpp"
#include "fggl/gui/fonts.hpp"
#include <algorithm>
#include <utility>
#include <string>
namespace fggl::gui::model {
math::vec2 calcTextBounds(const std::string& value, std::shared_ptr<FontFace> face) {
if ( face == nullptr ){
debug::warning("No preferred font sent, cowardly refusing to process text");
return {};
}
math::vec2 max{0, 0};
for (auto letter : value) {
auto metrics = face->metrics(letter);
max.x += metrics.size.x + (metrics.advance >> 6);
max.y = std::max(max.y, metrics.size.y);
}
return max;
}
inline math::vec2 calcBoxContrib(const Widget& widget, const std::string& name) {
return math::vec2{
widget.get_or_default<float>( name + "::left") + widget.get_or_default<float>(name + "::right"),
widget.get_or_default<float>( name + "::top" ) + widget.get_or_default<float>( name + "::bottom" )
};
}
void Widget::calcPrefSize(std::shared_ptr<FontFace> face) {
if ( !m_dirty ){
return;
}
auto padding = calcBoxContrib( *this, "padding");
auto border = calcBoxContrib( *this, "border");
auto content = math::vec2{0,0};
if (hasAttr("text")) {
content += calcTextBounds( get<std::string>("text"), std::move(face) );
}
m_cachedSize = padding + content + content;
debug::info("my preferred size is: ({}, {})", m_cachedSize.x, m_cachedSize.y);
m_dirty = false;
}
}
\ No newline at end of file
/*
* 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 11/03/23.
//
#include "fggl/gui/renderer/renderer.hpp"
namespace fggl::gui::renderer {
constexpr int PADDING = 15;
void draw_box(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 bottomRight) {
path.moveTo({topLeft.x, topLeft.y});
path.pathTo({bottomRight.x, topLeft.y});
path.pathTo({bottomRight.x, bottomRight.y});
path.pathTo({topLeft.x, bottomRight.y});
path.pathTo({topLeft.x, topLeft.y});
}
void draw_border_patch(gfx::Paint& paint, Box& bounds, Box& size, math::vec3 colour) {
gfx::Path2D path({0,0});
path.colour(colour);
// draw edges
draw_box(path, {bounds.left + size.left, bounds.top}, {bounds.right - size.right, bounds.top + size.top} );
draw_box(path, {bounds.right - size.right, bounds.top + size.top}, {bounds.right, bounds.bottom - size.bottom} );
draw_box(path, {bounds.left + size.left, bounds.bottom - size.bottom}, {bounds.right - size.right, bounds.bottom} );
draw_box(path, {bounds.left, bounds.top + size.top}, {bounds.left + size.left, bounds.bottom - size.bottom} );
// draw-corners
draw_box(path, {bounds.left, bounds.top}, {bounds.left + size.left, bounds.top + size.top} );
draw_box(path, {bounds.right - size.right, bounds.top}, {bounds.right, bounds.top + size.top} );
draw_box(path, {bounds.left, bounds.bottom - size.bottom}, {bounds.left + size.left, bounds.bottom} );
draw_box(path, {bounds.right - size.right, bounds.bottom - size.bottom}, {bounds.right, bounds.bottom});
paint.fill(path);
}
void draw_border_solid(gfx::Paint& paint, Box& bounds, Box& size, math::vec3 colour) {
gfx::Path2D path({0,0});
path.colour(colour);
// draw edges
draw_box(path, {bounds.left, bounds.top}, {bounds.right, bounds.top + size.top} );
draw_box(path, {bounds.right - size.right, bounds.top + size.top}, {bounds.right, bounds.bottom - size.bottom} );
draw_box(path, {bounds.left, bounds.bottom - size.bottom}, {bounds.right, bounds.bottom} );
draw_box(path, {bounds.left, bounds.top + size.top}, {bounds.left + size.left, bounds.bottom - size.bottom} );
paint.fill(path);
}
void draw_background_solid(gfx::Paint& paint, Box& bounds, math::vec3 colour) {
gfx::Path2D path({0,0});
path.colour(colour);
draw_box(path, {bounds.left, bounds.top}, {bounds.right, bounds.bottom} );
paint.fill(path);
}
void layout(model::Widget& current) {
if ( current.isLeaf() ) {
// if the widget has a defined size, use that
if ( current.hasAttr("size") && !current.hasAttr("text") ) {
return;
}
// else, use the model's preferred size
auto preferred = current.preferredSize();
if ( preferred.has_value() ) {
current.set<math::vec2>("size", preferred.value() );
}
} else {
auto topPad = current.get_or_default<float>("border:top") + current.get_or_default<float>("padding::top");
auto leftPad = current.get_or_default<float>("border::left") + current.get_or_default<float>("padding::left");
math::vec2 size = {topPad, leftPad};
// layout all children
for ( auto& child : current ) {
layout(child);
auto childSize = child.get_or_default<math::vec2>("size");
size.x = std::max( childSize.x, size.x );
size.y += childSize.y;
child.set<math::vec2>("position", {leftPad, size.y});
}
// set our size based on that
current.set<math::vec2>("size", size );
}
}
void visit(const model::Widget& root, gfx::Paint& paint, Box offset) {
// get border size
auto border = get_box(root, "border");
// calculate box bounds
auto pos = get_vec2(root, "position");
auto size = get_vec2(root, "size");
auto bounds = getBounds(pos, size);
bounds.top += offset.top;
bounds.left += offset.left;
// deal with right hand size bounds
//bounds.right = std::min( size.x, offset.width() );
//bounds.right += offset.left;
// deal with bottom bounds
//bounds.bottom = std::min( size.y, offset.height() );
//bounds.bottom += offset.top;
auto background = bounds.trim(border);
draw_background_solid(paint, background, get_vec3_rgb(root, "colour"));
draw_border_patch(paint, bounds, border, get_vec3_rgb(root, "border::colour"));
auto padding = get_box(root, "padding");
background = background.trim(padding);
if ( root.hasAttr("text") ) {
auto text = root.get<std::string>("text");
paint.text(text, {background.left, background.top + PADDING});
}
for (const auto& child : root) {
visit(child, paint, background);
}
}
} // namespace fggl::gui:;renderer
\ No newline at end of file
/*
* 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 17/04/22.
//
#include <fggl/gui/widget.hpp>
namespace fggl::gui {
void button_border(gfx::Path2D &path, glm::vec2 pos, glm::vec2 size) {
// outer box
path.colour({1.0F, 0.0F, 0.0F});
path.pathTo({pos.x + size.x, pos.y});
path.pathTo({pos.x + size.x, pos.y + size.y});
path.pathTo({pos.x, pos.y + size.y});
path.close();
// inner box
math::vec2 innerTop{pos.x + 5, pos.y + 5};
math::vec2 innerBottom{pos.x + size.x - 5, pos.y + size.y - 5};
path.colour({1.0F, 1.0F, 0.0F});
path.moveTo({innerTop.x, innerTop.y});
path.pathTo({innerBottom.x, innerTop.y});
path.pathTo({innerBottom.x, innerBottom.y});
path.pathTo({innerTop.x, innerBottom.y});
path.pathTo({innerTop.x, innerTop.y});
}
void draw_box(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 bottomRight) {
path.moveTo({topLeft.x, topLeft.y});
path.pathTo({bottomRight.x, topLeft.y});
path.pathTo({bottomRight.x, bottomRight.y});
path.pathTo({topLeft.x, bottomRight.y});
path.pathTo({topLeft.x, topLeft.y});
}
void draw_progress(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 size, float value) {
const auto bottomRight{topLeft + size};
// background
path.colour({0.5F, 0.5F, 0.5F});
draw_box(path, topLeft, bottomRight);
// fill
math::vec2 innerTop{topLeft.x + 5, topLeft.y + 5};
math::vec2 innerBottom{bottomRight.x - 5, bottomRight.y - 5};
// figure out how wide the bar should be
float barWidth = (innerBottom.x - innerTop.x) * value;
float trueBottom = innerBottom.x;
innerBottom.x = innerTop.x + barWidth;
// draw the bar
path.colour({0.8F, 0.0F, 0.0F});
draw_box(path, innerTop, innerBottom);
// part of the bar that's not filled in
math::vec2 emptyTop{innerBottom.x, innerTop.y};
math::vec2 emptyBottom{trueBottom, innerBottom.y};
path.colour({0.4F, 0.0F, 0.0F});
draw_box(path, emptyTop, emptyBottom);
}
void draw_slider(gfx::Path2D &path, glm::vec2 topLeft, glm::vec2 size, float value) {
draw_progress(path, topLeft, size, value);
// dimensions
const auto bottomRight{topLeft + size};
const math::vec2 innerTop{topLeft.x + 5, topLeft.y + 5};
const math::vec2 innerBottom{bottomRight.x - 5, bottomRight.y - 5};
// selector bar
float trackWidth = innerBottom.x - innerTop.x;
float selectorValue = trackWidth * value;
float selectorWidth = 6;
math::vec2 selectorTop{innerTop.x + selectorValue - (selectorWidth / 2), topLeft.y};
math::vec2 selectorBottom{selectorTop.x + selectorWidth, bottomRight.y};
path.colour({1.0F, 1.0F, 1.0F});
draw_box(path, selectorTop, selectorBottom);
}
void draw_button(gfx::Path2D &path, glm::vec2 pos, glm::vec2 size, bool active, bool pressed) {
// locations
math::vec2 outerTop{pos};
math::vec2 outerBottom{pos + size};
math::vec2 innerTop{pos.x + 5, pos.y + 5};
math::vec2 innerBottom{pos.x + size.x - 5, pos.y + size.y - 5};
math::vec3 baseColour{0.5F, 0.5F, 0.5F};
if (active) {
baseColour *= 1.2F;
}
if (pressed) {
baseColour *= 0.8F;
}
math::vec3 lightColour{baseColour * 1.2F};
math::vec3 darkColour{baseColour * 0.8F};
if (pressed) {
// flip light and dark for selected buttons
auto tmp = darkColour;
darkColour = lightColour;
lightColour = tmp;
}
// bottom side
path.colour(lightColour);
path.moveTo(outerTop);
path.pathTo(innerTop);
path.pathTo({innerBottom.x, innerTop.y});
path.pathTo({outerBottom.x, outerTop.y});
path.pathTo(outerTop);
// left side
path.colour(lightColour);
path.moveTo(outerTop);
path.pathTo(innerTop);
path.pathTo({innerTop.x, innerBottom.y});
path.pathTo({outerTop.x, outerBottom.y});
path.pathTo(outerTop);
// top side
path.colour(darkColour);
path.moveTo({outerTop.x, outerBottom.y});
path.pathTo({innerTop.x, innerBottom.y});
path.pathTo(innerBottom);
path.pathTo(outerBottom);
path.pathTo({outerTop.x, outerBottom.y});
// right side
path.colour(darkColour);
path.moveTo(outerBottom);
path.pathTo(innerBottom);
path.pathTo({innerBottom.x, innerTop.y});
path.pathTo({outerBottom.x, outerTop.y});
path.pathTo(outerBottom);
// inner box
path.colour(baseColour);
draw_box(path, innerTop, innerBottom);
}
}
\ No newline at end of file
/*
* 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 17/04/22.
//
#include <fggl/gui/widget.hpp>
#include <fggl/gui/widgets.hpp>
#include <spdlog/spdlog.h>
namespace fggl::gui {
Button::Button(math::vec2 pos, math::vec2 size) : Widget(pos, size), m_label(pos, size), m_active(false) {}
void Button::render(gfx::Paint &paint) {
gfx::Path2D path{topLeft()};
draw_button(path, topLeft(), size(), m_hover, m_active);
paint.fill(path);
m_label.render(paint);
}
void Button::activate() {
m_active = !m_active;
if (m_active) {
for (auto &callback : m_callbacks) {
callback();
}
m_active = false;
}
}
void Button::addCallback(Callback cb) {
m_callbacks.push_back(cb);
}
void Button::label(const std::string &value) {
m_label.text(value);
}
void Button::layout() {
m_label.size(topLeft(), size());
}
auto Button::label() const -> std::string {
return m_label.text();
}
void Label::layout() {
if (m_font == nullptr) {
return;
}
math::vec2 size;
for (const auto &letter : m_value) {
auto metrics = m_font->metrics(letter);
size.x += (metrics.advance << 6);
size.y = std::max(size.y, metrics.size.y);
}
m_naturalSize = size;
m_needsLayout = false;
}
Label::Label(math::vec2 pos, math::vec2 size) : Widget(pos, size) {
}
} // namespace fggl::gui
\ No newline at end of file
/*
* 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 20/11/2021.
//
#include <fggl/input/input.hpp>
#include <fggl/gfx/camera.hpp>
#include <fggl/input/camera_input.hpp>
namespace fggl::input {
void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
// see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
const auto &mouse = input.mouse;
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
glm::mat4 view = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
glm::vec3 viewDir = -glm::transpose(view)[2];
glm::vec3 rightDir = glm::transpose(view)[0];
float deltaAngleX = (2 * M_PI);
float deltaAngleY = (M_PI);
float xAngle = (-mouse.axisDelta(fggl::input::MouseAxis::X)) * deltaAngleX;
float yAngle = (-mouse.axisDelta(fggl::input::MouseAxis::Y)) * deltaAngleY;
// rotate the camera around the pivot on the first axis
glm::mat4x4 rotationMatrixX(1.0F);
rotationMatrixX = glm::rotate(rotationMatrixX, xAngle, fggl::math::UP);
position = (rotationMatrixX * (position - pivot)) + pivot;
// rotate the camera around the pivot on the second axis
glm::mat4x4 rotationMatrixY(1.0F);
rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir);
glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot;
camTransform.origin(finalPos);
}
void process_scroll(entity::EntityManager &ecs,
const Input &input,
entity::EntityID cam,
float minZoom,
float maxZoom) {
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
const glm::vec3 dir = (camTransform.origin() - camComp.target);
const glm::vec3 forward = glm::normalize(dir);
glm::vec3 motion(0.0F);
float delta = input.mouse.axis(fggl::input::MouseAxis::SCROLL_Y);
if ((glm::length(dir) < maxZoom && delta < 0.0F) || (glm::length(dir) > minZoom && delta > 0.0F)) {
motion -= (forward * delta);
camTransform.origin(camTransform.origin() + motion);
}
}
void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
float rotationValue = 0.0F;
glm::vec3 translation(0.0F);
const auto &keyboard = input.keyboard;
auto &settings = ecs.get<FreeCamKeys>(cam);
// calculate rotation (user input)
if (keyboard.down(settings.rotate_cw)) {
rotationValue = ROT_SPEED;
} else if (keyboard.down(settings.rotate_ccw)) {
rotationValue = -ROT_SPEED;
}
// calculate movement (user input)
if (keyboard.down(settings.forward)) {
translation -= fggl::math::RIGHT;
}
if (keyboard.down(settings.backward)) {
translation += fggl::math::RIGHT;
}
if (keyboard.down(settings.right)) {
translation += fggl::math::FORWARD;
}
if (keyboard.down(settings.left)) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto camTransform = ecs.get<fggl::math::Transform>(cam);
auto camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
// apply movement
if (translation != glm::vec3(0.0F)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0F)) * PAN_SPEED;
deltaMove.w = 0.0F;
position += deltaMove;
pivot += deltaMove;
}
// apply rotation
if (rotationValue != 0.0F) {
glm::mat4 rotation = glm::rotate(MAT_IDENTITY, rotationValue, fggl::math::UP);
position = (rotation * (position - pivot)) + pivot;
}
camTransform.origin(position);
camComp.target = pivot;
}
void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
glm::vec3 translation(0.0F);
const auto &mouse = input.mouse;
// calculate movement (user input)
if (mouse.axis(MouseAxis::Y) < 0.9F) {
translation -= fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::Y) > -0.9F) {
translation += fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::X) > -0.9F) {
translation += fggl::math::FORWARD;
}
if (mouse.axis(MouseAxis::X) < 0.9F) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
// apply movement
if (translation != glm::vec3(0.0F)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0F)) * PAN_SPEED;
deltaMove.w = 0.0F;
position += deltaMove;
pivot += deltaMove;
}
// move camera
camTransform.origin(position);
camComp.target = pivot;
}
}
/*
* 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/>.
*/
#include <fggl/input/input.hpp>
namespace fggl::input {
void Input::frame(float dt) {
keyboard.frame(dt);
mouse.frame(dt);
gamepads.frame(dt);
}
} // namespace fggl::input
/*
* 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/>.
*/
#include <fggl/input/mouse.hpp>
namespace fggl::input {
void MouseState::operator=(const MouseState &rhs) {
for (int i = 0; i < 4; i++) {
axis[i] = rhs.axis[i];
}
buttons = rhs.buttons;
}
} // namespace fggl::input
# math
find_package(glm CONFIG REQUIRED)
target_link_libraries(fggl PUBLIC glm::glm)
target_sources(fggl
PRIVATE
shapes.cpp
triangulation.cpp
)
/*
* 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/>.
*/
#include "fggl/math/shapes.hpp"
#include <vector>
namespace fggl::math::phs3d {
void AABB::emtpy() {
min = {FLT_MAX, FLT_MAX, FLT_MAX};
max = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
}
void AABB::add(const math::vec3 &point) {
min = minElm(min, point);
max = maxElm(max, point);
}
auto AABB::fromPoints(const std::vector<math::vec3> &points) -> AABB {
AABB box;
for (const auto &point : points) {
box.add(point);
}
return box;
}
void AABB::set(const AABB &other, const math::mat4 &m) {
// TODO this needs testing, I'm not sure if our vectors are oriented the same as 9.4.4
min = max = m[3]; // should be the translation component of the matrix
// this feels like something that should be vectorizable...
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (m[i][j] > 0.0F) {
min[j] += m[i][j] * other.min[j];
max[j] += m[i][j] * other.max[j];
} else {
min[j] += m[i][j] * other.max[j];
max[j] += m[i][j] * other.min[j];
}
}
}
}
auto Plane::fromPoints(const math::vec3 p1, const math::vec3 p2, const math::vec3 p3) -> Plane {
const auto e3 = p2 - p1;
const auto e1 = p3 - p2;
auto normal = glm::normalize(glm::cross(e3, e1));
auto d = glm::dot(p2, normal);
return {normal, d};
}
static auto bestFitNormal(const std::vector<math::vec3> &points) -> math::vec3 {
assert(!points.empty());
math::vec3 result;
math::vec3 p = points.back();
for (std::size_t i = 0; i < points.size(); ++i) {
math::vec3 c = points[i];
result.x += (p.z + c.z) * (p.y - c.y);
result.y += (p.x + c.x) * (p.z - c.z);
result.z += (p.y + c.y) - (p.x - c.x);
p = c;
}
return glm::normalize(result);
};
static auto bestFitD(const std::vector<math::vec3> &points, glm::vec3 normal) -> float {
math::vec3 sum;
for (const auto &point : points) {
sum += point;
}
sum *= 1.0F / points.size();
return glm::dot(sum, normal);
}
const char X = 0;
const char Y = 1;
const char Z = 2;
struct bary_axis {
const char a;
const char b;
};
static auto baryCalcAxis(const math::vec3 &normal) -> bary_axis {
if ((fabs(normal.x) >= fabs(normal.y)) && (fabs(normal.x) >= fabs(normal.z))) {
// discard x
return {Y, Z};
} else if (fabs(normal.y) >= fabs(normal.z)) {
// discard y
return {Z, X};
} else {
// discard z
return {X, Y};
}
}
auto Triangle::CartToBarycentric(const math::vec3 &cart, Barycentric &outVal) -> bool {
// everything is const because I'm paying the compiler is smarter than me...
const auto d1 = v[1] - v[0];
const auto d2 = v[2] - v[1];
const auto n = glm::cross(d1, d2);
// 0 = x, 1 = y, 2 = z
const auto ax = baryCalcAxis(n);
// first axis
const float u1 = v[0][ax.a] - v[2][ax.a];
const float u2 = v[1][ax.a] - v[2][ax.a];
const float u3 = cart[ax.a] - v[0][ax.a];
const float u4 = cart[ax.a] - v[2][ax.a];
// second axis
const float v1 = v[0][ax.b] - v[2][ax.b];
const float v2 = v[1][ax.b] - v[2][ax.b];
const float v3 = cart[ax.b] - v[0][ax.b];
const float v4 = cart[ax.b] - v[2][ax.b];
const float denom = v1 * u2 - v2 * u1;
if (denom == 0.0F) {
return false;
}
// finally, we can work it out
const float oneOverDenom = 1.0F / denom;
outVal.b[0] = (v4 * u2 - v2 * u4) * oneOverDenom;
outVal.b[1] = (v1 * u3 - v3 * u1) * oneOverDenom;
outVal.b[2] = 1.0F - outVal.b[0] - outVal.b[1];
return true;
}
auto Triangle::CartToBarycentric2(const math::vec3 &cart, Barycentric &outVal) -> bool {
const auto e1 = v[2] - v[1];
const auto e2 = v[0] - v[2];
const auto e3 = v[1] - v[0];
const auto d1 = cart - v[0];
const auto d2 = cart - v[1];
const auto d3 = cart - v[2];
const auto normal = glm::normalize(glm::cross(e1, e2));
const auto denom = glm::dot(glm::cross(e1, e2), normal);
assert(denom != 0.0F);
outVal.b[0] = glm::dot(glm::cross(e1, d3), normal) / denom;
outVal.b[1] = glm::dot(glm::cross(e2, d1), normal) / denom;
outVal.b[2] = glm::dot(glm::cross(e3, d2), normal) / denom;
return true;
}
} // namespace fggl::math::phys3d
\ No newline at end of file
/*
* 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/>.
*/
#include "fggl/math/triangulation.hpp"
namespace fggl::math {
/**
* Fast Triangulation for convex polygons.
*/
void fan_triangulation(const PolygonVertex &polygon, data::Mesh2D &mesh) {
assert(polygon.size() >= 3);
// add the first two points to the mesh
auto firstIdx = mesh.add_vertex(polygon[0]);
auto prevIdx = mesh.add_vertex(polygon[1]);
// deal with the indices
const auto nTris = polygon.size() - 2;
for (auto i = 0U; i < nTris; i++) {
mesh.add_index(firstIdx);
mesh.add_index(prevIdx);
auto currIdx = mesh.add_vertex(polygon[i + 2]);
mesh.add_index(currIdx);
prevIdx = currIdx;
}
}
} // namespace fggl::math