graphics refactor & new organization

This commit is contained in:
Noel Berry
2020-12-26 14:44:48 -08:00
parent 2c96f0813e
commit 15608d8309
28 changed files with 742 additions and 645 deletions

View File

@ -14,10 +14,11 @@
#include <blah/drawing/spritefont.h>
#include <blah/drawing/subtexture.h>
#include <blah/graphics/blend.h>
#include <blah/graphics/framebuffer.h>
#include <blah/graphics/graphics.h>
#include <blah/graphics/material.h>
#include <blah/graphics/mesh.h>
#include <blah/graphics/renderpass.h>
#include <blah/graphics/shader.h>
#include <blah/graphics/texture.h>

View File

@ -11,9 +11,9 @@ using namespace Blah;
namespace
{
static Config app_config;
static bool app_is_running = false;
static bool app_is_exiting = false;
Config app_config;
bool app_is_running = false;
bool app_is_exiting = false;
}
Config::Config()
@ -230,4 +230,65 @@ float App::content_scale()
void App::fullscreen(bool enabled)
{
PlatformBackend::set_fullscreen(enabled);
}
}
Renderer App::renderer()
{
return GraphicsBackend::renderer();
}
const RendererFeatures& Blah::App::renderer_features()
{
return GraphicsBackend::features();
}
namespace
{
class BackBuffer final : public FrameBuffer
{
Attachments empty_attachments;
TextureRef empty_texture;
virtual Attachments& attachments() override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any attachments");
return empty_attachments;
}
virtual const Attachments& attachments() const override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any attachments");
return empty_attachments;
}
virtual TextureRef& attachment(int index) override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any attachments");
return empty_texture;
}
virtual const TextureRef& attachment(int index) const override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any attachments");
return empty_texture;
}
virtual int width() const override
{
return App::draw_width();
}
virtual int height() const override
{
return App::draw_height();
}
virtual void clear(Color color) override
{
GraphicsBackend::clear_backbuffer(color);
}
};
}
extern const FrameBufferRef App::backbuffer = FrameBufferRef(new BackBuffer());

View File

@ -1,4 +1,5 @@
#pragma once
#include <blah/graphics/framebuffer.h>
namespace Blah
{
@ -23,6 +24,22 @@ namespace Blah
Config();
};
enum class Renderer
{
None = -1,
OpenGL,
D3D11,
Metal,
Count
};
struct RendererFeatures
{
bool instancing = false;
bool origin_bottom_left = false;
int max_texture_size = 0;
};
namespace App
{
// Runs the application
@ -60,5 +77,14 @@ namespace Blah
// Toggles fullscreen
void fullscreen(bool enabled);
// Returns the Rendering API in use
Renderer renderer();
// Retrieves the Renderer Features
const RendererFeatures& renderer_features();
// Reference to the window's back buffer
extern const FrameBufferRef backbuffer;
}
}

View File

@ -268,7 +268,7 @@ void Batch::set_texture(const TextureRef& texture)
if (m_batch.texture != texture)
{
m_batch.texture = texture;
m_batch.flip_vertically = Graphics::info()->origin_bottom_left && texture && texture->is_framebuffer();
m_batch.flip_vertically = App::renderer_features().origin_bottom_left && texture && texture->is_framebuffer();
}
}
@ -293,53 +293,53 @@ void Batch::render(const FrameBufferRef& target, const Mat4x4& matrix)
{
if (!m_mesh)
{
m_mesh = Graphics::create_mesh();
m_mesh = Mesh::create();
m_mesh->vertex_format(attributes, 4, sizeof(Vertex));
}
if (!m_default_shader)
m_default_shader = Graphics::create_shader(&data);
m_default_shader = Shader::create(&data);
if (!m_default_material)
m_default_material = Graphics::create_material(m_default_shader);
m_default_material = Material::create(m_default_shader);
}
// upload data
m_mesh->index_data(m_indices.data(), m_indices.size());
m_mesh->vertex_data(m_vertices.data(), m_vertices.size());
RenderCall call;
call.target = target;
call.mesh = m_mesh;
call.has_viewport = false;
call.viewport = Rect();
call.instance_count = 0;
call.depth = Compare::None;
call.cull = Cull::None;
RenderPass pass;
pass.target = target;
pass.mesh = m_mesh;
pass.has_viewport = false;
pass.viewport = Rect();
pass.instance_count = 0;
pass.depth = Compare::None;
pass.cull = Cull::None;
for (auto& b : m_batches)
render_single_batch(call, b, matrix);
render_single_batch(pass, b, matrix);
if (m_batch.elements > 0)
render_single_batch(call, m_batch, matrix);
render_single_batch(pass, m_batch, matrix);
}
void Batch::render_single_batch(RenderCall& call, const DrawBatch& b, const Mat4x4& matrix)
void Batch::render_single_batch(RenderPass& pass, const DrawBatch& b, const Mat4x4& matrix)
{
call.material = b.material;
if (!call.material)
call.material = m_default_material;
pass.material = b.material;
if (!pass.material)
pass.material = m_default_material;
call.material->set_texture(texture_uniform, b.texture, 0);
call.material->set_value(matrix_uniform, &matrix.m11, 16);
pass.material->set_texture(texture_uniform, b.texture, 0);
pass.material->set_value(matrix_uniform, &matrix.m11, 16);
call.blend = b.blend;
call.has_scissor = b.scissor.w >= 0 && b.scissor.h >= 0;
call.scissor = b.scissor;
call.index_start = (int64_t)b.offset * 3;
call.index_count = (int64_t)b.elements * 3;
pass.blend = b.blend;
pass.has_scissor = b.scissor.w >= 0 && b.scissor.h >= 0;
pass.scissor = b.scissor;
pass.index_start = (int64_t)b.offset * 3;
pass.index_count = (int64_t)b.elements * 3;
Graphics::render(call);
pass.perform();
}
void Batch::clear()

