/* * 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/swarm.hpp" #include "game/component/transform.hpp" #include "game/component/steering.hpp" #include "game/component/model.hpp" #include "game/component/picking.hpp" #include "resources/resource-manager.hpp" #include "math/quaternion.hpp" #include "config.hpp" #include #include namespace game { namespace ant { /** * Generates a random point in a unit sphere. * * @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238 */ template static math::vector3 sphere_random(Generator& rng) { const std::uniform_real_distribution distribution(T{-1}, T{1}); math::vector3 position; for (std::size_t i = 0; i < 3; ++i) position[i] = distribution(rng); return math::normalize(position) * std::cbrt(distribution(rng)); } entity::id create_swarm(game::context& ctx) { // Determine swarm properties const float3 swarm_center = {0, 100, 0}; const float swarm_radius = 25.0f; const std::size_t male_count = 50; const std::size_t queen_count = 50; const std::size_t alate_count = male_count + queen_count; const float3 male_scale = {0.5, 0.5, 0.5}; const float3 queen_scale = {1, 1, 1}; // Init transform component game::component::transform transform; transform.local = math::transform::identity; transform.world = transform.local; transform.warp = true; // Init picking component game::component::picking picking; picking.sphere = {float3{0, 0, 0}, 0.5f * 2.0f}; std::uint32_t male_picking_flags = 0b01; std::uint32_t queen_picking_flags = 0b10; // Create swarm entity entity::id swarm_eid = ctx.entity_registry->create(); transform.local.translation = swarm_center; transform.world = transform.local; transform.warp = true; ctx.entity_registry->emplace(swarm_eid, transform); // Init male model component game::component::model male_model; male_model.render_model = ctx.resource_manager->load("male-boid.mdl"); male_model.instance_count = 0; male_model.layers = 1; // Init queen model component game::component::model queen_model; queen_model.render_model = ctx.resource_manager->load("queen-boid.mdl"); queen_model.instance_count = 0; queen_model.layers = 1; // Init steering component game::component::steering steering; steering.agent.mass = 1.0f; steering.agent.velocity = {0, 0, 0}; steering.agent.acceleration = {0, 0, 0}; steering.agent.max_force = 4.0f; steering.agent.max_speed = 5.0f; steering.agent.max_speed_squared = steering.agent.max_speed * steering.agent.max_speed; steering.agent.orientation = math::quaternion::identity(); steering.agent.forward = steering.agent.orientation * config::global_forward; steering.agent.up = steering.agent.orientation * config::global_up; steering.wander_weight = 1.0f; steering.wander_noise = math::radians(2000.0f); steering.wander_distance = 10.0f; steering.wander_radius = 8.0f; steering.wander_angle = 0.0f; steering.wander_angle2 = 0.0f; steering.seek_weight = 0.2f; steering.seek_target = swarm_center; steering.flee_weight = 0.0f; steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight; // Construct and seed random number generator std::random_device seed; std::mt19937 rng(seed()); // Create alates for (std::size_t i = 0; i < alate_count; ++i) { // Generate random position in swarm sphere steering.agent.position = swarm_center + sphere_random(rng) * swarm_radius; transform.local.translation = steering.agent.position; entity::id alate_eid = ctx.entity_registry->create(); ctx.entity_registry->emplace(alate_eid, steering); if (i < male_count) { // Create male ctx.entity_registry->emplace(alate_eid, male_model); transform.local.scale = male_scale; transform.world = transform.local; ctx.entity_registry->emplace(alate_eid, transform); picking.flags = male_picking_flags; ctx.entity_registry->emplace(alate_eid, picking); } else { // Create queen ctx.entity_registry->emplace(alate_eid, queen_model); transform.local.scale = queen_scale; transform.world = transform.local; ctx.entity_registry->emplace(alate_eid, transform); picking.flags = queen_picking_flags; ctx.entity_registry->emplace(alate_eid, picking); } } return swarm_eid; } void destroy_swarm(game::context& ctx, entity::id swarm_eid) { // Destroy alates auto view = ctx.entity_registry->view(); ctx.entity_registry->destroy(view.begin(), view.end()); // Destroy swarm ctx.entity_registry->destroy(swarm_eid); } } // namespace ant } // namespace game