From 7039b1e26b1aba5a1ef8ee725caf054ba00a6a3e Mon Sep 17 00:00:00 2001 From: Joeyrp Date: Thu, 3 Feb 2022 16:08:42 -0500 Subject: [PATCH] Renderer now supports multiple arbitrarily sized frame buffers --- docs/core.todo | 2 +- src/core/core.cpp | 4 +- src/core/core.h | 2 +- src/graphics/igraphics.h | 11 +- src/graphics/opengl/glGraphics.cpp | 205 ++++++++++++------ src/graphics/opengl/glGraphics.h | 17 +- .../tester/scenes/simpleRenderScene.cpp | 14 +- .../tester/scenes/simpleRenderScene.h | 2 + src/run_modes/tester/tester.cpp | 4 +- 9 files changed, 175 insertions(+), 86 deletions(-) diff --git a/docs/core.todo b/docs/core.todo index f4764a4..0a1c015 100644 --- a/docs/core.todo +++ b/docs/core.todo @@ -24,7 +24,7 @@ Core: ✔ Test rotation of images @done (11/1/2021, 2:11:13 PM) ☐ Fix line rotation @low ✔ Add Roboto-Regular.ttf as an internal font @high @done (11/3/2021, 8:35:51 PM) - ☐ Allow an image size to be passed in for rendering to an image @high + ✔ Allow an image size to be passed in for rendering to an image @high @done (2/3/2022, 4:07:33 PM) GUI: diff --git a/src/core/core.cpp b/src/core/core.cpp index 2c3611d..67ff5c1 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -368,7 +368,7 @@ namespace lunarium } } - OpRes Core::BeginRenderToTexture() + OpRes Core::BeginRenderToTexture(int id) { if (mbMidRender) { @@ -376,7 +376,7 @@ namespace lunarium } mbMidTextureRender = true; - mpGraphics->BeginDraw(RenderTarget::RT_IMAGE); + mpGraphics->BeginDraw(id); return OpRes::OK(); } diff --git a/src/core/core.h b/src/core/core.h index 585d1e2..c2246f5 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -41,7 +41,7 @@ namespace lunarium void RunGameLoop(); - OpRes BeginRenderToTexture(); + OpRes BeginRenderToTexture(int id); Image* EndRenderToTexture(); diff --git a/src/graphics/igraphics.h b/src/graphics/igraphics.h index e101523..b7509a5 100644 --- a/src/graphics/igraphics.h +++ b/src/graphics/igraphics.h @@ -30,16 +30,16 @@ namespace lunarium protected: // Interface methods for use only by the engine friend Core; - IGraphics() : mbIsInit(false), mpFBTexture(nullptr) {} + IGraphics() : mbIsInit(false) {} virtual OpRes Initialize(Window* pWindow, bool enableDebugMessages = true) = 0; virtual void Shutdown() = 0; virtual void ResizeCanvas() = 0; - // If render target is set to image the image will be the same - // size as the window - virtual void BeginDraw(RenderTarget rt = RenderTarget::RT_WINDOW) = 0; + // framebuffer is the ID of the texture/framebuffer to render to + // -1 means draw to the default (most likely the main window) buffer + virtual void BeginDraw(int framebuffer = -1) = 0; virtual Image* EndDraw() = 0; public: // @@ -47,6 +47,8 @@ namespace lunarium virtual void SetClearColor(Color c) = 0; virtual Color GetClearColor() const = 0; + virtual int CreateRenderTexture(int width, int height, int channels) = 0; + // Draw Methods virtual void DrawFilledPolygon(float* pVerts, int numVerts, Color color, glm::vec2 position, float angle = 0.0f) = 0; virtual void DrawLine(glm::vec2 point1, glm::vec2 point2, Color color, float lineWidth, float angle = 0.0f) = 0; @@ -71,7 +73,6 @@ namespace lunarium bool mbIsInit; int mDefaultFont; Color mClearColor; - Image* mpFBTexture; }; } diff --git a/src/graphics/opengl/glGraphics.cpp b/src/graphics/opengl/glGraphics.cpp index b0a0fe5..d3471da 100644 --- a/src/graphics/opengl/glGraphics.cpp +++ b/src/graphics/opengl/glGraphics.cpp @@ -29,6 +29,10 @@ namespace lunarium void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { + if (type == GL_DEBUG_TYPE_ERROR) + { + Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "OPENGL ERROR"); + } Logger::Log(LogCategory::GRAPHICS, LogLevel::OGL_DEBUG, "%s (type: %s, source: %s, severity: %s), message: %s", @@ -60,7 +64,6 @@ namespace lunarium } mpWindow = pWindow; - mpFBTexture = new Image; ResizeCanvas(); // Also calls InitTextureFrameBuffer glEnable(GL_BLEND); @@ -100,41 +103,41 @@ namespace lunarium void OglGraphics::Shutdown() { - // Nothing to clean up at this point + // TODO: Clean up frame buffers } 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); + // NOTE: METHOD IS NO LONGER NEEDED - Logger::Log(LogCategory::GRAPHICS, LogLevel::INFO_VERBOSE, - "glViewport set to %d, %d", mFBWidth, mFBHeight); - - InitTextureFrameBuffer(); } - void OglGraphics::BeginDraw(RenderTarget rt) + void OglGraphics::BeginDraw(int framebuffer) { - mRT = rt; - - int width = 0, height = 0; - mpWindow->GetFramebufferSize(&width, &height); + mActiveFrameBuffer = framebuffer; - if (mFBWidth != width || mFBHeight != height) + if (mActiveFrameBuffer > -1) { - ResizeCanvas(); - } + 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); - if (mRT == RenderTarget::RT_IMAGE) + } + else { - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + 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); @@ -142,27 +145,26 @@ namespace lunarium Image* OglGraphics::EndDraw() { - if (mRT == RenderTarget::RT_IMAGE) + if (mActiveFrameBuffer > -1) { + FrameBuffer* fb = mFrameBuffers[mActiveFrameBuffer]; 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 - // TODO: Preallocate the buffer so we don't call new every frame! - unsigned char* buffer = new unsigned char[bufferSize]; - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*)buffer); - - // FlipImageVertically(buffer, mFBWidth, mFBHeight, 1); - // mpFBTexture->FreeRawData(); - mpFBTexture->SetData(buffer); - mpFBTexture->SetWidth(mFBWidth); - mpFBTexture->SetHeight(mFBHeight); - mpFBTexture->SetFormat(ImageFormat::RGBA); - - // return a copy of the image - return mpFBTexture; + glBindTexture(GL_TEXTURE_2D, fb->Texture.GetGLTextureID()); + + if (fb->Texture.GetFormat() == ImageFormat::RGBA) + { + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*)fb->Buffer); + Logger::Log(LogCategory::GRAPHICS, LogLevel::INFO, "RGBA ID: %d", mActiveFrameBuffer); + } + else if (fb->Texture.GetFormat() == ImageFormat::RGB) + { + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)fb->Buffer); + Logger::Log(LogCategory::GRAPHICS, LogLevel::INFO, "RGB ID: %d", mActiveFrameBuffer); + } + + fb->Texture.SetData(fb->Buffer); + + return &fb->Texture; } else { @@ -188,6 +190,74 @@ namespace lunarium 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; + } + // Draw Methods void OglGraphics::DrawLine(glm::vec2 point1, glm::vec2 point2, Color color, float lineWidth, float angle) { @@ -585,38 +655,41 @@ namespace lunarium void OglGraphics::InitTextureFrameBuffer() { + + // NOTE: METHOD IS NO LONGER NEEDED + // Frame buffer - glGenFramebuffers(1, &mFBO); - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + // 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); + // // 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); + // // 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); + // // 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); + // // 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"); - } + // 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); + // glBindFramebuffer(GL_FRAMEBUFFER, 0); } } \ No newline at end of file diff --git a/src/graphics/opengl/glGraphics.h b/src/graphics/opengl/glGraphics.h index 6801e15..b14b959 100644 --- a/src/graphics/opengl/glGraphics.h +++ b/src/graphics/opengl/glGraphics.h @@ -13,6 +13,7 @@ #include #include "glShader.h" #include "glText.h" +#include #include #include @@ -31,7 +32,7 @@ namespace lunarium virtual void ResizeCanvas(); - virtual void BeginDraw(RenderTarget rt = RenderTarget::RT_WINDOW); + virtual void BeginDraw(int framebuffer = -1); // -1 means draw to the window/default frame buffer /// The image returned by this method is dynamic /// and needs to be cleaned up @@ -42,6 +43,8 @@ namespace lunarium virtual void SetClearColor(Color c); virtual Color GetClearColor() const; + virtual int CreateRenderTexture(int width, int height, int channels); + // Draw Methods virtual void DrawFilledPolygon(float* pVerts, int numVerts, Color color, glm::vec2 position, float angle = 0.0f); virtual void DrawLine(glm::vec2 point1, glm::vec2 point2, Color color, float lineWidth, float angle = 0.0f); @@ -70,9 +73,15 @@ namespace lunarium int mFBHeight; // Objects for rendering to texture - unsigned int mFBO; - unsigned int mRBO; // for depth and stencil - RenderTarget mRT; + struct FrameBuffer + { + Image Texture; + unsigned char* Buffer; + unsigned int FBO; + unsigned int RBO; // for depth and stencil + }; + std::map mFrameBuffers; + int mActiveFrameBuffer; // TEXT glText mText; diff --git a/src/run_modes/tester/scenes/simpleRenderScene.cpp b/src/run_modes/tester/scenes/simpleRenderScene.cpp index d161857..3c81025 100644 --- a/src/run_modes/tester/scenes/simpleRenderScene.cpp +++ b/src/run_modes/tester/scenes/simpleRenderScene.cpp @@ -38,6 +38,10 @@ namespace lunarium mImageSize.Width = 1280; mImageSize.Height = 720; + // Create the render textures + mFrameBufferOne = Core::Graphics().CreateRenderTexture(mImageSize.Width, mImageSize.Height, 4); + mFrameBufferTwo = Core::Graphics().CreateRenderTexture(1024, 1024, 4); + angle = 0.0f; box_angle = 0.0f; @@ -94,7 +98,7 @@ namespace lunarium IGraphics& g = Core::Graphics(); Color prev = g.GetClearColor(); g.SetClearColor(Color(0.0f, 0.0f, 0.0f, 0.0f)); - OpRes result = Core::GetInstance().BeginRenderToTexture(); + OpRes result = Core::GetInstance().BeginRenderToTexture(mFrameBufferTwo); if (Failed(result)) { Logger::Log(mLogCat, LogLevel::WARNING, "Unable to render to texture: %s", result.Description.c_str()); @@ -119,7 +123,7 @@ namespace lunarium mImageSize.Height = Math::ClampI(mImageSize.Height, 180, 720); // Render to texture testing - OpRes result = Core::GetInstance().BeginRenderToTexture(); + OpRes result = Core::GetInstance().BeginRenderToTexture(mFrameBufferOne); if (Failed(result)) { Logger::Log(mLogCat, LogLevel::WARNING, "Unable to render to texture: %s", result.Description.c_str()); @@ -144,10 +148,10 @@ namespace lunarium g->DrawImage(*mpRenderedImage, Rectangle::MakeFromTopLeft(0.0f, 0.0f, (float)mpRenderedImage->GetWidth(), (float)mpRenderedImage->GetHeight()), Rectangle::MakeFromTopLeft(0.0f, 0.0f, (float)mImageSize.Width, (float)mImageSize.Height), Color(1.0f, 1.0f, 1.0f, 1.0f), angle); - // g->DrawBox(Rectangle::MakeFromTopLeft(0.0f, 0.0f, (float)mImageSize.Width, (float)mImageSize.Height), Color(0.0f, 0.0f, 0.0f, 1.0f), 1.0f, angle); + g->DrawBox(Rectangle::MakeFromTopLeft(0.0f, 0.0f, (float)mImageSize.Width, (float)mImageSize.Height), Color(0.0f, 0.0f, 0.0f, 1.0f), 1.0f, angle); - // g->DrawBox(Rectangle(400, 400, 128.0f, 128.0f), Color(0.0f, 1.0f, 0.0f, 1.0f), 2.0f, box_angle); + g->DrawBox(Rectangle(400, 400, 128.0f, 128.0f), Color(0.0f, 1.0f, 0.0f, 1.0f), 2.0f, box_angle); - // g->DrawString((*mGrid[{-2, 0,}])->msg.c_str(), Rectangle::MakeFromTopLeft(200.0f, 500.0f, 500.0f, 200.0f), Color(0.5f, 0.0f, 0.75f, 1.0f)); + g->DrawString((*mGrid[{-2, 0,}])->msg.c_str(), Rectangle::MakeFromTopLeft(200.0f, 500.0f, 500.0f, 200.0f), Color(0.5f, 0.0f, 0.75f, 1.0f)); } } \ No newline at end of file diff --git a/src/run_modes/tester/scenes/simpleRenderScene.h b/src/run_modes/tester/scenes/simpleRenderScene.h index 140e0be..3d36b1b 100644 --- a/src/run_modes/tester/scenes/simpleRenderScene.h +++ b/src/run_modes/tester/scenes/simpleRenderScene.h @@ -31,6 +31,8 @@ namespace lunarium int mTextBoxWidth; Sizei mImageSize; Image* mpRenderedImage; + int mFrameBufferOne; + int mFrameBufferTwo; float angle; float box_angle; diff --git a/src/run_modes/tester/tester.cpp b/src/run_modes/tester/tester.cpp index 3a08dfa..f8ec240 100644 --- a/src/run_modes/tester/tester.cpp +++ b/src/run_modes/tester/tester.cpp @@ -30,9 +30,9 @@ namespace lunarium mLogCat = Logger::RegisterCategory("TESTER"); #if BUILD_NO_EDITOR - Logger::Log(mLogCat, LogLevel::INFO, "BUILDING NO EDITOR!"); + Logger::Log(mLogCat, LogLevel::INFO, "NO EDITOR!"); #else - Logger::Log(mLogCat, LogLevel::INFO, "BUILDING WITH THE EDITOR!"); + Logger::Log(mLogCat, LogLevel::INFO, "EDITOR DETECTED!"); #endif mpScene = new SimpleRenderScene(mLogCat);