View File

@ -1,5 +1,4 @@
#pragma once
#include <blah/graphics/graphics.h>
#include <blah/containers/str.h>
#include <blah/math/vec2.h>
#include <blah/math/rect.h>
@ -9,6 +8,9 @@
#include <blah/drawing/subtexture.h>
#include <blah/drawing/spritefont.h>
#include <blah/containers/vector.h>
#include <blah/graphics/blend.h>
#include <blah/graphics/renderpass.h>
#include <blah/app.h>
namespace Blah
{
@ -114,7 +116,7 @@ namespace Blah
void set_texture(const TextureRef& texture);
// Draws the batch to the given target
void render(const FrameBufferRef& target);
void render(const FrameBufferRef& target = App::backbuffer);
// Draws the batch to the given target, with the provided matrix
void render(const FrameBufferRef& target, const Mat4x4& matrix);
@ -224,6 +226,6 @@ namespace Blah
Vector<int> m_layer_stack;
Vector<DrawBatch> m_batches;
void render_single_batch(RenderCall& call, const DrawBatch& b, const Mat4x4& matrix);
void render_single_batch(RenderPass& pass, const DrawBatch& b, const Mat4x4& matrix);
};
}

View File

@ -1,7 +1,6 @@
#include <blah/drawing/spritefont.h>
#include <blah/images/font.h>
#include <blah/images/packer.h>
#include <blah/graphics/graphics.h>
#include <blah/log.h>
using namespace Blah;
@ -199,7 +198,7 @@ void SpriteFont::build(const Font& font, float size, const uint32_t* charset)
packer.pack();
for (auto& it : packer.pages)
m_atlas.push_back(Graphics::create_texture(it));
m_atlas.push_back(Texture::create(it));
// add character subtextures
for (auto& it : packer.entries)

View File

