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 1401 additions and 150 deletions
......@@ -4,21 +4,28 @@
*/
#version 330 core
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
layout (location = 2) in vec3 VertexColour;
layout (location = 3) in vec2 VertexTex;
uniform mat4 MVPMatrix;
uniform mat4 MVMatrix;
uniform mat3 NormalMatrix;
out vec4 Position;
out vec3 Normal;
out vec4 Colour;
out Vertex {
vec3 Position;
vec3 Normal;
vec3 Colour;
vec2 TexPos;
};
void main() {
Colour = vec4(0.5, 0.5, 0.5, 1.0f);
Normal = NormalMatrix * VertexNormal;
Position = MVMatrix * vec4(VertexPosition, 1);
Colour = VertexColour;
Normal = mat3(transpose(inverse(MVMatrix))) * VertexNormal;
TexPos = VertexTex;
Position = vec3(MVMatrix * vec4(VertexPosition, 1));
gl_Position = MVPMatrix * vec4(VertexPosition, 1);
}
\ No newline at end of file
print("File has been loaded!")
-- when the scene loads, switch to topdown to show state integration
--switch_scene(state, "topdown");
\ No newline at end of file
---
prefabs:
- name: "environment"
- name: "rb_environment"
components:
gfx::material:
ambient: [0.0215, 0.1754, 0.0215]
diffuse: [1, 1, 1]
specular: [0.0633, 0.727811, 0.633]
shininess: 16
- name: "wallX"
parent: "environment"
- name: "rb_wallX"
parent: "rb_environment"
components:
Transform:
StaticMesh:
pipeline: redbook/debug
shape_id: "mesh_rb_wall_x"
shape:
type: box
scale: [1.0, 5.0, 41]
......@@ -22,12 +23,13 @@ prefabs:
type: box
extents: [0.5, 2.5, 20.5]
# Wall Z shorter to avoid z-fighting
- name: "wallZ"
parent: "environment"
- name: "rb_wallZ"
parent: "rb_environment"
components:
Transform:
StaticMesh:
pipeline: redbook/debug
shape_id: "mesh_rb_wall_z"
shape:
type: box
scale: [39, 5, 1]
......@@ -36,12 +38,13 @@ prefabs:
shape:
type: box
extents: [ 19.5, 2.5, 0.5 ]
- name: "floor"
parent: "environment"
- name: "rb_floor"
parent: "rb_environment"
components:
Transform:
StaticMesh:
pipeline: redbook/debug
shape_id: "mesh_rb_floor"
shape:
type: box # we don't (currently) support planes...
scale: [39, 0.5, 39]
......@@ -50,11 +53,12 @@ prefabs:
shape:
type: box # we don't (currently) support planes...
extents: [19.5, 0.25, 19.5]
- name: player
- name: rb_player
components:
Transform:
StaticMesh:
pipeline: redbook/debug
shape_id: "mesh_rb_player"
shape:
type: sphere
gfx::material:
......@@ -66,11 +70,14 @@ prefabs:
shape:
type: sphere
radius: 1
- name: collectable
- name: rb_collectable
tags:
- "collectable"
components:
Transform:
StaticMesh:
pipeline: redbook/debug
shape_id: "mesh_rb_collect"
shape:
type: box
gfx::material:
......@@ -81,4 +88,47 @@ prefabs:
phys::Body:
type: kinematic
shape:
type: box
\ No newline at end of file
type: box
- name: rb_light
components:
Transform:
gfx::phong::directional:
direction: [10, 5, 0]
scene:
- prefab: rb_wallX
components:
Transform:
origin: [20, 0, 0]
- prefab: rb_wallX
components:
Transform:
origin: [-20, 0, 0]
- prefab: rb_wallZ
components:
Transform:
origin: [0, 0, -20]
- prefab: rb_wallZ
components:
Transform:
origin: [0, 0, 20]
- prefab: rb_floor
components:
Transform:
origin: [0, -2.5, 0]
- prefab: rb_collectable
components:
Transform:
origin: [-5, -0.5, 12]
- prefab: rb_collectable
components:
Transform:
origin: [15, -0.5, 0.5]
- prefab: rb_collectable
components:
Transform:
origin: [6, -0.5, -15]
- prefab: rb_player
name: "player"
- prefab: rb_light
scripts:
- "rollball.lua"
\ No newline at end of file
---
floors:
ground:
visible: true
walls:
none:
visible: false
solid:
visible: true
\ No newline at end of file
......@@ -5,6 +5,7 @@ prefabs:
Transform:
StaticMesh:
pipeline: redbook/debug
shape_id: td_wall_x
shape:
type: box
scale: [1.0, 5.0, 41]
......@@ -24,6 +25,7 @@ prefabs:
Transform:
StaticMesh:
pipeline: redbook/debug
shape_id: td_wall_y
shape:
type: box
scale: [39, 5, 1]
......@@ -42,6 +44,7 @@ prefabs:
Transform:
StaticMesh:
pipeline: redbook/debug
shape_id: td_floor
shape:
type: box # we don't (currently) support planes...
scale: [39, 0.5, 39]
......@@ -60,6 +63,7 @@ prefabs:
Transform:
StaticMesh:
pipeline: redbook/lighting
shape_id: td_player
shape:
type: sphere
gfx::material:
......@@ -76,6 +80,7 @@ prefabs:
Transform:
StaticMesh:
pipeline: redbook/lighting
shape_id: td_collect
shape:
type: box
gfx::material:
......
......@@ -24,10 +24,15 @@
#include "GameScene.h"
#include "fggl/entity/loader/loader.hpp"
#include "fggl/mesh/components.hpp"
camera_type cam_mode = cam_free;
static void placeObject(fggl::entity::EntityManager& world, fggl::entity::EntityID floor, fggl::entity::EntityFactory* factory, fggl::util::GUID prototype, glm::vec3 targetPos) {
#ifndef NDEBUG
fggl::debug::trace("Creating object from prototype: {}", fggl::util::guid_to_string(prototype));
#endif
auto obj = factory->create(prototype, world);
auto& result = world.get<fggl::math::Transform>(obj);
......@@ -104,13 +109,13 @@ static fggl::entity::EntityID setupTerrain(fggl::entity::EntityManager& world) {
terrainData.setValue(x, z, (float)noise);
}
}
world.add<fggl::data::HeightMap>(terrain, terrainData);
// world.set<fggl::data::HeightMap>(terrain, &terrainData);
}
return terrain;
}
static fggl::entity::EntityID setupEnvironment(fggl::entity::EntityManager& world) {
static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& world) {
setupCamera(world);
return setupTerrain(world);
}
......@@ -120,15 +125,12 @@ static auto BUNKER_PROTOTYPE = "bunker"_fid;
static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) {
{
auto bunkerSpec = fggl::entity::EntitySpec{};
bunkerSpec.components[fggl::util::make_guid("transform")] = {};
fggl::entity::ComponentSpec procMesh{};
procMesh.set<std::string>("shader", "phong");
bunkerSpec.addComp(fggl::math::Transform::guid, {});
// mesh
int nSections = 2;
fggl::mesh::MultiMesh3D mesh;
fggl::data::Mesh mesh;
for (int j=-(nSections/2); j<=nSections/2; j++) {
const auto shapeOffset = glm::vec3( 0.0f, 0.5f, (float)j * 1.0f );
......@@ -140,15 +142,41 @@ static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) {
glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset,
glm::vec3( 0.0f, fggl::math::HALF_PI, 0.0f) );
fggl::data::make_cube( mesh, cubeMat );
fggl::data::make_slope( mesh, leftSlope );
fggl::data::make_slope( mesh, rightSlope );
fggl::data::make_cube( mesh.generate(), cubeMat);
fggl::data::make_slope( mesh.generate(), leftSlope );
fggl::data::make_slope( mesh.generate(), rightSlope );
}
mesh.removeDups();
//mesh.removeDups();
procMesh.set<std::vector<fggl::data::Vertex>>("vertexData", mesh.vertexList());
procMesh.set<std::vector<unsigned int>>("indexData", mesh.indexList());
bunkerSpec.components[fggl::util::make_guid("procedural_mesh")] = procMesh;
// generate mesh component data
// FIXME: find a better way to do this, avoid re-uploading the whole mesh.
fggl::entity::ComponentSpec procMesh{};
procMesh.set<std::string>("pipeline", "redbook/debug");
YAML::Node modelNode;
for (auto& submesh : mesh.meshes) {
YAML::Node vertexData;
for (auto& vertex : submesh.data) {
YAML::Node vertexNode;
vertexNode["position"] = vertex.position;
vertexNode["normal"] = vertex.normal;
vertexNode["colour"] = vertex.colour;
vertexNode["texPos"] = vertex.texPos;
vertexData.push_back(vertexNode);
}
YAML::Node indexData;
for (auto& index : submesh.indices) {
indexData.push_back(index);
}
YAML::Node meshNode;
meshNode["vertex"] = vertexData;
meshNode["index"] = indexData;
modelNode.push_back( meshNode );
}
procMesh.set("model", modelNode);
bunkerSpec.addComp(fggl::mesh::StaticMultiMesh3D::guid, procMesh);
factory->define(BUNKER_PROTOTYPE, bunkerSpec);
}
......@@ -159,7 +187,7 @@ void GameScene::setup() {
auto* entityFactory = m_owner.service<fggl::entity::EntityFactory>();
auto terrain = setupEnvironment(world());
auto terrain = setup_environment(world());
setupBunkerPrototype(entityFactory);
// create building prototype
......@@ -172,8 +200,8 @@ void GameScene::setup() {
}
}
void GameScene::update() {
Game::update();
void GameScene::update(float dt) {
Game::update(dt);
process_camera(world(), 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/>.
*/
//
// Created by webpigeon on 27/08/22.
//
#include "grid.hpp"
#include "fggl/assets/loader.hpp"
#include "fggl/entity/gridworld/zone.hpp"
#include "fggl/gui/model/parser.hpp"
#include "fggl/gui/renderer/renderer.hpp"
using namespace fggl::gfx::colours;
namespace demo {
using namespace fggl::entity::grid;
static void build_tileset(TileSet& tiles) {
tiles.m_floors.push_back(FloorTile{FloorTile::IMPOSSIBLE, BLACK});
tiles.m_floors.push_back(FloorTile{1, GREEN});
tiles.m_floors.push_back(FloorTile{1, YELLOW_GREEN});
fggl::entity::grid::WallTile noWall{};
tiles.m_walls.push_back(noWall);
fggl::entity::grid::WallTile solidWall{
.render = true,
.colour = DARK_SLATE_GRAY
};
tiles.m_walls.push_back(solidWall);
}
static void build_room(DemoGrid* area, fggl::math::vec2i center, fggl::math::vec2i size) {
for (int yOffset = -size.y; yOffset <= size.y; ++yOffset) {
auto yPos = yOffset + center.y;
area->setWallAt(center.x - size.x, yPos, false, 1);
area->setWallAt(center.x + size.x + 1, yPos, false, 1);
}
for (int xOffset = -size.x; xOffset <= size.x; ++xOffset) {
auto xPos = center.x + xOffset;
area->setWallAt(xPos, center.y - size.y, true, 1);
area->setWallAt(xPos, center.y + size.y + 1, true, 1);
for (int yOffset = -size.y; yOffset <= size.y; ++yOffset) {
auto yPos = yOffset + center.y;
area->setFloorAt(xPos, yPos, 1);
}
}
}
void build_doorway(DemoGrid* area, fggl::math::vec2i position, bool north, int size = 1) {
for ( auto offset = 0; offset < size; offset++) {
if ( north ) {
area->setWallAt(position.x + offset, position.y, north, 0);
} else {
area->setWallAt(position.x, position.y + offset, north, 0);
}
}
}
static fggl::entity::EntityID build_test_env(DemoGrid* area, LevelRules& rules) {
area->clear();
build_room(area, {5, 5}, {4,4});
build_room(area, {11, 5}, {1,1});
build_room(area, {17, 5}, {4,4});
build_doorway(area, {10, 5}, false, 1);
build_doorway(area, {13, 5}, false, 1);
// set goal
area->setFloorAt(17, 5, 2);
// level rules
rules.startingPower = 20;
rules.startingPos = {5, 5};
rules.startingDirection = 1;
// player
fggl::entity::EntityID player = fggl::entity::INVALID;
auto& manager = area->entities();
{
player = manager.create();
manager.add<CellPos>(player);
manager.add<RobotState>(player);
}
return player;
}
struct Action {
const char* name;
std::function<void(void)> callback;
};
GridScene::GridScene(fggl::App &app) : GameBase(app), m_tiles(), m_animator(15.0F), m_grid(nullptr), m_canvas() {
m_animator.add([this](){this->tickPlayer();});
auto btnGrid = std::make_unique<fggl::gui::GridBox>(0, 2);
std::array<Action, 4> actions{{
{"<", [this]() { this->rotate(true); }},
{">", [this]() { this->rotate(false); }},
{"^", [this]() { this->forward(); }},
{"Z", [this]() { } }
}};
fggl::math::vec2i pos{0, 0};
for (auto& action : actions) {
fggl::math::vec2i size{32, 32};
auto btn = std::make_unique<fggl::gui::Button>(pos, size);
btn->label(action.name);
btn->addCallback([=, this](){
this->m_program.m_instructions.push_back({action.name, action.callback});
});
btnGrid->add(std::move(btn));
}
// control buttons
{
fggl::math::vec2i size{64, 32};
auto btn = std::make_unique<fggl::gui::Button>(pos, size);
btn->label("go");
btn->addCallback([this](){
if ( !this->m_program.playing ) {
resetPuzzle();
this->m_program.m_currInstruction = 0;
this->m_program.playing = true;
}
});
btnGrid->add(std::move(btn));
}
{
fggl::math::vec2i size{64, 64};
auto btn = std::make_unique<fggl::gui::Button>(pos, size);
btn->label("Del");
btn->addCallback([this](){
if ( !this->m_program.playing ) {
if ( !m_program.m_instructions.empty() ) {
m_program.m_instructions.pop_back();
}
}
});
btnGrid->add(std::move(btn));
}
btnGrid->layout();
m_canvas.add(std::move(btnGrid));
// create a timeline panel
std::unique_ptr<robot::Timeline> timeline = std::make_unique<robot::Timeline>(m_program);
timeline->size({50,700}, {250, 250});
m_canvas.add(std::move(timeline));
m_canvas.layout();
}
void GridScene::activate() {
GameBase::activate();
fggl::debug::log(fggl::debug::Level::info, "GridScene::activate()");
m_animator.reset();
// fake loading the tileset
if ( m_tiles.m_floors.empty() ) {
build_tileset(m_tiles);
}
// create the grid world
m_grid = std::make_unique<DemoGrid>(m_tiles);
m_player = build_test_env(m_grid.get(), m_levelRules);
resetPuzzle();
}
void GridScene::deactivate() {
m_grid = nullptr;
}
constexpr float DRAW_SIZE = 64.0F;
constexpr float DRAW_HALF = DRAW_SIZE / 2.0F;
constexpr float WALL_HALF = 2.5F;
static void render_grid(fggl::gfx::Paint& paint, fggl::entity::grid::Area2D<255, 255>& grid, fggl::App& app) {
const fggl::math::vec2 wallOffsetNorth {0, -DRAW_HALF};
const fggl::math::vec2 wallOffsetWest {-DRAW_HALF, 0};
for (int i=0; i <= 31; ++i) {
for (int j=0; j <= 31; ++j) {
const fggl::math::vec2 drawPos{i * DRAW_SIZE, j*DRAW_SIZE};
// draw floor
{
auto &floor = grid.floorAt(i, j);
fggl::gfx::Path2D tileGfx = fggl::gfx::make_rect(drawPos, {DRAW_HALF, DRAW_HALF}, floor.colour);
paint.fill(tileGfx);
}
auto& wallNorth = grid.wallAt(i, j, true);
if ( wallNorth.render ) {
fggl::gfx::Path2D tileGfx = fggl::gfx::make_rect(drawPos + wallOffsetNorth, {DRAW_HALF, WALL_HALF}, wallNorth.colour);
paint.fill(tileGfx);
}
auto& wallWest = grid.wallAt(i, j, false);
if ( wallWest.render ) {
fggl::gfx::Path2D tileGfx = fggl::gfx::make_rect(drawPos + wallOffsetWest, {WALL_HALF, DRAW_HALF}, wallWest.colour);
paint.fill(tileGfx);
}
}
}
// UI test
auto widgetFactory = app.service<fggl::gui::model::WidgetFactory>();
auto widget = widgetFactory->buildEmpty();
widget->set("position", fggl::math::vec2{200.0F, 100.F});
widget->set("size", fggl::math::vec2{500.0F, 300.F});
widget->set("colour", fggl::gfx::colours::BLANCHED_ALMOND);
fggl::gui::model::attr_box_set(*widget, "padding", 5.0F);
auto handle = widgetFactory->buildEmpty();
handle->set("border::bottom",5.0F);
handle->set("position", fggl::math::vec2{0.0F, 0.0F});
//handle->set("size", fggl::math::vec2{INFINITY, 50.0F});
handle->set("text", "hello, world!");
handle->set("colour", fggl::gfx::colours::ORANGE);
fggl::gui::model::attr_box_set(*handle, "padding", 5.0F);
widget->addChild(*handle);
delete handle;
auto content = widgetFactory->buildEmpty();
content->set("position", fggl::math::vec2{0.0F, 50.0F});
//content->set("size", fggl::gui::model::UNDEFINED_SIZE);
content->set("colour", fggl::gfx::colours::BURLYWOOD);
widget->addChild(*content);
delete content;
fggl::gui::renderer::layout(*widget);
fggl::gui::renderer::visit(*widget, paint);
}
static void render_objects(fggl::gfx::Paint& paint, DemoGrid& grid) {
auto& manager = grid.entities();
auto entities = manager.find<CellPos>();
for (const auto& entity : entities ) {
//auto& sprite = manager.get<Sprite>(entity);
auto& cellPos = manager.get<CellPos>(entity);
// convert grid pos to world pos
fggl::math::vec2f drawPos = cellPos.pos;
drawPos *= DRAW_SIZE;
drawPos += cellPos.drawOffset;
fggl::gfx::ShapeOpts opts;
opts.angleOffset = (float)cellPos.direction * fggl::math::HALF_PI + cellPos.rotationOffset;
auto shape = fggl::gfx::make_shape(drawPos, DRAW_HALF, 3, NAVAJO_WHITE, opts);
paint.fill(shape);
}
}
static void update_canvas(fggl::input::Input& inputs, fggl::gui::Container& canvas) {
fggl::math::vec2f cursorPos {
inputs.mouse.axis(fggl::input::MouseAxis::X),
inputs.mouse.axis(fggl::input::MouseAxis::Y)
};
// in canvas space
fggl::math::vec2 projected {
fggl::math::rescale_ndc(cursorPos.x, 0, 1920.f),
fggl::math::rescale_ndc(cursorPos.y, 0, 1080.0f)
};
canvas.onMouseOver(projected);
// detect clicks
if (inputs.mouse.pressed(fggl::input::MouseButton::LEFT)) {
auto* widget = canvas.getChildAt(projected);
if (widget != nullptr) {
fggl::debug::info("Button clicked");
widget->activate();
}
}
}
void GridScene::update(float deltaTime) {
GameBase::update(deltaTime);
m_animator.update(deltaTime);
m_canvas.update(deltaTime);
update_canvas(input(), m_canvas);
}
void GridScene::tickPlayer() {
if ( !m_program.playing ){
return;
}
if ( m_program.m_currInstruction < m_program.m_instructions.size() ) {
auto& robotState = m_grid->entities().get<RobotState>(m_player);
if (robotState.power == 0) {
// a looser is you
resetPuzzle();
return;
}
robotState.power--;
m_program.step();
} else {
m_program.stop();
checkVictory();
}
}
void GridScene::resetPuzzle() {
// reset instruction panel
m_program.stop();
// reset robot state
auto& robotPos = m_grid->entities().get<CellPos>(m_player);
auto& robotState = m_grid->entities().get<RobotState>(m_player);
robotPos.pos = m_levelRules.startingPos;
robotPos.direction = m_levelRules.startingDirection;
robotState.power = m_levelRules.startingPower;
}
void GridScene::checkVictory() {
if ( !m_program.playing ) {
auto& botPos = m_grid->entities().get<CellPos>(m_player).pos;
auto gridCell = m_grid->floorAt(botPos.x, botPos.y);
if ( gridCell.colour == YELLOW_GREEN ) {
// a winner is you!
returnToMenu();
}
}
}
void GridScene::render(fggl::gfx::Graphics &gfx) {
fggl::gfx::Paint paint;
render_grid(paint, *m_grid, owner());
render_objects(paint, *m_grid);
m_canvas.render(paint);
gfx.draw2D(paint);
}
} // namespace demo
/*
* 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 "hexboard/scene.hpp"
namespace demo::hexboard {
constexpr float SCROLL_SPEED = 0.01F;
constexpr float HEX_SIZE = 64.0F;
constexpr std::array<fggl::grid::IntHex, 4> ISLAND_CENTERS {{
{2, 3},
{6, 7},
{9, 10},
{6, 3}
}};
Scene::Scene(fggl::App &app) : GameBase(app), m_board(nullptr), m_screen(1920.F, 1080.F) {
}
void Scene::activate() {
m_board = std::make_unique<fggl::grid::HexGrid>();
m_layout = std::make_unique<fggl::grid::Layout>( fggl::grid::Orientation::make_pointy(), HEX_SIZE );
//m_layout->m_origin = { 1920.0F * -0.5F, 1080.0F * -0.5F};
m_selections = std::make_unique<SelectionModel>();
const fggl::grid::TerrainType grass{
.data = std::make_shared<fggl::grid::MaterialData>()
};
grass.data->name = "grass";
grass.data->colour = {0.0F, 1.0F, 0.0};
for (auto islandPoint : ISLAND_CENTERS){
auto island = islandPoint.hexesInRange(2);
for (auto &hex : island) {
m_board->setTerrain(hex, grass);
}
}
m_camera = std::make_unique<Camera2D>();
}
void Scene::deactivate() {
m_board = nullptr;
m_selections = nullptr;
m_layout = nullptr;
}
void Scene::update(float delta) {
GameBase::update(delta);
// if the board is not set, abort
if ( m_board == nullptr ){
return;
}
// check if a button was pressed
auto& input = this->input();
{
const auto mouseNdc = fggl::input::mouse_axis(input.mouse);
const auto screenPos = ndc_to_screen(mouseNdc, m_screen);
// calculate what the user clicked on
auto worldPx = m_camera->unproject(screenPos);
m_selections->hover = fggl::grid::round2( m_layout->toGrid(worldPx) );
if (input.mouse.pressed(fggl::input::MouseButton::LEFT)) {
m_selections->selected = m_selections->hover;
m_camera->moveBy(mouseNdc * (m_screen * 0.5F) );
}
if ( input.mouse.down(fggl::input::MouseButton::RIGHT) ) {
if (input.mouse.pressed( fggl::input::MouseButton::RIGHT )) {
m_dragging = screenPos;
}
auto offset = screenPos - m_dragging.value();
m_camera->teleportBy(offset * SCROLL_SPEED);
} else if ( input.mouse.released(fggl::input::MouseButton::RIGHT) ) {
m_dragging = {};
}
}
m_camera->update(delta);
// flip y, because reasons
//auto offset = m_camera->getFocusLocation();
//m_layout->m_origin = -offset;
}
void Scene::drawGrid(fggl::gfx::Paint& paint) {
auto tiles = m_board->getAllTiles();
for ( const auto& tile : tiles ) {
auto terrain = m_board->getTerrain(tile);
if ( terrain.has_value() ) {
const auto& terrainData = terrain.value();
m_layout->paintHex(paint, tile, terrainData.colour, m_camera->getFocusLocation());
}
}
}
void Scene::drawSelections(fggl::gfx::Paint& paint) {
if ( m_selections == nullptr || m_dragging.has_value() ) {
return;
}
if ( m_selections->selected.has_value() ) {
m_layout->paintHex( paint, m_selections->selected.value(), fggl::gfx::colours::YELLOW, m_camera->getFocusLocation());
}
if ( m_selections->hover.has_value() ) {
m_layout->paintHex( paint, m_selections->hover.value(), fggl::gfx::colours::BLANCHED_ALMOND, m_camera->getFocusLocation());
}
}
void Scene::render(fggl::gfx::Graphics &gfx) {
// if the board is not set, abort
if ( m_board == nullptr ){
return;
}
// draw the grid
// FIXME don't hard-code the screen size
fggl::gfx::Paint paint;
drawGrid(paint);
drawSelections(paint);
gfx.draw2D(paint);
}
} // namespace demo::hexboard
\ 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 02/01/23.
//
#include "hexboard/camera.hpp"
#include "fggl/math/fmath.hpp"
#include <cmath>
namespace demo::hexboard {
void Camera2D::update(float delta) {
m_location = fggl::math::smooth_add( m_location, m_target, m_scale );
if ( m_trauma > 0 ) {
m_trauma = std::max( m_trauma - TRAUMA_DECAY, 0.0F );
}
}
}
\ No newline at end of file
......@@ -31,46 +31,47 @@
#include "fggl/entity/module.hpp"
#include "fggl/audio/openal/audio.hpp"
#include "fggl/gfx/atlas.hpp"
#include "fggl/display/glfw/window.hpp"
#include "fggl/gfx/ogl/compat.hpp"
#include "fggl/platform/paths.hpp"
#include "fggl/scenes/menu.hpp"
#include "fggl/modules/manager.hpp"
#include "fggl/data/assimp/module.hpp"
#include "fggl/assets/module.hpp"
#include "fggl/assets/packed/module.hpp"
#if __has_include("fggl/script/lua/module.hpp")
#include "fggl/script/lua/module.hpp"
#endif
#include "GameScene.h"
#include "rollball.hpp"
#include "topdown.hpp"
#include "grid.hpp"
#include "models/viewer.hpp"
#include "hexboard/scene.hpp"
static void setup_menu(fggl::App& app) {
auto *menu = app.addState<fggl::scenes::BasicMenu>("menu");
// add some menu items for the game states
menu->add("terrain", [&app]() {
auto* audio = app.service<fggl::audio::AudioService>();
audio->play("click.ogg", false);
app.change_state("game");
});
const std::array labels = {"terrain", "rollball", "Top Down", "Grid World", "Viewer", "gridworld"};
const std::array scenes = {"game", "rollball", "topdown", "gridworld", "viewer", "hexboard"};
menu->add("rollball", [&app]() {
auto* audio = app.service<fggl::audio::AudioService>();
audio->play("click.ogg", false);
app.change_state("rollball");
});
for (std::size_t i = 0; i < labels.size(); ++i) {
std::string sceneName = scenes.at(i);
menu->add("Top Down", [&app]() {
auto* audio = app.service<fggl::audio::AudioService>();
audio->play("click.ogg", false);
app.change_state("topdown");
});
menu->add(labels.at(i), [&app, sceneName]() {
auto* audio = app.service<fggl::audio::AudioService>();
audio->play("ui/click.ogg", false);
app.change_state(sceneName);
});
}
menu->add("quit", [&app]() {
auto* audio = app.service<fggl::audio::AudioService>();
audio->play("click.ogg", false);
//auto* audio = app.service<fggl::audio::AudioService>();
//audio->play("click.ogg", false);
app.running(false);
});
}
......@@ -86,7 +87,16 @@ int main(int argc, const char* argv[]) {
moduleManager.use<fggl::gfx::OpenGL4>();
moduleManager.use<fggl::display::GLFW>();
moduleManager.use<fggl::assets::AssetFolders>();
moduleManager.use<fggl::assets::PackedAssets>();
moduleManager.use<fggl::entity::ECS>();
#ifdef FGGL_HAS_LUA
moduleManager.use<fggl::script::Lua>();
#endif
// debug/testing use
moduleManager.use<fggl::data::AssimpLoader>();
#ifdef FGGL_MODULE_BULLET
moduleManager.use<fggl::phys::Bullet3>();
#else
......@@ -97,6 +107,16 @@ int main(int argc, const char* argv[]) {
// create the application
fggl::App app( &moduleManager, "fggl-demo" );
// force asset loading
{
auto* assetFinder = app.service<fggl::assets::CheckinAdapted>();
assetFinder->discover("core");
auto* assets = app.service<fggl::assets::AssetManager>();
auto* loader = app.service<fggl::assets::Loader>();
loader->load("ui/click.ogg", fggl::audio::ASSET_CLIP_SHORT, assets);
}
auto* windowing = app.service<fggl::display::WindowService>();
// make a window for our application
......@@ -105,18 +125,14 @@ int main(int argc, const char* argv[]) {
window->setFullscreen( true );
app.setWindow(window);
// load a bunch of modules to provide game functionality
//app.use<fggl::ecs3::ecsTypes>();
/*app.use<fggl::gfx::SceneUtils>();
#ifdef FGGL_MODULE_BULLET
app.use<FGGL_MODULE_BULLET>();
#endif*/
// our test states
setup_menu(app);
app.addState<GameScene>("game");
app.addState<demo::RollBall>("rollball");
app.addState<demo::TopDown>("topdown");
app.addState<demo::GridScene>("gridworld");
app.addState<demo::Viewer>("viewer");
app.addState<demo::hexboard::Scene>("hexboard");
return app.run(argc, argv);
}
/*
* 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 18/10/22.
//
#include "models/viewer.hpp"
#include "fggl/assets/module.hpp"
#include "fggl/data/assimp/module.hpp"
#include "fggl/mesh/mesh.hpp"
#include "fggl/mesh/components.hpp"
#include "fggl/input/camera_input.hpp"
#include "fggl/gfx/phong.hpp"
#include "fggl/gfx/camera.hpp"
#include "fggl/gfx/paint.hpp"
namespace demo {
static fggl::entity::EntityID build_model(fggl::entity::EntityManager& manager, fggl::assets::AssetManager *assets, fggl::assets::AssetID assetRef){
auto model = manager.create();
manager.add<fggl::math::Transform>(model);
auto& mesh = manager.add<fggl::mesh::StaticMultiMesh3D>(model);
auto* meshData = assets->get<fggl::mesh::MultiMesh3D>( assetRef );
if ( meshData == nullptr ) {
fggl::debug::warning("loading model did not work!");
} else {
mesh.mesh = *meshData;
mesh.pipeline = "redbook/debug";
}
manager.add<fggl::gfx::PhongMaterial>(model);
return model;
}
static void process_camera(fggl::entity::EntityManager& ecs, const fggl::input::Input& input) {
auto cameras = ecs.find<fggl::gfx::Camera>();
auto cam = cameras[0];
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 );
// scroll wheel
glm::vec3 motion(0.0f);
float delta = input.mouse.axis( fggl::input::MouseAxis::SCROLL_Y );
if ( (glm::length( dir ) < 25.0f && delta < 0.0f) || (glm::length( dir ) > 2.5f && delta > 0.0f) )
motion -= (forward * delta);
camTransform.origin( camTransform.origin() + motion );
fggl::input::process_arcball(ecs, input, cam);
}
static void setup_camera(fggl::entity::EntityManager& world) {
auto prototype = world.create();
// setup camera position/transform
auto& transform = world.add<fggl::math::Transform>(prototype);
transform.origin(glm::vec3(10.0f, 3.0f, 10.0f));
// setup camera components
world.add<fggl::gfx::Camera>(prototype);
// interactive camera
auto& cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype);
cameraKeys.forward = glfwGetKeyScancode(GLFW_KEY_W);
cameraKeys.backward = glfwGetKeyScancode(GLFW_KEY_S);
cameraKeys.left = glfwGetKeyScancode(GLFW_KEY_A);
cameraKeys.right = glfwGetKeyScancode(GLFW_KEY_D);
cameraKeys.rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q);
cameraKeys.rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E);
}
static void setup_lighting(fggl::entity::EntityManager& ecs) {
auto light = ecs.create();
ecs.add<fggl::math::Transform>(light);
auto& lightComp = ecs.add<fggl::gfx::DirectionalLight>(light);
lightComp.position = fggl::math::vec3( 10.0F, 5.0F, 0.0F );
lightComp.diffuse = fggl::gfx::colours::CORNSILK;
lightComp.ambient = fggl::gfx::colours::MIDNIGHT_BLUE;
lightComp.specular = fggl::gfx::colours::MIDNIGHT_BLUE;
}
Viewer::Viewer(fggl::App &app) : fggl::scenes::Game(app), m_model(fggl::entity::INVALID) {
}
void Viewer::activate() {
Game::activate();
auto* assetFinder = m_owner.service<fggl::assets::CheckinAdapted>();
assetFinder->discover("viewer");
// setup the assets we can select between
// TODO some form of introspection to automatically find declared/discovered assets of a given type
m_assets.clear();
m_assets.push_back( fggl::assets::make_asset_id_rt("viewer", "backpack/backpack.obj") );
m_assets.push_back( fggl::assets::make_asset_id_rt("viewer", "lowpoly_scifi/wallDoor_double.FBX") );
m_assets.push_back( fggl::assets::make_asset_id_rt("viewer", "lowpoly_scifi/wallDoor_double_end.FBX") );
m_assets.push_back( fggl::assets::make_asset_id_rt("viewer", "newell_teaset/teapot.obj") );
m_assets.push_back( fggl::assets::make_asset_id_rt("viewer", "humansanimatedpack/Paladin/Paladin.fbx") );
// create camera
setup_camera(world());
setup_lighting(world());
// setup model
m_model = fggl::entity::INVALID;
cycleAsset(0);
}
void Viewer::deactivate() {
Game::deactivate();
}
void Viewer::update(float dt) {
Game::update(dt);
process_camera(world(), input());
if ( input().keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F2)) ) {
m_debug = !m_debug;
}
if ( input().keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F3)) ) {
// trigger the asset cycle
m_lastAsset = (m_lastAsset + 1) % m_assets.size();
cycleAsset(m_lastAsset);
}
}
void Viewer::cycleAsset(uint64_t /*idx*/) {
auto *loader = owner().service<fggl::assets::Loader>();
auto *manager = owner().service<fggl::assets::AssetManager>();
auto nextAsset = m_assets[ m_lastAsset ];
loader->loadChain(nextAsset, manager);
if ( m_model != fggl::entity::INVALID) {
world().destroy(m_model);
}
m_model = build_model(world(), manager, nextAsset);
}
void Viewer::render(fggl::gfx::Graphics &gfx) {
Game::render(gfx);
gfx.drawScene(world(), m_debug);
}
}
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 04/09/22.
//
#include "robot/programmer.hpp"
namespace demo::robot {
Timeline::Timeline(Program& program) {
m_tracks.push_back(std::ref(program));
}
void Timeline::update(float deltaTime) {
auto currSize = size();
float trackHeight = m_tracks.size() * 32.0F;
std::size_t widestTrack = 0;
for ( auto& track : m_tracks ) {
widestTrack = std::max(widestTrack, track.get().size());
}
float instructionWidth = 32 * widestTrack;
if ( currSize.x < instructionWidth || currSize.y < trackHeight ) {
size( topLeft(), {instructionWidth, trackHeight} );
}
}
void Timeline::render(fggl::gfx::Paint &paint) {
fggl::gui::Panel::render(paint);
renderInstructions(paint);
}
void Timeline::renderInstructions(fggl::gfx::Paint& paint) {
const fggl::math::vec2f barExtents{16, 16};
for ( auto track=0U; track < m_tracks.size(); ++track) {
auto& trackRef = m_tracks[track].get();
for (auto i = 0U; i < trackRef.size(); ++i) {
auto barCenter = this->topLeft();
barCenter.x += (i * (barExtents.x * 2) ) + barExtents.x;
barCenter.y += (track * barExtents.y * 2) + barExtents.y;
// bar background
auto colour = fggl::gfx::colours::LIGHT_GRAY;
auto textColour = fggl::gfx::colours::DARK_SLATE_GRAY;
if (i % 2 == 0) {
colour = fggl::gfx::colours::WHITE;
}
if (i == trackRef.m_currInstruction && trackRef.playing) {
colour = fggl::gfx::colours::MIDNIGHT_BLUE;
textColour = fggl::gfx::colours::LIGHT_GRAY;
}
auto rect = fggl::gfx::make_rect(barCenter, barExtents, colour);
paint.fill(rect);
// bar instruction
auto& instruction = trackRef.m_instructions[i];
paint.text(instruction.name, barCenter, textColour);
}
}
}
}
......@@ -31,11 +31,11 @@
#include <array>
static const fggl::util::GUID playerPrefab = "player"_fid;
static const fggl::util::GUID collectablePrefab = "collectable"_fid;
static const fggl::util::GUID WallNPrefab = "wallX"_fid;
static const fggl::util::GUID WallEPrefab = "wallZ"_fid;
static const fggl::util::GUID floorPrefab = "floor"_fid;
static const fggl::util::GUID playerPrefab = "rb_player"_fid;
static const fggl::util::GUID collectablePrefab = "rb_collectable"_fid;
static const fggl::util::GUID WallNPrefab = "rb_wallX"_fid;
static const fggl::util::GUID WallEPrefab = "rb_wallZ"_fid;
static const fggl::util::GUID floorPrefab = "rb_floor"_fid;
static void setup_camera(fggl::entity::EntityManager& world) {
auto prototype = world.create();
......@@ -48,58 +48,13 @@ static void setup_camera(fggl::entity::EntityManager& world) {
world.add<fggl::gfx::Camera>(prototype);
}
static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& world, fggl::entity::EntityFactory* factory, const fggl::math::vec2& size, demo::RollState& state) {
factory->create(WallNPrefab, world, [size](auto& manager, const auto& entity) {
auto& transform = manager.template get<fggl::math::Transform>(entity);
transform.origin({size.x/2, 0.0F, 0.0F});
});
factory->create(WallNPrefab, world, [size](auto& manager, const auto& entity) {
auto& transform = manager.template get<fggl::math::Transform>(entity);
transform.origin({-size.x/2, 0.0F, 0.0F});
});
factory->create(WallEPrefab, world, [size](auto& manager, const auto& entity) {
auto& transform = manager.template get<fggl::math::Transform>(entity);
transform.origin({0.0F, 0.0F, -size.y/2});
});
factory->create(WallEPrefab, world, [size](auto& manager, const auto& entity) {
auto& transform = manager.template get<fggl::math::Transform>(entity);
transform.origin({0.0F, 0.0F, size.y/2});
});
factory->create(floorPrefab, world, [](auto& manager, const auto& entity) {
auto& transform = manager.template get<fggl::math::Transform>(entity);
transform.origin({0.0F, -2.5F, 0.0F});
});
// the player requires no special setup
state.player = factory->create(playerPrefab, world);
{
// collectables
std::array<fggl::math::vec3, 3> collectPos {{
{-5.0f, -0.5f, 12.0f},
{15.0f, -0.5f, 0.5f},
{6.0f, -0.5f, -15.0f}
}};
// build the collectables
int collectCount = 0;
for (auto& pos : collectPos) {
auto collectable = factory->create(collectablePrefab, world, [pos](auto& manager, const auto& entity) {
auto& transform = manager.template get<fggl::math::Transform>(entity);
transform.origin(pos);
});
state.collectables[collectCount++] = collectable;
}
}
static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& world, fggl::entity::EntityFactory* /*factory*/, const fggl::math::vec2& /*size*/, demo::RollState& state) {
// ensure the state is clean
state.closestPickup = fggl::entity::INVALID;
state.mode = demo::DebugMode::NORMAL;
state.time = 0.0F;
state.player = world.findByName("player");
state.collectables = world.findByTag("collectable");
return state.player;
}
......@@ -125,8 +80,16 @@ namespace demo {
auto* entFactory = m_owner.service<fggl::entity::EntityFactory>();
m_phys = physService->create(&world(), entFactory);
auto* scriptProvider = m_owner.service<fggl::script::ScriptProvider>();
if ( scriptProvider != nullptr ) {
m_scripts = scriptProvider->create();
m_scripts->setGlobal("state", this);
}
// asset loader
auto* assetLoader = m_owner.service<fggl::assets::Loader>();
assetLoader->load("rollball.yml", fggl::entity::PROTOTYPE_ASSET);
assetLoader->load("rollball.yml", fggl::entity::ENTITY_PROTOTYPE, entFactory);
assetLoader->loadChain( fggl::assets::make_asset_id_rt("core", "rollerball/rollball.yml"), this);
// collectable callbacks
/*auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable);
......@@ -145,9 +108,20 @@ namespace demo {
// create a 20x20 grid
setup_environment(world(), m_owner.service<fggl::entity::EntityFactory>(), WORLD_SIZE, state);
// activate scripts
if ( m_scripts != nullptr ) {
m_scripts->onActivate();
m_scripts->load("rollball.lua");
}
}
void RollBall::deactivate() {
// deactivate scripts
if ( m_scripts != nullptr ) {
m_scripts->onActivate();
}
// we need to clean up physics
if ( m_phys != nullptr ) {
delete m_phys;
......@@ -182,12 +156,10 @@ namespace demo {
return force;
}
void RollBall::update() {
Game::update();
void RollBall::update(float deltaTime) {
Game::update(deltaTime);
m_phys->step();
const float deltaTime = 1 / 60.0F;
auto& input = this->input();
if ( state.player != fggl::entity::INVALID ) {
......@@ -237,6 +209,11 @@ namespace demo {
spinCubes(world, deltaTime);
state.time += deltaTime;
}
// tick scripts
if ( m_scripts != nullptr ) {
m_scripts->onUpdate();
}
}
void RollBall::closestPickup(fggl::entity::EntityManager &world) {
......
......@@ -20,6 +20,8 @@
#include "fggl/data/storage.hpp"
#include "fggl/gfx/camera.hpp"
#include "fggl/gfx/phong.hpp"
#include "fggl/input/camera_input.hpp"
#include "fggl/entity/loader/loader.hpp"
......@@ -50,6 +52,17 @@ namespace demo {
cameraKeys.rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E);
}
static void setup_lighting(fggl::entity::EntityManager& ecs) {
auto light = ecs.create();
ecs.add<fggl::math::Transform>(light);
auto& lightComp = ecs.add<fggl::gfx::DirectionalLight>(light);
lightComp.position = fggl::math::vec3( 10.0F, 5.0F, 0.0F );
lightComp.diffuse = fggl::gfx::colours::CORNSILK;
lightComp.ambient = fggl::gfx::colours::MIDNIGHT_BLUE;
lightComp.specular = fggl::gfx::colours::MIDNIGHT_BLUE;
}
static void place_cover_boxes(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) {
std::array<fggl::math::vec3,8> boxPos = {{
{-10.0F, 0.0F, -10.0F},
......@@ -91,6 +104,7 @@ namespace demo {
}
}
setup_lighting(world);
place_cover_boxes(factory, world);
}
......@@ -118,7 +132,7 @@ void TopDown::activate() {
fggl::debug::log(fggl::debug::Level::info, "TopDown::activate()");
auto* assetLoader = m_owner.service<fggl::assets::Loader>();
assetLoader->load("topdown.yml", fggl::entity::PROTOTYPE_ASSET);
assetLoader->load("topdown.yml", fggl::entity::ENTITY_PROTOTYPE);
auto* factory = m_owner.service<fggl::entity::EntityFactory>();
//fggl::ecs3::load_prototype_file(world(), *storage, "topdown.yml");
......@@ -127,8 +141,8 @@ void TopDown::activate() {
populate_sample_level(factory, world());
}
void TopDown::update() {
Game::update();
void TopDown::update(float dt) {
Game::update(dt);
process_camera(world(), input());
if ( input().mouse.pressed(fggl::input::MouseButton::LEFT) ) {
......@@ -137,6 +151,10 @@ void TopDown::update() {
}
void TopDown::pick_object() {
if (!hasPhys()) {
return;
}
auto cameras = world().find<fggl::gfx::Camera>();
if ( cameras.empty() ) {
return;
......@@ -152,7 +170,7 @@ void TopDown::pick_object() {
auto ray = fggl::gfx::get_camera_ray(world(), cam, position);
auto hit = phys().raycast(ray);
if ( hit != fggl::entity::INVALID) {
//fggl::debug::log("hit: {}", hit);
fggl::debug::info("hit: {}", (int)hit);
} else {
fggl::debug::info("no hit");
}
......
......@@ -57,7 +57,7 @@ enum camera_type { cam_free, cam_arcball };
setup();
}
void update() override;
void update(float dt) override;
void render(fggl::gfx::Graphics& gfx) override;
private:
......
//----------------------------------------------------------------------------------------
//
// siv::PerlinNoise
// Perlin noise library for modern C++
//
// Copyright (C) 2013-2021 Ryo Suzuki <reputeless@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//----------------------------------------------------------------------------------------
/*
* 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/>.
*/
# pragma once
# include <cstdint>
......
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 27/08/22.
//
#ifndef FGGL_DEMO_INCLUDE_GRID_HPP
#define FGGL_DEMO_INCLUDE_GRID_HPP
#include <memory>
#include "fggl/scenes/game.hpp"
#include "fggl/entity/gridworld/zone.hpp"
#include "fggl/animation/animator.hpp"
#include "fggl/gui/gui.hpp"
#include "robot/programmer.hpp"
namespace demo {
constexpr int GRID_SIZE = 255;
using DemoGrid = fggl::entity::grid::Area2D<GRID_SIZE, GRID_SIZE>;
struct LevelRules {
fggl::math::vec2i startingPos;
uint32_t startingDirection;
uint32_t startingPower;
};
struct CellPos {
fggl::math::vec2i pos;
uint8_t direction;
fggl::math::vec2f drawOffset{0.0F, 0.0F};
float rotationOffset{0.0F};
};
struct RobotState {
uint32_t power = 64;
};
class GridScene : public fggl::scenes::GameBase {
public:
explicit GridScene(fggl::App& app);
void activate() override;
void deactivate() override;
void update(float dt) override;
void render(fggl::gfx::Graphics& gfx) override;
private:
// level
LevelRules m_levelRules;
fggl::entity::grid::TileSet m_tiles;
std::unique_ptr<DemoGrid> m_grid;
// control
fggl::entity::EntityID m_player = fggl::entity::INVALID;
robot::Program m_program;
// UI
fggl::gui::Container m_canvas;
fggl::animation::FrameAnimator m_animator;
void resetPuzzle();
void tickPlayer();
void checkVictory();
inline void forward() {
auto& cell = m_grid->entities().get<CellPos>(m_player);
fggl::math::vec2i moveDir{0,0};
if ( cell.direction == 0) {
moveDir.y = 1;
} else if (cell.direction == 1) {
moveDir.x = 1;
} else if (cell.direction == 2) {
moveDir.y = -1;
} else if (cell.direction == 3) {
moveDir.x = -1;
}
if ( m_grid->canMove(cell.pos, moveDir) ) {
cell.pos += moveDir;
}
}
inline void rotate(bool clockwise) {
auto& cell = m_grid->entities().get<CellPos>(m_player);
int direction = clockwise ? +1 : -1;
cell.direction = (cell.direction + 4 + direction) % 4;
}
};
}
#endif //FGGL_DEMO_INCLUDE_GRID_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 10/12/22.
//
#ifndef FGGL_DEMO_INCLUDE_HEXBOARD_BOARD_HPP
#define FGGL_DEMO_INCLUDE_HEXBOARD_BOARD_HPP
#include <cstdint>
namespace demo::hexboard {
constexpr uint64_t NO_SELECTION{0};
struct SelectionModel {
uint64_t m_hover = NO_SELECTION;
};
} // namespace demo::hexboard
#endif //FGGL_DEMO_INCLUDE_HEXBOARD_BOARD_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 02/01/23.
//
#ifndef FGGL_DEMO_INCLUDE_HEXBOARD_CAMERA_HPP
#define FGGL_DEMO_INCLUDE_HEXBOARD_CAMERA_HPP
#include "fggl/math/types.hpp"
namespace demo::hexboard {
constexpr float TRAUMA_DECAY = 0.01F;
constexpr float TRAUMA_LARGE = 0.5F;
constexpr float TRAUMA_SMALL = 0.1F;
constexpr float SPEED_SLOW = 0.01F;
constexpr float SPEED_MEDIUM = 0.1F;
constexpr float SPEED_FAST = 0.5F;
constexpr fggl::math::vec2 ndc_to_screen(fggl::math::vec2 ndcPos, fggl::math::vec2 screenSize) {
return fggl::math::vec2(
fggl::math::rescale_ndc(ndcPos.x, 0, screenSize.x),
fggl::math::rescale_ndc(ndcPos.y, 0, screenSize.y)
);
}
/**
* A 2D 'juiced' camera for grid worlds.
*
* This camera is based on HexBoard, and the GDC talk around this concept. Many of the ideas implemented here
* come from that source.
*
* @see https://www.youtube.com/watch?v=tu-Qe66AvtY
*/
class Camera2D {
public:
using Point = fggl::math::vec2;
/**
* Apply active camera effects.
*
* This method must be called once per frame of the camera's visual effects are to be animated.
*
* @param delta the amount of time that has passed
*/
void update(float delta);
inline fggl::math::vec2 unproject(fggl::math::vec2 screenPos) const {
auto location = screenPos;
location.x += 1920/2.0F;
location.y += 1080/2.0F;
return location + m_location;
// return screenPos + m_location;
}
inline fggl::math::vec2 project(fggl::math::vec2 worldPos) const {
return worldPos - m_location;
}
/**
* Move the camera to a new location.
*
* This will apply any movement effects the camera has applied to it. As a result movement to the location
* will not be instantaneous.
*
* @param newTarget the new target location
*/
inline void moveTo(Point newTarget) {
m_target = newTarget;
}
/**
* Move the camera by a defined amount.
*
* This will apply any movement effects the camera has applied to it. As a result movement to the location
* will not be instantaneous.
*
* @param delta the amount to add to the target, negative values will subject from the target location
*/
inline void moveBy(Point delta) {
m_target += delta;
}
/**
* Instantaneously move the camera to a new location.
*
* @param the new camera location
*/
inline void teleportTo(Point newTarget) {
m_location = newTarget;
m_target = newTarget;
}
/**
* Instantaneously adjust the camera location.
*
* @param delta the amount to adjust the camera location by
*/
inline void teleportBy(Point delta) {
m_location += delta;
m_target = m_location;
}
/**
* Get the current view offset of this camera.
*
* @return the current location this camera is pointing at
*/
inline Point getFocusLocation() const {
auto offset = m_location;
offset.x += 1920/2.0F;
offset.y += 1080/2.0F;
return -offset;
}
private:
Point m_location;
Point m_target;
float m_trauma;
float m_scale = SPEED_MEDIUM;
};
}
#endif //FGGL_DEMO_INCLUDE_HEXBOARD_CAMERA_HPP
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 10/12/22.
//
#ifndef FGGL_DEMO_INCLUDE_HEXBOARD_SCENE_H
#define FGGL_DEMO_INCLUDE_HEXBOARD_SCENE_H
#include <memory>
#include <optional>
#include "fggl/scenes/game.hpp"
#include "fggl/grid/hexgrid.hpp"
#include "fggl/grid/layout.hpp"
#include "camera.hpp"
namespace demo::hexboard {
struct SelectionModel {
std::optional<fggl::grid::IntHex> selected;
std::optional<fggl::grid::IntHex> hover;
};
class Scene : public fggl::scenes::GameBase {
public:
explicit Scene(fggl::App& app);
void activate() override;
void deactivate() override;
void update(float delta) override;
void render(fggl::gfx::Graphics& gfx) override;
private:
std::unique_ptr<fggl::grid::HexGrid> m_board;
std::unique_ptr<fggl::grid::Layout> m_layout;
std::unique_ptr<SelectionModel> m_selections;
std::unique_ptr<Camera2D> m_camera;
fggl::math::vec2 m_screen;
std::optional<fggl::math::vec2> m_dragging;
void drawGrid(fggl::gfx::Paint&);
void drawSelections(fggl::gfx::Paint&);
};
} // namespace demo::hexboard
#endif //FGGL_DEMO_INCLUDE_HEXBOARD_SCENE_H