diff --git a/OpenGL-Core/src/GLCore/Core/Application.cpp b/OpenGL-Core/src/GLCore/Core/Application.cpp index ad93a482..e8b16509 100644 --- a/OpenGL-Core/src/GLCore/Core/Application.cpp +++ b/OpenGL-Core/src/GLCore/Core/Application.cpp @@ -6,6 +6,9 @@ #include "Input.h" #include +#include + +#include "KeyCodes.h" namespace GLCore { @@ -47,6 +50,15 @@ namespace GLCore { { EventDispatcher dispatcher(e); dispatcher.Dispatch(BIND_EVENT_FN(OnWindowClose)); + dispatcher.Dispatch([&](KeyPressedEvent& e) { + + if (e.GetKeyCode() == HZ_KEY_ESCAPE) + { + m_Running = false; + return false; + } + }); + for (auto it = m_LayerStack.end(); it != m_LayerStack.begin(); ) { @@ -65,7 +77,10 @@ namespace GLCore { m_LastFrameTime = time; for (Layer* layer : m_LayerStack) + { layer->OnUpdate(timestep); + layer->OnRender(); + } m_ImGuiLayer->Begin(); for (Layer* layer : m_LayerStack) @@ -82,4 +97,6 @@ namespace GLCore { return true; } + + } \ No newline at end of file diff --git a/OpenGL-Core/src/GLCore/Core/Application.h b/OpenGL-Core/src/GLCore/Core/Application.h index aea828f2..e1b0da32 100644 --- a/OpenGL-Core/src/GLCore/Core/Application.h +++ b/OpenGL-Core/src/GLCore/Core/Application.h @@ -31,6 +31,7 @@ namespace GLCore { inline static Application& Get() { return *s_Instance; } private: bool OnWindowClose(WindowCloseEvent& e); + bool OnWindowResize(WindowResizeEvent& e); private: std::unique_ptr m_Window; ImGuiLayer* m_ImGuiLayer; diff --git a/OpenGL-Core/src/GLCore/Core/Layer.h b/OpenGL-Core/src/GLCore/Core/Layer.h index 8e4bf99a..a930ec27 100644 --- a/OpenGL-Core/src/GLCore/Core/Layer.h +++ b/OpenGL-Core/src/GLCore/Core/Layer.h @@ -15,6 +15,8 @@ namespace GLCore { virtual void OnAttach() {} virtual void OnDetach() {} virtual void OnUpdate(Timestep ts) {} + virtual void OnRender() {} + virtual void OnImGuiRender() {} virtual void OnEvent(Event& event) {} diff --git a/OpenGL-Core/src/GLCore/ImGui/ImGuiLayer.cpp b/OpenGL-Core/src/GLCore/ImGui/ImGuiLayer.cpp index 729987f0..ae096182 100644 --- a/OpenGL-Core/src/GLCore/ImGui/ImGuiLayer.cpp +++ b/OpenGL-Core/src/GLCore/ImGui/ImGuiLayer.cpp @@ -27,7 +27,7 @@ namespace GLCore { io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - + io.WantCaptureMouse = false; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/OpenGL-Core/src/GLCore/Util/OrthographicCameraController.cpp b/OpenGL-Core/src/GLCore/Util/OrthographicCameraController.cpp index b7f37144..2fe0d3a6 100644 --- a/OpenGL-Core/src/GLCore/Util/OrthographicCameraController.cpp +++ b/OpenGL-Core/src/GLCore/Util/OrthographicCameraController.cpp @@ -65,7 +65,7 @@ namespace GLCore::Utils { bool OrthographicCameraController::OnMouseScrolled(MouseScrolledEvent& e) { m_ZoomLevel -= e.GetYOffset() * 0.25f; - m_ZoomLevel = std::max(m_ZoomLevel, 0.25f); + m_ZoomLevel = std::max(m_ZoomLevel, 0.01f); m_Camera.SetProjection(-m_AspectRatio * m_ZoomLevel, m_AspectRatio * m_ZoomLevel, -m_ZoomLevel, m_ZoomLevel); return false; } diff --git a/OpenGL-Core/src/GLCore/Util/Renderer.cpp b/OpenGL-Core/src/GLCore/Util/Renderer.cpp new file mode 100644 index 00000000..211c2884 --- /dev/null +++ b/OpenGL-Core/src/GLCore/Util/Renderer.cpp @@ -0,0 +1,95 @@ +#include "glpch.h" +#include "Renderer.h" + +#include + +#include "stb_image.h" + + + +Texture CreateTexture(int width, int height) +{ + Texture result; + result.Width = width; + result.Height = height; + + glCreateTextures(GL_TEXTURE_2D, 1, &result.Handle); + + glTextureStorage2D(result.Handle, 1, GL_RGBA32F, width, height); + + glTextureParameteri(result.Handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureParameteri(result.Handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTextureParameteri(result.Handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTextureParameteri(result.Handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + return result; +} + +Texture LoadTexture(const std::filesystem::path& path) +{ + int width, height, channels; + std::string filepath = path.string(); + unsigned char* data = stbi_load(filepath.c_str(), &width, &height, &channels, 0); + + if (!data) + { + std::cerr << "Failed to load texture: " << filepath << "\n"; + return {}; + } + + GLenum format = channels == 4 ? GL_RGBA : + channels == 3 ? GL_RGB : + channels == 1 ? GL_RED : 0; + + Texture result; + result.Width = width; + result.Height = height; + + glCreateTextures(GL_TEXTURE_2D, 1, &result.Handle); + + glTextureStorage2D(result.Handle, 1, (format == GL_RGBA ? GL_RGBA8 : GL_RGB8), width, height); + + glTextureSubImage2D(result.Handle, 0, 0, 0, width, height, format, GL_UNSIGNED_BYTE, data); + + glTextureParameteri(result.Handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(result.Handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTextureParameteri(result.Handle, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(result.Handle, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glGenerateTextureMipmap(result.Handle); + stbi_image_free(data); + + return result; +} + +Framebuffer CreateFramebufferWithTexture(const Texture texture) +{ + Framebuffer result; + + glCreateFramebuffers(1, &result.Handle); + + if (!AttachTextureToFramebuffer(result, texture)) + { + glDeleteFramebuffers(1, &result.Handle); + return {}; + } + + return result; +} + +bool AttachTextureToFramebuffer(Framebuffer& framebuffer, const Texture texture) +{ + glNamedFramebufferTexture(framebuffer.Handle, GL_COLOR_ATTACHMENT0, texture.Handle, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + std::cerr << "Framebuffer is not complete!" << std::endl; + return false; + } + + framebuffer.ColorAttachment = texture; + return true; +} + diff --git a/OpenGL-Core/src/GLCore/Util/Renderer.h b/OpenGL-Core/src/GLCore/Util/Renderer.h new file mode 100644 index 00000000..604e323f --- /dev/null +++ b/OpenGL-Core/src/GLCore/Util/Renderer.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#define GLFW_INCLUDE_NONE +#include + +#include + +struct Texture +{ + GLuint Handle = 0; + uint32_t Width = 0; + uint32_t Height = 0; +}; + +struct Framebuffer +{ + GLuint Handle = 0; + Texture ColorAttachment; +}; + +Texture CreateTexture(int width, int height); +Texture LoadTexture(const std::filesystem::path& path); +Framebuffer CreateFramebufferWithTexture(const Texture texture); +bool AttachTextureToFramebuffer(Framebuffer& framebuffer, const Texture texture); diff --git a/OpenGL-Core/src/GLCore/Util/Shader.cpp b/OpenGL-Core/src/GLCore/Util/Shader.cpp index 02e60ddf..7e65822b 100644 --- a/OpenGL-Core/src/GLCore/Util/Shader.cpp +++ b/OpenGL-Core/src/GLCore/Util/Shader.cpp @@ -107,4 +107,75 @@ namespace GLCore::Utils { m_RendererID = program; } + GLint CreateComputeShader(const std::string& filepath) + { + std::string shaderSource = ReadFileAsString(filepath); + + GLuint shaderHandle = glCreateShader(GL_COMPUTE_SHADER); + + const GLchar* source = (const GLchar*)shaderSource.c_str(); + glShaderSource(shaderHandle, 1, &source, 0); + + glCompileShader(shaderHandle); + + GLint isCompiled = 0; + glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &isCompiled); + if (isCompiled == GL_FALSE) + { + GLint maxLength = 0; + glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &maxLength); + + std::vector infoLog(maxLength); + glGetShaderInfoLog(shaderHandle, maxLength, &maxLength, &infoLog[0]); + + std::cerr << infoLog.data() << std::endl; + + glDeleteShader(shaderHandle); + return 0; + } + + GLuint program = glCreateProgram(); + glAttachShader(program, shaderHandle); + glLinkProgram(program); + + GLint isLinked = 0; + glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked); + if (isLinked == GL_FALSE) + { + GLint maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + std::vector infoLog(maxLength); + glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); + + std::cerr << infoLog.data() << std::endl; + + glDeleteProgram(program); + glDeleteShader(shaderHandle); + + return 0; + } + + glDetachShader(program, shaderHandle); + return program; + } + + GLint ReloadComputeShader(GLint ComputeID, const std::string& filepath) + { + glDeleteProgram(ComputeID); + + return CreateComputeShader(filepath); + } + + Shader* ReloadGraphicsShader(Shader* shader, const std::string& vertexShaderPath, const std::string& fragmentShaderPath) + { + delete shader; + Shader* newShader = Shader::FromGLSLTextFiles(vertexShaderPath, fragmentShaderPath); + + return newShader; + } + + + + } \ No newline at end of file diff --git a/OpenGL-Core/src/GLCore/Util/Shader.h b/OpenGL-Core/src/GLCore/Util/Shader.h index 6431f71a..e578a546 100644 --- a/OpenGL-Core/src/GLCore/Util/Shader.h +++ b/OpenGL-Core/src/GLCore/Util/Shader.h @@ -8,19 +8,30 @@ namespace GLCore::Utils { class Shader { + public: + ~Shader(); GLuint GetRendererID() { return m_RendererID; } static Shader* FromGLSLTextFiles(const std::string& vertexShaderPath, const std::string& fragmentShaderPath); + private: Shader() = default; void LoadFromGLSLTextFiles(const std::string& vertexShaderPath, const std::string& fragmentShaderPath); GLuint CompileShader(GLenum type, const std::string& source); + private: GLuint m_RendererID; + }; + + GLint CreateComputeShader(const std::string& filepath); + GLint ReloadComputeShader(GLint ComputeID, std::string& filepath); + Shader* ReloadGraphicsShader(Shader* shader, const std::string& vertexShaderPath, const std::string& fragmentShaderPath); + + } \ No newline at end of file diff --git a/OpenGL-Core/src/GLCore/Util/Texture.cpp b/OpenGL-Core/src/GLCore/Util/Texture.cpp new file mode 100644 index 00000000..adc2dbe1 --- /dev/null +++ b/OpenGL-Core/src/GLCore/Util/Texture.cpp @@ -0,0 +1,61 @@ +#include "glpch.h" +#include "Texture.h" +#include "stb_image.h" + +Texture CreateTexture(int width, int height) +{ + Texture result; + result.Width = width; + result.Height = height; + + glCreateTextures(GL_TEXTURE_2D, 1, &result.TextureID); + + glTextureStorage2D(result.TextureID, 1, GL_RGBA32F, width, height); + + glTextureParameteri(result.TextureID, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureParameteri(result.TextureID, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTextureParameteri(result.TextureID, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTextureParameteri(result.TextureID, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + return result; +} + + +Texture LoadTexture(const std::string filepath) +{ + int width, height, channels; + + unsigned char* data = stbi_load(filepath.c_str(), &width, &height, &channels, 0); + + if (!data) + { + std::cerr << "Failed to load texture: " << filepath << "\n"; + return {}; + } + + GLenum format = channels == 4 ? GL_RGBA : + channels == 3 ? GL_RGB : + channels == 1 ? GL_RED : 0; + + Texture result; + result.Width = width; + result.Height = height; + + glCreateTextures(GL_TEXTURE_2D, 1, &result.TextureID); + + glTextureStorage2D(result.TextureID, 1, (format == GL_RGBA ? GL_RGBA8 : GL_RGB8), width, height); + + glTextureSubImage2D(result.TextureID, 0, 0, 0, width, height, format, GL_UNSIGNED_BYTE, data); + + glTextureParameteri(result.TextureID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(result.TextureID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTextureParameteri(result.TextureID, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(result.TextureID, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glGenerateTextureMipmap(result.TextureID); + stbi_image_free(data); + + return result; +} diff --git a/OpenGL-Core/src/GLCore/Util/Texture.h b/OpenGL-Core/src/GLCore/Util/Texture.h new file mode 100644 index 00000000..83949d23 --- /dev/null +++ b/OpenGL-Core/src/GLCore/Util/Texture.h @@ -0,0 +1,15 @@ +#pragma once +#include + +#include + +struct Texture +{ + GLuint TextureID = 0; + uint32_t Width = 0; + uint32_t Height = 0; +}; + +Texture CreateTexture(int width, int height); +Texture LoadTexture(const std::string filepath); + diff --git a/OpenGL-Core/vendor/Glad/bin/Debug-windows-x86_64/Glad/Glad.lib b/OpenGL-Core/vendor/Glad/bin/Debug-windows-x86_64/Glad/Glad.lib index efb8eedd..98464ccf 100644 Binary files a/OpenGL-Core/vendor/Glad/bin/Debug-windows-x86_64/Glad/Glad.lib and b/OpenGL-Core/vendor/Glad/bin/Debug-windows-x86_64/Glad/Glad.lib differ diff --git a/OpenGL-Core/vendor/Glad/bin/Debug-windows-x86_64/Glad/Glad.pdb b/OpenGL-Core/vendor/Glad/bin/Debug-windows-x86_64/Glad/Glad.pdb index e1ec1f1b..a6dd24c4 100644 Binary files a/OpenGL-Core/vendor/Glad/bin/Debug-windows-x86_64/Glad/Glad.pdb and b/OpenGL-Core/vendor/Glad/bin/Debug-windows-x86_64/Glad/Glad.pdb differ diff --git a/OpenGL-Sandbox/Shaders/CellRender.frag.glsl b/OpenGL-Sandbox/Shaders/CellRender.frag.glsl new file mode 100644 index 00000000..6e0970da --- /dev/null +++ b/OpenGL-Sandbox/Shaders/CellRender.frag.glsl @@ -0,0 +1,42 @@ +#version 440 core + + + +in vec2 v_LocalPosition; + +out vec4 FragColor; + +float BorderWidth = 0.0f; + +flat in int State; +flat in int Debug; + + +void main() +{ + + FragColor = vec4(0.5, 0.5, 0.5, 1.0); + //set border + if (v_LocalPosition.x < -1 + BorderWidth || v_LocalPosition.x > 1 - BorderWidth || + v_LocalPosition.y < -1 + BorderWidth || v_LocalPosition.y > 1 - BorderWidth) + { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + } + + if (State == 1) + { + FragColor = vec4(0.0, 0.0, 1.0, 1.0); + } + + if (Debug == 1) + { + FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } + if (Debug == 2) + { + FragColor = vec4(0.0, 1.0, 0.0, 1.0); + } + + + +} \ No newline at end of file diff --git a/OpenGL-Sandbox/Shaders/CellRender.vert.glsl b/OpenGL-Sandbox/Shaders/CellRender.vert.glsl new file mode 100644 index 00000000..9f42666c --- /dev/null +++ b/OpenGL-Sandbox/Shaders/CellRender.vert.glsl @@ -0,0 +1,46 @@ +#version 430 core + + +layout (location = 0) in vec2 aPos; + +layout(std430, binding = 1) buffer layoutName +{ + int data_SSBO[]; +}; + +layout(std430, binding = 4) buffer debugsdf +{ + int data_debug[]; +}; + + +out vec2 v_LocalPosition; +uniform int u_GridSize; + +float temp = 0.9f; + +int i = int(gl_InstanceID); + +vec2 cell = vec2(i % u_GridSize, floor(i / u_GridSize)); + + +uniform mat4 u_ViewProjection; + +flat out int Debug; +flat out int State; + + +void main() +{ + + v_LocalPosition = aPos; + vec2 position = (aPos + 1) / u_GridSize - 1; + + position = position + 2 * (cell / u_GridSize); + + State = data_SSBO[i]; + Debug = data_debug[i]; + + gl_Position = u_ViewProjection * vec4(position, 0.0f, 1.0f); + +} \ No newline at end of file diff --git a/OpenGL-Sandbox/Shaders/GameOfLife.comp.glsl b/OpenGL-Sandbox/Shaders/GameOfLife.comp.glsl new file mode 100644 index 00000000..66834c67 --- /dev/null +++ b/OpenGL-Sandbox/Shaders/GameOfLife.comp.glsl @@ -0,0 +1,64 @@ +#version 430 + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(binding = 1, std430) readonly buffer ssbo_in +{ + int State_in[]; +}; + +layout(binding = 2, std430) buffer ssbo_out +{ + int State_out[]; +}; + +uniform int u_GridSize; + +int CellIndex(vec2 CellCoord) +{ + return int(CellCoord.y) * u_GridSize + int(CellCoord.x); +} + +int cellActive(int x, int y) +{ + return int(State_in[CellIndex(vec2(x,y))]); +} + + +void main() { + +vec2 cell = gl_GlobalInvocationID.xy; + +int activeNeighbors = cellActive(int(cell.x+1), int(cell.y+1)) + + cellActive(int(cell.x+1), int(cell.y)) + + cellActive(int(cell.x+1), int(cell.y-1)) + + cellActive(int(cell.x), int(cell.y-1)) + + cellActive(int(cell.x-1), int(cell.y-1)) + + cellActive(int(cell.x-1), int(cell.y)) + + cellActive(int(cell.x-1), int(cell.y+1)) + + cellActive(int(cell.x), int(cell.y+1)); + +int i = CellIndex(cell); + +switch (activeNeighbors) +{ + case 2: + { // Active cells with 2 neighbors stay active. + State_out[i] = State_in[i]; + break; + } + case 3: + { // Cells with 3 neighbors become or stay active. + State_out[i] = 1; + break; + } + + default: + { // Cells with < 2 or > 3 neighbors become inactive. + State_out[i] = 0; + } + +} + + +} \ No newline at end of file diff --git a/OpenGL-Sandbox/Shaders/GridRender.frag.glsl b/OpenGL-Sandbox/Shaders/GridRender.frag.glsl new file mode 100644 index 00000000..f4861cf2 --- /dev/null +++ b/OpenGL-Sandbox/Shaders/GridRender.frag.glsl @@ -0,0 +1,28 @@ +#version 440 core + +in vec2 v_LocalPosition; + +out vec4 FragColor; + +uniform float u_BorderThickness; + +uniform vec4 u_ClearColor; + +void main() +{ + + FragColor = u_ClearColor; + + //set border + if (v_LocalPosition.x < -1 + u_BorderThickness || v_LocalPosition.x > 1 - u_BorderThickness || + v_LocalPosition.y < -1 + u_BorderThickness || v_LocalPosition.y > 1 - u_BorderThickness) + { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + } + + else + { + discard; + } + +} \ No newline at end of file diff --git a/OpenGL-Sandbox/Shaders/GridRender.vert.glsl b/OpenGL-Sandbox/Shaders/GridRender.vert.glsl new file mode 100644 index 00000000..7ed44a52 --- /dev/null +++ b/OpenGL-Sandbox/Shaders/GridRender.vert.glsl @@ -0,0 +1,36 @@ +#version 430 core + + +layout (location = 0) in vec2 aPos; + +layout(std430, binding = 3) buffer layoutName +{ + int data_SSBO[]; +}; + + +out vec2 v_LocalPosition; +uniform int u_GridSize; + +float temp = 0.9f; + +int i = int(gl_InstanceID); + +vec2 cell = vec2(i % u_GridSize, floor(i / u_GridSize)); + +int State = data_SSBO[gl_InstanceID]; + +uniform mat4 u_ViewProjection; + + +void main() +{ + + v_LocalPosition = aPos; + vec2 position = (aPos + 1) / u_GridSize - 1; + + position = position * State + 2 * (cell / u_GridSize); + + gl_Position = u_ViewProjection * vec4(position, 0.0f, 1.0f); + +} \ No newline at end of file diff --git a/OpenGL-Sandbox/Shaders/IslingModel.glsl b/OpenGL-Sandbox/Shaders/IslingModel.glsl new file mode 100644 index 00000000..84a0e6c0 --- /dev/null +++ b/OpenGL-Sandbox/Shaders/IslingModel.glsl @@ -0,0 +1,93 @@ +#version 430 + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(binding = 1, std430) readonly buffer ssbo_in +{ + int State_in[]; +}; + +layout(binding = 2, std430) buffer ssbo_out +{ + int State_out[]; +}; + +uniform int u_GridSize; +uniform float u_Temperature; +uniform float u_ChemicalPotential; +uniform float u_Time; +uniform int u_Step; + +int CellIndex(vec2 CellCoord) +{ + return int(CellCoord.y) * u_GridSize + int(CellCoord.x); +} + +int SpinState(int x, int y) +{ + int array_value = int(State_in[CellIndex(vec2(x,y))]); + + return array_value == 1 ? 1 : -1; +} + + +uint hash(uint x) +{ + x ^= x >> 16; + x *= 0x7feb352d; + x ^= x >> 15; + x *= 0x846ca68b; + x ^= x >> 16; + return x; +} + +float rand(uint seed) +{ + return float(hash(seed)) / float(0xffffffffu); +} + +void main() +{ + +vec2 cell = gl_GlobalInvocationID.xy; + +int i = CellIndex(cell); + +//float random = rand(dot(cell, vec2(12.9898, 78.233)) + u_Time); +//float random2 = rand(dot(cell, vec2(12.9898, 78.233)) + u_Time); + + +uint seed = uint(cell.x) + uint(cell.y) * uint(u_GridSize) + u_Step * 1664525u; + +float random = rand(seed); + + + +int energy_surrouding = SpinState(int(cell.x+1), int(cell.y+1)) + + SpinState(int(cell.x+1), int(cell.y)) + + SpinState(int(cell.x+1), int(cell.y-1)) + + SpinState(int(cell.x), int(cell.y-1)) + + SpinState(int(cell.x-1), int(cell.y-1)) + + SpinState(int(cell.x-1), int(cell.y)) + + SpinState(int(cell.x-1), int(cell.y+1)) + + SpinState(int(cell.x), int(cell.y+1)); + +float energy = 2.0f * float(SpinState(int(cell.x), int(cell.y))) * float(energy_surrouding); + +float q = exp(energy / u_Temperature); + +float probability = 1.0f / (1.0f + exp(float(energy) / float(u_Temperature))); + + +if (random < probability ) +{ + State_out[i] = 1 - State_in[i]; +} + +else +{ + State_out[i] = State_in[i]; +} + + +} \ No newline at end of file diff --git a/OpenGL-Sandbox/Shaders/LiquidGas.comp.glsl b/OpenGL-Sandbox/Shaders/LiquidGas.comp.glsl new file mode 100644 index 00000000..9b12f995 --- /dev/null +++ b/OpenGL-Sandbox/Shaders/LiquidGas.comp.glsl @@ -0,0 +1,111 @@ +#version 430 + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(binding = 1, std430) readonly buffer ssbo_in +{ + int State_in[]; +}; + +layout(binding = 2, std430) buffer ssbo_out +{ + int State_out[]; +}; + +uniform int u_GridSize; +uniform float u_Temperature; +uniform float u_ChemicalPotential; +uniform float u_Time; +uniform int u_Step; + +int CellIndex(vec2 CellCoord) +{ + return int(CellCoord.y) * u_GridSize + int(CellCoord.x); +} + +int SpinState(int x, int y) +{ + int array_value = int(State_in[CellIndex(vec2(x,y))]); + + return array_value == 1 ? 1 : -1; +} + +int ParticlePresent(int x, int y) +{ + return int(State_in[CellIndex(vec2(x,y))]); +} + + +uint hash(uint x) +{ + x ^= x >> 16; + x *= 0x7feb352d; + x ^= x >> 15; + x *= 0x846ca68b; + x ^= x >> 16; + return x; +} + +float rand(uint seed) +{ + return float(hash(seed)) / float(0xffffffffu); +} + +void main() +{ + +vec2 cell = gl_GlobalInvocationID.xy; + +int i = CellIndex(cell); + +//float random = rand(dot(cell, vec2(12.9898, 78.233)) + u_Time); +//float random2 = rand(dot(cell, vec2(12.9898, 78.233)) + u_Time); + + +uint seed = uint(cell.x) + uint(cell.y) * uint(u_GridSize) + u_Step * 1664525u; + +float random = rand(seed); + + + +int energy_surrouding = SpinState(int(cell.x+1), int(cell.y+1)) + + SpinState(int(cell.x+1), int(cell.y)) + + SpinState(int(cell.x+1), int(cell.y-1)) + + SpinState(int(cell.x), int(cell.y-1)) + + SpinState(int(cell.x-1), int(cell.y-1)) + + SpinState(int(cell.x-1), int(cell.y)) + + SpinState(int(cell.x-1), int(cell.y+1)) + + SpinState(int(cell.x), int(cell.y+1)); + +int particles_surrounding = ParticlePresent(int(cell.x+1), int(cell.y+1)) + + ParticlePresent(int(cell.x+1), int(cell.y)) + + ParticlePresent(int(cell.x+1), int(cell.y-1)) + + ParticlePresent(int(cell.x), int(cell.y-1)) + + ParticlePresent(int(cell.x-1), int(cell.y-1)) + + ParticlePresent(int(cell.x-1), int(cell.y)) + + ParticlePresent(int(cell.x-1), int(cell.y+1)) + + ParticlePresent(int(cell.x), int(cell.y+1)); + +float energy = 2.0f * float(SpinState(int(cell.x), int(cell.y))) * float(energy_surrouding); + +//float q = exp((energy - u_ChemicalPotential * float(particles_surrounding)) / u_Temperature); + +//float probability = 1.0f / (1.0f + exp((float(energy - u_ChemicalPotential * float(particles_surrounding))) / float(u_Temperature))); +float probability = 1.0f / (1.0f + exp((float(energy)) / float(u_Temperature))); + + +if (random < probability ) +{ + State_out[i] = 1 - State_in[i]; +} + +else +{ + State_out[i] = State_in[i]; +} + + + + + +} \ No newline at end of file diff --git a/OpenGL-Sandbox/assets/container.jpg b/OpenGL-Sandbox/assets/container.jpg new file mode 100644 index 00000000..1c5f7d52 Binary files /dev/null and b/OpenGL-Sandbox/assets/container.jpg differ diff --git a/OpenGL-Sandbox/src/GameOfLife.cpp b/OpenGL-Sandbox/src/GameOfLife.cpp new file mode 100644 index 00000000..b6dbddb5 --- /dev/null +++ b/OpenGL-Sandbox/src/GameOfLife.cpp @@ -0,0 +1,355 @@ +#include "GameOfLife.h" +#include + + +using namespace GLCore; +using namespace GLCore::Utils; + +GameOfLife::GameOfLife(int GridSize) + :m_CameraController(1.0f), m_GridSize(GridSize), + m_ParticleStates_in(GridSize* GridSize), + m_ParticleStates_out(GridSize* GridSize), + m_Grid(GridSize* GridSize) +{ + m_Shader = Shader::FromGLSLTextFiles("Shaders/CellRender.vert.glsl", "Shaders/CellRender.frag.glsl"); + + m_GridShader = Shader::FromGLSLTextFiles("Shaders/GridRender.vert.glsl", "Shaders/GridRender.frag.glsl"); + + m_CompShaderGameOfLife = CreateComputeShader("Shaders/GameOfLife.comp.glsl"); + m_CompShaderLiquidGas = CreateComputeShader("Shaders/LiquidGas.comp.glsl"); + + if (m_ShouldCreateFramebuffer) + { + CreateFramebufferWithTexture(); + m_ShouldCreateFramebuffer = false; + } + + FillGrid(); + RandomGrid(); + + float vertices[3 * 4] = { + // positions + 1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f + }; + + glGenVertexArrays(1, &m_VAO); + glGenBuffers(1, &m_VBO); + glGenBuffers(1, &m_EBO); + + glBindVertexArray(m_VAO); + + glBindBuffer(GL_ARRAY_BUFFER, m_VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + unsigned int indices[3 * 3] = { + 0, 1, 3, // first triangle + 1, 2, 3 // second triangle + }; + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + // position attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + + //create SSBO Buffer + glGenBuffers(1, &m_SSBO_in); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_in); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates_in.size() * sizeof(int), m_ParticleStates_in.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_in); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + + glGenBuffers(1, &m_SSBO_out); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_out); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates_out.size() * sizeof(int), m_ParticleStates_out.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_SSBO_out); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + + glGenBuffers(1, &m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_grid); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_Grid.size() * sizeof(int), m_Grid.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); +} + +GameOfLife::~GameOfLife() +{ + GLuint buffers[] = { m_VBO, m_EBO, m_SSBO_in, m_SSBO_out, m_SSBO_grid }; + glDeleteBuffers(IM_ARRAYSIZE(buffers), buffers); + + glDeleteVertexArrays(1, &m_VAO); + + glDeleteProgram(m_Shader->GetRendererID()); + glDeleteProgram(m_CompShaderGameOfLife); + glDeleteProgram(m_CompShaderLiquidGas); + + glDeleteFramebuffers(1, &m_FBO); + +} + +void GameOfLife::OnComputeGameOfLife() +{ + glUseProgram(m_CompShaderGameOfLife); + + int location = glGetUniformLocation(m_CompShaderGameOfLife, "u_GridSize"); + glUniform1i(location, m_GridSize); + + glDispatchCompute(m_GridSize / 8, m_GridSize / 8, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_in); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_SSBO_out); + + std::swap(m_SSBO_in, m_SSBO_out); + + glUseProgram(0); +} + +void GameOfLife::OnComputeLiquidGas() +{ + glUseProgram(m_CompShaderLiquidGas); + + int location = glGetUniformLocation(m_CompShaderLiquidGas, "u_GridSize"); + glUniform1i(location, m_GridSize); + + location = glGetUniformLocation(m_CompShaderLiquidGas, "u_Temperature"); + glUniform1f(location, m_Temperature); + + location = glGetUniformLocation(m_CompShaderLiquidGas, "u_ChemicalPotential"); + glUniform1f(location, m_ChemcialPotential); + + static int step = 0; + location = glGetUniformLocation(m_CompShaderLiquidGas, "u_Step"); + glUniform1i(location, step); + step++; + + auto time = float(glfwGetTime()); + + location = glGetUniformLocation(m_CompShaderLiquidGas, "u_Time"); + glUniform1f(location, time); + + glDispatchCompute(m_GridSize / 8, m_GridSize / 8, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_in); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_SSBO_out); + + std::swap(m_SSBO_in, m_SSBO_out); +} + +void GameOfLife::OnRender(Timestep ts) +{ + m_CameraController.OnUpdate(ts); + glBindFramebuffer(GL_FRAMEBUFFER, m_FBO); + glViewport(0, 0, (int)m_FBTextureSize.x, (int)m_FBTextureSize.y); + + glClearColor(m_ClearColor.r, m_ClearColor.g, m_ClearColor.b, m_ClearColor.a); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(m_Shader->GetRendererID()); + + int location = glGetUniformLocation(m_Shader->GetRendererID(), "u_GridSize"); + glUniform1i(location, m_GridSize); + + location = glGetUniformLocation(m_Shader->GetRendererID(), "u_ViewProjection"); + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(m_CameraController.GetCamera().GetViewProjectionMatrix())); + + glUseProgram(m_GridShader->GetRendererID()); + + location = glGetUniformLocation(m_GridShader->GetRendererID(), "u_GridSize"); + glUniform1i(location, m_GridSize); + + location = glGetUniformLocation(m_GridShader->GetRendererID(), "u_ViewProjection"); + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(m_CameraController.GetCamera().GetViewProjectionMatrix())); + + location = glGetUniformLocation(m_GridShader->GetRendererID(), "u_ClearColor"); + glUniform4fv(location, 1, glm::value_ptr(m_ClearColor)); + + location = glGetUniformLocation(m_GridShader->GetRendererID(), "u_BorderThickness"); + glUniform1f(location, m_BorderThickness); + + glUseProgram(m_Shader->GetRendererID()); + + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, m_GridSize * m_GridSize); + + + if (m_ShowGrid) + { + glUseProgram(m_GridShader->GetRendererID()); + + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, m_GridSize * m_GridSize); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + +} + +void GameOfLife::CreateFramebufferWithTexture() +{ + glGenFramebuffers(1, &m_FBO); + glBindFramebuffer(GL_FRAMEBUFFER, m_FBO); + + glGenTextures(1, &m_FBTexture); + glBindTexture(GL_TEXTURE_2D, m_FBTexture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, int(m_FBTextureSize.x), int(m_FBTextureSize.y), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FBTexture, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + LOG_WARN("Framebuffer Failed to Complete!"); + LOG_WARN(glCheckFramebufferStatus(GL_FRAMEBUFFER)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + +} + +void GameOfLife::OnEvent(Event& event) +{ + m_CameraController.OnEvent(event); +} + +void GameOfLife::RandomGrid() +{ + for (int i = 0; i < m_GridSize * m_GridSize; i++) + { + int choices = 2; + int picked_choice = (int)(rand() % choices); + if (picked_choice == 1) { + m_ParticleStates_in.at(i) = 1; + } + } +} + +void GameOfLife::FillGrid() +{ + for (int i = 0; i < m_GridSize * m_GridSize; i++) + { + m_Grid.at(i) = 1; + } +} + + +void GameOfLife::ReloadFramebuffer() +{ + glDeleteFramebuffers(1, &m_FBO); + + glDeleteTextures(1, &m_FBTexture); + + this->CreateFramebufferWithTexture(); +} + +void GameOfLife::SetGridBorderThickness(float thickness) +{ + m_BorderThickness = thickness; +} + + +GLuint GameOfLife::GetFBTextureID() +{ + return m_FBTexture; +} + +void GameOfLife::AddCell(int xcoord, int ycoord) +{ + int index = GetCellIndex(xcoord, ycoord, this); + + std::cout << "Index: " << index << "\n"; + m_ParticleStates_in.at(index) = 1; + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_in); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates_in.size() * sizeof(int), m_ParticleStates_in.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_in); +} + +void GameOfLife::RemoveCell(int xcoord, int ycoord) +{ + int index = GetCellIndex(xcoord, ycoord, this); + + std::cout << "Index: " << index << "\n"; + m_ParticleStates_in.at(index) = 0; + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_in); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates_in.size() * sizeof(int), m_ParticleStates_in.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_in); +} + +int GetCellIndex(int xcoord, int ycoord, GameOfLife* game) +{ + float gridsize = game->m_GridSize; + + float cellwidth = 1000.0f / gridsize; + + glm::vec2 CellCoord = glm::vec2(floor((xcoord) / cellwidth), floor((std::abs(ycoord - 1000)) / cellwidth)); + + std::cout << "Coord x" << CellCoord.x << " y" << CellCoord.y << "\n"; + + return (CellCoord.y) * gridsize + (CellCoord.x); +} + +void GameOfLife::ChangeGridSize(int newGridSize) +{ + GLuint buffers[] = { m_SSBO_in, m_SSBO_out, m_SSBO_grid }; + glDeleteBuffers(2, buffers); + + + m_ParticleStates_in.resize(newGridSize * newGridSize); + m_ParticleStates_out.resize(newGridSize * newGridSize); + m_Grid.resize(newGridSize * newGridSize); + + std::fill(m_ParticleStates_in.begin(), m_ParticleStates_in.end(), 0); + std::fill(m_ParticleStates_out.begin(), m_ParticleStates_out.end(), 0); + std::fill(m_Grid.begin(), m_Grid.end(), 0); + + m_GridSize = newGridSize; + + + FillGrid(); + RandomGrid(); + + //create SSBO Buffer + glGenBuffers(1, &m_SSBO_in); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_in); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates_in.size() * sizeof(int), m_ParticleStates_in.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_in); + + glGenBuffers(1, &m_SSBO_out); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_out); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates_out.size() * sizeof(int), m_ParticleStates_out.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_SSBO_out); + + glGenBuffers(1, &m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_grid); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_Grid.size() * sizeof(int), m_Grid.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + +} + +void GameOfLife::ResetSimulation() +{ + std::fill(m_ParticleStates_in.begin(), m_ParticleStates_in.end(), 0); + std::fill(m_ParticleStates_out.begin(), m_ParticleStates_out.end(), 0); + + RandomGrid(); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_in); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates_in.size() * sizeof(int), m_ParticleStates_in.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_in); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_out); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates_out.size() * sizeof(int), m_ParticleStates_out.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, m_SSBO_out); +} diff --git a/OpenGL-Sandbox/src/GameOfLife.h b/OpenGL-Sandbox/src/GameOfLife.h new file mode 100644 index 00000000..b5d21dc7 --- /dev/null +++ b/OpenGL-Sandbox/src/GameOfLife.h @@ -0,0 +1,85 @@ +#pragma once +#include +#include +#include +#include "GLCore/Core/Log.h" +#include "GLCore/Util/Shader.h" + +#define GRID_SIZE 100 + +using namespace GLCore; +using namespace GLCore::Utils; + + + + + +class GameOfLife +{ + friend int GetCellIndex(int xcoord, int ycoord, GameOfLife* game); +public: + + GameOfLife(int GridSize); + ~GameOfLife(); + + void OnComputeGameOfLife(); + void OnComputeLiquidGas(); + void OnRender(Timestep ts); + void CreateFramebufferWithTexture(); + void OnEvent(Event& e); + void RandomGrid(); + void AddCell(int xcoord, int ycoord); + void RemoveCell(int xcoord, int ycoord); + void FillGrid(); + void ToggleGrid() { m_ShowGrid = m_ShowGrid ? false : true; }; + void ChangeGridSize(int newsize); + void ResetSimulation(); + + void ReloadFramebuffer(); + + //Setters + void SetGridBorderThickness(float thickness); + + + //Getters + GLuint GetFBTextureID(); + + int m_GridSize; + glm::vec2 m_FBTextureSize = glm::vec2(500.0f, 500.0f); + + GLCore::Utils::OrthographicCameraController m_CameraController; + +private: + + std::vector m_ParticleStates_in; + std::vector m_ParticleStates_out; + std::vector m_Grid; + + //Conditions + bool m_ShowGrid = false; + bool m_ShouldCreateFramebuffer = true; + float m_BorderThickness = 0.1f; + + + //Drawing Stuff + GLuint m_VBO, m_VAO, m_EBO; + GLuint m_SSBO_in, m_SSBO_out, m_SSBO_grid; + GLuint m_CompShaderGameOfLife, m_CompShaderLiquidGas; + GLCore::Utils::Shader* m_Shader; + GLCore::Utils::Shader* m_GridShader; + glm::vec4 m_ClearColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f); + + //Framebuffer + GLuint m_FBO; + GLuint m_FBTexture; + + //Liquid Gas Comp Shader Variables +public: + float m_Temperature = 0.0f; + float m_ChemcialPotential = 0.0f; + + +}; + +GameOfLife* ChangeGridSize(GameOfLife* game, int newGridSize); + diff --git a/OpenGL-Sandbox/src/LiquidGas.cpp b/OpenGL-Sandbox/src/LiquidGas.cpp new file mode 100644 index 00000000..9209bcbe --- /dev/null +++ b/OpenGL-Sandbox/src/LiquidGas.cpp @@ -0,0 +1,408 @@ +#include "LiquidGas.h" + + +using namespace GLCore; +using namespace GLCore::Utils; + +#include +#include +#include +#include + + + + + +LiquidGas::LiquidGas(int GridSize) + :m_CameraController(1.0f), m_GridSize(GridSize), + m_Grid(GridSize* GridSize), + m_ParticleStates(GridSize * GridSize), + m_Debug(GridSize * GridSize), + m_Distribution(0, GridSize - 1) +{ + m_Shader = Shader::FromGLSLTextFiles("Shaders/CellRender.vert.glsl", "Shaders/CellRender.frag.glsl"); + + m_GridShader = Shader::FromGLSLTextFiles("Shaders/GridRender.vert.glsl", "Shaders/GridRender.frag.glsl"); + + if (m_ShouldCreateFramebuffer) + { + CreateFramebufferWithTexture(); + m_ShouldCreateFramebuffer = false; + } + + FillGrid(); + RandomParticlesFill(); + + float vertices[3 * 4] = { + // positions + 1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f + }; + + glGenVertexArrays(1, &m_VAO); + glGenBuffers(1, &m_VBO); + glGenBuffers(1, &m_EBO); + + glBindVertexArray(m_VAO); + + glBindBuffer(GL_ARRAY_BUFFER, m_VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + unsigned int indices[3 * 3] = { + 0, 1, 3, // first triangle + 1, 2, 3 // second triangle + }; + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + // position attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glGenBuffers(1, &m_SSBO_particles); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_particles); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates.size() * sizeof(int), m_ParticleStates.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_particles); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + + glGenBuffers(1, &m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_grid); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_Grid.size() * sizeof(int), m_Grid.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + + glGenBuffers(1, &m_SSBO_debug); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_debug); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_Debug.size() * sizeof(int), m_Debug.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, m_SSBO_debug); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); +} + +LiquidGas::~LiquidGas() +{ + GLuint buffers[] = { m_VBO, m_EBO, m_SSBO_grid, m_SSBO_particles}; + glDeleteBuffers(IM_ARRAYSIZE(buffers), buffers); + + glDeleteVertexArrays(1, &m_VAO); + + glDeleteProgram(m_Shader->GetRendererID()); + + glDeleteFramebuffers(1, &m_FBO); + +} + +int LiquidGas::CellIndex(int x, int y) +{ + if (std::sqrt(m_ParticleStates.size()) != int(std::sqrt(m_ParticleStates.size()))) + LOG_WARN("NOT A SQUARE NUMBER! CELL INDEX ERROR"); + + + int gridheight = m_GridSize; + return (y * gridheight + x); +} + +int LiquidGas::CellActive(int x, int y) +{ + + //out of bounds check + if (x > m_GridSize - 1 || x < 0 || + y > m_GridSize - 1 || y < 0) + { + return 0; + } + + int state = m_ParticleStates[this->CellIndex(x, y)]; + if (state == 0 || state == 1) + return state; + + else + { + LOG_WARN("CELL STATE NOT 0 or 1 !!!!!!"); + return state; + } +} + +std::random_device rd; +std::mt19937 gen(rd()); +std::uniform_real_distribution dist(0.0, 1.0); + +#define BIT(x) 1 << x +enum EdgeCase +{ + NONE = BIT(0), + + MAX_X_1 = BIT(1), + MAX_X_2 = BIT(2), + + MIN_X_1 = BIT(3), + MIN_X_2 = BIT(4), + + MAX_Y_1 = BIT(5), + MAX_Y_2 = BIT(6), + + MIN_Y_1 = BIT(7), + MIN_Y_2 = BIT(8) +}; + +struct Particles +{ + glm::ivec2 a; + glm::ivec2 b; + int max; +}; + +int GetEdgeCase(Particles& p) +{ + int edgecase = NONE; + + if (p.a.x == p.max) + edgecase |= MAX_X_1; + + if (p.b.x == p.max) + edgecase |= MAX_X_2; + + if (p.a.x == 0) + edgecase |= MIN_X_1; + + if (p.b.x == 0) + edgecase |= MIN_X_2; + + if (p.a.y == p.max) + edgecase |= MAX_Y_1; + + if (p.b.y == p.max) + edgecase |= MAX_Y_2; + + if (p.a.y == 0) + edgecase |= MIN_Y_1; + + if (p.b.y == 0) + edgecase |= MIN_Y_2; + + return edgecase; +} + + +constexpr double kB = 1.380649e-23; +void LiquidGas::OnCompute() +{ + + + int max_xy = m_GridSize - 1; + for (int i = 0; i < m_GridSize * m_GridSize; i++) + { + glm::ivec2 particle_1{ m_Distribution(m_Generator), m_Distribution(m_Generator) }; + glm::ivec2 particle_2{ m_Distribution(m_Generator), m_Distribution(m_Generator) }; + + + + //do nothing if equal + if (CellActive(particle_1.x, particle_1.y) == CellActive(particle_2.x, particle_2.y)) + return; + + int active_neighbours_1 = -1, active_neighbours_2 = -1; + + active_neighbours_1 = CellActive((particle_1.x + 1), (particle_1.y + 1)) + + CellActive((particle_1.x + 1), (particle_1.y)) + + CellActive((particle_1.x + 1), (particle_1.y - 1)) + + CellActive((particle_1.x), (particle_1.y - 1)) + + CellActive((particle_1.x - 1), (particle_1.y - 1)) + + CellActive((particle_1.x - 1), (particle_1.y)) + + CellActive((particle_1.x - 1), (particle_1.y + 1)) + + CellActive((particle_1.x), (particle_1.y + 1)); + + active_neighbours_2 = CellActive((particle_2.x + 1), (particle_2.y + 1)) + + CellActive((particle_2.x + 1), (particle_2.y)) + + CellActive((particle_2.x + 1), (particle_2.y - 1)) + + CellActive((particle_2.x), (particle_2.y - 1)) + + CellActive((particle_2.x - 1), (particle_2.y - 1)) + + CellActive((particle_2.x - 1), (particle_2.y)) + + CellActive((particle_2.x - 1), (particle_2.y + 1)) + + CellActive((particle_2.x), (particle_2.y + 1)); + + + + int energy_diff = std::abs(active_neighbours_1 - active_neighbours_2); + + + double q = std::exp( -energy_diff / (m_Temperature)); + + double swap_probability = (q) / (1.0 + q); + + int particle_1_index = CellIndex(particle_1.x, particle_1.y); + int particle_2_index = CellIndex(particle_2.x, particle_2.y); + + if (dist(gen) < swap_probability) + std::swap(m_ParticleStates[particle_1_index], m_ParticleStates[particle_2_index]); + } + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_particles); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates.size() * sizeof(int), m_ParticleStates.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_particles); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_debug); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_Debug.size() * sizeof(int), m_Debug.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, m_SSBO_debug); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + +} + +void LiquidGas::OnRender(Timestep ts) +{ + m_CameraController.OnUpdate(ts); + glBindFramebuffer(GL_FRAMEBUFFER, m_FBO); + glViewport(0, 0, (int)m_FBTextureSize.x, (int)m_FBTextureSize.y); + + glClearColor(m_ClearColor.r, m_ClearColor.g, m_ClearColor.b, m_ClearColor.a); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(m_Shader->GetRendererID()); + + int location = glGetUniformLocation(m_Shader->GetRendererID(), "u_GridSize"); + glUniform1i(location, m_GridSize); + + location = glGetUniformLocation(m_Shader->GetRendererID(), "u_ViewProjection"); + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(m_CameraController.GetCamera().GetViewProjectionMatrix())); + + glUseProgram(m_GridShader->GetRendererID()); + + location = glGetUniformLocation(m_GridShader->GetRendererID(), "u_GridSize"); + glUniform1i(location, m_GridSize); + + location = glGetUniformLocation(m_GridShader->GetRendererID(), "u_ViewProjection"); + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(m_CameraController.GetCamera().GetViewProjectionMatrix())); + + location = glGetUniformLocation(m_GridShader->GetRendererID(), "u_ClearColor"); + glUniform4fv(location, 1, glm::value_ptr(m_ClearColor)); + + location = glGetUniformLocation(m_GridShader->GetRendererID(), "u_BorderThickness"); + glUniform1f(location, m_BorderThickness); + + glUseProgram(m_Shader->GetRendererID()); + + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, m_GridSize * m_GridSize); + + + if (m_ShowGrid) + { + glUseProgram(m_GridShader->GetRendererID()); + + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, m_GridSize * m_GridSize); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void LiquidGas::CreateFramebufferWithTexture() +{ + glGenFramebuffers(1, &m_FBO); + glBindFramebuffer(GL_FRAMEBUFFER, m_FBO); + + glGenTextures(1, &m_FBTexture); + glBindTexture(GL_TEXTURE_2D, m_FBTexture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, int(m_FBTextureSize.x), int(m_FBTextureSize.y), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FBTexture, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + LOG_WARN("Framebuffer Failed to Complete!"); + LOG_WARN(glCheckFramebufferStatus(GL_FRAMEBUFFER)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + +} + +void LiquidGas::OnEvent(Event& event) +{ + //m_CameraController.OnEvent(event); +} + +void LiquidGas::RandomParticlesFill() +{ + for (int i = 0; i < m_GridSize * m_GridSize; i++) + { + int choices = 2; + int picked_choice = (int)(rand() % choices); + if (picked_choice == 1) { + m_ParticleStates.at(i) = 1; + } + } +} + +void LiquidGas::TestFill() +{ + for (int i = 0; i < 600; i++) + { + m_ParticleStates[i] = 1; + } +} + +void LiquidGas::FillGrid() +{ + for (int i = 0; i < m_GridSize * m_GridSize; i++) + { + m_Grid.at(i) = 1; + } +} + + +void LiquidGas::ReloadFramebuffer() +{ + glDeleteFramebuffers(1, &m_FBO); + + glDeleteTextures(1, &m_FBTexture); + + this->CreateFramebufferWithTexture(); +} + +void LiquidGas::SetGridBorderThickness(float thickness) +{ + m_BorderThickness = thickness; +} + + +GLuint LiquidGas::GetFBTextureID() +{ + return m_FBTexture; +} + + + +void LiquidGas::ChangeGridSize(int newGridSize) +{ + m_ParticleStates.resize(newGridSize * newGridSize); + m_Grid.resize(newGridSize * newGridSize); + + std::fill(m_ParticleStates.begin(), m_ParticleStates.end(), 0); + std::fill(m_Grid.begin(), m_Grid.end(), 0); + + m_GridSize = newGridSize; + + FillGrid(); + RandomParticlesFill(); + + //create SSBO Buffer + glGenBuffers(1, &m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_grid); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_Grid.size() * sizeof(int), m_Grid.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + + glGenBuffers(1, &m_SSBO_particles); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_SSBO_particles); + glBufferData(GL_SHADER_STORAGE_BUFFER, m_ParticleStates.size() * sizeof(int), m_ParticleStates.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m_SSBO_grid); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + +} + diff --git a/OpenGL-Sandbox/src/LiquidGas.h b/OpenGL-Sandbox/src/LiquidGas.h new file mode 100644 index 00000000..0b283426 --- /dev/null +++ b/OpenGL-Sandbox/src/LiquidGas.h @@ -0,0 +1,85 @@ +#pragma once +#include +#include +#include +#include "GLCore/Core/Log.h" +#include "GLCore/Util/Shader.h" +#include +#include + + +using namespace GLCore; +using namespace GLCore::Utils; + + +class LiquidGas +{ + friend int GetCellIndex(int xcoord, int ycoord, LiquidGas* game); +public: + + LiquidGas(int GridSize); + ~LiquidGas(); + + void OnCompute(); + void OnRender(Timestep ts); + void CreateFramebufferWithTexture(); + void OnEvent(Event& e); + void RandomParticlesFill(); + void AddCell(int xcoord, int ycoord); + void RemoveCell(int xcoord, int ycoord); + void FillGrid(); + void ToggleGrid() { m_ShowGrid = m_ShowGrid ? false : true; }; + void ChangeGridSize(int newsize); + + int CellIndex(int x, int y); + int CellActive(int x, int y); + + void ReloadFramebuffer(); + + void TestFill(); + + //Setters + void SetGridBorderThickness(float thickness); + + + GLuint GetFBTextureID(); + + int m_GridSize; + glm::vec2 m_FBTextureSize = glm::vec2(500.0f, 500.0f); + + GLCore::Utils::OrthographicCameraController m_CameraController; + + double m_Temperature = 5; + +private: + + std::default_random_engine m_Generator; + std::uniform_int_distribution m_Distribution; + + + std::vector m_Grid; + std::vector m_ParticleStates; + std::vector m_Debug; + + //Conditions + bool m_ShowGrid = false; + bool m_ShouldCreateFramebuffer = true; + float m_BorderThickness = 0.1f; + + + //Drawing Stuff + GLuint m_VBO, m_VAO, m_EBO; + GLuint m_SSBO_grid, m_SSBO_particles, m_SSBO_debug; + GLCore::Utils::Shader* m_Shader; + GLCore::Utils::Shader* m_GridShader; + glm::vec4 m_ClearColor = glm::vec4(0.5f, 0.5f, 0.5f, 1.0f); + + //Framebuffer + GLuint m_FBO; + GLuint m_FBTexture; +}; + +LiquidGas* ChangeGridSize(LiquidGas* game, int newGridSize); + + + diff --git a/OpenGL-Sandbox/src/SandboxApp.cpp b/OpenGL-Sandbox/src/SandboxApp.cpp index 8f527132..74b505ad 100644 --- a/OpenGL-Sandbox/src/SandboxApp.cpp +++ b/OpenGL-Sandbox/src/SandboxApp.cpp @@ -7,6 +7,7 @@ class Sandbox : public Application { public: Sandbox() + :Application("Phase Change Simulation", 2560, 1440) { PushLayer(new SandboxLayer()); } diff --git a/OpenGL-Sandbox/src/SandboxLayer.cpp b/OpenGL-Sandbox/src/SandboxLayer.cpp index 1b1c4f30..72e64519 100644 --- a/OpenGL-Sandbox/src/SandboxLayer.cpp +++ b/OpenGL-Sandbox/src/SandboxLayer.cpp @@ -1,39 +1,202 @@ #include "SandboxLayer.h" +#include +#include + using namespace GLCore; using namespace GLCore::Utils; + + SandboxLayer::SandboxLayer() + :m_WindowSize(glm::vec2(2560, 1440)) { + } SandboxLayer::~SandboxLayer() { + delete m_Game; + delete m_LiquidGas; + } void SandboxLayer::OnAttach() { EnableGLDebugging(); + m_Game->m_GridSize = INIT_GRID_SIZE; - // Init here } void SandboxLayer::OnDetach() { - // Shutdown here + } + + void SandboxLayer::OnEvent(Event& event) { - // Events here + m_Game->OnEvent(event); + + EventDispatcher dispatcher(event); + + dispatcher.Dispatch([&](MouseMovedEvent& e) + { + m_XMousePos = e.GetX(); + m_YMousePos = e.GetY(); + + + return false; + }); + + /*dispatcher.Dispatch([&](MouseButtonPressedEvent& e) + { + if (e.GetMouseButton() == RightClick) + { + m_Game->AddCell(m_XMousePos, m_YMousePos); + + } + return false; + });*/ + + dispatcher.Dispatch([&](MouseButtonPressedEvent& e) + { + if (e.GetMouseButton() == LeftClick) + { + m_Game->RemoveCell(m_XMousePos, m_YMousePos); + + + } + return false; + }); + + + } void SandboxLayer::OnUpdate(Timestep ts) { - // Render here + m_Game->m_Temperature = m_TempSlider; + m_Game->m_ChemcialPotential = m_CPSlider; + + if (m_ComputeState == COMPUTE && m_TimeCounter >= m_UpdateFrequency) + { + switch (m_WhatSimulation) + { + case GAMEOFLIFE: + { + m_Game->OnComputeGameOfLife(); + break; + } + + case LIQUIDGAS: + { + m_Game->OnComputeLiquidGas(); + break; + } + default: + break; + } + + m_TimeCounter = 0; + } + + m_Game->OnRender(ts); + + + m_TimeCounter += ts; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void SandboxLayer::OnRender() +{ + ShellExecute(NULL, L"open", L"C:\\this", NULL, NULL, SW_SHOWNORMAL); } void SandboxLayer::OnImGuiRender() { - // ImGui here + ImGui::GetIO().FontGlobalScale = 1.5f; + ImGui::DockSpaceOverViewport(); + + ImGui::GetIO().WantCaptureMouse = false; + + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 }); + + ImGui::Begin("Viewport", nullptr, ImGuiWindowFlags_NoResize); + ImVec2 viewportpanelsize = ImGui::GetContentRegionAvail(); + + if (m_ViewportSize != *((glm::vec2*)&viewportpanelsize)) + { + m_Game->m_FBTextureSize = { viewportpanelsize.x, viewportpanelsize.y }; + m_Game->ReloadFramebuffer(); + + m_ViewportSize = { viewportpanelsize.x, viewportpanelsize.y }; + } + + + ImGui::Image((ImTextureID)m_Game->GetFBTextureID(), viewportpanelsize); + ImGui::End(); + ImGui::PopStyleVar(); + + ImGui::Begin("Controls"); + + if (ImGui::Button("Start")) + { + m_ComputeState = m_ComputeState == COMPUTE ? PAUSED : COMPUTE; + } + + if (ImGui::Button("Reset")) + { + m_Game->ResetSimulation(); + m_ComputeState = PAUSED; + } + + + if (ImGui::SliderInt("GridSize", &m_GridSize, 4, 2000)) + { + m_ComputeState = PAUSED; + m_Game->ChangeGridSize(m_GridSize); + } + + float borderthickness = 0.1f; + + if (ImGui::SliderFloat("Grid Thickness", &borderthickness, 0.0f, 1.0f)) + { + m_Game->SetGridBorderThickness(borderthickness); + } + + ImGui::SliderFloat("Update Frequency", &m_UpdateFrequency, 0.0f, 0.1f); + + + if (ImGui::Button("Show Grid")) + { + m_Game->ToggleGrid(); + m_LiquidGas->ToggleGrid(); + } + + if (ImGui::Button("Switch Sim")) + { + m_WhatSimulation = m_WhatSimulation == GAMEOFLIFE ? LIQUIDGAS : GAMEOFLIFE; + } + + double max = 10.0; + double min = 0.0; + ImGui::SliderScalar("Temperature", ImGuiDataType_Double, &m_TempSlider, &min, &max); + + max = -1.0; + min = -3.0; + ImGui::SliderScalar("Chemical Potential", ImGuiDataType_Double, &m_CPSlider, &min, &max); + + + + + ImGui::End(); + + + } diff --git a/OpenGL-Sandbox/src/SandboxLayer.h b/OpenGL-Sandbox/src/SandboxLayer.h index f2096283..210627c5 100644 --- a/OpenGL-Sandbox/src/SandboxLayer.h +++ b/OpenGL-Sandbox/src/SandboxLayer.h @@ -1,7 +1,27 @@ #pragma once -#include -#include +#include "GameOfLife.h" +#include "LiquidGas.h" + +#define INIT_GRID_SIZE 64 + +enum State +{ + COMPUTE, + PAUSED +}; + +enum MouseButton +{ + RightClick = 0, + LeftClick +}; + +enum SimulationSelector +{ + GAMEOFLIFE, + LIQUIDGAS +}; class SandboxLayer : public GLCore::Layer { @@ -13,6 +33,32 @@ class SandboxLayer : public GLCore::Layer virtual void OnDetach() override; virtual void OnEvent(GLCore::Event& event) override; virtual void OnUpdate(GLCore::Timestep ts) override; + virtual void OnRender() override; virtual void OnImGuiRender() override; private: + + + + GameOfLife* m_Game = new GameOfLife(INIT_GRID_SIZE); + LiquidGas* m_LiquidGas = new LiquidGas(INIT_GRID_SIZE); + + State m_ComputeState = PAUSED; + SimulationSelector m_WhatSimulation = LIQUIDGAS; + + float m_UpdateFrequency = 0.0f; + float m_TimeCounter = 0; + + int m_GridSize = INIT_GRID_SIZE; + + + float m_XMousePos; + float m_YMousePos; + + //IMGUI sliders + double m_TempSlider = 2.0f; + double m_CPSlider = 2.0f; + + glm::vec2 m_WindowSize; + + glm::vec2 m_ViewportSize; }; \ No newline at end of file