diff --git a/QPongApp/CMakeLists.txt b/App/CMakeLists.txt similarity index 64% rename from QPongApp/CMakeLists.txt rename to App/CMakeLists.txt index 99d4a3cc077976f3b64dbce5d42dfc07e3bb778f..97fee2d3e586a17c77fad8cc24d5f61a4b8c5305 100644 --- a/QPongApp/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable( QPongApp +add_executable( QPongGame GameLayer.cpp Log.cpp MainMenuLayer.cpp @@ -6,20 +6,21 @@ add_executable( QPongApp Window.cpp ) -target_include_directories( QPongApp PUBLIC +target_include_directories( QPong PUBLIC . .. ../libs ../libs/imgui/examples/libs/gl3w ) -target_link_libraries( QPongApp PUBLIC +target_link_libraries( QPongGame PUBLIC GuiCore + QPong fftw3 fftw3_omp ) -add_custom_command(TARGET QPongApp +add_custom_command(TARGET QPongGame POST_BUILD COMMAND ln -sf ${PROJECT_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}/QPongApp ) \ No newline at end of file diff --git a/QPongApp/GameLayer.cpp b/App/GameLayer.cpp similarity index 60% rename from QPongApp/GameLayer.cpp rename to App/GameLayer.cpp index c2bcbd0b159d0bc854f923f88cb20023c94a8917..eccb93e116b4bee556b189aac48f25e36bc5c555 100644 --- a/QPongApp/GameLayer.cpp +++ b/App/GameLayer.cpp @@ -22,8 +22,22 @@ #include <thread> +void calculatePointPositions(float *points, int arraySize); +void calculateMomentumViewPositions(float *points, int arraySize); +void calculateTriangleIndices(uint32_t *indices, int arraySize); +void calculateBorderPotential(float *potential, double *yAt, int arrayElements); -double QPong::GameLayer::GameLayer::Bat::s_speedStepSize = 0.005; + +const std::string vertexShaderPath = "assets/shaders/particle.vert.glsl"; +const std::string fragmentShaderPath = "assets/shaders/particle.frag.glsl"; + +const QPong::ParticleProps initialParticleSettings = { + .position = {0.0, 0.0, 0.0}, + .momentum = {0.0, 0.0, 0.0}, + .pointUnsharpness = 100.0, + .startValue = 0.01, + .hbar = 0.000125 +}; template <typename T> const T pow2(const T v) @@ -31,111 +45,8 @@ const T pow2(const T v) return v * v; } -QPong::GameLayer::GameLayer::Bat::Bat(double x, double y, int keyUp, int keyDown) - : m_x{x} - , m_y{y} - , m_vx{0.0} - , m_vy{0.0} - , m_height{0.21} - , m_width{0.0} - , m_keyUp{keyUp} - , m_keyDown{keyDown} -{ - -} - -void QPong::GameLayer::GameLayer::Bat::onUpdate(GuiCore::Timestep ts) -{ - constexpr double speed = 20.0; - if (GuiCore::Input::isKeyPressed(m_keyDown)) - { - m_vy += s_speedStepSize * speed; - } - else if (GuiCore::Input::isKeyPressed(m_keyUp)) - { - m_vy -= s_speedStepSize * speed; - } - else - { - m_vy = 0.0; - } - - constexpr double maxSpeedFactor = 300.0; - if (m_vy > s_speedStepSize * maxSpeedFactor) - { - m_vy = s_speedStepSize * maxSpeedFactor; - } - if (m_vy < -s_speedStepSize * maxSpeedFactor) - { - m_vy = -s_speedStepSize * maxSpeedFactor; - } - m_y += (m_vy * ts.getSeconds()); - - constexpr double maxY {1.3}; - if (m_y > maxY) - { - m_y = maxY; - } - if (m_y < -maxY) - { - m_y = -maxY; - } -} - -double QPong::GameLayer::Bat::getPotential(double x, double y) const -{ - y -= m_y; - double dist = 0.0; - double scale = 0.0000001; - - if (y >= m_height) - { - dist = pow2(x - m_x) + pow2(y - m_height) + m_width; - } - else if (y <= (-m_height)) - { - dist = pow2(x - m_x) + pow2((-y) - m_height) + m_width; - } - else - { - dist = pow2(m_x - x) + m_width; - } - return 1.0 / (pow2(dist) ) * scale; -} - -double QPong::GameLayer::Bat::getX() const -{ - return m_x; -} - -QPong::GameLayer::GameLayer(QPong::GameLayer::Properties props) - :Layer("GameLayer") - , m_vertexShaderPath{"assets/shaders/particle.vert.glsl"} - , m_fragmentShaderPath{"assets/shaders/particle.frag.glsl"} - , m_arraySize{props.resolution} - , m_propagate{false} - , m_leftBat(-0.8, 0.0, Key::W, Key::S) - , m_rightBat(0.8, 0.0, Key::Up, Key::Down) - , m_speedScale{3.0f} - , m_randomMomentum {true} -{ - srand (time(NULL)); - m_arrayElements = m_arraySize * m_arraySize; - m_numberOfQuads = (m_arraySize - 1) * (m_arraySize - 1); - m_numberOfIndices = m_numberOfQuads * 6; - - m_initialParticleProperties.position.x = 0.0; - m_initialParticleProperties.position.y = 0.0; - m_initialParticleProperties.position.z = 0.0; - m_initialParticleProperties.momentum.x = 1.0; - m_initialParticleProperties.momentum.y = 0.5; - m_initialParticleProperties.momentum.z = 0.00; - m_initialParticleProperties.pointUnsharpness = 60.0; - m_initialParticleProperties.startValue = 0.01; - m_initialParticleProperties.hbar = 0.0002; -} -void QPong::GameLayer::calculatePointPositions(float *positions) +void QPong::calculatePointPositions(float *positions, int arraySize) { constexpr float size{3.175f}; constexpr float xOffset{0.0f}; @@ -148,35 +59,35 @@ void QPong::GameLayer::calculatePointPositions(float *positions) constexpr float yMin{-height / 2.0}; constexpr float yMax{(height / 2.0)}; - float dx = (xMax - xMin) / (m_arraySize - 1.0f); // "spacing" - float dy = (yMax - yMin) / (m_arraySize - 1.0f); + float dx = (xMax - xMin) / (arraySize - 1.0f); // "spacing" + float dy = (yMax - yMin) / (arraySize - 1.0f); uint32_t i {0}; // array index - for (int yId{0}; yId < m_arraySize; ++yId) + for (int yId{0}; yId < arraySize; ++yId) { - for (int xId{0}; xId < m_arraySize; ++xId) + for (int xId{0}; xId < arraySize; ++xId) { - positions[i++] = xMin + xOffset + (static_cast<float>(xId) * dx); // x-Pos - positions[i++] = yMax + yOffset - (static_cast<float>(yId) * dy); // y-Pos - positions[i++] = 0.0f; // z-Pos + positions[i++] = xMin + xOffset + (static_cast<float>(xId) * dx); // x-Pos + positions[i++] = yMax + yOffset - (static_cast<float>(yId) * dy); // y-Pos + positions[i++] = 0.0f; // z-Pos } } } -void QPong::GameLayer::calculateMomentumViewPositions(float *positions) +void QPong::calculateMomentumViewPositions(float *positions, int arraySize) { constexpr float xOffset {1.5f}; // has to be the same as in the shader float xMin {2.2f + xOffset}; float xMax {0.2f + xOffset}; float yMin {-1.0f}; float yMax {1.0f}; - float dx = (xMax - xMin) / (m_arraySize - 1.0f); // "spacing" - float dy = (yMax - yMin) / (m_arraySize - 1.0f); + float dx = (xMax - xMin) / (arraySize - 1.0f); // "spacing" + float dy = (yMax - yMin) / (arraySize - 1.0f); { uint32_t i {0}; // array index - for (int yId{0}; yId < m_arraySize; ++yId) + for (int yId{0}; yId < arraySize; ++yId) { - for (int xId{0}; xId < m_arraySize; ++xId) + for (int xId{0}; xId < arraySize; ++xId) { positions[i++] = xMin + (static_cast<float>(xId) * dx); // x-Pos positions[i++] = yMax - (static_cast<float>(yId) * dy); // y-Pos @@ -186,82 +97,65 @@ void QPong::GameLayer::calculateMomentumViewPositions(float *positions) } } -void QPong::GameLayer::calculateTriangleIndices(uint32_t *indices) +void QPong::calculateTriangleIndices(uint32_t *indices, int arraySize) { uint32_t i{0}; - for (int row {0}; row < (m_arraySize - 1); ++row) + for (int row {0}; row < (arraySize - 1); ++row) { - for (int k {0}; k < (m_arraySize - 1); ++k) + for (int k {0}; k < (arraySize - 1); ++k) { - int offset = row * m_arraySize; + int offset = row * arraySize; indices[i++] = offset + k; indices[i++] = offset + k + 1; - indices[i++] = offset + k + m_arraySize; + indices[i++] = offset + k + arraySize; indices[i++] = offset + k + 1; - indices[i++] = offset + k + m_arraySize; - indices[i++] = offset + k + m_arraySize + 1; + indices[i++] = offset + k + arraySize; + indices[i++] = offset + k + arraySize + 1; } } } -void QPong::GameLayer::calculateBorderPotential(float *potential) +void QPong::calculateBorderPotential(float *potential, double *yAt, int arrayElements) { double y {0.0}; - for (int i {0}; i < m_arrayElements; ++i) + for (int i {0}; i < arrayElements; ++i) { - y = m_yAt[i]; + y = yAt[i]; float pot = pow2(y) * pow2(y) * pow2(y) * pow2(y) * pow2(y) * pow2(y) * pow2(y) * 0.02; potential[i] = pot; } } -double QPong::GameLayer::posAt(int pos) const -{ - return 2.0 * pos / m_arraySize - 1.0; -} -double QPong::GameLayer::xAtIndex(int i) const -{ - return posAt(i % m_arraySize); -} -double QPong::GameLayer::yAtIndex(int i) const -{ - return posAt(i / m_arraySize); -} - - -double QPong::GameLayer::potentialAt(int pos) const -{ - if (pos > m_arraySize / 2) - { - pos -= m_arraySize; - } - return pos * M_PI * m_initialParticleProperties.hbar; -} - -double QPong::GameLayer::pxAtIndex(int i) const -{ - return potentialAt(i % m_arraySize); -} - -double QPong::GameLayer::pyAtIndex(int i) const +QPong::GameLayer::GameLayer(int resolution) + :Layer("GameLayer") + , m_arraySize{resolution} + , m_propagate{false} + , m_leftBat(-0.8, 0.0, Key::W, Key::S) + , m_rightBat(0.8, 0.0, Key::Up, Key::Down) + , m_particleProperties {initialParticleSettings} + , m_speedScale{5.0f} + , m_randomMomentum {true} + , m_useThreads {false} { - return potentialAt(i / m_arraySize); + srand (time(NULL)); + m_arrayElements = m_arraySize * m_arraySize; + m_numberOfQuads = (m_arraySize - 1) * (m_arraySize - 1); + constexpr int indicesPerQuad {6}; // two triangles + m_numberOfIndices = m_numberOfQuads * indicesPerQuad; } - - void QPong::GameLayer::initialiseParticleMomentum() { constexpr std::complex<double> ci {0.0, 1.0}; // complex number - const double hbar = m_initialParticleProperties.hbar; - const double A0 = m_initialParticleProperties.startValue; - const double px0 = m_initialParticleProperties.momentum.x / 20.0; - const double py0 = m_initialParticleProperties.momentum.y / 20.0; - const double x0 = m_initialParticleProperties.position.x; - const double y0 = m_initialParticleProperties.position.y; - const double lambda = m_initialParticleProperties.pointUnsharpness; + const double hbar = m_particleProperties.hbar; + const double A0 = m_particleProperties.startValue; + const double px0 = m_particleProperties.momentum.x / 20.0; + const double py0 = m_particleProperties.momentum.y / 20.0; + const double x0 = m_particleProperties.position.x; + const double y0 = m_particleProperties.position.y; + const double lambda = m_particleProperties.pointUnsharpness; double sum {0.0}; m_particleAbsouluteSumStart = 0.0; @@ -294,7 +188,7 @@ double QPong::GameLayer::absorbParticle(int i, double c) void QPong::GameLayer::applyPotentials(int indexBegin, int indexEnd, double dt) { constexpr std::complex<double> ci {0.0, 1.0}; - const double hbar = m_initialParticleProperties.hbar; + const double hbar = m_particleProperties.hbar; double x {0.0}; double y {0.0}; const double potential = 1.0; @@ -337,22 +231,17 @@ void QPong::GameLayer::applyPotentials(int indexBegin, int indexEnd, double dt) void QPong::GameLayer::moveForward(int indexBegin, int indexEnd, double dt) { constexpr std::complex<double> ci {0.0, 1.0}; - const double hbar = m_initialParticleProperties.hbar; - double px {0.0}; - double py {0.0}; - constexpr double m = 1.0; + const double hbar = m_particleProperties.hbar; const double N = 1.0 / static_cast<double>(m_arrayElements); for (int i{indexBegin}; i<indexEnd; ++i) { - px = m_pxAt[i]; - py = m_pyAt[i]; - double E = (pow2(px) + pow2(py)) / (2.0 * m); - m_psi[i] *= exp(-ci * dt / hbar * E) * N; + m_psi[i] *= exp(-ci * dt / hbar * m_E_At[i]) * N; } } void QPong::GameLayer::updateMomentumView(int indexBegin, int indexEnd) { + GuiCore::Timer timer("updateMomentumView"); int x {0}; int y {0}; for (int i {indexBegin}; i< indexEnd; ++i) @@ -386,36 +275,31 @@ void QPong::GameLayer::propagateStep(double dt) { applyPotentials(0, m_arrayElements, dt); - { - GuiCore::Timer t("fftw_execute(forward)"); - fftw_execute(m_planForward); - } - + fftw_execute(m_planForward); + moveForward(0, m_arrayElements, dt); - updateMomentumView(0, m_arrayElements); + if (m_renderMomentum) { - GuiCore::Timer t("fftw_execute(backwards"); - fftw_execute(m_planBackwards); + updateMomentumView(0, m_arrayElements); } + + fftw_execute(m_planBackwards); } void QPong::GameLayer::onAttach() { - GuiCore::Timer timer("GameLayer::onAttach()"); GuiCore::Glyphs::setup(); m_xAt = new double[m_arrayElements]; m_yAt = new double[m_arrayElements]; - m_pxAt = new double[m_arrayElements]; - m_pyAt = new double[m_arrayElements]; + m_E_At = new double[m_arrayElements]; for (int i {0}; i < m_arrayElements; ++i) { - m_xAt[i] = xAtIndex(i); - m_yAt[i] = yAtIndex(i); - m_pxAt[i] = pxAtIndex(i); - m_pyAt[i] = pyAtIndex(i); + m_xAt[i] = xAtIndex(i, m_arraySize); + m_yAt[i] = yAtIndex(i, m_arraySize); + m_E_At[i] = (pow2(pxAtIndex(i, m_arraySize, m_particleProperties.hbar)) + pow2(pyAtIndex(i, m_arraySize, m_particleProperties.hbar))) / (2.0 * 1.0); } m_renderMomentum = false; @@ -425,13 +309,13 @@ void QPong::GameLayer::onAttach() m_playerTwo.totalScore = 0; m_shader = GuiCore::Shader::fromGLSLTextFiles( - m_vertexShaderPath, - m_fragmentShaderPath + vertexShaderPath, + fragmentShaderPath ); // static verticie positions m_particleViewPositions = new float[m_arrayElements * 3]; - calculatePointPositions(m_particleViewPositions); + calculatePointPositions(m_particleViewPositions, m_arraySize); // Vertex array to store all buffers glGenVertexArrays(1, &m_vertexArray); @@ -446,7 +330,7 @@ void QPong::GameLayer::onAttach() // Setup indies buffer uint32_t *indices = new uint32_t[m_numberOfIndices]; - calculateTriangleIndices(indices); + calculateTriangleIndices(indices, m_arraySize); glGenBuffers(1, &m_indicesBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indicesBuffer); @@ -466,11 +350,12 @@ void QPong::GameLayer::onAttach() // Setup momentum view m_momentumViewPositions = new float[m_arrayElements * 3]; - calculateMomentumViewPositions(m_momentumViewPositions); + calculateMomentumViewPositions(m_momentumViewPositions, m_arraySize); // Initialise Borders m_staticBorderPotential = new float[m_arrayElements]; - calculateBorderPotential(m_staticBorderPotential); + calculateBorderPotential(m_staticBorderPotential, m_yAt, m_arrayElements); + m_dynamicPotential = new float[m_arrayElements]; glGenBuffers(1, &m_potentialBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_potentialBuffer); @@ -479,7 +364,9 @@ void QPong::GameLayer::onAttach() glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(float), 0); // Set orthografic view projectin "camera" which will not change. - m_viewProjectionPosition = glm::ortho(-3.2f, 3.2f, -1.8f, 1.8f, -1.0f, 1.0f); + constexpr float viewWidth {3.2f}; + constexpr float viewHeight {1.8f}; + m_viewProjectionPosition = glm::ortho(-viewWidth, viewWidth, -viewHeight, viewHeight, -1.0f, 1.0f); m_viewProjectionLocation = glGetUniformLocation(m_shader->getRendererID(), "u_viewProjection"); m_showPotentialsLocation = glGetUniformLocation(m_shader->getRendererID(), "u_showPotential"); m_isMomentumEnabled = glGetUniformLocation(m_shader->getRendererID(), "u_isMomentumEnabled"); @@ -503,8 +390,8 @@ void QPong::GameLayer::onDetach() delete(m_staticBorderPotential); delete(m_xAt); delete(m_yAt); - delete(m_pxAt); - delete(m_pyAt); + delete(m_E_At); + // delete(m_pyAt); } void QPong::GameLayer::onEvent(GuiCore::Event& event) @@ -518,16 +405,16 @@ void QPong::GameLayer::reset() int xRand = rand() % 100; int yRand = rand() % 100; int positive = rand() % 2; - m_initialParticleProperties.momentum.x = (1.0f - (2.0f * positive)) * (1.0 + (float)xRand / 120.0f); + m_particleProperties.momentum.x = (1.0f - (2.0f * positive)) * (1.0 + (float)xRand / 120.0f); positive = rand() % 2; - m_initialParticleProperties.momentum.y = (1.0f - (2.0f * positive)) * (1.0 + (float)yRand / 120.0f); + m_particleProperties.momentum.y = (1.0f - (2.0f * positive)) * (1.0 + (float)yRand / 120.0f); } initialiseParticleMomentum(); m_restart = false; m_gameCounts = true; m_playerOne.currentlyScored = 0.0; m_playerTwo.currentlyScored = 0.0; - + propagateStep(0.0); } void QPong::GameLayer::onUpdate(GuiCore::Timestep ts) @@ -559,62 +446,112 @@ void QPong::GameLayer::onUpdate(GuiCore::Timestep ts) } } - // Particle - glBindBuffer(GL_ARRAY_BUFFER, m_pointsBuffer); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * m_arrayElements * 3, m_particleViewPositions); - - glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); - glBufferSubData(GL_ARRAY_BUFFER, 0, m_memorySize, m_psi); - - glBindBuffer(GL_ARRAY_BUFFER, m_potentialBuffer); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * m_arrayElements, m_dynamicPotential); - - glUseProgram(m_shader->getRendererID()); - glUniform1i(m_showPotentialsLocation, static_cast<int>(m_potentialsEnabled)); - glUniform1i(m_isMomentumEnabled, static_cast<int>(m_renderMomentum)); - glUniformMatrix4fv(m_viewProjectionLocation, 1, GL_FALSE, &m_viewProjectionPosition[0][0]); - glBindVertexArray(m_vertexArray); - glDrawElements(GL_TRIANGLES, m_numberOfIndices, GL_UNSIGNED_INT, nullptr); - - // Momentum - if (m_renderMomentum) - { + // Particle glBindBuffer(GL_ARRAY_BUFFER, m_pointsBuffer); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * m_arrayElements * 3, m_momentumViewPositions); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * m_arrayElements * 3, m_particleViewPositions); glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); - glBufferSubData(GL_ARRAY_BUFFER, 0, m_memorySize, m_momentum); + glBufferSubData(GL_ARRAY_BUFFER, 0, m_memorySize, m_psi); + glBindBuffer(GL_ARRAY_BUFFER, m_potentialBuffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * m_arrayElements, m_dynamicPotential); + glUseProgram(m_shader->getRendererID()); - glUniform1i(m_showPotentialsLocation, 0); + glUniform1i(m_showPotentialsLocation, static_cast<int>(m_potentialsEnabled)); + glUniform1i(m_isMomentumEnabled, static_cast<int>(m_renderMomentum)); + glUniformMatrix4fv(m_viewProjectionLocation, 1, GL_FALSE, &m_viewProjectionPosition[0][0]); glBindVertexArray(m_vertexArray); glDrawElements(GL_TRIANGLES, m_numberOfIndices, GL_UNSIGNED_INT, nullptr); - } + // Momentum + if (m_renderMomentum) + { + glBindBuffer(GL_ARRAY_BUFFER, m_pointsBuffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * m_arrayElements * 3, m_momentumViewPositions); + + glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, m_memorySize, m_momentum); + + glUseProgram(m_shader->getRendererID()); + glUniform1i(m_showPotentialsLocation, 0); + glBindVertexArray(m_vertexArray); + + glDrawElements(GL_TRIANGLES, m_numberOfIndices, GL_UNSIGNED_INT, nullptr); + } std::stringstream totalScore; totalScore << m_playerOne.totalScore << " : " << m_playerTwo.totalScore; GuiCore::Glyphs::renderText(totalScore.str(), -0.32f, 1.45f, 0.0075f, {1.0, 1.0, 1.0}); } +void QPong::GameLayer::guiSelectThreadCount() +{ + /// @todo Rework threading more efficient. + // ImGui::Checkbox("Enable multiple threads", &m_useThreads); + if (m_useThreads) + { + if (ImGui::Button("Less threads")) + { + if (m_renderingThreadsNumber > 1) + { + bool isPowerOf2 = false; + while (!isPowerOf2) + { + m_renderingThreadsNumber--; + for (int i = 0; i < 10; i++) + { + if (m_renderingThreadsNumber == pow(2, i)) + { + isPowerOf2 = true; + break; + } + } + } + } + } + ImGui::SameLine(); + if (ImGui::Button("More Threads")) + { + if (m_renderingThreadsNumber < 200) + { + bool isPowerOf2 = false; + while (!isPowerOf2) + { + m_renderingThreadsNumber++; + for (int i = 0; i < 10; i++) + { + if (m_renderingThreadsNumber == pow(2, i)) + { + isPowerOf2 = true; + break; + } + } + } + } + } + ImGui::SameLine(); + ImGui::Text("%d", m_renderingThreadsNumber); + } +} + void QPong::GameLayer::onGuiRender() { // GUI settings for the particle ImGui::Begin("Particle Settings"); // Select initial momentum for particle at restart - ImGui::DragFloat2("Momentum [x,y]", glm::value_ptr(m_initialParticleProperties.momentum), 0.01f, -3.0f, 3.0f); + ImGui::DragFloat2("Momentum [x,y]", glm::value_ptr(m_particleProperties.momentum), 0.01f, -3.0f, 3.0f); ImGui::Checkbox("Random Momentum", &m_randomMomentum); - float pointUnsharpness = m_initialParticleProperties.pointUnsharpness; + float pointUnsharpness = m_particleProperties.pointUnsharpness; if (ImGui::DragFloat("Sharpness", &pointUnsharpness, 1.0f, 5.0f, 1000.0f)) { - m_initialParticleProperties.pointUnsharpness = pointUnsharpness; + m_particleProperties.pointUnsharpness = pointUnsharpness; } // Select hbar, changes apply on the fly. - float hbar = m_initialParticleProperties.hbar; + float hbar = m_particleProperties.hbar; if (ImGui::DragFloat("hbar", &hbar, 0.000001f, 0.000001f, 1.0f, "%.6f")) { - m_initialParticleProperties.hbar = hbar; + m_particleProperties.hbar = hbar; } // This value increases or decreases the time step made every iteration. @@ -623,62 +560,23 @@ void QPong::GameLayer::onGuiRender() ImGui::SetTooltip("Change the size of the timestep (dt)\nfor every iteration of the simulation."); } - // Decrease the - if (ImGui::Button("Less threads")) - { - if (m_renderingThreadsNumber > 1) - { - bool isPowerOf2 = false; - while (!isPowerOf2) - { - m_renderingThreadsNumber--; - for (int i = 0; i < 10; i++) - { - if (m_renderingThreadsNumber == pow(2, i)) - { - isPowerOf2 = true; - break; - } - } - } - } - } - ImGui::SameLine(); - if (ImGui::Button("More Threads")) - { - if (m_renderingThreadsNumber < 200) - { - bool isPowerOf2 = false; - while (!isPowerOf2) - { - m_renderingThreadsNumber++; - for (int i = 0; i < 10; i++) - { - if (m_renderingThreadsNumber == pow(2, i)) - { - isPowerOf2 = true; - break; - } - } - } - } - } - ImGui::SameLine(); - ImGui::Text("%d", m_renderingThreadsNumber); + guiSelectThreadCount(); + ImGui::Checkbox("Render Momentum", &m_renderMomentum); ImGui::Checkbox("Enable Potentials", &m_potentialsEnabled); ImGui::Checkbox("Enable Particle absorption", &m_removeParticleEnabled); + // Start/Stop propagating if (m_propagate) { - if (ImGui::Button("Pause")) + if (ImGui::Button("PAUSE")) { m_propagate = false; } } else { - if (ImGui::Button("Play")) + if (ImGui::Button("PLAY")) { m_propagate = true; } diff --git a/App/GameLayer.hpp b/App/GameLayer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6ad87e88549f71f02f78a02b99e4cba688b128a3 --- /dev/null +++ b/App/GameLayer.hpp @@ -0,0 +1,110 @@ +/// @file GameLayer.hpp +/// @author Armin Co +/// + +#ifndef QPONG_GAME_LAYER_HPP +#define QPONG_GAME_LAYER_HPP + +#include <glm/glm.hpp> + +#include <GuiCore/Layer.hpp> +#include <GuiCore/Shader.hpp> +#include <GuiCore/Timestep.hpp> + +#include <QPong/Bat.hpp> +#include <QPong/Particle.hpp> + +#include <complex> +#include <fftw3.h> + +namespace QPong +{ + struct ParticleProps + { + glm::vec3 position; + glm::vec3 momentum; + double pointUnsharpness; + double startValue; + double hbar; + }; + + struct Player + { + double currentlyScored; + int totalScore; + }; + + class GameLayer : public GuiCore::Layer + { + public: + GameLayer(int resolution); + virtual ~GameLayer(){}; + virtual void onAttach() override; + virtual void onDetach() override; + virtual void onUpdate(GuiCore::Timestep ts) override; + virtual void onGuiRender() override; + virtual void onEvent(GuiCore::Event &event) override; + + private: + void initialiseParticleMomentum(); + void propagateStep(double dt); + void applyPotentials(int indexBegin, int indexEnd, double dt); + void moveForward(int indexBegin, int indexEnd, double dt); + void updateMomentumView(int indexBegin, int indexEnd); + double absorbParticle(int i, double c); + + void reset(); + void guiSelectThreadCount(); + + Bat m_leftBat; + Bat m_rightBat; + + Player m_playerOne; + Player m_playerTwo; + + int m_arraySize; + int m_arrayElements; + int m_numberOfQuads; + int m_numberOfIndices; + + size_t m_memorySize; + GuiCore::Shader *m_shader; + GLuint m_vertexArray; ///< OpenGL object that "knows" all buffers. + GLuint m_pointsBuffer; ///< Buffer with the location for each element in the particle array + GLuint m_indicesBuffer; ///< Indicies to draw trinagles from the points to fill the "canvas" + GLuint m_colorBuffer; ///< Reference for OpenGL to load the paritcle data to the GPU + GLuint m_potentialBuffer; ///< Borders and bats + + float *m_particleViewPositions; + float *m_momentumViewPositions; + float *m_staticBorderPotential; + float *m_dynamicPotential; + + GLuint m_isMomentumEnabled; + GLuint m_viewProjectionLocation; + GLuint m_showPotentialsLocation; + glm::mat4 m_viewProjectionPosition; + + ParticleProps m_particleProperties; + fftw_plan m_planForward; + fftw_plan m_planBackwards; + double m_particleAbsouluteSumStart; + double *m_xAt; + double *m_yAt; + double *m_E_At; + std::complex<double> *m_psi; + std::complex<double> *m_momentum; + + bool m_gameCounts; + bool m_useThreads; + bool m_restart; + bool m_propagate; + bool m_renderMomentum; + bool m_randomMomentum; + bool m_removeParticleEnabled; + bool m_potentialsEnabled; + float m_speedScale{1.0f}; + int m_renderingThreadsNumber{8}; + }; +} +#endif \ No newline at end of file diff --git a/QPongApp/Log.cpp b/App/Log.cpp similarity index 100% rename from QPongApp/Log.cpp rename to App/Log.cpp diff --git a/QPongApp/Log.hpp b/App/Log.hpp similarity index 100% rename from QPongApp/Log.hpp rename to App/Log.hpp diff --git a/QPongApp/MainMenuLayer.cpp b/App/MainMenuLayer.cpp similarity index 100% rename from QPongApp/MainMenuLayer.cpp rename to App/MainMenuLayer.cpp diff --git a/QPongApp/MainMenuLayer.hpp b/App/MainMenuLayer.hpp similarity index 100% rename from QPongApp/MainMenuLayer.hpp rename to App/MainMenuLayer.hpp diff --git a/QPongApp/QPongApp.cpp b/App/QPongApp.cpp similarity index 100% rename from QPongApp/QPongApp.cpp rename to App/QPongApp.cpp diff --git a/QPongApp/Window.cpp b/App/Window.cpp similarity index 100% rename from QPongApp/Window.cpp rename to App/Window.cpp diff --git a/QPongApp/Window.hpp b/App/Window.hpp similarity index 100% rename from QPongApp/Window.hpp rename to App/Window.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e0c1a4b8c47d0f7e973a5aa99af84dbd8c46661..27a4f1c98e7595d8156c6d1586ae15533bd81858 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,11 @@ find_package(PkgConfig REQUIRED) # GUI/Window add_subdirectory(GuiCore) +# QPong Basics +add_subdirectory(QPong) + # QPong App sources -add_subdirectory(QPongApp) +add_subdirectory(App) # Libraries the project depends on: add_subdirectory(libs/spdlog) diff --git a/GuiCore/Application.cpp b/GuiCore/Application.cpp index 35ccc77582faf1a09a9dc513d4684d56a791aa95..771a7195e36bbdaf94b85eacd6dcffa1e2ab8e6f 100644 --- a/GuiCore/Application.cpp +++ b/GuiCore/Application.cpp @@ -8,23 +8,31 @@ #include "Log.hpp" #include "ApplicationEvent.hpp" - namespace GuiCore { - Application* Application::s_instance = nullptr; + Application *Application::s_instance = nullptr; Application::Application(const std::string &name, uint32_t width, uint32_t height) { if (s_instance == nullptr) { Log::setup(); - } s_instance = this; m_window = std::unique_ptr<IWindow>(IWindow::create({name, width, height})); - m_window->setEventCallback([this](auto &&... args) -> decltype(auto) { return this->onEvent(std::forward<decltype(args)>(args)...); }); - + m_window->setEventCallback([this](auto &&... args) -> decltype(auto) { + return this->onEvent(std::forward<decltype(args)>(args)...); }); m_guiLayer = std::make_shared<ImGuiLayer>(); pushLayer(m_guiLayer); + } + else + { + Log::get()->debug("Application object was created before."); + } + } + + Application::~Application() + { + s_instance = nullptr; } void Application::pushLayer(std::shared_ptr<Layer> layer) @@ -45,7 +53,7 @@ namespace GuiCore [this](auto &&... args) -> decltype(auto) { return this->onWindowClose(std::forward<decltype(args)>(args)...); }); dispatcher.dispatch<WindowResizeEvent>( [this](auto &&... args) -> decltype(auto) { return this->onWindowResize(std::forward<decltype(args)>(args)...); }); - + for (auto it = m_layerStack.rbegin(); it != m_layerStack.rend(); ++it) { if (event.handled) @@ -73,7 +81,7 @@ namespace GuiCore } m_guiLayer->begin(); - for (auto layer: m_layerStack) + for (auto layer : m_layerStack) { layer->onGuiRender(); } @@ -91,21 +99,20 @@ namespace GuiCore bool Application::onWindowResize(Event &event) { - WindowResizeEvent *rsEvent = static_cast<WindowResizeEvent*>(&event); + WindowResizeEvent *rsEvent = static_cast<WindowResizeEvent *>(&event); Log::get()->trace("Window minimized, {0}, {1}", rsEvent->getWidth(), rsEvent->getHeight()); - glViewport(0,0, rsEvent->getWidth(), rsEvent->getHeight()); + glViewport(0, 0, rsEvent->getWidth(), rsEvent->getHeight()); return false; } - Application& Application::get() + Application &Application::get() { return *s_instance; } - IWindow& Application::getWindow() + IWindow &Application::getWindow() { return *m_window; } } - diff --git a/GuiCore/Application.hpp b/GuiCore/Application.hpp index ad48c239feda5139a856edd86811142a668e8bbf..270db4c33bd6e557564c32c0c8edafdec16633cc 100644 --- a/GuiCore/Application.hpp +++ b/GuiCore/Application.hpp @@ -16,28 +16,46 @@ namespace GuiCore { + /// @brief Application + /// @details + /// The Application class contains the window + /// and the layer stack of the game/app. + /// The app can be started by calling the run method + /// after the app was created. + /// class Application { public: + /// @brief The constructor creates the window for the application. + /// @details + /// The application is a singleton and only one instance can be created. + /// To receive a reference to the application use the get() method. + /// + /// @param name "Title that will be shown oon the window." + /// @param width Width of the window. + /// @param height Height of the window. + /// Application(const std::string &name = "Application", uint32_t width = 1280, uint32_t height = 720); - virtual ~Application() {} + virtual ~Application(); void run(); - void onEvent(Event &e); + void onEvent(Event &event); void pushLayer(std::shared_ptr<Layer> layer); void popLayer(std::shared_ptr<Layer> layer); - IWindow& getWindow(); + IWindow &getWindow(); bool onWindowClose(Event &e); bool onWindowResize(Event &e); - static Application& get(); + static Application &get(); private: - std::unique_ptr<IWindow> m_window; + Application(Application&) = delete; + Application(Application&&) = delete; bool m_isRunnig = true; bool m_minimzed = false; - LayerStack m_layerStack; float m_lastFrameTime = 0.0f; + LayerStack m_layerStack; + std::unique_ptr<IWindow> m_window; std::shared_ptr<ImGuiLayer> m_guiLayer; static Application *s_instance; diff --git a/GuiCore/ApplicationEvent.hpp b/GuiCore/ApplicationEvent.hpp index b71ec60974387776611c6d6020b003c57fcd3381..3c095fa986412305c6fa1b3b4c07f2fd6102cbf7 100644 --- a/GuiCore/ApplicationEvent.hpp +++ b/GuiCore/ApplicationEvent.hpp @@ -14,13 +14,12 @@ namespace GuiCore { public: WindowResizeEvent(uint32_t width, uint32_t height) - : m_width{width} - , m_height{height} + : m_width{width}, m_height{height} { } - uint32_t getWidth() const {return m_width;} - uint32_t getHeight() const {return m_height;} + uint32_t getWidth() const { return m_width; } + uint32_t getHeight() const { return m_height; } std::string toString() const override { diff --git a/GuiCore/Event.hpp b/GuiCore/Event.hpp index c1390bd22f14913b3c29e5ae15a2f3b710350fff..58e87d4d85d10b53f9919ad62ae8b61805ec3666 100644 --- a/GuiCore/Event.hpp +++ b/GuiCore/Event.hpp @@ -15,26 +15,40 @@ namespace GuiCore enum class EventType { None = 0, - WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved, - AppTick, AppUpdate, AppRender, - KeyPressed, KeyReleased, KeyTyped, - MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled + WindowClose, + WindowResize, + WindowFocus, + WindowLostFocus, + WindowMoved, + AppTick, + AppUpdate, + AppRender, + KeyPressed, + KeyReleased, + KeyTyped, + MouseButtonPressed, + MouseButtonReleased, + MouseMoved, + MouseScrolled }; - + enum EventCategory - { - None = 0, - EventCategoryApplication = BIT(0), - EventCategoryInput = BIT(1), - EventCategoryKeyboard = BIT(2), - EventCategoryMouse = BIT(3), - EventCategoryMouseButton = BIT(4) - }; + { + None = 0, + EventCategoryApplication = BIT(0), + EventCategoryInput = BIT(1), + EventCategoryKeyboard = BIT(2), + EventCategoryMouse = BIT(3), + EventCategoryMouseButton = BIT(4) + }; + +#define EVENT_CLASS_TYPE(type) \ + static EventType getStaticType() { return EventType::type; } \ + virtual EventType getEventType() const override { return getStaticType(); } \ + virtual const char *getName() const override { return #type; } -#define EVENT_CLASS_TYPE(type) static EventType getStaticType() { return EventType::type; }\ - virtual EventType getEventType() const override { return getStaticType(); }\ - virtual const char* getName() const override { return #type; } -#define EVENT_CLASS_CATEGORY(category) virtual int getCategoryFlags() const override { return category; } +#define EVENT_CLASS_CATEGORY(category) \ + virtual int getCategoryFlags() const override { return category; } class Event { @@ -42,9 +56,9 @@ namespace GuiCore bool handled = false; virtual EventType getEventType() const = 0; - virtual const char * getName() const = 0; + virtual const char *getName() const = 0; virtual int getCategoryFlags() const = 0; - virtual std::string toString() const {return getName();} + virtual std::string toString() const { return getName(); } inline bool isInCategory(EventCategory category) { @@ -60,21 +74,22 @@ namespace GuiCore { } - template<typename T, typename F> - bool dispatch(const F& function) + template <typename T, typename F> + bool dispatch(const F &function) { if (m_event.getEventType() == T::getStaticType()) { - m_event.handled = function(static_cast<T&>(m_event)); + m_event.handled = function(static_cast<T &>(m_event)); return true; } return false; } + private: Event &m_event; }; - inline std::ostream& operator<<(std::ostream& os, const Event &e) + inline std::ostream &operator<<(std::ostream &os, const Event &e) { return os << e.toString(); } diff --git a/GuiCore/Glyphs.cpp b/GuiCore/Glyphs.cpp index 9d30992c002a8cc8ffbfe67f74bd3b5359cd7b08..034cbd9e73d2ce1a21f85ad71969a35da6863443 100644 --- a/GuiCore/Glyphs.cpp +++ b/GuiCore/Glyphs.cpp @@ -7,15 +7,11 @@ #include "Log.hpp" #include <glm/gtc/type_ptr.hpp> - -extern "C" { #include <ft2build.h> #include FT_FREETYPE_H -} - std::map<char, GuiCore::Character> GuiCore::Glyphs::s_characters; -GuiCore::Shader* GuiCore::Glyphs::m_shader; +GuiCore::Shader *GuiCore::Glyphs::m_shader; GLuint GuiCore::Glyphs::m_vertexArray; GLuint GuiCore::Glyphs::m_vertexBuffer; glm::mat4 GuiCore::Glyphs::projection = glm::ortho(-3.2f, 3.2f, -1.8f, 1.8f, -1.0f, 1.0f); @@ -24,8 +20,7 @@ void GuiCore::Glyphs::setup() { m_shader = GuiCore::Shader::fromGLSLTextFiles( "assets/shaders/glyph.vert.glsl", - "assets/shaders/glyph.frag.glsl" - ); + "assets/shaders/glyph.frag.glsl"); glGenVertexArrays(1, &m_vertexArray); glGenBuffers(1, &m_vertexBuffer); @@ -40,53 +35,55 @@ void GuiCore::Glyphs::setup() FT_Library ft; if (FT_Init_FreeType(&ft)) { - GuiCore::Log::get()->error("ERROR::FREETYPE: Could not init FreeType Library"); + GuiCore::Log::get()->error("Error::Freetype: Could not init FreeType Library"); } + const char *fontPath = "assets/Font.ttf"; FT_Face face; - if (FT_New_Face(ft, "assets/Font.ttf", 0, &face)) + if (FT_New_Face(ft, fontPath, 0, &face)) { - GuiCore::Log::get()->error("ERROR::FREETYPE: Failed to load font"); + GuiCore::Log::get()->error("Error::Freetype: Failed to load font from: {}", fontPath); } - FT_Set_Pixel_Sizes(face, 0, 48); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - for (unsigned char c = 0; c < 128; c++) + else { - // load character glyph - auto error = FT_Load_Char(face, c, FT_LOAD_RENDER); - if (error) + FT_Set_Pixel_Sizes(face, 0, 48); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + for (unsigned char c = 0; c < 128; c++) { - GuiCore::Log::get()->error("ERROR::FREETYTPE: Failed to load Glyph {0} {1}", c, error); - continue; + // load character glyph + auto error = FT_Load_Char(face, c, FT_LOAD_RENDER); + if (error) + { + GuiCore::Log::get()->error("Error::Freetype: Failed to load Glyph {0} {1}", c, error); + continue; + } + // generate texture + unsigned int texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RED, + face->glyph->bitmap.width, + face->glyph->bitmap.rows, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + face->glyph->bitmap.buffer); + // set texture options + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // now store character for later use + Character character = { + texture, + glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), + glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), + static_cast<uint32_t>(face->glyph->advance.x)}; + s_characters.insert(std::pair<char, Character>(c, character)); } - // generate texture - unsigned int texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RED, - face->glyph->bitmap.width, - face->glyph->bitmap.rows, - 0, - GL_RED, - GL_UNSIGNED_BYTE, - face->glyph->bitmap.buffer - ); - // set texture options - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // now store character for later use - Character character = { - texture, - glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), - glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), - static_cast<uint32_t>(face->glyph->advance.x) - }; - s_characters.insert(std::pair<char, Character>(c, character)); } } @@ -110,14 +107,14 @@ void GuiCore::Glyphs::renderText(std::string text, float x, float y, float scale float h = ch.size.y * scale; float vertices[6][4] = { - { xPos, yPos + h, 0.0f, 0.0f }, - { xPos, yPos, 0.0f, 1.0f }, - { xPos + w, yPos, 1.0f, 1.0f }, + {xPos, yPos + h, 0.0f, 0.0f}, + {xPos, yPos, 0.0f, 1.0f}, + {xPos + w, yPos, 1.0f, 1.0f}, + + {xPos, yPos + h, 0.0f, 0.0f}, + {xPos + w, yPos, 1.0f, 1.0f}, + {xPos + w, yPos + h, 1.0f, 0.0f}}; - { xPos, yPos + h, 0.0f, 0.0f }, - { xPos + w, yPos, 1.0f, 1.0f }, - { xPos + w, yPos + h, 1.0f, 0.0f } - }; glBindTexture(GL_TEXTURE_2D, ch.textureId); glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); diff --git a/GuiCore/Glyphs.hpp b/GuiCore/Glyphs.hpp index 75c395393f183f99d8feb1415a4aef4fc441494d..07f2172f91126cd75b73cc723f9d1c8390b739d0 100644 --- a/GuiCore/Glyphs.hpp +++ b/GuiCore/Glyphs.hpp @@ -8,31 +8,30 @@ #include <map> #include <glm/glm.hpp> - #include "Shader.hpp" - namespace GuiCore { -struct Character { - unsigned int textureId; // ID handle of the glyph texture - glm::ivec2 size; // Size of glyph - glm::ivec2 bearing; // Offset from baseline to left/top of glyph - unsigned int advance; // Offset to advance to next glyph -}; - -class Glyphs -{ -public: - static void setup(); - static void renderText(std::string text, float x, float y, float scale, glm::vec3 color); - -private: - static std::map<char, Character> s_characters; - static Shader *m_shader; - static GLuint m_vertexArray; - static GLuint m_vertexBuffer; - static glm::mat4 projection; -}; + struct Character + { + unsigned int textureId; // ID handle of the glyph texture + glm::ivec2 size; // Size of glyph + glm::ivec2 bearing; // Offset from baseline to left/top of glyph + unsigned int advance; // Offset to advance to next glyph + }; + + class Glyphs + { + public: + static void setup(); + static void renderText(std::string text, float x, float y, float scale, glm::vec3 color); + + private: + static std::map<char, Character> s_characters; + static Shader *m_shader; + static GLuint m_vertexArray; + static GLuint m_vertexBuffer; + static glm::mat4 projection; + }; } diff --git a/GuiCore/Timer.cpp b/GuiCore/Timer.cpp index 4d070e37f1298a6ea57f3f5df6923652dbfd2cc2..908adc57a163ec71b97320026d051461527fa5cf 100644 --- a/GuiCore/Timer.cpp +++ b/GuiCore/Timer.cpp @@ -10,7 +10,7 @@ using namespace GuiCore; Timer::Timer(const char* name) : m_name{name} { - m_startTimepoint = std::chrono::high_resolution_clock::now(); + start(); } Timer::~Timer() @@ -18,6 +18,11 @@ Timer::~Timer() stop(); } +void Timer::start() +{ + m_startTimepoint = std::chrono::high_resolution_clock::now(); +} + double Timer::stop() { auto endTimepoint = std::chrono::high_resolution_clock::now(); diff --git a/GuiCore/Timer.hpp b/GuiCore/Timer.hpp index 5508fe1e0fce97eb160ef611a628203e29b61c75..999b537efe40e8adb8de8c80fcbb5f7cdb2232a0 100644 --- a/GuiCore/Timer.hpp +++ b/GuiCore/Timer.hpp @@ -20,6 +20,7 @@ public: /// @brief Stops timer. ~Timer(); + void start(); double stop(); private: std::chrono::time_point< std::chrono::high_resolution_clock > m_startTimepoint; diff --git a/QPong/Bat.cpp b/QPong/Bat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d46d7f96ffc3e561a2a80ad756f84525ea40ef0 --- /dev/null +++ b/QPong/Bat.cpp @@ -0,0 +1,90 @@ +/// @file Bat.cpp +/// @author Armin Co +/// + +#include "Bat.hpp" +#include <GuiCore/Input.hpp> + +static constexpr double s_speedStepSize = 0.005; + +template <typename T> +const T pow2(const T v) +{ + return v * v; +} + +QPong::Bat::Bat(double x, double y, int keyUp, int keyDown) + : m_x{x} + , m_y{y} + , m_vx{0.0} + , m_vy{0.0} + , m_height{0.21} + , m_width{0.0} + , m_keyUp{keyUp} + , m_keyDown{keyDown} +{ +} + +void QPong::Bat::onUpdate(GuiCore::Timestep ts) +{ + constexpr double speed = 20.0; + if (GuiCore::Input::isKeyPressed(m_keyDown)) + { + m_vy += s_speedStepSize * speed; + } + else if (GuiCore::Input::isKeyPressed(m_keyUp)) + { + m_vy -= s_speedStepSize * speed; + } + else + { + m_vy = 0.0; + } + + constexpr double maxSpeedFactor = 300.0; + if (m_vy > s_speedStepSize * maxSpeedFactor) + { + m_vy = s_speedStepSize * maxSpeedFactor; + } + if (m_vy < -s_speedStepSize * maxSpeedFactor) + { + m_vy = -s_speedStepSize * maxSpeedFactor; + } + m_y += (m_vy * ts.getSeconds()); + + constexpr double maxY {1.3}; + if (m_y > maxY) + { + m_y = maxY; + } + if (m_y < -maxY) + { + m_y = -maxY; + } +} + +double QPong::Bat::getPotential(double x, double y) const +{ + y -= m_y; + double dist = 0.0; + double scale = 0.0000001; + + if (y >= m_height) + { + dist = pow2(x - m_x) + pow2(y - m_height) + m_width; + } + else if (y <= (-m_height)) + { + dist = pow2(x - m_x) + pow2((-y) - m_height) + m_width; + } + else + { + dist = pow2(m_x - x) + m_width; + } + return 1.0 / (pow2(dist) ) * scale; +} + +double QPong::Bat::getX() const +{ + return m_x; +} \ No newline at end of file diff --git a/QPong/Bat.hpp b/QPong/Bat.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3b46a997e27fc5194249d2753d28eeb19a67a45a --- /dev/null +++ b/QPong/Bat.hpp @@ -0,0 +1,32 @@ +/// @file Bat.hpp +/// @author Armin Co +/// + +#ifndef QPONG_BAT_HPP +#define QPONG_BAT_HPP + +#include <GuiCore/Timestep.hpp> + +namespace QPong +{ + class Bat + { + public: + Bat(double x, double y, int keyUp, int keyDown); + double getX() const; + double getY() const; + void onUpdate(GuiCore::Timestep ts); + double getPotential(double x, double y) const; + private: + double m_x; + double m_y; + double m_vx; + double m_vy; + double m_height; + double m_width; + int m_keyUp; + int m_keyDown; + }; +} + +#endif \ No newline at end of file diff --git a/QPong/CMakeLists.txt b/QPong/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..10028a94d1091711feb3706c9763846a0d745a9d --- /dev/null +++ b/QPong/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(QPong + Bat.cpp + Particle.cpp +) + +target_include_directories(QPong PUBLIC + .. +) + +target_link_libraries( QPong + GuiCore +) \ No newline at end of file diff --git a/QPong/Matchfield.cpp b/QPong/Matchfield.cpp new file mode 100644 index 0000000000000000000000000000000000000000..402362fdf9c73b4d6e4b9d09b85231c83b26cfd3 --- /dev/null +++ b/QPong/Matchfield.cpp @@ -0,0 +1,16 @@ +/// @file Matchfield.cpp +/// @author Armin Co +/// + +#include "Matchfield.hpp" + +#include <GuiCore/Input.hpp> + +using namespace QPong; + +Matchfield::Matchfield(unsigned size) + : m_leftBat(-0.8, 0.0, Key::W, Key::S) + , m_rightBat(0.8, 0.0, Key::Up, Key::Down) +{ + +} \ No newline at end of file diff --git a/QPong/Matchfield.hpp b/QPong/Matchfield.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f6327ace07627c7664fa09cb733fcdb789a8d6b0 --- /dev/null +++ b/QPong/Matchfield.hpp @@ -0,0 +1,48 @@ +/// @file Matchfield.hpp +/// @author Armin Co +/// + +#ifndef QPONG_MATCHFIELD_HPP +#define QPONG_MATCHFIELD_HPP + +#include <glm/glm.hpp> +#include "Bat.hpp" + +namespace QPong +{ + /// @brief Properties of the particle + /// + struct ParticleProperties + { + glm::vec3 position; + glm::vec3 momentum; + double pointUnsharpness; + double startValue; + double hbar; + }; + + /// @brief Score of a player. + /// + struct Player + { + double currentlyScored; + int totalScore; + }; + + class Matchfield + { + public: + Matchfield(unsigned size); + + private: + int m_size; + + Bat m_leftBat; + Bat m_rightBat; + + Player m_playerOne; + Player m_playerTwo; + }; +} + +#endif \ No newline at end of file diff --git a/QPong/Particle.cpp b/QPong/Particle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd98359a5d713080e14dc323841bab159cf2aaae --- /dev/null +++ b/QPong/Particle.cpp @@ -0,0 +1,38 @@ +#include "Particle.hpp" + +using namespace QPong; + +double QPong::posAt(int pos, int arraySize) +{ + return 2.0 * pos / arraySize - 1.0; +} + +double QPong::xAtIndex(int i, int arraySize) +{ + return posAt(i % arraySize, arraySize); +} + +double QPong::yAtIndex(int i, int arraySize) +{ + return posAt(i / arraySize, arraySize); +} + + +double QPong::potentialAt(int pos, int arraySize, double hbar) +{ + if (pos > arraySize / 2) + { + pos -= arraySize; + } + return pos * M_PI * hbar; +} + +double QPong::pxAtIndex(int i, int arraySize, double hbar) +{ + return potentialAt(i % arraySize, arraySize, hbar); +} + +double QPong::pyAtIndex(int i, int arraySize, double hbar) +{ + return potentialAt(i / arraySize, arraySize, hbar); +} diff --git a/QPong/Particle.hpp b/QPong/Particle.hpp new file mode 100644 index 0000000000000000000000000000000000000000..31bef370fd7951c6542d23cde79fbd5286b3c20c --- /dev/null +++ b/QPong/Particle.hpp @@ -0,0 +1,16 @@ + +#ifndef QPONG_PARTICLE_HPP +#define QPONG_PARTICLE_HPP + +namespace QPong +{ + + double posAt(int pos, int arraySize); + double xAtIndex(int i, int arraySize); + double yAtIndex(int i, int arraySize); + double potentialAt(int pos, int arraySize, double hbar); + double pxAtIndex(int i, int arraySize, double hbar); + double pyAtIndex(int i, int arraySize, double hbar); +} + +#endif \ No newline at end of file diff --git a/QPongApp/GameLayer.hpp b/QPongApp/GameLayer.hpp deleted file mode 100644 index 296c08a7e18504fa4a4746a6353ab5fdfdd11fef..0000000000000000000000000000000000000000 --- a/QPongApp/GameLayer.hpp +++ /dev/null @@ -1,146 +0,0 @@ -/// @file GameLayer.hpp -/// @author Armin Co -/// - -#ifndef QPONG_GAME_LAYER_HPP -#define QPONG_GAME_LAYER_HPP - -#include <glm/glm.hpp> - -#include <GuiCore/Layer.hpp> -#include <GuiCore/Shader.hpp> -#include <GuiCore/Timestep.hpp> - -#include <complex> -#include <fftw3.h> - -namespace QPong -{ -class GameLayer : public GuiCore::Layer -{ -public: - struct Properties - { - int resolution; - Properties(int res) : resolution {res}{}; - }; - - struct ParticleProps - { - glm::vec3 position; - glm::vec3 momentum; - double pointUnsharpness; - double startValue; - double hbar; - }; - - struct Player - { - double currentlyScored; - int totalScore; - }; - - class Bat - { - public: - Bat(double x, double y, int keyUp, int keyDown); - double getX() const; - double getY() const; - void onUpdate(GuiCore::Timestep ts); - double getPotential(double x, double y) const; - private: - double m_x; - double m_y; - double m_vx; - double m_vy; - double m_height; - double m_width; - int m_keyUp; - int m_keyDown; - static double s_speedStepSize; - }; - - GameLayer(Properties props); - virtual ~GameLayer(){}; - virtual void onAttach() override; - virtual void onDetach() override; - virtual void onUpdate(GuiCore::Timestep ts) override; - virtual void onGuiRender() override; - virtual void onEvent(GuiCore::Event &event) override; - -private: - void reset(); - - void calculatePointPositions(float *points); - void calculateMomentumViewPositions(float *points); - void calculateTriangleIndices(uint32_t *indices); - void calculateBorderPotential(float *potential); - void initialiseParticleMomentum(); - void propagateStep(double dt); - void applyPotentials(int indexBegin, int indexEnd, double dt); - void moveForward(int indexBegin, int indexEnd, double dt); - void updateMomentumView(int indexBegin, int indexEnd); - double absorbParticle(int i, double c); - // positions at array index - inline double posAt(int pos) const; - inline double xAtIndex(int i) const; - inline double yAtIndex(int i) const; - inline double potentialAt(int pos) const; - inline double pxAtIndex(int i) const; - inline double pyAtIndex(int i) const; - - GuiCore::Shader *m_shader; - std::string m_vertexShaderPath; - std::string m_fragmentShaderPath; - - int m_arraySize; - int m_arrayElements; - int m_numberOfQuads; - int m_numberOfIndices; - - GLuint m_vertexArray; ///< OpenGL object that "knows" all buffers. - GLuint m_pointsBuffer; ///< Buffer with the location for each element in the particle array - GLuint m_indicesBuffer; ///< Indicies to draw trinagles from the points to fill the "canvas" - GLuint m_colorBuffer; ///< Reference for OpenGL to load the paritcle data to the GPU - GLuint m_potentialBuffer; ///< Borders and bats - - float *m_particleViewPositions; - float *m_momentumViewPositions; - float *m_staticBorderPotential; - float *m_dynamicPotential; - - double *m_xAt; - double *m_yAt; - double *m_pxAt; - double *m_pyAt; - - glm::mat4 m_viewProjectionPosition; - int m_viewProjectionLocation; - GLuint m_showPotentialsLocation; - GLuint m_isMomentumEnabled; - - ParticleProps m_initialParticleProperties; - double m_particleAbsouluteSumStart; - fftw_plan m_planForward; - fftw_plan m_planBackwards; - - Bat m_leftBat; - Bat m_rightBat; - Player m_playerOne; - Player m_playerTwo; - bool m_gameCounts; - - bool m_restart; - bool m_propagate; - bool m_renderMomentum; - bool m_randomMomentum; - bool m_removeParticleEnabled; - bool m_potentialsEnabled; - int m_renderingThreadsNumber {8}; - size_t m_memorySize {0}; - float m_speedScale {1.0f}; - std::complex<double> *m_psi; - std::complex<double> *m_momentum; -}; -} -#endif \ No newline at end of file diff --git a/assets/assets b/assets/assets new file mode 120000 index 0000000000000000000000000000000000000000..b0f7c4ed5ae58935d8f2eaa53a476e3992ec1dd8 --- /dev/null +++ b/assets/assets @@ -0,0 +1 @@ +/home/armin/Master/semester_1/vertiefung_c++/qpong/assets \ No newline at end of file