diff --git a/CMakeLists.txt b/CMakeLists.txt index 7330ea3..591faef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,8 +223,8 @@ 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-matrix.hpp + ${EXECUTABLE_SOURCE_DIR}/game/pheromone-matrix.cpp ${EXECUTABLE_SOURCE_DIR}/game/level.hpp ${EXECUTABLE_SOURCE_DIR}/game/level.cpp ${EXECUTABLE_SOURCE_DIR}/game/biome.hpp diff --git a/src/application.cpp b/src/application.cpp index a7b483d..15a3c86 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -26,6 +26,7 @@ #include "states/title-state.hpp" #include "states/game-state.hpp" #include "game/colony.hpp" +#include "game/pheromone-matrix.hpp" #include "game/tool.hpp" #include "ui/menu.hpp" #include "ui/toolbar.hpp" @@ -456,7 +457,7 @@ int Application::execute() else { // Execute current state - while (accumulator >= dt) + //while (accumulator >= dt) { state->execute(); @@ -467,8 +468,8 @@ int Application::execute() // Perform tweening tweener->update(dt); - accumulator -= dt; - t += dt; + //accumulator -= dt; + //t += dt; } } @@ -859,6 +860,27 @@ bool Application::loadScene() framebufferBRenderTarget.framebuffer = framebufferA; } + // Pheromone PBO + { + glGenBuffers(1, &pheromonePBO); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pheromonePBO); + glBufferData(GL_PIXEL_UNPACK_BUFFER, 4 * PHEROMONE_MATRIX_COLUMNS * PHEROMONE_MATRIX_ROWS, nullptr, GL_DYNAMIC_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + glGenTextures(1, &pheromoneTextureID); + glBindTexture(GL_TEXTURE_2D, pheromoneTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PHEROMONE_MATRIX_COLUMNS, PHEROMONE_MATRIX_ROWS, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); + glBindTexture(GL_TEXTURE_2D, 0); + + pheromoneTexture.setWidth(PHEROMONE_MATRIX_COLUMNS); + pheromoneTexture.setHeight(PHEROMONE_MATRIX_ROWS); + pheromoneTexture.setTextureID(pheromoneTextureID); + } + // Setup skybox pass skyboxPass.setRenderTarget(&framebufferARenderTarget); @@ -1592,6 +1614,7 @@ bool Application::loadGame() lens->setSunDirection(glm::normalize(-sunlightCamera.getTranslation())); brush = new Brush(brushModel); + brush->setColony(colony); brush->setCameraController(surfaceCam); loadWorld(0); @@ -2029,7 +2052,11 @@ void Application::loadLevel(std::size_t index) // Load level const LevelParameterSet* levelParams = campaign.getLevelParams(currentWorldIndex, currentLevelIndex); currentLevel->load(*levelParams); - currentLevel->terrain.getSurfaceModel()->getGroup(0)->material = materialLoader->load("data/materials/debug-terrain-surface.mtl"); + + PhysicalMaterial* material = materialLoader->load("data/materials/debug-terrain-surface.mtl"); + material->albedoOpacityMap = &pheromoneTexture; + + currentLevel->terrain.getSurfaceModel()->getGroup(0)->material = material; } /* diff --git a/src/application.hpp b/src/application.hpp index e3a205c..2124e67 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -186,6 +186,10 @@ public: GLuint framebufferB; RenderTarget framebufferBRenderTarget; + GLuint pheromonePBO; + GLuint pheromoneTextureID; + Texture pheromoneTexture; + ClearRenderPass clearDepthPass; SoilRenderPass soilPass; LightingRenderPass lightingPass; diff --git a/src/configuration.hpp.in b/src/configuration.hpp.in index 86ebcf0..837231d 100644 --- a/src/configuration.hpp.in +++ b/src/configuration.hpp.in @@ -20,6 +20,9 @@ #ifndef CONFIGURATION_HPP #define CONFIGURATION_HPP +#include +using namespace Emergent; + #define ANTKEEPER_VERSION_MAJOR @ANTKEEPER_VERSION_MAJOR@ #define ANTKEEPER_VERSION_MINOR @ANTKEEPER_VERSION_MINOR@ #define ANTKEEPER_VERSION_PATCH @ANTKEEPER_VERSION_PATCH@ @@ -39,4 +42,21 @@ const int ANTKEEPER_UI_LAYER_MENU = 20; const int ANTKEEPER_UI_LAYER_DARKEN = 10; const int ANTKEEPER_UI_LAYER_HUD = 0; +const float WORLD_WIDTH = 100.0f; // cm +const float WORLD_HEIGHT = 100.0f; // cm +const float FRAMES_PER_SECOND = 60.0f; +const float TIMESTEP = 1.0f / FRAMES_PER_SECOND; +const float PHEROMONE_MATRIX_RESOLUTION = 4.0f; +const int PHEROMONE_MATRIX_COLUMNS = (int)(WORLD_WIDTH * PHEROMONE_MATRIX_RESOLUTION); +const int PHEROMONE_MATRIX_ROWS = (int)(WORLD_HEIGHT * PHEROMONE_MATRIX_RESOLUTION); +const Vector2 WORLD_BOUNDS_MIN = Vector2(-WORLD_WIDTH * 0.5f, -WORLD_HEIGHT * 0.5f); +const Vector2 WORLD_BOUNDS_MAX = Vector2(WORLD_WIDTH * 0.5f, WORLD_HEIGHT * 0.5f); +const float BRUSH_RADIUS = 0.5f; +const float EVAPORATION_FACTOR = 0.99925f; +const float DIFFUSIONS_PER_SECOND = 4.0f; +const int DIFFUSION_FRAME = static_cast(FRAMES_PER_SECOND / DIFFUSIONS_PER_SECOND); +const float HOMING_PHEROMONE_COLOR[] = {0.55f, 0.55f, 0.00f, 0.0f}; // CMYK +const float RECRUITMENT_PHEROMONE_COLOR[] = {0.00f, 0.55f, 0.55f, 0.0f}; // CMYK +const float ALARM_PHEROMONE_COLOR[] = {0.00f, 0.00f, 1.00f, 0.0f}; // CMYK + #endif // CONFIGURATION_HPP diff --git a/src/game/agent.cpp b/src/game/agent.cpp index 858c6ae..255240c 100644 --- a/src/game/agent.cpp +++ b/src/game/agent.cpp @@ -26,11 +26,12 @@ Agent::Agent(): forward(0, 0, -1), up(0, 1, 0), right(1, 0, 0), - rotation(1, 0, 0, 0), - wanderDirection(0, 0, -1), - velocity(0.0f) + rotation(1, 0, 0, 0) + //wanderDirection(0, 0, -1), + //velocity(0.0f) {} +/* void Agent::applyForce(const Vector3& force) { acceleration += force; @@ -93,21 +94,19 @@ Vector3 Agent::containment(const Vector3& probe) const return Vector3(0.0f); } - /* // Calculate difference between probe position and position on edge - Vector3 end = cartesian(step.end, - step.triangle->edge->vertex->position, - step.triangle->edge->next->vertex->position, - step.triangle->edge->previous->vertex->position); + //Vector3 end = cartesian(step.end, + // step.triangle->edge->vertex->position, + // step.triangle->edge->next->vertex->position, + // step.triangle->edge->previous->vertex->position); - Vector3 difference = probe - end; + //Vector3 difference = probe - end; - float depth = 0.0f; - if (nonzero(difference)) - { - depth = glm::length(difference); - } - */ + //float depth = 0.0f; + //if (nonzero(difference)) + //{ + // depth = glm::length(difference); + //} // Calculate edge normal const Vector3& a = step.edge->vertex->position; @@ -118,12 +117,10 @@ Vector3 Agent::containment(const Vector3& probe) const // Calculate reflection vector of forward vector and edge normal //Vector3 reflection = glm::reflect(forward, edgeNormal); - /* - Vector3 target = cartesian(step.end, - step.triangle->edge->vertex->position, - step.triangle->edge->next->vertex->position, - step.triangle->edge->previous->vertex->position) + reflection * 0.1f; - */ + //Vector3 target = cartesian(step.end, + // step.triangle->edge->vertex->position, + // step.triangle->edge->next->vertex->position, + // step.triangle->edge->previous->vertex->position) + reflection * 0.1f; //std::cout << "reflection: " << reflection.x << ", " << reflection.y << ", " << reflection.z << std::endl; @@ -152,6 +149,7 @@ Vector3 Agent::separation(const std::list& neighbors) const return force; } +*/ void Agent::setPosition(Navmesh::Triangle* triangle, const Vector3& position) { @@ -180,9 +178,10 @@ void Agent::setOrientation(const Vector3& newForward, const Vector3& newUp) rotation = glm::normalize(glm::quat_cast(Matrix3(right, up, forward))); // Align wander direction - wanderDirection = glm::normalize(project_on_plane(alignment * wanderDirection, Vector3(0.0f), up)); + //wanderDirection = glm::normalize(project_on_plane(alignment * wanderDirection, Vector3(0.0f), up)); } +/* void Agent::setMaxSpeed(float speed) { maxSpeed = speed; @@ -213,6 +212,7 @@ void Agent::setSeparationRadius(float radius) separationRadius = radius; separationRadiusSquared = separationRadius * separationRadius; } +*/ /** EXAMPLE USAGE Vector3 wanderForce = wander(dt) * wanderWeight; diff --git a/src/game/agent.hpp b/src/game/agent.hpp index 75b5298..bc508af 100644 --- a/src/game/agent.hpp +++ b/src/game/agent.hpp @@ -54,40 +54,42 @@ class Agent public: Agent(); + + /** * Adds a force to the agent's acceleration vector. * * @param force Acceleration force */ - void applyForce(const Vector3& force); + //void applyForce(const Vector3& force); /** * Calculates velocity based on current acceleration vector, then resets acceleration to zero. */ - void updateVelocity(); + //void updateVelocity(); /** * Calculates steering force for the wander behavior. */ - Vector3 wander(float dt); + //Vector3 wander(float dt); /** * Calculates steering force for the seek behavior. */ - Vector3 seek(const Vector3& target) const; + //Vector3 seek(const Vector3& target) const; /** * Calculates steering force for the flee behavior. */ - Vector3 flee(const Vector3& target) const; - - Vector3 containment(const Vector3& probe) const; + //Vector3 flee(const Vector3& target) const; - Vector3 separation(const std::list& neighbors) const; + //Vector3 containment(const Vector3& probe) const; - Vector3 forage(const Vector3& leftProbe, const Vector3& rightProbe); + //Vector3 separation(const std::list& neighbors) const; + //Vector3 forage(const Vector3& leftProbe, const Vector3& rightProbe); + /* void setMaxSpeed(float speed); void setVelocity(const Vector3& velocity); void setMaxAcceleration(float acceleration); @@ -96,6 +98,7 @@ public: void setWanderCircleRadius(float radius); void setWanderRate(float angle); void setSeparationRadius(float radius); + */ /** * Sets the position of the agent on a navmesh. @@ -132,7 +135,7 @@ public: const Vector3& getRight() const; const Quaternion& getRotation() const; - const Vector3& getVelocity() const; + //const Vector3& getVelocity() const; private: Navmesh::Triangle* navmeshTriangle; @@ -143,6 +146,7 @@ private: Vector3 right; Quaternion rotation; + /* // Limits float maxSpeed; float maxAcceleration; @@ -159,6 +163,7 @@ private: Vector3 wanderDirection; float separationRadius; float separationRadiusSquared; + */ }; inline const Navmesh::Triangle* Agent::getNavmeshTriangle() const @@ -201,9 +206,11 @@ inline const Quaternion& Agent::getRotation() const return rotation; } +/* inline const Vector3& Agent::getVelocity() const { return velocity; } +*/ #endif // AGENT_HPP \ No newline at end of file diff --git a/src/game/ant.cpp b/src/game/ant.cpp index fa676e3..87a4fe4 100644 --- a/src/game/ant.cpp +++ b/src/game/ant.cpp @@ -19,9 +19,39 @@ #include "ant.hpp" #include "colony.hpp" -#include "pheromone.hpp" +#include "pheromone-matrix.hpp" #include +float FRAMES_PER_SECOND = 60; +float TIMESTEP = 1.0f / FRAMES_PER_SECOND; +float ANT_LENGTH = 0.5f; // 0.5 cm, head to abdomen (not including legs / antennae) +float ANT_COLLISION_RADIUS = ANT_LENGTH * 1.25f; +float RECEPTOR_RADIUS = 0.4f; +float RECEPTOR_SEPARATION = 0.882f; +float RECEPTOR_DISTANCE = 0.588f; +float MOUTH_DISTANCE = 0.2646f; +float BITE_RADIUS = 0.0294f; +float FOOD_PARTICLE_RADIUS = 0.1176f; +float MAX_RECEPTOR_NOISE = 0.05f; // essentially an epsilon +float MAX_EXCITEMENT = 1.0f; +float MAX_PHEROMONE_TURNING_ANGLE = glm::radians(8.5f); +float MIN_WALK_TIME = 0.5f; // seconds +float MAX_WALK_TIME = 8.0f; // seconds +float MIN_REST_TIME = 0.15f; +float MAX_REST_TIME = 0.7f; +float MIN_CHEW_TIME = 0.25f; +float MAX_CHEW_TIME = 0.5f; +float DEEXCITEMENT_FACTOR = 0.999f; // This should probably always be less than the evaporation factor +float CALM_FACTOR = 0.995f; +float MAX_WALK_FORCE = 1.5; +float MAX_PANIC_FORCE = 0.1029f; +float MAX_WALK_SPEED = 3.0f; // cm/s +float MAX_PANIC_SPEED = 8.82f; // cm/s +float PANIC_RADIUS = 7.35f; +float WANDER_CIRCLE_DISTANCE = 0.441f; +float WANDER_CIRCLE_RADIUS = 0.0294f; +float MAX_WANDER_ANGLE = 0.15f; + inline float fwrap(float angle, float limit) { return angle - std::floor(angle / limit) * limit; @@ -41,6 +71,11 @@ Ant::Ant(Colony* colony): modelInstance.setPose(pose); animationTime = frand(0.0f, 60.0f); + + velocity = Vector3(0); + acceleration = Vector3(0); + wanderDirection = getForward(); + excitement = MAX_EXCITEMENT; } Ant::~Ant() @@ -52,7 +87,7 @@ void Ant::animate() { colony->getTripodGaitAnimation()->animate(pose, animationTime); pose->concatenate(); - animationTime = fwrap(animationTime + 2.0f, colony->getTripodGaitAnimation()->getEndTime()); + animationTime = fwrap(animationTime + 4.0f, colony->getTripodGaitAnimation()->getEndTime()); } void Ant::suspend(const Vector3& suspensionPoint, const Quaternion& suspensionRotation) @@ -100,57 +135,77 @@ void Ant::update(float dt) animate(); + // Calculate positions of receptors + receptorL = getPosition() + getForward() * RECEPTOR_DISTANCE; + receptorR = receptorL; + receptorL -= getRight() * RECEPTOR_SEPARATION * 0.5f; + receptorR += getRight() * RECEPTOR_SEPARATION * 0.5f; + // Steering if (state == Ant::State::WANDER) { - setWanderCircleDistance(4.0f); - setWanderCircleRadius(0.3f); - setWanderRate(glm::radians(90.0f)); - setSeparationRadius(0.5f); - setMaxSpeed(0.025f); + //setWanderCircleDistance(4.0f); + //setWanderCircleRadius(0.3f); + //setWanderRate(glm::radians(90.0f)); + //setSeparationRadius(0.5f); + //setMaxSpeed(0.025f); // Calculate wander force - Vector3 wanderForce = wander(dt); + Vector3 wanderForce = wander() * 1.5f; + Vector3 followForce = follow() * 3.0f; // Setup containment probes - Vector3 leftProbe = getForward() * probeForwardOffset - getRight() * probeLateralOffset; - Vector3 rightProbe = getForward() * probeForwardOffset + getRight() * probeLateralOffset; + //Vector3 leftProbe = getForward() * probeForwardOffset - getRight() * probeLateralOffset; + //Vector3 rightProbe = getForward() * probeForwardOffset + getRight() * probeLateralOffset; // Calculate containment force - Vector3 containmentForce = containment(leftProbe) + containment(rightProbe); + //Vector3 containmentForce = containment(leftProbe) + containment(rightProbe); // Determine neighbors - float neighborhoodSize = 2.0f; - AABB neighborhoodAABB(getPosition() - Vector3(neighborhoodSize * 0.5f), getPosition() + Vector3(neighborhoodSize * 0.5f)); - std::list neighbors; - colony->queryAnts(neighborhoodAABB, &neighbors); + //float neighborhoodSize = 2.0f; + //AABB neighborhoodAABB(getPosition() - Vector3(neighborhoodSize * 0.5f), getPosition() + Vector3(neighborhoodSize * 0.5f)); + //std::list neighbors; + //colony->queryAnts(neighborhoodAABB, &neighbors); // Calculate separation force - Vector3 separationForce = separation(neighbors); + //Vector3 separationForce = separation(neighbors); + + applyForce(wanderForce); + applyForce(followForce); + + float maxSpeed = MAX_WALK_SPEED * TIMESTEP; + + // Limit acceleration + float accelerationMagnitudeSquared = glm::dot(acceleration, acceleration); + if (accelerationMagnitudeSquared > MAX_WALK_FORCE * MAX_WALK_FORCE) + { + acceleration = glm::normalize(acceleration) * MAX_WALK_FORCE; + } + + // Accelerate + velocity += acceleration; + + // Limit speed + float speedSquared = glm::dot(velocity, velocity); + if (speedSquared > maxSpeed * maxSpeed) + { + velocity = glm::normalize(velocity) * maxSpeed; + } - // Calculate velocity - Vector3 velocity = getVelocity(); - velocity += wanderForce; - velocity += containmentForce * 0.0025f; - velocity += separationForce * 0.01f; - velocity = limit(velocity, 0.025f); - setVelocity(velocity); + Vector3 direction = glm::normalize(velocity); + setOrientation(direction, getUp()); - setOrientation(glm::normalize(velocity), getUp()); + // Deposit pheromones + Vector2 position2D = Vector2(getPosition().x, getPosition().z); + colony->getHomingMatrix()->deposit(position2D, excitement); + excitement *= DEEXCITEMENT_FACTOR; // Move ant move(velocity); } else if (state == Ant::State::IDLE) { - Vector3 leftProbe = getForward() * probeForwardOffset - getRight() * probeLateralOffset; - Vector3 rightProbe = getForward() * probeForwardOffset + getRight() * probeLateralOffset; - Vector3 containmentForce = containment(leftProbe) + containment(rightProbe); - Vector3 velocity = Vector3(0.0f); - velocity += containmentForce; - velocity = limit(velocity, 0.025f); - setVelocity(velocity); - //setOrientation(glm::normalize(velocity), getUp()); + velocity = Vector3(0.0f); // Move ant move(velocity); @@ -181,6 +236,7 @@ void Ant::update(float dt) */ } +/* Vector3 Ant::forage(const Vector3& leftReceptor, const Vector3& rightReceptor) { float leftSignal = 0.0f; @@ -233,8 +289,74 @@ Vector3 Ant::forage(const Vector3& leftReceptor, const Vector3& rightReceptor) return Vector3(0.0f); } +*/ void Ant::setState(Ant::State state) { this->state = state; } + +Vector3 Ant::seek(const Vector3& target) +{ + Vector3 steer(0.0f); + Vector3 difference = target - getPosition(); + + float distanceSquared = glm::dot(difference, difference); + if (distanceSquared > 0.0f) + { + float maxForce = MAX_WALK_FORCE; + + steer = glm::normalize(difference) * maxForce - velocity; + } + + return steer; +} + +Vector3 Ant::flee(const Vector3& target) +{ + return -seek(target); +} + +Vector3 Ant::wander() +{ + // Determine center of wander circle + Vector3 center = getPosition() + getForward() * WANDER_CIRCLE_DISTANCE; + + // Calculate wander target + Vector3 target = center + wanderDirection * WANDER_CIRCLE_RADIUS; + + // Rotate wander direction by a random displacement angle + float displacement = frand(-MAX_WANDER_ANGLE, MAX_WANDER_ANGLE); + wanderDirection = glm::normalize(glm::angleAxis(displacement, getUp()) * wanderDirection); + + return seek(target); +} + +Vector3 Ant::follow() +{ + const PheromoneMatrix* pheromoneMatrix = colony->getRecruitmentMatrix(); + + Vector2 receptorL2D = Vector2(receptorL.x, receptorL.z); + Vector2 receptorR2D = Vector2(receptorR.x, receptorR.z); + + float signalL = pheromoneMatrix->query(receptorL2D, RECEPTOR_RADIUS); + signalL += frand(0.0f, MAX_RECEPTOR_NOISE); + + float signalR = pheromoneMatrix->query(receptorR2D, RECEPTOR_RADIUS); + signalR += frand(0.0f, MAX_RECEPTOR_NOISE); + + if (signalL + signalR > 0.0f) + { + float angle = -MAX_PHEROMONE_TURNING_ANGLE * ((signalL - signalR) / (signalL + signalR)); + + Vector3 steer = glm::normalize(glm::angleAxis(angle, getUp()) * getForward()); + return steer; + } + + return Vector3(0.0f); +} + +void Ant::applyForce(const Vector3& force) +{ + acceleration += force; +} diff --git a/src/game/ant.hpp b/src/game/ant.hpp index 2bc02b1..ac80911 100644 --- a/src/game/ant.hpp +++ b/src/game/ant.hpp @@ -87,6 +87,16 @@ public: const ModelInstance* getModelInstance() const; ModelInstance* getModelInstance(); + + + + // Boid functions + Vector3 seek(const Vector3& target); + Vector3 flee(const Vector3& target); + Vector3 wander(); + Vector3 follow(); + void applyForce(const Vector3& force); + private: Vector3 forage(const Vector3& leftReceptor, const Vector3& rightReceptor); @@ -104,6 +114,17 @@ private: Transform transform; ModelInstance modelInstance; Pose* pose; + + // Boid variables + //Vector3 position; + //Quaternion rotation; + //Vector3 forward; + Vector3 velocity; + Vector3 acceleration; + Vector3 wanderDirection; + float excitement; + Vector3 receptorL; + Vector3 receptorR; }; inline const Colony* Ant::getColony() const diff --git a/src/game/colony.cpp b/src/game/colony.cpp index e7c34e3..3c842de 100644 --- a/src/game/colony.cpp +++ b/src/game/colony.cpp @@ -19,7 +19,7 @@ #include "colony.hpp" #include "ant.hpp" -#include "pheromone.hpp" +#include "pheromone-matrix.hpp" #include "../configuration.hpp" Colony::Colony(): @@ -31,14 +31,18 @@ Colony::Colony(): AABB octreeBounds(octreeMin, octreeMax); antOctree = new Octree(5, octreeBounds); - pheromoneOctree = new Octree(5, octreeBounds); + + // Create pheromone matrices + homingMatrix = new PheromoneMatrix(PHEROMONE_MATRIX_COLUMNS, PHEROMONE_MATRIX_ROWS, WORLD_BOUNDS_MIN, WORLD_BOUNDS_MAX); + recruitmentMatrix = new PheromoneMatrix(PHEROMONE_MATRIX_COLUMNS, PHEROMONE_MATRIX_ROWS, WORLD_BOUNDS_MIN, WORLD_BOUNDS_MAX); } Colony::~Colony() { killAll(); delete antOctree; - delete pheromoneOctree; + delete homingMatrix; + delete recruitmentMatrix; } Ant* Colony::spawn(Navmesh* navmesh, Navmesh::Triangle* triangle, const Vector3& position) @@ -91,7 +95,8 @@ void Colony::queryAnts(const BoundingVolume& volume, std::list* results) void Colony::killAll() { antOctree->clear(); - pheromoneOctree->clear(); + homingMatrix->clear(); + recruitmentMatrix->clear(); for (Ant* ant: ants) { diff --git a/src/game/colony.hpp b/src/game/colony.hpp index 8d0284e..2275d27 100644 --- a/src/game/colony.hpp +++ b/src/game/colony.hpp @@ -30,6 +30,7 @@ class Ant; class Agent; class Pheromone; class Gait; +class PheromoneMatrix; /** * A colony of ants. @@ -59,8 +60,13 @@ public: const Octree* getAntOctree() const; - const Octree* getPheromoneOctree() const; + const PheromoneMatrix* getHomingMatrix() const; + PheromoneMatrix* getHomingMatrix(); + + const PheromoneMatrix* getRecruitmentMatrix() const; + PheromoneMatrix* getRecruitmentMatrix(); + private: // Rendering Model* antModel; @@ -77,8 +83,8 @@ private: std::vector ants; Octree* antOctree; - std::vector pheromones; - Octree* pheromoneOctree; + PheromoneMatrix* homingMatrix; + PheromoneMatrix* recruitmentMatrix; }; inline const Model* Colony::getAntModel() const @@ -117,9 +123,24 @@ inline const Octree* Colony::getAntOctree() const return antOctree; } -inline const Octree* Colony::getPheromoneOctree() const +inline const PheromoneMatrix* Colony::getHomingMatrix() const +{ + return homingMatrix; +} + +inline PheromoneMatrix* Colony::getHomingMatrix() +{ + return homingMatrix; +} + +inline const PheromoneMatrix* Colony::getRecruitmentMatrix() const +{ + return recruitmentMatrix; +} + +inline PheromoneMatrix* Colony::getRecruitmentMatrix() { - return pheromoneOctree; + return recruitmentMatrix; } #endif // COLONY_HPP \ No newline at end of file diff --git a/src/game/pheromone-matrix.cpp b/src/game/pheromone-matrix.cpp new file mode 100644 index 0000000..cc34592 --- /dev/null +++ b/src/game/pheromone-matrix.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2017 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 "pheromone-matrix.hpp" +#include "../configuration.hpp" +#include +#include + +PheromoneMatrix::PheromoneMatrix(int columns, int rows, const Vector2& boundsMin, const Vector2& boundsMax) +{ + this->columns = columns; + this->rows = rows; + size = columns * rows; + bufferA = new float[size]; + bufferB = new float[size]; + activeBuffer = bufferA; + this->boundsMin = boundsMin; + this->boundsMax = boundsMax; + matrixWidth = boundsMax.x - boundsMin.x; + matrixHeight = boundsMax.y - boundsMin.y; + matrixHalfWidth = matrixWidth * 0.5f; + matrixHalfHeight = matrixHeight * 0.5f; + cellWidth = matrixWidth / static_cast(columns); + cellHeight = matrixHeight / static_cast(rows); + + diffusionKernelSize = 3; + diffusionKernelRadius = 1; + diffusionKernel = new float*[diffusionKernelSize]; + for (int i = 0; i < diffusionKernelSize; ++i) + { + diffusionKernel[i] = new float[diffusionKernelSize]; + } + + diffusionKernel[0][0] = 0.0083333f; diffusionKernel[0][1] = 0.0166667f; diffusionKernel[0][2] = 0.0083333f; + + diffusionKernel[1][0] = 0.0166667f; diffusionKernel[1][1] = 0.9f; diffusionKernel[1][2] = 0.0166667f; + + diffusionKernel[2][0] = 0.0083333f; diffusionKernel[2][1] = 0.0166667f; diffusionKernel[2][2] = 0.0083333f; + + clear(); +} + +PheromoneMatrix::~PheromoneMatrix() +{ + delete[] bufferA; + delete[] bufferB; + + for (int i = 0; i < diffusionKernelSize; ++i) + { + delete[] diffusionKernel[i]; + } + delete[] diffusionKernel; +} + +void PheromoneMatrix::clear() +{ + for (int i = 0; i < size; ++i) + { + activeBuffer[i] = 0.0f; + } +} + +void PheromoneMatrix::evaporate() +{ + for (int i = 0; i < size; ++i) + { + activeBuffer[i] *= EVAPORATION_FACTOR; + } +} + +void PheromoneMatrix::diffuse() +{ + float* diffusionBuffer = (activeBuffer == bufferA) ? bufferB : bufferA; + + int index = 0; + for (int i = 0; i < rows; ++i) + { + for (int j = 0; j < columns; ++j) + { + float concentration = 0.0f; + + for (int k = -diffusionKernelRadius; k <= diffusionKernelRadius; ++k) + { + int row = std::max(0, std::min(rows - 1, i + k)); + + for (int l = -diffusionKernelRadius; l <= diffusionKernelRadius; ++l) + { + int column = std::max(0, std::min(columns - 1, j + l)); + concentration += activeBuffer[row * columns + column] * diffusionKernel[k + diffusionKernelRadius][l + diffusionKernelRadius]; + } + } + + diffusionBuffer[index++] = concentration; + } + } + + activeBuffer = diffusionBuffer; +} + +float PheromoneMatrix::query(const Vector2& position) const +{ + int column = static_cast((matrixHalfWidth + position.x) / cellWidth); + int row = static_cast((matrixHalfHeight + position.y) / cellHeight); + + if (columns < 0 || column >= columns || row < 0 || row >= rows) + { + return 0.0f; + } + + int index = row * columns + column; + return activeBuffer[index]; +} + +float PheromoneMatrix::query(const Vector2& position, float radius) const +{ + float radiusSquared = radius * radius; + float concentration = 0.0f; + + for (float y = position.y - radius; y <= position.y + radius; y += cellHeight) + { + int row = static_cast((matrixHalfHeight + y) / cellHeight); + if (row < 0) + continue; + else if (row >= rows) + break; + + float dy = y - position.y; + + for (float x = position.x - radius; x <= position.x + radius; x += cellWidth) + { + int column = floor((matrixHalfWidth + x) / cellWidth); + if (column < 0) + continue; + else if (column >= columns) + break; + + float dx = x - position.x; + + float distanceSquared = dx * dx + dy * dy; + if (distanceSquared <= radiusSquared) + { + concentration += activeBuffer[row * columns + column]; + } + } + } + + return concentration; +} + +void PheromoneMatrix::deposit(const Vector2& position, float concentration) +{ + int column = static_cast((matrixHalfWidth + position.x) / cellWidth); + int row = static_cast((matrixHalfHeight + position.y) / cellHeight); + + if (columns < 0 || column >= columns || row < 0 || row >= rows) + { + return; + } + + int index = row * columns + column; + activeBuffer[index] += concentration; +} diff --git a/src/game/pheromone-matrix.hpp b/src/game/pheromone-matrix.hpp new file mode 100644 index 0000000..ce463bb --- /dev/null +++ b/src/game/pheromone-matrix.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 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 PHEROMONE_MATRIX_HPP +#define PHEROMONE_MATRIX_HPP + +#include +using namespace Emergent; + +class PheromoneMatrix +{ +public: + PheromoneMatrix(int columns, int rows, const Vector2& boundsMin, const Vector2& boundsMax); + + ~PheromoneMatrix(); + + void clear(); + void evaporate(); + void diffuse(); + + float query(const Vector2& position) const; + float query(const Vector2& position, float radius) const; + + void deposit(const Vector2& position, float concentration); + + float getCellWidth() const; + float getCellHeight() const; + + const float* getActiveBuffer() const; + +private: + int columns; + int rows; + int size; + float* bufferA; + float* bufferB; + float* activeBuffer; + float evaporationFactor; + Vector2 boundsMin; + Vector2 boundsMax; + float matrixWidth; + float matrixHeight; + float matrixHalfWidth; + float matrixHalfHeight; + float cellWidth; + float cellHeight; + int diffusionKernelRadius; + int diffusionKernelSize; + float** diffusionKernel; +}; + +inline float PheromoneMatrix::getCellWidth() const +{ + return cellWidth; +} + +inline float PheromoneMatrix::getCellHeight() const +{ + return cellHeight; +} + +inline const float* PheromoneMatrix::getActiveBuffer() const +{ + return activeBuffer; +} + +#endif // PHEROMONE_MATRIX_HPP \ No newline at end of file diff --git a/src/game/pheromone.cpp b/src/game/pheromone.cpp deleted file mode 100644 index a60ca90..0000000 --- a/src/game/pheromone.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2017 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 "pheromone.hpp" -#include - -void Pheromone::setStrength(float strength) -{ - this->strength = strength; - - // Calculate approximate radius - const float minimumStrength = 0.01f; // Pheromone strength epsilon - const float quadraticAttenuation = 1.0f; // Quadratic attenuation factor (1.0 = inverse-square falloff) - - radiusSquared = strength / (quadraticAttenuation * minimumStrength); - radius = std::sqrt(radiusSquared); -} - -float Pheromone::getAttenuatedStrength(const Vector3& position) const -{ - Vector3 difference = this->position - position; - - float distanceSquared = glm::dot(difference, difference); - - if (distanceSquared == 0.0f) - { - return strength; - } - - const float minimumStrength = 0.01f; // Pheromone strength epsilon - const float quadraticAttenuation = 1.0f; // Quadratic attenuation factor (1.0 = inverse-square falloff) - - return strength / (1.0f + quadraticAttenuation * distanceSquared); -} \ No newline at end of file diff --git a/src/game/pheromone.hpp b/src/game/pheromone.hpp deleted file mode 100644 index 7f6aaf2..0000000 --- a/src/game/pheromone.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2017 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 PHEROMONE_HPP -#define PHEROMONE_HPP - -#include -using namespace Emergent; - -class Pheromone -{ -public: - void setPosition(const Vector3& position); - void setStrength(float strength); - - const Vector3& getPosition() const; - float getStrength() const; - float getRadius() const; - float getRadiusSquared() const; - - float getAttenuatedStrength(const Vector3& position) const; - -private: - Vector3 position; - float strength; - float radius; - float radiusSquared; -}; - -inline void Pheromone::setPosition(const Vector3& position) -{ - this->position = position; -} - -inline const Vector3& Pheromone::getPosition() const -{ - return position; -} - -inline float Pheromone::getStrength() const -{ - return strength; -} - -inline float Pheromone::getRadius() const -{ - return radius; -} - -inline float Pheromone::getRadiusSquared() const -{ - return radiusSquared; -} - -#endif // PHEROMONE_HPP \ No newline at end of file diff --git a/src/game/terrain.cpp b/src/game/terrain.cpp index 7459309..c88d6b0 100644 --- a/src/game/terrain.cpp +++ b/src/game/terrain.cpp @@ -54,8 +54,8 @@ void Terrain::createSurface() *(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; + *(data++) = static_cast(j) / static_cast(columns); + *(data++) = static_cast(i) / static_cast(rows); } } diff --git a/src/game/tool.cpp b/src/game/tool.cpp index 86c9407..59a93b8 100644 --- a/src/game/tool.cpp +++ b/src/game/tool.cpp @@ -1,7 +1,10 @@ #include "tool.hpp" #include "ant.hpp" #include "colony.hpp" +#include "navmesh.hpp" +#include "pheromone-matrix.hpp" #include "../camera-controller.hpp" +#include "../configuration.hpp" #include #include @@ -256,6 +259,11 @@ void Forceps::setColony(Colony* colony) this->colony = colony; } +void Forceps::setNavmesh(Navmesh* navmesh) +{ + this->navmesh = navmesh; +} + void Forceps::pinch() { // Change state to pinching @@ -323,14 +331,22 @@ void Forceps::release() if (suspendedAnt != nullptr) { - /* - auto result = intersects(pickingRay, pickTriangle); - if (std::get<0>(result)) + Ray pickingRay; + pickingRay.origin = pick + Vector3(0, 1, 0); + pickingRay.direction = Vector3(0, -1, 0); + + const std::vector* navmeshTriangles = navmesh->getTriangles(); + for (Navmesh::Triangle* triangle: *navmeshTriangles) { - Vector3 barycentricPosition = Vector3(std::get<2>(result), std::get<3>(result), 1.0f - std::get<2>(result) - std::get<3>(result)); - pickAnt->setPosition(pickTriangle, barycentricPosition); + auto result = intersects(pickingRay, triangle); + if (std::get<0>(result)) + { + Vector3 barycentricPosition = Vector3(std::get<2>(result), std::get<3>(result), 1.0f - std::get<2>(result) - std::get<3>(result)); + suspendedAnt->setPosition(triangle, barycentricPosition); + + break; + } } - */ // Release suspended ant suspendedAnt->setState(Ant::State::WANDER); @@ -492,6 +508,7 @@ Brush::Brush(const Model* model) [this](float t) { descended = true; + paint(Vector2(pick.x, pick.z), BRUSH_RADIUS); } ); tweener->addTween(descentTween); @@ -503,6 +520,8 @@ Brush::Brush(const Model* model) targetTiltAngle = 0.0f; tiltAxis = Vector3(1.0f, 0.0f, 0.0f); targetTiltAxis = tiltAxis; + + colony = nullptr; } Brush::~Brush() @@ -536,6 +555,7 @@ void Brush::update(float dt) Vector3 difference = pick - oldPick; float distanceSquared = glm::dot(difference, difference); + // Calculate tilt if (distanceSquared > 0.005f) { float maxDistance = 0.25f; @@ -546,6 +566,34 @@ void Brush::update(float dt) targetTiltAngle = maxTiltAngle * tiltFactor; targetTiltAxis = glm::normalize(Vector3(difference.z, 0.0f, -difference.x)); } + + // Paint pheromones + Vector2 difference2D = Vector2(pick.x, pick.z) - Vector2(oldPick.x, oldPick.z); + float distance2DSquared = glm::dot(difference2D, difference2D); + if (distance2DSquared != 0.0f) + { + float distance2D = sqrt(distance2DSquared); + Vector2 direction2D = difference2D / distance2D; + + if (distance2D <= BRUSH_RADIUS) + { + paint(Vector2(pick.x, pick.z), BRUSH_RADIUS); + } + else + { + float stepDistance = BRUSH_RADIUS * 0.5f; + int stepCount = static_cast(distance2D / stepDistance + 0.5f); + + for (int i = 0; i < stepCount; ++i) + { + Vector2 circleCenter = Vector2(oldPick.x, oldPick.z) + direction2D * (stepDistance * i); + + paint(circleCenter, BRUSH_RADIUS); + } + + paint(Vector2(pick.x, pick.z), BRUSH_RADIUS); + } + } } float angleInterpolationFactor = 0.1f / (1.0 / 60.0f) * dt; @@ -562,6 +610,15 @@ void Brush::update(float dt) modelInstance.setTranslation(translation); modelInstance.setRotation(rotation); + + if (descended) + { + Vector2 paintPosition = Vector2(pick.x, pick.z); + paint(paintPosition, BRUSH_RADIUS); + + + } + oldPick = pick; } @@ -570,7 +627,6 @@ void Brush::press() ascentTween->stop(); descentTween->reset(); descentTween->start(); - } void Brush::release() @@ -580,3 +636,38 @@ void Brush::release() ascentTween->reset(); ascentTween->start(); } + +void Brush::setColony(Colony* colony) +{ + this->colony = colony; +} + +void Brush::paint(const Vector2& position, float radius) +{ + if (!colony) + { + return; + } + + PheromoneMatrix* pheromoneMatrix = colony->getRecruitmentMatrix(); + + float concentration = 1.0f; + float radiusSquared = radius * radius; + Vector2 cell; + Vector2 difference; + + for (cell.y = position.y - radius; cell.y <= position.y + radius; cell.y += pheromoneMatrix->getCellHeight()) + { + difference.y = cell.y - position.y; + + for (cell.x = position.x - radius; cell.x <= position.x + radius; cell.x += pheromoneMatrix->getCellWidth()) + { + difference.x = cell.x - position.x; + + if (glm::dot(difference, difference) <= radiusSquared) + { + pheromoneMatrix->deposit(cell, concentration); + } + } + } +} diff --git a/src/game/tool.hpp b/src/game/tool.hpp index 9ba1bbe..e27c7c4 100644 --- a/src/game/tool.hpp +++ b/src/game/tool.hpp @@ -27,6 +27,7 @@ using namespace Emergent; class Ant; class Colony; +class Navmesh; class SurfaceCameraController; /** @@ -156,6 +157,8 @@ public: */ void setColony(Colony* colony); + void setNavmesh(Navmesh* navmesh); + /** * Returns the current state of the forceps. */ @@ -185,6 +188,7 @@ private: Colony* colony; Ant* targetedAnt; Ant* suspendedAnt; + Navmesh* navmesh; }; inline Forceps::State Forceps::getState() const @@ -230,6 +234,13 @@ public: void focus(); void unfocus(); + /** + * Associates a colony with this lens. + * + * @param colony Colony with which to associate. + */ + void setColony(Colony* colony); + void setSunDirection(const Vector3& direction); /** @@ -247,6 +258,7 @@ private: Tweener* tweener; Tween* descentTween; Tween* ascentTween; + Colony* colony; }; inline const Spotlight* Lens::getSpotlight() const @@ -273,7 +285,16 @@ public: void press(); void release(); + /** + * Associates a colony with this brush. + * + * @param colony Colony with which to associate. + */ + void setColony(Colony* colony); + private: + void paint(const Vector2& position, float radius); + Pose* pose; float hoverDistance; bool descended; @@ -286,6 +307,7 @@ private: float targetTiltAngle; Vector3 tiltAxis; Vector3 targetTiltAxis; + Colony* colony; }; #endif // TOOL_HPP diff --git a/src/states/game-state.cpp b/src/states/game-state.cpp index c9da9cb..3981b09 100644 --- a/src/states/game-state.cpp +++ b/src/states/game-state.cpp @@ -24,11 +24,19 @@ #include "../game/colony.hpp" #include "../game/ant.hpp" #include "../game/tool.hpp" +#include "../game/pheromone-matrix.hpp" #include "../ui/toolbar.hpp" #include "../ui/menu.hpp" #include "../ui/pie-menu.hpp" #include +inline void cmykToRGB(const float* cmyk, float* rgb) +{ + rgb[0] = -((cmyk[0] * (1.0f - cmyk[3])) + cmyk[3] - 1.0f); + rgb[1] = -((cmyk[1] * (1.0f - cmyk[3])) + cmyk[3] - 1.0f); + rgb[2] = -((cmyk[2] * (1.0f - cmyk[3])) + cmyk[3] - 1.0f); +} + GameState::GameState(Application* application): ApplicationState(application) {} @@ -57,8 +65,11 @@ void GameState::enter() application->toolbar->getContainer()->setVisible(true); application->toolbar->getContainer()->setActive(true); + Navmesh* navmesh = application->currentLevel->terrain.getSurfaceNavmesh(); + // Setup tools application->forceps->setColony(application->colony); + application->forceps->setNavmesh(navmesh); // Add tools to scene application->defaultLayer->addObject(application->forceps->getModelInstance()); @@ -73,7 +84,6 @@ void GameState::enter() //application->defaultLayer->addObject(&application->biomeFloorModelInstance); // Spawn ants - Navmesh* navmesh = application->currentLevel->terrain.getSurfaceNavmesh(); for (int i = 0; i < 200; ++i) { Navmesh::Triangle* triangle = (*navmesh->getTriangles())[0]; @@ -242,6 +252,63 @@ void GameState::execute() pick = pickingRay.extrapolate(std::get<1>(result)); } */ + static bool bla = false; + bla = !bla; + // Update pheromone texture + if (bla) + { + float cmyk[4]; + float rgb[3]; + const float* bufferH = application->colony->getHomingMatrix()->getActiveBuffer(); + const float* bufferR = application->colony->getRecruitmentMatrix()->getActiveBuffer(); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, application->pheromonePBO); + GLubyte* data = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + GLubyte* channel = data; + + std::size_t index = 0; + for (int y = 0; y < application->pheromoneTexture.getHeight(); ++y) + { + for (int x = 0; x < application->pheromoneTexture.getWidth(); ++x) + { + float concentrationH = std::min(1.0f, bufferH[index]) * 0.35f; + float concentrationR = std::min(1.0f, bufferR[index]) * 0.35f; + + cmyk[0] = std::min(1.0f, concentrationH * HOMING_PHEROMONE_COLOR[0] + concentrationR * RECRUITMENT_PHEROMONE_COLOR[0]); + cmyk[1] = std::min(1.0f, concentrationH * HOMING_PHEROMONE_COLOR[1] + concentrationR * RECRUITMENT_PHEROMONE_COLOR[1]); + cmyk[2] = std::min(1.0f, concentrationH * HOMING_PHEROMONE_COLOR[2] + concentrationR * RECRUITMENT_PHEROMONE_COLOR[2]); + cmyk[3] = 0.35f; + + cmykToRGB(cmyk, rgb); + + GLubyte b = static_cast(std::min(255.0f, rgb[2] * 255.0f)); + GLubyte g = static_cast(std::min(255.0f, rgb[1] * 255.0f)); + GLubyte r = static_cast(std::min(255.0f, rgb[0] * 255.0f)); + + *(channel++) = b; + *(channel++) = g; + *(channel++) = r; + *(channel++) = 255; + + ++index; + } + } + + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + glBindTexture(GL_TEXTURE_2D, application->pheromoneTextureID); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, application->pheromoneTexture.getWidth(), application->pheromoneTexture.getHeight(), GL_BGRA, GL_UNSIGNED_BYTE, nullptr); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + + application->colony->getHomingMatrix()->evaporate(); + application->colony->getRecruitmentMatrix()->evaporate(); + static int frame = 0; + if (frame++ % DIFFUSION_FRAME == 0) + { + application->colony->getHomingMatrix()->diffuse(); + application->colony->getRecruitmentMatrix()->diffuse(); + }