Merge branch 'master' into master

This commit is contained in:
Noel Berry 2021-03-23 15:45:19 -07:00 committed by GitHub
commit 6c13753c64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 3134 additions and 1798 deletions

View File

@ -9,28 +9,26 @@ add_library(blah
src/core/app.cpp src/core/app.cpp
src/core/filesystem.cpp src/core/filesystem.cpp
src/core/log.cpp src/core/common.cpp
src/core/time.cpp src/core/time.cpp
src/graphics/batch.cpp
src/graphics/blend.cpp src/graphics/blend.cpp
src/graphics/framebuffer.cpp src/graphics/framebuffer.cpp
src/graphics/material.cpp src/graphics/material.cpp
src/graphics/mesh.cpp src/graphics/mesh.cpp
src/graphics/renderpass.cpp src/graphics/renderpass.cpp
src/graphics/shader.cpp src/graphics/shader.cpp
src/graphics/spritefont.cpp
src/graphics/subtexture.cpp
src/graphics/texture.cpp src/graphics/texture.cpp
src/input/input.cpp src/input/input.cpp
src/input/virtual_stick.cpp src/input/binding.cpp
src/input/virtual_button.cpp src/input/binding_registry.cpp
src/input/virtual_axis.cpp
src/containers/str.cpp src/containers/str.cpp
src/drawing/batch.cpp
src/drawing/spritefont.cpp
src/drawing/subtexture.cpp
src/images/aseprite.cpp src/images/aseprite.cpp
src/images/font.cpp src/images/font.cpp
src/images/image.cpp src/images/image.cpp
@ -54,11 +52,11 @@ add_library(blah
src/streams/memorystream.cpp src/streams/memorystream.cpp
src/streams/stream.cpp src/streams/stream.cpp
src/internal/graphics_backend_gl.cpp src/internal/graphics_backend_gl.cpp
src/internal/graphics_backend_d3d11.cpp src/internal/graphics_backend_d3d11.cpp
src/internal/graphics_backend_dummy.cpp src/internal/graphics_backend_dummy.cpp
src/internal/platform_backend_sdl2.cpp src/internal/platform_backend_sdl2.cpp
src/internal/platform_backend_win32.cpp
) )
target_include_directories(blah target_include_directories(blah
@ -69,34 +67,49 @@ target_include_directories(blah
) )
# Platform Variables # Platform Variables
set(SDL2_ENABLED true CACHE BOOL "Use SDL2 as the System implementation") set(PLATFORM_SDL2 true CACHE BOOL "Use SDL2 Platform Backend")
set(OPENGL_ENABLED true CACHE BOOL "Use OpenGL graphics implementation") set(PLATFORM_WIN32 false CACHE BOOL "Use Win32 Platform Backend")
set(D3D11_ENABLED false CACHE BOOL "Use D3D11 graphics implementation") set(GRAPHICS_OPENGL true CACHE BOOL "Use OpenGL Graphics Backend")
set(GRAPHICS_D3D11 false CACHE BOOL "Use D3D11 Graphics Backend")
set(LIBS "") set(LIBS "")
# add OpenGL definition if we're using it # use the OpenGL Graphics Backend
if (OPENGL_ENABLED) if (GRAPHICS_OPENGL)
add_compile_definitions(BLAH_USE_OPENGL)
endif()
# add D3D11 definition if we're using it add_compile_definitions(BLAH_GRAPHICS_OPENGL)
if (D3D11_ENABLED)
add_compile_definitions(BLAH_USE_D3D11) # use the D3D11 Graphics Backend
elseif (GRAPHICS_D3D11)
add_compile_definitions(BLAH_GRAPHICS_D3D11)
set(LIBS ${LIBS} d3d11.lib dxguid.lib D3Dcompiler.lib) set(LIBS ${LIBS} d3d11.lib dxguid.lib D3Dcompiler.lib)
endif() endif()
# Link and create SDL2 Definition if we're using it # use the SDL2 Platform Backend
if (SDL2_ENABLED) # Link and create SDL2 Definition
add_compile_definitions(BLAH_USE_SDL2) if (PLATFORM_SDL2)
add_compile_definitions(BLAH_PLATFORM_SDL2)
# Emscripten can import SDL2 directly
if (EMSCRIPTEN) if (EMSCRIPTEN)
set_target_properties(blah PROPERTIES COMPILE_FLAGS "-s USE_SDL=2") set_target_properties(blah PROPERTIES COMPILE_FLAGS "-s USE_SDL=2")
# Load SDL2 Normally
else()
# Try to find SDL2
if (DEFINED SDL2_LIBRARIES AND DEFINED SDL2_INCLUDE_DIRS)
set(SDL2_FOUND true)
else() else()
# Attempt to find SDL2
find_package(SDL2 QUIET) find_package(SDL2 QUIET)
endif()
# If CMake cannot find SDL2 library, then it gets downloaded and compiled that way # If CMake cannot find SDL2 library, then it gets downloaded and compiled that way
if (NOT ${SDL2_FOUND}) if (NOT ${SDL2_FOUND})
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
SDL2 SDL2
@ -108,11 +121,20 @@ if (SDL2_ENABLED)
FetchContent_Populate(SDL2) FetchContent_Populate(SDL2)
add_subdirectory(${sdl2_SOURCE_DIR} ${sdl2_BINARY_DIR}) add_subdirectory(${sdl2_SOURCE_DIR} ${sdl2_BINARY_DIR})
endif() endif()
endif() endif()
# Either way we are linking to SDL2 # Add Library and Include Dirs
set(LIBS ${LIBS} SDL2) set(LIBS ${LIBS} ${SDL2_LIBRARIES})
target_include_directories(blah PUBLIC ${SDL2_INCLUDE_DIRS})
endif() endif()
# use the Win32 Platform Backend
elseif (PLATFORM_WIN32)
add_compile_definitions(BLAH_PLATFORM_WIN32)
endif() endif()
target_link_libraries(blah PUBLIC ${LIBS}) target_link_libraries(blah PUBLIC ${LIBS})

View File

@ -1,17 +1,18 @@
## blah ## blah
A small C++ game framework for 2D games. A small 2D C++ Game Framework, using few dependencies and simple code to mainain easy building and portability.
Goal is to be simple and use as few dependencies as possible, to maintain easy building and portability.
**☆ This will likely see breaking changes! Use at your own risk! ☆** **☆ This will likely see breaking changes! Use at your own risk! ☆**
#### building #### building
- Requires C++17 and CMake 3.12+ - Requires C++17 and CMake 3.12+
- Platform Backend - A single *Platform* backend must be enabled:
- [SDL2](https://github.com/NoelFB/blah/blob/master/src/internal/platform_backend_sdl2.cpp) can be enabled in CMake with `SDL2_ENABLED`, and setting `SDL2_INCLUDE_DIRS` and `SDL2_LIBRARIES` - [SDL2](https://github.com/NoelFB/blah/blob/master/src/internal/platform_backend_sdl2.cpp) can be enabled in CMake with `PLATFORM_SDL2`, and setting `SDL2_INCLUDE_DIRS` and `SDL2_LIBRARIES`
- Graphics Backend - [WIN32](https://github.com/NoelFB/blah/blob/master/src/internal/platform_backend_win32.cpp) (UNFINISHED) can be enabled in CMake with `PLATFORM_WIN32`.
- [OpenGL](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend_gl.cpp) can be enabled in CMake with `OPENGL_ENABLED`. - Additional backends can be added by implementing the [Platform Backend](https://github.com/NoelFB/blah/blob/master/src/internal/platform_backend.h)
- [D3D11](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend_d3d11.cpp) (unfinished) can be enabled in CMake with `D3D11_ENABLED`. - A single *Graphics* backend must be enabled:
- Other backends can be added by implementing the [Platform Backend](https://github.com/NoelFB/blah/blob/master/src/internal/platform_backend.h) or [Graphics Backend](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend.h). - [OpenGL](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend_gl.cpp) can be enabled in CMake with `GRAPHICS_OPENGL`.
- [D3D11](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend_d3d11.cpp) can be enabled in CMake with `GRAPHICS_D3D11`.
- Additional backends can be added by implementing the [Graphics Backend](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend.h).
#### notes #### notes
- There's no Shader abstraction, so the [Sprite Batcher](https://github.com/NoelFB/blah/blob/master/include/blah/drawing/batch.h) has hard-coded GLSL/HLSL. This will need to change. - There's no Shader abstraction, so the [Sprite Batcher](https://github.com/NoelFB/blah/blob/master/include/blah/drawing/batch.h) has hard-coded GLSL/HLSL. This will need to change.
@ -38,7 +39,7 @@ void render()
App::backbuffer->clear(Color::black); App::backbuffer->clear(Color::black);
auto center = Vec2(App::backbuffer->width(), App::backbuffer->height()) / 2; auto center = Vec2(App::backbuffer->width(), App::backbuffer->height()) / 2;
auto rotation = Time::elapsed * Calc::TAU; auto rotation = Time::seconds * Calc::TAU;
auto transform = Mat3x2::create_transform(center, Vec2::zero, Vec2::one, rotation); auto transform = Mat3x2::create_transform(center, Vec2::zero, Vec2::one, rotation);
batch.push_matrix(transform); batch.push_matrix(transform);

View File

@ -2,16 +2,16 @@
#include "blah/core/app.h" #include "blah/core/app.h"
#include "blah/core/filesystem.h" #include "blah/core/filesystem.h"
#include "blah/core/log.h" #include "blah/core/common.h"
#include "blah/core/time.h" #include "blah/core/time.h"
#include "blah/containers/vector.h" #include "blah/containers/vector.h"
#include "blah/containers/stackvector.h" #include "blah/containers/stackvector.h"
#include "blah/containers/str.h" #include "blah/containers/str.h"
#include "blah/drawing/batch.h" #include "blah/graphics/batch.h"
#include "blah/drawing/spritefont.h" #include "blah/graphics/spritefont.h"
#include "blah/drawing/subtexture.h" #include "blah/graphics/subtexture.h"
#include "blah/graphics/blend.h" #include "blah/graphics/blend.h"
#include "blah/graphics/framebuffer.h" #include "blah/graphics/framebuffer.h"
@ -28,9 +28,8 @@
#include "blah/images/packer.h" #include "blah/images/packer.h"
#include "blah/input/input.h" #include "blah/input/input.h"
#include "blah/input/virtual_stick.h" #include "blah/input/binding.h"
#include "blah/input/virtual_button.h" #include "blah/input/binding_registry.h"
#include "blah/input/virtual_axis.h"
#include "blah/math/calc.h" #include "blah/math/calc.h"
#include "blah/math/circle.h" #include "blah/math/circle.h"

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <blah/core/log.h> #include <blah/core/common.h>
#include <new> #include <new>
#include <initializer_list> #include <initializer_list>

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
#include <stdarg.h> #include <stdarg.h>
#include <cstdio> #include <cstdio>
#include <blah/containers/vector.h> #include <blah/containers/vector.h>
@ -10,6 +10,7 @@ namespace Blah
class StrOf; class StrOf;
using String = StrOf<64>; using String = StrOf<64>;
// A simple String implementation
class Str class Str
{ {
public: public:
@ -78,7 +79,7 @@ namespace Blah
// Returns the unicode value at the given index. // Returns the unicode value at the given index.
// Assumes the index is a valid utf8 starting point. // Assumes the index is a valid utf8 starting point.
uint32_t utf8_at(int index) const; u32 utf8_at(int index) const;
// Returns the byte-length of the utf8 character. // Returns the byte-length of the utf8 character.
// Assumes the index is a valid utf8 starting point. // Assumes the index is a valid utf8 starting point.
@ -88,7 +89,7 @@ namespace Blah
Str& append(char c); Str& append(char c);
// appends the given unicode character // appends the given unicode character
Str& append(uint32_t c); Str& append(u32 c);
// appends the given c string // appends the given c string
Str& append(const char* start, const char* end = nullptr); Str& append(const char* start, const char* end = nullptr);
@ -100,7 +101,7 @@ namespace Blah
Str& append_fmt(const char* fmt, ...); Str& append_fmt(const char* fmt, ...);
// appends a utf16 string // appends a utf16 string
Str& append_utf16(const uint16_t* start, const uint16_t* end = nullptr, bool swapEndian = false); Str& append_utf16(const u16* start, const u16* end = nullptr, bool swapEndian = false);
// trims whitespace // trims whitespace
Str& trim(); Str& trim();
@ -138,8 +139,10 @@ namespace Blah
// returns a substring of the string // returns a substring of the string
String substr(int start, int end) const; String substr(int start, int end) const;
// Splits the string into a vector of strings
Vector<String> split(char ch) const; Vector<String> split(char ch) const;
// replaces all occurances of old string with the new string
Str& replace(const Str& old_str, const Str& new_str); Str& replace(const Str& old_str, const Str& new_str);
// replaces all occurances of the given character in the string // replaces all occurances of the given character in the string
@ -171,9 +174,14 @@ namespace Blah
// returns a pointer to the heap buffer or to our stack allocation // returns a pointer to the heap buffer or to our stack allocation
char* data() { return (m_buffer != nullptr ? m_buffer : ((char*)(this) + sizeof(Str))); } char* data() { return (m_buffer != nullptr ? m_buffer : ((char*)(this) + sizeof(Str))); }
// returns a pointer to the heap buffer or to our stack allocation
const char* data() const { return (m_buffer != nullptr ? m_buffer : ((char*)(this) + sizeof(Str))); } const char* data() const { return (m_buffer != nullptr ? m_buffer : ((char*)(this) + sizeof(Str))); }
// assigns the contents of the string
void set(const Str& str) { set(str.cstr(), str.cstr() + str.m_length); } void set(const Str& str) { set(str.cstr(), str.cstr() + str.m_length); }
// assigns the contents of the string
void set(const char* start, const char* end = nullptr); void set(const char* start, const char* end = nullptr);
private: private:
@ -187,6 +195,7 @@ namespace Blah
// combine string // combine string
inline Str operator+(const Str& lhs, const Str& rhs) { Str str; str.append(lhs).append(rhs); return str; } inline Str operator+(const Str& lhs, const Str& rhs) { Str str; str.append(lhs).append(rhs); return str; }
// A string with a local stack buffer of size T
template<int T> template<int T>
class StrOf : public Str class StrOf : public Str
{ {

View File

@ -1,10 +1,12 @@
#pragma once #pragma once
#include <blah/core/log.h> #include <blah/core/common.h>
#include <type_traits> #include <type_traits>
#include <initializer_list>
#include <new> #include <new>
namespace Blah namespace Blah
{ {
// A lightweight Vector implementation
template<class T> template<class T>
class Vector class Vector
{ {
@ -19,6 +21,7 @@ namespace Blah
Vector(int capacity); Vector(int capacity);
Vector(const Vector& src); Vector(const Vector& src);
Vector(Vector&& src) noexcept; Vector(Vector&& src) noexcept;
Vector(std::initializer_list<T> list);
~Vector(); ~Vector();
Vector& operator=(const Vector& src); Vector& operator=(const Vector& src);
@ -95,6 +98,17 @@ namespace Blah
src.m_count = 0; src.m_count = 0;
} }
template<class T>
inline Vector<T>::Vector(std::initializer_list<T> list)
{
m_buffer = nullptr;
m_count = m_capacity = 0;
reserve(list.size());
for (auto& it : list)
push_back(std::move(it));
}
template<class T> template<class T>
inline Vector<T>::~Vector() inline Vector<T>::~Vector()
{ {

View File

@ -1,31 +1,72 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <blah/core/log.h> #include <blah/core/common.h>
namespace Blah namespace Blah
{ {
// Application Event Functions
using AppEventFn = std::function<void()>; using AppEventFn = std::function<void()>;
// Application Logging Functions
using AppLogFn = std::function<void(const char* message, Log::Category category)>; using AppLogFn = std::function<void(const char* message, Log::Category category)>;
// Application Configuration
struct Config struct Config
{ {
// Application name.
// This has no default and must be set.
const char* name; 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; 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; 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; int max_updates;
// target framerate.
// defaults to 60.
int target_framerate; int target_framerate;
// Callback on application startup
// Defaults to nothing.
AppEventFn on_startup; AppEventFn on_startup;
// Callback on application shutdown
// Defaults to nothing.
AppEventFn on_shutdown; AppEventFn on_shutdown;
// Callback on application update
// Defaults to nothing.
AppEventFn on_update; AppEventFn on_update;
// Callback on application render
// Defaults to nothing.
AppEventFn on_render; AppEventFn on_render;
// Callback when the user has requested the application close.
// For example, pressing the Close button
// By default this calls `App::exit()`
AppEventFn on_exit_request; AppEventFn on_exit_request;
// Callback when the application logs info/warning/errors
// Defaults to printf.
AppLogFn on_log; AppLogFn on_log;
// Default config setup
Config(); Config();
}; };
// Renderer the Application is using
enum class Renderer enum class Renderer
{ {
None = -1, None = -1,
@ -35,16 +76,24 @@ namespace Blah
Count Count
}; };
// Features available on the current Renderer
struct RendererFeatures struct RendererFeatures
{ {
// Whether Mesh Instancing is available
bool instancing = false; bool instancing = false;
// Whether the Texture origin is the bottom left.
// This is true for OpenGL.
bool origin_bottom_left = false; bool origin_bottom_left = false;
// Maximum Texture Size available
int max_texture_size = 0; int max_texture_size = 0;
}; };
class FrameBuffer; class FrameBuffer;
using FrameBufferRef = std::shared_ptr<FrameBuffer>; using FrameBufferRef = std::shared_ptr<FrameBuffer>;
// Application
namespace App namespace App
{ {
// Runs the application // Runs the application
@ -53,7 +102,9 @@ namespace Blah
// Returns whether the application is running // Returns whether the application is running
bool is_running(); bool is_running();
// Exits the application // Exits the application.
// This only signals for the application to close, it will not stop
// until the current update and render calls are finished.
void exit(); void exit();
// Gets the config data used to run the application // Gets the config data used to run the application
@ -71,16 +122,20 @@ namespace Blah
// Gets the height of the window // Gets the height of the window
int height(); int height();
// Gets the drawable width of the window // 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(); int draw_width();
// Gets the drawable height of the window // 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(); int draw_height();
// Gets the content scale based on the OS // Gets the content scale based on the platform.
// macOS is usually 2.0, other platforms vary.
float content_scale(); float content_scale();
// Toggles fullscreen // Toggles fullscreen if supported on the platform.
// Otherwise this function does nothing.
void fullscreen(bool enabled); void fullscreen(bool enabled);
// Returns the Rendering API in use // Returns the Rendering API in use

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <cstdint>
// error / abort // error / abort
#if defined(DEBUG) || defined(_DEBUG) #if defined(DEBUG) || defined(_DEBUG)
@ -33,6 +34,16 @@
namespace Blah namespace Blah
{ {
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
namespace Log namespace Log
{ {
enum class Category enum class Category
@ -42,8 +53,8 @@ namespace Blah
Error Error
}; };
void print(const char* info, ...); void info(const char* message, ...);
void warn(const char* info, ...); void warn(const char* message, ...);
void error(const char* info, ...); void error(const char* message, ...);
} }
} }

View File

@ -5,40 +5,71 @@
namespace Blah namespace Blah
{ {
using FilePath = StrOf<265>; using FilePath = StrOf<265>;
class FileStream;
enum class FileMode enum class FileMode
{ {
None = 0, None = 0,
Read = 1 << 1, Read = 1 << 0,
Write = 1 << 2, Write = 1 << 1,
ReadWrite = Read | Write, ReadWrite = Read | Write,
}; };
namespace Directory namespace Directory
{ {
// Creates a new directory at the given location.
// Returns false if unable to create the directory.
bool create(const FilePath& path); bool create(const FilePath& path);
// Returns whether the given directory exists
bool exists(const FilePath& path); bool exists(const FilePath& path);
// Tries to delete a path and returns whether it was successful
bool remove(const FilePath& path); bool remove(const FilePath& path);
Vector<FilePath> enumerate(const FilePath& str, bool recursive = true);
// Enumerates over a directory and returns a list of files & directories
Vector<FilePath> enumerate(const FilePath& path, bool recursive = true);
// Opens the path in the File Explorer / Finder
void explore(const FilePath& path); void explore(const FilePath& path);
} }
namespace File namespace File
{ {
// Checks if the given file exists
bool exists(const FilePath& path); bool exists(const FilePath& path);
// Tries to delete a file and returns whether it was successful
bool remove(const FilePath& path); bool remove(const FilePath& path);
// Opens the given file and returns a stream
FileStream open(const FilePath& path, FileMode mode = FileMode::ReadWrite);
} }
namespace Path namespace Path
{ {
// Returns the file name of the path
FilePath get_file_name(const FilePath& path); FilePath get_file_name(const FilePath& path);
// Returns the file name of the path, without the file extension
FilePath get_file_name_no_ext(const FilePath& path); FilePath get_file_name_no_ext(const FilePath& path);
// Returns the path without any file extensions
FilePath get_path_no_ext(const FilePath& path); FilePath get_path_no_ext(const FilePath& path);
// Returns relative path
FilePath get_path_after(const FilePath& path, const FilePath& after); FilePath get_path_after(const FilePath& path, const FilePath& after);
// Gets the top directory name from the path
FilePath get_directory_name(const FilePath& path); FilePath get_directory_name(const FilePath& path);
// Normalizes a path (removes ../, changes \\ to /, removes redundant slashes, etc)
FilePath normalize(const FilePath& path); FilePath normalize(const FilePath& path);
// Joins two paths together
FilePath join(const FilePath& a, const FilePath& b); FilePath join(const FilePath& a, const FilePath& b);
// Joins two paths together
template<typename ... Args> template<typename ... Args>
FilePath join(const FilePath& a, const FilePath& b, const Args&... args) FilePath join(const FilePath& a, const FilePath& b, const Args&... args)
{ {

View File

@ -1,21 +1,21 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
namespace Blah namespace Blah
{ {
struct Time struct Time
{ {
// ticks per second (microseconds, in this case) // ticks per second (microseconds, in this case)
static constexpr uint64_t ticks_per_second = 1000000; static constexpr u64 ticks_per_second = 1000000;
// uptime, in ticks // uptime, in ticks
static uint64_t ticks; static u64 ticks;
// uptime, in seconds // uptime, in seconds
static double seconds; static double seconds;
// previous frame uptime, in ticks // previous frame uptime, in ticks
static uint64_t previous_ticks; static u64 previous_ticks;
// previous frame uptime, in seconds // previous frame uptime, in seconds
static double previous_seconds; static double previous_seconds;

View File

@ -1,76 +0,0 @@
#pragma once
#include <inttypes.h>
#include <blah/containers/str.h>
#include <blah/containers/vector.h>
#include <blah/drawing/subtexture.h>
#include <blah/math/vec2.h>
#include <unordered_map>
namespace Blah
{
class Font;
class SpriteFont
{
public:
struct Character
{
Subtexture subtexture;
float advance = 0;
Vec2 offset;
};
private:
// charset & kerning maps
std::unordered_map<uint32_t, Character> m_characters;
std::unordered_map<uint64_t, float> m_kerning;
// built texture
Vector<TextureRef> m_atlas;
public:
static const uint32_t* ASCII;
String name;
float size;
float ascent;
float descent;
float line_gap;
// Note:
// charset is a list of range pairs, until a 0 terminator (ex. 32,128,0)
SpriteFont();
SpriteFont(const char* file, float size);
SpriteFont(const char* file, float size, const uint32_t* charset);
SpriteFont(const Font& font, float size);
SpriteFont(const Font& font, float size, const uint32_t* charset);
SpriteFont(const SpriteFont&) = delete;
SpriteFont(SpriteFont&& src) noexcept;
~SpriteFont();
void dispose();
SpriteFont& operator=(const SpriteFont&) = delete;
SpriteFont& operator=(SpriteFont&& src) noexcept;
float height() const { return ascent - descent; }
float line_height() const { return ascent - descent + line_gap; }
const Vector<TextureRef>& textures() { return m_atlas; }
float width_of(const String& text) const;
float width_of_line(const String& text, int start = 0) const;
float height_of(const String& text) const;
void build(const char* file, float size, const uint32_t* charset);
void build(const Font& font, float size, const uint32_t* charset);
float get_kerning(uint32_t codepoint0, uint32_t codepoint1) const;
void set_kerning(uint32_t codepoint0, uint32_t codepoint1, float kerning);
Character& get_character(uint32_t codepoint) { return m_characters[codepoint]; }
const Character& get_character(uint32_t codepoint) const;
Character& operator[](uint32_t codepoint) { return m_characters[codepoint]; }
const Character& operator[](uint32_t codepoint) const;
};
}

View File

@ -5,8 +5,8 @@
#include <blah/math/mat3x2.h> #include <blah/math/mat3x2.h>
#include <blah/math/mat4x4.h> #include <blah/math/mat4x4.h>
#include <blah/math/color.h> #include <blah/math/color.h>
#include <blah/drawing/subtexture.h> #include <blah/graphics/subtexture.h>
#include <blah/drawing/spritefont.h> #include <blah/graphics/spritefont.h>
#include <blah/containers/vector.h> #include <blah/containers/vector.h>
#include <blah/graphics/blend.h> #include <blah/graphics/blend.h>
#include <blah/graphics/sampler.h> #include <blah/graphics/sampler.h>
@ -15,9 +15,14 @@
namespace Blah namespace Blah
{ {
// Spritebatcher Color Mode
enum class ColorMode enum class ColorMode
{ {
// Draws textures and shapes normally
Normal, Normal,
// Ignores the texture color but still uses transparency, essentially
// drawing the "shape" of the texture a solid color
Wash Wash
}; };
@ -38,7 +43,7 @@ namespace Blah
inline TextAlign operator|(TextAlign lhs, TextAlign rhs) { return static_cast<TextAlign>(static_cast<char>(lhs) | static_cast<char>(rhs)); } inline TextAlign operator|(TextAlign lhs, TextAlign rhs) { return static_cast<TextAlign>(static_cast<char>(lhs) | static_cast<char>(rhs)); }
inline TextAlign operator&(TextAlign lhs, TextAlign rhs) { return static_cast<TextAlign>(static_cast<char>(lhs) & static_cast<char>(rhs)); } inline TextAlign operator&(TextAlign lhs, TextAlign rhs) { return static_cast<TextAlign>(static_cast<char>(lhs) & static_cast<char>(rhs)); }
// A simple 2D sprite batcher, used for drawing shapes and textures // A 2D sprite batcher, used for drawing shapes and textures
class Batch class Batch
{ {
public: public:
@ -187,10 +192,10 @@ namespace Blah
Vec2 tex; Vec2 tex;
Color col; Color col;
uint8_t mult; u8 mult;
uint8_t wash; u8 wash;
uint8_t fill; u8 fill;
uint8_t pad; u8 pad;
}; };
struct DrawBatch struct DrawBatch
@ -219,11 +224,11 @@ namespace Blah
MeshRef m_mesh; MeshRef m_mesh;
Mat3x2 m_matrix; Mat3x2 m_matrix;
ColorMode m_color_mode; ColorMode m_color_mode;
uint8_t m_tex_mult; u8 m_tex_mult;
uint8_t m_tex_wash; u8 m_tex_wash;
DrawBatch m_batch; DrawBatch m_batch;
Vector<Vertex> m_vertices; Vector<Vertex> m_vertices;
Vector<uint32_t> m_indices; Vector<u32> m_indices;
Vector<Mat3x2> m_matrix_stack; Vector<Mat3x2> m_matrix_stack;
Vector<Rect> m_scissor_stack; Vector<Rect> m_scissor_stack;
Vector<BlendMode> m_blend_stack; Vector<BlendMode> m_blend_stack;

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
namespace Blah namespace Blah
{ {
@ -46,6 +46,7 @@ namespace Blah
RGBA = Red | Green | Blue | Alpha, RGBA = Red | Green | Blue | Alpha,
}; };
// BlendMode using for rendering
struct BlendMode struct BlendMode
{ {
// Normal, pre-multiplied, Blend Mode // Normal, pre-multiplied, Blend Mode
@ -61,7 +62,7 @@ namespace Blah
BlendFactor alpha_src; BlendFactor alpha_src;
BlendFactor alpha_dst; BlendFactor alpha_dst;
BlendMask mask; BlendMask mask;
uint32_t rgba; u32 rgba;
BlendMode() = default; BlendMode() = default;
@ -78,7 +79,7 @@ namespace Blah
BlendMode( BlendMode(
BlendOp color_op, BlendFactor color_src, BlendFactor color_dst, BlendOp color_op, BlendFactor color_src, BlendFactor color_dst,
BlendOp alpha_op, BlendFactor alpha_src, BlendFactor alpha_dst, BlendOp alpha_op, BlendFactor alpha_src, BlendFactor alpha_dst,
BlendMask blend_mask, uint32_t blend_rgba) : BlendMask blend_mask, u32 blend_rgba) :
color_op(color_op), color_op(color_op),
color_src(color_src), color_src(color_src),
color_dst(color_dst), color_dst(color_dst),

View File

@ -4,16 +4,8 @@
#include <blah/math/color.h> #include <blah/math/color.h>
#include <memory> #include <memory>
// 4 color attachments + 1 depth/stencil
#define BLAH_ATTACHMENTS 5
namespace Blah namespace Blah
{ {
typedef StackVector<TextureRef, BLAH_ATTACHMENTS> Attachments;
class FrameBuffer;
typedef std::shared_ptr<FrameBuffer> FrameBufferRef;
enum class ClearMask enum class ClearMask
{ {
None = 0, None = 0,
@ -23,6 +15,14 @@ namespace Blah
All = (int)Color | (int)Depth | (int)Stencil All = (int)Color | (int)Depth | (int)Stencil
}; };
// Up to 4 color attachments + 1 depth/stencil
using Attachments = StackVector<TextureRef, 5>;
class FrameBuffer;
using FrameBufferRef = std::shared_ptr<FrameBuffer>;
// FrameBuffer is a 2D Buffer that can be drawn to.
// It can hold up to 4 color Textures, and 1 Depth/Stencil Texture.
class FrameBuffer class FrameBuffer
{ {
protected: protected:
@ -65,7 +65,7 @@ namespace Blah
virtual int height() const = 0; virtual int height() const = 0;
// Clears the FrameBuffer // Clears the FrameBuffer
virtual void clear(Color color = Color::black, float depth = 1.0f, uint8_t stencil = 0, ClearMask mask = ClearMask::All) = 0; virtual void clear(Color color = Color::black, float depth = 1.0f, u8 stencil = 0, ClearMask mask = ClearMask::All) = 0;
}; };
} }

View File

@ -10,6 +10,7 @@ namespace Blah
class Material; class Material;
typedef std::shared_ptr<Material> MaterialRef; typedef std::shared_ptr<Material> MaterialRef;
// Materials hold values that can be assigned to a shader during rendering
class Material final class Material final
{ {
private: private:
@ -59,10 +60,10 @@ namespace Blah
// Sets the value. `length` is the total number of floats to set // Sets the value. `length` is the total number of floats to set
// For example if the uniform is a float2[4], a total of 8 float values // For example if the uniform is a float2[4], a total of 8 float values
// can be set. // can be set.
void set_value(const char* name, const float* value, int64_t length); void set_value(const char* name, const float* value, i64 length);
// Gets a pointer to the values of the given Uniform, or nullptr if it doesn't exist. // Gets a pointer to the values of the given Uniform, or nullptr if it doesn't exist.
const float* get_value(const char* name, int64_t* length = nullptr) const; const float* get_value(const char* name, i64* length = nullptr) const;
// Returns the internal Texture buffer // Returns the internal Texture buffer
const Vector<TextureRef>& textures() const; const Vector<TextureRef>& textures() const;

View File

@ -1,10 +1,11 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
#include <memory> #include <memory>
#include <blah/containers/stackvector.h> #include <blah/containers/stackvector.h>
namespace Blah namespace Blah
{ {
// Supported Vertex value types
enum class VertexType enum class VertexType
{ {
None, None,
@ -20,6 +21,7 @@ namespace Blah
UShort4 UShort4
}; };
// Vertex Attribute information
struct VertexAttribute struct VertexAttribute
{ {
// Location / Attribute Index // Location / Attribute Index
@ -32,6 +34,8 @@ namespace Blah
bool normalized = false; bool normalized = false;
}; };
// Vertex Format information.
// Holds a list of attributes and total stride per-vertex.
struct VertexFormat struct VertexFormat
{ {
// List of Attributes // List of Attributes
@ -44,15 +48,20 @@ namespace Blah
VertexFormat(std::initializer_list<VertexAttribute> attributes, int stride = 0); VertexFormat(std::initializer_list<VertexAttribute> attributes, int stride = 0);
}; };
// Supported Vertex Index formats
enum class IndexFormat enum class IndexFormat
{ {
// Indices are 16 bit unsigned integers
UInt16, UInt16,
// Indices are 32 bit unsigned integers
UInt32 UInt32
}; };
class Mesh; class Mesh;
typedef std::shared_ptr<Mesh> MeshRef; typedef std::shared_ptr<Mesh> MeshRef;
// A Mesh is a set of Indices and Vertices which are used for drawing
class Mesh class Mesh
{ {
protected: protected:
@ -73,21 +82,21 @@ namespace Blah
static MeshRef create(); static MeshRef create();
// Uploads the given index buffer to the Mesh // Uploads the given index buffer to the Mesh
virtual void index_data(IndexFormat format, const void* indices, int64_t count) = 0; virtual void index_data(IndexFormat format, const void* indices, i64 count) = 0;
// Uploads the given vertex buffer to the Mesh // Uploads the given vertex buffer to the Mesh
virtual void vertex_data(const VertexFormat& format, const void* vertices, int64_t count) = 0; virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) = 0;
// Uploads the given instance buffer to the Mesh // Uploads the given instance buffer to the Mesh
virtual void instance_data(const VertexFormat& format, const void* instances, int64_t count) = 0; virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) = 0;
// Gets the index count of the Mesh // Gets the index count of the Mesh
virtual int64_t index_count() const = 0; virtual i64 index_count() const = 0;
// Gets the vertex count of the Mesh // Gets the vertex count of the Mesh
virtual int64_t vertex_count() const = 0; virtual i64 vertex_count() const = 0;
// Gets the instance count of the Mesh // Gets the instance count of the Mesh
virtual int64_t instance_count() const = 0; virtual i64 instance_count() const = 0;
}; };
} }

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
#include <blah/math/rect.h> #include <blah/math/rect.h>
#include <blah/containers/str.h> #include <blah/containers/str.h>
#include <blah/graphics/texture.h> #include <blah/graphics/texture.h>
@ -11,6 +11,7 @@
namespace Blah namespace Blah
{ {
// Depth comparison function to use during a draw call
enum class Compare enum class Compare
{ {
None, None,
@ -24,13 +25,20 @@ namespace Blah
GreatorOrEqual GreatorOrEqual
}; };
// Cull mode during a draw call
enum class Cull enum class Cull
{ {
// No Culling enabled
None = 0, None = 0,
// Cull front faces
Front = 1, Front = 1,
// Cull back faces
Back = 2, Back = 2,
}; };
// A single draw call
struct RenderPass struct RenderPass
{ {
// Framebuffer to draw to // Framebuffer to draw to
@ -55,13 +63,13 @@ namespace Blah
Rect scissor; Rect scissor;
// First index in the Mesh to draw from // First index in the Mesh to draw from
int64_t index_start; i64 index_start;
// Total amount of indices to draw from the Mesh // Total amount of indices to draw from the Mesh
int64_t index_count; i64 index_count;
// Total amount of instances to draw from the Mesh // Total amount of instances to draw from the Mesh
int64_t instance_count; i64 instance_count;
// Depth Compare Function // Depth Compare Function
Compare depth; Compare depth;

View File

@ -2,24 +2,42 @@
namespace Blah namespace Blah
{ {
// Texture filter
enum class TextureFilter enum class TextureFilter
{ {
// None will fallback to whatever default the driver sets
None, None,
// Linear interpolation
Linear, Linear,
// Nearest Neighbour interpolation
Nearest Nearest
}; };
// Texture Wrap Mode
enum class TextureWrap enum class TextureWrap
{ {
// None will fallback to whatever default the driver sets
None, None,
// Clamps the texture to the edges
Clamp, Clamp,
// Repeats the texture
Repeat Repeat
}; };
// Texture Sampler State, applied during rendering
struct TextureSampler struct TextureSampler
{ {
// Filter Mode
TextureFilter filter; TextureFilter filter;
// Wrap X Mode
TextureWrap wrap_x; TextureWrap wrap_x;
// Wrap Y Mode
TextureWrap wrap_y; TextureWrap wrap_y;
TextureSampler() : TextureSampler() :

View File

@ -72,6 +72,7 @@ namespace Blah
class Shader; class Shader;
typedef std::shared_ptr<Shader> ShaderRef; typedef std::shared_ptr<Shader> ShaderRef;
// A shader used during Rendering
class Shader class Shader
{ {
protected: protected:

View File

@ -0,0 +1,132 @@
#pragma once
#include <blah/core/common.h>
#include <blah/containers/str.h>
#include <blah/containers/vector.h>
#include <blah/graphics/subtexture.h>
#include <blah/math/vec2.h>
#include <blah/core/filesystem.h>
#include <unordered_map>
namespace Blah
{
class Font;
// Sprite Font is a bitmap font implementation for font rendering.
// It can be constructed from a Font (ttf) and will automatically create
// texture atlases for rendering. You can add your own characters
// and textures to it.
class SpriteFont
{
public:
// Spritefont uses u32 codepoints
using Codepoint = u32;
// CharSet is a Vector of Character Ranges
struct CharRange;
using CharSet = Vector<CharRange>;
// Character range, used for building the Sprite Font
struct CharRange
{
Codepoint from;
Codepoint to;
CharRange();
CharRange(Codepoint single);
CharRange(Codepoint from, Codepoint to);
static const CharSet ASCII;
};
// Character Entry
struct Character
{
Subtexture subtexture;
float advance = 0;
Vec2 offset;
};
// SpriteFont name
String name;
// Height, in pixels
float size;
// Ascent, in pixels
float ascent;
// Descent, in pixels
float descent;
// Line Gap, in pixels
float line_gap;
SpriteFont();
SpriteFont(const FilePath& file, float size);
SpriteFont(const FilePath& file, float size, const CharSet& charset);
SpriteFont(const Font& font, float size);
SpriteFont(const Font& font, float size, const CharSet& charset);
SpriteFont(const SpriteFont&) = delete;
SpriteFont& operator=(const SpriteFont&) = delete;
SpriteFont(SpriteFont&& src) noexcept;
SpriteFont& operator=(SpriteFont&& src) noexcept;
~SpriteFont();
// releases all assets used by the spritefont
void dispose();
// gets the height of the sprite font
float height() const { return ascent - descent; }
// gets the line height of the sprite font (height + line gap)
float line_height() const { return ascent - descent + line_gap; }
// returns a list of all texture atlases
const Vector<TextureRef>& textures() { return m_atlas; }
// calculates the width of the given string
float width_of(const String& text) const;
// calculates the width of the next line
float width_of_line(const String& text, int start = 0) const;
// calculates the height of the given string
float height_of(const String& text) const;
// disposes the existing spritefont data and rebuilds from the given font file
void rebuild(const FilePath& file, float size, const CharSet& charset);
// disposes the existing spritefont data and rebuilds from the given font
void rebuild(const Font& font, float size, const CharSet& charset);
// gets the kerning between two characters
float get_kerning(Codepoint codepoint0, Codepoint codepoint1) const;
// sets the kerning between two characters
void set_kerning(Codepoint codepoint0, Codepoint codepoint1, float kerning);
// gets the character at the given codepoint
Character& get_character(Codepoint codepoint);
// gets the character at the given codepoint
const Character& get_character(Codepoint codepoint) const;
// gets the character at the given codepoint
Character& operator[](Codepoint codepoint);
// gets the character at the given codepoint
const Character& operator[](Codepoint codepoint) const;
private:
// character set
std::unordered_map<Codepoint, Character> m_characters;
// kerning
// key is 2 codepoints combined ((first << 32) | second)
std::unordered_map<u64, float> m_kerning;
// built texture
Vector<TextureRef> m_atlas;
};
}

View File

@ -5,11 +5,22 @@ namespace Blah
{ {
enum class TextureFormat enum class TextureFormat
{ {
// Invalid Format
None, None,
// Single 8-bit channe;
R, R,
// 2 8-bit channels
RG, RG,
// 4 8-bit channels
RGBA, RGBA,
// Depth 24, Stencil 8
DepthStencil, DepthStencil,
// Total Formats
Count Count
}; };
@ -18,6 +29,7 @@ namespace Blah
class Texture; class Texture;
typedef std::shared_ptr<Texture> TextureRef; typedef std::shared_ptr<Texture> TextureRef;
// A 2D Texture held by the GPU to be used during rendering
class Texture class Texture
{ {
protected: protected:

View File

@ -3,12 +3,13 @@
#include <blah/images/image.h> #include <blah/images/image.h>
#include <blah/containers/str.h> #include <blah/containers/str.h>
#include <blah/streams/stream.h> #include <blah/streams/stream.h>
#include <blah/core/filesystem.h>
namespace Blah namespace Blah
{ {
// A simple Aseprite file parser. // A simple Aseprite file parser.
// This implementation does not support Aseprite blendmodes, // This implementation does not support Aseprite blendmodes,
// besides the default blend mode. // aside from the default blend mode.
class Aseprite class Aseprite
{ {
public: public:
@ -130,7 +131,7 @@ namespace Blah
Vector<Color> palette; Vector<Color> palette;
Aseprite(); Aseprite();
Aseprite(const char* path); Aseprite(const FilePath& path);
Aseprite(Stream& stream); Aseprite(Stream& stream);
Aseprite(const Aseprite& src); Aseprite(const Aseprite& src);
Aseprite(Aseprite&& src) noexcept; Aseprite(Aseprite&& src) noexcept;

View File

@ -2,50 +2,102 @@
#include <blah/streams/stream.h> #include <blah/streams/stream.h>
#include <blah/images/image.h> #include <blah/images/image.h>
#include <blah/containers/str.h> #include <blah/containers/str.h>
#include <blah/core/filesystem.h>
namespace Blah namespace Blah
{ {
typedef uint32_t Codepoint; // Loads fonts from file and can blit individual characters to images
class Font class Font
{ {
public: public:
struct Char
// Font uses u32 Codepoints
using Codepoint = u32;
// Information provided for a single Character
struct Character
{ {
// character's glyph index
int glyph = 0; int glyph = 0;
// width of the character, in pixels
int width = 0; int width = 0;
// height of the character, in pixels
int height = 0; int height = 0;
// advance (how much to move horizontally)
float advance = 0; float advance = 0;
// render x-offset
float offset_x = 0; float offset_x = 0;
// render y-offset
float offset_y = 0; float offset_y = 0;
// scale the character was created at
float scale = 0; float scale = 0;
// whether the character has a visible glyph
bool has_glyph = false; bool has_glyph = false;
}; };
Font(); Font();
Font(Stream& stream); Font(Stream& stream);
Font(const char* path); Font(const FilePath& path);
Font(const Font&) = delete; Font(const Font&) = delete;
Font& operator=(const Font&) = delete; Font& operator=(const Font&) = delete;
Font(Font&& src) noexcept; Font(Font&& src) noexcept;
Font& operator=(Font&& src) noexcept; Font& operator=(Font&& src) noexcept;
~Font(); ~Font();
// Releases all Font resources
// Note that after doing this various properties may become invalid (ex. font name)
void dispose(); void dispose();
const char* family_name() const; // returns the font family name
const char* style_name() const; const String& family_name() const;
// returns the font style name
const String& style_name() const;
// the font ascent
int ascent() const; int ascent() const;
// the font descent
int descent() const; int descent() const;
// the font line gap (space between lines)
int line_gap() const; int line_gap() const;
// the height of the font
int height() const; int height() const;
// the height of the line, including line gap
int line_height() const; int line_height() const;
// gets the glyph index for the given codepoint
int get_glyph(Codepoint codepoint) const; int get_glyph(Codepoint codepoint) const;
// gets the font scale for the given font size in pixels
float get_scale(float size) const; float get_scale(float size) const;
// gets the font kerning between 2 glyphs
float get_kerning(int glyph1, int glyph2, float scale) const; float get_kerning(int glyph1, int glyph2, float scale) const;
Char get_character(int glyph, float scale) const;
bool get_image(const Char& ch, Color* pixels) const; // gets character data for the given glyph, at the provided scale
Character get_character(int glyph, float scale) const;
// Blits a character to the provided pixel array.
// The pixel array must be at least ch.width * ch.height in size!
// If the character doesn't exist, this will do nothing and return false.
bool get_image(const Character& ch, Color* pixels) const;
// Returns an image of the provided character.
// If the character doesn't exist, this will return an empty image.
Image get_image(const Character& ch) const;
// checks if the Font is valid
bool is_valid() const; bool is_valid() const;
private: private:

View File

@ -2,21 +2,31 @@
#include <blah/math/color.h> #include <blah/math/color.h>
#include <blah/math/rectI.h> #include <blah/math/rectI.h>
#include <blah/math/point.h> #include <blah/math/point.h>
#include <blah/core/filesystem.h>
namespace Blah namespace Blah
{ {
class Stream; class Stream;
// A 2D Bitmap stored on the CPU.
// For drawing images to the screen, use a Texture.
class Image class Image
{ {
public: public:
// width of the image, in pixels.
int width = 0; int width = 0;
// height of the image, in pixels.
int height = 0; int height = 0;
// pixel data of the image.
// this can be nullptr if the image is never assigned to anything.
Color* pixels = nullptr; Color* pixels = nullptr;
Image(); Image();
Image(Stream& stream); Image(Stream& stream);
Image(const char* file); Image(const FilePath& file);
Image(int width, int height); Image(int width, int height);
Image(const Image& src); Image(const Image& src);
Image& operator=(const Image& src); Image& operator=(const Image& src);
@ -24,19 +34,41 @@ namespace Blah
Image& operator=(Image&& src) noexcept; Image& operator=(Image&& src) noexcept;
~Image(); ~Image();
// disposes the existing image and recreates it from a stream
void from_stream(Stream& stream); void from_stream(Stream& stream);
// disposes the image and resets its values to defaults
void dispose(); void dispose();
// applies alpha premultiplication to the image data
void premultiply(); void premultiply();
// sets the pixels at the provided rectangle to the given data
// data must be at least rect.w * rect.h in size!
void set_pixels(const RectI& rect, Color* data); void set_pixels(const RectI& rect, Color* data);
bool save_png(const char* file) const;
// saves the image to a png file
bool save_png(const FilePath& file) const;
// saves the image to a png file
bool save_png(Stream& stream) const; bool save_png(Stream& stream) const;
bool save_jpg(const char* file, int quality) const;
// saves the image to a jpg file
bool save_jpg(const FilePath& file, int quality) const;
// saves the image to a jpg file
bool save_jpg(Stream& stream, int quality) const; bool save_jpg(Stream& stream, int quality) const;
void get_pixels(Color* dest, const Point& destPos, const Point& destSize, RectI sourceRect);
Image get_sub_image(const RectI& sourceRect); // gets the pixels from the given source rectangle
void get_pixels(Color* dest, const Point& dest_pos, const Point& dest_size, RectI source_rect);
// gets a sub image from this image
Image get_sub_image(const RectI& source_rect);
private: private:
// whether the stbi library owns the image data.
// we should let it free the data if it created it.
bool m_stbi_ownership; bool m_stbi_ownership;
}; };
} }

View File

@ -6,37 +6,67 @@
#include <blah/containers/str.h> #include <blah/containers/str.h>
#include <blah/containers/vector.h> #include <blah/containers/vector.h>
#include <blah/streams/bufferstream.h> #include <blah/streams/bufferstream.h>
#include <blah/core/filesystem.h>
namespace Blah namespace Blah
{ {
// Texture Packer, which takes source images and combines // Texture Packer, which takes source images and combines
// them into a single large texture. // them into a single large texture. Useful for 2D sprite batching.
class Packer class Packer
{ {
public: public:
// Packer Entry, which stores information about the resulting packed texture
class Entry class Entry
{ {
friend class Packer; friend class Packer;
private: private:
int64_t memory_index; i64 memory_index;
public: public:
uint64_t id;
// entry ID
u64 id;
// Texture Page that it was packed into.
// This won't be set until after the packer has run.
int page; int page;
// Whether the entry is empty
bool empty; bool empty;
// Packed frame rectangle.
// This won't be set until after the packer has run.
RectI frame; RectI frame;
// Packed position and size.
// This won't be set until after the packer has run.
RectI packed; RectI packed;
Entry(uint64_t id, const RectI& frame) Entry(u64 id, const RectI& frame)
: memory_index(0), id(id), page(0), empty(true), frame(frame), packed(0, 0, 0, 0) {} : memory_index(0)
, id(id)
, page(0)
, empty(true)
, frame(frame)
, packed(0, 0, 0, 0) {}
}; };
// maximum width / height of the generated texture
int max_size; int max_size;
// whether the generated texture must be a power of 2
bool power_of_two; bool power_of_two;
// spacing between each packed subtexture
int spacing; int spacing;
// padding on each subtexture (extrudes their borders outwards)
int padding; int padding;
// generated textures. There can be more than one if the packer was
// unable to fit all of the provided subtextures into the max_size.
Vector<Image> pages; Vector<Image> pages;
Vector<Entry> entries;
Packer(); Packer();
Packer(int max_size, int spacing, bool power_of_two); Packer(int max_size, int spacing, bool power_of_two);
@ -46,12 +76,25 @@ namespace Blah
Packer& operator=(Packer&& src) noexcept; Packer& operator=(Packer&& src) noexcept;
~Packer(); ~Packer();
void add(uint64_t id, int width, int height, const Color* pixels); // add a new entry
void add(uint64_t id, const Image& bitmap); void add(u64 id, int width, int height, const Color* pixels);
void add(uint64_t id, const String& path);
// add a new entry
void add(u64 id, const Image& bitmap);
// add a new entry
void add(u64 id, const FilePath& path);
// returns a vector of all the resulting entries
const Vector<Entry>& entries() const;
// perform the packing
void pack(); void pack();
// clear the current packer data
void clear(); void clear();
// dispose all resources used by the packer
void dispose(); void dispose();
private: private:
@ -67,9 +110,16 @@ namespace Blah
Node* Reset(const RectI& rect); Node* Reset(const RectI& rect);
}; };
// whether the packer has any changes that require it to run again
bool m_dirty; bool m_dirty;
// buffer of all the image data we will be packing
BufferStream m_buffer; BufferStream m_buffer;
void add_entry(uint64_t id, int w, int h, const Color* pixels); // Entries to pack & their resulting data
Vector<Entry> m_entries;
// adds a new entry
void add_entry(u64 id, int w, int h, const Color* pixels);
}; };
} }

View File

@ -0,0 +1,299 @@
#pragma once
#include <blah/input/input.h>
#include <blah/containers/stackvector.h>
#include <blah/math/point.h>
#include <blah/math/vec2.h>
namespace Blah
{
// Single input Binding
// You must call Binding::update() every frame to poll the input state.
// Alternatively, bindings can be registered to BindingRegistry which will
// automatically update them.
class Binding
{
public:
// Represents a Controller Trigger or a single direction of a Controller Axis.
struct TriggerBind
{
// Controller Index we're bound to
int controller = 0;
// The Axis we're bound to
Axis axis = Axis::None;
// Minimum value of the axis
float threshold = 0.01f;
// requires a positive value
// otherwise requires a negative value
bool positive = true;
TriggerBind() = default;
TriggerBind(Axis axis);
TriggerBind(int controller, Axis axis, float threshold, bool positive);
bool is_down(float axis_value) const;
};
// Represents a Controller Button.
struct ButtonBind
{
// Controller Index we're bound to
int controller = 0;
// Button we're bound to
Button button = Button::None;
ButtonBind() = default;
ButtonBind(Button button);
ButtonBind(int controller, Button button);
};
// Input Buffer for press events
float press_buffer = 0;
// Input Buffer for release events
float release_buffer = 0;
// List of bound Keys
StackVector<Key, 16> keys;
// List of bound Buttons
StackVector<ButtonBind, 16> buttons;
// List of bound Triggers / Axis
StackVector<TriggerBind, 16> triggers;
// List of bound Mouse buttons
StackVector<MouseButton, 16> mouse;
Binding() = default;
Binding(float press_buffer)
: press_buffer(press_buffer)
{
}
template<typename ... Args>
Binding(float press_buffer, const Args&... args)
: press_buffer(press_buffer)
{
add(args...);
}
// if the binding has been pressed
bool pressed() const;
// if the binding has been released
bool released() const;
// if the binding is currently held
bool down() const;
// returns the binding's value from 0-1
float value() const;
// returns the bindings signed value (0 or 1)
int sign() const;
// returns the timestamp of the last time the binding was pressed
double timestamp() const;
// updates the binding state
void update();
// consumes the current press, and pressed() will return false until the next press
void consume_press();
// consumes the current release, and released() will return false until the next release
void consume_release();
// adds a key to the binding
Binding& add(Key key);
// adds a button to the binding
Binding& add(ButtonBind button);
// adds an trigger to the binding
Binding& add(TriggerBind trigger);
// adds a mouse button to the binding
Binding& add(MouseButton mouse);
// adds an input to the binding
template<typename T, typename T2, typename ... Args>
Binding& add(T first, T2 second, const Args&... args)
{
add(first);
add(second, args...);
return *this;
}
// adds the left trigger to the binding
Binding& add_left_trigger(int controller, float threshold);
// adds the right trigger to the binding
Binding& add_right_trigger(int controller, float threshold);
// assigns all the bindings to the specific controller
Binding& set_controller(int index);
// removes all bindings
void clear();
private:
double m_last_timestamp = 0;
double m_last_press_time = -1;
double m_last_release_time = -1;
float m_value = 0.0f;
bool m_pressed = false;
bool m_released = false;
bool m_down = false;
bool m_press_consumed = false;
bool m_release_consumed = false;
bool get_pressed() const;
bool get_released() const;
bool get_down() const;
float get_value() const;
};
// Axis Binding (ex. Left/Right movement, or a Trigger)
// You must call AxisBinding::update() every frame to poll the input state.
// Alternatively, bindings can be registered to BindingRegistry which will
// automatically update them.
class AxisBinding
{
public:
enum class Overlap
{
Newer,
Older,
Cancel
};
// Negative Value Binding
Binding negative;
// Positive Value Binding
Binding positive;
// How to handle overlaps (ex. Left and Right are both held)
Overlap overlap = Overlap::Newer;
AxisBinding() = default;
AxisBinding(const Binding& negative, const Binding& positive, Overlap overlap = Overlap::Newer)
: negative(negative)
, positive(positive)
, overlap(overlap)
{}
// Current Value from -1 to 1
float value() const;
// Current value, either -1, 0, or 1
int sign() const;
// updates the Binding
void update();
// consumes the press buffer
void consume_press();
// consumes the release buffer
void consume_release();
// Adds a negative & positive binding pair
template<typename NegativeT, typename PositiveT>
AxisBinding& add(NegativeT negative, PositiveT positive)
{
this->negative.add(negative);
this->positive.add(positive);
return *this;
}
// Adds a Stick binding
AxisBinding& add_left_stick_x(int controller, float threshold);
AxisBinding& add_left_stick_y(int controller, float threshold);
AxisBinding& add_right_stick_x(int controller, float threshold);
AxisBinding& add_right_stick_y(int controller, float threshold);
// assigns all the bindings to the specific controller
AxisBinding& set_controller(int index);
// Clears all Bindings
void clear();
};
// Stick Binding (ex. Joystick, Dpad, Arrow Keys, WASD, etc)
// You must call StickBinding::update() every frame to poll the input state.
// Alternatively, bindings can be registered to BindingRegistry which will
// automatically update them.
class StickBinding
{
public:
// X Axis Binding
AxisBinding x;
// Y Axis Binding
AxisBinding y;
// An optional threshold for circular thresholds
float round_threshold = 0.0f;
StickBinding() = default;
StickBinding(const AxisBinding& x, const AxisBinding& y, float round_threshold = 0)
: x(x)
, y(y)
, round_threshold(round_threshold)
{}
// Current Value, -1 to 1
Vec2 value() const;
// Current value, either -1, 0, or 1
Point sign() const;
// Updates the Binding
void update();
// Consumes the Press Buffer
void consume_press();
// Consumes the Release Buffer
void consume_release();
// Adds directional bindings
template<typename LeftT, typename RightT, typename UpT, typename DownT>
StickBinding& add(LeftT left, RightT right, UpT up, DownT down)
{
x.negative.add(left);
x.positive.add(right);
y.negative.add(up);
y.positive.add(down);
return *this;
}
// Adds the dpad binding
StickBinding& add_dpad(int controller);
// Adds the left stick binding
StickBinding& add_left_stick(int controller, float threshold);
// Adds the right stick binding
StickBinding& add_right_stick(int controller, float threshold);
// assigns all the bindings to the specific controller
StickBinding& set_controller(int index);
// Clears all the bindings
void clear();
};
}

View File

@ -0,0 +1,35 @@
#pragma once
#include <blah/input/binding.h>
#include <blah/containers/vector.h>
#include <memory>
namespace Blah
{
using BindingRef = std::shared_ptr<Binding>;
using AxisBindingRef = std::shared_ptr<AxisBinding>;
using StickBindingRef = std::shared_ptr<StickBinding>;
// An Optional registry to automatically update Input Bindings.
// Once registered here, you do not need to explicitely call their update methods.
class BindingRegistry
{
public:
// registers a new binding
static BindingRef register_binding(const Binding& binding = Binding());
// registers a new axis binding
static AxisBindingRef register_axis(const AxisBinding& binding = AxisBinding());
// registers a new stick binding
static StickBindingRef register_stick(const StickBinding& binding = StickBinding());
// updates all the bindings. This is called
// automatically by the App loop.
static void update();
private:
static Vector<std::weak_ptr<Binding>> bindings;
static Vector<std::weak_ptr<AxisBinding>> axes;
static Vector<std::weak_ptr<StickBinding>> sticks;
};
}

View File

@ -1,11 +1,15 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
#include <blah/math/vec2.h> #include <blah/math/vec2.h>
#include <blah/containers/str.h>
// These are generally copied from the SDL2 Scancode Keys // These are generally copied from the SDL2 Scancode Keys,
// which are in turn based on the USB standards:
// https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
#define BLAH_KEY_DEFINITIONS \ #define BLAH_KEY_DEFINITIONS \
DEFINE_KEY(Unknown, 0) \ DEFINE_KEY(Unknown, 0) \
\
DEFINE_KEY(A, 4) \ DEFINE_KEY(A, 4) \
DEFINE_KEY(B, 5) \ DEFINE_KEY(B, 5) \
DEFINE_KEY(C, 6) \ DEFINE_KEY(C, 6) \
@ -32,6 +36,7 @@
DEFINE_KEY(X, 27) \ DEFINE_KEY(X, 27) \
DEFINE_KEY(Y, 28) \ DEFINE_KEY(Y, 28) \
DEFINE_KEY(Z, 29) \ DEFINE_KEY(Z, 29) \
\
DEFINE_KEY(D1, 30) \ DEFINE_KEY(D1, 30) \
DEFINE_KEY(D2, 31) \ DEFINE_KEY(D2, 31) \
DEFINE_KEY(D3, 32) \ DEFINE_KEY(D3, 32) \
@ -42,24 +47,27 @@
DEFINE_KEY(D8, 37) \ DEFINE_KEY(D8, 37) \
DEFINE_KEY(D9, 38) \ DEFINE_KEY(D9, 38) \
DEFINE_KEY(D0, 39) \ DEFINE_KEY(D0, 39) \
\
DEFINE_KEY(Enter, 40) \ DEFINE_KEY(Enter, 40) \
DEFINE_KEY(Escape, 41) \ DEFINE_KEY(Escape, 41) \
DEFINE_KEY(Backspace, 42) \ DEFINE_KEY(Backspace, 42) \
DEFINE_KEY(Tab, 43) \ DEFINE_KEY(Tab, 43) \
DEFINE_KEY(Space, 44) \ DEFINE_KEY(Space, 44) \
\
DEFINE_KEY(Minus, 45) \ DEFINE_KEY(Minus, 45) \
DEFINE_KEY(Equals, 46) \ DEFINE_KEY(Equals, 46) \
DEFINE_KEY(LeftBracket, 47) \ DEFINE_KEY(LeftBracket, 47) \
DEFINE_KEY(RightBracket, 48) \ DEFINE_KEY(RightBracket, 48) \
DEFINE_KEY(BackSlash, 49) \ DEFINE_KEY(Backslash, 49) \
DEFINE_KEY(NonUSHash, 50) \
DEFINE_KEY(Semicolon, 51) \ DEFINE_KEY(Semicolon, 51) \
DEFINE_KEY(Apostrophe, 52) \ DEFINE_KEY(Apostrophe, 52) \
DEFINE_KEY(Grave, 53) \ DEFINE_KEY(Tilde, 53) \
DEFINE_KEY(Comma, 54) \ DEFINE_KEY(Comma, 54) \
DEFINE_KEY(Period, 55) \ DEFINE_KEY(Period, 55) \
DEFINE_KEY(Slash, 56) \ DEFINE_KEY(Slash, 56) \
\
DEFINE_KEY(Capslock, 57) \ DEFINE_KEY(Capslock, 57) \
\
DEFINE_KEY(F1, 58) \ DEFINE_KEY(F1, 58) \
DEFINE_KEY(F2, 59) \ DEFINE_KEY(F2, 59) \
DEFINE_KEY(F3, 60) \ DEFINE_KEY(F3, 60) \
@ -72,6 +80,19 @@
DEFINE_KEY(F10, 67) \ DEFINE_KEY(F10, 67) \
DEFINE_KEY(F11, 68) \ DEFINE_KEY(F11, 68) \
DEFINE_KEY(F12, 69) \ DEFINE_KEY(F12, 69) \
DEFINE_KEY(F13, 104) \
DEFINE_KEY(F14, 105) \
DEFINE_KEY(F15, 106) \
DEFINE_KEY(F16, 107) \
DEFINE_KEY(F17, 108) \
DEFINE_KEY(F18, 109) \
DEFINE_KEY(F19, 110) \
DEFINE_KEY(F20, 111) \
DEFINE_KEY(F21, 112) \
DEFINE_KEY(F22, 113) \
DEFINE_KEY(F23, 114) \
DEFINE_KEY(F24, 115) \
\
DEFINE_KEY(PrintScreen, 70) \ DEFINE_KEY(PrintScreen, 70) \
DEFINE_KEY(ScrollLock, 71) \ DEFINE_KEY(ScrollLock, 71) \
DEFINE_KEY(Pause, 72) \ DEFINE_KEY(Pause, 72) \
@ -85,45 +106,17 @@
DEFINE_KEY(Left, 80) \ DEFINE_KEY(Left, 80) \
DEFINE_KEY(Down, 81) \ DEFINE_KEY(Down, 81) \
DEFINE_KEY(Up, 82) \ DEFINE_KEY(Up, 82) \
DEFINE_KEY(NumlockClear, 83) \ \
DEFINE_KEY(KP_Divide, 84) \ DEFINE_KEY(Numlock, 83) \
DEFINE_KEY(KP_Multiply, 85) \ \
DEFINE_KEY(KP_Minus, 86) \
DEFINE_KEY(KP_Plus, 87) \
DEFINE_KEY(KP_Enter, 88) \
DEFINE_KEY(KP_1, 89) \
DEFINE_KEY(KP_2, 90) \
DEFINE_KEY(KP_3, 91) \
DEFINE_KEY(KP_4, 92) \
DEFINE_KEY(KP_5, 93) \
DEFINE_KEY(KP_6, 94) \
DEFINE_KEY(KP_7, 95) \
DEFINE_KEY(KP_8, 96) \
DEFINE_KEY(KP_9, 97) \
DEFINE_KEY(KP_0, 98) \
DEFINE_KEY(KP_Period, 99) \
DEFINE_KEY(NonUSBackSlash, 100) \
DEFINE_KEY(Application, 101) \ DEFINE_KEY(Application, 101) \
DEFINE_KEY(Power, 102) \ \
DEFINE_KEY(KP_Equals, 103) \
DEFINE_KEY(F13, 104) \
DEFINE_KEY(F14, 105) \
DEFINE_KEY(F15, 106) \
DEFINE_KEY(F16, 107) \
DEFINE_KEY(F17, 108) \
DEFINE_KEY(F18, 109) \
DEFINE_KEY(F19, 110) \
DEFINE_KEY(F20, 111) \
DEFINE_KEY(F21, 112) \
DEFINE_KEY(F22, 113) \
DEFINE_KEY(F23, 114) \
DEFINE_KEY(F24, 115) \
DEFINE_KEY(Execute, 116) \ DEFINE_KEY(Execute, 116) \
DEFINE_KEY(Help, 117) \ DEFINE_KEY(Help, 117) \
DEFINE_KEY(Menu, 118) \ DEFINE_KEY(Menu, 118) \
DEFINE_KEY(Select, 119) \ DEFINE_KEY(Select, 119) \
DEFINE_KEY(Stop, 120) \ DEFINE_KEY(Stop, 120) \
DEFINE_KEY(Again, 121) \ DEFINE_KEY(Redo, 121) \
DEFINE_KEY(Undo, 122) \ DEFINE_KEY(Undo, 122) \
DEFINE_KEY(Cut, 123) \ DEFINE_KEY(Cut, 123) \
DEFINE_KEY(Copy, 124) \ DEFINE_KEY(Copy, 124) \
@ -132,92 +125,69 @@
DEFINE_KEY(Mute, 127) \ DEFINE_KEY(Mute, 127) \
DEFINE_KEY(VolumeUp, 128) \ DEFINE_KEY(VolumeUp, 128) \
DEFINE_KEY(VolumeDown, 129) \ DEFINE_KEY(VolumeDown, 129) \
DEFINE_KEY(KP_Comma, 133) \ \
DEFINE_KEY(KP_EqualsAs400, 134) \
DEFINE_KEY(International1, 135) \
DEFINE_KEY(International2, 136) \
DEFINE_KEY(International3, 137) \
DEFINE_KEY(International4, 138) \
DEFINE_KEY(International5, 139) \
DEFINE_KEY(International6, 140) \
DEFINE_KEY(International7, 141) \
DEFINE_KEY(International8, 142) \
DEFINE_KEY(International9, 143) \
DEFINE_KEY(Language1, 144) \
DEFINE_KEY(Language2, 145) \
DEFINE_KEY(Language3, 146) \
DEFINE_KEY(Language4, 147) \
DEFINE_KEY(Language5, 148) \
DEFINE_KEY(Language6, 149) \
DEFINE_KEY(Language7, 150) \
DEFINE_KEY(Language8, 151) \
DEFINE_KEY(Language9, 152) \
DEFINE_KEY(AltErase, 153) \ DEFINE_KEY(AltErase, 153) \
DEFINE_KEY(SysReq, 154) \ DEFINE_KEY(SysReq, 154) \
DEFINE_KEY(Cancel, 155) \ DEFINE_KEY(Cancel, 155) \
DEFINE_KEY(clear, 156) \ DEFINE_KEY(Clear, 156) \
DEFINE_KEY(Prior, 157) \ DEFINE_KEY(Prior, 157) \
DEFINE_KEY(Return2, 158) \ DEFINE_KEY(Enter2, 158) \
DEFINE_KEY(Separator, 159) \ DEFINE_KEY(Separator, 159) \
DEFINE_KEY(Out, 160) \ DEFINE_KEY(Out, 160) \
DEFINE_KEY(Oper, 161) \ DEFINE_KEY(Oper, 161) \
DEFINE_KEY(ClearAgain, 162) \ DEFINE_KEY(ClearAgain, 162) \
DEFINE_KEY(CRSEL, 163) \ \
DEFINE_KEY(EXSEL, 164) \ DEFINE_KEY(KeypadA, 188) \
DEFINE_KEY(KP_00, 176) \ DEFINE_KEY(KeypadB, 189) \
DEFINE_KEY(KP_000, 177) \ DEFINE_KEY(KeypadC, 190) \
DEFINE_KEY(ThousandsSeparator, 178) \ DEFINE_KEY(KeypadD, 191) \
DEFINE_KEY(DecimalSeparator, 179) \ DEFINE_KEY(KeypadE, 192) \
DEFINE_KEY(CurrencyUnit, 180) \ DEFINE_KEY(KeypadF, 193) \
DEFINE_KEY(CurrencySubUnit, 181) \ DEFINE_KEY(Keypad0, 98) \
DEFINE_KEY(KP_LeftParen, 182) \ DEFINE_KEY(Keypad00, 176) \
DEFINE_KEY(KP_RightParent, 183) \ DEFINE_KEY(Keypad000, 177) \
DEFINE_KEY(KP_LeftBrace, 184) \ DEFINE_KEY(Keypad1, 89) \
DEFINE_KEY(KP_RightBrace, 185) \ DEFINE_KEY(Keypad2, 90) \
DEFINE_KEY(KP_Tab, 186) \ DEFINE_KEY(Keypad3, 91) \
DEFINE_KEY(KP_BackSpace, 187) \ DEFINE_KEY(Keypad4, 92) \
DEFINE_KEY(KP_A, 188) \ DEFINE_KEY(Keypad5, 93) \
DEFINE_KEY(KP_B, 189) \ DEFINE_KEY(Keypad6, 94) \
DEFINE_KEY(KP_C, 190) \ DEFINE_KEY(Keypad7, 95) \
DEFINE_KEY(KP_D, 191) \ DEFINE_KEY(Keypad8, 96) \
DEFINE_KEY(KP_E, 192) \ DEFINE_KEY(Keypad9, 97) \
DEFINE_KEY(KP_F, 193) \ DEFINE_KEY(KeypadDivide, 84) \
DEFINE_KEY(KP_XOR, 194) \ DEFINE_KEY(KeypadMultiply, 85) \
DEFINE_KEY(KP_Power, 195) \ DEFINE_KEY(KeypadMinus, 86) \
DEFINE_KEY(KP_Percent, 196) \ DEFINE_KEY(KeypadPlus, 87) \
DEFINE_KEY(KP_Less, 197) \ DEFINE_KEY(KeypadEnter, 88) \
DEFINE_KEY(KP_Greater, 198) \ DEFINE_KEY(KeypadPeroid, 99) \
DEFINE_KEY(KP_Ampersand, 199) \ DEFINE_KEY(KeypadEquals, 103) \
DEFINE_KEY(KP_DoubleAmpersand, 200) \ DEFINE_KEY(KeypadComma, 133) \
DEFINE_KEY(KP_VerticalBar, 201) \ DEFINE_KEY(KeypadLeftParen, 182) \
DEFINE_KEY(KP_DoubleVerticalBar, 202) \ DEFINE_KEY(KeypadRightParen, 183) \
DEFINE_KEY(KP_Colon, 203) \ DEFINE_KEY(KeypadLeftBrace, 184) \
DEFINE_KEY(KP_Hash, 204) \ DEFINE_KEY(KeypadRightBrace, 185) \
DEFINE_KEY(KP_Space, 205) \ DEFINE_KEY(KeypadTab, 186) \
DEFINE_KEY(KP_At, 206) \ DEFINE_KEY(KeypadBackspace, 187) \
DEFINE_KEY(KP_EXCLAM, 207) \ DEFINE_KEY(KeypadXor, 194) \
DEFINE_KEY(KP_MemStore, 208) \ DEFINE_KEY(KeypadPower, 195) \
DEFINE_KEY(KP_MemRecall, 209) \ DEFINE_KEY(KeypadPercent, 196) \
DEFINE_KEY(KP_MemClear, 210) \ DEFINE_KEY(KeypadLess, 197) \
DEFINE_KEY(KP_MemAdd, 211) \ DEFINE_KEY(KeypadGreater, 198) \
DEFINE_KEY(KP_MemSubstract, 212) \ DEFINE_KEY(KeypadAmpersand, 199) \
DEFINE_KEY(KP_MemMultiply, 213) \ DEFINE_KEY(KeypadColon, 203) \
DEFINE_KEY(KP_MemDivide, 214) \ DEFINE_KEY(KeypadHash, 204) \
DEFINE_KEY(KP_PlusMinus, 215) \ DEFINE_KEY(KeypadSpace, 205) \
DEFINE_KEY(KP_Clear, 216) \ DEFINE_KEY(KeypadClear, 216) \
DEFINE_KEY(KP_ClearEntry, 217) \ \
DEFINE_KEY(KP_Binary, 218) \
DEFINE_KEY(KP_Octal, 219) \
DEFINE_KEY(KP_Decimal, 220) \
DEFINE_KEY(KP_Hexadecimal, 221) \
DEFINE_KEY(LeftControl, 224) \ DEFINE_KEY(LeftControl, 224) \
DEFINE_KEY(LeftShift, 225) \ DEFINE_KEY(LeftShift, 225) \
DEFINE_KEY(LeftAlt, 226) \ DEFINE_KEY(LeftAlt, 226) \
DEFINE_KEY(LeftGui, 227) \ DEFINE_KEY(LeftOS, 227) \
DEFINE_KEY(RightControl, 228) \ DEFINE_KEY(RightControl, 228) \
DEFINE_KEY(RightShift, 229) \ DEFINE_KEY(RightShift, 229) \
DEFINE_KEY(RightAlt, 230) \ DEFINE_KEY(RightAlt, 230) \
DEFINE_KEY(RightGui, 231) DEFINE_KEY(RightOS, 231)
#define BLAH_BUTTON_DEFINITIONS \ #define BLAH_BUTTON_DEFINITIONS \
DEFINE_BTN(None, -1) \ DEFINE_BTN(None, -1) \
@ -255,12 +225,6 @@ namespace Blah
// maximum number of keys the input will track // maximum number of keys the input will track
constexpr int max_keyboard_keys = 512; constexpr int max_keyboard_keys = 512;
// maximum length of text input that can be received per-frame
constexpr int max_text_input = 256;
// maximum number of nodes within a virtual input device
constexpr int max_virtual_nodes = 32;
} }
struct ControllerState struct ControllerState
@ -293,19 +257,19 @@ namespace Blah
float axis[Input::max_controller_axis]; float axis[Input::max_controller_axis];
// Timestamp, in milliseconds, since each button was last pressed // Timestamp, in milliseconds, since each button was last pressed
uint64_t button_timestamp[Input::max_controller_buttons]; u64 button_timestamp[Input::max_controller_buttons];
// Timestamp, in milliseconds, since each axis last had a value set // Timestamp, in milliseconds, since each axis last had a value set
uint64_t axis_timestamp[Input::max_controller_axis]; u64 axis_timestamp[Input::max_controller_axis];
// The USB Vendor ID // The USB Vendor ID
uint16_t vendor; u16 vendor;
// The USB Product ID // The USB Product ID
uint16_t product; u16 product;
// the Product Version // the Product Version
uint16_t version; u16 version;
}; };
struct KeyboardState struct KeyboardState
@ -313,8 +277,8 @@ namespace Blah
bool pressed[Input::max_keyboard_keys]; bool pressed[Input::max_keyboard_keys];
bool down[Input::max_keyboard_keys]; bool down[Input::max_keyboard_keys];
bool released[Input::max_keyboard_keys]; bool released[Input::max_keyboard_keys];
uint64_t timestamp[Input::max_keyboard_keys]; u64 timestamp[Input::max_keyboard_keys];
char text[Input::max_text_input]; String text;
}; };
struct MouseState struct MouseState
@ -322,7 +286,7 @@ namespace Blah
bool pressed[Input::max_mouse_buttons]; bool pressed[Input::max_mouse_buttons];
bool down[Input::max_mouse_buttons]; bool down[Input::max_mouse_buttons];
bool released[Input::max_mouse_buttons]; bool released[Input::max_mouse_buttons];
uint64_t timestamp[Input::max_mouse_buttons]; u64 timestamp[Input::max_mouse_buttons];
Vec2 screen_position; Vec2 screen_position;
Vec2 draw_position; Vec2 draw_position;
Vec2 position; Vec2 position;

View File

@ -1,86 +0,0 @@
#pragma once
#include <blah/input/input.h>
namespace Blah
{
// A virtual controller axis, which can be used to map multiple
// inputs to an axis. Note that you must call `update` every frame!
class VirtualAxis
{
private:
struct KeysNode
{
Key positive = Key::Unknown;
Key negative = Key::Unknown;
int value = 0;
void init(Key negative, Key positive);
void update();
};
struct ButtonsNode
{
int gamepad_id = 0;
Button positive = Button::None;
Button negative = Button::None;
int value = 0;
void init(int gamepad_id, Button negative, Button positive);
void update();
};
struct AxisNode
{
int gamepad_id = 0;
Axis axis = Axis::None;
float deadzone = 0;
float value = 0;
void init(int gamepad_id, Axis axis, float deadzone);
void update();
};
KeysNode m_keys[Input::max_virtual_nodes];
ButtonsNode m_buttons[Input::max_virtual_nodes];
AxisNode m_axes[Input::max_virtual_nodes];
int m_keys_len = 0;
int m_buttons_len = 0;
int m_axes_len = 0;
float m_press_buffer = 0;
float m_release_buffer = 0;
float m_repeat_delay = 0;
float m_repeat_interval = 0;
float m_value = 0;
int m_value_i = 0;
float m_last_value = 0;
int m_last_value_i = 0;
bool m_pressed = false;
bool m_released = false;
double m_last_press_time = -1;
double m_last_release_time = -1;
double m_repeat_press_time = -1;
public:
VirtualAxis& add_keys(Key negative, Key positive);
VirtualAxis& add_buttons(int gamepad_id, Button negative, Button positive);
VirtualAxis& add_axis(int gamepad_id, Axis axis, float deadzone);
VirtualAxis& repeat(float m_repeat_delay, float m_repeat_interval);
VirtualAxis& press_buffer(float duration);
VirtualAxis& release_buffer(float duration);
void update();
float value() const { return m_value; }
int value_i() const { return m_value_i; }
float last_value() const { return m_last_value; }
int last_value_i() const { return m_last_value_i; }
bool pressed() const { return m_pressed; }
bool released() const { return m_released; }
void clear_press_buffer() { m_last_press_time = -1; m_pressed = false; }
void clear_release_buffer() { m_last_release_time = -1; m_released = false; }
};
}

View File

@ -1,83 +0,0 @@
#pragma once
#include <blah/input/input.h>
namespace Blah
{
class VirtualButton
{
private:
struct KeyNode
{
Key key = Key::Unknown;
bool down = false;
bool pressed = false;
bool released = false;
void init(Key key);
void update();
};
struct ButtonNode
{
int gamepad_id = 0;
Button button = Button::None;
bool down = false;
bool pressed = false;
bool released = false;
void init(int gamepad_id, Button button);
void update();
};
struct AxisNode
{
int gamepad_id = 0;
Axis axis = Axis::None;
float threshold = 0;
bool greater_than = false;
bool down = false;
bool pressed = false;
bool released = false;
void init(int gamepad_id, Axis axis, float threshold, bool greater_than);
void update();
};
KeyNode m_keys[Input::max_virtual_nodes];
ButtonNode m_buttons[Input::max_virtual_nodes];
AxisNode m_axes[Input::max_virtual_nodes];
int m_keys_len = 0;
int m_buttons_len = 0;
int m_axes_len = 0;
float m_press_buffer = 0;
float m_release_buffer = 0;
float m_repeat_delay = 0;
float m_repeat_interval = 0;
bool m_down = false;
bool m_pressed = false;
bool m_released = false;
double m_last_press_time = -1;
double m_last_release_time = -1;
double m_repeat_press_time = -1;
public:
VirtualButton& add_key(Key key);
VirtualButton& add_button(int gamepad_id, Button button);
VirtualButton& add_axis(int gamepad_id, Axis axis, float threshold, bool greater_than);
VirtualButton& repeat(float m_repeat_delay, float m_repeat_interval);
VirtualButton& press_buffer(float duration);
VirtualButton& release_buffer(float duration);
void update();
bool down() const { return m_down; }
bool pressed() const { return m_pressed; }
bool released() const { return m_released; }
void clear_press_buffer() { m_last_press_time = -1; m_pressed = false; }
void clear_release_buffer() { m_last_release_time = -1; m_released = false; }
};
}

View File

@ -1,97 +0,0 @@
#pragma once
#include <blah/input/input.h>
#include <blah/math/vec2.h>
#include <blah/math/point.h>
namespace Blah
{
// A virtual controller stick, which can be used to map multiple
// inputs to a stick. Note that you must call `update` every frame!
class VirtualStick
{
private:
struct KeysNode
{
Key left;
Key right;
Key up;
Key down;
Point value;
void init(Key left, Key right, Key up, Key down);
void update();
};
struct ButtonsNode
{
int gamepad_id;
Button left;
Button right;
Button up;
Button down;
Point value;
void init(int gamepad_id, Button left, Button right, Button up, Button down);
void update();
};
struct AxesNode
{
int gamepad_id;
Axis horizontal;
Axis vertical;
float deadzone;
Vec2 value;
void init(int gamepad_id, Axis horizontal, Axis vertical, float deadzone);
void update();
};
KeysNode m_keys[Input::max_virtual_nodes];
ButtonsNode m_buttons[Input::max_virtual_nodes];
AxesNode m_axes[Input::max_virtual_nodes];
int m_keys_len = 0;
int m_buttons_len = 0;
int m_axes_len = 0;
float m_press_buffer = 0;
float m_release_buffer = 0;
float m_repeat_delay = 0;
float m_repeat_interval = 0;
Vec2 m_value = Vec2();
Point m_value_i = Point();
Vec2 m_last_value = Vec2();
Point m_last_value_i = Point();
bool m_pressed = false;
bool m_released = false;
float m_i_deadzone;
double m_last_press_time = -1;
double m_last_release_time = -1;
double m_repeat_press_time = -1;
public:
VirtualStick();
VirtualStick(float iDeadzone);
VirtualStick& add_keys(Key left, Key right, Key up, Key down);
VirtualStick& add_buttons(int gamepad_id, Button left, Button right, Button up, Button down);
VirtualStick& add_axes(int gamepad_id, Axis horizontal, Axis vertical, float deadzone);
VirtualStick& repeat(float m_repeat_delay, float m_repeat_interval);
VirtualStick& press_buffer(float duration);
VirtualStick& release_buffer(float duration);
void update();
const Vec2& value() const { return m_value; }
const Point& value_i() const { return m_value_i; }
const Vec2& last_value() const { return m_last_value; }
const Point& last_value_i() const { return m_last_value_i; }
bool pressed() const { return m_pressed; }
bool released() const { return m_released; }
void clear_press_buffer() { m_last_press_time = 0; }
void clear_release_buffer() { m_last_release_time = 0; }
};
}

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
#include <blah/math/vec2.h> #include <blah/math/vec2.h>
namespace Blah namespace Blah
@ -27,10 +27,6 @@ namespace Blah
Vec2 approach(const Vec2& t, const Vec2& target, float delta); Vec2 approach(const Vec2& t, const Vec2& target, float delta);
float clamp(float t, float min, float max);
int clamp_int(int t, int min, int max);
float map(float t, float old_min, float old_max, float new_min, float new_max); float map(float t, float old_min, float old_max, float new_min, float new_max);
float clamped_map(float t, float old_min, float old_max, float new_min, float new_max); float clamped_map(float t, float old_min, float old_max, float new_min, float new_max);
@ -43,6 +39,9 @@ namespace Blah
float abs(float x); float abs(float x);
template<class T>
T clamp(T value, T min, T max) { return value < min ? min : (value > max ? max : value); }
template<class T, class U> template<class T, class U>
T min(T a, U b) { return (T)(a < b ? a : b); } T min(T a, U b) { return (T)(a < b ? a : b); }

View File

@ -1,38 +1,41 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
#include <blah/containers/str.h> #include <blah/containers/str.h>
namespace Blah namespace Blah
{ {
struct Vec3;
struct Vec4; struct Vec4;
struct Color struct Color
{ {
uint8_t r; u8 r;
uint8_t g; u8 g;
uint8_t b; u8 b;
uint8_t a; u8 a;
Color(); Color();
Color(int rgb); Color(int rgb);
Color(int rgb, float alpha); Color(int rgb, float alpha);
Color(uint8_t r, uint8_t g, uint8_t b); Color(u8 r, u8 g, u8 b);
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a); Color(u8 r, u8 g, u8 b, u8 a);
Color(const Vec3& vec3);
Color(const Vec3& vec3, float alpha);
Color(const Vec4& vec4); Color(const Vec4& vec4);
// Parses a Hex string in the format of "#00000000" or "0x00000000" or "00000000" // Parses a Hex string in the format of "#00000000" or "0x00000000" or "00000000"
Color(const char* hexCstr); Color(const String& hex_string);
// Premultiplies the Color // Premultiplies the Color
void premultiply(); void premultiply();
// Returns an RGBA hex string of the color
String to_hex_rgba() const;
// Sets a Hex string to the given buffer, in the format of RRGGBBAA // Sets a Hex string to the given buffer, in the format of RRGGBBAA
// The buffer must be at least 8 bytes long // The buffer must be at least 8 bytes long
void to_hex_rgba(char* buffer) const; void to_hex_rgba(char* buffer) const;
// Returns an RGBA hex string of the color
String to_hex_rgba() const;
// Sets a Hex string to the given buffer, in the format of RRGGBB // Sets a Hex string to the given buffer, in the format of RRGGBB
// The buffer must be at least 6 bytes long // The buffer must be at least 6 bytes long
void to_hex_rgb(char* buffer) const; void to_hex_rgb(char* buffer) const;
@ -40,17 +43,17 @@ namespace Blah
// Returns an RGB hex string of the color // Returns an RGB hex string of the color
String to_hex_rgb() const; String to_hex_rgb() const;
// Convers the Color to a uint32_t // Convers the Color to a u32
uint32_t to_rgba() const; u32 to_rgba() const;
// Converts the Color to a Vec4 // Converts the Color to a Vec4
Vec4 to_vec4() const; Vec4 to_vec4() const;
// Returns a RGBA Color representation of the integer value // Returns a RGBA Color representation of the integer value
static Color from_rgba(uint32_t value); static Color from_rgba(u32 value);
// Returns a RGB Color representation of the integer value, with Alpha = 255 // Returns a RGB Color representation of the integer value, with Alpha = 255
static Color from_rgb(uint32_t value); static Color from_rgb(u32 value);
// Lerps between two colors // Lerps between two colors
static Color lerp(Color a, Color b, float amount); static Color lerp(Color a, Color b, float amount);

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <blah/math/calc.h> #include <blah/math/calc.h>
#include <blah/core/log.h> #include <blah/core/common.h>
namespace Blah namespace Blah
{ {
@ -8,6 +8,7 @@ namespace Blah
enum class Easers enum class Easers
{ {
Linear,
QuadIn, QuadOut, QuadInOut, QuadIn, QuadOut, QuadInOut,
CubeIn, CubeOut, CubeInOut, CubeIn, CubeOut, CubeInOut,
QuartIn, QuartOut, QuartInOut, QuartIn, QuartOut, QuartInOut,
@ -28,43 +29,41 @@ namespace Blah
For previews go here: https://easings.net/ For previews go here: https://easings.net/
*/ */
inline float linear(float t) constexpr float linear(float t)
{ {
return t; return t;
} }
inline float quad_in(float t) constexpr float quad_in(float t)
{ {
return t * t; return t * t;
} }
inline float quad_out(float t) constexpr float quad_out(float t)
{ {
return -(t * (t - 2)); return -(t * (t - 2));
} }
inline float quad_in_out(float t) constexpr float quad_in_out(float t)
{ {
if (t < 0.5f) if (t < 0.5f)
return 2 * t * t; return 2 * t * t;
else else
{
return (-2 * t * t) + (4 * t) - 1; return (-2 * t * t) + (4 * t) - 1;
} }
}
inline float cube_in(float t) constexpr float cube_in(float t)
{ {
return t * t * t; return t * t * t;
} }
inline float cube_out(float t) constexpr float cube_out(float t)
{ {
float f = (t - 1); float f = (t - 1);
return f * f * f + 1; return f * f * f + 1;
} }
inline float cube_in_out(float t) constexpr float cube_in_out(float t)
{ {
if (t < 0.5f) if (t < 0.5f)
return 4 * t * t * t; return 4 * t * t * t;
@ -75,18 +74,18 @@ namespace Blah
} }
} }
inline float quart_in(float t) constexpr float quart_in(float t)
{ {
return t * t * t * t; return t * t * t * t;
} }
inline float quart_out(float t) constexpr float quart_out(float t)
{ {
float f = (t - 1); float f = (t - 1);
return f * f * f * (1 - t) + 1; return f * f * f * (1 - t) + 1;
} }
inline float quart_in_out(float t) constexpr float quart_in_out(float t)
{ {
if (t < 0.5f) if (t < 0.5f)
return 8 * t * t * t * t; return 8 * t * t * t * t;
@ -97,18 +96,18 @@ namespace Blah
} }
} }
inline float quint_in(float t) constexpr float quint_in(float t)
{ {
return t * t * t * t * t; return t * t * t * t * t;
} }
inline float quint_out(float t) constexpr float quint_out(float t)
{ {
float f = (t - 1); float f = (t - 1);
return f * f * f * f * f + 1; return f * f * f * f * f + 1;
} }
inline float quint_in_out(float t) constexpr float quint_in_out(float t)
{ {
if (t < 0.5f) if (t < 0.5f)
return 16 * t * t * t * t * t; return 16 * t * t * t * t * t;
@ -216,7 +215,7 @@ namespace Blah
} }
} }
inline float bounce_out(float t) constexpr float bounce_out(float t)
{ {
if (t < 4 / 11.0f) if (t < 4 / 11.0f)
return (121 * t * t) / 16.0f; return (121 * t * t) / 16.0f;
@ -228,12 +227,12 @@ namespace Blah
return (54 / 5.0f * t * t) - (513 / 25.0f * t) + 268 / 25.0f; return (54 / 5.0f * t * t) - (513 / 25.0f * t) + 268 / 25.0f;
} }
inline float bounce_in(float t) constexpr float bounce_in(float t)
{ {
return 1 - bounce_out(1 - t); return 1 - bounce_out(1 - t);
} }
inline float bounce_in_out(float t) constexpr float bounce_in_out(float t)
{ {
if (t < 0.5f) if (t < 0.5f)
return 0.5f * bounce_in(t * 2); return 0.5f * bounce_in(t * 2);
@ -245,6 +244,8 @@ namespace Blah
{ {
switch (e) switch (e)
{ {
case Easers::Linear: return &linear;
case Easers::CubeIn: return &cube_in; case Easers::CubeIn: return &cube_in;
case Easers::CubeOut: return &cube_out; case Easers::CubeOut: return &cube_out;
case Easers::CubeInOut: return &cube_in_out; case Easers::CubeInOut: return &cube_in_out;
@ -296,6 +297,7 @@ namespace Blah
{ {
switch (e) switch (e)
{ {
case Easers::Linear: return "Linear";
case Easers::CubeIn: return "CubeIn"; case Easers::CubeIn: return "CubeIn";
case Easers::CubeOut: return "CubeOut"; case Easers::CubeOut: return "CubeOut";
case Easers::CubeInOut: return "CubeInOut"; case Easers::CubeInOut: return "CubeInOut";

View File

@ -10,7 +10,7 @@ namespace Blah
Vec2 a; Vec2 a;
Vec2 b; Vec2 b;
Line() {} Line() = default;
Line(float x0, float y0, float x1, float y1); Line(float x0, float y0, float x1, float y1);
Line(const Vec2& start, const Vec2& end); Line(const Vec2& start, const Vec2& end);

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
namespace Blah namespace Blah
{ {
@ -8,9 +8,9 @@ namespace Blah
public: public:
Stopwatch(); Stopwatch();
void reset(); void reset();
uint64_t microseconds(); u64 microseconds();
uint64_t milliseconds(); u64 milliseconds();
private: private:
uint64_t start_time; u64 start_time;
}; };
} }

View File

@ -12,9 +12,9 @@ namespace Blah
BufferStream& operator=(BufferStream&& bs) noexcept; BufferStream& operator=(BufferStream&& bs) noexcept;
~BufferStream(); ~BufferStream();
virtual int64_t length() const override { return m_length; } virtual i64 length() const override { return m_length; }
virtual int64_t position() const override { return m_position; } virtual i64 position() const override { return m_position; }
virtual int64_t seek(int64_t seekTo) override { return m_position = (seekTo < 0 ? 0 : (seekTo > m_length ? m_length : seekTo)); } virtual i64 seek(i64 seekTo) override { return m_position = (seekTo < 0 ? 0 : (seekTo > m_length ? m_length : seekTo)); }
virtual bool is_open() const override { return m_buffer != nullptr; } virtual bool is_open() const override { return m_buffer != nullptr; }
virtual bool is_readable() const override { return true; } virtual bool is_readable() const override { return true; }
virtual bool is_writable() const override { return true; } virtual bool is_writable() const override { return true; }
@ -25,13 +25,13 @@ namespace Blah
const char* data() const { return m_buffer; } const char* data() const { return m_buffer; }
protected: protected:
virtual int64_t read_into(void* ptr, int64_t length) override; virtual i64 read_into(void* ptr, i64 length) override;
virtual int64_t write_from(const void* ptr, int64_t length) override; virtual i64 write_from(const void* ptr, i64 length) override;
private: private:
char* m_buffer; char* m_buffer;
int64_t m_capacity; i64 m_capacity;
int64_t m_length; i64 m_length;
int64_t m_position; i64 m_position;
}; };
} }

View File

@ -8,22 +8,22 @@ namespace Blah
{ {
public: public:
FileStream(); FileStream();
FileStream(const char* path, FileMode mode = FileMode::ReadWrite); FileStream(const FilePath& path, FileMode mode = FileMode::ReadWrite);
FileStream(FileStream&& fs) noexcept; FileStream(FileStream&& fs) noexcept;
FileStream& operator=(FileStream&& fs) noexcept; FileStream& operator=(FileStream&& fs) noexcept;
~FileStream(); ~FileStream();
virtual int64_t length() const override; virtual i64 length() const override;
virtual int64_t position() const override; virtual i64 position() const override;
virtual int64_t seek(int64_t seekTo) override; virtual i64 seek(i64 seekTo) override;
virtual bool is_open() const override { return m_handle != nullptr; } virtual bool is_open() const override;
virtual bool is_readable() const override { return m_handle != nullptr && (m_mode == FileMode::ReadWrite || m_mode == FileMode::Read); } virtual bool is_readable() const override;
virtual bool is_writable() const override { return m_handle != nullptr && (m_mode == FileMode::ReadWrite || m_mode == FileMode::Write); } virtual bool is_writable() const override;
virtual void close() override; virtual void close() override;
protected: protected:
virtual int64_t read_into(void* ptr, int64_t length) override; virtual i64 read_into(void* ptr, i64 length) override;
virtual int64_t write_from(const void* ptr, int64_t length) override; virtual i64 write_from(const void* ptr, i64 length) override;
private: private:
FileMode m_mode; FileMode m_mode;

View File

@ -7,14 +7,14 @@ namespace Blah
{ {
public: public:
MemoryStream(); MemoryStream();
MemoryStream(char* data, int64_t length); MemoryStream(char* data, i64 length);
MemoryStream(MemoryStream&& ms) noexcept; MemoryStream(MemoryStream&& ms) noexcept;
MemoryStream& operator=(MemoryStream&& ms) noexcept; MemoryStream& operator=(MemoryStream&& ms) noexcept;
~MemoryStream() { m_data = nullptr; m_length = m_position = 0; } ~MemoryStream() { m_data = nullptr; m_length = m_position = 0; }
virtual int64_t length() const override { return m_length; } virtual i64 length() const override { return m_length; }
virtual int64_t position() const override { return m_position; } virtual i64 position() const override { return m_position; }
virtual int64_t seek(int64_t seekTo) override { return m_position = (seekTo < 0 ? 0 : (seekTo > m_length ? m_length : seekTo)); } virtual i64 seek(i64 seekTo) override { return m_position = (seekTo < 0 ? 0 : (seekTo > m_length ? m_length : seekTo)); }
virtual bool is_open() const override { return m_data != nullptr; } virtual bool is_open() const override { return m_data != nullptr; }
virtual bool is_readable() const override { return true; } virtual bool is_readable() const override { return true; }
virtual bool is_writable() const override { return true; } virtual bool is_writable() const override { return true; }
@ -24,12 +24,12 @@ namespace Blah
const char* data() const { return m_data; } const char* data() const { return m_data; }
protected: protected:
virtual int64_t read_into(void* ptr, int64_t length) override; virtual i64 read_into(void* ptr, i64 length) override;
virtual int64_t write_from(const void* ptr, int64_t length) override; virtual i64 write_from(const void* ptr, i64 length) override;
private: private:
char* m_data; char* m_data;
int64_t m_length; i64 m_length;
int64_t m_position; i64 m_position;
}; };
} }

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
#include <blah/containers/str.h> #include <blah/containers/str.h>
#include <blah/streams/endian.h> #include <blah/streams/endian.h>
#include <string.h> #include <string.h>
@ -16,13 +16,13 @@ namespace Blah
virtual ~Stream() = default; virtual ~Stream() = default;
// returns the length of the stream // returns the length of the stream
virtual int64_t length() const = 0; virtual i64 length() const = 0;
// returns the position of the stream // returns the position of the stream
virtual int64_t position() const = 0; virtual i64 position() const = 0;
// seeks the position of the stream // seeks the position of the stream
virtual int64_t seek(int64_t seek_to) = 0; virtual i64 seek(i64 seek_to) = 0;
// returns true of the stream is open // returns true of the stream is open
virtual bool is_open() const = 0; virtual bool is_open() const = 0;
@ -36,11 +36,11 @@ namespace Blah
// closes the stream // closes the stream
virtual void close() = 0; virtual void close() = 0;
// pipes the contents of this tream to another stream // pipes the contents of this stream to another stream
int64_t pipe(Stream& to, int64_t length); i64 pipe(Stream& to, i64 length);
// reads the amount of bytes into the given buffer, and returns the amount read // reads the amount of bytes into the given buffer, and returns the amount read
int64_t read(void* buffer, int64_t length) { return read_into(buffer, length); } i64 read(void* buffer, i64 length) { return read_into(buffer, length); }
// reads a string. if length < 0, assumes null-terminated // reads a string. if length < 0, assumes null-terminated
String read_string(int length = -1); String read_string(int length = -1);
@ -67,21 +67,21 @@ namespace Blah
} }
// writes the amount of bytes to the stream from the given buffer, and returns the amount written // writes the amount of bytes to the stream from the given buffer, and returns the amount written
int64_t write(const void* buffer, int64_t length); i64 write(const void* buffer, i64 length);
// writes the contents of a string to the stream // writes the contents of a string to the stream
int64_t write(const String& string); i64 write(const String& string);
// writes a number // writes a number
template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type> template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
int64_t write(const T& value) i64 write(const T& value)
{ {
return write<T>(value, Endian::Little); return write<T>(value, Endian::Little);
} }
// writes a number // writes a number
template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type> template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
int64_t write(const T& value, Endian endian) i64 write(const T& value, Endian endian)
{ {
T writing = value; T writing = value;
@ -93,9 +93,9 @@ namespace Blah
protected: protected:
// reads from the stream into the given buffer, and returns the number of bytes read // reads from the stream into the given buffer, and returns the number of bytes read
virtual int64_t read_into(void* buffer, int64_t length) = 0; virtual i64 read_into(void* buffer, i64 length) = 0;
// writes from the stream from the given buffer, and returns the number of bytes written // writes from the stream from the given buffer, and returns the number of bytes written
virtual int64_t write_from(const void* buffer, int64_t length) = 0; virtual i64 write_from(const void* buffer, i64 length) = 0;
}; };
} }

View File

@ -44,7 +44,7 @@ void Str::reserve(int size)
{ {
char* local = data(); char* local = data();
m_buffer = new char[m_capacity]; m_buffer = new char[m_capacity];
strncpy(m_buffer, local, m_local_size); memcpy(m_buffer, local, m_local_size);
m_buffer[m_local_size] = '\0'; m_buffer[m_local_size] = '\0';
} }
// expand from empty buffer // expand from empty buffer
@ -72,9 +72,9 @@ void Str::set_length(int len)
m_length = len; m_length = len;
} }
uint32_t Str::utf8_at(int index) const u32 Str::utf8_at(int index) const
{ {
uint32_t charcode = 0; u32 charcode = 0;
int t = (unsigned char)(this->operator[](index++)); int t = (unsigned char)(this->operator[](index++));
if (t < 128) if (t < 128)
@ -124,7 +124,7 @@ Str& Str::append(char c)
return *this; return *this;
} }
Str& Str::append(uint32_t c) Str& Str::append(u32 c)
{ {
// one octet // one octet
if (c < 0x80) if (c < 0x80)
@ -210,21 +210,21 @@ Str& Str::append_fmt(const char* fmt, ...)
return *this; return *this;
} }
Str& Str::append_utf16(const uint16_t* start, const uint16_t* end, bool swap_endian) Str& Str::append_utf16(const u16* start, const u16* end, bool swap_endian)
{ {
// converts utf16 into utf8 // converts utf16 into utf8
// more info: https://en.wikipedia.org/wiki/UTF-16#Description // more info: https://en.wikipedia.org/wiki/UTF-16#Description
const uint16_t surrogate_min = 0xd800u; const u16 surrogate_min = 0xd800u;
const uint16_t surrogate_max = 0xdbffu; const u16 surrogate_max = 0xdbffu;
while (start != end) while (start != end)
{ {
uint16_t next = (*start++); u16 next = (*start++);
if (swap_endian) if (swap_endian)
next = ((next & 0xff) << 8 | ((next & 0xff00) >> 8)); next = ((next & 0xff) << 8 | ((next & 0xff00) >> 8));
uint32_t cp = 0xffff & next; u32 cp = 0xffff & next;
if ((cp >= surrogate_min && cp <= surrogate_max)) if ((cp >= surrogate_min && cp <= surrogate_max))
{ {
@ -232,7 +232,7 @@ Str& Str::append_utf16(const uint16_t* start, const uint16_t* end, bool swap_end
if (swap_endian) if (swap_endian)
next = ((next & 0xff) << 8 | ((next & 0xff00) >> 8)); next = ((next & 0xff) << 8 | ((next & 0xff00) >> 8));
uint32_t trail = 0xffff & next; u32 trail = 0xffff & next;
cp = (cp << 10) + trail + 0x10000u - (surrogate_min << 10) - 0xdc00u; cp = (cp << 10) + trail + 0x10000u - (surrogate_min << 10) - 0xdc00u;
} }

View File

@ -1,5 +1,5 @@
#include <blah/core/app.h> #include <blah/core/app.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include <blah/core/time.h> #include <blah/core/time.h>
#include <blah/math/point.h> #include <blah/math/point.h>
#include <blah/graphics/framebuffer.h> #include <blah/graphics/framebuffer.h>
@ -35,8 +35,8 @@ namespace
Config app_config; Config app_config;
bool app_is_running = false; bool app_is_running = false;
bool app_is_exiting = false; bool app_is_exiting = false;
uint64_t time_last; u64 time_last;
uint64_t time_accumulator = 0; u64 time_accumulator = 0;
void app_iterate() void app_iterate()
{ {
@ -46,9 +46,9 @@ namespace
// update at a fixed timerate // update at a fixed timerate
// TODO: allow a non-fixed step update? // TODO: allow a non-fixed step update?
{ {
uint64_t time_target = (uint64_t)((1.0 / app_config.target_framerate) * Time::ticks_per_second); u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
uint64_t time_curr = PlatformBackend::ticks(); u64 time_curr = PlatformBackend::ticks();
uint64_t time_diff = time_curr - time_last; u64 time_diff = time_curr - time_last;
time_last = time_curr; time_last = time_curr;
time_accumulator += time_diff; time_accumulator += time_diff;
@ -66,7 +66,7 @@ namespace
// Do not allow us to fall behind too many updates // Do not allow us to fall behind too many updates
// (otherwise we'll get spiral of death) // (otherwise we'll get spiral of death)
uint64_t time_maximum = app_config.max_updates * time_target; u64 time_maximum = app_config.max_updates * time_target;
if (time_accumulator > time_maximum) if (time_accumulator > time_maximum)
time_accumulator = time_maximum; time_accumulator = time_maximum;
@ -300,7 +300,7 @@ namespace
return App::draw_height(); return App::draw_height();
} }
virtual void clear(Color color, float depth, uint8_t stencil, ClearMask mask) override virtual void clear(Color color, float depth, u8 stencil, ClearMask mask) override
{ {
GraphicsBackend::clear_backbuffer(color, depth, stencil, mask); GraphicsBackend::clear_backbuffer(color, depth, stencil, mask);
} }

View File

@ -1,11 +1,11 @@
#include <blah/core/log.h> #include <blah/core/common.h>
#include <blah/core/app.h> #include <blah/core/app.h>
#include <stdarg.h> // for logging methods #include <stdarg.h> // for logging methods
#include <stdio.h> // for sprintf #include <stdio.h> // for sprintf
using namespace Blah; using namespace Blah;
void Log::print(const char* format, ...) void Log::info(const char* format, ...)
{ {
char msg[BLAH_MESSAGE]; char msg[BLAH_MESSAGE];
va_list ap; va_list ap;

View File

@ -1,4 +1,5 @@
#include <blah/core/filesystem.h> #include <blah/core/filesystem.h>
#include <blah/streams/filestream.h>
#include "../internal/platform_backend.h" #include "../internal/platform_backend.h"
using namespace Blah; using namespace Blah;
@ -13,6 +14,11 @@ bool File::remove(const FilePath& path)
return PlatformBackend::file_delete(path.cstr()); return PlatformBackend::file_delete(path.cstr());
} }
FileStream File::open(const FilePath& path , FileMode mode)
{
return FileStream(path, mode);
}
bool Directory::create(const FilePath& path) bool Directory::create(const FilePath& path)
{ {
return PlatformBackend::dir_create(path.cstr()); return PlatformBackend::dir_create(path.cstr());

View File

@ -10,8 +10,8 @@ namespace
} }
} }
uint64_t Time::ticks = 0; u64 Time::ticks = 0;
uint64_t Time::previous_ticks = 0; u64 Time::previous_ticks = 0;
double Time::seconds = 0; double Time::seconds = 0;
double Time::previous_seconds = 0; double Time::previous_seconds = 0;
float Time::delta = 0; float Time::delta = 0;

View File

@ -1,4 +1,4 @@
#include <blah/drawing/batch.h> #include <blah/graphics/batch.h>
#include <blah/graphics/texture.h> #include <blah/graphics/texture.h>
#include <blah/graphics/framebuffer.h> #include <blah/graphics/framebuffer.h>
#include <blah/graphics/mesh.h> #include <blah/graphics/mesh.h>
@ -15,9 +15,7 @@ namespace
// TODO: // TODO:
// This shader needs to be graphics API agnostic // This shader needs to be graphics API agnostic
#ifdef BLAH_USE_OPENGL const ShaderData opengl_shader_data = {
const ShaderData shader_data = {
// vertex shader // vertex shader
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
"#version 300 es\n" "#version 300 es\n"
@ -62,8 +60,6 @@ namespace
"}" "}"
}; };
#elif BLAH_USE_D3D11
const char* d3d11_shader = "" const char* d3d11_shader = ""
"cbuffer constants : register(b0)\n" "cbuffer constants : register(b0)\n"
"{\n" "{\n"
@ -110,7 +106,7 @@ namespace
" input.mask.z * input.color;\n" " input.mask.z * input.color;\n"
"}\n"; "}\n";
const ShaderData shader_data = { const ShaderData d3d11_shader_data = {
d3d11_shader, d3d11_shader,
d3d11_shader, d3d11_shader,
{ {
@ -121,10 +117,6 @@ namespace
} }
}; };
#else
const ShaderData shader_data;
#endif
const VertexFormat format = VertexFormat( const VertexFormat format = VertexFormat(
{ {
{ 0, VertexType::Float2, false }, { 0, VertexType::Float2, false },
@ -164,12 +156,12 @@ namespace
{ \ { \
m_batch.elements += 2; \ m_batch.elements += 2; \
auto _i = m_indices.expand(6); \ auto _i = m_indices.expand(6); \
*_i++ = (uint32_t)m_vertices.size() + 0; \ *_i++ = (u32)m_vertices.size() + 0; \
*_i++ = (uint32_t)m_vertices.size() + 1; \ *_i++ = (u32)m_vertices.size() + 1; \
*_i++ = (uint32_t)m_vertices.size() + 2; \ *_i++ = (u32)m_vertices.size() + 2; \
*_i++ = (uint32_t)m_vertices.size() + 0; \ *_i++ = (u32)m_vertices.size() + 0; \
*_i++ = (uint32_t)m_vertices.size() + 2; \ *_i++ = (u32)m_vertices.size() + 2; \
*_i++ = (uint32_t)m_vertices.size() + 3; \ *_i++ = (u32)m_vertices.size() + 3; \
Vertex* _v = m_vertices.expand(4); \ Vertex* _v = m_vertices.expand(4); \
MAKE_VERTEX(_v, m_matrix, px0, py0, tx0, ty0, col0, mult, fill, wash); _v++; \ MAKE_VERTEX(_v, m_matrix, px0, py0, tx0, ty0, col0, mult, fill, wash); _v++; \
MAKE_VERTEX(_v, m_matrix, px1, py1, tx1, ty1, col1, mult, fill, wash); _v++; \ MAKE_VERTEX(_v, m_matrix, px1, py1, tx1, ty1, col1, mult, fill, wash); _v++; \
@ -181,9 +173,9 @@ namespace
{ \ { \
m_batch.elements += 1; \ m_batch.elements += 1; \
auto* _i = m_indices.expand(3); \ auto* _i = m_indices.expand(3); \
*_i++ = (uint32_t)m_vertices.size() + 0; \ *_i++ = (u32)m_vertices.size() + 0; \
*_i++ = (uint32_t)m_vertices.size() + 1; \ *_i++ = (u32)m_vertices.size() + 1; \
*_i++ = (uint32_t)m_vertices.size() + 2; \ *_i++ = (u32)m_vertices.size() + 2; \
Vertex* _v = m_vertices.expand(3); \ Vertex* _v = m_vertices.expand(3); \
MAKE_VERTEX(_v, m_matrix, px0, py0, tx0, ty0, col0, mult, fill, wash); _v++; \ MAKE_VERTEX(_v, m_matrix, px0, py0, tx0, ty0, col0, mult, fill, wash); _v++; \
MAKE_VERTEX(_v, m_matrix, px1, py1, tx1, ty1, col1, mult, fill, wash); _v++; \ MAKE_VERTEX(_v, m_matrix, px1, py1, tx1, ty1, col1, mult, fill, wash); _v++; \
@ -384,7 +376,12 @@ void Batch::render(const FrameBufferRef& target, const Mat4x4& matrix)
m_mesh = Mesh::create(); m_mesh = Mesh::create();
if (!m_default_shader) if (!m_default_shader)
m_default_shader = Shader::create(shader_data); {
if (App::renderer() == Renderer::OpenGL)
m_default_shader = Shader::create(opengl_shader_data);
else if (App::renderer() == Renderer::D3D11)
m_default_shader = Shader::create(d3d11_shader_data);
}
if (!m_default_material) if (!m_default_material)
m_default_material = Material::create(m_default_shader); m_default_material = Material::create(m_default_shader);
@ -423,8 +420,8 @@ void Batch::render_single_batch(RenderPass& pass, const DrawBatch& b, const Mat4
pass.blend = b.blend; pass.blend = b.blend;
pass.has_scissor = b.scissor.w >= 0 && b.scissor.h >= 0; pass.has_scissor = b.scissor.w >= 0 && b.scissor.h >= 0;
pass.scissor = b.scissor; pass.scissor = b.scissor;
pass.index_start = (int64_t)b.offset * 3; pass.index_start = (i64)b.offset * 3;
pass.index_count = (int64_t)b.elements * 3; pass.index_count = (i64)b.elements * 3;
pass.perform(); pass.perform();
} }
@ -1046,7 +1043,7 @@ void Batch::str(const SpriteFont& font, const String& text, const Vec2& pos, Tex
else else
offset.y = (font.ascent + font.descent + font.height() - font.height_of(text)) * 0.5f; offset.y = (font.ascent + font.descent + font.height() - font.height_of(text)) * 0.5f;
uint32_t last = 0; u32 last = 0;
for (int i = 0, l = text.length(); i < l; i++) for (int i = 0, l = text.length(); i < l; i++)
{ {
if (text[i] == '\n') if (text[i] == '\n')
@ -1067,7 +1064,7 @@ void Batch::str(const SpriteFont& font, const String& text, const Vec2& pos, Tex
} }
// get the character // get the character
uint32_t next = text.utf8_at(i); u32 next = text.utf8_at(i);
const auto& ch = font[next]; const auto& ch = font[next];
// draw it, if the subtexture exists // draw it, if the subtexture exists

View File

@ -12,7 +12,7 @@ FrameBufferRef FrameBuffer::create(int width, int height)
FrameBufferRef FrameBuffer::create(int width, int height, const TextureFormat* attachments, int attachment_count) FrameBufferRef FrameBuffer::create(int width, int height, const TextureFormat* attachments, int attachment_count)
{ {
BLAH_ASSERT(width > 0 && height > 0, "FrameBuffer width and height must be larger than 0"); BLAH_ASSERT(width > 0 && height > 0, "FrameBuffer width and height must be larger than 0");
BLAH_ASSERT(attachment_count <= BLAH_ATTACHMENTS, "Exceeded maximum attachment count"); BLAH_ASSERT(attachment_count <= Attachments::MaxCapacity, "Exceeded maximum attachment count");
BLAH_ASSERT(attachment_count > 0, "At least one attachment must be provided"); BLAH_ASSERT(attachment_count > 0, "At least one attachment must be provided");
int color_count = 0; int color_count = 0;
@ -29,7 +29,7 @@ FrameBufferRef FrameBuffer::create(int width, int height, const TextureFormat* a
} }
BLAH_ASSERT(depth_count <= 1, "FrameBuffer can only have 1 Depth/Stencil Texture"); BLAH_ASSERT(depth_count <= 1, "FrameBuffer can only have 1 Depth/Stencil Texture");
BLAH_ASSERT(color_count <= BLAH_ATTACHMENTS - 1, "Exceeded maximum Color attachment count"); BLAH_ASSERT(color_count <= Attachments::MaxCapacity - 1, "Exceeded maximum Color attachment count");
return GraphicsBackend::create_framebuffer(width, height, attachments, attachment_count); return GraphicsBackend::create_framebuffer(width, height, attachments, attachment_count);
} }

View File

@ -1,5 +1,5 @@
#include <blah/graphics/material.h> #include <blah/graphics/material.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include <cstring> #include <cstring>
using namespace Blah; using namespace Blah;
@ -277,7 +277,7 @@ TextureSampler Material::get_sampler(int slot, int index) const
return TextureSampler(); return TextureSampler();
} }
void Material::set_value(const char* name, const float* value, int64_t length) void Material::set_value(const char* name, const float* value, i64 length)
{ {
BLAH_ASSERT(m_shader, "Material Shader is invalid"); BLAH_ASSERT(m_shader, "Material Shader is invalid");
BLAH_ASSERT(length >= 0, "Length must be >= 0"); BLAH_ASSERT(length >= 0, "Length must be >= 0");
@ -311,7 +311,7 @@ void Material::set_value(const char* name, const float* value, int64_t length)
Log::warn("No Uniform '%s' exists", name); Log::warn("No Uniform '%s' exists", name);
} }
const float* Material::get_value(const char* name, int64_t* length) const const float* Material::get_value(const char* name, i64* length) const
{ {
BLAH_ASSERT(m_shader, "Material Shader is invalid"); BLAH_ASSERT(m_shader, "Material Shader is invalid");

View File

@ -22,6 +22,7 @@ VertexFormat::VertexFormat(std::initializer_list<VertexAttribute> attributes, in
{ {
switch (it.type) switch (it.type)
{ {
case VertexType::None: break;
case VertexType::Float: stride += 4; break; case VertexType::Float: stride += 4; break;
case VertexType::Float2: stride += 8; break; case VertexType::Float2: stride += 8; break;
case VertexType::Float3: stride += 12; break; case VertexType::Float3: stride += 12; break;

View File

@ -1,5 +1,5 @@
#include <blah/graphics/renderpass.h> #include <blah/graphics/renderpass.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include "../internal/graphics_backend.h" #include "../internal/graphics_backend.h"
using namespace Blah; using namespace Blah;
@ -38,7 +38,7 @@ void RenderPass::perform()
} }
// Validate Index Count // Validate Index Count
int64_t index_count = pass.mesh->index_count(); i64 index_count = pass.mesh->index_count();
if (pass.index_start + pass.index_count > index_count) if (pass.index_start + pass.index_count > index_count)
{ {
Log::warn( Log::warn(
@ -54,7 +54,7 @@ void RenderPass::perform()
} }
// Validate Instance Count // Validate Instance Count
int64_t instance_count = pass.mesh->instance_count(); i64 instance_count = pass.mesh->instance_count();
if (pass.instance_count > instance_count) if (pass.instance_count > instance_count)
{ {
Log::warn( Log::warn(

View File

@ -1,10 +1,19 @@
#include <blah/drawing/spritefont.h> #include <blah/graphics/spritefont.h>
#include <blah/images/font.h> #include <blah/images/font.h>
#include <blah/images/packer.h> #include <blah/images/packer.h>
#include <blah/core/log.h> #include <blah/core/common.h>
using namespace Blah; using namespace Blah;
SpriteFont::CharRange::CharRange()
: from(0), to(0) {}
SpriteFont::CharRange::CharRange(Codepoint single)
: from(single), to(single) {}
SpriteFont::CharRange::CharRange(Codepoint from, Codepoint to)
: from(from), to(to) {}
const SpriteFont::CharSet SpriteFont::CharRange::ASCII = SpriteFont::CharSet({ CharRange(32, 128) });
SpriteFont::SpriteFont() SpriteFont::SpriteFont()
{ {
size = 0; size = 0;
@ -13,27 +22,24 @@ SpriteFont::SpriteFont()
line_gap = 0; line_gap = 0;
} }
const uint32_t ascii[]{ 32, 128, 0 }; SpriteFont::SpriteFont(const FilePath& file, float size)
const uint32_t* SpriteFont::ASCII = ascii;
SpriteFont::SpriteFont(const char* file, float size)
{ {
build(file, size, ASCII); rebuild(file, size, CharRange::ASCII);
} }
SpriteFont::SpriteFont(const char* file, float size, const uint32_t* charset) SpriteFont::SpriteFont(const FilePath& file, float size, const CharSet& charset)
{ {
build(file, size, charset); rebuild(file, size, charset);
} }
SpriteFont::SpriteFont(const Font& font, float size) SpriteFont::SpriteFont(const Font& font, float size)
{ {
build(font, size, ASCII); rebuild(font, size, CharRange::ASCII);
} }
SpriteFont::SpriteFont(const Font& font, float size, const uint32_t* charset) SpriteFont::SpriteFont(const Font& font, float size, const CharSet& charset)
{ {
build(font, size, charset); rebuild(font, size, charset);
} }
SpriteFont::SpriteFont(SpriteFont&& src) noexcept SpriteFont::SpriteFont(SpriteFont&& src) noexcept
@ -79,7 +85,7 @@ float SpriteFont::width_of(const String& text) const
float width = 0; float width = 0;
float line_width = 0; float line_width = 0;
uint32_t last; Codepoint last = 0;
for (int i = 0; i < text.length(); i ++) for (int i = 0; i < text.length(); i ++)
{ {
if (text[i] == '\n') if (text[i] == '\n')
@ -116,7 +122,7 @@ float SpriteFont::width_of_line(const String& text, int start) const
float width = 0; float width = 0;
uint32_t last; Codepoint last = 0;
for (int i = start; i < text.length(); i ++) for (int i = start; i < text.length(); i ++)
{ {
if (text[i] == '\n') if (text[i] == '\n')
@ -157,16 +163,16 @@ float SpriteFont::height_of(const String& text) const
return height - line_gap; return height - line_gap;
} }
void SpriteFont::build(const char* file, float sz, const uint32_t* charset) void SpriteFont::rebuild(const FilePath& file, float sz, const CharSet& charset)
{ {
dispose(); dispose();
Font font(file); Font font(file);
if (font.is_valid()) if (font.is_valid())
build(font, sz, charset); rebuild(font, sz, charset);
} }
void SpriteFont::build(const Font& font, float size, const uint32_t* charset) void SpriteFont::rebuild(const Font& font, float size, const CharSet& charset)
{ {
dispose(); dispose();
@ -184,14 +190,13 @@ void SpriteFont::build(const Font& font, float size, const uint32_t* charset)
packer.max_size = 8192; packer.max_size = 8192;
packer.power_of_two = true; packer.power_of_two = true;
std::unordered_map<uint32_t, int> glyphs; std::unordered_map<Codepoint, int> glyphs;
Vector<Color> buffer; Vector<Color> buffer;
auto ranges = charset; for (auto& range : charset)
while (*ranges != 0)
{ {
auto from = *ranges; auto from = range.from;
auto to = *(ranges + 1); auto to = range.to + 1;
BLAH_ASSERT(to >= from, "Charset Range must be in pairs of [min,max]"); BLAH_ASSERT(to >= from, "Charset Range must be in pairs of [min,max]");
@ -204,7 +209,7 @@ void SpriteFont::build(const Font& font, float size, const uint32_t* charset)
glyphs[i] = glyph; glyphs[i] = glyph;
// add character // add character
Font::Char ch = font.get_character(glyph, scale); auto ch = font.get_character(glyph, scale);
m_characters[i].advance = ch.advance; m_characters[i].advance = ch.advance;
m_characters[i].offset = Vec2(ch.offset_x, ch.offset_y); m_characters[i].offset = Vec2(ch.offset_x, ch.offset_y);
@ -218,8 +223,6 @@ void SpriteFont::build(const Font& font, float size, const uint32_t* charset)
packer.add(i, ch.width, ch.height, buffer.data()); packer.add(i, ch.width, ch.height, buffer.data());
} }
} }
ranges += 2;
} }
buffer.clear(); buffer.clear();
@ -229,9 +232,9 @@ void SpriteFont::build(const Font& font, float size, const uint32_t* charset)
m_atlas.push_back(Texture::create(it)); m_atlas.push_back(Texture::create(it));
// add character subtextures // add character subtextures
for (auto& it : packer.entries) for (auto& it : packer.entries())
if (!it.empty) if (!it.empty)
m_characters[(uint32_t)it.id].subtexture = Subtexture(m_atlas[it.page], it.packed, it.frame); m_characters[(Codepoint)it.id].subtexture = Subtexture(m_atlas[it.page], it.packed, it.frame);
// add kerning // add kerning
for (auto a = glyphs.begin(); a != glyphs.end(); a++) for (auto a = glyphs.begin(); a != glyphs.end(); a++)
@ -243,9 +246,9 @@ void SpriteFont::build(const Font& font, float size, const uint32_t* charset)
} }
} }
float SpriteFont::get_kerning(uint32_t codepoint0, uint32_t codepoint1) const float SpriteFont::get_kerning(Codepoint codepoint0, Codepoint codepoint1) const
{ {
uint64_t index = ((uint64_t)codepoint0 << 32) | codepoint1; u64 index = ((u64)codepoint0 << 32) | codepoint1;
auto it = m_kerning.find(index); auto it = m_kerning.find(index);
if (it != m_kerning.end()) if (it != m_kerning.end())
@ -253,9 +256,9 @@ float SpriteFont::get_kerning(uint32_t codepoint0, uint32_t codepoint1) const
return 0.0f; return 0.0f;
} }
void SpriteFont::set_kerning(uint32_t codepoint0, uint32_t codepoint1, float value) void SpriteFont::set_kerning(Codepoint codepoint0, Codepoint codepoint1, float value)
{ {
uint64_t index = ((uint64_t)codepoint0 << 32) | codepoint1; u64 index = ((u64)codepoint0 << 32) | codepoint1;
if (value == 0) if (value == 0)
{ {
@ -267,7 +270,12 @@ void SpriteFont::set_kerning(uint32_t codepoint0, uint32_t codepoint1, float val
} }
} }
const SpriteFont::Character& SpriteFont::get_character(uint32_t codepoint) const SpriteFont::Character& SpriteFont::get_character(Codepoint codepoint)
{
return m_characters[codepoint];
}
const SpriteFont::Character& SpriteFont::get_character(Codepoint codepoint) const
{ {
static const Character empty; static const Character empty;
auto it = m_characters.find(codepoint); auto it = m_characters.find(codepoint);
@ -276,7 +284,12 @@ const SpriteFont::Character& SpriteFont::get_character(uint32_t codepoint) const
return empty; return empty;
} }
const SpriteFont::Character& SpriteFont::operator[](uint32_t codepoint) const SpriteFont::Character& SpriteFont::operator[](Codepoint codepoint)
{
return m_characters[codepoint];
}
const SpriteFont::Character& SpriteFont::operator[](Codepoint codepoint) const
{ {
static const Character empty; static const Character empty;
auto it = m_characters.find(codepoint); auto it = m_characters.find(codepoint);

View File

@ -1,4 +1,4 @@
#include <blah/drawing/subtexture.h> #include <blah/graphics/subtexture.h>
#include <blah/math/calc.h> #include <blah/math/calc.h>
using namespace Blah; using namespace Blah;

View File

@ -1,7 +1,7 @@
#include <blah/graphics/texture.h> #include <blah/graphics/texture.h>
#include <blah/images/image.h> #include <blah/images/image.h>
#include <blah/streams/stream.h> #include <blah/streams/stream.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include "../internal/graphics_backend.h" #include "../internal/graphics_backend.h"
using namespace Blah; using namespace Blah;

View File

@ -1,17 +1,13 @@
#include <blah/images/aseprite.h> #include <blah/images/aseprite.h>
#include <blah/streams/filestream.h> #include <blah/streams/filestream.h>
#include <blah/core/filesystem.h> #include <blah/core/filesystem.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include <blah/math/calc.h>
#define STBI_NO_STDIO #define STBI_NO_STDIO
#define STBI_ONLY_ZLIB #define STBI_ONLY_ZLIB
#include "../third_party/stb_image.h" #include "../third_party/stb_image.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MUL_UN8(a, b, t) \
((t) = (a) * (uint16_t)(b) + 0x80, ((((t) >> 8) + (t) ) >> 8))
using namespace Blah; using namespace Blah;
Aseprite::Aseprite() Aseprite::Aseprite()
@ -19,7 +15,7 @@ Aseprite::Aseprite()
} }
Aseprite::Aseprite(const char* path) Aseprite::Aseprite(const FilePath& path)
{ {
FileStream fs(path, FileMode::Read); FileStream fs(path, FileMode::Read);
parse(fs); parse(fs);
@ -98,10 +94,10 @@ void Aseprite::parse(Stream& stream)
// header // header
{ {
// filesize // filesize
stream.read<uint32_t>(Endian::Little); stream.read<u32>(Endian::Little);
// magic number // magic number
auto magic = stream.read<uint16_t>(Endian::Little); auto magic = stream.read<u16>(Endian::Little);
if (magic != 0xA5E0) if (magic != 0xA5E0)
{ {
BLAH_ERROR("File is not a valid Aseprite file"); BLAH_ERROR("File is not a valid Aseprite file");
@ -109,21 +105,21 @@ void Aseprite::parse(Stream& stream)
} }
// main info // main info
frame_count = stream.read<uint16_t>(Endian::Little); frame_count = stream.read<u16>(Endian::Little);
width = stream.read<uint16_t>(Endian::Little); width = stream.read<u16>(Endian::Little);
height = stream.read<uint16_t>(Endian::Little); height = stream.read<u16>(Endian::Little);
mode = static_cast<Aseprite::Modes>(stream.read<uint16_t>(Endian::Little) / 8); mode = static_cast<Aseprite::Modes>(stream.read<u16>(Endian::Little) / 8);
// don't care about other info // don't care about other info
stream.read<uint32_t>(Endian::Little); // Flags stream.read<u32>(Endian::Little); // Flags
stream.read<uint16_t>(Endian::Little); // Speed (deprecated) stream.read<u16>(Endian::Little); // Speed (deprecated)
stream.read<uint32_t>(Endian::Little); // Should be 0 stream.read<u32>(Endian::Little); // Should be 0
stream.read<uint32_t>(Endian::Little); // Should be 0 stream.read<u32>(Endian::Little); // Should be 0
stream.read<uint8_t>(Endian::Little); // Palette entry stream.read<u8>(Endian::Little); // Palette entry
stream.seek(stream.position() + 3); // Ignore these bytes stream.seek(stream.position() + 3); // Ignore these bytes
stream.read<uint16_t>(Endian::Little); // Number of colors (0 means 256 for old sprites) stream.read<u16>(Endian::Little); // Number of colors (0 means 256 for old sprites)
stream.read<int8_t>(Endian::Little); // Pixel width stream.read<i8>(Endian::Little); // Pixel width
stream.read<int8_t>(Endian::Little); // Pixel height stream.read<i8>(Endian::Little); // Pixel height
stream.seek(stream.position() + 92); // For Future stream.seek(stream.position() + 92); // For Future
} }
@ -133,22 +129,22 @@ void Aseprite::parse(Stream& stream)
for (int i = 0; i < frame_count; i++) for (int i = 0; i < frame_count; i++)
{ {
auto frameStart = stream.position(); auto frameStart = stream.position();
auto frameEnd = frameStart + stream.read<uint32_t>(Endian::Little); auto frameEnd = frameStart + stream.read<u32>(Endian::Little);
unsigned int chunks = 0; unsigned int chunks = 0;
// frame header // frame header
{ {
auto magic = stream.read<uint16_t>(Endian::Little); // magic number auto magic = stream.read<u16>(Endian::Little); // magic number
if (magic != 0xF1FA) if (magic != 0xF1FA)
{ {
BLAH_ERROR("File is not a valid Aseprite file"); BLAH_ERROR("File is not a valid Aseprite file");
return; return;
} }
auto old_chunk_count = stream.read<uint16_t>(Endian::Little); auto old_chunk_count = stream.read<u16>(Endian::Little);
frames[i].duration = stream.read<uint16_t>(Endian::Little); frames[i].duration = stream.read<u16>(Endian::Little);
stream.seek(stream.position() + 2); // for future stream.seek(stream.position() + 2); // for future
auto new_chunk_count = stream.read<uint32_t>(Endian::Little); auto new_chunk_count = stream.read<u32>(Endian::Little);
if (old_chunk_count == 0xFFFF) if (old_chunk_count == 0xFFFF)
chunks = new_chunk_count; chunks = new_chunk_count;
@ -163,8 +159,8 @@ void Aseprite::parse(Stream& stream)
for (unsigned int j = 0; j < chunks; j++) for (unsigned int j = 0; j < chunks; j++)
{ {
auto chunkStart = stream.position(); auto chunkStart = stream.position();
auto chunkEnd = chunkStart + stream.read<uint32_t>(Endian::Little); auto chunkEnd = chunkStart + stream.read<u32>(Endian::Little);
auto chunkType = static_cast<Chunks>(stream.read<uint16_t>(Endian::Little)); auto chunkType = static_cast<Chunks>(stream.read<u16>(Endian::Little));
switch (chunkType) switch (chunkType)
{ {
@ -189,17 +185,17 @@ void Aseprite::parse_layer(Stream& stream, int frame)
layers.emplace_back(); layers.emplace_back();
auto& layer = layers.back(); auto& layer = layers.back();
layer.flag = static_cast<LayerFlags>(stream.read<uint16_t>(Endian::Little)); layer.flag = static_cast<LayerFlags>(stream.read<u16>(Endian::Little));
layer.visible = ((int)layer.flag & (int)LayerFlags::Visible) == (int)LayerFlags::Visible; layer.visible = ((int)layer.flag & (int)LayerFlags::Visible) == (int)LayerFlags::Visible;
layer.type = static_cast<LayerTypes>(stream.read<uint16_t>(Endian::Little)); layer.type = static_cast<LayerTypes>(stream.read<u16>(Endian::Little));
layer.child_level = stream.read<uint16_t>(Endian::Little); layer.child_level = stream.read<u16>(Endian::Little);
stream.read<uint16_t>(Endian::Little); // width stream.read<u16>(Endian::Little); // width
stream.read<uint16_t>(Endian::Little); // height stream.read<u16>(Endian::Little); // height
layer.blendmode = stream.read<uint16_t>(Endian::Little); layer.blendmode = stream.read<u16>(Endian::Little);
layer.alpha = stream.read<uint8_t>(Endian::Little); layer.alpha = stream.read<u8>(Endian::Little);
stream.seek(stream.position() + 3); // for future stream.seek(stream.position() + 3); // for future
layer.name.set_length(stream.read<uint16_t>(Endian::Little)); layer.name.set_length(stream.read<u16>(Endian::Little));
stream.read(layer.name.cstr(), layer.name.length()); stream.read(layer.name.cstr(), layer.name.length());
layer.userdata.color = 0xffffff; layer.userdata.color = 0xffffff;
@ -213,20 +209,20 @@ void Aseprite::parse_cel(Stream& stream, int frameIndex, size_t maxPosition)
frame.cels.emplace_back(); frame.cels.emplace_back();
auto& cel = frame.cels.back(); auto& cel = frame.cels.back();
cel.layer_index = stream.read<uint16_t>(Endian::Little); cel.layer_index = stream.read<u16>(Endian::Little);
cel.x = stream.read<uint16_t>(Endian::Little); cel.x = stream.read<u16>(Endian::Little);
cel.y = stream.read<uint16_t>(Endian::Little); cel.y = stream.read<u16>(Endian::Little);
cel.alpha = stream.read<uint8_t>(Endian::Little); cel.alpha = stream.read<u8>(Endian::Little);
cel.linked_frame_index = -1; cel.linked_frame_index = -1;
auto celType = stream.read<uint16_t>(Endian::Little); auto celType = stream.read<u16>(Endian::Little);
stream.seek(stream.position() + 7); stream.seek(stream.position() + 7);
// RAW or DEFLATE // RAW or DEFLATE
if (celType == 0 || celType == 2) if (celType == 0 || celType == 2)
{ {
auto width = stream.read<uint16_t>(Endian::Little); auto width = stream.read<u16>(Endian::Little);
auto height = stream.read<uint16_t>(Endian::Little); auto height = stream.read<u16>(Endian::Little);
auto count = width * height * (int)mode; auto count = width * height * (int)mode;
cel.image = Image(width, height); cel.image = Image(width, height);
@ -282,7 +278,7 @@ void Aseprite::parse_cel(Stream& stream, int frameIndex, size_t maxPosition)
// this cel directly references a previous cel // this cel directly references a previous cel
else if (celType == 1) else if (celType == 1)
{ {
cel.linked_frame_index = stream.read<uint16_t>(Endian::Little); cel.linked_frame_index = stream.read<u16>(Endian::Little);
} }
// draw to frame if visible // draw to frame if visible
@ -298,21 +294,21 @@ void Aseprite::parse_cel(Stream& stream, int frameIndex, size_t maxPosition)
void Aseprite::parse_palette(Stream& stream, int frame) void Aseprite::parse_palette(Stream& stream, int frame)
{ {
/* size */ stream.read<uint32_t>(Endian::Little); /* size */ stream.read<u32>(Endian::Little);
auto start = stream.read<uint32_t>(Endian::Little); auto start = stream.read<u32>(Endian::Little);
auto end = stream.read<uint32_t>(Endian::Little); auto end = stream.read<u32>(Endian::Little);
stream.seek(stream.position() + 8); stream.seek(stream.position() + 8);
palette.resize(palette.size() + (end - start + 1)); palette.resize(palette.size() + (end - start + 1));
for (int p = 0, len = static_cast<int>(end - start) + 1; p < len; p++) for (int p = 0, len = static_cast<int>(end - start) + 1; p < len; p++)
{ {
auto hasName = stream.read<uint16_t>(Endian::Little); auto hasName = stream.read<u16>(Endian::Little);
palette[start + p] = stream.read<uint32_t>(Endian::Little); palette[start + p] = stream.read<u32>(Endian::Little);
if (hasName & 0xF000) if (hasName & 0xF000)
{ {
int len = stream.read<uint16_t>(Endian::Little); int len = stream.read<u16>(Endian::Little);
stream.seek(stream.position() + len); stream.seek(stream.position() + len);
} }
} }
@ -322,38 +318,38 @@ void Aseprite::parse_user_data(Stream& stream, int frame)
{ {
if (m_last_userdata != nullptr) if (m_last_userdata != nullptr)
{ {
auto flags = stream.read<uint32_t>(Endian::Little); auto flags = stream.read<u32>(Endian::Little);
// has text // has text
if (flags & (1 << 0)) if (flags & (1 << 0))
{ {
m_last_userdata->text.set_length(stream.read<uint16_t>(Endian::Little)); m_last_userdata->text.set_length(stream.read<u16>(Endian::Little));
stream.read(m_last_userdata->text.cstr(), m_last_userdata->text.length()); stream.read(m_last_userdata->text.cstr(), m_last_userdata->text.length());
} }
// has color // has color
if (flags & (1 << 1)) if (flags & (1 << 1))
m_last_userdata->color = stream.read<uint32_t>(Endian::Little); m_last_userdata->color = stream.read<u32>(Endian::Little);
} }
} }
void Aseprite::parse_tag(Stream& stream, int frame) void Aseprite::parse_tag(Stream& stream, int frame)
{ {
auto count = stream.read<uint16_t>(Endian::Little); auto count = stream.read<u16>(Endian::Little);
stream.seek(stream.position() + 8); stream.seek(stream.position() + 8);
for (int t = 0; t < count; t++) for (int t = 0; t < count; t++)
{ {
Tag tag; Tag tag;
tag.from = stream.read<uint16_t>(Endian::Little); tag.from = stream.read<u16>(Endian::Little);
tag.to = stream.read<uint16_t>(Endian::Little); tag.to = stream.read<u16>(Endian::Little);
tag.loops = static_cast<LoopDirections>(stream.read<int8_t>(Endian::Little)); tag.loops = static_cast<LoopDirections>(stream.read<i8>(Endian::Little));
stream.seek(stream.position() + 8); stream.seek(stream.position() + 8);
tag.color = Color(stream.read<int8_t>(), stream.read<int8_t>(), stream.read<int8_t>(Endian::Little), 255); tag.color = Color(stream.read<i8>(), stream.read<i8>(), stream.read<i8>(Endian::Little), 255);
stream.seek(stream.position() + 1); stream.seek(stream.position() + 1);
tag.name.set_length(stream.read<uint16_t>(Endian::Little)); tag.name.set_length(stream.read<u16>(Endian::Little));
stream.read(tag.name.cstr(), tag.name.length()); stream.read(tag.name.cstr(), tag.name.length());
tags.push_back(tag); tags.push_back(tag);
@ -362,12 +358,12 @@ void Aseprite::parse_tag(Stream& stream, int frame)
void Aseprite::parse_slice(Stream& stream, int frame) void Aseprite::parse_slice(Stream& stream, int frame)
{ {
int count = stream.read<uint32_t>(Endian::Little); int count = stream.read<u32>(Endian::Little);
int flags = stream.read<uint32_t>(Endian::Little); int flags = stream.read<u32>(Endian::Little);
stream.read<uint32_t>(Endian::Little); // reserved stream.read<u32>(Endian::Little); // reserved
String name; String name;
name.set_length(stream.read<uint16_t>(Endian::Little)); name.set_length(stream.read<u16>(Endian::Little));
stream.read(name.cstr(), name.length()); stream.read(name.cstr(), name.length());
for (int s = 0; s < count; s++) for (int s = 0; s < count; s++)
@ -376,19 +372,19 @@ void Aseprite::parse_slice(Stream& stream, int frame)
auto& slice = slices.back(); auto& slice = slices.back();
slice.name = name; slice.name = name;
slice.frame = stream.read<uint32_t>(Endian::Little); slice.frame = stream.read<u32>(Endian::Little);
slice.origin.x = stream.read<int32_t>(Endian::Little); slice.origin.x = stream.read<i32>(Endian::Little);
slice.origin.y = stream.read<int32_t>(Endian::Little); slice.origin.y = stream.read<i32>(Endian::Little);
slice.width = stream.read<uint32_t>(Endian::Little); slice.width = stream.read<u32>(Endian::Little);
slice.height = stream.read<uint32_t>(Endian::Little); slice.height = stream.read<u32>(Endian::Little);
// 9 slice (ignored atm) // 9 slice (ignored atm)
if (flags & (1 << 0)) if (flags & (1 << 0))
{ {
stream.read<int32_t>(Endian::Little); stream.read<i32>(Endian::Little);
stream.read<int32_t>(Endian::Little); stream.read<i32>(Endian::Little);
stream.read<uint32_t>(Endian::Little); stream.read<u32>(Endian::Little);
stream.read<uint32_t>(Endian::Little); stream.read<u32>(Endian::Little);
} }
// pivot point // pivot point
@ -396,8 +392,8 @@ void Aseprite::parse_slice(Stream& stream, int frame)
if (flags & (1 << 1)) if (flags & (1 << 1))
{ {
slice.has_pivot = true; slice.has_pivot = true;
slice.pivot.x = stream.read<uint32_t>(Endian::Little); slice.pivot.x = stream.read<u32>(Endian::Little);
slice.pivot.y = stream.read<uint32_t>(Endian::Little); slice.pivot.y = stream.read<u32>(Endian::Little);
} }
slice.userdata.color = 0xffffff; slice.userdata.color = 0xffffff;
@ -406,6 +402,9 @@ void Aseprite::parse_slice(Stream& stream, int frame)
} }
} }
#define MUL_UN8(a, b, t) \
((t) = (a) * (u16)(b) + 0x80, ((((t) >> 8) + (t) ) >> 8))
void Aseprite::render_cel(Cel* cel, Frame* frame) void Aseprite::render_cel(Cel* cel, Frame* frame)
{ {
Layer& layer = layers[cel->layer_index]; Layer& layer = layers[cel->layer_index];
@ -436,16 +435,16 @@ void Aseprite::render_cel(Cel* cel, Frame* frame)
auto dstH = frame->image.height; auto dstH = frame->image.height;
// blit pixels // blit pixels
int left = MAX(0, srcX); int left = Calc::max(0, srcX);
int right = MIN(dstW, srcX + srcW); int right = Calc::min(dstW, srcX + srcW);
int top = MAX(0, srcY); int top = Calc::max(0, srcY);
int bottom = MIN(dstH, srcY + srcH); int bottom = Calc::min(dstH, srcY + srcH);
if (layer.blendmode == 0) if (layer.blendmode == 0)
{ {
for (int dx = left, sx = -MIN(srcX, 0); dx < right; dx++, sx++) for (int dx = left, sx = -Calc::min(srcX, 0); dx < right; dx++, sx++)
{ {
for (int dy = top, sy = -MIN(srcY, 0); dy < bottom; dy++, sy++) for (int dy = top, sy = -Calc::min(srcY, 0); dy < bottom; dy++, sy++)
{ {
Color* srcColor = (src + sx + sy * srcW); Color* srcColor = (src + sx + sy * srcW);
Color* dstColor = (dst + dx + dy * dstW); Color* dstColor = (dst + dx + dy * dstW);

View File

@ -1,20 +1,31 @@
#include <blah/images/font.h> #include <blah/images/font.h>
#include <blah/streams/filestream.h> #include <blah/streams/filestream.h>
#include <blah/math/calc.h> #include <blah/math/calc.h>
#include <blah/core/log.h> #include <blah/core/common.h>
using namespace Blah; using namespace Blah;
#ifdef __CLANG__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
#define STBTT_STATIC #define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION
#include "../third_party/stb_truetype.h" #include "../third_party/stb_truetype.h"
String GetName(stbtt_fontinfo* font, int nameId) #ifdef __CLANG__
#pragma clang diagnostic pop
#endif
namespace
{
String get_font_name(stbtt_fontinfo* font, int nameId)
{ {
int length = 0; int length = 0;
// get the name // get the name
const uint16_t* ptr = (const uint16_t*)stbtt_GetFontNameStr(font, &length, const u16* ptr = (const u16*)stbtt_GetFontNameStr(font, &length,
STBTT_PLATFORM_ID_MICROSOFT, STBTT_PLATFORM_ID_MICROSOFT,
STBTT_MS_EID_UNICODE_BMP, STBTT_MS_EID_UNICODE_BMP,
STBTT_MS_LANG_ENGLISH, STBTT_MS_LANG_ENGLISH,
@ -28,6 +39,7 @@ String GetName(stbtt_fontinfo* font, int nameId)
str.append_utf16(ptr, ptr + length, Calc::is_little_endian()); str.append_utf16(ptr, ptr + length, Calc::is_little_endian());
return str; return str;
} }
}
Font::Font() Font::Font()
{ {
@ -44,7 +56,7 @@ Font::Font(Stream& stream) : Font()
load(stream); load(stream);
} }
Font::Font(const char* path) : Font() Font::Font(const FilePath& path) : Font()
{ {
FileStream fs(path, FileMode::Read); FileStream fs(path, FileMode::Read);
if (fs.is_readable()) if (fs.is_readable())
@ -112,8 +124,8 @@ void Font::load(Stream& stream)
m_font = new stbtt_fontinfo(); m_font = new stbtt_fontinfo();
auto fn = (stbtt_fontinfo*)m_font; auto fn = (stbtt_fontinfo*)m_font;
stbtt_InitFont(fn, m_data, 0); stbtt_InitFont(fn, m_data, 0);
m_family_name = GetName(fn, 1); m_family_name = get_font_name(fn, 1);
m_style_name = GetName(fn, 2); m_style_name = get_font_name(fn, 2);
// properties // properties
stbtt_GetFontVMetrics(fn, &m_ascent, &m_descent, &m_line_gap); stbtt_GetFontVMetrics(fn, &m_ascent, &m_descent, &m_line_gap);
@ -130,14 +142,14 @@ void Font::dispose()
m_style_name.dispose(); m_style_name.dispose();
} }
const char* Font::family_name() const const String& Font::family_name() const
{ {
return m_family_name.cstr(); return m_family_name;
} }
const char* Font::style_name() const const String& Font::style_name() const
{ {
return m_style_name.cstr(); return m_style_name;
} }
int Font::ascent() const int Font::ascent() const
@ -187,9 +199,9 @@ float Font::get_kerning(int glyph1, int glyph2, float scale) const
return stbtt_GetGlyphKernAdvance((stbtt_fontinfo*)m_font, glyph1, glyph2) * scale; return stbtt_GetGlyphKernAdvance((stbtt_fontinfo*)m_font, glyph1, glyph2) * scale;
} }
Font::Char Font::get_character(int glyph, float scale) const Font::Character Font::get_character(int glyph, float scale) const
{ {
Char ch; Character ch;
if (!m_font) if (!m_font)
return ch; return ch;
@ -215,7 +227,7 @@ Font::Char Font::get_character(int glyph, float scale) const
return ch; return ch;
} }
bool Font::get_image(const Font::Char& ch, Color* pixels) const bool Font::get_image(const Font::Character& ch, Color* pixels) const
{ {
if (ch.has_glyph) if (ch.has_glyph)
{ {
@ -239,6 +251,16 @@ bool Font::get_image(const Font::Char& ch, Color* pixels) const
return false; return false;
} }
Image Font::get_image(const Font::Character& ch) const
{
Image img(ch.width, ch.height);
if (get_image(ch, img.pixels))
return img;
return Image();
}
bool Font::is_valid() const bool Font::is_valid() const
{ {
return m_valid; return m_valid;

View File

@ -1,14 +1,13 @@
#include <blah/images/image.h> #include <blah/images/image.h>
#include <blah/streams/stream.h> #include <blah/streams/stream.h>
#include <blah/streams/filestream.h> #include <blah/streams/filestream.h>
#include <blah/core/log.h> #include <blah/core/common.h>
using namespace Blah; using namespace Blah;
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_JPEG #define STBI_ONLY_JPEG
#define STBI_ONLY_PNG #define STBI_ONLY_PNG
#define STBI_ONLY_BMP
#include "../third_party/stb_image.h" #include "../third_party/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
@ -18,7 +17,7 @@ namespace
{ {
int Blah_STBI_Read(void* user, char* data, int size) int Blah_STBI_Read(void* user, char* data, int size)
{ {
int64_t read = ((Stream*)user)->read(data, size); i64 read = ((Stream*)user)->read(data, size);
return (int)read; return (int)read;
} }
@ -29,8 +28,8 @@ namespace
int Blah_STBI_Eof(void* user) int Blah_STBI_Eof(void* user)
{ {
int64_t position = ((Stream*)user)->position(); i64 position = ((Stream*)user)->position();
int64_t length = ((Stream*)user)->length(); i64 length = ((Stream*)user)->length();
if (position >= length) if (position >= length)
return 1; return 1;
@ -59,7 +58,7 @@ Image::Image(Stream& stream)
from_stream(stream); from_stream(stream);
} }
Image::Image(const char* file) Image::Image(const FilePath& file)
{ {
width = height = 0; width = height = 0;
pixels = nullptr; pixels = nullptr;
@ -155,7 +154,7 @@ void Image::from_stream(Stream& stream)
callbacks.skip = Blah_STBI_Skip; callbacks.skip = Blah_STBI_Skip;
int x, y, comps; int x, y, comps;
uint8_t* data = stbi_load_from_callbacks(&callbacks, &stream, &x, &y, &comps, 4); u8* data = stbi_load_from_callbacks(&callbacks, &stream, &x, &y, &comps, 4);
if (data == nullptr) if (data == nullptr)
{ {
@ -175,6 +174,7 @@ void Image::dispose()
stbi_image_free(pixels); stbi_image_free(pixels);
else else
delete[] pixels; delete[] pixels;
pixels = nullptr; pixels = nullptr;
width = height = 0; width = height = 0;
m_stbi_ownership = false; m_stbi_ownership = false;
@ -186,9 +186,9 @@ void Image::premultiply()
{ {
for (int n = 0; n < width * height; n ++) for (int n = 0; n < width * height; n ++)
{ {
pixels[n].r = (uint8_t)(pixels[n].r * pixels[n].a / 255); pixels[n].r = (u8)(pixels[n].r * pixels[n].a / 255);
pixels[n].g = (uint8_t)(pixels[n].g * pixels[n].a / 255); pixels[n].g = (u8)(pixels[n].g * pixels[n].a / 255);
pixels[n].b = (uint8_t)(pixels[n].b * pixels[n].a / 255); pixels[n].b = (u8)(pixels[n].b * pixels[n].a / 255);
} }
} }
} }
@ -203,7 +203,7 @@ void Image::set_pixels(const RectI& rect, Color* data)
} }
} }
bool Image::save_png(const char* file) const bool Image::save_png(const FilePath& file) const
{ {
FileStream fs(file, FileMode::Write); FileStream fs(file, FileMode::Write);
return save_png(fs); return save_png(fs);
@ -232,7 +232,7 @@ bool Image::save_png(Stream& stream) const
return false; return false;
} }
bool Image::save_jpg(const char* file, int quality) const bool Image::save_jpg(const FilePath& file, int quality) const
{ {
FileStream fs(file, FileMode::Write); FileStream fs(file, FileMode::Write);
return save_jpg(fs, quality); return save_jpg(fs, quality);
@ -269,33 +269,31 @@ bool Image::save_jpg(Stream& stream, int quality) const
return false; return false;
} }
void Image::get_pixels(Color* dest, const Point& dest_pos, const Point& dest_size, RectI source_rect)
void Image::get_pixels(Color* dest, const Point& destPos, const Point& destSize, RectI sourceRect)
{ {
// can't be outside of the source image // can't be outside of the source image
if (sourceRect.x < 0) sourceRect.x = 0; if (source_rect.x < 0) source_rect.x = 0;
if (sourceRect.y < 0) sourceRect.y = 0; if (source_rect.y < 0) source_rect.y = 0;
if (sourceRect.x + sourceRect.w > width) sourceRect.w = width - sourceRect.x; if (source_rect.x + source_rect.w > width) source_rect.w = width - source_rect.x;
if (sourceRect.y + sourceRect.h > height) sourceRect.h = height - sourceRect.y; if (source_rect.y + source_rect.h > height) source_rect.h = height - source_rect.y;
// can't be larger than our destination // can't be larger than our destination
if (sourceRect.w > destSize.x - destPos.x) if (source_rect.w > dest_size.x - dest_pos.x)
sourceRect.w = destSize.x - destPos.x; source_rect.w = dest_size.x - dest_pos.x;
if (sourceRect.h > destSize.y - destPos.y) if (source_rect.h > dest_size.y - dest_pos.y)
sourceRect.h = destSize.y - destPos.y; source_rect.h = dest_size.y - dest_pos.y;
for (int y = 0; y < sourceRect.h; y++) for (int y = 0; y < source_rect.h; y++)
{ {
int to = destPos.x + (destPos.y + y) * destSize.x; int to = dest_pos.x + (dest_pos.y + y) * dest_size.x;
int from = sourceRect.x + (sourceRect.y + y) * width; int from = source_rect.x + (source_rect.y + y) * width;
memcpy(dest + to, pixels + from, sizeof(Color) * (int)sourceRect.w); memcpy(dest + to, pixels + from, sizeof(Color) * (int)source_rect.w);
} }
} }
Image Image::get_sub_image(const RectI& sourceRect) Image Image::get_sub_image(const RectI& source_rect)
{ {
Image img(sourceRect.w, sourceRect.h); Image img(source_rect.w, source_rect.h);
get_pixels(img.pixels, Point::zero, Point(img.width, img.height), sourceRect); get_pixels(img.pixels, Point::zero, Point(img.width, img.height), source_rect);
return img; return img;
} }

View File

@ -1,5 +1,5 @@
#include <blah/images/packer.h> #include <blah/images/packer.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
@ -19,7 +19,7 @@ Packer::Packer(Packer&& src) noexcept
padding = src.padding; padding = src.padding;
m_dirty = src.m_dirty; m_dirty = src.m_dirty;
pages = std::move(src.pages); pages = std::move(src.pages);
entries = std::move(src.entries); m_entries = std::move(src.m_entries);
m_buffer = std::move(src.m_buffer); m_buffer = std::move(src.m_buffer);
} }
@ -31,7 +31,7 @@ Packer& Packer::operator=(Packer&& src) noexcept
padding = src.padding; padding = src.padding;
m_dirty = src.m_dirty; m_dirty = src.m_dirty;
pages = std::move(src.pages); pages = std::move(src.pages);
entries = std::move(src.entries); m_entries = std::move(src.m_entries);
m_buffer = std::move(src.m_buffer); m_buffer = std::move(src.m_buffer);
return *this; return *this;
} }
@ -41,22 +41,22 @@ Packer::~Packer()
dispose(); dispose();
} }
void Packer::add(uint64_t id, int width, int height, const Color* pixels) void Packer::add(u64 id, int width, int height, const Color* pixels)
{ {
add_entry(id, width, height, pixels); add_entry(id, width, height, pixels);
} }
void Packer::add(uint64_t id, const Image& image) void Packer::add(u64 id, const Image& image)
{ {
add_entry(id, image.width, image.height, image.pixels); add_entry(id, image.width, image.height, image.pixels);
} }
void Packer::add(uint64_t id, const String& path) void Packer::add(u64 id, const FilePath& path)
{ {
add(id, Image(path.cstr())); add(id, Image(path.cstr()));
} }
void Packer::add_entry(uint64_t id, int w, int h, const Color* pixels) void Packer::add_entry(u64 id, int w, int h, const Color* pixels)
{ {
m_dirty = true; m_dirty = true;
@ -125,7 +125,12 @@ void Packer::add_entry(uint64_t id, int w, int h, const Color* pixels)
} }
} }
entries.push_back(entry); m_entries.push_back(entry);
}
const Vector<Packer::Entry>& Packer::entries() const
{
return m_entries;
} }
void Packer::pack() void Packer::pack()
@ -137,7 +142,7 @@ void Packer::pack()
pages.clear(); pages.clear();
// only if we have stuff to pack // only if we have stuff to pack
auto count = entries.size(); auto count = m_entries.size();
if (count > 0) if (count > 0)
{ {
// get all the sources sorted largest -> smallest // get all the sources sorted largest -> smallest
@ -146,8 +151,8 @@ void Packer::pack()
sources.resize(count); sources.resize(count);
int index = 0; int index = 0;
for (int i = 0; i < entries.size(); i++) for (int i = 0; i < m_entries.size(); i++)
sources[index++] = &entries[i]; sources[index++] = &m_entries[i];
std::sort(sources.begin(), sources.end(), [](Packer::Entry* a, Packer::Entry* b) std::sort(sources.begin(), sources.end(), [](Packer::Entry* a, Packer::Entry* b)
{ {
@ -292,14 +297,14 @@ void Packer::pack()
void Packer::clear() void Packer::clear()
{ {
pages.clear(); pages.clear();
entries.clear(); m_entries.clear();
m_dirty = false; m_dirty = false;
} }
void Packer::dispose() void Packer::dispose()
{ {
pages.clear(); pages.clear();
entries.clear(); m_entries.clear();
m_buffer.close(); m_buffer.close();
max_size = 0; max_size = 0;
power_of_two = 0; power_of_two = 0;

469
src/input/binding.cpp Normal file
View File

@ -0,0 +1,469 @@
#include <blah/input/binding.h>
#include <blah/math/calc.h>
#include <blah/core/time.h>
using namespace Blah;
Binding::TriggerBind::TriggerBind(Axis axis)
: axis(axis)
{
}
Binding::TriggerBind::TriggerBind(int controller, Axis axis, float threshold, bool positive)
: controller(controller), axis(axis), threshold(threshold), positive(positive)
{
}
bool Binding::TriggerBind::is_down(float axis_value) const
{
if ((axis_value > 0 && positive) || (axis_value < 0 && !positive))
{
if (Calc::abs(axis_value) >= threshold)
return true;
}
return false;
}
Binding::ButtonBind::ButtonBind(Button button)
: button(button) {}
Binding::ButtonBind::ButtonBind(int controller, Button button)
: controller(controller), button(button) {}
bool Binding::pressed() const
{
if (m_press_consumed)
return false;
if (m_last_press_time >= 0 && (Time::seconds - m_last_press_time) <= press_buffer)
return true;
return m_pressed;
}
bool Binding::released() const
{
if (m_release_consumed)
return false;
if (m_last_release_time >= 0 && (Time::seconds - m_last_release_time) <= release_buffer)
return true;
return m_released;
}
bool Binding::down() const
{
return m_down;
}
float Binding::value() const
{
return m_value;
}
int Binding::sign() const
{
return (int)Calc::sign(m_value);
}
double Binding::timestamp() const
{
return m_last_timestamp;
}
void Binding::update()
{
m_press_consumed = false;
m_release_consumed = false;
if (get_pressed())
{
m_last_timestamp = Time::seconds;
m_last_press_time = Time::seconds;
m_pressed = true;
}
else
{
m_pressed = false;
}
if (get_released())
{
m_last_release_time = Time::seconds;
m_released = true;
}
else
{
m_released = false;
}
m_down = get_down();
m_value = get_value();
}
void Binding::consume_press()
{
m_press_consumed = true;
m_last_press_time = -1;
}
void Binding::consume_release()
{
m_release_consumed = true;
m_last_release_time = -1;
}
Binding& Binding::add(Key key)
{
keys.push_back(key);
return *this;
}
Binding& Binding::add(ButtonBind button)
{
buttons.push_back(button);
return *this;
}
Binding& Binding::add(TriggerBind trigger)
{
triggers.push_back(trigger);
return *this;
}
Binding& Binding::add(MouseButton button)
{
mouse.push_back(button);
return *this;
}
Binding& Binding::add_left_trigger(int controller, float threshold)
{
triggers.push_back(TriggerBind(controller, Axis::LeftTrigger, threshold, true));
return *this;
}
Binding& Binding::add_right_trigger(int controller, float threshold)
{
triggers.push_back(TriggerBind(controller, Axis::RightTrigger, threshold, true));
return *this;
}
Binding& Binding::set_controller(int index)
{
for (auto& it : buttons)
it.controller = index;
for (auto& it : triggers)
it.controller = index;
return *this;
}
void Binding::clear()
{
keys.clear();
buttons.clear();
triggers.clear();
mouse.clear();
}
bool Binding::get_pressed() const
{
for (auto& it : keys)
if (Input::pressed(it))
return true;
for (auto& it : mouse)
if (Input::pressed(it))
return true;
for (auto& it : buttons)
if (Input::pressed(it.controller, it.button))
return true;
for (auto& it : triggers)
{
if (it.controller < 0 || it.controller >= Input::max_controllers)
continue;
if ((int)it.axis < 0 || (int)it.axis >= Input::max_controller_axis)
continue;
if (it.is_down(Input::state()->controllers[it.controller].axis[(int)it.axis]) &&
!it.is_down(Input::last_state()->controllers[it.controller].axis[(int)it.axis]))
return true;
}
return false;
}
bool Binding::get_released() const
{
for (auto& it : keys)
if (Input::released(it))
return true;
for (auto& it : mouse)
if (Input::released(it))
return true;
for (auto& it : buttons)
if (Input::released(it.controller, it.button))
return true;
for (auto& it : triggers)
{
if (it.controller < 0 || it.controller >= Input::max_controllers)
continue;
if ((int)it.axis < 0 || (int)it.axis >= Input::max_controller_axis)
continue;
if (!it.is_down(Input::state()->controllers[it.controller].axis[(int)it.axis]) &&
it.is_down(Input::last_state()->controllers[it.controller].axis[(int)it.axis]))
return true;
}
return false;
}
bool Binding::get_down() const
{
for (auto& it : keys)
if (Input::down(it))
return true;
for (auto& it : mouse)
if (Input::down(it))
return true;
for (auto& it : buttons)
if (Input::down(it.controller, it.button))
return true;
for (auto& it : triggers)
{
if (it.controller < 0 || it.controller >= Input::max_controllers)
continue;
if ((int)it.axis < 0 || (int)it.axis >= Input::max_controller_axis)
continue;
if (it.is_down(Input::state()->controllers[it.controller].axis[(int)it.axis]))
return true;
}
return false;
}
float Binding::get_value() const
{
for (auto& it : keys)
if (Input::down(it))
return 1.0f;
for (auto& it : mouse)
if (Input::down(it))
return 1.0f;
for (auto& it : buttons)
if (Input::down(it.controller, it.button))
return 1.0f;
float highest = 0;
for (auto& it : triggers)
{
if (it.controller < 0 || it.controller >= Input::max_controllers)
continue;
if ((int)it.axis < 0 || (int)it.axis >= Input::max_controller_axis)
continue;
float raw_value = Input::state()->controllers[it.controller].axis[(int)it.axis];
if (it.is_down(raw_value))
{
float mapped_value = Calc::clamped_map(Calc::abs(raw_value), it.threshold, 1.0f, 0.0f, 1.0f);
if (mapped_value > highest)
highest = mapped_value;
}
}
return highest;
}
float AxisBinding::value() const
{
float neg = negative.value();
float pos = positive.value();
// neither are down
if (neg <= 0 && pos <= 0)
return 0;
// negative-only is down
if (neg > 0 && pos <= 0)
return -neg;
// positive-only is down
if (pos > 0 && neg <= 0)
return pos;
// both are down:
// overlap cancel out
if (overlap == Overlap::Cancel)
return 0;
// overlap takes older
if (overlap == Overlap::Older)
{
if (negative.timestamp() < positive.timestamp())
return -neg;
else
return pos;
}
// overlap takes newer
if (negative.timestamp() > positive.timestamp())
return -neg;
else
return pos;
}
int AxisBinding::sign() const
{
return (int)Calc::sign(value());
}
void AxisBinding::update()
{
negative.update();
positive.update();
}
void AxisBinding::consume_press()
{
negative.consume_press();
positive.consume_press();
}
void AxisBinding::consume_release()
{
negative.consume_release();
positive.consume_release();
}
AxisBinding& AxisBinding::add_left_stick_x(int controller, float threshold)
{
negative.add(Binding::TriggerBind(controller, Axis::LeftX, threshold, false));
positive.add(Binding::TriggerBind(controller, Axis::LeftX, threshold, true));
return *this;
}
AxisBinding& AxisBinding::add_left_stick_y(int controller, float threshold)
{
negative.add(Binding::TriggerBind(controller, Axis::LeftY, threshold, false));
positive.add(Binding::TriggerBind(controller, Axis::LeftY, threshold, true));
return *this;
}
AxisBinding& AxisBinding::add_right_stick_x(int controller, float threshold)
{
negative.add(Binding::TriggerBind(controller, Axis::RightX, threshold, false));
positive.add(Binding::TriggerBind(controller, Axis::RightX, threshold, true));
return *this;
}
AxisBinding& AxisBinding::add_right_stick_y(int controller, float threshold)
{
negative.add(Binding::TriggerBind(controller, Axis::RightY, threshold, false));
positive.add(Binding::TriggerBind(controller, Axis::RightY, threshold, true));
return *this;
}
AxisBinding& AxisBinding::set_controller(int index)
{
negative.set_controller(index);
positive.set_controller(index);
return *this;
}
void AxisBinding::clear()
{
negative.clear();
positive.clear();
}
Vec2 StickBinding::value() const
{
Vec2 result = Vec2(x.value(), y.value());
if (round_threshold > 0 && result.length() < round_threshold)
return Vec2::zero;
return result;
}
Point StickBinding::sign() const
{
Vec2 result = value();
return Point((int)Calc::sign(result.x), (int)Calc::sign(result.y));
}
void StickBinding::update()
{
x.update();
y.update();
}
void StickBinding::consume_press()
{
x.consume_press();
y.consume_press();
}
void StickBinding::consume_release()
{
x.consume_release();
y.consume_release();
}
StickBinding& StickBinding::add_dpad(int controller)
{
x.negative.add(Binding::ButtonBind(controller, Button::Left));
x.positive.add(Binding::ButtonBind(controller, Button::Right));
y.negative.add(Binding::ButtonBind(controller, Button::Up));
y.positive.add(Binding::ButtonBind(controller, Button::Down));
return *this;
}
StickBinding& StickBinding::add_left_stick(int controller, float threshold)
{
x.add_left_stick_x(controller, threshold);
y.add_left_stick_y(controller, threshold);
return *this;
}
StickBinding& StickBinding::add_right_stick(int controller, float threshold)
{
x.add_right_stick_x(controller, threshold);
y.add_right_stick_y(controller, threshold);
return *this;
}
StickBinding& StickBinding::set_controller(int index)
{
x.set_controller(index);
y.set_controller(index);
return *this;
}
void StickBinding::clear()
{
x.clear();
y.clear();
}

View File

@ -0,0 +1,70 @@
#include <blah/input/binding_registry.h>
using namespace Blah;
Vector<std::weak_ptr<Binding>> BindingRegistry::bindings;
Vector<std::weak_ptr<AxisBinding>> BindingRegistry::axes;
Vector<std::weak_ptr<StickBinding>> BindingRegistry::sticks;
BindingRef BindingRegistry::register_binding(const Binding& binding)
{
auto result = std::make_shared<Binding>(binding);
bindings.push_back(std::weak_ptr<Binding>(result));
return result;
}
AxisBindingRef BindingRegistry::register_axis(const AxisBinding& binding)
{
auto result = std::make_shared<AxisBinding>(binding);
axes.push_back(std::weak_ptr<AxisBinding>(result));
return result;
}
StickBindingRef BindingRegistry::register_stick(const StickBinding& binding)
{
auto result = std::make_shared<StickBinding>(binding);
sticks.push_back(std::weak_ptr<StickBinding>(result));
return result;
}
void BindingRegistry::update()
{
for (int i = 0; i < bindings.size(); i++)
{
if (bindings[i].use_count() <= 0)
{
bindings.erase(i);
i--;
}
else if (auto binding = bindings[i].lock())
{
binding->update();
}
}
for (int i = 0; i < axes.size(); i++)
{
if (axes[i].use_count() <= 0)
{
axes.erase(i);
i--;
}
else if (auto binding = axes[i].lock())
{
binding->update();
}
}
for (int i = 0; i < sticks.size(); i++)
{
if (sticks[i].use_count() <= 0)
{
sticks.erase(i);
i--;
}
else if (auto binding = sticks[i].lock())
{
binding->update();
}
}
}

View File

@ -1,7 +1,8 @@
#include <blah/input/input.h> #include <blah/input/input.h>
#include <blah/input/binding_registry.h>
#include <blah/core/app.h> #include <blah/core/app.h>
#include <blah/core/time.h> #include <blah/core/time.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include <blah/math/point.h> #include <blah/math/point.h>
#include "../internal/input_backend.h" #include "../internal/input_backend.h"
#include <string.h> #include <string.h>
@ -47,10 +48,9 @@ void InputBackend::frame()
g_next_state.mouse.pressed[i] = false; g_next_state.mouse.pressed[i] = false;
g_next_state.mouse.released[i] = false; g_next_state.mouse.released[i] = false;
} }
g_next_state.mouse.wheel = Point::zero;
for (int i = 0; i < Blah::Input::max_text_input; i++) g_next_state.mouse.wheel = Point::zero;
g_next_state.keyboard.text[i] = 0; g_next_state.keyboard.text.clear();
for (int i = 0; i < Blah::Input::max_controllers; i++) for (int i = 0; i < Blah::Input::max_controllers; i++)
{ {
@ -66,6 +66,9 @@ void InputBackend::frame()
} }
} }
} }
// update bindings
BindingRegistry::update();
} }
void InputBackend::on_mouse_move(float x, float y) void InputBackend::on_mouse_move(float x, float y)
@ -135,10 +138,10 @@ void InputBackend::on_key_up(Key key)
void InputBackend::on_text_utf8(const char* text) void InputBackend::on_text_utf8(const char* text)
{ {
strncat(g_next_state.keyboard.text, text, Blah::Input::max_text_input); g_next_state.keyboard.text += text;
} }
void InputBackend::on_controller_connect(int index, const char* name, int is_gamepad, int button_count, int axis_count, uint16_t vendor, uint16_t product, uint16_t version) void InputBackend::on_controller_connect(int index, const char* name, int is_gamepad, int button_count, int axis_count, u16 vendor, u16 product, u16 version)
{ {
if (index < Blah::Input::max_controllers) if (index < Blah::Input::max_controllers)
{ {

View File

@ -1,166 +0,0 @@
#include <blah/input/virtual_axis.h>
#include <blah/core/time.h>
#include <blah/core/log.h>
using namespace Blah;
VirtualAxis& VirtualAxis::add_keys(Key negative, Key positive)
{
if (m_axes_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualAxis Keys out of bounds!");
else
{
m_keys[m_keys_len].init(negative, positive);
m_keys_len++;
}
return *this;
}
VirtualAxis& VirtualAxis::add_buttons(int gamepad_id, Button negative, Button positive)
{
if (m_axes_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualAxis Buttons out of bounds!");
else
{
m_buttons[m_buttons_len].init(gamepad_id, negative, positive);
m_buttons_len++;
}
return *this;
}
VirtualAxis& VirtualAxis::add_axis(int gamepad_id, Axis axis, float deadzone)
{
if (m_axes_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualAxis Axes out of bounds!");
else
{
m_axes[m_axes_len].init(gamepad_id, axis, deadzone);
m_axes_len++;
}
return *this;
}
VirtualAxis& VirtualAxis::repeat(float m_repeat_delay, float m_repeat_interval)
{
this->m_repeat_delay = m_repeat_delay;
this->m_repeat_interval = m_repeat_interval;
return *this;
}
VirtualAxis& VirtualAxis::press_buffer(float duration)
{
this->m_press_buffer = duration;
return *this;
}
VirtualAxis& VirtualAxis::release_buffer(float duration)
{
this->m_release_buffer = duration;
return *this;
}
void VirtualAxis::update()
{
m_last_value = m_value;
m_value = 0;
for (int i = 0; i < m_keys_len; i++)
{
m_keys[i].update();
if (m_value == 0)
m_value = (float)m_keys[i].value;
}
for (int i = 0; i < m_buttons_len; i++)
{
m_buttons[i].update();
if (m_value == 0)
m_value = (float)m_buttons[i].value;
}
for (int i = 0; i < m_axes_len; i++)
{
m_axes[i].update();
if (m_value == 0)
m_value = m_axes[i].value;
}
//Valuei
m_last_value_i = m_value_i;
if (m_value > 0)
m_value_i = 1;
else if (m_value < 0)
m_value_i = -1;
else
m_value_i = 0;
//pressed?
m_pressed = false;
if (m_value_i != 0 && m_last_value_i != m_value_i)
{
m_pressed = true;
m_last_press_time = m_repeat_press_time = Time::seconds;
}
else if (m_value_i == m_last_value_i && m_value_i != 0)
{
if (Time::seconds - m_last_press_time <= m_press_buffer)
m_pressed = true;
else if (m_repeat_interval > 0 && Time::seconds >= m_repeat_press_time + m_repeat_delay)
{
int prev = (int)((Time::previous_seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval);
int cur = (int)((Time::seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval);
m_pressed = prev < cur;
}
}
//released?
if (m_last_value_i != 0 && m_value_i != m_last_value_i)
{
m_released = true;
m_last_release_time = Time::seconds;
}
else if (Time::seconds - m_last_release_time <= m_release_buffer)
m_released = true;
else
m_released = false;
}
void VirtualAxis::KeysNode::init(Key negative, Key positive)
{
this->negative = negative;
this->positive = positive;
}
void VirtualAxis::KeysNode::update()
{
value = Input::axis_check(value, negative, positive);
}
void VirtualAxis::ButtonsNode::init(int gamepad_id, Button negative, Button positive)
{
this->gamepad_id = gamepad_id;
this->negative = negative;
this->positive = positive;
}
void VirtualAxis::ButtonsNode::update()
{
value = Input::axis_check(value, gamepad_id, negative, positive);
}
void VirtualAxis::AxisNode::init(int gamepad_id, Axis axis, float deadzone)
{
this->gamepad_id = gamepad_id;
this->axis = axis;
this->deadzone = deadzone;
}
void VirtualAxis::AxisNode::update()
{
value = Input::axis_check(gamepad_id, axis);
if (value < deadzone && value > -deadzone)
value = 0;
}

View File

@ -1,174 +0,0 @@
#include <blah/input/virtual_button.h>
#include <blah/core/time.h>
#include <blah/core/log.h>
using namespace Blah;
VirtualButton& VirtualButton::add_key(Key key)
{
if (m_keys_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualButton Keys out of bounds!");
else
{
m_keys[m_keys_len].init(key);
m_keys_len++;
}
return *this;
}
VirtualButton& VirtualButton::add_button(int gamepad_id, Button button)
{
if (m_buttons_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualButton Buttons out of bounds!");
else
{
m_buttons[m_buttons_len].init(gamepad_id, button);
m_buttons_len++;
}
return *this;
}
VirtualButton& VirtualButton::add_axis(int gamepad_id, Axis axis, float threshold, bool greater_than)
{
if (m_axes_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualButton Axes out of bounds!");
else
{
m_axes[m_axes_len].init(gamepad_id, axis, threshold, greater_than);
m_axes_len++;
}
return *this;
}
VirtualButton& VirtualButton::repeat(float m_repeat_delay, float m_repeat_interval)
{
this->m_repeat_delay = m_repeat_delay;
this->m_repeat_interval = m_repeat_interval;
return *this;
}
VirtualButton& VirtualButton::press_buffer(float duration)
{
this->m_press_buffer = duration;
return *this;
}
VirtualButton& VirtualButton::release_buffer(float duration)
{
this->m_release_buffer = duration;
return *this;
}
void VirtualButton::update()
{
m_down = false;
m_pressed = false;
m_released = false;
// Keys
for (int i = 0; i < m_keys_len; i++)
{
m_keys[i].update();
m_down = m_down || m_keys[i].down;
m_pressed = m_pressed || m_keys[i].pressed;
m_released = m_released || m_keys[i].released;
}
// Buttons
for (int i = 0; i < m_buttons_len; i++)
{
m_buttons[i].update();
m_down = m_down || m_buttons[i].down;
m_pressed = m_pressed || m_buttons[i].pressed;
m_released = m_released || m_buttons[i].released;
}
// Axes
for (int i = 0; i < m_axes_len; i++)
{
m_axes[i].update();
m_down = m_down || m_axes[i].down;
m_pressed = m_pressed || m_axes[i].pressed;
m_released = m_released || m_axes[i].released;
}
// pressed?
if (m_pressed)
{
m_repeat_press_time = m_last_press_time = Time::seconds;
}
else if (Time::seconds - m_last_press_time <= m_press_buffer)
{
m_pressed = true;
}
else if (m_down && m_repeat_interval > 0 && Time::seconds >= m_repeat_press_time + m_repeat_delay)
{
int prev = (int)((Time::previous_seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval);
int cur = (int)((Time::seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval);
m_pressed = prev < cur;
}
// released?
if (m_released)
m_last_release_time = Time::seconds;
else
m_released = Time::seconds - m_last_release_time <= m_release_buffer;
}
void VirtualButton::KeyNode::init(Key key)
{
this->key = key;
}
void VirtualButton::KeyNode::update()
{
down = Input::down(key);
pressed = Input::pressed(key);
released = Input::released(key);
}
void VirtualButton::ButtonNode::init(int gamepad_id, Button button)
{
this->gamepad_id = gamepad_id;
this->button = button;
}
void VirtualButton::ButtonNode::update()
{
down = Input::down(gamepad_id, button);
pressed = Input::pressed(gamepad_id, button);
released = Input::released(gamepad_id, button);
}
void VirtualButton::AxisNode::init(int gamepad_id, Axis axis, float threshold, bool greater_than)
{
this->gamepad_id = gamepad_id;
this->axis = axis;
this->threshold = threshold;
this->greater_than = greater_than;
}
void VirtualButton::AxisNode::update()
{
float curr = Input::state()->controllers[gamepad_id].axis[(int)axis];
float prev = Input::last_state()->controllers[gamepad_id].axis[(int)axis];
if (greater_than)
{
down = curr >= threshold;
pressed = down && prev < threshold;
released = !down && prev >= threshold;
}
else
{
down = curr <= threshold;
pressed = down && prev > threshold;
released = !down && prev <= threshold;
}
}

View File

@ -1,191 +0,0 @@
#include <blah/input/virtual_stick.h>
#include <blah/core/time.h>
#include <blah/core/log.h>
using namespace Blah;
VirtualStick::VirtualStick()
{
this->m_i_deadzone = 0;
}
VirtualStick::VirtualStick(float iDeadzone)
{
this->m_i_deadzone = iDeadzone;
}
VirtualStick& VirtualStick::add_keys(Key left, Key right, Key up, Key down)
{
if (m_keys_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualStick Keys out of bounds!");
else
{
m_keys[m_keys_len].init(left, right, up, down);
m_keys_len++;
}
return *this;
}
VirtualStick& VirtualStick::add_buttons(int gamepad_id, Button left, Button right, Button up, Button down)
{
if (m_buttons_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualStick Buttons out of bounds!");
else
{
m_buttons[m_buttons_len].init(gamepad_id, left, right, up, down);
m_buttons_len++;
}
return *this;
}
VirtualStick& VirtualStick::add_axes(int gamepad_id, Axis horizontal, Axis vertical, float deadzone)
{
if (m_axes_len >= Input::max_virtual_nodes)
BLAH_ERROR("VirtualStick Axes out of bounds!");
else
{
m_axes[m_axes_len].init(gamepad_id, horizontal, vertical, deadzone);
m_axes_len++;
}
return *this;
}
VirtualStick& VirtualStick::repeat(float repeat_delay, float repeat_interval)
{
this->m_repeat_delay = repeat_delay;
this->m_repeat_interval = repeat_interval;
return *this;
}
VirtualStick& VirtualStick::press_buffer(float duration)
{
m_press_buffer = duration;
return *this;
}
VirtualStick& VirtualStick::release_buffer(float duration)
{
m_release_buffer = duration;
return *this;
}
void VirtualStick::update()
{
m_last_value = m_value;
m_value = Vec2::zero;
for (int i = 0; i < m_keys_len; i++)
{
m_keys[i].update();
if (m_value == Vec2::zero)
m_value = m_keys[i].value;
}
for (int i = 0; i < m_buttons_len; i++)
{
m_buttons[i].update();
if (m_value == Vec2::zero)
m_value = m_buttons[i].value;
}
for (int i = 0; i < m_axes_len; i++)
{
m_axes[i].update();
if (m_value == Vec2::zero)
m_value = m_axes[i].value;
}
//Valuei
m_last_value_i = m_value_i;
if (m_value.x > m_i_deadzone)
m_value_i.x = 1;
else if (m_value.x < -m_i_deadzone)
m_value_i.x = -1;
else
m_value_i.x = 0;
if (m_value.y > m_i_deadzone)
m_value_i.y = 1;
else if (m_value.y < -m_i_deadzone)
m_value_i.y = -1;
else
m_value_i.y = 0;
//pressed?
m_pressed = false;
if (m_value_i != Point::zero && m_last_value_i != m_value_i)
{
m_pressed = true;
m_last_press_time = m_repeat_press_time = Time::seconds;
}
else if (m_value_i == m_last_value_i && m_value_i != Point::zero)
{
if (Time::seconds - m_last_press_time <= m_press_buffer)
m_pressed = true;
else if (m_repeat_interval > 0 && Time::seconds >= m_repeat_press_time + m_repeat_delay)
{
int prev = (int)((Time::previous_seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval);
int cur = (int)((Time::seconds - m_repeat_press_time - m_repeat_delay) / m_repeat_interval);
m_pressed = prev < cur;
}
}
//released?
if (m_last_value_i != Point::zero && m_value_i != m_last_value_i)
{
m_released = true;
m_last_release_time = Time::seconds;
}
else if (Time::seconds - m_last_release_time <= m_release_buffer)
m_released = true;
else
m_released = false;
}
void VirtualStick::KeysNode::init(Key left, Key right, Key up, Key down)
{
this->left = left;
this->right = right;
this->up = up;
this->down = down;
}
void VirtualStick::KeysNode::update()
{
value.x = Input::axis_check(value.x, left, right);
value.y = Input::axis_check(value.y, up, down);
}
void VirtualStick::ButtonsNode::init(int gamepad_id, Button left, Button right, Button up, Button down)
{
this->gamepad_id = gamepad_id;
this->left = left;
this->right = right;
this->up = up;
this->down = down;
}
void VirtualStick::ButtonsNode::update()
{
value.x = Input::axis_check(value.x, gamepad_id, left, right);
value.y = Input::axis_check(value.y, gamepad_id, up, down);
}
void VirtualStick::AxesNode::init(int gamepad_id, Axis horizontal, Axis vertical, float deadzone)
{
this->gamepad_id = gamepad_id;
this->horizontal = horizontal;
this->vertical = vertical;
this->deadzone = deadzone;
}
void VirtualStick::AxesNode::update()
{
value.x = Input::axis_check(gamepad_id, horizontal);
value.y = Input::axis_check(gamepad_id, vertical);
if (value.length() < deadzone)
value = Vec2::zero;
}

View File

@ -39,7 +39,7 @@ namespace Blah
void render(const RenderPass& pass); void render(const RenderPass& pass);
// Clears the backbuffer // Clears the backbuffer
void clear_backbuffer(Color color, float depth, uint8_t stencil, ClearMask mask); void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask);
// Creates a new Texture. // Creates a new Texture.
// if the Texture is invalid, this should return an empty reference. // if the Texture is invalid, this should return an empty reference.

View File

@ -1,11 +1,11 @@
#ifdef BLAH_USE_D3D11 #ifdef BLAH_GRAPHICS_D3D11
// TODO: // TODO:
// Note the D3D11 Implementation is still a work-in-progress // Note the D3D11 Implementation is still a work-in-progress
#include "../internal/graphics_backend.h" #include "../internal/graphics_backend.h"
#include "../internal/platform_backend.h" #include "../internal/platform_backend.h"
#include <blah/core/log.h> #include <blah/core/common.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stddef.h> #include <stddef.h>
@ -36,7 +36,7 @@ namespace Blah
struct StoredInputLayout struct StoredInputLayout
{ {
uint32_t shader_hash; u32 shader_hash;
VertexFormat format; VertexFormat format;
ID3D11InputLayout* layout; ID3D11InputLayout* layout;
}; };
@ -145,6 +145,9 @@ namespace Blah
m_size = width * height * 4; m_size = width * height * 4;
is_depth_stencil = true; is_depth_stencil = true;
break; break;
case TextureFormat::None:
case TextureFormat::Count:
break;
} }
if (!is_depth_stencil) if (!is_depth_stencil)
@ -349,7 +352,7 @@ namespace Blah
return m_attachments[0]->height(); return m_attachments[0]->height();
} }
virtual void clear(Color color, float depth, uint8_t stencil, ClearMask mask) override virtual void clear(Color color, float depth, u8 stencil, ClearMask mask) override
{ {
float col[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; float col[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f };
@ -386,7 +389,7 @@ namespace Blah
Vector<Vector<float>> fragment_uniform_values; Vector<Vector<float>> fragment_uniform_values;
StackVector<ShaderData::HLSL_Attribute, 16> attributes; StackVector<ShaderData::HLSL_Attribute, 16> attributes;
Vector<UniformInfo> uniform_list; Vector<UniformInfo> uniform_list;
uint32_t hash = 0; u32 hash = 0;
bool valid = false; bool valid = false;
D3D11_Shader(const ShaderData* data) D3D11_Shader(const ShaderData* data)
@ -542,10 +545,10 @@ namespace Blah
class D3D11_Mesh : public Mesh class D3D11_Mesh : public Mesh
{ {
private: private:
int64_t m_vertex_count = 0; i64 m_vertex_count = 0;
int64_t m_vertex_capacity = 0; i64 m_vertex_capacity = 0;
int64_t m_index_count = 0; i64 m_index_count = 0;
int64_t m_index_capacity = 0; i64 m_index_capacity = 0;
public: public:
ID3D11Buffer* vertex_buffer = nullptr; ID3D11Buffer* vertex_buffer = nullptr;
@ -567,7 +570,7 @@ namespace Blah
index_buffer->Release(); index_buffer->Release();
} }
virtual void index_data(IndexFormat format, const void* indices, int64_t count) override virtual void index_data(IndexFormat format, const void* indices, i64 count) override
{ {
m_index_count = count; m_index_count = count;
@ -579,8 +582,8 @@ namespace Blah
switch (format) switch (format)
{ {
case IndexFormat::UInt16: index_stride = sizeof(int16_t); break; case IndexFormat::UInt16: index_stride = sizeof(i16); break;
case IndexFormat::UInt32: index_stride = sizeof(int32_t); break; case IndexFormat::UInt32: index_stride = sizeof(i32); break;
} }
if (m_index_capacity > 0 && indices) if (m_index_capacity > 0 && indices)
@ -621,7 +624,7 @@ namespace Blah
} }
} }
virtual void vertex_data(const VertexFormat& format, const void* vertices, int64_t count) override virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override
{ {
m_vertex_count = count; m_vertex_count = count;
@ -669,22 +672,22 @@ namespace Blah
} }
} }
virtual void instance_data(const VertexFormat& format, const void* instances, int64_t count) override virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override
{ {
} }
virtual int64_t index_count() const override virtual i64 index_count() const override
{ {
return m_index_count; return m_index_count;
} }
virtual int64_t vertex_count() const override virtual i64 vertex_count() const override
{ {
return m_vertex_count; return m_vertex_count;
} }
virtual int64_t instance_count() const override virtual i64 instance_count() const override
{ {
return 0; return 0;
} }
@ -696,7 +699,7 @@ namespace Blah
state.last_size = Point(App::draw_width(), App::draw_height()); state.last_size = Point(App::draw_width(), App::draw_height());
// Define Swap Chain // Define Swap Chain
DXGI_SWAP_CHAIN_DESC desc = { 0 }; DXGI_SWAP_CHAIN_DESC desc = {};
desc.BufferDesc.RefreshRate.Numerator = 0; desc.BufferDesc.RefreshRate.Numerator = 0;
desc.BufferDesc.RefreshRate.Denominator = 1; desc.BufferDesc.RefreshRate.Denominator = 1;
desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
@ -705,7 +708,6 @@ namespace Blah
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 1; desc.BufferCount = 1;
desc.OutputWindow = (HWND)PlatformBackend::d3d11_get_hwnd(); desc.OutputWindow = (HWND)PlatformBackend::d3d11_get_hwnd();
//desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
desc.Windowed = true; desc.Windowed = true;
// Creation Flags // Creation Flags
@ -764,10 +766,10 @@ namespace Blah
dxgi_device->GetAdapter(&dxgi_adapter); dxgi_device->GetAdapter(&dxgi_adapter);
dxgi_adapter->GetDesc(&adapter_desc); dxgi_adapter->GetDesc(&adapter_desc);
Log::print("D3D11 %ls", adapter_desc.Description); Log::info("D3D11 %ls", adapter_desc.Description);
} }
else else
Log::print("D3D11"); Log::info("D3D11");
} }
return true; return true;
@ -1056,7 +1058,7 @@ namespace Blah
} }
} }
void GraphicsBackend::clear_backbuffer(Color color, float depth, uint8_t stencil, ClearMask mask) void GraphicsBackend::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
{ {
if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color) if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color)
{ {
@ -1243,6 +1245,9 @@ namespace Blah
int size = 0; int size = 0;
switch (it.type) switch (it.type)
{ {
case UniformType::None: break;
case UniformType::Texture2D: break;
case UniformType::Sampler2D: break;
case UniformType::Float: size = 1; break; case UniformType::Float: size = 1; break;
case UniformType::Float2: size = 2; break; case UniformType::Float2: size = 2; break;
case UniformType::Float3: size = 3; break; case UniformType::Float3: size = 3; break;
@ -1304,6 +1309,7 @@ namespace Blah
{ {
switch (format.attributes[i].type) switch (format.attributes[i].type)
{ {
case VertexType::None: break;
case VertexType::Float: it->Format = DXGI_FORMAT_R32_FLOAT; break; case VertexType::Float: it->Format = DXGI_FORMAT_R32_FLOAT; break;
case VertexType::Float2: it->Format = DXGI_FORMAT_R32G32_FLOAT; break; case VertexType::Float2: it->Format = DXGI_FORMAT_R32G32_FLOAT; break;
case VertexType::Float3: it->Format = DXGI_FORMAT_R32G32B32_FLOAT; break; case VertexType::Float3: it->Format = DXGI_FORMAT_R32G32B32_FLOAT; break;
@ -1320,6 +1326,7 @@ namespace Blah
{ {
switch (format.attributes[i].type) switch (format.attributes[i].type)
{ {
case VertexType::None: break;
case VertexType::Float: it->Format = DXGI_FORMAT_R32_FLOAT; break; case VertexType::Float: it->Format = DXGI_FORMAT_R32_FLOAT; break;
case VertexType::Float2: it->Format = DXGI_FORMAT_R32G32_FLOAT; break; case VertexType::Float2: it->Format = DXGI_FORMAT_R32G32_FLOAT; break;
case VertexType::Float3: it->Format = DXGI_FORMAT_R32G32B32_FLOAT; break; case VertexType::Float3: it->Format = DXGI_FORMAT_R32G32B32_FLOAT; break;
@ -1428,18 +1435,21 @@ namespace Blah
switch (sampler.filter) switch (sampler.filter)
{ {
case TextureFilter::None: break;
case TextureFilter::Nearest: desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; break; case TextureFilter::Nearest: desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; break;
case TextureFilter::Linear: desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; break; case TextureFilter::Linear: desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; break;
} }
switch (sampler.wrap_x) switch (sampler.wrap_x)
{ {
case TextureWrap::None: break;
case TextureWrap::Clamp: desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; break; case TextureWrap::Clamp: desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; break;
case TextureWrap::Repeat: desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; break; case TextureWrap::Repeat: desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; break;
} }
switch (sampler.wrap_y) switch (sampler.wrap_y)
{ {
case TextureWrap::None: break;
case TextureWrap::Clamp: desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; break; case TextureWrap::Clamp: desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; break;
case TextureWrap::Repeat: desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; break; case TextureWrap::Repeat: desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; break;
} }
@ -1538,4 +1548,4 @@ namespace Blah
} }
} }
#endif // BLAH_USE_D3D11 #endif // BLAH_GRAPHICS_D3D11

View File

@ -1,8 +1,8 @@
#if !(defined(BLAH_USE_OPENGL) || defined(BLAH_USE_D3D11)) #if !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11))
#include "../internal/graphics_backend.h" #include "../internal/graphics_backend.h"
#include "../internal/platform_backend.h" #include "../internal/platform_backend.h"
#include <blah/core/log.h> #include <blah/core/common.h>
namespace Blah namespace Blah
{ {
@ -134,9 +134,9 @@ namespace Blah
class Dummy_Mesh : public Mesh class Dummy_Mesh : public Mesh
{ {
private: private:
int64_t m_index_count = 0; i64 m_index_count = 0;
int64_t m_vertex_count = 0; i64 m_vertex_count = 0;
int64_t m_instance_count = 0; i64 m_instance_count = 0;
public: public:
Dummy_Mesh() Dummy_Mesh()
@ -144,32 +144,32 @@ namespace Blah
} }
virtual void index_data(IndexFormat format, const void* indices, int64_t count) override virtual void index_data(IndexFormat format, const void* indices, i64 count) override
{ {
m_index_count = count; m_index_count = count;
} }
virtual void vertex_data(const VertexFormat& format, const void* vertices, int64_t count) override virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override
{ {
m_vertex_count = count; m_vertex_count = count;
} }
virtual void instance_data(const VertexFormat& format, const void* instances, int64_t count) override virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override
{ {
m_instance_count = count; m_instance_count = count;
} }
virtual int64_t index_count() const override virtual i64 index_count() const override
{ {
return m_index_count; return m_index_count;
} }
virtual int64_t vertex_count() const override virtual i64 vertex_count() const override
{ {
return m_vertex_count; return m_vertex_count;
} }
virtual int64_t instance_count() const override virtual i64 instance_count() const override
{ {
return m_instance_count; return m_instance_count;
} }
@ -177,7 +177,7 @@ namespace Blah
bool GraphicsBackend::init() bool GraphicsBackend::init()
{ {
Log::print("Dummy Renderer"); Log::info("Dummy Renderer");
return true; return true;
} }
@ -232,4 +232,4 @@ namespace Blah
} }
} }
#endif // !(defined(BLAH_USE_OPENGL) || defined(BLAH_USE_D3D11)) #endif // !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11))

View File

@ -1,8 +1,8 @@
#ifdef BLAH_USE_OPENGL #ifdef BLAH_GRAPHICS_OPENGL
#include "../internal/graphics_backend.h" #include "../internal/graphics_backend.h"
#include "../internal/platform_backend.h" #include "../internal/platform_backend.h"
#include <blah/core/log.h> #include <blah/core/common.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stddef.h> #include <stddef.h>
@ -401,7 +401,7 @@ namespace Blah
else if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) else if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
Log::warn("GL (%s:%s) %s", typeName, severityName, message); Log::warn("GL (%s:%s) %s", typeName, severityName, message);
else else
Log::print("GL (%s) %s", typeName, message); Log::info("GL (%s) %s", typeName, message);
} }
// assign attributes // assign attributes
@ -484,7 +484,7 @@ namespace Blah
components = 4; components = 4;
} }
uint32_t location = (uint32_t)(attribute.index); u32 location = (u32)(attribute.index);
gl.EnableVertexAttribArray(location); gl.EnableVertexAttribArray(location);
gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr); gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr);
gl.VertexAttribDivisor(location, divisor); gl.VertexAttribDivisor(location, divisor);
@ -674,7 +674,7 @@ namespace Blah
GLuint m_id; GLuint m_id;
int m_width; int m_width;
int m_height; int m_height;
StackVector<TextureRef, BLAH_ATTACHMENTS> m_attachments; StackVector<TextureRef, Attachments::MaxCapacity> m_attachments;
public: public:
@ -749,7 +749,7 @@ namespace Blah
return m_height; return m_height;
} }
virtual void clear(Color color, float depth, uint8_t stencil, ClearMask mask) override virtual void clear(Color color, float depth, u8 stencil, ClearMask mask) override
{ {
int clear = 0; int clear = 0;
@ -967,13 +967,13 @@ namespace Blah
GLuint m_index_buffer; GLuint m_index_buffer;
GLuint m_vertex_buffer; GLuint m_vertex_buffer;
GLuint m_instance_buffer; GLuint m_instance_buffer;
int64_t m_index_count; i64 m_index_count;
int64_t m_vertex_count; i64 m_vertex_count;
int64_t m_instance_count; i64 m_instance_count;
uint16_t m_vertex_size; u16 m_vertex_size;
uint16_t m_instance_size; u16 m_instance_size;
uint8_t m_vertex_attribs_enabled; u8 m_vertex_attribs_enabled;
uint8_t m_instance_attribs_enabled; u8 m_instance_attribs_enabled;
Vector<GLuint> m_vertex_attribs; Vector<GLuint> m_vertex_attribs;
Vector<GLuint> m_instance_attribs; Vector<GLuint> m_instance_attribs;
GLenum m_index_format; GLenum m_index_format;
@ -1026,7 +1026,7 @@ namespace Blah
return m_index_size; return m_index_size;
} }
virtual void index_data(IndexFormat format, const void* indices, int64_t count) override virtual void index_data(IndexFormat format, const void* indices, i64 count) override
{ {
m_index_count = count; m_index_count = count;
@ -1053,7 +1053,7 @@ namespace Blah
gl.BindVertexArray(0); gl.BindVertexArray(0);
} }
virtual void vertex_data(const VertexFormat& format, const void* vertices, int64_t count) override virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override
{ {
m_vertex_count = count; m_vertex_count = count;
@ -1074,7 +1074,7 @@ namespace Blah
gl.BindVertexArray(0); gl.BindVertexArray(0);
} }
virtual void instance_data(const VertexFormat& format, const void* instances, int64_t count) override virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override
{ {
m_instance_count = count; m_instance_count = count;
@ -1095,17 +1095,17 @@ namespace Blah
gl.BindVertexArray(0); gl.BindVertexArray(0);
} }
virtual int64_t index_count() const override virtual i64 index_count() const override
{ {
return m_index_count; return m_index_count;
} }
virtual int64_t vertex_count() const override virtual i64 vertex_count() const override
{ {
return m_vertex_count; return m_vertex_count;
} }
virtual int64_t instance_count() const override virtual i64 instance_count() const override
{ {
return m_instance_count; return m_instance_count;
} }
@ -1147,7 +1147,7 @@ namespace Blah
gl.GetIntegerv(0x0D33, &gl.max_texture_size); gl.GetIntegerv(0x0D33, &gl.max_texture_size);
// log // log
Log::print("OpenGL %s, %s", Log::info("OpenGL %s, %s",
gl.GetString(GL_VERSION), gl.GetString(GL_VERSION),
gl.GetString(GL_RENDERER)); gl.GetString(GL_RENDERER));
@ -1496,7 +1496,7 @@ namespace Blah
} }
} }
void GraphicsBackend::clear_backbuffer(Color color, float depth, uint8_t stencil, ClearMask mask) void GraphicsBackend::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
{ {
int clear = 0; int clear = 0;
@ -1517,4 +1517,4 @@ namespace Blah
} }
} }
#endif // BLAH_USE_OPENGL #endif // BLAH_GRAPHICS_OPENGL

View File

@ -37,7 +37,7 @@ namespace Blah
// Call this when a Controller is connected. Note that the Name parameter must be kept valid // Call this when a Controller is connected. Note that the Name parameter must be kept valid
// until on_controller_disconnect is called with the same index. // until on_controller_disconnect is called with the same index.
void on_controller_connect(int index, const char* name, int isGamepad, int buttonCount, int axisCount, uint16_t vendor, uint16_t product, uint16_t version); void on_controller_connect(int index, const char* name, int isGamepad, int buttonCount, int axisCount, u16 vendor, u16 product, u16 version);
// Call this when a controller is disconnected // Call this when a controller is disconnected
void on_controller_disconnect(int index); void on_controller_disconnect(int index);

View File

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <inttypes.h> #include <blah/core/common.h>
#include <blah/core/filesystem.h> #include <blah/core/filesystem.h>
#include <blah/containers/vector.h> #include <blah/containers/vector.h>
@ -22,7 +22,7 @@ namespace Blah
void shutdown(); void shutdown();
// The time, in ticks (microseconds) since the Application was started // The time, in ticks (microseconds) since the Application was started
uint64_t ticks(); u64 ticks();
// Called every frame // Called every frame
void frame(); void frame();
@ -91,19 +91,19 @@ namespace Blah
bool file_open(const char* path, FileHandle* handle, FileMode mode); bool file_open(const char* path, FileHandle* handle, FileMode mode);
// Returns the length of the file // Returns the length of the file
int64_t file_length(FileHandle file); i64 file_length(FileHandle file);
// Returns the Position of the file // Returns the Position of the file
int64_t file_position(FileHandle file); i64 file_position(FileHandle file);
// Seeks the Position of the file and returns the new position from the start of the file // Seeks the Position of the file and returns the new position from the start of the file
int64_t file_seek(FileHandle file, int64_t seekTo); i64 file_seek(FileHandle file, i64 seekTo);
// Reads a specific number of elements of a given size from the file into ptr // Reads a specific number of elements of a given size from the file into ptr
int64_t file_read(FileHandle file, void* ptr, int64_t size); i64 file_read(FileHandle file, void* ptr, i64 size);
// Writes a specific number of elements of the given size from ptr to the file // Writes a specific number of elements of the given size from ptr to the file
int64_t file_write(FileHandle file, const void* ptr, int64_t size); i64 file_write(FileHandle file, const void* ptr, i64 size);
// Closes a file // Closes a file
void file_close(FileHandle file); void file_close(FileHandle file);

View File

@ -1,4 +1,4 @@
#ifdef BLAH_USE_SDL2 #ifdef BLAH_PLATFORM_SDL2
#include "../internal/platform_backend.h" #include "../internal/platform_backend.h"
#include "../internal/input_backend.h" #include "../internal/input_backend.h"
@ -6,7 +6,7 @@
#include <blah/input/input.h> #include <blah/input/input.h>
#include <blah/core/app.h> #include <blah/core/app.h>
#include <blah/core/filesystem.h> #include <blah/core/filesystem.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include <blah/core/time.h> #include <blah/core/time.h>
#include <SDL.h> #include <SDL.h>
@ -41,12 +41,34 @@ namespace
void sdl_log(void* userdata, int category, SDL_LogPriority priority, const char* message) void sdl_log(void* userdata, int category, SDL_LogPriority priority, const char* message)
{ {
if (priority <= SDL_LOG_PRIORITY_INFO) if (priority <= SDL_LOG_PRIORITY_INFO)
Log::print(message); Log::info(message);
else if (priority <= SDL_LOG_PRIORITY_WARN) else if (priority <= SDL_LOG_PRIORITY_WARN)
Log::warn(message); Log::warn(message);
else else
Log::error(message); Log::error(message);
} }
int find_joystick_index(SDL_JoystickID instance_id)
{
for (int i = 0; i < Blah::Input::max_controllers; i++)
if (joysticks[i] != nullptr && SDL_JoystickInstanceID(joysticks[i]) == instance_id)
return i;
return -1;
}
int find_gamepad_index(SDL_JoystickID instance_id)
{
for (int i = 0; i < Blah::Input::max_controllers; i++)
{
if (gamepads[i] != nullptr)
{
auto joystick = SDL_GameControllerGetJoystick(gamepads[i]);
if (SDL_JoystickInstanceID(joystick) == instance_id)
return i;
}
}
return -1;
}
} }
bool PlatformBackend::init(const Config* config) bool PlatformBackend::init(const Config* config)
@ -65,7 +87,7 @@ bool PlatformBackend::init(const Config* config)
// Get SDL version // Get SDL version
SDL_version version; SDL_version version;
SDL_GetVersion(&version); SDL_GetVersion(&version);
Log::print("SDL v%i.%i.%i", version.major, version.minor, version.patch); Log::info("SDL v%i.%i.%i", version.major, version.minor, version.patch);
// initialize SDL // initialize SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
@ -170,11 +192,11 @@ void PlatformBackend::shutdown()
SDL_Quit(); SDL_Quit();
} }
uint64_t PlatformBackend::ticks() u64 PlatformBackend::ticks()
{ {
auto counter = SDL_GetPerformanceCounter(); auto counter = SDL_GetPerformanceCounter();
auto per_second = (double)SDL_GetPerformanceFrequency(); auto per_second = (double)SDL_GetPerformanceFrequency();
return (uint64_t)(counter * (Time::ticks_per_second / per_second)); return (u64)(counter * (Time::ticks_per_second / per_second));
} }
// Macro defined by X11 conflicts with MouseButton enum // Macro defined by X11 conflicts with MouseButton enum
@ -247,46 +269,56 @@ void PlatformBackend::frame()
// Joystick Controller // Joystick Controller
else if (event.type == SDL_JOYDEVICEADDED) else if (event.type == SDL_JOYDEVICEADDED)
{ {
Sint32 index = event.jdevice.which; auto index = event.jdevice.which;
if (SDL_IsGameController(index) == SDL_FALSE) if (SDL_IsGameController(index) == SDL_FALSE && index >= 0 && index < Input::max_controllers)
{ {
SDL_Joystick* ptr = joysticks[index] = SDL_JoystickOpen(index); auto ptr = joysticks[index] = SDL_JoystickOpen(index);
const char* name = SDL_JoystickName(ptr); auto name = SDL_JoystickName(ptr);
int button_count = SDL_JoystickNumButtons(ptr); auto button_count = SDL_JoystickNumButtons(ptr);
int axis_count = SDL_JoystickNumAxes(ptr); auto axis_count = SDL_JoystickNumAxes(ptr);
uint16_t vendor = SDL_JoystickGetVendor(ptr); auto vendor = SDL_JoystickGetVendor(ptr);
uint16_t product = SDL_JoystickGetProduct(ptr); auto product = SDL_JoystickGetProduct(ptr);
uint16_t version = SDL_JoystickGetProductVersion(ptr); auto version = SDL_JoystickGetProductVersion(ptr);
InputBackend::on_controller_connect(index, name, 0, button_count, axis_count, vendor, product, version); InputBackend::on_controller_connect(index, name, 0, button_count, axis_count, vendor, product, version);
} }
} }
else if (event.type == SDL_JOYDEVICEREMOVED) else if (event.type == SDL_JOYDEVICEREMOVED)
{ {
Sint32 index = event.jdevice.which; auto index = find_joystick_index(event.jdevice.which);
if (index >= 0)
{
if (SDL_IsGameController(index) == SDL_FALSE) if (SDL_IsGameController(index) == SDL_FALSE)
{ {
InputBackend::on_controller_disconnect(index); InputBackend::on_controller_disconnect(index);
SDL_JoystickClose(joysticks[index]); SDL_JoystickClose(joysticks[index]);
} }
} }
}
else if (event.type == SDL_JOYBUTTONDOWN) else if (event.type == SDL_JOYBUTTONDOWN)
{ {
Sint32 index = event.jdevice.which; auto index = find_joystick_index(event.jdevice.which);
if (index >= 0)
{
if (SDL_IsGameController(index) == SDL_FALSE) if (SDL_IsGameController(index) == SDL_FALSE)
InputBackend::on_button_down(index, event.jbutton.button); InputBackend::on_button_down(index, event.jbutton.button);
} }
}
else if (event.type == SDL_JOYBUTTONUP) else if (event.type == SDL_JOYBUTTONUP)
{ {
Sint32 index = event.jdevice.which; auto index = find_joystick_index(event.jdevice.which);
if (index >= 0)
{
if (SDL_IsGameController(index) == SDL_FALSE) if (SDL_IsGameController(index) == SDL_FALSE)
InputBackend::on_button_up(index, event.jbutton.button); InputBackend::on_button_up(index, event.jbutton.button);
} }
}
else if (event.type == SDL_JOYAXISMOTION) else if (event.type == SDL_JOYAXISMOTION)
{ {
Sint32 index = event.jaxis.which; auto index = find_joystick_index(event.jdevice.which);
if (index >= 0)
{
if (SDL_IsGameController(index) == SDL_FALSE) if (SDL_IsGameController(index) == SDL_FALSE)
{ {
float value; float value;
@ -297,48 +329,60 @@ void PlatformBackend::frame()
InputBackend::on_axis_move(index, event.jaxis.axis, value); InputBackend::on_axis_move(index, event.jaxis.axis, value);
} }
} }
}
// Gamepad Controller // Gamepad Controller
else if (event.type == SDL_CONTROLLERDEVICEADDED) else if (event.type == SDL_CONTROLLERDEVICEADDED)
{ {
Sint32 index = event.cdevice.which; auto index = event.cdevice.which;
SDL_GameController* ptr = gamepads[index] = SDL_GameControllerOpen(index); if (index >= 0 && index < Input::max_controllers)
const char* name = SDL_GameControllerName(ptr); {
uint16_t vendor = SDL_GameControllerGetVendor(ptr); auto ptr = gamepads[index] = SDL_GameControllerOpen(index);
uint16_t product = SDL_GameControllerGetProduct(ptr); auto name = SDL_GameControllerName(ptr);
uint16_t version = SDL_GameControllerGetProductVersion(ptr); auto vendor = SDL_GameControllerGetVendor(ptr);
auto product = SDL_GameControllerGetProduct(ptr);
auto version = SDL_GameControllerGetProductVersion(ptr);
InputBackend::on_controller_connect(index, name, 1, 15, 6, vendor, product, version); InputBackend::on_controller_connect(index, name, 1, 15, 6, vendor, product, version);
} }
}
else if (event.type == SDL_CONTROLLERDEVICEREMOVED) else if (event.type == SDL_CONTROLLERDEVICEREMOVED)
{ {
Sint32 index = event.cdevice.which; auto index = find_gamepad_index(event.cdevice.which);
if (index >= 0)
{
InputBackend::on_controller_disconnect(index); InputBackend::on_controller_disconnect(index);
SDL_GameControllerClose(gamepads[index]); SDL_GameControllerClose(gamepads[index]);
} }
}
else if (event.type == SDL_CONTROLLERBUTTONDOWN) else if (event.type == SDL_CONTROLLERBUTTONDOWN)
{ {
Sint32 index = event.cbutton.which; auto index = find_gamepad_index(event.cdevice.which);
if (index >= 0)
{
int button = (int)Button::None; int button = (int)Button::None;
if (event.cbutton.button >= 0 && event.cbutton.button < 15) if (event.cbutton.button >= 0 && event.cbutton.button < 15)
button = event.cbutton.button; // NOTE: These map directly to Engine Buttons enum! button = event.cbutton.button; // NOTE: These map directly to Engine Buttons enum!
InputBackend::on_button_down(index, button); InputBackend::on_button_down(index, button);
} }
}
else if (event.type == SDL_CONTROLLERBUTTONUP) else if (event.type == SDL_CONTROLLERBUTTONUP)
{ {
Sint32 index = event.cbutton.which; auto index = find_gamepad_index(event.cdevice.which);
if (index >= 0)
{
int button = (int)Button::None; int button = (int)Button::None;
if (event.cbutton.button >= 0 && event.cbutton.button < 15) if (event.cbutton.button >= 0 && event.cbutton.button < 15)
button = event.cbutton.button; // NOTE: These map directly to Engine Buttons enum! button = event.cbutton.button; // NOTE: These map directly to Engine Buttons enum!
InputBackend::on_button_up(index, button); InputBackend::on_button_up(index, button);
} }
}
else if (event.type == SDL_CONTROLLERAXISMOTION) else if (event.type == SDL_CONTROLLERAXISMOTION)
{ {
Sint32 index = event.caxis.which; auto index = find_gamepad_index(event.cdevice.which);
if (index >= 0)
{
int axis = (int)Axis::None; int axis = (int)Axis::None;
if (event.caxis.axis >= 0 && event.caxis.axis < 6) if (event.caxis.axis >= 0 && event.caxis.axis < 6)
axis = event.caxis.axis; // NOTE: These map directly to Engine Axis enum! axis = event.caxis.axis; // NOTE: These map directly to Engine Axis enum!
@ -353,11 +397,12 @@ void PlatformBackend::frame()
} }
} }
} }
}
void PlatformBackend::sleep(int milliseconds) void PlatformBackend::sleep(int milliseconds)
{ {
if (milliseconds >= 0) if (milliseconds >= 0)
SDL_Delay((uint32_t)milliseconds); SDL_Delay((u32)milliseconds);
} }
void PlatformBackend::present() void PlatformBackend::present()
@ -611,27 +656,27 @@ bool PlatformBackend::file_open(const char* path, PlatformBackend::FileHandle* h
return ptr != nullptr; return ptr != nullptr;
} }
int64_t PlatformBackend::file_length(PlatformBackend::FileHandle stream) i64 PlatformBackend::file_length(PlatformBackend::FileHandle stream)
{ {
return SDL_RWsize((SDL_RWops*)stream); return SDL_RWsize((SDL_RWops*)stream);
} }
int64_t PlatformBackend::file_position(PlatformBackend::FileHandle stream) i64 PlatformBackend::file_position(PlatformBackend::FileHandle stream)
{ {
return SDL_RWtell((SDL_RWops*)stream); return SDL_RWtell((SDL_RWops*)stream);
} }
int64_t PlatformBackend::file_seek(PlatformBackend::FileHandle stream, int64_t seekTo) i64 PlatformBackend::file_seek(PlatformBackend::FileHandle stream, i64 seekTo)
{ {
return SDL_RWseek((SDL_RWops*)stream, seekTo, RW_SEEK_SET); return SDL_RWseek((SDL_RWops*)stream, seekTo, RW_SEEK_SET);
} }
int64_t PlatformBackend::file_read(PlatformBackend::FileHandle stream, void* ptr, int64_t length) i64 PlatformBackend::file_read(PlatformBackend::FileHandle stream, void* ptr, i64 length)
{ {
return SDL_RWread((SDL_RWops*)stream, ptr, sizeof(char), length); return SDL_RWread((SDL_RWops*)stream, ptr, sizeof(char), length);
} }
int64_t PlatformBackend::file_write(PlatformBackend::FileHandle stream, const void* ptr, int64_t length) i64 PlatformBackend::file_write(PlatformBackend::FileHandle stream, const void* ptr, i64 length)
{ {
return SDL_RWwrite((SDL_RWops*)stream, ptr, sizeof(char), length); return SDL_RWwrite((SDL_RWops*)stream, ptr, sizeof(char), length);
} }
@ -677,4 +722,4 @@ void* PlatformBackend::d3d11_get_hwnd()
#endif #endif
} }
#endif // BLAH_USE_SDL2 #endif // BLAH_PLATFORM_SDL2

View File

@ -0,0 +1,776 @@
#ifdef BLAH_PLATFORM_WIN32
// Note:
// This backend implementation is unfinished!
// It's missing a few things, namely:
// - Controller Support
// - Mouse wheel
// - Text input
// - Probably other stuff?
// (And error testing)
#include "../internal/platform_backend.h"
#include "../internal/input_backend.h"
#include "../internal/graphics_backend.h"
#include <blah/input/input.h>
#include <blah/core/app.h>
#include <blah/core/filesystem.h>
#include <blah/core/common.h>
#include <blah/core/time.h>
#include <blah/math/stopwatch.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winuser.h> // for SetProcessDPIAware
#include <filesystem> // for File Reading/Writing
#include <shellapi.h> // for file explore
#include <shlobj.h> // for known folder
#include <chrono> // for ticks method
using namespace Blah;
namespace fs = std::filesystem;
namespace
{
// Primary Window
HWND g_hwnd;
// Working Directories
FilePath g_working_directory;
FilePath g_user_directory;
// Timestamp for calculating ticks
std::chrono::system_clock::duration g_start_time;
// OpenGL Methods
// These are only loaded if built using the OpenGL Backend
HMODULE g_opengl_dll;
void* (WINAPI* g_wglGetProcAddress) (const char* proc);
HGLRC(WINAPI* g_wglCreateContext) (HDC hdc);
BOOL(WINAPI* g_wglDeleteContext) (HGLRC hglrc);
BOOL(WINAPI* g_wglMakeCurrent) (HDC hdc, HGLRC hglrc);
// fullscreen state
RECT g_windowed_position;
bool g_fullscreen = false;
}
Key blah_scancode_to_key(WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK blah_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool PlatformBackend::init(const Config* config)
{
// Required to call this for Windows
// I'm not sure why SDL2 doesn't do this on Windows automatically?
SetProcessDPIAware();
// Get the hInstance
HINSTANCE hInstance = GetModuleHandle(NULL);
// Create the Window Class
WNDCLASS wc = {};
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = "BLAH WINDOW";
wc.hInstance = hInstance;
wc.lpfnWndProc = blah_window_procedure;
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.lpszMenuName = NULL;
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
// Register the Window class
RegisterClass(&wc);
// Create the Window Instance
g_hwnd = CreateWindow("BLAH WINDOW", config->name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL);
// Failed to create the Window
if (g_hwnd == NULL)
{
Log::error("Window Creation Failed");
return false;
}
// Create the OpenGL device info
if (App::renderer() == Renderer::OpenGL)
{
// Load the DLL
g_opengl_dll = LoadLibraryA("opengl32.dll");
if (g_opengl_dll == NULL)
{
Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll");
return false;
}
// Get the Windows GL functions we need
g_wglGetProcAddress = (void*(WINAPI*) (const char*))GetProcAddress(g_opengl_dll, "wglGetProcAddress");
g_wglCreateContext = (HGLRC(WINAPI*) (HDC))GetProcAddress(g_opengl_dll, "wglCreateContext");
g_wglDeleteContext = (BOOL(WINAPI*) (HGLRC))GetProcAddress(g_opengl_dll, "wglDeleteContext");
g_wglMakeCurrent = (BOOL(WINAPI*) (HDC, HGLRC))GetProcAddress(g_opengl_dll, "wglMakeCurrent");
// TODO:
// Allow the user to apply (some of) these values before instantiation.
// Also applies to the SDL2 Backend
PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
32, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
24, // 32-bit z-buffer
8, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
HDC hdc = GetDC(g_hwnd);
// get the best available match of pixel format for the device context
int pixel_format = ChoosePixelFormat(hdc, &pfd);
// make that the pixel format of the device context
SetPixelFormat(hdc, pixel_format, &pfd);
}
// Reset our game timer
g_start_time = std::chrono::system_clock::now().time_since_epoch();
// Get Working Directory
{
TCHAR buffer[MAX_PATH];
GetModuleFileName(NULL, buffer, MAX_PATH);
auto normalized = Path::normalize(buffer);
auto end = normalized.last_index_of('/');;
if (end >= 0)
g_working_directory = FilePath(normalized.begin(), normalized.begin() + end);
else
g_working_directory = normalized;
g_working_directory.append("/");
}
// Get Application User Directory
{
PWSTR path = NULL;
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, NULL, &path)))
{
auto end = path;
while (*end != 0) end++;
FilePath result;
result.append_utf16((u16*)path, (u16*)end);
g_user_directory = Path::join(Path::normalize(result), config->name) + "/";
}
CoTaskMemFree(path);
}
// Not currently fullscreen
g_fullscreen = false;
// Finished Platform Setup
return true;
}
void PlatformBackend::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_hwnd, SW_SHOW);
}
void PlatformBackend::shutdown()
{
DestroyWindow(g_hwnd);
}
u64 PlatformBackend::ticks()
{
// Todo:
// This should account for whatever Time::ticks_per_second is set to
auto now = std::chrono::system_clock::now().time_since_epoch();
return std::chrono::duration_cast<std::chrono::microseconds>(now - g_start_time).count();
}
LRESULT CALLBACK blah_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
{
auto config = App::config();
if (config->on_exit_request != nullptr)
config->on_exit_request();
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN:
InputBackend::on_mouse_down(MouseButton::Left);
return 0;
case WM_LBUTTONUP:
InputBackend::on_mouse_up(MouseButton::Left);
return 0;
case WM_RBUTTONDOWN:
InputBackend::on_mouse_down(MouseButton::Right);
return 0;
case WM_RBUTTONUP:
InputBackend::on_mouse_up(MouseButton::Right);
return 0;
case WM_MBUTTONDOWN:
InputBackend::on_mouse_down(MouseButton::Middle);
return 0;
case WM_MBUTTONUP:
InputBackend::on_mouse_up(MouseButton::Middle);
return 0;
case WM_MOUSEMOVE:
InputBackend::on_mouse_move((u16)(lParam), lParam >> 16);
return 0;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
{
auto is_repeat = ((lParam & (1 << 30)) >> 30) == 1;
if (!is_repeat)
{
auto key = blah_scancode_to_key(wParam, lParam);
if (key != Key::Unknown)
InputBackend::on_key_down(key);
}
return 0;
}
case WM_KEYUP:
case WM_SYSKEYUP:
{
auto key = blah_scancode_to_key(wParam, lParam);
if (key != Key::Unknown)
InputBackend::on_key_up(key);
return 0;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void PlatformBackend::frame()
{
// Catch & Dispatch Window Messages
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void PlatformBackend::sleep(int milliseconds)
{
if (milliseconds > 0)
Sleep(milliseconds);
}
void PlatformBackend::present()
{
if (App::renderer() == Renderer::OpenGL)
{
HDC hdc = GetDC(g_hwnd);
SwapBuffers(hdc);
}
}
const char* PlatformBackend::get_title()
{
return nullptr;
}
void PlatformBackend::set_title(const char* title)
{
SetWindowText(g_hwnd, title);
}
void PlatformBackend::get_position(int* x, int* y)
{
RECT rect;
if (GetWindowRect(g_hwnd, &rect))
{
*x = rect.left;
*y = rect.top;
}
}
void PlatformBackend::set_position(int x, int y)
{
int w, h;
get_size(&w, &h);
SetWindowPos(g_hwnd, NULL, x, y, w, h, 0);
}
void PlatformBackend::set_fullscreen(bool enabled)
{
if (g_fullscreen == enabled)
return;
g_fullscreen = enabled;
if (g_fullscreen)
{
GetWindowRect(g_hwnd, &g_windowed_position);
int w = GetSystemMetrics(SM_CXSCREEN);
int h = GetSystemMetrics(SM_CYSCREEN);
SetWindowLongPtr(g_hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
SetWindowPos(g_hwnd, HWND_TOP, 0, 0, w, h, 0);
ShowWindow(g_hwnd, SW_SHOW);
}
else
{
SetWindowLongPtr(g_hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(g_hwnd, HWND_TOP,
g_windowed_position.left,
g_windowed_position.top,
g_windowed_position.right - g_windowed_position.left,
g_windowed_position.bottom - g_windowed_position.top, 0);
ShowWindow(g_hwnd, SW_SHOW);
}
}
void PlatformBackend::get_size(int* width, int* height)
{
RECT rect;
if (GetClientRect(g_hwnd, &rect))
{
*width = rect.right - rect.left;
*height = rect.bottom - rect.top;
}
}
void PlatformBackend::set_size(int width, int height)
{
RECT client_rect;
RECT border_rect;
GetClientRect(g_hwnd, &client_rect);
GetWindowRect(g_hwnd, &border_rect);
int border_width = (border_rect.right - border_rect.left) - (client_rect.right - client_rect.left);
int border_height = (border_rect.bottom - border_rect.top) - (client_rect.bottom - client_rect.top);
SetWindowPos(g_hwnd, NULL, border_rect.left, border_rect.top, width + border_width, height + border_height, 0);
}
void PlatformBackend::get_draw_size(int* width, int* height)
{
RECT rect;
if (GetClientRect(g_hwnd, &rect))
{
*width = rect.right - rect.left;
*height = rect.bottom - rect.top;
}
}
float PlatformBackend::get_content_scale()
{
// base value of Windows DPI
// as seen here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow
constexpr float base_raw_value = 96.0f;
UINT raw_value = GetDpiForWindow(g_hwnd);
return (raw_value / base_raw_value);
}
// FILE IO
const char* PlatformBackend::app_path()
{
return g_working_directory.cstr();
}
const char* PlatformBackend::user_path()
{
return g_user_directory.cstr();
}
bool PlatformBackend::file_exists(const char* path)
{
return fs::is_regular_file(path);
}
bool PlatformBackend::file_delete(const char* path)
{
return fs::remove(path);
}
bool PlatformBackend::dir_create(const char* path)
{
std::error_code error;
return fs::create_directories(path, error);
}
bool PlatformBackend::dir_exists(const char* path)
{
return fs::is_directory(path);
}
bool PlatformBackend::dir_delete(const char* path)
{
BLAH_ERROR("not implemented");
return false;
}
void PlatformBackend::dir_enumerate(Vector<FilePath>& list, const char* path, bool recursive)
{
if (fs::is_directory(path))
{
if (recursive)
{
for (auto& p : fs::recursive_directory_iterator(path))
list.emplace_back(p.path().string().c_str());
}
else
{
for (auto& p : fs::directory_iterator(path))
list.emplace_back(p.path().string().c_str());
}
}
}
void PlatformBackend::dir_explore(const char* path)
{
ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT);
}
bool PlatformBackend::file_open(const char* path, PlatformBackend::FileHandle* handle, FileMode mode)
{
int access = 0;
int creation = 0;
if (((int)mode & (int)FileMode::Read) == (int)FileMode::Read)
{
access |= GENERIC_READ;
creation = OPEN_EXISTING;
}
if (((int)mode & (int)FileMode::Write) == (int)FileMode::Write)
{
access |= GENERIC_WRITE;
creation = OPEN_ALWAYS;
}
auto result = CreateFile(path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
if (result == INVALID_HANDLE_VALUE)
return false;
*handle = result;
return true;
}
i64 PlatformBackend::file_length(PlatformBackend::FileHandle handle)
{
// Todo: cache this value? not sure how performant it is
LARGE_INTEGER file_size;
if (GetFileSizeEx(handle, &file_size))
return file_size.QuadPart;
return 0;
}
i64 PlatformBackend::file_position(PlatformBackend::FileHandle handle)
{
// Todo: handle 64-bit values properly
// Todo: cache this value? not sure how performant it is
return SetFilePointer(handle, 0, NULL, FILE_CURRENT);
}
i64 PlatformBackend::file_seek(PlatformBackend::FileHandle handle, i64 seekTo)
{
// Todo: handle 64-bit values properly
return SetFilePointer(handle, seekTo, NULL, FILE_BEGIN);
}
i64 PlatformBackend::file_read(PlatformBackend::FileHandle handle, void* ptr, i64 length)
{
DWORD read = 0;
if (ReadFile(handle, ptr, length, &read, NULL))
return read;
return 0;
}
i64 PlatformBackend::file_write(PlatformBackend::FileHandle handle, const void* ptr, i64 length)
{
DWORD written = 0;
if (WriteFile(handle, ptr, length, &written, NULL))
return written;
return 0;
}
void PlatformBackend::file_close(PlatformBackend::FileHandle handle)
{
CloseHandle(handle);
}
void* PlatformBackend::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_wglGetProcAddress(name);
if ((p == 0) ||
(p == (void*)0x1) ||
(p == (void*)0x2) ||
(p == (void*)0x3) ||
(p == (void*)-1))
{
p = (void*)GetProcAddress(g_opengl_dll, name);
}
return p;
}
void* PlatformBackend::gl_context_create()
{
HDC hdc = GetDC(g_hwnd);
return g_wglCreateContext(hdc);
}
void PlatformBackend::gl_context_make_current(void* context)
{
if (context != nullptr)
{
HDC hdc = GetDC(g_hwnd);
g_wglMakeCurrent(hdc, (HGLRC)context);
}
else
g_wglMakeCurrent(NULL, NULL);
}
void PlatformBackend::gl_context_destroy(void* context)
{
g_wglDeleteContext((HGLRC)context);
}
void* PlatformBackend::d3d11_get_hwnd()
{
return g_hwnd;
}
Key blah_scancode_to_key(WPARAM wParam, LPARAM lParam)
{
Key key = Key::Unknown;
switch (wParam)
{
case VK_CANCEL: key = Key::Cancel; break;
case VK_BACK: key = Key::Backspace; break;
case VK_TAB: key = Key::Tab; break;
case VK_CLEAR: key = Key::Clear; break;
case VK_RETURN: key = Key::Enter; break;
case VK_SHIFT: key = Key::LeftShift; break;
case VK_CONTROL: key = Key::LeftControl; break;
case VK_MENU: key = Key::Menu; break;
case VK_PAUSE: key = Key::Pause; break;
case VK_CAPITAL: key = Key::Capslock; break;
case VK_ESCAPE: key = Key::Escape; break;
case VK_SPACE: key = Key::Space; break;
case VK_PRIOR: key = Key::Prior; break;
case VK_END: key = Key::End; break;
case VK_HOME: key = Key::Home; break;
case VK_LEFT: key = Key::Left; break;
case VK_UP: key = Key::Up; break;
case VK_RIGHT: key = Key::Right; break;
case VK_DOWN: key = Key::Down; break;
case VK_SELECT: key = Key::Select; break;
case VK_PRINT: key = Key::PrintScreen; break;
case VK_EXECUTE: key = Key::Execute; break;
case VK_SNAPSHOT: key = Key::PrintScreen; break;
case VK_INSERT: key = Key::Insert; break;
case VK_DELETE: key = Key::Delete; break;
case VK_HELP: key = Key::Help; break;
case VK_LWIN: key = Key::LeftOS; break;
case VK_RWIN: key = Key::RightOS; break;
case VK_APPS: key = Key::Application; break;
case VK_SLEEP: key = Key::Unknown; break;
case VK_NUMPAD0: key = Key::Keypad0; break;
case VK_NUMPAD1: key = Key::Keypad1; break;
case VK_NUMPAD2: key = Key::Keypad2; break;
case VK_NUMPAD3: key = Key::Keypad3; break;
case VK_NUMPAD4: key = Key::Keypad4; break;
case VK_NUMPAD5: key = Key::Keypad5; break;
case VK_NUMPAD6: key = Key::Keypad6; break;
case VK_NUMPAD7: key = Key::Keypad7; break;
case VK_NUMPAD8: key = Key::Keypad8; break;
case VK_NUMPAD9: key = Key::Keypad9; break;
case VK_F1: key = Key::F1; break;
case VK_F2: key = Key::F2; break;
case VK_F3: key = Key::F3; break;
case VK_F4: key = Key::F4; break;
case VK_F5: key = Key::F5; break;
case VK_F6: key = Key::F6; break;
case VK_F7: key = Key::F7; break;
case VK_F8: key = Key::F8; break;
case VK_F9: key = Key::F9; break;
case VK_F10: key = Key::F10; break;
case VK_F11: key = Key::F11; break;
case VK_F12: key = Key::F12; break;
case VK_F13: key = Key::F13; break;
case VK_F14: key = Key::F14; break;
case VK_F15: key = Key::F15; break;
case VK_F16: key = Key::F16; break;
case VK_F17: key = Key::F17; break;
case VK_F18: key = Key::F18; break;
case VK_F19: key = Key::F19; break;
case VK_F20: key = Key::F20; break;
case VK_F21: key = Key::F21; break;
case VK_F22: key = Key::F22; break;
case VK_F23: key = Key::F23; break;
case VK_F24: key = Key::F24; break;
case VK_NUMLOCK: key = Key::Numlock; break;
case VK_LSHIFT: key = Key::LeftShift; break;
case VK_RSHIFT: key = Key::RightShift; break;
case VK_LCONTROL: key = Key::LeftControl; break;
case VK_RCONTROL: key = Key::RightControl; break;
case VK_VOLUME_MUTE: key = Key::Mute; break;
case VK_VOLUME_DOWN: key = Key::VolumeDown; break;
case VK_VOLUME_UP: key = Key::VolumeUp; break;
}
if (key == Key::Unknown)
{
int scancode = (lParam >> 16) & 0xFF;
switch (scancode)
{
case 1: key = Key::Escape; break;
case 2: key = Key::D1; break;
case 3: key = Key::D2; break;
case 4: key = Key::D3; break;
case 5: key = Key::D4; break;
case 6: key = Key::D5; break;
case 7: key = Key::D6; break;
case 8: key = Key::D7; break;
case 9: key = Key::D8; break;
case 10: key = Key::D9; break;
case 11: key = Key::D0; break;
case 12: key = Key::Minus; break;
case 13: key = Key::Equals; break;
case 14: key = Key::Backspace; break;
case 15: key = Key::Tab; break;
case 16: key = Key::Q; break;
case 17: key = Key::W; break;
case 18: key = Key::E; break;
case 19: key = Key::R; break;
case 20: key = Key::T; break;
case 21: key = Key::Y; break;
case 22: key = Key::U; break;
case 23: key = Key::I; break;
case 24: key = Key::O; break;
case 25: key = Key::P; break;
case 26: key = Key::LeftBracket; break;
case 27: key = Key::RightBracket; break;
case 28: key = Key::Enter; break;
case 29: key = Key::LeftControl; break;
case 30: key = Key::A; break;
case 31: key = Key::S; break;
case 32: key = Key::D; break;
case 33: key = Key::F; break;
case 34: key = Key::G; break;
case 35: key = Key::H; break;
case 36: key = Key::J; break;
case 37: key = Key::K; break;
case 38: key = Key::L; break;
case 39: key = Key::Semicolon; break;
case 40: key = Key::Apostrophe; break;
case 41: key = Key::Tilde; break;
case 42: key = Key::LeftShift; break;
case 43: key = Key::Backslash; break;
case 44: key = Key::Z; break;
case 45: key = Key::X; break;
case 46: key = Key::C; break;
case 47: key = Key::V; break;
case 48: key = Key::B; break;
case 49: key = Key::N; break;
case 50: key = Key::M; break;
case 51: key = Key::Comma; break;
case 52: key = Key::Period; break;
case 53: key = Key::Slash; break;
case 54: key = Key::RightShift; break;
case 55: key = Key::PrintScreen; break;
case 56: key = Key::LeftAlt; break;
case 57: key = Key::Space; break;
case 58: key = Key::Capslock; break;
case 59: key = Key::F1; break;
case 60: key = Key::F2; break;
case 61: key = Key::F3; break;
case 62: key = Key::F4; break;
case 63: key = Key::F5; break;
case 64: key = Key::F6; break;
case 65: key = Key::F7; break;
case 66: key = Key::F8; break;
case 67: key = Key::F9; break;
case 68: key = Key::F10; break;
case 71: key = Key::Home; break;
case 72: key = Key::Up; break;
case 73: key = Key::PageUp; break;
case 74: key = Key::KeypadMinus; break;
case 75: key = Key::Left; break;
case 76: key = Key::Keypad5; break;
case 77: key = Key::Right; break;
case 78: key = Key::KeypadPlus; break;
case 79: key = Key::End; break;
case 80: key = Key::Down; break;
case 81: key = Key::PageDown; break;
case 82: key = Key::Insert; break;
case 83: key = Key::Delete; break;
case 87: key = Key::F11; break;
case 88: key = Key::F12; break;
case 89: key = Key::Pause; break;
case 91: key = Key::LeftOS; break;
case 92: key = Key::RightOS; break;
case 93: key = Key::Application; break;
case 100: key = Key::F13; break;
case 101: key = Key::F14; break;
case 102: key = Key::F15; break;
case 103: key = Key::F16; break;
case 104: key = Key::F17; break;
case 105: key = Key::F18; break;
case 106: key = Key::F19; break;
}
}
return key;
}
#endif // BLAH_PLATFORM_WINDOWS

View File

@ -43,16 +43,6 @@ Vec2 Calc::approach(const Vec2& t, const Vec2& target, float delta)
return t + (target - t).normal() * delta; return t + (target - t).normal() * delta;
} }
float Calc::clamp(float t, float min, float max)
{
return t < min ? min : (t > max ? max : t);
}
int Calc::clamp_int(int t, int min, int max)
{
return t < min ? min : (t > max ? max : t);
}
float Calc::map(float t, float old_min, float old_max, float new_min, float new_max) float Calc::map(float t, float old_min, float old_max, float new_min, float new_max)
{ {
return new_min + ((t - old_min) / (old_max - old_min)) * (new_max - new_min); return new_min + ((t - old_min) / (old_max - old_min)) * (new_max - new_min);

View File

@ -1,4 +1,5 @@
#include <blah/math/color.h> #include <blah/math/color.h>
#include <blah/math/vec3.h>
#include <blah/math/vec4.h> #include <blah/math/vec4.h>
using namespace Blah; using namespace Blah;
@ -7,45 +8,69 @@ char const hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B
#define LT_HEX_VALUE(n) ((n >= '0' && n <= '9') ? (n - '0') : ((n >= 'A' && n <= 'F') ? (10 + n - 'A') : ((n >= 'a' && n <= 'f') ? (10 + n - 'a') : 0))) #define LT_HEX_VALUE(n) ((n >= '0' && n <= '9') ? (n - '0') : ((n >= 'A' && n <= 'F') ? (10 + n - 'A') : ((n >= 'a' && n <= 'f') ? (10 + n - 'a') : 0)))
Color::Color() Color::Color()
: r(0), g(0), b(0), a(0) {} : r(0)
, g(0)
, b(0)
, a(0) {}
Color::Color(int rgb) : Color::Color(int rgb)
r((uint8_t)((rgb & 0xFF0000) >> 16)), : r((u8)((rgb & 0xFF0000) >> 16))
g((uint8_t)((rgb & 0x00FF00) >> 8)), , g((u8)((rgb & 0x00FF00) >> 8))
b((uint8_t)(rgb & 0x0000FF)), , b((u8)(rgb & 0x0000FF))
a(255) {} , a(255) {}
Color::Color(int rgb, float alpha) : Color::Color(int rgb, float alpha)
r((int)(((uint8_t)((rgb & 0xFF0000) >> 16)) * alpha)), : r((int)(((u8)((rgb & 0xFF0000) >> 16)) * alpha))
g((int)(((uint8_t)((rgb & 0x00FF00) >> 8)) * alpha)), , g((int)(((u8)((rgb & 0x00FF00) >> 8)) * alpha))
b((int)(((uint8_t)(rgb & 0x0000FF)) * alpha)), , b((int)(((u8)(rgb & 0x0000FF)) * alpha))
a((int)(255 * alpha)) {} , a((int)(255 * alpha)) {}
Color::Color(uint8_t r, uint8_t g, uint8_t b) Color::Color(u8 r, u8 g, u8 b)
: r(r), g(g), b(b), a(255) {} : r(r)
, g(g)
, b(b)
, a(255) {}
Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) Color::Color(u8 r, u8 g, u8 b, u8 a)
: r(r), g(g), b(b), a(a) {} : r(r)
, g(g)
, b(b)
, a(a) {}
Color::Color(const Vec3& vec3)
: r((int)(vec3.x * 255))
, g((int)(vec3.y * 255))
, b((int)(vec3.z * 255))
, a((int)(255)) {}
Color::Color(const Vec3& vec3, float alpha)
: r((int)(vec3.x * alpha * 255))
, g((int)(vec3.y* alpha * 255))
, b((int)(vec3.z* alpha * 255))
, a((int)(alpha * 255)) {}
Color::Color(const Vec4& vec4) Color::Color(const Vec4& vec4)
: r((int)(vec4.x * 255)), g((int)(vec4.y * 255)), b((int)(vec4.z * 255)), a((int)(vec4.w * 255)) {} : r((int)(vec4.x * 255))
, g((int)(vec4.y * 255))
, b((int)(vec4.z * 255))
, a((int)(vec4.w * 255)) {}
Color::Color(const char* value) : r(0), g(0), b(0), a(255) Color::Color(const String& value)
: r(0)
, g(0)
, b(0)
, a(255)
{ {
int length = 0;
while (value[length] != '\0' && length < 10)
length ++;
int offset = 0; int offset = 0;
if (length > 0 && value[0] == '#') if (value.length() > 0 && value[0] == '#')
offset = 1; offset = 1;
else if (length >= 1 && value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) else if (value.length() >= 1 && value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
offset = 2; offset = 2;
if (length - offset >= 8) if (value.length() - offset >= 8)
a = (LT_HEX_VALUE(value[offset + 6]) << 4) + LT_HEX_VALUE(value[offset + 7]); a = (LT_HEX_VALUE(value[offset + 6]) << 4) + LT_HEX_VALUE(value[offset + 7]);
if (length - offset >= 6) if (value.length() - offset >= 6)
{ {
r = (LT_HEX_VALUE(value[offset + 0]) << 4) + LT_HEX_VALUE(value[offset + 1]); r = (LT_HEX_VALUE(value[offset + 0]) << 4) + LT_HEX_VALUE(value[offset + 1]);
g = (LT_HEX_VALUE(value[offset + 2]) << 4) + LT_HEX_VALUE(value[offset + 3]); g = (LT_HEX_VALUE(value[offset + 2]) << 4) + LT_HEX_VALUE(value[offset + 3]);
@ -60,13 +85,13 @@ void Color::premultiply()
b = b * a / 255; b = b * a / 255;
} }
uint32_t Color::to_rgba() const u32 Color::to_rgba() const
{ {
return return
((uint32_t)r << 24) | ((u32)r << 24) |
((uint32_t)g << 16) | ((u32)g << 16) |
((uint32_t)b << 8) | ((u32)b << 8) |
(uint32_t)a; (u32)a;
} }
Vec4 Color::to_vec4() const Vec4 Color::to_vec4() const
@ -110,24 +135,24 @@ String Color::to_hex_rgb() const
return str; return str;
} }
Color Color::from_rgba(uint32_t value) Color Color::from_rgba(u32 value)
{ {
return return
{ {
(uint8_t)((value & 0xFF000000) >> 24), (u8)((value & 0xFF000000) >> 24),
(uint8_t)((value & 0x00FF0000) >> 16), (u8)((value & 0x00FF0000) >> 16),
(uint8_t)((value & 0x0000FF00) >> 8), (u8)((value & 0x0000FF00) >> 8),
(uint8_t)((value & 0x000000FF)) (u8)((value & 0x000000FF))
}; };
} }
Color Color::from_rgb(uint32_t value) Color Color::from_rgb(u32 value)
{ {
return return
{ {
(uint8_t)((value & 0xFF0000) >> 16), (u8)((value & 0xFF0000) >> 16),
(uint8_t)((value & 0x00FF00) >> 8), (u8)((value & 0x00FF00) >> 8),
(uint8_t)((value & 0x0000FF)) (u8)((value & 0x0000FF))
}; };
} }
@ -137,10 +162,10 @@ Color Color::lerp(Color a, Color b, float amount)
if (amount > 1) amount = 1; if (amount > 1) amount = 1;
return Color( return Color(
(uint8_t)(a.r + (b.r - a.r) * amount), (u8)(a.r + (b.r - a.r) * amount),
(uint8_t)(a.g + (b.g - a.g) * amount), (u8)(a.g + (b.g - a.g) * amount),
(uint8_t)(a.b + (b.b - a.b) * amount), (u8)(a.b + (b.b - a.b) * amount),
(uint8_t)(a.a + (b.a - a.a) * amount) (u8)(a.a + (b.a - a.a) * amount)
); );
} }
@ -155,9 +180,9 @@ Color Color::operator*(float multiply) const
Color& Color::operator=(const int rgb) Color& Color::operator=(const int rgb)
{ {
r = (uint8_t)((rgb & 0xFF0000) >> 16); r = (u8)((rgb & 0xFF0000) >> 16);
g = (uint8_t)((rgb & 0x00FF00) >> 8); g = (u8)((rgb & 0x00FF00) >> 8);
b = (uint8_t)(rgb & 0x0000FF); b = (u8)(rgb & 0x0000FF);
a = 255; a = 255;
return *this; return *this;
} }

View File

@ -31,7 +31,7 @@ Vec2 Line::closest_point(const Vec2& pt) const
Vec2 v = b - a; Vec2 v = b - a;
Vec2 w = pt - a; Vec2 w = pt - a;
float t = Vec2::dot(w, v) / Vec2::dot(v, v); float t = Vec2::dot(w, v) / Vec2::dot(v, v);
t = Calc::clamp(t, 0, 1); t = Calc::clamp(t, 0.0f, 1.0f);
return v * t + a; return v * t + a;
} }

View File

@ -1,5 +1,5 @@
#include <blah/math/mat4x4.h> #include <blah/math/mat4x4.h>
#include <blah/core/log.h> #include <blah/core/common.h>
using namespace Blah; using namespace Blah;

View File

@ -14,13 +14,12 @@ void Stopwatch::reset()
start_time = std::chrono::duration_cast<std::chrono::microseconds>(system_clock::now().time_since_epoch()).count(); start_time = std::chrono::duration_cast<std::chrono::microseconds>(system_clock::now().time_since_epoch()).count();
} }
uint64_t Stopwatch::milliseconds() u64 Stopwatch::milliseconds()
{ {
return microseconds() / 1000; return microseconds() / 1000;
} }
u64 Stopwatch::microseconds()
uint64_t Stopwatch::microseconds()
{ {
return std::chrono::duration_cast<std::chrono::microseconds>(system_clock::now().time_since_epoch()).count() - start_time; return std::chrono::duration_cast<std::chrono::microseconds>(system_clock::now().time_since_epoch()).count() - start_time;
} }

View File

@ -42,7 +42,7 @@ BufferStream::~BufferStream()
delete[] m_buffer; delete[] m_buffer;
} }
int64_t BufferStream::read_into(void* ptr, int64_t len) i64 BufferStream::read_into(void* ptr, i64 len)
{ {
if (m_buffer == nullptr || ptr == nullptr) if (m_buffer == nullptr || ptr == nullptr)
return 0; return 0;
@ -58,7 +58,7 @@ int64_t BufferStream::read_into(void* ptr, int64_t len)
return len; return len;
} }
int64_t BufferStream::write_from(const void* ptr, int64_t len) i64 BufferStream::write_from(const void* ptr, i64 len)
{ {
if (len < 0) if (len < 0)
return 0; return 0;

View File

@ -1,5 +1,5 @@
#include <blah/streams/filestream.h> #include <blah/streams/filestream.h>
#include <blah/core/log.h> #include <blah/core/common.h>
#include "../internal/platform_backend.h" #include "../internal/platform_backend.h"
#include <string.h> #include <string.h>
@ -11,7 +11,7 @@ FileStream::FileStream()
m_mode = FileMode::None; m_mode = FileMode::None;
} }
FileStream::FileStream(const char* path, FileMode mode) FileStream::FileStream(const FilePath& path, FileMode mode)
: m_mode(mode) : m_mode(mode)
{ {
if (!PlatformBackend::file_open(path, &m_handle, mode)) if (!PlatformBackend::file_open(path, &m_handle, mode))
@ -39,7 +39,7 @@ FileStream::~FileStream()
PlatformBackend::file_close(m_handle); PlatformBackend::file_close(m_handle);
} }
int64_t FileStream::length() const i64 FileStream::length() const
{ {
if (m_handle == nullptr) if (m_handle == nullptr)
return 0; return 0;
@ -47,7 +47,7 @@ int64_t FileStream::length() const
return PlatformBackend::file_length(m_handle); return PlatformBackend::file_length(m_handle);
} }
int64_t FileStream::position() const i64 FileStream::position() const
{ {
if (m_handle == nullptr) if (m_handle == nullptr)
return 0; return 0;
@ -55,7 +55,7 @@ int64_t FileStream::position() const
return PlatformBackend::file_position(m_handle); return PlatformBackend::file_position(m_handle);
} }
int64_t FileStream::seek(int64_t seek_to) i64 FileStream::seek(i64 seek_to)
{ {
if (m_handle == nullptr) if (m_handle == nullptr)
return 0; return 0;
@ -63,7 +63,22 @@ int64_t FileStream::seek(int64_t seek_to)
return PlatformBackend::file_seek(m_handle, seek_to); return PlatformBackend::file_seek(m_handle, seek_to);
} }
int64_t FileStream::read_into(void* ptr, int64_t length) bool FileStream::is_open() const
{
return m_handle != nullptr;
}
bool FileStream::is_readable() const
{
return m_handle != nullptr && (m_mode == FileMode::ReadWrite || m_mode == FileMode::Read);
}
bool FileStream::is_writable() const
{
return m_handle != nullptr && (m_mode == FileMode::ReadWrite || m_mode == FileMode::Write);
}
i64 FileStream::read_into(void* ptr, i64 length)
{ {
if (m_handle == nullptr) if (m_handle == nullptr)
{ {
@ -74,7 +89,7 @@ int64_t FileStream::read_into(void* ptr, int64_t length)
return PlatformBackend::file_read(m_handle, ptr, length); return PlatformBackend::file_read(m_handle, ptr, length);
} }
int64_t FileStream::write_from(const void* ptr, int64_t length) i64 FileStream::write_from(const void* ptr, i64 length)
{ {
if (length <= 0) if (length <= 0)
return 0; return 0;

View File

@ -6,7 +6,7 @@ using namespace Blah;
MemoryStream::MemoryStream() MemoryStream::MemoryStream()
: m_data(nullptr), m_length(0), m_position(0) {} : m_data(nullptr), m_length(0), m_position(0) {}
MemoryStream::MemoryStream(char* data, int64_t length) MemoryStream::MemoryStream(char* data, i64 length)
: m_data(data), m_length(length), m_position(0) {} : m_data(data), m_length(length), m_position(0) {}
MemoryStream::MemoryStream(MemoryStream&& src) noexcept MemoryStream::MemoryStream(MemoryStream&& src) noexcept
@ -28,7 +28,7 @@ MemoryStream& MemoryStream::operator=(MemoryStream&& src) noexcept
return *this; return *this;
} }
int64_t MemoryStream::read_into(void* ptr, int64_t len) i64 MemoryStream::read_into(void* ptr, i64 len)
{ {
if (len < 0 || ptr == nullptr) if (len < 0 || ptr == nullptr)
return 0; return 0;
@ -41,7 +41,7 @@ int64_t MemoryStream::read_into(void* ptr, int64_t len)
return len; return len;
} }
int64_t MemoryStream::write_from(const void* ptr, int64_t len) i64 MemoryStream::write_from(const void* ptr, i64 len)
{ {
if (len < 0 || ptr == nullptr) if (len < 0 || ptr == nullptr)
return 0; return 0;

View File

@ -4,10 +4,10 @@
using namespace Blah; using namespace Blah;
int64_t Stream::pipe(Stream& stream, int64_t length) i64 Stream::pipe(Stream& stream, i64 length)
{ {
const int BUFFER_LENGTH = 4096; const int BUFFER_LENGTH = 4096;
int64_t result = 0; i64 result = 0;
char buffer[BUFFER_LENGTH]; char buffer[BUFFER_LENGTH];
while (length > 0) while (length > 0)
@ -59,12 +59,12 @@ String Stream::read_line()
return result; return result;
} }
int64_t Stream::write(const void* buffer, int64_t length) i64 Stream::write(const void* buffer, i64 length)
{ {
return write_from(buffer, length); return write_from(buffer, length);
} }
int64_t Stream::write(const String& string) i64 Stream::write(const String& string)
{ {
return write_from(string.begin(), string.length()); return write_from(string.begin(), string.length());
} }