|
|
|
|
/******************************************************************************
|
|
|
|
|
* 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 "../openFontData.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)
|
|
|
|
|
{
|
|
|
|
|
uint32_t level = LogLevel::OGL_DEBUG;
|
|
|
|
|
if (type == GL_DEBUG_TYPE_ERROR)
|
|
|
|
|
{
|
|
|
|
|
level = LogLevel::ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Logger::Log(LogCategory::GRAPHICS, level,
|
|
|
|
|
"%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;
|
|
|
|
|
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(OpenFontData, OpenDataSize, 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()
|
|
|
|
|
{
|
|
|
|
|
// Clean up frame buffers
|
|
|
|
|
std::vector<int> ids;
|
|
|
|
|
for (auto iter = mFrameBuffers.begin(); iter != mFrameBuffers.end(); iter++)
|
|
|
|
|
{
|
|
|
|
|
ids.push_back(iter->first);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < ids.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
DestroyRenderTexture(ids[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mFrameBuffers.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void OglGraphics::ResizeCanvas()
|
|
|
|
|
{
|
|
|
|
|
// NOTE: METHOD IS NO LONGER NEEDED
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void OglGraphics::BeginDraw(int framebuffer)
|
|
|
|
|
{
|
|
|
|
|
mActiveFrameBuffer = framebuffer;
|
|
|
|
|
|
|
|
|
|
if (mActiveFrameBuffer > -1)
|
|
|
|
|
{
|
|
|
|
|
mFBWidth = mFrameBuffers[mActiveFrameBuffer]->Texture.GetWidth();
|
|
|
|
|
mFBHeight = mFrameBuffers[mActiveFrameBuffer]->Texture.GetHeight();
|
|
|
|
|
glViewport(0, 0, mFBWidth, mFBHeight);
|
|
|
|
|
mProjection = glm::ortho(0.0f, (GLfloat)mFBWidth, (GLfloat)mFBHeight, 0.0f, -1.0f, 1.0f);
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBuffers[mActiveFrameBuffer]->FBO);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int width = 0, height = 0;
|
|
|
|
|
mpWindow->GetFramebufferSize(&width, &height);
|
|
|
|
|
if (mFBWidth != width || mFBHeight != height)
|
|
|
|
|
{
|
|
|
|
|
mFBWidth = width;
|
|
|
|
|
mFBHeight = height;
|
|
|
|
|
glViewport(0, 0, mFBWidth, mFBHeight);
|
|
|
|
|
mProjection = glm::ortho(0.0f, (GLfloat)mFBWidth, (GLfloat)mFBHeight, 0.0f, -1.0f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Image* OglGraphics::EndDraw()
|
|
|
|
|
{
|
|
|
|
|
if (mActiveFrameBuffer > -1)
|
|
|
|
|
{
|
|
|
|
|
FrameBuffer* fb = mFrameBuffers[mActiveFrameBuffer];
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, fb->Texture.GetGLTextureID());
|
|
|
|
|
|
|
|
|
|
GLenum format = GL_RGBA;
|
|
|
|
|
if (fb->Texture.GetFormat() == ImageFormat::RGB)
|
|
|
|
|
{
|
|
|
|
|
format = GL_RGB;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glGetTexImage(GL_TEXTURE_2D, 0, format, GL_UNSIGNED_BYTE, (void*)fb->Buffer);
|
|
|
|
|
|
|
|
|
|
fb->Texture.SetData(fb->Buffer);
|
|
|
|
|
|
|
|
|
|
return &fb->Texture;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mpWindow->SwapBuffers();
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
// USER METHODS
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
void OglGraphics::SetClearColor(Color c)
|
|
|
|
|
{
|
|
|
|
|
mClearColor = c;
|
|
|
|
|
glClearColor(mClearColor.R, mClearColor.G, mClearColor.B, mClearColor.A);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Color OglGraphics::GetClearColor() const
|
|
|
|
|
{
|
|
|
|
|
return mClearColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int OglGraphics::CreateRenderTexture(int width, int height, int channels)
|
|
|
|
|
{
|
|
|
|
|
FrameBuffer* fb = new FrameBuffer;
|
|
|
|
|
fb->Texture.SetWidth(width);
|
|
|
|
|
fb->Texture.SetHeight(height);
|
|
|
|
|
|
|
|
|
|
GLint format = -1;
|
|
|
|
|
if (3 == channels)
|
|
|
|
|
{
|
|
|
|
|
fb->Texture.SetFormat(ImageFormat::RGB);
|
|
|
|
|
format = GL_RGB;
|
|
|
|
|
}
|
|
|
|
|
else if (4 == channels)
|
|
|
|
|
{
|
|
|
|
|
fb->Texture.SetFormat(ImageFormat::RGBA);
|
|
|
|
|
format = GL_RGBA;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "OglGraphics::CreateRenderTexture failed - invalid channels value: %n", channels);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Frame buffer
|
|
|
|
|
glGenFramebuffers(1, &fb->FBO);
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fb->FBO);
|
|
|
|
|
|
|
|
|
|
// Texture
|
|
|
|
|
unsigned int id = -1;
|
|
|
|
|
glGenTextures(1, &id);
|
|
|
|
|
fb->Texture.SetGLTextureID(id);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, fb->Texture.GetGLTextureID());
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, 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, fb->Texture.GetGLTextureID(), 0);
|
|
|
|
|
|
|
|
|
|
// Render Buffer for depth/stencil testing
|
|
|
|
|
glGenRenderbuffers(1, &fb->RBO);
|
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, fb->RBO);
|
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
|
|
|
|
|
|
|
// Atach the render buffer
|
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->RBO);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// finalize the FrameBuffer object
|
|
|
|
|
fb->Buffer = new unsigned char[width * height * channels];
|
|
|
|
|
fb->Texture.SetData(nullptr);
|
|
|
|
|
int next_id = mFrameBuffers.size();
|
|
|
|
|
mFrameBuffers[next_id] = fb;
|
|
|
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
|
|
|
|
|
|
return next_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OglGraphics::DestroyRenderTexture(int texture)
|
|
|
|
|
{
|
|
|
|
|
if (mFrameBuffers.find(texture) == mFrameBuffers.end())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FrameBuffer* rm = mFrameBuffers[texture];
|
|
|
|
|
mFrameBuffers.erase(texture);
|
|
|
|
|
|
|
|
|
|
glDeleteFramebuffers(1, &rm->FBO);
|
|
|
|
|
delete rm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw Methods
|
|
|
|
|
void OglGraphics::DrawLine(glm::vec2 point1, glm::vec2 point2, Color color, float lineWidth, float angle)
|
|
|
|
|
{
|
|
|
|
|
mShapeShader.MakeActive();
|
|
|
|
|
mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
model = glm::rotate(model, angle, glm::vec3(0.0f, 0.0f, 1.0f));
|
|
|
|
|
mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model));
|
|
|
|
|
mShapeShader.SetUniformf("shapeColor", { color.R, color.G, color.B, color.A });
|
|
|
|
|
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));
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model));
|
|
|
|
|
mShapeShader.SetUniformf("shapeColor", { color.R, color.G, color.B, color.A });
|
|
|
|
|
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));
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model));
|
|
|
|
|
mShapeShader.SetUniformf("shapeColor", { color.R, color.G, color.B, color.A });
|
|
|
|
|
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::DrawFilledPolygon(float* pVerts, int numVerts, Color color, glm::vec2 position, float angle)
|
|
|
|
|
{
|
|
|
|
|
mShapeShader.MakeActive();
|
|
|
|
|
mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
model = glm::translate(model, glm::vec3(position.x, position.y, 0.0f));
|
|
|
|
|
model = glm::rotate(model, angle, glm::vec3(0.0f, 0.0f, 1.0f));
|
|
|
|
|
mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model));
|
|
|
|
|
mShapeShader.SetUniformf("shapeColor", { color.R, color.G, color.B, color.A });
|
|
|
|
|
glBindVertexArray(mRectVAO);
|
|
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, mRectVBO);
|
|
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pVerts), pVerts);
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
glDrawArrays(GL_TRIANGLES, 0, numVerts);
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OglGraphics::DrawFilledBox(Rectangle box, Color color, float angle)
|
|
|
|
|
{
|
|
|
|
|
mShapeShader.MakeActive();
|
|
|
|
|
mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
model = glm::translate(model, glm::vec3(box.X, box.Y, 0.0f));
|
|
|
|
|
model = glm::rotate(model, angle, glm::vec3(0.0f, 0.0f, 1.0f));
|
|
|
|
|
model = glm::scale(model, glm::vec3(box.HalfWidth * 2, box.HalfHeight * 2, 1.0f));
|
|
|
|
|
mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model));
|
|
|
|
|
mShapeShader.SetUniformf("shapeColor", { color.R, color.G, color.B, color.A });
|
|
|
|
|
glBindVertexArray(mRectVAO);
|
|
|
|
|
|
|
|
|
|
GLfloat vertices[6][4] = {
|
|
|
|
|
{ -0.5f, 0.5f, },
|
|
|
|
|
{ 0.5f, -0.5f, },
|
|
|
|
|
{ -0.5f, -0.5f, },
|
|
|
|
|
|
|
|
|
|
{ -0.5f, 0.5f, },
|
|
|
|
|
{ 0.5f, 0.5f, },
|
|
|
|
|
{ 0.5f, -0.5f, }
|
|
|
|
|
};
|
|
|
|
|
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, float angle)
|
|
|
|
|
{
|
|
|
|
|
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, angle);
|
|
|
|
|
|
|
|
|
|
// // bottom left to right
|
|
|
|
|
// DrawLine(glm::vec2(topLeft.x, botRight.y), glm::vec2(botRight.x, botRight.y), color, thickness, angle);
|
|
|
|
|
|
|
|
|
|
// // right side bottom to top
|
|
|
|
|
// DrawLine(glm::vec2(botRight.x, botRight.y), glm::vec2(botRight.x, topLeft.y), color, thickness, angle);
|
|
|
|
|
|
|
|
|
|
// // top right to left
|
|
|
|
|
// DrawLine(glm::vec2(botRight.x, topLeft.y), glm::vec2(topLeft.x, topLeft.y), color, thickness, angle);
|
|
|
|
|
|
|
|
|
|
mShapeShader.MakeActive();
|
|
|
|
|
mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection));
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
model = glm::translate(model, glm::vec3(dimensions.CenterPoint().x, dimensions.CenterPoint().y, 0.0f));
|
|
|
|
|
model = glm::scale(model, glm::vec3(dimensions.HalfWidth * 2, dimensions.HalfHeight * 2, 1.0f));
|
|
|
|
|
model = glm::rotate(model, angle, glm::vec3(0.0f, 0.0f, 1.0f));
|
|
|
|
|
mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model));
|
|
|
|
|
mShapeShader.SetUniformf("shapeColor", { color.R, color.G, color.B, color.A });
|
|
|
|
|
glBindVertexArray(mRectVAO);
|
|
|
|
|
|
|
|
|
|
GLfloat lineVerts[4][4] = {
|
|
|
|
|
{ -0.5f, -0.5f, },
|
|
|
|
|
{ 0.5f, -0.5f },
|
|
|
|
|
|
|
|
|
|
{ 0.5f, 0.5f, },
|
|
|
|
|
{ -0.5f, 0.5f, },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, mRectVBO);
|
|
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(lineVerts), lineVerts);
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
glLineWidth(thickness);
|
|
|
|
|
glDrawArrays(GL_LINE_LOOP, 0, 4);
|
|
|
|
|
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OglGraphics::DrawImage(Image& image, glm::vec2 position, Color color, float angle)
|
|
|
|
|
{
|
|
|
|
|
float half_width = image.GetWidth() / 2;
|
|
|
|
|
float half_height = image.GetHeight() / 2;
|
|
|
|
|
DrawImage(image, Rectangle::MakeFromTopLeft(0, 0, image.GetWidth(), image.GetHeight()),
|
|
|
|
|
Rectangle(position.x + half_width, position.y + half_height, half_width, half_height), color, angle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OglGraphics::DrawImage(Image& image, Rectangle source, Rectangle destination, Color color, float angle)
|
|
|
|
|
{
|
|
|
|
|
glm::mat4 id = glm::mat4(1.0f);
|
|
|
|
|
glm::vec3 pos = glm::vec3(destination.X, destination.Y, 0.0f);
|
|
|
|
|
glm::mat4 trans = glm::translate(id, pos);
|
|
|
|
|
trans = glm::rotate(trans, angle, glm::vec3(0.0f, 0.0f, 1.0f));
|
|
|
|
|
trans = glm::scale(trans, glm::vec3(destination.HalfWidth * 2, destination.HalfHeight * 2, 1.0f));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mImageShader.MakeActive();
|
|
|
|
|
|
|
|
|
|
float xScale = (source.HalfWidth * 2) / image.GetWidth();
|
|
|
|
|
float xOffset = source.left() / image.GetWidth();
|
|
|
|
|
float yScale = (source.HalfHeight * 2) / image.GetHeight();
|
|
|
|
|
float yOffset = source.top() / image.GetHeight();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// * -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.R, color.G, color.B, color.A });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.5f, 0.5f, 0.0f, 0.0f,
|
|
|
|
|
// 0.5f, -0.5f, 1.0f, 1.0f,
|
|
|
|
|
// -0.5f, -0.5f, 0.0f, 1.0f,
|
|
|
|
|
|
|
|
|
|
// -0.5f, 0.5f, 0.0f, 0.0f,
|
|
|
|
|
// 0.5f, 0.5f, 1.0f, 0.0f,
|
|
|
|
|
// 0.5f, -0.5f, 1.0f, 1.0f
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
GLfloat vertices[] = {
|
|
|
|
|
//Pos // Tex
|
|
|
|
|
-0.5f, 0.5f, 0.0f, 1.0f,
|
|
|
|
|
0.5f, -0.5f, 1.0f, 0.0f,
|
|
|
|
|
-0.5f, -0.5f, 0.0f, 0.0f,
|
|
|
|
|
|
|
|
|
|
-0.5f, 0.5f, 0.0f, 1.0f,
|
|
|
|
|
0.5f, 0.5f, 1.0f, 1.0f,
|
|
|
|
|
0.5f, -0.5f, 1.0f, 0.0f
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// NOTE: METHOD IS NO LONGER NEEDED
|
|
|
|
|
|
|
|
|
|
// 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_RGBA, mFBWidth, mFBHeight, 0, GL_RGBA, 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);
|
|
|
|
|
}
|
|
|
|
|
}
|