@ -6,7 +6,7 @@ using namespace Blah;
Subtexture::Subtexture() {}
Subtexture::Subtexture(const TextureRef& texture)
: Subtexture(texture, Rect(0, 0, texture->width(), texture->height())) {}
: Subtexture(texture, Rect(0, 0, (float)texture->width(), (float)texture->height())) {}
Subtexture::Subtexture(const TextureRef& texture, Rect source)
: Subtexture(texture, source, Rect(0, 0, source.w, source.h)) {}
@ -48,8 +48,8 @@ void Subtexture::crop_info(const Rect& clip, Rect* dest_source, Rect* dest_frame
{
*dest_source = (clip + source.top_left() + frame.top_left()).overlap_rect(source);
dest_frame->x = Calc::min(0, frame.x + clip.x);
dest_frame->y = Calc::min(0, frame.y + clip.y);
dest_frame->x = Calc::min(0.0f, frame.x + clip.x);
dest_frame->y = Calc::min(0.0f, frame.y + clip.y);
dest_frame->w = clip.w;
dest_frame->h = clip.h;
}

View File

@ -0,0 +1,25 @@
#include <blah/graphics/blend.h>
using namespace Blah;
const BlendMode BlendMode::Normal = BlendMode(
BlendOp::Add,
BlendFactor::One,
BlendFactor::OneMinusSrcAlpha,
BlendOp::Add,
BlendFactor::One,
BlendFactor::OneMinusSrcAlpha,
BlendMask::RGBA,
0xffffffff
);
const BlendMode BlendMode::Subtract = BlendMode(
BlendOp::ReverseSubtract,
BlendFactor::One,
BlendFactor::One,
BlendOp::Add,
BlendFactor::One,
BlendFactor::One,
BlendMask::RGBA,
0xffffffff
);

View File

@ -0,0 +1,96 @@
#pragma once
#include <inttypes.h>
namespace Blah
{
enum class BlendOp
{
Add,
Subtract,
ReverseSubtract,
Min,
Max
};
enum class BlendFactor
{
Zero,
One,
SrcColor,
OneMinusSrcColor,
DstColor,
OneMinusDstColor,
SrcAlpha,
OneMinusSrcAlpha,
DstAlpha,
OneMinusDstAlpha,
ConstantColor,
OneMinusConstantColor,
ConstantAlpha,
OneMinusConstantAlpha,
SrcAlphaSaturate,
Src1Color,
OneMinusSrc1Color,
Src1Alpha,
OneMinusSrc1Alpha
};
enum class BlendMask
{
None = 0,
Red = 1,
Green = 2,
Blue = 4,
Alpha = 8,
RGB = Red | Green | Blue,
RGBA = Red | Green | Blue | Alpha,
};
struct BlendMode
{
BlendOp colorOp;
BlendFactor colorSrc;
BlendFactor colorDst;
BlendOp alphaOp;
BlendFactor alphaSrc;
BlendFactor alphaDst;
BlendMask mask;
uint32_t rgba;
BlendMode() = default;
BlendMode(BlendOp op, BlendFactor src, BlendFactor dst)
{
colorOp = op;
colorSrc = src;
colorDst = dst;
alphaOp = op;
alphaSrc = src;
alphaDst = dst;
mask = BlendMask::RGBA;
rgba = 0xffffffff;
}
BlendMode(BlendOp rgbOp, BlendFactor rgbSrc, BlendFactor rgbDst, BlendOp aOp, BlendFactor aSrc, BlendFactor aDst, BlendMask blendMask, uint32_t blendColor)
{
colorOp = rgbOp;
colorSrc = rgbSrc;
colorDst = rgbDst;
alphaOp = aOp;
alphaSrc = aSrc;
alphaDst = aDst;
mask = blendMask;
rgba = blendColor;
}
inline bool operator==(const BlendMode& rhs) const {
return colorOp == rhs.colorOp && colorSrc == rhs.colorSrc && colorDst == rhs.colorDst &&
alphaOp == rhs.alphaOp && alphaSrc == rhs.alphaSrc && alphaDst == rhs.alphaDst &&
mask == rhs.mask && rgba == rhs.rgba;
}
inline bool operator!=(const BlendMode& rhs) const { return !(*this == rhs); }
static const BlendMode Normal;
static const BlendMode Subtract;
};
}

View File

@ -0,0 +1,38 @@
#include <blah/graphics/framebuffer.h>
#include <blah/internal/graphics_backend.h>
using namespace Blah;
FrameBufferRef FrameBuffer::create(int width, int height)
{
static const TextureFormat attachment = TextureFormat::RGBA;
return create(width, height, &attachment, 1);
}
FrameBufferRef FrameBuffer::create(int width, int height, const TextureFormat* attachments, int attachment_count)
{
BLAH_ASSERT(width > 0 && height > 0, "FrameBuffer width and height must be larger than 0");
BLAH_ASSERT(attachment_count <= BLAH_ATTACHMENTS, "Exceeded maximum attachment count");
BLAH_ASSERT(attachment_count > 0, "At least one attachment must be provided");
int color_count = 0;
int depth_count = 0;
for (int i = 0; i < attachment_count; i++)
{
BLAH_ASSERT((int)attachments[i] > (int)TextureFormat::None && (int)attachments[i] < (int)TextureFormat::Count, "Invalid texture format");
if (attachments[i] == TextureFormat::DepthStencil)
depth_count++;
else
color_count++;
}
BLAH_ASSERT(depth_count <= 1, "FrameBuffer can only have 1 Depth/Stencil Texture");
BLAH_ASSERT(color_count <= BLAH_ATTACHMENTS - 1, "Exceeded maximum Color attachment count");
if (color_count <= BLAH_ATTACHMENTS - 1 && depth_count <= 1 && width > 0 && height > 0)
return GraphicsBackend::create_framebuffer(width, height, attachments, attachment_count);
return FrameBufferRef();
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <blah/graphics/texture.h>
#include <blah/containers/stackvector.h>
#include <blah/math/color.h>
#include <memory>
// 4 color attachments + 1 depth/stencil
@ -10,9 +11,21 @@ namespace Blah
{
typedef StackVector<TextureRef, BLAH_ATTACHMENTS> Attachments;
class FrameBuffer;
typedef std::shared_ptr<FrameBuffer> FrameBufferRef;
class FrameBuffer
{
public:
// Creates a new FrameBuffer with a single Color attachment
// If the FrameBuffer creation fails, it will return an invalid FrameBufferRef.
static FrameBufferRef create(int width, int height);
// Creates a new FrameBuffer with the given Texture Attachments. You must provide at least one Attachment.
// If the FrameBuffer creation fails, it will return an invalid FrameBufferRef.
static FrameBufferRef create(int width, int height, const TextureFormat* attachments, int attachmentCount);
virtual ~FrameBuffer() = default;
// Gets the list of Attachments from the FrameBuffer
@ -32,7 +45,9 @@ namespace Blah
// Gets the height of the FrameBuffer
virtual int height() const = 0;
// Clears the FrameBuffer
virtual void clear(Color color) = 0;
};
typedef std::shared_ptr<FrameBuffer> FrameBufferRef;
}

View File

@ -1,255 +0,0 @@
#include <blah/graphics/graphics.h>
#include <blah/graphics/texture.h>
#include <blah/graphics/framebuffer.h>
#include <blah/graphics/mesh.h>
#include <blah/graphics/material.h>
#include <blah/graphics/shader.h>
#include <blah/log.h>
#include <blah/app.h>
#include <blah/internal/graphics_backend.h>
#include <blah/images/image.h>
#include <string.h>
using namespace Blah;
const BlendMode BlendMode::Normal = BlendMode(
BlendOp::Add,
BlendFactor::One,
BlendFactor::OneMinusSrcAlpha,
BlendOp::Add,
BlendFactor::One,
BlendFactor::OneMinusSrcAlpha,
BlendMask::RGBA,
0xffffffff
);
const BlendMode BlendMode::Subtract = BlendMode(
BlendOp::ReverseSubtract,
BlendFactor::One,
BlendFactor::One,
BlendOp::Add,
BlendFactor::One,
BlendFactor::One,
BlendMask::RGBA,
0xffffffff
);
const GraphicsInfo* Graphics::info()
{
return GraphicsBackend::info();
}
GraphicsRenderer Graphics::renderer()
{
return GraphicsBackend::renderer();
}
TextureRef Graphics::create_texture(const Image& image)
{
auto tex = create_texture(image.width, image.height, TextureFormat::RGBA);
tex->set_data((unsigned char*)image.pixels);
return tex;
}
TextureRef Graphics::create_texture(int width, int height, unsigned char* rgba)
{
auto tex = create_texture(width, height, TextureFormat::RGBA);
tex->set_data(rgba);
return tex;
}
TextureRef Graphics::create_texture(int width, int height, TextureFormat format)
{
BLAH_ASSERT(width > 0 && height > 0, "Texture width and height must be larger than 0");
BLAH_ASSERT((int)format > (int)TextureFormat::None && (int)format < (int)TextureFormat::Count, "Invalid texture format");
if (width > 0 && height > 0)
return GraphicsBackend::create_texture(width, height, TextureFilter::Linear, TextureWrap::Repeat, TextureWrap::Repeat, format);
return TextureRef();
}
TextureRef Graphics::create_texture(Stream& stream)
{
Image img = Image(stream);
if (img.pixels != nullptr && img.width > 0 && img.height > 0)
{
auto tex = create_texture(img.width, img.height, TextureFormat::RGBA);
tex->set_data((unsigned char*)img.pixels);
return tex;
}
return TextureRef();
}
TextureRef Graphics::create_texture(const char* file)
{
Image img = Image(file);
if (img.pixels != nullptr)
{
auto tex = create_texture(img.width, img.height, TextureFormat::RGBA);
tex->set_data((unsigned char*)img.pixels);
return tex;
}
return TextureRef();
}
FrameBufferRef Graphics::create_framebuffer(int width, int height)
{
static const TextureFormat attachment = TextureFormat::RGBA;
return create_framebuffer(width, height, &attachment, 1);
}
FrameBufferRef Graphics::create_framebuffer(int width, int height, const TextureFormat* attachments, int attachment_count)
{
BLAH_ASSERT(width > 0 && height > 0, "FrameBuffer width and height must be larger than 0");
BLAH_ASSERT(attachment_count <= BLAH_ATTACHMENTS, "Exceeded maximum attachment count");
BLAH_ASSERT(attachment_count > 0, "At least one attachment must be provided");
int color_count = 0;
int depth_count = 0;
for (int i = 0; i < attachment_count; i++)
{
BLAH_ASSERT((int)attachments[i] > (int)TextureFormat::None && (int)attachments[i] < (int)TextureFormat::Count, "Invalid texture format");
if (attachments[i] == TextureFormat::DepthStencil)
depth_count++;
else
color_count++;
}
BLAH_ASSERT(depth_count <= 1, "FrameBuffer can only have 1 Depth/Stencil Texture");
BLAH_ASSERT(color_count <= BLAH_ATTACHMENTS - 1, "Exceeded maximum Color attachment count");
if (color_count <= BLAH_ATTACHMENTS - 1 && depth_count <= 1 && width > 0 && height > 0)
return GraphicsBackend::create_framebuffer(width, height, attachments, attachment_count);
return FrameBufferRef();
}
ShaderRef Graphics::create_shader(const ShaderData* data)
{
return GraphicsBackend::create_shader(data);
}
MaterialRef Graphics::create_material(const ShaderRef& shader)
{
BLAH_ASSERT(shader, "The provided shader is invalid");
if (shader)
return MaterialRef(new Material(shader));
return MaterialRef();
}
MeshRef Graphics::create_mesh()
{
return GraphicsBackend::create_mesh();
}
RenderCall::RenderCall()
{
blend = BlendMode::Normal;
target = Graphics::backbuffer;
mesh = MeshRef();
material = MaterialRef();
has_viewport = false;
has_scissor = false;
viewport = Rect();
scissor = Rect();
index_start = 0;
index_count = 0;
instance_count = 0;
depth = Compare::None;
cull = Cull::None;
}
void Graphics::render(const RenderCall& render_call)
{
// Validate Material
if (!render_call.material)
{
Log::warn("Trying to draw with an invalid Material");
return;
}
// Validate Shader
if (!render_call.material->shader())
{
Log::warn("Trying to draw with an invalid Shader");
return;
}
// Validate Mesh
if (!render_call.mesh)
{
Log::warn("Trying to draw with an invalid Mesh");
return;
}
// copy call
RenderCall call = render_call;
// Validate Index Count
int64_t index_count = call.mesh->index_count();
if (call.index_start + call.index_count > index_count)
{
Log::warn(
"Trying to draw more indices than exist in the index buffer (%i-%i / %i)",
call.index_start,
call.index_start + call.index_count,
index_count);
if (call.index_start > call.index_count)
return;
call.index_count = call.index_count - call.index_start;
}
// Validate Instance Count
int64_t instance_count = call.mesh->instance_count();
if (call.instance_count > instance_count)
{
Log::warn(
"Trying to draw more instances than exist in the index buffer (%i / %i)",
call.instance_count,
instance_count);
call.instance_count = instance_count;
}
// get the total drawable size
Point draw_size;
if (!call.target)
draw_size = Point(App::draw_width(), App::draw_height());
else
draw_size = Point(call.target->width(), call.target->height());
// Validate Viewport
if (!call.has_viewport)
{
call.viewport.x = 0;
call.viewport.y = 0;
call.viewport.w = (float)draw_size.x;
call.viewport.h = (float)draw_size.y;
}
else
{
call.viewport = call.viewport.overlap_rect(Rect(0, 0, draw_size.x, draw_size.y));
}
// Validate Scissor
if (call.has_scissor)
call.scissor = call.scissor.overlap_rect(Rect(0, 0, draw_size.x, draw_size.y));
GraphicsBackend::render(call);
}
void Graphics::clear(const FrameBufferRef& target, uint32_t rgba)
{
GraphicsBackend::clear(target, rgba);
}

View File

@ -1,246 +0,0 @@
#pragma once
#include <inttypes.h>
#include <blah/math/rect.h>
#include <blah/containers/str.h>
#include <blah/graphics/texture.h>
#include <blah/graphics/framebuffer.h>
#include <blah/graphics/mesh.h>
#include <blah/graphics/shader.h>
#include <blah/graphics/material.h>
#include <memory>
namespace Blah
{
class Stream;
class Image;
enum class Compare
{
None,
Always,
Never,
Less,
Equal,
LessOrEqual,
Greater,
NotEqual,
GreatorOrEqual
};
enum class Cull
{
None = 0,
Front = 1,
Back = 2,
Both = 3
};
enum class BlendOp
{
Add,
Subtract,
ReverseSubtract,
Min,
Max
};
enum class BlendFactor
{
Zero,
One,
SrcColor,
OneMinusSrcColor,
DstColor,
OneMinusDstColor,
SrcAlpha,
OneMinusSrcAlpha,
DstAlpha,
OneMinusDstAlpha,
ConstantColor,
OneMinusConstantColor,
ConstantAlpha,
OneMinusConstantAlpha,
SrcAlphaSaturate,
Src1Color,
OneMinusSrc1Color,
Src1Alpha,
OneMinusSrc1Alpha
};
enum class BlendMask
{
None = 0,
Red = 1,
Green = 2,
Blue = 4,
Alpha = 8,
RGB = Red | Green | Blue,
RGBA = Red | Green | Blue | Alpha,
};
struct BlendMode
{
BlendOp colorOp;
BlendFactor colorSrc;
BlendFactor colorDst;
BlendOp alphaOp;
BlendFactor alphaSrc;
BlendFactor alphaDst;
BlendMask mask;
uint32_t rgba;
BlendMode() = default;
BlendMode(BlendOp op, BlendFactor src, BlendFactor dst)
{
colorOp = op;
colorSrc = src;
colorDst = dst;
alphaOp = op;
alphaSrc = src;
alphaDst = dst;
mask = BlendMask::RGBA;
rgba = 0xffffffff;
}
BlendMode(BlendOp rgbOp, BlendFactor rgbSrc, BlendFactor rgbDst, BlendOp aOp, BlendFactor aSrc, BlendFactor aDst, BlendMask blendMask, uint32_t blendColor)
{
colorOp = rgbOp;
colorSrc = rgbSrc;
colorDst = rgbDst;
alphaOp = aOp;
alphaSrc = aSrc;
alphaDst = aDst;
mask = blendMask;
rgba = blendColor;
}
inline bool operator==(const BlendMode& rhs) const {
return colorOp == rhs.colorOp && colorSrc == rhs.colorSrc && colorDst == rhs.colorDst &&
alphaOp == rhs.alphaOp && alphaSrc == rhs.alphaSrc && alphaDst == rhs.alphaDst &&
mask == rhs.mask && rgba == rhs.rgba;
}
inline bool operator!=(const BlendMode& rhs) const { return !(*this == rhs); }
static const BlendMode Normal;
static const BlendMode Subtract;
};
struct RenderCall
{
// Framebuffer to draw to
FrameBufferRef target;
// Mesh to draw with
MeshRef mesh;
// Material to draw with
MaterialRef material;
// Whether the Render Call should use a specific viewport
bool has_viewport;
// Whether the Render Call should use a scissor rectangle
bool has_scissor;
// The viewport (only used if hasViewport is true)
Rect viewport;
// The scissor rectangle (only used if hasScissor is true)
Rect scissor;
// First index in the Mesh to draw from
int64_t index_start;
// Total amount of indices to draw from the Mesh
int64_t index_count;
// Total amount of instances to draw from the Mesh
int64_t instance_count;
// Depth Compare Function
Compare depth;
// Cull Mode
Cull cull;
// Blend Mode
BlendMode blend;
// Initializes a default Render Call
RenderCall();
};
enum class GraphicsRenderer
{
None = -1,
OpenGL,
D3D11,
Metal,
Count
};
struct GraphicsInfo
{
bool instancing = false;
bool origin_bottom_left = false;
int max_texture_size = 0;
};
namespace Graphics
{
const FrameBufferRef backbuffer;
// Gets graphics information from the graphics device
const GraphicsInfo* info();
// Gets the Renderer implementation type
GraphicsRenderer renderer();
// Creates a new Texture.
// If the Texture creation fails, it will return an invalid TextureRef.
TextureRef create_texture(const Image& image);
// Creates a new Texture.
// If the Texture creation fails, it will return an invalid TextureRef.
TextureRef create_texture(int width, int height, unsigned char* rgba);
// Creates a new Texture.
// If the Texture creation fails, it will return an invalid TextureRef.
TextureRef create_texture(int width, int height, TextureFormat format);
// Creates a new Texture from a Stream.
// If the Texture creation fails, it will return an invalid TextureRef.
TextureRef create_texture(Stream& stream);
// Creates a new Texture from a File.
// If the Texture creation fails, it will return an invalid TextureRef.
TextureRef create_texture(const char* file);
// Creates a new FrameBuffer with a single Color attachment
// If the FrameBuffer creation fails, it will return an invalid FrameBufferRef.
FrameBufferRef create_framebuffer(int width, int height);
// Creates a new FrameBuffer with the given Texture Attachments. You must provide at least one Attachment.
// If the FrameBuffer creation fails, it will return an invalid FrameBufferRef.
FrameBufferRef create_framebuffer(int width, int height, const TextureFormat* attachments, int attachmentCount);
// Creates a Shader with the given Shader Data.
// If the Shader creation fails, it will return an invalid ShaderRef.
ShaderRef create_shader(const ShaderData* data);
// Creates a new Material from the given Shader.
// If the Shader is invalid, it will return an invalid MaterialRef.
MaterialRef create_material(const ShaderRef& shader);
// Creates a new Mesh.
// If the Mesh creation fails, it will return an invalid Mesh.
MeshRef create_mesh();
// Submits and Flushes the given render call to the graphics device.
void render(const RenderCall& render_call);
// Clears the given FrameBuffer to the given color.
void clear(const FrameBufferRef& target, uint32_t rgba);
}
}

View File

@ -26,14 +26,24 @@ namespace
}
}
MaterialRef Material::create(const ShaderRef& shader)
{
BLAH_ASSERT(shader, "The provided shader is invalid");
if (shader)
return MaterialRef(new Material(shader));
return MaterialRef();
}
Material::Material(const ShaderRef& shader)
{
BLAH_ASSERT(shader, "Material is being created with an invalid shader");
m_shader = shader;
auto& uniforms = shader->uniforms();
Vector<size_t> float_offsets;
size_t float_size = 0;
Vector<int> float_offsets;
int float_size = 0;
for (auto& uniform : uniforms)
{

View File

@ -6,15 +6,23 @@
namespace Blah
{
class Material;
typedef std::shared_ptr<Material> MaterialRef;
class Material
{
public:
private:
Material(const ShaderRef& shader);
Material(const Material& src) = delete;
Material(Material&& src) = delete;
Material& operator=(const Material& src) = delete;
Material& operator=(Material&& src) = delete;
public:
// Creates a new Material from the given Shader.
// If the Shader is invalid, it will return an invalid MaterialRef.
static MaterialRef create(const ShaderRef& shader);
// Returns the Shader assigned to the Material.
const ShaderRef shader() const;
@ -52,6 +60,4 @@ namespace Blah
Vector<float*> m_floats;
Vector<float> m_data;
};
typedef std::shared_ptr<Material> MaterialRef;
}

View File

@ -1,7 +1,13 @@
#include "mesh.h"
#include <blah/internal/graphics_backend.h>
using namespace Blah;
MeshRef Mesh::create()
{
return GraphicsBackend::create_mesh();
}
void Mesh::vertex_format(const VertexAttribute* attributes, int attribute_count, int stride)
{
if (stride < 0)

View File

@ -44,9 +44,16 @@ namespace Blah
bool normalized;
};
class Mesh;
typedef std::shared_ptr<Mesh> MeshRef;
class Mesh
{
public:
// Creates a new Mesh.
// If the Mesh creation fails, it will return an invalid Mesh.
static MeshRef create();
virtual ~Mesh() = default;
// Sets the Vertex Format of the Mesh
@ -79,6 +86,4 @@ namespace Blah
virtual void vertex_format_internal(const VertexAttribute* attributes, int count, int stride) = 0;
virtual void instance_format_internal(const VertexAttribute* attributes, int count, int stride) = 0;
};
typedef std::shared_ptr<Mesh> MeshRef;
}

View File

@ -0,0 +1,111 @@
#include <blah/graphics/renderpass.h>
#include <blah/internal/graphics_backend.h>
#include <blah/log.h>
using namespace Blah;
RenderPass::RenderPass()
{
blend = BlendMode::Normal;
target = App::backbuffer;
mesh = MeshRef();
material = MaterialRef();
has_viewport = false;
has_scissor = false;
viewport = Rect();
scissor = Rect();
index_start = 0;
index_count = 0;
instance_count = 0;
depth = Compare::None;
cull = Cull::None;
}
void RenderPass::perform()
{
// Validate Material
if (!material)
{
Log::warn("Trying to draw with an invalid Material");
return;
}
// Validate Shader
if (!material->shader())
{
Log::warn("Trying to draw with an invalid Shader");
return;
}
// Validate Mesh
if (!mesh)
{
Log::warn("Trying to draw with an invalid Mesh");
return;
}
// copy call
RenderPass pass = *this;
// Validate Backbuffer
if (!pass.target)
{
pass.target = App::backbuffer;
Log::warn("Trying to draw with an invalid Target; falling back to Back Buffer");
}
// Validate Index Count
int64_t index_count = pass.mesh->index_count();
if (pass.index_start + pass.index_count > index_count)
{
Log::warn(
"Trying to draw more indices than exist in the index buffer (%i-%i / %i)",
pass.index_start,
pass.index_start + pass.index_count,
index_count);
if (pass.index_start > pass.index_count)
return;
pass.index_count = pass.index_count - pass.index_start;
}
// Validate Instance Count
int64_t instance_count = pass.mesh->instance_count();
if (pass.instance_count > instance_count)
{
Log::warn(
"Trying to draw more instances than exist in the index buffer (%i / %i)",
pass.instance_count,
instance_count);
pass.instance_count = instance_count;
}
// get the total drawable size
Vec2 draw_size;
if (!pass.target)
draw_size = Vec2(App::draw_width(), App::draw_height());
else
draw_size = Vec2(pass.target->width(), pass.target->height());
// Validate Viewport
if (!pass.has_viewport)
{
pass.viewport.x = 0;
pass.viewport.y = 0;
pass.viewport.w = draw_size.x;
pass.viewport.h = draw_size.y;
}
else
{
pass.viewport = pass.viewport.overlap_rect(Rect(0, 0, draw_size.x, draw_size.y));
}
// Validate Scissor
if (pass.has_scissor)
pass.scissor = pass.scissor.overlap_rect(Rect(0, 0, draw_size.x, draw_size.y));
// perform render
GraphicsBackend::render(pass);
}

View File

@ -0,0 +1,82 @@
#pragma once
#include <inttypes.h>
#include <blah/math/rect.h>
#include <blah/containers/str.h>
#include <blah/graphics/texture.h>
#include <blah/graphics/framebuffer.h>
#include <blah/graphics/mesh.h>
#include <blah/graphics/shader.h>
#include <blah/graphics/material.h>
#include <blah/graphics/blend.h>
namespace Blah
{
enum class Compare
{
None,
Always,
Never,
Less,
Equal,
LessOrEqual,
Greater,
NotEqual,
GreatorOrEqual
};
enum class Cull
{
None = 0,
Front = 1,
Back = 2,
Both = 3
};
struct RenderPass
{
// Framebuffer to draw to
FrameBufferRef target;
// Mesh to draw with
MeshRef mesh;
// Material to draw with
MaterialRef material;
// Whether the RenderPass should use a specific viewport
bool has_viewport;
// Whether the RenderPass should use a scissor rectangle
bool has_scissor;
// The viewport (only used if hasViewport is true)
Rect viewport;
// The scissor rectangle (only used if hasScissor is true)
Rect scissor;
// First index in the Mesh to draw from
int64_t index_start;
// Total amount of indices to draw from the Mesh
int64_t index_count;
// Total amount of instances to draw from the Mesh
int64_t instance_count;
// Depth Compare Function
Compare depth;
// Cull Mode
Cull cull;
// Blend Mode
BlendMode blend;
// Initializes a default RenderPass
RenderPass();
// Performs the render
void perform();
};
}

View File

@ -0,0 +1,9 @@
#include <blah/graphics/shader.h>
#include <blah/internal/graphics_backend.h>
using namespace Blah;
ShaderRef Shader::create(const ShaderData* data)
{
return GraphicsBackend::create_shader(data);
}

View File

@ -30,9 +30,17 @@ namespace Blah
const char* fragment;
};
class Shader;
typedef std::shared_ptr<Shader> ShaderRef;
class Shader
{
public:
// Creates a Shader with the given Shader Data.
// If the Shader creation fails, it will return an invalid ShaderRef.
static ShaderRef create(const ShaderData* data);
virtual ~Shader() = default;
// Gets a list of Shader Uniforms from Shader
@ -42,5 +50,4 @@ namespace Blah
virtual const Vector<UniformInfo>& uniforms() const = 0;
};
typedef std::shared_ptr<Shader> ShaderRef;
}

