From ce4973b9a140220f5a343bd970001abaa0167658 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sat, 9 Mar 2019 00:03:54 +0800 Subject: [PATCH] Add initial terrain system and terrain patch component --- src/entity/components/component-type.hpp | 1 + .../components/terrain-patch-component.cpp | 29 ++ .../components/terrain-patch-component.hpp | 37 ++ src/entity/systems/terrain-system.cpp | 360 ++++++++++++++++++ src/entity/systems/terrain-system.hpp | 103 +++++ src/game.cpp | 150 ++++---- src/game.hpp | 13 +- src/resources/entity-template-loader.cpp | 26 ++ src/resources/resource-manager.hpp | 1 + src/states/sandbox-state.cpp | 2 +- 10 files changed, 633 insertions(+), 89 deletions(-) create mode 100644 src/entity/components/terrain-patch-component.cpp create mode 100644 src/entity/components/terrain-patch-component.hpp create mode 100644 src/entity/systems/terrain-system.cpp create mode 100644 src/entity/systems/terrain-system.hpp diff --git a/src/entity/components/component-type.hpp b/src/entity/components/component-type.hpp index 910659d..55bec30 100644 --- a/src/entity/components/component-type.hpp +++ b/src/entity/components/component-type.hpp @@ -31,6 +31,7 @@ enum class ComponentType MODEL, STEERING, SOUND_SOURCE, + TERRAIN_PATCH, TOOL, TRANSFORM }; diff --git a/src/entity/components/terrain-patch-component.cpp b/src/entity/components/terrain-patch-component.cpp new file mode 100644 index 0000000..645c660 --- /dev/null +++ b/src/entity/components/terrain-patch-component.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017-2019 Christopher J. Howard + * + * This file is part of Antkeeper Source Code. + * + * Antkeeper Source Code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper Source Code 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper Source Code. If not, see . + */ + +#include "terrain-patch-component.hpp" + +ComponentBase* TerrainPatchComponent::clone() const +{ + TerrainPatchComponent* component = new TerrainPatchComponent(); + component->position = position; + + return component; +} + diff --git a/src/entity/components/terrain-patch-component.hpp b/src/entity/components/terrain-patch-component.hpp new file mode 100644 index 0000000..f4023be --- /dev/null +++ b/src/entity/components/terrain-patch-component.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017-2019 Christopher J. Howard + * + * This file is part of Antkeeper Source Code. + * + * Antkeeper Source Code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper Source Code 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper Source Code. If not, see . + */ + +#ifndef TERRAIN_PATCH_COMPONENT_HPP +#define TERRAIN_PATCH_COMPONENT_HPP + +#include "../component.hpp" +#include "component-type.hpp" +#include + +class TerrainPatchComponent: public Component +{ +public: + virtual ComponentBase* clone() const; + + // Position in integer terrain coordinates + std::tuple position; +}; + +#endif // TERRAIN_PATCH_COMPONENT_HPP + diff --git a/src/entity/systems/terrain-system.cpp b/src/entity/systems/terrain-system.cpp new file mode 100644 index 0000000..6540779 --- /dev/null +++ b/src/entity/systems/terrain-system.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2017-2019 Christopher J. Howard + * + * This file is part of Antkeeper Source Code. + * + * Antkeeper Source Code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper Source Code 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper Source Code. If not, see . + */ + +#include "terrain-system.hpp" +#include "graphics/vertex-format.hpp" + +TerrainSystem::TerrainSystem(ComponentManager* componentManager): + System(componentManager), + terrainCreationGroup(componentManager), + terrainGroup(componentManager) +{ + terrainCreationGroup.addGroupObserver(this); + terrainGroup.addGroupObserver(this); + + patchSize = 100.0f; +} + +TerrainSystem::~TerrainSystem() +{} + +void TerrainSystem::update(float t, float dt) +{ + + auto members = terrainGroup.getMembers(); + for (const TerrainGroup::Member* member: *members) + { + ModelComponent* model = std::get<0>(member->components); + TerrainPatchComponent* patch = std::get<1>(member->components); + TransformComponent* transform = std::get<2>(member->components); + } +} + +void TerrainSystem::memberRegistered(const TerrainCreationGroup::Member* member) +{ + TerrainPatchComponent* patch = std::get<0>(member->components); + TransformComponent* transform = std::get<1>(member->components); + + // Generate a subdivided plane mesh + TriangleMesh* patchMesh = generatePlane(5); + + // Generate a model from the subdivided plane + Model* patchModel = generateModel(patchMesh); + + // Add model component to the entity + ModelComponent* model = new ModelComponent(); + model->model.setModel(patchModel); + componentManager->addComponent(member->entity, model); + + // Set scale of the transform component + transform->transform.scale = Vector3(patchSize); + transform->transform.translation = Vector3(std::get<0>(patch->position), 0.0f, std::get<1>(patch->position)) * patchSize; +} + +void TerrainSystem::memberUnregistered(const TerrainCreationGroup::Member* member) +{} + +void TerrainSystem::memberRegistered(const TerrainGroup::Member* member) +{ + // Add terrain patch to the patch map + TerrainPatchComponent* patch = std::get<1>(member->components); + patchMap[patch->position] = member; +} + +void TerrainSystem::memberUnregistered(const TerrainGroup::Member* member) +{ + // Remove terrain patch from the patch map + TerrainPatchComponent* patch = std::get<1>(member->components); + auto it = patchMap.find(patch->position); + if (it != patchMap.end()) + { + patchMap.erase(it); + } + + // TODO: free created terrain patch model +} + +TriangleMesh* TerrainSystem::generatePlane(int subdivisions) +{ + //std::size_t quadCount = std::pow(4, subdivisions); + //std::size_t triangleCount = quadCount * 2; + std::size_t columns = std::pow(2, subdivisions); + std::size_t rows = columns; + std::size_t vertexCount = (columns + 1) * (rows + 1); + float vertexIncrement = 1.0f / static_cast(columns); + + // Generate vertices + std::vector vertices; + Vector3 position(0.0f); + position.z = -0.5f; + for (std::size_t i = 0; i <= rows; ++i) + { + position.x = -0.5f; + for (std::size_t j = 0; j <= columns; ++j) + { + vertices.push_back(position); + position.x += vertexIncrement; + + } + + position.z += vertexIncrement; + } + + // Generate indices + std::vector indices; + for (std::size_t i = 0; i < rows; ++i) + { + for (std::size_t j = 0; j < columns; ++j) + { + unsigned int a = i * (columns + 1) + j; + unsigned int b = (i + 1) * (columns + 1) + j; + unsigned int c = i * (columns + 1) + j + 1; + unsigned int d = (i + 1) * (columns + 1) + j + 1; + + indices.push_back(a); + indices.push_back(b); + indices.push_back(c); + indices.push_back(c); + indices.push_back(b); + indices.push_back(d); + } + } + + return new TriangleMesh(vertices, indices); + + /* + // Generate navmesh + surfaceNavmesh.create(surfaceVertices, surfaceIndices); + + // Calculate vertex normals + calculateSurfaceNormals(); + + // Create and load VAO, VBO, and IBO + glGenVertexArrays(1, &surfaceVAO); + glBindVertexArray(surfaceVAO); + glGenBuffers(1, &surfaceVBO); + glBindBuffer(GL_ARRAY_BUFFER, surfaceVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * surfaceVertexSize * surfaceVertexCount, surfaceVertexData, GL_STATIC_DRAW); + glEnableVertexAttribArray(EMERGENT_VERTEX_POSITION); + glVertexAttribPointer(EMERGENT_VERTEX_POSITION, 3, GL_FLOAT, GL_FALSE, surfaceVertexSize * sizeof(float), (char*)0 + 0 * sizeof(float)); + glEnableVertexAttribArray(EMERGENT_VERTEX_NORMAL); + glVertexAttribPointer(EMERGENT_VERTEX_NORMAL, 3, GL_FLOAT, GL_FALSE, surfaceVertexSize * sizeof(float), (char*)0 + 3 * sizeof(float)); + glEnableVertexAttribArray(EMERGENT_VERTEX_TEXCOORD); + glVertexAttribPointer(EMERGENT_VERTEX_TEXCOORD, 2, GL_FLOAT, GL_FALSE, surfaceVertexSize * sizeof(float), (char*)0 + 6 * sizeof(float)); + glGenBuffers(1, &surfaceIBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surfaceIBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::uint32_t) * surfaceIndexCount, surfaceIndexData, GL_STATIC_DRAW); + + // Setup material + //surfaceMaterial.flags = static_cast(PhysicalMaterial::Flags::OBJECT); + + // Setup buffers + surfaceModel.setVAO(surfaceVAO); + surfaceModel.setVBO(surfaceVBO); + surfaceModel.setIBO(surfaceIBO); + + // Create model group + Model::Group* group = new Model::Group(); + group->name = "default"; + group->material = nullptr;//&surfaceMaterial; + group->indexOffset = 0; + group->triangleCount = surfaceTriangleCount; + + // Add group to the model + surfaceModel.addGroup(group); + + // Set model bounds + surfaceModel.setBounds(surfaceNavmesh.getBounds()); + */ +} + +Model* TerrainSystem::generateModel(TriangleMesh* mesh) +{ + std::size_t triangleCount = mesh->getTriangles()->size(); + // Vertex position + vertex normal + std::size_t vertexSize = 3 + 3; + std::size_t vertexCount = triangleCount * 3; + + #if defined(DEBUG) + const Vector3 barycentricCoordinates[3] = + { + Vector3(1, 0, 0), + Vector3(0, 1, 0), + Vector3(0, 0, 1) + }; + + vertexSize += 3; + #endif // DEBUG + + // Generate vertex data + float* vertexData = new float[vertexSize * vertexCount]; + float* v = vertexData; + for (std::size_t i = 0; i < triangleCount; ++i) + { + const TriangleMesh::Triangle* triangle = (*mesh->getTriangles())[i]; + + const TriangleMesh::Vertex* a = triangle->edge->vertex; + const TriangleMesh::Vertex* b = triangle->edge->next->vertex; + const TriangleMesh::Vertex* c = triangle->edge->previous->vertex; + const TriangleMesh::Vertex* abc[] = {a, b, c}; + const Vector3& normal = triangle->normal; + + + for (std::size_t j = 0; j < 3; ++j) + { + *(v++) = abc[j]->position[0]; + *(v++) = abc[j]->position[1]; + *(v++) = abc[j]->position[2]; + *(v++) = normal.x; + *(v++) = normal.y; + *(v++) = normal.z; + + #if defined(DEBUG) + { + *(v++) = barycentricCoordinates[j].x; + *(v++) = barycentricCoordinates[j].y; + *(v++) = barycentricCoordinates[j].z; + } + #endif // DEBUG + } + + // Calculate smoothed vertex normal + /* + Vector3 normal(0.0f); + TriangleMesh::Edge* start = vertex->edge; + TriangleMesh::Edge* e = start; + do + { + normal += e->triangle->normal; + e = e->previous->symmetric; + } + while (e != start && e != nullptr); + normal = glm::normalize(normal); + */ + } + + // Generate index data + std::size_t indexCount = triangleCount * 3; + std::uint32_t* indexData = new std::uint32_t[indexCount]; + std::uint32_t* index = indexData; + for (std::size_t i = 0; i < triangleCount; ++i) + { + *(index++) = i * 3; + *(index++) = i * 3 + 1; + *(index++) = i * 3 + 2; + } + + // Calculate AABB bounds + AABB bounds; + bounds.setMin(mesh->getVertices()->front()->position); + bounds.setMax(mesh->getVertices()->front()->position); + for (TriangleMesh::Vertex* vertex: *mesh->getVertices()) + { + bounds.add(vertex->position); + } + + GLuint vao; + GLuint vbo; + GLuint ibo; + + // Generate and bind VAO + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + // Generate and bind VBO, then upload vertex data + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertexCount, vertexData, GL_STATIC_DRAW); + + // Setup vertex attribute arrays + std::size_t attribOffset = 0; + std::size_t attribSize = 0; + + // Vertex position attribute + attribSize = 3; + glEnableVertexAttribArray(VERTEX_POSITION); + glVertexAttribPointer(VERTEX_POSITION, attribSize, GL_FLOAT, GL_FALSE, sizeof(float) * vertexSize, (char*)0 + attribOffset * sizeof(float)); + attribOffset += attribSize; + + // Vertex normal attribute + attribSize = 3; + glEnableVertexAttribArray(VERTEX_NORMAL); + glVertexAttribPointer(VERTEX_NORMAL, attribSize, GL_FLOAT, GL_FALSE, sizeof(float) * vertexSize, (char*)0 + attribOffset * sizeof(float)); + attribOffset += attribSize; + + #if defined(DEBUG) + { + // Vertex barycentric coordinates attribute + attribSize = 3; + glEnableVertexAttribArray(VERTEX_BARYCENTRIC); + glVertexAttribPointer(VERTEX_BARYCENTRIC, attribSize, GL_FLOAT, GL_FALSE, sizeof(float) * vertexSize, (char*)0 + attribOffset * sizeof(float)); + attribOffset += attribSize; + } + #endif // DEBUG + + // Generate and bind IBO, then upload index data + glGenBuffers(1, &ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::uint32_t) * indexCount, indexData, GL_STATIC_DRAW); + + // Delete vertex and index data + delete[] vertexData; + delete[] indexData; + + // Create model + Model* model = new Model(); + + // Set model buffers + model->setVAO(vao); + model->setVBO(vbo); + model->setIBO(ibo); + + // Set model bounds + model->setBounds(bounds); + + // Create model material + Material* material = new Material(); + material->setShader(nullptr); + ShaderVariable* albedo = material->addVariable("albedo"); + ShaderVariable* roughness = material->addVariable("roughness"); + ShaderVariable* metalness = material->addVariable("metalness"); + ShaderVariable* opacity = material->addVariable("opacity"); + albedo->setValue(Vector3(0.8f)); + roughness->setValue(0.5f); + metalness->setValue(0.0f); + opacity->setValue(1.0f); + + // Create model group + Model::Group* group = new Model::Group(); + group->name = std::string(); + group->material = material; + group->indexOffset = 0; + group->triangleCount = triangleCount; + + // Add model group to the model + model->addGroup(group); + + return model; +} + +void TerrainSystem::projectMesh(TriangleMesh* mesh) +{ + +} + diff --git a/src/entity/systems/terrain-system.hpp b/src/entity/systems/terrain-system.hpp new file mode 100644 index 0000000..211da3e --- /dev/null +++ b/src/entity/systems/terrain-system.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017-2019 Christopher J. Howard + * + * This file is part of Antkeeper Source Code. + * + * Antkeeper Source Code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper Source Code 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper Source Code. If not, see . + */ + +#ifndef TERRAIN_SYSTEM_HPP +#define TERRAIN_SYSTEM_HPP + +#include "../entity-group.hpp" +#include "../components/model-component.hpp" +#include "../components/terrain-patch-component.hpp" +#include "../components/transform-component.hpp" +#include "../system.hpp" +#include + +// The terrain system creates a model and adds the model component to new members of this group. +typedef EntityGroup TerrainCreationGroup; +typedef EntityGroup TerrainGroup; + +class TerrainSystem: + public System, + public TerrainCreationGroup::Observer, + public TerrainGroup::Observer +{ +public: + TerrainSystem(ComponentManager* componentManager); + virtual ~TerrainSystem(); + + virtual void update(float t, float dt); + +private: + virtual void memberRegistered(const TerrainCreationGroup::Member* member); + virtual void memberUnregistered(const TerrainCreationGroup::Member* member); + virtual void memberRegistered(const TerrainGroup::Member* member); + virtual void memberUnregistered(const TerrainGroup::Member* member); + + /** + * Generates a subdivided plane triangle mesh. + * + * 0 subdivisions: + * +---+ + * | / | + * +---+ + * + * 1 subdivision: + * +---+---+ + * | / | / | + * +---+---+ + * | / | / | + * +---+---+ + * + * 2 subdivisions: + * +---+---+---+---+ + * | / | / | / | / | + * +---+---+---+---+ + * | / | / | / | / | + * +---+---+---+---+ + * | / | / | / | / | + * +---+---+---+---+ + * | / | / | / | / | + * +---+---+---+---+ + * + * @param subdivisions Number of subdivisions. + * @return Generated triangle mesh. + */ + TriangleMesh* generatePlane(int subdivisions); + + /** + * Generates a model from a triangle mesh. + * + * @param mesh Triangle mesh from which a model will be generated. + * @return Generated model. + */ + Model* generateModel(TriangleMesh* mesh); + + /** + * Projects the vertices of a triangle mesh onto terrain brush geometry. + */ + void projectMesh(TriangleMesh* mesh); + + TerrainCreationGroup terrainCreationGroup; + TerrainGroup terrainGroup; + + std::map, const TerrainGroup::Member*> patchMap; + float patchSize; +}; + +#endif // TERRAIN_SYSTEM_HPP + diff --git a/src/game.cpp b/src/game.cpp index 30d265c..7706826 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -52,6 +52,7 @@ #include "entity/systems/behavior-system.hpp" #include "entity/systems/steering-system.hpp" #include "entity/systems/particle-system.hpp" +#include "entity/systems/terrain-system.hpp" #include "stb/stb_image_write.h" #include #include @@ -158,20 +159,30 @@ Game::Game(int argc, char* argv[]): currentState(nullptr), window(nullptr) { - // Get paths + // Form resource paths dataPath = getDataPath(); configPath = getConfigPath(); + controlsPath = configPath + "/controls/"; - // Create config path if it doesn't exist - if (!pathExists(configPath)) + // Create nonexistent config directories + std::vector configPaths; + configPaths.push_back(configPath); + configPaths.push_back(controlsPath); + for (const std::string& path: configPaths) { - createDirectory(configPath); + if (!pathExists(path)) + { + createDirectory(path); + } } // Setup resource manager resourceManager = new ResourceManager(); - resourceManager->include(dataPath); + + // Include resource search paths in order of priority + resourceManager->include(controlsPath); resourceManager->include(configPath); + resourceManager->include(dataPath); splashState = new SplashState(this); sandboxState = new SandboxState(this); @@ -251,6 +262,10 @@ void Game::setup() setupControls(); setupGameplay(); + #if defined(DEBUG) + toggleWireframe(); + #endif // DEBUG + screenshotQueued = false; @@ -318,6 +333,7 @@ void Game::setup() behaviorSystem = new BehaviorSystem(componentManager); steeringSystem = new SteeringSystem(componentManager); locomotionSystem = new LocomotionSystem(componentManager); + terrainSystem = new TerrainSystem(componentManager); particleSystem = new ParticleSystem(componentManager); particleSystem->resize(1000); particleSystem->setMaterial(smokeMaterial); @@ -335,10 +351,12 @@ void Game::setup() systemManager->addSystem(locomotionSystem); systemManager->addSystem(collisionSystem); systemManager->addSystem(toolSystem); + systemManager->addSystem(terrainSystem); systemManager->addSystem(particleSystem); systemManager->addSystem(cameraSystem); systemManager->addSystem(renderSystem); + /* EntityID sidewalkPanel; sidewalkPanel = createInstanceOf("sidewalk-panel"); @@ -351,6 +369,7 @@ void Game::setup() EntityID lollipop = createInstanceOf("lollipop"); setTranslation(lollipop, Vector3(30.0f, 3.5f * 0.5f, -30.0f)); setRotation(lollipop, glm::angleAxis(glm::radians(8.85f), Vector3(1.0f, 0.0f, 0.0f))); + */ // Load navmesh TriangleMesh* navmesh = resourceManager->load("sidewalk.mesh"); @@ -392,9 +411,11 @@ void Game::setup() } - EntityID tool0 = createInstanceOf("lens"); + //EntityID tool0 = createInstanceOf("lens"); + + EntityID patch0 = createInstanceOf("terrain-patch"); - changeState(splashState); + changeState(sandboxState); } void Game::update(float t, float dt) @@ -459,9 +480,7 @@ void Game::render() } void Game::exit() -{ - saveControlProfile(); -} +{} void Game::handleEvent(const WindowResizedEvent& event) { @@ -484,7 +503,7 @@ void Game::handleEvent(const WindowResizedEvent& event) void Game::handleEvent(const GamepadConnectedEvent& event) { // Unmap all controls - inputMapper->reset(); + inputRouter->reset(); // Reload control profile loadControlProfile(); @@ -668,7 +687,7 @@ void Game::setupGraphics() defaultCompositor.addPass(lightingPass); defaultCompositor.addPass(clearSilhouettePass); defaultCompositor.addPass(silhouettePass); - defaultCompositor.addPass(finalPass); + //defaultCompositor.addPass(finalPass); defaultCompositor.load(nullptr); // Setup UI render pass @@ -1281,40 +1300,12 @@ void Game::setupControls() // Load control profile loadControlProfile(); - - /* - controlProfile.registerControl("exit", &exitControl); - controlProfile.registerControl("toggle-fullscreen", &toggleFullscreenControl); - controlProfile.registerControl("open-tool-menu", &openToolMenuControl); - controlProfile.registerControl("move-forward", &moveForwardControl); - controlProfile.registerControl("move-back", &moveBackControl); - controlProfile.registerControl("move-left", &moveLeftControl); - controlProfile.registerControl("move-right", &moveRightControl); - controlProfile.registerControl("orbit-ccw", &orbitCCWControl); - controlProfile.registerControl("orbit-cw", &orbitCWControl); - controlProfile.registerControl("zoom-in", &zoomInControl); - controlProfile.registerControl("zoom-out", &zoomOutControl); - controlProfile.registerControl("adjust-camera", &adjustCameraControl); - controlProfile.registerControl("drag-camera", &dragCameraControl); - controlProfile.registerControl("toggle-wireframe", &toggleWireframeControl); - controlProfile.registerControl("screenshot", &screenshotControl); - controlProfile.registerControl("toggle-edit-mode", &toggleEditModeControl); - */ - - /* - // Save default control bindings - std::string bindingsPath = getConfigPath() + "/bindings/"; - if (!pathExists(bindingsPath)) - { - createDirectory(bindingsPath); - } - controlProfile.save(bindingsPath + "default.csv"); - - // Load control bindings - std::string controlProfileFilename = getConfigPath() + "/bindings/" + controlProfileName + ".csv"; - controlProfile.load(controlProfileFilename, deviceManager); - */ + // Setup input mapper + inputMapper = new InputMapper(&eventDispatcher); + inputMapper->setCallback(std::bind(&Game::mapInput, this, std::placeholders::_1)); + inputMapper->setControl(nullptr); + inputMapper->setEnabled(false); } void Game::setupGameplay() @@ -1332,7 +1323,6 @@ void Game::setupGameplay() orbitCam->attachCamera(&camera); freeCam = new FreeCam(); freeCam->attachCamera(&camera); - } void Game::resetSettings() @@ -1360,7 +1350,7 @@ void Game::resetSettings() fontSizePT = 14.0f; // Set default control profile name - controlProfileName = "default"; + controlProfileName = "default-controls"; } void Game::loadSettings() @@ -1413,7 +1403,7 @@ void Game::loadStrings() void Game::loadControlProfile() { // Load control profile - std::string controlProfilePath = "/controls/" + controlProfileName + ".csv"; + std::string controlProfilePath = controlProfileName + ".csv"; CSVTable* controlProfile = resourceManager->load(controlProfilePath); for (const CSVRow& row: *controlProfile) @@ -1451,7 +1441,7 @@ void Game::loadControlProfile() // Map control if (scancode != Scancode::UNKNOWN) { - inputMapper->map(control, keyboard, scancode); + inputRouter->addMapping(KeyMapping(control, keyboard, scancode)); } } else if (deviceType == "mouse") @@ -1480,7 +1470,7 @@ void Game::loadControlProfile() } // Map control - inputMapper->map(control, mouse, axis); + inputRouter->addMapping(MouseMotionMapping(control, mouse, axis)); } else if (eventType == "wheel") { @@ -1504,7 +1494,7 @@ void Game::loadControlProfile() } // Map control - inputMapper->map(control, mouse, axis); + inputRouter->addMapping(MouseWheelMapping(control, mouse, axis)); } else if (eventType == "button") { @@ -1517,7 +1507,7 @@ void Game::loadControlProfile() stream >> button; // Map control - inputMapper->map(control, mouse, button); + inputRouter->addMapping(MouseButtonMapping(control, mouse, button)); } else { @@ -1557,7 +1547,7 @@ void Game::loadControlProfile() const std::list* gamepads = deviceManager->getGamepads(); for (Gamepad* gamepad: *gamepads) { - inputMapper->map(control, gamepad, axis, negative); + inputRouter->addMapping(GamepadAxisMapping(control, gamepad, axis, negative)); } } else if (eventType == "button") @@ -1574,7 +1564,7 @@ void Game::loadControlProfile() const std::list* gamepads = deviceManager->getGamepads(); for (Gamepad* gamepad: *gamepads) { - inputMapper->map(control, gamepad, button); + inputRouter->addMapping(GamepadButtonMapping(control, gamepad, button)); } } else @@ -1589,28 +1579,6 @@ void Game::loadControlProfile() continue; } } - - // Map controls - /* - inputMapper->map(&exitControl, keyboard, Scancode::ESC); - inputMapper->map(&toggleFullscreenControl, keyboard, Scancode::F11); - inputMapper->map(&screenshotControl, keyboard, Scancode::F12); - inputMapper->map(&openToolMenuControl, keyboard, Scancode::LEFT_SHIFT); - inputMapper->map(&moveForwardControl, keyboard, Scancode::W); - inputMapper->map(&moveBackControl, keyboard, Scancode::S); - inputMapper->map(&moveLeftControl, keyboard, Scancode::A); - inputMapper->map(&moveRightControl, keyboard, Scancode::D); - inputMapper->map(&orbitCCWControl, keyboard, Scancode::Q); - inputMapper->map(&orbitCWControl, keyboard, Scancode::E); - inputMapper->map(&zoomInControl, mouse, MouseWheelAxis::POSITIVE_Y); - inputMapper->map(&zoomInControl, keyboard, Scancode::EQUAL); - inputMapper->map(&zoomOutControl, mouse, MouseWheelAxis::NEGATIVE_Y); - inputMapper->map(&zoomOutControl, keyboard, Scancode::MINUS); - inputMapper->map(&adjustCameraControl, mouse, 2); - inputMapper->map(&dragCameraControl, mouse, 3); - inputMapper->map(&toggleWireframeControl, keyboard, Scancode::V); - inputMapper->map(&toggleEditModeControl, keyboard, Scancode::TAB); - */ } void Game::saveControlProfile() @@ -1626,7 +1594,7 @@ void Game::saveControlProfile() Control* control = it->second; // Look up list of mappings for the control - const std::list* mappings = inputMapper->getMappings(control); + const std::list* mappings = inputRouter->getMappings(control); if (!mappings) { continue; @@ -1772,13 +1740,6 @@ void Game::saveControlProfile() } } } - - // Create controls directory if it doesn't exist - std::string controlsPath = getConfigPath() + "/controls/"; - if (!pathExists(controlsPath)) - { - createDirectory(controlsPath); - } // Form full path to control profile file std::string controlProfilePath = controlsPath + controlProfileName + ".csv"; @@ -2104,6 +2065,25 @@ void Game::screenshot() window->swapBuffers(); } +void Game::mapInput(const InputMapping& mapping) +{ + // Skip mouse motion events + if (mapping.getType() == InputMappingType::MOUSE_MOTION) + { + return; + } + + // Add input mapping to input router + if (mapping.control != nullptr) + { + inputRouter->addMapping(mapping); + } + + // Disable input mapping generation + inputMapper->setControl(nullptr); + inputMapper->setEnabled(false); +} + void Game::boxSelect(float x, float y, float w, float h) { boxSelectionContainer->setTranslation(Vector2(x, y)); @@ -2116,8 +2096,6 @@ void Game::boxSelect(float x, float y, float w, float h) boxSelectionContainer->setVisible(true); } - - void Game::fadeIn(float duration, const Vector3& color, std::function callback) { if (fadeInAnimation.isPlaying()) diff --git a/src/game.hpp b/src/game.hpp index d074bb6..f160a0f 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -62,6 +62,7 @@ class ToolSystem; class BehaviorSystem; class SteeringSystem; class LocomotionSystem; +class TerrainSystem; class ComponentBase; enum class ComponentType; typedef std::vector> CSVTable; @@ -155,8 +156,11 @@ private: void setTimeOfDay(float time); void toggleWireframe(); - void screenshot(); void queueScreenshot(); + void screenshot(); + + // Callback for the input mapper + void mapInput(const InputMapping& mapping); public: @@ -184,6 +188,7 @@ public: // Paths std::string dataPath; std::string configPath; + std::string controlsPath; // Settings CSVTable* settingsTable; @@ -252,6 +257,9 @@ public: // Map of control names std::map controlNameMap; + // Input mapper + InputMapper* inputMapper; + // Logic float time; float timestep; @@ -380,7 +388,6 @@ public: Lens* lens; Forceps* forceps; Brush* brush; - ParticleSystem* particleSystem; // ECS EntityManager* entityManager; @@ -394,6 +401,8 @@ public: BehaviorSystem* behaviorSystem; SteeringSystem* steeringSystem; LocomotionSystem* locomotionSystem; + ParticleSystem* particleSystem; + TerrainSystem* terrainSystem; bool screenshotQueued; diff --git a/src/resources/entity-template-loader.cpp b/src/resources/entity-template-loader.cpp index 2cf4a28..92488f3 100644 --- a/src/resources/entity-template-loader.cpp +++ b/src/resources/entity-template-loader.cpp @@ -23,6 +23,7 @@ #include "../entity/components/ant-hill-component.hpp" #include "../entity/components/collision-component.hpp" #include "../entity/components/model-component.hpp" +#include "../entity/components/terrain-patch-component.hpp" #include "../entity/components/tool-component.hpp" #include "../entity/components/transform-component.hpp" #include "../entity/entity-template.hpp" @@ -77,6 +78,30 @@ static ComponentBase* loadModelComponent(ResourceManager* resourceManager, const return component; } +static ComponentBase* loadTerrainPatchComponent(const std::vector& parameters) +{ + if (parameters.size() != 3) + { + throw std::runtime_error("loadTerrainPatchComponent(): Invalid parameter count."); + } + + std::tuple position; + std::stringstream stream; + for (std::size_t i = 1; i < parameters.size(); ++i) + { + stream << parameters[i]; + if (i < parameters.size() - 1) + stream << ' '; + } + stream >> std::get<0>(position); + stream >> std::get<1>(position); + + TerrainPatchComponent* component = new TerrainPatchComponent(); + component->position = position; + + return component; +} + static ComponentBase* loadToolComponent(const std::vector& parameters) { if (parameters.size() != 1) @@ -133,6 +158,7 @@ static ComponentBase* loadComponent(ResourceManager* resourceManager, const std: if (parameters[0] == "ant-hill") return loadAntHillComponent(parameters); if (parameters[0] == "collision") return loadCollisionComponent(resourceManager, parameters); if (parameters[0] == "model") return loadModelComponent(resourceManager, parameters); + if (parameters[0] == "terrain-patch") return loadTerrainPatchComponent(parameters); if (parameters[0] == "tool") return loadToolComponent(parameters); if (parameters[0] == "transform") return loadTransformComponent(parameters); diff --git a/src/resources/resource-manager.hpp b/src/resources/resource-manager.hpp index 4116d3d..33083c8 100644 --- a/src/resources/resource-manager.hpp +++ b/src/resources/resource-manager.hpp @@ -129,6 +129,7 @@ T* ResourceManager::load(const std::string& path) opened = true; data = ResourceLoader::load(this, &fs); fs.close(); + break; } if (!opened) diff --git a/src/states/sandbox-state.cpp b/src/states/sandbox-state.cpp index 9f76c3a..9384017 100755 --- a/src/states/sandbox-state.cpp +++ b/src/states/sandbox-state.cpp @@ -72,7 +72,7 @@ void SandboxState::enter() toolIndex = 0; game->selectTool(toolIndex); - //game->currentTool->setActive(false); + game->currentTool->setActive(false); //game->mouse->warp(game->window, game->w / 2, game->h / 2); zoom = 0.5f;