mirror of
				https://github.com/NoelFB/blah.git
				synced 2025-11-04 01:41:34 +08:00 
			
		
		
		
	Refactored Graphics to allow Renderer choice at runtime
This commit is contained in:
		@ -38,9 +38,8 @@ add_library(blah
 | 
			
		||||
	src/streams/memorystream.cpp
 | 
			
		||||
	src/streams/stream.cpp
 | 
			
		||||
 | 
			
		||||
	src/internal/graphics_gl.cpp
 | 
			
		||||
	src/internal/graphics_d3d11.cpp
 | 
			
		||||
	src/internal/graphics_dummy.cpp
 | 
			
		||||
	src/internal/renderer_opengl.cpp
 | 
			
		||||
	src/internal/renderer_d3d11.cpp
 | 
			
		||||
	src/internal/platform_sdl2.cpp
 | 
			
		||||
	src/internal/platform_win32.cpp
 | 
			
		||||
)
 | 
			
		||||
@ -55,22 +54,24 @@ target_include_directories(blah
 | 
			
		||||
# Platform Variables
 | 
			
		||||
set(BLAH_PLATFORM_SDL2 true CACHE BOOL "Use SDL2 Platform Backend")
 | 
			
		||||
set(BLAH_PLATFORM_WIN32 false CACHE BOOL "Use Win32 Platform Backend")
 | 
			
		||||
set(BLAH_GRAPHICS_OPENGL true CACHE BOOL "Use OpenGL Graphics Backend")
 | 
			
		||||
set(BLAH_GRAPHICS_D3D11 false CACHE BOOL "Use D3D11 Graphics Backend")
 | 
			
		||||
set(BLAH_RENDERER_OPENGL true CACHE BOOL "Make OpenGL Renderer available")
 | 
			
		||||
if (WIN32)
 | 
			
		||||
	set(BLAH_RENDERER_D3D11 true CACHE BOOL "Make D3D11 Renderer available")
 | 
			
		||||
else()
 | 
			
		||||
	set(BLAH_RENDERER_D3D11 false CACHE BOOL "Make D3D11 Renderer available")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
set(LIBS "")
 | 
			
		||||
 | 
			
		||||
# use the OpenGL Graphics Backend
 | 
			
		||||
if (BLAH_GRAPHICS_OPENGL)
 | 
			
		||||
# use the OpenGL Renderer Backend
 | 
			
		||||
if (BLAH_RENDERER_OPENGL)
 | 
			
		||||
	add_compile_definitions(BLAH_RENDERER_OPENGL)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
	add_compile_definitions(BLAH_GRAPHICS_OPENGL)
 | 
			
		||||
 | 
			
		||||
# use the D3D11 Graphics Backend
 | 
			
		||||
elseif (BLAH_GRAPHICS_D3D11)
 | 
			
		||||
 | 
			
		||||
	add_compile_definitions(BLAH_GRAPHICS_D3D11)
 | 
			
		||||
# use the D3D11 Renderer Backend
 | 
			
		||||
if (BLAH_RENDERER_D3D11)
 | 
			
		||||
	add_compile_definitions(BLAH_RENDERER_D3D11)
 | 
			
		||||
	set(LIBS ${LIBS} d3d11.lib dxguid.lib D3Dcompiler.lib)
 | 
			
		||||
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# use the SDL2 Platform Backend
 | 
			
		||||
@ -91,7 +92,7 @@ if (BLAH_PLATFORM_SDL2)
 | 
			
		||||
		FetchContent_Declare(
 | 
			
		||||
			SDL2
 | 
			
		||||
			GIT_REPOSITORY https://github.com/libsdl-org/SDL
 | 
			
		||||
			GIT_TAG release-2.0.18 # grab latest stable release
 | 
			
		||||
			GIT_TAG release-2.0.20 # grab latest stable release
 | 
			
		||||
			GIT_PROGRESS TRUE
 | 
			
		||||
		)
 | 
			
		||||
		FetchContent_MakeAvailable(SDL2)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								README.md
									
									
									
									
									
								
							@ -9,10 +9,10 @@ A small 2D C++ Game Framework, using few dependencies and simple code to mainain
 | 
			
		||||
	- [SDL2](https://github.com/NoelFB/blah/blob/master/src/internal/platform_sdl2.cpp) can be enabled in CMake with `BLAH_PLATFORM_SDL2` (default)
 | 
			
		||||
	- [WIN32](https://github.com/NoelFB/blah/blob/master/src/internal/platform_win32.cpp) (UNFINISHED) can be enabled in CMake with `BLAH_PLATFORM_WIN32`
 | 
			
		||||
	- Additional platforms can be added by implementing the [Platform Backend](https://github.com/NoelFB/blah/blob/master/src/internal/platform.h)
 | 
			
		||||
 - A single *Graphics* implementation must be enabled:
 | 
			
		||||
	- [OpenGL](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_gl.cpp) can be enabled in CMake with `BLAH_GRAPHICS_OPENGL` (default)
 | 
			
		||||
	- [D3D11](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_d3d11.cpp) can be enabled in CMake with `BLAH_GRAPHICS_D3D11`
 | 
			
		||||
	- Additional graphics can be added by implementing the [Graphics Backend](https://github.com/NoelFB/blah/blob/master/src/internal/graphics.h)
 | 
			
		||||
 - At least one *Renderer* implementation must be enabled:
 | 
			
		||||
	- [OpenGL](https://github.com/NoelFB/blah/blob/master/src/internal/renderer_gl.cpp) can be enabled in CMake with `BLAH_RENDERER_OPENGL`
 | 
			
		||||
	- [D3D11](https://github.com/NoelFB/blah/blob/master/src/internal/renderer_d3d11.cpp) can be enabled in CMake with `BLAH_RENDERER_D3D11`
 | 
			
		||||
	- Additional renderers can be added by implementing the [Renderer Backend](https://github.com/NoelFB/blah/blob/master/src/internal/renderer.h)
 | 
			
		||||
 
 | 
			
		||||
#### notes
 | 
			
		||||
 - There's no Shader abstraction, so the [Sprite Batcher](https://github.com/NoelFB/blah/blob/master/src/graphics/batch.cpp) has hard-coded GLSL/HLSL. This will need to change.
 | 
			
		||||
@ -27,38 +27,26 @@ A small 2D C++ Game Framework, using few dependencies and simple code to mainain
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
Batch batch;
 | 
			
		||||
TextureRef tex;
 | 
			
		||||
 | 
			
		||||
void startup()
 | 
			
		||||
{
 | 
			
		||||
	tex = Texture::create("player.png");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void render()
 | 
			
		||||
{
 | 
			
		||||
	App::backbuffer->clear(Color::black);
 | 
			
		||||
	
 | 
			
		||||
	auto center = Vec2f(App::backbuffer->width(), App::backbuffer->height()) / 2;
 | 
			
		||||
	auto rotation = Time::seconds * Calc::TAU;
 | 
			
		||||
	auto transform = Mat3x2f::create_transform(center, Vec2f::zero, Vec2f::one, rotation);
 | 
			
		||||
 | 
			
		||||
	batch.push_matrix(transform);
 | 
			
		||||
	batch.rect(Rectf(-32, -32, 64, 64), Color::red);
 | 
			
		||||
	batch.tex(tex, Vec2f(64, 0), Color::white);
 | 
			
		||||
	batch.pop_matrix();
 | 
			
		||||
	
 | 
			
		||||
	batch.render();
 | 
			
		||||
	batch.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
	Config config;
 | 
			
		||||
	config.name = "blah app";
 | 
			
		||||
	config.width = 1280;
 | 
			
		||||
	config.height = 720;
 | 
			
		||||
	config.on_startup = startup;
 | 
			
		||||
	config.on_render = render;
 | 
			
		||||
	config.on_render = []()
 | 
			
		||||
	{
 | 
			
		||||
		App::backbuffer()->clear(Color::black);
 | 
			
		||||
 | 
			
		||||
		auto center = App::get_backbuffer_size() / 2;
 | 
			
		||||
		auto rotation = Time::seconds * Calc::TAU;
 | 
			
		||||
		auto transform = Mat3x2f::create_transform(center, Vec2f::zero, Vec2f::one, rotation);
 | 
			
		||||
 | 
			
		||||
		batch.push_matrix(transform);
 | 
			
		||||
		batch.rect(Rectf(-32, -32, 64, 64), Color::red);
 | 
			
		||||
		batch.pop_matrix();
 | 
			
		||||
 | 
			
		||||
		batch.render();
 | 
			
		||||
		batch.clear();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	App::run(&config);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
@ -10,74 +10,20 @@ namespace Blah
 | 
			
		||||
	// Application Logging Functions
 | 
			
		||||
	using AppLogFn = Func<void(const char* message, Log::Category category)>;
 | 
			
		||||
 | 
			
		||||
	// Application Configuration
 | 
			
		||||
	struct Config
 | 
			
		||||
	{
 | 
			
		||||
		// Application name.
 | 
			
		||||
		// This has no default and must be set.
 | 
			
		||||
		const char* name;
 | 
			
		||||
 | 
			
		||||
		// Starting width, in pixels.
 | 
			
		||||
		// Depending on the OS DPI, the true window size may be a multiple of this.
 | 
			
		||||
		// This has no default and must be set.
 | 
			
		||||
		int width;
 | 
			
		||||
 | 
			
		||||
		// Starting height, in pixels.
 | 
			
		||||
		// Depending on the OS DPI, the true window size may be a multiple of this.
 | 
			
		||||
		// This has no default and must be set.
 | 
			
		||||
		int height;
 | 
			
		||||
 | 
			
		||||
		// maximum updates to run before "giving up" and reducing frame rate.
 | 
			
		||||
		// this avoids the 'spiral of death'.
 | 
			
		||||
		// defaults to 5.
 | 
			
		||||
		int max_updates;
 | 
			
		||||
 | 
			
		||||
		// target framerate.
 | 
			
		||||
		// defaults to 60.
 | 
			
		||||
		int target_framerate;
 | 
			
		||||
 | 
			
		||||
		// Callback on application startup
 | 
			
		||||
		// Defaults to nothing.
 | 
			
		||||
		AppEventFn on_startup;
 | 
			
		||||
 | 
			
		||||
		// Callback on application shutdown
 | 
			
		||||
		// Defaults to nothing.
 | 
			
		||||
		AppEventFn on_shutdown;
 | 
			
		||||
 | 
			
		||||
		// Callback on application update
 | 
			
		||||
		// Defaults to nothing.
 | 
			
		||||
		AppEventFn on_update;
 | 
			
		||||
 | 
			
		||||
		// Callback on application render
 | 
			
		||||
		// Defaults to nothing.
 | 
			
		||||
		AppEventFn on_render;
 | 
			
		||||
 | 
			
		||||
		// Callback when the user has requested the application close.
 | 
			
		||||
		// For example, pressing the Close button, ALT+F4, etc
 | 
			
		||||
		// By default this calls `App::exit()`
 | 
			
		||||
		AppEventFn on_exit_request;
 | 
			
		||||
 | 
			
		||||
		// Callback when the application logs info/warning/errors
 | 
			
		||||
		// Defaults to printf.
 | 
			
		||||
		AppLogFn on_log;
 | 
			
		||||
 | 
			
		||||
		// Default config setup
 | 
			
		||||
		Config();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Renderer the Application is using
 | 
			
		||||
	enum class Renderer
 | 
			
		||||
	// Type of Renderer the Application is using
 | 
			
		||||
	enum class RendererType
 | 
			
		||||
	{
 | 
			
		||||
		None = -1,
 | 
			
		||||
		OpenGL,
 | 
			
		||||
		D3D11,
 | 
			
		||||
		Metal,
 | 
			
		||||
		Count
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Features available on the current Renderer
 | 
			
		||||
	// Renderer Information
 | 
			
		||||
	struct RendererFeatures
 | 
			
		||||
	{
 | 
			
		||||
		// The type of Renderer being used
 | 
			
		||||
		RendererType type = RendererType::None;
 | 
			
		||||
 | 
			
		||||
		// Whether Mesh Instancing is available
 | 
			
		||||
		bool instancing = false;
 | 
			
		||||
 | 
			
		||||
@ -89,6 +35,55 @@ namespace Blah
 | 
			
		||||
		int max_texture_size = 0;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Application Configuration
 | 
			
		||||
	struct Config
 | 
			
		||||
	{
 | 
			
		||||
		// Application name.
 | 
			
		||||
		const char* name = "blah";
 | 
			
		||||
		
 | 
			
		||||
		// Which renderer to use
 | 
			
		||||
		// Default depends on the Platform
 | 
			
		||||
		RendererType renderer_type = RendererType::None;
 | 
			
		||||
 | 
			
		||||
		// Starting width, in pixels.
 | 
			
		||||
		// Depending on the OS DPI, the true window size may be a multiple of this.
 | 
			
		||||
		int width = 1280;
 | 
			
		||||
 | 
			
		||||
		// Starting height, in pixels.
 | 
			
		||||
		// Depending on the OS DPI, the true window size may be a multiple of this.
 | 
			
		||||
		int height = 720;
 | 
			
		||||
 | 
			
		||||
		// maximum updates to run before "giving up" and reducing frame rate.
 | 
			
		||||
		// this avoids the 'spiral of death'.
 | 
			
		||||
		// defaults to 5.
 | 
			
		||||
		int max_updates = 5;
 | 
			
		||||
 | 
			
		||||
		// target framerate.
 | 
			
		||||
		// defaults to 60.
 | 
			
		||||
		int target_framerate = 60;
 | 
			
		||||
 | 
			
		||||
		// Callback on application startup
 | 
			
		||||
		AppEventFn on_startup = nullptr;
 | 
			
		||||
 | 
			
		||||
		// Callback on application shutdown
 | 
			
		||||
		AppEventFn on_shutdown = nullptr;
 | 
			
		||||
 | 
			
		||||
		// Callback on application update
 | 
			
		||||
		AppEventFn on_update = nullptr;
 | 
			
		||||
 | 
			
		||||
		// Callback on application render
 | 
			
		||||
		AppEventFn on_render = nullptr;
 | 
			
		||||
 | 
			
		||||
		// Callback when the user has requested the application close.
 | 
			
		||||
		// For example, pressing the Close button, ALT+F4, etc
 | 
			
		||||
		// By default this calls `App::exit()`
 | 
			
		||||
		AppEventFn on_exit_request = nullptr;
 | 
			
		||||
 | 
			
		||||
		// Callback when the application logs info/warning/errors
 | 
			
		||||
		// Defaults to printf.
 | 
			
		||||
		AppLogFn on_log = nullptr;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Forward declare Target for the BackBuffer
 | 
			
		||||
	class Target;
 | 
			
		||||
	using TargetRef = Ref<Target>;
 | 
			
		||||
@ -119,31 +114,22 @@ namespace Blah
 | 
			
		||||
		// Sets the Window Title
 | 
			
		||||
		void set_title(const char* title);
 | 
			
		||||
 | 
			
		||||
		// Gets the Window Position
 | 
			
		||||
		// Gets the Window Position in Screen Coordinates
 | 
			
		||||
		Point get_position();
 | 
			
		||||
 | 
			
		||||
		// Sets the Window Position
 | 
			
		||||
		// Sets the Window Position in Screen Coordinates
 | 
			
		||||
		void set_position(Point point);
 | 
			
		||||
 | 
			
		||||
		// Gets the Window Size
 | 
			
		||||
		// Gets the size of the Window in Screen Coordinates.
 | 
			
		||||
		// On High DPI displays this may not be 1:1 with pixels.
 | 
			
		||||
		// For the size in pixels, use App::get_backbuffer_size()
 | 
			
		||||
		Point get_size();
 | 
			
		||||
 | 
			
		||||
		// Sets the Window Size
 | 
			
		||||
		// Sets the Window Size in Screen Coordinates
 | 
			
		||||
		void set_size(Point point);
 | 
			
		||||
 | 
			
		||||
		// Gets the width of the window
 | 
			
		||||
		int width();
 | 
			
		||||
 | 
			
		||||
		// Gets the height of the window
 | 
			
		||||
		int height();
 | 
			
		||||
		
 | 
			
		||||
		// Gets the drawable width of the window, in pixels.
 | 
			
		||||
		// This may differ from the width when on platforms with High DPI Displays.
 | 
			
		||||
		int draw_width();
 | 
			
		||||
 | 
			
		||||
		// Gets the drawable height of the window, in pixels.
 | 
			
		||||
		// This may differ from the height when on platforms with High DPI Displays.
 | 
			
		||||
		int draw_height();
 | 
			
		||||
		// Gets the size of the BackBuffer, in pixels
 | 
			
		||||
		Point get_backbuffer_size();
 | 
			
		||||
 | 
			
		||||
		// Gets the content scale based on the platform.
 | 
			
		||||
		// macOS is usually 2.0, other platforms vary.
 | 
			
		||||
@ -156,14 +142,11 @@ namespace Blah
 | 
			
		||||
		// Otherwise this function does nothing.
 | 
			
		||||
		void fullscreen(bool enabled);
 | 
			
		||||
 | 
			
		||||
		// Returns the Rendering API in use
 | 
			
		||||
		Renderer renderer();
 | 
			
		||||
 | 
			
		||||
		// Retrieves the Renderer Features
 | 
			
		||||
		const RendererFeatures& renderer_features();
 | 
			
		||||
		const RendererFeatures& renderer();
 | 
			
		||||
 | 
			
		||||
		// Reference to the window's back buffer
 | 
			
		||||
		extern const TargetRef backbuffer;
 | 
			
		||||
		// Gets the BackBuffer
 | 
			
		||||
		const TargetRef& backbuffer();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace System
 | 
			
		||||
 | 
			
		||||
@ -128,7 +128,7 @@ namespace Blah
 | 
			
		||||
		void set_sampler(const TextureSampler& sampler);
 | 
			
		||||
 | 
			
		||||
		// Draws the batch to the given target
 | 
			
		||||
		void render(const TargetRef& target = App::backbuffer);
 | 
			
		||||
		void render(const TargetRef& target = nullptr);
 | 
			
		||||
 | 
			
		||||
		// Draws the batch to the given target, with the provided matrix
 | 
			
		||||
		void render(const TargetRef& target, const Mat4x4f& matrix);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										586
									
								
								src/app.cpp
									
									
									
									
									
								
							
							
						
						
									
										586
									
								
								src/app.cpp
									
									
									
									
									
								
							@ -3,7 +3,7 @@
 | 
			
		||||
#include <blah/time.h>
 | 
			
		||||
#include <blah/graphics/target.h>
 | 
			
		||||
#include "internal/platform.h"
 | 
			
		||||
#include "internal/graphics.h"
 | 
			
		||||
#include "internal/renderer.h"
 | 
			
		||||
#include "internal/input.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
@ -13,286 +13,7 @@
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
Config::Config()
 | 
			
		||||
{
 | 
			
		||||
	name = nullptr;
 | 
			
		||||
	width = 0;
 | 
			
		||||
	height = 0;
 | 
			
		||||
	target_framerate = 60;
 | 
			
		||||
	max_updates = 5;
 | 
			
		||||
 | 
			
		||||
	on_startup = nullptr;
 | 
			
		||||
	on_shutdown = nullptr;
 | 
			
		||||
	on_update = nullptr;
 | 
			
		||||
	on_render = nullptr;
 | 
			
		||||
	on_exit_request = App::exit;
 | 
			
		||||
	on_log = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
	Config app_config;
 | 
			
		||||
	bool app_is_running = false;
 | 
			
		||||
	bool app_is_exiting = false;
 | 
			
		||||
	u64 time_last;
 | 
			
		||||
	u64 time_accumulator = 0;
 | 
			
		||||
 | 
			
		||||
	void app_iterate()
 | 
			
		||||
	{
 | 
			
		||||
		// update at a fixed timerate
 | 
			
		||||
		// TODO: allow a non-fixed step update?
 | 
			
		||||
		{
 | 
			
		||||
			u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
 | 
			
		||||
			u64 time_curr = Platform::ticks();
 | 
			
		||||
			u64 time_diff = time_curr - time_last;
 | 
			
		||||
			time_last = time_curr;
 | 
			
		||||
			time_accumulator += time_diff;
 | 
			
		||||
 | 
			
		||||
			// do not let us run too fast
 | 
			
		||||
			while (time_accumulator < time_target)
 | 
			
		||||
			{
 | 
			
		||||
				int milliseconds = (int)(time_target - time_accumulator) / (Time::ticks_per_second / 1000);
 | 
			
		||||
				Platform::sleep(milliseconds);
 | 
			
		||||
 | 
			
		||||
				time_curr = Platform::ticks();
 | 
			
		||||
				time_diff = time_curr - time_last;
 | 
			
		||||
				time_last = time_curr;
 | 
			
		||||
				time_accumulator += time_diff;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Do not allow us to fall behind too many updates
 | 
			
		||||
			// (otherwise we'll get spiral of death)
 | 
			
		||||
			u64 time_maximum = app_config.max_updates * time_target;
 | 
			
		||||
			if (time_accumulator > time_maximum)
 | 
			
		||||
				time_accumulator = time_maximum;
 | 
			
		||||
 | 
			
		||||
			// do as many updates as we can
 | 
			
		||||
			while (time_accumulator >= time_target)
 | 
			
		||||
			{
 | 
			
		||||
				time_accumulator -= time_target;
 | 
			
		||||
 | 
			
		||||
				Time::delta = (1.0f / app_config.target_framerate);
 | 
			
		||||
 | 
			
		||||
				if (Time::pause_timer > 0)
 | 
			
		||||
				{
 | 
			
		||||
					Time::pause_timer -= Time::delta;
 | 
			
		||||
					if (Time::pause_timer <= -0.0001)
 | 
			
		||||
						Time::delta = -Time::pause_timer;
 | 
			
		||||
					else
 | 
			
		||||
						continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Time::previous_ticks = Time::ticks;
 | 
			
		||||
				Time::ticks += time_target;
 | 
			
		||||
				Time::previous_seconds = Time::seconds;
 | 
			
		||||
				Time::seconds += Time::delta;
 | 
			
		||||
 | 
			
		||||
				Input::update_state();
 | 
			
		||||
				Platform::update(Input::state);
 | 
			
		||||
				Input::update_bindings();
 | 
			
		||||
				Graphics::update();
 | 
			
		||||
 | 
			
		||||
				if (app_config.on_update != nullptr)
 | 
			
		||||
					app_config.on_update();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// render
 | 
			
		||||
		{
 | 
			
		||||
			Graphics::before_render();
 | 
			
		||||
 | 
			
		||||
			if (app_config.on_render != nullptr)
 | 
			
		||||
				app_config.on_render();
 | 
			
		||||
 | 
			
		||||
			Graphics::after_render();
 | 
			
		||||
			Platform::present();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::run(const Config* c)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT(!app_is_running, "The Application is already running");
 | 
			
		||||
	BLAH_ASSERT(c != nullptr, "The Application requires a valid Config");
 | 
			
		||||
	BLAH_ASSERT(c->name != nullptr, "The Application Name cannot be null");
 | 
			
		||||
	BLAH_ASSERT(c->width > 0 && c->height > 0, "The Width and Height must be larget than 0");
 | 
			
		||||
	BLAH_ASSERT(c->max_updates > 0, "Max Updates must be >= 1");
 | 
			
		||||
	BLAH_ASSERT(c->target_framerate > 0, "Target Framerate must be >= 1");
 | 
			
		||||
 | 
			
		||||
	app_config = *c;
 | 
			
		||||
	app_is_running = true;
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
 | 
			
		||||
	// initialize the system
 | 
			
		||||
	if (!Platform::init(app_config))
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Failed to initialize Platform module");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize graphics
 | 
			
		||||
	if (!Graphics::init())
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Failed to initialize Graphics module");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// input
 | 
			
		||||
	Input::init();
 | 
			
		||||
 | 
			
		||||
	// prepare by updating input & platform once
 | 
			
		||||
	Input::update_state();
 | 
			
		||||
	Platform::update(Input::state);
 | 
			
		||||
 | 
			
		||||
	// startup
 | 
			
		||||
	if (app_config.on_startup != nullptr)
 | 
			
		||||
		app_config.on_startup();
 | 
			
		||||
 | 
			
		||||
	time_last = Platform::ticks();
 | 
			
		||||
	time_accumulator = 0;
 | 
			
		||||
 | 
			
		||||
	// display window
 | 
			
		||||
	Platform::ready();
 | 
			
		||||
 | 
			
		||||
	// Begin main loop
 | 
			
		||||
	// Emscripten requires the main loop be separated into its own call
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
	emscripten_set_main_loop(app_iterate, 0, 1);
 | 
			
		||||
#else
 | 
			
		||||
	while (!app_is_exiting)
 | 
			
		||||
		app_iterate();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// shutdown
 | 
			
		||||
	if (app_config.on_shutdown != nullptr)
 | 
			
		||||
		app_config.on_shutdown();
 | 
			
		||||
 | 
			
		||||
	Graphics::shutdown();
 | 
			
		||||
	Platform::shutdown();
 | 
			
		||||
 | 
			
		||||
	// clear static state
 | 
			
		||||
	app_is_running = false;
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
 | 
			
		||||
	Time::ticks = 0;
 | 
			
		||||
	Time::seconds = 0;
 | 
			
		||||
	Time::previous_ticks = 0;
 | 
			
		||||
	Time::previous_seconds = 0;
 | 
			
		||||
	Time::delta = 0;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::exit()
 | 
			
		||||
{
 | 
			
		||||
	if (!app_is_exiting && app_is_running)
 | 
			
		||||
		app_is_exiting = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Config& App::config()
 | 
			
		||||
{
 | 
			
		||||
	return app_config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::path()
 | 
			
		||||
{
 | 
			
		||||
	return Platform::app_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::user_path()
 | 
			
		||||
{
 | 
			
		||||
	return Platform::user_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::get_title()
 | 
			
		||||
{
 | 
			
		||||
	return Platform::get_title();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_title(const char* title)
 | 
			
		||||
{
 | 
			
		||||
	Platform::set_title(title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_position()
 | 
			
		||||
{
 | 
			
		||||
	Point result;
 | 
			
		||||
	Platform::get_position(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_position(Point point)
 | 
			
		||||
{
 | 
			
		||||
	Platform::set_position(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_size()
 | 
			
		||||
{
 | 
			
		||||
	Point result;
 | 
			
		||||
	Platform::get_size(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_size(Point point)
 | 
			
		||||
{
 | 
			
		||||
	Platform::set_size(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int App::width()
 | 
			
		||||
{
 | 
			
		||||
	return get_size().x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int App::height()
 | 
			
		||||
{
 | 
			
		||||
	return get_size().y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int App::draw_width()
 | 
			
		||||
{
 | 
			
		||||
	int w, h;
 | 
			
		||||
	Platform::get_draw_size(&w, &h);
 | 
			
		||||
	return w;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int App::draw_height()
 | 
			
		||||
{
 | 
			
		||||
	int w, h;
 | 
			
		||||
	Platform::get_draw_size(&w, &h);
 | 
			
		||||
	return h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float App::content_scale()
 | 
			
		||||
{
 | 
			
		||||
	return Platform::get_content_scale();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::fullscreen(bool enabled)
 | 
			
		||||
{
 | 
			
		||||
	Platform::set_fullscreen(enabled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::focused()
 | 
			
		||||
{
 | 
			
		||||
	return Platform::get_focused();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Renderer App::renderer()
 | 
			
		||||
{
 | 
			
		||||
	return Graphics::renderer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RendererFeatures& Blah::App::renderer_features()
 | 
			
		||||
{
 | 
			
		||||
	return Graphics::features();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::open_url(const char* url)
 | 
			
		||||
{
 | 
			
		||||
	Platform::open_url(url);
 | 
			
		||||
}
 | 
			
		||||
#define BLAH_ASSERT_RUNNING() BLAH_ASSERT(app_is_running, "The App is not running (call App::run)")
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
@ -316,20 +37,315 @@ namespace
 | 
			
		||||
 | 
			
		||||
		int width() const override
 | 
			
		||||
		{
 | 
			
		||||
			return App::draw_width();
 | 
			
		||||
			int w, h;
 | 
			
		||||
			Platform::get_draw_size(&w, &h);
 | 
			
		||||
			return w;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int height() const override
 | 
			
		||||
		{
 | 
			
		||||
			return App::draw_height();
 | 
			
		||||
			int w, h;
 | 
			
		||||
			Platform::get_draw_size(&w, &h);
 | 
			
		||||
			return h;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void clear(Color color, float depth, u8 stencil, ClearMask mask) override
 | 
			
		||||
		{
 | 
			
		||||
			Graphics::clear_backbuffer(color, depth, stencil, mask);
 | 
			
		||||
			if (Renderer::instance)
 | 
			
		||||
				Renderer::instance->clear_backbuffer(color, depth, stencil, mask);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Config app_config;
 | 
			
		||||
	bool app_is_running = false;
 | 
			
		||||
	bool app_is_exiting = false;
 | 
			
		||||
	u64 app_time_last;
 | 
			
		||||
	u64 app_time_accumulator = 0;
 | 
			
		||||
	TargetRef app_backbuffer;
 | 
			
		||||
 | 
			
		||||
	void app_iterate()
 | 
			
		||||
	{
 | 
			
		||||
		// update at a fixed timerate
 | 
			
		||||
		// TODO: allow a non-fixed step update?
 | 
			
		||||
		{
 | 
			
		||||
			u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
 | 
			
		||||
			u64 time_curr = Platform::ticks();
 | 
			
		||||
			u64 time_diff = time_curr - app_time_last;
 | 
			
		||||
			app_time_last = time_curr;
 | 
			
		||||
			app_time_accumulator += time_diff;
 | 
			
		||||
 | 
			
		||||
			// do not let us run too fast
 | 
			
		||||
			while (app_time_accumulator < time_target)
 | 
			
		||||
			{
 | 
			
		||||
				int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000);
 | 
			
		||||
				Platform::sleep(milliseconds);
 | 
			
		||||
 | 
			
		||||
				time_curr = Platform::ticks();
 | 
			
		||||
				time_diff = time_curr - app_time_last;
 | 
			
		||||
				app_time_last = time_curr;
 | 
			
		||||
				app_time_accumulator += time_diff;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
const TargetRef App::backbuffer = TargetRef(new BackBuffer());
 | 
			
		||||
			// Do not allow us to fall behind too many updates
 | 
			
		||||
			// (otherwise we'll get spiral of death)
 | 
			
		||||
			u64 time_maximum = app_config.max_updates * time_target;
 | 
			
		||||
			if (app_time_accumulator > time_maximum)
 | 
			
		||||
				app_time_accumulator = time_maximum;
 | 
			
		||||
 | 
			
		||||
			// do as many updates as we can
 | 
			
		||||
			while (app_time_accumulator >= time_target)
 | 
			
		||||
			{
 | 
			
		||||
				app_time_accumulator -= time_target;
 | 
			
		||||
 | 
			
		||||
				Time::delta = (1.0f / app_config.target_framerate);
 | 
			
		||||
 | 
			
		||||
				if (Time::pause_timer > 0)
 | 
			
		||||
				{
 | 
			
		||||
					Time::pause_timer -= Time::delta;
 | 
			
		||||
					if (Time::pause_timer <= -0.0001)
 | 
			
		||||
						Time::delta = -Time::pause_timer;
 | 
			
		||||
					else
 | 
			
		||||
						continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Time::previous_ticks = Time::ticks;
 | 
			
		||||
				Time::ticks += time_target;
 | 
			
		||||
				Time::previous_seconds = Time::seconds;
 | 
			
		||||
				Time::seconds += Time::delta;
 | 
			
		||||
 | 
			
		||||
				Input::update_state();
 | 
			
		||||
				Platform::update(Input::state);
 | 
			
		||||
				Input::update_bindings();
 | 
			
		||||
				Renderer::instance->update();
 | 
			
		||||
 | 
			
		||||
				if (app_config.on_update != nullptr)
 | 
			
		||||
					app_config.on_update();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// render
 | 
			
		||||
		{
 | 
			
		||||
			Renderer::instance->before_render();
 | 
			
		||||
 | 
			
		||||
			if (app_config.on_render != nullptr)
 | 
			
		||||
				app_config.on_render();
 | 
			
		||||
 | 
			
		||||
			Renderer::instance->after_render();
 | 
			
		||||
			Platform::present();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::run(const Config* c)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT(!app_is_running, "The Application is already running");
 | 
			
		||||
	BLAH_ASSERT(c != nullptr, "The Application requires a valid Config");
 | 
			
		||||
	BLAH_ASSERT(c->name != nullptr, "The Application Name cannot be null");
 | 
			
		||||
	BLAH_ASSERT(c->width > 0 && c->height > 0, "The Width and Height must be larget than 0");
 | 
			
		||||
	BLAH_ASSERT(c->max_updates > 0, "Max Updates must be >= 1");
 | 
			
		||||
	BLAH_ASSERT(c->target_framerate > 0, "Target Framerate must be >= 1");
 | 
			
		||||
 | 
			
		||||
	// copy config over
 | 
			
		||||
	app_config = *c;
 | 
			
		||||
 | 
			
		||||
	// exit the application by default
 | 
			
		||||
	if (!app_config.on_exit_request)
 | 
			
		||||
		app_config.on_exit_request = App::exit;
 | 
			
		||||
 | 
			
		||||
	// default renderer type
 | 
			
		||||
	if (app_config.renderer_type == RendererType::None)
 | 
			
		||||
		app_config.renderer_type = Renderer::default_type();
 | 
			
		||||
 | 
			
		||||
	// default values
 | 
			
		||||
	app_is_running = true;
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
	app_backbuffer = TargetRef(new BackBuffer());
 | 
			
		||||
 | 
			
		||||
	// initialize the system
 | 
			
		||||
	if (!Platform::init(app_config))
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Failed to initialize Platform module");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize graphics
 | 
			
		||||
	{
 | 
			
		||||
		// instantiate
 | 
			
		||||
		Renderer::instance = Renderer::try_make_renderer(app_config.renderer_type);
 | 
			
		||||
 | 
			
		||||
		// wasn't able to make any
 | 
			
		||||
		if (Renderer::instance == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Renderer implementation was not found");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!Renderer::instance->init())
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Renderer failed to initialize");
 | 
			
		||||
			delete Renderer::instance;
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// input
 | 
			
		||||
	Input::init();
 | 
			
		||||
 | 
			
		||||
	// prepare by updating input & platform once
 | 
			
		||||
	Input::update_state();
 | 
			
		||||
	Platform::update(Input::state);
 | 
			
		||||
 | 
			
		||||
	// startup
 | 
			
		||||
	if (app_config.on_startup != nullptr)
 | 
			
		||||
		app_config.on_startup();
 | 
			
		||||
 | 
			
		||||
	app_time_last = Platform::ticks();
 | 
			
		||||
	app_time_accumulator = 0;
 | 
			
		||||
 | 
			
		||||
	// display window
 | 
			
		||||
	Platform::ready();
 | 
			
		||||
 | 
			
		||||
	// Begin main loop
 | 
			
		||||
	// Emscripten requires the main loop be separated into its own call
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
	emscripten_set_main_loop(app_iterate, 0, 1);
 | 
			
		||||
#else
 | 
			
		||||
	while (!app_is_exiting)
 | 
			
		||||
		app_iterate();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// shutdown
 | 
			
		||||
	if (app_config.on_shutdown != nullptr)
 | 
			
		||||
		app_config.on_shutdown();
 | 
			
		||||
 | 
			
		||||
	Renderer::instance->shutdown();
 | 
			
		||||
	Platform::shutdown();
 | 
			
		||||
 | 
			
		||||
	// clear static state
 | 
			
		||||
	app_is_running = false;
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
	app_backbuffer = nullptr;
 | 
			
		||||
 | 
			
		||||
	delete Renderer::instance;
 | 
			
		||||
	Renderer::instance = nullptr;
 | 
			
		||||
 | 
			
		||||
	Time::ticks = 0;
 | 
			
		||||
	Time::seconds = 0;
 | 
			
		||||
	Time::previous_ticks = 0;
 | 
			
		||||
	Time::previous_seconds = 0;
 | 
			
		||||
	Time::delta = 0;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::exit()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	if (!app_is_exiting && app_is_running)
 | 
			
		||||
		app_is_exiting = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Config& App::config()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return app_config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::path()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::app_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::user_path()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::user_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::get_title()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_title();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_title(const char* title)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_title(title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_position()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Point result;
 | 
			
		||||
	Platform::get_position(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_position(Point point)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_position(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_size()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Point result;
 | 
			
		||||
	Platform::get_size(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_size(Point point)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_size(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_backbuffer_size()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	if (app_backbuffer)
 | 
			
		||||
		return Point(app_backbuffer->width(), app_backbuffer->height());
 | 
			
		||||
	return Point(0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float App::content_scale()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_content_scale();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool App::focused()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return Platform::get_focused();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::fullscreen(bool enabled)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::set_fullscreen(enabled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RendererFeatures& App::renderer()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	BLAH_ASSERT_RENDERER();
 | 
			
		||||
	return Renderer::instance->features;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TargetRef& App::backbuffer()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	return app_backbuffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::open_url(const char* url)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RUNNING();
 | 
			
		||||
	Platform::open_url(url);
 | 
			
		||||
}
 | 
			
		||||
@ -378,7 +378,7 @@ void Batch::set_texture(const TextureRef& texture)
 | 
			
		||||
	if (m_batch.texture != texture)
 | 
			
		||||
	{
 | 
			
		||||
		m_batch.texture = texture;
 | 
			
		||||
		m_batch.flip_vertically = App::renderer_features().origin_bottom_left && texture && texture->is_framebuffer();
 | 
			
		||||
		m_batch.flip_vertically = App::renderer().origin_bottom_left && texture && texture->is_framebuffer();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -392,13 +392,8 @@ void Batch::set_sampler(const TextureSampler& sampler)
 | 
			
		||||
 | 
			
		||||
void Batch::render(const TargetRef& target)
 | 
			
		||||
{
 | 
			
		||||
	Point size;
 | 
			
		||||
	if (!target)
 | 
			
		||||
		size = Point(App::draw_width(), App::draw_height());
 | 
			
		||||
	else
 | 
			
		||||
		size = Point(target->width(), target->height());
 | 
			
		||||
 | 
			
		||||
	render(target, Mat4x4f::create_ortho_offcenter(0, (float)size.x, (float)size.y, 0, 0.01f, 1000.0f));
 | 
			
		||||
	TargetRef ref = (target ? target : App::backbuffer());
 | 
			
		||||
	render(ref, Mat4x4f::create_ortho_offcenter(0, (float)ref->width(), (float)ref->height(), 0, 0.01f, 1000.0f));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Batch::render(const TargetRef& target, const Mat4x4f& matrix)
 | 
			
		||||
@ -414,9 +409,9 @@ void Batch::render(const TargetRef& target, const Mat4x4f& matrix)
 | 
			
		||||
 | 
			
		||||
		if (!m_default_shader)
 | 
			
		||||
		{
 | 
			
		||||
			if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
			if (App::renderer().type == RendererType::OpenGL)
 | 
			
		||||
				m_default_shader = Shader::create(opengl_shader_data);
 | 
			
		||||
			else if (App::renderer() == Renderer::D3D11)
 | 
			
		||||
			else if (App::renderer().type == RendererType::D3D11)
 | 
			
		||||
				m_default_shader = Shader::create(d3d11_shader_data);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,16 @@
 | 
			
		||||
#include <blah/graphics/mesh.h>
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
#include "../internal/renderer.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MeshRef Mesh::create()
 | 
			
		||||
{
 | 
			
		||||
	return Graphics::create_mesh();
 | 
			
		||||
	BLAH_ASSERT_RENDERER();
 | 
			
		||||
 | 
			
		||||
	if (Renderer::instance)
 | 
			
		||||
		return Renderer::instance->create_mesh();
 | 
			
		||||
 | 
			
		||||
	return MeshRef();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VertexFormat::VertexFormat(std::initializer_list<VertexAttribute> attributes, int stride)
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,13 @@
 | 
			
		||||
#include <blah/graphics/renderpass.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
#include "../internal/renderer.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
RenderPass::RenderPass()
 | 
			
		||||
{
 | 
			
		||||
	blend = BlendMode::Normal;
 | 
			
		||||
	target = App::backbuffer;
 | 
			
		||||
	target = App::backbuffer();
 | 
			
		||||
	mesh = MeshRef();
 | 
			
		||||
	material = MaterialRef();
 | 
			
		||||
	has_viewport = false;
 | 
			
		||||
@ -23,17 +23,21 @@ RenderPass::RenderPass()
 | 
			
		||||
 | 
			
		||||
void RenderPass::perform()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RENDERER();
 | 
			
		||||
	BLAH_ASSERT(material, "Trying to draw with an invalid Material");
 | 
			
		||||
	BLAH_ASSERT(material->shader(), "Trying to draw with an invalid Shader");
 | 
			
		||||
	BLAH_ASSERT(mesh, "Trying to draw with an invalid Mesh");
 | 
			
		||||
 | 
			
		||||
	if (!Renderer::instance)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	// copy call
 | 
			
		||||
	RenderPass pass = *this;
 | 
			
		||||
 | 
			
		||||
	// Validate Backbuffer
 | 
			
		||||
	if (!pass.target)
 | 
			
		||||
	{
 | 
			
		||||
		pass.target = App::backbuffer;
 | 
			
		||||
		pass.target = App::backbuffer();
 | 
			
		||||
		Log::warn("Trying to draw with an invalid Target; falling back to Back Buffer");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -86,5 +90,5 @@ void RenderPass::perform()
 | 
			
		||||
		pass.scissor = pass.scissor.overlap_rect(Rectf(0, 0, draw_size.x, draw_size.y));
 | 
			
		||||
 | 
			
		||||
	// perform render
 | 
			
		||||
	Graphics::render(pass);
 | 
			
		||||
	Renderer::instance->render(pass);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,20 @@
 | 
			
		||||
#include <blah/graphics/shader.h>
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
#include "../internal/renderer.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
ShaderRef Shader::create(const ShaderData& data)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RENDERER();
 | 
			
		||||
	BLAH_ASSERT(data.vertex.length() > 0, "Must provide a Vertex Shader");
 | 
			
		||||
	BLAH_ASSERT(data.fragment.length() > 0, "Must provide a Fragment Shader");
 | 
			
		||||
	BLAH_ASSERT(data.hlsl_attributes.size() > 0 || App::renderer() != Renderer::D3D11, "D3D11 Shaders must have hlsl_attributes assigned");
 | 
			
		||||
	BLAH_ASSERT(data.hlsl_attributes.size() > 0 || App::renderer().type != RendererType::D3D11, "D3D11 Shaders must have hlsl_attributes assigned");
 | 
			
		||||
 | 
			
		||||
	auto shader = Graphics::create_shader(&data);
 | 
			
		||||
	ShaderRef shader;
 | 
			
		||||
 | 
			
		||||
	if (Renderer::instance)
 | 
			
		||||
		shader = Renderer::instance->create_shader(&data);
 | 
			
		||||
	
 | 
			
		||||
	// validate the shader
 | 
			
		||||
	if (shader)
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
#include <blah/graphics/target.h>
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
#include "../internal/renderer.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ TargetRef Target::create(int width, int height)
 | 
			
		||||
 | 
			
		||||
TargetRef Target::create(int width, int height, const AttachmentFormats& textures)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RENDERER();
 | 
			
		||||
	BLAH_ASSERT(width > 0 && height > 0, "Target width and height must be larger than 0");
 | 
			
		||||
	BLAH_ASSERT(textures.size() > 0, "At least one texture must be provided");
 | 
			
		||||
 | 
			
		||||
@ -29,7 +30,10 @@ TargetRef Target::create(int width, int height, const AttachmentFormats& texture
 | 
			
		||||
	BLAH_ASSERT(depth_count <= 1, "Target can only have 1 Depth/Stencil Texture");
 | 
			
		||||
	BLAH_ASSERT(color_count <= Attachments::capacity - 1, "Exceeded maximum Color texture count");
 | 
			
		||||
 | 
			
		||||
	return Graphics::create_target(width, height, textures.data(), textures.size());
 | 
			
		||||
	if (Renderer::instance)
 | 
			
		||||
		return Renderer::instance->create_target(width, height, textures.data(), textures.size());
 | 
			
		||||
 | 
			
		||||
	return TargetRef();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TextureRef& Target::texture(int index)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
#include <blah/images/image.h>
 | 
			
		||||
#include <blah/streams/stream.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
#include "../internal/renderer.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,13 @@ TextureRef Texture::create(const Image& image)
 | 
			
		||||
 | 
			
		||||
TextureRef Texture::create(int width, int height, TextureFormat format, unsigned char* data)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT_RENDERER();
 | 
			
		||||
	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");
 | 
			
		||||
 | 
			
		||||
	auto tex = Graphics::create_texture(width, height, format);
 | 
			
		||||
	if (Renderer::instance)
 | 
			
		||||
	{
 | 
			
		||||
		auto tex = Renderer::instance->create_texture(width, height, format);
 | 
			
		||||
 | 
			
		||||
		if (tex && data != nullptr)
 | 
			
		||||
			tex->set_data(data);
 | 
			
		||||
@ -24,6 +27,9 @@ TextureRef Texture::create(int width, int height, TextureFormat format, unsigned
 | 
			
		||||
		return tex;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return TextureRef();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TextureRef Texture::create(Stream& stream)
 | 
			
		||||
{
 | 
			
		||||
	return create(Image(stream));
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
#include <blah/numerics/calc.h>
 | 
			
		||||
#include "internal/input.h"
 | 
			
		||||
#include "internal/platform.h"
 | 
			
		||||
#include <blah/graphics/target.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
@ -124,8 +125,8 @@ void MouseState::on_move(const Vec2f& pos, const Vec2f& screen_pos)
 | 
			
		||||
	position = pos;
 | 
			
		||||
	screen_position = screen_pos;
 | 
			
		||||
 | 
			
		||||
	Point size = Point(App::width(), App::height());
 | 
			
		||||
	Point draw = Point(App::draw_width(), App::draw_height());
 | 
			
		||||
	Point size = App::get_size();
 | 
			
		||||
	Point draw = Point(App::backbuffer()->width(), App::backbuffer()->height());
 | 
			
		||||
 | 
			
		||||
	draw_position.x = (position.x / (float)size.x) * draw.x;
 | 
			
		||||
	draw_position.y = (position.y / (float)size.y) * draw.y;
 | 
			
		||||
 | 
			
		||||
@ -1,60 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include <blah/graphics/renderpass.h>
 | 
			
		||||
#include <blah/graphics/texture.h>
 | 
			
		||||
#include <blah/graphics/target.h>
 | 
			
		||||
#include <blah/graphics/shader.h>
 | 
			
		||||
#include <blah/graphics/mesh.h>
 | 
			
		||||
#include <blah/graphics/material.h>
 | 
			
		||||
#include <blah/numerics/color.h>
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	// Graphics backend API used for rendering.
 | 
			
		||||
	// All rendering ends up going through here.
 | 
			
		||||
	namespace Graphics
 | 
			
		||||
	{
 | 
			
		||||
		// Initializes the graphics backend
 | 
			
		||||
		bool init();
 | 
			
		||||
 | 
			
		||||
		// Shuts down the graphics backend
 | 
			
		||||
		void shutdown();
 | 
			
		||||
 | 
			
		||||
		// Returns info about the renderer
 | 
			
		||||
		const RendererFeatures& features();
 | 
			
		||||
 | 
			
		||||
		// Returns the renderer type
 | 
			
		||||
		Renderer renderer();
 | 
			
		||||
 | 
			
		||||
		// Called once per frame
 | 
			
		||||
		void update();
 | 
			
		||||
 | 
			
		||||
		// Called before rendering begins
 | 
			
		||||
		void before_render();
 | 
			
		||||
 | 
			
		||||
		// Called after renderings ends
 | 
			
		||||
		void after_render();
 | 
			
		||||
 | 
			
		||||
		// Performs a draw call
 | 
			
		||||
		void render(const RenderPass& pass);
 | 
			
		||||
 | 
			
		||||
		// Clears the backbuffer
 | 
			
		||||
		void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask);
 | 
			
		||||
 | 
			
		||||
		// Creates a new Texture.
 | 
			
		||||
		// if the Texture is invalid, this should return an empty reference.
 | 
			
		||||
		TextureRef create_texture(int width, int height, TextureFormat format);
 | 
			
		||||
 | 
			
		||||
		// Creates a new Target.
 | 
			
		||||
		// if the Target is invalid, this should return an empty reference.
 | 
			
		||||
		TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count);
 | 
			
		||||
 | 
			
		||||
		// Creates a new Shader.
 | 
			
		||||
		// if the Shader is invalid, this should return an empty reference.
 | 
			
		||||
		ShaderRef create_shader(const ShaderData* data);
 | 
			
		||||
 | 
			
		||||
		// Creates a new Mesh.
 | 
			
		||||
		// if the Mesh is invalid, this should return an empty reference.
 | 
			
		||||
		MeshRef create_mesh();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,215 +0,0 @@
 | 
			
		||||
#if !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11))
 | 
			
		||||
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	class Dummy_Texture : public Texture
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		int m_width;
 | 
			
		||||
		int m_height;
 | 
			
		||||
		TextureFormat m_format;
 | 
			
		||||
		bool m_framebuffer;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
 | 
			
		||||
		Dummy_Texture(int width, int height, TextureFormat format, bool framebuffer)
 | 
			
		||||
		{
 | 
			
		||||
			m_width = width;
 | 
			
		||||
			m_height = height;
 | 
			
		||||
			m_format = format;
 | 
			
		||||
			m_framebuffer = framebuffer;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual int width() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_width;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual int height() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_height;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual TextureFormat format() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_format;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void set_data(unsigned char* data) override
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void get_data(unsigned char* data) override
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual bool is_framebuffer() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_framebuffer;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class Dummy_Target : public Target
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		Attachments m_attachments;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
 | 
			
		||||
		Dummy_Target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
		{
 | 
			
		||||
			for (int i = 0; i < attachmentCount; i++)
 | 
			
		||||
			{
 | 
			
		||||
				m_attachments.push_back(
 | 
			
		||||
					TextureRef(new Dummy_Texture(width, height, attachments[i], true))
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual Attachments& textures() override
 | 
			
		||||
		{
 | 
			
		||||
			return m_attachments;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual const Attachments& textures() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_attachments;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void clear(Color color) override
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class Dummy_Shader : public Shader
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		Vector<UniformInfo> m_uniforms;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		Dummy_Shader(const ShaderData* data)
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual Vector<UniformInfo>& uniforms() override
 | 
			
		||||
		{
 | 
			
		||||
			return m_uniforms;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual const Vector<UniformInfo>& uniforms() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_uniforms;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class Dummy_Mesh : public Mesh
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		i64 m_index_count = 0;
 | 
			
		||||
		i64 m_vertex_count = 0;
 | 
			
		||||
		i64 m_instance_count = 0;
 | 
			
		||||
	public:
 | 
			
		||||
 | 
			
		||||
		Dummy_Mesh()
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void index_data(IndexFormat format, const void* indices, i64 count) override
 | 
			
		||||
		{
 | 
			
		||||
			m_index_count = count;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override
 | 
			
		||||
		{
 | 
			
		||||
			m_vertex_count = count;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override
 | 
			
		||||
		{
 | 
			
		||||
			m_instance_count = count;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual i64 index_count() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_index_count;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual i64 vertex_count() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_vertex_count;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual i64 instance_count() const override
 | 
			
		||||
		{
 | 
			
		||||
			return m_instance_count;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool Graphics::init()
 | 
			
		||||
	{
 | 
			
		||||
		Log::info("Dummy Renderer");
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Renderer Graphics::renderer()
 | 
			
		||||
	{
 | 
			
		||||
		return Renderer::None;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const RendererFeatures& Graphics::features()
 | 
			
		||||
	{
 | 
			
		||||
		static const RendererFeatures features{ false, true, 4096 };
 | 
			
		||||
		return features;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::update() {}
 | 
			
		||||
	void Graphics::before_render() {}
 | 
			
		||||
	void Graphics::after_render() {}
 | 
			
		||||
 | 
			
		||||
	TextureRef Graphics::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	{
 | 
			
		||||
		return TextureRef(new Dummy_Texture(width, height, format, false));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
	{
 | 
			
		||||
		return TargetRef(new Dummy_Target(width, height, attachments, attachmentCount));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ShaderRef Graphics::create_shader(const ShaderData* data)
 | 
			
		||||
	{
 | 
			
		||||
		return ShaderRef(new Dummy_Shader(data));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MeshRef Graphics::create_mesh()
 | 
			
		||||
	{
 | 
			
		||||
		return MeshRef(new Dummy_Mesh());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::render(const RenderPass& pass)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::clear_backbuffer(Color color)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11))
 | 
			
		||||
@ -97,6 +97,9 @@ namespace Blah
 | 
			
		||||
		// gets the contents of the clipboard into the given string
 | 
			
		||||
		const char* get_clipboard();
 | 
			
		||||
 | 
			
		||||
		// Tries to open a URL in a web browser
 | 
			
		||||
		void open_url(const char* url);
 | 
			
		||||
 | 
			
		||||
		// OpenGL Methods
 | 
			
		||||
		void* gl_get_func(const char* name);
 | 
			
		||||
		void* gl_context_create();
 | 
			
		||||
@ -105,8 +108,5 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
		// D3D11 Methods
 | 
			
		||||
		void* d3d11_get_hwnd();
 | 
			
		||||
 | 
			
		||||
		// Tries to open a URL in a web browser
 | 
			
		||||
		void open_url(const char* url);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include "input.h"
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "renderer.h"
 | 
			
		||||
#include <blah/input.h>
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include <blah/filesystem.h>
 | 
			
		||||
@ -37,7 +37,7 @@ namespace Blah
 | 
			
		||||
		char* base_path = nullptr;
 | 
			
		||||
		char* user_path = nullptr;
 | 
			
		||||
		bool displayed = false;
 | 
			
		||||
	};
 | 
			
		||||
	} g_platform;
 | 
			
		||||
 | 
			
		||||
	// Blah SDL2 File
 | 
			
		||||
	class SDL2File : public File
 | 
			
		||||
@ -118,8 +118,6 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
static Blah::SDL2Platform g_platform;
 | 
			
		||||
 | 
			
		||||
bool Platform::init(const Config& config)
 | 
			
		||||
{
 | 
			
		||||
	g_platform = SDL2Platform();
 | 
			
		||||
@ -150,7 +148,7 @@ bool Platform::init(const Config& config)
 | 
			
		||||
	int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
 | 
			
		||||
 | 
			
		||||
	// enable OpenGL
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	if (config.renderer_type == RendererType::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		flags |= SDL_WINDOW_OPENGL;
 | 
			
		||||
 | 
			
		||||
@ -217,7 +215,7 @@ void Platform::ready()
 | 
			
		||||
	// enable V-Sync
 | 
			
		||||
	// TODO:
 | 
			
		||||
	// This should be a toggle or controllable in some way
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	if (App::renderer().type == RendererType::OpenGL)
 | 
			
		||||
		SDL_GL_SetSwapInterval(1);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
@ -454,7 +452,7 @@ void Platform::sleep(int milliseconds)
 | 
			
		||||
 | 
			
		||||
void Platform::present()
 | 
			
		||||
{
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	if (App::renderer().type == RendererType::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GL_SwapWindow(g_platform.window);
 | 
			
		||||
	}
 | 
			
		||||
@ -514,7 +512,7 @@ void Platform::set_size(int width, int height)
 | 
			
		||||
 | 
			
		||||
void Platform::get_draw_size(int* width, int* height)
 | 
			
		||||
{
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	if (App::renderer().type == RendererType::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GL_GetDrawableSize(g_platform.window, width, height);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,11 @@
 | 
			
		||||
#ifdef BLAH_PLATFORM_WIN32
 | 
			
		||||
 | 
			
		||||
// Note:
 | 
			
		||||
// This backend implementation is unfinished! 
 | 
			
		||||
// It's missing a few things, namely:
 | 
			
		||||
// - Controller Support
 | 
			
		||||
// (And error testing)
 | 
			
		||||
// This is unfinished! It is missing Controller Support!
 | 
			
		||||
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include "input.h"
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "renderer.h"
 | 
			
		||||
#include <blah/input.h>
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include <blah/filesystem.h>
 | 
			
		||||
@ -22,11 +19,20 @@
 | 
			
		||||
#include <shellapi.h>   // for file explore
 | 
			
		||||
#include <shlobj.h>     // for known folder
 | 
			
		||||
#include <chrono>       // for ticks method
 | 
			
		||||
#include <Xinput.h>     // for XInput
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	using Duration = std::chrono::system_clock::duration;
 | 
			
		||||
 | 
			
		||||
	typedef HRESULT(WINAPI* DirectInput8Create_fn)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN);
 | 
			
		||||
	typedef DWORD(WINAPI* XInputGetCapabilities_fn)(DWORD, DWORD, XINPUT_CAPABILITIES*);
 | 
			
		||||
	typedef DWORD(WINAPI* XInputGetState_fn)(DWORD, XINPUT_STATE*);
 | 
			
		||||
	typedef void*(WINAPI* wglGetProcAddress_fn)(const char*);
 | 
			
		||||
	typedef HGLRC(WINAPI* wglCreateContext_fn)(HDC);
 | 
			
		||||
	typedef BOOL(WINAPI* wglDeleteContext_fn)(HGLRC);
 | 
			
		||||
	typedef BOOL(WINAPI* wglMakeCurrent_fn)(HDC, HGLRC);
 | 
			
		||||
 | 
			
		||||
	// Win32 Platform State
 | 
			
		||||
	struct Win32Platform
 | 
			
		||||
	{
 | 
			
		||||
@ -38,14 +44,34 @@ namespace Blah
 | 
			
		||||
		RECT         windowed_position;
 | 
			
		||||
		bool         fullscreen = false;
 | 
			
		||||
		InputState*  input_state = nullptr;
 | 
			
		||||
		String       clipboard;
 | 
			
		||||
 | 
			
		||||
		// XInput
 | 
			
		||||
		struct
 | 
			
		||||
		{
 | 
			
		||||
			HMODULE dll;
 | 
			
		||||
			XInputGetCapabilities_fn get_capabilities;
 | 
			
		||||
			XInputGetState_fn get_state;
 | 
			
		||||
		} xinput;
 | 
			
		||||
 | 
			
		||||
		struct Joystick
 | 
			
		||||
		{
 | 
			
		||||
			bool connected = false;
 | 
			
		||||
			bool accounted = false;
 | 
			
		||||
			GUID dinstance = GUID_NULL;
 | 
			
		||||
			DWORD xindex   = 0;
 | 
			
		||||
		} joysticks[Input::max_controllers];
 | 
			
		||||
 | 
			
		||||
		// OpenGL Methods
 | 
			
		||||
		// These are only loaded if built using the OpenGL Backend
 | 
			
		||||
		HMODULE  opengl_dll;
 | 
			
		||||
		void*    (WINAPI* wglGetProcAddress)  (const char* proc);
 | 
			
		||||
		HGLRC    (WINAPI* wglCreateContext)   (HDC hdc);
 | 
			
		||||
		BOOL     (WINAPI* wglDeleteContext)   (HGLRC hglrc);
 | 
			
		||||
		BOOL     (WINAPI* wglMakeCurrent)     (HDC hdc, HGLRC hglrc);
 | 
			
		||||
		struct
 | 
			
		||||
		{
 | 
			
		||||
			HMODULE dll;
 | 
			
		||||
			wglGetProcAddress_fn get_proc_address;
 | 
			
		||||
			wglCreateContext_fn create_context;
 | 
			
		||||
			wglDeleteContext_fn delete_context;
 | 
			
		||||
			wglMakeCurrent_fn make_current;
 | 
			
		||||
		} gl;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Win32 File Class
 | 
			
		||||
@ -53,11 +79,16 @@ namespace Blah
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		HANDLE m_handle;
 | 
			
		||||
		LARGE_INTEGER m_size;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		Win32File(HANDLE handle)
 | 
			
		||||
		{
 | 
			
		||||
			m_handle = handle;
 | 
			
		||||
 | 
			
		||||
			LARGE_INTEGER file_size;
 | 
			
		||||
			if (GetFileSizeEx(m_handle, &file_size))
 | 
			
		||||
				m_size = file_size;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~Win32File()
 | 
			
		||||
@ -67,13 +98,7 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
		size_t length() override
 | 
			
		||||
		{
 | 
			
		||||
			// Todo: cache this value? not sure how performant it is
 | 
			
		||||
 | 
			
		||||
			LARGE_INTEGER file_size;
 | 
			
		||||
			if (GetFileSizeEx(m_handle, &file_size))
 | 
			
		||||
				return file_size.QuadPart;
 | 
			
		||||
 | 
			
		||||
			return 0;
 | 
			
		||||
			return m_size.QuadPart;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t position() override
 | 
			
		||||
@ -149,11 +174,14 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Main Windows Procedure callback
 | 
			
		||||
	LRESULT CALLBACK win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 | 
			
		||||
 | 
			
		||||
	// Converts Windows scancode to Blah key
 | 
			
		||||
	Key win32_scancode_to_key(WPARAM wParam, LPARAM lParam);
 | 
			
		||||
 | 
			
		||||
	// Main Windows Procedure callback
 | 
			
		||||
	LRESULT CALLBACK win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 | 
			
		||||
	// Detects joysticks (connects & disconnects)
 | 
			
		||||
	void win32_detect_joysticks();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
@ -198,22 +226,30 @@ bool Platform::init(const Config& config)
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Setup Window Size based on content scale
 | 
			
		||||
	{
 | 
			
		||||
		auto scale = get_content_scale();
 | 
			
		||||
		int sw = (int)(App::config().width * scale);
 | 
			
		||||
		int sh = (int)(App::config().height * scale);
 | 
			
		||||
		set_size(sw, sh);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create the OpenGL device info
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	if (config.renderer_type == RendererType::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		// Load the DLL
 | 
			
		||||
		g_platform.opengl_dll = LoadLibraryA("opengl32.dll");
 | 
			
		||||
		if (g_platform.opengl_dll == NULL)
 | 
			
		||||
		g_platform.gl.dll = LoadLibraryA("opengl32.dll");
 | 
			
		||||
		if (g_platform.gl.dll == NULL)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Get the Windows GL functions we need
 | 
			
		||||
		g_platform.wglGetProcAddress = (void* (WINAPI*)(const char*))GetProcAddress(g_platform.opengl_dll, "wglGetProcAddress");
 | 
			
		||||
		g_platform.wglCreateContext = (HGLRC(WINAPI*) (HDC))GetProcAddress(g_platform.opengl_dll, "wglCreateContext");
 | 
			
		||||
		g_platform.wglDeleteContext = (BOOL(WINAPI*) (HGLRC))GetProcAddress(g_platform.opengl_dll, "wglDeleteContext");
 | 
			
		||||
		g_platform.wglMakeCurrent = (BOOL(WINAPI*) (HDC, HGLRC))GetProcAddress(g_platform.opengl_dll, "wglMakeCurrent");
 | 
			
		||||
		g_platform.gl.get_proc_address = (wglGetProcAddress_fn)GetProcAddress(g_platform.gl.dll, "wglGetProcAddress");
 | 
			
		||||
		g_platform.gl.create_context = (wglCreateContext_fn)GetProcAddress(g_platform.gl.dll, "wglCreateContext");
 | 
			
		||||
		g_platform.gl.delete_context = (wglDeleteContext_fn)GetProcAddress(g_platform.gl.dll, "wglDeleteContext");
 | 
			
		||||
		g_platform.gl.make_current = (wglMakeCurrent_fn)GetProcAddress(g_platform.gl.dll, "wglMakeCurrent");
 | 
			
		||||
 | 
			
		||||
		// TODO:
 | 
			
		||||
		// Allow the user to apply (some of) these values before instantiation.
 | 
			
		||||
@ -250,8 +286,25 @@ bool Platform::init(const Config& config)
 | 
			
		||||
		SetPixelFormat(hdc, pixel_format, &pfd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Reset our game timer
 | 
			
		||||
	g_platform.start_time = std::chrono::system_clock::now().time_since_epoch();
 | 
			
		||||
	// xinput api
 | 
			
		||||
	{
 | 
			
		||||
		const char* dlls[] = { "xinput1_4.dll", "xinput1_3.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll", NULL };
 | 
			
		||||
 | 
			
		||||
		for (int i = 0; dlls[i]; i++)
 | 
			
		||||
		{
 | 
			
		||||
			g_platform.xinput.dll = LoadLibraryA(dlls[i]);
 | 
			
		||||
 | 
			
		||||
			if (g_platform.xinput.dll)
 | 
			
		||||
			{
 | 
			
		||||
				g_platform.xinput.get_capabilities = (XInputGetCapabilities_fn)GetProcAddress(g_platform.xinput.dll, "XInputGetCapabilities");
 | 
			
		||||
				g_platform.xinput.get_state = (XInputGetState_fn)GetProcAddress(g_platform.xinput.dll, "XInputGetState");
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!g_platform.xinput.dll)
 | 
			
		||||
			Log::warn("Failed to find XInput dll; No Controller Support");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get Working Directory
 | 
			
		||||
	{
 | 
			
		||||
@ -283,6 +336,9 @@ bool Platform::init(const Config& config)
 | 
			
		||||
		CoTaskMemFree(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Reset our game timer
 | 
			
		||||
	g_platform.start_time = std::chrono::system_clock::now().time_since_epoch();
 | 
			
		||||
 | 
			
		||||
	// Not currently fullscreen
 | 
			
		||||
	g_platform.fullscreen = false;
 | 
			
		||||
 | 
			
		||||
@ -292,20 +348,18 @@ bool Platform::init(const Config& config)
 | 
			
		||||
 | 
			
		||||
void Platform::ready()
 | 
			
		||||
{
 | 
			
		||||
	// Setup Window Size
 | 
			
		||||
	{
 | 
			
		||||
		auto scale = get_content_scale();
 | 
			
		||||
		int sw = (int)(App::config().width * scale);
 | 
			
		||||
		int sh = (int)(App::config().height * scale);
 | 
			
		||||
		set_size(sw, sh);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Display the game window
 | 
			
		||||
	ShowWindow(g_platform.hwnd, SW_SHOW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::shutdown()
 | 
			
		||||
{
 | 
			
		||||
	if (g_platform.xinput.dll)
 | 
			
		||||
		FreeLibrary(g_platform.xinput.dll);
 | 
			
		||||
 | 
			
		||||
	if (g_platform.gl.dll)
 | 
			
		||||
		FreeLibrary(g_platform.gl.dll);
 | 
			
		||||
 | 
			
		||||
	DestroyWindow(g_platform.hwnd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -334,6 +388,17 @@ LRESULT CALLBACK Blah::win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam
 | 
			
		||||
		PostQuitMessage(0);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// Controller connected event
 | 
			
		||||
	case WM_DEVICECHANGE:
 | 
			
		||||
	{
 | 
			
		||||
		// DBT_DEVNODES_CHANGED = 0x0007
 | 
			
		||||
		// https://docs.microsoft.com/en-us/windows/win32/devio/wm-devicechange
 | 
			
		||||
		if (wParam == 0x0007)
 | 
			
		||||
			win32_detect_joysticks();
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Mouse Input
 | 
			
		||||
	case WM_LBUTTONDOWN:
 | 
			
		||||
		g_platform.input_state->mouse.on_press(MouseButton::Left);
 | 
			
		||||
@ -410,8 +475,13 @@ LRESULT CALLBACK Blah::win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam
 | 
			
		||||
void Platform::update(InputState& state)
 | 
			
		||||
{
 | 
			
		||||
	// store reference to input state
 | 
			
		||||
	bool first_update = g_platform.input_state == nullptr;
 | 
			
		||||
	g_platform.input_state = &state;
 | 
			
		||||
 | 
			
		||||
	// if this is the first update, poll joysticks that are already connected
 | 
			
		||||
	if (first_update)
 | 
			
		||||
		win32_detect_joysticks();
 | 
			
		||||
 | 
			
		||||
	// Catch & Dispatch Window Messages
 | 
			
		||||
	MSG msg;
 | 
			
		||||
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
 | 
			
		||||
@ -429,7 +499,7 @@ void Platform::sleep(int milliseconds)
 | 
			
		||||
 | 
			
		||||
void Platform::present()
 | 
			
		||||
{
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	if (App::renderer().type == RendererType::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		HDC hdc = GetDC(g_platform.hwnd);
 | 
			
		||||
		SwapBuffers(hdc);
 | 
			
		||||
@ -463,6 +533,12 @@ void Platform::set_position(int x, int y)
 | 
			
		||||
	SetWindowPos(g_platform.hwnd, NULL, x, y, w, h, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::get_focused()
 | 
			
		||||
{
 | 
			
		||||
	Log::warn("App::focused not implemented for Win32 yet");
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_fullscreen(bool enabled)
 | 
			
		||||
{
 | 
			
		||||
	if (g_platform.fullscreen == enabled)
 | 
			
		||||
@ -628,30 +704,19 @@ FileRef Platform::file_open(const char* path, FileMode mode)
 | 
			
		||||
	return FileRef(new Win32File(result));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clipboard
 | 
			
		||||
void Platform::set_clipboard(const char* text)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT(false, "Not Implemented Yet");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* Platform::get_clipboard()
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT(false, "Not Implemented Yet");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* Platform::gl_get_func(const char* name)
 | 
			
		||||
{
 | 
			
		||||
	// this check is taken from https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions
 | 
			
		||||
	// wglGetProcAddress doesn't always return valid pointers for some specific methods?
 | 
			
		||||
 | 
			
		||||
	void* p = (void*)g_platform.wglGetProcAddress(name);
 | 
			
		||||
	void* p = (void*)g_platform.gl.get_proc_address(name);
 | 
			
		||||
	if ((p == 0) ||
 | 
			
		||||
		(p == (void*)0x1) ||
 | 
			
		||||
		(p == (void*)0x2) ||
 | 
			
		||||
		(p == (void*)0x3) ||
 | 
			
		||||
		(p == (void*)-1))
 | 
			
		||||
	{
 | 
			
		||||
		p = (void*)GetProcAddress(g_platform.opengl_dll, name);
 | 
			
		||||
		p = (void*)GetProcAddress(g_platform.gl.dll, name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p;
 | 
			
		||||
@ -660,7 +725,7 @@ void* Platform::gl_get_func(const char* name)
 | 
			
		||||
void* Platform::gl_context_create()
 | 
			
		||||
{
 | 
			
		||||
	HDC hdc = GetDC(g_platform.hwnd);
 | 
			
		||||
	return g_platform.wglCreateContext(hdc);
 | 
			
		||||
	return g_platform.gl.create_context(hdc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::gl_context_make_current(void* context)
 | 
			
		||||
@ -668,15 +733,15 @@ void Platform::gl_context_make_current(void* context)
 | 
			
		||||
	if (context != nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		HDC hdc = GetDC(g_platform.hwnd);
 | 
			
		||||
		g_platform.wglMakeCurrent(hdc, (HGLRC)context);
 | 
			
		||||
		g_platform.gl.make_current(hdc, (HGLRC)context);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		g_platform.wglMakeCurrent(NULL, NULL);
 | 
			
		||||
		g_platform.gl.make_current(NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::gl_context_destroy(void* context)
 | 
			
		||||
{
 | 
			
		||||
	g_platform.wglDeleteContext((HGLRC)context);
 | 
			
		||||
	g_platform.gl.delete_context((HGLRC)context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* Platform::d3d11_get_hwnd()
 | 
			
		||||
@ -864,4 +929,116 @@ Key Blah::win32_scancode_to_key(WPARAM wParam, LPARAM lParam)
 | 
			
		||||
	return Key::Unknown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Blah::win32_detect_joysticks()
 | 
			
		||||
{
 | 
			
		||||
	// mark all joysticks as unnacounted for
 | 
			
		||||
	for (int i = 0; i < Input::max_controllers; i++)
 | 
			
		||||
		g_platform.joysticks[i].accounted = false;
 | 
			
		||||
 | 
			
		||||
	// check for xinput controllers
 | 
			
		||||
	if (g_platform.xinput.dll)
 | 
			
		||||
	{
 | 
			
		||||
		for (DWORD index = 0; index < XUSER_MAX_COUNT; index++)
 | 
			
		||||
		{
 | 
			
		||||
			// can't get capabilities; not connected
 | 
			
		||||
			XINPUT_CAPABILITIES xic;
 | 
			
		||||
			if (g_platform.xinput.get_capabilities(index, 0, &xic) != ERROR_SUCCESS)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			// already connected
 | 
			
		||||
			bool already_connected = false;
 | 
			
		||||
			for (int i = 0; i < Input::max_controllers; i++)
 | 
			
		||||
			{
 | 
			
		||||
				auto& it = g_platform.joysticks[i];
 | 
			
		||||
				if (it.connected && it.dinstance == GUID_NULL && it.xindex == index)
 | 
			
		||||
				{
 | 
			
		||||
					it.accounted = true;
 | 
			
		||||
					already_connected = true;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (already_connected)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			// find an empty slot and mark connected
 | 
			
		||||
			for (int i = 0; i < Input::max_controllers; i++)
 | 
			
		||||
			{
 | 
			
		||||
				auto& it = g_platform.joysticks[i];
 | 
			
		||||
				if (!it.connected)
 | 
			
		||||
				{
 | 
			
		||||
					it.connected = it.accounted = true;
 | 
			
		||||
					it.dinstance = GUID_NULL;
 | 
			
		||||
					it.xindex = index;
 | 
			
		||||
 | 
			
		||||
					Log::info("Connected XInput [%i]", i);
 | 
			
		||||
 | 
			
		||||
					// TODO:
 | 
			
		||||
					// Get Product Info & Proper Name
 | 
			
		||||
					g_platform.input_state->controllers[i].on_connect("Xbox Controller", true, 15, 6, 0, 0, 0);
 | 
			
		||||
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// call disconnect on joysticks that aren't accounted for
 | 
			
		||||
	for (int i = 0; i < Input::max_controllers; i++)
 | 
			
		||||
	{
 | 
			
		||||
		auto& it = g_platform.joysticks[i];
 | 
			
		||||
		if (it.connected && !it.accounted)
 | 
			
		||||
		{
 | 
			
		||||
			Log::info("Disconnected [%i]", i);
 | 
			
		||||
			g_platform.input_state->controllers[i].on_disconnect();
 | 
			
		||||
			it = Win32Platform::Joystick();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_clipboard(const char* text)
 | 
			
		||||
{
 | 
			
		||||
	auto len = strlen(text);
 | 
			
		||||
	if (auto glob = GlobalAlloc(GMEM_MOVEABLE, len))
 | 
			
		||||
	{
 | 
			
		||||
		if (auto data = GlobalLock(glob))
 | 
			
		||||
		{
 | 
			
		||||
			memcpy(data, text, len);
 | 
			
		||||
			GlobalUnlock(glob);
 | 
			
		||||
 | 
			
		||||
			if (OpenClipboard(nullptr))
 | 
			
		||||
			{
 | 
			
		||||
				SetClipboardData(CF_TEXT, data);
 | 
			
		||||
				CloseClipboard();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GlobalFree(glob);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
const char* Platform::get_clipboard()
 | 
			
		||||
{
 | 
			
		||||
	if (OpenClipboard(nullptr))
 | 
			
		||||
	{
 | 
			
		||||
		HANDLE data = GetClipboardData(CF_TEXT);
 | 
			
		||||
		if (data)
 | 
			
		||||
		{
 | 
			
		||||
			auto text = static_cast<const char*>(GlobalLock(data));
 | 
			
		||||
			if (text)
 | 
			
		||||
				g_platform.clipboard = text;
 | 
			
		||||
			GlobalUnlock(data);
 | 
			
		||||
		}
 | 
			
		||||
		CloseClipboard();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return g_platform.clipboard.cstr();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::open_url(const char* url)
 | 
			
		||||
{
 | 
			
		||||
	auto cmd = String("start ") + url;
 | 
			
		||||
	system(cmd.cstr());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // BLAH_PLATFORM_WIN32
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										90
									
								
								src/internal/renderer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/internal/renderer.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include <blah/graphics/renderpass.h>
 | 
			
		||||
#include <blah/graphics/texture.h>
 | 
			
		||||
#include <blah/graphics/target.h>
 | 
			
		||||
#include <blah/graphics/shader.h>
 | 
			
		||||
#include <blah/graphics/mesh.h>
 | 
			
		||||
#include <blah/graphics/material.h>
 | 
			
		||||
#include <blah/numerics/color.h>
 | 
			
		||||
 | 
			
		||||
#define BLAH_ASSERT_RENDERER() BLAH_ASSERT(Renderer::instance, "Renderer has not been created")
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	class Renderer
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
 | 
			
		||||
		// Reference to the current Renderer in use
 | 
			
		||||
		inline static Renderer* instance = nullptr;
 | 
			
		||||
 | 
			
		||||
		// Renderer Features
 | 
			
		||||
		RendererFeatures features;
 | 
			
		||||
 | 
			
		||||
		virtual ~Renderer() = default;
 | 
			
		||||
 | 
			
		||||
		// Initialize the Graphics
 | 
			
		||||
		virtual bool init() = 0;
 | 
			
		||||
 | 
			
		||||
		// Shutdown the Graphics
 | 
			
		||||
		virtual void shutdown() = 0;
 | 
			
		||||
 | 
			
		||||
		// Called once per frame
 | 
			
		||||
		virtual void update() = 0;
 | 
			
		||||
 | 
			
		||||
		// Called before rendering begins
 | 
			
		||||
		virtual void before_render() = 0;
 | 
			
		||||
 | 
			
		||||
		// Called after renderings ends
 | 
			
		||||
		virtual void after_render() = 0;
 | 
			
		||||
 | 
			
		||||
		// Performs a draw call
 | 
			
		||||
		virtual void render(const RenderPass& pass) = 0;
 | 
			
		||||
 | 
			
		||||
		// Clears the backbuffer
 | 
			
		||||
		virtual void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) = 0;
 | 
			
		||||
 | 
			
		||||
		// Creates a new Texture.
 | 
			
		||||
		// if the Texture is invalid, this should return an empty reference.
 | 
			
		||||
		virtual TextureRef create_texture(int width, int height, TextureFormat format) = 0;
 | 
			
		||||
 | 
			
		||||
		// Creates a new Target.
 | 
			
		||||
		// if the Target is invalid, this should return an empty reference.
 | 
			
		||||
		virtual TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count) = 0;
 | 
			
		||||
 | 
			
		||||
		// Creates a new Shader.
 | 
			
		||||
		// if the Shader is invalid, this should return an empty reference.
 | 
			
		||||
		virtual ShaderRef create_shader(const ShaderData* data) = 0;
 | 
			
		||||
 | 
			
		||||
		// Creates a new Mesh.
 | 
			
		||||
		// if the Mesh is invalid, this should return an empty reference.
 | 
			
		||||
		virtual MeshRef create_mesh() = 0;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		static Renderer* try_make_opengl();
 | 
			
		||||
		static Renderer* try_make_d3d11();
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		static Renderer* try_make_renderer(RendererType type)
 | 
			
		||||
		{
 | 
			
		||||
			switch (type)
 | 
			
		||||
			{
 | 
			
		||||
			case RendererType::None: return nullptr;
 | 
			
		||||
			case RendererType::OpenGL: return try_make_opengl();
 | 
			
		||||
			case RendererType::D3D11: return try_make_d3d11();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static RendererType default_type()
 | 
			
		||||
		{
 | 
			
		||||
#if defined(BLAH_RENDERER_D3D11) and defined(_WIN32)
 | 
			
		||||
			return RendererType::D3D11;
 | 
			
		||||
#else
 | 
			
		||||
			return RendererType::OpenGL;
 | 
			
		||||
#endif
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
#ifdef BLAH_GRAPHICS_D3D11
 | 
			
		||||
#ifdef BLAH_RENDERER_D3D11
 | 
			
		||||
 | 
			
		||||
// TODO:
 | 
			
		||||
// Note the D3D11 Implementation is still a work-in-progress
 | 
			
		||||
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "renderer.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
@ -15,12 +15,16 @@
 | 
			
		||||
#include <d3d11.h>
 | 
			
		||||
#include <d3dcompiler.h>
 | 
			
		||||
 | 
			
		||||
// shorthand to our internal state
 | 
			
		||||
#define renderer ((Renderer_D3D11*)Renderer::instance)
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	class D3D11_Shader;
 | 
			
		||||
 | 
			
		||||
	struct D3D11
 | 
			
		||||
	class Renderer_D3D11 : public Renderer
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		// main resources
 | 
			
		||||
		ID3D11Device* device = nullptr;
 | 
			
		||||
		ID3D11DeviceContext* context = nullptr;
 | 
			
		||||
@ -28,9 +32,6 @@ namespace Blah
 | 
			
		||||
		ID3D11RenderTargetView* backbuffer_view = nullptr;
 | 
			
		||||
		ID3D11DepthStencilView* backbuffer_depth_view = nullptr;
 | 
			
		||||
 | 
			
		||||
		// supported renderer features
 | 
			
		||||
		RendererFeatures features;
 | 
			
		||||
 | 
			
		||||
		// last backbuffer size
 | 
			
		||||
		Point last_size;
 | 
			
		||||
 | 
			
		||||
@ -72,6 +73,18 @@ namespace Blah
 | 
			
		||||
		Vector<StoredSampler> sampler_cache;
 | 
			
		||||
		Vector<StoredDepthStencil> depthstencil_cache;
 | 
			
		||||
 | 
			
		||||
		bool init() override;
 | 
			
		||||
		void shutdown() override;
 | 
			
		||||
		void update() override;
 | 
			
		||||
		void before_render() override;
 | 
			
		||||
		void after_render() override;
 | 
			
		||||
		void render(const RenderPass& pass) override;
 | 
			
		||||
		void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) override;
 | 
			
		||||
		TextureRef create_texture(int width, int height, TextureFormat format) override;
 | 
			
		||||
		TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count) override;
 | 
			
		||||
		ShaderRef create_shader(const ShaderData* data) override;
 | 
			
		||||
		MeshRef create_mesh() override;
 | 
			
		||||
 | 
			
		||||
		ID3D11InputLayout* get_layout(D3D11_Shader* shader, const VertexFormat& format);
 | 
			
		||||
		ID3D11BlendState* get_blend(const BlendMode& blend);
 | 
			
		||||
		ID3D11RasterizerState* get_rasterizer(const RenderPass& pass);
 | 
			
		||||
@ -79,17 +92,12 @@ namespace Blah
 | 
			
		||||
		ID3D11DepthStencilState* get_depthstencil(const RenderPass& pass);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// D3D11 State
 | 
			
		||||
	D3D11 state;
 | 
			
		||||
 | 
			
		||||
	// Utility Methods
 | 
			
		||||
	D3D11_BLEND_OP blend_op(BlendOp op);
 | 
			
		||||
	D3D11_BLEND blend_factor(BlendFactor factor);
 | 
			
		||||
	bool reflect_uniforms(Vector<UniformInfo>& append_uniforms_to, Vector<ID3D11Buffer*>& append_buffers_to, ID3DBlob* shader, ShaderType shader_type);
 | 
			
		||||
	void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type);
 | 
			
		||||
 | 
			
		||||
	// ~ BEGIN IMPLEMENTATION ~
 | 
			
		||||
 | 
			
		||||
	class D3D11_Texture : public Texture
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
@ -160,7 +168,7 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
			m_dxgi_format = desc.Format;
 | 
			
		||||
 | 
			
		||||
			auto hr = state.device->CreateTexture2D(&desc, NULL, &texture);
 | 
			
		||||
			auto hr = renderer->device->CreateTexture2D(&desc, NULL, &texture);
 | 
			
		||||
			if (!SUCCEEDED(hr))
 | 
			
		||||
			{
 | 
			
		||||
				if (texture)
 | 
			
		||||
@ -171,7 +179,7 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
			if (!is_depth_stencil)
 | 
			
		||||
			{
 | 
			
		||||
				hr = state.device->CreateShaderResourceView(texture, NULL, &view);
 | 
			
		||||
				hr = renderer->device->CreateShaderResourceView(texture, NULL, &view);
 | 
			
		||||
				if (!SUCCEEDED(hr))
 | 
			
		||||
				{
 | 
			
		||||
					texture->Release();
 | 
			
		||||
@ -220,7 +228,7 @@ namespace Blah
 | 
			
		||||
			box.back = 1;
 | 
			
		||||
 | 
			
		||||
			// set data
 | 
			
		||||
			state.context->UpdateSubresource(
 | 
			
		||||
			renderer->context->UpdateSubresource(
 | 
			
		||||
				texture,
 | 
			
		||||
				0,
 | 
			
		||||
				&box,
 | 
			
		||||
@ -249,7 +257,7 @@ namespace Blah
 | 
			
		||||
				desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
 | 
			
		||||
				desc.MiscFlags = 0;
 | 
			
		||||
 | 
			
		||||
				hr = state.device->CreateTexture2D(&desc, NULL, &staging);
 | 
			
		||||
				hr = renderer->device->CreateTexture2D(&desc, NULL, &staging);
 | 
			
		||||
				if (!SUCCEEDED(hr))
 | 
			
		||||
				{
 | 
			
		||||
					BLAH_ASSERT(false, "Failed to create staging texture to get data");
 | 
			
		||||
@ -266,7 +274,7 @@ namespace Blah
 | 
			
		||||
			box.back = 1;
 | 
			
		||||
 | 
			
		||||
			// copy data to staging texture
 | 
			
		||||
			state.context->CopySubresourceRegion(
 | 
			
		||||
			renderer->context->CopySubresourceRegion(
 | 
			
		||||
				staging, 0,
 | 
			
		||||
				0, 0, 0,
 | 
			
		||||
				texture, 0,
 | 
			
		||||
@ -274,7 +282,7 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
			// get data
 | 
			
		||||
			D3D11_MAPPED_SUBRESOURCE map;
 | 
			
		||||
			hr = state.context->Map(staging, 0, D3D11_MAP_READ, 0, &map);
 | 
			
		||||
			hr = renderer->context->Map(staging, 0, D3D11_MAP_READ, 0, &map);
 | 
			
		||||
 | 
			
		||||
			if (!SUCCEEDED(hr))
 | 
			
		||||
			{
 | 
			
		||||
@ -287,7 +295,7 @@ namespace Blah
 | 
			
		||||
			for (int y = 0; y < m_height; y++)
 | 
			
		||||
				memcpy(data + y * bytes_per_row, (unsigned char*)map.pData + map.RowPitch * y, bytes_per_row);
 | 
			
		||||
 | 
			
		||||
			state.context->Unmap(staging, 0);
 | 
			
		||||
			renderer->context->Unmap(staging, 0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bool is_framebuffer() const override
 | 
			
		||||
@ -316,12 +324,12 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
				if (attachments[i] == TextureFormat::DepthStencil)
 | 
			
		||||
				{
 | 
			
		||||
					state.device->CreateDepthStencilView(tex->texture, nullptr, &depth_view);
 | 
			
		||||
					renderer->device->CreateDepthStencilView(tex->texture, nullptr, &depth_view);
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					ID3D11RenderTargetView* view = nullptr;
 | 
			
		||||
					state.device->CreateRenderTargetView(tex->texture, nullptr, &view);
 | 
			
		||||
					renderer->device->CreateRenderTargetView(tex->texture, nullptr, &view);
 | 
			
		||||
					color_views.push_back(view);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@ -355,7 +363,7 @@ namespace Blah
 | 
			
		||||
			if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color)
 | 
			
		||||
			{
 | 
			
		||||
				for (int i = 0; i < color_views.size(); i++)
 | 
			
		||||
					state.context->ClearRenderTargetView(color_views[i], col);
 | 
			
		||||
					renderer->context->ClearRenderTargetView(color_views[i], col);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (depth_view)
 | 
			
		||||
@ -367,7 +375,7 @@ namespace Blah
 | 
			
		||||
					flags |= D3D11_CLEAR_STENCIL;
 | 
			
		||||
 | 
			
		||||
				if (flags != 0)
 | 
			
		||||
					state.context->ClearDepthStencilView(depth_view, flags, depth, stencil);
 | 
			
		||||
					renderer->context->ClearDepthStencilView(depth_view, flags, depth, stencil);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
@ -442,7 +450,7 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
			// create vertex shader
 | 
			
		||||
			{
 | 
			
		||||
				hr = state.device->CreateVertexShader(
 | 
			
		||||
				hr = renderer->device->CreateVertexShader(
 | 
			
		||||
					vertex_blob->GetBufferPointer(),
 | 
			
		||||
					vertex_blob->GetBufferSize(),
 | 
			
		||||
					NULL,
 | 
			
		||||
@ -454,7 +462,7 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
			// create fragment shader
 | 
			
		||||
			{
 | 
			
		||||
				hr = state.device->CreatePixelShader(
 | 
			
		||||
				hr = renderer->device->CreatePixelShader(
 | 
			
		||||
					fragment_blob->GetBufferPointer(),
 | 
			
		||||
					fragment_blob->GetBufferSize(),
 | 
			
		||||
					NULL,
 | 
			
		||||
@ -607,7 +615,7 @@ namespace Blah
 | 
			
		||||
					data.pSysMem = indices;
 | 
			
		||||
 | 
			
		||||
					// create
 | 
			
		||||
					auto hr = state.device->CreateBuffer(&desc, &data, &index_buffer);
 | 
			
		||||
					auto hr = renderer->device->CreateBuffer(&desc, &data, &index_buffer);
 | 
			
		||||
					BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@ -615,13 +623,13 @@ namespace Blah
 | 
			
		||||
			{
 | 
			
		||||
				D3D11_MAPPED_SUBRESOURCE map;
 | 
			
		||||
 | 
			
		||||
				auto hr = state.context->Map(index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
 | 
			
		||||
				auto hr = renderer->context->Map(index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
 | 
			
		||||
				BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data");
 | 
			
		||||
 | 
			
		||||
				if (SUCCEEDED(hr))
 | 
			
		||||
				{
 | 
			
		||||
					memcpy(map.pData, indices, index_stride * count);
 | 
			
		||||
					state.context->Unmap(index_buffer, 0);
 | 
			
		||||
					renderer->context->Unmap(index_buffer, 0);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@ -655,7 +663,7 @@ namespace Blah
 | 
			
		||||
					data.pSysMem = vertices;
 | 
			
		||||
 | 
			
		||||
					// create
 | 
			
		||||
					auto hr = state.device->CreateBuffer(&desc, &data, &vertex_buffer);
 | 
			
		||||
					auto hr = renderer->device->CreateBuffer(&desc, &data, &vertex_buffer);
 | 
			
		||||
					BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@ -663,13 +671,13 @@ namespace Blah
 | 
			
		||||
			else if (vertices)
 | 
			
		||||
			{
 | 
			
		||||
				D3D11_MAPPED_SUBRESOURCE map;
 | 
			
		||||
				auto hr = state.context->Map(vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
 | 
			
		||||
				auto hr = renderer->context->Map(vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
 | 
			
		||||
				BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data");
 | 
			
		||||
 | 
			
		||||
				if (SUCCEEDED(hr))
 | 
			
		||||
				{
 | 
			
		||||
					memcpy(map.pData, vertices, vertex_format.stride * count);
 | 
			
		||||
					state.context->Unmap(vertex_buffer, 0);
 | 
			
		||||
					renderer->context->Unmap(vertex_buffer, 0);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@ -695,10 +703,9 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool Graphics::init()
 | 
			
		||||
	bool Renderer_D3D11::init()
 | 
			
		||||
	{
 | 
			
		||||
		state = D3D11();
 | 
			
		||||
		state.last_size = Point(App::draw_width(), App::draw_height());
 | 
			
		||||
		last_size = Point(App::backbuffer()->width(), App::backbuffer()->height());
 | 
			
		||||
 | 
			
		||||
		// Define Swap Chain
 | 
			
		||||
		DXGI_SWAP_CHAIN_DESC desc = {};
 | 
			
		||||
@ -729,21 +736,21 @@ namespace Blah
 | 
			
		||||
			0,
 | 
			
		||||
			D3D11_SDK_VERSION,
 | 
			
		||||
			&desc,
 | 
			
		||||
			&state.swap_chain,
 | 
			
		||||
			&state.device,
 | 
			
		||||
			&swap_chain,
 | 
			
		||||
			&device,
 | 
			
		||||
			&feature_level,
 | 
			
		||||
			&state.context);
 | 
			
		||||
			&context);
 | 
			
		||||
 | 
			
		||||
		// Exit out if it's not OK
 | 
			
		||||
		if (!SUCCEEDED(hr) || !state.swap_chain || !state.device || !state.context)
 | 
			
		||||
		if (!SUCCEEDED(hr) || !swap_chain || !device || !context)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		// Get the backbuffer
 | 
			
		||||
		ID3D11Texture2D* frame_buffer = nullptr;
 | 
			
		||||
		state.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer);
 | 
			
		||||
		swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer);
 | 
			
		||||
		if (frame_buffer)
 | 
			
		||||
		{
 | 
			
		||||
			state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer_view);
 | 
			
		||||
			device->CreateRenderTargetView(frame_buffer, nullptr, &backbuffer_view);
 | 
			
		||||
			frame_buffer->Release();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -751,9 +758,10 @@ namespace Blah
 | 
			
		||||
		// create a depth backbuffer
 | 
			
		||||
 | 
			
		||||
		// Store Features
 | 
			
		||||
		state.features.instancing = true;
 | 
			
		||||
		state.features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
 | 
			
		||||
		state.features.origin_bottom_left = false;
 | 
			
		||||
		features.type = RendererType::D3D11;
 | 
			
		||||
		features.instancing = true;
 | 
			
		||||
		features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
 | 
			
		||||
		features.origin_bottom_left = false;
 | 
			
		||||
 | 
			
		||||
		// Print Driver Info
 | 
			
		||||
		{
 | 
			
		||||
@ -761,7 +769,7 @@ namespace Blah
 | 
			
		||||
			IDXGIAdapter* dxgi_adapter;
 | 
			
		||||
			DXGI_ADAPTER_DESC adapter_desc;
 | 
			
		||||
 | 
			
		||||
			hr = state.device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgi_device);
 | 
			
		||||
			hr = device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgi_device);
 | 
			
		||||
 | 
			
		||||
			if (SUCCEEDED(hr))
 | 
			
		||||
			{
 | 
			
		||||
@ -782,85 +790,72 @@ namespace Blah
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Renderer Graphics::renderer()
 | 
			
		||||
	{
 | 
			
		||||
		return Renderer::D3D11;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::shutdown()
 | 
			
		||||
	void Renderer_D3D11::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
		// release cached objects
 | 
			
		||||
		for (auto& it : state.blend_cache)
 | 
			
		||||
		for (auto& it : blend_cache)
 | 
			
		||||
			it.state->Release();
 | 
			
		||||
		for (auto& it : state.depthstencil_cache)
 | 
			
		||||
		for (auto& it : depthstencil_cache)
 | 
			
		||||
			it.state->Release();
 | 
			
		||||
		for (auto& it : state.layout_cache)
 | 
			
		||||
		for (auto& it : layout_cache)
 | 
			
		||||
			it.layout->Release();
 | 
			
		||||
		for (auto& it : state.rasterizer_cache)
 | 
			
		||||
		for (auto& it : rasterizer_cache)
 | 
			
		||||
			it.state->Release();
 | 
			
		||||
		for (auto& it : state.sampler_cache)
 | 
			
		||||
		for (auto& it : sampler_cache)
 | 
			
		||||
			it.state->Release();
 | 
			
		||||
 | 
			
		||||
		// release main devices
 | 
			
		||||
		if (state.backbuffer_view)
 | 
			
		||||
			state.backbuffer_view->Release();
 | 
			
		||||
		if (state.backbuffer_depth_view)
 | 
			
		||||
			state.backbuffer_depth_view->Release();
 | 
			
		||||
		state.swap_chain->Release();
 | 
			
		||||
		state.context->ClearState();
 | 
			
		||||
		state.context->Flush();
 | 
			
		||||
		state.context->Release();
 | 
			
		||||
		state.device->Release();
 | 
			
		||||
 | 
			
		||||
		// reset state
 | 
			
		||||
		state = D3D11();
 | 
			
		||||
		if (backbuffer_view)
 | 
			
		||||
			backbuffer_view->Release();
 | 
			
		||||
		if (backbuffer_depth_view)
 | 
			
		||||
			backbuffer_depth_view->Release();
 | 
			
		||||
		swap_chain->Release();
 | 
			
		||||
		context->ClearState();
 | 
			
		||||
		context->Flush();
 | 
			
		||||
		context->Release();
 | 
			
		||||
		device->Release();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const RendererFeatures& Graphics::features()
 | 
			
		||||
	{
 | 
			
		||||
		return state.features;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::update()
 | 
			
		||||
	void Renderer_D3D11::update()
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::before_render()
 | 
			
		||||
	void Renderer_D3D11::before_render()
 | 
			
		||||
	{
 | 
			
		||||
		HRESULT hr;
 | 
			
		||||
 | 
			
		||||
		auto next_size = Point(App::draw_width(), App::draw_height());
 | 
			
		||||
		if (state.last_size != next_size)
 | 
			
		||||
		auto next_size = Point(App::backbuffer()->width(), App::backbuffer()->height());
 | 
			
		||||
		if (last_size != next_size)
 | 
			
		||||
		{
 | 
			
		||||
			// release old buffer
 | 
			
		||||
			if (state.backbuffer_view)
 | 
			
		||||
				state.backbuffer_view->Release();
 | 
			
		||||
			if (backbuffer_view)
 | 
			
		||||
				backbuffer_view->Release();
 | 
			
		||||
 | 
			
		||||
			// perform resize
 | 
			
		||||
			hr = state.swap_chain->ResizeBuffers(0, next_size.x, next_size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
 | 
			
		||||
			hr = swap_chain->ResizeBuffers(0, next_size.x, next_size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
 | 
			
		||||
			BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize");
 | 
			
		||||
			state.last_size = next_size;
 | 
			
		||||
			last_size = next_size;
 | 
			
		||||
 | 
			
		||||
			// get the new buffer
 | 
			
		||||
			ID3D11Texture2D* frame_buffer = nullptr;
 | 
			
		||||
			hr = state.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer);
 | 
			
		||||
			hr = swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer);
 | 
			
		||||
			if (SUCCEEDED(hr) && frame_buffer)
 | 
			
		||||
			{
 | 
			
		||||
				hr = state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer_view);
 | 
			
		||||
				hr = device->CreateRenderTargetView(frame_buffer, nullptr, &backbuffer_view);
 | 
			
		||||
				BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize");
 | 
			
		||||
				frame_buffer->Release();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::after_render()
 | 
			
		||||
	void Renderer_D3D11::after_render()
 | 
			
		||||
	{
 | 
			
		||||
		auto hr = state.swap_chain->Present(1, 0);
 | 
			
		||||
		auto hr = swap_chain->Present(1, 0);
 | 
			
		||||
		BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TextureRef Graphics::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	TextureRef Renderer_D3D11::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	{
 | 
			
		||||
		auto result = new D3D11_Texture(width, height, format, false);
 | 
			
		||||
 | 
			
		||||
@ -871,12 +866,12 @@ namespace Blah
 | 
			
		||||
		return TextureRef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachment_count)
 | 
			
		||||
	TargetRef Renderer_D3D11::create_target(int width, int height, const TextureFormat* attachments, int attachment_count)
 | 
			
		||||
	{
 | 
			
		||||
		return TargetRef(new D3D11_Target(width, height, attachments, attachment_count));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ShaderRef Graphics::create_shader(const ShaderData* data)
 | 
			
		||||
	ShaderRef Renderer_D3D11::create_shader(const ShaderData* data)
 | 
			
		||||
	{
 | 
			
		||||
		auto result = new D3D11_Shader(data);
 | 
			
		||||
		if (result->valid)
 | 
			
		||||
@ -886,23 +881,23 @@ namespace Blah
 | 
			
		||||
		return ShaderRef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MeshRef Graphics::create_mesh()
 | 
			
		||||
	MeshRef Renderer_D3D11::create_mesh()
 | 
			
		||||
	{
 | 
			
		||||
		return MeshRef(new D3D11_Mesh());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::render(const RenderPass& pass)
 | 
			
		||||
	void Renderer_D3D11::render(const RenderPass& pass)
 | 
			
		||||
	{
 | 
			
		||||
		auto ctx = state.context;
 | 
			
		||||
		auto ctx = context;
 | 
			
		||||
		auto mesh = (D3D11_Mesh*)pass.mesh.get();
 | 
			
		||||
		auto shader = (D3D11_Shader*)(pass.material->shader().get());
 | 
			
		||||
 | 
			
		||||
		// OM
 | 
			
		||||
		{
 | 
			
		||||
			// Set the Target
 | 
			
		||||
			if (pass.target == App::backbuffer || !pass.target)
 | 
			
		||||
			if (pass.target == App::backbuffer() || !pass.target)
 | 
			
		||||
			{
 | 
			
		||||
				ctx->OMSetRenderTargets(1, &state.backbuffer_view, state.backbuffer_depth_view);
 | 
			
		||||
				ctx->OMSetRenderTargets(1, &backbuffer_view, backbuffer_depth_view);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
@ -912,14 +907,14 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
			// Depth
 | 
			
		||||
			{
 | 
			
		||||
				auto depthstencil = state.get_depthstencil(pass);
 | 
			
		||||
				auto depthstencil = get_depthstencil(pass);
 | 
			
		||||
				if (depthstencil)
 | 
			
		||||
					ctx->OMSetDepthStencilState(depthstencil, 0);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Blend Mode
 | 
			
		||||
			{
 | 
			
		||||
				auto blend = state.get_blend(pass.blend);
 | 
			
		||||
				auto blend = get_blend(pass.blend);
 | 
			
		||||
				if (blend)
 | 
			
		||||
				{
 | 
			
		||||
					auto color = Color::from_rgba(pass.blend.rgba);
 | 
			
		||||
@ -941,7 +936,7 @@ namespace Blah
 | 
			
		||||
			ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
 | 
			
		||||
 | 
			
		||||
			// Assign Layout
 | 
			
		||||
			auto layout = state.get_layout(shader, mesh->vertex_format);
 | 
			
		||||
			auto layout = get_layout(shader, mesh->vertex_format);
 | 
			
		||||
			ctx->IASetInputLayout(layout);
 | 
			
		||||
 | 
			
		||||
			// Assign Vertex Buffer
 | 
			
		||||
@ -1000,7 +995,7 @@ namespace Blah
 | 
			
		||||
			auto& samplers = pass.material->samplers();
 | 
			
		||||
			for (int i = 0; i < samplers.size(); i++)
 | 
			
		||||
			{
 | 
			
		||||
				auto sampler = state.get_sampler(samplers[i]);
 | 
			
		||||
				auto sampler = get_sampler(samplers[i]);
 | 
			
		||||
				if (sampler)
 | 
			
		||||
					ctx->PSSetSamplers(i, 1, &sampler);
 | 
			
		||||
			}
 | 
			
		||||
@ -1034,7 +1029,7 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
			// Rasterizer
 | 
			
		||||
			{
 | 
			
		||||
				auto rasterizer = state.get_rasterizer(pass);
 | 
			
		||||
				auto rasterizer = get_rasterizer(pass);
 | 
			
		||||
				if (rasterizer)
 | 
			
		||||
					ctx->RSSetState(rasterizer);
 | 
			
		||||
			}
 | 
			
		||||
@ -1065,15 +1060,15 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
 | 
			
		||||
	void Renderer_D3D11::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
 | 
			
		||||
	{
 | 
			
		||||
		if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color)
 | 
			
		||||
		{
 | 
			
		||||
			float clear[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f };
 | 
			
		||||
			state.context->ClearRenderTargetView(state.backbuffer_view, clear);
 | 
			
		||||
			context->ClearRenderTargetView(backbuffer_view, clear);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (state.backbuffer_depth_view)
 | 
			
		||||
		if (backbuffer_depth_view)
 | 
			
		||||
		{
 | 
			
		||||
			UINT flags = 0;
 | 
			
		||||
			if (((int)mask & (int)ClearMask::Depth) == (int)ClearMask::Depth)
 | 
			
		||||
@ -1082,7 +1077,7 @@ namespace Blah
 | 
			
		||||
				flags |= D3D11_CLEAR_STENCIL;
 | 
			
		||||
 | 
			
		||||
			if (flags != 0)
 | 
			
		||||
				state.context->ClearDepthStencilView(state.backbuffer_depth_view, flags, depth, stencil);
 | 
			
		||||
				context->ClearDepthStencilView(backbuffer_depth_view, flags, depth, stencil);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1179,7 +1174,7 @@ namespace Blah
 | 
			
		||||
				buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
 | 
			
		||||
 | 
			
		||||
				ID3D11Buffer* buffer;
 | 
			
		||||
				state.device->CreateBuffer(&buffer_desc, nullptr, &buffer);
 | 
			
		||||
				renderer->device->CreateBuffer(&buffer_desc, nullptr, &buffer);
 | 
			
		||||
				append_buffers_to.push_back(buffer);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@ -1278,14 +1273,14 @@ namespace Blah
 | 
			
		||||
			if (buffers[i])
 | 
			
		||||
			{
 | 
			
		||||
				D3D11_MAPPED_SUBRESOURCE map;
 | 
			
		||||
				state.context->Map(buffers[i], 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
 | 
			
		||||
				renderer->context->Map(buffers[i], 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
 | 
			
		||||
				memcpy(map.pData, values[i].begin(), values[i].size() * sizeof(float));
 | 
			
		||||
				state.context->Unmap(buffers[i], 0);
 | 
			
		||||
				renderer->context->Unmap(buffers[i], 0);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ID3D11InputLayout* D3D11::get_layout(D3D11_Shader* shader, const VertexFormat& format)
 | 
			
		||||
	ID3D11InputLayout* Renderer_D3D11::get_layout(D3D11_Shader* shader, const VertexFormat& format)
 | 
			
		||||
	{
 | 
			
		||||
		// find existing
 | 
			
		||||
		for (auto& it : layout_cache)
 | 
			
		||||
@ -1374,7 +1369,7 @@ namespace Blah
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ID3D11BlendState* D3D11::get_blend(const BlendMode& blend)
 | 
			
		||||
	ID3D11BlendState* Renderer_D3D11::get_blend(const BlendMode& blend)
 | 
			
		||||
	{
 | 
			
		||||
		for (auto& it : blend_cache)
 | 
			
		||||
			if (it.blend == blend)
 | 
			
		||||
@ -1414,7 +1409,7 @@ namespace Blah
 | 
			
		||||
			desc.RenderTarget[i] = desc.RenderTarget[0];
 | 
			
		||||
 | 
			
		||||
		ID3D11BlendState* blend_state = nullptr;
 | 
			
		||||
		auto hr = state.device->CreateBlendState(&desc, &blend_state);
 | 
			
		||||
		auto hr = renderer->device->CreateBlendState(&desc, &blend_state);
 | 
			
		||||
 | 
			
		||||
		if (SUCCEEDED(hr))
 | 
			
		||||
		{
 | 
			
		||||
@ -1427,7 +1422,7 @@ namespace Blah
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ID3D11SamplerState* D3D11::get_sampler(const TextureSampler& sampler)
 | 
			
		||||
	ID3D11SamplerState* Renderer_D3D11::get_sampler(const TextureSampler& sampler)
 | 
			
		||||
	{
 | 
			
		||||
		for (auto& it : sampler_cache)
 | 
			
		||||
			if (it.sampler == sampler)
 | 
			
		||||
@ -1462,7 +1457,7 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ID3D11SamplerState* result;
 | 
			
		||||
		auto hr = state.device->CreateSamplerState(&desc, &result);
 | 
			
		||||
		auto hr = renderer->device->CreateSamplerState(&desc, &result);
 | 
			
		||||
 | 
			
		||||
		if (SUCCEEDED(hr))
 | 
			
		||||
		{
 | 
			
		||||
@ -1475,7 +1470,7 @@ namespace Blah
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ID3D11RasterizerState* D3D11::get_rasterizer(const RenderPass& pass)
 | 
			
		||||
	ID3D11RasterizerState* Renderer_D3D11::get_rasterizer(const RenderPass& pass)
 | 
			
		||||
	{
 | 
			
		||||
		for (auto& it : rasterizer_cache)
 | 
			
		||||
			if (it.cull == pass.cull && it.has_scissor == pass.has_scissor)
 | 
			
		||||
@ -1502,7 +1497,7 @@ namespace Blah
 | 
			
		||||
		desc.AntialiasedLineEnable = false;
 | 
			
		||||
 | 
			
		||||
		ID3D11RasterizerState* result;
 | 
			
		||||
		auto hr = state.device->CreateRasterizerState(&desc, &result);
 | 
			
		||||
		auto hr = renderer->device->CreateRasterizerState(&desc, &result);
 | 
			
		||||
 | 
			
		||||
		if (SUCCEEDED(hr))
 | 
			
		||||
		{
 | 
			
		||||
@ -1516,7 +1511,7 @@ namespace Blah
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ID3D11DepthStencilState* D3D11::get_depthstencil(const RenderPass& pass)
 | 
			
		||||
	ID3D11DepthStencilState* Renderer_D3D11::get_depthstencil(const RenderPass& pass)
 | 
			
		||||
	{
 | 
			
		||||
		for (auto& it : depthstencil_cache)
 | 
			
		||||
			if (it.depth == pass.depth)
 | 
			
		||||
@ -1541,7 +1536,7 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ID3D11DepthStencilState* result;
 | 
			
		||||
		auto hr = state.device->CreateDepthStencilState(&desc, &result);
 | 
			
		||||
		auto hr = renderer->device->CreateDepthStencilState(&desc, &result);
 | 
			
		||||
 | 
			
		||||
		if (SUCCEEDED(hr))
 | 
			
		||||
		{
 | 
			
		||||
@ -1555,4 +1550,17 @@ namespace Blah
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // BLAH_GRAPHICS_D3D11
 | 
			
		||||
Blah::Renderer* Blah::Renderer::try_make_d3d11()
 | 
			
		||||
{
 | 
			
		||||
	return new Blah::Renderer_D3D11();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else // BLAH_RENDERER_D3D11
 | 
			
		||||
 | 
			
		||||
#include "renderer.h"
 | 
			
		||||
Blah::Renderer* Blah::Renderer::try_make_d3d11()
 | 
			
		||||
{
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
#ifdef BLAH_GRAPHICS_OPENGL
 | 
			
		||||
#ifdef BLAH_RENDERER_OPENGL
 | 
			
		||||
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "renderer.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
@ -338,14 +338,22 @@ typedef void (APIENTRY* DEBUGPROC)(GLenum source,
 | 
			
		||||
	const GLchar* message,
 | 
			
		||||
	const void* userParam);
 | 
			
		||||
 | 
			
		||||
// shorthand to our internal state
 | 
			
		||||
#define renderer ((Renderer_OpenGL*)Renderer::instance)
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	struct State
 | 
			
		||||
	class Renderer_OpenGL : public Renderer
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
 | 
			
		||||
		struct Bindings
 | 
			
		||||
		{
 | 
			
		||||
			// GL function pointers
 | 
			
		||||
			#define GL_FUNC(name, ret, ...) typedef ret (*name ## Func) (__VA_ARGS__); name ## Func name;
 | 
			
		||||
			GL_FUNCTIONS
 | 
			
		||||
			#undef GL_FUNC
 | 
			
		||||
		} gl;
 | 
			
		||||
 | 
			
		||||
		// state
 | 
			
		||||
		void* context;
 | 
			
		||||
@ -358,11 +366,19 @@ namespace Blah
 | 
			
		||||
		int max_samples;
 | 
			
		||||
		int max_texture_image_units;
 | 
			
		||||
		int max_texture_size;
 | 
			
		||||
		RendererFeatures features;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// static state
 | 
			
		||||
	State gl;
 | 
			
		||||
		bool init() override;
 | 
			
		||||
		void shutdown() override;
 | 
			
		||||
		void update() override;
 | 
			
		||||
		void before_render() override;
 | 
			
		||||
		void after_render() override;
 | 
			
		||||
		void render(const RenderPass& pass) override;
 | 
			
		||||
		void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) override;
 | 
			
		||||
		TextureRef create_texture(int width, int height, TextureFormat format) override;
 | 
			
		||||
		TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count) override;
 | 
			
		||||
		ShaderRef create_shader(const ShaderData* data) override;
 | 
			
		||||
		MeshRef create_mesh() override;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// debug callback
 | 
			
		||||
	void APIENTRY gl_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
 | 
			
		||||
@ -408,7 +424,7 @@ namespace Blah
 | 
			
		||||
	GLuint gl_mesh_assign_attributes(GLuint buffer, GLenum buffer_type, const VertexFormat& format, GLint divisor)
 | 
			
		||||
	{
 | 
			
		||||
		// bind
 | 
			
		||||
		gl.BindBuffer(buffer_type, buffer);
 | 
			
		||||
		renderer->gl.BindBuffer(buffer_type, buffer);
 | 
			
		||||
 | 
			
		||||
		// TODO: disable existing enabled attributes ..
 | 
			
		||||
		// ...
 | 
			
		||||
@ -485,9 +501,9 @@ namespace Blah
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			u32 location = (u32)(attribute.index);
 | 
			
		||||
			gl.EnableVertexAttribArray(location);
 | 
			
		||||
			gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr);
 | 
			
		||||
			gl.VertexAttribDivisor(location, divisor);
 | 
			
		||||
			renderer->gl.EnableVertexAttribArray(location);
 | 
			
		||||
			renderer->gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr);
 | 
			
		||||
			renderer->gl.VertexAttribDivisor(location, divisor);
 | 
			
		||||
 | 
			
		||||
			ptr += components * component_size;
 | 
			
		||||
		}
 | 
			
		||||
@ -565,9 +581,9 @@ namespace Blah
 | 
			
		||||
			m_gl_format = GL_RED;
 | 
			
		||||
			m_gl_type = GL_UNSIGNED_BYTE;
 | 
			
		||||
 | 
			
		||||
			if (width > gl.max_texture_size || height > gl.max_texture_size)
 | 
			
		||||
			if (width > renderer->max_texture_size || height > renderer->max_texture_size)
 | 
			
		||||
			{
 | 
			
		||||
				Log::error("Exceeded Max Texture Size of %i", gl.max_texture_size);
 | 
			
		||||
				Log::error("Exceeded Max Texture Size of %i", renderer->max_texture_size);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@ -601,16 +617,16 @@ namespace Blah
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			gl.GenTextures(1, &m_id);
 | 
			
		||||
			gl.ActiveTexture(GL_TEXTURE0);
 | 
			
		||||
			gl.BindTexture(GL_TEXTURE_2D, m_id);
 | 
			
		||||
			gl.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, width, height, 0, m_gl_format, m_gl_type, nullptr);
 | 
			
		||||
			renderer->gl.GenTextures(1, &m_id);
 | 
			
		||||
			renderer->gl.ActiveTexture(GL_TEXTURE0);
 | 
			
		||||
			renderer->gl.BindTexture(GL_TEXTURE_2D, m_id);
 | 
			
		||||
			renderer->gl.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, width, height, 0, m_gl_format, m_gl_type, nullptr);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~OpenGL_Texture()
 | 
			
		||||
		{
 | 
			
		||||
			if (m_id > 0)
 | 
			
		||||
				gl.DeleteTextures(1, &m_id);
 | 
			
		||||
			if (m_id > 0 && renderer)
 | 
			
		||||
				renderer->gl.DeleteTextures(1, &m_id);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GLuint gl_id() const
 | 
			
		||||
@ -639,26 +655,26 @@ namespace Blah
 | 
			
		||||
			{
 | 
			
		||||
				m_sampler = sampler;
 | 
			
		||||
 | 
			
		||||
				gl.BindTexture(GL_TEXTURE_2D, m_id);
 | 
			
		||||
				gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (m_sampler.filter == TextureFilter::Nearest ? GL_NEAREST : GL_LINEAR));
 | 
			
		||||
				gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (m_sampler.filter == TextureFilter::Nearest ? GL_NEAREST : GL_LINEAR));
 | 
			
		||||
				gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (m_sampler.wrap_x == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT));
 | 
			
		||||
				gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (m_sampler.wrap_y == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT));
 | 
			
		||||
				renderer->gl.BindTexture(GL_TEXTURE_2D, m_id);
 | 
			
		||||
				renderer->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (m_sampler.filter == TextureFilter::Nearest ? GL_NEAREST : GL_LINEAR));
 | 
			
		||||
				renderer->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (m_sampler.filter == TextureFilter::Nearest ? GL_NEAREST : GL_LINEAR));
 | 
			
		||||
				renderer->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (m_sampler.wrap_x == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT));
 | 
			
		||||
				renderer->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (m_sampler.wrap_y == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void set_data(unsigned char* data) override
 | 
			
		||||
		{
 | 
			
		||||
			gl.ActiveTexture(GL_TEXTURE0);
 | 
			
		||||
			gl.BindTexture(GL_TEXTURE_2D, m_id);
 | 
			
		||||
			gl.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, m_width, m_height, 0, m_gl_format, m_gl_type, data);
 | 
			
		||||
			renderer->gl.ActiveTexture(GL_TEXTURE0);
 | 
			
		||||
			renderer->gl.BindTexture(GL_TEXTURE_2D, m_id);
 | 
			
		||||
			renderer->gl.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, m_width, m_height, 0, m_gl_format, m_gl_type, data);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void get_data(unsigned char* data) override
 | 
			
		||||
		{
 | 
			
		||||
			gl.ActiveTexture(GL_TEXTURE0);
 | 
			
		||||
			gl.BindTexture(GL_TEXTURE_2D, m_id);
 | 
			
		||||
			gl.GetTexImage(GL_TEXTURE_2D, 0, m_gl_internal_format, m_gl_type, data);
 | 
			
		||||
			renderer->gl.ActiveTexture(GL_TEXTURE0);
 | 
			
		||||
			renderer->gl.BindTexture(GL_TEXTURE_2D, m_id);
 | 
			
		||||
			renderer->gl.GetTexImage(GL_TEXTURE_2D, 0, m_gl_internal_format, m_gl_type, data);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual bool is_framebuffer() const override
 | 
			
		||||
@ -680,11 +696,11 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
		OpenGL_Target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
		{
 | 
			
		||||
			gl.GenFramebuffers(1, &m_id);
 | 
			
		||||
			renderer->gl.GenFramebuffers(1, &m_id);
 | 
			
		||||
			m_width = width;
 | 
			
		||||
			m_height = height;
 | 
			
		||||
 | 
			
		||||
			gl.BindFramebuffer(GL_FRAMEBUFFER, m_id);
 | 
			
		||||
			renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, m_id);
 | 
			
		||||
 | 
			
		||||
			for (int i = 0; i < attachmentCount; i++)
 | 
			
		||||
			{
 | 
			
		||||
@ -696,20 +712,20 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
				if (attachments[i] != TextureFormat::DepthStencil)
 | 
			
		||||
				{
 | 
			
		||||
					gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, gltex->gl_id(), 0);
 | 
			
		||||
					renderer->gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, gltex->gl_id(), 0);
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, gltex->gl_id(), 0);
 | 
			
		||||
					renderer->gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, gltex->gl_id(), 0);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~OpenGL_Target()
 | 
			
		||||
		{
 | 
			
		||||
			if (m_id > 0)
 | 
			
		||||
			if (m_id > 0 && renderer)
 | 
			
		||||
			{
 | 
			
		||||
				gl.DeleteFramebuffers(1, &m_id);
 | 
			
		||||
				renderer->gl.DeleteFramebuffers(1, &m_id);
 | 
			
		||||
				m_id = 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@ -740,13 +756,13 @@ namespace Blah
 | 
			
		||||
			if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil)
 | 
			
		||||
				clear |= GL_STENCIL_BUFFER_BIT;
 | 
			
		||||
 | 
			
		||||
			gl.BindFramebuffer(GL_FRAMEBUFFER, m_id);
 | 
			
		||||
			gl.Disable(GL_SCISSOR_TEST);
 | 
			
		||||
			gl.ColorMask(true, true, true, true);
 | 
			
		||||
			gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
 | 
			
		||||
			gl.ClearDepth(depth);
 | 
			
		||||
			gl.ClearStencil(stencil);
 | 
			
		||||
			gl.Clear(clear);
 | 
			
		||||
			renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, m_id);
 | 
			
		||||
			renderer->gl.Disable(GL_SCISSOR_TEST);
 | 
			
		||||
			renderer->gl.ColorMask(true, true, true, true);
 | 
			
		||||
			renderer->gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
 | 
			
		||||
			renderer->gl.ClearDepth(depth);
 | 
			
		||||
			renderer->gl.ClearStencil(stencil);
 | 
			
		||||
			renderer->gl.Clear(clear);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
@ -778,47 +794,47 @@ namespace Blah
 | 
			
		||||
			GLchar log[1024];
 | 
			
		||||
			GLsizei log_length = 0;
 | 
			
		||||
 | 
			
		||||
			GLuint vertex_shader = gl.CreateShader(GL_VERTEX_SHADER);
 | 
			
		||||
			GLuint vertex_shader = renderer->gl.CreateShader(GL_VERTEX_SHADER);
 | 
			
		||||
			{
 | 
			
		||||
				const GLchar* source = (const GLchar*)data->vertex.cstr();
 | 
			
		||||
				gl.ShaderSource(vertex_shader, 1, &source, nullptr);
 | 
			
		||||
				gl.CompileShader(vertex_shader);
 | 
			
		||||
				gl.GetShaderInfoLog(vertex_shader, 1024, &log_length, log);
 | 
			
		||||
				renderer->gl.ShaderSource(vertex_shader, 1, &source, nullptr);
 | 
			
		||||
				renderer->gl.CompileShader(vertex_shader);
 | 
			
		||||
				renderer->gl.GetShaderInfoLog(vertex_shader, 1024, &log_length, log);
 | 
			
		||||
 | 
			
		||||
				if (log_length > 0)
 | 
			
		||||
				{
 | 
			
		||||
					gl.DeleteShader(vertex_shader);
 | 
			
		||||
					renderer->gl.DeleteShader(vertex_shader);
 | 
			
		||||
					Log::error(log);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			GLuint fragment_shader = gl.CreateShader(GL_FRAGMENT_SHADER);
 | 
			
		||||
			GLuint fragment_shader = renderer->gl.CreateShader(GL_FRAGMENT_SHADER);
 | 
			
		||||
			{
 | 
			
		||||
				const GLchar* source = (const GLchar*)data->fragment.cstr();
 | 
			
		||||
				gl.ShaderSource(fragment_shader, 1, &source, nullptr);
 | 
			
		||||
				gl.CompileShader(fragment_shader);
 | 
			
		||||
				gl.GetShaderInfoLog(fragment_shader, 1024, &log_length, log);
 | 
			
		||||
				renderer->gl.ShaderSource(fragment_shader, 1, &source, nullptr);
 | 
			
		||||
				renderer->gl.CompileShader(fragment_shader);
 | 
			
		||||
				renderer->gl.GetShaderInfoLog(fragment_shader, 1024, &log_length, log);
 | 
			
		||||
 | 
			
		||||
				if (log_length > 0)
 | 
			
		||||
				{
 | 
			
		||||
					gl.DeleteShader(vertex_shader);
 | 
			
		||||
					gl.DeleteShader(fragment_shader);
 | 
			
		||||
					renderer->gl.DeleteShader(vertex_shader);
 | 
			
		||||
					renderer->gl.DeleteShader(fragment_shader);
 | 
			
		||||
					Log::error(log);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// create actual shader program
 | 
			
		||||
			GLuint id = gl.CreateProgram();
 | 
			
		||||
			gl.AttachShader(id, vertex_shader);
 | 
			
		||||
			gl.AttachShader(id, fragment_shader);
 | 
			
		||||
			gl.LinkProgram(id);
 | 
			
		||||
			gl.GetProgramInfoLog(id, 1024, &log_length, log);
 | 
			
		||||
			gl.DetachShader(id, vertex_shader);
 | 
			
		||||
			gl.DetachShader(id, fragment_shader);
 | 
			
		||||
			gl.DeleteShader(vertex_shader);
 | 
			
		||||
			gl.DeleteShader(fragment_shader);
 | 
			
		||||
			GLuint id = renderer->gl.CreateProgram();
 | 
			
		||||
			renderer->gl.AttachShader(id, vertex_shader);
 | 
			
		||||
			renderer->gl.AttachShader(id, fragment_shader);
 | 
			
		||||
			renderer->gl.LinkProgram(id);
 | 
			
		||||
			renderer->gl.GetProgramInfoLog(id, 1024, &log_length, log);
 | 
			
		||||
			renderer->gl.DetachShader(id, vertex_shader);
 | 
			
		||||
			renderer->gl.DetachShader(id, fragment_shader);
 | 
			
		||||
			renderer->gl.DeleteShader(vertex_shader);
 | 
			
		||||
			renderer->gl.DeleteShader(fragment_shader);
 | 
			
		||||
 | 
			
		||||
			if (log_length > 0)
 | 
			
		||||
			{
 | 
			
		||||
@ -832,7 +848,7 @@ namespace Blah
 | 
			
		||||
				const int max_name_length = 256;
 | 
			
		||||
 | 
			
		||||
				GLint active_uniforms = 0;
 | 
			
		||||
				gl.GetProgramiv(id, GL_ACTIVE_UNIFORMS, &active_uniforms);
 | 
			
		||||
				renderer->gl.GetProgramiv(id, GL_ACTIVE_UNIFORMS, &active_uniforms);
 | 
			
		||||
 | 
			
		||||
				for (int i = 0; i < active_uniforms; i++)
 | 
			
		||||
				{
 | 
			
		||||
@ -841,7 +857,7 @@ namespace Blah
 | 
			
		||||
					GLenum type;
 | 
			
		||||
					GLchar name[max_name_length + 1];
 | 
			
		||||
 | 
			
		||||
					gl.GetActiveUniform(id, i, max_name_length, &length, &size, &type, name);
 | 
			
		||||
					renderer->gl.GetActiveUniform(id, i, max_name_length, &length, &size, &type, name);
 | 
			
		||||
					name[length] = '\0';
 | 
			
		||||
 | 
			
		||||
					// array names end with "[0]", and we don't want that
 | 
			
		||||
@ -863,7 +879,7 @@ namespace Blah
 | 
			
		||||
						tex_uniform.array_length = size;
 | 
			
		||||
						tex_uniform.type = UniformType::Texture2D;
 | 
			
		||||
						tex_uniform.shader = ShaderType::Fragment;
 | 
			
		||||
						uniform_locations.push_back(gl.GetUniformLocation(id, name));
 | 
			
		||||
						uniform_locations.push_back(renderer->gl.GetUniformLocation(id, name));
 | 
			
		||||
						m_uniforms.push_back(tex_uniform);
 | 
			
		||||
 | 
			
		||||
						UniformInfo sampler_uniform;
 | 
			
		||||
@ -872,7 +888,7 @@ namespace Blah
 | 
			
		||||
						sampler_uniform.array_length = size;
 | 
			
		||||
						sampler_uniform.type = UniformType::Sampler2D;
 | 
			
		||||
						sampler_uniform.shader = ShaderType::Fragment;
 | 
			
		||||
						uniform_locations.push_back(gl.GetUniformLocation(id, name));
 | 
			
		||||
						uniform_locations.push_back(renderer->gl.GetUniformLocation(id, name));
 | 
			
		||||
						m_uniforms.push_back(sampler_uniform);
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
@ -882,7 +898,7 @@ namespace Blah
 | 
			
		||||
						uniform.type = UniformType::None;
 | 
			
		||||
						uniform.buffer_index = 0;
 | 
			
		||||
						uniform.array_length = size;
 | 
			
		||||
						uniform_locations.push_back(gl.GetUniformLocation(id, name));
 | 
			
		||||
						uniform_locations.push_back(renderer->gl.GetUniformLocation(id, name));
 | 
			
		||||
						uniform.shader = (ShaderType)((int)ShaderType::Vertex | (int)ShaderType::Fragment);
 | 
			
		||||
 | 
			
		||||
						if (type == GL_FLOAT)
 | 
			
		||||
@ -912,15 +928,15 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
			// assign ID if the uniforms were valid
 | 
			
		||||
			if (!valid_uniforms)
 | 
			
		||||
				gl.DeleteProgram(id);
 | 
			
		||||
				renderer->gl.DeleteProgram(id);
 | 
			
		||||
			else
 | 
			
		||||
				m_id = id;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~OpenGL_Shader()
 | 
			
		||||
		{
 | 
			
		||||
			if (m_id > 0)
 | 
			
		||||
				gl.DeleteProgram(m_id);
 | 
			
		||||
			if (m_id > 0 && renderer)
 | 
			
		||||
				renderer->gl.DeleteProgram(m_id);
 | 
			
		||||
			m_id = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -975,19 +991,22 @@ namespace Blah
 | 
			
		||||
			m_vertex_attribs_enabled = 0;
 | 
			
		||||
			m_instance_attribs_enabled = 0;
 | 
			
		||||
 | 
			
		||||
			gl.GenVertexArrays(1, &m_id);
 | 
			
		||||
			renderer->gl.GenVertexArrays(1, &m_id);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~OpenGL_Mesh()
 | 
			
		||||
		{
 | 
			
		||||
			if (renderer)
 | 
			
		||||
			{
 | 
			
		||||
				if (m_vertex_buffer != 0)
 | 
			
		||||
				gl.DeleteBuffers(1, &m_vertex_buffer);
 | 
			
		||||
					renderer->gl.DeleteBuffers(1, &m_vertex_buffer);
 | 
			
		||||
				if (m_index_buffer != 0)
 | 
			
		||||
				gl.DeleteBuffers(1, &m_index_buffer);
 | 
			
		||||
					renderer->gl.DeleteBuffers(1, &m_index_buffer);
 | 
			
		||||
				if (m_instance_buffer != 0)
 | 
			
		||||
				gl.DeleteBuffers(1, &m_instance_buffer);
 | 
			
		||||
					renderer->gl.DeleteBuffers(1, &m_instance_buffer);
 | 
			
		||||
				if (m_id != 0)
 | 
			
		||||
				gl.DeleteVertexArrays(1, &m_id);
 | 
			
		||||
					renderer->gl.DeleteVertexArrays(1, &m_id);
 | 
			
		||||
			}
 | 
			
		||||
			m_id = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1010,10 +1029,10 @@ namespace Blah
 | 
			
		||||
		{
 | 
			
		||||
			m_index_count = count;
 | 
			
		||||
 | 
			
		||||
			gl.BindVertexArray(m_id);
 | 
			
		||||
			renderer->gl.BindVertexArray(m_id);
 | 
			
		||||
			{
 | 
			
		||||
				if (m_index_buffer == 0)
 | 
			
		||||
					gl.GenBuffers(1, &(m_index_buffer));
 | 
			
		||||
					renderer->gl.GenBuffers(1, &(m_index_buffer));
 | 
			
		||||
 | 
			
		||||
				switch (format)
 | 
			
		||||
				{
 | 
			
		||||
@ -1027,52 +1046,52 @@ namespace Blah
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer);
 | 
			
		||||
				gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, m_index_size * count, indices, GL_DYNAMIC_DRAW);
 | 
			
		||||
				renderer->gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer);
 | 
			
		||||
				renderer->gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, m_index_size * count, indices, GL_DYNAMIC_DRAW);
 | 
			
		||||
			}
 | 
			
		||||
			gl.BindVertexArray(0);
 | 
			
		||||
			renderer->gl.BindVertexArray(0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override
 | 
			
		||||
		{
 | 
			
		||||
			m_vertex_count = count;
 | 
			
		||||
 | 
			
		||||
			gl.BindVertexArray(m_id);
 | 
			
		||||
			renderer->gl.BindVertexArray(m_id);
 | 
			
		||||
			{
 | 
			
		||||
				// Create Buffer if it doesn't exist yet
 | 
			
		||||
				if (m_vertex_buffer == 0)
 | 
			
		||||
					gl.GenBuffers(1, &(m_vertex_buffer));
 | 
			
		||||
					renderer->gl.GenBuffers(1, &(m_vertex_buffer));
 | 
			
		||||
 | 
			
		||||
				// TODO:
 | 
			
		||||
				// Cache this
 | 
			
		||||
				m_vertex_size = gl_mesh_assign_attributes(m_vertex_buffer, GL_ARRAY_BUFFER, format, 0);
 | 
			
		||||
 | 
			
		||||
				// Upload Buffer
 | 
			
		||||
				gl.BindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer);
 | 
			
		||||
				gl.BufferData(GL_ARRAY_BUFFER, m_vertex_size * count, vertices, GL_DYNAMIC_DRAW);
 | 
			
		||||
				renderer->gl.BindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer);
 | 
			
		||||
				renderer->gl.BufferData(GL_ARRAY_BUFFER, m_vertex_size * count, vertices, GL_DYNAMIC_DRAW);
 | 
			
		||||
			}
 | 
			
		||||
			gl.BindVertexArray(0);
 | 
			
		||||
			renderer->gl.BindVertexArray(0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override
 | 
			
		||||
		{
 | 
			
		||||
			m_instance_count = count;
 | 
			
		||||
 | 
			
		||||
			gl.BindVertexArray(m_id);
 | 
			
		||||
			renderer->gl.BindVertexArray(m_id);
 | 
			
		||||
			{
 | 
			
		||||
				// Create Buffer if it doesn't exist yet
 | 
			
		||||
				if (m_instance_buffer == 0)
 | 
			
		||||
					gl.GenBuffers(1, &(m_instance_buffer));
 | 
			
		||||
					renderer->gl.GenBuffers(1, &(m_instance_buffer));
 | 
			
		||||
 | 
			
		||||
				// TODO:
 | 
			
		||||
				// Cache this
 | 
			
		||||
				m_instance_size = gl_mesh_assign_attributes(m_instance_buffer, GL_ARRAY_BUFFER, format, 1);
 | 
			
		||||
 | 
			
		||||
				// Upload Buffer
 | 
			
		||||
				gl.BindBuffer(GL_ARRAY_BUFFER, m_instance_buffer);
 | 
			
		||||
				gl.BufferData(GL_ARRAY_BUFFER, m_instance_size * count, instances, GL_DYNAMIC_DRAW);
 | 
			
		||||
				renderer->gl.BindBuffer(GL_ARRAY_BUFFER, m_instance_buffer);
 | 
			
		||||
				renderer->gl.BufferData(GL_ARRAY_BUFFER, m_instance_size * count, instances, GL_DYNAMIC_DRAW);
 | 
			
		||||
			}
 | 
			
		||||
			gl.BindVertexArray(0);
 | 
			
		||||
			renderer->gl.BindVertexArray(0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual i64 index_count() const override
 | 
			
		||||
@ -1091,21 +1110,19 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool Graphics::init()
 | 
			
		||||
	bool Renderer_OpenGL::init()
 | 
			
		||||
	{
 | 
			
		||||
		gl = State();
 | 
			
		||||
 | 
			
		||||
		// create gl context
 | 
			
		||||
		gl.context = Platform::gl_context_create();
 | 
			
		||||
		if (gl.context == nullptr)
 | 
			
		||||
		context = Platform::gl_context_create();
 | 
			
		||||
		if (context == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Failed to create OpenGL Context");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		Platform::gl_context_make_current(gl.context);
 | 
			
		||||
		Platform::gl_context_make_current(context);
 | 
			
		||||
 | 
			
		||||
		// bind opengl functions
 | 
			
		||||
		#define GL_FUNC(name, ...) gl.name = (State::name ## Func)(Platform::gl_get_func("gl" #name));
 | 
			
		||||
		#define GL_FUNC(name, ...) gl.name = (Renderer_OpenGL::Bindings::name ## Func)(Platform::gl_get_func("gl" #name));
 | 
			
		||||
		GL_FUNCTIONS
 | 
			
		||||
		#undef GL_FUNC
 | 
			
		||||
 | 
			
		||||
@ -1118,13 +1135,13 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// get opengl info
 | 
			
		||||
		gl.GetIntegerv(0x8CDF, &gl.max_color_attachments);
 | 
			
		||||
		gl.GetIntegerv(0x80E9, &gl.max_element_indices);
 | 
			
		||||
		gl.GetIntegerv(0x80E8, &gl.max_element_vertices);
 | 
			
		||||
		gl.GetIntegerv(0x84E8, &gl.max_renderbuffer_size);
 | 
			
		||||
		gl.GetIntegerv(0x8D57, &gl.max_samples);
 | 
			
		||||
		gl.GetIntegerv(0x8872, &gl.max_texture_image_units);
 | 
			
		||||
		gl.GetIntegerv(0x0D33, &gl.max_texture_size);
 | 
			
		||||
		gl.GetIntegerv(0x8CDF, &max_color_attachments);
 | 
			
		||||
		gl.GetIntegerv(0x80E9, &max_element_indices);
 | 
			
		||||
		gl.GetIntegerv(0x80E8, &max_element_vertices);
 | 
			
		||||
		gl.GetIntegerv(0x84E8, &max_renderbuffer_size);
 | 
			
		||||
		gl.GetIntegerv(0x8D57, &max_samples);
 | 
			
		||||
		gl.GetIntegerv(0x8872, &max_texture_image_units);
 | 
			
		||||
		gl.GetIntegerv(0x0D33, &max_texture_size);
 | 
			
		||||
 | 
			
		||||
		// log
 | 
			
		||||
		Log::info("OpenGL %s, %s",
 | 
			
		||||
@ -1136,34 +1153,25 @@ namespace Blah
 | 
			
		||||
		gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
 | 
			
		||||
 | 
			
		||||
		// assign info
 | 
			
		||||
		gl.features.instancing = true;
 | 
			
		||||
		gl.features.origin_bottom_left = true;
 | 
			
		||||
		gl.features.max_texture_size = gl.max_texture_size;
 | 
			
		||||
		features.type = RendererType::OpenGL;
 | 
			
		||||
		features.instancing = true;
 | 
			
		||||
		features.origin_bottom_left = true;
 | 
			
		||||
		features.max_texture_size = max_texture_size;
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Renderer Graphics::renderer()
 | 
			
		||||
	void Renderer_OpenGL::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
		return Renderer::OpenGL;
 | 
			
		||||
		Platform::gl_context_destroy(context);
 | 
			
		||||
		context = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
		Platform::gl_context_destroy(gl.context);
 | 
			
		||||
		gl.context = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
	void Renderer_OpenGL::update() {}
 | 
			
		||||
	void Renderer_OpenGL::before_render() {}
 | 
			
		||||
	void Renderer_OpenGL::after_render() {}
 | 
			
		||||
 | 
			
		||||
	const RendererFeatures& Graphics::features()
 | 
			
		||||
	{
 | 
			
		||||
		return gl.features;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::update() {}
 | 
			
		||||
	void Graphics::before_render() {}
 | 
			
		||||
	void Graphics::after_render() {}
 | 
			
		||||
 | 
			
		||||
	TextureRef Graphics::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	TextureRef Renderer_OpenGL::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	{
 | 
			
		||||
		auto resource = new OpenGL_Texture(width, height, format);
 | 
			
		||||
 | 
			
		||||
@ -1176,7 +1184,7 @@ namespace Blah
 | 
			
		||||
		return TextureRef(resource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
	TargetRef Renderer_OpenGL::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
	{
 | 
			
		||||
		auto resource = new OpenGL_Target(width, height, attachments, attachmentCount);
 | 
			
		||||
 | 
			
		||||
@ -1189,7 +1197,7 @@ namespace Blah
 | 
			
		||||
		return TargetRef(resource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ShaderRef Graphics::create_shader(const ShaderData* data)
 | 
			
		||||
	ShaderRef Renderer_OpenGL::create_shader(const ShaderData* data)
 | 
			
		||||
	{
 | 
			
		||||
		auto resource = new OpenGL_Shader(data);
 | 
			
		||||
 | 
			
		||||
@ -1202,7 +1210,7 @@ namespace Blah
 | 
			
		||||
		return ShaderRef(resource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MeshRef Graphics::create_mesh()
 | 
			
		||||
	MeshRef Renderer_OpenGL::create_mesh()
 | 
			
		||||
	{
 | 
			
		||||
		auto resource = new OpenGL_Mesh();
 | 
			
		||||
 | 
			
		||||
@ -1215,24 +1223,20 @@ namespace Blah
 | 
			
		||||
		return MeshRef(resource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::render(const RenderPass& pass)
 | 
			
		||||
	void Renderer_OpenGL::render(const RenderPass& pass)
 | 
			
		||||
	{
 | 
			
		||||
		// Bind the Target
 | 
			
		||||
		Point size;
 | 
			
		||||
		if (pass.target == App::backbuffer)
 | 
			
		||||
		if (pass.target == App::backbuffer())
 | 
			
		||||
		{
 | 
			
		||||
			gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
 | 
			
		||||
			size.x = App::draw_width();
 | 
			
		||||
			size.y = App::draw_height();
 | 
			
		||||
			renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
 | 
			
		||||
		}
 | 
			
		||||
		else if (pass.target)
 | 
			
		||||
		{
 | 
			
		||||
			auto framebuffer = (OpenGL_Target*)pass.target.get();
 | 
			
		||||
			gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id());
 | 
			
		||||
			size.x = pass.target->width();
 | 
			
		||||
			size.y = pass.target->height();
 | 
			
		||||
			renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto size = Point(pass.target->width(), pass.target->height());
 | 
			
		||||
		auto shader_ref = pass.material->shader();
 | 
			
		||||
		auto shader = (OpenGL_Shader*)shader_ref.get();
 | 
			
		||||
		auto mesh = (OpenGL_Mesh*)pass.mesh.get();
 | 
			
		||||
@ -1241,7 +1245,7 @@ namespace Blah
 | 
			
		||||
		// TODO: I don't love how material values are assigned or set here
 | 
			
		||||
		// TODO: this should be cached?
 | 
			
		||||
		{
 | 
			
		||||
			gl.UseProgram(shader->gl_id());
 | 
			
		||||
			renderer->gl.UseProgram(shader->gl_id());
 | 
			
		||||
 | 
			
		||||
			int texture_slot = 0;
 | 
			
		||||
			int gl_texture_slot = 0;
 | 
			
		||||
@ -1266,24 +1270,24 @@ namespace Blah
 | 
			
		||||
						auto tex = pass.material->get_texture(texture_slot, n);
 | 
			
		||||
						auto sampler = pass.material->get_sampler(texture_slot, n);
 | 
			
		||||
 | 
			
		||||
						gl.ActiveTexture(GL_TEXTURE0 + gl_texture_slot);
 | 
			
		||||
						renderer->gl.ActiveTexture(GL_TEXTURE0 + gl_texture_slot);
 | 
			
		||||
 | 
			
		||||
						if (!tex)
 | 
			
		||||
						{
 | 
			
		||||
							gl.BindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
							renderer->gl.BindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
						}
 | 
			
		||||
						else
 | 
			
		||||
						{
 | 
			
		||||
							auto gl_tex = ((OpenGL_Texture*)tex.get());
 | 
			
		||||
							gl_tex->update_sampler(sampler);
 | 
			
		||||
							gl.BindTexture(GL_TEXTURE_2D, gl_tex->gl_id());
 | 
			
		||||
							renderer->gl.BindTexture(GL_TEXTURE_2D, gl_tex->gl_id());
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						texture_ids[n] = gl_texture_slot;
 | 
			
		||||
						gl_texture_slot++;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					gl.Uniform1iv(location, (GLint)uniform.array_length, &texture_ids[0]);
 | 
			
		||||
					renderer->gl.Uniform1iv(location, (GLint)uniform.array_length, &texture_ids[0]);
 | 
			
		||||
					texture_slot++;
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
@ -1291,37 +1295,37 @@ namespace Blah
 | 
			
		||||
				// Float
 | 
			
		||||
				if (uniform.type == UniformType::Float)
 | 
			
		||||
				{
 | 
			
		||||
					gl.Uniform1fv(location, (GLint)uniform.array_length, data);
 | 
			
		||||
					renderer->gl.Uniform1fv(location, (GLint)uniform.array_length, data);
 | 
			
		||||
					data += uniform.array_length;
 | 
			
		||||
				}
 | 
			
		||||
				// Float2
 | 
			
		||||
				else if (uniform.type == UniformType::Float2)
 | 
			
		||||
				{
 | 
			
		||||
					gl.Uniform2fv(location, (GLint)uniform.array_length, data);
 | 
			
		||||
					renderer->gl.Uniform2fv(location, (GLint)uniform.array_length, data);
 | 
			
		||||
					data += 2 * uniform.array_length;
 | 
			
		||||
				}
 | 
			
		||||
				// Float3
 | 
			
		||||
				else if (uniform.type == UniformType::Float3)
 | 
			
		||||
				{
 | 
			
		||||
					gl.Uniform3fv(location, (GLint)uniform.array_length, data);
 | 
			
		||||
					renderer->gl.Uniform3fv(location, (GLint)uniform.array_length, data);
 | 
			
		||||
					data += 3 * uniform.array_length;
 | 
			
		||||
				}
 | 
			
		||||
				// Float4
 | 
			
		||||
				else if (uniform.type == UniformType::Float4)
 | 
			
		||||
				{
 | 
			
		||||
					gl.Uniform4fv(location, (GLint)uniform.array_length, data);
 | 
			
		||||
					renderer->gl.Uniform4fv(location, (GLint)uniform.array_length, data);
 | 
			
		||||
					data += 4 * uniform.array_length;
 | 
			
		||||
				}
 | 
			
		||||
				// Matrix3x2
 | 
			
		||||
				else if (uniform.type == UniformType::Mat3x2)
 | 
			
		||||
				{
 | 
			
		||||
					gl.UniformMatrix3x2fv(location, (GLint)uniform.array_length, 0, data);
 | 
			
		||||
					renderer->gl.UniformMatrix3x2fv(location, (GLint)uniform.array_length, 0, data);
 | 
			
		||||
					data += 6 * uniform.array_length;
 | 
			
		||||
				}
 | 
			
		||||
				// Matrix4x4
 | 
			
		||||
				else if (uniform.type == UniformType::Mat4x4)
 | 
			
		||||
				{
 | 
			
		||||
					gl.UniformMatrix4fv(location, (GLint)uniform.array_length, 0, data);
 | 
			
		||||
					renderer->gl.UniformMatrix4fv(location, (GLint)uniform.array_length, 0, data);
 | 
			
		||||
					data += 16 * uniform.array_length;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@ -1336,11 +1340,11 @@ namespace Blah
 | 
			
		||||
			GLenum alphaSrc = gl_get_blend_factor(pass.blend.alpha_src);
 | 
			
		||||
			GLenum alphaDst = gl_get_blend_factor(pass.blend.alpha_dst);
 | 
			
		||||
 | 
			
		||||
			gl.Enable(GL_BLEND);
 | 
			
		||||
			gl.BlendEquationSeparate(colorOp, alphaOp);
 | 
			
		||||
			gl.BlendFuncSeparate(colorSrc, colorDst, alphaSrc, alphaDst);
 | 
			
		||||
			renderer->gl.Enable(GL_BLEND);
 | 
			
		||||
			renderer->gl.BlendEquationSeparate(colorOp, alphaOp);
 | 
			
		||||
			renderer->gl.BlendFuncSeparate(colorSrc, colorDst, alphaSrc, alphaDst);
 | 
			
		||||
 | 
			
		||||
			gl.ColorMask(
 | 
			
		||||
			renderer->gl.ColorMask(
 | 
			
		||||
				((int)pass.blend.mask & (int)BlendMask::Red),
 | 
			
		||||
				((int)pass.blend.mask & (int)BlendMask::Green),
 | 
			
		||||
				((int)pass.blend.mask & (int)BlendMask::Blue),
 | 
			
		||||
@ -1351,7 +1355,7 @@ namespace Blah
 | 
			
		||||
			unsigned char b = pass.blend.rgba >> 8;
 | 
			
		||||
			unsigned char a = pass.blend.rgba;
 | 
			
		||||
 | 
			
		||||
			gl.BlendColor(
 | 
			
		||||
			renderer->gl.BlendColor(
 | 
			
		||||
				r / 255.0f,
 | 
			
		||||
				g / 255.0f,
 | 
			
		||||
				b / 255.0f,
 | 
			
		||||
@ -1362,38 +1366,38 @@ namespace Blah
 | 
			
		||||
		{
 | 
			
		||||
			if (pass.depth == Compare::None)
 | 
			
		||||
			{
 | 
			
		||||
				gl.Disable(GL_DEPTH_TEST);
 | 
			
		||||
				renderer->gl.Disable(GL_DEPTH_TEST);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				gl.Enable(GL_DEPTH_TEST);
 | 
			
		||||
				renderer->gl.Enable(GL_DEPTH_TEST);
 | 
			
		||||
 | 
			
		||||
				switch (pass.depth)
 | 
			
		||||
				{
 | 
			
		||||
				case Compare::None: break;
 | 
			
		||||
				case Compare::Always:
 | 
			
		||||
					gl.DepthFunc(GL_ALWAYS);
 | 
			
		||||
					renderer->gl.DepthFunc(GL_ALWAYS);
 | 
			
		||||
					break;
 | 
			
		||||
				case Compare::Equal:
 | 
			
		||||
					gl.DepthFunc(GL_EQUAL);
 | 
			
		||||
					renderer->gl.DepthFunc(GL_EQUAL);
 | 
			
		||||
					break;
 | 
			
		||||
				case Compare::Greater:
 | 
			
		||||
					gl.DepthFunc(GL_GREATER);
 | 
			
		||||
					renderer->gl.DepthFunc(GL_GREATER);
 | 
			
		||||
					break;
 | 
			
		||||
				case Compare::GreatorOrEqual:
 | 
			
		||||
					gl.DepthFunc(GL_GEQUAL);
 | 
			
		||||
					renderer->gl.DepthFunc(GL_GEQUAL);
 | 
			
		||||
					break;
 | 
			
		||||
				case Compare::Less:
 | 
			
		||||
					gl.DepthFunc(GL_LESS);
 | 
			
		||||
					renderer->gl.DepthFunc(GL_LESS);
 | 
			
		||||
					break;
 | 
			
		||||
				case Compare::LessOrEqual:
 | 
			
		||||
					gl.DepthFunc(GL_LEQUAL);
 | 
			
		||||
					renderer->gl.DepthFunc(GL_LEQUAL);
 | 
			
		||||
					break;
 | 
			
		||||
				case Compare::Never:
 | 
			
		||||
					gl.DepthFunc(GL_NEVER);
 | 
			
		||||
					renderer->gl.DepthFunc(GL_NEVER);
 | 
			
		||||
					break;
 | 
			
		||||
				case Compare::NotEqual:
 | 
			
		||||
					gl.DepthFunc(GL_NOTEQUAL);
 | 
			
		||||
					renderer->gl.DepthFunc(GL_NOTEQUAL);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@ -1403,18 +1407,18 @@ namespace Blah
 | 
			
		||||
		{
 | 
			
		||||
			if (pass.cull == Cull::None)
 | 
			
		||||
			{
 | 
			
		||||
				gl.Disable(GL_CULL_FACE);
 | 
			
		||||
				renderer->gl.Disable(GL_CULL_FACE);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				gl.Enable(GL_CULL_FACE);
 | 
			
		||||
				renderer->gl.Enable(GL_CULL_FACE);
 | 
			
		||||
 | 
			
		||||
				if (pass.cull == Cull::Back)
 | 
			
		||||
					gl.CullFace(GL_BACK);
 | 
			
		||||
					renderer->gl.CullFace(GL_BACK);
 | 
			
		||||
				else if (pass.cull == Cull::Front)
 | 
			
		||||
					gl.CullFace(GL_FRONT);
 | 
			
		||||
					renderer->gl.CullFace(GL_FRONT);
 | 
			
		||||
				else
 | 
			
		||||
					gl.CullFace(GL_FRONT_AND_BACK);
 | 
			
		||||
					renderer->gl.CullFace(GL_FRONT_AND_BACK);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1423,14 +1427,14 @@ namespace Blah
 | 
			
		||||
			Rectf viewport = pass.viewport;
 | 
			
		||||
			viewport.y = size.y - viewport.y - viewport.h;
 | 
			
		||||
 | 
			
		||||
			gl.Viewport((GLint)viewport.x, (GLint)viewport.y, (GLint)viewport.w, (GLint)viewport.h);
 | 
			
		||||
			renderer->gl.Viewport((GLint)viewport.x, (GLint)viewport.y, (GLint)viewport.w, (GLint)viewport.h);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Scissor
 | 
			
		||||
		{
 | 
			
		||||
			if (!pass.has_scissor)
 | 
			
		||||
			{
 | 
			
		||||
				gl.Disable(GL_SCISSOR_TEST);
 | 
			
		||||
				renderer->gl.Disable(GL_SCISSOR_TEST);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
@ -1442,21 +1446,21 @@ namespace Blah
 | 
			
		||||
				if (scissor.h < 0)
 | 
			
		||||
					scissor.h = 0;
 | 
			
		||||
 | 
			
		||||
				gl.Enable(GL_SCISSOR_TEST);
 | 
			
		||||
				gl.Scissor((GLint)scissor.x, (GLint)scissor.y, (GLint)scissor.w, (GLint)scissor.h);
 | 
			
		||||
				renderer->gl.Enable(GL_SCISSOR_TEST);
 | 
			
		||||
				renderer->gl.Scissor((GLint)scissor.x, (GLint)scissor.y, (GLint)scissor.w, (GLint)scissor.h);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Draw the Mesh
 | 
			
		||||
		{
 | 
			
		||||
			gl.BindVertexArray(mesh->gl_id());
 | 
			
		||||
			renderer->gl.BindVertexArray(mesh->gl_id());
 | 
			
		||||
 | 
			
		||||
			GLenum index_format = mesh->gl_index_format();
 | 
			
		||||
			int index_size = mesh->gl_index_size();
 | 
			
		||||
 | 
			
		||||
			if (pass.instance_count > 0)
 | 
			
		||||
			{
 | 
			
		||||
				gl.DrawElementsInstanced(
 | 
			
		||||
				renderer->gl.DrawElementsInstanced(
 | 
			
		||||
					GL_TRIANGLES,
 | 
			
		||||
					(GLint)(pass.index_count),
 | 
			
		||||
					index_format,
 | 
			
		||||
@ -1465,18 +1469,18 @@ namespace Blah
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				gl.DrawElements(
 | 
			
		||||
				renderer->gl.DrawElements(
 | 
			
		||||
					GL_TRIANGLES,
 | 
			
		||||
					(GLint)(pass.index_count),
 | 
			
		||||
					index_format,
 | 
			
		||||
					(void*)(index_size * pass.index_start));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			gl.BindVertexArray(0);
 | 
			
		||||
			renderer->gl.BindVertexArray(0);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Graphics::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
 | 
			
		||||
	void Renderer_OpenGL::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
 | 
			
		||||
	{
 | 
			
		||||
		int clear = 0;
 | 
			
		||||
 | 
			
		||||
@ -1487,14 +1491,28 @@ namespace Blah
 | 
			
		||||
		if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil)
 | 
			
		||||
			clear |= GL_STENCIL_BUFFER_BIT;
 | 
			
		||||
 | 
			
		||||
		gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
 | 
			
		||||
		gl.Disable(GL_SCISSOR_TEST);
 | 
			
		||||
		gl.ColorMask(true, true, true, true);
 | 
			
		||||
		gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
 | 
			
		||||
		gl.ClearDepth(depth);
 | 
			
		||||
		gl.ClearStencil(stencil);
 | 
			
		||||
		gl.Clear(clear);
 | 
			
		||||
		renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
 | 
			
		||||
		renderer->gl.Disable(GL_SCISSOR_TEST);
 | 
			
		||||
		renderer->gl.ColorMask(true, true, true, true);
 | 
			
		||||
		renderer->gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
 | 
			
		||||
		renderer->gl.ClearDepth(depth);
 | 
			
		||||
		renderer->gl.ClearStencil(stencil);
 | 
			
		||||
		renderer->gl.Clear(clear);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // BLAH_GRAPHICS_OPENGL
 | 
			
		||||
Blah::Renderer* Blah::Renderer::try_make_opengl()
 | 
			
		||||
{
 | 
			
		||||
	return new Blah::Renderer_OpenGL();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else // BLAH_RENDERER_OPENGL
 | 
			
		||||
 | 
			
		||||
#include "renderer.h"
 | 
			
		||||
Blah::Renderer* Blah::Renderer::try_make_opengl()
 | 
			
		||||
{
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user