Skip to content
Snippets Groups Projects
main.cpp 13.1 KiB
Newer Older
#include <filesystem>
#include <utility>
#include <fggl/gfx/window.hpp>
#include <fggl/gfx/camera.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 <fggl/util/chrono.hpp>
#include <fggl/util/service.h>
Joseph Walton-Rivers's avatar
Joseph Walton-Rivers committed
#include <fggl/ecs3/ecs.hpp>
Joseph Walton-Rivers's avatar
Joseph Walton-Rivers committed
#include <fggl/debug/debug.h>
#include <imgui.h>
#include <fggl/scenes/Scene.h>
#include <PerlinNoise.hpp>

constexpr bool showNormals = false;

// prototype of resource discovery
Joseph Walton-Rivers's avatar
Joseph Walton-Rivers committed
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;
	}

}

Joseph Walton-Rivers's avatar
Joseph Walton-Rivers committed
enum camera_type { cam_free, cam_arcball };
camera_type cam_mode = cam_free;

//TODO proper input system
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];

Joseph Walton-Rivers's avatar
Joseph Walton-Rivers committed
	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);
	} else if ( cam_mode == cam_free ) {
		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 setup() override {
    }
    void cleanup() override {
    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");
        }
    }
    void render() override {
private:
    InputManager m_inputs;
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 cleanup() override {
    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() );
        }
    void render() override {
        debugInspector();
    void debugInspector() {
        auto types = m_world.types();

        ImGui::Begin("Entities");
        auto entityItr = m_world.all();
        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)
                            );
                        ImGui::TreePop();
                    }
                    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::TreePop();
                ImGui::Separator();
int main(int argc, char* argv[]) {
    auto& locator = fggl::util::ServiceLocator::instance();

    // setup ECS Types
    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 );

	// storage API
    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");

    // Main game/event loop
	fggl::util::Timer time{};
	time.frequency( glfwGetTimerFrequency() );
	time.setup( glfwGetTimerValue() );

	while( !window->closeRequested() ) {
		//
		// Setup setup
		time.tick( glfwGetTimerValue() );
		inputs->frame( time.delta() );
		glfwModule->context.pollEvents();
		debug->frameStart();
		//
		// update step
		//
        scenes->update();

        // render the scene
        window->activate();
        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();