diff --git a/CMakeLists.txt b/CMakeLists.txt index 667e8b3..9eb2918 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,8 +121,12 @@ set(EXECUTABLE_SOURCES ${EXECUTABLE_SOURCE_DIR}/game/nest.cpp ${EXECUTABLE_SOURCE_DIR}/game/navmesh.hpp ${EXECUTABLE_SOURCE_DIR}/game/navmesh.cpp + ${EXECUTABLE_SOURCE_DIR}/game/pheromone.hpp ${EXECUTABLE_SOURCE_DIR}/game/pheromone.cpp - ${EXECUTABLE_SOURCE_DIR}/game/pheromone.cpp + ${EXECUTABLE_SOURCE_DIR}/game/level.hpp + ${EXECUTABLE_SOURCE_DIR}/game/level.cpp + ${EXECUTABLE_SOURCE_DIR}/game/biome.hpp + ${EXECUTABLE_SOURCE_DIR}/game/biome.cpp ${EXECUTABLE_SOURCE_DIR}/debug.hpp ${EXECUTABLE_SOURCE_DIR}/debug.cpp ${EXECUTABLE_SOURCE_DIR}/camera-controller.hpp diff --git a/data b/data index 935cc4c..8e81768 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 935cc4cf3090766c657eb1f11b6c26adb164a325 +Subproject commit 8e817687dd5f843360a2cd90be66ae0e31ae9d4b diff --git a/lib/emergent b/lib/emergent index af59998..14b8aeb 160000 --- a/lib/emergent +++ b/lib/emergent @@ -1 +1 @@ -Subproject commit af599983ef5650ef399fb30dc985a98f436b5db0 +Subproject commit 14b8aeb2bf524d6f55407df259bad3eee85ef95c diff --git a/src/application.cpp b/src/application.cpp index 65e7cfd..cc291e3 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -273,6 +273,11 @@ Application::Application(int argc, char* argv[]): std::cout << "success" << std::endl; } + // Clear screen to black + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + SDL_GL_SwapWindow(window); + // Get display DPI std::cout << "Getting DPI of display 0... "; if (SDL_GetDisplayDPI(0, &dpi, nullptr, nullptr) != 0) @@ -313,13 +318,13 @@ Application::Application(int argc, char* argv[]): menuControlProfile->registerControl("toggle_fullscreen", &toggleFullscreen); menuControlProfile->registerControl("escape", &escape); menuLeft.bindKey(keyboard, SDL_SCANCODE_LEFT); - menuLeft.bindKey(keyboard, SDL_SCANCODE_A); + //menuLeft.bindKey(keyboard, SDL_SCANCODE_A); menuRight.bindKey(keyboard, SDL_SCANCODE_RIGHT); - menuRight.bindKey(keyboard, SDL_SCANCODE_D); + //menuRight.bindKey(keyboard, SDL_SCANCODE_D); menuUp.bindKey(keyboard, SDL_SCANCODE_UP); - menuUp.bindKey(keyboard, SDL_SCANCODE_W); + //menuUp.bindKey(keyboard, SDL_SCANCODE_W); menuDown.bindKey(keyboard, SDL_SCANCODE_DOWN); - menuDown.bindKey(keyboard, SDL_SCANCODE_S); + //menuDown.bindKey(keyboard, SDL_SCANCODE_S); menuSelect.bindKey(keyboard, SDL_SCANCODE_RETURN); menuSelect.bindKey(keyboard, SDL_SCANCODE_SPACE); menuSelect.bindKey(keyboard, SDL_SCANCODE_Z); @@ -367,11 +372,6 @@ Application::Application(int argc, char* argv[]): titleState = new TitleState(this); experimentState = new ExperimentState(this); - // Clear screen to black - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - SDL_GL_SwapWindow(window); - // Setup loaders textureLoader = new TextureLoader(); materialLoader = new MaterialLoader(); diff --git a/src/application.hpp b/src/application.hpp index c15580e..120e244 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -25,6 +25,8 @@ using namespace Emergent; #include "mesh.hpp" #include "game/terrain.hpp" +#include "game/level.hpp" +#include "game/biome.hpp" #include "input.hpp" #include "controls.hpp" #include "settings.hpp" @@ -96,6 +98,7 @@ public: Compositor shadowCompositor; Camera sunlightCamera; RenderTarget defaultRenderTarget; + SoilRenderPass soilPass; LightingRenderPass lightingPass; DebugRenderPass debugPass; Compositor defaultCompositor; @@ -253,12 +256,13 @@ public: Menu* settingsMenu; // Models - Model* displayModel; Model* antModel; - ModelInstance* displayModelInstance; ModelInstance* antModelInstance; // Game variables + Campaign campaign; + Biosphere biosphere; + Colony* colony; SurfaceCameraController* surfaceCam; TunnelCameraController* tunnelCam; diff --git a/src/configuration.hpp.in b/src/configuration.hpp.in index 4bfd57b..358f276 100644 --- a/src/configuration.hpp.in +++ b/src/configuration.hpp.in @@ -26,4 +26,11 @@ #define ANTKEEPER_VERSION_STRING "@ANTKEEPER_VERSION@" #cmakedefine ANTKEEPER_DEBUG +#if defined(ANTKEEPER_DEBUG) + #define ANTKEEPER_FIRST_WORLD_INDEX 0 +#else + #define ANTKEEPER_FIRST_WORLD_INDEX 1 +#endif +#define ANTKEEPER_FIRST_LEVEL_INDEX 1 + #endif // CONFIGURATION_HPP diff --git a/src/game/biome.cpp b/src/game/biome.cpp new file mode 100644 index 0000000..7062387 --- /dev/null +++ b/src/game/biome.cpp @@ -0,0 +1,123 @@ +#include "biome.hpp" +#include "../settings.hpp" + +#if defined(_WIN32) || defined(__CYGWIN__) +#include "../windows-dirent.h" +#else +#include +#endif + +#include + + +Biome::Biome() +{} + +Biome::~Biome() +{} + +bool Biome::load() +{ + ParameterDict parameters; + if (!parameters.load(filename)) + { + return false; + } + + parameters.get("name", &name); + parameters.get("soil-horizon-o", &soilHorizonOFilename); + parameters.get("soil-horizon-a", &soilHorizonAFilename); + parameters.get("soil-horizon-b", &soilHorizonBFilename); + parameters.get("soil-horizon-c", &soilHorizonCFilename); + parameters.get("cubemap", &cubemapName); + + TextureLoader textureLoader; + textureLoader.setCubemap(false); + textureLoader.setMipmapChain(false); + + // Load soil horizon textures + soilHorizonO = textureLoader.load(std::string("data/textures/") + soilHorizonOFilename); + soilHorizonA = textureLoader.load(std::string("data/textures/") + soilHorizonAFilename); + soilHorizonB = textureLoader.load(std::string("data/textures/") + soilHorizonBFilename); + soilHorizonC = textureLoader.load(std::string("data/textures/") + soilHorizonCFilename); + + // Load diffuse cubemap + textureLoader.setCubemap(true); + textureLoader.setMipmapChain(false); + std::string diffuseCubemapFilename = std::string("data/textures/") + cubemapName + std::string("-diffuse.png"); + diffuseCubemap = textureLoader.load(diffuseCubemapFilename); + if (!diffuseCubemap) + { + std::cerr << "Failed to load diffuse cubemap \"" << diffuseCubemapFilename << "\"" << std::endl; + } + + // Load specular cubemap + textureLoader.setCubemap(true); + textureLoader.setMipmapChain(true); + std::string specularCubemapFilename = std::string("data/textures/") + cubemapName + std::string("-specular_m%02d.png"); + specularCubemap = textureLoader.load(specularCubemapFilename); + if (!specularCubemap) + { + std::cerr << "Failed to load specular cubemap \"" << specularCubemapFilename << "\"" << std::endl; + } + + return true; +} + +bool Biosphere::load(const std::string& directory) +{ + // Open biomes directory + DIR* dir = opendir(directory.c_str()); + if (dir == nullptr) + { + std::cout << "Failed to open biome directory \"" << directory << "\"" << std::endl; + return false; + } + + // Scan directory for .bio files + for (struct dirent* entry = readdir(dir); entry != nullptr; entry = readdir(dir)) + { + if (entry->d_type == DT_DIR || *entry->d_name == '.') + { + continue; + } + + std::string filename = entry->d_name; + std::string::size_type delimeter = filename.find_last_of('.'); + if (delimeter == std::string::npos) + { + continue; + } + + std::string extension = filename.substr(delimeter + 1); + if (extension != "bio") + { + continue; + } + + // Add biome + std::string name = filename.substr(0, delimeter); + Biome* biome = &biomes[name]; + biome->filename = directory + filename; + } + + // Close biomes directory + closedir(dir); + + // Load biomes + for (std::map::iterator it = biomes.begin(); it != biomes.end(); ++it) + { + Biome* biome = &it->second; + + if (!biome->load()) + { + std::cout << "Failed to load biome \"" << biome->filename << "\"" << std::endl; + } + else + { + std::cout << "Loaded biome \"" << biome->filename << "\"" << std::endl; + } + } + + return true; +} diff --git a/src/game/biome.hpp b/src/game/biome.hpp new file mode 100644 index 0000000..c85d0b0 --- /dev/null +++ b/src/game/biome.hpp @@ -0,0 +1,42 @@ +#ifndef BIOME_HPP +#define BIOME_HPP + +#include +#include +#include + +using namespace Emergent; + +class Biome +{ +public: + Biome(); + ~Biome(); + + bool load(); + + std::string filename; + std::string name; + std::string soilHorizonOFilename; + std::string soilHorizonAFilename; + std::string soilHorizonBFilename; + std::string soilHorizonCFilename; + std::string cubemapName; + + Texture* soilHorizonO; + Texture* soilHorizonA; + Texture* soilHorizonB; + Texture* soilHorizonC; + Texture* diffuseCubemap; + Texture* specularCubemap; +}; + +class Biosphere +{ +public: + bool load(const std::string& directory); + + std::map biomes; +}; + +#endif // BIOME_HPP diff --git a/src/game/level.cpp b/src/game/level.cpp new file mode 100644 index 0000000..721823b --- /dev/null +++ b/src/game/level.cpp @@ -0,0 +1,133 @@ +#include "level.hpp" +#include "../settings.hpp" + +#if defined(_WIN32) || defined(__CYGWIN__) +#include "../windows-dirent.h" +#else +#include +#endif + +#include +#include + +Level::Level(): + worldIndex(-1), + levelIndex(-1) +{} + +Level::~Level() +{} + +bool Level::load() +{ + ParameterDict parameters; + if (!parameters.load(filename)) + { + return false; + } + + parameters.get("biome", &biome); + parameters.get("heightmap", &heightmap); + + return true; +} + +Campaign::Campaign() +{} + +Campaign::~Campaign() +{} + +bool Campaign::load(const std::string& directory) +{ + // Open levels directory + DIR* dir = opendir(directory.c_str()); + if (dir == nullptr) + { + std::cout << "Failed to open levels directory \"" << directory << "\"" << std::endl; + return false; + } + + // Scan directory for .lvl files + for (struct dirent* entry = readdir(dir); entry != nullptr; entry = readdir(dir)) + { + if (entry->d_type == DT_DIR || *entry->d_name == '.') + { + continue; + } + + std::string filename = entry->d_name; + std::string::size_type delimeter = filename.find_last_of('.'); + if (delimeter == std::string::npos) + { + continue; + } + + std::string extension = filename.substr(delimeter + 1); + if (extension != "lvl") + { + continue; + } + + std::string worldIndexString = filename.substr(0, 2); + std::string levelIndexString = filename.substr(3, 2); + int worldIndex = -1; + int levelIndex = -1; + + std::stringstream stream; + stream << worldIndexString; + stream >> worldIndex; + + stream.str(std::string()); + stream.clear(); + stream << levelIndexString; + stream >> levelIndex; + + if (worldIndex < 0 || levelIndex < 0) + { + std::cout << "Invalid level \"" << filename << "\"" << std::endl; + continue; + } + + // Resize vector to accommodate maximum world index + if (worldIndex >= static_cast(levels.size())) + { + levels.resize(worldIndex + 1); + } + + // Resize vector to accommodate maximum level index + if (levelIndex >= static_cast(levels[worldIndex].size())) + { + levels[worldIndex].resize(levelIndex + 1); + } + + // Add level + Level* level = &levels[worldIndex][levelIndex]; + level->filename = directory + filename; + level->worldIndex = worldIndex; + level->levelIndex = levelIndex; + } + + // Close levels directory + closedir(dir); + + // Load levels + for (std::size_t i = ANTKEEPER_FIRST_WORLD_INDEX; i < levels.size(); ++i) + { + for (std::size_t j = ANTKEEPER_FIRST_LEVEL_INDEX; j < levels[i].size(); ++j) + { + Level* level = &levels[i][j]; + + if (!level->load()) + { + std::cout << "Failed to load level " << i << "-" << j << std::endl; + } + else + { + std::cout << "Loaded level " << i << "-" << j << std::endl; + } + } + } + + return true; +} \ No newline at end of file diff --git a/src/game/level.hpp b/src/game/level.hpp new file mode 100644 index 0000000..906d4cb --- /dev/null +++ b/src/game/level.hpp @@ -0,0 +1,35 @@ +#ifndef LEVEL_SELECTOR_HPP +#define LEVEL_SELECTOR_HPP + +#include "../configuration.hpp" +#include +#include + +class Level +{ +public: + Level(); + ~Level(); + + bool load(); + + std::string filename; + int worldIndex; + int levelIndex; + + std::string biome; + std::string heightmap; +}; + +class Campaign +{ +public: + Campaign(); + ~Campaign(); + + bool load(const std::string& directory); + + std::vector> levels; +}; + +#endif // LEVEL_SELECTOR_HPP diff --git a/src/game/navmesh.hpp b/src/game/navmesh.hpp index 2679643..c1f0f55 100644 --- a/src/game/navmesh.hpp +++ b/src/game/navmesh.hpp @@ -181,12 +181,12 @@ public: Edge* edge; }; -private: /** * Calculates the faceted surface normals for each triangle. */ void calculateNormals(); +private: /** * Reads Wavefront OBJ data from an input stream * diff --git a/src/game/terrain.cpp b/src/game/terrain.cpp index f6b1be6..87c5b69 100644 --- a/src/game/terrain.cpp +++ b/src/game/terrain.cpp @@ -12,12 +12,14 @@ void Terrain::create(int columns, int rows, const Vector3& dimensions) void Terrain::createSurface() { - std::size_t vertexCount = (columns + 1) * (rows + 1); - std::size_t triangleCount = columns * rows * 2; - std::size_t indexCount = triangleCount * 3; - - surfaceVertices.resize(vertexCount); - surfaceIndices.resize(indexCount); + surfaceVertexSize = 3 + 3 + 2; + surfaceVertexCount = (columns + 1) * (rows + 1); + surfaceTriangleCount = columns * rows * 2; + surfaceIndexCount = surfaceTriangleCount * 3; + surfaceVertexData = new float[surfaceVertexSize * surfaceVertexCount]; + surfaceIndexData = new std::uint32_t[surfaceIndexCount]; + surfaceVertices.resize(surfaceVertexCount); + surfaceIndices.resize(surfaceIndexCount); // Calculate scale and offset Vector2 scale(dimensions.x / (float)columns, dimensions.z / (float)rows); @@ -29,11 +31,21 @@ void Terrain::createSurface() for (int j = 0; j <= columns; ++j) { std::size_t index = i * (columns + 1) + j; - Vector3* vertex = &surfaceVertices[index]; + Vector3* vertex = &surfaceVertices[index]; vertex->x = (float)j * scale.x + offset.x; - vertex->y = rand()%10; + vertex->y = 0.0f; vertex->z = (float)i * scale.y + offset.y; + + float* data = &surfaceVertexData[index * surfaceVertexSize]; + *(data++) = vertex->x; + *(data++) = vertex->y; + *(data++) = vertex->z; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = 0.0f; + *(data++) = static_cast(j) / static_cast(columns) * 2.0f; + *(data++) = static_cast(i) / static_cast(rows) * 2.0f; } } @@ -42,42 +54,90 @@ void Terrain::createSurface() { for (int j = 0; j < columns; ++j) { - std::size_t a = i * (columns + 1) + j; - std::size_t b = (i + 1) * (columns + 1) + j; - std::size_t c = i * (columns + 1) + j + 1; - std::size_t d = (i + 1) * (columns + 1) + j + 1; + 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; std::size_t index = (i * columns + j) * 2 * 3; - surfaceIndices[index] = a; - surfaceIndices[index + 1] = b; - surfaceIndices[index + 2] = c; - surfaceIndices[index + 3] = c; - surfaceIndices[index + 4] = b; - surfaceIndices[index + 5] = d; + surfaceIndices[index++] = a; + surfaceIndices[index++] = b; + surfaceIndices[index++] = c; + surfaceIndices[index++] = c; + surfaceIndices[index++] = b; + surfaceIndices[index] = d; } } - // Create winged-edge mesh - surfaceMesh.create(surfaceVertices, surfaceIndices); + // Generate index data + for (std::size_t i = 0; i < surfaceIndexCount; ++i) + { + surfaceIndexData[i] = surfaceIndices[i]; + } + + // 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 = &surfaceMaterial; + group->indexOffset = 0; + group->triangleCount = surfaceTriangleCount; - // Create model - surfaceModel.create(&surfaceMesh); + // Add group to the model + surfaceModel.addGroup(group); } void Terrain::createSubsurface() { - std::size_t vertexCount = (columns + 1) * 4 + (rows + 1) * 4; - std::size_t triangleCount = columns * 4 + rows * 4 + 2; - std::size_t indexCount = triangleCount * 3; - - subsurfaceVertices.resize(vertexCount); - subsurfaceIndices.resize(indexCount); + subsurfaceVertexSize = 3 + 3 + 2; + subsurfaceVertexCount = (columns + 1) * 4 + (rows + 1) * 4; + subsurfaceTriangleCount = columns * 4 + rows * 4 + 2; + subsurfaceIndexCount = subsurfaceTriangleCount * 3; + subsurfaceVertexData = new float[subsurfaceVertexSize * subsurfaceVertexCount]; + subsurfaceIndexData = new std::uint32_t[subsurfaceIndexCount]; + subsurfaceVertices.resize(subsurfaceVertexCount); + subsurfaceIndices.resize(subsurfaceIndexCount); + float maxDimension = dimensions.y; + float textureScaleX = dimensions.x / maxDimension; + float textureScaleY = dimensions.y / maxDimension; + float textureScaleZ = dimensions.z / maxDimension; + // Calculate floor position float subsurfaceFloor = -dimensions.y; // Calculate vertex positions Vector3* vertex = &subsurfaceVertices[0]; + float* data = &subsurfaceVertexData[0]; // Top row for (int j = 0; j <= columns; ++j) @@ -85,9 +145,27 @@ void Terrain::createSubsurface() int i = 0; std::size_t surfaceIndex = i * (columns + 1) + j; const Vector3& surfaceVertex = surfaceVertices[surfaceIndex]; + float u = 1.0f - (static_cast(j) / static_cast(columns)) * textureScaleX; *(vertex++) = surfaceVertex; + *(data++) = surfaceVertex.x; + *(data++) = surfaceVertex.y; + *(data++) = surfaceVertex.z; + *(data++) = 0.0f; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = u; + *(data++) = 0.0f; + *(vertex++) = Vector3(surfaceVertex.x, subsurfaceFloor, surfaceVertex.z); + *(data++) = surfaceVertex.x; + *(data++) = subsurfaceFloor; + *(data++) = surfaceVertex.z; + *(data++) = 0.0f; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = u; + *(data++) = textureScaleY; } // Bottom row @@ -96,9 +174,27 @@ void Terrain::createSubsurface() int i = rows; std::size_t surfaceIndex = i * (columns + 1) + j; const Vector3& surfaceVertex = surfaceVertices[surfaceIndex]; + float u = (static_cast(j) / static_cast(columns)) * textureScaleX; *(vertex++) = surfaceVertex; + *(data++) = surfaceVertex.x; + *(data++) = surfaceVertex.y; + *(data++) = surfaceVertex.z; + *(data++) = 0.0f; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = u; + *(data++) = 0.0f; + *(vertex++) = Vector3(surfaceVertex.x, subsurfaceFloor, surfaceVertex.z); + *(data++) = surfaceVertex.x; + *(data++) = subsurfaceFloor; + *(data++) = surfaceVertex.z; + *(data++) = 0.0f; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = u; + *(data++) = textureScaleY; } // Left column @@ -107,9 +203,27 @@ void Terrain::createSubsurface() int j = 0; std::size_t surfaceIndex = i * (columns + 1) + j; const Vector3& surfaceVertex = surfaceVertices[surfaceIndex]; + float u = (static_cast(i) / static_cast(rows)) * textureScaleZ; *(vertex++) = surfaceVertex; + *(data++) = surfaceVertex.x; + *(data++) = surfaceVertex.y; + *(data++) = surfaceVertex.z; + *(data++) = 0.0f; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = u; + *(data++) = 0.0f; + *(vertex++) = Vector3(surfaceVertex.x, subsurfaceFloor, surfaceVertex.z); + *(data++) = surfaceVertex.x; + *(data++) = subsurfaceFloor; + *(data++) = surfaceVertex.z; + *(data++) = 0.0f; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = u; + *(data++) = textureScaleY; } // Right column @@ -118,9 +232,27 @@ void Terrain::createSubsurface() int j = columns; std::size_t surfaceIndex = i * (columns + 1) + j; const Vector3& surfaceVertex = surfaceVertices[surfaceIndex]; - + float u = 1.0f - (static_cast(i) / static_cast(rows)) * textureScaleZ; + *(vertex++) = surfaceVertex; + *(data++) = surfaceVertex.x; + *(data++) = surfaceVertex.y; + *(data++) = surfaceVertex.z; + *(data++) = 0.0f; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = u; + *(data++) = 0.0f; + *(vertex++) = Vector3(surfaceVertex.x, subsurfaceFloor, surfaceVertex.z); + *(data++) = surfaceVertex.x; + *(data++) = subsurfaceFloor; + *(data++) = surfaceVertex.z; + *(data++) = 0.0f; + *(data++) = 0.0f; + *(data++) = 1.0f; + *(data++) = u; + *(data++) = textureScaleY; } // Generate indices @@ -192,11 +324,176 @@ void Terrain::createSubsurface() (*(index++)) = c; (*(index++)) = d; - // Create winged-edge mesh - subsurfaceMesh.create(subsurfaceVertices, subsurfaceIndices); + // Generate index data + for (std::size_t i = 0; i < subsurfaceIndexCount; ++i) + { + subsurfaceIndexData[i] = subsurfaceIndices[i]; + } + + // Generate navmesh + subsurfaceNavmesh.create(subsurfaceVertices, subsurfaceIndices); + + // Create and load VAO, VBO, and IBO + glGenVertexArrays(1, &subsurfaceVAO); + glBindVertexArray(subsurfaceVAO); + glGenBuffers(1, &subsurfaceVBO); + glBindBuffer(GL_ARRAY_BUFFER, subsurfaceVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * subsurfaceVertexSize * subsurfaceVertexCount, subsurfaceVertexData, GL_STATIC_DRAW); + glEnableVertexAttribArray(EMERGENT_VERTEX_POSITION); + glVertexAttribPointer(EMERGENT_VERTEX_POSITION, 3, GL_FLOAT, GL_FALSE, subsurfaceVertexSize * sizeof(float), (char*)0 + 0 * sizeof(float)); + glEnableVertexAttribArray(EMERGENT_VERTEX_NORMAL); + glVertexAttribPointer(EMERGENT_VERTEX_NORMAL, 3, GL_FLOAT, GL_FALSE, subsurfaceVertexSize * sizeof(float), (char*)0 + 3 * sizeof(float)); + glEnableVertexAttribArray(EMERGENT_VERTEX_TEXCOORD); + glVertexAttribPointer(EMERGENT_VERTEX_TEXCOORD, 2, GL_FLOAT, GL_FALSE, subsurfaceVertexSize * sizeof(float), (char*)0 + 6 * sizeof(float)); + glGenBuffers(1, &subsurfaceIBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, subsurfaceIBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::uint32_t) * subsurfaceIndexCount, subsurfaceIndexData, GL_STATIC_DRAW); + + // Setup material + subsurfaceMaterial.flags = static_cast(PhysicalMaterial::Flags::SOIL); + + // Setup buffers + subsurfaceModel.setVAO(subsurfaceVAO); + subsurfaceModel.setVBO(subsurfaceVBO); + subsurfaceModel.setIBO(subsurfaceIBO); + + // Create model group + Model::Group* group = new Model::Group(); + group->name = "default"; + group->material = &subsurfaceMaterial; + group->indexOffset = 0; + group->triangleCount = subsurfaceTriangleCount; + + // Add group to the model + subsurfaceModel.addGroup(group); +} + +void Terrain::calculateSurfaceNormals() +{ + for (std::size_t i = 0; i < surfaceVertexCount; ++i) + { + const Navmesh::Vertex* vertex = (*surfaceNavmesh.getVertices())[i]; + + Vector3 normal(0.0f); + const Navmesh::Edge* start = vertex->edge; + const Navmesh::Edge* e = start; + do + { + normal += e->triangle->normal; + e = e->previous->symmetric; + } + while (e != start && e != nullptr); + normal = glm::normalize(normal); + + float* data = &surfaceVertexData[i * surfaceVertexSize]; + data[3] = normal.x; + data[4] = normal.y; + data[5] = normal.z; + } +} + +bool Terrain::load(const std::string& filename) +{ + int width; + int height; + int channels; + + stbi_set_flip_vertically_on_load(true); + + // Load image data + unsigned char* pixels = stbi_load(filename.c_str(), &width, &height, &channels, 1); + + if (width != columns + 1 || height != rows + 1) + { + // Free loaded image data + stbi_image_free(pixels); + return false; + } + + // Set surface vertex heights + for (int i = 0; i <= rows; ++i) + { + for (int j = 0; j <= columns; ++j) + { + std::size_t index = i * (columns + 1) + j; + + float elevation = (float)pixels[index] / 255.0f * 5.0f; + + surfaceVertexData[index * surfaceVertexSize + 1] = elevation; + surfaceVertices[index].y = elevation; + (*surfaceNavmesh.getVertices())[index]->position.y = elevation; + } + } + + // Free loaded image data + stbi_image_free(pixels); + + // Set subsurface vertex heights + std::size_t subsurfaceIndex = 0; + + // Top row + for (int j = 0; j <= columns; ++j) + { + int i = 0; + std::size_t surfaceIndex = i * (columns + 1) + j; + float elevation = surfaceVertices[surfaceIndex].y; + + subsurfaceVertexData[subsurfaceIndex * subsurfaceVertexSize + 1] = elevation; + subsurfaceVertices[subsurfaceIndex].y = elevation; + (*subsurfaceNavmesh.getVertices())[subsurfaceIndex]->position.y = elevation; + subsurfaceIndex += 2; + } + // Bottom row + for (int j = 0; j <= columns; ++j) + { + int i = rows; + std::size_t surfaceIndex = i * (columns + 1) + j; + float elevation = surfaceVertices[surfaceIndex].y; + + subsurfaceVertexData[subsurfaceIndex * subsurfaceVertexSize + 1] = elevation; + subsurfaceVertices[subsurfaceIndex].y = elevation; + (*subsurfaceNavmesh.getVertices())[subsurfaceIndex]->position.y = elevation; + subsurfaceIndex += 2; + } + // Left column + for (int i = 0; i <= rows; ++i) + { + int j = 0; + std::size_t surfaceIndex = i * (columns + 1) + j; + float elevation = surfaceVertices[surfaceIndex].y; + + subsurfaceVertexData[subsurfaceIndex * subsurfaceVertexSize + 1] = elevation; + subsurfaceVertices[subsurfaceIndex].y = elevation; + (*subsurfaceNavmesh.getVertices())[subsurfaceIndex]->position.y = elevation; + subsurfaceIndex += 2; + } + // Right column + for (int i = 0; i <= rows; ++i) + { + int j = columns; + std::size_t surfaceIndex = i * (columns + 1) + j; + float elevation = surfaceVertices[surfaceIndex].y; + + subsurfaceVertexData[subsurfaceIndex * subsurfaceVertexSize + 1] = elevation; + subsurfaceVertices[subsurfaceIndex].y = elevation; + (*subsurfaceNavmesh.getVertices())[subsurfaceIndex]->position.y = elevation; + subsurfaceIndex += 2; + } + + // Calculate navmesh normals + surfaceNavmesh.calculateNormals(); + subsurfaceNavmesh.calculateNormals(); + + // Calculate vertex normals + calculateSurfaceNormals(); + + // Update VBOs + glBindBuffer(GL_ARRAY_BUFFER, surfaceVBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, surfaceVertexCount * surfaceVertexSize * sizeof(float), surfaceVertexData); + glBindBuffer(GL_ARRAY_BUFFER, subsurfaceVBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, subsurfaceVertexCount * subsurfaceVertexSize * sizeof(float), subsurfaceVertexData); - // Create model - subsurfaceModel.create(&subsurfaceMesh); + return true; } struct voxel diff --git a/src/game/terrain.hpp b/src/game/terrain.hpp index 97ae09f..d5784d6 100644 --- a/src/game/terrain.hpp +++ b/src/game/terrain.hpp @@ -20,6 +20,8 @@ #ifndef TERRAIN_HPP #define TERRAIN_HPP +#include "navmesh.hpp" +#include "../materials.hpp" #include using namespace Emergent; @@ -36,43 +38,94 @@ public: */ void create(int columns, int rows, const Vector3& dimensions); - /// Returns the winged-edge mesh representing the terrain surface. - const WingedEdge* getSurfaceMesh() const; + /// Loads a heightmap + bool load(const std::string& filename); - /// Returns the winged-edge mesh representing the terrain subsurface. - const WingedEdge* getSubsurfaceMesh() const; + /// Returns the navmesh representing the terrain surface. + const Navmesh* getSurfaceNavmesh() const; + + /// Returns the navmesh representing the terrain surface. + Navmesh* getSurfaceNavmesh(); + + /// Returns the navmesh representing the terrain subsurface. + const Navmesh* getSubsurfaceNavmesh() const; + + /// Returns the navmesh representing the terrain subsurface. + Navmesh* getSubsurfaceNavmesh(); /// Returns the model representing the terrain surface. const Model* getSurfaceModel() const; + /// Returns the model representing the terrain surface. + Model* getSurfaceModel(); + /// Returns the model representing the terrain subsurface. const Model* getSubsurfaceModel() const; + /// Returns the model representing the terrain subsurface. + Model* getSubsurfaceModel(); + private: void createSurface(); void createSubsurface(); + void calculateSurfaceNormals(); + int columns; int rows; Vector3 dimensions; + + // Surface + std::size_t surfaceVertexSize; + std::size_t surfaceVertexCount; + std::size_t surfaceTriangleCount; + std::size_t surfaceIndexCount; + float* surfaceVertexData; + std::uint32_t* surfaceIndexData; std::vector surfaceVertices; - std::vector subsurfaceVertices; std::vector surfaceIndices; - std::vector subsurfaceIndices; - WingedEdge surfaceMesh; - WingedEdge subsurfaceMesh; + GLuint surfaceVAO; + GLuint surfaceVBO; + GLuint surfaceIBO; + PhysicalMaterial surfaceMaterial; Model surfaceModel; + Navmesh surfaceNavmesh; + + // Subsurface + std::size_t subsurfaceVertexSize; + std::size_t subsurfaceVertexCount; + std::size_t subsurfaceTriangleCount; + std::size_t subsurfaceIndexCount; + float* subsurfaceVertexData; + std::uint32_t* subsurfaceIndexData; + std::vector subsurfaceVertices; + std::vector subsurfaceIndices; + GLuint subsurfaceVAO; + GLuint subsurfaceVBO; + GLuint subsurfaceIBO; + PhysicalMaterial subsurfaceMaterial; Model subsurfaceModel; + Navmesh subsurfaceNavmesh; }; -inline const WingedEdge* Terrain::getSurfaceMesh() const +inline Navmesh* Terrain::getSurfaceNavmesh() { - return &surfaceMesh; + return &surfaceNavmesh; }; -inline const WingedEdge* Terrain::getSubsurfaceMesh() const +inline const Navmesh* Terrain::getSurfaceNavmesh() const { - return &subsurfaceMesh; + return &surfaceNavmesh; +}; + +inline Navmesh* Terrain::getSubsurfaceNavmesh() +{ + return &subsurfaceNavmesh; +}; + +inline const Navmesh* Terrain::getSubsurfaceNavmesh() const +{ + return &subsurfaceNavmesh; }; inline const Model* Terrain::getSurfaceModel() const @@ -80,9 +133,19 @@ inline const Model* Terrain::getSurfaceModel() const return &surfaceModel; } +inline Model* Terrain::getSurfaceModel() +{ + return &surfaceModel; +} + inline const Model* Terrain::getSubsurfaceModel() const { return &subsurfaceModel; } +inline Model* Terrain::getSubsurfaceModel() +{ + return &subsurfaceModel; +} + #endif // TERRAIN_HPP diff --git a/src/materials.hpp b/src/materials.hpp index 00f67dd..c0acfbe 100644 --- a/src/materials.hpp +++ b/src/materials.hpp @@ -50,23 +50,30 @@ inline unsigned int UIMaterial::getMaterialFormatID() const class PhysicalMaterial: public Material { public: + enum class Flags + { + OBJECT = 0x01, + TERRAIN = 0x02, + SOIL = 0x04, + }; + PhysicalMaterial(): albedoOpacityMap(nullptr), metalnessRoughnessMap(nullptr), - normalOcclusionMap(nullptr) + normalOcclusionMap(nullptr), + flags((unsigned int)Flags::OBJECT) {}; virtual ~PhysicalMaterial() {}; virtual unsigned int getMaterialFormatID() const; + unsigned int flags; Vector3 albedo; float opacity; float metalness; float roughness; - Texture* albedoOpacityMap; Texture* metalnessRoughnessMap; Texture* normalOcclusionMap; - bool shadowCaster; bool shadowReceiver; }; diff --git a/src/render-passes.cpp b/src/render-passes.cpp index 9a65be3..a0441b3 100644 --- a/src/render-passes.cpp +++ b/src/render-passes.cpp @@ -187,36 +187,37 @@ void ClippingRenderPass::setClippingPlane(const Plane& plane) this->clippingPlane = Vector4(plane.getNormal(), plane.getDistance()); } -CappingRenderPass::CappingRenderPass(): +SoilRenderPass::SoilRenderPass(): shader(nullptr) { horizonTexturesParam = parameterSet.addParameter("horizonTextures", ShaderParameter::Type::INT, 4); modelParam = parameterSet.addParameter("modelMatrix", ShaderParameter::Type::MATRIX_4, 1); modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1); + + horizonOTexture = nullptr; + horizonATexture = nullptr; + horizonBTexture = nullptr; + horizonCTexture = nullptr; } -bool CappingRenderPass::load(const RenderContext* renderContext) +bool SoilRenderPass::load(const RenderContext* renderContext) { - shaderLoader.undefine(); - shaderLoader.define("TEXTURE_COUNT", 4); - + shaderLoader.define("VERTEX_POSITION", EMERGENT_VERTEX_POSITION); + shaderLoader.define("VERTEX_TEXCOORD", EMERGENT_VERTEX_TEXCOORD); + shaderLoader.define("VERTEX_NORMAL", EMERGENT_VERTEX_NORMAL); shader = shaderLoader.load("data/shaders/soil-profile.glsl", ¶meterSet); if (!shader) { return false; } - TextureLoader textureLoader; - horizonOTexture = textureLoader.load("data/textures/horizon-o.png"); - horizonATexture = textureLoader.load("data/textures/horizon-a.png"); - horizonBTexture = textureLoader.load("data/textures/horizon-b.png"); - horizonCTexture = textureLoader.load("data/textures/horizon-c.png"); - return true; } -void CappingRenderPass::unload() +void SoilRenderPass::unload() { + shaderLoader.undefine(); + delete shader; shader = nullptr; @@ -231,16 +232,12 @@ void CappingRenderPass::unload() horizonCTexture = nullptr; } -void CappingRenderPass::render(const RenderContext* renderContext) +void SoilRenderPass::render(const RenderContext* renderContext) { - glStencilFunc(GL_NOTEQUAL, 0, ~0); - glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO); - //glDepthMask(GL_FALSE); - // Bind shader shader->bind(); - // Bind texture + // Bind textures glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, horizonOTexture->getTextureID()); glActiveTexture(GL_TEXTURE1); @@ -254,12 +251,32 @@ void CappingRenderPass::render(const RenderContext* renderContext) int textureUnits[] = {0, 1, 2, 3}; shader->setParameter(horizonTexturesParam, 0, &textureUnits[0], 4); + // Enable depth testing + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LEQUAL); + + // Enable backface culling + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + const Camera& camera = *(renderContext->camera); const std::list* operations = renderContext->queue->getOperations(); // Render operations for (const RenderOperation& operation: *operations) - { + { + // Skip render operations with unsupported materials + if (operation.material->getMaterialFormatID() != static_cast(MaterialFormat::PHYSICAL)) + { + continue; + } + const PhysicalMaterial* material = static_cast(operation.material); + if (!(material->flags & (unsigned int)PhysicalMaterial::Flags::SOIL)) + { + continue; + } + const Matrix4& modelMatrix = operation.transform; Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix; shader->setParameter(modelParam, modelMatrix); @@ -268,9 +285,6 @@ void CappingRenderPass::render(const RenderContext* renderContext) glBindVertexArray(operation.vao); glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset); } - - //glDepthMask(GL_TRUE); - glDisable(GL_STENCIL_TEST); } LightingRenderPass::LightingRenderPass(): @@ -305,18 +319,6 @@ LightingRenderPass::LightingRenderPass(): bool LightingRenderPass::load(const RenderContext* renderContext) { - // For each render operation - /* - if (renderContext != nullptr) - { - const std::list* operations = renderContext->queue->getOperations(); - for (const RenderOperation& operation: *operations) - { - loadShader(operation); - } - } - */ - // Load tree shadow TextureLoader textureLoader; treeShadow = textureLoader.load("data/textures/tree-shadow-0.png"); @@ -341,34 +343,6 @@ bool LightingRenderPass::load(const RenderContext* renderContext) { std::cerr << "Failed to load cubemap" << std::endl; } - - // Load unit plane - unitPlaneModel = modelLoader->load("data/models/unit-plane.mdl"); - if (!unitPlaneModel) - { - std::cout << "Failed to load unit plane" << std::endl; - } - - // Setup capping plane model - for (int i = 0; i < 5; ++i) - { - cappingPlaneInstances[i].setModel(unitPlaneModel); - } - - // Create capping render context - cappingRenderContext.queue = &cappingRenderQueue; - - // Load clipping render pass - if (!clippingRenderPass.load(nullptr)) - { - return false; - } - - // Load capping render pass - if (!cappingRenderPass.load(nullptr)) - { - return false; - } // Load lighting shader shaderLoader.undefine(); @@ -390,9 +364,6 @@ bool LightingRenderPass::load(const RenderContext* renderContext) void LightingRenderPass::unload() { - cappingRenderQueue.clear(); - clippingRenderPass.unload(); - cappingRenderPass.unload(); delete lightingShader; lightingShader = nullptr; @@ -855,6 +826,10 @@ void LightingRenderPass::render(const RenderContext* renderContext) continue; } const PhysicalMaterial* material = static_cast(operation.material); + if (!(material->flags & (unsigned int)PhysicalMaterial::Flags::OBJECT)) + { + continue; + } // Skip render operations with unsupported vertex formats diff --git a/src/render-passes.hpp b/src/render-passes.hpp index 0747b4f..68a9e38 100644 --- a/src/render-passes.hpp +++ b/src/render-passes.hpp @@ -71,16 +71,31 @@ private: }; /** - * Caps clipped geometry. + * Renders soil profiles. + * + * A soil profile generally of five soil horizons: O, A, B, C, and R. + * + * Horizon O: Organic + * Horizon A: Surface + * Horizon B: Subsoil + * Horizon C: Substratum + * Horizon R: Bedrock + * + * In this render pass, only the O, A, B, and C horizons are used. */ -class CappingRenderPass: public RenderPass +class SoilRenderPass: public RenderPass { public: - CappingRenderPass(); + SoilRenderPass(); virtual bool load(const RenderContext* renderContext); virtual void unload(); virtual void render(const RenderContext* renderContext); + inline void setHorizonOTexture(Texture* texture) { horizonOTexture = texture; } + inline void setHorizonATexture(Texture* texture) { horizonATexture = texture; } + inline void setHorizonBTexture(Texture* texture) { horizonBTexture = texture; } + inline void setHorizonCTexture(Texture* texture) { horizonCTexture = texture; } + private: ShaderParameterSet parameterSet; const ShaderParameter* modelParam; @@ -113,14 +128,6 @@ public: inline void setShadowCamera(const Camera* camera) { this->shadowCamera = camera; } inline void setModelLoader(ModelLoader* modelLoader) { this->modelLoader = modelLoader; } - inline void setClippingPlanes(const Plane* planes) - { - for (int i = 0; i < 5; ++i) - { - this->clippingPlanes[i] = planes[i]; - } - } - private: bool loadShader(const RenderOperation& operation); @@ -152,15 +159,6 @@ private: const Camera* shadowCamera; float time; - // Cutaway clipping and capping - Plane clippingPlanes[5]; - Model* unitPlaneModel; - ModelInstance cappingPlaneInstances[5]; - RenderQueue cappingRenderQueue; - RenderContext cappingRenderContext; - ClippingRenderPass clippingRenderPass; - CappingRenderPass cappingRenderPass; - ModelLoader* modelLoader; }; diff --git a/src/states/experiment-state.cpp b/src/states/experiment-state.cpp index afdb769..7d72099 100644 --- a/src/states/experiment-state.cpp +++ b/src/states/experiment-state.cpp @@ -210,7 +210,6 @@ void ExperimentState::enter() objectsLayer->addObject(&application->camera); - objectsLayer->addObject(application->displayModelInstance); objectsLayer->addObject(application->antModelInstance); objectsLayer->addObject(application->lineBatcher->getBatch()); @@ -416,6 +415,7 @@ void ExperimentState::execute() Vector3 pick; + /* if (dragging) { auto result = pickingRay.intersects(*terrain.getSurfaceMesh()); @@ -458,30 +458,13 @@ void ExperimentState::execute() application->clippingPlaneOffsets[2] = Vector3(dragMin.x, -halfWorldSize, 0.0f); application->clippingPlaneOffsets[3] = Vector3(0.0f, -halfWorldSize, dragMax.z); } - + */ // Calculate clipping planes float halfWorldSize = worldSize * 0.5f; - // E, N, W, S, B - //application->clippingPlaneOffsets[0] = Vector3(halfWorldSize * 0.5f, -halfWorldSize, 0.0f); - //application->clippingPlaneOffsets[1] = Vector3(0.0f, -halfWorldSize, -halfWorldSize * 0.5f); - //application->clippingPlaneOffsets[2] = Vector3(-halfWorldSize * 0.5f, -halfWorldSize, 0.0f); - //application->clippingPlaneOffsets[3] = Vector3(0.0f, -halfWorldSize, halfWorldSize * 0.5f); - application->clippingPlaneOffsets[4] = Vector3(0.0f, -worldSize * 2.0f, 0.0f); - application->clippingPlaneNormals[0] = Vector3(1.0f, 0.0f, 0.0f); - application->clippingPlaneNormals[1] = Vector3(0.0f, 0.0f, -1.0f); - application->clippingPlaneNormals[2] = Vector3(-1.0f, 0.0f, 0.0f); - application->clippingPlaneNormals[3] = Vector3(0.0f, 0.0f, 1.0f); - application->clippingPlaneNormals[4] = Vector3(0.0f, -1.0f, 0.0f); - - for (int i = 0; i < 5; ++i) - { - application->clippingPlanes[i].set(application->clippingPlaneNormals[i], application->clippingPlaneOffsets[i]); - } - application->lightingPass.setClippingPlanes(&application->clippingPlanes[0]); application->lineBatcher->getBatch()->update(); diff --git a/src/states/splash-state.cpp b/src/states/splash-state.cpp index d52d35c..5a32207 100644 --- a/src/states/splash-state.cpp +++ b/src/states/splash-state.cpp @@ -105,8 +105,8 @@ void SplashState::enter() // Colors - application->selectedColor = Vector4(1.0f, 1.0f, 1.0f, 1.0f); - application->deselectedColor = Vector4(0.35f, 0.35f, 0.35f, 1.0f); + application->selectedColor = Vector4(0.0f, 0.0f, 0.0f, 1.0f); + application->deselectedColor = Vector4(0.0f, 0.0f, 0.0f, 0.35f); // Build UI application->uiRootElement = new UIContainer(); @@ -158,7 +158,8 @@ void SplashState::enter() application->menuSelectorLabel = new UILabel(); application->menuSelectorLabel->setAnchor(Anchor::TOP_LEFT); application->menuSelectorLabel->setFont(application->menuFont); - application->menuSelectorLabel->setText(">"); + application->menuSelectorLabel->setText("<"); + application->menuSelectorLabel->setTintColor(application->selectedColor); /* application->menuSelectorLabel = new UIImage(); @@ -351,10 +352,10 @@ void SplashState::enter() application->copyrightFadeOutTween->setUpdateCallback(std::bind(UIElement::setTintColor, application->copyrightImage, std::placeholders::_1)); application->tweener->addTween(application->copyrightFadeOutTween); - application->anyKeyFadeInTween = new Tween(EaseFunction::LINEAR, 0.0f, 1.5f, Vector4(1.0f, 1.0f, 1.0f, 0.0f), Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + application->anyKeyFadeInTween = new Tween(EaseFunction::LINEAR, 0.0f, 1.5f, Vector4(0.0f, 0.0f, 0.0f, 0.0f), Vector4(0.0f, 0.0f, 0.0f, 1.0f)); application->anyKeyFadeInTween->setUpdateCallback(std::bind(UIElement::setTintColor, application->anyKeyLabel, std::placeholders::_1)); application->tweener->addTween(application->anyKeyFadeInTween); - application->anyKeyFadeOutTween = new Tween(EaseFunction::LINEAR, 0.0f, 1.5f, Vector4(1.0f, 1.0f, 1.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, -1.0f)); + application->anyKeyFadeOutTween = new Tween(EaseFunction::LINEAR, 0.0f, 1.5f, Vector4(0.0f, 0.0f, 0.0f, 1.0f), Vector4(0.0f, 0.0f, 0.0f, -1.0f)); application->anyKeyFadeOutTween->setUpdateCallback(std::bind(UIElement::setTintColor, application->anyKeyLabel, std::placeholders::_1)); application->tweener->addTween(application->anyKeyFadeOutTween); @@ -492,17 +493,21 @@ void SplashState::enter() application->selectMenuItem(application->selectedMenuItemIndex); // Models - application->displayModel = application->modelLoader->load("data/models/icosphere.mdl"); application->antModel = application->modelLoader->load("data/models/debug-worker.mdl"); // Model instances - application->displayModelInstance = new ModelInstance(); application->antModelInstance = new ModelInstance(); // Allocate game variables application->surfaceCam = new SurfaceCameraController(); application->tunnelCam = new TunnelCameraController(); + // Load biosphere + application->biosphere.load("data/biomes/"); + + // Load campaign + application->campaign.load("data/levels/"); + // Setup screen fade-in transition fadeIn = false; fadeOut = false; diff --git a/src/states/title-state.cpp b/src/states/title-state.cpp index 9e01d3a..a214a0f 100644 --- a/src/states/title-state.cpp +++ b/src/states/title-state.cpp @@ -47,9 +47,6 @@ void TitleState::enter() fadeIn = false; fadeOut = false; - application->displayModelInstance->setModel(application->displayModel); - application->displayModelInstance->setTransform(Transform::getIdentity()); - application->antModelInstance->setModel(application->antModel); application->antModelInstance->setTransform(Transform::getIdentity()); @@ -61,7 +58,7 @@ void TitleState::enter() Billboard* bgBillboard = application->bgBatch.getBillboard(0); bgBillboard->setDimensions(Vector2(1.0f, 1.0f)); bgBillboard->setTranslation(Vector3(0.5f, 0.5f, 0.0f)); - bgBillboard->setTintColor(Vector4(1, 0, 0, 1)); + bgBillboard->setTintColor(Vector4(1, 1, 1, 1)); application->bgBatch.update(); application->vignettePass.setRenderTarget(&application->defaultRenderTarget); @@ -80,6 +77,39 @@ void TitleState::enter() application->sunlight.setColor(glm::vec3(1.0f)); application->sunlight.setDirection(glm::normalize(glm::vec3(0.5, -1, -0.5))); + const Level* level = &application->campaign.levels[1][1]; + const Biome* biome = &application->biosphere.biomes[level->biome]; + + // Setup soil pass + application->soilPass.setRenderTarget(&application->defaultRenderTarget); + TextureLoader textureLoader; + + /*application->soilPass.setHorizonOTexture(textureLoader.load("data/textures/debug-soil-horizon-o.png")); + application->soilPass.setHorizonATexture(textureLoader.load("data/textures/debug-soil-horizon-a.png")); + application->soilPass.setHorizonBTexture(textureLoader.load("data/textures/debug-soil-horizon-b.png")); + application->soilPass.setHorizonCTexture(textureLoader.load("data/textures/debug-soil-horizon-c.png"));*/ + + + application->soilPass.setHorizonOTexture(biome->soilHorizonO); + application->soilPass.setHorizonATexture(biome->soilHorizonA); + application->soilPass.setHorizonBTexture(biome->soilHorizonB); + application->soilPass.setHorizonCTexture(biome->soilHorizonC); + application->defaultCompositor.addPass(&application->soilPass); + + // Create terrain + std::string heightmap = std::string("data/textures/") + level->heightmap; + + terrain.create(255, 255, Vector3(50, 20, 50)); + terrain.load(heightmap); + terrain.getSurfaceModel()->getGroup(0)->material = application->materialLoader->load("data/materials/debug-terrain-surface.mtl"); + terrainSurface.setModel(terrain.getSurfaceModel()); + terrainSurface.setTranslation(Vector3(0, 0, 0)); + + terrainSubsurface.setModel(terrain.getSubsurfaceModel()); + terrainSubsurface.setTranslation(Vector3(0, 0, 0)); + application->scene.getLayer(0)->addObject(&terrainSurface); + application->scene.getLayer(0)->addObject(&terrainSubsurface); + navmesh = terrain.getSurfaceNavmesh(); // Setup lighting pass application->lightingPass.setRenderTarget(&application->defaultRenderTarget); @@ -114,8 +144,6 @@ void TitleState::enter() application->scene.getLayer(0)->addObject(lightA); application->scene.getLayer(0)->addObject(lightB); application->scene.getLayer(0)->addObject(lightC); - - application->scene.getLayer(0)->addObject(application->displayModelInstance); application->scene.getLayer(0)->addObject(&application->camera); // Load compositor @@ -140,9 +168,9 @@ void TitleState::enter() // Setup camera controller application->surfaceCam->setCamera(&application->camera); application->surfaceCam->setFocalPoint(Vector3(0.0f)); - application->surfaceCam->setFocalDistance(10.0f); - application->surfaceCam->setElevation(0.0f); - application->surfaceCam->setAzimuth(0.0f); + application->surfaceCam->setFocalDistance(300.0f); + application->surfaceCam->setElevation(glm::radians(32.5f)); + application->surfaceCam->setAzimuth(glm::radians(-45.0f)); application->surfaceCam->setTargetFocalPoint(application->surfaceCam->getFocalPoint()); application->surfaceCam->setTargetFocalDistance(application->surfaceCam->getFocalDistance()); application->surfaceCam->setTargetElevation(application->surfaceCam->getElevation()); @@ -154,19 +182,17 @@ void TitleState::enter() wasDragging = dragging; application->arcball.setCenter(Vector2(application->width * 0.5f, application->height * 0.5f)); application->arcball.setRadius(application->height * 0.5f); - - // Load navmesh - - navmesh.loadOBJ("data/textures/icosphere.obj"); + + // Setup colony colony.setAntModel(application->antModel); - for (int i = 0; i < 10; ++i) + for (int i = 0; i < 50; ++i) { - Navmesh::Triangle* triangle = (*navmesh.getTriangles())[0]; + Navmesh::Triangle* triangle = (*navmesh->getTriangles())[0]; - ant = colony.spawn(&navmesh, triangle, normalize_barycentric(Vector3(0.5f))); + ant = colony.spawn(navmesh, triangle, normalize_barycentric(Vector3(0.5f))); Vector3 forward = glm::normalize(triangle->edge->vertex->position - triangle->edge->next->vertex->position); Vector3 up = triangle->normal; @@ -202,12 +228,6 @@ void TitleState::execute() application->titleFadeInTween->start(); } - if (stateTime >= copyrightDelay && !application->copyrightImage->isVisible()) - { - //application->copyrightImage->setVisible(true); - //application->copyrightFadeInTween->start(); - } - if (stateTime >= pressAnyKeyDelay && !application->anyKeyLabel->isVisible()) { application->anyKeyLabel->setVisible(true); @@ -266,8 +286,6 @@ void TitleState::execute() application->titleFadeInTween->stop(); application->titleFadeOutTween->start(); - //application->copyrightFadeInTween->stop(); - //application->copyrightFadeOutTween->start(); application->anyKeyFadeInTween->stop(); application->anyKeyFadeOutTween->stop(); application->anyKeyLabel->setVisible(false); @@ -295,6 +313,7 @@ void TitleState::execute() fadeIn = true; } + /* glm::ivec2 mousePosition = application->mouse->getCurrentPosition(); mousePosition.y = application->height - mousePosition.y; if (dragging && !wasDragging) @@ -313,7 +332,7 @@ void TitleState::execute() application->displayModelInstance->setTransform(transform); } wasDragging = dragging; - + */ // Check if application was closed @@ -334,6 +353,23 @@ void TitleState::execute() application->surfaceCam->rotate(-rotationSpeed); if (application->cameraRotateCCW.isTriggered()) application->surfaceCam->rotate(rotationSpeed); + + // Move camera + Vector2 movementVector(0.0f); + if (application->cameraMoveLeft.isTriggered()) + movementVector.x -= application->cameraMoveLeft.getCurrentValue(); + if (application->cameraMoveRight.isTriggered()) + movementVector.x += application->cameraMoveRight.getCurrentValue(); + if (application->cameraMoveForward.isTriggered()) + movementVector.y -= application->cameraMoveForward.getCurrentValue(); + if (application->cameraMoveBack.isTriggered()) + movementVector.y += application->cameraMoveBack.getCurrentValue(); + if (movementVector.x != 0.0f || movementVector.y != 0.0f) + { + movementVector *= 0.005f * application->surfaceCam->getFocalDistance() * dt / (1.0f / 60.0f); + application->surfaceCam->move(movementVector); + } + // Zoom camera float zoomFactor = application->surfaceCam->getFocalDistance() / 20.0f * dt / (1.0f / 60.0f); if (application->cameraZoomIn.isTriggered()) diff --git a/src/states/title-state.hpp b/src/states/title-state.hpp index 1a85897..7037cb8 100644 --- a/src/states/title-state.hpp +++ b/src/states/title-state.hpp @@ -25,6 +25,7 @@ #include "../game/ant.hpp" #include "../game/colony.hpp" +#include "../game/terrain.hpp" #include using namespace Emergent; @@ -60,7 +61,10 @@ private: Colony colony; Ant* ant; - Navmesh navmesh; + Navmesh* navmesh; + Terrain terrain; + ModelInstance terrainSurface; + ModelInstance terrainSubsurface; }; #endif // TITLE_STATE_HPP \ No newline at end of file