View File

@ -0,0 +1,60 @@
#include <blah/graphics/texture.h>
#include <blah/images/image.h>
#include <blah/streams/stream.h>
#include <blah/internal/graphics_backend.h>
#include <blah/log.h>
using namespace Blah;
TextureRef Texture::create(const Image& image)
{
auto tex = create(image.width, image.height, TextureFormat::RGBA);
tex->set_data((unsigned char*)image.pixels);
return tex;
}
TextureRef Texture::create(int width, int height, unsigned char* rgba)
{
auto tex = create(width, height, TextureFormat::RGBA);
tex->set_data(rgba);
return tex;
}
TextureRef Texture::create(int width, int height, TextureFormat format)
{
BLAH_ASSERT(width > 0 && height > 0, "Texture width and height must be larger than 0");
BLAH_ASSERT((int)format > (int)TextureFormat::None && (int)format < (int)TextureFormat::Count, "Invalid texture format");
if (width > 0 && height > 0)
return GraphicsBackend::create_texture(width, height, TextureFilter::Linear, TextureWrap::Repeat, TextureWrap::Repeat, format);
return TextureRef();
}
TextureRef Texture::create(Stream& stream)
{
Image img = Image(stream);
if (img.pixels != nullptr && img.width > 0 && img.height > 0)
{
auto tex = create(img.width, img.height, TextureFormat::RGBA);
tex->set_data((unsigned char*)img.pixels);
return tex;
}
return TextureRef();
}
TextureRef Texture::create(const char* file)
{
Image img = Image(file);
if (img.pixels != nullptr)
{
auto tex = create(img.width, img.height, TextureFormat::RGBA);
tex->set_data((unsigned char*)img.pixels);
return tex;
}
return TextureRef();
}

View File

@ -28,9 +28,35 @@ namespace Blah
Count
};
class Image;
class Stream;
class Texture;
typedef std::shared_ptr<Texture> TextureRef;
class Texture
{
public:
// Creates a new Texture.
// If the Texture creation fails, it will return an invalid TextureRef.
static TextureRef create(const Image& image);
// Creates a new Texture.
// If the Texture creation fails, it will return an invalid TextureRef.
static TextureRef create(int width, int height, unsigned char* rgba);
// Creates a new Texture.
// If the Texture creation fails, it will return an invalid TextureRef.
static TextureRef create(int width, int height, TextureFormat format);
// Creates a new Texture from a Stream.
// If the Texture creation fails, it will return an invalid TextureRef.
static TextureRef create(Stream& stream);
// Creates a new Texture from a File.
// If the Texture creation fails, it will return an invalid TextureRef.
static TextureRef create(const char* file);
virtual ~Texture() = default;
// gets the width of the texture
@ -70,6 +96,4 @@ namespace Blah
// Returns true if the Texture is part of a FrameBuffer
virtual bool is_framebuffer() const = 0;
};
typedef std::shared_ptr<Texture> TextureRef;
}