diff --git a/data b/data index f6fed6a..463807e 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit f6fed6a858a3b4d71b879df0267b2881f270a18f +Subproject commit 463807e1e360d62997874f3a5b185f93c9999b50 diff --git a/src/application.cpp b/src/application.cpp index 15a3c86..2acca75 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -705,7 +705,8 @@ bool Application::loadModels() forcepsModel = modelLoader->load("data/models/forceps.mdl"); lensModel = modelLoader->load("data/models/lens.mdl"); brushModel = modelLoader->load("data/models/brush.mdl"); - biomeFloorModel = modelLoader->load("data/models/desert-floor.mdl"); + sidewalkPanelModel = modelLoader->load("data/models/sidewalk-panel.mdl"); + soilModel = modelLoader->load("data/models/soil.mdl"); if (!antModel || !antHillModel || !nestModel || !forcepsModel || !lensModel || !brushModel) { @@ -717,7 +718,21 @@ bool Application::loadModels() antHillModelInstance.setModel(antHillModel); antHillModelInstance.setRotation(glm::angleAxis(glm::radians(90.0f), Vector3(1, 0, 0))); nestModelInstance.setModel(nestModel); - biomeFloorModelInstance.setModel(biomeFloorModel); + sidewalkPanelInstance.setModel(sidewalkPanelModel); + + sidewalkPanelInstance1.setModel(sidewalkPanelModel); + sidewalkPanelInstance2.setModel(sidewalkPanelModel); + sidewalkPanelInstance3.setModel(sidewalkPanelModel); + sidewalkPanelInstance4.setModel(sidewalkPanelModel); + soilInstance.setModel(soilModel); + + float offset = 100.5f; + sidewalkPanelInstance1.setTranslation(Vector3(-offset, 0.0f, 0.0f)); + sidewalkPanelInstance2.setTranslation(Vector3(-offset * 2.0f, 0.0f, 0.0f)); + sidewalkPanelInstance3.setTranslation(Vector3(offset, 0.0f, 0.0f)); + sidewalkPanelInstance4.setTranslation(Vector3(offset * 2.0f, 0.0f, 0.0f)); + + soilInstance.setTranslation(Vector3(0.0f, -3.0f, 0.0f)); return true; } @@ -1446,7 +1461,7 @@ bool Application::loadUI() pauseMenu->setLineSpacing(1.0f); pauseMenuResumeItem = pauseMenu->addItem(); - pauseMenuResumeItem->setActivatedCallback(std::bind(&Application::unpauseSimulation, this)); + pauseMenuResumeItem->setActivatedCallback(std::bind(&Application::closePauseMenu, this)); pauseMenuLevelsItem = pauseMenu->addItem(); pauseMenuLevelsItem->setActivatedCallback(std::bind(&Application::openMenu, this, levelsMenu)); @@ -1562,6 +1577,8 @@ bool Application::loadControls() gameControlProfile->registerControl("turn-left", &turnLeft); gameControlProfile->registerControl("turn-right", &turnRight); gameControlProfile->registerControl("toggle-pause", &togglePause); + gameControlProfile->registerControl("toggle-pause-menu", &togglePauseMenu); + gameControlProfile->registerControl("fast-forward", &fastForward); cameraMoveForward.bindKey(keyboard, SDL_SCANCODE_W); cameraMoveBack.bindKey(keyboard, SDL_SCANCODE_S); @@ -1580,6 +1597,8 @@ bool Application::loadControls() turnLeft.bindKey(keyboard, SDL_SCANCODE_LEFT); turnRight.bindKey(keyboard, SDL_SCANCODE_RIGHT); togglePause.bindKey(keyboard, SDL_SCANCODE_SPACE); + togglePauseMenu.bindKey(keyboard, SDL_SCANCODE_ESCAPE); + fastForward.bindKey(keyboard, SDL_SCANCODE_F); return true; } @@ -2054,7 +2073,7 @@ void Application::loadLevel(std::size_t index) currentLevel->load(*levelParams); PhysicalMaterial* material = materialLoader->load("data/materials/debug-terrain-surface.mtl"); - material->albedoOpacityMap = &pheromoneTexture; + //material->albedoOpacityMap = &pheromoneTexture; currentLevel->terrain.getSurfaceModel()->getGroup(0)->material = material; } @@ -2087,6 +2106,16 @@ void Application::loadLevel() */ void Application::pauseSimulation() +{ + simulationPaused = true; +} + +void Application::unpauseSimulation() +{ + simulationPaused = false; +} + +void Application::openPauseMenu() { simulationPaused = true; @@ -2102,7 +2131,7 @@ void Application::pauseSimulation() pauseMenu->select(0); } -void Application::unpauseSimulation() +void Application::closePauseMenu() { simulationPaused = false; diff --git a/src/application.hpp b/src/application.hpp index 2124e67..7a7fb5d 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -110,6 +110,9 @@ public: void pauseSimulation(); void unpauseSimulation(); + void openPauseMenu(); + void closePauseMenu(); + void setDisplayDebugInfo(bool display); std::u32string getLevelName(std::size_t world, std::size_t level) const; @@ -163,7 +166,12 @@ public: ModelInstance antModelInstance; ModelInstance antHillModelInstance; ModelInstance nestModelInstance; - ModelInstance biomeFloorModelInstance; + ModelInstance sidewalkPanelInstance; + ModelInstance sidewalkPanelInstance1; + ModelInstance sidewalkPanelInstance2; + ModelInstance sidewalkPanelInstance3; + ModelInstance sidewalkPanelInstance4; + ModelInstance soilInstance; // Graphics Renderer renderer; @@ -242,6 +250,8 @@ public: Control turnLeft; Control turnRight; Control togglePause; + Control togglePauseMenu; + Control fastForward; Arcball arcball; // Misc @@ -376,7 +386,8 @@ public: Model* forcepsModel; Model* lensModel; Model* brushModel; - Model* biomeFloorModel; + Model* sidewalkPanelModel; + Model* soilModel; // Game variables Biosphere biosphere; diff --git a/src/game/ant.cpp b/src/game/ant.cpp index 87a4fe4..ff714e0 100644 --- a/src/game/ant.cpp +++ b/src/game/ant.cpp @@ -220,76 +220,7 @@ void Ant::update(float dt) // Update model instance modelInstance.setTransform(transform); } - - // Locomotion - - /* - As the ant moves forward, legs in the stance phase are kept grounded via IK. If IK constraints are violated, the swinging legs are grounded - and the grounded legs begin swinging. - - Two poses are loaded from the model file: midswing and touchdown. - - touchdown is the pose in which all legs are at the end of their swing phases and need to be grounded - midswing is the pose in which all legs are at the highest point in their swing phases - - when a grounded leg enters the swing phases, its current pose is saved as the liftoff pose, then an animation is created using the liftoff pose, midswing pose, and touchdown pose. - */ -} - -/* -Vector3 Ant::forage(const Vector3& leftReceptor, const Vector3& rightReceptor) -{ - float leftSignal = 0.0f; - float rightSignal = 0.0f; - - // Detect pheromones with left receptor - std::list leftPheromones; - colony->getPheromoneOctree()->query(AABB(leftReceptor, leftReceptor), &leftPheromones); - for (Pheromone* pheromone: leftPheromones) - { - Vector3 difference = pheromone->getPosition() - rightReceptor; - - float distanceSquared = glm::dot(difference, difference); - if (distanceSquared <= pheromone->getRadiusSquared()) - { - // Calculate attenuated pheromone strength using inverse-square law - float strength = pheromone->getStrength() / ((distanceSquared == 0.0f) ? 1.0f : distanceSquared); - leftSignal += strength; - } - } - - // Detect pheromones with right receptor - std::list rightPheromones; - colony->getPheromoneOctree()->query(AABB(rightReceptor, rightReceptor), &rightPheromones); - for (Pheromone* pheromone: rightPheromones) - { - Vector3 difference = pheromone->getPosition() - rightReceptor; - - float distanceSquared = glm::dot(difference, difference); - if (distanceSquared <= pheromone->getRadiusSquared()) - { - // Calculate attenuated pheromone strength using inverse-square law - float strength = pheromone->getStrength() / ((distanceSquared == 0.0f) ? 1.0f : distanceSquared); - rightSignal += strength; - } - } - - // Add noise - const float maxNoise = 0.1f; - leftSignal += frand(0.0f, maxNoise); - rightSignal += frand(0.0f, maxNoise); - - if (leftSignal + rightSignal > 0.0f) - { - const float maxPheromoneTurningAngle = 0.1f; - - // Use Weber's law (Perna et al.) to calculate turning angle based on pheromone signals - float turningAngle = maxPheromoneTurningAngle * ((leftSignal - rightSignal) / (leftSignal + rightSignal)); - } - - return Vector3(0.0f); } -*/ void Ant::setState(Ant::State state) { diff --git a/src/game/pheromone-matrix.cpp b/src/game/pheromone-matrix.cpp index cc34592..ead03b5 100644 --- a/src/game/pheromone-matrix.cpp +++ b/src/game/pheromone-matrix.cpp @@ -48,9 +48,7 @@ PheromoneMatrix::PheromoneMatrix(int columns, int rows, const Vector2& boundsMin } 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(); diff --git a/src/game/tool.cpp b/src/game/tool.cpp index 59a93b8..8024171 100644 --- a/src/game/tool.cpp +++ b/src/game/tool.cpp @@ -38,7 +38,6 @@ Forceps::Forceps(const Model* model) // Allocate pose and initialize to bind pose pose = new Pose(model->getSkeleton()); pose->reset(); - pose->concatenate(); // Setup model instance modelInstance.setModel(model); @@ -82,6 +81,10 @@ Forceps::Forceps(const Model* model) suspendedAnt = nullptr; cameraController = nullptr; pick = Vector3(0.0f); + + // Open forceps + pinchAnimation->animate(pose, 0.0f); + pose->concatenate(); } Forceps::~Forceps() @@ -370,7 +373,7 @@ Lens::Lens(const Model* model) spotlight.setIntensity(10000.0f); spotlight.setAttenuation(Vector3(1, 0, 1)); spotlight.setCutoff(glm::radians(45.0f)); - spotlight.setExponent(700.0f); + spotlight.setExponent(1000.0f); spotlight.setActive(false); unfocusedDistance = 18.0f; @@ -496,7 +499,7 @@ Brush::Brush(const Model* model) hoverDistance = 0.5f; // Setup timing - float descentDuration = 0.1f; + float descentDuration = 0.05f; float ascentDuration = 0.1f; // Allocate tweener and and setup tweens diff --git a/src/game/tool.hpp b/src/game/tool.hpp index e27c7c4..e0525ef 100644 --- a/src/game/tool.hpp +++ b/src/game/tool.hpp @@ -203,6 +203,9 @@ inline Ant* Forceps::getSuspendedAnt() const /** * The lens tool can be used to burn ants. + * + * @see https://taylorpetrick.com/blog/post/dispersion-opengl + * @see https://taylorpetrick.com/portfolio/webgl/lense */ class Lens: public Tool { diff --git a/src/states/game-state.cpp b/src/states/game-state.cpp index 3981b09..714bcc1 100644 --- a/src/states/game-state.cpp +++ b/src/states/game-state.cpp @@ -79,10 +79,15 @@ void GameState::enter() // Add terrain to scene - application->defaultLayer->addObject(&application->currentLevel->terrainSurface); + //application->defaultLayer->addObject(&application->currentLevel->terrainSurface); //application->defaultLayer->addObject(&application->currentLevel->terrainSubsurface); - //application->defaultLayer->addObject(&application->biomeFloorModelInstance); - + application->defaultLayer->addObject(&application->sidewalkPanelInstance); + application->defaultLayer->addObject(&application->sidewalkPanelInstance1); + application->defaultLayer->addObject(&application->sidewalkPanelInstance2); + application->defaultLayer->addObject(&application->sidewalkPanelInstance3); + application->defaultLayer->addObject(&application->sidewalkPanelInstance4); + application->defaultLayer->addObject(&application->soilInstance); + // Spawn ants for (int i = 0; i < 200; ++i) { @@ -135,7 +140,7 @@ void GameState::enter() void GameState::execute() { // Pause simulation - if (application->escape.isTriggered() && !application->escape.wasTriggered()) + if (application->togglePause.isTriggered() && !application->togglePause.wasTriggered()) { if (application->simulationPaused) { @@ -146,6 +151,18 @@ void GameState::execute() application->pauseSimulation(); } } + // Open pause menu + else if (application->togglePauseMenu.isTriggered() && !application->togglePauseMenu.wasTriggered()) + { + if (application->activeMenu == application->pauseMenu) + { + application->closePauseMenu(); + } + else + { + application->openPauseMenu(); + } + } // Navigate menu if (application->activeMenu != nullptr) @@ -252,63 +269,9 @@ 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(); - } + @@ -360,7 +323,70 @@ void GameState::execute() application->currentTool->update(application->dt); } - application->colony->update(application->dt); + int iterations = application->fastForward.isTriggered() ? 10 : 1; + for (int iteration = 0; iteration < iterations; ++iteration) + { + 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(); + } + + application->colony->update(application->dt); + } + + 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); + } } } @@ -370,9 +396,9 @@ void GameState::exit() application->mouse->removeMouseButtonObserver(this); // Clear scene - application->defaultLayer->removeObject(&application->currentLevel->terrainSurface); - application->defaultLayer->removeObject(&application->currentLevel->terrainSubsurface); - application->defaultLayer->removeObject(&application->biomeFloorModelInstance); + //application->defaultLayer->removeObject(&application->currentLevel->terrainSurface); + //application->defaultLayer->removeObject(&application->currentLevel->terrainSubsurface); + application->defaultLayer->removeObject(&application->sidewalkPanelInstance); application->defaultLayer->removeObject(application->forceps->getModelInstance()); application->defaultLayer->removeObject(application->lens->getModelInstance()); application->defaultLayer->removeObject(application->lens->getSpotlight());