diff --git a/CMakeLists.txt b/CMakeLists.txt index c2fd243..3454211 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,27 +6,23 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(blah + src/app.cpp + src/filesystem.cpp + src/common.cpp + src/time.cpp + src/input.cpp - src/core/app.cpp - src/core/filesystem.cpp - src/core/common.cpp - src/core/time.cpp - src/graphics/batch.cpp src/graphics/blend.cpp - src/graphics/framebuffer.cpp src/graphics/material.cpp src/graphics/mesh.cpp src/graphics/renderpass.cpp src/graphics/shader.cpp src/graphics/spritefont.cpp src/graphics/subtexture.cpp + src/graphics/target.cpp src/graphics/texture.cpp - src/input/input.cpp - src/input/binding.cpp - src/input/binding_registry.cpp - src/containers/str.cpp src/images/aseprite.cpp @@ -34,18 +30,17 @@ add_library(blah src/images/image.cpp src/images/packer.cpp - src/math/calc.cpp - src/math/circle.cpp - src/math/color.cpp - src/math/line.cpp - src/math/mat3x2.cpp - src/math/mat4x4.cpp - src/math/point.cpp - src/math/quad.cpp - src/math/rect.cpp - src/math/rectI.cpp - src/math/stopwatch.cpp - src/math/vec2.cpp + src/numerics/calc.cpp + src/numerics/circle.cpp + src/numerics/color.cpp + src/numerics/line.cpp + src/numerics/mat3x2.cpp + src/numerics/mat4x4.cpp + src/numerics/point.cpp + src/numerics/quad.cpp + src/numerics/rect.cpp + src/numerics/rectI.cpp + src/numerics/vec2.cpp src/streams/bufferstream.cpp src/streams/filestream.cpp diff --git a/include/blah.h b/include/blah.h index 49adfad..c674724 100644 --- a/include/blah.h +++ b/include/blah.h @@ -1,9 +1,10 @@ #pragma once -#include "blah/core/app.h" -#include "blah/core/filesystem.h" -#include "blah/core/common.h" -#include "blah/core/time.h" +#include "blah/app.h" +#include "blah/filesystem.h" +#include "blah/common.h" +#include "blah/time.h" +#include "blah/input.h" #include "blah/containers/vector.h" #include "blah/containers/stackvector.h" @@ -14,12 +15,12 @@ #include "blah/graphics/subtexture.h" #include "blah/graphics/blend.h" -#include "blah/graphics/framebuffer.h" #include "blah/graphics/material.h" #include "blah/graphics/mesh.h" #include "blah/graphics/renderpass.h" #include "blah/graphics/sampler.h" #include "blah/graphics/shader.h" +#include "blah/graphics/target.h" #include "blah/graphics/texture.h" #include "blah/images/aseprite.h" @@ -27,25 +28,21 @@ #include "blah/images/image.h" #include "blah/images/packer.h" -#include "blah/input/input.h" -#include "blah/input/binding.h" -#include "blah/input/binding_registry.h" -#include "blah/math/calc.h" -#include "blah/math/circle.h" -#include "blah/math/color.h" -#include "blah/math/ease.h" -#include "blah/math/line.h" -#include "blah/math/mat3x2.h" -#include "blah/math/mat4x4.h" -#include "blah/math/point.h" -#include "blah/math/quad.h" -#include "blah/math/rect.h" -#include "blah/math/rectI.h" -#include "blah/math/stopwatch.h" -#include "blah/math/vec2.h" -#include "blah/math/vec3.h" -#include "blah/math/vec4.h" +#include "blah/numerics/calc.h" +#include "blah/numerics/circle.h" +#include "blah/numerics/color.h" +#include "blah/numerics/ease.h" +#include "blah/numerics/line.h" +#include "blah/numerics/mat3x2.h" +#include "blah/numerics/mat4x4.h" +#include "blah/numerics/point.h" +#include "blah/numerics/quad.h" +#include "blah/numerics/rect.h" +#include "blah/numerics/rectI.h" +#include "blah/numerics/vec2.h" +#include "blah/numerics/vec3.h" +#include "blah/numerics/vec4.h" #include "blah/streams/bufferstream.h" #include "blah/streams/filestream.h" diff --git a/include/blah/core/app.h b/include/blah/app.h similarity index 91% rename from include/blah/core/app.h rename to include/blah/app.h index 18e0049..d200149 100644 --- a/include/blah/core/app.h +++ b/include/blah/app.h @@ -1,15 +1,13 @@ #pragma once -#include -#include -#include +#include namespace Blah { // Application Event Functions - using AppEventFn = std::function; + using AppEventFn = Func; // Application Logging Functions - using AppLogFn = std::function; + using AppLogFn = Func; // Application Configuration struct Config @@ -90,8 +88,9 @@ namespace Blah int max_texture_size = 0; }; - class FrameBuffer; - using FrameBufferRef = std::shared_ptr; + // Forward declare Target for the BackBuffer + class Target; + using TargetRef = Ref; // Application namespace App @@ -145,6 +144,6 @@ namespace Blah const RendererFeatures& renderer_features(); // Reference to the window's back buffer - extern const FrameBufferRef backbuffer; + extern const TargetRef backbuffer; } } \ No newline at end of file diff --git a/include/blah/common.h b/include/blah/common.h new file mode 100644 index 0000000..f6708bc --- /dev/null +++ b/include/blah/common.h @@ -0,0 +1,56 @@ +#pragma once +#include // for integer types +#include // for size_t type +#include // for std::shared_ptr +#include // for std::function + +// Asserts +#if defined(DEBUG) || defined(_DEBUG) +#include // for abort +#define BLAH_ASSERT(condition, msg) \ + do { if (!(condition)) { Blah::Log::error("%s\n\tin %s:%d", (msg), __FILE__, __LINE__); abort(); } } while(0) +#else +#define BLAH_ASSERT(condition, msg) \ + do { if (!(condition)) { Blah::Log::error("%s\n\tin %s:%d", (msg), __FILE__, __LINE__); } } while(0) +#endif + +// maximum length of a print/warn/error message +#ifndef BLAH_MESSAGE +#define BLAH_MESSAGE 1024 +#endif + +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; + + template + using Ref = std::shared_ptr; + + template + using WeakRef = std::weak_ptr; + + template + using Func = std::function; + + namespace Log + { + enum class Category + { + Info, + Warning, + Error + }; + + void info(const char* message, ...); + void warn(const char* message, ...); + void error(const char* message, ...); + } +} \ No newline at end of file diff --git a/include/blah/containers/stackvector.h b/include/blah/containers/stackvector.h index 1982e33..e29892a 100644 --- a/include/blah/containers/stackvector.h +++ b/include/blah/containers/stackvector.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include @@ -16,7 +16,7 @@ namespace Blah int m_count; public: - static inline constexpr size_t MaxCapacity = Capacity; + static constexpr size_t capacity = Capacity; StackVector(); StackVector(const std::initializer_list& init); @@ -30,7 +30,6 @@ namespace Blah void clear(); int size() const; - constexpr int capacity() { return Capacity; } T* expand(int amount = 1); void push_back(const T& item); diff --git a/include/blah/containers/str.h b/include/blah/containers/str.h index 02a88f5..8a5b9c1 100644 --- a/include/blah/containers/str.h +++ b/include/blah/containers/str.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include #include @@ -10,6 +10,7 @@ namespace Blah template class StrOf; using String = StrOf<64>; + using FilePath = StrOf<260>; // A simple String implementation class Str diff --git a/include/blah/containers/vector.h b/include/blah/containers/vector.h index 7c28599..84ccb42 100644 --- a/include/blah/containers/vector.h +++ b/include/blah/containers/vector.h @@ -1,6 +1,5 @@ #pragma once -#include -#include +#include #include #include @@ -104,7 +103,7 @@ namespace Blah { m_buffer = nullptr; m_count = m_capacity = 0; - reserve(list.size()); + reserve((int)list.size()); for (auto& it : list) push_back(std::move(it)); } diff --git a/include/blah/core/common.h b/include/blah/core/common.h deleted file mode 100644 index 71fd98e..0000000 --- a/include/blah/core/common.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include - -// error / abort -#if defined(DEBUG) || defined(_DEBUG) - -#include -#define BLAH_ERROR(message) \ - do { Blah::Log::error(message "\n\tin file: %s:%d", __FILE__, __LINE__); abort(); } while(0) - -#define BLAH_ERROR_FMT(message, ...) \ - do { Blah::Log::error(message "\n\tin file: %s:%d", __VA_ARGS__, __FILE__, __LINE__); abort(); } while(0) - -#else - -#define BLAH_ERROR(message) \ - Blah::Log::error(message "\n\tin file: %s:%d", __FILE__, __LINE__) - -#define BLAH_ERROR_FMT(message, ...) \ - Blah::Log::error(message "\n\tin file: %s:%d", __VA_ARGS__, __FILE__, __LINE__) - -#endif - -#define BLAH_ASSERT(condition, message) \ - do { if (!(condition)) { BLAH_ERROR(message); } } while(0) - -#define BLAH_ASSERT_FMT(condition, message, ...) \ - do { if (!(condition)) { BLAH_ERROR_FMT(message, __VA_ARGS__); } } while(0) - -// maximum length of a print/warn/error message -#ifndef BLAH_MESSAGE -#define BLAH_MESSAGE 1024 -#endif - -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 - { - enum class Category - { - Info, - Warning, - Error - }; - - void info(const char* message, ...); - void warn(const char* message, ...); - void error(const char* message, ...); - } -} \ No newline at end of file diff --git a/include/blah/core/filesystem.h b/include/blah/filesystem.h similarity index 64% rename from include/blah/core/filesystem.h rename to include/blah/filesystem.h index 05fadf1..b3fdcf0 100644 --- a/include/blah/core/filesystem.h +++ b/include/blah/filesystem.h @@ -1,12 +1,13 @@ #pragma once +#include #include #include namespace Blah { - using FilePath = StrOf<265>; - class FileStream; - + class File; + using FileRef = Ref; + enum class FileMode { // Opens an existing file for reading. @@ -22,6 +23,41 @@ namespace Blah Create, }; + class File + { + protected: + File() = default; + + public: + // Opens a file at the given path. + // If it fails, this will return an empty reference. + static FileRef open(const FilePath& path, FileMode mode); + + // checks if the given file exists + static bool exists(const FilePath& path); + + // deletes the given file + static bool destroy(const FilePath& path); + + // Default Destructor + virtual ~File() = default; + + // Gets the File Length + virtual size_t length() = 0; + + // Gets the current File Position + virtual size_t position() = 0; + + // Seeks to the given position in the File + virtual size_t seek(size_t position) = 0; + + // Reads from the File into the buffer, and returns how many bytes were successfully read + virtual size_t read(unsigned char* buffer, size_t length) = 0; + + // Writes from the buffer into the File, nd returns how many bytes were successfully written + virtual size_t write(const unsigned char* buffer, size_t length) = 0; + }; + namespace Directory { // Creates a new directory at the given location. @@ -32,7 +68,7 @@ namespace Blah bool exists(const FilePath& path); // Tries to delete a path and returns whether it was successful - bool remove(const FilePath& path); + bool destroy(const FilePath& path); // Enumerates over a directory and returns a list of files & directories Vector enumerate(const FilePath& path, bool recursive = true); @@ -41,18 +77,6 @@ namespace Blah void explore(const FilePath& path); } - namespace File - { - // Checks if the given file exists - bool exists(const FilePath& path); - - // Tries to delete a file and returns whether it was successful - bool remove(const FilePath& path); - - // Opens the given file and returns a stream - FileStream open(const FilePath& path, FileMode mode); - } - namespace Path { // Returns the file name of the path diff --git a/include/blah/graphics/batch.h b/include/blah/graphics/batch.h index 118e25b..5a69f09 100644 --- a/include/blah/graphics/batch.h +++ b/include/blah/graphics/batch.h @@ -1,17 +1,17 @@ #pragma once #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include #include #include #include -#include +#include namespace Blah { @@ -125,10 +125,10 @@ namespace Blah void set_sampler(const TextureSampler& sampler); // Draws the batch to the given target - void render(const FrameBufferRef& target = App::backbuffer); + void render(const TargetRef& target = App::backbuffer); // Draws the batch to the given target, with the provided matrix - void render(const FrameBufferRef& target, const Mat4x4& matrix); + void render(const TargetRef& target, const Mat4x4& matrix); // Clears the batch void clear(); diff --git a/include/blah/graphics/blend.h b/include/blah/graphics/blend.h index 87b82ea..685d8ed 100644 --- a/include/blah/graphics/blend.h +++ b/include/blah/graphics/blend.h @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace Blah { diff --git a/include/blah/graphics/framebuffer.h b/include/blah/graphics/framebuffer.h deleted file mode 100644 index 48230a0..0000000 --- a/include/blah/graphics/framebuffer.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace Blah -{ - enum class ClearMask - { - None = 0, - Color = 1, - Depth = 2, - Stencil = 4, - All = (int)Color | (int)Depth | (int)Stencil - }; - - // Up to 4 color attachments + 1 depth/stencil - using Attachments = StackVector; - using AttachmentFormats = StackVector; - - class FrameBuffer; - using FrameBufferRef = std::shared_ptr; - - // 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 - { - protected: - FrameBuffer() = default; - - public: - // Copy / Moves not allowed - FrameBuffer(const FrameBuffer&) = delete; - FrameBuffer(FrameBuffer&&) = delete; - FrameBuffer& operator=(const FrameBuffer&) = delete; - FrameBuffer& operator=(FrameBuffer&&) = delete; - - // Default Destructor - virtual ~FrameBuffer() = default; - - // Creates a new FrameBuffer with a single Color attachment - // If the FrameBuffer creation fails, it will return an invalid FrameBufferRef. - static FrameBufferRef create(int width, int height); - - // Creates a new FrameBuffer with the given Texture Attachments. You must provide at least one Attachment. - // If the FrameBuffer creation fails, it will return an invalid FrameBufferRef. - static FrameBufferRef create(int width, int height, const AttachmentFormats& attachments); - - // Gets the list of Attachments from the FrameBuffer - virtual Attachments& attachments() = 0; - - // Gets the list of Attachments from the FrameBuffer - virtual const Attachments& attachments() const = 0; - - // Gets the Attachment at a given index from the FrameBuffer - TextureRef& attachment(int index); - - // Gets the Attachment at a given index from the FrameBuffer - const TextureRef& attachment(int index) const; - - // Gets the width of the FrameBuffer - virtual int width() const; - - // Gets the height of the FrameBuffer - virtual int height() const; - - // Clears the FrameBuffer - virtual void clear(Color color = Color::black, float depth = 1.0f, u8 stencil = 0, ClearMask mask = ClearMask::All) = 0; - }; - -} diff --git a/include/blah/graphics/material.h b/include/blah/graphics/material.h index b5790de..9c24e85 100644 --- a/include/blah/graphics/material.h +++ b/include/blah/graphics/material.h @@ -1,14 +1,14 @@ #pragma once +#include #include #include #include #include -#include namespace Blah { class Material; - typedef std::shared_ptr MaterialRef; + using MaterialRef = Ref; // Materials hold values that can be assigned to a shader during rendering class Material final diff --git a/include/blah/graphics/mesh.h b/include/blah/graphics/mesh.h index 15beeb4..1159424 100644 --- a/include/blah/graphics/mesh.h +++ b/include/blah/graphics/mesh.h @@ -1,6 +1,5 @@ #pragma once -#include -#include +#include #include namespace Blah @@ -59,7 +58,7 @@ namespace Blah }; class Mesh; - typedef std::shared_ptr MeshRef; + using MeshRef = Ref; // A Mesh is a set of Indices and Vertices which are used for drawing class Mesh diff --git a/include/blah/graphics/renderpass.h b/include/blah/graphics/renderpass.h index df3d962..3647cd8 100644 --- a/include/blah/graphics/renderpass.h +++ b/include/blah/graphics/renderpass.h @@ -1,9 +1,9 @@ #pragma once -#include -#include +#include +#include #include #include -#include +#include #include #include #include @@ -42,7 +42,7 @@ namespace Blah struct RenderPass { // Framebuffer to draw to - FrameBufferRef target; + TargetRef target; // Mesh to draw with MeshRef mesh; diff --git a/include/blah/graphics/shader.h b/include/blah/graphics/shader.h index c7d18af..339ae18 100644 --- a/include/blah/graphics/shader.h +++ b/include/blah/graphics/shader.h @@ -1,7 +1,7 @@ #pragma once +#include #include #include -#include namespace Blah { @@ -70,7 +70,7 @@ namespace Blah }; class Shader; - typedef std::shared_ptr ShaderRef; + using ShaderRef = Ref; // A shader used during Rendering class Shader diff --git a/include/blah/graphics/spritefont.h b/include/blah/graphics/spritefont.h index e0e1096..7165fa6 100644 --- a/include/blah/graphics/spritefont.h +++ b/include/blah/graphics/spritefont.h @@ -1,10 +1,10 @@ #pragma once -#include +#include #include #include #include -#include -#include +#include +#include #include namespace Blah diff --git a/include/blah/graphics/subtexture.h b/include/blah/graphics/subtexture.h index 43bd833..fb0faf5 100644 --- a/include/blah/graphics/subtexture.h +++ b/include/blah/graphics/subtexture.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include namespace Blah { diff --git a/include/blah/graphics/target.h b/include/blah/graphics/target.h new file mode 100644 index 0000000..5d10f6f --- /dev/null +++ b/include/blah/graphics/target.h @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include +#include + +namespace Blah +{ + enum class ClearMask + { + None = 0, + Color = 1, + Depth = 2, + Stencil = 4, + All = (int)Color | (int)Depth | (int)Stencil + }; + + // Up to 4 color textures + 1 depth/stencil + using Attachments = StackVector; + using AttachmentFormats = StackVector; + + class Target; + using TargetRef = Ref; + + // Target is a 2D Buffer that can be drawn to. + // It can hold up to 4 color Textures, and 1 Depth/Stencil Texture. + class Target + { + protected: + Target() = default; + + public: + // Copy / Moves not allowed + Target(const Target&) = delete; + Target(Target&&) = delete; + Target& operator=(const Target&) = delete; + Target& operator=(Target&&) = delete; + + // Default Destructor + virtual ~Target() = default; + + // Creates a new Target with a single Color texture + // If the Target creation fails, it will return an invalid TargetRef. + static TargetRef create(int width, int height); + + // Creates a new Target with the given Texture Attachments. You must provide at least one Attachment. + // If the Target creation fails, it will return an invalid TargetRef. + static TargetRef create(int width, int height, const AttachmentFormats& textures); + + // Gets the list of Attachments from the Target + virtual Attachments& textures() = 0; + + // Gets the list of Attachments from the Target + virtual const Attachments& textures() const = 0; + + // Gets the Attachment at a given index from the Target + TextureRef& texture(int index); + + // Gets the Attachment at a given index from the Target + const TextureRef& texture(int index) const; + + // Gets the width of the Target + virtual int width() const; + + // Gets the height of the Target + virtual int height() const; + + // Clears the Target + virtual void clear(Color color = Color::black, float depth = 1.0f, u8 stencil = 0, ClearMask mask = ClearMask::All) = 0; + }; + +} diff --git a/include/blah/graphics/texture.h b/include/blah/graphics/texture.h index c288ea4..5b0b8e7 100644 --- a/include/blah/graphics/texture.h +++ b/include/blah/graphics/texture.h @@ -1,6 +1,6 @@ #pragma once -#include -#include +#include +#include namespace Blah { @@ -28,7 +28,7 @@ namespace Blah class Image; class Stream; class Texture; - typedef std::shared_ptr TextureRef; + using TextureRef = Ref; // A 2D Texture held by the GPU to be used during rendering class Texture diff --git a/include/blah/images/aseprite.h b/include/blah/images/aseprite.h index db35aa0..d158ea9 100644 --- a/include/blah/images/aseprite.h +++ b/include/blah/images/aseprite.h @@ -1,9 +1,9 @@ #pragma once -#include +#include #include #include #include -#include +#include namespace Blah { diff --git a/include/blah/images/font.h b/include/blah/images/font.h index 6958276..f235fc4 100644 --- a/include/blah/images/font.h +++ b/include/blah/images/font.h @@ -2,7 +2,7 @@ #include #include #include -#include +#include namespace Blah { diff --git a/include/blah/images/image.h b/include/blah/images/image.h index 4e9bbb5..f89fb5e 100644 --- a/include/blah/images/image.h +++ b/include/blah/images/image.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include namespace Blah { diff --git a/include/blah/images/packer.h b/include/blah/images/packer.h index a42d41a..2f1439a 100644 --- a/include/blah/images/packer.h +++ b/include/blah/images/packer.h @@ -1,12 +1,12 @@ #pragma once #include -#include -#include -#include +#include +#include +#include #include #include #include -#include +#include namespace Blah { diff --git a/include/blah/input/input.h b/include/blah/input.h similarity index 59% rename from include/blah/input/input.h rename to include/blah/input.h index 9ca7300..a789c2d 100644 --- a/include/blah/input/input.h +++ b/include/blah/input.h @@ -1,8 +1,9 @@ #pragma once -#include -#include +#include +#include #include +#include // These are generally copied from the SDL2 Scancode Keys, // which are in turn based on the USB standards: @@ -345,6 +346,306 @@ namespace Blah Right = 2, }; + class InputBinding; + using InputBindingRef = Ref; + + class AxisBinding; + using AxisBindingRef = Ref; + + class StickBinding; + using StickBindingRef = Ref; + + // Single input Binding + // You must call Binding::update() every frame to poll the input state. + // Alternatively, bindings can be registered to Input which will + // automatically update them. + class InputBinding + { + 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 keys; + + // List of bound Buttons + StackVector buttons; + + // List of bound Triggers / Axis + StackVector triggers; + + // List of bound Mouse buttons + StackVector mouse; + + InputBinding() = default; + + InputBinding(float press_buffer) + : press_buffer(press_buffer) + { + + } + + template + InputBinding(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 + InputBinding& add(Key key); + + // adds a button to the binding + InputBinding& add(ButtonBind button); + + // adds an trigger to the binding + InputBinding& add(TriggerBind trigger); + + // adds a mouse button to the binding + InputBinding& add(MouseButton mouse); + + // adds an input to the binding + template + InputBinding& add(T first, T2 second, const Args&... args) + { + add(first); + add(second, args...); + return *this; + } + + // adds the left trigger to the binding + InputBinding& add_left_trigger(int controller, float threshold); + + // adds the right trigger to the binding + InputBinding& add_right_trigger(int controller, float threshold); + + // assigns all the bindings to the specific controller + InputBinding& 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 Input which will + // automatically update them. + class AxisBinding + { + public: + + enum class Overlap + { + Newer, + Older, + Cancel + }; + + // Negative Value Binding + InputBinding negative; + + // Positive Value Binding + InputBinding positive; + + // How to handle overlaps (ex. Left and Right are both held) + Overlap overlap = Overlap::Newer; + + AxisBinding() = default; + + AxisBinding(const InputBinding& negative, const InputBinding& 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 + 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 Input 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 + 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(); + }; + namespace Input { // Returns the Input State of the current frame. @@ -427,5 +728,14 @@ namespace Blah // returns a string name of the given button const char* name_of(Button button); + + // registers a new binding + InputBindingRef register_binding(const InputBinding& binding); + + // registers a new axis binding + AxisBindingRef register_binding(const AxisBinding& binding); + + // registers a new stick binding + StickBindingRef register_binding(const StickBinding& binding); } } \ No newline at end of file diff --git a/include/blah/input/binding.h b/include/blah/input/binding.h deleted file mode 100644 index 2d93f28..0000000 --- a/include/blah/input/binding.h +++ /dev/null @@ -1,299 +0,0 @@ -#pragma once -#include -#include -#include -#include - -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 keys; - - // List of bound Buttons - StackVector buttons; - - // List of bound Triggers / Axis - StackVector triggers; - - // List of bound Mouse buttons - StackVector mouse; - - Binding() = default; - - Binding(float press_buffer) - : press_buffer(press_buffer) - { - - } - - template - 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 - 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 - 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 - 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(); - }; -} \ No newline at end of file diff --git a/include/blah/input/binding_registry.h b/include/blah/input/binding_registry.h deleted file mode 100644 index 4ec1b16..0000000 --- a/include/blah/input/binding_registry.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include -#include -#include - -namespace Blah -{ - using BindingRef = std::shared_ptr; - using AxisBindingRef = std::shared_ptr; - using StickBindingRef = std::shared_ptr; - - // 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> bindings; - static Vector> axes; - static Vector> sticks; - }; -} \ No newline at end of file diff --git a/include/blah/math/stopwatch.h b/include/blah/math/stopwatch.h deleted file mode 100644 index 69260b2..0000000 --- a/include/blah/math/stopwatch.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include - -namespace Blah -{ - class Stopwatch - { - public: - Stopwatch(); - void reset(); - u64 microseconds(); - u64 milliseconds(); - private: - u64 start_time; - }; -} \ No newline at end of file diff --git a/include/blah/math/calc.h b/include/blah/numerics/calc.h similarity index 96% rename from include/blah/math/calc.h rename to include/blah/numerics/calc.h index 10fe9c8..6f89030 100644 --- a/include/blah/math/calc.h +++ b/include/blah/numerics/calc.h @@ -1,6 +1,6 @@ #pragma once -#include -#include +#include +#include namespace Blah { diff --git a/include/blah/math/circle.h b/include/blah/numerics/circle.h similarity index 91% rename from include/blah/math/circle.h rename to include/blah/numerics/circle.h index 70d7c84..8c7df39 100644 --- a/include/blah/math/circle.h +++ b/include/blah/numerics/circle.h @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace Blah { diff --git a/include/blah/math/color.h b/include/blah/numerics/color.h similarity index 98% rename from include/blah/math/color.h rename to include/blah/numerics/color.h index 4bb5029..fb355f9 100644 --- a/include/blah/math/color.h +++ b/include/blah/numerics/color.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include namespace Blah diff --git a/include/blah/math/ease.h b/include/blah/numerics/ease.h similarity index 98% rename from include/blah/math/ease.h rename to include/blah/numerics/ease.h index 99a7e3b..bc8da41 100644 --- a/include/blah/math/ease.h +++ b/include/blah/numerics/ease.h @@ -1,6 +1,6 @@ #pragma once -#include -#include +#include +#include namespace Blah { @@ -289,7 +289,7 @@ namespace Blah break; } - BLAH_ERROR("Invalid Easer Type"); + BLAH_ASSERT(false, "Invalid Easer Type"); return nullptr; } diff --git a/include/blah/math/line.h b/include/blah/numerics/line.h similarity index 95% rename from include/blah/math/line.h rename to include/blah/numerics/line.h index e36fb03..3d230ab 100644 --- a/include/blah/math/line.h +++ b/include/blah/numerics/line.h @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace Blah { diff --git a/include/blah/math/mat3x2.h b/include/blah/numerics/mat3x2.h similarity index 100% rename from include/blah/math/mat3x2.h rename to include/blah/numerics/mat3x2.h diff --git a/include/blah/math/mat4x4.h b/include/blah/numerics/mat4x4.h similarity index 100% rename from include/blah/math/mat4x4.h rename to include/blah/numerics/mat4x4.h diff --git a/include/blah/math/point.h b/include/blah/numerics/point.h similarity index 100% rename from include/blah/math/point.h rename to include/blah/numerics/point.h diff --git a/include/blah/math/quad.h b/include/blah/numerics/quad.h similarity index 89% rename from include/blah/math/quad.h rename to include/blah/numerics/quad.h index 12d1ea1..fecfb62 100644 --- a/include/blah/math/quad.h +++ b/include/blah/numerics/quad.h @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace Blah { diff --git a/include/blah/math/rect.h b/include/blah/numerics/rect.h similarity index 94% rename from include/blah/math/rect.h rename to include/blah/numerics/rect.h index 1b9714d..a149d23 100644 --- a/include/blah/math/rect.h +++ b/include/blah/numerics/rect.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include namespace Blah { diff --git a/include/blah/math/rectI.h b/include/blah/numerics/rectI.h similarity index 100% rename from include/blah/math/rectI.h rename to include/blah/numerics/rectI.h diff --git a/include/blah/math/vec2.h b/include/blah/numerics/vec2.h similarity index 99% rename from include/blah/math/vec2.h rename to include/blah/numerics/vec2.h index 1449a2c..1d2aa39 100644 --- a/include/blah/math/vec2.h +++ b/include/blah/numerics/vec2.h @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace Blah { diff --git a/include/blah/math/vec3.h b/include/blah/numerics/vec3.h similarity index 100% rename from include/blah/math/vec3.h rename to include/blah/numerics/vec3.h diff --git a/include/blah/math/vec4.h b/include/blah/numerics/vec4.h similarity index 100% rename from include/blah/math/vec4.h rename to include/blah/numerics/vec4.h diff --git a/include/blah/streams/bufferstream.h b/include/blah/streams/bufferstream.h index d2d03b3..62c44a5 100644 --- a/include/blah/streams/bufferstream.h +++ b/include/blah/streams/bufferstream.h @@ -12,28 +12,28 @@ namespace Blah BufferStream& operator=(BufferStream&& bs) noexcept; ~BufferStream(); - virtual i64 length() const override { return m_length; } - virtual i64 position() const override { return m_position; } - virtual i64 seek(i64 seekTo) override { return m_position = (seekTo < 0 ? 0 : (seekTo > m_length ? m_length : seekTo)); } + virtual size_t length() const override { return m_length; } + virtual size_t position() const override { return m_position; } + virtual size_t seek(size_t 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_readable() const override { return true; } virtual bool is_writable() const override { return true; } virtual void close() override; - void resize(i64 length); + void resize(size_t length); void clear() { m_length = m_position = 0; } char* data() { return m_buffer; } const char* data() const { return m_buffer; } protected: - virtual i64 read_into(void* ptr, i64 length) override; - virtual i64 write_from(const void* ptr, i64 length) override; + virtual size_t read_into(void* ptr, size_t length) override; + virtual size_t write_from(const void* ptr, size_t length) override; private: char* m_buffer; - i64 m_capacity; - i64 m_length; - i64 m_position; + size_t m_capacity; + size_t m_length; + size_t m_position; }; } \ No newline at end of file diff --git a/include/blah/streams/filestream.h b/include/blah/streams/filestream.h index 5be7594..ee63a8e 100644 --- a/include/blah/streams/filestream.h +++ b/include/blah/streams/filestream.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include namespace Blah { @@ -11,22 +11,21 @@ namespace Blah FileStream(const FilePath& path, FileMode mode); FileStream(FileStream&& fs) noexcept; FileStream& operator=(FileStream&& fs) noexcept; - ~FileStream(); - i64 length() const override; - i64 position() const override; - i64 seek(i64 seekTo) override; + size_t length() const override; + size_t position() const override; + size_t seek(size_t position) override; bool is_open() const override; bool is_readable() const override; bool is_writable() const override; void close() override; protected: - i64 read_into(void* ptr, i64 length) override; - i64 write_from(const void* ptr, i64 length) override; + size_t read_into(void* ptr, size_t length) override; + size_t write_from(const void* ptr, size_t length) override; private: FileMode m_mode; - void* m_handle; + FileRef m_file; }; } \ No newline at end of file diff --git a/include/blah/streams/memorystream.h b/include/blah/streams/memorystream.h index acb9504..6272242 100644 --- a/include/blah/streams/memorystream.h +++ b/include/blah/streams/memorystream.h @@ -7,14 +7,14 @@ namespace Blah { public: MemoryStream(); - MemoryStream(char* data, i64 length); + MemoryStream(char* data, size_t length); MemoryStream(MemoryStream&& ms) noexcept; MemoryStream& operator=(MemoryStream&& ms) noexcept; ~MemoryStream() override { m_data = nullptr; m_length = m_position = 0; } - i64 length() const override { return m_length; } - i64 position() const override { return m_position; } - i64 seek(i64 seekTo) override { return m_position = (seekTo < 0 ? 0 : (seekTo > m_length ? m_length : seekTo)); } + size_t length() const override { return m_length; } + size_t position() const override { return m_position; } + size_t seek(size_t seekTo) override { return m_position = (seekTo < 0 ? 0 : (seekTo > m_length ? m_length : seekTo)); } bool is_open() const override { return m_data != nullptr; } bool is_readable() const override { return true; } bool is_writable() const override { return true; } @@ -24,12 +24,12 @@ namespace Blah const char* data() const { return m_data; } protected: - i64 read_into(void* ptr, i64 length) override; - i64 write_from(const void* ptr, i64 length) override; + size_t read_into(void* ptr, size_t length) override; + size_t write_from(const void* ptr, size_t length) override; private: char* m_data; - i64 m_length; - i64 m_position; + size_t m_length; + size_t m_position; }; } \ No newline at end of file diff --git a/include/blah/streams/stream.h b/include/blah/streams/stream.h index 5aeb205..36900fe 100644 --- a/include/blah/streams/stream.h +++ b/include/blah/streams/stream.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include #include @@ -16,13 +16,13 @@ namespace Blah virtual ~Stream() = default; // returns the length of the stream - virtual i64 length() const = 0; + virtual size_t length() const = 0; // returns the position of the stream - virtual i64 position() const = 0; + virtual size_t position() const = 0; // seeks the position of the stream - virtual i64 seek(i64 seek_to) = 0; + virtual size_t seek(size_t position) = 0; // returns true of the stream is open virtual bool is_open() const = 0; @@ -37,10 +37,10 @@ namespace Blah virtual void close() = 0; // pipes the contents of this stream to another stream - i64 pipe(Stream& to, i64 length); + size_t pipe(Stream& to, size_t length); // reads the amount of bytes into the given buffer, and returns the amount read - i64 read(void* buffer, i64 length) { return read_into(buffer, length); } + size_t read(void* buffer, size_t length) { return read_into(buffer, length); } // reads a string. if length < 0, assumes null-terminated 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 - i64 write(const void* buffer, i64 length); + size_t write(const void* buffer, size_t length); // writes the contents of a string to the stream - i64 write(const String& string); + size_t write(const String& string); // writes a number template::value, T>::type> - i64 write(const T& value) + size_t write(const T& value) { return write(value, Endian::Little); } // writes a number template::value, T>::type> - i64 write(const T& value, Endian endian) + size_t write(const T& value, Endian endian) { T writing = value; @@ -93,9 +93,9 @@ namespace Blah protected: // reads from the stream into the given buffer, and returns the number of bytes read - virtual i64 read_into(void* buffer, i64 length) = 0; + virtual size_t read_into(void* buffer, size_t length) = 0; // writes from the stream from the given buffer, and returns the number of bytes written - virtual i64 write_from(const void* buffer, i64 length) = 0; + virtual size_t write_from(const void* buffer, size_t length) = 0; }; } \ No newline at end of file diff --git a/include/blah/core/time.h b/include/blah/time.h similarity index 88% rename from include/blah/core/time.h rename to include/blah/time.h index a2ce64a..9064f51 100644 --- a/include/blah/core/time.h +++ b/include/blah/time.h @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace Blah { @@ -47,4 +47,16 @@ namespace Blah // returns true between time intervals static bool between_interval(float interval, float offset = 0); }; + + class Stopwatch + { + public: + Stopwatch(); + void reset(); + u64 microseconds(); + u64 milliseconds(); + + private: + u64 start_time; + }; } \ No newline at end of file diff --git a/src/core/app.cpp b/src/app.cpp similarity index 87% rename from src/core/app.cpp rename to src/app.cpp index 50bdd19..7a5737c 100644 --- a/src/core/app.cpp +++ b/src/app.cpp @@ -1,11 +1,11 @@ -#include -#include -#include -#include -#include -#include "../internal/platform_backend.h" -#include "../internal/graphics_backend.h" -#include "../internal/input_backend.h" +#include +#include +#include +#include +#include +#include "internal/platform_backend.h" +#include "internal/graphics_backend.h" +#include "internal/input_backend.h" #ifdef __EMSCRIPTEN__ #include @@ -261,21 +261,20 @@ namespace { // A dummy Frame Buffer that represents the Back Buffer // it doesn't actually contain any textures or details. - class BackBuffer final : public FrameBuffer + class BackBuffer final : public Target { - Attachments empty_attachments; - TextureRef empty_texture; + Attachments empty_textures; - Attachments& attachments() override + Attachments& textures() override { - BLAH_ASSERT(false, "Backbuffer doesn't have any attachments"); - return empty_attachments; + BLAH_ASSERT(false, "Backbuffer doesn't have any textures"); + return empty_textures; } - const Attachments& attachments() const override + const Attachments& textures() const override { - BLAH_ASSERT(false, "Backbuffer doesn't have any attachments"); - return empty_attachments; + BLAH_ASSERT(false, "Backbuffer doesn't have any textures"); + return empty_textures; } int width() const override @@ -296,4 +295,4 @@ namespace } -extern const FrameBufferRef App::backbuffer = FrameBufferRef(new BackBuffer()); \ No newline at end of file +extern const TargetRef App::backbuffer = TargetRef(new BackBuffer()); \ No newline at end of file diff --git a/src/core/common.cpp b/src/common.cpp similarity index 94% rename from src/core/common.cpp rename to src/common.cpp index 152a00b..3cc4f71 100644 --- a/src/core/common.cpp +++ b/src/common.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include // for logging methods #include // for sprintf diff --git a/src/core/filesystem.cpp b/src/filesystem.cpp similarity index 75% rename from src/core/filesystem.cpp rename to src/filesystem.cpp index 6b80adf..03e2b7b 100644 --- a/src/core/filesystem.cpp +++ b/src/filesystem.cpp @@ -1,24 +1,24 @@ -#include +#include #include -#include "../internal/platform_backend.h" +#include "internal/platform_backend.h" using namespace Blah; +FileRef File::open(const FilePath& path, FileMode mode) +{ + return PlatformBackend::file_open(path.cstr(), mode); +} + bool File::exists(const FilePath& path) { return PlatformBackend::file_exists(path.cstr()); } -bool File::remove(const FilePath& path) +bool File::destroy(const FilePath& path) { return PlatformBackend::file_delete(path.cstr()); } -FileStream File::open(const FilePath& path , FileMode mode) -{ - return FileStream(path, mode); -} - bool Directory::create(const FilePath& path) { return PlatformBackend::dir_create(path.cstr()); @@ -29,7 +29,7 @@ bool Directory::exists(const FilePath& path) return PlatformBackend::dir_exists(path.cstr()); } -bool Directory::remove(const FilePath& path) +bool Directory::destroy(const FilePath& path) { return PlatformBackend::dir_delete(path.cstr()); } @@ -56,11 +56,11 @@ void Directory::explore(const FilePath& path) FilePath Path::get_file_name(const FilePath& path) { const char* cstr = path.cstr(); - for (int n = path.length() - 2; n >= 0; n --) - if (*(cstr + n) == '/') - { - return FilePath(cstr + n + 1); - } + auto length = path.length(); + + for (auto n = length; n > 0; n--) + if (*(cstr + n - 1) == '/') + return FilePath(cstr + n); return path; } @@ -85,7 +85,7 @@ FilePath Path::get_directory_name(const FilePath& path) FilePath directory = path; while (directory.ends_with("/")) directory = directory.substr(0, -1); - int last = directory.last_index_of('/'); + auto last = directory.last_index_of('/'); if (last >= 0) directory = directory.substr(0, last + 1); return directory; @@ -107,8 +107,8 @@ FilePath Path::normalize(const FilePath& path) { FilePath normalized; - int len = path.length(); - for (int n = 0; n < len; n++) + auto len = path.length(); + for (auto n = 0; n < len; n++) { // normalize slashes if (path[n] == '\\' || path[n] == '/') @@ -120,16 +120,19 @@ FilePath Path::normalize(const FilePath& path) else if (path[n] == '.' && n < len - 1 && path[n + 1] == '.') { // search backwards for last / - bool couldMoveUp = false; - for (int k = normalized.length() - 2; k >= 0; k--) - if (normalized[k] == '/') - { - normalized = normalized.substr(0, k); - couldMoveUp = true; - break; - } + bool could_move_up = false; + if (normalized.length() > 0) + { + for (auto k = normalized.length() - 1; k > 0; k--) + if (normalized[k - 1] == '/') + { + normalized = normalized.substr(0, k - 1); + could_move_up = true; + break; + } + } - if (!couldMoveUp) + if (!could_move_up) normalized.append('.'); else n++; diff --git a/src/graphics/batch.cpp b/src/graphics/batch.cpp index ed676cb..cd02f90 100644 --- a/src/graphics/batch.cpp +++ b/src/graphics/batch.cpp @@ -1,11 +1,11 @@ #include #include -#include +#include #include #include #include -#include -#include +#include +#include #include using namespace Blah; @@ -185,7 +185,7 @@ namespace #define INSERT_BATCH() \ do { \ m_batches.expand(); \ - for (int i = m_batches.size() - 1; i > m_batch_insert; i --) \ + for (auto i = m_batches.size() - 1; i > m_batch_insert; i --) \ m_batches[i] = std::move(m_batches[i - 1]); \ m_batches[m_batch_insert++] = m_batch; \ m_batch.offset += m_batch.elements; \ @@ -378,7 +378,7 @@ void Batch::set_sampler(const TextureSampler& sampler) m_batch.sampler = sampler; } -void Batch::render(const FrameBufferRef& target) +void Batch::render(const TargetRef& target) { Point size; if (!target) @@ -389,7 +389,7 @@ void Batch::render(const FrameBufferRef& target) render(target, Mat4x4::create_ortho_offcenter(0, (float)size.x, (float)size.y, 0, 0.01f, 1000.0f)); } -void Batch::render(const FrameBufferRef& target, const Mat4x4& matrix) +void Batch::render(const TargetRef& target, const Mat4x4& matrix) { // nothing to draw if ((m_batches.size() <= 0 && m_batch.elements <= 0) || m_indices.size() <= 0) diff --git a/src/graphics/framebuffer.cpp b/src/graphics/framebuffer.cpp deleted file mode 100644 index b61b864..0000000 --- a/src/graphics/framebuffer.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include "../internal/graphics_backend.h" - -using namespace Blah; - -FrameBufferRef FrameBuffer::create(int width, int height) -{ - return create(width, height, { TextureFormat::RGBA }); -} - -FrameBufferRef FrameBuffer::create(int width, int height, const AttachmentFormats& attachments) -{ - BLAH_ASSERT(width > 0 && height > 0, "FrameBuffer width and height must be larger than 0"); - BLAH_ASSERT(attachments.size() > 0, "At least one attachment must be provided"); - - int color_count = 0; - int depth_count = 0; - - for (int i = 0; i < attachments.size(); i++) - { - BLAH_ASSERT((int)attachments[i] > (int)TextureFormat::None && (int)attachments[i] < (int)TextureFormat::Count, "Invalid texture format"); - - if (attachments[i] == TextureFormat::DepthStencil) - depth_count++; - else - color_count++; - } - - BLAH_ASSERT(depth_count <= 1, "FrameBuffer can only have 1 Depth/Stencil Texture"); - BLAH_ASSERT(color_count <= Attachments::MaxCapacity - 1, "Exceeded maximum Color attachment count"); - - return GraphicsBackend::create_framebuffer(width, height, attachments.data(), attachments.size()); -} - -TextureRef& FrameBuffer::attachment(int index) -{ - return attachments()[index]; -} - -const TextureRef& FrameBuffer::attachment(int index) const -{ - return attachments()[index]; -} - -int FrameBuffer::width() const -{ - return attachments()[0]->width(); -} - -int FrameBuffer::height() const -{ - return attachments()[0]->height(); -} diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index ae72d31..2816e75 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include using namespace Blah; @@ -19,7 +19,7 @@ namespace case UniformType::Mat3x2: components = 6; break; case UniformType::Mat4x4: components = 16; break; default: - BLAH_ERROR("Unespected Uniform Type"); + BLAH_ASSERT(false, "Unespected Uniform Type"); break; } diff --git a/src/graphics/renderpass.cpp b/src/graphics/renderpass.cpp index 83b7cc0..d786e19 100644 --- a/src/graphics/renderpass.cpp +++ b/src/graphics/renderpass.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "../internal/graphics_backend.h" using namespace Blah; @@ -66,11 +66,7 @@ void RenderPass::perform() } // get the total drawable size - Vec2 draw_size; - if (!pass.target) - draw_size = Vec2(App::draw_width(), App::draw_height()); - else - draw_size = Vec2(pass.target->width(), pass.target->height()); + auto draw_size = Vec2(pass.target->width(), pass.target->height()); // Validate Viewport if (!pass.has_viewport) diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index f4b7711..8e82230 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "../internal/graphics_backend.h" using namespace Blah; @@ -21,7 +21,8 @@ ShaderRef Shader::create(const ShaderData& data) for (auto& it : uniforms) if (it.type == UniformType::None) { - BLAH_ERROR_FMT("Uniform '%s' has an invalid type!\n\tOnly Float/Float2/Float3/Float4/Mat3x2/Mat4x4/Texture are allowed!", it.name.cstr()); + auto error = String::fmt("Uniform '%s' has an invalid type!\n\tOnly Float/Float2/Float3/Float4/Mat3x2/Mat4x4/Texture are allowed!", it.name.cstr()); + BLAH_ASSERT(false, error.cstr()); return ShaderRef(); } @@ -30,7 +31,8 @@ ShaderRef Shader::create(const ShaderData& data) for (int j = i + 1; j < uniforms.size(); j ++) if (uniforms[i].name == uniforms[j].name) { - BLAH_ERROR_FMT("Shader Uniform names '%s' overlap! All Names must be unique.", uniforms[0].name.cstr()); + auto error = String::fmt("Shader Uniform names '%s' overlap! All Names must be unique.", uniforms[0].name.cstr()); + BLAH_ASSERT(false, error.cstr()); return ShaderRef(); } } diff --git a/src/graphics/spritefont.cpp b/src/graphics/spritefont.cpp index 08c4db4..4459877 100644 --- a/src/graphics/spritefont.cpp +++ b/src/graphics/spritefont.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include using namespace Blah; @@ -123,7 +123,7 @@ float SpriteFont::width_of_line(const String& text, int start) const float width = 0; Codepoint last = 0; - for (int i = start; i < text.length(); i ++) + for (auto i = start; i < text.length(); i ++) { if (text[i] == '\n') return width; @@ -139,7 +139,9 @@ float SpriteFont::width_of_line(const String& text, int start) const width += get_kerning(last, next); // move to thext utf8 character - i += text.utf8_length(i) - 1; + auto len = text.utf8_length(i); + if (len > 0) + i += len - 1; last = next; } @@ -153,7 +155,7 @@ float SpriteFont::height_of(const String& text) const return 0; float height = line_height(); - for (int i = 0; i < text.length(); i ++) + for (auto i = 0; i < text.length(); i ++) { if (text[i] == '\n') height += line_height(); diff --git a/src/graphics/subtexture.cpp b/src/graphics/subtexture.cpp index b38f1c2..3580a41 100644 --- a/src/graphics/subtexture.cpp +++ b/src/graphics/subtexture.cpp @@ -1,5 +1,5 @@ #include -#include +#include using namespace Blah; diff --git a/src/graphics/target.cpp b/src/graphics/target.cpp new file mode 100644 index 0000000..03d3eac --- /dev/null +++ b/src/graphics/target.cpp @@ -0,0 +1,53 @@ +#include +#include "../internal/graphics_backend.h" + +using namespace Blah; + +TargetRef Target::create(int width, int height) +{ + return create(width, height, { TextureFormat::RGBA }); +} + +TargetRef Target::create(int width, int height, const AttachmentFormats& textures) +{ + BLAH_ASSERT(width > 0 && height > 0, "Target width and height must be larger than 0"); + BLAH_ASSERT(textures.size() > 0, "At least one texture must be provided"); + + int color_count = 0; + int depth_count = 0; + + for (int i = 0; i < textures.size(); i++) + { + BLAH_ASSERT((int)textures[i] > (int)TextureFormat::None && (int)textures[i] < (int)TextureFormat::Count, "Invalid texture format"); + + if (textures[i] == TextureFormat::DepthStencil) + depth_count++; + else + color_count++; + } + + BLAH_ASSERT(depth_count <= 1, "Target can only have 1 Depth/Stencil Texture"); + BLAH_ASSERT(color_count <= Attachments::capacity - 1, "Exceeded maximum Color texture count"); + + return GraphicsBackend::create_target(width, height, textures.data(), textures.size()); +} + +TextureRef& Target::texture(int index) +{ + return textures()[index]; +} + +const TextureRef& Target::texture(int index) const +{ + return textures()[index]; +} + +int Target::width() const +{ + return textures()[0]->width(); +} + +int Target::height() const +{ + return textures()[0]->height(); +} diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index 12b3b35..a614e3f 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include "../internal/graphics_backend.h" using namespace Blah; diff --git a/src/images/aseprite.cpp b/src/images/aseprite.cpp index 230fc4d..72d90f0 100644 --- a/src/images/aseprite.cpp +++ b/src/images/aseprite.cpp @@ -1,8 +1,8 @@ #include #include -#include -#include -#include +#include +#include +#include #define STBI_NO_STDIO #define STBI_ONLY_ZLIB @@ -81,7 +81,7 @@ void Aseprite::parse(Stream& stream) { if (!stream.is_readable()) { - BLAH_ERROR("Stream is not readable"); + BLAH_ASSERT(false, "Stream is not readable"); return; } @@ -96,7 +96,7 @@ void Aseprite::parse(Stream& stream) auto magic = stream.read(Endian::Little); if (magic != 0xA5E0) { - BLAH_ERROR("File is not a valid Aseprite file"); + BLAH_ASSERT(false, "File is not a valid Aseprite file"); return; } @@ -133,7 +133,7 @@ void Aseprite::parse(Stream& stream) auto magic = stream.read(Endian::Little); // magic number if (magic != 0xF1FA) { - BLAH_ERROR("File is not a valid Aseprite file"); + BLAH_ASSERT(false, "File is not a valid Aseprite file"); return; } @@ -247,7 +247,7 @@ void Aseprite::parse_cel(Stream& stream, int frameIndex, size_t maxPosition) if (res < 0) { - BLAH_ERROR("Unable to parse Aseprite file"); + BLAH_ASSERT(false, "Unable to parse Aseprite file"); return; } } @@ -460,6 +460,6 @@ void Aseprite::render_cel(Cel* cel, Frame* frame) } else { - BLAH_ERROR("Aseprite blendmodes aren't implemented"); + BLAH_ASSERT(false, "Aseprite blendmodes aren't implemented"); } } diff --git a/src/images/font.cpp b/src/images/font.cpp index ba85a0a..7b2568c 100644 --- a/src/images/font.cpp +++ b/src/images/font.cpp @@ -1,7 +1,7 @@ #include #include -#include -#include +#include +#include using namespace Blah; @@ -111,7 +111,7 @@ void Font::load(Stream& stream) if (!stream.is_readable()) { - BLAH_ERROR("Unable to load a font as the Stream was not readable"); + BLAH_ASSERT(false, "Unable to load a font as the Stream was not readable"); return; } diff --git a/src/images/image.cpp b/src/images/image.cpp index e08da42..c564d11 100644 --- a/src/images/image.cpp +++ b/src/images/image.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include using namespace Blah; @@ -148,7 +148,7 @@ void Image::from_stream(Stream& stream) if (!stream.is_readable()) { - BLAH_ERROR("Unable to load image as the Stream was not readable"); + BLAH_ASSERT(false, "Unable to load image as the Stream was not readable"); return; } @@ -162,7 +162,7 @@ void Image::from_stream(Stream& stream) if (data == nullptr) { - BLAH_ERROR("Unable to load image as the Stream's data was not a valid image"); + BLAH_ASSERT(false, "Unable to load image as the Stream's data was not a valid image"); return; } diff --git a/src/images/packer.cpp b/src/images/packer.cpp index 929c971..0d71224 100644 --- a/src/images/packer.cpp +++ b/src/images/packer.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -169,7 +169,7 @@ void Packer::pack() // make sure the largest isn't too large if (sources[0]->packed.w + padding * 2 > max_size || sources[0]->packed.h + padding * 2 > max_size) { - BLAH_ERROR("Source image is larger than max atlas size"); + BLAH_ASSERT(false, "Source image is larger than max atlas size"); return; } diff --git a/src/input.cpp b/src/input.cpp new file mode 100644 index 0000000..e47ab0f --- /dev/null +++ b/src/input.cpp @@ -0,0 +1,937 @@ +#include +#include +#include +#include +#include +#include +#include "internal/input_backend.h" +#include + +using namespace Blah; + +namespace +{ + InputState g_last_state; + InputState g_curr_state; + InputState g_next_state; + InputState g_empty_state; + ControllerState g_empty_controller; + Vector> g_bindings; + Vector> g_axes; + Vector> g_sticks; +} + +void InputBackend::init() +{ + g_empty_controller.name = "Disconnected"; + for (int i = 0; i < Blah::Input::max_controllers; i++) + g_empty_state.controllers[i].name = g_empty_controller.name; + + g_last_state = g_empty_state; + g_curr_state = g_empty_state; + g_next_state = g_empty_state; + g_bindings.dispose(); + g_axes.dispose(); + g_sticks.dispose(); +} + +void InputBackend::frame() +{ + // cycle states + g_last_state = g_curr_state; + g_curr_state = g_next_state; + + // copy state, clear pressed / released values + { + for (int i = 0; i < Blah::Input::max_keyboard_keys; i++) + { + g_next_state.keyboard.pressed[i] = false; + g_next_state.keyboard.released[i] = false; + } + + for (int i = 0; i < Blah::Input::max_mouse_buttons; i++) + { + g_next_state.mouse.pressed[i] = false; + g_next_state.mouse.released[i] = false; + } + + g_next_state.mouse.wheel = Point::zero; + g_next_state.keyboard.text.clear(); + + for (int i = 0; i < Blah::Input::max_controllers; i++) + { + ControllerState* controller = &(g_next_state.controllers[i]); + + if (!controller->is_connected) + controller->name = nullptr; + + for (int j = 0; j < Blah::Input::max_controller_buttons; j++) + { + controller->pressed[j] = false; + controller->released[j] = false; + } + } + } + + // update bindings + + for (int i = 0; i < g_bindings.size(); i++) + { + if (g_bindings[i].use_count() <= 0) + { + g_bindings.erase(i); + i--; + } + else if (auto binding = g_bindings[i].lock()) + { + binding->update(); + } + } + + for (int i = 0; i < g_axes.size(); i++) + { + if (g_axes[i].use_count() <= 0) + { + g_axes.erase(i); + i--; + } + else if (auto binding = g_axes[i].lock()) + { + binding->update(); + } + } + + for (int i = 0; i < g_sticks.size(); i++) + { + if (g_sticks[i].use_count() <= 0) + { + g_sticks.erase(i); + i--; + } + else if (auto binding = g_sticks[i].lock()) + { + binding->update(); + } + } +} + +void InputBackend::on_mouse_move(float x, float y) +{ + g_next_state.mouse.position.x = x; + g_next_state.mouse.position.y = y; + + Point size = Point(App::width(), App::height()); + Point draw = Point(App::draw_width(), App::draw_height()); + + g_next_state.mouse.draw_position.x = (x / (float)size.x) * draw.x; + g_next_state.mouse.draw_position.y = (y / (float)size.y) * draw.y; +} + +void InputBackend::on_mouse_screen_move(float x, float y) +{ + g_next_state.mouse.screen_position.x = x; + g_next_state.mouse.screen_position.y = y; +} + +void InputBackend::on_mouse_down(MouseButton button) +{ + int i = (int)button; + if (i >= 0 && i < Blah::Input::max_mouse_buttons) + { + g_next_state.mouse.down[i] = true; + g_next_state.mouse.pressed[i] = true; + g_next_state.mouse.timestamp[i] = Time::ticks; + } +} + +void InputBackend::on_mouse_up(MouseButton button) +{ + int i = (int)button; + if (i >= 0 && i < Blah::Input::max_mouse_buttons) + { + g_next_state.mouse.down[i] = false; + g_next_state.mouse.released[i] = true; + } +} + +void InputBackend::on_key_down(Key key) +{ + int i = (int)key; + if (i >= 0 && i < Blah::Input::max_keyboard_keys) + { + g_next_state.keyboard.down[i] = true; + g_next_state.keyboard.pressed[i] = true; + g_next_state.keyboard.timestamp[i] = Time::ticks; + } +} + +void InputBackend::on_mouse_wheel(Point wheel) +{ + g_next_state.mouse.wheel = wheel; +} + +void InputBackend::on_key_up(Key key) +{ + int i = (int)key; + if (i >= 0 && i < Blah::Input::max_keyboard_keys) + { + g_next_state.keyboard.down[i] = false; + g_next_state.keyboard.released[i] = true; + } +} + +void InputBackend::on_text_utf8(const char* text) +{ + 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, u16 vendor, u16 product, u16 version) +{ + if (index < Blah::Input::max_controllers) + { + ControllerState* controller = &(g_next_state.controllers[index]); + *controller = g_empty_controller; + controller->name = name; + controller->is_connected = true; + controller->is_gamepad = is_gamepad; + controller->button_count = button_count; + controller->axis_count = axis_count; + controller->vendor = vendor; + controller->product = product; + controller->version = version; + } +} + +void InputBackend::on_controller_disconnect(int index) +{ + if (index < Blah::Input::max_controllers) + g_next_state.controllers[index] = g_empty_controller; +} + +void InputBackend::on_button_down(int index, int button) +{ + if (index < Blah::Input::max_controllers && + button < Blah::Input::max_controller_buttons && + g_next_state.controllers[index].is_connected && + button < g_next_state.controllers[index].button_count) + { + g_next_state.controllers[index].down[button] = true; + g_next_state.controllers[index].pressed[button] = true; + g_next_state.controllers[index].button_timestamp[button] = Time::ticks; + } +} + +void InputBackend::on_button_up(int index, int button) +{ + if (index < Blah::Input::max_controllers && + button < Blah::Input::max_controller_buttons && + g_next_state.controllers[index].is_connected && + button < g_next_state.controllers[index].button_count) + { + g_next_state.controllers[index].down[button] = false; + g_next_state.controllers[index].released[button] = true; + } +} + +void InputBackend::on_axis_move(int index, int axis, float value) +{ + if (index < Blah::Input::max_controllers && + axis < Blah::Input::max_controller_axis && + g_next_state.controllers[index].is_connected && + axis < g_next_state.controllers[index].axis_count) + { + g_next_state.controllers[index].axis[axis] = value; + g_next_state.controllers[index].axis_timestamp[axis] = Time::ticks; + } +} + +const InputState* Input::state() +{ + return &g_curr_state; +} + +const InputState* Input::last_state() +{ + return &g_last_state; +} + +Vec2 Input::mouse() +{ + return g_curr_state.mouse.position; +} + + +Vec2 Input::mouse_draw() +{ + return Vec2(g_curr_state.mouse.draw_position); +} + +Vec2 Input::mouse_screen() +{ + return Vec2(g_curr_state.mouse.screen_position); +} + +bool Input::pressed(MouseButton button) +{ + int i = (int)button; + return i >= 0 && i < Blah::Input::max_mouse_buttons&& g_curr_state.mouse.pressed[i]; +} + +bool Input::down(MouseButton button) +{ + int i = (int)button; + return i >= 0 && i < Blah::Input::max_mouse_buttons&& g_curr_state.mouse.down[i]; +} + +bool Input::released(MouseButton button) +{ + int i = (int)button; + return i >= 0 && i < Blah::Input::max_mouse_buttons&& g_curr_state.mouse.released[i]; +} + +Point Input::mouse_wheel() +{ + return g_curr_state.mouse.wheel; +} + +bool Input::pressed(Key key) +{ + int i = (int)key; + return i > 0 && i < Blah::Input::max_keyboard_keys&& g_curr_state.keyboard.pressed[i]; +} + +bool Input::down(Key key) +{ + int i = (int)key; + return i > 0 && i < Blah::Input::max_keyboard_keys&& g_curr_state.keyboard.down[i]; +} + +bool Input::released(Key key) +{ + int i = (int)key; + return i > 0 && i < Blah::Input::max_keyboard_keys&& g_curr_state.keyboard.released[i]; +} + +bool Input::ctrl() +{ + return down(Key::LeftControl) || down(Key::RightControl); +} + +bool Input::shift() +{ + return down(Key::LeftShift) || down(Key::RightShift); +} + +bool Input::alt() +{ + return down(Key::LeftAlt) || down(Key::RightAlt); +} + +const char* Input::text() +{ + return g_curr_state.keyboard.text; +} + +const ControllerState* Input::controller(int controllerIndex) +{ + if (controllerIndex >= Blah::Input::max_controllers) + { + Log::warn("Trying to access a out-of-range controller at %i", controllerIndex); + return &g_empty_controller; + } + else if (!g_curr_state.controllers[controllerIndex].is_connected) + { + return &g_empty_controller; + } + else + { + return &g_curr_state.controllers[controllerIndex]; + } +} + +bool Input::pressed(int controllerIndex, Button button) +{ + int i = (int)button; + if (controllerIndex < Blah::Input::max_controllers && i >= 0 && i < Blah::Input::max_controller_buttons) + return g_curr_state.controllers[controllerIndex].pressed[i]; + return false; +} + +bool Input::down(int controllerIndex, Button button) +{ + int i = (int)button; + if (controllerIndex < Blah::Input::max_controllers && i >= 0 && i < Blah::Input::max_controller_buttons) + return g_curr_state.controllers[controllerIndex].down[i]; + return false; +} + +bool Input::released(int controllerIndex, Button button) +{ + int i = (int)button; + if (controllerIndex < Blah::Input::max_controllers && i >= 0 && i < Blah::Input::max_controller_buttons) + return g_curr_state.controllers[controllerIndex].released[i]; + return false; +} + +float Input::axis_check(int controllerIndex, Axis axis) +{ + int i = (int)axis; + if (controllerIndex < Blah::Input::max_controllers && i >= 0 && i < Blah::Input::max_controller_axis) + return g_curr_state.controllers[controllerIndex].axis[i]; + return 0; +} + +int Input::axis_check(int fallback, Key negative, Key positive) +{ + if (Input::pressed(positive)) + return 1; + else if (Input::pressed(negative)) + return -1; + else + { + bool pos = Input::down(positive); + bool neg = Input::down(negative); + + if (pos && neg) + return fallback; + else if (pos) + return 1; + else if (neg) + return -1; + else + return 0; + } +} + +int Input::axis_check(int fallback, int controllerIndex, Button negative, Button positive) +{ + if (Input::pressed(controllerIndex, positive)) + return 1; + else if (Input::pressed(controllerIndex, negative)) + return -1; + else + { + bool pos = Input::down(controllerIndex, positive); + bool neg = Input::down(controllerIndex, negative); + + if (pos && neg) + return fallback; + else if (pos) + return 1; + else if (neg) + return -1; + else + return 0; + } +} + +const char* Input::name_of(Key key) +{ + switch (key) + { + #define DEFINE_KEY(name, value) case Key::name: return #name; + BLAH_KEY_DEFINITIONS + #undef DEFINE_KEY + } + + return "Unknown"; +} + +const char* Input::name_of(Button button) +{ + switch (button) + { + #define DEFINE_BTN(name, value) case Button::name: return #name; + BLAH_BUTTON_DEFINITIONS + #undef DEFINE_BTN + } + + return "Unknown"; +} + +InputBindingRef Input::register_binding(const InputBinding& binding) +{ + auto result = std::make_shared(binding); + g_bindings.push_back(WeakRef(result)); + return result; +} + +AxisBindingRef Input::register_binding(const AxisBinding& binding) +{ + auto result = std::make_shared(binding); + g_axes.push_back(WeakRef(result)); + return result; +} + +StickBindingRef Input::register_binding(const StickBinding& binding) +{ + auto result = std::make_shared(binding); + g_sticks.push_back(WeakRef(result)); + return result; +} + + +InputBinding::TriggerBind::TriggerBind(Axis axis) + : axis(axis) +{ + +} + +InputBinding::TriggerBind::TriggerBind(int controller, Axis axis, float threshold, bool positive) + : controller(controller), axis(axis), threshold(threshold), positive(positive) +{ + +} + +bool InputBinding::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; +} + +InputBinding::ButtonBind::ButtonBind(Button button) + : button(button) {} + +InputBinding::ButtonBind::ButtonBind(int controller, Button button) + : controller(controller), button(button) {} + +bool InputBinding::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 InputBinding::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 InputBinding::down() const +{ + return m_down; +} + +float InputBinding::value() const +{ + return m_value; +} + +int InputBinding::sign() const +{ + return (int)Calc::sign(m_value); +} + +double InputBinding::timestamp() const +{ + return m_last_timestamp; +} + +void InputBinding::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 InputBinding::consume_press() +{ + m_press_consumed = true; + m_last_press_time = -1; +} + +void InputBinding::consume_release() +{ + m_release_consumed = true; + m_last_release_time = -1; +} + +InputBinding& InputBinding::add(Key key) +{ + keys.push_back(key); + return *this; +} + +InputBinding& InputBinding::add(ButtonBind button) +{ + buttons.push_back(button); + return *this; +} + +InputBinding& InputBinding::add(TriggerBind trigger) +{ + triggers.push_back(trigger); + return *this; +} + +InputBinding& InputBinding::add(MouseButton button) +{ + mouse.push_back(button); + return *this; +} + +InputBinding& InputBinding::add_left_trigger(int controller, float threshold) +{ + triggers.push_back(TriggerBind(controller, Axis::LeftTrigger, threshold, true)); + return *this; +} + +InputBinding& InputBinding::add_right_trigger(int controller, float threshold) +{ + triggers.push_back(TriggerBind(controller, Axis::RightTrigger, threshold, true)); + return *this; +} + +InputBinding& InputBinding::set_controller(int index) +{ + for (auto& it : buttons) + it.controller = index; + for (auto& it : triggers) + it.controller = index; + + return *this; +} + +void InputBinding::clear() +{ + keys.clear(); + buttons.clear(); + triggers.clear(); + mouse.clear(); +} + +bool InputBinding::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 InputBinding::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 InputBinding::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 InputBinding::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(InputBinding::TriggerBind(controller, Axis::LeftX, threshold, false)); + positive.add(InputBinding::TriggerBind(controller, Axis::LeftX, threshold, true)); + return *this; +} + +AxisBinding& AxisBinding::add_left_stick_y(int controller, float threshold) +{ + negative.add(InputBinding::TriggerBind(controller, Axis::LeftY, threshold, false)); + positive.add(InputBinding::TriggerBind(controller, Axis::LeftY, threshold, true)); + return *this; +} + +AxisBinding& AxisBinding::add_right_stick_x(int controller, float threshold) +{ + negative.add(InputBinding::TriggerBind(controller, Axis::RightX, threshold, false)); + positive.add(InputBinding::TriggerBind(controller, Axis::RightX, threshold, true)); + return *this; +} + +AxisBinding& AxisBinding::add_right_stick_y(int controller, float threshold) +{ + negative.add(InputBinding::TriggerBind(controller, Axis::RightY, threshold, false)); + positive.add(InputBinding::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(InputBinding::ButtonBind(controller, Button::Left)); + x.positive.add(InputBinding::ButtonBind(controller, Button::Right)); + y.negative.add(InputBinding::ButtonBind(controller, Button::Up)); + y.positive.add(InputBinding::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(); +} + diff --git a/src/input/binding.cpp b/src/input/binding.cpp deleted file mode 100644 index 595df6f..0000000 --- a/src/input/binding.cpp +++ /dev/null @@ -1,469 +0,0 @@ -#include -#include -#include - -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(); -} \ No newline at end of file diff --git a/src/input/binding_registry.cpp b/src/input/binding_registry.cpp deleted file mode 100644 index 43ec290..0000000 --- a/src/input/binding_registry.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include - -using namespace Blah; - -Vector> BindingRegistry::bindings; -Vector> BindingRegistry::axes; -Vector> BindingRegistry::sticks; - -BindingRef BindingRegistry::register_binding(const Binding& binding) -{ - auto result = std::make_shared(binding); - bindings.push_back(std::weak_ptr(result)); - return result; -} - -AxisBindingRef BindingRegistry::register_axis(const AxisBinding& binding) -{ - auto result = std::make_shared(binding); - axes.push_back(std::weak_ptr(result)); - return result; -} - -StickBindingRef BindingRegistry::register_stick(const StickBinding& binding) -{ - auto result = std::make_shared(binding); - sticks.push_back(std::weak_ptr(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(); - } - } -} diff --git a/src/input/input.cpp b/src/input/input.cpp deleted file mode 100644 index c209317..0000000 --- a/src/input/input.cpp +++ /dev/null @@ -1,406 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "../internal/input_backend.h" -#include - -using namespace Blah; - -namespace -{ - InputState g_last_state; - InputState g_curr_state; - InputState g_next_state; - InputState g_empty_state; - ControllerState g_empty_controller; -} - -void InputBackend::init() -{ - g_empty_controller.name = "Disconnected"; - for (int i = 0; i < Blah::Input::max_controllers; i++) - g_empty_state.controllers[i].name = g_empty_controller.name; - - g_last_state = g_empty_state; - g_curr_state = g_empty_state; - g_next_state = g_empty_state; -} - -void InputBackend::frame() -{ - // cycle states - g_last_state = g_curr_state; - g_curr_state = g_next_state; - - // copy state, clear pressed / released values - { - for (int i = 0; i < Blah::Input::max_keyboard_keys; i++) - { - g_next_state.keyboard.pressed[i] = false; - g_next_state.keyboard.released[i] = false; - } - - for (int i = 0; i < Blah::Input::max_mouse_buttons; i++) - { - g_next_state.mouse.pressed[i] = false; - g_next_state.mouse.released[i] = false; - } - - g_next_state.mouse.wheel = Point::zero; - g_next_state.keyboard.text.clear(); - - for (int i = 0; i < Blah::Input::max_controllers; i++) - { - ControllerState* controller = &(g_next_state.controllers[i]); - - if (!controller->is_connected) - controller->name = nullptr; - - for (int j = 0; j < Blah::Input::max_controller_buttons; j++) - { - controller->pressed[j] = false; - controller->released[j] = false; - } - } - } - - // update bindings - BindingRegistry::update(); -} - -void InputBackend::on_mouse_move(float x, float y) -{ - g_next_state.mouse.position.x = x; - g_next_state.mouse.position.y = y; - - Point size = Point(App::width(), App::height()); - Point draw = Point(App::draw_width(), App::draw_height()); - - g_next_state.mouse.draw_position.x = (x / (float)size.x) * draw.x; - g_next_state.mouse.draw_position.y = (y / (float)size.y) * draw.y; -} - -void InputBackend::on_mouse_screen_move(float x, float y) -{ - g_next_state.mouse.screen_position.x = x; - g_next_state.mouse.screen_position.y = y; -} - -void InputBackend::on_mouse_down(MouseButton button) -{ - int i = (int)button; - if (i >= 0 && i < Blah::Input::max_mouse_buttons) - { - g_next_state.mouse.down[i] = true; - g_next_state.mouse.pressed[i] = true; - g_next_state.mouse.timestamp[i] = Time::ticks; - } -} - -void InputBackend::on_mouse_up(MouseButton button) -{ - int i = (int)button; - if (i >= 0 && i < Blah::Input::max_mouse_buttons) - { - g_next_state.mouse.down[i] = false; - g_next_state.mouse.released[i] = true; - } -} - -void InputBackend::on_key_down(Key key) -{ - int i = (int)key; - if (i >= 0 && i < Blah::Input::max_keyboard_keys) - { - g_next_state.keyboard.down[i] = true; - g_next_state.keyboard.pressed[i] = true; - g_next_state.keyboard.timestamp[i] = Time::ticks; - } -} - -void InputBackend::on_mouse_wheel(Point wheel) -{ - g_next_state.mouse.wheel = wheel; -} - -void InputBackend::on_key_up(Key key) -{ - int i = (int)key; - if (i >= 0 && i < Blah::Input::max_keyboard_keys) - { - g_next_state.keyboard.down[i] = false; - g_next_state.keyboard.released[i] = true; - } -} - -void InputBackend::on_text_utf8(const char* text) -{ - 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, u16 vendor, u16 product, u16 version) -{ - if (index < Blah::Input::max_controllers) - { - ControllerState* controller = &(g_next_state.controllers[index]); - *controller = g_empty_controller; - controller->name = name; - controller->is_connected = true; - controller->is_gamepad = is_gamepad; - controller->button_count = button_count; - controller->axis_count = axis_count; - controller->vendor = vendor; - controller->product = product; - controller->version = version; - } -} - -void InputBackend::on_controller_disconnect(int index) -{ - if (index < Blah::Input::max_controllers) - g_next_state.controllers[index] = g_empty_controller; -} - -void InputBackend::on_button_down(int index, int button) -{ - if (index < Blah::Input::max_controllers && - button < Blah::Input::max_controller_buttons && - g_next_state.controllers[index].is_connected && - button < g_next_state.controllers[index].button_count) - { - g_next_state.controllers[index].down[button] = true; - g_next_state.controllers[index].pressed[button] = true; - g_next_state.controllers[index].button_timestamp[button] = Time::ticks; - } -} - -void InputBackend::on_button_up(int index, int button) -{ - if (index < Blah::Input::max_controllers && - button < Blah::Input::max_controller_buttons && - g_next_state.controllers[index].is_connected && - button < g_next_state.controllers[index].button_count) - { - g_next_state.controllers[index].down[button] = false; - g_next_state.controllers[index].released[button] = true; - } -} - -void InputBackend::on_axis_move(int index, int axis, float value) -{ - if (index < Blah::Input::max_controllers && - axis < Blah::Input::max_controller_axis && - g_next_state.controllers[index].is_connected && - axis < g_next_state.controllers[index].axis_count) - { - g_next_state.controllers[index].axis[axis] = value; - g_next_state.controllers[index].axis_timestamp[axis] = Time::ticks; - } -} - -const InputState* Input::state() -{ - return &g_curr_state; -} - -const InputState* Input::last_state() -{ - return &g_last_state; -} - -Vec2 Input::mouse() -{ - return g_curr_state.mouse.position; -} - - -Vec2 Input::mouse_draw() -{ - return Vec2(g_curr_state.mouse.draw_position); -} - -Vec2 Input::mouse_screen() -{ - return Vec2(g_curr_state.mouse.screen_position); -} - -bool Input::pressed(MouseButton button) -{ - int i = (int)button; - return i >= 0 && i < Blah::Input::max_mouse_buttons&& g_curr_state.mouse.pressed[i]; -} - -bool Input::down(MouseButton button) -{ - int i = (int)button; - return i >= 0 && i < Blah::Input::max_mouse_buttons&& g_curr_state.mouse.down[i]; -} - -bool Input::released(MouseButton button) -{ - int i = (int)button; - return i >= 0 && i < Blah::Input::max_mouse_buttons&& g_curr_state.mouse.released[i]; -} - -Point Input::mouse_wheel() -{ - return g_curr_state.mouse.wheel; -} - -bool Input::pressed(Key key) -{ - int i = (int)key; - return i > 0 && i < Blah::Input::max_keyboard_keys&& g_curr_state.keyboard.pressed[i]; -} - -bool Input::down(Key key) -{ - int i = (int)key; - return i > 0 && i < Blah::Input::max_keyboard_keys&& g_curr_state.keyboard.down[i]; -} - -bool Input::released(Key key) -{ - int i = (int)key; - return i > 0 && i < Blah::Input::max_keyboard_keys&& g_curr_state.keyboard.released[i]; -} - -bool Input::ctrl() -{ - return down(Key::LeftControl) || down(Key::RightControl); -} - -bool Input::shift() -{ - return down(Key::LeftShift) || down(Key::RightShift); -} - -bool Input::alt() -{ - return down(Key::LeftAlt) || down(Key::RightAlt); -} - -const char* Input::text() -{ - return g_curr_state.keyboard.text; -} - -const ControllerState* Input::controller(int controllerIndex) -{ - if (controllerIndex >= Blah::Input::max_controllers) - { - Log::warn("Trying to access a out-of-range controller at %i", controllerIndex); - return &g_empty_controller; - } - else if (!g_curr_state.controllers[controllerIndex].is_connected) - { - return &g_empty_controller; - } - else - { - return &g_curr_state.controllers[controllerIndex]; - } -} - -bool Input::pressed(int controllerIndex, Button button) -{ - int i = (int)button; - if (controllerIndex < Blah::Input::max_controllers && i >= 0 && i < Blah::Input::max_controller_buttons) - return g_curr_state.controllers[controllerIndex].pressed[i]; - return false; -} - -bool Input::down(int controllerIndex, Button button) -{ - int i = (int)button; - if (controllerIndex < Blah::Input::max_controllers && i >= 0 && i < Blah::Input::max_controller_buttons) - return g_curr_state.controllers[controllerIndex].down[i]; - return false; -} - -bool Input::released(int controllerIndex, Button button) -{ - int i = (int)button; - if (controllerIndex < Blah::Input::max_controllers && i >= 0 && i < Blah::Input::max_controller_buttons) - return g_curr_state.controllers[controllerIndex].released[i]; - return false; -} - -float Input::axis_check(int controllerIndex, Axis axis) -{ - int i = (int)axis; - if (controllerIndex < Blah::Input::max_controllers && i >= 0 && i < Blah::Input::max_controller_axis) - return g_curr_state.controllers[controllerIndex].axis[i]; - return 0; -} - -int Input::axis_check(int fallback, Key negative, Key positive) -{ - if (Input::pressed(positive)) - return 1; - else if (Input::pressed(negative)) - return -1; - else - { - bool pos = Input::down(positive); - bool neg = Input::down(negative); - - if (pos && neg) - return fallback; - else if (pos) - return 1; - else if (neg) - return -1; - else - return 0; - } -} - -int Input::axis_check(int fallback, int controllerIndex, Button negative, Button positive) -{ - if (Input::pressed(controllerIndex, positive)) - return 1; - else if (Input::pressed(controllerIndex, negative)) - return -1; - else - { - bool pos = Input::down(controllerIndex, positive); - bool neg = Input::down(controllerIndex, negative); - - if (pos && neg) - return fallback; - else if (pos) - return 1; - else if (neg) - return -1; - else - return 0; - } -} - -const char* Input::name_of(Key key) -{ - switch (key) - { - #define DEFINE_KEY(name, value) case Key::name: return #name; - BLAH_KEY_DEFINITIONS - #undef DEFINE_KEY - } - - return "Unknown"; -} - -const char* Input::name_of(Button button) -{ - switch (button) - { - #define DEFINE_BTN(name, value) case Button::name: return #name; - BLAH_BUTTON_DEFINITIONS - #undef DEFINE_BTN - } - - return "Unknown"; -} \ No newline at end of file diff --git a/src/internal/graphics_backend.h b/src/internal/graphics_backend.h index 9185c39..16a1261 100644 --- a/src/internal/graphics_backend.h +++ b/src/internal/graphics_backend.h @@ -1,12 +1,12 @@ #pragma once -#include +#include #include #include -#include +#include #include #include #include -#include +#include namespace Blah { @@ -45,9 +45,9 @@ namespace Blah // if the Texture is invalid, this should return an empty reference. TextureRef create_texture(int width, int height, TextureFormat format); - // Creates a new FrameBuffer. - // if the FrameBuffer is invalid, this should return an empty reference. - FrameBufferRef create_framebuffer(int width, int height, const TextureFormat* attachments, int attachment_count); + // Creates a new Target. + // if the Target is invalid, this should return an empty reference. + TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count); // Creates a new Shader. // if the Shader is invalid, this should return an empty reference. diff --git a/src/internal/graphics_backend_d3d11.cpp b/src/internal/graphics_backend_d3d11.cpp index 03b5467..39771bf 100644 --- a/src/internal/graphics_backend_d3d11.cpp +++ b/src/internal/graphics_backend_d3d11.cpp @@ -5,7 +5,7 @@ #include "../internal/graphics_backend.h" #include "../internal/platform_backend.h" -#include +#include #include #include #include @@ -252,7 +252,7 @@ namespace Blah hr = state.device->CreateTexture2D(&desc, NULL, &staging); if (!SUCCEEDED(hr)) { - BLAH_ERROR("Failed to create staging texture to get data"); + BLAH_ASSERT(false, "Failed to create staging texture to get data"); return; } } @@ -270,7 +270,7 @@ namespace Blah if (!SUCCEEDED(hr)) { - BLAH_ERROR("Failed to get texture data"); + BLAH_ASSERT(false, "Failed to get texture data"); return; } @@ -285,16 +285,16 @@ namespace Blah }; - class D3D11_FrameBuffer : public FrameBuffer + class D3D11_Target : public Target { private: Attachments m_attachments; public: - StackVector color_views; + StackVector color_views; ID3D11DepthStencilView* depth_view = nullptr; - D3D11_FrameBuffer(int width, int height, const TextureFormat* attachments, int attachment_count) + D3D11_Target(int width, int height, const TextureFormat* attachments, int attachment_count) { for (int i = 0; i < attachment_count; i++) { @@ -315,19 +315,19 @@ namespace Blah } } - ~D3D11_FrameBuffer() + ~D3D11_Target() { for (auto& it : color_views) it->Release(); color_views.clear(); } - Attachments& attachments() override + Attachments& textures() override { return m_attachments; } - const Attachments& attachments() const override + const Attachments& textures() const override { return m_attachments; } @@ -841,9 +841,9 @@ namespace Blah return TextureRef(); } - FrameBufferRef GraphicsBackend::create_framebuffer(int width, int height, const TextureFormat* attachments, int attachment_count) + TargetRef GraphicsBackend::create_target(int width, int height, const TextureFormat* attachments, int attachment_count) { - return FrameBufferRef(new D3D11_FrameBuffer(width, height, attachments, attachment_count)); + return TargetRef(new D3D11_Target(width, height, attachments, attachment_count)); } ShaderRef GraphicsBackend::create_shader(const ShaderData* data) @@ -876,7 +876,7 @@ namespace Blah } else { - auto target = (D3D11_FrameBuffer*)(pass.target.get()); + auto target = (D3D11_Target*)(pass.target.get()); ctx->OMSetRenderTargets(target->color_views.size(), target->color_views.begin(), target->depth_view); } diff --git a/src/internal/graphics_backend_dummy.cpp b/src/internal/graphics_backend_dummy.cpp index 1f97c83..d723eb9 100644 --- a/src/internal/graphics_backend_dummy.cpp +++ b/src/internal/graphics_backend_dummy.cpp @@ -2,7 +2,7 @@ #include "../internal/graphics_backend.h" #include "../internal/platform_backend.h" -#include +#include namespace Blah { @@ -56,14 +56,14 @@ namespace Blah }; - class Dummy_FrameBuffer : public FrameBuffer + class Dummy_Target : public Target { private: Attachments m_attachments; public: - Dummy_FrameBuffer(int width, int height, const TextureFormat* attachments, int attachmentCount) + Dummy_Target(int width, int height, const TextureFormat* attachments, int attachmentCount) { for (int i = 0; i < attachmentCount; i++) { @@ -73,12 +73,12 @@ namespace Blah } } - virtual Attachments& attachments() override + virtual Attachments& textures() override { return m_attachments; } - virtual const Attachments& attachments() const override + virtual const Attachments& textures() const override { return m_attachments; } @@ -186,9 +186,9 @@ namespace Blah return TextureRef(new Dummy_Texture(width, height, format, false)); } - FrameBufferRef GraphicsBackend::create_framebuffer(int width, int height, const TextureFormat* attachments, int attachmentCount) + TargetRef GraphicsBackend::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount) { - return FrameBufferRef(new Dummy_FrameBuffer(width, height, attachments, attachmentCount)); + return TargetRef(new Dummy_Target(width, height, attachments, attachmentCount)); } ShaderRef GraphicsBackend::create_shader(const ShaderData* data) diff --git a/src/internal/input_backend.h b/src/internal/input_backend.h index 8c09624..8615ef1 100644 --- a/src/internal/input_backend.h +++ b/src/internal/input_backend.h @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace Blah { diff --git a/src/internal/platform_backend.h b/src/internal/platform_backend.h index 4f94a7e..6ca47ac 100644 --- a/src/internal/platform_backend.h +++ b/src/internal/platform_backend.h @@ -1,6 +1,6 @@ #pragma once -#include -#include +#include +#include #include namespace Blah @@ -66,6 +66,9 @@ namespace Blah // Returns the absolute path to the user directory where save data and settings should be stored const char* user_path(); + // Opens a file and sets the handle, or returns an empty handle if it fails + FileRef file_open(const char* path, FileMode mode); + // Returns true if a file with the given path exists bool file_exists(const char* path); @@ -87,27 +90,6 @@ namespace Blah // 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 - i64 file_length(FileHandle file); - - // Returns the Position of the file - i64 file_position(FileHandle file); - - // Seeks the Position of the file and returns the new position from the start of the file - i64 file_seek(FileHandle file, i64 seekTo); - - // Reads a specific number of elements of a given size from the file into ptr - i64 file_read(FileHandle file, void* ptr, i64 size); - - // Writes a specific number of elements of the given size from ptr to the file - i64 file_write(FileHandle file, const void* ptr, i64 size); - - // Closes a file - void file_close(FileHandle file); - // OpenGL Methods void* gl_get_func(const char* name); void* gl_context_create(); diff --git a/src/internal/platform_backend_sdl2.cpp b/src/internal/platform_backend_sdl2.cpp index b3ece7c..aa9c68b 100644 --- a/src/internal/platform_backend_sdl2.cpp +++ b/src/internal/platform_backend_sdl2.cpp @@ -3,11 +3,11 @@ #include "../internal/platform_backend.h" #include "../internal/input_backend.h" #include "../internal/graphics_backend.h" -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include @@ -30,7 +30,7 @@ namespace fs = std::filesystem; using namespace Blah; -namespace +namespace Blah { SDL_Window* window = nullptr; SDL_Joystick* joysticks[Blah::Input::max_controllers]; @@ -70,671 +70,687 @@ namespace } return -1; } -} -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? -#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::info("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) + // Custom File class + class Blah_SDL2_File : public File { - Log::error("Failed to initialize SDL2"); - return false; - } + private: + SDL_RWops* m_handle; - int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; + public: + Blah_SDL2_File(SDL_RWops* handle) + { + m_handle = handle; + } - // enable OpenGL - if (App::renderer() == Renderer::OpenGL) + ~Blah_SDL2_File() + { + if (m_handle) + SDL_RWclose(m_handle); + } + + size_t length() override + { + return SDL_RWsize(m_handle); + } + + size_t position() override + { + return SDL_RWtell(m_handle); + } + + size_t seek(size_t position) override + { + return SDL_RWseek(m_handle, position, RW_SEEK_SET); + } + + size_t read(unsigned char* buffer, size_t length) override + { + return SDL_RWread(m_handle, buffer, sizeof(char), length); + } + + size_t write(const unsigned char* buffer, size_t length) override + { + return SDL_RWwrite(m_handle, buffer, sizeof(char), length); + } + }; + + bool PlatformBackend::init(const Config* config) { - flags |= SDL_WINDOW_OPENGL; - -#ifdef __EMSCRIPTEN__ - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); -#else - 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); + // Required to call this for Windows + // I'm not sure why SDL2 doesn't do this on Windows automatically? + #if _WIN32 + SetProcessDPIAware(); + #endif // 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); -#endif - } - // enable DirectX - else if (App::renderer() == Renderer::D3D11) - { + // 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::info("SDL v%i.%i.%i", version.major, version.minor, version.patch); - // 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) + // initialize SDL + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) { - // scale the window up basesd on the display DPI - float hidpiRes = 96; - float dpi = (ddpi / hidpiRes); - if (dpi != 1) + Log::error("Failed to initialize SDL2"); + return false; + } + + int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; + + // enable OpenGL + if (App::renderer() == Renderer::OpenGL) + { + flags |= SDL_WINDOW_OPENGL; + + #ifdef __EMSCRIPTEN__ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + #else + 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); + #endif + } + // enable DirectX + else if (App::renderer() == Renderer::D3D11) + { + + } + + // 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) { - 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)); + // 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 + #endif - // set window properties - SDL_SetWindowResizable(window, SDL_TRUE); - SDL_SetWindowMinimumSize(window, 256, 256); + // set window properties + SDL_SetWindowResizable(window, SDL_TRUE); + SDL_SetWindowMinimumSize(window, 256, 256); - return true; -} - -void PlatformBackend::ready() -{ -#ifndef __EMSCRIPTEN__ - // enable V-Sync - // TODO: - // This should be a toggle or controllable in some way - if (App::renderer() == Renderer::OpenGL) - SDL_GL_SetSwapInterval(1); -#endif -} - -void PlatformBackend::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(); -} - -u64 PlatformBackend::ticks() -{ - auto counter = SDL_GetPerformanceCounter(); - auto per_second = (double)SDL_GetPerformanceFrequency(); - return (u64)(counter * (Time::ticks_per_second / per_second)); -} - -// Macro defined by X11 conflicts with MouseButton enum -#undef None - -void PlatformBackend::frame() -{ - // update the mouse every frame - { - int winX, winY, x, y; - SDL_GetWindowPosition(window, &winX, &winY); - SDL_GetGlobalMouseState(&x, &y); - - InputBackend::on_mouse_move((float)(x - winX), (float)(y - winY)); - InputBackend::on_mouse_screen_move((float)x, (float)y); + return true; } - // poll normal events - SDL_Event event; - while (SDL_PollEvent(&event)) + void PlatformBackend::ready() { - 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; - InputBackend::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; - InputBackend::on_mouse_up(btn); - } - else if (event.type == SDL_MOUSEWHEEL) - { - InputBackend::on_mouse_wheel(Point(event.wheel.x, event.wheel.y)); - } - // Keyboard - else if (event.type == SDL_KEYDOWN) - { - if (event.key.repeat == 0) - InputBackend::on_key_down((Key)event.key.keysym.scancode); - } - else if (event.type == SDL_KEYUP) - { - if (event.key.repeat == 0) - InputBackend::on_key_up((Key)event.key.keysym.scancode); - } - else if (event.type == SDL_TEXTINPUT) - { - InputBackend::on_text_utf8(event.text.text); - } - // Joystick Controller - else if (event.type == SDL_JOYDEVICEADDED) - { - auto index = event.jdevice.which; + #ifndef __EMSCRIPTEN__ + // enable V-Sync + // TODO: + // This should be a toggle or controllable in some way + if (App::renderer() == Renderer::OpenGL) + SDL_GL_SetSwapInterval(1); + #endif + } - if (SDL_IsGameController(index) == SDL_FALSE && index >= 0 && index < Input::max_controllers) + void PlatformBackend::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(); + } + + u64 PlatformBackend::ticks() + { + auto counter = SDL_GetPerformanceCounter(); + auto per_second = (double)SDL_GetPerformanceFrequency(); + return (u64)(counter * (Time::ticks_per_second / per_second)); + } + + // Macro defined by X11 conflicts with MouseButton enum + #undef None + + void PlatformBackend::frame() + { + // update the mouse every frame + { + int winX, winY, x, y; + SDL_GetWindowPosition(window, &winX, &winY); + SDL_GetGlobalMouseState(&x, &y); + + InputBackend::on_mouse_move((float)(x - winX), (float)(y - winY)); + InputBackend::on_mouse_screen_move((float)x, (float)y); + } + + // poll normal events + SDL_Event event; + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) { - auto ptr = joysticks[index] = SDL_JoystickOpen(index); - auto name = SDL_JoystickName(ptr); - auto button_count = SDL_JoystickNumButtons(ptr); - auto axis_count = SDL_JoystickNumAxes(ptr); - auto vendor = SDL_JoystickGetVendor(ptr); - auto product = SDL_JoystickGetProduct(ptr); - auto version = SDL_JoystickGetProductVersion(ptr); - - InputBackend::on_controller_connect(index, name, 0, button_count, axis_count, vendor, product, version); + auto config = App::config(); + if (config->on_exit_request != nullptr) + config->on_exit_request(); } - } - else if (event.type == SDL_JOYDEVICEREMOVED) - { - auto index = find_joystick_index(event.jdevice.which); - if (index >= 0) + // Mouse + else if (event.type == SDL_MOUSEBUTTONDOWN) { - if (SDL_IsGameController(index) == SDL_FALSE) + 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; + InputBackend::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; + InputBackend::on_mouse_up(btn); + } + else if (event.type == SDL_MOUSEWHEEL) + { + InputBackend::on_mouse_wheel(Point(event.wheel.x, event.wheel.y)); + } + // Keyboard + else if (event.type == SDL_KEYDOWN) + { + if (event.key.repeat == 0) + InputBackend::on_key_down((Key)event.key.keysym.scancode); + } + else if (event.type == SDL_KEYUP) + { + if (event.key.repeat == 0) + InputBackend::on_key_up((Key)event.key.keysym.scancode); + } + else if (event.type == SDL_TEXTINPUT) + { + InputBackend::on_text_utf8(event.text.text); + } + // Joystick Controller + else if (event.type == SDL_JOYDEVICEADDED) + { + auto index = event.jdevice.which; + + if (SDL_IsGameController(index) == SDL_FALSE && index >= 0 && index < Input::max_controllers) + { + auto ptr = joysticks[index] = SDL_JoystickOpen(index); + auto name = SDL_JoystickName(ptr); + auto button_count = SDL_JoystickNumButtons(ptr); + auto axis_count = SDL_JoystickNumAxes(ptr); + auto vendor = SDL_JoystickGetVendor(ptr); + auto product = SDL_JoystickGetProduct(ptr); + auto version = SDL_JoystickGetProductVersion(ptr); + + InputBackend::on_controller_connect(index, name, 0, button_count, axis_count, vendor, product, version); + } + } + else if (event.type == SDL_JOYDEVICEREMOVED) + { + auto index = find_joystick_index(event.jdevice.which); + if (index >= 0) + { + if (SDL_IsGameController(index) == SDL_FALSE) + { + InputBackend::on_controller_disconnect(index); + SDL_JoystickClose(joysticks[index]); + } + } + } + else if (event.type == SDL_JOYBUTTONDOWN) + { + auto index = find_joystick_index(event.jdevice.which); + if (index >= 0) + { + if (SDL_IsGameController(index) == SDL_FALSE) + InputBackend::on_button_down(index, event.jbutton.button); + } + } + else if (event.type == SDL_JOYBUTTONUP) + { + auto index = find_joystick_index(event.jdevice.which); + if (index >= 0) + { + if (SDL_IsGameController(index) == SDL_FALSE) + InputBackend::on_button_up(index, event.jbutton.button); + } + } + else if (event.type == SDL_JOYAXISMOTION) + { + auto index = find_joystick_index(event.jdevice.which); + if (index >= 0) + { + 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; + InputBackend::on_axis_move(index, event.jaxis.axis, value); + } + } + } + // Gamepad Controller + else if (event.type == SDL_CONTROLLERDEVICEADDED) + { + auto index = event.cdevice.which; + if (index >= 0 && index < Input::max_controllers) + { + auto ptr = gamepads[index] = SDL_GameControllerOpen(index); + auto name = SDL_GameControllerName(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); + } + } + else if (event.type == SDL_CONTROLLERDEVICEREMOVED) + { + auto index = find_gamepad_index(event.cdevice.which); + if (index >= 0) { InputBackend::on_controller_disconnect(index); - SDL_JoystickClose(joysticks[index]); + SDL_GameControllerClose(gamepads[index]); } } - } - else if (event.type == SDL_JOYBUTTONDOWN) - { - auto index = find_joystick_index(event.jdevice.which); - if (index >= 0) + else if (event.type == SDL_CONTROLLERBUTTONDOWN) { - if (SDL_IsGameController(index) == SDL_FALSE) - InputBackend::on_button_down(index, event.jbutton.button); - } - } - else if (event.type == SDL_JOYBUTTONUP) - { - auto index = find_joystick_index(event.jdevice.which); - if (index >= 0) - { - if (SDL_IsGameController(index) == SDL_FALSE) - InputBackend::on_button_up(index, event.jbutton.button); - } - } - else if (event.type == SDL_JOYAXISMOTION) - { - auto index = find_joystick_index(event.jdevice.which); - if (index >= 0) - { - if (SDL_IsGameController(index) == SDL_FALSE) + auto index = find_gamepad_index(event.cdevice.which); + if (index >= 0) { + 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! + + InputBackend::on_button_down(index, button); + } + } + else if (event.type == SDL_CONTROLLERBUTTONUP) + { + auto index = find_gamepad_index(event.cdevice.which); + if (index >= 0) + { + 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! + + InputBackend::on_button_up(index, button); + } + } + else if (event.type == SDL_CONTROLLERAXISMOTION) + { + auto index = find_gamepad_index(event.cdevice.which); + if (index >= 0) + { + 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.jaxis.value >= 0) - value = event.jaxis.value / 32767.0f; + if (event.caxis.value >= 0) + value = event.caxis.value / 32767.0f; else - value = event.jaxis.value / 32768.0f; - InputBackend::on_axis_move(index, event.jaxis.axis, value); + value = event.caxis.value / 32768.0f; + + InputBackend::on_axis_move(index, axis, value); } } } - // Gamepad Controller - else if (event.type == SDL_CONTROLLERDEVICEADDED) - { - auto index = event.cdevice.which; - if (index >= 0 && index < Input::max_controllers) - { - auto ptr = gamepads[index] = SDL_GameControllerOpen(index); - auto name = SDL_GameControllerName(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); - } - } - else if (event.type == SDL_CONTROLLERDEVICEREMOVED) - { - auto index = find_gamepad_index(event.cdevice.which); - if (index >= 0) - { - InputBackend::on_controller_disconnect(index); - SDL_GameControllerClose(gamepads[index]); - } - } - else if (event.type == SDL_CONTROLLERBUTTONDOWN) - { - auto index = find_gamepad_index(event.cdevice.which); - if (index >= 0) - { - 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! - - InputBackend::on_button_down(index, button); - } - } - else if (event.type == SDL_CONTROLLERBUTTONUP) - { - auto index = find_gamepad_index(event.cdevice.which); - if (index >= 0) - { - 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! - - InputBackend::on_button_up(index, button); - } - } - else if (event.type == SDL_CONTROLLERAXISMOTION) - { - auto index = find_gamepad_index(event.cdevice.which); - if (index >= 0) - { - 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; - - InputBackend::on_axis_move(index, axis, value); - } - } } -} -void PlatformBackend::sleep(int milliseconds) -{ - if (milliseconds >= 0) - SDL_Delay((u32)milliseconds); -} - -void PlatformBackend::present() -{ - if (App::renderer() == Renderer::OpenGL) + void PlatformBackend::sleep(int milliseconds) { - SDL_GL_SwapWindow(window); + if (milliseconds >= 0) + SDL_Delay((u32)milliseconds); } - // display the window - // this avoids a short black screen on macoS - if (!displayed) + void PlatformBackend::present() { - SDL_ShowWindow(window); - displayed = true; + if (App::renderer() == Renderer::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* PlatformBackend::get_title() -{ - return nullptr; -} - -void PlatformBackend::set_title(const char* title) -{ - SDL_SetWindowTitle(window, title); -} - -void PlatformBackend::get_position(int* x, int* y) -{ - SDL_GetWindowPosition(window, x, y); -} - -void PlatformBackend::set_position(int x, int y) -{ - SDL_SetWindowPosition(window, x, y); -} - -void PlatformBackend::set_fullscreen(bool enabled) -{ - if (enabled) - SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); - else - SDL_SetWindowFullscreen(window, 0); -} - -void PlatformBackend::get_size(int* width, int* height) -{ - SDL_GetWindowSize(window, width, height); -} - -void PlatformBackend::set_size(int width, int height) -{ - SDL_SetWindowSize(window, width, height); -} - -void PlatformBackend::get_draw_size(int* width, int* height) -{ - if (App::renderer() == Renderer::OpenGL) + const char* PlatformBackend::get_title() { - SDL_GL_GetDrawableSize(window, width, height); + return nullptr; } - else + + void PlatformBackend::set_title(const char* title) + { + SDL_SetWindowTitle(window, title); + } + + void PlatformBackend::get_position(int* x, int* y) + { + SDL_GetWindowPosition(window, x, y); + } + + void PlatformBackend::set_position(int x, int y) + { + SDL_SetWindowPosition(window, x, y); + } + + void PlatformBackend::set_fullscreen(bool enabled) + { + if (enabled) + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); + else + SDL_SetWindowFullscreen(window, 0); + } + + void PlatformBackend::get_size(int* width, int* height) { SDL_GetWindowSize(window, width, height); } -} -float PlatformBackend::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* PlatformBackend::app_path() -{ - if (basePath == nullptr) - basePath = SDL_GetBasePath(); - return basePath; -} - -const char* PlatformBackend::user_path() -{ - if (userPath == nullptr) + void PlatformBackend::set_size(int width, int height) { - const Config* config = App::config(); - userPath = SDL_GetPrefPath(nullptr, config->name); + SDL_SetWindowSize(window, width, height); } - return userPath; -} - -// Windows File System methods -#if _WIN32 - -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& list, const char* path, bool recursive) -{ - if (fs::is_directory(path)) + void PlatformBackend::get_draw_size(int* width, int* height) { - if (recursive) + if (App::renderer() == Renderer::OpenGL) { - for (auto& p : fs::recursive_directory_iterator(path)) - list.emplace_back(p.path().string().c_str()); + SDL_GL_GetDrawableSize(window, width, height); } else { - for (auto& p : fs::directory_iterator(path)) - list.emplace_back(p.path().string().c_str()); + SDL_GetWindowSize(window, width, height); } } -} -void PlatformBackend::dir_explore(const char* path) -{ - ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); -} - -// Non-Windows File System Methods -#else - -bool PlatformBackend::file_exists(const char* path) -{ - struct stat buffer; - return (stat(path, &buffer) == 0) && S_ISREG(buffer.st_mode); -} - -bool PlatformBackend::file_delete(const char* path) -{ - BLAH_ERROR("not implemented"); - return false; -} - -bool PlatformBackend::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 PlatformBackend::dir_exists(const char* path) -{ - struct stat buffer; - return (stat(path, &buffer) == 0) && S_ISDIR(buffer.st_mode); -} - -bool PlatformBackend::dir_delete(const char* path) -{ - BLAH_ERROR("not implemented"); - return false; -} - -void PlatformBackend::dir_enumerate(Vector& list, const char* path, bool recursive) -{ - DIR* dirp = opendir(path); - if (dirp != NULL) + float PlatformBackend::get_content_scale() { - struct dirent* dp; - while ((dp = readdir(dirp)) != NULL) + // 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* PlatformBackend::app_path() + { + if (basePath == nullptr) + basePath = SDL_GetBasePath(); + return basePath; + } + + const char* PlatformBackend::user_path() + { + if (userPath == nullptr) { - if (dp->d_name[0] == '.') - continue; - - FilePath subpath = FilePath(path); - if (subpath.end()[-1] != '/') subpath = subpath.append("/"); - subpath = subpath.append(dp->d_name); - list.push_back(subpath); - - if (recursive && dp->d_type == DT_DIR) - dir_enumerate(list, subpath + "/", true); + const Config* config = App::config(); + userPath = SDL_GetPrefPath(nullptr, config->name); } - closedir(dirp); + + return userPath; } -} -void PlatformBackend::dir_explore(const char* path) -{ - BLAH_ERROR("'dir_explore' Not Implemented"); -} + // Windows File System methods + #if _WIN32 -#endif - -bool PlatformBackend::file_open(const char* path, PlatformBackend::FileHandle* handle, FileMode mode) -{ - const char* sdl_mode = ""; - - switch (mode) + bool PlatformBackend::file_exists(const char* path) { - case FileMode::OpenRead: - sdl_mode = "rb"; - break; - case FileMode::Open: - sdl_mode = "r+b"; - break; - case FileMode::CreateWrite: - sdl_mode = "wb"; - break; - case FileMode::Create: - sdl_mode = "w+b"; - break; + return fs::is_regular_file(path); } - auto ptr = SDL_RWFromFile(path, sdl_mode); - *handle = (PlatformBackend::FileHandle)ptr; - return ptr != nullptr; -} + bool PlatformBackend::file_delete(const char* path) + { + return fs::remove(path); + } -i64 PlatformBackend::file_length(PlatformBackend::FileHandle stream) -{ - return SDL_RWsize((SDL_RWops*)stream); -} + bool PlatformBackend::dir_create(const char* path) + { + std::error_code error; + return fs::create_directories(path, error); + } -i64 PlatformBackend::file_position(PlatformBackend::FileHandle stream) -{ - return SDL_RWtell((SDL_RWops*)stream); -} + bool PlatformBackend::dir_exists(const char* path) + { + return fs::is_directory(path); + } -i64 PlatformBackend::file_seek(PlatformBackend::FileHandle stream, i64 seekTo) -{ - return SDL_RWseek((SDL_RWops*)stream, seekTo, RW_SEEK_SET); -} + bool PlatformBackend::dir_delete(const char* path) + { + BLAH_ASSERT(false, "not implemented"); + return false; + } -i64 PlatformBackend::file_read(PlatformBackend::FileHandle stream, void* ptr, i64 length) -{ - return SDL_RWread((SDL_RWops*)stream, ptr, sizeof(char), length); -} + void PlatformBackend::dir_enumerate(Vector& 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()); + } + } + } -i64 PlatformBackend::file_write(PlatformBackend::FileHandle stream, const void* ptr, i64 length) -{ - return SDL_RWwrite((SDL_RWops*)stream, ptr, sizeof(char), length); -} + void PlatformBackend::dir_explore(const char* path) + { + ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); + } -void PlatformBackend::file_close(PlatformBackend::FileHandle stream) -{ - if (stream != nullptr) - SDL_RWclose((SDL_RWops*)stream); -} + // Non-Windows File System Methods + #else -void* PlatformBackend::gl_get_func(const char* name) -{ - return SDL_GL_GetProcAddress(name); -} + bool PlatformBackend::file_exists(const char* path) + { + struct stat buffer; + return (stat(path, &buffer) == 0) && S_ISREG(buffer.st_mode); + } -void* PlatformBackend::gl_context_create() -{ - void* pointer = SDL_GL_CreateContext(window); - if (pointer == nullptr) - Log::error("SDL_GL_CreateContext failed: %s", SDL_GetError()); - return pointer; -} + bool PlatformBackend::file_delete(const char* path) + { + BLAH_ASSERT(false, "not implemented"); + return false; + } -void PlatformBackend::gl_context_make_current(void* context) -{ - SDL_GL_MakeCurrent(window, context); -} + bool PlatformBackend::dir_create(const char* path) + { + char tmp[265]; + char* p = NULL; + size_t len; -void PlatformBackend::gl_context_destroy(void* context) -{ - SDL_GL_DeleteContext(context); -} + 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 PlatformBackend::dir_exists(const char* path) + { + struct stat buffer; + return (stat(path, &buffer) == 0) && S_ISDIR(buffer.st_mode); + } + + bool PlatformBackend::dir_delete(const char* path) + { + BLAH_ASSERT(false, "not implemented"); + return false; + } + + void PlatformBackend::dir_enumerate(Vector& 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); + if (subpath.end()[-1] != '/') subpath = subpath.append("/"); + subpath = subpath.append(dp->d_name); + list.push_back(subpath); + + if (recursive && dp->d_type == DT_DIR) + dir_enumerate(list, subpath + "/", true); + } + closedir(dirp); + } + } + + void PlatformBackend::dir_explore(const char* path) + { + BLAH_ASSERT(false, "'dir_explore' Not Implemented"); + } + + #endif + + FileRef PlatformBackend::file_open(const char* path, FileMode mode) + { + const char* sdl_mode = ""; + + switch (mode) + { + case FileMode::OpenRead: + sdl_mode = "rb"; + break; + case FileMode::Open: + sdl_mode = "r+b"; + break; + case FileMode::CreateWrite: + sdl_mode = "wb"; + break; + case FileMode::Create: + sdl_mode = "w+b"; + break; + } + + auto ptr = SDL_RWFromFile(path, sdl_mode); + if (!ptr) + return FileRef(); + + return FileRef(new Blah_SDL2_File(ptr)); + } + + void* PlatformBackend::gl_get_func(const char* name) + { + return SDL_GL_GetProcAddress(name); + } + + void* PlatformBackend::gl_context_create() + { + void* pointer = SDL_GL_CreateContext(window); + if (pointer == nullptr) + Log::error("SDL_GL_CreateContext failed: %s", SDL_GetError()); + return pointer; + } + + void PlatformBackend::gl_context_make_current(void* context) + { + SDL_GL_MakeCurrent(window, context); + } + + void PlatformBackend::gl_context_destroy(void* context) + { + SDL_GL_DeleteContext(context); + } + + void* PlatformBackend::d3d11_get_hwnd() + { + #if _WIN32 + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + SDL_GetWindowWMInfo(window, &info); + return info.info.win.window; + #else + return nullptr; + #endif + } -void* PlatformBackend::d3d11_get_hwnd() -{ -#if _WIN32 - SDL_SysWMinfo info; - SDL_VERSION(&info.version); - SDL_GetWindowWMInfo(window, &info); - return info.info.win.window; -#else - return nullptr; -#endif } #endif // BLAH_PLATFORM_SDL2 diff --git a/src/internal/platform_backend_win32.cpp b/src/internal/platform_backend_win32.cpp index ab5bb19..cc6d1b2 100644 --- a/src/internal/platform_backend_win32.cpp +++ b/src/internal/platform_backend_win32.cpp @@ -9,12 +9,11 @@ #include "../internal/platform_backend.h" #include "../internal/input_backend.h" #include "../internal/graphics_backend.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #define WIN32_LEAN_AND_MEAN #include @@ -27,7 +26,7 @@ using namespace Blah; namespace fs = std::filesystem; -namespace +namespace Blah { // Primary Window HWND g_hwnd; @@ -50,219 +49,322 @@ namespace // 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); + // Converts Windows scancode to Blah key + Key scancode_to_key(WPARAM wParam, LPARAM lParam); -bool PlatformBackend::init(const Config* config) -{ - // Required to call this for Windows - SetProcessDPIAware(); + // Main Windows Procedure callback + LRESULT CALLBACK window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - // 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 = LoadCursor(NULL, IDC_ARROW); - 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) + // Custom File class + class Blah_Win32_File : public File { - Log::error("Window Creation Failed"); - return false; - } + private: + HANDLE m_handle; - // Create the OpenGL device info - if (App::renderer() == Renderer::OpenGL) - { - // Load the DLL - g_opengl_dll = LoadLibraryA("opengl32.dll"); - if (g_opengl_dll == NULL) + public: + Blah_Win32_File(HANDLE handle) { - Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll"); + m_handle = handle; + } + + ~Blah_Win32_File() + { + CloseHandle(m_handle); + } + + size_t length() override + { + // Todo: cache this value? not sure how performant it is + + LARGE_INTEGER file_size; + if (GetFileSizeEx(m_handle, &file_size)) + return file_size.QuadPart; + + return 0; + } + + size_t position() override + { + LARGE_INTEGER move; + LARGE_INTEGER result; + + move.QuadPart = 0; + result.QuadPart = 0; + + SetFilePointerEx(m_handle, move, &result, FILE_CURRENT); + + return result.QuadPart; + } + + size_t seek(size_t position) override + { + LARGE_INTEGER move; + LARGE_INTEGER result; + + move.QuadPart = position; + result.QuadPart = 0; + + SetFilePointerEx(m_handle, move, &result, FILE_BEGIN); + + return result.QuadPart; + } + + size_t read(unsigned char* buffer, size_t length) override + { + static const DWORD read_step = 65536; + + size_t read = 0; + + while (read < length) + { + DWORD to_read = read_step; + if (to_read > length - read) + to_read = (DWORD)(length - read); + + DWORD moved = 0; + if (ReadFile(m_handle, buffer + read, to_read, &moved, NULL)) + read += moved; + + if (moved < to_read) + break; + } + + return read; + } + + size_t write(const unsigned char* buffer, size_t length) override + { + static const DWORD write_step = 65536; + + size_t written = 0; + + while (written < length) + { + DWORD to_write = write_step; + if (to_write > length - written) + to_write = (DWORD)(length - written); + + DWORD moved = 0; + if (WriteFile(m_handle, buffer + written, to_write, &moved, NULL)) + written += moved; + + if (moved < to_write) + break; + } + + return written; + } + }; + + bool PlatformBackend::init(const Config* config) + { + // Required to call this for Windows + 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 = window_procedure; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + 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; } - // 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 = + // Create the OpenGL device info + if (App::renderer() == Renderer::OpenGL) { - 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, // 32-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, // 24-bit z-buffer - 8, // 8-bit stencil buffer - 0, // no auxiliary buffer - PFD_MAIN_PLANE, // main layer - 0, // reserved - 0, 0, 0 // layer masks ignored - }; + // 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; + } - HDC hdc = GetDC(g_hwnd); + // 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"); - // get the best available match of pixel format for the device context - int pixel_format = ChoosePixelFormat(hdc, &pfd); + // TODO: + // Allow the user to apply (some of) these values before instantiation. + // Also applies to the SDL2 Backend - // make that the pixel format of the device context - SetPixelFormat(hdc, pixel_format, &pfd); - } + 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, // 32-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, // 24-bit z-buffer + 8, // 8-bit stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; - // Reset our game timer - g_start_time = std::chrono::system_clock::now().time_since_epoch(); + HDC hdc = GetDC(g_hwnd); - // 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 the best available match of pixel format for the device context + int pixel_format = ChoosePixelFormat(hdc, &pfd); - // 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) + "/"; + // make that the pixel format of the device context + SetPixelFormat(hdc, pixel_format, &pfd); } - CoTaskMemFree(path); - } - - // Not currently fullscreen - g_fullscreen = false; - // Finished Platform Setup - return true; -} + // Reset our game timer + g_start_time = std::chrono::system_clock::now().time_since_epoch(); -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); -} + // Get Working Directory + { + TCHAR buffer[MAX_PATH]; + GetModuleFileName(NULL, buffer, MAX_PATH); -void PlatformBackend::shutdown() -{ - DestroyWindow(g_hwnd); -} + 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("/"); + } -u64 PlatformBackend::ticks() -{ - // Todo: - // This should account for whatever Time::ticks_per_second is set to + // Get Application User Directory + { + PWSTR path = NULL; + if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, NULL, &path))) + { + auto end = path; + while (*end != 0) end++; - auto now = std::chrono::system_clock::now().time_since_epoch(); - return std::chrono::duration_cast(now - g_start_time).count(); -} + FilePath result; + result.append_utf16((u16*)path, (u16*)end); -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; + g_user_directory = Path::join(Path::normalize(result), config->name) + "/"; + } + CoTaskMemFree(path); + } + + // Not currently fullscreen + g_fullscreen = false; + + // Finished Platform Setup + return true; } - case WM_DESTROY: - PostQuitMessage(0); - return 0; + 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); + } - // Mouse Input - case WM_LBUTTONDOWN: - InputBackend::on_mouse_down(MouseButton::Left); - return 0; + // Display the game window + ShowWindow(g_hwnd, SW_SHOW); + } - case WM_LBUTTONUP: - InputBackend::on_mouse_up(MouseButton::Left); - return 0; + void PlatformBackend::shutdown() + { + DestroyWindow(g_hwnd); + } - case WM_RBUTTONDOWN: - InputBackend::on_mouse_down(MouseButton::Right); - return 0; + u64 PlatformBackend::ticks() + { + // Todo: + // This should account for whatever Time::ticks_per_second is set to - case WM_RBUTTONUP: - InputBackend::on_mouse_up(MouseButton::Right); - return 0; + auto now = std::chrono::system_clock::now().time_since_epoch(); + return std::chrono::duration_cast(now - g_start_time).count(); + } - case WM_MBUTTONDOWN: - InputBackend::on_mouse_down(MouseButton::Middle); - return 0; + LRESULT CALLBACK 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_MBUTTONUP: - InputBackend::on_mouse_up(MouseButton::Middle); - return 0; + case WM_DESTROY: + PostQuitMessage(0); + return 0; - case WM_MOUSEMOVE: - InputBackend::on_mouse_move((float)((u16)lParam), (float)(lParam >> 16)); - return 0; + // Mouse Input + case WM_LBUTTONDOWN: + InputBackend::on_mouse_down(MouseButton::Left); + return 0; - case WM_MOUSEWHEEL: - InputBackend::on_mouse_wheel(Point(0, GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA)); - return 0; + case WM_LBUTTONUP: + InputBackend::on_mouse_up(MouseButton::Left); + return 0; - // Text Input - case WM_UNICHAR: - if (wParam == UNICODE_NOCHAR) - return 1; - case WM_CHAR: + 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((float)((u16)lParam), (float)(lParam >> 16)); + return 0; + + case WM_MOUSEWHEEL: + InputBackend::on_mouse_wheel(Point(0, GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA)); + return 0; + + // Text Input + case WM_UNICHAR: + if (wParam == UNICODE_NOCHAR) + return 1; + case WM_CHAR: { String result; result.append((u32)wParam); @@ -271,569 +373,481 @@ LRESULT CALLBACK blah_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARA return 0; } - // Keyboard Input - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - { - auto is_repeat = ((lParam & (1 << 30)) >> 30) == 1; - if (!is_repeat) + // Keyboard Input + case WM_KEYDOWN: + case WM_SYSKEYDOWN: { - auto key = blah_scancode_to_key(wParam, lParam); - if (key != Key::Unknown) - InputBackend::on_key_down(key); + auto is_repeat = ((lParam & (1 << 30)) >> 30) == 1; + if (!is_repeat) + { + auto key = scancode_to_key(wParam, lParam); + if (key != Key::Unknown) + InputBackend::on_key_down(key); + } + return 0; } - 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); -} - -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& list, const char* path, bool recursive) -{ - if (fs::is_directory(path)) - { - if (recursive) + case WM_KEYUP: + case WM_SYSKEYUP: { - for (auto& p : fs::recursive_directory_iterator(path)) - list.emplace_back(p.path().string().c_str()); + auto key = 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 { - for (auto& p : fs::directory_iterator(path)) - list.emplace_back(p.path().string().c_str()); + 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::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; - - switch (mode) + void PlatformBackend::get_size(int* width, int* height) { - case FileMode::OpenRead: - access = GENERIC_READ; - creation = OPEN_EXISTING; - break; - case FileMode::Open: - access = GENERIC_READ | GENERIC_WRITE; - creation = OPEN_EXISTING; - break; - case FileMode::CreateWrite: - access = GENERIC_WRITE; - creation = CREATE_ALWAYS; - break; - case FileMode::Create: - access = GENERIC_READ | GENERIC_WRITE; - creation = CREATE_ALWAYS; - break; + RECT rect; + if (GetClientRect(g_hwnd, &rect)) + { + *width = rect.right - rect.left; + *height = rect.bottom - rect.top; + } } - auto result = CreateFile(path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + void PlatformBackend::set_size(int width, int height) + { + RECT client_rect; + RECT border_rect; - if (result == INVALID_HANDLE_VALUE) + 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); + } + + 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_ASSERT(false, "not implemented"); 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) -{ - LARGE_INTEGER move; - LARGE_INTEGER result; - - move.QuadPart = 0; - result.QuadPart = 0; - - SetFilePointerEx(handle, move, &result, FILE_CURRENT); - - return result.QuadPart; -} - -i64 PlatformBackend::file_seek(PlatformBackend::FileHandle handle, i64 seek_to) -{ - LARGE_INTEGER move; - LARGE_INTEGER result; - - move.QuadPart = seek_to; - result.QuadPart = 0; - - SetFilePointerEx(handle, move, &result, FILE_BEGIN); - - return result.QuadPart; -} - -i64 PlatformBackend::file_read(PlatformBackend::FileHandle handle, void* ptr, i64 length) -{ - static const DWORD read_step = 65536; - - i64 read = 0; - - while (read < length) + void PlatformBackend::dir_enumerate(Vector& list, const char* path, bool recursive) { - DWORD to_read = read_step; - if (to_read > length - read) - to_read = (DWORD)(length - read); + 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()); + } + } + } - DWORD moved = 0; - if (ReadFile(handle, (unsigned char*)ptr + read, to_read, &moved, NULL)) - read += moved; + void PlatformBackend::dir_explore(const char* path) + { + ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT); + } - if (moved < to_read) + FileRef PlatformBackend::file_open(const char* path, FileMode mode) + { + int access = 0; + int creation = 0; + + switch (mode) + { + case FileMode::OpenRead: + access = GENERIC_READ; + creation = OPEN_EXISTING; break; - } - - return read; -} - -i64 PlatformBackend::file_write(PlatformBackend::FileHandle handle, const void* ptr, i64 length) -{ - static const DWORD write_step = 65536; - - i64 written = 0; - - while (written < length) - { - DWORD to_write = write_step; - if (to_write > length - written) - to_write = (DWORD)(length - written); - - DWORD moved = 0; - if (WriteFile(handle, (unsigned char*)ptr + written, to_write, &moved, NULL)) - written += moved; - - if (moved < to_write) + case FileMode::Open: + access = GENERIC_READ | GENERIC_WRITE; + creation = OPEN_EXISTING; break; + case FileMode::CreateWrite: + access = GENERIC_WRITE; + creation = CREATE_ALWAYS; + break; + case FileMode::Create: + access = GENERIC_READ | GENERIC_WRITE; + creation = CREATE_ALWAYS; + break; + } + + auto result = CreateFile(path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + + if (result == INVALID_HANDLE_VALUE) + return FileRef(); + + return FileRef(new Blah_Win32_File(result)); } - return written; -} - -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)) + void* PlatformBackend::gl_get_func(const char* name) { - p = (void*)GetProcAddress(g_opengl_dll, 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; } - 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) + void* PlatformBackend::gl_context_create() { 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_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; + return g_wglCreateContext(hdc); } - if (key == Key::Unknown) + void PlatformBackend::gl_context_make_current(void* context) { - int scancode = (lParam >> 16) & 0xFF; - - switch (scancode) + if (context != nullptr) { - 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; + HDC hdc = GetDC(g_hwnd); + g_wglMakeCurrent(hdc, (HGLRC)context); } + else + g_wglMakeCurrent(NULL, NULL); } - return key; + void PlatformBackend::gl_context_destroy(void* context) + { + g_wglDeleteContext((HGLRC)context); + } + + void* PlatformBackend::d3d11_get_hwnd() + { + return g_hwnd; + } + + Key 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_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 diff --git a/src/math/stopwatch.cpp b/src/math/stopwatch.cpp deleted file mode 100644 index 33191cb..0000000 --- a/src/math/stopwatch.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -using namespace std::chrono; -using namespace Blah; - -Stopwatch::Stopwatch() -{ - reset(); -} - -void Stopwatch::reset() -{ - start_time = std::chrono::duration_cast(system_clock::now().time_since_epoch()).count(); -} - -u64 Stopwatch::milliseconds() -{ - return microseconds() / 1000; -} - -u64 Stopwatch::microseconds() -{ - return std::chrono::duration_cast(system_clock::now().time_since_epoch()).count() - start_time; -} diff --git a/src/math/calc.cpp b/src/numerics/calc.cpp similarity index 98% rename from src/math/calc.cpp rename to src/numerics/calc.cpp index 0fc2655..9e3dac2 100644 --- a/src/math/calc.cpp +++ b/src/numerics/calc.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/src/math/circle.cpp b/src/numerics/circle.cpp similarity index 74% rename from src/math/circle.cpp rename to src/numerics/circle.cpp index e4bda69..59b88f2 100644 --- a/src/math/circle.cpp +++ b/src/numerics/circle.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include using namespace Blah; diff --git a/src/math/color.cpp b/src/numerics/color.cpp similarity index 97% rename from src/math/color.cpp rename to src/numerics/color.cpp index fbff76c..f3274f1 100644 --- a/src/math/color.cpp +++ b/src/numerics/color.cpp @@ -1,6 +1,6 @@ -#include -#include -#include +#include +#include +#include using namespace Blah; diff --git a/src/math/line.cpp b/src/numerics/line.cpp similarity index 96% rename from src/math/line.cpp rename to src/numerics/line.cpp index 647adf3..7851ed6 100644 --- a/src/math/line.cpp +++ b/src/numerics/line.cpp @@ -1,7 +1,7 @@ -#include -#include -#include -#include +#include +#include +#include +#include using namespace Blah; diff --git a/src/math/mat3x2.cpp b/src/numerics/mat3x2.cpp similarity index 95% rename from src/math/mat3x2.cpp rename to src/numerics/mat3x2.cpp index d2fd277..bf31642 100644 --- a/src/math/mat3x2.cpp +++ b/src/numerics/mat3x2.cpp @@ -1,7 +1,7 @@ -#include -#include +#include +#include +#include #include -#include using namespace Blah; @@ -132,8 +132,8 @@ Mat3x2 Mat3x2::create_scale(float scaleX, float scaleY, Vec2 centerPoint) Mat3x2 Mat3x2::create_rotation(float radians) { - float c = cosf(radians); - float s = sinf(radians); + float c = Calc::cos(radians); + float s = Calc::sin(radians); return Mat3x2(c, s, -s, c, 0, 0); } diff --git a/src/math/mat4x4.cpp b/src/numerics/mat4x4.cpp similarity index 98% rename from src/math/mat4x4.cpp rename to src/numerics/mat4x4.cpp index 7c2b063..d4b4fbb 100644 --- a/src/math/mat4x4.cpp +++ b/src/numerics/mat4x4.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include using namespace Blah; diff --git a/src/math/point.cpp b/src/numerics/point.cpp similarity index 91% rename from src/math/point.cpp rename to src/numerics/point.cpp index a2484e1..ba2b031 100644 --- a/src/math/point.cpp +++ b/src/numerics/point.cpp @@ -1,6 +1,6 @@ -#include -#include -#include +#include +#include +#include using namespace Blah; @@ -33,7 +33,7 @@ bool Point::operator !=(const Point& rhs) { return x != rhs.x || y != rhs.y; } float Point::length() const { - return sqrtf((float)(x * x + y * y)); + return Calc::sqrt((float)(x * x + y * y)); } int Point::length_squared() const diff --git a/src/math/quad.cpp b/src/numerics/quad.cpp similarity index 86% rename from src/math/quad.cpp rename to src/numerics/quad.cpp index 9330719..481d09a 100644 --- a/src/math/quad.cpp +++ b/src/numerics/quad.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include namespace Blah { diff --git a/src/math/rect.cpp b/src/numerics/rect.cpp similarity index 96% rename from src/math/rect.cpp rename to src/numerics/rect.cpp index 8f7dbc1..67e687e 100644 --- a/src/math/rect.cpp +++ b/src/numerics/rect.cpp @@ -1,9 +1,9 @@ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include using namespace Blah; diff --git a/src/math/rectI.cpp b/src/numerics/rectI.cpp similarity index 94% rename from src/math/rectI.cpp rename to src/numerics/rectI.cpp index c8d3a5e..9464c9d 100644 --- a/src/math/rectI.cpp +++ b/src/numerics/rectI.cpp @@ -1,8 +1,8 @@ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include using namespace Blah; diff --git a/src/math/vec2.cpp b/src/numerics/vec2.cpp similarity index 97% rename from src/math/vec2.cpp rename to src/numerics/vec2.cpp index dff0d29..613c9a3 100644 --- a/src/math/vec2.cpp +++ b/src/numerics/vec2.cpp @@ -1,7 +1,6 @@ -#include -#include -#include -#include +#include +#include +#include using namespace Blah; diff --git a/src/streams/bufferstream.cpp b/src/streams/bufferstream.cpp index 284f806..722600c 100644 --- a/src/streams/bufferstream.cpp +++ b/src/streams/bufferstream.cpp @@ -42,7 +42,7 @@ BufferStream::~BufferStream() delete[] m_buffer; } -i64 BufferStream::read_into(void* ptr, i64 len) +size_t BufferStream::read_into(void* ptr, size_t len) { if (m_buffer == nullptr || ptr == nullptr) return 0; @@ -58,7 +58,7 @@ i64 BufferStream::read_into(void* ptr, i64 len) return len; } -i64 BufferStream::write_from(const void* ptr, i64 len) +size_t BufferStream::write_from(const void* ptr, size_t len) { if (len < 0) return 0; @@ -78,7 +78,7 @@ i64 BufferStream::write_from(const void* ptr, i64 len) return len; } -void BufferStream::resize(i64 length) +void BufferStream::resize(size_t length) { if (m_capacity > length) { diff --git a/src/streams/filestream.cpp b/src/streams/filestream.cpp index 789f5b8..84396bf 100644 --- a/src/streams/filestream.cpp +++ b/src/streams/filestream.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "../internal/platform_backend.h" #include @@ -7,105 +7,90 @@ using namespace Blah; FileStream::FileStream() { - m_handle = nullptr; m_mode = FileMode::OpenRead; } FileStream::FileStream(const FilePath& path, FileMode mode) : m_mode(mode) + , m_file(File::open(path, mode)) { - if (!PlatformBackend::file_open(path, &m_handle, mode)) - m_handle = nullptr; + } FileStream::FileStream(FileStream&& src) noexcept { - m_handle = src.m_handle; + m_file = src.m_file; m_mode = src.m_mode; - src.m_handle = nullptr; } FileStream& FileStream::operator=(FileStream&& src) noexcept { - m_handle = src.m_handle; + m_file = src.m_file; m_mode = src.m_mode; - src.m_handle = nullptr; return *this; } -FileStream::~FileStream() +size_t FileStream::length() const { - if (m_handle != nullptr) - PlatformBackend::file_close(m_handle); + if (m_file) + return m_file->length(); + return 0; } -i64 FileStream::length() const +size_t FileStream::position() const { - if (m_handle == nullptr) - return 0; - - return PlatformBackend::file_length(m_handle); + if (m_file) + return m_file->position(); + return 0; } -i64 FileStream::position() const +size_t FileStream::seek(size_t seek_to) { - if (m_handle == nullptr) - return 0; - - return PlatformBackend::file_position(m_handle); -} - -i64 FileStream::seek(i64 seek_to) -{ - if (m_handle == nullptr) - return 0; - - return PlatformBackend::file_seek(m_handle, seek_to); + if (m_file) + return m_file->seek(seek_to); + return 0; } bool FileStream::is_open() const { - return m_handle != nullptr; + return m_file.get(); } bool FileStream::is_readable() const { - return m_handle != nullptr && (m_mode != FileMode::CreateWrite); + return m_file.get() && (m_mode != FileMode::CreateWrite); } bool FileStream::is_writable() const { - return m_handle != nullptr && (m_mode != FileMode::OpenRead); + return m_file.get() && (m_mode != FileMode::OpenRead); } -i64 FileStream::read_into(void* ptr, i64 length) -{ - if (m_handle == nullptr) - { - BLAH_ERROR("Unable to read from Stream"); - return 0; - } - - return PlatformBackend::file_read(m_handle, ptr, length); -} - -i64 FileStream::write_from(const void* ptr, i64 length) +size_t FileStream::read_into(void* ptr, size_t length) { if (length <= 0) return 0; - if (m_handle == nullptr) - { - BLAH_ERROR("Unable to write to Stream"); - return 0; - } + if (m_file) + return m_file->read((unsigned char*)ptr, length); - return PlatformBackend::file_write(m_handle, ptr, length); + BLAH_ASSERT(false, "Unable to read from Stream"); + return 0; +} + +size_t FileStream::write_from(const void* ptr, size_t length) +{ + if (length <= 0) + return 0; + + if (m_file) + return m_file->write((const unsigned char*)ptr, length); + + BLAH_ASSERT(false, "Unable to write to Stream"); + return 0; } void FileStream::close() { - if (m_handle != nullptr) - PlatformBackend::file_close(m_handle); - m_handle = nullptr; + m_file.reset(); } \ No newline at end of file diff --git a/src/streams/memorystream.cpp b/src/streams/memorystream.cpp index 2fc568b..faeac03 100644 --- a/src/streams/memorystream.cpp +++ b/src/streams/memorystream.cpp @@ -6,7 +6,7 @@ using namespace Blah; MemoryStream::MemoryStream() : m_data(nullptr), m_length(0), m_position(0) {} -MemoryStream::MemoryStream(char* data, i64 length) +MemoryStream::MemoryStream(char* data, size_t length) : m_data(data), m_length(length), m_position(0) {} MemoryStream::MemoryStream(MemoryStream&& src) noexcept @@ -28,7 +28,7 @@ MemoryStream& MemoryStream::operator=(MemoryStream&& src) noexcept return *this; } -i64 MemoryStream::read_into(void* ptr, i64 len) +size_t MemoryStream::read_into(void* ptr, size_t len) { if (len < 0 || ptr == nullptr) return 0; @@ -41,7 +41,7 @@ i64 MemoryStream::read_into(void* ptr, i64 len) return len; } -i64 MemoryStream::write_from(const void* ptr, i64 len) +size_t MemoryStream::write_from(const void* ptr, size_t len) { if (len < 0 || ptr == nullptr) return 0; diff --git a/src/streams/stream.cpp b/src/streams/stream.cpp index 5001ca1..0130609 100644 --- a/src/streams/stream.cpp +++ b/src/streams/stream.cpp @@ -4,10 +4,10 @@ using namespace Blah; -i64 Stream::pipe(Stream& stream, i64 length) +size_t Stream::pipe(Stream& stream, size_t length) { const int BUFFER_LENGTH = 4096; - i64 result = 0; + size_t result = 0; char buffer[BUFFER_LENGTH]; while (length > 0) @@ -59,12 +59,12 @@ String Stream::read_line() return result; } -i64 Stream::write(const void* buffer, i64 length) +size_t Stream::write(const void* buffer, size_t length) { return write_from(buffer, length); } -i64 Stream::write(const String& string) +size_t Stream::write(const String& string) { return write_from(string.begin(), string.length()); } diff --git a/src/core/time.cpp b/src/time.cpp similarity index 70% rename from src/core/time.cpp rename to src/time.cpp index 2a61d8d..50671a9 100644 --- a/src/core/time.cpp +++ b/src/time.cpp @@ -1,14 +1,8 @@ -#include +#include +#include using namespace Blah; - -namespace -{ - double modf(double x, double m) - { - return x - (int)(x / m) * m; - } -} +using namespace std::chrono; u64 Time::ticks = 0; u64 Time::previous_ticks = 0; @@ -48,6 +42,11 @@ bool Time::on_time(double time, double timestamp) bool Time::between_interval(double time, float interval, float offset) { + static const auto modf = [](double x, double m) + { + return x - (int)(x / m) * m; + }; + return modf(time - offset, ((double)interval) * 2) >= interval; } @@ -55,3 +54,23 @@ bool Time::between_interval(float interval, float offset) { return between_interval(Time::seconds, interval, offset); } + +Stopwatch::Stopwatch() +{ + reset(); +} + +void Stopwatch::reset() +{ + start_time = std::chrono::duration_cast(system_clock::now().time_since_epoch()).count(); +} + +u64 Stopwatch::milliseconds() +{ + return microseconds() / 1000; +} + +u64 Stopwatch::microseconds() +{ + return std::chrono::duration_cast(system_clock::now().time_since_epoch()).count() - start_time; +}