From 66c7f0335b564897f40d7196a723f1a7f50d3519 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Mon, 3 Jul 2017 23:28:08 +0800 Subject: [PATCH] Fix the timestep --- data | 2 +- src/application.cpp | 115 +++++++++++++++++-- src/application.hpp | 7 +- src/render-passes.cpp | 5 + src/states/main-menu-state.cpp | 196 +++++++++++++++++++++++++++++++++ src/states/main-menu-state.hpp | 6 + src/states/title-state.cpp | 59 +--------- src/states/title-state.hpp | 6 +- 8 files changed, 325 insertions(+), 71 deletions(-) diff --git a/data b/data index 1e82819..bca60d7 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 1e82819229df7c2113938a1bc0ea35b6f3335493 +Subproject commit bca60d7e0fe476dcf32de8ab4f17d08326ed37b9 diff --git a/src/application.cpp b/src/application.cpp index 739c08b..d796025 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #define OPENGL_VERSION_MAJOR 3 @@ -343,15 +344,28 @@ Application::~Application() int Application::execute() { + // Fixed timestep + // @see http://gafferongames.com/game-physics/fix-your-timestep/ + t = 0.0f; + dt = 1.0f / 60.0f; + float accumulator = 0.0f; + + int performanceSampleSize = 15; // Number of frames to sample + int performanceSampleFrame = 0; // Current sample frame + float performanceSampleTime = 0.0f; // Current sample time + // Start frame timer frameTimer.start(); while (state != nullptr) { - // Calculate delta time (in seconds) then reset frame timer - dt = static_cast(frameTimer.microseconds().count()) / 1000000.0f; + // Calculate frame time (in milliseconds) then reset frame timer + float frameTime = static_cast(frameTimer.microseconds().count()) / 1000.0f; frameTimer.reset(); + // Add frame time (in seconds) to accumulator + accumulator += frameTime / 1000.0f; + // If the user tried to close the application if (inputManager->wasClosed() || escape.isTriggered()) { @@ -361,7 +375,16 @@ int Application::execute() else { // Execute current state - state->execute(); + while (accumulator >= dt) + { + state->execute(); + + // Perform tweening + tweener->update(dt); + + accumulator -= dt; + t += dt; + } } // Check for state change @@ -375,6 +398,7 @@ int Application::execute() if (nextState != nullptr) { state->enter(); + tweener->update(0.0f); // Reset frame timer to counteract frames eaten by state exit() and enter() functions frameTimer.reset(); @@ -394,8 +418,27 @@ int Application::execute() changeFullscreen(); } - // Perform tweening - tweener->update(dt); + // Add frame time to performance sample time and increment the frame count + performanceSampleTime += frameTime; + ++performanceSampleFrame; + + // If performance sample is complete + if (performanceSampleFrame >= performanceSampleSize) + { + // Calculate mean frame time + float meanFrameTime = performanceSampleTime / static_cast(performanceSampleSize); + + // Reset perform sample timers + performanceSampleTime = 0.0f; + performanceSampleFrame = 0; + + // Update frame time label + std::string frameTimeString; + std::stringstream stream; + stream << meanFrameTime; + stream >> frameTimeString; + frameTimeLabel->setText(frameTimeString); + } // Update UI uiRootElement->update(); @@ -530,8 +573,9 @@ bool Application::loadModels() { antModel = modelLoader->load("data/models/debug-worker.mdl"); antHillModel = modelLoader->load("data/models/ant-hill.mdl"); + nestModel = modelLoader->load("data/models/nest.mdl"); - if (!antModel || !antHillModel) + if (!antModel || !antHillModel || !nestModel) { return false; } @@ -540,6 +584,7 @@ bool Application::loadModels() antModelInstance.setTransform(Transform::getIdentity()); antHillModelInstance.setModel(antHillModel); antHillModelInstance.setRotation(glm::angleAxis(glm::radians(90.0f), Vector3(1, 0, 0))); + nestModelInstance.setModel(nestModel); return true; } @@ -551,6 +596,49 @@ bool Application::loadScene() defaultLayer = scene.addLayer(); uiLayer = scene.addLayer(); + // BG + bgBatch.resize(1); + BillboardBatch::Range* bgRange = bgBatch.addRange(); + bgRange->start = 0; + bgRange->length = 1; + Billboard* bgBillboard = bgBatch.getBillboard(0); + bgBillboard->setDimensions(Vector2(1.0f, 1.0f)); + bgBillboard->setTranslation(Vector3(0.5f, 0.5f, 0.0f)); + bgBillboard->setTintColor(Vector4(1, 1, 1, 1)); + bgBatch.update(); + + vignettePass.setRenderTarget(&defaultRenderTarget); + bgCompositor.addPass(&vignettePass); + bgCompositor.load(nullptr); + bgCamera.setOrthographic(0, 1.0f, 1.0f, 0, -1.0f, 1.0f); + bgCamera.lookAt(glm::vec3(0), glm::vec3(0, 0, -1), glm::vec3(0, 1, 0)); + bgCamera.setCompositor(&bgCompositor); + bgCamera.setCompositeIndex(0); + + // Setup soil pass + soilPass.setRenderTarget(&defaultRenderTarget); + defaultCompositor.addPass(&soilPass); + + // Setup lighting pass + lightingPass.setRenderTarget(&defaultRenderTarget); + lightingPass.setShadowMap(0); + lightingPass.setShadowCamera(&camera); + lightingPass.setModelLoader(modelLoader); + defaultCompositor.addPass(&lightingPass); + + + // Load compositor + defaultCompositor.load(nullptr); + + // Setup camera + camera.lookAt( + glm::vec3(0.0f, 0.0f, 10.0f), + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 1.0f, 0.0f)); + camera.setCompositor(&defaultCompositor); + camera.setCompositeIndex(0); + defaultLayer->addObject(&camera); + // Debug lineBatcher = new LineBatcher(4096); BillboardBatch* lineBatch = lineBatcher->getBatch(); @@ -628,8 +716,8 @@ bool Application::loadUI() strings.get("quit-to-desktop", &quitToDesktopString); // Set colors - selectedColor = Vector4(0.0f, 0.0f, 0.0f, 1.0f); - deselectedColor = Vector4(0.0f, 0.0f, 0.0f, 0.35f); + selectedColor = Vector4(1.0f, 1.0f, 1.0f, 1.0f); + deselectedColor = Vector4(1.0f, 1.0f, 1.0f, 0.35f); // Setup root UI element uiRootElement = new UIContainer(); @@ -640,7 +728,7 @@ bool Application::loadUI() // Create blackout element (for screen transitions) blackoutImage = new UIImage(); blackoutImage->setDimensions(Vector2(width, height)); - blackoutImage->setLayerOffset(99); + blackoutImage->setLayerOffset(98); blackoutImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f)); blackoutImage->setVisible(false); uiRootElement->addChild(blackoutImage); @@ -684,6 +772,15 @@ bool Application::loadUI() versionLabel->setText(versionString); titleScreenInfoContainer->addChild(versionLabel); + frameTimeLabel = new UILabel(); + frameTimeLabel->setAnchor(Vector2(0.0f, 0.0f)); + frameTimeLabel->setLayerOffset(99); + frameTimeLabel->setFont(copyrightFont); + frameTimeLabel->setTranslation(Vector2(0.0f)); + frameTimeLabel->setTintColor(Vector4(1.0f, 1.0f, 0.0f, 1.0f)); + frameTimeLabel->setText(""); + uiRootElement->addChild(frameTimeLabel); + // Create "Press any key" element anyKeyLabel = new UILabel(); anyKeyLabel->setAnchor(Vector2(0.5f, 1.0f)); diff --git a/src/application.hpp b/src/application.hpp index 18cc5d5..c4ad93b 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -142,8 +142,6 @@ public: Camera uiCamera; Camera bgCamera; DirectionalLight sunlight; - DirectionalLight fillLight; - DirectionalLight backLight; Spotlight lensHotspot; Spotlight lensFalloff; ModelInstance lensToolObject; @@ -151,6 +149,7 @@ public: ModelInstance navigatorObject; ModelInstance antModelInstance; ModelInstance antHillModelInstance; + ModelInstance nestModelInstance; // Graphics Renderer renderer; @@ -207,6 +206,7 @@ public: // Misc Timer frameTimer; + float t; float dt; // UI text @@ -236,6 +236,8 @@ public: UILabel* copyrightLabel; UILabel* versionLabel; + UILabel* frameTimeLabel; + UILabel* anyKeyLabel; UILabel* menuSelectorLabel; UIContainer* mainMenuContainer; @@ -302,6 +304,7 @@ public: // Models Model* antModel; Model* antHillModel; + Model* nestModel; // Game variables Campaign campaign; diff --git a/src/render-passes.cpp b/src/render-passes.cpp index a0441b3..5bc4fa9 100644 --- a/src/render-passes.cpp +++ b/src/render-passes.cpp @@ -237,6 +237,11 @@ void SoilRenderPass::render(const RenderContext* renderContext) // Bind shader shader->bind(); + if (!horizonOTexture || !horizonATexture || !horizonBTexture || !horizonCTexture) + { + return; + } + // Bind textures glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, horizonOTexture->getTextureID()); diff --git a/src/states/main-menu-state.cpp b/src/states/main-menu-state.cpp index 0482806..e8d9b2a 100644 --- a/src/states/main-menu-state.cpp +++ b/src/states/main-menu-state.cpp @@ -19,6 +19,150 @@ #include "main-menu-state.hpp" #include "../application.hpp" +#include "../debug.hpp" +#include "../camera-controller.hpp" + +void drawShaft(LineBatcher* lineBatcher, const Shaft* shaft); + +void drawChamber(LineBatcher* lineBatcher, const Chamber* chamber) +{ + float helixAngle = chamber->parent->getHelixAngle(chamber->relativeDepth); + float minAngle = helixAngle - chamber->centralAngle * 0.5f; + float maxAngle = helixAngle + chamber->centralAngle * 0.5f; + + // Find position on helix + Vector3 helixPosition = chamber->parent->getHelixPosition(chamber->relativeDepth); + helixPosition.y = -helixPosition.y; + + // Move annulus toward helix by the inner radius + Vector3 helixDirection = glm::normalize(Vector3(std::cos(helixAngle), 0.0f, std::sin(helixAngle))); + Vector3 offset = helixPosition - helixDirection * (chamber->innerRadius - chamber->parent->shaftRadius); + + int stepCount = 10; + float angleStep = chamber->centralAngle / (float)stepCount; + for (int i = 0; i < stepCount; ++i) + { + float angle0 = minAngle + angleStep * (float)i; + float angle1 = minAngle + angleStep * (float)(i + 1); + + float x0 = std::cos(angle0); + float z0 = std::sin(angle0); + float x1 = std::cos(angle1); + float z1 = std::sin(angle1); + + Vector3 innerStart; + innerStart.x = x0 * chamber->innerRadius; + innerStart.y = 0.0f; + innerStart.z = z0 * chamber->innerRadius; + + Vector3 outerStart; + outerStart.x = x0 * chamber->outerRadius; + outerStart.y = 0.0f; + outerStart.z = z0 * chamber->outerRadius; + + Vector3 innerEnd; + innerEnd.x = x1 * chamber->innerRadius; + innerEnd.y = 0.0f; + innerEnd.z = z1 * chamber->innerRadius; + + Vector3 outerEnd; + outerEnd.x = x1 * chamber->outerRadius; + outerEnd.y = 0.0f; + outerEnd.z = z1 * chamber->outerRadius; + + lineBatcher->draw(offset + innerStart, offset + innerEnd); + lineBatcher->draw(offset + outerStart, offset + outerEnd); + } + + Vector3 leftWallStart; + leftWallStart.x = std::cos(minAngle) * chamber->innerRadius; + leftWallStart.y = 0.0f; + leftWallStart.z = std::sin(minAngle) * chamber->innerRadius; + + Vector3 leftWallEnd; + leftWallEnd.x = std::cos(minAngle) * chamber->outerRadius; + leftWallEnd.y = 0.0f; + leftWallEnd.z = std::sin(minAngle) * chamber->outerRadius; + + Vector3 rightWallStart; + rightWallStart.x = std::cos(maxAngle) * chamber->innerRadius; + rightWallStart.y = 0.0f; + rightWallStart.z = std::sin(maxAngle) * chamber->innerRadius; + + Vector3 rightWallEnd; + rightWallEnd.x = std::cos(maxAngle) * chamber->outerRadius; + rightWallEnd.y = 0.0f; + rightWallEnd.z = std::sin(maxAngle) * chamber->outerRadius; + + lineBatcher->draw(offset + leftWallStart, offset + leftWallEnd); + lineBatcher->draw(offset + rightWallStart, offset + rightWallEnd); + + if (chamber->child != nullptr) + { + drawShaft(lineBatcher, chamber->child); + } +} + +void drawShaft(LineBatcher* lineBatcher, const Shaft* shaft) +{ + // Draw helix + int stepCount = 50; + float depthStep = shaft->shaftDepth / (float)stepCount; + for (int i = 0; i < stepCount; ++i) + { + Vector3 start = shaft->getHelixPosition((float)i * depthStep); + Vector3 end = shaft->getHelixPosition((float)(i + 1) * depthStep); + + start.y = -start.y; + end.y = -end.y; + + lineBatcher->draw(start, end); + } + + // Draw children + for (const Chamber* chamber: shaft->children) + { + drawChamber(lineBatcher, chamber); + } +} + +void MainMenuState::generateNest() +{ + NestParameters params; + + params.randomSeed = std::rand(); + + params.maxShaftGeneration = 2; + params.minShaftRadius = 0.0f; + params.maxShaftRadius = 0.0f; + params.minShaftDepth = 4.0f; + params.maxShaftDepth = 6.0f; + params.minShaftHelixRadius = 0.1f; + params.maxShaftHelixRadius = 1.0f; + params.minShaftHelixPitch = 0.25f; + params.maxShaftHelixPitch = 0.75f; + params.minShaftChamberCount = 1; + params.maxShaftChamberCount = 5; + params.minShaftChamberPitch = 0.5f; + params.maxShaftChamberPitch = 2.0f; + + params.minChamberInnerRadius = 0.2f; + params.maxChamberInnerRadius = 0.2f; + params.minChamberOuterRadius = 0.5f; + params.maxChamberOuterRadius = 0.5f; + params.minChamberCentralAngle = glm::radians(240.0f); + params.maxChamberCentralAngle = glm::radians(240.0f); + + nest.setParameters(params); + nest.generate(); + + // Draw nest + application->lineBatcher->setColor(Vector4(1.0f)); + application->lineBatcher->setWidth(0.015f); + application->lineBatcher->begin(); + drawShaft(application->lineBatcher, nest.getRootShaft()); + application->lineBatcher->end(); +} MainMenuState::MainMenuState(Application* application): ApplicationState(application) @@ -34,7 +178,29 @@ void MainMenuState::enter() application->menuSelectorLabel->setVisible(true); // Start fade-in + application->blackoutImage->setVisible(true); application->fadeInTween->start(); + + // Add nest + application->defaultLayer->addObject(&application->nestModelInstance); + + application->surfaceCam->setCamera(&application->camera); + application->surfaceCam->setFocalPoint(Vector3(-10.0f, -13.3f, 0.0f)); + application->surfaceCam->setFocalDistance(89.5f); + application->surfaceCam->setElevation(glm::radians(15.0f)); + application->surfaceCam->setAzimuth(glm::radians(0.0f)); + application->surfaceCam->setTargetFocalPoint(application->surfaceCam->getFocalPoint()); + application->surfaceCam->setTargetFocalDistance(application->surfaceCam->getFocalDistance()); + application->surfaceCam->setTargetElevation(application->surfaceCam->getElevation()); + application->surfaceCam->setTargetAzimuth(application->surfaceCam->getAzimuth()); + application->surfaceCam->update(0.0f); + + // 3D camera + application->camera.setPerspective( + glm::radians(25.0f), + (float)application->width / (float)application->height, + 0.1f, + 1000.0f); } void MainMenuState::execute() @@ -77,6 +243,36 @@ void MainMenuState::execute() application->menuSelectorLabel->setTranslation( Vector2(container->getPosition().x - application->menuSelectorLabel->getDimensions().x * 1.5f, container->getPosition().y + lineHeight * 0.5f - application->menuSelectorLabel->getDimensions().y * 0.5f + lineHeight * application->selectedMenuItemIndex)); + + // Move camera + Vector2 movementVector(0.0f); + if (application->cameraMoveLeft.isTriggered()) + movementVector.x -= application->cameraMoveLeft.getCurrentValue(); + if (application->cameraMoveRight.isTriggered()) + movementVector.x += application->cameraMoveRight.getCurrentValue(); + if (application->cameraMoveForward.isTriggered()) + movementVector.y -= application->cameraMoveForward.getCurrentValue(); + if (application->cameraMoveBack.isTriggered()) + movementVector.y += application->cameraMoveBack.getCurrentValue(); + if (movementVector.x != 0.0f || movementVector.y != 0.0f) + { + movementVector *= 0.005f * application->surfaceCam->getFocalDistance() * application->dt / (1.0f / 60.0f); + application->surfaceCam->setTargetFocalPoint(application->surfaceCam->getTargetFocalPoint() + Vector3(movementVector.x, -movementVector.y, 0.0f)); + + Vector3 focal = application->surfaceCam->getFocalPoint(); + std::cout << focal.x << ", " << focal.y << ", " << focal.z << "; " << application->surfaceCam->getFocalDistance() << std::endl; + } + + // Zoom camera + float zoomFactor = application->surfaceCam->getFocalDistance() / 20.0f * application->dt / (1.0f / 60.0f); + if (application->cameraZoomIn.isTriggered()) + application->surfaceCam->zoom(zoomFactor * application->cameraZoomIn.getCurrentValue()); + if (application->cameraZoomOut.isTriggered()) + application->surfaceCam->zoom(-zoomFactor * application->cameraZoomOut.getCurrentValue()); + application->surfaceCam->update(application->dt); + + application->surfaceCam->setTargetAzimuth(application->surfaceCam->getTargetAzimuth() + glm::radians(2.0f) * application->dt); + application->surfaceCam->update(application->dt); } void MainMenuState::exit() diff --git a/src/states/main-menu-state.hpp b/src/states/main-menu-state.hpp index 1105143..6e5c0e2 100644 --- a/src/states/main-menu-state.hpp +++ b/src/states/main-menu-state.hpp @@ -22,6 +22,7 @@ #include "../application-state.hpp" #include "../input.hpp" +#include "../game/nest.hpp" #include using namespace Emergent; @@ -38,6 +39,11 @@ public: virtual void mouseButtonPressed(int button, int x, int y); virtual void mouseButtonReleased(int button, int x, int y); + +private: + void generateNest(); + + Nest nest; }; #endif // MAIN_MENU_STATE_HPP \ No newline at end of file diff --git a/src/states/title-state.cpp b/src/states/title-state.cpp index 1f1a619..7cc5afd 100644 --- a/src/states/title-state.cpp +++ b/src/states/title-state.cpp @@ -45,39 +45,13 @@ void TitleState::enter() fadeIn = false; fadeOut = false; - // BG - application->bgBatch.resize(1); - BillboardBatch::Range* bgRange = application->bgBatch.addRange(); - bgRange->start = 0; - bgRange->length = 1; - Billboard* bgBillboard = application->bgBatch.getBillboard(0); - bgBillboard->setDimensions(Vector2(1.0f, 1.0f)); - bgBillboard->setTranslation(Vector3(0.5f, 0.5f, 0.0f)); - bgBillboard->setTintColor(Vector4(1, 1, 1, 1)); - application->bgBatch.update(); - - application->vignettePass.setRenderTarget(&application->defaultRenderTarget); - application->bgCompositor.addPass(&application->vignettePass); - application->bgCompositor.load(nullptr); - application->bgCamera.setOrthographic(0, 1.0f, 1.0f, 0, -1.0f, 1.0f); - application->bgCamera.lookAt(glm::vec3(0), glm::vec3(0, 0, -1), glm::vec3(0, 1, 0)); - application->bgCamera.setCompositor(&application->bgCompositor); - application->bgCamera.setCompositeIndex(0); - + application->backgroundLayer->addObject(&application->bgCamera); application->backgroundLayer->addObject(&application->bgBatch); // Title ant hill application->defaultLayer->addObject(&application->antHillModelInstance); - // Setup lighting - application->sunlight.setColor(glm::vec3(1.0f)); - application->sunlight.setDirection(glm::normalize(glm::vec3(0.5, -1, -0.5))); - - // Setup soil pass - application->soilPass.setRenderTarget(&application->defaultRenderTarget); - application->defaultCompositor.addPass(&application->soilPass); - // Create terrain application->terrain.create(255, 255, Vector3(50, 20, 50)); application->terrain.getSurfaceModel()->getGroup(0)->material = application->materialLoader->load("data/materials/debug-terrain-surface.mtl"); @@ -92,34 +66,7 @@ void TitleState::enter() // Load level application->loadLevel(); - // Setup lighting pass - application->lightingPass.setRenderTarget(&application->defaultRenderTarget); - application->lightingPass.setShadowMap(0); - application->lightingPass.setShadowCamera(&application->camera); - application->lightingPass.setModelLoader(application->modelLoader); - application->defaultCompositor.addPass(&application->lightingPass); - - application->camera.lookAt( - glm::vec3(0.0f, 0.0f, 10.0f), - glm::vec3(0.0f, 0.0f, 0.0f), - glm::vec3(0.0f, 1.0f, 0.0f)); - - application->camera.setCompositor(&application->defaultCompositor); - application->camera.setCompositeIndex(0); - - // Setup scene - application->defaultLayer->addObject(&application->camera); - - // Load compositor - RenderQueue renderQueue; - const std::list* objects = application->defaultLayer->getObjects(); - for (const SceneObject* object: *objects) - renderQueue.queue(object); - RenderContext renderContext; - renderContext.camera = nullptr; - renderContext.layer = application->defaultLayer; - renderContext.queue = &renderQueue; - application->defaultCompositor.load(&renderContext); + application->inputManager->addWindowObserver(this); windowResized(application->width, application->height); @@ -244,6 +191,8 @@ void TitleState::exit() // Remove objects from scene application->defaultLayer->removeObject(&application->antHillModelInstance); + application->backgroundLayer->removeObject(&application->bgCamera); + application->backgroundLayer->removeObject(&application->bgBatch); application->inputManager->removeWindowObserver(this); } diff --git a/src/states/title-state.hpp b/src/states/title-state.hpp index db7ced0..d44d44f 100644 --- a/src/states/title-state.hpp +++ b/src/states/title-state.hpp @@ -26,7 +26,6 @@ #include "../game/ant.hpp" #include "../game/colony.hpp" - #include using namespace Emergent; @@ -49,10 +48,9 @@ public: private: float stateTime; bool fadeIn; - bool fadeOut; - + bool fadeOut; int substate; - + Colony colony; Ant* ant; Navmesh* navmesh;