Browse Source

Port pheromone drawing and following code from Processing prototype

master
C. J. Howard 6 years ago
parent
commit
37f53560e4
18 changed files with 757 additions and 210 deletions
  1. +2
    -2
      CMakeLists.txt
  2. +31
    -4
      src/application.cpp
  3. +4
    -0
      src/application.hpp
  4. +20
    -0
      src/configuration.hpp.in
  5. +22
    -22
      src/game/agent.cpp
  6. +17
    -10
      src/game/agent.hpp
  7. +154
    -32
      src/game/ant.cpp
  8. +21
    -0
      src/game/ant.hpp
  9. +9
    -4
      src/game/colony.cpp
  10. +26
    -5
      src/game/colony.hpp
  11. +178
    -0
      src/game/pheromone-matrix.cpp
  12. +83
    -0
      src/game/pheromone-matrix.hpp
  13. +0
    -50
      src/game/pheromone.cpp
  14. +0
    -71
      src/game/pheromone.hpp
  15. +2
    -2
      src/game/terrain.cpp
  16. +98
    -7
      src/game/tool.cpp
  17. +22
    -0
      src/game/tool.hpp
  18. +68
    -1
      src/states/game-state.cpp

+ 2
- 2
CMakeLists.txt View File

@ -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

+ 31
- 4
src/application.cpp View File

@ -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;
}
/*

+ 4
- 0
src/application.hpp View File

@ -186,6 +186,10 @@ public:
GLuint framebufferB;
RenderTarget framebufferBRenderTarget;
GLuint pheromonePBO;
GLuint pheromoneTextureID;
Texture pheromoneTexture;
ClearRenderPass clearDepthPass;
SoilRenderPass soilPass;
LightingRenderPass lightingPass;

+ 20
- 0
src/configuration.hpp.in View File

@ -20,6 +20,9 @@
#ifndef CONFIGURATION_HPP
#define CONFIGURATION_HPP
#include <emergent/emergent.hpp>
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<int>(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

+ 22
- 22
src/game/agent.cpp View File

@ -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;

+ 17
- 10
src/game/agent.hpp View File

@ -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<Agent*>& neighbors) const;
//Vector3 containment(const Vector3& probe) const;
Vector3 forage(const Vector3& leftProbe, const Vector3& rightProbe);
//Vector3 separation(const std::list<Agent*>& 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

+ 154
- 32
src/game/ant.cpp View File

@ -19,9 +19,39 @@
#include "ant.hpp"
#include "colony.hpp"
#include "pheromone.hpp"
#include "pheromone-matrix.hpp"
#include <cmath>
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<Agent*> neighbors;
colony->queryAnts(neighborhoodAABB, &neighbors);
//float neighborhoodSize = 2.0f;
//AABB neighborhoodAABB(getPosition() - Vector3(neighborhoodSize * 0.5f), getPosition() + Vector3(neighborhoodSize * 0.5f));
//std::list<Agent*> 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;
}

+ 21
- 0
src/game/ant.hpp View File

@ -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

+ 9
- 4
src/game/colony.cpp View File

@ -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<Agent*>(5, octreeBounds);
pheromoneOctree = new Octree<Pheromone*>(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)
{

+ 26
- 5
src/game/colony.hpp View File

@ -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<Agent*>* getAntOctree() const;
const Octree<Pheromone*>* 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<Ant*> ants;
Octree<Agent*>* antOctree;
std::vector<Pheromone*> pheromones;
Octree<Pheromone*>* 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<Pheromone*>* 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

+ 178
- 0
src/game/pheromone-matrix.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "pheromone-matrix.hpp"
#include "../configuration.hpp"
#include <algorithm>
#include <cmath>
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<float>(columns);
cellHeight = matrixHeight / static_cast<float>(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<int>(0, std::min<int>(rows - 1, i + k));
for (int l = -diffusionKernelRadius; l <= diffusionKernelRadius; ++l)
{
int column = std::max<int>(0, std::min<int>(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<int>((matrixHalfWidth + position.x) / cellWidth);
int row = static_cast<int>((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<int>((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<int>((matrixHalfWidth + position.x) / cellWidth);
int row = static_cast<int>((matrixHalfHeight + position.y) / cellHeight);
if (columns < 0 || column >= columns || row < 0 || row >= rows)
{
return;
}
int index = row * columns + column;
activeBuffer[index] += concentration;
}

+ 83
- 0
src/game/pheromone-matrix.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef PHEROMONE_MATRIX_HPP
#define PHEROMONE_MATRIX_HPP
#include <emergent/emergent.hpp>
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

+ 0
- 50
src/game/pheromone.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "pheromone.hpp"
#include <cmath>
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);
}

+ 0
- 71
src/game/pheromone.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef PHEROMONE_HPP
#define PHEROMONE_HPP
#include <emergent/emergent.hpp>
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

+ 2
- 2
src/game/terrain.cpp View File

@ -54,8 +54,8 @@ void Terrain::createSurface()
*(data++) = 0.0f;
*(data++) = 1.0f;
*(data++) = 0.0f;
*(data++) = static_cast<float>(j) / static_cast<float>(columns) * 2.0f;
*(data++) = static_cast<float>(i) / static_cast<float>(rows) * 2.0f;
*(data++) = static_cast<float>(j) / static_cast<float>(columns);
*(data++) = static_cast<float>(i) / static_cast<float>(rows);
}
}

+ 98
- 7
src/game/tool.cpp View File

@ -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 <iostream>
#include <list>
@ -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<Navmesh::Triangle*>* 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<int>(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);
}
}
}
}

+ 22
- 0
src/game/tool.hpp View File

@ -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<float>* descentTween;
Tween<float>* 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

+ 68
- 1
src/states/game-state.cpp View File

@ -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 <cmath>
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<float>(1.0f, bufferH[index]) * 0.35f;
float concentrationR = std::min<float>(1.0f, bufferR[index]) * 0.35f;
cmyk[0] = std::min<float>(1.0f, concentrationH * HOMING_PHEROMONE_COLOR[0] + concentrationR * RECRUITMENT_PHEROMONE_COLOR[0]);
cmyk[1] = std::min<float>(1.0f, concentrationH * HOMING_PHEROMONE_COLOR[1] + concentrationR * RECRUITMENT_PHEROMONE_COLOR[1]);
cmyk[2] = std::min<float>(1.0f, concentrationH * HOMING_PHEROMONE_COLOR[2] + concentrationR * RECRUITMENT_PHEROMONE_COLOR[2]);
cmyk[3] = 0.35f;
cmykToRGB(cmyk, rgb);
GLubyte b = static_cast<GLubyte>(std::min<float>(255.0f, rgb[2] * 255.0f));
GLubyte g = static_cast<GLubyte>(std::min<float>(255.0f, rgb[1] * 255.0f));
GLubyte r = static_cast<GLubyte>(std::min<float>(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();
}

Loading…
Cancel
Save