diff --git a/src/game/ant/cladogenesis.cpp b/src/game/ant/cladogenesis.cpp index a4e661a..a16c4cd 100644 --- a/src/game/ant/cladogenesis.cpp +++ b/src/game/ant/cladogenesis.cpp @@ -22,7 +22,7 @@ namespace game { namespace ant { -genome* cladogenesis(gene_pool& pool, std::random_device& rng) +genome* cladogenesis(const gene_pool& pool, std::random_device& rng) { // Allocate genome ant::genome* genome = new ant::genome(); diff --git a/src/game/ant/cladogenesis.hpp b/src/game/ant/cladogenesis.hpp index e7f5beb..0edf9af 100644 --- a/src/game/ant/cladogenesis.hpp +++ b/src/game/ant/cladogenesis.hpp @@ -35,7 +35,7 @@ namespace ant { * * @return New genome. */ -genome* cladogenesis(gene_pool& pool, std::random_device& rng); +genome* cladogenesis(const gene_pool& pool, std::random_device& rng); } // namespace ant } // namespace game diff --git a/src/game/ant/gene-frequency-table.hpp b/src/game/ant/gene-frequency-table.hpp index 7f7527b..7c5089a 100644 --- a/src/game/ant/gene-frequency-table.hpp +++ b/src/game/ant/gene-frequency-table.hpp @@ -25,7 +25,7 @@ namespace game { namespace ant { - + /** * Gene frequency table. * @@ -37,8 +37,8 @@ struct gene_frequency_table /// Gene array. std::vector genes; - /// Gene discrete probability distribution. - std::discrete_distribution distribution; + /// Weight array + std::vector weights; /** * Samples a gene from the frequency table. @@ -50,8 +50,12 @@ struct gene_frequency_table * @return Randomly sampled gene. */ template - const T* sample(Generator& g) + const T* sample(Generator& g) const { + if (genes.empty()) + return nullptr; + + std::discrete_distribution distribution(weights.begin(), weights.end()); return genes[distribution(g)]; } }; diff --git a/src/game/ant/gene/loader/diet-loader.cpp b/src/game/ant/gene/loader/diet-loader.cpp new file mode 100644 index 0000000..14c2cab --- /dev/null +++ b/src/game/ant/gene/loader/diet-loader.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 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 "game/ant/gene/loader/gene-loader.hpp" +#include "resources/resource-loader.hpp" +#include "resources/resource-manager.hpp" +#include "resources/json.hpp" +#include "game/ant/gene/diet.hpp" +#include "math/angles.hpp" +#include "math/constants.hpp" +#include + +using namespace game::ant; + +static void deserialize_diet_phene(phene::diet& phene, const json& phene_element, resource_manager* resource_manager) +{ + +} + +template <> +gene::diet* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Load JSON data + json* data = resource_loader::load(resource_manager, file, path); + + // Validate gene file + auto diet_element = data->find("diet"); + if (diet_element == data->end()) + throw std::runtime_error("Invalid diet gene."); + + // Allocate gene + gene::diet* diet = new gene::diet(); + + // Deserialize gene + gene::deserialize_gene(*diet, &deserialize_diet_phene, *diet_element, resource_manager); + + // Free JSON data + delete data; + + return diet; +} diff --git a/src/game/ant/gene/loader/founding-mode-loader.cpp b/src/game/ant/gene/loader/founding-mode-loader.cpp new file mode 100644 index 0000000..9defaa6 --- /dev/null +++ b/src/game/ant/gene/loader/founding-mode-loader.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 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 "game/ant/gene/loader/gene-loader.hpp" +#include "resources/resource-loader.hpp" +#include "resources/resource-manager.hpp" +#include "resources/json.hpp" +#include "game/ant/gene/founding-mode.hpp" +#include "math/angles.hpp" +#include "math/constants.hpp" +#include + +using namespace game::ant; + +static void deserialize_founding_mode_phene(phene::founding_mode& phene, const json& phene_element, resource_manager* resource_manager) +{ + +} + +template <> +gene::founding_mode* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Load JSON data + json* data = resource_loader::load(resource_manager, file, path); + + // Validate gene file + auto founding_mode_element = data->find("founding_mode"); + if (founding_mode_element == data->end()) + throw std::runtime_error("Invalid founding mode gene."); + + // Allocate gene + gene::founding_mode* founding_mode = new gene::founding_mode(); + + // Deserialize gene + gene::deserialize_gene(*founding_mode, &deserialize_founding_mode_phene, *founding_mode_element, resource_manager); + + // Free JSON data + delete data; + + return founding_mode; +} diff --git a/src/game/ant/gene/loader/nest-site-loader.cpp b/src/game/ant/gene/loader/nest-site-loader.cpp new file mode 100644 index 0000000..e9ff16b --- /dev/null +++ b/src/game/ant/gene/loader/nest-site-loader.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 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 "game/ant/gene/loader/gene-loader.hpp" +#include "resources/resource-loader.hpp" +#include "resources/resource-manager.hpp" +#include "resources/json.hpp" +#include "game/ant/gene/nest-site.hpp" +#include "math/angles.hpp" +#include "math/constants.hpp" +#include + +using namespace game::ant; + +static void deserialize_nest_site_phene(phene::nest_site& phene, const json& phene_element, resource_manager* resource_manager) +{ + +} + +template <> +gene::nest_site* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Load JSON data + json* data = resource_loader::load(resource_manager, file, path); + + // Validate gene file + auto nest_site_element = data->find("nest_site"); + if (nest_site_element == data->end()) + throw std::runtime_error("Invalid nest site gene."); + + // Allocate gene + gene::nest_site* nest_site = new gene::nest_site(); + + // Deserialize gene + gene::deserialize_gene(*nest_site, &deserialize_nest_site_phene, *nest_site_element, resource_manager); + + // Free JSON data + delete data; + + return nest_site; +} diff --git a/src/game/context.hpp b/src/game/context.hpp index 80605f0..8eaccea 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -54,6 +54,7 @@ #include "application.hpp" #include "game/state/base.hpp" #include "game/loop.hpp" +#include "game/ecoregion.hpp" #include "state-machine.hpp" #include "debug/performance-sampler.hpp" #include @@ -293,6 +294,8 @@ struct context game::system::proteome* proteome_system; double3 rgb_wavelengths; + + const ecoregion* active_ecoregion; }; } // namespace game diff --git a/src/game/ecoregion-loader.cpp b/src/game/ecoregion-loader.cpp new file mode 100644 index 0000000..17f8b32 --- /dev/null +++ b/src/game/ecoregion-loader.cpp @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2021 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 "game/ecoregion.hpp" +#include "resources/resource-loader.hpp" +#include "resources/resource-manager.hpp" +#include "resources/json.hpp" +#include + +template <> +game::ecoregion* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Load JSON data + json* data = resource_loader::load(resource_manager, file, path); + + // Validate ecoregion file + auto ecoregion_element = data->find("ecoregion"); + if (ecoregion_element == data->end()) + throw std::runtime_error("Invalid ecoregion file."); + + // Allocate and init ecoregion + game::ecoregion* ecoregion = new game::ecoregion(); + ecoregion->elevation = 0.0f; + ecoregion->latitude = 0.0f; + ecoregion->longitude = 0.0f; + ecoregion->terrain_material = nullptr; + ecoregion->horizon_material = nullptr; + + if (auto element = ecoregion_element->find("name"); element != ecoregion_element->end()) + ecoregion->name = element->get(); + + if (auto location_element = ecoregion_element->find("location"); location_element != ecoregion_element->end()) + { + if (auto element = location_element->find("elevation"); element != location_element->end()) + ecoregion->elevation = element->get(); + if (auto element = location_element->find("latitude"); element != location_element->end()) + ecoregion->latitude = math::radians(element->get()); + if (auto element = location_element->find("longitude"); element != location_element->end()) + ecoregion->longitude = math::radians(element->get()); + } + + if (auto terrain_element = ecoregion_element->find("terrain"); terrain_element != ecoregion_element->end()) + { + if (auto element = terrain_element->find("material"); element != terrain_element->end()) + ecoregion->terrain_material = resource_manager->load(element->get()); + if (auto element = terrain_element->find("albedo"); element != terrain_element->end()) + ecoregion->terrain_albedo = {(*element)[0].get(), (*element)[1].get(), (*element)[2].get()}; + if (auto element = terrain_element->find("horizon_material"); element != terrain_element->end()) + ecoregion->horizon_material = resource_manager->load(element->get()); + } + + // Load gene pools + if (auto gene_pools_element = ecoregion_element->find("gene_pools"); gene_pools_element != ecoregion_element->end()) + { + // For each gene pool + for (auto gene_pool_element = gene_pools_element->begin(); gene_pool_element != gene_pools_element->end(); ++gene_pool_element) + { + // Allocate gene pool + ecoregion->gene_pools.resize(ecoregion->gene_pools.size() + 1); + game::ant::gene_pool& gene_pool = ecoregion->gene_pools.back(); + + // Read gene pool name + if (auto name_element = gene_pool_element->find("name"); name_element != gene_pool_element->end()) + gene_pool.name = name_element->get(); + + // Load genes + if (auto genes_element = gene_pool_element->find("genes"); genes_element != gene_pool_element->end()) + { + // Load antennae genes + if (auto antennae_elements = genes_element->find("antennae"); antennae_elements != genes_element->end()) + { + for (auto antennae_element = antennae_elements->begin(); antennae_element != antennae_elements->end(); ++antennae_element) + { + float weight = 0.0f; + const game::ant::gene::antennae* gene = nullptr; + + if (auto weight_element = antennae_element->find("weight"); weight_element != antennae_element->end()) + weight = weight_element->get(); + if (auto gene_element = antennae_element->find("gene"); gene_element != antennae_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.antennae.weights.push_back(weight); + gene_pool.antennae.genes.push_back(gene); + } + } + } + + // Load body size genes + if (auto body_size_elements = genes_element->find("body_size"); body_size_elements != genes_element->end()) + { + for (auto body_size_element = body_size_elements->begin(); body_size_element != body_size_elements->end(); ++body_size_element) + { + float weight = 0.0f; + const game::ant::gene::body_size* gene = nullptr; + + if (auto weight_element = body_size_element->find("weight"); weight_element != body_size_element->end()) + weight = weight_element->get(); + if (auto gene_element = body_size_element->find("gene"); gene_element != body_size_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.body_size.weights.push_back(weight); + gene_pool.body_size.genes.push_back(gene); + } + } + } + + // Load cocoon genes + if (auto cocoon_elements = genes_element->find("cocoon"); cocoon_elements != genes_element->end()) + { + for (auto cocoon_element = cocoon_elements->begin(); cocoon_element != cocoon_elements->end(); ++cocoon_element) + { + float weight = 0.0f; + const game::ant::gene::cocoon* gene = nullptr; + + if (auto weight_element = cocoon_element->find("weight"); weight_element != cocoon_element->end()) + weight = weight_element->get(); + if (auto gene_element = cocoon_element->find("gene"); gene_element != cocoon_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.cocoon.weights.push_back(weight); + gene_pool.cocoon.genes.push_back(gene); + } + } + } + + // Load diet genes + if (auto diet_elements = genes_element->find("diet"); diet_elements != genes_element->end()) + { + for (auto diet_element = diet_elements->begin(); diet_element != diet_elements->end(); ++diet_element) + { + float weight = 0.0f; + const game::ant::gene::diet* gene = nullptr; + + if (auto weight_element = diet_element->find("weight"); weight_element != diet_element->end()) + weight = weight_element->get(); + if (auto gene_element = diet_element->find("gene"); gene_element != diet_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.diet.weights.push_back(weight); + gene_pool.diet.genes.push_back(gene); + } + } + } + + // Load egg genes + if (auto egg_elements = genes_element->find("egg"); egg_elements != genes_element->end()) + { + for (auto egg_element = egg_elements->begin(); egg_element != egg_elements->end(); ++egg_element) + { + float weight = 0.0f; + const game::ant::gene::egg* gene = nullptr; + + if (auto weight_element = egg_element->find("weight"); weight_element != egg_element->end()) + weight = weight_element->get(); + if (auto gene_element = egg_element->find("gene"); gene_element != egg_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.egg.weights.push_back(weight); + gene_pool.egg.genes.push_back(gene); + } + } + } + + // Load eyes genes + if (auto eyes_elements = genes_element->find("eyes"); eyes_elements != genes_element->end()) + { + for (auto eyes_element = eyes_elements->begin(); eyes_element != eyes_elements->end(); ++eyes_element) + { + float weight = 0.0f; + const game::ant::gene::eyes* gene = nullptr; + + if (auto weight_element = eyes_element->find("weight"); weight_element != eyes_element->end()) + weight = weight_element->get(); + if (auto gene_element = eyes_element->find("gene"); gene_element != eyes_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.eyes.weights.push_back(weight); + gene_pool.eyes.genes.push_back(gene); + } + } + } + + // Load foraging time genes + if (auto foraging_time_elements = genes_element->find("foraging_time"); foraging_time_elements != genes_element->end()) + { + for (auto foraging_time_element = foraging_time_elements->begin(); foraging_time_element != foraging_time_elements->end(); ++foraging_time_element) + { + float weight = 0.0f; + const game::ant::gene::foraging_time* gene = nullptr; + + if (auto weight_element = foraging_time_element->find("weight"); weight_element != foraging_time_element->end()) + weight = weight_element->get(); + if (auto gene_element = foraging_time_element->find("gene"); gene_element != foraging_time_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.foraging_time.weights.push_back(weight); + gene_pool.foraging_time.genes.push_back(gene); + } + } + } + + // Load founding mode genes + if (auto founding_mode_elements = genes_element->find("founding_mode"); founding_mode_elements != genes_element->end()) + { + for (auto founding_mode_element = founding_mode_elements->begin(); founding_mode_element != founding_mode_elements->end(); ++founding_mode_element) + { + float weight = 0.0f; + const game::ant::gene::founding_mode* gene = nullptr; + + if (auto weight_element = founding_mode_element->find("weight"); weight_element != founding_mode_element->end()) + weight = weight_element->get(); + if (auto gene_element = founding_mode_element->find("gene"); gene_element != founding_mode_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.founding_mode.weights.push_back(weight); + gene_pool.founding_mode.genes.push_back(gene); + } + } + } + + // Load gaster genes + if (auto gaster_elements = genes_element->find("gaster"); gaster_elements != genes_element->end()) + { + for (auto gaster_element = gaster_elements->begin(); gaster_element != gaster_elements->end(); ++gaster_element) + { + float weight = 0.0f; + const game::ant::gene::gaster* gene = nullptr; + + if (auto weight_element = gaster_element->find("weight"); weight_element != gaster_element->end()) + weight = weight_element->get(); + if (auto gene_element = gaster_element->find("gene"); gene_element != gaster_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.gaster.weights.push_back(weight); + gene_pool.gaster.genes.push_back(gene); + } + } + } + + // Load head genes + if (auto head_elements = genes_element->find("head"); head_elements != genes_element->end()) + { + for (auto head_element = head_elements->begin(); head_element != head_elements->end(); ++head_element) + { + float weight = 0.0f; + const game::ant::gene::head* gene = nullptr; + + if (auto weight_element = head_element->find("weight"); weight_element != head_element->end()) + weight = weight_element->get(); + if (auto gene_element = head_element->find("gene"); gene_element != head_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.head.weights.push_back(weight); + gene_pool.head.genes.push_back(gene); + } + } + } + + // Load larva genes + if (auto larva_elements = genes_element->find("larva"); larva_elements != genes_element->end()) + { + for (auto larva_element = larva_elements->begin(); larva_element != larva_elements->end(); ++larva_element) + { + float weight = 0.0f; + const game::ant::gene::larva* gene = nullptr; + + if (auto weight_element = larva_element->find("weight"); weight_element != larva_element->end()) + weight = weight_element->get(); + if (auto gene_element = larva_element->find("gene"); gene_element != larva_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.larva.weights.push_back(weight); + gene_pool.larva.genes.push_back(gene); + } + } + } + + // Load legs genes + if (auto legs_elements = genes_element->find("legs"); legs_elements != genes_element->end()) + { + for (auto legs_element = legs_elements->begin(); legs_element != legs_elements->end(); ++legs_element) + { + float weight = 0.0f; + const game::ant::gene::legs* gene = nullptr; + + if (auto weight_element = legs_element->find("weight"); weight_element != legs_element->end()) + weight = weight_element->get(); + if (auto gene_element = legs_element->find("gene"); gene_element != legs_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.legs.weights.push_back(weight); + gene_pool.legs.genes.push_back(gene); + } + } + } + + // Load mandibles genes + if (auto mandibles_elements = genes_element->find("mandibles"); mandibles_elements != genes_element->end()) + { + for (auto mandibles_element = mandibles_elements->begin(); mandibles_element != mandibles_elements->end(); ++mandibles_element) + { + float weight = 0.0f; + const game::ant::gene::mandibles* gene = nullptr; + + if (auto weight_element = mandibles_element->find("weight"); weight_element != mandibles_element->end()) + weight = weight_element->get(); + if (auto gene_element = mandibles_element->find("gene"); gene_element != mandibles_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.mandibles.weights.push_back(weight); + gene_pool.mandibles.genes.push_back(gene); + } + } + } + + // Load mesosoma genes + if (auto mesosoma_elements = genes_element->find("mesosoma"); mesosoma_elements != genes_element->end()) + { + for (auto mesosoma_element = mesosoma_elements->begin(); mesosoma_element != mesosoma_elements->end(); ++mesosoma_element) + { + float weight = 0.0f; + const game::ant::gene::mesosoma* gene = nullptr; + + if (auto weight_element = mesosoma_element->find("weight"); weight_element != mesosoma_element->end()) + weight = weight_element->get(); + if (auto gene_element = mesosoma_element->find("gene"); gene_element != mesosoma_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.mesosoma.weights.push_back(weight); + gene_pool.mesosoma.genes.push_back(gene); + } + } + } + + // Load nest site genes + if (auto nest_site_elements = genes_element->find("nest_site"); nest_site_elements != genes_element->end()) + { + for (auto nest_site_element = nest_site_elements->begin(); nest_site_element != nest_site_elements->end(); ++nest_site_element) + { + float weight = 0.0f; + const game::ant::gene::nest_site* gene = nullptr; + + if (auto weight_element = nest_site_element->find("weight"); weight_element != nest_site_element->end()) + weight = weight_element->get(); + if (auto gene_element = nest_site_element->find("gene"); gene_element != nest_site_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.nest_site.weights.push_back(weight); + gene_pool.nest_site.genes.push_back(gene); + } + } + } + + // Load ocelli genes + if (auto ocelli_elements = genes_element->find("ocelli"); ocelli_elements != genes_element->end()) + { + for (auto ocelli_element = ocelli_elements->begin(); ocelli_element != ocelli_elements->end(); ++ocelli_element) + { + float weight = 0.0f; + const game::ant::gene::ocelli* gene = nullptr; + + if (auto weight_element = ocelli_element->find("weight"); weight_element != ocelli_element->end()) + weight = weight_element->get(); + if (auto gene_element = ocelli_element->find("gene"); gene_element != ocelli_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.ocelli.weights.push_back(weight); + gene_pool.ocelli.genes.push_back(gene); + } + } + } + + // Load pigmentation genes + if (auto pigmentation_elements = genes_element->find("pigmentation"); pigmentation_elements != genes_element->end()) + { + for (auto pigmentation_element = pigmentation_elements->begin(); pigmentation_element != pigmentation_elements->end(); ++pigmentation_element) + { + float weight = 0.0f; + const game::ant::gene::pigmentation* gene = nullptr; + + if (auto weight_element = pigmentation_element->find("weight"); weight_element != pigmentation_element->end()) + weight = weight_element->get(); + if (auto gene_element = pigmentation_element->find("gene"); gene_element != pigmentation_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.pigmentation.weights.push_back(weight); + gene_pool.pigmentation.genes.push_back(gene); + } + } + } + + // Load pilosity genes + if (auto pilosity_elements = genes_element->find("pilosity"); pilosity_elements != genes_element->end()) + { + for (auto pilosity_element = pilosity_elements->begin(); pilosity_element != pilosity_elements->end(); ++pilosity_element) + { + float weight = 0.0f; + const game::ant::gene::pilosity* gene = nullptr; + + if (auto weight_element = pilosity_element->find("weight"); weight_element != pilosity_element->end()) + weight = weight_element->get(); + if (auto gene_element = pilosity_element->find("gene"); gene_element != pilosity_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.pilosity.weights.push_back(weight); + gene_pool.pilosity.genes.push_back(gene); + } + } + } + + // Load sculpturing genes + if (auto sculpturing_elements = genes_element->find("sculpturing"); sculpturing_elements != genes_element->end()) + { + for (auto sculpturing_element = sculpturing_elements->begin(); sculpturing_element != sculpturing_elements->end(); ++sculpturing_element) + { + float weight = 0.0f; + const game::ant::gene::sculpturing* gene = nullptr; + + if (auto weight_element = sculpturing_element->find("weight"); weight_element != sculpturing_element->end()) + weight = weight_element->get(); + if (auto gene_element = sculpturing_element->find("gene"); gene_element != sculpturing_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.sculpturing.weights.push_back(weight); + gene_pool.sculpturing.genes.push_back(gene); + } + } + } + + // Load sting genes + if (auto sting_elements = genes_element->find("sting"); sting_elements != genes_element->end()) + { + for (auto sting_element = sting_elements->begin(); sting_element != sting_elements->end(); ++sting_element) + { + float weight = 0.0f; + const game::ant::gene::sting* gene = nullptr; + + if (auto weight_element = sting_element->find("weight"); weight_element != sting_element->end()) + weight = weight_element->get(); + if (auto gene_element = sting_element->find("gene"); gene_element != sting_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.sting.weights.push_back(weight); + gene_pool.sting.genes.push_back(gene); + } + } + } + + // Load waist genes + if (auto waist_elements = genes_element->find("waist"); waist_elements != genes_element->end()) + { + for (auto waist_element = waist_elements->begin(); waist_element != waist_elements->end(); ++waist_element) + { + float weight = 0.0f; + const game::ant::gene::waist* gene = nullptr; + + if (auto weight_element = waist_element->find("weight"); weight_element != waist_element->end()) + weight = weight_element->get(); + if (auto gene_element = waist_element->find("gene"); gene_element != waist_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.waist.weights.push_back(weight); + gene_pool.waist.genes.push_back(gene); + } + } + } + + // Load wings genes + if (auto wings_elements = genes_element->find("wings"); wings_elements != genes_element->end()) + { + for (auto wings_element = wings_elements->begin(); wings_element != wings_elements->end(); ++wings_element) + { + float weight = 0.0f; + const game::ant::gene::wings* gene = nullptr; + + if (auto weight_element = wings_element->find("weight"); weight_element != wings_element->end()) + weight = weight_element->get(); + if (auto gene_element = wings_element->find("gene"); gene_element != wings_element->end()) + gene = resource_manager->load(gene_element->get()); + + if (gene) + { + gene_pool.wings.weights.push_back(weight); + gene_pool.wings.genes.push_back(gene); + } + } + } + } + } + } + + // Free JSON data + delete data; + + return ecoregion; +} diff --git a/src/game/ecoregion.hpp b/src/game/ecoregion.hpp new file mode 100644 index 0000000..9ca0cec --- /dev/null +++ b/src/game/ecoregion.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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 ANTKEEPER_GAME_ECOREGION_HPP +#define ANTKEEPER_GAME_ECOREGION_HPP + +#include "utility/fundamental-types.hpp" +#include "render/material.hpp" +#include "game/ant/gene-pool.hpp" +#include +#include + +namespace game { + +/** + * + */ +struct ecoregion +{ + /// Ecoregion name. + std::string name; + + /// Elevation, in meters. + float elevation; + + /// Latitude, in radians. + float latitude; + + /// Longitude, in radians. + float longitude; + + /// Terrain material. + render::material* terrain_material; + + /// Terrain albedo. + float3 terrain_albedo; + + /// Horizon material. + render::material* horizon_material; + + /// Array of gene pools. + std::vector gene_pools; + + /// Discrete probability distribution of gene pools. + std::discrete_distribution gene_pool_distribution; +}; + +} // namespace game + +#endif // ANTKEEPER_GAME_ECOREGION_HPP diff --git a/src/game/load.cpp b/src/game/load.cpp index ce17205..aabe1cc 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -43,176 +43,6 @@ namespace game { namespace load { -void biome(game::context& ctx, const std::filesystem::path& path) -{ - ctx.logger->push_task("Loading biome from \"" + path.string() + "\""); - - /* - image img; - img.format(1, 4); - img.resize(2048, 2048); - - auto width = img.get_width(); - auto height = img.get_height(); - unsigned char* pixels = (unsigned char*)img.data(); - - const float frequency = 400.0f; - float scale_x = 1.0f / static_cast(width - 1) * frequency; - float scale_y = 1.0f / static_cast(height - 1) * frequency; - - std::for_each - ( - std::execution::par_unseq, - img.begin>(), - img.end>(), - [pixels, width, height, scale_x, scale_y, frequency](auto& pixel) - { - const std::size_t i = &pixel - (math::vector*)pixels; - const std::size_t y = i / width; - const std::size_t x = i % width; - - const float2 position = - { - static_cast(x) * scale_x, - static_cast(y) * scale_y - }; - - const auto - [ - f1_sqr_distance, - f1_displacement, - f1_id - ] = math::noise::voronoi::f1(position, 1.0f, {frequency, frequency}); - - const float f1_distance = std::sqrt(f1_sqr_distance); - - const float2 uv = (position + f1_displacement) / frequency; - - pixel = - { - static_cast(std::min(255.0f, f1_distance * 255.0f)), - static_cast(std::min(255.0f, uv[0] * 255.0f)), - static_cast(std::min(255.0f, uv[1] * 255.0f)), - static_cast(f1_id % 256) - }; - } - ); - - stbi_flip_vertically_on_write(1); - stbi_write_tga((ctx.config_path / "gallery" / "voronoi-f1-400-nc8-2k.tga").string().c_str(), img.get_width(), img.get_height(), img.get_channel_count(), img.data()); - */ - - try - { - json* data = ctx.resource_manager->load(path); - - // Load location - if (auto location = data->find("location"); location != data->end()) - { - double elevation = 0.0; - double latitude = 0.0; - double longitude = 0.0; - - if (auto location_ele = location->find("elevation"); location_ele != location->end()) - elevation = location_ele->get(); - else - ctx.logger->warning("Biome elevation undefined"); - - if (auto location_lat = location->find("latitude"); location_lat != location->end()) - latitude = math::radians(location_lat->get()); - else - ctx.logger->warning("Biome latitude undefined"); - - if (auto location_lon = location->find("longitude"); location_lon != location->end()) - longitude = math::radians(location_lon->get()); - else - ctx.logger->warning("Biome longitude undefined"); - - // Set location - game::world::set_location(ctx, elevation, latitude, longitude); - } - else - { - ctx.logger->warning("Biome location undefined"); - } - - // Setup sky - ctx.sky_pass->set_sky_model(ctx.resource_manager->load("celestial-hemisphere.mdl")); - - // Load terrain - if (auto terrain = data->find("terrain"); terrain != data->end()) - { - if (auto material = terrain->find("material"); material != terrain->end()) - { - render::material* terrain_material = ctx.resource_manager->load(material->get()); - ctx.terrain_system->set_patch_material(terrain_material); - } - else - { - ctx.logger->warning("Biome terrain material undefined"); - } - - if (auto material = terrain->find("horizon_material"); material != terrain->end()) - { - render::model* terrestrial_hemisphere_model = ctx.resource_manager->load("terrestrial-hemisphere.mdl"); - (*terrestrial_hemisphere_model->get_groups())[0]->set_material(ctx.resource_manager->load(material->get())); - ctx.ground_pass->set_ground_model(terrestrial_hemisphere_model); - } - else - { - ctx.logger->warning("Biome terrain horizon material undefined"); - } - - // Terrain elevation function - ctx.terrain_system->set_elevation_function - ( - [](float x, float z) -> float - { - const float2 position = float2{x, z}; - - const std::size_t octaves = 3; - const float lacunarity = 1.5f; - const float gain = 0.5f; - - const float fbm = math::noise::fbm - ( - position * 0.005f, - octaves, - lacunarity, - gain - ); - - float y = fbm * 4.0f; - - return y; - } - ); - - // Setup lighting - double3 terrain_albedo = {0, 0, 0}; - if (terrain->contains("albedo")) - { - const auto& albedo_element = (*terrain)["albedo"]; - - terrain_albedo[0] = albedo_element[0].get(); - terrain_albedo[1]= albedo_element[1].get(); - terrain_albedo[2] = albedo_element[2].get(); - - } - ctx.astronomy_system->set_bounce_albedo(terrain_albedo); - } - else - { - ctx.logger->warning("Biome terrain undefined"); - } - } - catch (...) - { - ctx.logger->pop_task(EXIT_FAILURE); - } - ctx.logger->pop_task(EXIT_SUCCESS); -} - void colony(game::context& ctx, const std::filesystem::path& path) { ctx.logger->push_task("Loading colony from \"" + path.string() + "\""); diff --git a/src/game/load.hpp b/src/game/load.hpp index 532b24a..a538962 100644 --- a/src/game/load.hpp +++ b/src/game/load.hpp @@ -25,11 +25,6 @@ namespace game { namespace load { -/** - * Loads a biome. - */ -void biome(game::context& ctx, const std::filesystem::path& path); - /** * Loads a colony */ diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index aa3203b..6286551 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -133,6 +133,7 @@ boot::boot(game::context& ctx, int argc, char** argv): setup_ui(); setup_debugging(); setup_loop(); + ctx.active_ecoregion = nullptr; } catch (const std::exception& e) { diff --git a/src/game/state/main-menu.cpp b/src/game/state/main-menu.cpp index e1c858e..31234b3 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/state/main-menu.cpp @@ -24,6 +24,7 @@ #include "game/world.hpp" #include "game/load.hpp" #include "game/menu.hpp" +#include "game/ecoregion.hpp" #include "game/ant/swarm.hpp" #include "render/passes/clear-pass.hpp" #include "render/passes/ground-pass.hpp" @@ -247,7 +248,7 @@ main_menu::main_menu(game::context& ctx, bool fade_in): { game::world::cosmogenesis(ctx); game::world::create_observer(ctx); - game::load::biome(ctx, "desert-scrub.bio"); + game::world::enter_ecoregion(ctx, *ctx.resource_manager->load("seedy-scrub.eco")); } // Set world time diff --git a/src/game/state/nest-selection.cpp b/src/game/state/nest-selection.cpp index b76c52c..cc6b46f 100644 --- a/src/game/state/nest-selection.cpp +++ b/src/game/state/nest-selection.cpp @@ -56,6 +56,7 @@ #include "game/ant/morphogenesis.hpp" #include "game/ant/phenome.hpp" #include "game/ant/genome.hpp" +#include "game/ant/cladogenesis.hpp" using namespace game::ant; @@ -68,25 +69,28 @@ nest_selection::nest_selection(game::context& ctx): ctx.logger->push_task("Entering nest selection state"); - ctx.logger->push_task("Loading genome"); - ant::genome genome; - genome.antennae = ctx.resource_manager->load("pogonomyrmex-antennae.dna"); - genome.eyes = ctx.resource_manager->load("pogonomyrmex-eyes.dna"); - genome.gaster = ctx.resource_manager->load("pogonomyrmex-gaster.dna"); - genome.head = ctx.resource_manager->load("pogonomyrmex-head.dna"); - genome.legs = ctx.resource_manager->load("pogonomyrmex-legs.dna"); - genome.mandibles = ctx.resource_manager->load("pogonomyrmex-mandibles.dna"); - genome.mesosoma = ctx.resource_manager->load("pogonomyrmex-mesosoma.dna"); - genome.ocelli = ctx.resource_manager->load("ocelli-absent.dna"); - genome.pigmentation = ctx.resource_manager->load("rust-pigmentation.dna"); - genome.sculpturing = ctx.resource_manager->load("politus-sculpturing.dna"); - genome.sting = ctx.resource_manager->load("pogonomyrmex-sting.dna"); - genome.waist = ctx.resource_manager->load("pogonomyrmex-waist.dna"); - genome.wings = ctx.resource_manager->load("wings-absent.dna"); + ctx.logger->push_task("Generating genome"); + + std::random_device rng; + ant::genome* genome = ant::cladogenesis(ctx.active_ecoregion->gene_pools[0], rng); + + // genome.antennae = ctx.resource_manager->load("pogonomyrmex-antennae.dna"); + // genome.eyes = ctx.resource_manager->load("pogonomyrmex-eyes.dna"); + // genome.gaster = ctx.resource_manager->load("pogonomyrmex-gaster.dna"); + // genome.head = ctx.resource_manager->load("pogonomyrmex-head.dna"); + // genome.legs = ctx.resource_manager->load("pogonomyrmex-legs.dna"); + // genome.mandibles = ctx.resource_manager->load("pogonomyrmex-mandibles.dna"); + // genome.mesosoma = ctx.resource_manager->load("pogonomyrmex-mesosoma.dna"); + // genome.ocelli = ctx.resource_manager->load("ocelli-absent.dna"); + // genome.pigmentation = ctx.resource_manager->load("rust-pigmentation.dna"); + // genome.sculpturing = ctx.resource_manager->load("politus-sculpturing.dna"); + // genome.sting = ctx.resource_manager->load("pogonomyrmex-sting.dna"); + // genome.waist = ctx.resource_manager->load("pogonomyrmex-waist.dna"); + // genome.wings = ctx.resource_manager->load("wings-absent.dna"); ctx.logger->pop_task(EXIT_SUCCESS); ctx.logger->push_task("Building worker phenome"); - ant::phenome worker_phenome = ant::phenome(genome, ant::caste::worker); + ant::phenome worker_phenome = ant::phenome(*genome, ant::caste::worker); ctx.logger->pop_task(EXIT_SUCCESS); ctx.logger->push_task("Generating worker model"); diff --git a/src/game/world.cpp b/src/game/world.cpp index f7850fa..3453416 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -17,46 +17,59 @@ * along with Antkeeper source code. If not, see . */ -#include "game/world.hpp" -#include "scene/text.hpp" -#include "physics/light/vmag.hpp" +#include "application.hpp" #include "color/color.hpp" +#include "config.hpp" +#include "debug/logger.hpp" +#include "entity/archetype.hpp" +#include "entity/commands.hpp" #include "game/component/atmosphere.hpp" #include "game/component/blackbody.hpp" #include "game/component/celestial-body.hpp" +#include "game/component/observer.hpp" #include "game/component/orbit.hpp" #include "game/component/terrain.hpp" #include "game/component/transform.hpp" -#include "game/component/observer.hpp" #include "game/system/astronomy.hpp" -#include "game/system/orbit.hpp" #include "game/system/atmosphere.hpp" -#include "entity/commands.hpp" -#include "entity/archetype.hpp" +#include "game/system/orbit.hpp" +#include "game/system/terrain.hpp" +#include "game/world.hpp" +#include "geom/solid-angle.hpp" #include "geom/spherical.hpp" #include "gl/drawing-mode.hpp" +#include "gl/texture-filter.hpp" +#include "gl/texture-wrapping.hpp" #include "gl/vertex-array.hpp" #include "gl/vertex-attribute.hpp" #include "gl/vertex-buffer.hpp" +#include "math/hash/hash.hpp" +#include "math/noise/noise.hpp" #include "physics/light/photometry.hpp" -#include "physics/orbit/orbit.hpp" +#include "physics/light/vmag.hpp" #include "physics/orbit/ephemeris.hpp" -#include "physics/time/gregorian.hpp" +#include "physics/orbit/orbit.hpp" #include "physics/time/constants.hpp" +#include "physics/time/gregorian.hpp" #include "physics/time/utc.hpp" +#include "render/material-flags.hpp" #include "render/material.hpp" #include "render/model.hpp" +#include "render/passes/ground-pass.hpp" #include "render/passes/shadow-map-pass.hpp" +#include "render/passes/sky-pass.hpp" #include "render/vertex-attribute.hpp" +#include "resources/image.hpp" +#include "resources/json.hpp" #include "resources/resource-manager.hpp" #include "scene/ambient-light.hpp" #include "scene/directional-light.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "render/material-flags.hpp" -#include "geom/solid-angle.hpp" -#include "config.hpp" +#include "scene/text.hpp" +#include +#include +#include #include +#include namespace game { namespace world { @@ -524,5 +537,112 @@ void create_moon(game::context& ctx) ctx.logger->pop_task(EXIT_SUCCESS); } +void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion) +{ + /* + image img; + img.format(1, 4); + img.resize(2048, 2048); + + auto width = img.get_width(); + auto height = img.get_height(); + unsigned char* pixels = (unsigned char*)img.data(); + + const float frequency = 400.0f; + float scale_x = 1.0f / static_cast(width - 1) * frequency; + float scale_y = 1.0f / static_cast(height - 1) * frequency; + + std::for_each + ( + std::execution::par_unseq, + img.begin>(), + img.end>(), + [pixels, width, height, scale_x, scale_y, frequency](auto& pixel) + { + const std::size_t i = &pixel - (math::vector*)pixels; + const std::size_t y = i / width; + const std::size_t x = i % width; + + const float2 position = + { + static_cast(x) * scale_x, + static_cast(y) * scale_y + }; + + const auto + [ + f1_sqr_distance, + f1_displacement, + f1_id + ] = math::noise::voronoi::f1(position, 1.0f, {frequency, frequency}); + + const float f1_distance = std::sqrt(f1_sqr_distance); + + const float2 uv = (position + f1_displacement) / frequency; + + pixel = + { + static_cast(std::min(255.0f, f1_distance * 255.0f)), + static_cast(std::min(255.0f, uv[0] * 255.0f)), + static_cast(std::min(255.0f, uv[1] * 255.0f)), + static_cast(f1_id % 256) + }; + } + ); + + stbi_flip_vertically_on_write(1); + stbi_write_tga((ctx.config_path / "gallery" / "voronoi-f1-400-nc8-2k.tga").string().c_str(), img.get_width(), img.get_height(), img.get_channel_count(), img.data()); + */ + + + ctx.logger->push_task("Entering ecoregion " + ecoregion.name); + try + { + // Set active ecoregion + ctx.active_ecoregion = &ecoregion; + + // Set location + game::world::set_location(ctx, ecoregion.elevation, ecoregion.latitude, ecoregion.longitude); + + // Setup sky + ctx.sky_pass->set_sky_model(ctx.resource_manager->load("celestial-hemisphere.mdl")); + render::model* terrestrial_hemisphere_model = ctx.resource_manager->load("terrestrial-hemisphere.mdl"); + (*terrestrial_hemisphere_model->get_groups())[0]->set_material(ecoregion.horizon_material); + ctx.ground_pass->set_ground_model(terrestrial_hemisphere_model); + + // Setup terrain + ctx.terrain_system->set_patch_material(ecoregion.terrain_material); + ctx.terrain_system->set_elevation_function + ( + [](float x, float z) -> float + { + const float2 position = float2{x, z}; + + const std::size_t octaves = 3; + const float lacunarity = 1.5f; + const float gain = 0.5f; + + const float fbm = math::noise::fbm + ( + position * 0.005f, + octaves, + lacunarity, + gain + ); + + float y = fbm * 4.0f; + + return y; + } + ); + ctx.astronomy_system->set_bounce_albedo(double3(ecoregion.terrain_albedo)); + } + catch (...) + { + ctx.logger->pop_task(EXIT_FAILURE); + } + ctx.logger->pop_task(EXIT_SUCCESS); +} + } // namespace world } // namespace game diff --git a/src/game/world.hpp b/src/game/world.hpp index c76f99b..b1f32e1 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_GAME_WORLD_HPP #include "game/context.hpp" +#include "game/ecoregion.hpp" namespace game { @@ -72,6 +73,14 @@ void set_time(game::context& ctx, int year, int month, int day, int hour, int mi */ void set_time_scale(game::context& ctx, double scale); +/** + * Enters a ecoregion. + * + * @param ctx Game context. + * @param ecoregion Ecoregion to enter. + */ +void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion); + } // namespace menu } // namespace game