Newer
Older
#include <iostream>
#include <fggl/gfx/window.hpp>
#include <fggl/input/camera_input.h>
#include <fggl/gfx/compat.hpp>
#include <fggl/gfx/ogl/compat.hpp>
#include <fggl/data/procedural.hpp>
#include <fggl/data/storage.hpp>
#include <PerlinNoise.hpp>
void discover(const std::filesystem::path& base) {
std::vector< std::filesystem::path > contentPacks;
for ( auto& item : std::filesystem::directory_iterator(base) ) {
// content pack detection
if ( std::filesystem::is_directory( item ) ) {
auto manifest = item.path() / "manifest.yml";
if ( std::filesystem::exists( manifest ) ) {
contentPacks.push_back( item.path() );
}
}
}
// what did we find?
std::cerr << "found pack(s): " << std::endl;
for ( auto& pack : contentPacks ) {
std::cerr << pack << std::endl;
}
}
enum camera_type { cam_free, cam_arcball };
camera_type cam_mode = cam_free;
using InputManager = std::shared_ptr<fggl::input::Input>;
void process_camera(fggl::ecs3::World& ecs, const InputManager& input) {
auto cameras = ecs.findMatching<fggl::gfx::Camera>();
fggl::ecs3::entity_t 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 );
if ( cam_mode == cam_arcball || input->mouse.button( fggl::input::MouseButton::MIDDLE ) ) {
fggl::input::process_arcball(ecs, *input, cam);
fggl::input::process_freecam(ecs, *input, cam);
fggl::input::process_edgescroll( ecs, *input, cam );
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);
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 );
}
class MenuScene : public fggl::scenes::Scene {
public:
explicit MenuScene(InputManager inputs) : m_inputs(std::move(inputs)) {}
~MenuScene() override = default;
void update() override {
bool leftMouse = m_inputs->mouse.button(fggl::input::MouseButton::LEFT);
if (leftMouse) {
auto scenes = fggl::util::ServiceLocator::instance().providePtr<fggl::scenes::SceneManager>();
scenes->activate("game");
}
}
class GameScene : public fggl::scenes::Scene {
public:
explicit GameScene(fggl::ecs3::World& world, InputManager inputs) : m_world(world), m_inputs(std::move(inputs)) { };
~GameScene() override = default;
void setup() override {
auto types = m_world.types();
// create camera using strings
{
auto prototype = m_world.create(false);
m_world.add(prototype, types.find(fggl::math::Transform::name));
m_world.add(prototype, types.find(fggl::gfx::Camera::name));
m_world.add(prototype, types.find(fggl::input::FreeCamKeys::name));
auto camTf = m_world.get<fggl::math::Transform>(prototype);
camTf->origin( glm::vec3(10.0f, 3.0f, 10.0f) );
auto cameraKeys = m_world.get<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);
fggl::ecs3::entity_t terrain;
terrain = m_world.create(false);
m_world.add(terrain, types.find(fggl::math::Transform::name));
auto camTf = m_world.get<fggl::math::Transform>(terrain);
camTf->origin( glm::vec3(0.0f, 0.0f, 0.0f) );
//auto terrainData = m_world.get<fggl::data::HeightMap>(terrain);
fggl::data::HeightMap terrainData{};
terrainData.clear();
const siv::PerlinNoise::seed_type seed = 123456u;
const siv::PerlinNoise perlin{ seed };
for (int y = 0; y < 255; ++y) {
for (int x = 0; x < 255; ++x) {
const double noise = perlin.octave2D_11( (x * 0.01), (y * 0.01) , 4) * 10.f;
terrainData.heightValues[x * 255 +y] = (float)noise;
}
}
m_world.set<fggl::data::HeightMap>(terrain, &terrainData);
}
// create foundation object
fggl::ecs3::entity_t foundation;
{
foundation = m_world.create(true);
m_world.add(foundation, types.find(fggl::math::Transform::name));
// plot rendering
fggl::data::Mesh mesh;
fggl::data::make_cube(mesh);
mesh.removeDups();
// add mesh as a component
constexpr char shader[] = "phong";
fggl::gfx::StaticMesh staticMesh{mesh, shader};
m_world.set<fggl::gfx::StaticMesh>(foundation, &staticMesh);
}
// create building prototype
fggl::ecs3::entity_t bunker;
{
bunker = m_world.create(true);
m_world.add(bunker, types.find(fggl::math::Transform::name));
// mesh
int nSections = 2;
constexpr float HALF_PI = M_PI / 2.0f;
constexpr char shader[] = "phong";
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) );
const auto rightSlope = fggl::math::modelMatrix(
glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset,
glm::vec3( 0.0f, HALF_PI, 0.0f) );
fggl::data::make_cube( mesh, cubeMat );
fggl::data::make_slope( mesh, leftSlope );
fggl::data::make_slope( mesh, rightSlope );
}
mesh.removeDups();
fggl::gfx::StaticMesh staticMesh{mesh, shader};
m_world.set<fggl::gfx::StaticMesh>(bunker, &staticMesh);
}
for (int i=0; i<3; ++i) {
glm::vec3 location(i * 6.5f + 1.0f, 0.0f, -7.0f);
placeObject(m_world, terrain, foundation, location);
}
int nCubes = 3;
for ( int i=0; i<nCubes; i++ ) {
glm::vec3 location;
location.x = i * 6.f + 1.0f;
location.z = -5.0f + 1.0f;
placeObject(m_world, terrain, bunker, location);
void update() override {
process_camera(m_world, m_inputs);
// JWR - this doesn't really seem like it belongs in the game scene...
if ( m_inputs->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F10)) ) {
auto dbgui = fggl::util::ServiceLocator::instance().providePtr<fggl::debug::DebugUI>();
dbgui->visible( !dbgui->visible() );
}
for (auto& entity : entityItr) {
std::string label = "entity-" + std::to_string(entity);
if ( ImGui::TreeNode(label.c_str()) ){
auto entComp = m_world.getComponents(entity);
for ( auto comp : entComp ){
auto meta = types.meta(comp);
ImGui::Text("%s (%d) - %lu bytes", meta->name(), comp, meta->size());
}
ImGui::TreePop();
}
}
ImGui::End();
};
private:
fggl::ecs3::World& m_world;
InputManager m_inputs;
};
void gamepadDebug(bool* visible) {
auto inputs = fggl::util::ServiceLocator::instance().providePtr<fggl::input::Input>();
auto &gamepads = inputs->gamepads;
ImGui::Begin("GamePad", visible);
for (int i = 0; i < 16; i++) {
std::string title = gamepads.name(i);
bool present = gamepads.present(i);
if (ImGui::TreeNode(title.c_str())) {
ImGui::Text("present: %s", present ? "yes" : "no");
if (present) {
if (ImGui::TreeNode("buttons##2")) {
for (auto &btn: fggl::input::GamepadButtonsMicrosoft) {
ImGui::Text("%s: %i %i %i", btn.name,
gamepads.button(i, btn.id),
gamepads.buttonPressed(i, btn.id),
gamepads.buttonReleased(i, btn.id)
);
if (ImGui::TreeNode("axes##2")) {
for (auto &axis: fggl::input::GamepadAxes) {
ImGui::Text("%s: %f %f", axis.name,
gamepads.axis(i, axis.id),
gamepads.axisDelta(i, axis.id)
);
ImGui::TreePop();
ImGui::Separator();
int main(int argc, char* argv[]) {
auto& locator = fggl::util::ServiceLocator::instance();
fggl::ecs3::TypeRegistry types;
types.make<fggl::math::Transform>();
fggl::ecs3::ModuleManager modules(types);
fggl::ecs3::World ecs(types);
// input management
auto inputs = std::make_shared<fggl::input::Input>();
locator.supply<fggl::input::Input>(inputs);
// window
auto glfwModule = modules.load<fggl::gfx::ecsGlfwModule>(inputs);
auto window = glfwModule->createWindow("Demo Game");
window->fullscreen( true );
auto storage = std::make_shared<fggl::data::Storage>();
locator.supply<fggl::data::Storage>(storage);
//discover( storage.resolvePath(fggl::data::Data, "../../packs") );
// Opengl APIs
auto glModule = modules.load<fggl::gfx::ecsOpenGLModule>(window, storage);
fggl::gfx::loadPipeline(glModule, "unlit", false);
fggl::gfx::loadPipeline(glModule, "phong", false);
fggl::gfx::loadPipeline(glModule, "normals", false);
// debug layer
std::shared_ptr<fggl::debug::DebugUI> debug = std::make_shared<fggl::debug::DebugUI>(window);
locator.supply<fggl::debug::DebugUI>(debug);
debug->addWindow("gamepad", gamepadDebug);
debug->addWindow("imgui-demo", ImGui::ShowDemoWindow);
debug->addWindow("imgui-about", ImGui::ShowAboutWindow);
debug->addWindow("imgui-help", [](bool* val) { ImGui::ShowUserGuide(); } );
debug->visible(true);
// Scene management
auto scenes = std::make_shared<fggl::scenes::SceneManager>();
locator.supply<fggl::scenes::SceneManager>(scenes);
scenes->create("main_menu", std::make_shared<MenuScene>(inputs));
scenes->create("game", std::make_shared<GameScene>(ecs, inputs));
scenes->activate("main_menu");
fggl::util::Timer time{};
time.frequency( glfwGetTimerFrequency() );
time.setup( glfwGetTimerValue() );
while( !window->closeRequested() ) {
time.tick( glfwGetTimerValue() );
inputs->frame( time.delta() );
//
// update step
//
scenes->update();
// render the scene
glModule->ogl.clear();
// allow the scene to do stuff, then actually render
scenes->render();
fggl::gfx::renderMeshes(glModule, ecs, time.delta());
debug->draw();
window->swap();
return 0;
}