first true commit

This commit is contained in:
Noel Berry
2020-08-26 00:38:01 -07:00
parent c20900d690
commit 8d7e5e4224
88 changed files with 26844 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include <blah/graphics/graphics.h>
namespace Blah
{
namespace Internal
{
class GraphicsDevice;
// graphics device metadata used to instantiate and destroy
// devices of specific apis
struct GraphicsDeviceInfo
{
GfxAPI api;
bool (*supported)();
GraphicsDevice* (*create)();
void (*destroy)(GraphicsDevice*);
};
// graphics implementations
extern GraphicsDeviceInfo OpenGL_DeviceInfo;
// graphics device
// each graphics implementation needs to implement this
// ex. one for opengl, one for vulkan, etc
class GraphicsDevice
{
public:
bool valid = false;
GraphicsInfo info;
virtual void startup() = 0;
virtual void update() = 0;
virtual void shutdown() = 0;
virtual void before_render() = 0;
virtual void after_render() = 0;
virtual TextureRef create_texture(int width, int height, TextureFilter filter, TextureWrap wrap_x, TextureWrap wrap_y, TextureFormat format) = 0;
virtual FrameBufferRef create_framebuffer(int width, int height, const TextureFormat* attachments, int attachmentCount) = 0;
virtual ShaderRef create_shader(const ShaderData* data) = 0;
virtual MeshRef create_mesh() = 0;
virtual void render(RenderCall* call) = 0;
virtual void clear(const FrameBufferRef& target, uint32_t rgba) = 0;
};
namespace Graphics
{
// Picks a graphics API based on the available APIs
GfxAPI pick_api();
// Initializes the Graphics with the given API
bool init(GfxAPI api);
// Is called internally by the Application every update
void frame();
// Is called internally by the Application before every render
void before_render();
// Is called internally by the Application after every render
void after_render();
// Called when the Application is shutting down
void shutdown();
}
}
}
File diff suppressed because it is too large Load Diff
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include <blah/input/input.h>
namespace Blah
{
namespace Internal
{
namespace Input
{
// This is called internally by the app, and initializes the input state
void init();
// This is called internally by the app, and updates the input state
void frame();
// Call this when the Mouse moves relative to the window
void on_mouse_move(float x, float y);
// Call this when the Mouse moves relative to the screen
void on_mouse_screen_move(float x, float y);
// Call this when a Mouse Button is pressed
void on_mouse_down(MouseButton button);
// Call this when a Mouse Button is released
void on_mouse_up(MouseButton button);
// Call this when the Mouse Wheel moves
void on_mouse_wheel(Point wheel);
// Call this when a keyboard key is pressed
void on_key_down(Key key);
// Call this when a keyboard key is released
void on_key_up(Key key);
// Call this on Text Input
void on_text_utf8(const char* text);
// 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.
void on_controller_connect(int index, const char* name, int isGamepad, int buttonCount, int axisCount);
// Call this when a controller is disconnected
void on_controller_disconnect(int index);
// Call this when a controller button is pressed
void on_button_down(int index, int button);
// Call this when a controller button is released
void on_button_up(int index, int button);
/// Call this when a controller axis is moved
void on_axis_move(int index, int axis, float value);
}
}
}
+120
View File
@@ -0,0 +1,120 @@
#pragma once
#include <inttypes.h>
#include <blah/filesystem.h>
#include <blah/containers/list.h>
namespace Blah
{
struct Config;
enum class FileMode;
namespace Internal
{
namespace Platform
{
typedef void* FileHandle;
// Initialize the System
bool init(const Config* config);
// Called after the on_startup callback, but before the update loop begins
void ready();
// Called during shutdown
void shutdown();
// The time, in milliseconds, since the Application was started
uint64_t time();
// Called every frame
void frame();
// Sleeps the current thread
void sleep(int milliseconds);
// Called to present the window contents
void present();
// Gets the Application Window Title in UTF-8
const char* get_title();
// Sets the Application Window Title in UTF-8
void set_title(const char* title);
// Gets the Application Window Position, in Screen Coordinates
void get_position(int* x, int* y);
// Sets the Application Window Position, in Screen Coordinates
void set_position(int x, int y);
// Sets the Window Fullscreen if enabled is not 0
void set_fullscreen(bool enabled);
// Gets the Application Window Size, in Screen Coordinates
void get_size(int* width, int* height);
// Sets the Application Window Size, in Screen Coordinates
void set_size(int width, int height);
// Gets the Application Window Drawing Size, in Pixels. This may differ from the Window Size on hi-dpi displays.
void get_draw_size(int* width, int* height);
// Gets the Desktop Content Scale. Gui should be scaled by this value
float get_content_scale();
// Returns the absoluate path to the directory that the application was started from
const char* app_path();
// Returns the absolute path to the user directory where save data and settings should be stored
const char* user_path();
// Returns true if a file with the given path exists
bool file_exists(const char* path);
// Returns true if a file with the given path was deleted
bool file_delete(const char* path);
// Returns true if a directory with the given path was successfully created
bool dir_create(const char* path);
// Returns true if a directory with the given path exists
bool dir_exists(const char* path);
// Returns true if a directory with the given path was deleted
bool dir_delete(const char* path);
// enumerates a directory and appends each file to the given list
void dir_enumerate(List<FilePath>& list, const char* path, bool recursive);
// opens a directory in the OS file explorer / finder
void dir_explore(const char* path);
// Opens a file and sets the handle. returns true if the file was successfully opened
bool file_open(const char* path, FileHandle* handle, FileMode mode);
// Returns the length of the file
int64_t file_length(FileHandle file);
// Returns the Position of the file
int64_t file_position(FileHandle 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);
// 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);
// 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);
// Closes a file
void file_close(FileHandle file);
// OpenGL Methods
void* gl_get_func(const char* name);
void* gl_context_create();
void gl_context_make_current(void* context);
void gl_context_destroy(void* context);
}
}
}
+652
View File
@@ -0,0 +1,652 @@
#ifdef BLAH_USE_SDL2
#include <blah/internal/platform.h>
#include <blah/internal/input.h>
#include <blah/input/input.h>
#include <blah/app.h>
#include <blah/filesystem.h>
#include <blah/log.h>
#include <SDL.h>
#include <SDL_vulkan.h>
#if _WIN32
// on Windows we're using the C++ <filesystem> API for now
#include <windows.h>
#include <winuser.h> // for SetProcessDPIAware
#include <filesystem> // for File Reading/Writing
#include <shellapi.h> // for file explore
namespace fs = std::filesystem;
#else
// on non-Windows we use POSIX standard file system stuff
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#endif
using namespace Blah;
using namespace Internal;
namespace
{
SDL_Window* window = nullptr;
SDL_Joystick* joysticks[BLAH_MAX_CONTROLLERS];
SDL_GameController* gamepads[BLAH_MAX_CONTROLLERS];
char* basePath = nullptr;
char* userPath = nullptr;
bool displayed = false;
void sdl_log(void* userdata, int category, SDL_LogPriority priority, const char* message)
{
if (priority <= SDL_LOG_PRIORITY_INFO)
Log::print(message);
else if (priority <= SDL_LOG_PRIORITY_WARN)
Log::warn(message);
else
Log::error(message);
}
}
bool Platform::init(const Config* config)
{
// Required to call this for Windows
// I'm not sure why SDL2 doesn't do this on Windows automatically?
#if _WIN32
SetProcessDPIAware();
#endif
// TODO:
// control this via some kind of config flag
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
SDL_LogSetOutputFunction(sdl_log, nullptr);
// Get SDL version
SDL_version version;
SDL_GetVersion(&version);
Log::print("SDL v%i.%i.%i", version.major, version.minor, version.patch);
// initialize SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
{
Log::error("Failed to initialize SDL2");
return false;
}
// GL Attributes
if (config->graphics == GfxAPI::OpenGL)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// TODO:
// This should be controlled via the gfx api somehow?
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
}
// set up window flags
int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
if (config->graphics == GfxAPI::OpenGL)
flags |= SDL_WINDOW_OPENGL;
// create the window
window = SDL_CreateWindow(config->name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config->width, config->height, flags);
if (window == nullptr)
{
Log::error("Failed to create a Window");
return false;
}
// Scale Window to monitor for High DPI displays
// Other platforms do this automatically ... Windows we need to explitely do so
#if _WIN32
{
// find the display index
int display = SDL_GetWindowDisplayIndex(window);
float ddpi, hdpi, vdpi;
if (SDL_GetDisplayDPI(display, &ddpi, &hdpi, &vdpi) == 0)
{
// scale the window up basesd on the display DPI
float hidpiRes = 96;
float dpi = (ddpi / hidpiRes);
if (dpi != 1)
{
SDL_DisplayMode mode;
SDL_GetDesktopDisplayMode(display, &mode);
SDL_SetWindowPosition(window, (int)(mode.w - config->width * dpi) / 2, (int)(mode.h - config->height * dpi) / 2);
SDL_SetWindowSize(window, (int)(config->width * dpi), (int)(config->height * dpi));
}
}
}
#endif
// set window properties
SDL_SetWindowResizable(window, SDL_TRUE);
SDL_SetWindowMinimumSize(window, 256, 256);
return true;
}
void Platform::ready()
{
// enable V-Sync
if (App::config()->graphics == GfxAPI::OpenGL)
SDL_GL_SetSwapInterval(1);
}
void Platform::shutdown()
{
if (window != nullptr)
SDL_DestroyWindow(window);
window = nullptr;
displayed = false;
if (basePath != nullptr)
SDL_free(basePath);
if (userPath != nullptr)
SDL_free(userPath);
SDL_Quit();
}
uint64_t Platform::time()
{
return (uint64_t)SDL_GetTicks();
}
void Platform::frame()
{
// update the mouse every frame
{
int winX, winY, x, y;
SDL_GetWindowPosition(window, &winX, &winY);
SDL_GetGlobalMouseState(&x, &y);
Internal::Input::on_mouse_move((float)(x - winX), (float)(y - winY));
Internal::Input::on_mouse_screen_move((float)x, (float)y);
}
// poll normal events
SDL_Event event;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
auto config = App::config();
if (config->on_exit_request != nullptr)
config->on_exit_request();
}
// Mouse
else if (event.type == SDL_MOUSEBUTTONDOWN)
{
MouseButton btn = MouseButton::None;
if (event.button.button == SDL_BUTTON_LEFT)
btn = MouseButton::Left;
else if (event.button.button == SDL_BUTTON_RIGHT)
btn = MouseButton::Right;
else if (event.button.button == SDL_BUTTON_MIDDLE)
btn = MouseButton::Middle;
Internal::Input::on_mouse_down(btn);
}
else if (event.type == SDL_MOUSEBUTTONUP)
{
MouseButton btn = MouseButton::None;
if (event.button.button == SDL_BUTTON_LEFT)
btn = MouseButton::Left;
else if (event.button.button == SDL_BUTTON_RIGHT)
btn = MouseButton::Right;
else if (event.button.button == SDL_BUTTON_MIDDLE)
btn = MouseButton::Middle;
Internal::Input::on_mouse_up(btn);
}
else if (event.type == SDL_MOUSEWHEEL)
{
Internal::Input::on_mouse_wheel(Point(event.wheel.x, event.wheel.y));
}
// Keyboard
else if (event.type == SDL_KEYDOWN)
{
if (event.key.repeat == 0)
Internal::Input::on_key_down((Key)event.key.keysym.scancode);
}
else if (event.type == SDL_KEYUP)
{
if (event.key.repeat == 0)
Internal::Input::on_key_up((Key)event.key.keysym.scancode);
}
else if (event.type == SDL_TEXTINPUT)
{
Internal::Input::on_text_utf8(event.text.text);
}
// Joystick Controller
else if (event.type == SDL_JOYDEVICEADDED)
{
Sint32 index = event.jdevice.which;
if (SDL_IsGameController(index) == SDL_FALSE)
{
SDL_Joystick* ptr = joysticks[index] = SDL_JoystickOpen(index);
const char* name = SDL_JoystickName(ptr);
int button_count = SDL_JoystickNumButtons(ptr);
int axis_count = SDL_JoystickNumAxes(ptr);
Internal::Input::on_controller_connect(index, name, 0, button_count, axis_count);
}
}
else if (event.type == SDL_JOYDEVICEREMOVED)
{
Sint32 index = event.jdevice.which;
if (SDL_IsGameController(index) == SDL_FALSE)
{
Internal::Input::on_controller_disconnect(index);
SDL_JoystickClose(joysticks[index]);
}
}
else if (event.type == SDL_JOYBUTTONDOWN)
{
Sint32 index = event.jdevice.which;
if (SDL_IsGameController(index) == SDL_FALSE)
Internal::Input::on_button_down(index, event.jbutton.button);
}
else if (event.type == SDL_JOYBUTTONUP)
{
Sint32 index = event.jdevice.which;
if (SDL_IsGameController(index) == SDL_FALSE)
Internal::Input::on_button_up(index, event.jbutton.button);
}
else if (event.type == SDL_JOYAXISMOTION)
{
Sint32 index = event.jaxis.which;
if (SDL_IsGameController(index) == SDL_FALSE)
{
float value;
if (event.jaxis.value >= 0)
value = event.jaxis.value / 32767.0f;
else
value = event.jaxis.value / 32768.0f;
Internal::Input::on_axis_move(index, event.jaxis.axis, value);
}
}
// Gamepad Controller
else if (event.type == SDL_CONTROLLERDEVICEADDED)
{
Sint32 index = event.cdevice.which;
SDL_GameController* ptr = gamepads[index] = SDL_GameControllerOpen(index);
const char* name = SDL_GameControllerName(ptr);
Internal::Input::on_controller_connect(index, name, 1, 15, 6);
}
else if (event.type == SDL_CONTROLLERDEVICEREMOVED)
{
Sint32 index = event.cdevice.which;
Internal::Input::on_controller_disconnect(index);
SDL_GameControllerClose(gamepads[index]);
}
else if (event.type == SDL_CONTROLLERBUTTONDOWN)
{
Sint32 index = event.cbutton.which;
int button = (int)Button::None;
if (event.cbutton.button >= 0 && event.cbutton.button < 15)
button = event.cbutton.button; // NOTE: These map directly to Engine Buttons enum!
Internal::Input::on_button_down(index, button);
}
else if (event.type == SDL_CONTROLLERBUTTONUP)
{
Sint32 index = event.cbutton.which;
int button = (int)Button::None;
if (event.cbutton.button >= 0 && event.cbutton.button < 15)
button = event.cbutton.button; // NOTE: These map directly to Engine Buttons enum!
Internal::Input::on_button_up(index, button);
}
else if (event.type == SDL_CONTROLLERAXISMOTION)
{
Sint32 index = event.caxis.which;
int axis = (int)Axis::None;
if (event.caxis.axis >= 0 && event.caxis.axis < 6)
axis = event.caxis.axis; // NOTE: These map directly to Engine Axis enum!
float value;
if (event.caxis.value >= 0)
value = event.caxis.value / 32767.0f;
else
value = event.caxis.value / 32768.0f;
Internal::Input::on_axis_move(index, axis, value);
}
}
}
void Platform::sleep(int milliseconds)
{
if (milliseconds >= 0)
SDL_Delay((uint32_t)milliseconds);
}
void Platform::present()
{
if (App::config()->graphics == GfxAPI::OpenGL)
{
SDL_GL_SwapWindow(window);
}
// display the window
// this avoids a short black screen on macoS
if (!displayed)
{
SDL_ShowWindow(window);
displayed = true;
}
}
const char* Platform::get_title()
{
return nullptr;
}
void Platform::set_title(const char* title)
{
SDL_SetWindowTitle(window, title);
}
void Platform::get_position(int* x, int* y)
{
SDL_GetWindowPosition(window, x, y);
}
void Platform::set_position(int x, int y)
{
SDL_SetWindowPosition(window, x, y);
}
void Platform::set_fullscreen(bool enabled)
{
if (enabled)
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
else
SDL_SetWindowFullscreen(window, 0);
}
void Platform::get_size(int* width, int* height)
{
SDL_GetWindowSize(window, width, height);
}
void Platform::set_size(int width, int height)
{
SDL_SetWindowSize(window, width, height);
}
void Platform::get_draw_size(int* width, int* height)
{
auto config = App::config();
if (config->graphics == GfxAPI::OpenGL)
{
SDL_GL_GetDrawableSize(window, width, height);
}
/*
else if (config->graphics == GfxAPI::Vulkan)
{
SDL_Vulkan_GetDrawableSize(window, width, height);
}
*/
else
{
SDL_GetWindowSize(window, width, height);
}
}
float Platform::get_content_scale()
{
// TODO:
// This is incorrect! but for some reason the scale
// is HUGE if I use the Display DPI on macOS :/
#if __APPLE__
return 2.0f;
#endif
#if _WIN32
float hidpiRes = 96;
#else
float hidpiRes = 72;
#endif
int index = SDL_GetWindowDisplayIndex(window);
if (index < 0)
Log::error(SDL_GetError());
float ddpi, x, y;
if (SDL_GetDisplayDPI(index, &ddpi, &x, &y) != 0)
Log::error(SDL_GetError());
return (ddpi / hidpiRes);
}
// FILE IO
const char* Platform::app_path()
{
if (basePath == nullptr)
basePath = SDL_GetBasePath();
return basePath;
}
const char* Platform::user_path()
{
if (userPath == nullptr)
{
const Config* config = App::config();
userPath = SDL_GetPrefPath(nullptr, config->name);
}
return userPath;
}
// Windows File System methods
#if _WIN32
bool Platform::file_exists(const char* path)
{
return fs::is_regular_file(path);
}
bool Platform::file_delete(const char* path)
{
return fs::remove(path);
}
bool Platform::dir_create(const char* path)
{
std::error_code error;
return fs::create_directories(path, error);
}
bool Platform::dir_exists(const char* path)
{
return fs::is_directory(path);
}
bool Platform::dir_delete(const char* path)
{
BLAH_ERROR("not implemented");
return false;
}
void Platform::dir_enumerate(List<FilePath>& list, const char* path, bool recursive)
{
if (fs::is_directory(path))
{
if (recursive)
{
for (auto& p : fs::recursive_directory_iterator(path))
{
FilePath str(p.path().string().c_str());
list.add(str);
}
}
else
{
for (auto& p : fs::directory_iterator(path))
{
FilePath str(p.path().string().c_str());
list.add(str);
}
}
}
}
void Platform::dir_explore(const char* path)
{
ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT);
}
// Non-Windows File System Methods
#else
bool Platform::file_exists(const char* path)
{
struct stat buffer;
return (stat(path, &buffer) == 0) && S_ISREG(buffer.st_mode);
}
bool Platform::file_delete(const char* path)
{
BLAH_ERROR("not implemented");
return false;
}
bool Platform::dir_create(const char* path)
{
char tmp[265];
char* p = NULL;
size_t len;
snprintf(tmp, sizeof(tmp), "%s", path);
len = strlen(tmp);
if (tmp[len - 1] == '/')
tmp[len - 1] = 0;
for (p = tmp + 1; *p; p++)
if (*p == '/') {
*p = 0;
mkdir(tmp, S_IRWXU);
*p = '/';
}
return mkdir(tmp, S_IRWXU) == 0;
}
bool Platform::dir_exists(const char* path)
{
struct stat buffer;
return (stat(path, &buffer) == 0) && S_ISDIR(buffer.st_mode);
}
bool Platform::dir_delete(const char* path)
{
BLAH_ERROR("not implemented");
return false;
}
void Platform::dir_enumerate(List<FilePath>& list, const char* path, bool recursive)
{
DIR* dirp = opendir(path);
if (dirp != NULL)
{
struct dirent* dp;
while ((dp = readdir(dirp)) != NULL)
{
if (dp->d_name[0] == '.')
continue;
FilePath subpath = FilePath(path).Append(dp->d_name);
list.Add(subpath);
if (recursive && dp->d_type == DT_DIR)
dir_enumerate(list, subpath + "/", true);
}
closedir(dirp);
}
}
void Platform::dir_explore(const char* path)
{
BLAH_ERROR("not implemented");
}
#endif
bool Platform::file_open(const char* path, Platform::FileHandle* handle, FileMode mode)
{
const char* sdlMode = "rb";
if (mode == FileMode::Write)
sdlMode = "wb";
auto ptr = SDL_RWFromFile(path, sdlMode);
*handle = (Platform::FileHandle)ptr;
return ptr != nullptr;
}
int64_t Platform::file_length(Platform::FileHandle stream)
{
return SDL_RWsize((SDL_RWops*)stream);
}
int64_t Platform::file_position(Platform::FileHandle stream)
{
return SDL_RWtell((SDL_RWops*)stream);
}
int64_t Platform::file_seek(Platform::FileHandle stream, int64_t seekTo)
{
return SDL_RWseek((SDL_RWops*)stream, seekTo, RW_SEEK_SET);
}
int64_t Platform::file_read(Platform::FileHandle stream, void* ptr, int64_t length)
{
return SDL_RWread((SDL_RWops*)stream, ptr, sizeof(char), length);
}
int64_t Platform::file_write(Platform::FileHandle stream, const void* ptr, int64_t length)
{
return SDL_RWwrite((SDL_RWops*)stream, ptr, sizeof(char), length);
}
void Platform::file_close(Platform::FileHandle stream)
{
if (stream != nullptr)
SDL_RWclose((SDL_RWops*)stream);
}
void* Platform::gl_get_func(const char* name)
{
return SDL_GL_GetProcAddress(name);
}
void* Platform::gl_context_create()
{
void* pointer = SDL_GL_CreateContext(window);
if (pointer == nullptr)
Log::error("SDL_GL_CreateContext failed: %s", SDL_GetError());
return pointer;
}
void Platform::gl_context_make_current(void* context)
{
SDL_GL_MakeCurrent(window, context);
}
void Platform::gl_context_destroy(void* context)
{
SDL_GL_DeleteContext(context);
}
#endif // BLAH_USE_SDL2
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff