You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lunarium_OLD/src/graphics/opengl/glGraphics.cpp

559 lines
19 KiB
C++

/******************************************************************************
* File - OGLGraphics.cpp
* Author - Joey Pollack
* Date - 2021/08/30 (y/m/d)
* Mod Date - 2021/09/02 (y/m/d)
* Description - An openGL implementation of the engine Graphics interface.
******************************************************************************/
#include "glGraphics.h"
#include "defaultShaders.h"
#include <window/window.h>
#include <assets/types/image.h>
#include "../internalFontData.h"
#include <utils/logger.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <utils/stb/stb_image.h>
namespace lunarium
{
////////////////////////////////////////////////////////////
// DEBUG MESSAGE CALLBACK
////////////////////////////////////////////////////////////
std::map<int, std::string> OglGraphics::mDebugMsgTypes;
std::map<int, std::string> OglGraphics::mDebugMsgSources;
std::map<int, std::string> OglGraphics::mDebugMsgSeverity;
void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::OGL_DEBUG,
"%s (type: %s, source: %s, severity: %s), message: %s",
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
OglGraphics::mDebugMsgSources[source].c_str(),
OglGraphics::mDebugMsgTypes[type].c_str(),
OglGraphics::mDebugMsgSeverity[severity].c_str(),
message);
}
////////////////////////////////////////////////////////////
// RESTRICTED METHODS
////////////////////////////////////////////////////////////
OglGraphics::OglGraphics()
{
}
OpRes OglGraphics::Initialize(Window* pWindow, bool enableDebugMessages)
{
if (!pWindow->IsInit())
{
return OpRes::Fail("Can not initialize Graphics interface. The Window must be initialized first!");
}
if (mbIsInit)
{
return OpRes::Fail("Can not initialize Graphics interface. It is already initialized");
}
mpWindow = pWindow;
mpFBTexture = new Image;
ResizeCanvas(); // Also calls InitTextureFrameBuffer
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (enableDebugMessages)
{
InitDebugMsgSystem();
}
// Initialize the 2D drawing systems
InitImageSystem();
InitShapeSystem();
// Init text rendering
OpRes res = mText.Initialize();
if (Failed(res))
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::WARNING, "Could not initialized the text renderer - %s", res.Description);
}
// Load the default internal font
const char* font = "OpenSans-Regular.ttf";
// GenerateFontFileAt(font);
mDefaultFont = mText.LoadFont(FontData, DataSize, font);
if (mDefaultFont < 0)
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::WARNING, "Unable to load the default font: %s", font);
}
else
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::INFO, "Successfully created default font: %s", font);
}
return OpRes::OK();
}
void OglGraphics::Shutdown()
{
// Nothing to clean up at this point
}
void OglGraphics::ResizeCanvas()
{
// Get viewport size from glfw window
glfwGetFramebufferSize(mpWindow->GetWindow(), &mFBWidth, &mFBHeight);
glViewport(0, 0, mFBWidth, mFBHeight);
mProjection = glm::ortho(0.0f, (GLfloat)mFBWidth, (GLfloat)mFBHeight, 0.0f, -1.0f, 1.0f);
// mProjection = glm::ortho(0.0f, (GLfloat)mFBWidth, 0.0f, (GLfloat)mFBHeight, -1.0f, 1.0f);
Logger::Log(LogCategory::GRAPHICS, LogLevel::INFO_VERBOSE,
"glViewport set to %d, %d", mFBWidth, mFBHeight);
InitTextureFrameBuffer();
}
void OglGraphics::BeginDraw(RenderTarget rt)
{
mRT = rt;
int width = 0, height = 0;
mpWindow->GetFramebufferSize(&width, &height);
if (mFBWidth != width || mFBHeight != height)
{
ResizeCanvas();
}
if (mRT == RenderTarget::RT_IMAGE)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
Image* OglGraphics::EndDraw()
{
if (mRT == RenderTarget::RT_IMAGE)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, mpFBTexture->GetGLTextureID());
// Buffer width and height (in pixels) is the same as the screen size
// Need to multiply these by the number bytes per pixel
//int bufferSize = mFBWidth * mFBHeight * 4; // NOTE: Assuming 4 channels for now
//unsigned char* buffer = new unsigned char[bufferSize];
//glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*)buffer);
//FlipImageVertically(buffer, mFBWidth, mFBHeight, 1);
//glSetTexImage()
mpFBTexture->FreeRawData();
mpFBTexture->SetWidth(mFBWidth);
mpFBTexture->SetHeight(mFBHeight);
mpFBTexture->SetFormat(ImageFormat::RGBA);
// return a copy of the image
return mpFBTexture;
}
else
{
mpWindow->SwapBuffers();
return nullptr;
}
}
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// USER METHODS
////////////////////////////////////////////////////////////
void OglGraphics::SetClearColor(Color c)
{
mClearColor = c;
glClearColor(mClearColor.Red, mClearColor.Green, mClearColor.Blue, mClearColor.Alpha);
}
Color OglGraphics::GetClearColor() const
{
return mClearColor;
}
// Draw Methods
void OglGraphics::DrawLine(glm::vec2 point1, glm::vec2 point2, Color color, float lineWidth)
{
mShapeShader.MakeActive();
mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
mShapeShader.SetUniformf("shapeColor", { color.Red, color.Green, color.Blue, color.Alpha });
glBindVertexArray(mRectVAO);
GLfloat lineVerts[6][4] = {
{ point1.x, point1.y, },
{ point2.x, point2.y, },
{ 0.0f, 0.0f, },
{ 0.0f, 0.0f, },
{ 0.0f, 0.0f, },
{ 0.0f, 0.0f, }
};
glBindBuffer(GL_ARRAY_BUFFER, mRectVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(lineVerts), lineVerts);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glLineWidth(lineWidth);
glDrawArrays(GL_LINES, 0, 2);
glBindVertexArray(0);
}
void OglGraphics::DrawEllipse(glm::vec2 center, glm::vec2 radii, Color color, float thickness, int resolution)
{
mShapeShader.MakeActive();
mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
mShapeShader.SetUniformf("shapeColor", { color.Red, color.Green, color.Blue, color.Alpha });
glBindVertexArray(mEllipseVAO);
GLfloat points[360][2];
for (int i = 0; i < 360; i++)
{
float rad = i * 3.14159f / 180.0f;
points[i][0] = center.x + (radii.x * cos(rad));
points[i][1] = center.y + (radii.y * sin(rad));
}
glBindBuffer(GL_ARRAY_BUFFER, mEllipseVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(points), points);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glLineWidth(thickness);
glDrawArrays(GL_LINE_LOOP, 0, 360);
glBindVertexArray(0);
}
void OglGraphics::DrawFilledEllipse(glm::vec2 center, glm::vec2 radii, Color color, int resolution)
{
mShapeShader.MakeActive();
mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
mShapeShader.SetUniformf("shapeColor", { color.Red, color.Green, color.Blue, color.Alpha });
glBindVertexArray(mEllipseVAO);
GLfloat points[362][2];
points[0][0] = center.x;
points[0][1] = center.y;
for (int i = 1; i < 362; i++)
{
float rad = (i - 1) * 3.14159f / 180.0f;
points[i][0] = center.x + (radii.x * cos(rad));
points[i][1] = center.y + (radii.y * sin(rad));
}
glBindBuffer(GL_ARRAY_BUFFER, mEllipseVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(points), points);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 362);
glBindVertexArray(0);
}
void OglGraphics::DrawFilledBox(glm::vec2 topLeft, glm::vec2 botRight, Color color)
{
mShapeShader.MakeActive();
mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
mShapeShader.SetUniformf("shapeColor", { color.Red, color.Green, color.Blue, color.Alpha });
glBindVertexArray(mRectVAO);
GLfloat vertices[6][4] = {
{ topLeft.x, botRight.y, },
{ botRight.x, topLeft.y, },
{ topLeft.x, topLeft.y, },
{ topLeft.x, botRight.y, },
{ botRight.x, botRight.y, },
{ botRight.x, topLeft.y, }
};
glBindBuffer(GL_ARRAY_BUFFER, mRectVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
void OglGraphics::DrawBox(Rectangle dimensions, Color color, float thickness)
{
glm::vec2 topLeft(dimensions.Left, dimensions.Top);
glm::vec2 botRight(dimensions.right(), dimensions.bottom());
// left side top to bottom
DrawLine(glm::vec2(topLeft.x, topLeft.y), glm::vec2(topLeft.x, botRight.y), color, thickness);
// bottom left to right
DrawLine(glm::vec2(topLeft.x, botRight.y), glm::vec2(botRight.x, botRight.y), color, thickness);
// right side bottom to top
DrawLine(glm::vec2(botRight.x, botRight.y), glm::vec2(botRight.x, topLeft.y), color, thickness);
// top right to left
DrawLine(glm::vec2(botRight.x, topLeft.y), glm::vec2(topLeft.x, topLeft.y), color, thickness);
}
void OglGraphics::DrawImage(Image& image, glm::vec2 topLeft, Color color)
{
DrawImage(image, Rectangle(0.0f, 0.0f, (float)image.GetWidth(), (float)image.GetHeight()),
Rectangle(topLeft.x, topLeft.y, (float)image.GetWidth(), (float)image.GetHeight()), color);
}
void OglGraphics::DrawImage(Image& image, Rectangle source, Rectangle destination, Color color)
{
glm::mat4 id = glm::mat4(1.0f);
glm::vec3 pos = glm::vec3(destination.Left, destination.Top, 0.0f);
glm::mat4 trans = glm::translate(id, pos);
trans = glm::scale(trans, glm::vec3(destination.Width, destination.Height, 1.0f));
mImageShader.MakeActive();
// float widthDiff = image.GetWidth() - source.Width;
// if (source.Width == image.GetWidth() && source.Height == image.GetHeight())
// {
// mImageShader.SetUniformf("uvManip", { 1.0f, 0.0f, 1.0f, 0.0f }); // No uv Manipulation
// mImageShader.SetUniformMatrix("model", 1, glm::value_ptr(trans));
// mImageShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
// mImageShader.SetUniformf("spriteColor", { color.Red, color.Green, color.Blue, color.Alpha });
// }
// else
// {
// NOTE: Pretty sure these values will work out to be correct with out the if check
float xScale = source.Width / image.GetWidth();
float xOffset = source.Left / image.GetWidth();
float yScale = source.Height / image.GetHeight();
float yOffset = source.Top / image.GetHeight();
// Logger::Log(LogCategory::GRAPHICS, LogLevel::INFO_VERBOSE, "uvManip Values: %f, %f, %f, %f", xScale, xOffset, yScale, yOffset);
// * -1.0f on yScale will flip the image vertically
mImageShader.SetUniformf("uvManip", { xScale, xOffset, yScale /** -1.0f*/, yOffset});
mImageShader.SetUniformMatrix("model", 1, glm::value_ptr(trans));
mImageShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
mImageShader.SetUniformf("spriteColor", { color.Red, color.Green, color.Blue, color.Alpha });
//}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, image.GetGLTextureID());
glBindVertexArray(mImageVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
void OglGraphics::DrawString(const char* string, Rectangle boundingArea, Color color, float scale, int font)
{
mText.DrawString(font, string, glm::vec2(boundingArea.Left, boundingArea.Top),
glm::vec2(boundingArea.right(), boundingArea.bottom()), color, scale, mProjection);
}
// Takes raw image data and creates and Image class instance out of it.
// The raw data must be in one of the ImageFormats and it must be 1 byte per channel.
void OglGraphics::RegisterImage(Image& image)
{
unsigned int glFormat[4] = { GL_RGB, GL_RGBA, GL_BGR, GL_BGRA };
unsigned int textureID = 0;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, glFormat[image.GetFormat()], image.GetWidth(),
image.GetHeight(), 0, glFormat[image.GetFormat()], GL_UNSIGNED_BYTE, image.GetData());
glGenerateMipmap(GL_TEXTURE_2D);
// TODO: Move this to a different function to allow for more user options
// Or make a version of the method that takes a struct for these options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
image.SetGLTextureID(textureID);
}
// Fonts
int OglGraphics::DefaultFont() const
{
return mDefaultFont;
}
// For weight, 400 is normal and 700 is bold
int OglGraphics::CreateNewFont(const char* name, const unsigned char* fontData, int bufferSize, float size, int weight)
{
return mText.LoadFont(fontData, bufferSize, name, size, weight);
}
////////////////////////////////////////////////////////////
// HELPER INIT METHODS
////////////////////////////////////////////////////////////
void OglGraphics::InitDebugMsgSystem()
{
// DEBUG MESSAGE TYPES
mDebugMsgTypes[GL_DEBUG_TYPE_ERROR] = "ERROR";
mDebugMsgTypes[GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR] = "DEPRECATED_BEHAVIOR";
mDebugMsgTypes[GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR] = "UNDEFINED_BEHAVIOR";
mDebugMsgTypes[GL_DEBUG_TYPE_PORTABILITY] = "PORTABILITY";
mDebugMsgTypes[GL_DEBUG_TYPE_PERFORMANCE] = "PERFORMANCE";
mDebugMsgTypes[GL_DEBUG_TYPE_OTHER] = "OTHER";
mDebugMsgTypes[GL_DEBUG_TYPE_MARKER] = "MARKER";
mDebugMsgTypes[GL_DEBUG_TYPE_PUSH_GROUP] = "PUSH_GROUP";
mDebugMsgTypes[GL_DEBUG_TYPE_POP_GROUP] = "POP_GROUP";
// DEBUG MESSAGE SOURCES
mDebugMsgSources[GL_DEBUG_SOURCE_API] = "API";
mDebugMsgSources[GL_DEBUG_SOURCE_WINDOW_SYSTEM] = "WINDOW_SYSTEM";
mDebugMsgSources[GL_DEBUG_SOURCE_SHADER_COMPILER] = "SHADER_COMPILER";
mDebugMsgSources[GL_DEBUG_SOURCE_THIRD_PARTY] = "THIRD_PARTY";
mDebugMsgSources[GL_DEBUG_SOURCE_APPLICATION] = "APPLICATION";
mDebugMsgSources[GL_DEBUG_SOURCE_OTHER] = "OTHER";
// DEBUG MESSAGE SEVERITY
mDebugMsgSeverity[GL_DEBUG_SEVERITY_HIGH] = "HIGH";
mDebugMsgSeverity[GL_DEBUG_SEVERITY_MEDIUM] = "MEDIUM";
mDebugMsgSeverity[GL_DEBUG_SEVERITY_LOW] = "LOW";
mDebugMsgSeverity[GL_DEBUG_SEVERITY_NOTIFICATION] = "NOTIFICATION";
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(MessageCallback, 0);
}
void OglGraphics::InitImageSystem()
{
// Setup geometry
// https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites
GLuint VBO;
GLfloat vertices[] = {
// Pos // Tex
0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 1.0f
};
// GLfloat vertices[] = {
// // Pos // Tex
// 0.0f, 1.0f, 0.0f, 1.0f,
// 1.0f, 0.0f, 1.0f, 0.0f,
// 0.0f, 0.0f, 0.0f, 0.0f,
// 0.0f, 1.0f, 0.0f, 1.0f,
// 1.0f, 1.0f, 1.0f, 1.0f,
// 1.0f, 0.0f, 1.0f, 0.0f
// };
glGenVertexArrays(1, &mImageVAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(mImageVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Setup Shader Program
mImageShader.SetSource(glShader::TYPE_VERTEX, OGLDefaultShaders.DefaultSpriteVertex, false);
mImageShader.SetSource(glShader::TYPE_FRAGMENT, OGLDefaultShaders.DefaultSpriteFragment, false);
if (!mImageShader.BuildProgram())
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "Unable to build default image shader program");
}
}
void OglGraphics::InitShapeSystem()
{
// Create rectangle buffers
glGenVertexArrays(1, &mRectVAO);
glGenBuffers(1, &mRectVBO);
glBindVertexArray(mRectVAO);
glBindBuffer(GL_ARRAY_BUFFER, mRectVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Create Ellipse buffers
glGenVertexArrays(1, &mEllipseVAO);
glGenBuffers(1, &mEllipseVBO);
glBindVertexArray(mEllipseVAO);
glBindBuffer(GL_ARRAY_BUFFER, mEllipseVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 362 * 2, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Load shape shaders
mShapeShader.SetSource(glShader::TYPE_VERTEX, OGLDefaultShaders.DefaultShapeVertex, false);
mShapeShader.SetSource(glShader::TYPE_FRAGMENT, OGLDefaultShaders.DefaultShapeFragment, false);
if (!mShapeShader.BuildProgram())
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::WARNING, "Unable to build shape shader program.");
}
}
void OglGraphics::InitTextureFrameBuffer()
{
// Frame buffer
glGenFramebuffers(1, &mFBO);
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
// Texture
unsigned int id = -1;
glGenTextures(1, &id);
mpFBTexture->SetGLTextureID(id);
glBindTexture(GL_TEXTURE_2D, mpFBTexture->GetGLTextureID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mFBWidth, mFBHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
// Attach texture
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mpFBTexture->GetGLTextureID(), 0);
// Render Buffer for depth/stencil testing
glGenRenderbuffers(1, &mRBO);
glBindRenderbuffer(GL_RENDERBUFFER, mRBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mFBWidth, mFBHeight);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// Atach the render buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mRBO);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::WARNING, "Unable to initialize framebuffer for rendering to a texture");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}