::Create()
method.You can find more information in comments below.
class IRenderer : public virtual IReleasable
{
public:
using Bindings = std::vector<std::pair<int, AnsiString>> // Describes binding points for shader programs
using Shaders = std::vector<std::pair<ShaderType, AnsiString>> // Describes shaders for shader program
enum class InitializationStatus
{
Initialized, // Initialized successfully
NotInitialized, // Initialization was not performed yet
DeviceContext, // Unable to get platform device context
LoadNativeGraphics, // Unable to load native graphics
InitNativeGraphics, // Unable to initialize native graphics
CreateNativeGraphics, // Unable to create native graphics class instance
Version, // Native graphics version is too low
Feature, // Native graphics has no required feature
FindPixelFormat, // Unable to find suitable pixel format
SetPixelFormat, // Unable to set pixel format
FindConfig, // Unable to find configuration for pixel format
CreateContext, // Unable to create native graphics context
ActivateContext, // Unable to activate native graphics context
CreateSurface, // Unable to create window surface
DefaultVertexArray, // Unable to create default vertex array
DefaultTextures, // Unable to create default textures
DefaultProgram, // Unable to create default shader program
DefaultFramebuffer, // Unable to create default framebuffer
DefaultTextureFramebuffer, // Unable to create default texture framebuffer
Window // Specified window is not supported
};
protected:
InitializationStatus m_eInitializationStatus;
Version2 m_sMinVersion;
Version2 m_sMaxVersion;
Version2 m_sVersion;
RendererCapabilities m_sCapabilities;
IWindow *m_pWindow;
INativeGraphics *m_pNativeGraphics;
IProgram *m_pActiveProgram;
IFramebuffer *m_pActiveFramebuffer;
IVertexArray *m_pActiveVertexArray;
IBaseTexture *m_pActiveTarget;
ITexture *m_pActiveMask;
const nitisa::Block *m_pActiveBlock;
const Bitmask *m_pActiveBitmask;
const CubicBezierSplineLimits *m_pActiveSplineLimits;
const Mat4f *m_pActivePrimitiveMatrix;
Rect m_sViewport;
RendererTechnology m_eTechnology;
protected:
// Helper getters
virtual IReleasableListener *getProgramReleaseListener() = 0;
virtual IReleasableListener *getFramebufferReleaseListener() = 0;
virtual IReleasableListener *getVertexArrayReleaseListener() = 0;
virtual IReleasableListener *getTextureReleaseListener() = 0;
virtual IReleasableListener *getTextureMultisampleReleaseListener() = 0;
// Helper methods
virtual void AddProgram(IProgram *program) = 0;
virtual void AddFramebuffer(IFramebuffer *framebuffer) = 0;
virtual void AddVertexArray(IVertexArray *vertex_array) = 0;
virtual void AddTexture(ITexture *texture) = 0;
virtual void AddTextureMultisample(ITextureMultisample *texture) = 0;
public:
// Properties
InitializationStatus const &InitializationStatus; // Initialization status
Version2 const &MinVersion; // Minimum native graphics version supported
Version2 const &MaxVersion; // Minimum native graphics version supported
Version2 const &Version; // Currently used native graphics version
RendererCapabilities const &Capabilities; // Some renderer capabilities
IWindow* const &Window; // Assigned window
INativeGraphics* const &NativeGraphics; // Native graphics object
IProgram* const &ActiveProgram; // Active shader program
IFramebuffer* const &ActiveFramebuffer; // Active framebuffer
IVertexArray* const &ActiveVertexArray; // Active vertex array
IBaseTexture* const &ActiveTarget; // Active render target
ITexture* const &ActiveMask; // Active mask
const nitisa::Block* const &ActiveBlock; // Active block clipper
const Bitmask* const &ActiveBitmask; // Active binary mask
const CubicBezierSplineLimits* const &ActiveSplineLimits; // Active cubic bezier spline clipping
const Mat4f* const &ActivePrimitiveMatrix; // Active primitive transformation matrix
Rect const &Viewport; // Current viewport
RendererTechnology const &Technology; // Technology renderer uses
// Getters
virtual bool setWindow(IWindow *value, const bool fast_switch, const bool force) = 0; // Assign window and initialize if it's not a fast switch. Clean up if nullptr. "force" is used with "fast_switch" equal to true only. If "force" is true, previous window resources won't be cleaned up
// Object getters
virtual int getPlatformFontCount() = 0; // Return count of platform fonts
virtual IPlatformFont *getPlatformFont(const int index) = 0; // Return platform font by index
virtual int getProgramCount() = 0; // Return count of shader programs
virtual IProgram *getProgram(const int index) = 0; // Return shader program by index
virtual int getFramebufferCount() = 0; // Return count of framebuffers
virtual IFramebuffer *getFramebuffer(const int index) = 0; // Return framebuffer by index
virtual int getVertexArrayCount() = 0; // Return count of vertex arrays
virtual IVertexArray *getVertexArray(const int index) = 0; // Return vertex array by index
virtual int getTextureCount() = 0; // Return count of textures
virtual ITexture *getTexture(const int index) = 0; // Return texture by index
virtual int getImageTextureCount() = 0; // Return count of textures created from system memory images
virtual ITexture *getImageTexture(const int index) = 0; // Return texture created from system memory image by index
virtual const nitisa::Image *getImageTextureData(const int index) = 0; // Return image of texture created from system memory image by index
virtual int getMaskCount() = 0; // Return mask count
virtual ITexture *getMask(const int index) = 0; // Return mask by index
virtual const Mat4f *getMaskMatrix(const int index) = 0; // Return mask matrix by index
virtual int getTextureMultisampleCount() = 0; // Return count of multi-sample textures
virtual ITextureMultisample *getTextureMultisample(const int index) = 0; // Return multi-sample texture by index
// Setters
virtual bool setWindow(IWindow *value) = 0; // Assign window and initialize. Clean up if nullptr
// Active state setters
virtual IRenderer *ActivateProgram(IProgram *value) = 0; // Activate new shader program. Use nullptr to activate default program
virtual IRenderer *ActivateFramebuffer(IFramebuffer *value) = 0; // Activate new framebuffer. Use nullptr to activate default framebuffer
virtual IRenderer *ActivateVertexArray(IVertexArray *value) = 0; // Activate new vertex array. Use nullptr to activate default vertex array
virtual IRenderer *ActivateTarget(IBaseTexture *value) = 0; // Activate new rendering target. Use nullptr to remove active rendering target. If it's nullptr, draw on window. If it's not nullptr, draw on rendering target
virtual IRenderer *ActivateMask(ITexture *value) = 0; // Activate new mask. Use nullptr to deactivate mask usage
virtual IRenderer *ActivateBlock(const Block *value) = 0; // Activate new block clipper. Use nullptr to remove block clipper
virtual IRenderer *ActivateBlock(const Block &value) = 0; // Activate new block clipper
virtual IRenderer *ActivateBitmask(const Bitmask *value) = 0; // Activate new binary mask. Use nullptr to deactivate binary mask usage
virtual IRenderer *ActivateBitmask(const Bitmask &value) = 0; // Activate new binary mask
virtual IRenderer *ActivateSplineLimits(const CubicBezierSplineLimits *value) = 0; // Activate new spline clipper. Use nullptr to remove spline clipper
virtual IRenderer *ActivateSplineLimits(const CubicBezierSplineLimits &value) = 0; // Activate new spline clipper
virtual IRenderer *ActivatePrimitiveMatrix(const Mat4f *value) = 0; // Activate new primitive matrix. Use nullptr to activate default (identity) primitive matrix
virtual IRenderer *ActivatePrimitiveMatrix(const Mat4f &value) = 0; // Activate new primitive matrix
// Platform fonts
virtual IPlatformFont *CreatePlatformFont(const String &fontname, const int height, const FontWeight weight, const bool italic, const bool underline, const bool strikeout, const bool monospace) = 0; // Create platform font with specified parameters. Create only if not exists with same parameters
// Programs
virtual IProgram *CreateProgram(const AnsiString &vertex, const AnsiString &fragment, const Bindings &bindings = { }) = 0; // Create shader program from specified source codes of vertex and fragment shaders. May return nullptr not only when error happend but also if shader programs are not supported
virtual IProgram *CreateProgram(const AnsiString &fragment) = 0; // Create shader program from source code of predefined format. May return nullptr
virtual IProgram *CreateProgram(const Shaders &shaders, const Bindings &bindings = { }) = 0; // Create shader program from list of shader sources with their types. Can return nullptr in case of error or if shader programs are not supported
// Framebuffers
virtual IFramebuffer *CreateFramebuffer() = 0; // Create new framebuffer
// Vertex arrays
virtual IVertexArray *CreateVertexArray(const VertexFormat &format) = 0; // Create new vertex array with specified vertex format
virtual IVertexArray *CreateVertexArray(const VertexFormat &format, const size_t vertex_count, const bool immutable) = 0; // Create new vertex array with specified vertex format and vertex count. Can be either immutable (unchangable) or dynamic
virtual IVertexArray *CreateVertexArray(const VertexFormat &format, const size_t polygon_count, const size_t polygon_size, const bool immutable) = 0; // Create new vertex array with specified vertex format, polygon count and size of each polygon. Can be either immutable (unchangable) or dynamic
// Textures
virtual ITexture *CreateTexture(const int width, const int height, const int level_count, const TextureFormat format, const bool precomputed) = 0; // Create texture with specified size and format
virtual ITexture *CreateTextureFromImage(const nitisa::Image &data, const int level_count, const TextureFormat format, const bool precomputed) = 0; // Create texture by image data, if corresponding to data texture already exists, return existing one
virtual ITexture *CreateTextureFromFile(const String &filename, const int level_count, const TextureFormat format, const bool precomputed) = 0; // Create texture from image in file. If corresponding texture(same filename and format) already was loaded, return it instead of loading it again
virtual ITextureMultisample *CreateTextureMultisample(const int width, const int height, const TextureFormat format, const int samples, const bool fixed_sample_locations) = 0; // Create multi-sample texture
// Helpers
virtual IRenderer *CreateInstance() = 0; // Create same class object(without initializing, with same constructor parameters - double buffering and multisampling if supported)
virtual IRenderer *RestoreState() = 0; // Restore state(VAO, VBO, Program, Program subroutines, ...)
virtual IRenderer *Activate() = 0; // Should be called before drawing outside DrawBegin()/DrawEnd() block if using multiple renderers. DrawBegin() calls it automatically. Also when form is being destroyed(receive OnDestroy notification), it also calls this method
// Draw and present
virtual bool DrawBegin(Rect &viewport) = 0; // Prepare for drawing. Viewport may be changed(if client area of window was changed, it will be set to new client area). Should always be called before start drawing. It is done automatically in default form implementation
virtual bool DrawEnd() = 0; // Clean up after drawing. Should be called after all drawings. It is called automatically by default form implementation
virtual bool Present() = 0; // Draw to screen. Render target should be set to nullptr
// Masks
virtual bool PushMask(ITexture *mask, const Mat4f &matrix) = 0; // Add mask in list. The matrix should be relative to prev mask. Texture is unchanged. Alpha channel of masks is used for clipping
virtual void PopMask() = 0; // Delete last added masking texture. Texture is unchanged
// Fill entire area of current render target
virtual IRenderer *Clear(const Color &color) = 0; // Clear with specified color. Clear color is unchanged after it. Use it for texture clean(when render target is texture)
virtual IRenderer *DrawLine(const PointF &p1, const PointF &p2, const Color &color) = 0; // Draw line
virtual IRenderer *DrawLine(const PointF &p1, const PointF &p2, const Color &c1, const Color &c2) = 0; // Draw line. Points can have different colors
virtual IRenderer *DrawLines(const std::vector<PointF> &points, const Color &color, const bool loop) = 0; // Draw lines
virtual IRenderer *DrawTriangle(const PointF &p1, const PointF &p2, const PointF &p3, const Color &color) = 0; // Draw triangle
virtual IRenderer *DrawTriangle(const PointF &p1, const PointF &p2, const PointF &p3, const Color &c1, const Color &c2, const Color &c3) = 0; // Draw triangle. Points can have different colors
virtual IRenderer *DrawRectangle(const RectF &rect, const Color &color) = 0; // Draw rectangle
virtual IRenderer *DrawRectangle(const RectF &rect, const BorderColor &colors) = 0; // Draw rectangle. Points can have different colors
virtual IRenderer *DrawChecker(const RectF &rect, const PointF &grid_size, const Color &c1, const Color &c2) = 0; // Draw checker pattern
virtual IRenderer *DrawHSVPlane(const RectF &rect, const float hue) = 0; // Draw HSV rectangle with specified Hue value
virtual IRenderer *DrawPolygon(const std::vector<PointF> &points, const Color &color) = 0; // Draw polygon
virtual IRenderer *DrawPolygons(const std::vector<std::vector<PointF>> &polygons, const Color &color) = 0; // Draw polygons
virtual IRenderer *DrawGradient(const RectF &rect, nitisa::Gradient &g) = 0; // Draw gradient
virtual IRenderer *DrawHSVGradient(const RectF &rect, nitisa::Gradient &g) = 0; // Draw gradient. Gradient point colors are in HSV color space
virtual IRenderer *DrawImage(IBaseTexture *image, const float transparency) = 0; // Draw image
virtual IRenderer *DrawImage(IBaseTexture *image, const float transparency, const RectF &part) = 0; // Draw image part
virtual IRenderer *DrawBlock(const RectF &block, const RectF &border, const RectF &radius, const BlockColors &colors) = 0; // Draw block
virtual IRenderer *DrawBlock(const RectF &block, const RectF &border, const RectF &radius, const BlockColors &colors, const RectF &rect) = 0; // Draw block part
virtual IRenderer *DrawText(const String &text, IPlatformFont *pf, const float distance, const Color &color) = 0; // Draw text
virtual IRenderer *BlurImage(ITexture *image, const int radius, const BlurType type) = 0; // Blur image
virtual IRenderer *BlurImage(ITexture *source, const int radius, const BlurType type, ITexture *target) = 0; // Blur image without modifying original one
virtual IRenderer *InversePixels(const RectF &rect) = 0; // Inverse pixels in specified rectangle on current target
};
Since release 3.0.0 there is possibility of adding binary masking. It means you may specify 32-bit binary mask where each bit indicates whether pixel should be drawn(value 1) or omitted(value 0). Lines have only 1D binary mask. Other primitives have 2D binary masks which means you may specify two masks - for X and for Y directions. The starting point of mask is start point of line, left-top corner of primitive rectangle, or left-top corner of bounding rectangle(for triangles). The binary mask coordinates then calculated like this: for example, we have line from { 10, 10 } point to { 100, 10 } point. In this case a coordinate in binary mask space will be (X - 10) % 32, where X is coordinate on a line, 10 is a start coordinate, and 32 is count of bits in binary mask. The similar algorithm is used for two dimensional masks as well. Also, 2D binary masks have option how binary mask coordinates will be calculated. Either primitive or form coordinates can be used for calculation of binary mask coordinates. When primitive coordinates are used, they are applied before any transformations. When form ones are used, there is no any transformations at all. With binary masks you can easily achieve effects like on the following image.
To activate/deactivate binary mask use ActivateBitmask()
methods.
There are several so called active states in the renderer. It means that if you activate some feature, it remains active until you deactivate it or activate with another parameters. These features are: program - current active shader program used in drawing, framebuffer - current active framebuffer being used for drawing operations, vertex array - the vertex array which is the source of vertex data used for drawing, target - a texture where drawing is happening to, mask - texture being used to clip drawing operations with active target, block - primitive being used for clipping, bitmask - binary masking applied during drawing, spline limits - cubic bezier splines which clip rendering by its edges, primitive matrix - the matrix being used to transform primitives before rendering.
To active those states you need to call corresponding Activate* method with need program, framebuffer, mask, matrix, etc. Activated state remains active until you deactivate it with calling Activate* method with nullptr argument. To change active state you do not need to deactivate it first. For example, you may activate primitive matrix, draw some primitives, activate another primitive matrix, draw another bunch of primitives and then deactivate primitive matrix.
Do not forget to leave a renderer state the same as it was before you started drawing. Lets say you create your own widget. That widget will definitely have the method which draws the widget. So, at the beginning of the drawing you need to store current renderer active state, use renderer's methods to change active state as you need to proper drawing your widget, and then restore states. You do not need to save all active states. Save and restore only those ones you change during drawing your widget. For example you may do it this way.
// Save active state for target and primitive matrix
ITexture *active_target{ renderer->ActiveTarget };
Mat4f *pactive_primitive_matrix{ renderer->ActivePrimitiveMatrix }, active_primitive_matrix;
if (pactive_primitive_matrix)
{
active_primitive_matrix = *pactive_primitive_matrix;
pactive_primitive_matrx = &active_primitive_matrix;
}
...
// Change active target and primitive matrix as you need by ActivateTarget() and ActivatePrimitiveMatrix() methods to draw your widget
...
// Restore states
renderer->ActivateTarget(active_target);
renderer->ActivatePrimitiveNatrix(active_primitive_matrix);
In order to simplify save/restore active states process, we have added several helper classes. They are CStoreBitmask, CStoreBlock, CStoreFramebuffer, CStoreMask, CStorePrimitiveMatrix, CStoreProgram, CStoreSplineLimits, CStoreTarget, CStoreVertexArray and CStoreState. The last one saves and restores all active states. So, the code above can be simplified using these classes to following one.
// Save active state for target and primitive matrix
CStoreTarget store_target{ renderer };
CStorePrimitiveMatrix store_primitive_matrix{ renderer };
...
// Change active target and primitive matrix as you need by ActivateTarget() and ActivatePrimitiveMatrix() methods to draw your widget
...
In this case you don't need "restore" part. It is automatically done in CStore* classes' destructors.
There are two types of masks being used during rendering. The first one is the mask in active state. In this state only one texture (image) can be used at the same moment. This mask texture is applied only when drawing to texture (ActiveTarget is set to some texture). The other mask type is so called global mask. The global mask is being used to limit drawing area during rendering to form/window directly. Its main purpose is clipping child widgets by parent ones during rendering on a form. Widgets can add global masks by PushMask() method and remove mask by calling PopMask() method. Several global masks can be applied simultaneously. They form an hierarchy which results as clipping by all global masks one by one and drawing only what is left after limiting by all those masks.
Only alpha channel of masks is being used. This mask's texture alpha channel is used to specify opacity of a drawn pixel. When its value is 0, a pixel won't be drawn at all. When its value is 127, a pixel will be drawn half transparent. When its value is 255, a pixel will be drawn as usually
There are three methods for creation of shader programs in renderer. The first one require to specify both vertex and fragment shader source code. The second one requires only a fragment shader source code. The third one can be used to create programs with multiple different type shaders. All source codes should be in language particular renderer uses. GLSL for renderer which uses OpenGL or HLSL for renderer which uses DirectX and so on.
The first method allow you to create completely custom shader programs. The second method will use all the renderer default functionality except for pixel color calculation which should be implemented in your source code.
The feature of limitation drawing operations by two splines was added in version 7.0.0. After activating spline limitation by calling of ActivateSplineLimits() with spline limits provided all primitive drawing operations will draw only those pixels that lies between specified two splines. When limitation is no longer needed, you have to call ActivateSplineLimits(nullptr). On the picture below you can see rectangles drawn with such a limitation. The first spline in this case is a cubic bezier spline and the second one is a line lying on the X axis.
As you can see from declaration of CubicBezierSpline each spline consists of 4 points. The P1 and P2 points are the beginning and the ending of the spline. Points C1 and C2 are control points. So it is actually a cubic bezier curve(it is also clear from the name of the structure). If you need quadratic bezier curve instead, just set both control points same value so they are equal. If you need straight line, set C1 the same value as you have for line start(P1) and set C2 the same value as you have for line end(P2).
Since release 11.0.0 you can create and use multi-sample textures in renderer. Renderer has got CreateTextureMultisample() method which you can use to create such a textures. Please note that OpenGL 3.2+ is required to this feature works. If OpenGL version lower the method will return nullptr. Additionally ActiveTarget property type, ActivateTarget() and DrawImage() texture arguments are now IBaseTexture pointers. Both ITexture and ITextureMultisample are derived from IBaseTexture and thus both common and multi-sample textures can be used as render target and can be drawn by DrawImage() methods. Below you can see difference between drawing into common texture (left) and into multi-sample texture (right). Although using multi-sample texture makes better result it also works slower.
In release 11.0.0 DrawBegin() method has got second argument IWindow *window
. This argument is being used for multi-window rendering. If you work with Nitisa like described in tutorials, you create separate renderer for each form/window using Create() method of the CRenderer helper class from Platform package. But this is not the only way. If your application has several windows compatible with the same renderer settings (which is almost always true), you may use only one renderer to draw on every window. It may be very helpful because in this case you may share all renderer resources (like textures, framebuffers, vertex arrays and shader programs) between all your windows instead of creating duplicates as in case of using separate renderer for each window.
To turn on multi-window renderer all you need is to provide instance of the same renderer to all (or some) your forms. You may create main form as usual using CRenderer::Create()
method to create its renderer but after that you may use main form renderer to pass it to your other forms. You may use Application->MainForm->getRenderer()
to get main form renderer when you need it if you set main form as the main one in the application manager.
That is all is needed for multi-window rendering start working. Default form implementation handles all automatically.
This feature has no meaning on the Android platform as there is always only one window (native window) and only one renderer at the same time.
Namespace: | nitisa |
Include: | Nitisa/Interfaces/IRenderer.h |