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

@ -17,17 +17,21 @@ add_library(blah
public/blah/time.cpp public/blah/time.cpp
public/blah/time.h public/blah/time.h
public/blah/graphics/graphics.h public/blah/graphics/blend.h
public/blah/graphics/graphics.cpp public/blah/graphics/blend.cpp
public/blah/graphics/texture.h
public/blah/graphics/framebuffer.h public/blah/graphics/framebuffer.h
public/blah/graphics/shader.h public/blah/graphics/framebuffer.cpp
public/blah/graphics/mesh.h
public/blah/graphics/mesh.cpp
public/blah/graphics/material.h public/blah/graphics/material.h
public/blah/graphics/material.cpp public/blah/graphics/material.cpp
public/blah/graphics/mesh.h
public/blah/graphics/mesh.cpp
public/blah/graphics/renderpass.h
public/blah/graphics/renderpass.cpp
public/blah/graphics/shader.h
public/blah/graphics/shader.cpp
public/blah/graphics/texture.h
public/blah/graphics/texture.cpp
public/blah/input/input.h
public/blah/input/input.cpp public/blah/input/input.cpp
public/blah/input/virtual_stick.cpp public/blah/input/virtual_stick.cpp
public/blah/input/virtual_stick.h public/blah/input/virtual_stick.h
@ -94,15 +98,15 @@ add_library(blah
public/blah/streams/stream.cpp public/blah/streams/stream.cpp
public/blah/streams/stream.h public/blah/streams/stream.h
private/blah/third_party/stb_image.h
private/blah/third_party/stb_image_write.h
private/blah/third_party/stb_truetype.h
private/blah/internal/graphics_backend.h private/blah/internal/graphics_backend.h
private/blah/internal/graphics_backend_gl.cpp private/blah/internal/graphics_backend_gl.cpp
private/blah/internal/input_backend.h private/blah/internal/input_backend.h
private/blah/internal/platform_backend.h private/blah/internal/platform_backend.h
private/blah/internal/platform_backend_sdl2.cpp private/blah/internal/platform_backend_sdl2.cpp
)
private/blah/third_party/stb_image.h
private/blah/third_party/stb_image_write.h
private/blah/third_party/stb_truetype.h)
target_include_directories(blah target_include_directories(blah
PUBLIC PUBLIC

View File

@ -6,8 +6,8 @@ this will likely see breaking changes.
#### building #### building
- Requires C++17 and CMake - Requires C++17 and CMake
- The [SDL2 platform backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/platform_backend_sdl2.cpp) can be enabled in CMake with `BLAH_USE_SDL2`, and setting `SDL2_INCLUDE_DIRS` and `SDL2_LIBRARIES` - The [SDL2 platform backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/platform_backend_sdl2.cpp) can be enabled in CMake with `SDL2_ENABLED`, and setting `SDL2_INCLUDE_DIRS` and `SDL2_LIBRARIES`
- The [OpenGL graphics backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/graphics_backend_gl.cpp) can be enabled in CMake with `BLAH_USE_OPENGL`. - The [OpenGL graphics backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/graphics_backend_gl.cpp) can be enabled in CMake with `OPENGL_ENABLED`.
- Other backends can be added by implementing the [Platform Backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/platform_backend.h) or [Graphics Backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/graphics_backend.h). - Other backends can be added by implementing the [Platform Backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/platform_backend.h) or [Graphics Backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/graphics_backend.h).
#### notes #### notes
@ -26,17 +26,17 @@ Batch batch;
void render() void render()
{ {
Graphics::clear(Graphics::backbuffer, 0x00000000); App::backbuffer->clear(Color::black);
Vec2 center = Vec2(App::draw_width(), App::draw_height()) / 2; auto center = Vec2(App::backbuffer->width(), App::backbuffer->height()) / 2;
float rotation = Time::elapsed * Calc::TAU; auto rotation = Time::elapsed * Calc::TAU;
Mat3x2 transform = Mat3x2::create_transform(center, Vec2::zero, Vec2::one, rotation); auto transform = Mat3x2::create_transform(center, Vec2::zero, Vec2::one, rotation);
batch.push_matrix(transform); batch.push_matrix(transform);
batch.rect(Rect(-32, -32, 64, 64), 0xff0000); batch.rect(Rect(-32, -32, 64, 64), Color::red);
batch.pop_matrix(); batch.pop_matrix();
batch.render(Graphics::backbuffer); batch.render();
batch.clear(); batch.clear();
} }

View File

@ -1,5 +1,12 @@
#pragma once #pragma once
#include <blah/graphics/graphics.h> #include <blah/app.h>
#include <blah/graphics/renderpass.h>
#include <blah/graphics/texture.h>
#include <blah/graphics/framebuffer.h>
#include <blah/graphics/shader.h>
#include <blah/graphics/mesh.h>
#include <blah/graphics/material.h>
#include <blah/math/color.h>
namespace Blah namespace Blah
{ {
@ -14,10 +21,10 @@ namespace Blah
void shutdown(); void shutdown();
// Returns info about the renderer // Returns info about the renderer
const GraphicsInfo* info(); const RendererFeatures& features();
// Returns the renderer type // Returns the renderer type
GraphicsRenderer renderer(); Renderer renderer();
// Called once per frame // Called once per frame
void frame(); void frame();
@ -29,10 +36,10 @@ namespace Blah
void after_render(); void after_render();
// Performs a draw call // Performs a draw call
void render(const RenderCall& call); void render(const RenderPass& pass);
// Clears a buffer // Clears the backbuffer
void clear(const FrameBufferRef& target, uint32_t rgba); void clear_backbuffer(Color color);
// Creates a new Texture. // Creates a new Texture.
// if the Texture is invalid, this should return an empty reference. // if the Texture is invalid, this should return an empty reference.

View File

@ -1,6 +1,5 @@
#ifdef BLAH_USE_OPENGL #ifdef BLAH_USE_OPENGL
#include <blah/graphics/graphics.h>
#include <blah/internal/graphics_backend.h> #include <blah/internal/graphics_backend.h>
#include <blah/internal/platform_backend.h> #include <blah/internal/platform_backend.h>
#include <blah/log.h> #include <blah/log.h>
@ -360,7 +359,7 @@ namespace Blah
int max_samples; int max_samples;
int max_texture_image_units; int max_texture_image_units;
int max_texture_size; int max_texture_size;
GraphicsInfo info; RendererFeatures features;
}; };
// static state // static state
@ -691,7 +690,7 @@ namespace Blah
for (int i = 0; i < attachmentCount; i++) for (int i = 0; i < attachmentCount; i++)
{ {
auto tex = Graphics::create_texture(width, height, attachments[i]); auto tex = Texture::create(width, height, attachments[i]);
auto gltex = ((OpenGL_Texture*)tex.get()); auto gltex = ((OpenGL_Texture*)tex.get());
gltex->framebuffer_parent = true; gltex->framebuffer_parent = true;
@ -751,6 +750,20 @@ namespace Blah
{ {
return m_height; return m_height;
} }
virtual void clear(Color color) override
{
auto rgba = color.to_rgba();
unsigned char r = rgba >> 24;
unsigned char g = rgba >> 16;
unsigned char b = rgba >> 8;
unsigned char a = rgba;
gl.BindFramebuffer(GL_FRAMEBUFFER, m_id);
gl.Disable(GL_SCISSOR_TEST);
gl.ClearColor(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
gl.Clear(GL_COLOR_BUFFER_BIT);
}
}; };
class OpenGL_Shader : public Shader class OpenGL_Shader : public Shader
@ -1106,16 +1119,16 @@ namespace Blah
gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1); gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
// assign info // assign info
gl.info.instancing = true; gl.features.instancing = true;
gl.info.origin_bottom_left = true; gl.features.origin_bottom_left = true;
gl.info.max_texture_size = gl.max_texture_size; gl.features.max_texture_size = gl.max_texture_size;
return true; return true;
} }
GraphicsRenderer GraphicsBackend::renderer() Renderer GraphicsBackend::renderer()
{ {
return GraphicsRenderer::OpenGL; return Renderer::OpenGL;
} }
void GraphicsBackend::shutdown() void GraphicsBackend::shutdown()
@ -1124,9 +1137,9 @@ namespace Blah
gl.context = nullptr; gl.context = nullptr;
} }
const GraphicsInfo* GraphicsBackend::info() const RendererFeatures& GraphicsBackend::features()
{ {
return &gl.info; return gl.features;
} }
void GraphicsBackend::frame() {} void GraphicsBackend::frame() {}
@ -1185,27 +1198,27 @@ namespace Blah
return MeshRef(resource); return MeshRef(resource);
} }
void GraphicsBackend::render(const RenderCall& call) void GraphicsBackend::render(const RenderPass& pass)
{ {
// Bind the Target // Bind the Target
Point size; Point size;
if (!call.target) if (pass.target == App::backbuffer)
{ {
gl.BindFramebuffer(GL_FRAMEBUFFER, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
size.x = App::draw_width(); size.x = App::draw_width();
size.y = App::draw_height(); size.y = App::draw_height();
} }
else else if (pass.target)
{ {
auto framebuffer = (OpenGL_FrameBuffer*)call.target.get(); auto framebuffer = (OpenGL_FrameBuffer*)pass.target.get();
gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id()); gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id());
size.x = call.target->width(); size.x = pass.target->width();
size.y = call.target->height(); size.y = pass.target->height();
} }
auto shader_ref = call.material->shader(); auto shader_ref = pass.material->shader();
auto shader = (OpenGL_Shader*)shader_ref.get(); auto shader = (OpenGL_Shader*)shader_ref.get();
auto mesh = (OpenGL_Mesh*)call.mesh.get(); auto mesh = (OpenGL_Mesh*)pass.mesh.get();
// Use the Shader // Use the Shader
// TODO: I don't love how material values are assigned or set here // TODO: I don't love how material values are assigned or set here
@ -1227,7 +1240,7 @@ namespace Blah
{ {
for (int n = 0; n < uniform.array_length; n++) for (int n = 0; n < uniform.array_length; n++)
{ {
auto tex = call.material->get_texture(i, n); auto tex = pass.material->get_texture(i, n);
gl.ActiveTexture(GL_TEXTURE0 + texture_slot); gl.ActiveTexture(GL_TEXTURE0 + texture_slot);
@ -1249,59 +1262,59 @@ namespace Blah
// Float // Float
else if (uniform.type == UniformType::Float) else if (uniform.type == UniformType::Float)
{ {
gl.Uniform1fv(location, (GLint)uniform.array_length, (const GLfloat*)call.material->get_value(i)); gl.Uniform1fv(location, (GLint)uniform.array_length, (const GLfloat*)pass.material->get_value(i));
} }
// Float2 // Float2
else if (uniform.type == UniformType::Float2) else if (uniform.type == UniformType::Float2)
{ {
gl.Uniform2fv(location, (GLint)uniform.array_length, (const GLfloat*)call.material->get_value(i)); gl.Uniform2fv(location, (GLint)uniform.array_length, (const GLfloat*)pass.material->get_value(i));
} }
// Float3 // Float3
else if (uniform.type == UniformType::Float3) else if (uniform.type == UniformType::Float3)
{ {
gl.Uniform3fv(location, (GLint)uniform.array_length, (const GLfloat*)call.material->get_value(i)); gl.Uniform3fv(location, (GLint)uniform.array_length, (const GLfloat*)pass.material->get_value(i));
} }
// Float4 // Float4
else if (uniform.type == UniformType::Float4) else if (uniform.type == UniformType::Float4)
{ {
gl.Uniform4fv(location, (GLint)uniform.array_length, (const GLfloat*)call.material->get_value(i)); gl.Uniform4fv(location, (GLint)uniform.array_length, (const GLfloat*)pass.material->get_value(i));
} }
// Matrix3x2 // Matrix3x2
else if (uniform.type == UniformType::Mat3x2) else if (uniform.type == UniformType::Mat3x2)
{ {
gl.UniformMatrix3x2fv(location, (GLint)uniform.array_length, 0, (const GLfloat*)call.material->get_value(i)); gl.UniformMatrix3x2fv(location, (GLint)uniform.array_length, 0, (const GLfloat*)pass.material->get_value(i));
} }
// Matrix4x4 // Matrix4x4
else if (uniform.type == UniformType::Mat4x4) else if (uniform.type == UniformType::Mat4x4)
{ {
gl.UniformMatrix4fv(location, (GLint)uniform.array_length, 0, (const GLfloat*)call.material->get_value(i)); gl.UniformMatrix4fv(location, (GLint)uniform.array_length, 0, (const GLfloat*)pass.material->get_value(i));
} }
} }
} }
// Blend Mode // Blend Mode
{ {
GLenum colorOp = gl_get_blend_func(call.blend.colorOp); GLenum colorOp = gl_get_blend_func(pass.blend.colorOp);
GLenum alphaOp = gl_get_blend_func(call.blend.alphaOp); GLenum alphaOp = gl_get_blend_func(pass.blend.alphaOp);
GLenum colorSrc = gl_get_blend_factor(call.blend.colorSrc); GLenum colorSrc = gl_get_blend_factor(pass.blend.colorSrc);
GLenum colorDst = gl_get_blend_factor(call.blend.colorDst); GLenum colorDst = gl_get_blend_factor(pass.blend.colorDst);
GLenum alphaSrc = gl_get_blend_factor(call.blend.alphaSrc); GLenum alphaSrc = gl_get_blend_factor(pass.blend.alphaSrc);
GLenum alphaDst = gl_get_blend_factor(call.blend.alphaDst); GLenum alphaDst = gl_get_blend_factor(pass.blend.alphaDst);
gl.Enable(GL_BLEND); gl.Enable(GL_BLEND);
gl.BlendEquationSeparate(colorOp, alphaOp); gl.BlendEquationSeparate(colorOp, alphaOp);
gl.BlendFuncSeparate(colorSrc, colorDst, alphaSrc, alphaDst); gl.BlendFuncSeparate(colorSrc, colorDst, alphaSrc, alphaDst);
gl.ColorMask( gl.ColorMask(
((int)call.blend.mask & (int)BlendMask::Red), ((int)pass.blend.mask & (int)BlendMask::Red),
((int)call.blend.mask & (int)BlendMask::Green), ((int)pass.blend.mask & (int)BlendMask::Green),
((int)call.blend.mask & (int)BlendMask::Blue), ((int)pass.blend.mask & (int)BlendMask::Blue),
((int)call.blend.mask & (int)BlendMask::Alpha)); ((int)pass.blend.mask & (int)BlendMask::Alpha));
unsigned char r = call.blend.rgba >> 24; unsigned char r = pass.blend.rgba >> 24;
unsigned char g = call.blend.rgba >> 16; unsigned char g = pass.blend.rgba >> 16;
unsigned char b = call.blend.rgba >> 8; unsigned char b = pass.blend.rgba >> 8;
unsigned char a = call.blend.rgba; unsigned char a = pass.blend.rgba;
gl.BlendColor( gl.BlendColor(
r / 255.0f, r / 255.0f,
@ -1312,7 +1325,7 @@ namespace Blah
// Depth Function // Depth Function
{ {
if (call.depth == Compare::None) if (pass.depth == Compare::None)
{ {
gl.Disable(GL_DEPTH_TEST); gl.Disable(GL_DEPTH_TEST);
} }
@ -1320,7 +1333,7 @@ namespace Blah
{ {
gl.Enable(GL_DEPTH_TEST); gl.Enable(GL_DEPTH_TEST);
switch (call.depth) switch (pass.depth)
{ {
case Compare::None: break; case Compare::None: break;
case Compare::Always: case Compare::Always:
@ -1353,7 +1366,7 @@ namespace Blah
// Cull Mode // Cull Mode
{ {
if (call.cull == Cull::None) if (pass.cull == Cull::None)
{ {
gl.Disable(GL_CULL_FACE); gl.Disable(GL_CULL_FACE);
} }
@ -1361,9 +1374,9 @@ namespace Blah
{ {
gl.Enable(GL_CULL_FACE); gl.Enable(GL_CULL_FACE);
if (call.cull == Cull::Back) if (pass.cull == Cull::Back)
gl.CullFace(GL_BACK); gl.CullFace(GL_BACK);
else if (call.cull == Cull::Front) else if (pass.cull == Cull::Front)
gl.CullFace(GL_FRONT); gl.CullFace(GL_FRONT);
else else
gl.CullFace(GL_FRONT_AND_BACK); gl.CullFace(GL_FRONT_AND_BACK);
@ -1372,7 +1385,7 @@ namespace Blah
// Viewport // Viewport
{ {
Rect viewport = call.viewport; Rect viewport = pass.viewport;
viewport.y = size.y - viewport.y - viewport.h; viewport.y = size.y - viewport.y - viewport.h;
gl.Viewport((GLint)viewport.x, (GLint)viewport.y, (GLint)viewport.w, (GLint)viewport.h); gl.Viewport((GLint)viewport.x, (GLint)viewport.y, (GLint)viewport.w, (GLint)viewport.h);
@ -1380,13 +1393,13 @@ namespace Blah
// Scissor // Scissor
{ {
if (!call.has_scissor) if (!pass.has_scissor)
{ {
gl.Disable(GL_SCISSOR_TEST); gl.Disable(GL_SCISSOR_TEST);
} }
else else
{ {
Rect scissor = call.scissor; Rect scissor = pass.scissor;
scissor.y = size.y - scissor.y - scissor.h; scissor.y = size.y - scissor.y - scissor.h;
if (scissor.w < 0) if (scissor.w < 0)
@ -1403,46 +1416,37 @@ namespace Blah
{ {
gl.BindVertexArray(mesh->gl_id()); gl.BindVertexArray(mesh->gl_id());
if (call.instance_count > 0) if (pass.instance_count > 0)
{ {
gl.DrawElementsInstanced( gl.DrawElementsInstanced(
GL_TRIANGLES, GL_TRIANGLES,
(GLint)(call.index_count), (GLint)(pass.index_count),
GL_UNSIGNED_INT, GL_UNSIGNED_INT,
(void*)(sizeof(int) * call.index_start), (void*)(sizeof(int) * pass.index_start),
(GLint)call.instance_count); (GLint)pass.instance_count);
} }
else else
{ {
gl.DrawElements( gl.DrawElements(
GL_TRIANGLES, GL_TRIANGLES,
(GLint)(call.index_count), (GLint)(pass.index_count),
GL_UNSIGNED_INT, GL_UNSIGNED_INT,
(void*)(sizeof(int) * call.index_start)); (void*)(sizeof(int) * pass.index_start));
} }
gl.BindVertexArray(0); gl.BindVertexArray(0);
} }
} }
void GraphicsBackend::clear(const FrameBufferRef& target, uint32_t rgba) void GraphicsBackend::clear_backbuffer(Color color)
{ {
if (!target) auto rgba = color.to_rgba();
{
gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
}
else
{
auto framebuffer = (OpenGL_FrameBuffer*)target.get();
if (framebuffer != nullptr)
gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id());
}
unsigned char r = rgba >> 24; unsigned char r = rgba >> 24;
unsigned char g = rgba >> 16; unsigned char g = rgba >> 16;
unsigned char b = rgba >> 8; unsigned char b = rgba >> 8;
unsigned char a = rgba; unsigned char a = rgba;
gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.Disable(GL_SCISSOR_TEST); gl.Disable(GL_SCISSOR_TEST);
gl.ClearColor(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); gl.ClearColor(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
gl.Clear(GL_COLOR_BUFFER_BIT); gl.Clear(GL_COLOR_BUFFER_BIT);

View File

@ -76,7 +76,7 @@ bool PlatformBackend::init(const Config* config)
int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
// GL Attributes // GL Attributes
if (GraphicsBackend::renderer() == GraphicsRenderer::OpenGL) if (App::renderer() == Renderer::OpenGL)
{ {
flags |= SDL_WINDOW_OPENGL; flags |= SDL_WINDOW_OPENGL;
@ -135,7 +135,7 @@ bool PlatformBackend::init(const Config* config)
void PlatformBackend::ready() void PlatformBackend::ready()
{ {
// enable V-Sync // enable V-Sync
if (GraphicsBackend::renderer() == GraphicsRenderer::OpenGL) if (App::renderer() == Renderer::OpenGL)
SDL_GL_SetSwapInterval(1); SDL_GL_SetSwapInterval(1);
} }
@ -342,7 +342,7 @@ void PlatformBackend::sleep(int milliseconds)
void PlatformBackend::present() void PlatformBackend::present()
{ {
if (GraphicsBackend::renderer() == GraphicsRenderer::OpenGL) if (App::renderer() == Renderer::OpenGL)
{ {
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
} }
@ -396,7 +396,7 @@ void PlatformBackend::set_size(int width, int height)
void PlatformBackend::get_draw_size(int* width, int* height) void PlatformBackend::get_draw_size(int* width, int* height)
{ {
if (GraphicsBackend::renderer() == GraphicsRenderer::OpenGL) if (App::renderer() == Renderer::OpenGL)
{ {
SDL_GL_GetDrawableSize(window, width, height); SDL_GL_GetDrawableSize(window, width, height);
} }

View File

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

View File

@ -11,9 +11,9 @@ using namespace Blah;
namespace namespace
{ {
static Config app_config; Config app_config;
static bool app_is_running = false; bool app_is_running = false;
static bool app_is_exiting = false; bool app_is_exiting = false;
} }
Config::Config() Config::Config()
@ -231,3 +231,64 @@ void App::fullscreen(bool enabled)
{ {
PlatformBackend::set_fullscreen(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 #pragma once
#include <blah/graphics/framebuffer.h>
namespace Blah namespace Blah
{ {
@ -23,6 +24,22 @@ namespace Blah
Config(); 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 namespace App
{ {
// Runs the application // Runs the application
@ -60,5 +77,14 @@ namespace Blah
// Toggles fullscreen // Toggles fullscreen
void fullscreen(bool enabled); 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) if (m_batch.texture != texture)
{ {
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) if (!m_mesh)
{ {
m_mesh = Graphics::create_mesh(); m_mesh = Mesh::create();
m_mesh->vertex_format(attributes, 4, sizeof(Vertex)); m_mesh->vertex_format(attributes, 4, sizeof(Vertex));
} }
if (!m_default_shader) if (!m_default_shader)
m_default_shader = Graphics::create_shader(&data); m_default_shader = Shader::create(&data);
if (!m_default_material) if (!m_default_material)
m_default_material = Graphics::create_material(m_default_shader); m_default_material = Material::create(m_default_shader);
} }
// upload data // upload data
m_mesh->index_data(m_indices.data(), m_indices.size()); m_mesh->index_data(m_indices.data(), m_indices.size());
m_mesh->vertex_data(m_vertices.data(), m_vertices.size()); m_mesh->vertex_data(m_vertices.data(), m_vertices.size());
RenderCall call; RenderPass pass;
call.target = target; pass.target = target;
call.mesh = m_mesh; pass.mesh = m_mesh;
call.has_viewport = false; pass.has_viewport = false;
call.viewport = Rect(); pass.viewport = Rect();
call.instance_count = 0; pass.instance_count = 0;
call.depth = Compare::None; pass.depth = Compare::None;
call.cull = Cull::None; pass.cull = Cull::None;
for (auto& b : m_batches) for (auto& b : m_batches)
render_single_batch(call, b, matrix); render_single_batch(pass, b, matrix);
if (m_batch.elements > 0) 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; pass.material = b.material;
if (!call.material) if (!pass.material)
call.material = m_default_material; pass.material = m_default_material;
call.material->set_texture(texture_uniform, b.texture, 0); pass.material->set_texture(texture_uniform, b.texture, 0);
call.material->set_value(matrix_uniform, &matrix.m11, 16); pass.material->set_value(matrix_uniform, &matrix.m11, 16);
call.blend = b.blend; pass.blend = b.blend;
call.has_scissor = b.scissor.w >= 0 && b.scissor.h >= 0; pass.has_scissor = b.scissor.w >= 0 && b.scissor.h >= 0;
call.scissor = b.scissor; pass.scissor = b.scissor;
call.index_start = (int64_t)b.offset * 3; pass.index_start = (int64_t)b.offset * 3;
call.index_count = (int64_t)b.elements * 3; pass.index_count = (int64_t)b.elements * 3;
Graphics::render(call); pass.perform();
} }
void Batch::clear() void Batch::clear()

View File

@ -1,5 +1,4 @@
#pragma once #pragma once
#include <blah/graphics/graphics.h>
#include <blah/containers/str.h> #include <blah/containers/str.h>
#include <blah/math/vec2.h> #include <blah/math/vec2.h>
#include <blah/math/rect.h> #include <blah/math/rect.h>
@ -9,6 +8,9 @@
#include <blah/drawing/subtexture.h> #include <blah/drawing/subtexture.h>
#include <blah/drawing/spritefont.h> #include <blah/drawing/spritefont.h>
#include <blah/containers/vector.h> #include <blah/containers/vector.h>
#include <blah/graphics/blend.h>
#include <blah/graphics/renderpass.h>
#include <blah/app.h>
namespace Blah namespace Blah
{ {
@ -114,7 +116,7 @@ namespace Blah
void set_texture(const TextureRef& texture); void set_texture(const TextureRef& texture);
// Draws the batch to the given target // 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 // Draws the batch to the given target, with the provided matrix
void render(const FrameBufferRef& target, const Mat4x4& matrix); void render(const FrameBufferRef& target, const Mat4x4& matrix);
@ -224,6 +226,6 @@ namespace Blah
Vector<int> m_layer_stack; Vector<int> m_layer_stack;
Vector<DrawBatch> m_batches; 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/drawing/spritefont.h>
#include <blah/images/font.h> #include <blah/images/font.h>
#include <blah/images/packer.h> #include <blah/images/packer.h>
#include <blah/graphics/graphics.h>
#include <blah/log.h> #include <blah/log.h>
using namespace Blah; using namespace Blah;
@ -199,7 +198,7 @@ void SpriteFont::build(const Font& font, float size, const uint32_t* charset)
packer.pack(); packer.pack();
for (auto& it : packer.pages) for (auto& it : packer.pages)
m_atlas.push_back(Graphics::create_texture(it)); m_atlas.push_back(Texture::create(it));
// add character subtextures // add character subtextures
for (auto& it : packer.entries) for (auto& it : packer.entries)

View File

@ -6,7 +6,7 @@ using namespace Blah;
Subtexture::Subtexture() {} Subtexture::Subtexture() {}
Subtexture::Subtexture(const TextureRef& texture) 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::Subtexture(const TextureRef& texture, Rect source)
: Subtexture(texture, source, Rect(0, 0, source.w, source.h)) {} : 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_source = (clip + source.top_left() + frame.top_left()).overlap_rect(source);
dest_frame->x = Calc::min(0, frame.x + clip.x); dest_frame->x = Calc::min(0.0f, frame.x + clip.x);
dest_frame->y = Calc::min(0, frame.y + clip.y); dest_frame->y = Calc::min(0.0f, frame.y + clip.y);
dest_frame->w = clip.w; dest_frame->w = clip.w;
dest_frame->h = clip.h; 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 #pragma once
#include <blah/graphics/texture.h> #include <blah/graphics/texture.h>
#include <blah/containers/stackvector.h> #include <blah/containers/stackvector.h>
#include <blah/math/color.h>
#include <memory> #include <memory>
// 4 color attachments + 1 depth/stencil // 4 color attachments + 1 depth/stencil
@ -10,9 +11,21 @@ namespace Blah
{ {
typedef StackVector<TextureRef, BLAH_ATTACHMENTS> Attachments; typedef StackVector<TextureRef, BLAH_ATTACHMENTS> Attachments;
class FrameBuffer;
typedef std::shared_ptr<FrameBuffer> FrameBufferRef;
class FrameBuffer class FrameBuffer
{ {
public: 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; virtual ~FrameBuffer() = default;
// Gets the list of Attachments from the FrameBuffer // Gets the list of Attachments from the FrameBuffer
@ -32,7 +45,9 @@ namespace Blah
// Gets the height of the FrameBuffer // Gets the height of the FrameBuffer
virtual int height() const = 0; 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) Material::Material(const ShaderRef& shader)
{ {
BLAH_ASSERT(shader, "Material is being created with an invalid shader"); BLAH_ASSERT(shader, "Material is being created with an invalid shader");
m_shader = shader; m_shader = shader;
auto& uniforms = shader->uniforms(); auto& uniforms = shader->uniforms();
Vector<size_t> float_offsets; Vector<int> float_offsets;
size_t float_size = 0; int float_size = 0;
for (auto& uniform : uniforms) for (auto& uniform : uniforms)
{ {

View File

@ -6,15 +6,23 @@
namespace Blah namespace Blah
{ {
class Material;
typedef std::shared_ptr<Material> MaterialRef;
class Material class Material
{ {
public: private:
Material(const ShaderRef& shader); Material(const ShaderRef& shader);
Material(const Material& src) = delete; Material(const Material& src) = delete;
Material(Material&& src) = delete; Material(Material&& src) = delete;
Material& operator=(const Material& src) = delete; Material& operator=(const Material& src) = delete;
Material& operator=(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. // Returns the Shader assigned to the Material.
const ShaderRef shader() const; const ShaderRef shader() const;
@ -52,6 +60,4 @@ namespace Blah
Vector<float*> m_floats; Vector<float*> m_floats;
Vector<float> m_data; Vector<float> m_data;
}; };
typedef std::shared_ptr<Material> MaterialRef;
} }

View File

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

View File

@ -44,9 +44,16 @@ namespace Blah
bool normalized; bool normalized;
}; };
class Mesh;
typedef std::shared_ptr<Mesh> MeshRef;
class Mesh class Mesh
{ {
public: public:
// Creates a new Mesh.
// If the Mesh creation fails, it will return an invalid Mesh.
static MeshRef create();
virtual ~Mesh() = default; virtual ~Mesh() = default;
// Sets the Vertex Format of the Mesh // 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 vertex_format_internal(const VertexAttribute* attributes, int count, int stride) = 0;
virtual void instance_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; const char* fragment;
}; };
class Shader;
typedef std::shared_ptr<Shader> ShaderRef;
class Shader class Shader
{ {
public: 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; virtual ~Shader() = default;
// Gets a list of Shader Uniforms from Shader // Gets a list of Shader Uniforms from Shader
@ -42,5 +50,4 @@ namespace Blah
virtual const Vector<UniformInfo>& uniforms() const = 0; 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 Count
}; };
class Image;
class Stream;
class Texture;
typedef std::shared_ptr<Texture> TextureRef;
class Texture class Texture
{ {
public: 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; virtual ~Texture() = default;
// gets the width of the texture // gets the width of the texture
@ -70,6 +96,4 @@ namespace Blah
// Returns true if the Texture is part of a FrameBuffer // Returns true if the Texture is part of a FrameBuffer
virtual bool is_framebuffer() const = 0; virtual bool is_framebuffer() const = 0;
}; };
typedef std::shared_ptr<Texture> TextureRef;
} }