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 1385 additions and 379 deletions
---
- define: label
attrs:
value: """
- define: button
children:
- template: label
- define: textinput
children:
- template: label
- define: checkbox
attrs:
state: False
- define: radio
attrs:
state: False
- define: frame
......@@ -12,7 +12,7 @@ uniform mat4 model;
void main()
{
gl_Position = view * model * vec4(aPos, 1.0);
gl_Position = view * model * vec4(aPos, 1.0);
mat3 normalMatrix = mat3(transpose(inverse(view * model)));
vs_out.normal = normalize(vec3(vec4(normalMatrix * aNormal, 0.0)));
}
---
# This currently isn't functional, it's an experiment in how this
# might be defined in a user-friendly(ish) way.
prefabs:
wallX:
transform:
staticMesh:
pipeline: phong
shape: # TODO
rigidBody:
type: static
shape:
type: box
extents: [0.5, 2.5, 20.5]
wallZ:
transform:
staticMesh:
pipeline: phong
rigidBody:
type: static
shape:
type: box
extents: [39.0, 2.5, 0.5]
floor:
transform:
staticMesh:
pipeline: phong
shape: # TODO
rigidBody:
type: static
shape: box
extents: [20, 0.5, 20.0]
player:
transform:
rigidBody:
mass: 1
shape:
type: sphere
extents: [0.5, 0.5, 0.5]
dynamics:
staticMesh:
pipeline: phong
shape: # TODO
collectable:
transform:
staticMesh:
shader: phong
shape: # TODO
callbacks:
scene:
entities:
- prefab: wallX
transform:
origin: [20, 0, 0]
- prefab: wallX
transform:
origin: [-20, 0, 0]
- prefab: wallZ
transform: [0, 0, -20]
- prefab: wallZ
transform: [0, 0, 20]
- prefab: floor
transform: [0, -2.5, 0]
- prefab: player
\ No newline at end of file
/**
* OpenGL RedBook Shader.
* Examples 7.8, 7.9 and 7.10.
*
* Reflections are happening in camera space
*/
#version 330 core
in Vertex {
vec3 Position;
vec3 Normal;
vec3 Colour;
vec2 TexPos;
};
out vec4 FragColour;
const float constant = 1.0;
const float linear = 0.022;
const float quadratic = 0.0019;
float specPower = 0.5;
const float shininess = 8;
uniform sampler2D diffuseTexture;
uniform sampler2D specularTexture;
uniform mat4 MVMatrix;
uniform vec3 viewerPos_ws;
struct DirectionalLight {
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
struct Material {
vec3 emission;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform DirectionalLight light;
uniform Material material;
const int hasPos = 0;
vec4 calcDirLight(DirectionalLight light, vec3 Normal, vec3 viewDir, vec4 specPx, vec4 diffPx) {
vec3 lightDir = normalize( ( vec4(light.direction, 1) * MVMatrix).xyz - Position.xyz );
vec4 ambient = 0.1 * vec4( light.ambient, 1);
vec3 reflectDir = reflect( -lightDir, Normal );
float spec = pow( max( dot(viewDir, reflectDir), 0.0), shininess);
vec4 specular = 1.0 * vec4( light.specular, 1) * specPower * (spec * specPx);
float diff = max( dot(Normal, lightDir), 0.0 );
vec4 diffuse = vec4(light.diffuse,1) * (diff * diffPx);
if ( hasPos == 1 ) {
vec3 lightPos = ( vec4(light.direction, 1) * MVMatrix).xyz;
float distance = length( lightPos - Position.xyz );
float att = 1.0 / (constant +
(linear * distance) +
(quadratic * (distance * distance)));
ambient *= att;
diffuse *= att;
specular *= att;
}
return (ambient + diffuse + specular);
}
void main() {
vec3 viewDir = normalize(-Position);
vec4 diffPx = vec4(material.diffuse, 1);
vec4 specPx = vec4(material.specular, 1);
if ( hasPos != 1) {
diffPx *= texture(diffuseTexture, TexPos);
specPx *= texture(specularTexture, TexPos);
}
FragColour = vec4(Colour, 1);
FragColour *= calcDirLight(light, Normal, viewDir, specPx, diffPx);
}
\ No newline at end of file
/**
* OpenGL RedBook Shader.
* Example 7.8
*/
#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 Vertex {
vec3 Position;
vec3 Normal;
vec3 Colour;
vec2 TexPos;
};
void main() {
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
......@@ -86,8 +86,6 @@ void main() {
}
float diffuse = max(0.0, dot(Normal, lightDirection));
if (diffuse == 0.0)
specular = 0.0;
else
......
......@@ -8,9 +8,9 @@ uniform mat4 MVPMatrix;
uniform mat4 MVMatrix;
uniform mat3 NormalMatrix;
in vec4 VertexPosition;
in vec3 VertexNormal;
in vec4 VertexColour;
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
layout (location = 2) in vec3 VertexColour;
out vec4 Position;
out vec3 Normal;
......@@ -20,7 +20,8 @@ out int matIndex;
void main() {
Colour = vec4(1.0, 1.0, 1.0, 1.0f);
Normal = NormalMatrix * VertexNormal;
Position = MVMatrix * VertexPosition;
gl_Position = MVPMatrix * VertexPosition;
Position = MVMatrix * vec4(VertexPosition, 1.0);
matIndex = 0;
gl_Position = MVPMatrix * vec4(VertexPosition, 1.0);
}
\ 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: "wallX"
- 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: "rb_wallX"
parent: "rb_environment"
components:
Transform:
StaticMesh:
pipeline: redbook/lighting
pipeline: redbook/debug
shape_id: "mesh_rb_wall_x"
shape:
type: box
scale: [1.0, 5.0, 41]
......@@ -14,11 +23,13 @@ prefabs:
type: box
extents: [0.5, 2.5, 20.5]
# Wall Z shorter to avoid z-fighting
- name: "wallZ"
- name: "rb_wallZ"
parent: "rb_environment"
components:
Transform:
StaticMesh:
pipeline: redbook/lighting
pipeline: redbook/debug
shape_id: "mesh_rb_wall_z"
shape:
type: box
scale: [39, 5, 1]
......@@ -27,11 +38,13 @@ prefabs:
shape:
type: box
extents: [ 19.5, 2.5, 0.5 ]
- name: "floor"
- name: "rb_floor"
parent: "rb_environment"
components:
Transform:
StaticMesh:
pipeline: redbook/lighting
pipeline: redbook/debug
shape_id: "mesh_rb_floor"
shape:
type: box # we don't (currently) support planes...
scale: [39, 0.5, 39]
......@@ -40,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/lighting
pipeline: redbook/debug
shape_id: "mesh_rb_player"
shape:
type: sphere
gfx::material:
......@@ -56,11 +70,14 @@ prefabs:
shape:
type: sphere
radius: 1
- name: collectable
- name: rb_collectable
tags:
- "collectable"
components:
Transform:
StaticMesh:
pipeline: redbook/lighting
pipeline: redbook/debug
shape_id: "mesh_rb_collect"
shape:
type: box
gfx::material:
......@@ -72,5 +89,46 @@ prefabs:
type: kinematic
shape:
type: box
phys::Callbacks:
phys::Cache:
\ No newline at end of file
- 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
......@@ -4,47 +4,66 @@ prefabs:
components:
Transform:
StaticMesh:
pipeline: phong
pipeline: redbook/debug
shape_id: td_wall_x
shape:
type: box
scale: [1.0, 5.0, 41]
phys::Body:
type: static
shape:
type: box
extents: [0.5, 2.5, 20.5]
gfx::material:
ambient: [0.25, 0.25, 0.25]
diffuse: [0.4, 0.4, 0.4]
specular: [0.774597,0.774597,0.774597]
shininess: 0.6
# phys::Body:
# type: static
# shape:
# type: box
# extents: [0.5, 2.5, 20.5]
# Wall Z shorter to avoid z-fighting
- name: "wallZ"
components:
Transform:
StaticMesh:
pipeline: phong
pipeline: redbook/debug
shape_id: td_wall_y
shape:
type: box
scale: [39, 5, 1]
phys::Body:
type: static
shape:
type: box
extents: [ 19.5, 2.5, 0.5 ]
gfx::material:
ambient: [0.25, 0.25, 0.25]
diffuse: [0.4, 0.4, 0.4]
specular: [0.774597,0.774597,0.774597]
shininess: 0.6
# phys::Body:
# type: static
# shape:
# type: box
# extents: [ 19.5, 2.5, 0.5 ]
- name: "floor"
components:
Transform:
StaticMesh:
pipeline: phong
pipeline: redbook/debug
shape_id: td_floor
shape:
type: box # we don't (currently) support planes...
scale: [39, 0.5, 39]
phys::Body:
type: static
shape:
type: box # we don't (currently) support planes...
extents: [19.5, 0.25, 19.5]
gfx::material:
ambient: [0.25, 0.25, 0.25]
diffuse: [0.4, 0.4, 0.4]
specular: [0.774597,0.774597,0.774597]
shininess: 0.6
# phys::Body:
# type: static
# shape:
# type: box # we don't (currently) support planes...
# extents: [19.5, 0.25, 19.5]
- name: player
components:
Transform:
StaticMesh:
pipeline: phong
pipeline: redbook/lighting
shape_id: td_player
shape:
type: sphere
gfx::material:
......@@ -52,15 +71,16 @@ prefabs:
diffuse: [0.4, 0.4, 0.4]
specular: [0.774597,0.774597,0.774597]
shininess: 0.6
phys::Body:
shape:
type: sphere
radius: 1
# phys::Body:
# shape:
# type: sphere
# radius: 1
- name: collectable
components:
Transform:
StaticMesh:
pipeline: phong
pipeline: redbook/lighting
shape_id: td_collect
shape:
type: box
gfx::material:
......@@ -68,9 +88,9 @@ prefabs:
diffuse: [1, 1, 1]
specular: [0.0633, 0.727811, 0.633]
shininess: 0.6
phys::Body:
type: kinematic
shape:
type: box
phys::Callbacks:
phys::Cache:
\ No newline at end of file
# phys::Body:
# type: kinematic
# shape:
# type: box
# phys::Callbacks:
# phys::Cache:
\ No newline at end of file
......@@ -23,31 +23,36 @@
//
#include "GameScene.h"
#include "fggl/entity/loader/loader.hpp"
#include "fggl/mesh/components.hpp"
camera_type cam_mode = cam_free;
static void placeObject(fggl::ecs3::World& world, fggl::ecs::entity_t parent, fggl::ecs::entity_t prototype, glm::vec3 targetPos) {
auto obj = world.copy(prototype);
auto result = world.get<fggl::math::Transform>(obj);
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);
int xPos = (int)targetPos.x;
int zPos = (int)targetPos.z * -1;
// figure out the floor height
auto heightMap = world.get<fggl::data::HeightMap>(parent);
targetPos.y = heightMap->getValue(xPos, zPos); // TODO should really be the gradient at the required point
result->origin( targetPos );
auto heightMap = world.get<fggl::data::HeightMap>(floor);
targetPos.y = heightMap.getValue(xPos, zPos); // TODO should really be the gradient at the required point
result.origin( targetPos );
}
static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& input) {
auto cameras = ecs.findMatching<fggl::gfx::Camera>();
fggl::ecs3::entity_t cam = cameras[0];
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 dir = ( camTransform.origin() - camComp.target );
const glm::vec3 forward = glm::normalize( dir );
// scroll wheel
......@@ -55,7 +60,7 @@ static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& inp
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 );
camTransform.origin( camTransform.origin() + motion );
if ( cam_mode == cam_arcball || input.mouse.down( fggl::input::MouseButton::MIDDLE ) ) {
fggl::input::process_arcball(ecs, input, cam);
......@@ -65,35 +70,31 @@ static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& inp
fggl::input::process_edgescroll( ecs, input, cam );
}
static void setupCamera(fggl::ecs3::World& world) {
auto prototype = world.create(false);
static void setupCamera(fggl::entity::EntityManager& world) {
auto prototype = world.create();
// setup camera position/transform
auto* transform = world.add<fggl::math::Transform>(prototype);
if ( transform != nullptr) {
transform->origin(glm::vec3(10.0f, 3.0f, 10.0f));
}
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);
auto* cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype);
if ( cameraKeys != nullptr ) {
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);
}
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 fggl::ecs3::entity_t setupTerrain(fggl::ecs3::World& world) {
fggl::ecs3::entity_t terrain;
static fggl::entity::EntityID setupTerrain(fggl::entity::EntityManager& world) {
fggl::entity::EntityID terrain;
{
terrain = world.create(false);
terrain = world.create();
auto* camTf = world.add<fggl::math::Transform>(terrain);
camTf->origin( glm::vec3(-128.0f, 0.0f, 128.0f) );
auto& camTf = world.add<fggl::math::Transform>(terrain);
camTf.origin( glm::vec3(-128.0f, 0.0f, 128.0f) );
//auto terrainData = m_world.get<fggl::data::HeightMap>(terrain);
fggl::data::HeightMap terrainData{};
......@@ -102,64 +103,92 @@ static fggl::ecs3::entity_t setupTerrain(fggl::ecs3::World& world) {
const siv::PerlinNoise::seed_type seed = 123456U;
const siv::PerlinNoise perlin{ seed };
for (int z = 0; z < fggl::data::heightMaxZ; ++z) {
for (int x = 0; x < fggl::data::heightMaxX; ++x) {
for (std::size_t z = 0; z < fggl::data::heightMaxZ; ++z) {
for (std::size_t x = 0; x < fggl::data::heightMaxX; ++x) {
const double noise = perlin.octave2D_11( (x * 0.01), (z * 0.01) , 4) * 10.f;
terrainData.setValue(x, z, (float)noise);
}
}
world.set<fggl::data::HeightMap>(terrain, &terrainData);
world.add<fggl::data::HeightMap>(terrain, terrainData);
}
return terrain;
}
static fggl::ecs3::entity_t setupEnvironment(fggl::ecs3::World& world) {
auto& types = world.types();
static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& world) {
setupCamera(world);
return setupTerrain(world);
}
static fggl::ecs3::entity_t setupBunkerPrototype(fggl::ecs3::World& world) {
auto& types = world.types();
fggl::ecs3::entity_t bunker;
static auto BUNKER_PROTOTYPE = "bunker"_fid;
static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) {
{
bunker = world.create(true);
world.add(bunker, types.find(fggl::math::Transform::name));
auto bunkerSpec = fggl::entity::EntitySpec{};
bunkerSpec.addComp(fggl::math::Transform::guid, {});
// mesh
int nSections = 2;
constexpr float HALF_PI = M_PI / 2.0f;
constexpr char shader[] = "phong";
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 );
const auto cubeMat = glm::translate( fggl::math::mat4( 1.0f ) , shapeOffset );
const auto leftSlope = fggl::math::modelMatrix(
glm::vec3(-1.0f, 0.0f, 0.0f) + shapeOffset,
glm::vec3( 0.0f, -HALF_PI, 0.0f) );
glm::vec3( 0.0f, -fggl::math::HALF_PI, 0.0f) );
const auto rightSlope = fggl::math::modelMatrix(
glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset,
glm::vec3( 0.0f, HALF_PI, 0.0f) );
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();
// 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);
}
fggl::data::StaticMesh staticMesh{mesh, shader};
world.set<fggl::data::StaticMesh>(bunker, &staticMesh);
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);
}
return bunker;
}
void GameScene::setup() {
m_canvas.size( fggl::math::vec2(0,0), fggl::math::vec2(100, 100));
auto terrain = setupEnvironment(world());
auto bunkerPrototype = setupBunkerPrototype(world());
auto* entityFactory = m_owner.service<fggl::entity::EntityFactory>();
auto terrain = setup_environment(world());
setupBunkerPrototype(entityFactory);
// create building prototype
int nCubes = 3;
......@@ -167,14 +196,12 @@ void GameScene::setup() {
glm::vec3 location;
location.x = i * 6.f + 1.0f;
location.z = -5.0f + 1.0f;
placeObject(world(), terrain, bunkerPrototype, location);
placeObject(world(), terrain, entityFactory, BUNKER_PROTOTYPE, location);
}
}
void GameScene::update() {
Game::update();
auto& inputSystem = input();
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
......@@ -23,52 +23,55 @@
#if __has_include("fggl/phys/bullet/bullet.hpp")
#include "fggl/phys/bullet/bullet.hpp"
#else
#include "fggl/phys/null.hpp"
#endif
#include "fggl/fggl.hpp"
#include "fggl/ecs/component_fwd.hpp"
#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);
});
}
......@@ -84,11 +87,36 @@ 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
moduleManager.use<fggl::phys::NullPhysics>();
#endif
moduleManager.resolve();
// 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
......@@ -97,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);
}
}
}
}
......@@ -23,108 +23,38 @@
#include "fggl/gfx/phong.hpp"
#include "fggl/input/camera_input.hpp"
#include "fggl/ecs3/prototype/loader.hpp"
#include "fggl/debug/draw.hpp"
#include <array>
struct Prefabs {
fggl::ecs3::entity_t collectable;
fggl::ecs3::entity_t player;
};
static void setup_prefabs(fggl::data::Storage* storage, fggl::ecs3::World& world, Prefabs& prefabs) {
fggl::ecs3::load_prototype_file(world, *storage, "rollball.yml");
{
// player (cube because my sphere function doesn't exist yet
prefabs.player = world.findPrototype("player");
world.add<fggl::phys::Dynamics>(prefabs.player);
}
#include "fggl/entity/entity.hpp"
#include "fggl/entity/loader/loader.hpp"
{
// collectable
prefabs.collectable = world.findPrototype("collectable");
#include "fggl/debug/draw.hpp"
// we need both of these for callbacks to trigger.
world.add<fggl::phys::CollisionCallbacks>(prefabs.collectable);
world.add<fggl::phys::CollisionCache>(prefabs.collectable);
}
#include <array>
}
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::ecs3::World& world) {
auto prototype = world.create(false);
static void setup_camera(fggl::entity::EntityManager& world) {
auto prototype = world.create();
// setup camera position/transform
auto* transform = world.add<fggl::math::Transform>(prototype);
if ( transform != nullptr) {
transform->origin(glm::vec3(10.0f, 3.0f, 10.0f));
}
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);
}
static fggl::ecs3::entity_t setup_environment(fggl::ecs3::World& world, const fggl::math::vec2& size, demo::RollState& state) {
{
auto northWall = world.createFromPrototype("wallX");
auto* transform = world.get<fggl::math::Transform>(northWall);
transform->origin({size.x/2, 0.0F, 0.0F});
}
{
auto southWall = world.createFromPrototype("wallX");
auto* transform = world.get<fggl::math::Transform>(southWall);
transform->origin({-size.x/2, 0.0F, 0.0F});
}
{
auto westWall = world.createFromPrototype("wallZ");
auto* transform = world.get<fggl::math::Transform>(westWall);
transform->origin({0.0F, 0.0F, -size.y/2});
}
{
auto eastWall = world.createFromPrototype("wallZ");
auto* transform = world.get<fggl::math::Transform>(eastWall);
transform->origin({0.0F, 0.0F, size.y/2});
}
{
auto floor = world.createFromPrototype("floor");
auto *transform = world.get<fggl::math::Transform>(floor);
transform->origin({0.0F, -2.5F, 0.0F});
}
{
// player just starts off as the prefab dictates
state.player = world.createFromPrototype("player");
}
{
// 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 = world.createFromPrototype("collectable");
auto* transform = world.get<fggl::math::Transform>(collectable);
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::ecs::NULL_ENTITY;
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;
}
......@@ -138,32 +68,68 @@ namespace demo {
constexpr fggl::math::vec3 COLOUR_BLUE{0.0F, 0.0F, 1.0F};
constexpr float MOVE_FORCE{3.0F};
RollBall::RollBall(fggl::App &app) : Game(app) {
RollBall::RollBall(fggl::App &app) : Game(app), m_phys(nullptr) {
}
void RollBall::activate() {
Game::activate();
fggl::debug::log(fggl::debug::Level::info, "RollBall::activate()");
// attach physics
auto* physService = m_owner.service<fggl::phys::PhysicsProvider>();
auto* entFactory = m_owner.service<fggl::entity::EntityFactory>();
m_phys = physService->create(&world(), entFactory);
Prefabs prefabs{};
setup_prefabs(m_owner.service<fggl::data::Storage>(), world(), prefabs);
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::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);
/*auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable);
collectableCallbacks->onEnter = [this](auto ourEntity, auto theirEntity) {
if ( theirEntity == state.player) {
//if ( ourEntity == state.closestPickup ) {
// // we're the closest pickup and we're about to get killed, so need to not be.
// state.closestPickup = fggl::ecs::NULL_ENTITY;
//}
this->world().destroy(ourEntity);
world().destroy(ourEntity);
}
};
};*/
// actual scene objects
setup_camera(world());
// create a 20x20 grid
setup_environment(world(), WORLD_SIZE, state);
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;
m_phys = nullptr;
}
// now let the rest of the scene do its bit
Game::deactivate();
}
fggl::math::vec3 calc_move_vector(const fggl::input::Input& input) {
......@@ -190,14 +156,13 @@ namespace demo {
return force;
}
void RollBall::update() {
Game::update();
const float deltaTime = 1 / 60.0F;
void RollBall::update(float deltaTime) {
Game::update(deltaTime);
m_phys->step();
auto& input = this->input();
if ( state.player != fggl::ecs3::NULL_ENTITY ) {
if ( state.player != fggl::entity::INVALID ) {
auto &world = this->world();
// mode selection
......@@ -209,26 +174,26 @@ namespace demo {
auto force = calc_move_vector(input);
if ( force != fggl::math::VEC3_ZERO ) {
force = glm::normalize(force) * MOVE_FORCE;
auto *dynamics = world.get<fggl::phys::Dynamics>(state.player);
dynamics->force += force;
auto& dynamics = world.get<fggl::phys::Dynamics>(state.player);
dynamics.force += force;
}
// track player position with camera
{
auto cameras = world.findMatching<fggl::gfx::Camera>();
fggl::ecs3::entity_t cam = cameras[0];
auto *camComp = world.get<fggl::gfx::Camera>(cam);
camComp->target = world.get<fggl::math::Transform>(state.player)->origin();
auto cameras = world.find<fggl::gfx::Camera>();
auto cam = cameras[0];
auto& camComp = world.get<fggl::gfx::Camera>(cam);
camComp.target = world.get<fggl::math::Transform>(state.player).origin();
auto *camTransform = world.get<fggl::math::Transform>(cam);
camTransform->origin( camComp->target + cameraOffset );
auto& camTransform = world.get<fggl::math::Transform>(cam);
camTransform.origin( camComp.target + cameraOffset );
}
// distance mode
if ( state.mode == DebugMode::DISTANCE ) {
closestPickup(world);
} else {
if ( state.closestPickup != fggl::ecs::NULL_ENTITY ) {
if ( state.closestPickup != fggl::entity::INVALID ) {
if ( world.alive(state.closestPickup) ){
auto *renderer = world.tryGet<fggl::gfx::PhongMaterial>(state.closestPickup);
if (renderer != nullptr) {
......@@ -236,7 +201,7 @@ namespace demo {
}
}
state.closestPickup = fggl::ecs::NULL_ENTITY;
state.closestPickup = fggl::entity::INVALID;
}
}
......@@ -244,20 +209,25 @@ namespace demo {
spinCubes(world, deltaTime);
state.time += deltaTime;
}
// tick scripts
if ( m_scripts != nullptr ) {
m_scripts->onUpdate();
}
}
void RollBall::closestPickup(fggl::ecs3::World &world) {
void RollBall::closestPickup(fggl::entity::EntityManager &world) {
float closestDistance = FLT_MAX;
fggl::ecs::entity_t closestEntity = fggl::ecs::NULL_ENTITY;
auto playerPos = world.get<fggl::math::Transform>(state.player)->origin();
auto closestEntity = fggl::entity::INVALID;
auto playerPos = world.get<fggl::math::Transform>(state.player).origin();
for ( const auto& pickup : state.collectables ) {
if ( !world.alive(pickup) ) {
continue;
}
auto* transform = world.get<fggl::math::Transform>(pickup);
auto distance = glm::distance(transform->origin(), playerPos);
auto& transform = world.get<fggl::math::Transform>(pickup);
auto distance = glm::distance(transform.origin(), playerPos);
if ( distance < closestDistance) {
closestDistance = distance;
closestEntity = pickup;
......@@ -275,7 +245,7 @@ namespace demo {
// now, deal with the new closest
state.closestPickup = closestEntity;
if (closestEntity != fggl::ecs::NULL_ENTITY) {
if (closestEntity != fggl::entity::INVALID) {
auto *renderer = world.tryGet<fggl::gfx::PhongMaterial>(state.closestPickup);
if (renderer != nullptr) {
renderer->diffuse = COLOUR_BLUE;
......@@ -284,18 +254,18 @@ namespace demo {
}
// closest pickup should face the player
if ( state.closestPickup != fggl::ecs::NULL_ENTITY) {
auto* transform = world.get<fggl::math::Transform>(state.closestPickup);
auto* playerTransform = world.get<fggl::math::Transform>( state.player );
transform->lookAt(playerTransform->origin());
if ( state.closestPickup != fggl::entity::INVALID) {
auto& transform = world.get<fggl::math::Transform>(state.closestPickup);
auto& playerTransform = world.get<fggl::math::Transform>( state.player );
transform.lookAt(playerTransform.origin());
// lazy, so using the debug draw line for this bit
dd::line( &playerTransform->origin()[0], &transform->origin()[0], dd::colors::White );
dd::line( &playerTransform.origin()[0], &transform.origin()[0], dd::colors::White );
}
}
void RollBall::spinCubes(fggl::ecs3::World& world, float deltaTime) {
void RollBall::spinCubes(fggl::entity::EntityManager& world, float deltaTime) {
// rotation
for ( const auto& entity : state.collectables ) {
if ( !world.alive(entity) || entity == state.closestPickup ) {
......@@ -303,8 +273,8 @@ namespace demo {
}
// rotate the cubes
auto* transform = world.get<fggl::math::Transform>(entity);
transform->rotateEuler( COLLECTABLE_ROTATION * deltaTime );
auto& transform = world.get<fggl::math::Transform>(entity);
transform.rotateEuler( COLLECTABLE_ROTATION * deltaTime );
}
}
......
......@@ -20,36 +20,50 @@
#include "fggl/data/storage.hpp"
#include "fggl/gfx/camera.hpp"
#include "fggl/gfx/phong.hpp"
#include "fggl/input/camera_input.hpp"
#include "fggl/ecs3/prototype/loader.hpp"
#include "fggl/entity/loader/loader.hpp"
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;
namespace demo {
static void create_topdown_camera(fggl::ecs3::World& world) {
auto prototype = world.create(false);
static void create_topdown_camera(fggl::entity::EntityManager& world) {
auto prototype = world.create();
// setup camera position/transform
auto* transform = world.add<fggl::math::Transform>(prototype);
if ( transform != nullptr) {
transform->origin(glm::vec3(10.0f, 50.0f, 10.0f));
}
auto& transform = world.add<fggl::math::Transform>(prototype);
transform.origin(glm::vec3(10.0f, 50.0f, 10.0f));
// setup camera components
auto* camera = world.add<fggl::gfx::Camera>(prototype);
camera->target = glm::vec3(0.0f, 0.0f, 0.0f);
auto* cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype);
if ( cameraKeys != nullptr ) {
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);
}
auto& camera = world.add<fggl::gfx::Camera>(prototype);
camera.target = glm::vec3(0.0f, 0.0f, 0.0f);
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 place_cover_boxes(fggl::ecs3::World& world) {
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},
{-10.0F, 0.0F, 10.0F},
......@@ -61,52 +75,52 @@ namespace demo {
{ 0.0F, 0.0F, 10.0F},
}};
for (auto pos : boxPos) {
auto box = world.createFromPrototype("collectable");
auto* transform = world.get<fggl::math::Transform>(box);
transform->origin(pos);
auto box = factory->create(collectablePrefab, world);
auto& transform = world.get<fggl::math::Transform>(box);
transform.origin(pos);
}
}
static void build_arena(fggl::ecs3::World& world) {
static void build_arena(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) {
{
auto floor = world.createFromPrototype("floor");
auto* transform = world.get<fggl::math::Transform>(floor);
transform->origin({0.0F, -2.5F, 0.0F});
fggl::debug::log("created floor: {}", floor);
auto floor = factory->create(floorPrefab, world);
auto& transform = world.get<fggl::math::Transform>(floor);
transform.origin({0.0F, -2.5F, 0.0F});
//fggl::debug::log("created floor: {}", floor);
}
fggl::math::vec2 size{40.0F, 40.0F};
for (auto side : {-1.0F, 1.0F})
{
{
auto northWall = world.createFromPrototype("wallX");
auto *transform = world.get<fggl::math::Transform>(northWall);
transform->origin({size.x / 2 * side, 0.0F, 0.0F});
auto obj = factory->create(WallNPrefab, world);
auto& transform = world.get<fggl::math::Transform>(obj);
transform.origin({size.x / 2 * side, 0.0F, 0.0F});
}
{
auto westWall = world.createFromPrototype("wallZ");
auto* transform = world.get<fggl::math::Transform>(westWall);
transform->origin({0.0F, 0.0F, size.y/2 * side });
auto obj = factory->create(WallEPrefab, world);
auto& transform = world.get<fggl::math::Transform>(obj);
transform.origin({0.0F, 0.0F, size.y/2 * side });
}
}
place_cover_boxes(world);
setup_lighting(world);
place_cover_boxes(factory, world);
}
static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& input) {
auto cameras = ecs.findMatching<fggl::gfx::Camera>();
static void process_camera(fggl::entity::EntityManager& ecs, const fggl::input::Input& input) {
auto cameras = ecs.find<fggl::gfx::Camera>();
if ( !cameras.empty() ) {
fggl::ecs3::entity_t cam = cameras[0];
auto cam = cameras[0];
fggl::input::process_scroll(ecs, input, cam);
fggl::input::process_freecam(ecs, input, cam);
fggl::input::process_edgescroll(ecs, input, cam);
}
}
static void populate_sample_level(fggl::ecs3::World& world) {
static void populate_sample_level(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) {
create_topdown_camera(world);
build_arena(world);
build_arena(factory, world);
}
TopDown::TopDown(fggl::App& app) : fggl::scenes::Game(app) {
......@@ -116,15 +130,19 @@ TopDown::TopDown(fggl::App& app) : fggl::scenes::Game(app) {
void TopDown::activate() {
Game::activate();
auto* storage = m_owner.service<fggl::data::Storage>();
fggl::ecs3::load_prototype_file(world(), *storage, "topdown.yml");
fggl::debug::log(fggl::debug::Level::info, "TopDown::activate()");
auto* assetLoader = m_owner.service<fggl::assets::Loader>();
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");
// create a sample level
populate_sample_level(world());
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) ) {
......@@ -133,12 +151,16 @@ void TopDown::update() {
}
void TopDown::pick_object() {
auto cameras = world().findMatching<fggl::gfx::Camera>();
if (!hasPhys()) {
return;
}
auto cameras = world().find<fggl::gfx::Camera>();
if ( cameras.empty() ) {
return;
}
fggl::ecs3::entity_t cam = cameras[0];
auto cam = cameras[0];
fggl::math::vec2 position {
input().mouse.axis(fggl::input::MouseAxis::X),
......@@ -147,10 +169,10 @@ void TopDown::pick_object() {
auto ray = fggl::gfx::get_camera_ray(world(), cam, position);
auto hit = phys().raycast(ray);
if ( hit != fggl::ecs3::NULL_ENTITY) {
fggl::debug::log("hit: {}", hit);
if ( hit != fggl::entity::INVALID) {
fggl::debug::info("hit: {}", (int)hit);
} else {
fggl::debug::log("no hit");
fggl::debug::info("no hit");
}
}
......