Asset+Render: Add Framebuffers
This commit is contained in:
@@ -36,6 +36,7 @@ public:
|
||||
virtual bool isTexture() const { return false; }
|
||||
virtual bool isTexture2D() const { return false; }
|
||||
virtual bool isTextureCubemap() const { return false; }
|
||||
virtual bool isTextureFramebuffer() const { return false; }
|
||||
|
||||
protected:
|
||||
Asset(std::string_view path)
|
||||
|
||||
@@ -24,12 +24,20 @@ Texture::~Texture()
|
||||
}
|
||||
|
||||
void Texture::init(uint32_t width, uint32_t height, uint8_t channels)
|
||||
{
|
||||
init(width, height,
|
||||
(channels == 3) ? GL_RGB8 : GL_RGBA8,
|
||||
(channels == 3) ? GL_RGB : GL_RGBA,
|
||||
GL_UNSIGNED_BYTE);
|
||||
}
|
||||
|
||||
void Texture::init(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType)
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
m_internalFormat = (channels == 3) ? GL_RGB8 : GL_RGBA8;
|
||||
m_dataFormat = (channels == 3) ? GL_RGB : GL_RGBA;
|
||||
m_internalFormat = internalFormat;
|
||||
m_dataFormat = dataFormat;
|
||||
m_dataType = dataType;
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
@@ -49,7 +57,7 @@ std::shared_ptr<Texture2D> Texture2D::create(std::string_view path)
|
||||
VERIFY(data, "failed to load image: '{}'", path);
|
||||
|
||||
result->init(width, height, channels);
|
||||
result->create(data);
|
||||
result->createImpl(data);
|
||||
|
||||
// Clean resources
|
||||
stbi_image_free(data);
|
||||
@@ -78,7 +86,7 @@ std::shared_ptr<Texture2D> Texture2D::create(aiTexture* texture)
|
||||
}
|
||||
|
||||
result->init(width, height, channels);
|
||||
result->create(data);
|
||||
result->createImpl(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -99,7 +107,7 @@ void Texture2D::unbind() const
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Texture2D::create(unsigned char* data)
|
||||
void Texture2D::createImpl(unsigned char* data)
|
||||
{
|
||||
m_id = UINT_MAX;
|
||||
|
||||
@@ -121,7 +129,7 @@ void Texture2D::create(unsigned char* data)
|
||||
m_width, m_height, // Image width/height
|
||||
0, // Always 0 (legacy)
|
||||
m_dataFormat, // Texture source format
|
||||
GL_UNSIGNED_BYTE, // Texture source datatype
|
||||
m_dataType, // Texture source datatype
|
||||
data); // Image data
|
||||
|
||||
// Set the texture wrapping / filtering options
|
||||
@@ -143,7 +151,7 @@ std::shared_ptr<TextureCubemap> TextureCubemap::create(std::string_view path)
|
||||
{
|
||||
auto result = std::shared_ptr<TextureCubemap>(new TextureCubemap(path));
|
||||
|
||||
result->create();
|
||||
result->createImpl();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -164,7 +172,7 @@ void TextureCubemap::unbind() const
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
|
||||
void TextureCubemap::create()
|
||||
void TextureCubemap::createImpl()
|
||||
{
|
||||
m_id = UINT_MAX;
|
||||
|
||||
@@ -206,7 +214,7 @@ void TextureCubemap::create()
|
||||
m_width, m_height, // Image width/height
|
||||
0, // Always 0 (legacy)
|
||||
m_dataFormat, // Texture source format
|
||||
GL_UNSIGNED_BYTE, // Texture source datatype
|
||||
m_dataType, // Texture source datatype
|
||||
data); // Image data
|
||||
|
||||
// Clean resources
|
||||
@@ -224,4 +232,44 @@ void TextureCubemap::create()
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
std::shared_ptr<TextureFramebuffer> TextureFramebuffer::create(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType)
|
||||
{
|
||||
auto result = std::shared_ptr<TextureFramebuffer>(new TextureFramebuffer(""));
|
||||
|
||||
result->init(width, height, internalFormat, dataFormat, dataType);
|
||||
result->createImpl();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextureFramebuffer::createImpl()
|
||||
{
|
||||
m_id = UINT_MAX;
|
||||
|
||||
// Create texture object
|
||||
glGenTextures(1, &m_id);
|
||||
|
||||
// Bind texture object
|
||||
glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
|
||||
// Generate texture
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D, // Texture target
|
||||
0, // Midmap level, base starts at level 0
|
||||
m_internalFormat, // Texture format
|
||||
m_width, m_height, // Image width/height
|
||||
0, // Always 0 (legacy)
|
||||
m_dataFormat, // Texture source format
|
||||
m_dataType, // Texture source datatype
|
||||
NULL); // Image data
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// Unbind texture object
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
} // namespace Inferno
|
||||
|
||||
+38
-12
@@ -10,20 +10,20 @@
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <string_view>
|
||||
|
||||
#include "glad/glad.h"
|
||||
|
||||
#include "inferno/asset/asset-manager.h"
|
||||
|
||||
struct aiTexture;
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
class Texture2D;
|
||||
class TextureCubemap;
|
||||
|
||||
class Texture : public Asset {
|
||||
public:
|
||||
virtual ~Texture();
|
||||
|
||||
void init(uint32_t width, uint32_t height, uint8_t channels);
|
||||
void init(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType);
|
||||
|
||||
virtual void bind(uint32_t unit = 0) const = 0;
|
||||
virtual void unbind() const = 0;
|
||||
@@ -33,12 +33,11 @@ public:
|
||||
uint32_t id() const { return m_id; }
|
||||
uint32_t internalFormat() const { return m_internalFormat; }
|
||||
uint32_t dataFormat() const { return m_dataFormat; }
|
||||
uint32_t dataType() const { return m_dataType; }
|
||||
|
||||
virtual bool is2D() const { return false; }
|
||||
virtual bool isCubemap() const { return false; }
|
||||
|
||||
friend Texture2D;
|
||||
friend TextureCubemap;
|
||||
virtual bool isTexture2D() const override { return false; }
|
||||
virtual bool isTextureCubemap() const override { return false; }
|
||||
virtual bool isTextureFramebuffer() const override { return false; }
|
||||
|
||||
protected:
|
||||
Texture(std::string_view path)
|
||||
@@ -52,6 +51,7 @@ protected:
|
||||
uint32_t m_id { 0 };
|
||||
uint32_t m_internalFormat { 0 };
|
||||
uint32_t m_dataFormat { 0 };
|
||||
uint32_t m_dataType { GL_UNSIGNED_BYTE };
|
||||
|
||||
private:
|
||||
virtual bool isTexture() const override { return true; }
|
||||
@@ -76,10 +76,9 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool isTexture2D() const override { return true; }
|
||||
void createImpl(unsigned char* data);
|
||||
|
||||
private:
|
||||
void create(unsigned char* data);
|
||||
virtual bool isTexture2D() const override { return true; }
|
||||
};
|
||||
|
||||
// -------------------------------------
|
||||
@@ -100,10 +99,34 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
void createImpl();
|
||||
|
||||
virtual bool isTextureCubemap() const override { return true; }
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class TextureFramebuffer final : public Texture {
|
||||
public:
|
||||
virtual ~TextureFramebuffer() = default;
|
||||
|
||||
// Factory function
|
||||
static std::shared_ptr<TextureFramebuffer> create(
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType = GL_UNSIGNED_BYTE);
|
||||
|
||||
virtual void bind(uint32_t) const override {}
|
||||
virtual void unbind() const override {}
|
||||
|
||||
private:
|
||||
void create();
|
||||
TextureFramebuffer(std::string_view path)
|
||||
: Texture(path)
|
||||
{
|
||||
}
|
||||
|
||||
void createImpl();
|
||||
|
||||
virtual bool isTextureFramebuffer() const override { return true; }
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
@@ -117,6 +140,9 @@ inline bool Asset::fastIs<Texture2D>() const { return isTexture2D(); }
|
||||
|
||||
template<>
|
||||
inline bool Asset::fastIs<TextureCubemap>() const { return isTextureCubemap(); }
|
||||
|
||||
template<>
|
||||
inline bool Asset::fastIs<TextureFramebuffer>() const { return isTextureFramebuffer(); }
|
||||
// clang-format on
|
||||
|
||||
} // namespace Inferno
|
||||
|
||||
@@ -246,18 +246,13 @@ void BufferLayout::calculateOffsetsAndStride()
|
||||
// -----------------------------------------
|
||||
|
||||
VertexBuffer::VertexBuffer(size_t size)
|
||||
: VertexBuffer(size, nullptr)
|
||||
{
|
||||
glGenBuffers(1, &m_id);
|
||||
bind();
|
||||
|
||||
// Reserve data on the GPU
|
||||
glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
VertexBuffer::VertexBuffer(float* vertices, size_t size)
|
||||
VertexBuffer::VertexBuffer(size_t size, float* vertices)
|
||||
{
|
||||
m_id = UINT_MAX;
|
||||
glGenBuffers(1, &m_id);
|
||||
bind();
|
||||
|
||||
@@ -297,6 +292,7 @@ void VertexBuffer::uploadData(const void* data, uint32_t size)
|
||||
IndexBuffer::IndexBuffer(uint32_t* indices, size_t size)
|
||||
: m_count(size / sizeof(uint32_t))
|
||||
{
|
||||
m_id = UINT_MAX;
|
||||
glCreateBuffers(1, &m_id);
|
||||
bind();
|
||||
|
||||
@@ -335,6 +331,7 @@ void IndexBuffer::uploadData(const void* data, uint32_t size)
|
||||
|
||||
VertexArray::VertexArray()
|
||||
{
|
||||
m_id = UINT_MAX;
|
||||
glCreateVertexArrays(1, &m_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
* Copyright (C) 2022,2024 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
@@ -31,9 +31,10 @@ enum class BufferElementType {
|
||||
// -----------------------------------------
|
||||
|
||||
// Describes one element of the BufferLayout
|
||||
class BufferElement {
|
||||
class BufferElement final {
|
||||
public:
|
||||
BufferElement(BufferElementType type, std::string name, bool normalized = false);
|
||||
~BufferElement() = default;
|
||||
|
||||
uint32_t getTypeSize() const;
|
||||
uint32_t getTypeCount() const;
|
||||
@@ -65,10 +66,11 @@ private:
|
||||
// -----------------------------------------
|
||||
|
||||
// Layout that describes raw vertex data
|
||||
class BufferLayout {
|
||||
class BufferLayout final {
|
||||
public:
|
||||
BufferLayout() {}
|
||||
BufferLayout(const std::initializer_list<BufferElement>& elements);
|
||||
~BufferLayout() = default;
|
||||
|
||||
const std::vector<BufferElement>& getElements() const { return m_elements; }
|
||||
uint32_t getStride() const { return m_stride; }
|
||||
@@ -90,10 +92,10 @@ private:
|
||||
// -----------------------------------------
|
||||
|
||||
// GPU memory which holds raw vertex data
|
||||
class VertexBuffer { // Vertex Buffer Object, VBO
|
||||
class VertexBuffer final { // Vertex Buffer Object, VBO
|
||||
public:
|
||||
VertexBuffer(size_t size);
|
||||
VertexBuffer(float* vertices, size_t size);
|
||||
VertexBuffer(size_t size, float* vertices);
|
||||
~VertexBuffer();
|
||||
|
||||
void bind() const;
|
||||
@@ -113,7 +115,7 @@ private:
|
||||
// -----------------------------------------
|
||||
|
||||
// Vertices order of rendering
|
||||
class IndexBuffer { // Element Buffer Object, EBO
|
||||
class IndexBuffer final { // Element Buffer Object, EBO
|
||||
public:
|
||||
IndexBuffer(uint32_t* indices, size_t size);
|
||||
~IndexBuffer();
|
||||
@@ -133,7 +135,7 @@ private:
|
||||
// -----------------------------------------
|
||||
|
||||
// Array that holds the vertex attributes configuration
|
||||
class VertexArray { // Vertex Array Object, VAO
|
||||
class VertexArray final { // Vertex Array Object, VAO
|
||||
public:
|
||||
VertexArray();
|
||||
~VertexArray();
|
||||
|
||||
@@ -1,21 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
* Copyright (C) 2022,2024 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstdint> // int8_t
|
||||
|
||||
#include "glad/glad.h"
|
||||
#include "ruc/format/log.h"
|
||||
#include "ruc/meta/assert.h"
|
||||
|
||||
#include "inferno/asset/texture.h"
|
||||
#include "inferno/render/framebuffer.h"
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
Framebuffer::Framebuffer()
|
||||
Framebuffer::Framebuffer(const Properties& init)
|
||||
{
|
||||
VERIFY(static_cast<int8_t>(init.type) != 0,
|
||||
"malformed framebuffer type: {}", init.type);
|
||||
|
||||
m_id = UINT_MAX;
|
||||
glGenFramebuffers(1, &m_id);
|
||||
bind();
|
||||
|
||||
if (init.type & Type::Color) {
|
||||
ruc::error("color!");
|
||||
// Set color attachment 0 out of 32
|
||||
m_textures[0] = TextureFramebuffer::create(init.width, init.height, GL_RGB, GL_RGB);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textures[0]->id(), 0);
|
||||
}
|
||||
|
||||
// This combined texture is required for older GPUs
|
||||
if (init.type & Type::Depth && init.type & Stencil) {
|
||||
ruc::error("both!");
|
||||
m_textures[3] = TextureFramebuffer::create(init.width, init.height, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[3]->id(), 0);
|
||||
}
|
||||
else if (init.type & Type::Depth) {
|
||||
ruc::error("depth!");
|
||||
m_textures[1] = TextureFramebuffer::create(init.width, init.height, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_textures[1]->id(), 0);
|
||||
}
|
||||
else if (init.type & Type::Stencil) {
|
||||
ruc::error("stencil!");
|
||||
m_textures[2] = TextureFramebuffer::create(init.width, init.height, GL_STENCIL_INDEX, GL_STENCIL_INDEX);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_textures[2]->id(), 0);
|
||||
}
|
||||
|
||||
check();
|
||||
unbind();
|
||||
}
|
||||
|
||||
Framebuffer::~Framebuffer()
|
||||
{
|
||||
glDeleteFramebuffers(1, &m_id);
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
bool Framebuffer::check() const
|
||||
{
|
||||
VERIFY(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
|
||||
"malformed framebuffer: {:#x}", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Framebuffer::bind() const
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_id);
|
||||
}
|
||||
|
||||
void Framebuffer::unbind() const
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
} // namespace Inferno
|
||||
|
||||
@@ -1,17 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
* Copyright (C) 2022,2024 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // int8_t
|
||||
#include <memory> // std::shared_ptr
|
||||
|
||||
#include "inferno/asset/texture.h"
|
||||
#include "ruc/meta/core.h"
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
class Framebuffer {
|
||||
class Framebuffer final { // Frame Buffer Object, FBO
|
||||
public:
|
||||
Framebuffer();
|
||||
virtual ~Framebuffer();
|
||||
enum Type : int8_t {
|
||||
None = 0,
|
||||
Color = BIT(0),
|
||||
Depth = BIT(1),
|
||||
Stencil = BIT(2),
|
||||
};
|
||||
|
||||
struct Properties {
|
||||
Type type { None };
|
||||
uint32_t width { 1280 };
|
||||
uint32_t height { 720 };
|
||||
};
|
||||
|
||||
Framebuffer(const Properties& init);
|
||||
~Framebuffer();
|
||||
|
||||
bool check() const;
|
||||
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
|
||||
private:
|
||||
uint32_t m_id { 0 };
|
||||
std::shared_ptr<TextureFramebuffer> m_textures[4] { nullptr, nullptr, nullptr, nullptr };
|
||||
};
|
||||
|
||||
// Make bitwise OR '|' return the enum type instead of int
|
||||
inline Framebuffer::Type operator|(Framebuffer::Type lhs, Framebuffer::Type rhs)
|
||||
{
|
||||
return static_cast<Framebuffer::Type>(
|
||||
static_cast<int8_t>(lhs) | static_cast<int8_t>(rhs));
|
||||
}
|
||||
|
||||
} // namespace Inferno
|
||||
|
||||
Reference in New Issue
Block a user