Browse Source

Asset+Render: Add Framebuffers

master
Riyyi 4 months ago
parent
commit
538d0b5ce7
  1. 1
      src/inferno/asset/asset-manager.h
  2. 68
      src/inferno/asset/texture.cpp
  3. 50
      src/inferno/asset/texture.h
  4. 13
      src/inferno/render/buffer.cpp
  5. 16
      src/inferno/render/buffer.h
  6. 62
      src/inferno/render/framebuffer.cpp
  7. 43
      src/inferno/render/framebuffer.h

1
src/inferno/asset/asset-manager.h

@ -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)

68
src/inferno/asset/texture.cpp

@ -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

50
src/inferno/asset/texture.h

@ -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

13
src/inferno/render/buffer.cpp

@ -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);
}

16
src/inferno/render/buffer.h

@ -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();

62
src/inferno/render/framebuffer.cpp

@ -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

43
src/inferno/render/framebuffer.h

@ -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

Loading…
Cancel
Save