#pragma once #include #include #include #include #include #include namespace Blah { class Stream; class Shader; using ShaderRef = Ref; class Texture; using TextureRef = Ref; class Target; using TargetRef = Ref; class Mesh; using MeshRef = Ref; class Material; using MaterialRef = Ref; // Type of Renderer the Application is using enum class RendererType { None = -1, OpenGL, D3D11, }; // Renderer Information struct RendererInfo { // The type of Renderer being used RendererType type = RendererType::None; // Whether Mesh Instancing is available bool instancing = false; // Whether the Texture origin is the bottom left. // This is true for OpenGL. bool origin_bottom_left = false; // Maximum Texture Size available int max_texture_size = 0; }; // Depth comparison function to use during a draw call enum class Compare { None, Always, Never, Less, Equal, LessOrEqual, Greater, NotEqual, GreaterOrEqual }; // Cull mode during a draw call enum class Cull { // No Culling enabled None = 0, // Cull front faces Front = 1, // Cull back faces Back = 2, }; enum class BlendOp { Add, Subtract, ReverseSubtract, Min, Max }; enum class BlendFactor { Zero, One, SrcColor, OneMinusSrcColor, DstColor, OneMinusDstColor, SrcAlpha, OneMinusSrcAlpha, DstAlpha, OneMinusDstAlpha, ConstantColor, OneMinusConstantColor, ConstantAlpha, OneMinusConstantAlpha, SrcAlphaSaturate, Src1Color, OneMinusSrc1Color, Src1Alpha, OneMinusSrc1Alpha }; enum class BlendMask { None = 0, Red = 1, Green = 2, Blue = 4, Alpha = 8, RGB = Red | Green | Blue, RGBA = Red | Green | Blue | Alpha, }; // BlendMode using for rendering struct BlendMode { // Normal is Premultipled Alpha // TODO: potentially rename normal to match that it is for Premultiplied Alpha? static const BlendMode Normal; static const BlendMode NonPremultiplied; static const BlendMode Subtract; static const BlendMode Additive; BlendOp color_op; BlendFactor color_src; BlendFactor color_dst; BlendOp alpha_op; BlendFactor alpha_src; BlendFactor alpha_dst; BlendMask mask; u32 rgba; BlendMode() = default; BlendMode(BlendOp op, BlendFactor src, BlendFactor dst) : color_op(op), color_src(src), color_dst(dst), alpha_op(op), alpha_src(src), alpha_dst(dst), mask(BlendMask::RGBA), rgba(0xffffffff) {} BlendMode( BlendOp color_op, BlendFactor color_src, BlendFactor color_dst, BlendOp alpha_op, BlendFactor alpha_src, BlendFactor alpha_dst, BlendMask blend_mask, u32 blend_rgba) : color_op(color_op), color_src(color_src), color_dst(color_dst), alpha_op(alpha_op), alpha_src(alpha_src), alpha_dst(alpha_dst), mask(blend_mask), rgba(blend_rgba) {} constexpr bool operator==(const BlendMode& rhs) const { return color_op == rhs.color_op && color_src == rhs.color_src && color_dst == rhs.color_dst && alpha_op == rhs.alpha_op && alpha_src == rhs.alpha_src && alpha_dst == rhs.alpha_dst && mask == rhs.mask && rgba == rhs.rgba; } constexpr bool operator!=(const BlendMode& rhs) const { return !(*this == rhs); } }; // Texture filter enum class TextureFilter { None, // Will fallback to whatever default the driver sets Linear, // Linear interpolation Nearest // Nearest Neighbour interpolation }; // Texture Wrap Mode enum class TextureWrap { None, // Will fallback to whatever default the driver sets Clamp, // Clamps the texture to the edges Repeat // Repeats the texture }; // Texture Sampler State, applied during rendering struct TextureSampler { // Filter Mode TextureFilter filter; // Wrap X Mode TextureWrap wrap_x; // Wrap Y Mode TextureWrap wrap_y; TextureSampler() : filter(TextureFilter::Linear), wrap_x(TextureWrap::Repeat), wrap_y(TextureWrap::Repeat) {} TextureSampler(TextureFilter filter) : filter(filter), wrap_x(TextureWrap::Repeat), wrap_y(TextureWrap::Repeat) {} TextureSampler(TextureFilter filter, TextureWrap wrap_x, TextureWrap wrap_y) : filter(filter), wrap_x(wrap_x), wrap_y(wrap_y) {} bool operator==(const TextureSampler& rhs) const { return filter == rhs.filter && wrap_x == rhs.wrap_x && wrap_y == rhs.wrap_y; } bool operator!=(const TextureSampler& rhs) const { return !(*this == rhs); } }; enum class TextureFormat { None, // Invalid Format R, // Single 8-bit channel RG, // 2 8-bit channels RGBA, // 4 8-bit channels DepthStencil, // Depth 24, Stencil 8 Count // Total Formats }; enum class ClearMask { None = 0, Color = 1, Depth = 2, Stencil = 4, All = (int)Color | (int)Depth | (int)Stencil }; // Supported Uniform Types enum class UniformType { None, Float, Float2, Float3, Float4, Mat3x2, Mat4x4, Texture2D, Sampler2D }; // Supported Shader Types enum class ShaderType { None = 0, Vertex = 1 << 0, Fragment = 1 << 1 }; // Uniform Info, provided by the Shader struct UniformInfo { // Name of the Uniform String name; // The Value type of the Uniform UniformType type; // The Shader type the Uniform is a part of ShaderType shader; // Texture / Sampler register index, which shaders can manually assign int register_index = 0; // Some rendering APIs have uniform buffers. The `buffer_index` // specifies which buffer the uniform belongs to int buffer_index = 0; // Array length of the Uniform (ex. a vec2[4] would be 4) int array_length = 0; }; // Supported Vertex value types enum class VertexType { None, Float, Float2, Float3, Float4, Byte4, UByte4, Short2, UShort2, Short4, UShort4 }; // Vertex Attribute information struct VertexAttribute { // Location / Attribute Index int index = 0; // Vertex Type VertexType type = VertexType::None; // Whether the Vertex should be normalized (doesn't apply to Floats) bool normalized = false; }; // Vertex Format information. // Holds a list of attributes and total stride per-vertex. struct VertexFormat { // List of Attributes StackVector attributes; // Total size in bytes of each Vertex element int stride = 0; VertexFormat() = default; VertexFormat(const StackVector& attributes, int stride = 0); }; // Supported Vertex Index formats enum class IndexFormat { // Indices are 16-bit unsigned integers UInt16, // Indices are 32-bit unsigned integers UInt32 }; // Data to be passed to the shader to construct it struct ShaderData { struct HLSL_Attribute { // Semantic Name const char* semantic_name = nullptr; // (optional) Semantic Index int semantic_index = 0; }; // Vertex Shader Program data String vertex; // Fragment Shader Program data String fragment; // HLSL Attributes - required for D3D11 StackVector hlsl_attributes; }; // A shader used during Rendering class Shader { protected: Shader() = default; public: // Copy / Moves not allowed Shader(const Shader&) = delete; Shader(Shader&&) = delete; Shader& operator=(const Shader&) = delete; Shader& operator=(Shader&&) = delete; // Default Destructor virtual ~Shader() = default; // Creates a Shader with the given Shader Data. // If the Shader creation fails, it will return an invalid ShaderRef. static ShaderRef create(const ShaderData& data); // Gets a list of Shader Uniforms from Shader virtual Vector& uniforms() = 0; // Gets a list of Shader Uniforms from Shader virtual const Vector& uniforms() const = 0; }; // A 2D Texture held by the GPU to be used during rendering class Texture { protected: Texture() = default; public: // Copy / Moves not allowed Texture(const Texture&) = delete; Texture(Texture&&) = delete; Texture& operator=(const Texture&) = delete; Texture& operator=(Texture&&) = delete; // Default Destructor virtual ~Texture() = default; // Creates a new Texture. // If the Texture creation fails, it will return an invalid TextureRef. static TextureRef create(const Image& image); // Creates a new Texture. // If image data is provided, it should be the full size of the texture. // If the Texture creation fails, it will return an invalid TextureRef. static TextureRef create(int width, int height, TextureFormat format, unsigned char* data = nullptr); // Creates a new Texture from a Stream. // If the Texture creation fails, it will return an invalid TextureRef. static TextureRef create(Stream& stream); // Creates a new Texture from a File. // If the Texture creation fails, it will return an invalid TextureRef. static TextureRef create(const FilePath& file); // gets the width of the texture virtual int width() const = 0; // gets the height of the texture virtual int height() const = 0; // Gets the format of the Texture virtual TextureFormat format() const = 0; // Sets the data of the Texture. // Note that the data should be the same format and size as the Texture. There is no row padding. virtual void set_data(const u8* data) = 0; // Sets the data of the Texture to the provided Color buffer. // If the Texture Format is not RGBA, this won't do anything. void set_data(const Color* data); // Gets the data of the Texture. // Note that the data will be written to in the same format as the Texture, // and you should allocate enough space for the full texture. There is no row padding. virtual void get_data(u8* data) = 0; // Gets the data of the Texture. // If the Texture Format is not RGBA, this won't do anything. void get_data(Color* data); // Returns true if the Texture is part of a FrameBuffer virtual bool is_framebuffer() const = 0; }; // Up to 4 color textures + 1 depth/stencil using Attachments = StackVector; using AttachmentFormats = StackVector; // 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; }; // A Mesh is a set of Indices and Vertices which are used for drawing class Mesh { protected: Mesh() = default; public: // Copy / Moves not allowed Mesh(const Mesh&) = delete; Mesh(Mesh&&) = delete; Mesh& operator=(const Mesh&) = delete; Mesh& operator=(Mesh&&) = delete; // Default Destructor virtual ~Mesh() = default; // Creates a new Mesh. // If the Mesh creation fails, it will return an invalid Mesh. static MeshRef create(); // Uploads the given index buffer to the Mesh virtual void index_data(IndexFormat format, const void* indices, i64 count) = 0; // Uploads the given vertex buffer to the Mesh virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) = 0; // Uploads the given instance buffer to the Mesh virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) = 0; // Gets the index count of the Mesh virtual i64 index_count() const = 0; // Gets the vertex count of the Mesh virtual i64 vertex_count() const = 0; // Gets the instance count of the Mesh virtual i64 instance_count() const = 0; }; // Materials hold values that can be assigned to a shader during rendering class Material final { private: Material(const ShaderRef& shader); public: // Copy / Moves not allowed Material(const Material&) = delete; Material(Material&&) = delete; Material& operator=(const Material&) = delete; Material& operator=(Material&&) = delete; // Default destructor ~Material() = default; // Creates a new Material from the given Shader. // If the Shader is invalid, it will return an invalid MaterialRef. static MaterialRef create(const ShaderRef& shader); // Clones the material and returns a new one MaterialRef clone() const; // Returns the Shader assigned to the Material. ShaderRef shader() const; // Sets the texture void set_texture(const char* name, const TextureRef& texture, int array_index = 0); // Sets the texture void set_texture(int register_index, const TextureRef& texture); // Gets the texture, or an empty reference if invalid TextureRef get_texture(const char* name, int array_index = 0) const; // Gets the texture, or an empty reference if invalid TextureRef get_texture(int register_index) const; // Sets the sampler void set_sampler(const char* name, const TextureSampler& sampler, int array_index = 0); // Sets the sampler void set_sampler(int register_index, const TextureSampler& sampler); // Gets the sampler TextureSampler get_sampler(const char* name, int array_index = 0) const; // Gets the sampler TextureSampler get_sampler(int register_index) const; // Sets the value. `length` is the total number of floats to set // For example if the uniform is a float2[4], a total of 8 float values // can be set. void set_value(const char* name, const float* value, i64 length); // Shorthands to more easily assign uniform values void set_value(const char* name, float value); void set_value(const char* name, const Vec2f& value); void set_value(const char* name, const Vec3f& value); void set_value(const char* name, const Vec4f& value); void set_value(const char* name, const Mat3x2f& value); void set_value(const char* name, const Mat4x4f& value); void set_value(const char* name, const Vector& value); void set_value(const char* name, const Vector& value); void set_value(const char* name, const Vector& value); void set_value(const char* name, const Vector& value); void set_value(const char* name, const Vector& value); void set_value(const char* name, const Vector& value); // Gets a pointer to the values of the given Uniform, or nullptr if it doesn't exist. const float* get_value(const char* name, i64* length = nullptr) const; // Checks if the shader attached to the material has a uniform value with the given name bool has_value(const char* name) const; // Returns the internal Texture buffer const Vector& textures() const; // Returns the internal Sampler buffer const Vector& samplers() const; // Returns the interal float buffer of all the values const float* data() const; private: ShaderRef m_shader; Vector m_textures; Vector m_samplers; Vector m_data; }; // A single draw call struct DrawCall { // Framebuffer to draw to TargetRef target; // Mesh to draw with MeshRef mesh; // Material to draw with MaterialRef material; // Whether the DrawCall should use a specific viewport bool has_viewport; // Whether the DrawCall should use a scissor rectangle bool has_scissor; // The viewport (only used if hasViewport is true) Rectf viewport; // The scissor rectangle (only used if hasScissor is true) Rectf scissor; // First index in the Mesh to draw from i64 index_start; // Total amount of indices to draw from the Mesh i64 index_count; // Total amount of instances to draw from the Mesh i64 instance_count; // Depth Compare Function Compare depth; // Cull Mode Cull cull; // Blend Mode BlendMode blend; // Initializes a default DrawCall DrawCall(); // Performs the render void perform(); }; }