1 /**
2  * Copyright:       Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved
3  * Authors:         $(Gabriel Gheorghe)
4  * License:         $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007)
5  * Source:          $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/graphics/backend/impl.d)
6  * Documentation:
7  * Coverage:
8 **/
9 module liberty.graphics.backend.impl;
10 
11 import bindbc.opengl;
12 import liberty.logger;
13 import liberty.graphics.backend.data;
14 import liberty.graphics.backend.factory;
15 import liberty.math.vector;
16 
17 /// Backend class for graphics engine.
18 /// It implements $(D IGfxBackendFactory) service.
19 final class GfxBackend : IGfxBackendFactory {
20   private {
21     // getInfo
22     GfxBackendInfo info;
23     // getOptions
24     GfxBackendOptions options;
25   }
26 
27   /// Instantiate class using $(D GfxBackendInfo) and $(D GfxBackendOptions).
28   this(GfxBackendInfo info, GfxBackendOptions options) {
29     // Initialize backend info and options
30     this.info = info;
31     this.options = options;
32     // Enable depth and stencil test by default
33     setDepthTestEnabled(true);
34     setStencilTestEnabled(true);
35     // TODO. Apply options to this, call graphics api functions
36     setBackColor(45, 45, 45, 255);
37     enableAnisotropicFiltering(0.0f);
38     Logger.info("Graphics backend has been created successfully", typeof(this).stringof);
39   }
40 
41   /// Clear the depth, stencil and color of the screen.
42   /// Returns reference to this so it can be used in a stream.
43   typeof(this) clearScreen() {
44     glClearDepth(1.0);
45     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
46     glClearColor(
47       options.backColor.r / 255.0f,
48       options.backColor.g / 255.0f,
49       options.backColor.b / 255.0f,
50       options.backColor.a / 255.0f
51     ); // TODO. Optimize
52     glDepthFunc(GL_LEQUAL);
53     return this;
54   }
55 
56   /// Enable or disable wireframe.
57   /// Returns reference to this so it can be used in a stream.
58   typeof(this) setWireframeEnabled(bool enabled = true) {
59       glPolygonMode(GL_FRONT_AND_BACK, enabled ? GL_LINE : GL_FILL);
60   options.wireframeEnabled = enabled;
61     return this;
62   }
63 
64   /// Swap between wireframe and non-wireframe mode.
65   /// Returns reference to this so it can be used in a stream.
66   typeof(this) swapWireframe() {
67     glPolygonMode(GL_FRONT_AND_BACK, options.wireframeEnabled ? GL_FILL : GL_LINE);
68     options.wireframeEnabled = !options.wireframeEnabled;
69     return this;
70   }
71 
72   /// Enable or disable depth test.
73   /// Returns reference to this so it can be used in a stream.
74   typeof(this) setDepthTestEnabled(bool enabled = true) {
75     enabled
76       ? glEnable(GL_DEPTH_TEST)
77       : glDisable(GL_DEPTH_TEST);
78     options.depthTestEnabled = enabled;
79     return this;
80   }
81 
82   /// Set false depth mask to disable writing to depth buffer, render stuff that
83   /// shouldn't influence the depth buffer then set true to enable it again.
84   /// Returns reference to this so it can be used in a stream.
85   typeof(this) setDepthMask(bool value = true) {
86     glDepthMask(value);
87     return this;
88   }
89   
90   /// Enable or disable stencil test.
91   /// Returns reference to this so it can be used in a stream.
92   typeof(this) setStencilTestEnabled(bool enabled = true) {
93     enabled
94       ? glEnable(GL_STENCIL_TEST)
95       : glDisable(GL_STENCIL_TEST);
96     options.stencilTestEnabled = enabled;
97     return this;
98   }
99   
100   /// Set true stencil mask and each bit is written to the stencil buffer as is.
101   /// Set false stencil mask and each bit ends up as 0 in the stencil buffer, disabling writes.
102   /// TODO: Enable custom stencil mask.
103   /// Returns reference to this so it can be used in a stream.
104   typeof(this) setStencilMask(bool value = true) {
105     glStencilMask(value ? 0xFF : 0x00);
106     return this;
107   }
108 
109   /// Enable or disable texture.
110   /// Returns reference to this so it can be used in a stream.
111   typeof(this) setTextureEnabled(bool enabled = true) {
112     enabled
113       ? glEnable(GL_TEXTURE_2D)
114       : glDisable(GL_TEXTURE_2D);
115     options.textureEnabled = enabled;
116     return this;
117   }
118 
119   /// Enable or disable culling.
120   /// Returns reference to this so it can be used in a stream.
121   typeof(this) setCullingEnabled(bool enabled = true) {
122     enabled
123       ? (glEnable(GL_CULL_FACE), glCullFace(GL_BACK))
124       : glDisable(GL_CULL_FACE);
125     options.cullingEnabled = enabled;
126     return this;
127   }
128 
129   /**
130    * Set back color r-red, g-green, b-blue, a-alpha scalars.
131    * A channel value must be in range 0-255, so it can handle 256 possible values.
132    * Returns reference to this so it can be used in a stream.
133   **/
134   typeof(this) setBackColor(ubyte r, ubyte g, ubyte b, ubyte a)   {
135     return setBackColor(Color4(r, g, b, a));
136   }
137 
138   /**
139    * Set back color using $(Color4).
140    * A channel value must be in range 0-255, so it can handle 256 possible values.
141    * Returns reference to this so it can be used in a stream.
142   **/
143   typeof(this) setBackColor(Color4 color)   {
144     options.backColor = color;
145     return this;
146   }
147 
148   /**
149    * Enable the alpha blend.
150    * Returns reference to this so it can be used in a stream.
151   **/
152   typeof(this) enableAlphaBlend() {
153     glEnable(GL_BLEND);
154     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
155     options.alphaBlendEnabled = true;
156     return this;
157   }
158 
159   /**
160    * Disable the blend.
161    * Returns reference to this so it can be used in a stream.
162   **/
163   typeof(this) disableBlend() {
164     glDisable(GL_BLEND);
165     options.alphaBlendEnabled = false;
166     return this;
167   }
168 
169   /**
170    * Enable and set anisotropic filtering if possible.
171    * Use 0.0f to disable it.
172    * Only values 4.0f, 8.0f and 16.0f are supported for enabling it.
173    * Returns reference to this so it can be used in a stream.
174   **/
175   typeof(this) enableAnisotropicFiltering(float value)  
176   in (value == 0.0f || value == 4.0f || value == 8.0f || value == 16.0f,
177     "Only values 0.0f, 4.0f, 8.0f and 16.0f are supported.")
178   do {
179     options.anisotropicFiltering = value;
180     return this;
181   }
182 
183   /**
184    * Returns true if the given extension is supported.
185   **/
186   bool supportsExtension(string extension)   const {
187     foreach (el; info.extensions)
188       if (el == extension)
189         return true;
190         
191     return false;
192   }
193 
194   /// Returns backend info.
195   GfxBackendInfo getInfo()   {
196     return info;
197   }
198 
199   /// Returns backend options.
200   GfxBackendOptions getOptions()   {
201     return options;
202   }
203 }