1 /* 2 ******************************************************************************************* 3 * Dgame (a D game framework) - Copyright (c) Randy Schütt 4 * 5 * This software is provided 'as-is', without any express or implied warranty. 6 * In no event will the authors be held liable for any damages arising from 7 * the use of this software. 8 * 9 * Permission is granted to anyone to use this software for any purpose, 10 * including commercial applications, and to alter it and redistribute it 11 * freely, subject to the following restrictions: 12 * 13 * 1. The origin of this software must not be misrepresented; you must not claim 14 * that you wrote the original software. If you use this software in a product, 15 * an acknowledgment in the product documentation would be appreciated but is 16 * not required. 17 * 18 * 2. Altered source versions must be plainly marked as such, and must not be 19 * misrepresented as being the original software. 20 * 21 * 3. This notice may not be removed or altered from any source distribution. 22 ******************************************************************************************* 23 */ 24 module Dgame.Graphic.Shape; 25 26 private: 27 28 import derelict.opengl3.gl; 29 30 import Dgame.Graphic.Drawable; 31 import Dgame.Graphic.Transformable; 32 import Dgame.Graphic.Texture; 33 34 import Dgame.Math.Vertex; 35 import Dgame.Math.Vector2; 36 import Dgame.Math.Rect; 37 import Dgame.Graphic.Color; 38 import Dgame.Math.Geometry; 39 40 public: 41 42 /** 43 * Shape defines a drawable geometric shape. 44 * 45 * Author: Randy Schuett (rswhite4@googlemail.com) 46 */ 47 class Shape : Transformable, Drawable { 48 public: 49 /** 50 * Defines how the Shape should be filled. 51 */ 52 enum Fill : ubyte { 53 Full, /// Full / Complete fill 54 Point, /// Show only the points 55 Line /// Show only the lines 56 } 57 58 protected: 59 Texture* _texture; 60 Vertex[] _vertices; 61 62 @nogc 63 override void draw(ref const Window wnd) nothrow { 64 glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_LINE_BIT); 65 scope(exit) glPopAttrib(); 66 67 if (this.lineWidth != 1) { 68 glLineWidth(this.lineWidth); 69 70 if (this.antiAliasing) 71 glEnable(GL_LINE_SMOOTH); 72 } 73 74 final switch (this.fill) { 75 case Fill.Full: 76 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 77 break; 78 79 case Fill.Line: 80 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 81 break; 82 83 case Fill.Point: 84 glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); 85 break; 86 } 87 88 wnd.draw(this.geometry, super.getMatrix(), _texture, _vertices); 89 } 90 91 public: 92 /** 93 * The geometric type of the shape 94 * 95 * See: Geometric enum 96 */ 97 Geometry geometry; 98 /** 99 * Fill option. Default is Fill.Full 100 */ 101 Fill fill = Fill.Full; 102 /** 103 * Option for the line width. Default is 1 104 */ 105 ubyte lineWidth = 1; 106 /** 107 * Optional anti-alias for lines thicker than 1. Default is false. 108 * 109 * Note: this is redundant if you have already enabled anti-aliasing 110 */ 111 bool antiAliasing = false; 112 113 final: 114 /** 115 * CTor 116 */ 117 @nogc 118 this(Geometry geo) pure nothrow { 119 this.geometry = geo; 120 } 121 122 /** 123 * CTor 124 */ 125 this(Geometry geo, Vertex[] vertices) pure nothrow { 126 this(geo); 127 128 _vertices ~= vertices; 129 } 130 131 /** 132 * CTor for circles 133 */ 134 this(size_t radius, const Vector2f center, size_t vecNum = 30) pure nothrow { 135 import std.math : PI, cos, sin; 136 137 assert(vecNum >= 10, "Too few edges for a circle"); 138 139 this(Geometry.TriangleFan); 140 141 enum real PIx2 = PI * 2; 142 immutable float Deg2Rad = PIx2 / vecNum; 143 144 _vertices.reserve(vecNum); 145 146 for (size_t i = 0; i < vecNum; i++) { 147 immutable float degInRad = i * Deg2Rad; 148 149 immutable float x = center.x + cos(degInRad) * radius; 150 immutable float y = center.y + sin(degInRad) * radius; 151 152 this.append(Vector2f(x, y)); 153 } 154 } 155 156 /** 157 * Clear all Vertices but preserve the storage and capacity 158 */ 159 void clear() nothrow { 160 _vertices.length = 0; 161 _vertices.assumeSafeAppend(); 162 } 163 164 /** 165 * Stores a Vertex 166 */ 167 void append(ref const Vertex vertex) pure nothrow { 168 _vertices ~= vertex; 169 } 170 171 /** 172 * Stores a Vertex 173 */ 174 void append(const Vector2f vec) pure nothrow { 175 _vertices ~= Vertex(vec); 176 } 177 178 /** 179 * Stores multiple Vertices 180 */ 181 void append(Vertex[] vertices) pure nothrow { 182 _vertices.reserve(vertices.length); 183 _vertices ~= vertices; 184 } 185 186 /** 187 * Returns all Vertices 188 */ 189 @nogc 190 inout(Vertex[]) getVertices() inout pure nothrow { 191 return _vertices; 192 } 193 194 /** 195 * Set the color of <b>all</b> Vertices 196 * 197 * Note: If you only want to set specific Vertices to a specific color, you should use getVertices() 198 * and adapt the specific entries. 199 */ 200 @nogc 201 void setColor(const Color4b col) pure nothrow { 202 foreach (ref Vertex v; _vertices) { 203 v.color = Color4f(col); 204 } 205 } 206 207 /** 208 * Set or reset a Texture 209 */ 210 @nogc 211 void setTexture(Texture* texture) pure nothrow { 212 _texture = texture; 213 if (texture) 214 this.setTextureRect(Rect(0, 0, texture.width, texture.height)); 215 } 216 217 /** 218 * Set (or reset) a Texture and set the corresponding Rect 219 */ 220 @nogc 221 void setTexture(Texture* texture, const Rect rect) pure nothrow { 222 _texture = texture; 223 224 if (texture) 225 this.setTextureRect(rect); 226 } 227 228 /** 229 * Returns the current texture or null 230 */ 231 @nogc 232 inout(Texture*) getTexture() inout pure nothrow { 233 return _texture; 234 } 235 236 /** 237 * Set the corresponding Texture Rect 238 */ 239 @nogc 240 void setTextureRect(const Rect rect) pure nothrow { 241 assert(_texture, "No texture defined"); 242 243 const Rect clip = this.getVertexRect(); 244 foreach (ref Vertex v; _vertices) { 245 immutable float xratio = clip.width > 0 ? (v.position.x - clip.x) / clip.width : 0; 246 immutable float yratio = clip.height > 0 ? (v.position.y - clip.y) / clip.height : 0; 247 248 v.texCoord.x = (rect.x + rect.width * xratio) / _texture.width; 249 v.texCoord.y = (rect.y + rect.height * yratio) / _texture.height; 250 } 251 } 252 253 /** 254 * Returns the Rect which contains all vertices 255 */ 256 @nogc 257 Rect getVertexRect() const pure nothrow { 258 assert(_vertices.length > 0, "No vertices"); 259 260 float left = _vertices[0].position.x; 261 float top = _vertices[0].position.y; 262 float right = _vertices[0].position.x; 263 float bottom = _vertices[0].position.y; 264 265 foreach (ref const Vertex v; _vertices[1 .. $]) { 266 // Update left and right 267 if (v.position.x < left) 268 left = v.position.x; 269 else if (v.position.x > right) 270 right = v.position.x; 271 // Update top and bottom 272 if (v.position.y < top) 273 top = v.position.y; 274 else if (v.position.y > bottom) 275 bottom = v.position.y; 276 } 277 278 immutable int l = cast(int) left; 279 immutable int t = cast(int) top; 280 immutable uint w = cast(uint)(right - left); 281 immutable uint h = cast(uint)(bottom - top); 282 283 return Rect(l, t, w, h); 284 } 285 286 /** 287 * Returns the clip Rect 288 */ 289 @nogc 290 Rect getClipRect() const pure nothrow { 291 Rect rect = this.getVertexRect(); 292 rect.move(Vector2i(super.getPosition())); 293 294 return rect; 295 } 296 }