Skip to content
Snippets Groups Projects
Commit d3be0ee6 authored by Armin Co's avatar Armin Co
Browse files

Version 1.0

parent 90b6ceb2
No related branches found
No related tags found
No related merge requests found
Showing with 735 additions and 83 deletions
......@@ -3,6 +3,7 @@ project(QPong VERSION 0.0.1 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra")
# Check for PkgConfig
find_package(PkgConfig REQUIRED)
......
......@@ -4,7 +4,6 @@
#include <GLFW/glfw3.h>
#include "Application.hpp"
#include "Log.hpp"
#include "ApplicationEvent.hpp"
......@@ -65,10 +64,13 @@ namespace GuiCore
Timestep timeStep = time - m_lastFrameTime;
m_lastFrameTime = time;
if (!m_minimzed)
{
for (auto layer : m_layerStack)
{
layer->onUpdate(timeStep);
}
}
m_guiLayer->begin();
for (auto layer: m_layerStack)
......@@ -89,6 +91,10 @@ namespace GuiCore
bool Application::onWindowResize(Event &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());
return false;
}
......
......@@ -12,6 +12,7 @@
#include "IWindow.hpp"
#include "LayerStack.hpp"
#include "ImGuiLayer.hpp"
#include "ApplicationEvent.hpp"
namespace GuiCore
{
......@@ -34,6 +35,7 @@ namespace GuiCore
private:
std::unique_ptr<IWindow> m_window;
bool m_isRunnig = true;
bool m_minimzed = false;
LayerStack m_layerStack;
float m_lastFrameTime = 0.0f;
std::shared_ptr<ImGuiLayer> m_guiLayer;
......
find_package(Freetype REQUIRED)
# GuiCore library
add_library(GuiCore
Application.cpp
......@@ -6,17 +8,20 @@ add_library(GuiCore
LayerStack.cpp
Log.cpp
Timestep.cpp
Glyphs.cpp
)
target_link_libraries(GuiCore
spdlog
)
target_include_directories(GuiCore PRIVATE
target_include_directories(GuiCore PUBLIC
../libs/spdlog/include/
${FREETYPE_INCLUDE_DIRS}
)
target_link_libraries(GuiCore
spdlog
${FREETYPE_LIBRARIES}
)
# ImGui is a dependency of GuiCore
add_library(ImGui
Shader.cpp
......@@ -29,8 +34,11 @@ add_library(ImGui
../libs/imgui/backends/imgui_impl_opengl3.cpp
../libs/imgui/examples/libs/gl3w/GL/gl3w.c
)
# target_include_directories(ImGui PRIVATE
# ${FREETYPE_INCLUDE_DIRS}
# )
target_include_directories(ImGui PRIVATE
target_include_directories(ImGui PUBLIC
../libs
../libs/imgui # for glfw
../libs/imgui/backends
......@@ -40,6 +48,7 @@ target_include_directories(ImGui PRIVATE
target_link_libraries(GuiCore
ImGui
# ${FREETYPE_LIBRARIES}
)
# Check for GLFW3 and link
......
/// @file Glyphs.cpp
/// @author Armin Co
///
#include "Glyphs.hpp"
#include <GLFW/glfw3.h>
#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;
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);
void GuiCore::Glyphs::setup()
{
m_shader = GuiCore::Shader::fromGLSLTextFiles(
"assets/shaders/glyph.vert.glsl",
"assets/shaders/glyph.frag.glsl"
);
glGenVertexArrays(1, &m_vertexArray);
glGenBuffers(1, &m_vertexBuffer);
glBindVertexArray(m_vertexArray);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
FT_Library ft;
if (FT_Init_FreeType(&ft))
{
GuiCore::Log::get()->error("ERROR::FREETYPE: Could not init FreeType Library");
}
FT_Face face;
if (FT_New_Face(ft, "assets/arial.ttf", 0, &face))
{
GuiCore::Log::get()->error("ERROR::FREETYPE: Failed to load font");
}
FT_Set_Pixel_Sizes(face, 0, 48);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (unsigned char c = 0; c < 128; c++)
{
// load character glyph
auto error = FT_Load_Char(face, c, FT_LOAD_RENDER);
if (error)
{
GuiCore::Log::get()->error("ERROR::FREETYTPE: 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),
face->glyph->advance.x
};
s_characters.insert(std::pair<char, Character>(c, character));
}
}
void GuiCore::Glyphs::renderText(std::string text, float x, float y, float scale, glm::vec3 color)
{
glUseProgram(m_shader->getRendererID());
glUniformMatrix4fv(glGetUniformLocation(m_shader->getRendererID(), "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniform3f(glGetUniformLocation(m_shader->getRendererID(), "textColor"), color.r, color.g, color.b);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(m_vertexArray);
std::string::const_iterator c;
for (c = text.begin(); c != text.end(); c++)
{
Character ch = s_characters[*c];
float xPos = x + ch.bearing.x * scale;
float yPos = y - (ch.size.y - ch.bearing.y) * scale;
float w = ch.size.x * 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 + 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);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
x += (ch.advance >> 6) * scale;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
\ No newline at end of file
/// @file Glyphs.hpp
/// @author Armin Co
///
#ifndef GUI_CORE_GLYPHS_HPP
#define GUI_CORE_GLYPHS_HPP
#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;
};
}
#endif
\ No newline at end of file
......@@ -8,6 +8,8 @@
#include "Shader.hpp"
#include "Log.hpp"
namespace GuiCore
{
static std::string readFileAsString(const std::string &filepath)
......
......@@ -8,7 +8,6 @@
#include <string>
#include <GL/gl3w.h>
#include <GLFW/glfw3.h>
namespace GuiCore
{
class Shader
......
......@@ -12,11 +12,100 @@
#include <GuiCore/Log.hpp>
#include <GuiCore/Input.hpp>
#include <GuiCore/Glyphs.hpp>
#include "GameLayer.hpp"
#include "Log.hpp"
#include <string>
#include <thread>
#include "Log.hpp"
double QPong::GameLayer::GameLayer::Bat::s_speedStepSize = 0.005;
template <typename T>
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")
......@@ -24,6 +113,8 @@ QPong::GameLayer::GameLayer(QPong::GameLayer::Properties props)
, m_fragmentShaderPath{"assets/shaders/test.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_arrayElements = m_arraySize * m_arraySize;
m_numberOfQuads = (m_arraySize - 1) * (m_arraySize - 1);
......@@ -32,11 +123,11 @@ QPong::GameLayer::GameLayer(QPong::GameLayer::Properties props)
m_initialParticleProperties.position.x = 0.0;
m_initialParticleProperties.position.y = 0.0;
m_initialParticleProperties.position.z = 0.0;
m_initialParticleProperties.momentum.x = 0.1;
m_initialParticleProperties.momentum.y = 0.0;
m_initialParticleProperties.momentum.x = 1.0;
m_initialParticleProperties.momentum.y = 0.5;
m_initialParticleProperties.momentum.z = 0.00;
m_initialParticleProperties.smoothEdge = 100.0;
m_initialParticleProperties.startValue = 0.1;
m_initialParticleProperties.pointUnsharpness = 60.0;
m_initialParticleProperties.startValue = 0.01;
m_initialParticleProperties.hbar = 0.0002;
}
......@@ -51,9 +142,31 @@ void QPong::GameLayer::calculatePointPositions(float *positions)
float dy = (yMax - yMin) / (m_arraySize - 1.0f);
{
uint32_t i {0}; // array index
for (uint32_t yId{0}; yId < m_arraySize; ++yId)
for (int yId{0}; yId < m_arraySize; ++yId)
{
for (int xId{0}; xId < m_arraySize; ++xId)
{
positions[i++] = xMin + (static_cast<float>(xId) * dx); // x-Pos
positions[i++] = yMax - (static_cast<float>(yId) * dy); // y-Pos
positions[i++] = 0.0f; // z-Pos
}
}
}
}
void QPong::GameLayer::calculateMomentumViewPositions(float *positions)
{
float xMin {0.2f};
float xMax {2.2f};
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);
{
uint32_t i {0}; // array index
for (int yId{0}; yId < m_arraySize; ++yId)
{
for (uint32_t xId{0}; xId < m_arraySize; ++xId)
for (int xId{0}; xId < m_arraySize; ++xId)
{
positions[i++] = xMin + (static_cast<float>(xId) * dx); // x-Pos
positions[i++] = yMax - (static_cast<float>(yId) * dy); // y-Pos
......@@ -66,11 +179,11 @@ void QPong::GameLayer::calculatePointPositions(float *positions)
void QPong::GameLayer::calculateTriangleIndices(uint32_t *indices)
{
uint32_t i{0};
for (uint32_t row {0}; row < (m_arraySize - 1); ++row)
for (int row {0}; row < (m_arraySize - 1); ++row)
{
for (uint32_t k {0}; k < (m_arraySize - 1); ++k)
for (int k {0}; k < (m_arraySize - 1); ++k)
{
uint32_t offset = row * m_arraySize;
int offset = row * m_arraySize;
indices[i++] = offset + k;
indices[i++] = offset + k + 1;
indices[i++] = offset + k + m_arraySize;
......@@ -81,23 +194,34 @@ void QPong::GameLayer::calculateTriangleIndices(uint32_t *indices)
}
}
double const QPong::GameLayer::posAt(int pos) const
void QPong::GameLayer::calculateBorderPotential(float *potential)
{
double y {0.0};
for (int i {0}; i < m_arrayElements; ++i)
{
y = yAtIndex(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 const QPong::GameLayer::xAtIndex(int i) const
double QPong::GameLayer::xAtIndex(int i) const
{
return posAt(i % m_arraySize);
}
double const QPong::GameLayer::yAtIndex(int i) const
double QPong::GameLayer::yAtIndex(int i) const
{
return posAt(i / m_arraySize);
}
double const QPong::GameLayer::potentialAt(int pos) const
double QPong::GameLayer::potentialAt(int pos) const
{
if (pos > m_arraySize / 2)
{
......@@ -106,21 +230,17 @@ double const QPong::GameLayer::potentialAt(int pos) const
return pos * M_PI * m_initialParticleProperties.hbar;
}
double const QPong::GameLayer::pxAtIndex(int i) const
double QPong::GameLayer::pxAtIndex(int i) const
{
return potentialAt(i % m_arraySize);
}
double const QPong::GameLayer::pyAtIndex(int i) const
double QPong::GameLayer::pyAtIndex(int i) const
{
return potentialAt(i / m_arraySize);
}
template <typename T>
const T pow2(const T v)
{
return v * v;
}
void QPong::GameLayer::initialiseParticleMomentum()
{
......@@ -131,7 +251,7 @@ void QPong::GameLayer::initialiseParticleMomentum()
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.smoothEdge;
const double lambda = m_initialParticleProperties.pointUnsharpness;
double sum {0.0};
m_particleAbsouluteSumStart = 0.0;
......@@ -152,49 +272,168 @@ void QPong::GameLayer::initialiseParticleMomentum()
Log::get()->trace("Particle initialised");
}
void QPong::GameLayer::propagateStep(double dt)
double QPong::GameLayer::absorbParticle(int i, double c)
{
double normPrevious = pow2(std::real(m_psi[i])) + pow2(std::imag(m_psi[i]));
double absorb = pow2(std::cos(c * 10));
m_psi[i] *= absorb;
double delta = normPrevious - (pow2(std::real(m_psi[i])) + pow2(std::imag(m_psi[i])));
return delta;
}
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;
// manipulate particle in postion
double x {0.0};
double y {0.0};
const double potential = 1.0;
for (int i {0}; i < m_arrayElements; ++i)
for (int i {indexBegin}; i < indexEnd; ++i)
{
double potSum = 1.0;
if (m_potentialsEnabled)
{
auto psi = m_psi[i] * exp(-ci * dt/hbar * potential * 1.0);
potSum = 0.0;
x = xAtIndex(i);
y = yAtIndex(i);
if (m_removeParticleEnabled)
{
if (x < m_leftBat.getX())
{
double distanceToEdge = -m_leftBat.getX() + x;
double c = distanceToEdge * (M_PI / 2.0) / (1.0 + m_leftBat.getX());
m_playerTwo.currentlyScored += absorbParticle(i, c);
}
if (x>m_rightBat.getX())
{
double distanceToEdge = x - m_rightBat.getX();
double c = distanceToEdge * (M_PI / 2.0) / (1.0 + m_rightBat.getX());
m_playerOne.currentlyScored += absorbParticle(i, c);
}
}
double potLeftBat = m_leftBat.getPotential(x, y);
double potRightBat = m_rightBat.getPotential(x, y);
potSum = potLeftBat + potRightBat + m_staticBorderPotential[i];
}
auto psi = m_psi[i] * exp(-ci * dt/hbar * potential * potSum);
m_psi[i] = psi;
if (m_potentialsEnabled)
{
m_dynamicPotential[i] = potSum;
}
}
}
// plan forward
fftw_execute(m_planForward);
// manipulate in momentum space
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 N = 1.0 / static_cast<double>(m_arrayElements);
for (int i{0}; i<m_arrayElements; ++i)
for (int i{indexBegin}; i<indexEnd; ++i)
{
px = pxAtIndex(i);
py = pyAtIndex(i);
double E = (pow2(px) + pow2(py)) / (2.0 * m);
m_psi[i] *= exp(-ci * dt / hbar * E) * N;
}
// plan backwards
}
void QPong::GameLayer::updateMomentumView(int indexBegin, int indexEnd)
{
int x {0};
int y {0};
for (int i {indexBegin}; i< indexEnd; ++i)
{
x = i % m_arraySize;
y = i / m_arraySize;
auto halfArraySize = m_arraySize / 2;
if (y > halfArraySize)
{
y = y - halfArraySize;
}
else
{
y = y + halfArraySize;
}
if (x > halfArraySize)
{
x = x - halfArraySize;
}
else
{
x = x + halfArraySize;
}
m_momentum[i] = m_psi[(y * m_arraySize )- x] * 100.0;
}
}
void QPong::GameLayer::propagateStep(double dt)
{
std::vector<std::thread> workers;
workers.reserve(m_renderingThreadsNumber);
for (auto workerId{0}; workerId < m_renderingThreadsNumber; ++workerId)
{
int begin = workerId * (m_arrayElements / m_renderingThreadsNumber);
int end = (workerId + 1) * (m_arrayElements / m_renderingThreadsNumber);
workers.emplace_back(&QPong::GameLayer::applyPotentials, this, begin, end, dt);
}
for (auto &worker : workers)
{
worker.join();
}
fftw_execute(m_planBackwards);
workers.clear();
for (auto workerId{0}; workerId < m_renderingThreadsNumber; ++workerId)
{
int begin = workerId * (m_arrayElements / m_renderingThreadsNumber);
int end = (workerId + 1) * (m_arrayElements / m_renderingThreadsNumber);
workers.emplace_back(&QPong::GameLayer::moveForward, this, begin, end, dt);
}
for (auto &worker : workers)
{
worker.join();
}
if (m_renderMomentum)
{
workers.clear();
for (auto workerId{0}; workerId < m_renderingThreadsNumber; ++workerId)
{
int begin = workerId * (m_arrayElements / m_renderingThreadsNumber);
int end = (workerId + 1) * (m_arrayElements / m_renderingThreadsNumber);
workers.emplace_back(&QPong::GameLayer::updateMomentumView, this, begin, end);
}
for (auto &worker : workers)
{
worker.join();
}
}
fftw_execute(m_planForward);
}
void QPong::GameLayer::onAttach()
{
GuiCore::Glyphs::setup();
m_renderMomentum = false;
m_removeParticleEnabled = true;
m_potentialsEnabled = true;
m_playerOne.totalScore = 0;
m_playerTwo.totalScore = 0;
m_shader = GuiCore::Shader::fromGLSLTextFiles(
m_vertexShaderPath,
m_fragmentShaderPath
);
// static verticie positions
float *positions = new float[m_arrayElements * 3];
calculatePointPositions(positions);
m_particleViewPositions = new float[m_arrayElements * 3];
calculatePointPositions(m_particleViewPositions);
// Vertex array to store all buffers
glGenVertexArrays(1, &m_vertexArray);
......@@ -203,32 +442,48 @@ void QPong::GameLayer::onAttach()
// Buffer of all point locations
glGenBuffers(1, &m_pointsBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_pointsBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * (m_arrayElements), positions, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * (m_arrayElements), m_particleViewPositions, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, 0);
// Setup indies buffer
uint32_t *indices = new uint32_t[m_numberOfIndices];
calculateTriangleIndices(indices);
glGenBuffers(1, &m_indicesBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indicesBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * m_numberOfIndices, indices, GL_STATIC_DRAW);
delete(positions);
delete(indices);
// Setup particle buffer
m_memorySize = sizeof(fftw_complex) * m_arrayElements;
m_psi = static_cast<std::complex<double>*>(fftw_malloc(m_memorySize));
m_momentum = static_cast<std::complex<double>*>(fftw_malloc(m_memorySize));
glGenBuffers(1, &m_particleBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_particleBuffer);
glGenBuffers(1, &m_colorBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);
glBufferData(GL_ARRAY_BUFFER, m_memorySize, nullptr, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_DOUBLE, GL_FALSE, sizeof(fftw_complex), 0);
// Setup momentum view
m_momentumViewPositions = new float[m_arrayElements * 3];
calculateMomentumViewPositions(m_momentumViewPositions);
// Initialise Borders
m_staticBorderPotential = new float[m_arrayElements];
calculateBorderPotential(m_staticBorderPotential);
m_dynamicPotential = new float[m_arrayElements];
glGenBuffers(1, &m_potentialBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_potentialBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * m_arrayElements, m_staticBorderPotential, GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(float), 0);
// Set orthografic view projectin "camera" which will not change.
m_viewPojection = glm::ortho(-3.2f, 3.2f, -1.8f, 1.8f, -1.0f, 1.0f);
m_viewProjectionPosition = glm::ortho(-3.2f, 3.2f, -1.8f, 1.8f, -1.0f, 1.0f);
m_viewProjectionLocation = glGetUniformLocation(m_shader->getRendererID(), "u_viewProjection");
m_showPotentialsLocation = glGetUniformLocation(m_shader->getRendererID(), "u_showPotential");
fftw_init_threads();
m_planForward = fftw_plan_dft_2d(m_arraySize, m_arraySize, (fftw_complex *)m_psi, (fftw_complex *)m_psi, FFTW_FORWARD, FFTW_MEASURE);
......@@ -242,40 +497,164 @@ void QPong::GameLayer::onDetach()
glDeleteVertexArrays(1, &m_vertexArray);
glDeleteBuffers(1, &m_pointsBuffer);
glDeleteBuffers(1, &m_indicesBuffer);
glDeleteBuffers(1, &m_particleBuffer);
glDeleteBuffers(1, &m_colorBuffer);
fftw_free(m_psi);
delete(m_momentum);
delete(m_dynamicPotential);
delete(m_staticBorderPotential);
}
void QPong::GameLayer::onEvent(GuiCore::Event& event)
{
}
void QPong::GameLayer::reset()
{
initialiseParticleMomentum();
m_restart = false;
m_gameCounts = true;
m_playerOne.currentlyScored = 0.0;
m_playerTwo.currentlyScored = 0.0;
}
void QPong::GameLayer::onUpdate(GuiCore::Timestep ts)
{
GuiCore::Glyphs::renderText("QPong", -0.31f, 1.59f, 0.005f, glm::vec3(0.5f, 0.9f, 0.9f));
if (m_restart)
{
initialiseParticleMomentum();
m_restart = false;
reset();
}
if (m_propagate)
{
m_leftBat.onUpdate(ts);
m_rightBat.onUpdate(ts);
propagateStep(ts.getSeconds() * m_speedScale);
if (m_gameCounts)
{
if (m_playerOne.currentlyScored > 35.0)
{
m_playerOne.totalScore++;
m_gameCounts = false;
m_propagate = false;
}
if (m_playerTwo.currentlyScored > 35.0)
{
m_playerTwo.totalScore++;
m_gameCounts = false;
m_propagate = false;
}
}
}
// 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());
glUniformMatrix4fv(m_viewProjectionLocation, 1, GL_FALSE, &m_viewPojection[0][0]);
glUniform1i(m_showPotentialsLocation, 1);
glUniformMatrix4fv(m_viewProjectionLocation, 1, GL_FALSE, &m_viewProjectionPosition[0][0]);
glBindVertexArray(m_vertexArray);
glDrawElements(GL_TRIANGLES, m_numberOfIndices, GL_UNSIGNED_INT, nullptr);
if (m_propagate)
// Momentum
if (m_renderMomentum)
{
propagateStep(ts.getSeconds()*m_speedScale);
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(), -1.82f, 1.52f, 0.0075f, {1.0, 1.0, 1.0});
}
void QPong::GameLayer::onGuiRender()
{
ImGui::Begin("Controls");
// Select initial momentum
ImGui::DragFloat2("Momentum [x,y]", glm::value_ptr(m_initialParticleProperties.momentum), 0.01f, -1.0f, 1.0f);
// 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);
float pointUnsharpness = m_initialParticleProperties.pointUnsharpness;
if (ImGui::DragFloat("Sharpness", &pointUnsharpness, 1.0f, 5.0f, 1000.0f))
{
m_initialParticleProperties.pointUnsharpness = pointUnsharpness;
}
// Select hbar, changes apply on the fly.
float hbar = m_initialParticleProperties.hbar;
if (ImGui::DragFloat("hbar", &hbar, 0.000001f, 0.000001f, 1.0f, "%.6f"))
{
m_initialParticleProperties.hbar = hbar;
}
// This value increases or decreases the time step made every iteration.
ImGui::DragFloat("Speed Factor", &m_speedScale, 0.1, 0.1, 20.0);
if (ImGui::IsItemHovered()) {
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);
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)
{
......@@ -296,6 +675,11 @@ void QPong::GameLayer::onGuiRender()
{
m_restart = true;
}
ImGui::Text("Player 1: %d", m_playerOne.totalScore); ImGui::SameLine();
ImGui::Text(" | Live Score: %.1f\%%", m_playerOne.currentlyScored);
ImGui::Text("Player 2: %d", m_playerTwo.totalScore); ImGui::SameLine();
ImGui::Text(" | Live Score: %.1f\%%", m_playerTwo.currentlyScored);
ImGui::End();
......
......@@ -29,11 +29,37 @@ public:
{
glm::vec3 position;
glm::vec3 momentum;
double smoothEdge;
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;
......@@ -43,18 +69,25 @@ public:
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 const double posAt(int pos) const;
inline const double xAtIndex(int i) const;
inline const double yAtIndex(int i) const;
inline const double potentialAt(int pos) const;
inline const double pxAtIndex(int i) const;
inline const double pyAtIndex(int i) const;
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;
......@@ -68,22 +101,39 @@ private:
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_particleBuffer; ///< Reference for OpenGL to load the paritcle data to the GPU
GLuint m_gameObjectsBuffer; ///< Buffer for game objects like borders and bats.
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;
glm::mat4 m_viewPojection;
glm::mat4 m_viewProjectionPosition;
int m_viewProjectionLocation;
GLuint m_showPotentialsLocation;
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_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
......@@ -23,6 +23,8 @@ void MainMenuLayer::onAttach()
{
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_backgroundColor = {0.2f, 0.2f, 0.2f};
createNewGame();
}
......
......@@ -75,8 +75,6 @@ void Window::setup(const GuiCore::WindowProperties &properties)
WindowCloseEvent event;
data.eventCallback(event);
});
/// @todo add key callback ...
}
void Window::shutdown()
......
File added
#version 450 core
in vec2 textureCoordinates;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
color = vec4(textColor, texture(text, textureCoordinates).r) ;
}
\ No newline at end of file
#version 450 core
layout (location = 0) in vec4 vertex;
out vec2 textureCoordinates;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
textureCoordinates = vertex.zw;
}
\ No newline at end of file
#version 450 core
in vec3 v_fragmentColor;
out vec3 pixelColor;
in vec3 fragmentColor;
out vec4 pixelColor;
void main()
{
pixelColor = v_fragmentColor;
pixelColor = vec4(fragmentColor, 1.0);
}
\ No newline at end of file
#version 450 core
layout (location = 0) in vec3 v_position;
layout (location = 1) in vec2 v_particle_complex;
layout (location = 2) in float potential;
uniform mat4 u_viewProjection;
uniform int u_showPotential;
out vec3 fragmentColor;
void main()
{
float pot = potential * 120.0;
if (u_showPotential==0)
{
pot = 0.0;
}
gl_Position = u_viewProjection * vec4(v_position, 1.0f);
float real = v_particle_complex[0];
float imag = v_particle_complex[1];
float real2 = real * real;
float imag2 = imag * imag;
fragmentColor = vec3(
sqrt(real2) * 20.0,
sqrt(real2) * 15.0 + sqrt(imag2) * 15.0,
sqrt(imag2) * 20.0);
sqrt(real2) * 5.0 + pot,
sqrt(real2) * 5.0 + sqrt(imag2) * 5.0 + pot / 2.0,
sqrt(imag2) * 5.0);
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment