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 633 additions and 171 deletions
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)
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
namespace fggl::gui { namespace fggl::gui {
Widget *Container::getChildAt(const math::vec2 &point) { auto Container::getChildAt(const math::vec2 &point) -> Widget * {
for (auto &child : m_children) { for (auto &child : m_children) {
if (child->contains(point)) { if (child->contains(point)) {
return child->getChildAt(point); return child->getChildAt(point);
...@@ -27,7 +27,7 @@ namespace fggl::gui { ...@@ -27,7 +27,7 @@ namespace fggl::gui {
return nullptr; return nullptr;
} }
bool Container::contains(const math::vec2 &point) { auto Container::contains(const math::vec2 &point) -> bool {
return true; return true;
} }
...@@ -82,8 +82,8 @@ namespace fggl::gui { ...@@ -82,8 +82,8 @@ namespace fggl::gui {
int rows = m_children.size() / m_cols; int rows = m_children.size() / m_cols;
// figure out the width and heights // figure out the width and heights
float* widths = new float[m_cols]{0.0F}; auto* widths = new float[m_cols]{0.0F};
float* heights = new float[rows]{0.0F}; auto* heights = new float[rows]{0.0F};
for ( auto idx = 0U; idx < m_children.size(); ++idx) { for ( auto idx = 0U; idx < m_children.size(); ++idx) {
auto& child = m_children[idx]; auto& child = m_children[idx];
int col = idx % m_cols; int col = idx % m_cols;
...@@ -95,8 +95,8 @@ namespace fggl::gui { ...@@ -95,8 +95,8 @@ namespace fggl::gui {
// populate the grid // populate the grid
fggl::math::vec2i pos{0, 0}; fggl::math::vec2i pos{0, 0};
int row = 0; unsigned int row = 0;
int col = 0; unsigned int col = 0;
for ( auto& child : m_children ) { for ( auto& child : m_children ) {
fggl::math::vec2i size{ widths[col], heights[row] }; fggl::math::vec2i size{ widths[col], heights[row] };
child->size(pos, size); child->size(pos, size);
...@@ -113,6 +113,9 @@ namespace fggl::gui { ...@@ -113,6 +113,9 @@ namespace fggl::gui {
} }
} }
// cleanup variables
delete[] widths;
delete[] heights;
} }
} }
...@@ -145,7 +148,7 @@ namespace fggl::gui { ...@@ -145,7 +148,7 @@ namespace fggl::gui {
void Panel::render(gfx::Paint &paint) { void Panel::render(gfx::Paint &paint) {
// background painting time // background painting time
gfx::Path2D background(topLeft()); gfx::Path2D background(topLeft());
background.colour(math::vec3(32.0f / 255.0f, 74.0F / 255.0F, 135.0F / 255.0F)); background.colour(math::vec3(32.0F / 255.0F, 74.0F / 255.0F, 135.0F / 255.0F));
draw_box(background, topLeft(), bottomRight()); draw_box(background, topLeft(), bottomRight());
paint.fill(background); paint.fill(background);
......
...@@ -25,19 +25,19 @@ namespace fggl::gui { ...@@ -25,19 +25,19 @@ namespace fggl::gui {
FontLibrary::FontLibrary(data::Storage *storage) : m_context(nullptr), m_storage(storage) { FontLibrary::FontLibrary(data::Storage *storage) : m_context(nullptr), m_storage(storage) {
FT_Init_FreeType(&m_context); FT_Init_FreeType(&m_context);
m_defaultFont = getFont(DEFAULT_FONT_NAME);
} }
FontLibrary::~FontLibrary() { FontLibrary::~FontLibrary() {
// free all fonts // free all fonts
for (auto &face : m_cache) { m_defaultFont = nullptr;
face.second = nullptr; m_cache.clear();
}
// shut the library down // shut the library down
FT_Done_FreeType(m_context); FT_Done_FreeType(m_context);
} }
GlyphMetrics &FontFace::populateMetrics(char letter) { auto FontFace::populateMetrics(char letter) -> GlyphMetrics & {
if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER)) { if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER)) {
// something bad happened // something bad happened
return m_metrics['?']; return m_metrics['?'];
...@@ -54,7 +54,7 @@ namespace fggl::gui { ...@@ -54,7 +54,7 @@ namespace fggl::gui {
return it.first->second; return it.first->second;
} }
void FontFace::texture(char letter, int &width, int &height, void **buff) { void FontFace::texture(char letter, int &width, int &height, unsigned char **buff) {
if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER)) { if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER)) {
// something bad happened // something bad happened
return; return;
......
/*
* 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
...@@ -20,9 +20,9 @@ ...@@ -20,9 +20,9 @@
namespace fggl::gui { namespace fggl::gui {
void buttonBorder(gfx::Path2D &path, glm::vec2 pos, glm::vec2 size) { void button_border(gfx::Path2D &path, glm::vec2 pos, glm::vec2 size) {
// outer box // outer box
path.colour({1.0f, 0.0f, 0.0f}); path.colour({1.0F, 0.0F, 0.0F});
path.pathTo({pos.x + size.x, pos.y}); path.pathTo({pos.x + size.x, pos.y});
path.pathTo({pos.x + size.x, pos.y + size.y}); path.pathTo({pos.x + size.x, pos.y + size.y});
path.pathTo({pos.x, pos.y + size.y}); path.pathTo({pos.x, pos.y + size.y});
...@@ -32,7 +32,7 @@ namespace fggl::gui { ...@@ -32,7 +32,7 @@ namespace fggl::gui {
math::vec2 innerTop{pos.x + 5, pos.y + 5}; math::vec2 innerTop{pos.x + 5, pos.y + 5};
math::vec2 innerBottom{pos.x + size.x - 5, pos.y + size.y - 5}; math::vec2 innerBottom{pos.x + size.x - 5, pos.y + size.y - 5};
path.colour({1.0f, 1.0f, 0.0f}); path.colour({1.0F, 1.0F, 0.0F});
path.moveTo({innerTop.x, innerTop.y}); path.moveTo({innerTop.x, innerTop.y});
path.pathTo({innerBottom.x, innerTop.y}); path.pathTo({innerBottom.x, innerTop.y});
path.pathTo({innerBottom.x, innerBottom.y}); path.pathTo({innerBottom.x, innerBottom.y});
...@@ -52,7 +52,7 @@ namespace fggl::gui { ...@@ -52,7 +52,7 @@ namespace fggl::gui {
const auto bottomRight{topLeft + size}; const auto bottomRight{topLeft + size};
// background // background
path.colour({0.5f, 0.5f, 0.5f}); path.colour({0.5F, 0.5F, 0.5F});
draw_box(path, topLeft, bottomRight); draw_box(path, topLeft, bottomRight);
// fill // fill
...@@ -65,13 +65,13 @@ namespace fggl::gui { ...@@ -65,13 +65,13 @@ namespace fggl::gui {
innerBottom.x = innerTop.x + barWidth; innerBottom.x = innerTop.x + barWidth;
// draw the bar // draw the bar
path.colour({0.8f, 0.0f, 0.0f}); path.colour({0.8F, 0.0F, 0.0F});
draw_box(path, innerTop, innerBottom); draw_box(path, innerTop, innerBottom);
// part of the bar that's not filled in // part of the bar that's not filled in
math::vec2 emptyTop{innerBottom.x, innerTop.y}; math::vec2 emptyTop{innerBottom.x, innerTop.y};
math::vec2 emptyBottom{trueBottom, innerBottom.y}; math::vec2 emptyBottom{trueBottom, innerBottom.y};
path.colour({0.4f, 0.0f, 0.0f}); path.colour({0.4F, 0.0F, 0.0F});
draw_box(path, emptyTop, emptyBottom); draw_box(path, emptyTop, emptyBottom);
} }
...@@ -90,7 +90,7 @@ namespace fggl::gui { ...@@ -90,7 +90,7 @@ namespace fggl::gui {
math::vec2 selectorTop{innerTop.x + selectorValue - (selectorWidth / 2), topLeft.y}; math::vec2 selectorTop{innerTop.x + selectorValue - (selectorWidth / 2), topLeft.y};
math::vec2 selectorBottom{selectorTop.x + selectorWidth, bottomRight.y}; math::vec2 selectorBottom{selectorTop.x + selectorWidth, bottomRight.y};
path.colour({1.0f, 1.0f, 1.0f}); path.colour({1.0F, 1.0F, 1.0F});
draw_box(path, selectorTop, selectorBottom); draw_box(path, selectorTop, selectorBottom);
} }
...@@ -101,18 +101,18 @@ namespace fggl::gui { ...@@ -101,18 +101,18 @@ namespace fggl::gui {
math::vec2 innerTop{pos.x + 5, pos.y + 5}; math::vec2 innerTop{pos.x + 5, pos.y + 5};
math::vec2 innerBottom{pos.x + size.x - 5, pos.y + size.y - 5}; math::vec2 innerBottom{pos.x + size.x - 5, pos.y + size.y - 5};
math::vec3 baseColour{0.5f, 0.5f, 0.5f}; math::vec3 baseColour{0.5F, 0.5F, 0.5F};
if (active) { if (active) {
baseColour *= 1.2f; baseColour *= 1.2F;
} }
if (pressed) { if (pressed) {
baseColour *= 0.8f; baseColour *= 0.8F;
} }
math::vec3 lightColour{baseColour * 1.2f}; math::vec3 lightColour{baseColour * 1.2F};
math::vec3 darkColour{baseColour * 0.8f}; math::vec3 darkColour{baseColour * 0.8F};
if (pressed) { if (pressed) {
// flip light and dark for selected buttons // flip light and dark for selected buttons
auto tmp = darkColour; auto tmp = darkColour;
......
...@@ -55,7 +55,7 @@ namespace fggl::gui { ...@@ -55,7 +55,7 @@ namespace fggl::gui {
m_label.size(topLeft(), size()); m_label.size(topLeft(), size());
} }
std::string Button::label() const { auto Button::label() const -> std::string {
return m_label.text(); return m_label.text();
} }
......
...@@ -27,10 +27,10 @@ namespace fggl::input { ...@@ -27,10 +27,10 @@ namespace fggl::input {
// see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/ // see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/
auto &camTransform = ecs.get<fggl::math::Transform>(cam); auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam); auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
auto &mouse = input.mouse; const auto &mouse = input.mouse;
glm::vec4 position(camTransform.origin(), 1.0f); glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0f); glm::vec4 pivot(camComp.target, 1.0F);
glm::mat4 view = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up()); glm::mat4 view = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
glm::vec3 viewDir = -glm::transpose(view)[2]; glm::vec3 viewDir = -glm::transpose(view)[2];
glm::vec3 rightDir = glm::transpose(view)[0]; glm::vec3 rightDir = glm::transpose(view)[0];
...@@ -41,12 +41,12 @@ namespace fggl::input { ...@@ -41,12 +41,12 @@ namespace fggl::input {
float yAngle = (-mouse.axisDelta(fggl::input::MouseAxis::Y)) * deltaAngleY; float yAngle = (-mouse.axisDelta(fggl::input::MouseAxis::Y)) * deltaAngleY;
// rotate the camera around the pivot on the first axis // rotate the camera around the pivot on the first axis
glm::mat4x4 rotationMatrixX(1.0f); glm::mat4x4 rotationMatrixX(1.0F);
rotationMatrixX = glm::rotate(rotationMatrixX, xAngle, fggl::math::UP); rotationMatrixX = glm::rotate(rotationMatrixX, xAngle, fggl::math::UP);
position = (rotationMatrixX * (position - pivot)) + pivot; position = (rotationMatrixX * (position - pivot)) + pivot;
// rotate the camera aroud the pivot on the second axis // rotate the camera around the pivot on the second axis
glm::mat4x4 rotationMatrixY(1.0f); glm::mat4x4 rotationMatrixY(1.0F);
rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir); rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir);
glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot; glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot;
...@@ -66,17 +66,17 @@ namespace fggl::input { ...@@ -66,17 +66,17 @@ namespace fggl::input {
glm::vec3 motion(0.0F); glm::vec3 motion(0.0F);
float delta = input.mouse.axis(fggl::input::MouseAxis::SCROLL_Y); 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)) { if ((glm::length(dir) < maxZoom && delta < 0.0F) || (glm::length(dir) > minZoom && delta > 0.0F)) {
motion -= (forward * delta); motion -= (forward * delta);
camTransform.origin(camTransform.origin() + motion); camTransform.origin(camTransform.origin() + motion);
} }
} }
void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) { void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
float rotationValue = 0.0f; float rotationValue = 0.0F;
glm::vec3 translation(0.0f); glm::vec3 translation(0.0F);
auto &keyboard = input.keyboard; const auto &keyboard = input.keyboard;
auto &settings = ecs.get<FreeCamKeys>(cam); auto &settings = ecs.get<FreeCamKeys>(cam);
// calculate rotation (user input) // calculate rotation (user input)
...@@ -107,24 +107,24 @@ namespace fggl::input { ...@@ -107,24 +107,24 @@ namespace fggl::input {
auto camTransform = ecs.get<fggl::math::Transform>(cam); auto camTransform = ecs.get<fggl::math::Transform>(cam);
auto camComp = ecs.get<fggl::gfx::Camera>(cam); auto camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0f); glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0f); glm::vec4 pivot(camComp.target, 1.0F);
// apply movement // apply movement
if (translation != glm::vec3(0.0f)) { if (translation != glm::vec3(0.0F)) {
const auto rotation = (position - pivot); const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z); const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP); const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0f)) * PAN_SPEED; auto deltaMove = (rotationMat * glm::vec4(translation, 1.0F)) * PAN_SPEED;
deltaMove.w = 0.0f; deltaMove.w = 0.0F;
position += deltaMove; position += deltaMove;
pivot += deltaMove; pivot += deltaMove;
} }
// apply rotation // apply rotation
if (rotationValue != 0.0f) { if (rotationValue != 0.0F) {
glm::mat4 rotation = glm::rotate(MAT_IDENTITY, rotationValue, fggl::math::UP); glm::mat4 rotation = glm::rotate(MAT_IDENTITY, rotationValue, fggl::math::UP);
position = (rotation * (position - pivot)) + pivot; position = (rotation * (position - pivot)) + pivot;
} }
...@@ -134,24 +134,24 @@ namespace fggl::input { ...@@ -134,24 +134,24 @@ namespace fggl::input {
} }
void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) { void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
glm::vec3 translation(0.0f); glm::vec3 translation(0.0F);
auto &mouse = input.mouse; const auto &mouse = input.mouse;
// calculate movement (user input) // calculate movement (user input)
if (mouse.axis(MouseAxis::Y) < 0.9f) { if (mouse.axis(MouseAxis::Y) < 0.9F) {
translation -= fggl::math::RIGHT; translation -= fggl::math::RIGHT;
} }
if (mouse.axis(MouseAxis::Y) > -0.9f) { if (mouse.axis(MouseAxis::Y) > -0.9F) {
translation += fggl::math::RIGHT; translation += fggl::math::RIGHT;
} }
if (mouse.axis(MouseAxis::X) > -0.9f) { if (mouse.axis(MouseAxis::X) > -0.9F) {
translation += fggl::math::FORWARD; translation += fggl::math::FORWARD;
} }
if (mouse.axis(MouseAxis::X) < 0.9f) { if (mouse.axis(MouseAxis::X) < 0.9F) {
translation -= fggl::math::FORWARD; translation -= fggl::math::FORWARD;
} }
...@@ -159,17 +159,17 @@ namespace fggl::input { ...@@ -159,17 +159,17 @@ namespace fggl::input {
auto &camTransform = ecs.get<fggl::math::Transform>(cam); auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam); auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0f); glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0f); glm::vec4 pivot(camComp.target, 1.0F);
// apply movement // apply movement
if (translation != glm::vec3(0.0f)) { if (translation != glm::vec3(0.0F)) {
const auto rotation = (position - pivot); const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z); const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP); const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0f)) * PAN_SPEED; auto deltaMove = (rotationMat * glm::vec4(translation, 1.0F)) * PAN_SPEED;
deltaMove.w = 0.0f; deltaMove.w = 0.0F;
position += deltaMove; position += deltaMove;
pivot += deltaMove; pivot += deltaMove;
......
...@@ -27,7 +27,7 @@ namespace fggl::math::phs3d { ...@@ -27,7 +27,7 @@ namespace fggl::math::phs3d {
max = maxElm(max, point); max = maxElm(max, point);
} }
AABB AABB::fromPoints(const std::vector<math::vec3> &points) { auto AABB::fromPoints(const std::vector<math::vec3> &points) -> AABB {
AABB box; AABB box;
for (const auto &point : points) { for (const auto &point : points) {
box.add(point); box.add(point);
...@@ -42,7 +42,7 @@ namespace fggl::math::phs3d { ...@@ -42,7 +42,7 @@ namespace fggl::math::phs3d {
// this feels like something that should be vectorizable... // this feels like something that should be vectorizable...
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
if (m[i][j] > 0.0f) { if (m[i][j] > 0.0F) {
min[j] += m[i][j] * other.min[j]; min[j] += m[i][j] * other.min[j];
max[j] += m[i][j] * other.max[j]; max[j] += m[i][j] * other.max[j];
} else { } else {
...@@ -53,7 +53,7 @@ namespace fggl::math::phs3d { ...@@ -53,7 +53,7 @@ namespace fggl::math::phs3d {
} }
} }
Plane Plane::fromPoints(const math::vec3 p1, const math::vec3 p2, const math::vec3 p3) { auto Plane::fromPoints(const math::vec3 p1, const math::vec3 p2, const math::vec3 p3) -> Plane {
const auto e3 = p2 - p1; const auto e3 = p2 - p1;
const auto e1 = p3 - p2; const auto e1 = p3 - p2;
auto normal = glm::normalize(glm::cross(e3, e1)); auto normal = glm::normalize(glm::cross(e3, e1));
...@@ -61,7 +61,7 @@ namespace fggl::math::phs3d { ...@@ -61,7 +61,7 @@ namespace fggl::math::phs3d {
return {normal, d}; return {normal, d};
} }
static math::vec3 bestFitNormal(const std::vector<math::vec3> &points) { static auto bestFitNormal(const std::vector<math::vec3> &points) -> math::vec3 {
assert(!points.empty()); assert(!points.empty());
math::vec3 result; math::vec3 result;
...@@ -80,9 +80,9 @@ namespace fggl::math::phs3d { ...@@ -80,9 +80,9 @@ namespace fggl::math::phs3d {
return glm::normalize(result); return glm::normalize(result);
}; };
static float bestFitD(const std::vector<math::vec3> &points, glm::vec3 normal) { static auto bestFitD(const std::vector<math::vec3> &points, glm::vec3 normal) -> float {
math::vec3 sum; math::vec3 sum;
for (auto &point : points) { for (const auto &point : points) {
sum += point; sum += point;
} }
sum *= 1.0F / points.size(); sum *= 1.0F / points.size();
...@@ -98,7 +98,7 @@ namespace fggl::math::phs3d { ...@@ -98,7 +98,7 @@ namespace fggl::math::phs3d {
const char b; const char b;
}; };
static bary_axis baryCalcAxis(const math::vec3 &normal) { static auto baryCalcAxis(const math::vec3 &normal) -> bary_axis {
if ((fabs(normal.x) >= fabs(normal.y)) && (fabs(normal.x) >= fabs(normal.z))) { if ((fabs(normal.x) >= fabs(normal.y)) && (fabs(normal.x) >= fabs(normal.z))) {
// discard x // discard x
return {Y, Z}; return {Y, Z};
...@@ -111,7 +111,7 @@ namespace fggl::math::phs3d { ...@@ -111,7 +111,7 @@ namespace fggl::math::phs3d {
} }
} }
bool Triangle::CartToBarycentric(const math::vec3 &cart, Barycentric &outVal) { auto Triangle::CartToBarycentric(const math::vec3 &cart, Barycentric &outVal) -> bool {
// everything is const because I'm paying the compiler is smarter than me... // everything is const because I'm paying the compiler is smarter than me...
const auto d1 = v[1] - v[0]; const auto d1 = v[1] - v[0];
...@@ -134,19 +134,19 @@ namespace fggl::math::phs3d { ...@@ -134,19 +134,19 @@ namespace fggl::math::phs3d {
const float v4 = cart[ax.b] - v[2][ax.b]; const float v4 = cart[ax.b] - v[2][ax.b];
const float denom = v1 * u2 - v2 * u1; const float denom = v1 * u2 - v2 * u1;
if (denom == 0.0f) { if (denom == 0.0F) {
return false; return false;
} }
// finally, we can work it out // finally, we can work it out
const float oneOverDenom = 1.0f / denom; const float oneOverDenom = 1.0F / denom;
outVal.b[0] = (v4 * u2 - v2 * u4) * oneOverDenom; outVal.b[0] = (v4 * u2 - v2 * u4) * oneOverDenom;
outVal.b[1] = (v1 * u3 - v3 * u1) * oneOverDenom; outVal.b[1] = (v1 * u3 - v3 * u1) * oneOverDenom;
outVal.b[2] = 1.0f - outVal.b[0] - outVal.b[1]; outVal.b[2] = 1.0F - outVal.b[0] - outVal.b[1];
return true; return true;
} }
bool Triangle::CartToBarycentric2(const math::vec3 &cart, Barycentric &outVal) { auto Triangle::CartToBarycentric2(const math::vec3 &cart, Barycentric &outVal) -> bool {
const auto e1 = v[2] - v[1]; const auto e1 = v[2] - v[1];
const auto e2 = v[0] - v[2]; const auto e2 = v[0] - v[2];
const auto e3 = v[1] - v[0]; const auto e3 = v[1] - v[0];
...@@ -157,7 +157,7 @@ namespace fggl::math::phs3d { ...@@ -157,7 +157,7 @@ namespace fggl::math::phs3d {
const auto normal = glm::normalize(glm::cross(e1, e2)); const auto normal = glm::normalize(glm::cross(e1, e2));
const auto denom = glm::dot(glm::cross(e1, e2), normal); const auto denom = glm::dot(glm::cross(e1, e2), normal);
assert(denom != 0.0f); assert(denom != 0.0F);
outVal.b[0] = glm::dot(glm::cross(e1, d3), normal) / denom; outVal.b[0] = glm::dot(glm::cross(e1, d3), normal) / denom;
outVal.b[1] = glm::dot(glm::cross(e2, d1), normal) / denom; outVal.b[1] = glm::dot(glm::cross(e2, d1), normal) / denom;
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
*/ */
#include "fggl/math/triangulation.hpp" #include "fggl/math/triangulation.hpp"
#include <iostream>
namespace fggl::math { namespace fggl::math {
...@@ -29,7 +28,7 @@ namespace fggl::math { ...@@ -29,7 +28,7 @@ namespace fggl::math {
// deal with the indices // deal with the indices
const auto nTris = polygon.size() - 2; const auto nTris = polygon.size() - 2;
for (auto i = 0u; i < nTris; i++) { for (auto i = 0U; i < nTris; i++) {
mesh.add_index(firstIdx); mesh.add_index(firstIdx);
mesh.add_index(prevIdx); mesh.add_index(prevIdx);
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
namespace fggl::phys { namespace fggl::phys {
bool NullPhysics::factory(modules::ModuleService serviceName, modules::Services &serviceManager) { auto NullPhysics::factory(modules::ServiceName serviceName, modules::Services &serviceManager) -> bool {
if (serviceName == phys::PhysicsProvider::service) { if (serviceName == phys::PhysicsProvider::service) {
serviceManager.bind<phys::PhysicsProvider, NullPhysicsProvider>(); serviceManager.bind<phys::PhysicsProvider, NullPhysicsProvider>();
return true; return true;
......
...@@ -25,34 +25,36 @@ ...@@ -25,34 +25,36 @@
namespace fggl::platform { namespace fggl::platform {
inline static std::filesystem::path get_user_path(const char *env, const char *fallback) { namespace {
const char *path = std::getenv(env); inline auto get_user_path(const char *env, const char *fallback) -> std::filesystem::path {
if (path != nullptr) { const char *path = std::getenv(env);
return {path}; if (path != nullptr) {
return {path};
}
return {fallback};
} }
return {fallback};
}
static std::vector<std::filesystem::path> get_path_list(const char *env, const char *folderName) { auto get_path_list(const char *env, const char *folderName) -> std::vector<std::filesystem::path> {
const char *pathList = std::getenv(env); const char *pathList = std::getenv(env);
std::vector<std::filesystem::path> paths; std::vector<std::filesystem::path> paths;
if (pathList) { if (pathList) {
std::string pathListStr(pathList); std::string pathListStr(pathList);
std::string::size_type pos = 0; std::string::size_type pos = 0;
while (pos < pathListStr.size()) { while (pos < pathListStr.size()) {
std::string::size_type nextPos = pathListStr.find(':', pos); std::string::size_type nextPos = pathListStr.find(':', pos);
if (nextPos == std::string::npos) { if (nextPos == std::string::npos) {
nextPos = pathListStr.size(); nextPos = pathListStr.size();
}
std::string path = pathListStr.substr(pos, nextPos - pos);
paths.push_back(std::filesystem::path(path) / folderName);
pos = nextPos + 1;
} }
std::string path = pathListStr.substr(pos, nextPos - pos);
paths.push_back(std::filesystem::path(path) / folderName);
pos = nextPos + 1;
} }
return paths;
} }
return paths;
} }
EnginePaths calc_engine_paths(const char *base) { auto calc_engine_paths(const char *base) -> EnginePaths {
auto dataDirs = get_path_list(ENV_DATA_DIRS, base); auto dataDirs = get_path_list(ENV_DATA_DIRS, base);
if (dataDirs.empty()) { if (dataDirs.empty()) {
for (const auto &defaultDir : DEFAULT_DATA_DIRS) { for (const auto &defaultDir : DEFAULT_DATA_DIRS) {
...@@ -76,7 +78,7 @@ namespace fggl::platform { ...@@ -76,7 +78,7 @@ namespace fggl::platform {
}; };
} }
std::filesystem::path locate_data(const EnginePaths &paths, const std::filesystem::path &relPath) { auto locate_data(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userData / relPath; auto userPath = paths.userData / relPath;
if (std::filesystem::exists(userPath)) { if (std::filesystem::exists(userPath)) {
return userPath; return userPath;
...@@ -98,11 +100,11 @@ namespace fggl::platform { ...@@ -98,11 +100,11 @@ namespace fggl::platform {
return debugPath; return debugPath;
} }
// if the file existed, it shoudl exist in the user space // if the file existed, it should exist in the user space
return userPath; return userPath;
} }
std::filesystem::path locate_config(const EnginePaths &paths, const std::filesystem::path &relPath) { auto locate_config(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userConfig / relPath; auto userPath = paths.userConfig / relPath;
if (std::filesystem::exists(userPath)) { if (std::filesystem::exists(userPath)) {
return userPath; return userPath;
...@@ -120,7 +122,7 @@ namespace fggl::platform { ...@@ -120,7 +122,7 @@ namespace fggl::platform {
return userPath; return userPath;
} }
std::filesystem::path locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath) { auto locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userCache / relPath; auto userPath = paths.userCache / relPath;
if (std::filesystem::exists(userPath)) { if (std::filesystem::exists(userPath)) {
return userPath; return userPath;
......
...@@ -28,7 +28,7 @@ namespace fggl::scenes { ...@@ -28,7 +28,7 @@ namespace fggl::scenes {
m_input = app.service<input::Input>(); m_input = app.service<input::Input>();
} }
void GameBase::update(float dt) { void GameBase::update(float /*dt*/) {
// detect the user quitting // detect the user quitting
if (m_input != nullptr) { if (m_input != nullptr) {
bool escapePressed = m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_ESCAPE)); bool escapePressed = m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_ESCAPE));
...@@ -59,7 +59,7 @@ namespace fggl::scenes { ...@@ -59,7 +59,7 @@ namespace fggl::scenes {
m_world.reset(); m_world.reset();
} }
void Game::update(float dt) { void Game::update(float /*dt*/) {
assert(m_world && "called game update, but there was no world - was activate called?"); assert(m_world && "called game update, but there was no world - was activate called?");
if (m_input != nullptr) { if (m_input != nullptr) {
...@@ -67,18 +67,23 @@ namespace fggl::scenes { ...@@ -67,18 +67,23 @@ namespace fggl::scenes {
if (escapePressed) { if (escapePressed) {
m_owner.change_state(m_previous); m_owner.change_state(m_previous);
} }
if ( m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F2)) ) {
m_debug = !m_debug;
}
} }
if (m_phys != nullptr) { if (m_phys != nullptr) {
m_phys->step(); m_phys->step();
} }
// debug render toggle
//m_world->reapEntities(); //m_world->reapEntities();
} }
void Game::render(fggl::gfx::Graphics &gfx) { void Game::render(fggl::gfx::Graphics &gfx) {
if (m_world != nullptr) { if (m_world != nullptr) {
gfx.drawScene(*m_world); gfx.drawScene(*m_world, m_debug);
} }
} }
......
...@@ -22,19 +22,21 @@ namespace fggl::scenes { ...@@ -22,19 +22,21 @@ namespace fggl::scenes {
using fggl::input::MouseButton; using fggl::input::MouseButton;
using fggl::input::MouseAxis; using fggl::input::MouseAxis;
BasicMenu::BasicMenu(fggl::App &app) : AppState(app), m_inputs(nullptr), m_active(), m_hover(nullptr) { constexpr float SCREEN_WIDTH = 1920.0F;
m_inputs = app.service<input::Input>(); constexpr float SCREEN_HEIGHT = 1080.0F;
BasicMenu::BasicMenu(fggl::App &app) : AppState(app), m_inputs(app.service<input::Input>()), m_active(), m_cursorPos(), m_hover(nullptr) {
} }
void BasicMenu::update(float dt) { void BasicMenu::update(float /*dt*/) {
if (m_inputs != nullptr) { if (m_inputs != nullptr) {
m_cursorPos.x = m_inputs->mouse.axis(MouseAxis::X); m_cursorPos.x = m_inputs->mouse.axis(MouseAxis::X);
m_cursorPos.y = m_inputs->mouse.axis(MouseAxis::Y); m_cursorPos.y = m_inputs->mouse.axis(MouseAxis::Y);
// in canvas space // in canvas space
math::vec2 projected; math::vec2 projected;
projected.x = math::rescale_ndc(m_cursorPos.x, 0, 1920.f); projected.x = math::rescale_ndc(m_cursorPos.x, 0, SCREEN_WIDTH);
projected.y = math::rescale_ndc(m_cursorPos.y, 0, 1080.0f); projected.y = math::rescale_ndc(m_cursorPos.y, 0, SCREEN_HEIGHT);
m_canvas.onMouseOver(projected); m_canvas.onMouseOver(projected);
if (m_inputs->mouse.pressed(MouseButton::LEFT)) { if (m_inputs->mouse.pressed(MouseButton::LEFT)) {
...@@ -61,24 +63,23 @@ namespace fggl::scenes { ...@@ -61,24 +63,23 @@ namespace fggl::scenes {
} }
void BasicMenu::add(const std::string &name, callback cb) { void BasicMenu::add(const std::string &name, const Callback& callback) {
m_items[name] = cb; m_items[name] = callback;
const math::vec2 btnSize{150.0f, 30.0f}; const math::vec2 btnSize{150.0F, 30.0F};
const float spacing = 5; const float spacing = 5;
const float padX = 50.0f; const float padX = 50.0F;
const float padY = 50.0f; const float padY = 50.0F;
// figure out the position based off the old logic // figure out the position based off the old logic
// FIXME should be the container's job // FIXME should be the container's job
math::vec2 pos{1920.0f - (padX + btnSize.x), padY}; math::vec2 pos{SCREEN_WIDTH - (padX + btnSize.x), padY};
auto btnIdx = m_items.size() - 1; pos.y += ( (m_items.size()-1.0F) * (btnSize.y + spacing));
pos.y += (btnIdx * (btnSize.y + spacing));
// build the button // build the button
auto btn = std::make_unique<gui::Button>(pos, btnSize); auto btn = std::make_unique<gui::Button>(pos, btnSize);
btn->label(name); btn->label(name);
btn->addCallback(cb); btn->addCallback(callback);
m_canvas.add(std::move(btn)); m_canvas.add(std::move(btn));
} }
......
...@@ -27,7 +27,7 @@ namespace fggl::util { ...@@ -27,7 +27,7 @@ namespace fggl::util {
std::map<GUID, std::string> guidTable; std::map<GUID, std::string> guidTable;
} }
GUID internString(const char *str) { auto intern_string(const char *str) -> GUID {
assert(str != nullptr); assert(str != nullptr);
GUID guid = make_guid(str); GUID guid = make_guid(str);
...@@ -40,7 +40,7 @@ namespace fggl::util { ...@@ -40,7 +40,7 @@ namespace fggl::util {
return guid; return guid;
} }
std::string guidToString(GUID guid) { auto guid_to_string(GUID guid) -> std::string {
auto tableValue = guidTable.find(guid); auto tableValue = guidTable.find(guid);
if (tableValue != guidTable.end()) { if (tableValue != guidTable.end()) {
return tableValue->second; return tableValue->second;
...@@ -51,12 +51,12 @@ namespace fggl::util { ...@@ -51,12 +51,12 @@ namespace fggl::util {
} }
fggl::util::GUID operator "" _fid(const char *str) { auto operator "" _fid(const char *str) -> fggl::util::GUID {
fggl::util::internString(str); fggl::util::intern_string(str);
return fggl::util::make_guid(str); return fggl::util::make_guid(str);
} }
fggl::util::GUID operator "" _fid(const char *str, std::size_t) { auto operator "" _fid(const char *str, std::size_t) -> fggl::util::GUID {
fggl::util::internString(str); fggl::util::intern_string(str);
return fggl::util::make_guid(str); return fggl::util::make_guid(str);
} }
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#ifndef FGGL_INCLUDE_FGGL_APP_HPP #ifndef FGGL_INCLUDE_FGGL_APP_HPP
#define FGGL_INCLUDE_FGGL_APP_HPP #define FGGL_INCLUDE_FGGL_APP_HPP
#define assertm(exp, msg) assert(((void)msg, exp)) #define ASSERT_MSG(exp, msg) assert(((void)(msg), exp))
#include <cassert> #include <cassert>
#include <string> #include <string>
...@@ -44,6 +44,8 @@ namespace fggl { ...@@ -44,6 +44,8 @@ namespace fggl {
* A state is responsible for managing user interaction with the app. When created, the appstate * A state is responsible for managing user interaction with the app. When created, the appstate
* is passed a reference to the application that owns it. The lifetime of the state is bounded * is passed a reference to the application that owns it. The lifetime of the state is bounded
* by the lifetype of this object. * by the lifetype of this object.
*
* Ie. the lifetime of reference to the App is at least that of the AppState.
* *
* @param owner a non-owned reference to the owner of the state. * @param owner a non-owned reference to the owner of the state.
*/ */
...@@ -72,11 +74,27 @@ namespace fggl { ...@@ -72,11 +74,27 @@ namespace fggl {
*/ */
virtual void render(gfx::Graphics &paint) = 0; virtual void render(gfx::Graphics &paint) = 0;
/**
* Notify the State it has just been switched to.
*
* This state should perform any setup it requires (eg, registering callbacks, requesting
* resources, etc...).
*
* FIXME: This should probably be RAII
*/
virtual void activate() {} virtual void activate() {}
/**
* Notify the State it is about to be switched from.
*
* This state should perform any cleanup it requires (eg, unregistering callbacks, freeing
* resources, etc...).
*
* FIXME: This should probably be RAII
*/
virtual void deactivate() {} virtual void deactivate() {}
inline App& owner() { [[nodiscard]] inline auto owner() const -> App& {
return m_owner; return m_owner;
} }
...@@ -84,46 +102,97 @@ namespace fggl { ...@@ -84,46 +102,97 @@ namespace fggl {
App &m_owner; App &m_owner;
}; };
/*! \class App app.hpp fggl/app.hpp
*
* Main entrypoint to the game framework.
*/
class App { class App {
public: public:
explicit App(modules::Manager *serivces, const Identifer &name); /**
App(modules::Manager *services, const Identifer &name, const Identifer &folderName); * Create an instance of an application, with a set of modules and name.
*
* The name is used to derrive verious settings, such as the location of same games and user
* configuration and content.
*/
explicit App(modules::Manager *manager, const Identifer &name);
/**
* Create an instance of an application, with a set of modules and name.
*
* This version of the constructor allows explicitly setting the name used for user-data rather
* than derriving it from the application name. This is useful if you want to use a shortened
* version of your application name for configuration.
*/
App(modules::Manager *manager, const Identifer &name, const Identifer &folderName);
// class is non copy-able // class is non copy-able
App(const App &app) = delete; App(const App &app) = delete;
App(const App &&app) = delete; App(const App &&app) = delete;
App &operator=(const App &other) = delete; auto operator=(const App &other) -> App & = delete;
App &operator=(App &&other) = delete; auto operator=(App &&other) -> App & = delete;
/**
* Set the currently active window.
*
* FIXME: this is a nasty hack to get round the setup order for windows, graphics APIs and
* screen refreshes. Should be fixed with observer pattern.
*/
inline void setWindow(display::Window *window) { inline void setWindow(display::Window *window) {
m_window = window; m_window = window;
} }
/** /**
* Perform main game loop functions. * Perform main game loop functions.
*
* You should pass in argc and argv used to invoke main to this method. At the moment it does
* not use these, but in the future they will be used to provide game-agnostic options.
*/ */
int run(int argc, const char **argv); auto run(int argc, const char **argv) -> int;
/**
* Register a new state with the application.
*
* States are intended to be a block of code which can be started, will execute a single frame
* of work, and can be invoked repeatedly. For example, a main menu, options menu,
* single-player game and multi-player game might be all be suitable to be states. When a state
* change is requested, the currently running state is stopped and garabage collected.
*
* This is similar to the concept of an 'activity' in Android.
*/
template<typename T> template<typename T>
T *addState(const Identifer &name) { auto addState(const Identifer &name) -> T * {
static_assert(std::is_base_of<AppState, T>::value, "States must be AppStates"); static_assert(std::is_base_of<AppState, T>::value, "States must be AppStates");
return m_states.put<T>(name, *this); return m_states.put<T>(name, *this);
} }
/**
* Request the game changes states.
*
* This will be executed at the next opporunity (most likley next iteration of the game loop).
* Identifer should be an identifier used when calling addState, and the state should previouslly
* been registered using addState.
*/
inline void change_state(const Identifer &name) { inline void change_state(const Identifer &name) {
m_expectedScene = name; m_expectedScene = name;
/*m_states.active().deactivate();
m_states.change(name);
m_states.active().activate();*/
} }
inline AppState &active_state() const { /**
* Return the currently active state (the state that is currently executing).
*/
inline auto active_state() const -> AppState & {
return m_states.active(); return m_states.active();
} }
/**
* Get a pointer to a service.
*
* This is the primary way in which states can get access to resources they require.
*
* returns nullptr if the service does not exist, or cannot be provided.
*/
template<typename T> template<typename T>
inline T *service() { inline auto service() -> T * {
try { try {
return m_subsystems->template get<T>(); return m_subsystems->template get<T>();
} catch (std::out_of_range &e) { } catch (std::out_of_range &e) {
...@@ -132,11 +201,11 @@ namespace fggl { ...@@ -132,11 +201,11 @@ namespace fggl {
} }
} }
inline modules::Services& services() { inline auto services() -> modules::Services& {
return m_subsystems->services(); return m_subsystems->services();
} }
inline bool running() const { inline auto running() const -> bool {
return m_running; return m_running;
} }
......
This diff is collapsed.
...@@ -41,6 +41,24 @@ namespace fggl::assets { ...@@ -41,6 +41,24 @@ namespace fggl::assets {
struct AssetBoxT : public AssetBox { struct AssetBoxT : public AssetBox {
T* asset = nullptr; T* asset = nullptr;
explicit inline AssetBoxT(T* aasset) : asset(aasset) {}
// no copy: we own our resource!
AssetBoxT(const AssetBoxT&) = delete;
AssetBoxT& operator=(const AssetBoxT&) = delete;
// move OK - we can steal the asset
AssetBoxT(AssetBoxT&& other) : asset(other.asset) {
other.asset = nullptr;
}
inline ~AssetBoxT() override {
if ( asset != nullptr ) {
delete asset;
asset = nullptr;
}
}
inline void release() override { inline void release() override {
asset = nullptr; asset = nullptr;
} }
...@@ -48,7 +66,7 @@ namespace fggl::assets { ...@@ -48,7 +66,7 @@ namespace fggl::assets {
class AssetManager { class AssetManager {
public: public:
constexpr const static modules::ModuleService service = modules::make_service("fggl::assets::Manager"); constexpr const static modules::ServiceName service = modules::make_service("fggl::assets::Manager");
AssetManager() = default; AssetManager() = default;
virtual ~AssetManager() = default; virtual ~AssetManager() = default;
...@@ -60,7 +78,7 @@ namespace fggl::assets { ...@@ -60,7 +78,7 @@ namespace fggl::assets {
AssetManager &operator=(AssetManager &&) = delete; AssetManager &operator=(AssetManager &&) = delete;
template<typename T> template<typename T>
T* get(const AssetGUID &guid) const { T* get(const AssetID &guid) const {
try { try {
const auto &assetRecord = m_registry.at(guid); const auto &assetRecord = m_registry.at(guid);
std::shared_ptr<AssetBoxT<T>> casted = std::dynamic_pointer_cast<AssetBoxT<T>>(assetRecord); std::shared_ptr<AssetBoxT<T>> casted = std::dynamic_pointer_cast<AssetBoxT<T>>(assetRecord);
...@@ -74,29 +92,40 @@ namespace fggl::assets { ...@@ -74,29 +92,40 @@ namespace fggl::assets {
} }
} }
/**
* Pass ownership of the asset to the asset system.
*
* Once this method is called, the asset system owns the asset, and will free
* it when it is no longer required. The asset system assumes it is fully aware
* of all usages of the assets it manages via its graph.
*
* @tparam T the asset type to be managed
* @param guid the asset name
* @param assetRef the asset itself
* @return the owned asset pointer
*/
template<typename T> template<typename T>
T* set(const AssetGUID &guid, T* assetRef) { T* set(const AssetID &guid, T* assetRef) {
auto ptr = std::make_shared<AssetBoxT<T>>(); auto ptr = std::make_shared<AssetBoxT<T>>(assetRef);
ptr->asset = assetRef;
m_registry[guid] = ptr; m_registry[guid] = ptr;
return (*ptr).asset; return (*ptr).asset;
} }
inline void require(const AssetGUID &guid) { inline void require(const AssetID &/*guid*/) {
//m_registry.at(guid).refCount++; //m_registry.at(guid).refCount++;
} }
inline bool has(const AssetGUID &guid) { inline bool has(const AssetID &guid) {
return m_registry.contains(guid); return m_registry.contains(guid);
} }
inline void release(const AssetGUID &guid) { inline void release(const AssetGUID &/*guid*/) {
//m_registry.at(guid).refCount--; //m_registry.at(guid).refCount--;
} }
inline std::vector<AssetGUID> known() { inline std::vector<AssetID> known() {
std::vector<AssetGUID> assetList; std::vector<AssetID> assetList;
for ( auto& itr : m_registry ) { for ( auto& itr : m_registry ) {
assetList.push_back(itr.first); assetList.push_back(itr.first);
} }
...@@ -104,7 +133,7 @@ namespace fggl::assets { ...@@ -104,7 +133,7 @@ namespace fggl::assets {
} }
private: private:
std::map<AssetGUID, std::shared_ptr<AssetBox>> m_registry; std::map<AssetID, std::shared_ptr<AssetBox>> m_registry;
}; };
} // namespace fggl::assets } // namespace fggl::assets
......
This diff is collapsed.