/* * 2D Physic Engine * app.cpp: basic application handling. * Based on pikuma.com Learn Game Physics Engine Programming course. * Copyright (c) 2022 986-Studio. All rights reserved. * * Created by Manoƫl Trapier on 07/06/2022. */ #include #include #include #include #include #include #include #include #include #include void application::parseParameters(int argc, char *argv[]) { // Nothing to do for now } void application::setup() { this->running = graphics::openWindow(); this->millisecsPreviousFrame = SDL_GetTicks(); this->anchor = vec2(graphics::width() / 2.0,30); this->particles.push_back(new particle(graphics::width() / 2.0, graphics::height() / 2.0, 2.0, 10)); } void application::input() { SDL_Event event; while(SDL_PollEvent(&event)) { //ImGui_ImplSDL2_ProcessEvent(&event); //ImGuiIO &io = ImGui::GetIO(); switch(event.type) { case SDL_QUIT: this->running = false; break; case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_1) { this->showVelocity ^= true; } if (event.key.keysym.sym == SDLK_2) { this->showForceSum ^= true; } if (event.key.keysym.sym == SDLK_3) { this->showAllForces ^= true; } if (event.key.keysym.sym == SDLK_ESCAPE) { this->running = false; } if (event.key.keysym.sym == SDLK_UP) this->pushForce.y = -50 * PIXELS_PER_METER; if (event.key.keysym.sym == SDLK_RIGHT) this->pushForce.x = 50 * PIXELS_PER_METER; if (event.key.keysym.sym == SDLK_DOWN) this->pushForce.y = 50 * PIXELS_PER_METER; if (event.key.keysym.sym == SDLK_LEFT) this->pushForce.x = -50 * PIXELS_PER_METER; break; case SDL_KEYUP: if (event.key.keysym.sym == SDLK_UP) this->pushForce.y = 0; if (event.key.keysym.sym == SDLK_RIGHT) this->pushForce.x = 0; if (event.key.keysym.sym == SDLK_DOWN) this->pushForce.y = 0; if (event.key.keysym.sym == SDLK_LEFT) this->pushForce.x = 0; break; case SDL_MOUSEBUTTONDOWN: if (event.button.button == SDL_BUTTON_LEFT) { this->leftButtonPressed = true; } break; case SDL_MOUSEBUTTONUP: if (event.button.button == SDL_BUTTON_LEFT) { if (this->leftButtonPressed) { vec2 impulseDirection = (particles[0]->position - this->mouseCursor).unitVector(); double impulseMagnitude = (particles[0]->position - this->mouseCursor).magnitude() * 5.0; particles[0]->velocity = impulseDirection * impulseMagnitude; } this->leftButtonPressed = false; } break; case SDL_MOUSEMOTION: this->mouseCursor.x = event.motion.x; this->mouseCursor.y = event.motion.y; break; } } } void application::syncAndDeltatime() { waitTime.start(); /* Let's make sure we are running at the expected framerate */ uint32_t now = SDL_GetTicks(); int32_t timeToWait = MILLISECS_PER_FRAME - (now - this->millisecsPreviousFrame); if ((timeToWait > 0) && (timeToWait <= MILLISECS_PER_FRAME)) { SDL_Delay(timeToWait); } this->deltaTime = (SDL_GetTicks() - this->millisecsPreviousFrame) / 1000.0; /* clamp maximum deltatime to avoid weird behaviours */ if (this->deltaTime > 0.016) { this->deltaTime = 0.016; } this->millisecsPreviousFrame = SDL_GetTicks(); waitTime.stop(); } void application::update() { this->syncAndDeltatime(); updateTime.start(); for (auto part:this->particles) { part->addForce(pushForce); vec2 friction = force::generateFrictionForce(*part, 20); part->addForce(friction); vec2 drag = force::generateDragForce(*part, 0.001); part->addForce(drag); vec2 weight = vec2(0, part->mass * 9.81 * PIXELS_PER_METER); part->addForce(weight); } vec2 spring = force::generateSpringForce(*this->particles[0], this->anchor, this->restLenght, this->k); this->particles[0]->addForce(spring); for (auto part:this->particles) { part->integrate(deltaTime); } for (auto part:this->particles) { // check the particles position and keep the particle in the window. if ( ((part->position.y - part->radius) <= 0) || ((part->position.y + part->radius) >= graphics::height())) { if ((part->position.y - part->radius) <= 0) { part->position.y = part->radius; } else { part->position.y = graphics::height() - part->radius; } part->velocity.y = -part->velocity.y; } if ( ((part->position.x - part->radius) <= 0) || ((part->position.x + part->radius) >= graphics::width())) { if ((part->position.x - part->radius) <= 0) { part->position.x = part->radius; } else { part->position.x = graphics::width() - part->radius; } part->velocity.x = -part->velocity.x; } } updateTime.stop(); } void application::render() { renderTime.start(); graphics::clearScreen(0xFF0F0721); if (this->leftButtonPressed) { graphics::draw::line(this->particles[0]->position.x, this->particles[0]->position.y, this->mouseCursor.x, this->mouseCursor.y, 0xFFFFFFFF); } graphics::draw::line(this->anchor.x, this->anchor.y, this->particles[0]->position.x, this->particles[0]->position.y, 0xFF313131); graphics::draw::fillCircle(this->anchor.x, this->anchor.y, 5, 0xFF001155); for(auto part: this->particles) { graphics::draw::fillCircle(part->position.x, part->position.y, part->radius, 0xFFAA3300); } for(auto part: this->particles) { part->forceDebug(this->showVelocity, this->showForceSum, this->showAllForces); } graphics::draw::text(5, 5, 0x11FF22, "Wait time: %02.2f ms", waitTime.get()); graphics::draw::text(5, 17, 0x11FF22, "Update time: %02.2f ms", updateTime.get()); graphics::draw::text(5, 29, 0x11FF22, "Render time: %02.2f ms", renderTime.get()); graphics::draw::text(5, 41, 0x11FF22, "FPS: %.1f | dT: %.3f", 1000 / (waitTime.get() + updateTime.get() + renderTime.get()), deltaTime); graphics::draw::text(5, 53, 0x11FF22, "DBG: V:%c S:%c A:%c", this->showVelocity ? 'Y':'N', this->showForceSum ? 'Y':'N', this->showAllForces ? 'Y':'N'); graphics::renderFrame(); renderTime.stop(); } void application::destroy() { // Nothing for now. for (auto part:this->particles) { delete part; } graphics::closeWindow(); }